This document describes VTCode's terminal user interface system, which provides an interactive TUI for agent sessions with real-time markdown rendering, syntax highlighting, and modal overlays. The UI architecture supports both streaming stdout output and an inline session manager with scrollback history.
For information about the Agent Execution flow that drives UI updates, see Agent Execution. For configuration of UI behavior and appearance, see Configuration.
VTCode's UI system operates in two modes: direct terminal output via ANSI escape codes, and inline TUI sessions with interactive components. The architecture separates rendering concerns from event processing and modal management.
Sources: vtcode-core/src/utils/ansi.rs1-1400 src/agent/runloop/unified/inline_events/context.rs1-156 src/agent/runloop/unified/inline_events/driver.rs1-196
The AnsiRenderer struct provides a unified interface for writing styled terminal output, automatically adapting to either direct stdout or TUI session mode. This abstraction allows the same rendering code to work in both CLI-only and interactive TUI environments.
| Mode | When Active | Output Destination |
|---|---|---|
| Stdout | No TUI session active | Direct terminal via AutoStream<io::Stdout> |
| TUI | InlineHandle provided | Forwarded to InlineSink → InlineSession |
Key Methods:
| Method | Purpose |
|---|---|
stdout() | Create renderer for direct terminal output |
with_inline_ui(handle, config) | Create renderer forwarding to TUI session |
line(style, text) | Write a styled line with newline |
render_markdown(style, text) | Parse and render markdown with syntax highlighting |
stream_markdown_response(text, prev_lines) | Incrementally update streaming markdown (TUI only) |
show_list_modal(...) | Display interactive list picker |
show_secure_prompt_modal(...) | Display password/secret input modal |
Sources: vtcode-core/src/utils/ansi.rs24-709
The MessageStyle enum categorizes output by logical role, determining indentation, color, and whether content should be logged to the transcript:
Each style has an associated indent(), style() (ANSI color), and message_kind() (for TUI classification).
Sources: vtcode-core/src/utils/ansi.rs104-150 vtcode-core/src/utils/message_style.rs1-100
AnsiRenderer detects terminal capabilities at initialization through AnsiCapabilities:
The renderer respects the color output policy configured via CLI args or config. When NO_COLOR=1, all ANSI styling is stripped.
Sources: vtcode-core/src/utils/ansi.rs39-77 vtcode-core/src/utils/ansi_capabilities.rs1-200
The inline TUI system is implemented in the vtcode-tui crate and re-exported through vtcode-core::ui::tui. A TUI session provides scrollback history, interactive modals, and event-driven input processing.
Sources: src/agent/runloop/unified/inline_events/driver.rs70-131
The InlineHandle provides methods for the agent to send UI commands to the TUI session:
| Method | Purpose |
|---|---|
append_line(kind, segments) | Add a line to the transcript |
append_pasted_message(kind, text, lines) | Add multi-line paste as collapsed placeholder |
show_list_modal(title, items, config) | Display file picker, history, etc. |
show_secure_prompt(title, prompt) | Display secure input modal |
close_modal() | Dismiss active modal |
clear_screen() | Clear transcript history |
set_placeholder(text) | Update input placeholder text |
Sources: vtcode-core/src/ui/tui.rs1-5 vtcode-tui/src/lib.rs100-300
The event loop polls for user input and processes it through specialized processors:
Sources: src/agent/runloop/unified/inline_events/driver.rs70-131 src/agent/runloop/unified/inline_events/context.rs59-155
VTCode renders markdown content with full syntax highlighting for code blocks, diff rendering, and inline formatting. The rendering system is implemented in vtcode-tui and exposed through a facade in vtcode-core.
Sources: vtcode-core/src/ui/markdown.rs50-88 vtcode-tui/src/ui/markdown/renderer.rs1-500
Rendered markdown is represented as a vector of MarkdownLine, each containing styled segments:
The AnsiRenderer consumes these lines and either writes them directly to stdout or forwards them to the TUI session via InlineSink.
Sources: vtcode-core/src/ui/markdown.rs1-99
Syntax highlighting is controlled by SyntaxHighlightingConfig:
| Field | Purpose |
|---|---|
enabled | Enable/disable syntax highlighting |
theme | Theme name (e.g., "base16-ocean.dark") |
cache_themes | Cache parsed themes for performance |
max_file_size_mb | Skip highlighting for large files |
enabled_languages | Filter languages (empty = all) |
highlight_timeout_ms | Timeout per code block |
Sources: vtcode-core/src/config/loader.rs400-450
When markdown contains diff-style output, the renderer applies special styling:
The `highlight_line_for_diff` function detects `+`, `-`, and `@@ ` prefixes and applies appropriate colors from `ThemeStyles`.
**Sources:** <FileRef file-url="https://github.com/vinhnx/vtcode/blob/80efcd43/vtcode-core/src/ui/markdown.rs#L11-L14" min=11 max=14 file-path="vtcode-core/src/ui/markdown.rs">Hii</FileRef> <FileRef file-url="https://github.com/vinhnx/vtcode/blob/80efcd43/vtcode-tui/src/ui/markdown/diff.rs#L1-L150" min=1 max=150 file-path="vtcode-tui/src/ui/markdown/diff.rs">Hii</FileRef>
---
## Interactive Components
The TUI system provides several interactive modal types for user input and selection. Modals are managed through `InlineModalProcessor` and displayed via `InlineHandle`.
### Modal Types
| Modal Type | Use Cases | Key Features |
|-----------|-----------|--------------|
| **List Modal** | File picker, history, skills, MCP providers | Search, preview, multi-select |
| **Secure Prompt** | API keys, passwords | Hidden input characters |
| **Overlay** | Command palette, settings manager | Filterable, hierarchical |
| **Wizard** | Multi-step setup flows | Progressive disclosure |
### List Modal Architecture
```mermaid
graph TB
Trigger["Slash command or hotkey"] --> Show["show_list_modal(title, items, config)"]
Show --> Render["Display modal overlay<br/>with search box"]
Render --> Event["User interaction:<br/>- Type to search<br/>- Arrow keys navigate<br/>- Enter selects<br/>- Esc cancels"]
Event --> Preview["SelectionChanged event"]
Preview --> Handler["InlineModalProcessor::handle_preview"]
Handler --> Update["Update preview pane"]
Event --> Submit["Submitted(Selection) event"]
Submit --> Process["InlineModalProcessor::handle_submit"]
Process --> Action["Return InlineLoopAction"]
Action --> Close["close_modal()"]
Sources: vtcode-core/src/utils/ansi.rs195-223 src/agent/runloop/unified/inline_events/modal.rs1-300
The file picker modal is used for selecting files to read or attach to prompts:
When a file is selected, the event handler can either:
handle_preview)handle_submit)Sources: vtcode-tui/src/types.rs50-100
The history picker displays previous session archives with search/filter:
Sources: src/agent/runloop/unified/palettes/history_picker.rs1-200
Slash commands provide a structured way for users to interact with the agent runtime. Commands are parsed by InlineInputProcessor and dispatched to command handlers.
| Slash Command | Handler | Description |
|---|---|---|
/share-log | share_log_handler | Upload transcript to paste service |
/resume | resume_session_handler | Open history picker |
/schema | schema_handler | Display tool schemas |
/model | model_picker_handler | Switch LLM model |
/plan | plan_mode_handler | Toggle plan mode (HITL) |
/mcp | mcp_manager_handler | Manage MCP providers |
/skills | skills_manager_handler | View/load skills |
/settings | settings_handler | Interactive settings editor |
/edit | editor_handler | Launch external editor |
/mode | mode_handler | Cycle editing modes |
Sources: src/agent/runloop/unified/slash_commands.rs1-500
Sources: src/agent/runloop/unified/inline_events/input.rs1-150
The InlineQueueState manages a queue of pending submissions, allowing users to type multiple messages before the agent processes them:
When the user presses Ctrl+Q or uses QueueSubmit, the input is added to the queue but the prompt is immediately available for the next message.
Sources: src/agent/runloop/unified/inline_events/queue.rs1-80
The UI provides rich feedback during tool execution, including pre-execution indicators, streaming progress, and post-execution summaries.
Before tool execution, a summary is rendered to the transcript:
• Ran cargo build --release
└ $ cargo build --release
• Edit file src/main.rs
└ old_str: "fn main()"
└ new_str: "async fn main()"
Pre-execution Indicator for file modifications:
❋ Writing src/main.rs...
Sources: src/agent/runloop/unified/tool_summary.rs23-258
| Component | Purpose |
|---|---|
describe_tool_action | Generate human-readable headline |
humanize_tool_name | Convert tool names to display labels |
render_tool_call_summary | Render styled summary with highlights |
render_file_operation_indicator | Show pre-execution file write indicator |
stream_label_from_output | Determine output type (stdout, stderr, error) |
Sources: src/agent/runloop/unified/tool_summary.rs137-470
While the LLM is generating a response, the UI may display:
Sources: vtcode-core/src/utils/spinner.rs1-100 src/agent/runloop/status_line.rs1-200
Tool execution may require user approval through interactive prompts. These are managed by prompt_tool_permission in the tool routing layer.
Sources: src/agent/runloop/unified/tool_routing.rs316-677
The approval prompt displays:
cargo build")For shell commands, additional context is shown:
Sources: src/agent/runloop/unified/tool_routing.rs506-525 src/agent/runloop/unified/tool_routing/permission_prompt.rs1-400
Users can approve shell command prefixes to avoid repeated prompts:
If accepted, the approval is persisted to vtcode.toml:
Sources: src/agent/runloop/unified/tool_routing.rs258-293
The status line appears at the bottom of the TUI session, displaying:
| Mode | Displayed Elements |
|---|---|
minimal | Model, turn count |
default | Model, turn count, tokens, permissions |
verbose | All above + reasoning effort, cache hit rate, session ID |
Sources: vtcode-core/src/config/types.rs300-350
All rendered output is recorded to an in-memory transcript via vtcode_core::utils::transcript::append. This transcript can be:
/share-log command (uploads to paste service)Diagnostic errors (MessageStyle::Error, MessageStyle::ToolError) are logged to the tracing infrastructure with target vtcode_transcript but may be suppressed from the visible transcript in TUI mode based on show_diagnostics_in_transcript setting.
Sources: vtcode-core/src/utils/ansi.rs152-160 vtcode-core/src/utils/transcript.rs1-100
Refresh this wiki
This wiki was recently refreshed. Please wait 1 day to refresh again.