Skip to content

feat(tui): replace custom ratatui TUI with tmux's native UI#26

Merged
jakemassoth merged 1 commit into
mainfrom
tmux-tui
Jul 9, 2026
Merged

feat(tui): replace custom ratatui TUI with tmux's native UI#26
jakemassoth merged 1 commit into
mainfrom
tmux-tui

Conversation

@jakemassoth

Copy link
Copy Markdown
Owner

Implements the spike recommendation (PR #23, branch spike-tui — the decision was already made): drop the custom ratatui/vt100 TUI and adopt tmux's native UI. Line cooks move from one tmux session per ticket to one shared yeschef session with one window per ticket, and yeschef tui collapses to tmux attach.

native tmux brigade tab bar

What changed

Session/window model — the windows are now real. The whole brigade lives in one yeschef tmux session: the head chef at window 0 and one real tmux window per cook, addressed as yeschef:<window>. That's what lets tmux attach render every cook as a native tab.

The tmux status line is the TUI. tmux.conf ships a window-status-format that renders each tab from a per-window @status user option — a glyph + colour, re-rendered live by tmux with no polling and no custom render code:
CHEF ★ magenta · IN_PROGRESS ● yellow · DONE ✓ green · BLOCKED ■ red · NEW ○ grey.
A new backend method set_window_status pushes @status on every ticket … status-set, right next to the existing SQLite write, so an attached tab bar recolours the instant a cook reports. The window name stays the stable ticket id (the send/peek/kill target); status decoration lives only in @status, so they never collide.

The 3 requirements the TUI must satisfy, all now stock tmux:

  1. See / switch cooks — the status-line tab bar; prefix+n/p/<n>, prefix+w tree.
  2. Back to the head chef — window 0, always prefix+0 (plus a prefix+c alias).
  3. At-a-glance status — colour + glyph per tab, driven by @status.

Detach is stock prefix+d.

Backend delta (TmuxBackend)

Method Before (session-per-ticket) After (windows-in-session)
ensure_session no-op new-session -d -s yeschef -n headchef … (head chef = window 0, idempotent)
new_window new-session -d -s yeschef-<w> new-window -t yeschef: -n <w>
send_keys/capture_pane -t yeschef-<w> -t yeschef:<w>
list_windows list sessions, strip prefix list-windows -t yeschef (real active/dead)
kill_window kill-session kill-window -t yeschef:<w>
attach attach -t yeschef-<w> attach -t yeschef (select window if given)
set_window_status new: set-window-option -t yeschef:<w> @status <STATUS>

Deleted

  • src/commands/tui.rs — the entire custom TUI (ratatui event loop, vt100 render_pane, PseudoTerminal, head-chef pinning, its unit tests). run_tui is now ~3 lines in orchestrate.rs.
  • Deps ratatui, vt100, tui-term (and transitive crossterm); Cargo.lock updated.
  • Backend methods that existed only for the custom pane: capture_pane_styled, capture_raw_styled, the shared capture_styled helper, and the bare head-chef-session machinery (ensure_raw_session, attach_raw, names::headchef_session).
  • docs/tui-v2-demo.tape (custom-TUI demo); docs/tui-demo.tape rewritten for the native UI.

Net: −~960 lines + 3 crates, +~40 lines of tmux config + one backend method.

How I verified

  • cargo fmt --check, cargo clippy -D warnings -D clippy::pedantic, cargo test --bin yeschef (52 unit tests), the --ignored e2e suite (17 tests, rewritten to the windows-in-session model on a throwaway socket), and nix flake check — all green.
  • Headless end-to-end run on a throwaway YESCHEF_TMUX_SOCKET: spawned 3 fake cooks, set distinct statuses, and confirmed one yeschef session with head chef window 0 (@status=CHEF) + 3 cook windows carrying the right @status.
  • Recorded docs/tui-demo.gif (VHS, embedded above): the coloured tab bar, switching cooks (prefix+n), jumping to the head chef (prefix+0), a live status flip (db-migrate grey ○ → red ■ while attached), and a clean prefix+d detach.

Notes

  • Less isolation between cooks (one session, shared server lifecycle) — each cook is still its own window/pane/process and kill-window is per-cook. Confirmed acceptable in the spike.
  • Tab bar overflows past ~6–8 cooks (tmux shows </>); prefix+w is the escape hatch — noted in AGENTS.md.
  • Based on the latest origin/main (includes the e2e socket-isolation fix, PR fix(tmux): isolate e2e tests on a throwaway socket, never the live yeschef server #24); no rebase needed.

Move line cooks from one tmux session per ticket to one shared `yeschef`
session with one window per ticket; `yeschef tui` collapses to `tmux attach`.
tmux's status line becomes the brigade tab bar — one colour-coded tab per cook,
driven by a per-window `@status` user option that a `window-status-format`
renders live (no polling, no custom render code). The head chef is window 0
(prefix+0); detach is stock prefix+d.

Backend (TmuxBackend) now maps a ticket "window" onto a real tmux window
(`yeschef:<window>`): ensure_session creates the session with the head chef at
window 0 and is idempotent, new_window/kill_window/list_windows drive real
windows, and a new set_window_status pushes @status on every
`ticket ... status-set`.

Deletes src/commands/tui.rs (the ratatui/vt100 event loop, PseudoTerminal,
head-chef pinning, unit tests) and the ratatui/vt100/tui-term deps, plus the
styled-capture and bare-head-chef-session backend methods that existed only for
the old custom pane. e2e tests are rewritten to the windows-in-session model.

Verified: cargo fmt/clippy/test + the --ignored e2e suite + `nix flake check`
all pass; manually spawned fake cooks on a throwaway socket, attached, and
confirmed the coloured tab bar, window switching, prefix+0, a live status flip
(grey -> red), and a clean detach (demo in docs/tui-demo.gif).
@jakemassoth
jakemassoth merged commit 6225c24 into main Jul 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant