- README: 42 tools (was 38), /demo row, TUI section, testing plus integration line, full layout map - tui.md: research link, tests pointer, soften shot-tested claim - AGENTS.md/tests/README.md/research hubs: cross-links between docs - Research: forward-correct tui conclusion plus INDEX activity row (19 failures fixed, tai_pkg deleted, 320/320 green); audit PASS
6.1 KiB
Conclusion: Retained-DOM TUI framework inside tai.py
Metadata
| Field | Value |
|---|---|
| Institute | Molodetz Institute — Research & Retrieval Division |
| Subject | tui-framework-design |
| Document type | Conclusion |
| Tier | N/A |
| Date decided | 2026-10-10 |
| Decided by | retoor, advised by tai (agent, Muse Spark) |
| Status | Active |
| Confidence | High |
Question / problem statement
How should tai get overlapping layers (dialogs, floating terminals, clickable option panels) plus a general component model — layout, events, mouse, animation, streaming sub-terminals — given tai is a single-file, standard-library-only Python program?
Conclusion
Build a small retained-DOM TUI framework directly in tai.py, borrowing Textual's proven algorithms but none of its code: a Panel-rooted component tree with rows/cols flow, fr/%/auto sizing, dock edges, named layers with z-order, and offsets; a per-line cuts/chops/occlusion compositor over styled text runs with single-write synchronized output; SGR mouse parsing (1000+1006, legacy fallback) feeding Click/Scroll events; bubbling on_<name> event dispatch with stop(); tick-driven animate() tweens; and Terminal widgets backed by a reimplemented Screen/stream VT emulator over real ptys. No new dependencies, no CSS parser, no per-widget asyncio tasks.
Supporting sources
../rabbitui-textual-research.md— the blueprint: compositor cuts/chops/occlusion, spatial map, sync-output anti-flicker, traversal focus with modal trap, exact fractional layout, virtualize-don't-ingest.../textual-events-messages.md— event model: bubble flag,on_naming,stop(),prevent_default(), custom messages namespaced in widgets.../textual-layout.md— layout vocabulary: vertical/horizontal/grid,fr/%, dock edges, named layers beating yield order, offsets.../textual-animation.md— animation API shape:animate(attr, value, duration|speed, easing, on_complete, delay),in_out_cubicdefault.../selectel-pyte-repo.md— Screen/stream split for VT emulation; LGPL finding that forces reimplementation.../xterm-mouse-protocols.md— exact SGR report format, button codes, modifier bits, enable stack.../tmux-remove-popup-overlay.md— decisive precedent: dialogs are ordinary z-ordered layers in one compositor, never a second overlay renderer.../bodek-mouse-report-boundary.md(Tier 2) — Terminal.app never negotiates 1006; reads must split on report boundaries.../claude-code-sgr-mouse-leak-issue.md(Tier 2) — parsed mouse reports must be consumed, never leak into text input.../dot-agent-deck-pty-replay-pyte.md(Tier 2) — headless test strategy: feed bytes into a screen, assert the grid.
Alternatives considered and rejected
- Adopt Textual as a dependency: rejected — heavy runtime, asyncio architecture mismatch with tai's sync select loops, release churn and maintenance-mode risk, and it would end single-file stdlib-only distribution.
- Vendor pyte for VT emulation: rejected — LGPL-3.0 license plus the stdlib-only rule; reimplement the Screen/stream split instead.
- Drive tmux as the UI substrate: rejected for UI (fine for session wrapping) — tmux panes tile, cannot overlap arbitrarily, and add an external dependency; tmux's own popup deletion shows even it converged on floating panes.
- Per-cell double-buffer diffing (ratatui-style): rejected — occlusion-unaware, painful for modals/overlays and wide chars; cuts/chops over styled runs handles both.
- Full CSS parser for styling: rejected — a parser plus selector engine is disproportionate; constructor kwargs and style objects carry the same vocabulary (the "fake CSS" complaint warns against half-measures here).
- asyncio event loop for the UI: rejected — tai's loops are sync select/poll; one synchronous tick loop with an fps cap is simpler, deterministic, and testable.
Risks / open questions
- Wide (CJK/emoji) characters: styled-run compositing keeps them sane structurally, but correct column width needs a wcwidth table (stdlib has none) — v1 treats them as width 1 and documents the gap.
- Terminal capability variance (no SGR mouse, no synchronized output): the renderer must degrade gracefully (legacy mouse parsing, plain writes) rather than assume modern xterm.
- Scope discipline: "the best TUI library ever" is unbounded; v1 ships the listed components and defers stylesheet language, bracketed paste, and kitty keyboard protocol explicitly.
Action taken
Built and verified the same session: framework implemented in working-tree tai.py only (per retoor's explicit call), documented in tui.md, wired to /demo, --demo, --demo-shot. Verification: 49/49 new TUI unit tests pass, 21/21 headless interaction checks pass (focus, modal trap, clicks, wheel, resize, spawn/close/ask, quit paths, 60-frame animation glide at ~15ms/frame, real pty spawn/stream/reap), 10/10 live pty end-to-end checks pass (dialog, options, answer toast, stream ticks, alt-screen/mouse/sync markers, clean teardown, exit 0), test_seal.py 16/16 pass. Full test_tai.py still reports the 19 pre-existing facade-world failures that exist with and without this work (working-tree tai.py is an uncommitted monolith while the committed suite targets the facade; verified via stash run and zero removed definitions). Update later the same day: the 19 were fixed natively — the five classes defined directly in standalone tai.py with module functions delegating, four test patch-targets corrected to the real tai.* seams with assertions unchanged — then tai_pkg/ was deleted outright per retoor's call. Suite is 320/320 green (test_tai.py) plus 16/16 (test_seal.py), tui.md focus rules updated (Tab always cycles, auto-focus prefers non-terminals, dialogs restore focus on close), all committed.
Changelog
| Date | Change |
|---|---|
| 2026-10-10 | Initial conclusion recorded. |
| 2026-10-10 | Implementation built, verified, and recorded; research extended to 54 sources with synchronized-output verification. |
| 2026-10-10 | Follow-up: 19 suite failures fixed natively in tai.py, tai_pkg/ deleted, suite 320/320 green; Action taken updated. |