# The Terminal Workbench

> The TUI is the steering wheel: it renders the current work, accepts keys and slash-style choices, starts agent runs, and keeps the screen usable while work is live.

- Repository: browser-use/terminal
- GitHub: https://github.com/browser-use/terminal
- Human wiki: https://grok-wiki.com/public/wiki/browser-use-terminal-686510dbe50c
- Complete Markdown: https://grok-wiki.com/public/wiki/browser-use-terminal-686510dbe50c/llms-full.txt

## Source Files

- `crates/browser-use-tui/src/main.rs`
- `crates/browser-use-tui/src/runtime.rs`
- `crates/browser-use-tui/src/render.rs`
- `crates/browser-use-tui/src/composer.rs`
- `crates/browser-use-tui/src/settings.rs`
- `crates/browser-use-tui/src/palette.rs`
- `crates/browser-use-tui/src/transcript.rs`
- `docs/terminal-ui-product-ux.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [crates/browser-use-tui/src/main.rs](crates/browser-use-tui/src/main.rs)
- [crates/browser-use-tui/src/runtime.rs](crates/browser-use-tui/src/runtime.rs)
- [crates/browser-use-tui/src/render.rs](crates/browser-use-tui/src/render.rs)
- [crates/browser-use-tui/src/composer.rs](crates/browser-use-tui/src/composer.rs)
- [crates/browser-use-tui/src/settings.rs](crates/browser-use-tui/src/settings.rs)
- [crates/browser-use-tui/src/palette.rs](crates/browser-use-tui/src/palette.rs)
- [crates/browser-use-tui/src/transcript.rs](crates/browser-use-tui/src/transcript.rs)
- [docs/terminal-ui-product-ux.md](docs/terminal-ui-product-ux.md)
- [docs/terminal-ui-testing.md](docs/terminal-ui-testing.md)
</details>

# The Terminal Workbench

The Terminal Workbench is the Rust TUI for Browser Use Terminal. It is the place where a user sets up an account, picks a model and browser backend, types a task, watches the live work, steers the task with follow-ups, opens history, and fixes setup when something is missing.

A simple way to read the code is: `main.rs` is the driver, `render.rs` is the dashboard, `composer.rs` is the text input, `palette.rs` is the slash menu, `transcript.rs` turns raw events into readable work, and `runtime.rs` starts the agent run with the selected model and browser settings.

Sources: [crates/browser-use-tui/src/main.rs:56-78](), [docs/terminal-ui-product-ux.md:53-80]()

## Product Model

The product document says there is one main screen: the workbench. Setup, browser, history, and action surfaces are temporary helpers around that main screen. The intended user flow is small: set up once, tell the browser what to do, watch enough to trust it, interrupt or steer when needed, then get a useful result.

The code follows that idea with a `Surface` enum for the main screen, setup screens, account/API key flows, model and browser screens, history, and developer tools. It also has a smaller `ProductState` enum for the user-facing lifecycle: setup needed, ready, running, result, failed, or cancelled.

```rust
// crates/browser-use-tui/src/main.rs
enum ProductState {
    SetupNeeded,
    Ready,
    Running,
    Result,
    Failed,
    Cancelled,
}
```

Sources: [docs/terminal-ui-product-ux.md:53-80](), [crates/browser-use-tui/src/main.rs:116-163](), [crates/browser-use-tui/src/main.rs:190-198]()

## Main Ownership Boundaries

```text
User keys / paste
      |
      v
main.rs
  - App state
  - key routing
  - command dispatch
  - setup/auth/model/browser choices
      |
      +--> composer.rs       edits prompt text
      +--> palette.rs        filters slash commands
      +--> render.rs         draws main view, popups, composer, footer
      +--> transcript.rs     converts event records into readable lines
      +--> runtime.rs        starts the selected agent/browser run
      +--> Store             sessions, settings, events
```

The workbench is not just a screen renderer. It owns the loop that reads terminal events, drains store and auth notifications, starts worker threads, and redraws when the state changes. Rendering is deliberately separate: `render(frame, app)` asks the app for a projected `WorkbenchState`, decides the product state, then draws either setup, the main view, or a modal overlay.

Sources: [crates/browser-use-tui/src/main.rs:679-777](), [crates/browser-use-tui/src/main.rs:3085-3170](), [crates/browser-use-tui/src/render.rs:93-129]()

## State Comes From the Store

The TUI keeps a local cache of sessions and event records. `AppStateCache::hydrate` loads sessions and events from `Store`; later notifications refresh sessions or only the events after the last seen sequence number. When the view needs data, `project_if_needed` calls `project_workbench` to create the current `WorkbenchState`.

This matters because the terminal does not poll the agent directly for every display detail. It watches durable session events and settings, then projects them into a screen model.

Sources: [crates/browser-use-tui/src/main.rs:324-360](), [crates/browser-use-tui/src/main.rs:362-377](), [crates/browser-use-tui/src/main.rs:445-463](), [crates/browser-use-tui/src/main.rs:484-557]()

## Starting and Steering Work

When the user presses Enter on a new task, `submit` checks readiness, takes the trimmed composer text, and dispatches `StartTask`. That command creates a session, appends a `session.input` event, selects the session, resets native history, and starts an agent thread. Follow-ups use the same composer but append `session.followup`; if the selected session is no longer active, the TUI can restart the agent for that session.

```rust
// crates/browser-use-tui/src/main.rs
AppCommand::StartTask(text) => {
    let session = self.store.create_session(None, std::env::current_dir()?)?;
    self.store.append_event(
        &session.id,
        "session.input",
        serde_json::json!({ "text": text }),
    )?;
    self.selected_session_id = Some(session.id.clone());
    self.native_history.reset_with_clear();
    self.start_agent_for_session(session.id)?;
}
```

The agent is started on a named background thread. The thread calls `run_agent_thread`; panics are caught so they can be recorded instead of silently breaking the terminal loop.

Sources: [crates/browser-use-tui/src/main.rs:913-963](), [crates/browser-use-tui/src/main.rs:980-1012](), [crates/browser-use-tui/src/main.rs:1052-1083]()

## Runtime and Provider Neutrality

The workbench is BYOC/BYOK-friendly because account, model, backend, browser, and API keys are choices stored as settings, not hardcoded assumptions. `settings.rs` maps `AgentBackend` values to core provider backends and defines account choices for Codex login, OpenAI API key, Anthropic API key, and OpenRouter API key. Browser choices are also explicit: Local Chrome, Browser Use cloud, and Headless Chromium.

`runtime.rs` turns the selected browser into `AgentRunOptions`: local mode for Local Chrome, managed headless mode for Headless Chromium, and cloud mode for Browser Use cloud. Cloud mode requires a `BROWSER_USE_API_KEY` from either the store setting or the environment. This keeps the workbench portable across local credentials, user-provided keys, and different model providers.

Sources: [crates/browser-use-tui/src/settings.rs:4-50](), [crates/browser-use-tui/src/settings.rs:60-89](), [crates/browser-use-tui/src/settings.rs:89-153](), [crates/browser-use-tui/src/runtime.rs:12-60](), [crates/browser-use-tui/src/runtime.rs:62-102]()

## Composer: The Steering Input

The composer owns prompt text, cursor position, wrapping, paste insertion, and editing keys. Empty composer lines render a placeholder; non-empty composer lines use a `> ` prompt prefix. The renderer chooses the placeholder by session state: active sessions say "Type to steer the agent...", completed sessions say "Ask a follow-up...", and no session says "Tell the browser what to do...".

The composer supports normal typing, Enter submission handled by `main.rs`, Shift/Alt/Meta Enter as newline paths, cursor movement, word deletion, line deletion, paste normalization, and wrapped cursor placement. This is why the workbench can feel like a small text editor without making the rest of the app handle text mechanics.

Sources: [crates/browser-use-tui/src/composer.rs:6-41](), [crates/browser-use-tui/src/composer.rs:70-123](), [crates/browser-use-tui/src/composer.rs:125-207](), [crates/browser-use-tui/src/render.rs:1242-1285]()

## Slash Palette and Temporary Surfaces

A leading `/` opens the slash command palette only when the main composer is empty. While the palette is open, typed characters update `palette_filter` instead of the composer; Backspace edits the filter; Enter executes the selected action; Esc closes the palette. The command list is intentionally small: task, history, browser, model, auth, and update.

The palette and other temporary surfaces render as centered modal overlays when possible. Text-input popups like API key and telemetry own their input cursor, so the composer underneath is cleared or hidden to avoid duplicated typing.

Sources: [crates/browser-use-tui/src/main.rs:1294-1336](), [crates/browser-use-tui/src/main.rs:2168-2226](), [crates/browser-use-tui/src/palette.rs:1-70](), [crates/browser-use-tui/src/render.rs:458-491](), [crates/browser-use-tui/src/render.rs:686-826]()

## Rendering the Workbench

`render_main` splits the terminal into a body, bottom area, and optional footer. The body changes by product state: setup lines, ready lines, or work lines. The bottom area is either a temporary bottom pane or the composer. Running, result, failed, and cancelled states pin the important work near the composer so new activity grows toward the input rather than drifting away.

The composer is a bordered input box with the selected browser punched into the bottom border. Beneath it, a status row shows the model, context bar, and session cost when usage events include cost data. The context bar is based on `model.usage` events and a 60k-token display budget.

Sources: [crates/browser-use-tui/src/render.rs:151-295](), [crates/browser-use-tui/src/render.rs:297-367](), [crates/browser-use-tui/src/render.rs:979-1114](), [crates/browser-use-tui/src/render.rs:1152-1227]()

## Transcript: Turning Events Into Human Work

The raw event log is not shown directly. `transcript.rs` maps event types into transcript nodes: prompts, assistant markdown, result files, grouped timeline rows, browser activity, errors, cancelled state, and live status. It hides many low-level model and tool events, merges repeated timeline groups, compacts repeated file reads, and keeps transient thinking out of terminal scrollback.

For a newcomer, this is the difference between "the engine made many internal noises" and "the dashboard says it read files, ran a command, opened a page, or produced a result."

Sources: [crates/browser-use-tui/src/transcript.rs:20-92](), [crates/browser-use-tui/src/transcript.rs:368-443](), [crates/browser-use-tui/src/transcript.rs:591-715](), [crates/browser-use-tui/src/transcript.rs:760-910](), [crates/browser-use-tui/src/transcript.rs:913-981]()

## Keyboard Behavior

| Input | Behavior |
| --- | --- |
| `Enter` | Run a new task, send a follow-up, or execute the selected surface row |
| `Tab` | Open history |
| `F2` | Open browser surface |
| `/` | Open slash palette when the main composer is empty |
| `Esc` | Close overlays; on main with an active task, press again quickly to stop |
| `Ctrl+C` | Clear input, stop current task, or require a second press to quit |
| `Ctrl+Q` | Quit immediately |

The product UX document asks for a tiny keyboard model, and the implementation mostly keeps that model centralized in `App::handle_key`.

Sources: [docs/terminal-ui-product-ux.md:485-498](), [crates/browser-use-tui/src/main.rs:1125-1200](), [crates/browser-use-tui/src/main.rs:1215-1291]()

## Setup, Auth, Model, and Browser Choices

Setup is an activation and repair flow, not a permanent dashboard. First-run setup appears when setup is incomplete, no session is selected, and the composer is empty. Model selection persists the display model, provider model, account, and backend. Browser selection persists the browser and, for Browser Use cloud, starts API key entry if the key is missing.

This is also where provider-neutral design shows up in the UI: users choose an account path and a model, while the provider/backend mapping stays in settings and runtime code.

Sources: [crates/browser-use-tui/src/main.rs:1369-1374](), [crates/browser-use-tui/src/main.rs:1474-1508](), [crates/browser-use-tui/src/main.rs:1733-1772](), [crates/browser-use-tui/src/main.rs:1774-1798](), [crates/browser-use-tui/src/main.rs:2135-2143]()

## Terminal Safety and Live Redraws

The TUI runs in raw mode, enables bracketed paste, requests enhanced keyboard reporting, and uses an inline Ratatui viewport. Its loop drains store/auth notifications, refreshes from the store as a fallback, debounces resize events, animates the welcome view, animates live status, polls for terminal events, and redraws only when needed.

Terminal UI correctness is treated as more than compilation. The repository testing guide requires real terminal or tmux checks for keyboard behavior, visible text, missing escape leaks, paste markers, resize behavior, and clean selectable output.

Sources: [crates/browser-use-tui/src/main.rs:3085-3177](), [crates/browser-use-tui/src/main.rs:3180-3261](), [crates/browser-use-tui/src/main.rs:3474-3559](), [docs/terminal-ui-testing.md:1-33]()

## Why This Page Matters

The workbench is the part of the repo where product shape and runtime architecture meet. It must stay simple for users, but it cannot be vendor-specific or brittle: the code keeps account/model/browser choices explicit, stores durable events, renders from projected state, and starts the selected backend through runtime configuration. That is the key pattern to preserve when adding new providers, browser modes, setup paths, or workbench actions.

Sources: [crates/browser-use-tui/src/settings.rs:52-153](), [crates/browser-use-tui/src/runtime.rs:43-50](), [crates/browser-use-tui/src/main.rs:980-1049]()
