sparkles:ui — Feature Specification
Status: living inventory · Date: 2026-08-05 · Scope: libs/ui (libs/ui/src/sparkles/ui/*.d), the sibling backend adapters (sparkles:ui-raylib, sparkles:ui-tui), and sparkles:input — the shared visual language behind every sparkles UI.
sparkles:ui is a canvas-first UI toolkit: one widget tree, laid out once, painted by pluggable backends. A widget names a semantic Slot, never a concrete color; a backend supplies only draw primitives and input events. The pipeline is view() → layout() → buildDisplayList() → paint(canvas), and every stage before paint is @safe and GL-free, so the whole toolkit is unit-testable through a RecordingCanvas with no window and no terminal.
This spec is the source of truth for the toolkit and the decision record for the choices behind it — in particular the layout model (LAY2), which requirement pre-narrowed to a survey of docs/research/ui-layout/ and which is settled here.
It supersedes docs/specs/hue/ui-architecture.md, which proposed the library before it existed; that page now holds only hue's own consumption requirements.
Design sources
The toolkit's design is grounded in three research catalogs in this repository. Where a requirement below cites one, the catalog is the evidence, this spec is the decision.
| Source | What it grounds |
|---|---|
| UI layout catalog | the layout model — 24 engines surveyed across box-flow, flexbox, constraints-down, solver, retained, immediate and tiling families (LAY) |
| Sean Parent catalog | the architectural rules — Whole/Part ownership, value semantics, explicit relationships, illegal states unrepresentable (PRN) |
| Tree-view case study | the view-model/view split, exemplified by the tree widget (WGT, VMD) |
Documentation map
| Page | What it covers |
|---|---|
| Overview (this page) | what the toolkit is · the three levels · the package graph · the status/ID/traceability scheme · module coverage |
| Feature requirements | library-wide requirements: the three levels, the canvas-first contract, the package graph and its dependency-cycle constraints, build/@nogc posture |
| Architectural principles | the binding rules the toolkit is held to, each traced to its source in the Sean Parent catalog — no incidental data structures, value semantics, explicit relationships, narrow contracts |
| Layout | the LAY2 decision record — the surveyed families, the verdict (box-flow + orientation-aware measure + clip), the integer-unit rule, and the explicit list of what is not implemented |
| Theme | the unified runtime-swappable design language — syntax rules, semantic slots, glyph sets and metrics in one value, gated by terminal capabilities |
| Widgets | the view-model/view split, the widget catalog, Props vs handlers, keys and element identity |
| Input | the abstract event vocabulary, the tier-0/1/2 capability ladder, and the backend adapter contracts |
| State machines | presentation-free behavior: scrollbar, selection, hover, focus, disclosure, timeline |
| Containers | the container tier: ScrollView (owned scrolling) and the single-window docking layout (splits, tabbed groups, drag-to-redock, focus/capture ownership) — SCV/DCK |
| Editor (planned) | the editable-text component — the EditorState machine (EDT), per-backend text input incl. IME/soft-keyboard phasing (EDI), the editor widget (EDR), and its consumers (EDU) — the capability behind hue's diff write wave (UIA9) |
| Backends | the isCanvas seam, the shipped targets, per-backend declared capabilities, and forward-compatibility rules for additional GPU backends |
| Open implementation issues | concrete deferred gaps: Whole/copy semantics, the closed widget sum, and the native pointer grab |
| Interaction review | the 2026-07-31 audit of every pointer/keyboard behavior: where it lives (toolkit vs apps/hue), the GUI/TUI divergences, and the Phase B redesign scope (IXR/IXB) |
| Migration | absorbing core-cli's UI components and porting apps/hue onto the toolkit — the milestone plan |
| Application host (proposed) | the sibling sparkles:ui-app package: backend selection, the shared window/font CLI, and the frame/event loop — the layer above the canvases, so an application never names one |
The three levels
Each lower level is usable independently and free of presentation:
| Level | Content | Spec |
|---|---|---|
| 1 — state machines | pure logic over abstract input → state + derived geometry, in abstract units | STM |
| 2 — layout | renderer-agnostic containers and sizing, producing rectangles | LAY |
| 3 — widgets | view(state) → WidgetTree, composing levels 1 and 2 with draw primitives | WGT |
Beneath them sit two cross-cutting concerns — the theme (THM), which resolves a widget's semantic slot to concrete appearance, and input (INP), which feeds the state machines.
Render targets
The toolkit itself is backend-agnostic; a target is a type satisfying isCanvas!T plus, for interactive targets, an input adapter.
| Target | Package | Canvas | Notes |
|---|---|---|---|
| TUI | sparkles:ui-tui | GridCanvas | cell grid over sparkles:tui; retained via the cell-diff compositor |
| GUI | sparkles:ui-raylib | RaylibCanvas | GPU quads over sparkles:raylib-text; immediate mode |
| HTML | sparkles:ui | interp/html | serializes the tree to markup + CSS; pure-CSS interactivity where possible |
| testing | sparkles:ui | RecordingCanvas | captures a DrawOp[]; the GL-free seam every unit test renders through |
Status scheme
Every requirement row carries one Status:
| Status | Meaning |
|---|---|
| not started | no implementation yet. |
| researched | design/notes exist (in code comments or a sibling doc), but no implementation. |
| partial | implemented with a documented limitation or missing sub-case (the row's notes say what is missing). |
full (<sha>) | fully implemented; <sha> is the primary commit (the "commit hash evidence"). Where several commits contributed, the earliest feature commit is cited and later refinements are noted. |
| decided | a decision requirement rather than an implementation one — the choice is settled and recorded on the page itself. Used only where there is nothing to implement, e.g. "the layout model is box-flow". |
This matches the hue spec's scheme so the two trees can cross-reference status without translation.
ID scheme
Requirement IDs are <AREA><n> — a short area mnemonic plus a number, unique within a document (e.g. LAY4, WGT2, TGT1). Areas: UIA/PKG/NFR (library-wide), PRN (principles), LAY (layout), THM (theme), WGT/VMD (widgets and view models), INP (input), STM (state machines), TGT (backends), MIG (migration). Each area's mnemonic is expanded at its section heading.
Traceability
Every source file under libs/ui/src/, libs/input/src/ and the backend adapter packages is covered by at least one requirement. The Module coverage table at the foot of each spec lists each file against the requirement IDs that own it, so coverage is auditable in both directions: requirement → code (the "Traces to" column of every row) and code → requirement (the coverage tables).
| Source file | Primary spec + areas |
|---|---|
libs/ui/src/sparkles/ui/geometry.d | layout — LAY3, LAY6 |
libs/ui/src/sparkles/ui/layout.d | layout — LAY1–LAY8, LAY11, LAY12 |
libs/ui/src/sparkles/ui/wrap.d | layout — LAY10 |
libs/ui/src/sparkles/ui/tracks.d | layout — LAY9 |
libs/ui/src/sparkles/ui/style.d | theme — THM1–THM5 |
libs/ui/src/sparkles/ui/theme.d | theme — THM6–THM9 |
libs/ui/src/sparkles/ui/canvas.d | backends — TGT1, TGT5 |
libs/ui/src/sparkles/ui/widget.d | widgets — WGT1–WGT6 |
libs/ui/src/sparkles/ui/display_list.d | backends — TGT2 |
libs/ui/src/sparkles/ui/state.d | state machines — STM1–STM7 |
libs/ui/src/sparkles/ui/interp/immediate.d | backends — TGT3 |
libs/ui/src/sparkles/ui/interp/cells.d | backends — TGT6; superseded by the cell adapter and retired with it |
libs/ui/src/sparkles/ui/interp/html.d | backends — TGT4, TGT7 |
libs/ui/src/sparkles/ui/components/ | widgets — VMD*, WGT7+ |
libs/input/src/sparkles/input/ | input — INP1–INP9 |
libs/ui-tui/src/ | backends — TGT6 |
libs/ui-raylib/src/ | backends — TGT6 |
→ Feature requirements · Principles · Layout · Widgets