From 233cc5d6e65f8b58670fa8aa76ce4f1b636e43b6 Mon Sep 17 00:00:00 2001 From: Marcos Paulo Date: Sat, 5 Sep 2026 07:09:38 +0000 Subject: [PATCH] feat(blocks): extract six full-guide block components PhasePanel, FleetDiagram, HandoffTable, WorktreeMap, RouteTable, SkillPackage as static shells. Each takes typed props and renders server-side markup with the data-* hooks verified by scripts/verify.mjs (data-phase, data-tree, data-worker, data-route, data-skill-file). No client:* directives; interactive islands wire up in task 15. Tokens only. No raw hex or px font sizes (check-tokens passes). The parent-background seam colour for the gap:1px grid trick in FleetDiagram is a documented token gap; see component header comment and the task final report. --- src/components/blocks/FleetDiagram.astro | 160 +++++++++++++++++++++++ src/components/blocks/HandoffTable.astro | 90 +++++++++++++ src/components/blocks/PhasePanel.astro | 128 ++++++++++++++++++ src/components/blocks/RouteTable.astro | 117 +++++++++++++++++ src/components/blocks/SkillPackage.astro | 90 +++++++++++++ src/components/blocks/WorktreeMap.astro | 160 +++++++++++++++++++++++ 6 files changed, 745 insertions(+) create mode 100644 src/components/blocks/FleetDiagram.astro create mode 100644 src/components/blocks/HandoffTable.astro create mode 100644 src/components/blocks/PhasePanel.astro create mode 100644 src/components/blocks/RouteTable.astro create mode 100644 src/components/blocks/SkillPackage.astro create mode 100644 src/components/blocks/WorktreeMap.astro diff --git a/src/components/blocks/FleetDiagram.astro b/src/components/blocks/FleetDiagram.astro new file mode 100644 index 0000000..6e171f9 --- /dev/null +++ b/src/components/blocks/FleetDiagram.astro @@ -0,0 +1,160 @@ +--- +// FleetDiagram — orchestrator card, an arrow, and a 1-up of worker cards. +// +// Static shell: the captain renders literally, the workers render as toggle +// buttons with the `data-worker` hook asserted by `scripts/verify.mjs`. The +// `initial` worker is marked active and pressed. +// +// The source uses `gap:1px` over a coloured parent to draw hairlines between +// workers; here the parent uses `var(--ink)` and the cards paint over it, so +// the seam reads as the same tone — see the final report for the token gap +// (source uses an off-token seam colour for the divider). + +interface Worker { + /** Used as the `data-worker` hook and the key in the source. */ + id: string; + /** Short label rendered uppercase, e.g. "UI". */ + label: string; + /** Body copy describing the worker's remit. */ + strong: string; + /** Path label, e.g. "agent/ui". */ + code: string; +} + +interface Props { + orchestrator: { + eyebrow: string; + /** May contain inline `
`; rendered with `set:html`. */ + title: string; + code: string; + }; + workers: Worker[]; + initial?: string; +} + +const { orchestrator, workers, initial = workers[0]?.id } = Astro.props; +--- + +
+
+ {orchestrator.eyebrow} +

+ {orchestrator.code} +

+ +
+ { + workers.map((worker) => ( + + )) + } +
+
+ + diff --git a/src/components/blocks/HandoffTable.astro b/src/components/blocks/HandoffTable.astro new file mode 100644 index 0000000..258ef3e --- /dev/null +++ b/src/components/blocks/HandoffTable.astro @@ -0,0 +1,90 @@ +--- +// HandoffTable — the four-row "what crosses contexts" table. +// +// Static shell: the row data is passed in via `rows` so this component has +// no opinion on what each handoff package contains. The table structure is +// the load-bearing part of the design (dark header, blue row labels, +// muted body) and lives here so the next page that needs it gets the same +// beat for free. + +interface Row { + /** The package name, rendered as a `` (column 1). */ + package: string; + /** What the package contains (column 2). */ + contains: string; + /** Why this matters (column 3). */ + why: string; +} + +interface Props { + /** Column headers in render order. */ + columns: [string, string, string]; + rows: Row[]; +} + +const { columns, rows } = Astro.props; +--- + + + + + + + + + + + { + rows.map((row) => ( + + + + + + )) + } + +
{columns[0]}{columns[1]}{columns[2]}
{row.package}{row.contains}{row.why}
+ + diff --git a/src/components/blocks/PhasePanel.astro b/src/components/blocks/PhasePanel.astro new file mode 100644 index 0000000..95876e0 --- /dev/null +++ b/src/components/blocks/PhasePanel.astro @@ -0,0 +1,128 @@ +--- +// PhasePanel — the three-step "Click a phase / see the handoff" block. +// +// Static shell: the tab buttons and the panel markup are server-rendered. +// The `initial` phase's content is shown by default; the other tabs still +// carry the `data-phase` hook so the interactive island (task 15) can swap +// the panel on click. +// +// Every `data-phase` value is asserted by `scripts/verify.mjs`. + +export type PhaseId = 'plan' | 'build' | 'review'; + +interface Phase { + id: PhaseId; + /** Two-letter label rendered in the tab, e.g. "PLAN". */ + label: string; + /** Numeric prefix, e.g. "01". */ + number: string; + title: string; + body: string; + /** Headline + small caption shown above the panel title. */ + meta: { deliverable: string; gate: string }; + /** Code-line evidence shown at the bottom of the panel. */ + evidence: string; +} + +interface Props { + phases: Phase[]; + /** Initial active phase. Defaults to the first entry. */ + initial?: PhaseId; +} + +const { phases, initial = phases[0]?.id ?? 'plan' } = Astro.props; +const active = phases.find((phase) => phase.id === initial) ?? phases[0]; +--- + +
+ { + phases.map((phase) => ( + + )) + } +
+ +
+
+ {active.meta.deliverable} + {active.meta.gate} +
+

{active.title}

+

{active.body}

+ {active.evidence} +
+ + diff --git a/src/components/blocks/RouteTable.astro b/src/components/blocks/RouteTable.astro new file mode 100644 index 0000000..7789f05 --- /dev/null +++ b/src/components/blocks/RouteTable.astro @@ -0,0 +1,117 @@ +--- +// RouteTable — the model-routing matrix. Four buttons (one per job profile), +// each carrying the `data-route` hook asserted by `scripts/verify.mjs`. +// +// Static shell: the initial route is marked active and pressed. The shell +// renders the column header + the four rows; task 15 will hydrate the +// "route detail" panel to the right. + +interface Route { + id: 'plan' | 'build' | 'explore' | 'review' | string; + /** Strong label, e.g. "Plan". */ + strong: string; + /** Profile descriptor, e.g. "strong / broad". */ + profile: string; + /** Prompt shape copy. */ + prompt: string; +} + +interface Props { + routes: Route[]; + initial?: string; +} + +const { routes, initial = routes[0]?.id ?? 'plan' } = Astro.props; +--- + +
+
+ Work + Profile + Prompt shape +
+ { + routes.map((route) => ( + + )) + } +
+ + diff --git a/src/components/blocks/SkillPackage.astro b/src/components/blocks/SkillPackage.astro new file mode 100644 index 0000000..a238c82 --- /dev/null +++ b/src/components/blocks/SkillPackage.astro @@ -0,0 +1,90 @@ +--- +// SkillPackage — the four-file skill-package picker (SKILL.md, references/, +// scripts/, assets/). Buttons carry the `data-skill-file` hook asserted by +// `scripts/verify.mjs`. +// +// Static shell: the initial file is marked active and selected. The block is +// paired with a `
` detail panel by the parent page; this component +// renders only the picker. + +interface PackageFile { + id: 'skill' | 'references' | 'scripts' | 'assets' | string; + /** Path rendered inside ``, e.g. "SKILL.md". */ + path: string; + /** Helper copy under the path. */ + small: string; +} + +interface Props { + files: PackageFile[]; + initial?: string; +} + +const { files, initial = files[0]?.id ?? 'skill' } = Astro.props; +--- + +
+ SKILL PACKAGE + { + files.map((file) => ( + + )) + } +
+ + diff --git a/src/components/blocks/WorktreeMap.astro b/src/components/blocks/WorktreeMap.astro new file mode 100644 index 0000000..28399c7 --- /dev/null +++ b/src/components/blocks/WorktreeMap.astro @@ -0,0 +1,160 @@ +--- +// WorktreeMap — the repository-topology diagram with the SVG trunk-and-branches +// behind four `tree-node` buttons. +// +// Static shell: the SVG paths render server-side; the nodes are buttons with +// the `data-tree` hook asserted by `scripts/verify.mjs`. The `root` node and +// the `initial` branch are marked selected. + +interface Branch { + /** The `data-tree` hook, e.g. "ui", "tests", "docs". */ + id: string; + /** Uppercase label, e.g. "UI AGENT". */ + label: string; + /** Strong line, e.g. "agent/ui". */ + strong: string; + /** Status small, e.g. "3 files · working". */ + small: string; + /** CSS modifier so each branch picks up its tone. */ + tone: 'ui' | 'tests' | 'docs' | string; +} + +interface Props { + branches: Branch[]; + initial?: string; +} + +const { branches, initial = 'main' } = Astro.props; +--- + +
+ + + { + branches.map((branch) => ( + + )) + } +
+ +