Refactor the List-specific screen into a data-driven, reusable viewer: - generic Blueprint type + shared LoadState machine + registry - vue-router: / (gallery) and /b/:slug (illustration) - BlueprintViewer renders any blueprint; specimens stay bespoke - add Grid blueprint (6 functions incl. Position) + GridSpecimen
4.0 KiB
Design — blueprints
Decisions for building blueprints out from a single List illustration into a
gallery of many. Terminology: see CONTEXT.md.
D1 · Content sourcing: hand-authored typed data + bespoke specimen
Each blueprint is a typed TS data module (src/data/<name>Blueprint.ts),
transcribed by hand from its ontology README, paired with a bespoke Specimen
component. The generic Viewer renders everything else (Readout, composition map,
state machine) from that data.
- Why not parse the ontology at build time? The Specimen must be hand-built for every blueprint regardless (the ontology only provides an ASCII sketch), so parsing would automate only the cheap, structured text — at the cost of fragile markdown/mermaid parsing and a build-time coupling to the ontology repo's exact format. Not worth it.
- Source of truth stays the
blueprint-ontologyrepo; the data modules are a faithful transcription, cited in each module's header.
Structural split this implies:
| Part | Generic (data-driven) | Bespoke (per blueprint) |
|---|---|---|
| Chrome (title block, tabs, legend) | ✓ | |
| Readout (dossier per function) | ✓ | |
| Composition map | ✓ (from extends + composes data) |
|
| State machine | ✓ (from nodes/edges data) | |
| Specimen | ✓ component slotted into the Viewer |
D2 · Routing: vue-router, URL per blueprint
Add vue-router. Routes: / → Gallery, /b/:slug → the blueprint's illustration
(e.g. /b/list). Deep-linkable and shareable; browser back/forward works. The nginx
SPA fallback (try_files … /index.html) already serves client routes, so no server
change is needed. A blueprint registry (src/data/blueprints.ts) maps slug → { data module, specimen component, meta } and is the single source both the router
and the Gallery read from.
D3 · Gallery: illustrated-only, grows over time
The Gallery lists only blueprints that have an illustration; it grows as we add
them. No greyed "planned" cards. Rationale: keeps the landing clean and truthful, and
avoids maintaining metadata for 45 not-yet-built entries. A blueprint appears in the
Gallery precisely when it is registered in src/data/blueprints.ts.
D4 · Build-out slice #1: foundation + Grid
Foundation (refactor, no UX change to List):
pnpm add vue-router;App.vue→ thin shell with a header (link home) +<RouterView/>.src/views/GalleryView.vue(/) — cards from the registry.src/views/BlueprintView.vue(/b/:slug) — resolves the registry entry, renders the Viewer.src/components/BlueprintViewer.vue— the generic Viewer, generalized from the current List-specificApp.vue: title block, legend, function tabs + full view, two panes, optional composition map + state machine, footer. Props: aBlueprintobject + the blueprint's Specimen component (injected via<component :is>).- Generic
Blueprinttype insrc/data/blueprint.ts(signature, functions, coreInvariants, optional composition, optional stateMachine, meta).listBlueprint.tsconforms to it. Specimens move tosrc/specimens/. src/data/blueprints.ts— registry:slug → { meta, blueprint, specimen }.
Content: gridBlueprint.ts + src/specimens/GridSpecimen.vue (2D tile grid).
Ships: Gallery listing List + Grid; /b/list (same UX, refactored) and /b/grid.
Gates before push: pnpm build clean · pnpm lint · pnpm fmt; then commit → push
(auto-deploys) → verify https://blueprints.apoena.dev.
Subsequent slices add one blueprint each (Table, Feed, …).