580293867d
Deletes the pre-Astro pages, scripts, and stylesheets that the migration replaced, and moves the ones it did not replace out of the way. Deleted (32 files): app.js, responsive.css, landing.css, rules/app.js, rules/styles.css, skills/app.js, the ten route index.html files, and the root hands-on/ copy, which is byte-identical to public/hands-on/ -- the one the build actually ships. Moved to legacy/ (12 files): styles.css, full-guide/audit.css, chapters.css, skills/styles.css, skills-review/styles.css, skills-review/change-lens.css, and the skills-review/app.js module graph. These are not dead. The Astro pages import them and the build fails without them, which the plan had not accounted for. They go to legacy/ rather than src/ because check-tokens.mjs sweeps src, and these files are full of raw hex and unnamed breakpoints: moving one into src/ should mean migrating it to tokens in the same change, not adding a scan exclusion. The prettier, stylelint, and eslint ignore lists that already named these files at their old paths now name legacy/ instead. verify.mjs no longer reads app.js. The 102 Portuguese strings were extracted from its translations.pt object before deletion into .agents/snapshots/full-guide-pt.json -- a legacy capture, not a snapshot of the Astro build, so the assertion still compares against an independent source. The brace-matching helper's assertion is replaced by one that rejects an empty snapshot entry, without which trimming the snapshot would make the presence check pass vacuously. Count stays at 84. audit-ui.mjs reads the ten pages from dist/ and resolves Astro's base-absolute hrefs against it. Before deleting anything, rendered-text-diff was run across all ten routes plus both Portuguese pages: every one at parity, 0 missing and 0 extra. That comparison is not repeatable once the legacy files are gone. computed-style-diff on /full-guide/ stays at 32 differences, so the moves are style-neutral. Docs updated to match: README, AGENTS.md, GATES.md, the architecture context, the operations guide's lab instructions, and the three skills that told you to serve the vanilla site. Publishing is not part of this commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
3.4 KiB
TypeScript
94 lines
3.4 KiB
TypeScript
---
|
|
// /skills/ — chapter page. Migrated from skills/index.html in task 13.
|
|
// Identical URL (/skills/). The package-anatomy picker is an Astro island
|
|
// (SkillPackageExplorer) — the only JS the page ships. Copy lives in
|
|
// src/content/chapters/skills.json.
|
|
|
|
import { getEntry } from 'astro:content';
|
|
import ChapterLayout from '../layouts/ChapterLayout.astro';
|
|
import ChapterHero from '../components/blocks/ChapterHero.astro';
|
|
import SkillPackageExplorer from '../components/islands/SkillPackageExplorer.astro';
|
|
import skillsStylesheet from '../../legacy/styles/skills.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<T>(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');
|
|
---
|
|
|
|
<ChapterLayout title="AI For Dummies — Skills" description={lede.replace(/<[^>]+>/g, '')}>
|
|
<link slot="styles" rel="stylesheet" href={skillsStylesheet} />
|
|
<a slot="top-previous" href={`${base}summary/`}>← ROUTE MAP</a>
|
|
<span slot="top-center">03 / SKILLS</span>
|
|
<a slot="top-next" href={`${base}skills-review/`}>review desk ↗</a>
|
|
|
|
<ChapterHero eyebrow={eyebrow}>
|
|
<span slot="title" set:html={title} />
|
|
<p set:html={lede} />
|
|
</ChapterHero>
|
|
|
|
<section class="pipeline package-anatomy">
|
|
<div>
|
|
<p class="eyebrow">{anatomyEyebrow.en}</p>
|
|
<h2 set:html={anatomySection.title.en} />
|
|
<p class="package-hint">{anatomyCopy.en}</p>
|
|
</div>
|
|
<SkillPackageExplorer />
|
|
</section>
|
|
|
|
<section class="practice">
|
|
<div>
|
|
<p class="eyebrow">{createEyebrow.en}</p>
|
|
<h2 set:html={createSection.title.en} />
|
|
</div>
|
|
<div class="steps">
|
|
{
|
|
createSteps.map((step, index) => (
|
|
<article>
|
|
<b>{String(index + 1).padStart(2, '0')}</b>
|
|
<div>
|
|
<strong>{step.label.en}</strong>
|
|
<span>{step.copy.en}</span>
|
|
</div>
|
|
</article>
|
|
))
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<nav slot="footer-links" class="links" aria-label="Chapter navigation">
|
|
<a href={`${base}agents/`}>Agents & trees →</a>
|
|
<a href={`${base}rules/`}>Rules case study →</a>
|
|
<a href={`${base}skills-review/`}>Review submitted skills →</a>
|
|
<a href={`${base}full-guide/#create-skill`}>Full guide: skill forge →</a>
|
|
</nav>
|
|
</ChapterLayout>
|