components — Terminal UI Component Library
Terminal UI Component Library
389 React (Ink) components powering the terminal UI. From low-level design system primitives to complex interactive dialogs. Includes PromptInput, Messages rendering, StructuredDiff viewer, model/theme pickers, MCP server dialogs, agent status views, and full settings panels.
🔵 Ink (React Terminal) 🟢 Design System 🟡 Virtual Scrolling 🔴 Accessibility
🏢 Think of It Like a Building
If Claude Code were a building, the components module is every room and piece of furniture inside it. The design-system primitives (Dialog, Tabs, Pane, ThemedText) are the standardized building materials—walls, doors, and light switches that every room shares. PromptInput is the front desk where visitors (users) speak. Messages is the main hall displaying the conversation. FullscreenLayout is the blueprint that decides where the hall, front desk, and side rooms go. Specialized rooms like permissions, mcp, and Settings are offices where specific business gets handled. And VirtualMessageList is the clever concierge who only turns on the lights in rooms that guests can actually see, keeping energy costs (render cycles) low.
Architecture
Component hierarchy from App.tsx root through layout, messages, and input layers:
flowchart TD App["App.tsx\n(Root Providers)"] App --> FPS["FpsMetricsProvider"] App --> Stats["StatsProvider"] App --> AppState["AppStateProvider"] AppState --> FSLayout["FullscreenLayout\n(ScrollBox + Sticky Header)"] FSLayout --> Logo["LogoV2/\n(Header + Status)"] FSLayout --> MsgList["VirtualMessageList\n(Virtual Scroll)"] FSLayout --> Prompt["PromptInput/\n(REPL Input)"] FSLayout --> ModalOverlay["Modal Overlay"] MsgList --> Messages["Messages.tsx\n(Normalize + Group)"] Messages --> Message["Message.tsx\n(Route by Type)"] Message --> UserText["UserTextMessage"] Message --> AssistantText["AssistantTextMessage"] Message --> ToolUse["ToolUseMessage\n+ Progress"] Message --> Thinking["ThinkingMessage"] Message --> System["SystemMessage"] ModalOverlay --> Dialog["design-system/Dialog"] ModalOverlay --> FuzzyPicker["design-system/FuzzyPicker"] ModalOverlay --> PermReq["permissions/\nPermissionRequest"] ModalOverlay --> SlashCmd["Slash Command Pane"] Prompt --> TextInput["TextInput\n(Cursor + Vim)"] Prompt --> Typeahead["Typeahead\n(@-mentions)"] Prompt --> HistSearch["History Search\n(Ctrl+R)"]
Data Flow
Sequence of events when the user types a message and sees the response rendered:
sequenceDiagram participant User as User (Keyboard) participant TI as TextInput participant PI as PromptInput participant REPL as REPL Engine participant API as Claude API participant Msgs as Messages.tsx participant VML as VirtualMessageList participant Ink as Ink Renderer User->>TI: Keystroke events TI->>PI: onChange(buffer) PI->>PI: Typeahead / History hooks User->>PI: Enter (submit) PI->>REPL: onSubmit(userMessage) REPL->>API: Stream request API-->>REPL: Token chunks REPL->>Msgs: Append message blocks Msgs->>Msgs: Normalize, collapse, group Msgs->>VML: Updated message list VML->>VML: Binary search visible range VML->>Ink: Render visible messages only Ink->>User: Terminal output (blit-optimized)
🚶 Step-by-Step Narrated Walkthrough
Follow a single user message from typing to rendered response as it flows through the components pipeline:
- Keystroke Capture — The user presses a key. Ink’s
useInputhook fires insideTextInput.tsx, updating the internal buffer and cursor position. In vim mode the keystroke is routed through the vim state machine first. - PromptInput Processing —
PromptInput.tsx(2,338 lines) receives the updated buffer. If the character is@, the typeahead menu opens viauseTypeahead. Arrow keys triggeruseArrowKeyHistoryto cycle through past inputs. - Suggestion Overlay —
usePromptSuggestionqueries model-powered completions. If matches exist, aFuzzyPickeroverlay appears in the modal slot, navigable with arrow keys. - Submit — User presses Enter.
PromptInputcallsonSubmit(userMessage), passing the text to the REPL engine. The input buffer is cleared and the prompt resets. - API Streaming — The REPL engine sends the message to the Claude API. Token chunks arrive via streaming. Each chunk is appended to the in-progress
AssistantMessagein state. - Message Normalization —
Messages.tsx(833 lines) receives the updated message array. It collapses consecutive same-type blocks, groups tool_use/tool_result pairs, and filters for brief mode if active. - Lookup Tables —
buildMessageLookups()creates indices: tool name → block, UUID → message index, in-progress tool IDs. These are passed as props to avoid re-computation in children. - Virtual Scroll Calculation —
VirtualMessageListuses cached heights (WeakMap) and cumulative sums. A binary search finds the first visible message index given the currentscrollTop. Only messages in the viewport plus a small buffer are rendered. - Message Routing — Each visible message hits
Message.tsx(626 lines), which routes by type:UserTextMessage,AssistantTextMessage,ToolUseMessage(with spinner whileshouldAnimate=true), orThinkingMessage. - Terminal Render — Ink compiles the React tree to terminal escape sequences. Blit caching ensures only changed cells are redrawn. Memoized subtrees (LogoHeader, completed Messages) produce zero writes, keeping the frame budget under 16ms even for 2,800-message transcripts.
Key Components
| Component | File | Lines | Role |
|---|---|---|---|
App | App.tsx | ~55 | Root provider wrapper: FpsMetrics, Stats, AppState contexts |
FullscreenLayout | FullscreenLayout.tsx | ~350 | Layout manager: scrollable area, sticky header, modal overlay, floating companion |
Messages | Messages.tsx | 833 | Memoized transcript renderer: normalizes, collapses, groups, filters messages |
Message | Message.tsx | 626 | Single message router: dispatches to UserText, AssistantText, ToolUse, etc. |
VirtualMessageList | VirtualMessageList.tsx | ~500 | Virtual scroll: binary search visible range, height caching, search indexing |
PromptInput | PromptInput/PromptInput.tsx | 2,338 | Primary REPL input: vim/normal/voice modes, history, typeahead, buddy notifications |
TextInput | TextInput.tsx | ~280 | Base text field: cursor rendering, voice waveform, animation frame support |
Dialog | design-system/Dialog.tsx | ~150 | Modal dialog: title/subtitle, Esc to cancel, Enter to confirm keybindings |
Tabs | design-system/Tabs.tsx | ~200 | Tabbed interface: keyboard nav, fixed content height, tab index tracking |
FuzzyPicker | design-system/FuzzyPicker.tsx | ~300 | Search dropdown: async filtering, cursor nav, preview pane, multi-action |
ThemeProvider | design-system/ThemeProvider.tsx | ~120 | Theme context: auto/dark/light modes, live OSC 11 terminal watching |
ThemedText | design-system/ThemedText.tsx | ~60 | Color-aware text: theme color lookup, hover color context |
Pane | design-system/Pane.tsx | ~80 | Command pane wrapper: colored top divider, padding, modal-aware rendering |
PermissionRequest | permissions/PermissionRequest.tsx | ~250 | Tool permission dispatcher: routes Bash, FileEdit, WebFetch to specialized dialogs |
Spinner | Spinner.tsx | ~85 | Loading spinner: frame-based animation, emoji rotation, custom patterns |
Sub-Modules
PromptInput/
Primary REPL input system — 21 files handling all user input modalities.
- Normal, Vim, and Voice input modes
- Arrow-key history & Ctrl+R search
- @-mention typeahead & prompt suggestions
- Multiline editing & inline paste
Messages/
Message rendering pipeline — 33+ files for all block types and transcript display.
- Normalization, collapsing & grouping
- VirtualMessageList (lazy render + binary search)
- Specialized renderers: text, tool use, thinking, attachments
- Brief mode filtering & transcript mode
agents/
Agent task status views — 13 files for coordinator and sub-agent tracking.
- Agent task progress & status badges
- Coordinator hierarchy display
- Parallel task monitoring
design-system/
Core terminal UI primitives — 16 files providing the shared component vocabulary.
- Dialog, Tabs, Pane, ProgressBar
- FuzzyPicker, ThemedText, ThemeProvider
- Color tokens & layout utilities
permissions/
Tool permission dialogs — 15 files gating bash, file edit, web fetch, and more.
- Risk assessment display
- Approve / Reject keybinding flow
- Sticky footer for scroll-aware prompts
mcp/
MCP server management — 12 files for server connections and approval dialogs.
- Server connection status
- Approval workflows
- Tool discovery & listing
Configuration
| Setting | Type | Default | Description |
|---|---|---|---|
theme | 'dark' | 'light' | 'auto' | 'auto' | UI theme; auto watches terminal via OSC 11 escape sequence |
mode (PromptInput) | 'normal' | 'vim' | 'voice' | 'normal' | Input editing mode |
multiline | boolean | false | Enable multi-line prompt editing (Shift+Enter for newline) |
visibleCount (FuzzyPicker) | number | 10 | Max items visible in the picker dropdown |
direction (FuzzyPicker) | 'down' | 'up' | 'down' | Dropdown direction relative to input |
contentHeight (Tabs) | number | auto | Fixed height for tab content area (prevents layout shift) |
borderStyle (Box) | 'single' | 'double' | 'round' | 'bold' | 'single' | Border style for Ink Box components |
isCancelActive (Dialog) | boolean | true | Whether Esc/Ctrl-C close the dialog (disable when editing text inside) |
style (Message) | 'condensed' | undefined | undefined | Condensed rendering for compact views |
Directory Map
Source tree for components
components/
├── App.tsx ← Root provider wrapper
├── FullscreenLayout.tsx ← Layout: scroll + sticky header + modals
├── Messages.tsx ← Transcript normalizer & renderer (833 LOC)
├── Message.tsx ← Single message router (626 LOC)
├── VirtualMessageList.tsx ← Virtual scroll with binary search
├── TextInput.tsx ← Base text field with cursor & vim
├── Spinner.tsx ← Animated loading indicators
├── HighlightedCode/ ← Syntax highlighting wrapper
├── StructuredDiff/ ← Side-by-side diff viewer
│
├── PromptInput/ ← REPL input system (21 files)
│ ├── PromptInput.tsx ← Main input component (2,338 LOC)
│ ├── inputModes.ts ← Normal / Vim / Voice state machines
│ └── ...
│
├── messages/ ← Message block renderers (33 files)
│ ├── UserTextMessage.tsx ← User input display
│ ├── AssistantTextMessage.tsx ← Assistant response display
│ ├── ToolUseMessage.tsx ← Tool invocation + progress
│ ├── ThinkingMessage.tsx ← Thinking block (collapsible)
│ └── ...
│
├── design-system/ ← UI primitives (16 files)
│ ├── Dialog.tsx ← Modal dialogs
│ ├── Tabs.tsx ← Tabbed interface
│ ├── FuzzyPicker.tsx ← Search dropdown
│ ├── Pane.tsx ← Command pane wrapper
│ ├── ThemedText.tsx ← Color-aware text
│ ├── ThemeProvider.tsx ← Theme context + OSC 11 watching
│ └── ProgressBar.tsx ← Progress indicators
│
├── agents/ ← Agent status views (13 files)
├── permissions/ ← Tool permission dialogs (15 files)
├── mcp/ ← MCP server management (12 files)
├── tasks/ ← Task progress tracking (12 files)
├── Spinner/ ← Spinner variants (12 files)
├── CustomSelect/ ← Dropdown menus (10 files)
├── FeedbackSurvey/ ← In-app surveys (9 files)
├── LogoV2/ ← Branding & status feeds (15 files)
├── hooks/ ← Hook mode dialogs (6 files)
├── wizard/ ← Multi-step setup flows (5 files)
├── sandbox/ ← Sandbox interactions (5 files)
├── shell/ ← Shell output display (4 files)
├── Settings/ ← Configuration dialogs (4 files)
├── HelpV2/ ← Help system UI (3 files)
├── diff/ ← Diff/patch rendering (3 files)
├── ui/ ← Tree select, ordered lists (3 files)
├── memory/ ← Memory context indicators (2 files)
├── teams/ ← Team/swarm management (2 files)
├── TrustDialog/ ← Trust & security decisions (2 files)
├── ManagedSettingsSecurityDialog/ ← Security policy enforcement (2 files)
├── grove/ ← Code grove integration (1 file)
├── skills/ ← Skill management (1 file)
├── ClaudeCodeHint/ ← Tutorial hint popover (1 file)
├── DesktopUpsell/ ← Desktop app marketing (1 file)
├── LspRecommendation/ ← LSP server suggestions (1 file)
└── Passes/ ← Guest pass management (1 file)