diff options
Diffstat (limited to 'docs/specs/2026-07-12-component-generation-spec.org')
| -rw-r--r-- | docs/specs/2026-07-12-component-generation-spec.org | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/docs/specs/2026-07-12-component-generation-spec.org b/docs/specs/2026-07-12-component-generation-spec.org new file mode 100644 index 0000000..95c4712 --- /dev/null +++ b/docs/specs/2026-07-12-component-generation-spec.org @@ -0,0 +1,206 @@ +#+TITLE: Widget Component Generation — Spec +#+AUTHOR: Craig Jennings +#+DATE: 2026-07-12 +#+TODO: TODO | DONE +#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED + +* DOING Widget component generation +:PROPERTIES: +:ID: 3ac0d42c-db1a-4d21-bce4-e63785fef0ba +:END: +- 2026-07-12 Sun @ 20:57:50 -0500 — READY → DOING: phases decomposed into todo.org tasks under "Retro widget catalogue" (=:SPEC_ID:= stamped); Phase 1 extraction starts now per the option-1 approval. +- 2026-07-12 Sun @ 20:57:50 -0500 — DRAFT → READY: spec-review passed (Ready; 3 findings — the option-1 supersession, the card-record refactor, stale counts — all accepted and folded same pass). +- 2026-07-12 Sun @ 10:12:06 -0500 — drafted. + +** Prototype iterations +Per =claude-rules/ui-prototyping.md=, a non-trivial-UI spec carries its prototype evidence here. This spec's UI evidence is the [[file:../prototypes/panel-widget-gallery.html][panel widget gallery]] itself — the living catalogue is the prototype set, not a numbered iteration series: + +- *Research:* reference photos of period hardware (Berna 3 studio suite, aviation clusters, broadcast consoles, pedals, marine/telegraph instruments) filed in [[file:../../working/retro-stereo-widgets/references/][working/retro-stereo-widgets/references/]]; in-chat-pasted references have provenance noted in card notes and the 2026-07-11/12 session archive. +- *Iterations:* the gallery's git history is the iteration record — N-series base widgets, then reference-derived R01–R56 built and behaviorally verified card by card (7fff507 through the gallery-upgrades squash merge bc93388 and beyond, 2026-07-12). 109 cards as of the 2026-07-12 review, every one CDP-verified (drag tracking, click response, animation, zero console errors); the committed probe suite lives in =tests/gallery-probes/=. +- *Final:* the gallery at current =main= is the settled visual + behavioral spec each component port is judged against — it keeps growing, so a port is judged against its card as it stands, not a frozen SHA. + +* Metadata +| Status | doing | +|----------+-----------------------------------| +| Owner | Craig Jennings | +|----------+-----------------------------------| +| Reviewer | Claude Code (archsetup) | +|----------+-----------------------------------| +| Related | [[file:../../todo.org][todo.org — Retro widget catalogue]] | +|----------+-----------------------------------| + +* Summary +The panel widget gallery (109 behaviorally-verified retro-instrument cards as of the 2026-07-12 review) is a spec, not yet a library. This document defines how gallery cards become reusable components for three real targets — web (vanilla JS, the gallery itself as first consumer), Emacs (svg.el), and waybar (GTK CSS). The web library is extracted whole (a lossless transform of code that already exists); the Emacs and waybar ports are driven by actual demand rather than porting the full catalogue. + +* Problem / Context +The gallery grew card by card as a visual and behavioral catalogue: each card is a self-contained inline builder function inside one ~4300-line HTML file (as of the 2026-07-12 review; it still grows). That was right for the collection phase — fast iteration, one canvas, one review surface. It is wrong for consumption: a waybar panel, an Emacs game, or a web UI that wants a VU meter today would have to copy-paste from the gallery's internals and hand-fork the styling. + +The groundwork is already laid. Tokens are single-sourced in [[file:../prototypes/tokens.json][tokens.json]]; =gen_tokens.py= emits the gallery's =:root= CSS block, [[file:../prototypes/tokens-waybar.css][tokens-waybar.css]], and [[file:../prototypes/gallery-tokens.el][gallery-tokens.el]] (27 tests, 100% coverage). An svg.el proof widget ([[file:../prototypes/gallery-widget.el][gallery-widget.el]], the needle gauge, 10 ERT tests plus an rsvg-convert side-by-side render) proved the Emacs pipeline — the riskiest unknown — end to end. What's missing is the middle layer: parameterized, importable widgets per target, and a decision procedure for which widgets earn a port. + +* Goals and Non-Goals +** Goals +- A consumer in any of the three targets can use a ported widget without reading the gallery's source. +- The gallery stays the single visual/behavioral spec; ports are judged against it, and the web port *is* what the gallery runs. +- Porting effort tracks demand: a widget is ported when a named consumer wants it, not because it exists. +- Every port carries tests in its target's idiom (CDP behavioral for web, ERT + visual harness for Emacs, panel test suite for waybar). + +** Non-Goals +- Emacs/waybar ports of the full catalogue. Most cards are catalogue entries; a scripted port with no consumer is inventory nobody asked for. (The web extraction is exempt — it transforms code that already exists.) +- Shared rendering code across targets. Reuse lives in shared tokens and shared SVG geometry math, not shared code — each target renders in its own idiom (recorded architecture decision). +- Framework wrappers (React or otherwise) ahead of a real framework consumer. +- Level-2/3 codegen (widget-spec → renderer generation) in this spec's v1 — that's the Phase 5 decision point, deliberately after hand-porting experience exists. + +** Scope tiers +- v1: full web library extraction (with the card-record refactor), demand inventory for the scripted targets, Emacs ports of the demanded widgets, one waybar pilot panel, the Level-2 generator go/no-go. +- Out of scope: new widget collection as part of this work (the catalogue keeps growing on its own track — new cards land as =GW.*= calls once extracted), shared cross-target rendering code, interaction parity beyond each target's native idiom. +- vNext (log to todo.org at hand-off): React/framework wrappers, Level-2/3 codegen if Phase 5 says go, ports beyond the demand matrix as consumers appear. + +* Design +** For a consumer +Web: include =widgets.js= (classic script) after the token =:root= block; call a builder — e.g. =GW.vuMeter(el, {value: 0.6})= — and get back a handle with setters. The gallery page itself becomes a grid of exactly these calls, so the demo and the library cannot drift. + +Emacs: =(require 'gallery-widgets)= (the =gallery-widget.el= pattern, one file per widget family or one shared file — Phase 3 sizes this); each widget is a function from parameters to an svg.el object, tokens resolved through =gallery-tokens.el=. Interaction (keymap, click regions) is per-widget Emacs idiom, not a translation of pointer drags. + +Waybar: no widget functions — waybar consumes =tokens-waybar.css= plus the banked composition idioms (lit/unlit key states, LED rows, engraved captions). The pilot restyles one real panel (audio, in =~/.dotfiles=) so its GTK CSS reads from the generated token sheet; "component" at this target means a documented CSS pattern, because GTK CSS has no scripting surface. + +** For an implementer +Three layers, already agreed: tokens (values) → widget spec (the gallery card: geometry, params, behavior) → per-target renderers. The card is the contract. A port re-reads the card's builder for geometry constants and behavioral rules (detent snapping, VU ballistics via the =vuDb()= law, exclusive-select), then re-expresses them natively. Geometry math (polar helpers, scale interpolation tables like =EDGE_SCALE=, =SEG14_MAP=) is copied deliberately per target and cross-checked by tests, not shared as a runtime dependency — the false-coupling judgment from the token work applies to code too. + +Extraction order inside Phase 1: the card builders already share =svgEl=/=polar=/=vuDb=/drag helpers and document-scoped gradients/filters; lifting them means (a) moving the shared engine and each builder into =widgets.js= behind a parameter object, (b) replacing its gallery card with a declarative card record (no / title / stage builder / options / readout / note / spec sheet incl. reference link / validation lamp) rendered by one =card()= path, (c) re-running the existing CDP behavioral probes unchanged — the probes drive the rendered DOM, so a green re-run is the no-regression proof. Batches of roughly 10-15 widgets keep each commit reviewable and the gallery working throughout. + +* Alternatives Considered +** Port everything to every target up front +- Good, because the catalogue would be uniformly consumable. +- Bad, because ~109 × 3 ports is months of work with no consumer for most of it, and unconsumed ports rot silently. +- Bad, because it front-loads the Level-2 codegen question before hand-port experience exists to answer it. + +** Shared renderer core (one geometry engine, thin target adapters) +- Good, because geometry fixes would land once. +- Bad, because the targets' rendering models genuinely differ (DOM+CSS animation vs static svg.el re-render vs GTK CSS with no scripting); an abstraction spanning them is speculative and was already rejected in the recorded architecture decision. +- Neutral, because shared *values* (tokens) and shared *math* (cross-checked constants) capture most of the benefit without the coupling. + +** Skip the web extraction; keep the gallery monolithic and port only outward +- Good, because it's less work now. +- Bad, because the gallery-as-demo property is lost: web consumers copy-paste internals, and the spec and any web usage drift apart immediately. +- Bad, because the extraction is cheap insurance — the CDP tests already exist to catch regressions. + +* Decisions [8/8] +** DONE SVG-first substrate +- Context: curved dials, needles, glow filters must render identically enough across web and Emacs; librsvg reproduces gradients and blur filters. +- Decision: widgets with curved/needle geometry are authored as SVG in every scripted target; plain-CSS forms stay CSS only where the target is CSS-native (waybar). +- Consequences: easier — one geometry language, rsvg side-by-side verification works; harder — some pure-CSS gallery cards need SVG re-expression when ported to Emacs. +- Resolved live with Craig through the gallery/svg.el work (2026-07-11/12); proven by the needle-gauge slice (b137223). + +** DONE Single token source +- Context: the amber retune required a 30-site find/replace before tokenization. +- Decision: =tokens.json= is the only place a design value is authored; =gen_tokens.py= emits all three target artifacts; generated blocks carry markers and are never hand-edited. +- Consequences: easier — one-line retunes, drift impossible; harder — adding a token means touching the generator (tested, 100% coverage). +- Resolved and shipped (4408779). + +** DONE Per-target interaction layers +- Context: pointer drag has no Emacs equivalent; GTK CSS has no scripting. +- Decision: interaction is designed per target in its native idiom; only the widget's *state model* (positions, ranges, detents, exclusivity) is common, carried by the card. +- Consequences: easier — each target feels native; harder — behavioral parity is a per-port judgment, tested per target rather than shared. +- Resolved live with Craig (2026-07-11). + +** DONE Demand-driven porting (Emacs and waybar targets) +- Context: 109 cards (as of 2026-07-12 review), three targets, most cards have no Emacs or waybar consumer today. +- Decision: an *Emacs or waybar* port happens when the demand matrix names a real consumer (a waybar panel, an Emacs game/UI); the matrix is Phase 1's deliverable and Craig approves it. The *web* extraction is exempt: it is a lossless transform of code that already exists, so it covers the full catalogue (option-1 approval, 2026-07-12). +- Consequences: easier — port effort lands where it's used, and the web library is complete from day one; harder — the Emacs/waybar surface stays partially ported indefinitely, by design. +- Agreed in the 2026-07-12 outline; web-extraction exemption approved with the option-1 go-ahead (2026-07-12). + +** DONE Gallery-as-spec +- Context: the ui-prototyping rule wants working prototypes as design evidence. +- Decision: the gallery is the canonical visual + behavioral spec; every port is judged against its card, and the web library's demo *is* the gallery. +- Consequences: easier — no separate spec artifact to maintain; harder — gallery regressions are spec regressions, so its CDP checks are load-bearing. +- Agreed in the 2026-07-12 outline; prototype evidence under the status heading. + +** DONE Level-1 codegen only, for now +- Context: token codegen shipped; widget-level codegen (Level 2/3) is speculative before hand-port experience. +- Decision: defer the Level-2 go/no-go to Phase 5, after roughly five hand ports. +- Consequences: easier — no premature generator abstraction; harder — early ports carry hand-duplicated geometry constants until the decision point. +- Recorded architecture decision (2026-07-11 session). + +** DONE Waybar pilot is the audio panel +- Context: the pilot should be a real panel with active development and a full test suite. +- Decision: the audio panel (=~/.dotfiles=) is the waybar pilot; archsetup owns the dotfiles work end to end per the standing rule in =.ai/notes.org=. +- Consequences: easier — freshest panel, known suite; harder — visual changes to a daily-driver panel need the manual-testing checklist. +- Agreed in the 2026-07-12 outline. + +** DONE Web library packaging +- Context: the gallery is a single self-contained =file://= HTML page; ES modules don't load over =file://=, and a React consumer doesn't exist yet. +- Decision: =widgets.js= ships as a classic script exposing one namespace object (working name =GW=), loaded by the gallery via a relative =<script src>=; the shared helpers (=svgEl=, =polar=, =vuDb=, scale tables) move inside it. Framework wrappers are vNext, demand-gated. +- Consequences: easier — gallery keeps working from =file://=, zero build tooling; harder — no tree-shaking or import isolation (acceptable at this scale). +- Approved by Craig 2026-07-12 (componentization go-ahead, option 1). The extraction itself is ungated — a lossless transform verified by the CDP probes; Craig's per-card validation pass (the status lamps) gates only the per-widget Emacs ports and the final blessing. + +* Review findings [3/3] +** DONE Extraction scope and gate superseded by the option-1 approval :blocking: +The spec's Phase 1 → Phase 2 order gates the web extraction on the demand matrix, and the Web-library-packaging decision says the build is "gated on his per-card validation pass." Craig's componentization go-ahead (option 1, 2026-07-12 ~21:30) superseded both: the web extraction covers the *full* catalogue as a lossless transform and starts immediately; the demand matrix and the validation lamps gate only the per-widget Emacs ports and the final blessing. An implementer reading the spec as written would follow the retired order. Recommended change: re-scope the Demand-driven-porting decision to the Emacs/waybar targets, drop the extraction gate from the packaging decision, and reorder/reword Phases 1-2. +Disposition: accepted — pre-agreed by Craig (recorded in todo.org and the 2026-07-12 session log). Folded into Decisions 4 and 8 and Phases 1-2. +** DONE Card-as-component refactor absent from Phase 2 +The approved build includes restructuring each gallery card into a declarative record (no/title/stage builder/options/readout/note/spec-sheet incl. reference link/validation lamp) so the page becomes data plus =GW.*= calls, but Phase 2 as written only covers lifting builders into =widgets.js=. Without it the "gallery becomes calls into the library" claim leaves the card chrome hand-rolled per card. Recommended change: name the card-record refactor as part of Phase 2's deliverable. +Disposition: accepted — folded into the Design section and Phase 2. +** DONE Stale gallery facts +The spec states 84 cards, a 2258-line file, the R-series closed at R31, and 52a43ec as the final SHA. The gallery has since grown to 109 cards (R-series through R56, ~4300 lines) across the gallery-upgrades merge and the takuzu-survey builds. Not a design problem, but stale numbers in the contract mislead the implementer sizing the extraction. Recommended change: refresh the counts and phrase them as of the review date, since the catalogue still grows. +Disposition: accepted — counts refreshed and dated throughout. + +* Implementation phases +** Phase 1 — Web library extraction +Lift *all* card builders into =docs/prototypes/widgets.js= behind parameter objects (classic script, =GW= namespace, shared helpers inside), and restructure each gallery card as a declarative record (no / title / stage builder / options / readout / note / spec sheet incl. reference link / validation lamp) so the page becomes data plus =GW.*= calls. Batched (roughly 10-15 widgets per batch); a green re-run of the CDP behavioral probes (=tests/gallery-probes/=) is the per-batch no-regression gate, and each batch leaves the gallery fully working from =file://=. Ungated by the validation lamps (option-1 approval — lossless transform). + +** Phase 2 — Demand inventory (Emacs / waybar) +Build the widget-to-target matrix for the *scripted-port* targets: walk the live waybar panels (net/bt/audio/maint) and the Emacs surfaces Craig names (games, dashboards), and record which gallery cards each actually wants. Deliverable: a short matrix table in this spec's appendix, approved by Craig. Tree untouched. Runs in parallel with or after Phase 1 — the extraction does not wait on it. + +** Phase 3 — Emacs ports +Extend the =gallery-widget.el= pattern for the demanded widgets: per-widget ERT (token resolution, geometry math normal/boundary/error, SVG structure, state-tracks-value) plus the rsvg-convert side-by-side visual harness against the gallery card. Keymap/click-region interaction designed per widget. Wired into =make test-elisp=. + +** Phase 4 — Waybar pilot (audio panel) +Restyle the audio panel's GTK CSS onto =tokens-waybar.css= plus the banked composition idioms. Lives in =~/.dotfiles=; archsetup drives edit/test/commit/push end to end and drops the dotfiles inbox note. Visual result goes on the manual-testing checklist (daily-driver panel). + +** Phase 5 — Generator decision point +After ~5 hand ports, revisit Level-2 codegen with evidence: how much of each port was mechanical duplication vs judgment. Deliverable: a go/no-go recorded as a decision here (go spawns its own spec). Tree untouched. + +* Acceptance criteria +- [ ] Demand matrix exists in the appendix and Craig has approved it. +- [ ] Every gallery card is a declarative record calling into =widgets.js=; CDP probes green; gallery renders unchanged from =file://=. +- [ ] Every demanded Emacs widget has ERT coverage and an rsvg side-by-side render matching its card; =make test-unit= green. +- [ ] The audio panel reads its palette from =tokens-waybar.css=; panel suite green; manual-testing checklist entry filed. +- [ ] Phase 5 go/no-go recorded as a dated decision. + +* Readiness dimensions +Answer each, or write "N/A because…". +- Data model & ownership: =tokens.json= user-authored; =:root= block, =tokens-waybar.css=, =gallery-tokens.el= generated (marker-fenced, never hand-edited); =widgets.js= and Emacs widget files hand-authored; the gallery HTML owns card layout and demo calls. +- Errors, empty states & failure: builders validate parameter ranges and clamp (the gallery's existing behavior — e.g. no-cross flag clamps, detent wrap); Emacs widgets signal =user-error= on out-of-range tokens/params (the =gallery-widget-token= missing-token error is the pattern). +- Security & privacy: N/A because everything is local rendering of design assets; no credentials, no network. +- Observability: web — CDP probes plus zero-console-error checks; Emacs — ERT output; waybar — the panel's existing smoke/AT-SPI harness. +- Performance & scale: N/A beyond the existing gallery scale (109 cards render fine); ports are per-widget, no aggregate load. +- Reuse & lost opportunities: tokens and geometry constants reused by value with cross-checking tests; svg.el, librsvg, and GTK CSS used as the platforms provide them; deliberate non-reuse of cross-target code recorded above. +- Architecture fit & weak points: fits the shipped 3-layer architecture; weak point is spec drift between a card and its ports — mitigated by the side-by-side harness and by the gallery-as-demo property on web. +- Config surface: no new knobs; token values remain the only tuning surface. +- Documentation plan: =docs/prototypes/README.org= gains a consumers section (how to use =widgets.js= / the Emacs widgets / the waybar tokens); no user-facing docs beyond that. +- Dev tooling: existing =make test-unit= / =make test-elisp= cover new suites (tests auto-discovered under =tests/gallery-*=); =gen_tokens.py= unchanged; no new targets expected. +- Rollout, compatibility & rollback: web/Emacs are additive files; the audio-panel restyle is the one shared-state change — it rides the dotfiles repo, reverts with =git revert=, and changes styling only (no behavior). +- External APIs & deps: rsvg-convert and svg.el already proven by the slice; waybar GTK CSS subset already exercised by the live panels; no unverified assumptions. + +* Risks, Rabbit Holes, and Drawbacks +- Pure-CSS cards demanded into Emacs need SVG re-expression — sized per widget in Phase 3; if one balloons, it goes back to the demand matrix rather than stalling the phase. +- The gallery extraction could tempt a redesign pass; the phase is a *lossless transform* only — builders move and cards become records, but no widget's look or behavior changes (the probes enforce it). +- GTK CSS renders differently from the browser (no SVG filters, different gradient behavior); the pilot may reveal idioms that don't translate — acceptable, the pilot exists to find them, and findings feed the composition-idiom notes rather than blocking. +- Hand-duplicated geometry constants can drift before Phase 5; each port's tests pin its constants against the card's values. + +* References / Appendix +- Architecture + banked variant/composition ledger: 2026-07-11/12 session archive (=.ai/sessions/=). +- Token generator: [[file:../prototypes/gen_tokens.py][gen_tokens.py]], tests in =tests/gallery-tokens/=. +- Emacs slice: [[file:../prototypes/gallery-widget.el][gallery-widget.el]], tests in =tests/gallery-widgets/=. +- Demand matrix: (Phase 1 deliverable — lands here.) + +* Review and iteration history +** 2026-07-12 Sun @ 20:57:50 -0500 — Claude Code (archsetup) — reviewer + responder +- What: full spec-review (code read against the live gallery, probes re-baselined green at 753380e). Three findings, all accepted and folded: the option-1 approval superseded the demand-gated extraction order (Phases 1-2 swapped and re-scoped, Decisions 4 and 8 reworded), the card-record refactor named as part of the extraction phase, stale counts refreshed (84→109 cards, R31→R56). Rubric: Ready. Flipped READY, then DOING with the phase decomposition into todo.org. +- Why: Craig's componentization go-ahead (option 1, 2026-07-12 ~21:30) landed after drafting — extraction is a lossless transform and proceeds ungated; the validation lamps gate only Emacs ports and the final blessing. The spec had to match the approved build before tasks decomposed from it. +- Artifacts: the three completed findings under =* Review findings=; probe repair commit 753380e; todo.org build tasks under "Retro widget catalogue" (=:SPEC_ID:= 3ac0d42c-db1a-4d21-bce4-e63785fef0ba). + +** 2026-07-12 Sun @ 10:12:06 -0500 — Craig Jennings — author +- What: initial draft. +- Why: collection phase closed at R01–R31; porting needs a decision procedure and per-target shape before any consumer work starts. +- Artifacts: [[file:../prototypes/panel-widget-gallery.html][panel-widget-gallery.html]], todo.org "Retro widget catalogue" (DOING). |
