Skip to content

AcceptedOwner sparkles:uiReviewed 2026-08-05

sparkles:ui — Feature Specification ​

Abstract ​

sparkles:ui is the user-interface toolkit Sparkles applications share, whether they appear in a terminal, a desktop window, or a static HTML page. An application describes its interface once, as a tree of widgets derived from its state. The toolkit lays that tree out and reduces it to a flat list of drawing operations, which a small backend paints for its target. Widgets name the role each part plays rather than its color, so one theme restyles every target at once. No step before painting needs a window or a terminal, so an entire interface can be tested as data.

Introduction ​

Sparkles applications must look and behave alike in very different places. The hue file viewer runs both in a terminal and in a GPU-accelerated window, a documentation gallery renders the same views to HTML, and a terminal emulator embeds as one pane of a larger interface. Each place measures space in its own unit: character cells, pixels, or CSS lengths. Each delivers input differently, from escape sequences to polled device state, while static HTML offers only what pure CSS can express. And each can draw a different subset of what a design asks for.

Writing every widget once per target multiplies the code, and worse, lets behavior drift. Selection, scrolling and focus end up modeled several times, the models disagree, and a fix on one target never reaches the others. Native widget toolkits do not close the gap, because a terminal has none and the browser's behave differently from a desktop's. A floating-point layout engine borrowed from the web brings its own defect: rounding produces off-by-one cells on a terminal grid.

The toolkit is canvas-first. It owns all semantic behavior and composition, and a backend contributes only primitive drawing, text measurement and the translation of native input. The toolkit is built in three levels, each usable without the ones above it. State machines hold behavior that has no appearance, such as scrolling, selection, hover and focus. Layout places every widget in whole cells, the abstract grid unit that is one character position on a terminal and whose device size a pixel or HTML backend chooses. Widgets compose the two, and each names a semantic slot, such as "error text" or "selected row", never a concrete color.

The laid-out tree then becomes a display list: a flat sequence of drawing operations whose slots are already resolved against the theme, so a backend never consults the theme or the tree. Layout obtains text measurement through the canvas rather than a device, so a test substitutes a recording canvas, which measures and records without any window, and inspects the list directly. A target that cannot honour a feature declares the limitation, and the toolkit degrades visibly rather than silently.

This specification, spread across the pages listed below, covers the toolkit, the abstract input vocabulary sparkles:input, and the contract each backend adapter package must meet. It uses no native OS widgets on any target. Choosing a backend, opening a window, loading fonts and running the frame loop belong to the application host, sparkles:ui-app, which has its own specification. Decoding terminal escape sequences belongs to sparkles:tui, and rasterizing glyphs to sparkles:raylib-text. The layout model is deliberately smaller than CSS: no constraint solver, no full grid, no wrapping of boxes; the layout page records each exclusion and its reason. hue's UI architecture holds only the requirements hue places on the toolkit as a consumer; what the library is and does is defined here.

This page is the source of truth for the toolkit and the record of the decisions behind it, the layout model (LAY2) above all. It defines the three levels, the render targets, and the status, ID and traceability schemes every sibling page follows. Each sibling page below holds the requirements for one area under its own ID prefix, and Design sources lists the research catalogs the decisions rest on.

Documentation map ​

PageWhat it covers
Overview (this page)what the toolkit is · the three levels · the package graph · the status/ID/traceability scheme · module coverage
Feature requirementslibrary-wide requirements: the three levels, the canvas-first contract, the package graph and its dependency-cycle constraints, build/@nogc posture
Architectural principlesthe 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
Layoutthe 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
Themethe unified runtime-swappable design language — syntax rules, semantic slots, glyph sets and metrics in one value, gated by terminal capabilities
Widgetsthe view-model/view split, the widget catalog, Props vs handlers, keys and element identity
Inputthe abstract event vocabulary, the tier-0/1/2 capability ladder, and the backend adapter contracts
State machinespresentation-free behavior: scrollbar, selection, hover, focus, disclosure, timeline
Keymap & lanternkeyboard policy as data — Binding!(Cmd, Scope) tables over app-supplied enums, scope-order precedence with marker UDAs, two-direction resolution (resolve/bindingsAt), and the which-key-style guide machine + panel — KEY/LTN
Gutter channelsthe per-line chrome model — line numbers, coverage counts, fold arrows, diff markers and blame as one set of layout channels beside the content, composed after layout, width-reserved, priority-merged and budgeted — GUT
Containersthe container tier: ScrollView (owned scrolling) and the single-window docking layout (splits, tabbed groups, drag-to-redock, focus/capture ownership) — SCV/DCK
Inspectorthe generic inspector component — a tree over a subject + details pane + the adapter-defined selection/extent contract, with the widget-tree adapter (toolkit self-inspection) — INS
Property treethe reflective property-tree component — PropertyTree!T as an adapter over the tree widget: the type-only walk, metadata UDAs, path addressing, ranked fuzzy filtering, value-semantic edits with host-stored undo/redo, and the read-only script-free HTML posture — PRT
Anchored overlays (proposed)the one anchored-overlay primitive behind every floating surface: the anchor value, the placement solve, the ordered top-layer arena that fills DCK13's rung, triggers, dismissal, layering and modality — POP/ANC/PLC/TRG/DSM/LYR/MDL
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)
Effects & imagesraster images in the widget tree and subtree effects: the effect bracket, the tier model that decides what survives to a cell grid, the effect registry, and built-in effects written once in D for both the terminal and the GPU — EFX/IMG
Effects demosthe acceptance demos for EFX/IMG: one runnable command per visible feature, and what it should show
Backendsthe isCanvas seam, the shipped targets, per-backend declared capabilities, and forward-compatibility rules for additional GPU backends
Open implementation issuesconcrete deferred gaps: Whole/copy semantics, the closed widget sum, and the native pointer grab
Interaction reviewthe dated audit of every pointer/keyboard behavior: where it lives (toolkit vs apps/hue), the GUI/TUI divergences, and the redesign it scoped (IXR/IXB)
Migrationabsorbing core-cli's UI components and porting apps/hue onto the toolkit — the milestone plan
Application hostthe 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

Design sources ​

The toolkit's design is grounded in four research catalogs in this repository. Where a requirement below cites one, the catalog is the evidence, this spec is the decision.

SourceWhat it grounds
UI layout catalogthe layout model — 24 engines surveyed across box-flow, flexbox, constraints-down, solver, retained, immediate and tiling families (LAY)
Sean Parent catalogthe architectural rules — Whole/Part ownership, value semantics, explicit relationships, illegal states unrepresentable (PRN)
Tree-view case studythe view-model/view split, exemplified by the tree widget (WGT, VMD)
Anchored-overlay catalogthe anchored-overlay primitive — 38 subjects on anchors, placement, layering, triggers, dismissal and modality (POP)

The three levels ​

Each lower level is usable independently and free of presentation:

LevelContentSpec
1 — state machinespure logic over abstract input → state + derived geometry, in abstract unitsSTM
2 — layoutrenderer-agnostic containers and sizing, producing rectanglesLAY
3 — widgetsview(state) → WidgetTree, composing levels 1 and 2 with draw primitivesWGT

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.

TargetPackageCanvasNotes
TUIsparkles:ui-tuiGridCanvascell grid over sparkles:tui; retained via the cell-diff compositor
GUIsparkles:ui-raylibRaylibCanvasGPU quads over sparkles:raylib-text; immediate mode
HTMLsparkles:uiinterp/htmlserializes the tree to markup + CSS; pure-CSS interactivity where possible
testingsparkles:uiRecordingCanvascaptures a DrawOp[]; the GL-free seam every unit test renders through

Status scheme ​

Every requirement row carries one Status:

StatusMeaning
not startedno implementation yet.
researcheddesign/notes exist (in code comments or a sibling doc), but no implementation.
partialimplemented 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.
decideda 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), INS (inspector), GUT (gutter channels), PRT (property tree), TGT (backends), MIG (migration), and — for anchored overlays — POP (the primitive), ANC (anchors), PLC (placement), TRG (triggers), DSM (dismissal), LYR (layering) and MDL (modality and focus). 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 filePrimary spec + areas
libs/ui/src/sparkles/ui/geometry.dlayout — LAY3, LAY6
libs/ui/src/sparkles/ui/layout.dlayout — LAY1–LAY8, LAY11, LAY12
libs/ui/src/sparkles/ui/wrap.dlayout — LAY10
libs/ui/src/sparkles/ui/tracks.dlayout — LAY9
libs/ui/src/sparkles/ui/style.dtheme — THM1–THM5
libs/ui/src/sparkles/ui/theme.dtheme — THM6–THM9
libs/ui/src/sparkles/ui/canvas.dbackends — TGT1, TGT5
libs/ui/src/sparkles/ui/widget.dwidgets — WGT1–WGT6
libs/ui/src/sparkles/ui/display_list.dbackends — TGT2
libs/ui/src/sparkles/ui/state.dstate machines — STM1–STM13; input — INP10 (the hit-list consumers); anchored overlays — ANC3, ANC7
libs/ui/src/sparkles/ui/keymap.dkeymap & lantern — KEY2–KEY10, KEY13
libs/ui/src/sparkles/ui/lantern.dkeymap & lantern — LTN1–LTN4, LTN9–LTN12
libs/ui/src/sparkles/ui/components/lantern_view.dkeymap & lantern — LTN5–LTN8, LTN14
libs/ui/src/sparkles/ui/interp/immediate.dbackends — TGT3
libs/ui/src/sparkles/ui/interp/cells.dbackends — TGT6; superseded by the cell adapter and retired with it
libs/ui/src/sparkles/ui/interp/html.dbackends — TGT4, TGT7
libs/ui/src/sparkles/ui/components/widgets — VMD*, WGT7+
libs/ui/src/sparkles/ui/components/inspector.dinspector — INS1–INS5
libs/ui/src/sparkles/ui/components/gutter.dgutter channels — GUT1–GUT9
libs/ui/src/sparkles/ui/property_tree.d (planned)property tree — PRT1–PRT11, PRT14–PRT35
libs/ui/src/sparkles/ui/components/property_view.dproperty tree — PRT12–PRT13, PRT21, PRT23, PRT25–PRT28, PRT32–PRT35
libs/input/src/sparkles/input/input — INP1–INP9
libs/ui-tui/src/backends — TGT6
libs/ui-raylib/src/backends — TGT6
libs/ui/src/sparkles/ui/overlay/anchor.d (planned)anchored overlays — ANC1–ANC9
libs/ui/src/sparkles/ui/overlay/place.d (planned)anchored overlays — PLC1–PLC15
libs/ui/src/sparkles/ui/overlay/arena.d (planned)anchored overlays — POP4, POP7, LYR1–LYR4, LYR9, LYR10, LYR12
libs/ui/src/sparkles/ui/overlay/policy.d (planned)anchored overlays — TRG1–TRG5, DSM1–DSM6, DSM11, MDL2, MDL3
libs/ui/src/sparkles/ui/overlay/package.d (planned)anchored overlays — re-exports only

→ Feature requirements · Principles · Layout · Widgets