# vtcode-ui
[Root AGENTS.md](../AGENTS.md) | Unified UI: design system, theme registry, TUI framework. Consolidated from `vtcode-design` and `vtcode-theme`.
## Modules
| Area | Path |
|---|---|
| Design system | `design/` — color conversion, style bridging, layout, diff, panel primitives |
| Theme registry | `theme/` — ThemeStyles, runtime state, syntax theme resolution |
| TUI framework | `tui/` — session, widgets, runner, markdown rendering, config |
## Rules
- `design` and `theme` are re-exported at crate root (`pub use design::*; pub use theme::*`) for backward compatibility with the old standalone crates.
- `publish = false` — internal crate, not published to crates.io.
- `tui/core_tui/` owns the full terminal session lifecycle; `session/layout_state.rs` owns rendered hit areas and `session/render_state.rs` owns redraw/full-clear scheduling. `tui/core_tui/app/session/task_panel.rs` retains compact TODO-panel wrapping/height/header helpers; `tui/ui/` has reusable widgets (Markdown, interactive list). Markdown tables use Codex-style padded horizontal separators, width-aware column shrinking, and aligned key/value records when a grid becomes unreadable, so callers must pass content width after transcript framing. Bridge prompts use the bounded deferred-event queue while transient overlays own input; keep them prompt-only so slash-command parsing remains terminal-only.
- `tui/config/constants/` holds TUI-specific defaults — keep them here, not in `vtcode-config`; snapshot tests live in `tui/core_tui/widgets/snapshots/`.
## Gotchas
- Style bridging is centralized in `design/color.rs` + `design/style.rs` (crate-internal); downstream code uses the `tui/core_tui/style.rs` wrappers — do not fork new converters.
- The `crossterm` dependency enables `event-stream` and `osc52` features; do not duplicate these in downstream crates. It is a maintained fork at `patches/crossterm` (root `[patch.crates-io]`) that adds `Event::ColorSchemeReport` for the Contour `CSI ? 997`/`2031` dark-light extension — re-apply that patch when upgrading crossterm, and treat the fork as workspace code for `-D warnings`.
- Standalone and core session defaults are inline; callers that need alternate-screen rendering must opt in explicitly.
- Floating approval/list overlays own mouse input only inside `modal_list_area`; wheel events outside that hitbox must pass through to the transcript so long plan markdown remains scrollable.
- Floating overlays reuse one bottom-half rectangle for popup rendering and transcript clipping; keep `transcript_area` as the source of truth for scroll metrics and hit-testing, and do not pass an explicit transcript rectangle to `apply_view_rows` as if it were the full viewport before rendering (that breaks scroll-anchor restoration). Modal list hit-testing must go through `modal::visible_index_at_row` with the same `footer_hint`/inline-editor inputs render used, or clicks drift by the summary/editor rows. Jump-to-last-change pill render (`widgets/transcript.rs`) and `Session::jump_pill_rect` hit-test must mirror; tracking is bounded (`MAX_TRACKED_TRANSCRIPT_CHANGES`), highlight is sticky until manual scroll/new distinct change/clear.
- `ActivityState` is the authoritative busy/idle signal even without animation. `Blocked` is quiescent despite its status text, so it must not trigger spinners or slash-command blocking; keep Building/Recovery mode boundaries intact. Derive `InputOwner` from overlay/activity state; activity changes update modal restore flags. Transcript cache validity is explicit, not revision zero. Tick sends coalesce, but input and PTY bytes stay ordered.
- The shared active-PTY counter is also a global loading observer; compact PTY rendering may hide live rows, so keep its footer status fallback in `session/state.rs`.
- PTY/tool reflow must preserve explicit status color on the `•` prefix; apply action/tool styling only to the verb so success, failure, and warning remain visually distinct; fullscreen `Ctrl+T` opens ordered session-local Transcript Review (rich/raw via `r`) but remains text transpose outside fullscreen, and complete PTY captures stay behind bounded live lines. Compact review hints are the only normal-transcript open target; derive their label from the primary binding and rebuild their hit regions after transcript reflow.
- Tool and PTY blocks reserve at least one blank line above and below, except directly after agent prose which glues to its work log like a caption (zero gap; the dim section rule separates); shell syntax highlighting is accepted only when it produces distinct token colors, otherwise semantic token styles are the fallback. Diff rendering uses a soft add/delete row tint plus stronger intraline chips across modal preview, markdown, reflow, and ANSI output; full-width tint detection keys off the actual row marker and uncoloured side-by-side divider, never a later `+`/`-`-prefixed word chip; file and hunk section headers are bold foreground-only metadata, ANSI16 and no-color remain foreground-only, responsive diff layout measures post-frame content width, hides the visible gutter when source room is tight, and falls back from side-by-side below the shared threshold; overlays cache one `vtcode_diff::DiffDocument` at open time and only relayout or scroll it. Inline TUI diff bodies word-wrap via transcript reflow (no ellipsis under the safety cap); expandable `review full diff` + `DiffReviewAnchor` only when the complete unified body is still held (never registry excerpts); compact tinted rows hang under the marker cell; activation takes the matching anchor without cloning unified content; the review overlay stays full-viewport, paints and counts remaining rows from one `layout_display_lines` pass, and does not claim full-diff expand from truncated previews.
- Task-panel tree rows use the shared hanging-prefix wrapper in `session/text_utils.rs`; keep panel row heights derived from wrapped content so long titles wrap cleanly in the docked panel (compact transcript shows header plus the accented current `▶` row, expanded shows header+truncated tree; both surfaces style status through text only — done struck-through/italic/dimmed, current bold `primary`, blocked `warning` — via the shared style bridge, never forked converters). `toggle_tool_display_mode` is a rebindable session action (default `Alt+T`); dispatch it before the legacy `Alt+T` text-edit shortcut and invalidate transcript caches after toggling. Info/Warning/Error transcript groups (plain colored lines, no borders) must invalidate from their first line when a member changes or is appended, because later lines affect the cached group head; each Info tool-summary line is a boundary, not part of the group. Error uses the `error` token, Warning uses the dedicated amber `warning` token (scheme-picked bright/dark amber, never the brand `logo_accent`), Info uses dimmed foreground.
- Panic-hook terminal mutation tracking is set only after a successful terminal mutation, so partial TUI setup errors do not emit restore sequences. Alternate-screen teardown clears the alternate viewport before leaving it, and render/finalize writes must use the shared terminal-operation lock so no final frame reaches the main scrollback after restoration is claimed. Reasoning summaries use the dimmed italic style and arrive only through the provider-classified normalized stream; raw or continuation-only reasoning stays hidden.