Step 15: UI 组件架构
分析日期: 2026-04-16 核心目录: src/components/ (~100+ 文件), src/screens/ (3 文件)
1. 组件树图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
App (src/components/App.tsx)
|-- FpsMetricsProvider
| |-- StatsProvider
| |-- AppStateProvider
| |-- <children> (REPL 屏幕)
|
REPL (src/screens/REPL.tsx)
|-- FullscreenLayout
| |-- scrollable:
| | |-- LogoHeader (memoized)
| | | |-- LogoV2
| | | |-- StatusNotices
| | |
| | |-- Messages (OR VirtualMessageList)
| | |-- Message (per message, switch-dispatched)
| | |-- [user/assistant/system/attachment/grouped/collapsed]
| |
| |-- bottom:
| | |-- Spinner / BashModeProgress / ToolUseLoader
| | |-- PermissionRequest (when toolUseConfirmQueue active)
| | |-- PromptInput
| | |-- ShimmeredInput
| | |-- PromptInputFooter
| | | |-- PromptInputFooterLeftSide
| | | |-- PromptInputFooterSuggestions
| | | |-- Notifications
| | |-- PromptInputHelpMenu
| | |-- HistorySearchInput
| | |-- VoiceIndicator
| |
| |-- overlay: (PermissionRequest when pending)
| |-- modal: (/config, /help 等斜杠命令面板)
| |-- bottomFloat: (伴游精灵气泡)
2. 组件类别表
核心布局
| 组件 | 文件 | 职责 | 关键 Hooks |
|---|---|---|---|
| App | App.tsx | 顶层包装;提供 FPS/stat/app state | React Compiler runtime |
| FullscreenLayout | FullscreenLayout.tsx | 全屏终端布局:可滚动区 + 固定底部 + 覆盖层 + 模态槽 | useUnseenDivider, useSyncExternalStore |
| ScrollKeybindingHandler | ScrollKeybindingHandler.tsx | 键盘驱动滚动 | useKeybinding, useKeybindings |
| StatusLine | StatusLine.tsx | 底部状态栏 | useAppState, useSettings |
| OffscreenFreeze | OffscreenFreeze.tsx | 防止离屏消息重渲染 | React.memo |
消息/转录
| 组件 | 文件 | 职责 |
|---|---|---|
| Messages | Messages.tsx | 编排完整消息列表:规范化、分组、折叠、虚拟化、搜索 |
| Message | Message.tsx | 每条消息调度器:按 type 切换到类型特定子组件 |
| VirtualMessageList | VirtualMessageList.tsx | 全屏模式虚拟化滚动;useVirtualScroll, 高度缓存, 粘性提示追踪 |
| MessageActions | messageActions.tsx | 每条消息导航光标、详细切换、选择状态 |
消息子组件 (30+ 类型特定渲染器)
| 类型 | 组件 | 渲染内容 |
|---|---|---|
| assistant/text | AssistantTextMessage | Markdown + API 错误检测 |
| assistant/tool_use | AssistantToolUseMessage | 工具进度、名称、输入预览 |
| assistant/thinking | AssistantThinkingMessage | 思考块 (详细/转录模式可见) |
| user/text | UserTextMessage | 用户文本 + 计划内容 |
| user/image | UserImageMessage | 粘贴图片 |
| user/tool_result | UserToolResultMessage | 调度到 success/error/reject/canceled |
| system | SystemTextMessage | 系统消息 |
| attachment | AttachmentMessage | 文件附件 |
| grouped_tool_use | GroupedToolUseContent | 分组读/搜工具调用 |
| collapsed_read_search | CollapsedReadSearchContent | 折叠读/搜内容 |
权限 (15+ 特定工具权限组件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PermissionRequest.tsx: permissionComponentForTool(tool)
|
+-- FileEditTool --> FileEditPermissionRequest (diff 视图)
+-- FileWriteTool --> FileWritePermissionRequest (写入差异预览)
+-- BashTool --> BashPermissionRequest (命令显示)
+-- PowerShellTool --> PowerShellPermissionRequest
+-- WebFetchTool --> WebFetchPermissionRequest (URL 显示)
+-- NotebookEditTool --> NotebookEditPermissionRequest (单元格差异)
+-- GlobTool/GrepTool/FileReadTool --> FilesystemPermissionRequest (文件路径)
+-- EnterPlanModeTool --> EnterPlanModePermissionRequest
+-- ExitPlanModeV2Tool --> ExitPlanModePermissionRequest
+-- SkillTool --> SkillPermissionRequest
+-- AskUserQuestionTool --> AskUserQuestionPermissionRequest (多问题导航)
+-- default --> FallbackPermissionRequest
所有权限组件共享:
- PermissionDialog: 圆角顶部边框 + 标题/副标题/worker 徽章
- PermissionPrompt: Select 选项 + Tab 展开反馈输入 + 分析
Agent/任务
| 组件 | 文件 | 职责 |
|---|---|---|
| BackgroundTask | tasks/BackgroundTask.tsx | 后台任务状态 (bash shell, 远程 agent, 进程内队友) |
| BackgroundTaskStatus | tasks/BackgroundTaskStatus.tsx | 聚合后台任务状态行 |
| ShellProgress | tasks/ShellProgress.tsx | 实时 shell 进度 |
| AsyncAgentDetailDialog | tasks/AsyncAgentDetailDialog.tsx | 异步 agent 详情 |
| InProcessTeammateDetailDialog | tasks/InProcessTeammateDetailDialog.tsx | 进程内队友详情 |
| DreamDetailDialog | tasks/DreamDetailDialog.tsx | Dream(推测)任务详情 |
| AgentsList/AgentsMenu/AgentDetail/AgentEditor | agents/ | Agent 管理 |
| CreateAgentWizard | agents/new-agent-creation/ | 多步创建向导 |
设置/配置
| 组件 | 文件 | 职责 |
|---|---|---|
| Settings | Settings/Settings.tsx | /config 主面板 |
| MCPSettings/MCPListPanel | mcp/ | MCP 服务器配置 |
| SandboxSettings | sandbox/ | 沙箱配置 |
| HooksConfigMenu | hooks/ | Hook 配置菜单 |
| ThemePicker/ModelPicker | 各自 | 主题/模型选择 |
设计系统
| 组件 | 文件 | 职责 |
|---|---|---|
| ThemeProvider | design-system/ThemeProvider.tsx | 主题上下文;’auto’ -> OSC 11 检测 |
| ThemedText | design-system/ThemedText.tsx | 主题感知文本组件 |
| ThemedBox | design-system/ThemedBox.tsx | 主题感知盒子组件 |
| Dialog | design-system/Dialog.tsx | 确认/取消对话框 + 键绑定 |
| Pane | design-system/Pane.tsx | 斜杠命令内容面板 |
| FuzzyPicker | design-system/FuzzyPicker.tsx | 模糊搜索选择器 |
| KeyboardShortcutHint | design-system/ | 快捷键显示 |
Markdown/代码高亮
| 组件 | 文件 | 渲染策略 |
|---|---|---|
| Markdown | Markdown.tsx | 混合渲染:表格用 React flexbox,其他用 ANSI 字符串;LRU token 缓存 (500 条) |
| StreamingMarkdown | Markdown.tsx | 流式优化:稳定前缀 memoized,不稳定后缀逐 delta 重解析 |
| MarkdownTable | MarkdownTable.tsx | 表格用 React flexbox 组件 |
| HighlightedCode | HighlightedCode.tsx | tree-sitter 语法高亮 (回退到 HighlightedCodeFallback) |
| StructuredDiff | StructuredDiff.tsx | 结构化差异显示 + tree-sitter 高亮 |
3. 消息渲染管线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
原始 API 消息 (Message[])
|
v
Messages.tsx: normalizeMessages() + reorderMessagesInUI()
|-- collapseReadSearchGroups()
|-- collapseBackgroundBashNotifications()
|-- collapseHookSummaries()
|-- collapseTeammateShutdowns()
|-- applyGrouping()
|-- buildMessageLookups()
|-- filterForBriefTool() [if brief mode]
|-- dropTextInBriefTurns() [if brief mode]
|
v
RenderableMessage[] (规范化、分组、折叠)
|
v
Messages.tsx: renderItem()
|
v
Message.tsx: switch(message.type)
|
+-- "user" --> UserMessage (param.type switch)
| +-- "text" --> UserTextMessage
| +-- "image" --> UserImageMessage
| +-- "tool_result" --> UserToolResultMessage -> (success/error/reject/canceled)
| +-- (isCompactSummary) --> CompactSummary
|
+-- "assistant" --> map content blocks
| +-- "text" --> AssistantTextMessage --> Markdown
| +-- "tool_use" --> AssistantToolUseMessage
| +-- "thinking" --> AssistantThinkingMessage
| +-- "redacted_thinking" --> AssistantRedactedThinkingMessage
|
+-- "system" --> subtype switch
| +-- "compact_boundary" --> CompactBoundaryMessage
| +-- "local_command" --> UserTextMessage
| +-- default --> SystemTextMessage
|
+-- "attachment" --> AttachmentMessage
+-- "grouped_tool_use" --> GroupedToolUseContent
+-- "collapsed_read_search" --> CollapsedReadSearchContent
Markdown 渲染:
string content
|-- stripPromptXMLTags()
|-- cachedLexer() [LRU, 500条, hash 键控]
| |-- Fast path: 无 MD 语法 -> 单段 token
| |-- Hit: 返回缓存 token
| |-- Miss: marked.lexer() -> 缓存并返回
|
v
MarkdownBody: 迭代 token
+-- "table" --> MarkdownTable (React flexbox)
+-- other --> formatToken() -> ANSI 字符串 -> <Ansi>
4. 权限对话框流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
工具执行需要权限
|
v
PermissionRequest.tsx: permissionComponentForTool(tool)
| (映射 Tool 类 -> 权限组件)
v
特定权限组件 (如 BashPermissionRequest)
|-- PermissionDialog (共享框架: 圆角边框、标题、副标题、worker 徽章)
|-- PermissionPrompt (Select 选项 + Tab 反馈输入 + 分析)
|-- 工具特定内容:
| +-- Bash: 命令显示、命令描述、选项 (allow/deny/always-allow)
| +-- FileEdit: diff 视图、文件路径
| +-- FileWrite: 写入差异预览
| +-- Filesystem: 文件路径显示
| +-- AskUserQuestion: 多问题导航、预览、提交
|
v
用户选择 --> PermissionDecision (allow/deny/always-allow/always-deny)
|
v
决策应用到工具执行管线
5. 关键架构观察
-
React Compiler: 编译输出使用
react/compiler-runtime(_c()) 进行自动 memoization。几乎所有组件使用编译器缓存槽。 -
虚拟滚动: 全屏模式使用
VirtualMessageList+useVirtualScroll。非全屏模式使用普通.map()+MAX_MESSAGES_WITHOUT_VIRTUALIZATION上限。 -
消息规范化管线: 原始 API 消息经过多步管线:规范化、重排序、分组(读/搜折叠)、后台 Bash 折叠、hook 摘要折叠、队友关闭折叠、brief 模式过滤。
-
Markdown 三层优化: (a) 无 MD 语法的快速路径跳过, (b) LRU token 缓存 (500 条, hash 键控), (c) 流式分割渲染(稳定前缀 memoized + 不稳定后缀逐 delta 重解析)。
-
权限调度模式:
PermissionRequest.tsx使用工具类到组件的映射函数,每个工具特定组件共享PermissionDialog框架和PermissionPrompt选项系统。 -
FullscreenLayout 插槽: 5 个渲染插槽:scrollable(消息)、bottom(提示/旋转器)、overlay(权限请求)、modal(斜杠命令)、bottomFloat(伴游气泡)。
-
REPL 作为编排器:
REPL屏幕 (screens/REPL.tsx) 是中央编排器,持有绝大多数应用状态。