diff --git a/src/components/islands/SkillPackageExplorer.astro b/src/components/islands/SkillPackageExplorer.astro index 2018019..8552734 100644 --- a/src/components/islands/SkillPackageExplorer.astro +++ b/src/components/islands/SkillPackageExplorer.astro @@ -71,7 +71,7 @@ const packageFiles: PackageFile[] = [ class:list={[{ active: index === 0 }]} data-skill-file={file.id} role="tab" - aria-selected={String(index === 0)} + aria-selected={index === 0 ? 'true' : 'false'} > {`${file.prefix}${file.label}`} {file.caption} diff --git a/src/pages/agents.astro b/src/pages/agents.astro index 6caad73..00d207a 100644 --- a/src/pages/agents.astro +++ b/src/pages/agents.astro @@ -7,8 +7,22 @@ import { getEntry } from 'astro:content'; import ChapterLayout from '../layouts/ChapterLayout.astro'; import ChapterHero from '../components/blocks/ChapterHero.astro'; +// Required-field guard. The chapters schema marks section eyebrow / +// panelLabel / panelCode / steps / copy as optional because the schema +// does not know which page consumes which shape. These four pages do +// consume them — fail loudly here rather than rendering a blank section. +function requireField(value: T | undefined, name: string): T { + if (value === undefined) { + throw new Error(`agents chapter: missing required field "${name}"`); + } + return value; +} + const base = import.meta.env.BASE_URL; const chapter = await getEntry('chapters', 'agents'); +if (!chapter) { + throw new Error('agents chapter: missing collection entry'); +} const lede = chapter.data.lede.en; const title = chapter.data.title.en; const eyebrow = chapter.data.eyebrow.en; @@ -16,6 +30,19 @@ const cards = chapter.data.cards ?? []; const sections = chapter.data.sections ?? []; const treeSection = sections[0]; const handoffSection = sections[1]; +if (!treeSection) { + throw new Error('agents chapter: missing sections[0]'); +} +if (!handoffSection) { + throw new Error('agents chapter: missing sections[1]'); +} +// Narrow the section fields this page renders. Each `requireField` either +// returns a non-null value or throws — TS narrows from `T | undefined` to `T`. +const treeEyebrow = requireField(treeSection.eyebrow, 'sections[0].eyebrow'); +const treePanelLabel = requireField(treeSection.panelLabel, 'sections[0].panelLabel'); +const treePanelCode = requireField(treeSection.panelCode, 'sections[0].panelCode'); +const handoffEyebrow = requireField(handoffSection.eyebrow, 'sections[1].eyebrow'); +const handoffSteps = requireField(handoffSection.steps, 'sections[1].steps'); --- ]+>/g, '')}> @@ -30,12 +57,12 @@ const handoffSection = sections[1];
-

{treeSection.eyebrow.en}

+

{treeEyebrow.en}

- {treeSection.panelLabel.en} - {treeSection.panelCode.en} + {treePanelLabel.en} + {treePanelCode.en}
@@ -53,12 +80,12 @@ const handoffSection = sections[1];
-

{handoffSection.eyebrow.en}

+

{handoffEyebrow.en}

{ - handoffSection.steps.map((step, index) => ( + handoffSteps.map((step, index) => (
{String(index + 1).padStart(2, '0')}
diff --git a/src/pages/models.astro b/src/pages/models.astro index d910e47..041166f 100644 --- a/src/pages/models.astro +++ b/src/pages/models.astro @@ -8,8 +8,22 @@ import { getEntry } from 'astro:content'; import ChapterLayout from '../layouts/ChapterLayout.astro'; import ChapterHero from '../components/blocks/ChapterHero.astro'; +// Required-field guard. The chapters schema marks section eyebrow / +// panelLabel / panelCode / steps / copy as optional because the schema +// does not know which page consumes which shape. These four pages do +// consume them — fail loudly here rather than rendering a blank section. +function requireField(value: T | undefined, name: string): T { + if (value === undefined) { + throw new Error(`models chapter: missing required field "${name}"`); + } + return value; +} + const base = import.meta.env.BASE_URL; const chapter = await getEntry('chapters', 'models'); +if (!chapter) { + throw new Error('models chapter: missing collection entry'); +} const lede = chapter.data.lede.en; const title = chapter.data.title.en; const eyebrow = chapter.data.eyebrow.en; @@ -18,6 +32,17 @@ const sections = chapter.data.sections ?? []; // First section carries the routing-rule panel, second carries the steps. const ruleSection = sections[0]; const sequenceSection = sections[1]; +if (!ruleSection) { + throw new Error('models chapter: missing sections[0]'); +} +if (!sequenceSection) { + throw new Error('models chapter: missing sections[1]'); +} +const ruleEyebrow = requireField(ruleSection.eyebrow, 'sections[0].eyebrow'); +const rulePanelLabel = requireField(ruleSection.panelLabel, 'sections[0].panelLabel'); +const rulePanelCode = requireField(ruleSection.panelCode, 'sections[0].panelCode'); +const sequenceEyebrow = requireField(sequenceSection.eyebrow, 'sections[1].eyebrow'); +const sequenceSteps = requireField(sequenceSection.steps, 'sections[1].steps'); --- ]+>/g, '')}> @@ -44,23 +69,23 @@ const sequenceSection = sections[1];
-

{ruleSection.eyebrow.en}

+

{ruleEyebrow.en}

- {ruleSection.panelLabel.en} - {ruleSection.panelCode.en} + {rulePanelLabel.en} + {rulePanelCode.en}
-

{sequenceSection.eyebrow.en}

+

{sequenceEyebrow.en}

{ - sequenceSection.steps.map((step, index) => ( + sequenceSteps.map((step, index) => (
{String(index + 1).padStart(2, '0')}
diff --git a/src/pages/skills.astro b/src/pages/skills.astro index a31fdf2..9e9518b 100644 --- a/src/pages/skills.astro +++ b/src/pages/skills.astro @@ -10,14 +10,38 @@ import ChapterHero from '../components/blocks/ChapterHero.astro'; import SkillPackageExplorer from '../components/islands/SkillPackageExplorer.astro'; import skillsStylesheet from '../../skills/styles.css?url'; +// Required-field guard. The chapters schema marks section eyebrow / +// panelLabel / panelCode / steps / copy as optional because the schema +// does not know which page consumes which shape. These four pages do +// consume them — fail loudly here rather than rendering a blank section. +function requireField(value: T | undefined, name: string): T { + if (value === undefined) { + throw new Error(`skills chapter: missing required field "${name}"`); + } + return value; +} + const base = import.meta.env.BASE_URL; const chapter = await getEntry('chapters', 'skills'); +if (!chapter) { + throw new Error('skills chapter: missing collection entry'); +} const lede = chapter.data.lede.en; const title = chapter.data.title.en; const eyebrow = chapter.data.eyebrow.en; const sections = chapter.data.sections ?? []; const anatomySection = sections[0]; const createSection = sections[1]; +if (!anatomySection) { + throw new Error('skills chapter: missing sections[0]'); +} +if (!createSection) { + throw new Error('skills chapter: missing sections[1]'); +} +const anatomyEyebrow = requireField(anatomySection.eyebrow, 'sections[0].eyebrow'); +const anatomyCopy = requireField(anatomySection.copy, 'sections[0].copy'); +const createEyebrow = requireField(createSection.eyebrow, 'sections[1].eyebrow'); +const createSteps = requireField(createSection.steps, 'sections[1].steps'); --- ]+>/g, '')}> @@ -33,21 +57,21 @@ const createSection = sections[1];
-

{anatomySection.eyebrow.en}

+

{anatomyEyebrow.en}

-

{anatomySection.copy.en}

+

{anatomyCopy.en}

-

{createSection.eyebrow.en}

+

{createEyebrow.en}

{ - createSection.steps.map((step, index) => ( + createSteps.map((step, index) => (
{String(index + 1).padStart(2, '0')}