feat(tui): Phase 2A — renderer-agnostic state + selectors + primitives - #74
Merged
Conversation
… primitives)
Pure-function TUI scaffolding. No JSX yet (Phase 2B); everything here
is testable in isolation. Phase 2B will wire OpenTUI components on top
of this state shape with no further reducer changes.
Six new modules, all TDD-driven (61 new tests, 434 → 495 total):
1. **`src/tui/should-use-tui.ts`** — pure decision-table predicate
for when the alt-screen TUI can run. 11 tests over the env matrix
from `docs/design/tui-design.md` §5: --no-tui, planning modes,
non-TTY, NO_COLOR, CI, custom-logger, terminal-size, opt-in
fall-through. Stable reason strings for "TUI unavailable (<reason>)".
2. **`src/tui/primitives/sparkline.ts`** — Float32Array ring buffer
+ unicode-block renderer (▁..▇). 9 tests cover wrap, default vs
explicit width, padding, range-mapping, NaN/negative clamp.
Three buffers per State (throughput, remote ops, parallel%); the
1 Hz tick samples them all.
3. **`src/tui/primitives/timeline-layout.ts`** — Gantt-bar layout
for the Timeline view. (startNs, endNs, totalNs) → (startCol,
widthCols). 9 tests cover scale, 1-cell visibility floor, right-
edge clamp, not-started skip, input order preservation.
4. **`src/tui/state/critical-path.ts`** — topo-DP for the longest
path through the task DAG, weight chosen by status (actual /
currentElapsed / historyAvg / 0 for skipped/failed). Persistent
tasks excluded. 8 tests cover ties (earlier dep wins),
parallel-chain max, running-task elapsed, predicted-history.
5. **`src/tui/state/store.ts`** — full reducer over Action =
`{ type:'event' } | { type:'tick' } | { type:'key' }`. State carries
tasks (insertion = topo order), workerSlots (mirror of scheduler
allocation), history table, three sparkline buffers, remote
counters (capped 1024 latencies), active view, focusPanel,
selectedTaskId, filters per-view, taskDetailOpen, showHelp,
dirty flag. 13 tests cover runStart init, taskStart/Complete slot
mgmt, log buffering (split on \\n, pendingLine flush at end),
cacheProbe, remoteCache counters + latency cap, runEnd, keys
(viewChange/toggleHelp/selectTask), tick (sparkline sampling).
Log buffer capped at 10k lines with N-elided sentinel.
6. **`src/tui/state/selectors.ts`** — pure (State) → result for the
five Phase 2/3 views: parallelPct (header gauge), ready queue,
blocked queue, topBlockers (by dependentsCount), slowVsHistory
(>1.5× ratio), cacheMissImpact (ranked by history avg). 11 tests
cover empty/edge cases.
Added: `@opentui/core` + `@opentui/react` to deps. No imports yet —
Phase 2B's `src/tui/tui.ts` (the single import site) lands with the
React components + the `--tui` CLI flag. The compile-gate experiment
documented in `tui-design.md` §2 runs there too.
https://claude.ai/code/session_016HXj6HW6bxSn8EYuKcxTD9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2A of the TUI: renderer-agnostic foundation. Pure functions
only, no JSX yet — every module is fully unit-tested. Phase 2B wires
the OpenTUI React components and the
--tuiCLI flag on top of thisstate shape (zero reducer changes expected).
Tests: 434 → 495. New modules, all TDD-driven:
src/tui/should-use-tui.tssrc/tui/primitives/sparkline.tssrc/tui/primitives/timeline-layout.tssrc/tui/state/critical-path.tssrc/tui/state/store.tssrc/tui/state/selectors.tsNotable design touches (from
docs/design/tui-design.md)tasks(insertion = topo order),workerSlotsmirror, three sparkline buffers, remote counters(1024-latency cap), per-view filters, overlay flags,
dirtyflag.\n, parks partial trailing inpendingLine, flushes ontaskComplete. Capped at 10k lines with adrop-1k strategy +
…N more lines elided…sentinel.taskStart.slot(Phase 1'sscheduler change) and frees on
taskComplete. Selectors computeparallelPctfromworkerSlotsdirectly.actualMs, runninguse
currentElapsedMs, waiting/ready usehistoryAvgMs,skipped/failed are 0, persistent are excluded.
▁, highest →▇. Full-block█deliberately omitted (clips on some terminals per design doc).What's NOT in this PR
Header,TaskList,LogPane,ProgressBar,StatusBar, and the 5 views).src/tui/tui-shim.ts) andsrc/tui/tui.tsentry.--tuiCLI flag and the compile-gate experiment.All of that ships in Phase 2B against this branch's state shape.
Test plan
bun src/bin.ts run format— cleanbun src/bin.ts run lint— 0 warnings, 0 errorsbun src/bin.ts run test— 495 pass, 0 failcritical-path, should-use-tui, tui-store, tui-selectors)
Dependencies
@opentui/core,@opentui/reactadded. No imports yet — theyship for Phase 2B's renderer. Lockfile updated.
https://claude.ai/code/session_016HXj6HW6bxSn8EYuKcxTD9
Generated by Claude Code