Developer guide for the Bun + React + Silvery TUI in workbench-ui/.
- Bun >= 1.3
tmux- Ghostty for the supported terminal target
- At least one of
codex,cursor-agent,claude,gemini, oropencode
Optional viewer commands:
ffmpegandffprobefor videommdcfor Mermaid diagramspdftoppmandpdfinfofor PDFs
cd workbench-ui
bun install
bun run startTo exercise the real launcher from the repository root:
./bin/workbench-cli .
./bin/workbench-cli . --harness codex
./bin/workbench-cli . --hotThe short installed alias is work. Hot mode notices when the current
directory is inside a development checkout and runs that checkout when its
dependencies are installed. WORKBENCH_CLI_HOT_ROOT overrides detection.
Run from workbench-ui/:
bun run typecheck
bun test
bun run check
bun run screenshot
bun run test:terminalUse the checks proportionally:
typecheck,test, andcheckare the normal baseline.screenshotdrives the real Workbench through a browser PTY and checks UI interaction, layout, selection, previews, and resizing.test:terminalis required for changes to PTY output, cursor handling, resizing, scrolling, tmux, terminal ownership, or frame rendering. It runs alternate-screen, inline, Codex-style, sticky-transcript, and real-shell scenarios with fragmented ANSI input.
The deterministic terminal scenarios also resize a populated composer through 80×24, 147×74, 319×82, and 120×40, then type, backspace, and paste multiline text. They check the complete reference frame, unique composer rows, and cursor position after edits, and send input immediately after a rapid resize burst. These checks run for both alternate-screen and inline fixtures, including Codex.
The mouse regression answers terminal pixel-geometry probes and uses real
browser pointer events to click Help, New Workspace, tabs, and the agent pane
after zooming in/out and resizing. Run it separately with
bun scripts/terminal-regression.ts --mouse-only --codex --chunk-seed=1.
Workbench requests cell-coordinate SGR reports so the host terminal converts
clicks using its current font size, including zoom without a grid resize.
The screenshot runner chooses the package root as its fixture workspace and
writes ignored artifacts to artifacts/screenshots/.
work [path] [--harness <id>] [--hot] [--terminal-trace] [--splash]- The path defaults to the current directory.
- Missing paths entered through New Workspace are created recursively.
--harnessand--agentacceptcodex,cursor,claude,gemini, oropencode.- Automatic selection tries Codex, Cursor, then Claude Code, and falls back to Codex.
--hot,--dev, and--watchare aliases for serialized process restart.--terminal-traceenables metadata-only terminal diagnostics.--splashholds the startup artwork until the first key press or click.
Re-selecting the currently active harness is an intentional restart: Workbench
kills that harness's private tmux session and recreates it in the same tab.
The ↻ control in the harness header performs the same action.
Workbench state lives at ~/.workbench/workbench-ui-state.json. Harnesses and
terminals use named sessions on the private socket
~/.workbench/tmux-ui.sock.
- Shutdown and hot reload save state and detach panes.
- Closing a harness, terminal, or workspace kills its backing tmux session.
- Every workspace, harness, and terminal has a stable persisted ID.
- Each workspace starts with one harness and one shell terminal.
- Shell terminals are normalized to their owning workspace cwd when restored.
Never use or clean the user's default tmux server as part of Workbench tests.
scripts/hot-runner.ts owns hot reload. It sends SIGTERM, waits for Workbench
to persist state, detach panes, and restore the host terminal, and only then
starts the replacement process.
Do not replace this with Bun's native --watch or in-process hot replacement.
Those paths can skip shutdown and leave multiple renderers or PTY owners
fighting over the same session.
Start with the deterministic regression suite. Do not apply speculative fixes for terminal corruption while it is green.
If the fixture cannot reproduce a live issue:
work --terminal-traceThe default trace is ~/.workbench/terminal-trace.ndjson. Set
WORKBENCH_TERMINAL_TRACE=/path/to/file.ndjson for another location. Traces
contain dimensions, buffer positions, opaque row identifiers, byte counts, and
control-sequence counts—not terminal text or raw ANSI.
| Variable | Purpose |
|---|---|
WORKBENCH_UI_CWD |
Starting workspace |
WORKBENCH_UI_HARNESS_ID / WORKBENCH_UI_AGENT_ID |
Initial harness |
WORKBENCH_UI_THEME |
Initial theme |
WORKBENCH_UI_IMAGE_PROTOCOL |
kitty, sixel, or halfblock |
WORKBENCH_UI_CELL_ASPECT |
Image cell-aspect override |
WORKBENCH_UI_PRESERVE_DIM=1 |
Preserve SGR dim |
WORKBENCH_CLI_HOT=1 |
Enable hot mode |
WORKBENCH_CLI_HOT_ROOT |
Explicit development checkout |
WORKBENCH_TERMINAL_TRACE |
1 or a custom trace path |
WORKBENCH_SCREENSHOT_QUERY |
Browser fixture options |
src/index.ts startup and terminal capability probing
src/app/ controller, lifecycle, persistence scheduling
src/state/ state creation, restoration, harness definitions
src/terminal/ PTY/tmux/xterm, scrolling, cursor, trace/probe
src/components/ shell, tabs, sidebars, dialogs, viewers
src/components/viewers/ Markdown, text, image, PDF, and video surfaces
src/media/ decoding and terminal image protocols
src/text/ file tree, syntax, diffs, editor data
src/ui/ theme, pane sizing, toasts
scripts/ hot runner, screenshots, terminal regression
test-harness/ deterministic coding-agent and browser fixtures
The three central files are app/WorkbenchApp.tsx,
components/Workbench.tsx, and terminal/terminal-panel.ts. Consult the root
AGENT.md before changing their lifecycle, input, or rendering
contracts.
Releases are tag-driven. After the version in package.json is bumped and the
commit is on main, push a v<version> tag. .github/workflows/release.yml
runs typechecking/tests, packages the source tarball, and creates the GitHub
Release.