feat: gallery + generic blueprint viewer, add Grid blueprint

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
This commit is contained in:
Julien Calixte
2026-07-02 18:34:49 +02:00
parent 4405497658
commit 03804b10dc
21 changed files with 2304 additions and 765 deletions

70
DESIGN.md Normal file
View File

@@ -0,0 +1,70 @@
# Design — `blueprints`
Decisions for building `blueprints` out from a single List illustration into a
gallery of many. Terminology: see [CONTEXT.md](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-ontology` repo; 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-specific `App.vue`: title block, legend, function tabs + full view,
two panes, optional composition map + state machine, footer. Props: a `Blueprint`
object + the blueprint's Specimen component (injected via `<component :is>`).
- Generic `Blueprint` type in `src/data/blueprint.ts` (signature, functions,
coreInvariants, optional composition, optional stateMachine, meta). `listBlueprint.ts`
conforms to it. Specimens move to `src/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, …).