15 KiB
Design
Source of truth
- Status: Active
- Last refreshed: 2026-09-08
- Primary product surface: generic
etaf-playground.elworkspace plus theexamples/research-shelf.etaf/.el/.ecssconsumer triplet - Additional product surface:
examples/task-workbench.el, a complete composable task app. Its initial layout, interactions, and visual states are checked in real GUI Emacs; current user direction is modern, spacious, consistently aligned controls and a 100-task dataset. - Visual reference:
design/research-shelf.html - Evidence reviewed: ETAF public View/Component/Data/Resource APIs,
etaf-uiButton/Checkbox/Panel/DataGrid/Pagination, andetaf-sqlite's typed source contract.
Brand
- Personality: editorial, focused, warm, quietly intelligent.
- Trust signals: durable SQLite data, visible save state, selected-item detail, explicit progress/status, and recoverable storage errors.
- Avoid: generic admin dashboards, KPI walls, fake charts, rainbow gradients, and a showcase that exists only to exercise APIs.
Product goals
- Build a useful personal reading/research shelf backed by a real SQLite file.
- Ship a deterministic 256-record fixture by default so pagination and warm incremental updates are exercised on a realistic surface; fixture size and page size remain configurable for focused tests.
- Make triage, selection, progress updates, filtering, pagination, and recovery feel like one coherent product rather than isolated demos.
- Exercise existing ETAF components through composition before adding any new component type.
- Keep the static
.etaffile responsible for shell/section composition while the.elcompanion owns storage, state, callbacks, and lifecycle.
Personas and jobs
- Primary persona: an Emacs user collecting papers, books, essays, and notes.
- Jobs: find what to read next, filter by state/priority, inspect one item, update progress, mark an item finished, and recover after a storage error.
- Contexts: compact desktop window, fullscreen review, keyboard-first use, and a local SQLite database that survives unmount/remount.
Information architecture
- Shell: compact brand bar -> filter rail -> reading list -> detail inspector.
- Filter rail: All, In progress, Unread, Finished, and Starred; each is a real action and the list summary exposes the matching count.
- Reading list: title/author/status/progress rows, single selection, paging, empty/loading/error states.
- Detail inspector: title, author, type, status, progress, priority, note,
last updated, and actions (
+10%,Finish,Star,Archive). - Footer: database path/status, visible result count, and current page.
Design principles
- One useful workflow: every visible control helps decide or update what to read next.
- Editorial hierarchy over dashboard chrome: one strong title, restrained metadata, and generous whitespace around the selected record.
- State is durable and observable: SQLite is the source of truth; UI status tells the user when a mutation is loading, saved, or failed.
- Composition first: reuse public UI Components and keep storage/state in the
companion, not in
.etafor a parallel widget layer. - Fail closed: a SQLite failure keeps the last usable list and exposes a retry action instead of replacing the whole surface with a stack trace.
Visual language
- Color: ink
#172033, paper#F7F3EA, cobalt#3657D6, mint#3E9B8F, coral#D86B5D, amber#C58A3A, muted slate#6D7482. - Typography: readable proportional labels with compact monospace metadata; title and selected record use bold weight, not oversized headings.
- Spacing/layout rhythm: 1-cell outer rhythm, 2-cell panel padding, 1-cell workspace gaps, and stable intrinsic control widths.
- Shape/elevation: thin borders, small radius-like grouped rows where Ebox permits, no floating overlays or decorative shadows.
- Motion: immediate state changes; no timer-driven animation; reduced-motion safe by construction.
- Imagery/iconography: Unicode marks with semantic meaning:
⌕,★,✓,◷,↗,⚠, and·.
Components
- Existing components to reuse:
etaf-button,etaf-checkbox,etaf-label,etaf-panel,etaf-data-grid, andetaf-paginationfrometaf-ui. - Companion composition:
research-shelf-shell,filter-rail,reading-list, anddetail-inspector; these are ordinary ETAF Components, not a second UI kit. - Variants/states: light/dark Theme, selected/unselected row, starred, empty, loading, error, saved, disabled-at-boundary, and archived.
- Token ownership: palette and spacing tokens live in the companion;
.etafcontains only validated structural facts and labels.
Playground framework boundary
etaf-playground.elis a reusable source/preview workspace, not an application module. It discovers same-basename.etaf/.elfiles and an optional.ecssfile, creates the left source session, and mounts the right preview.- The framework owns inert source readers, companion registration overrides, source tabs, window layout, refresh/reset/close, and ETAF Runtime lifecycle. It does not require a catalog or name a business application.
- A companion owns its Components, storage adapters, state, resources, events, style consumers, and root factory. The framework must not reference a business component, database package, table schema, palette, or application-specific ref.
.etafand.ecssare read as data..elis evaluated only when a preview is first loaded or the companion source is explicitly dirty; normal ETAF duplicate-definition errors remain intact outside that deliberate reload boundary.- New examples add a same-basename
.etaf/.elpair and may add.ecss; the generic framework and its build file do not change.
Research Shelf companion boundary
Research Shelf is intentionally compact: the executable companion is one file, with comments making the small app's boundaries visible:
examples/research-shelf.etaf inert structure source
examples/research-shelf.ecss inert style source
examples/research-shelf.el DATA / THEME / STATE / VIEW / ROOT
The .el file is the only Playground registration point and palette owner:
its THEME section may use the optional etaf-theme-tp adapter to turn
TP light/dark pairs into an ETAF semantic Theme plist. The rest of the app
never calls TP or embeds renderer palette names. The VIEW section creates a
Data Controller inside the shell Component, so ETAF owns its effect Scope and
disposes the SQLite source with the Component. Detail and filter Components
consume Context and dispatch named Actions; they do not reach into SQLite or
duplicate selection matching. The .etaf structure and .ecss tokens are
applied at the root. If the product grows beyond this size, these commented
sections can later move into modules without changing the public Playground
triplet.
Accessibility
- Target: keyboard-complete and high-contrast desktop UI.
- Keyboard/focus: Tab order follows filters -> rows -> detail actions -> pager; RET dispatches the focused public ref; selected row is announced in text.
- Color is never the only state signal; status uses text and Unicode marks.
- Disabled actions remain visible with disabled semantics and explanatory help.
Responsive behavior
- Wide/fullscreen: one Flex line presents rail / list / detail as three columns.
- Medium: rail and list remain together while detail wraps to the next line.
- Narrow: the same three Components wrap in document order into a vertical flow.
- The shell uses one wrapping Flex composition with semantic basis/grow weights;
it does not read window size or maintain breakpoint state. Use
rowonly for compact intrinsic controls and Flex whenever free space must be distributed.
Interaction states
- Loading: list keeps its frame and shows
◷ Loading library…. - Empty: selected filter explains why there are no matches and offers
All. - Error: storage card shows
⚠ Could not savewithRetry; last committed data remains visible. - Success: mutation shows
✓ Saved locallyand updatesupdatedmetadata. - Disabled:
Finishis disabled for finished records;+10%is disabled at 100%; archive is disabled while loading. - Page density: activating
Rows N ✎uses Emacs's native minibuffer to accept any integer from 1 through 100, then reloads page one.
Content voice
- Tone: concise, observant, and useful; never framework-centric in primary UI.
- Terminology: Reading, In progress, Finished, Starred, Saved locally, Storage issue.
- Microcopy: action + result (
Mark finished,Saved locally) instead of genericDone.
Implementation constraints
- Framework/styling: source/preview orchestration is public ETAF API only;
concrete examples may use
etaf-uiandetaf-sqlite; no HTML/CSS runtime dependency. - Public authoring boundary: intentional hot reload goes through
etaf-component-redefine-run; application code does not bind ETAF private registry variables. Controllers created in Component setup use automatic Scope ownership, and selected rows useetaf-data-selected-itemwith an explicit:item-key. - Data schema: one typed
reading_itemsSQLite table with id/title/author/ kind/status/progress/priority/starred/note/updated columns. - Pressure fixture:
etaf-research-shelf-fixture-sizedefaults to 256 andetaf-research-shelf-page-sizedefaults to 12; existing local rows are preserved and missing fixture rows are topped up with fresh IDs. - Performance: one Data mutation -> one Runtime generation/publication. The
retained Research Shelf batch evaluator uses 5 unmeasured warmups and 30
measured samples at fixed 1413×62 geometry, with p95 and max at most 50ms.
Current GUI acceptance has the additional requirements below. The accepted
105ms p50 wording is historical design context, not the current evaluator
gate;
DOC-PERF-001keeps its formal disposition in M0b1. - Compatibility: any same-basename
.etaf/.elexample is discoverable;.ecssis optional, and the bundled Research Shelf remains only one consumer. - Verification: inert reader tests, source-tab/session tests, unsaved source refresh tests, SQLite temp-file integration tests, mount/remount tests, repeated selection/mutation tests, and clean GUI screenshots.
Performance evidence lanes
The batch latency lane, trace lane, and GUI lane share one
repository/environment/scenario/fixture/build identity but remain separate
measurements. The retained Research Shelf 1413×62 batch evaluator owns its
uninstrumented 5/30 p95/max 50ms regression checks. Batch verifier duration
measures batch Lisp work, not GUI first paint.
The trace lane owns a representative instrumented invocation with cost class,
work counters, turns, allocation, and GC. The GUI lane owns the real Emacs
action sequence and reviewed visual evidence. Current GUI acceptance additionally
requires three independent foreground sample sets with each operation's p95 and
max at most 50ms from action callback through forced redisplay. This gate remains
outstanding. Preserve all warmups, GC observations and samples through the
measurement entry in ../etaf/scripts/README.md; returning from redisplay does
not certify compositor presentation. No one lane may be used to claim another
lane passed.
Open questions
- User may rename the product after reviewing the first HTML/ETAF render; architecture and interaction contracts remain unchanged.
Task Workbench visual contract
- Source: user review of the actual Workbench GUI on 2026-09-05, especially adjacent buttons, cramped rows and excessive detail-panel width. Reuse the existing restrained cobalt emphasis; no separate design-system layer.
- Hierarchy: a title and quiet workspace subtitle, a grouped toolbar, then filter rail / primary task list / compact detail and service cards. Footer counts and storage scope use muted text. The task list receives extra width.
- Color: cool gray canvas, white cards, soft slate borders, cobalt primary action; dark mode has a charcoal canvas and lighter slate cards. All official control states use the existing semantic Theme tokens, including disabled, selected, checkbox and pagination colors.
- Actions: New task is primary. Search and retry are secondary; location, deletion and error simulation are quiet controls. The current filter is derived from the Data query and visibly distinct. Disabled controls retain their official semantics and palette.
- Rhythm: 10px within a horizontal control group, 16px between workspace
columns, one text line between vertical groups, and
(1 2)card padding. Ebox Row:item-gapis pixels; Column:item-gapis lines. Flex uses:gap '(1 (16))for one line vertically and 16px horizontally. Keep buttons compact with horizontal padding, rather than making every table row tall. - Data: 100 deterministic tasks, mixed completion states, 10 rows per page. Test all pages, search, filtering, selection, CRUD, two independent Apps and resource recovery. Do not replace the real dataset with a smaller acceptance fixture. Initial seed titles remain stable for reproducible input checks.
- Responsive: retain declarative Flex wrapping in source order. Side columns have stable basis sizes; the task list grows. Root padding and viewport scroll are legitimate composition and must work together without example workarounds.
- Panel alignment: workspace panels align at the top and keep their own content-driven heights in layout lines. Toggling “显示说明” collapses the detail content without changing the filter, task or service line counts. Following cards and the footer may move naturally in document flow; do not reserve invisible help space or introduce viewport-specific height constants. This follows the user's 2026-09-08 review of disclosure-induced panel resizing.
- Native text geometry: Emacs shares ascent and descent across each displayed row. Moving content may therefore change a card's pixel height while its layout line count stays fixed. The user explicitly accepts this behavior; preserve layout semantics and interaction instead of forcing uniform rows.
- Width distribution: filter/list/inspector grow with weights
1/4/1, keeping emphasis on the task list while a side column fills its line when wrapped. Filter buttons wrap into a horizontal group when their panel has room. Detail and service cards share a wrapping Flex: stacked in the desktop side column, side by side with independent heights on a wider wrapped row, then stacked again when each card's usable minimum width no longer fits. - Table sizing: the task title receives the remaining column width through the
existing
(fr 1)Grid track convention; the action column keeps its compact character width. Header and body consume the same tracks. Resizing changes geometry without replacing custom cell state or semantic identity. - Pagination: full previous/next buttons with readable labels and horizontal padding, a quiet centered page/range summary, and clear disabled states. Workbench uses Chinese labels; the shared component retains configurable text.
- Acceptance: inspect light/dark, selected/disabled, wide/compact and scrolled GUI states after functional checks. Preserve the goal's three real GUI performance sample sets with per-operation p95 and max at most 50ms.
- Assumption: no new brand was requested; the repository's existing cobalt, neutral surfaces and typography are the design basis.