Browse Source

Restructure content

Use content collections for neater organization.
master
TheoryOfNekomata 6 months ago
parent
commit
a61466987f
56 changed files with 374 additions and 55 deletions
  1. +9
    -21
      astro.config.mjs
  2. +5
    -5
      package.json
  3. +3
    -0
      patchouli.book.json
  4. +3
    -3
      pnpm-lock.yaml
  5. +9
    -5
      src/components/FrequenciesForm/index.tsx
  6. +2
    -2
      src/components/Score.astro
  7. +7
    -0
      src/content/appendices/01-piano-key-frequencies.mdx
  8. +0
    -0
      src/content/chapters/01-introduction.mdx
  9. +0
    -0
      src/content/chapters/02-speed.mdx
  10. +0
    -0
      src/content/chapters/03-analysis.mdx
  11. +0
    -0
      src/content/chapters/04-sound-projection.mdx
  12. +3
    -0
      src/content/chapters/05-limits.mdx
  13. +3
    -0
      src/content/chapters/06-overcoming-doubts.mdx
  14. +3
    -0
      src/content/chapters/07-fingering.mdx
  15. +3
    -0
      src/content/chapters/08-pulse.mdx
  16. +3
    -0
      src/content/chapters/09-sensation.mdx
  17. +3
    -0
      src/content/chapters/10-autopilot.mdx
  18. +0
    -0
      src/content/chapters/11-musicality.mdx
  19. +3
    -0
      src/content/chapters/12-the-musician.mdx
  20. +3
    -0
      src/content/chapters/13-ornaments.mdx
  21. +3
    -0
      src/content/chapters/14-rests.mdx
  22. +3
    -0
      src/content/chapters/15-gestures.mdx
  23. +3
    -0
      src/content/chapters/16-articulation.mdx
  24. +3
    -0
      src/content/chapters/17-expression.mdx
  25. +3
    -0
      src/content/chapters/18-mistakes.mdx
  26. +3
    -0
      src/content/chapters/19-performance.mdx
  27. +0
    -0
      src/content/chapters/20-trills.mdx
  28. +0
    -0
      src/content/chapters/21-speed-and-anticipation.mdx
  29. +3
    -0
      src/content/chapters/22-thirds.mdx
  30. +28
    -0
      src/content/config.ts
  31. +186
    -0
      src/content/layouts/Appendix.astro
  32. +0
    -0
      src/content/layouts/Cover.astro
  33. +8
    -0
      src/content/layouts/Default.astro
  34. +0
    -0
      src/content/special/foreword.mdx
  35. +18
    -0
      src/pages/[...slug].astro
  36. +19
    -0
      src/pages/appendices/[...slug].astro
  37. +0
    -8
      src/pages/appendix01-piano-key-frequencies.mdx
  38. +0
    -0
      src/pages/ch05-limits.mdx
  39. +0
    -0
      src/pages/ch06-overcoming-doubts.mdx
  40. +0
    -0
      src/pages/ch07-fingering.mdx
  41. +0
    -0
      src/pages/ch08-pulse.mdx
  42. +0
    -0
      src/pages/ch09-sensation.mdx
  43. +0
    -0
      src/pages/ch10-autopilot.mdx
  44. +0
    -0
      src/pages/ch12-the-musician.mdx
  45. +0
    -0
      src/pages/ch13-ornaments.mdx
  46. +0
    -0
      src/pages/ch14-rests.mdx
  47. +0
    -0
      src/pages/ch15-gestures.mdx
  48. +0
    -0
      src/pages/ch16-articulation.mdx
  49. +0
    -0
      src/pages/ch17-expression.mdx
  50. +0
    -0
      src/pages/ch18-mistakes.mdx
  51. +0
    -0
      src/pages/ch19-performance.mdx
  52. +0
    -0
      src/pages/ch22-thirds.mdx
  53. +18
    -0
      src/pages/chapters/[...slug].astro
  54. +10
    -0
      src/pages/index.astro
  55. +0
    -7
      src/pages/index.mdx
  56. +4
    -4
      src/pages/toc.astro

+ 9
- 21
astro.config.mjs View File

@@ -1,37 +1,25 @@
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import mdx from '@astrojs/mdx';
import AutoImport from 'astro-auto-import';

const defaultLayoutPlugin = () => (tree, file) => {
const path = file.history.at(-1).split('/').at(-1);
file.data.astro.frontmatter.layout = (
path.startsWith('index.')
? '../layouts/Cover.astro'
: '../layouts/Default.astro'
);
};
import autoImport from 'astro-auto-import';
import react from '@astrojs/react';

// https://astro.build/config
export default defineConfig({
trailingSlash: 'never',
output: 'static',
build: {
format: 'file',
format: 'file'
},
compressHTML: false,
markdown: {
remarkPlugins: [defaultLayoutPlugin],
extendDefaultPlugins: true,
},
integrations: [
tailwind({
applyBaseStyles: false,
applyBaseStyles: false
}),
AutoImport({
imports: [
'./src/components/Score.astro',
],
autoImport({
imports: ['./src/components/Score.astro']
}),
mdx()
mdx(),
react()
],
});

+ 5
- 5
package.json View File

@@ -16,12 +16,12 @@
"@astrojs/react": "^3.3.1",
"@astrojs/tailwind": "^5.1.0",
"@theoryofnekomata/react-musical-keyboard": "1.0.13",
"@types/react": "^18.3.0",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"astro": "^4.5.16",
"jsdom": "^24.0.0",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.4",
"verovio": "^4.1.0"
@@ -33,7 +33,7 @@
"@types/verovio": "^3.13.4",
"archiver": "^7.0.1",
"astro-auto-import": "^0.4.2",
"tsx": "^4.7.2",
"prop-types": "^15.8.1"
"prop-types": "^15.8.1",
"tsx": "^4.7.2"
}
}

+ 3
- 0
patchouli.book.json View File

@@ -0,0 +1,3 @@
{
"title": "Book Name"
}

+ 3
- 3
pnpm-lock.yaml View File

@@ -21,7 +21,7 @@ dependencies:
specifier: 1.0.13
version: 1.0.13(mem@6.1.1)(react@18.3.1)
'@types/react':
specifier: ^18.3.0
specifier: ^18.3.1
version: 18.3.1
'@types/react-dom':
specifier: ^18.3.0
@@ -33,10 +33,10 @@ dependencies:
specifier: ^24.0.0
version: 24.0.0
react:
specifier: ^18.3.0
specifier: ^18.3.1
version: 18.3.1
react-dom:
specifier: ^18.3.0
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
tailwindcss:
specifier: ^3.4.3


+ 9
- 5
src/components/FrequenciesForm/index.tsx View File

@@ -163,7 +163,7 @@ export const FrequenciesForm = () => {
return (
<div>
{showForm && (
<>
<div className="print-hidden">
<form>
<input
type="number"
@@ -197,6 +197,7 @@ export const FrequenciesForm = () => {
/>
</form>
<div style={{ position: 'relative', backgroundColor: 'black', }}>
{/* @ts-ignore */}
<MusicalKeyboard
hasMap
startKey={0}
@@ -214,17 +215,20 @@ export const FrequenciesForm = () => {
/>
</MusicalKeyboard>
</div>
</>
</div>
)}

<table
className="alternate-rows"
style={{
fontSize: '0.875em',
fontSize: '0.75em',
}}
>
<caption>
MIDI note frequencies and their stretched counterparts
(base frequency={baseFrequency} Hz for key #{baseKey}, factor={(stretchFactorNumerator/equalDivisionOfTheOctave).toFixed(3)})
(base frequency={baseFrequency} Hz for key #{baseKey}, stretch factor={(stretchFactorNumerator/equalDivisionOfTheOctave).toFixed(3)}).
First figures indicate non-stretched frequencies, second figures indicate stretched frequencies. Parenthesized figures represent difference
between the frequencies.
</caption>
<thead>
<tr>
@@ -279,7 +283,7 @@ export const FrequenciesForm = () => {
<table>
<caption>
MIDI note frequencies and their stretched counterparts
(base frequency={baseFrequency} Hz for key #{baseKey}, factor={(stretchFactorNumerator/equalDivisionOfTheOctave).toFixed(3)})
(base frequency={baseFrequency} Hz for key #{baseKey}, stretch factor={(stretchFactorNumerator/equalDivisionOfTheOctave).toFixed(3)})
</caption>
<thead>
<tr>


+ 2
- 2
src/components/Score.astro View File

@@ -7,7 +7,7 @@ const scoreXml = await loadScore(id);
---

<div class="score-wrapper">
<a href={`scores/${id}.svg`}>
<a href={`../scores/${id}.svg`}>
<figure>
<Fragment set:html={scoreXml} />
<figcaption>
@@ -16,7 +16,7 @@ const scoreXml = await loadScore(id);
</figure>
</a>
<div class="screen-controls">
<a href={`scores/${id}.musicxml`}>
<a href={`../scores/${id}.musicxml`}>
Download MusicXML
</a>
</div>


+ 7
- 0
src/content/appendices/01-piano-key-frequencies.mdx View File

@@ -0,0 +1,7 @@
---
title: Piano Key Frequencies
---

import { FrequenciesForm } from '../../components/FrequenciesForm';

<FrequenciesForm client:load />

src/pages/ch01-introduction.mdx → src/content/chapters/01-introduction.mdx View File


src/pages/ch02-speed.mdx → src/content/chapters/02-speed.mdx View File


src/pages/ch03-analysis.mdx → src/content/chapters/03-analysis.mdx View File


src/pages/ch04-sound-projection.mdx → src/content/chapters/04-sound-projection.mdx View File


+ 3
- 0
src/content/chapters/05-limits.mdx View File

@@ -0,0 +1,3 @@
---
title: Limits
---

+ 3
- 0
src/content/chapters/06-overcoming-doubts.mdx View File

@@ -0,0 +1,3 @@
---
title: Overcoming Doubts
---

+ 3
- 0
src/content/chapters/07-fingering.mdx View File

@@ -0,0 +1,3 @@
---
title: Fingering
---

+ 3
- 0
src/content/chapters/08-pulse.mdx View File

@@ -0,0 +1,3 @@
---
title: Pulse
---

+ 3
- 0
src/content/chapters/09-sensation.mdx View File

@@ -0,0 +1,3 @@
---
title: Sensation
---

+ 3
- 0
src/content/chapters/10-autopilot.mdx View File

@@ -0,0 +1,3 @@
---
title: Autopilot
---

src/pages/ch11-musicality.mdx → src/content/chapters/11-musicality.mdx View File


+ 3
- 0
src/content/chapters/12-the-musician.mdx View File

@@ -0,0 +1,3 @@
---
title: The Musician
---

+ 3
- 0
src/content/chapters/13-ornaments.mdx View File

@@ -0,0 +1,3 @@
---
title: Ornaments
---

+ 3
- 0
src/content/chapters/14-rests.mdx View File

@@ -0,0 +1,3 @@
---
title: Rests
---

+ 3
- 0
src/content/chapters/15-gestures.mdx View File

@@ -0,0 +1,3 @@
---
title: Gestures
---

+ 3
- 0
src/content/chapters/16-articulation.mdx View File

@@ -0,0 +1,3 @@
---
title: Articulation
---

+ 3
- 0
src/content/chapters/17-expression.mdx View File

@@ -0,0 +1,3 @@
---
title: Expression
---

+ 3
- 0
src/content/chapters/18-mistakes.mdx View File

@@ -0,0 +1,3 @@
---
title: Mistakes
---

+ 3
- 0
src/content/chapters/19-performance.mdx View File

@@ -0,0 +1,3 @@
---
title: Performance
---

src/pages/ch20-trills.mdx → src/content/chapters/20-trills.mdx View File


src/pages/ch21-speed-and-anticipation.mdx → src/content/chapters/21-speed-and-anticipation.mdx View File


+ 3
- 0
src/content/chapters/22-thirds.mdx View File

@@ -0,0 +1,3 @@
---
title: Thirds
---

+ 28
- 0
src/content/config.ts View File

@@ -0,0 +1,28 @@
import { z, defineCollection } from 'astro:content';

const specialCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
}),
});

const chaptersCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
}),
});

const appendicesCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
}),
});

export const collections = {
'special': specialCollection,
'chapters': chaptersCollection,
'appendices': appendicesCollection,
};

+ 186
- 0
src/content/layouts/Appendix.astro View File

@@ -0,0 +1,186 @@
---
const { title, titlePrefix } = Astro.props.frontmatter || Astro.props;
---

<!doctype html>
<html lang="en-PH">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<title data-prefix={titlePrefix}>{title}</title>
<style is:global>
html {
@apply font-body;
}

body {
@apply w-full;
@apply mx-auto;
@apply max-w-screen-md;
@apply leading-relaxed;
@apply box-border;
}

p {
@apply my-6;
@apply indent-6;
@apply text-justify;
}

figure {
@apply m-0;
}

figcaption {
@apply text-center;
}

.screen-controls {
@apply flex;
@apply justify-center;
}

a:link {
@apply text-blue-600;
}

a:visited {
@apply text-purple-600;
}

div.score-wrapper {
@apply mt-8;
@apply mb-16;
}

title[data-prefix]::before {
@apply block;
@apply text-xl;
content: attr(data-prefix);
}

table {
@apply caption-bottom;
@apply w-full;
@apply border-spacing-0;
}

table caption {
@apply mt-4;
@apply px-16;
}

table.alternate-rows {
@apply border-0;
}

table.alternate-rows tbody tr:nth-child(2n + 1) * {
@apply relative;
}

table.alternate-rows tbody tr:nth-child(2n + 1) > *::before {
@apply absolute;
@apply top-0;
@apply left-0;
@apply w-full;
@apply h-full;
@apply bg-current;
@apply opacity-10;
@apply p-0;

content: '';
}

@media only screen {
html {
@apply text-gray-800;
}

body {
@apply px-8;
@apply my-16;
}

div.score-wrapper figure svg {
@apply text-gray-800;
}
}

@media only print {
head {
@apply block;
@apply text-black;
}

div.score-wrapper figure svg {
@apply text-black;
}

title {
@apply block;
@apply text-3xl;
@apply text-center;
@apply mb-12;
@apply font-bold;
}

body {
@apply max-w-full;
@apply m-0;
}

.screen-controls {
@apply hidden;
}

a:link {
@apply text-inherit;
@apply no-underline;
}

a:visited {
@apply text-inherit;
}

figcaption {
@apply text-sm;
}

.print-hidden {
display: none;
}
}

@media only screen and (prefers-color-scheme: dark) {
html {
@apply bg-black;
@apply text-white;
}

div.score-wrapper figure svg {
@apply text-white;
}

img.score {
filter: invert(100%) grayscale(100%);
}

a:link {
@apply text-blue-400;
}

a:visited {
@apply text-purple-400;
}
}

@page {
margin: 2cm;
}
</style>
</head>
<body>
<slot />
</body>
</html>

src/layouts/Cover.astro → src/content/layouts/Cover.astro View File


src/layouts/Default.astro → src/content/layouts/Default.astro View File

@@ -54,6 +54,10 @@ const { title } = Astro.props.frontmatter || Astro.props;
@apply mb-16;
}

table {
caption-side: bottom;
}

@media only screen {
html {
@apply text-gray-800;
@@ -108,6 +112,10 @@ const { title } = Astro.props.frontmatter || Astro.props;
figcaption {
@apply text-sm;
}

.print-hidden {
display: none;
}
}

@media only screen and (prefers-color-scheme: dark) {

src/pages/foreword.mdx → src/content/special/foreword.mdx View File


+ 18
- 0
src/pages/[...slug].astro View File

@@ -0,0 +1,18 @@
---
import { getCollection } from 'astro:content';
import Default from '../content/layouts/Default.astro';

export const getStaticPaths = async () => {
const entries = await getCollection('special');
return entries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}

const { entry } = Astro.props;
const { Content } = await entry.render();
---

<Default title={entry.data.title}>
<Content />
</Default>

+ 19
- 0
src/pages/appendices/[...slug].astro View File

@@ -0,0 +1,19 @@
---
import { getCollection } from 'astro:content';
import Layout from '../../content/layouts/Appendix.astro';

export const getStaticPaths = async () => {
const entries = await getCollection('appendices');
return entries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}

const { entry } = Astro.props;
const { Content } = await entry.render();
const titlePrefix = String.fromCharCode(parseInt(entry.slug) - 1 + 65);
---

<Layout title={entry.data.title} titlePrefix={`Appendix ${titlePrefix}`}>
<Content />
</Layout>

+ 0
- 8
src/pages/appendix01-piano-key-frequencies.mdx View File

@@ -1,8 +0,0 @@
---
title: Piano Key Frequencies
layout: ../layouts/Default.astro
---

import { FrequenciesForm } from '../components/FrequenciesForm';

<FrequenciesForm client:load />

+ 0
- 0
src/pages/ch05-limits.mdx View File


+ 0
- 0
src/pages/ch06-overcoming-doubts.mdx View File


+ 0
- 0
src/pages/ch07-fingering.mdx View File


+ 0
- 0
src/pages/ch08-pulse.mdx View File


+ 0
- 0
src/pages/ch09-sensation.mdx View File


+ 0
- 0
src/pages/ch10-autopilot.mdx View File


+ 0
- 0
src/pages/ch12-the-musician.mdx View File


+ 0
- 0
src/pages/ch13-ornaments.mdx View File


+ 0
- 0
src/pages/ch14-rests.mdx View File


+ 0
- 0
src/pages/ch15-gestures.mdx View File


+ 0
- 0
src/pages/ch16-articulation.mdx View File


+ 0
- 0
src/pages/ch17-expression.mdx View File


+ 0
- 0
src/pages/ch18-mistakes.mdx View File


+ 0
- 0
src/pages/ch19-performance.mdx View File


+ 0
- 0
src/pages/ch22-thirds.mdx View File


+ 18
- 0
src/pages/chapters/[...slug].astro View File

@@ -0,0 +1,18 @@
---
import { getCollection } from 'astro:content';
import Default from '../../content/layouts/Default.astro';

export const getStaticPaths = async () => {
const entries = await getCollection('chapters');
return entries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
}

const { entry } = Astro.props;
const { Content } = await entry.render();
---

<Default title={entry.data.title}>
<Content />
</Default>

+ 10
- 0
src/pages/index.astro View File

@@ -0,0 +1,10 @@
---
import Default from '../content/layouts/Cover.astro';
import { title } from '../../patchouli.book.json';
---

<Default title={title}>
<a href="toc.html">
<img src="cover.jpg" alt={title} />
</a>
</Default>

+ 0
- 7
src/pages/index.mdx View File

@@ -1,7 +0,0 @@
---
title: Book Name
---

<a href="toc.html">
<img src="cover.jpg" alt={frontmatter.title} />
</a>

+ 4
- 4
src/pages/toc.astro View File

@@ -1,6 +1,6 @@
---
import { readdir, readFile } from 'node:fs/promises';
const { default: Default } = await import('../layouts/Default.astro');
import Default from '../content/layouts/Default.astro';

type Frontmatter = Record<string, unknown>;

@@ -9,7 +9,7 @@ interface PageProps {
}

const title = 'Table of Contents'
const allPages = await readdir('src/pages');
const allPages = await readdir('src/content/chapters');
const pages = allPages.filter((p) => (
!p.startsWith('index.')
&& !p.endsWith('.astro')
@@ -18,7 +18,7 @@ const pages = allPages.filter((p) => (

const pagesContentImported = await Promise.all(
pages.map(async (p) => {
const fileBuffer = await readFile(`src/pages/${p}`);
const fileBuffer = await readFile(`src/content/chapters/${p}`);
const file = fileBuffer.toString('utf-8');

const [, frontmatterRaw] = file.split('---');
@@ -39,7 +39,7 @@ const pagesContentImported = await Promise.all(
<ol>
{pagesContentImported.map(([p, f]) => (
<li>
<a href={p.replace('.mdx', '')}>
<a href={`chapters/${p.replace('.mdx', '')}`}>
{f.frontmatter.title}
</a>
</li>


Loading…
Cancel
Save