Claude Code UI 组件系统与状态管理深度分析
概述
Claude Code 使用 React + Ink 架构在终端中渲染交互式 UI。这是一个自定义的 Ink 实现(非标准 ink npm 包),位于 src/ink/,包含自定义 reconciler、screen buffer、输入处理和布局引擎。状态管理采用 自定义轻量 Store(类 Zustand),通过 useSyncExternalStore 桥接到 React。
1. UI 组件架构总览
架构分层
┌─────────────────────────────────────────────────┐
│ Screens (3) │
│ REPL · ResumeConversation · Doctor │
├─────────────────────────────────────────────────┤
│ Components (144) │
│ 消息展示 · 权限对话框 · 输入系统 · 设计系统 ... │
├─────────────────────────────────────────────────┤
│ Hooks (85+) │
│ useAppState · useTextInput · useCanUseTool ... │
├─────────────────────────────────────────────────┤
│ State Management (6 files) │
│ AppState · Store · Selectors · onChangeAppState │
├─────────────────────────────────────────────────┤
│ Ink Engine (src/ink/, 48 files) │
│ reconciler · screen · renderer · input · layout │
├─────────────────────────────────────────────────┤
│ React Reconciler │
│ Node.js Terminal (TTY) │
└─────────────────────────────────────────────────┘
Provider 嵌套结构
// src/components/App.tsx
<FpsMetricsProvider>
<StatsProvider>
<AppStateProvider initialState={initialState} onChangeAppState={onChangeAppState}>
<MailboxProvider>
<VoiceProvider>
{children} // REPL, dialogs, etc.
</VoiceProvider>
</MailboxProvider>
</AppStateProvider>
</StatsProvider>
</FpsMetricsProvider>
2. 组件分类(144 个)
2.1 消息展示系统 (components/messages/, 34 个文件)
负责各类消息的渲染——用户输入、AI 回复、工具使用结果。
| 文件 | 职责 |
|---|---|
AssistantTextMessage.tsx | AI 助手文本回复 |
AssistantThinkingMessage.tsx | 思考过程展示 |
AssistantToolUseMessage.tsx | 工具调用展示 |
UserTextMessage.tsx | 用户输入消息 |
UserBashInputMessage.tsx | 用户 Bash 命令 |
UserImageMessage.tsx | 用户粘贴图片 |
SystemTextMessage.tsx | 系统消息 |
CompactBoundaryMessage.tsx | 上下文压缩边界 |
GroupedToolUseContent.tsx | 分组工具调用 |
UserToolResultMessage/ | 工具结果展示(目录) |
2.2 权限对话框系统 (components/permissions/, 30 个文件)
Claude Code 的核心安全机制——每个工具调用都可能需要用户审批。
| 文件 | 职责 |
|---|---|
PermissionRequest.tsx | 权限请求基座组件 |
PermissionDialog.tsx | 权限对话框容器 |
BashPermissionRequest/ | Bash 命令权限 |
FileEditPermissionRequest/ | 文件编辑权限 |
FileWritePermissionRequest/ | 文件写入权限 |
WebFetchPermissionRequest/ | Web 抓取权限 |
ExitPlanModePermissionRequest/ | 退出计划模式权限 |
SandboxPermissionRequest.tsx | 沙箱权限 |
WorkerPendingPermission.tsx | Worker 待处理权限 |
2.3 输入系统 (components/PromptInput/, 21 个文件)
终端输入的完整实现,包含历史搜索、快捷键、补全、语音等。
| 文件 | 职责 |
|---|---|
PromptInput.tsx | 主输入组件 (2339行),最复杂的单文件 |
PromptInputFooter.tsx | 输入框下方区域(token 显示等) |
PromptInputFooterSuggestions.tsx | 补全建议展示 |
HistorySearchInput.tsx | 历史搜索输入 |
inputModes.ts | 输入模式管理 |
ShimmeredInput.tsx | 带闪烁效果的输入 |
VoiceIndicator.tsx | 语音输入指示器 |
2.4 设计系统 (components/design-system/, 16 个文件)
可复用的底层 UI 原子组件。
| 文件 | 职责 |
|---|---|
ThemeProvider.tsx | 主题提供者 |
ThemedText.tsx | 主题化文本 |
ThemedBox.tsx | 主题化容器 |
Dialog.tsx | 对话框基座 |
Divider.tsx | 分隔线 |
Pane.tsx | 面板容器 |
ProgressBar.tsx | 进度条 |
Tabs.tsx | 标签页 |
FuzzyPicker.tsx | 模糊搜索选择器 |
ListItem.tsx | 列表项 |
LoadingState.tsx | 加载状态 |
StatusIcon.tsx | 状态图标 |
2.5 Agent/Teammate 系统 (components/agents/, 14 个文件)
Agent 定义编辑与管理。
| 文件 | 职责 |
|---|---|
AgentsList.tsx | Agent 列表 |
AgentDetail.tsx | Agent 详情 |
AgentEditor.tsx | Agent 编辑器 |
ModelSelector.tsx | 模型选择器 |
ToolSelector.tsx | 工具选择器 |
ColorPicker.tsx | 颜色选择器 |
2.6 任务系统 (components/tasks/, 12 个文件)
后台任务与 Agent 任务的展示。
| 文件 | 职责 |
|---|---|
BackgroundTask.tsx | 后台任务展示 |
BackgroundTasksDialog.tsx | 后台任务列表对话框 |
InProcessTeammateDetailDialog.tsx | 同事实时详情 |
RemoteSessionDetailDialog.tsx | 远程会话详情 |
ShellProgress.tsx | Shell 执行进度 |
2.7 其他关键组件
| 文件 | 职责 |
|---|---|
App.tsx | 顶层 Provider 包装器 |
Messages.tsx | 消息列表容器(834行) |
Spinner.tsx | 加载动画(562行) |
Onboarding.tsx | 首次使用引导流程 |
ExitFlow.tsx | 退出流程 |
FullscreenLayout.tsx | 全屏布局(637行),ScrollBox + bottom pin |
VirtualMessageList.tsx | 虚拟消息列表,长会话性能优化 |
StatusLine.tsx | 状态行 |
Markdown.tsx | Markdown 渲染 |
SearchBox.tsx | 搜索框 |
QuickOpenDialog.tsx | 快速打开对话框 |
ThemePicker.tsx | 主题选择器 |
ModelPicker.tsx | 模型选择器 |
组件分类统计
消息展示 (messages/) 34 个文件
权限对话框 (permissions/) 30 个文件
输入系统 (PromptInput/) 21 个文件
设计系统 (design-system/) 16 个文件
Agent 管理 (agents/) 14 个文件
任务管理 (tasks/) 12 个文件
独立组件 37 个文件
─────────
总计 ~144 个文件
3. 状态管理设计
3.1 Store 机制 (src/state/store.ts)
一个极简的 自定义 Store 实现(34 行),类似 Zustand 的核心:
// src/state/store.ts
type Store<T> = {
getState: () => T
setState: (updater: (prev: T) => T) => void
subscribe: (listener: Listener) => () => void
}
function createStore<T>(initialState: T, onChange?: OnChange<T>): Store<T> {
let state = initialState
const listeners = new Set<Listener>()
return {
getState: () => state,
setState: (updater) => {
const prev = state
const next = updater(prev)
if (Object.is(next, prev)) return // 浅比较跳过
state = next
onChange?.({ newState: next, oldState: prev })
for (const listener of listeners) listener()
},
subscribe: (listener) => {
listeners.add(listener)
return () => listeners.delete(listener)
},
}
}
关键设计决策:
- 不可变更新:
setState接受(prev) => next函数 - 浅比较优化:
Object.is跳过无变化更新 - 变更回调:
onChange用于副作用同步(如持久化到 CCR) - 订阅机制:标准 listener set,与
useSyncExternalStore兼容
3.2 AppState 定义 (src/state/AppStateStore.ts)
AppState 是一个 巨型不可变对象(~90 个顶级字段),类型定义约 450 行。使用 DeepImmutable<T> 包装确保不可变性。
核心字段结构:
type AppState = DeepImmutable<{
// === 核心配置 ===
settings: SettingsJson // 用户设置
verbose: boolean // 详细模式
mainLoopModel: ModelSetting // 当前模型
mainLoopModelForSession: ModelSetting
statusLineText: string | undefined
isBriefOnly: boolean // Brief 模式
// === UI 导航状态 ===
expandedView: 'none' | 'tasks' | 'teammates'
viewSelectionMode: 'none' | 'selecting-agent' | 'viewing-agent'
footerSelection: FooterItem | null
selectedIPAgentIndex: number
coordinatorTaskIndex: number
activeOverlays: ReadonlySet<string>
// === 权限与安全 ===
toolPermissionContext: ToolPermissionContext
// === Agent/Team ===
agent: string | undefined
kairosEnabled: boolean
teamContext?: { teamName, teammates, ... }
standaloneAgentContext?: { name, color }
agentNameRegistry: Map<string, AgentId>
foregroundedTaskId?: string
viewingAgentTaskId?: string
// === 远程/Bridge ===
remoteSessionUrl: string | undefined
remoteConnectionStatus: 'connecting' | 'connected' | 'reconnecting' | 'disconnected'
replBridgeEnabled: boolean
replBridgeConnected: boolean
replBridgeSessionActive: boolean
// === MCP/插件 ===
mcp: { clients, tools, commands, resources, pluginReconnectKey }
plugins: { enabled, disabled, commands, errors, installationStatus }
// === 通知/对话 ===
notifications: { current, queue }
elicitation: { queue }
// === 推测执行 ===
speculation: SpeculationState
speculationSessionTimeSavedMs: number
// === 提示建议 ===
promptSuggestion: { text, promptId, shownAt, acceptedAt }
skillImprovement: { suggestion }
}> & {
// 非 DeepImmutable 字段(含函数类型)
tasks: { [taskId: string]: TaskState }
todos: { [agentId: string]: TodoList }
inbox: { messages: [...] }
workerSandboxPermissions: { queue, selectedIndex }
// ... 更多
}
3.3 React 桥接 (src/state/AppState.tsx)
通过三个核心 Hook 桥接到 React:
// 读取状态切片(自动订阅,Object.is 比较)
export function useAppState<T>(selector: (state: AppState) => T): T {
const store = useAppStore()
return useSyncExternalStore(store.subscribe, () => selector(store.getState()))
}
// 获取 setState(不订阅任何状态,引用稳定)
export function useSetAppState() {
return useAppStore().setState
}
// 获取整个 store(用于非 React 代码)
export function useAppStateStore() {
return useAppStore()
}
3.4 变更副作用 (src/state/onChangeAppState.ts)
onChangeAppState 是状态变更的唯一副作用入口,在 createStore 的 onChange 回调中触发:
组件调用 setAppState
↓
store.setState(updater)
↓
Object.is 比较 → 不同则:
↓
onChange({ newState, oldState }) ← onChangeAppState
↓
├─ toolPermissionContext.mode 变更 → notifySessionMetadataChanged + notifyPermissionModeChanged
├─ mainLoopModel 变更 → updateSettingsForSource + setMainLoopModelOverride
├─ expandedView 变更 → saveGlobalConfig (showExpandedTodos / showSpinnerTree)
├─ verbose 变更 → saveGlobalConfig
└─ settings 变更 → clearApiKeyHelperCache + applyConfigEnvironmentVariables
↓
通知所有 listener → useSyncExternalStore 触发重渲染
3.5 Selectors (src/state/selectors.ts)
纯数据提取函数,用于派生计算状态:
// 获取当前查看的 teammate 任务
getViewedTeammateTask(appState) → InProcessTeammateTaskState | undefined
// 确定用户输入应路由到哪个 agent
getActiveAgentForInput(appState) →
| { type: 'leader' }
| { type: 'viewed', task }
| { type: 'named_agent', task }
4. 关键 Hooks 分析(85+ 个)
4.1 分类总览
状态管理类 useAppState, useSetAppState, useSettings, useAppStateStore
输入处理类 useTextInput, useInputBuffer, useArrowKeyHistory, useVimInput,
usePasteHandler, useTypeahead, useCopyOnSelect, useDoublePress
工具权限类 useCanUseTool + toolPermission/ 子目录 (3 个 handler)
会话管理类 useRemoteSession, useSSHSession, useReplBridge, useAssistantHistory
IDE 集成类 useIDEIntegration, useIdeConnectionStatus, useIdeSelection, useIdeAtMentioned
任务/Agent 类 useTasksV2, useTaskListWatcher, useBackgroundTaskNavigation,
useSwarmInitialization, useTeammateViewAutoExit
键盘绑定类 useGlobalKeybindings, useCommandKeybindings, useExitOnCtrlCD
性能/渲染类 useAfterFirstRender, useTerminalSize, useVirtualScroll, useDeferredHookMessages
通知/事件类 useNotifications, useInboxPoller, useUpdateNotification, useNotifyAfterTimeout
4.2 useTextInput (src/hooks/useTextInput.ts, 529 行)
最复杂的 hook 之一——终端文本输入的完整实现。管理光标位置、文本缓冲区、kill ring、快捷键映射、粘贴处理等。
type UseTextInputProps = {
value: string
onChange: (value: string) => void
onSubmit?: (value: string) => void
onExit?: () => void
focus?: boolean
multiline?: boolean
cursorChar: string
columns: number
// ... 20+ 个配置项
}
内部实现:
- Cursor 类:管理光标位置、选择、kill ring(类似 Emacs)
- mapInput 函数:将按键映射到操作
- useDoublePress:双击检测(如双击 Escape)
- 图像粘贴支持:通过
onImagePaste回调
4.3 useCanUseTool (src/hooks/useCanUseTool.tsx, 204 行)
工具权限检查的核心 hook。协调配置权限、用户交互和分类器审批。
工具调用请求
↓
useCanUseTool(tool, input, context, message, toolUseID)
↓
hasPermissionsToUseTool() → 配置级预检
↓
├─ allow → resolve(allow)
├─ deny → resolve(deny)
└─ ask → 根据会话类型分派:
├─ interactive → handleInteractivePermission (显示 UI 等待用户)
├─ swarm worker → handleSwarmWorkerPermission
└─ coordinator → handleCoordinatorPermission
4.4 useSettings (src/hooks/useSettings.ts, 17 行)
最简单的 hook 之一——useAppState 的薄封装:
function useSettings(): ReadonlySettings {
return useAppState(s => s.settings)
}
4.5 Permission 权限系统 (src/hooks/toolPermission/)
toolPermission/
├── PermissionContext.ts // 权限上下文工厂 + ResolveOnce 竞态控制
├── permissionLogging.ts // 权限决策日志
└── handlers/
├── interactiveHandler.ts // 交互式 UI 权限处理
├── swarmWorkerHandler.ts // Swarm Worker 权限处理
└── coordinatorHandler.ts // Coordinator 权限处理
createPermissionContext() 创建一个冻结对象,封装了:
- 竞态控制:
createResolveOnce()确保只 resolve 一次 - 权限持久化:
persistPermissions()写入配置 - 分类器检查:
tryClassifier()Bash 命令安全分类器 - Hook 执行:
runHooks()用户自定义权限 hook - 队列操作:
pushToQueue / removeFromQueue / updateQueueItem
5. Ink 渲染引擎 (src/ink/, 48 个文件)
Claude Code 使用 自定义 Ink 实现,不是标准 npm ink 包。
5.1 核心架构
ink.tsx (1723 行)
├── Ink 类:主控制器
│ ├── LogUpdate: 终端输出管理
│ ├── Terminal: 终端抽象层
│ ├── FocusManager: 焦点管理
│ └── reconciler: React reconciler 桥接
├── reconciler.ts: 自定义 React reconciler
├── renderer.ts: 渲染器
├── render-node-to-output.ts: 节点到输出的转换
├── render-to-screen.ts: 屏幕渲染
├── screen.ts: 屏幕缓冲区
├── output.ts: 输出管理
├── selection.ts: 文本选择系统
└── focus.ts: 焦点管理
5.2 渲染管线
React 组件树
↓
React Reconciler (fiber)
↓
Ink reconciler → Yoga Layout (Flexbox)
↓
render-node-to-output → Output (字符网格)
↓
optimize → 差异计算
↓
writeDiffToTerminal → ANSI escape codes
↓
终端屏幕
5.3 关键特性
- Alt Screen 模式:全屏应用使用
ENTER_ALT_SCREEN/EXIT_ALT_SCREEN - 文本选择:
selection.ts实现终端内文本选择和复制 - 搜索高亮:
searchHighlight.ts和searchHighlight功能 - 鼠标支持:
hit-test.ts处理点击事件 - Bidi 支持:
bidi.ts处理双向文本 - 性能优化:
optimizer.ts做差异渲染,log-update.ts增量更新
5.4 Ink Hooks (src/ink/hooks/, 12 个)
| Hook | 职责 |
|---|---|
use-input.ts | 用户输入处理(键盘事件) |
use-stdin.ts | stdin 流管理 |
use-app.ts | 应用级控制(退出等) |
use-terminal-focus.ts | 终端焦点状态 |
use-terminal-title.ts | 终端标题设置 |
use-terminal-viewport.ts | 终端视口尺寸 |
use-animation-frame.ts | 动画帧回调 |
use-interval.ts | 定时器 |
use-selection.ts | 文本选择 |
use-search-highlight.ts | 搜索高亮 |
use-declared-cursor.ts | 光标声明 |
use-tab-status.ts | Tab 状态(iTerm2) |
6. Screen 系统 (src/screens/, 3 个文件)
6.1 REPL.tsx (5006 行) — 主交互循环
Claude Code 的核心组件——用户与 AI 对话的主界面。
REPL
├── GlobalKeybindingHandlers // 全局快捷键
├── CommandKeybindingHandlers // 命令快捷键
├── CancelRequestHandler // 取消请求处理
├── FullscreenLayout
│ ├── scrollable: Messages (消息列表)
│ ├── bottom: Spinner + PromptInput
│ ├── overlay: PermissionRequest
│ └── modal: 命令面板/对话框
└── VoiceKeybindingHandler // 语音快捷键
REPL 管理的核心状态:
query— 发送 API 请求messages— 消息历史toolUseConfirmQueue— 权限确认队列isWaitingForResponse— 等待 AI 响应- 各种 ref 管理暂停、计时、上下文
6.2 ResumeConversation.tsx
会话恢复界面——用户选择继续哪个历史会话。
6.3 Doctor.tsx
诊断界面——检查和修复配置问题。
7. 关键组件深度分析
7.1 Messages.tsx (834 行)
消息列表容器,处理消息归一化、分组、过滤和虚拟滚动。
// 核心数据流
原始 messages[]
↓
normalizeMessages() // 归一化
↓
getMessagesAfterCompactBoundary() // 压缩边界
↓
collapseReadSearchGroups() // 折叠 Read/Search
↓
collapseHookSummaries() // 折叠 Hook 摘要
↓
collapseBackgroundBashNotifications() // 折叠后台 Bash
↓
applyGrouping() // 工具调用分组
↓
filterForBriefTool() // Brief 模式过滤
↓
VirtualMessageList // 虚拟滚动渲染
关键设计:
- LogoHeader 被
React.memo包装,避免消息更新时重绘 logo(性能关键) - OffscreenFreeze 冻结不可见区域
- 使用
InVirtualListContext和MessageActionsSelectedContext两个 Context
7.2 PromptInput.tsx (2339 行)
最复杂的单文件组件——终端输入框的完整实现。
核心职责:
- 文本输入和光标管理
- 补全建议显示
- 输入模式切换(普通/命令/搜索)
- 粘贴处理(文本和图片)
- 历史导航
- 快捷键绑定
- Token 预算显示
- 权限模式切换 (Shift+Tab)
- Agent 选择(footer pill 导航)
- 悬浮伴侣(Companion Sprite)集成
使用的关键 hooks:
useAppState(s => s.toolPermissionContext) // 权限上下文
useAppState(s => s.mainLoopModel) // 当前模型
useAppState(s => s.footerSelection) // footer 选择
useAppState(s => s.viewingAgentTaskId) // 查看的 Agent
useAppState(s => s.expandedView) // 展开视图
useArrowKeyHistory() // 历史导航
useTextInput() // 文本输入核心
useTypeahead() // 前缀补全
useInputBuffer() // 输入缓冲
usePromptSuggestion() // 建议
useIdeAtMentioned() // IDE @提及
7.3 Spinner.tsx (562 行)
加载动画组件,显示 AI 正在处理。
关键设计:
- SpinnerWithVerb 是入口,根据
isBriefOnly分支到BriefSpinner或SpinnerWithVerbInner - 使用
useAnimationFrame驱动动画(在SpinnerAnimationRow中) useAppState(s => s.expandedView)决定是否展开任务列表useAppState(s => s.viewingAgentTaskId)用于 teammate 视图- 包含 token 预算进度条和任务树
7.4 FullscreenLayout.tsx (637 行)
全屏布局组件——将屏幕分为滚动区域和固定底部。
┌──────────────────────────────┐
│ scrollable │ ← Messages (可滚动)
│ (messages) │
│ │
├──────────────────────────────┤
│ overlay (可选) │ ← PermissionRequest 等
├──────────────────────────────┤
│ bottom (固定) │ ← Spinner + PromptInput
└──────────────────────────────┘
modal (绝对定位,覆盖全部)
bottomFloat (右下角浮动)
使用 ScrollBox(Ink 组件)实现滚动,ScrollChromeContext 提供 sticky prompt 上下文。
7.5 Onboarding.tsx (244 行)
首次使用引导——步骤式向导。
type StepId = 'preflight' | 'theme' | 'oauth' | 'api-key' | 'security' | 'terminal-setup'
// 步骤数组动态构建
const steps: OnboardingStep[] = [
{ id: 'preflight', component: <PreflightStep /> },
{ id: 'theme', component: <ThemePicker /> },
{ id: 'oauth', component: <ConsoleOAuthFlow /> }, // 条件
{ id: 'api-key', component: <ApproveApiKey /> }, // 条件
{ id: 'security', component: <SecurityStep /> },
{ id: 'terminal-setup', component: <TerminalSetup /> },
]
8. Context Provider 系统
除了核心 AppState Store,还有 9 个独立的 React Context:
| Context | 文件 | 职责 |
|---|---|---|
AppStoreContext | state/AppState.tsx | 核心 AppState Store |
StatsContext | context/stats.tsx | 统计数据存储 |
FpsMetricsContext | context/fpsMetrics.tsx | FPS 指标 |
MailboxContext | context/mailbox.tsx | 消息邮箱 |
VoiceContext | context/voice.tsx | 语音模式 |
ModalContext | context/modalContext.tsx | 模态对话框 |
TerminalWriteContext | ink/useTerminalNotification.ts | 终端原始写入 |
TerminalSizeContext | ink/components/TerminalSizeContext.tsx | 终端尺寸 |
ClockContext | ink/components/ClockContext.tsx | 时钟/时间 |
KeybindingContext | keybindings/KeybindingContext.tsx | 快捷键绑定 |
ScrollChromeContext | components/FullscreenLayout.tsx | 滚动 Chrome |
PromptOverlayContext | context/promptOverlayContext.tsx | 提示覆盖层 |
MCPConnectionContext | services/mcp/MCPConnectionManager.tsx | MCP 连接管理 |
WizardContext | components/wizard/WizardProvider.tsx | 向导流程 |
TabsContext | components/design-system/Tabs.tsx | 标签页 |
ThemeContext | components/design-system/ThemeProvider.tsx | 设计主题 |
9. 数据流模式
9.1 状态更新流程
9.2 工具权限请求流程
9.3 消息渲染流程
9.4 对话框启动流程
showSetupDialog 自动包装 AppStateProvider + KeybindingSetup,确保每个对话框都有完整上下文。
10. 性能优化策略
10.1 React Compiler
所有组件文件开头都有 import { c as _c } from "react/compiler-runtime" ——使用 React Compiler 自动 memoization,减少手动 useMemo / useCallback。
10.2 虚拟滚动
VirtualMessageList 只渲染可见区域的消息,长会话(2800+ 条消息)不会卡顿。
10.3 增量渲染
Ink 的 optimizer.ts 做帧级差异计算,只写变化的字符到终端。
10.4 Memoized 子树
// Messages.tsx 中 LogoHeader 被 memo 包装
const LogoHeader = React.memo(function LogoHeader({ agentDefinitions }) {
return <OffscreenFreeze>
<Box flexDirection="column" gap={1}>
<LogoV2 />
<React.Suspense fallback={null}>
<StatusNotices agentDefinitions={agentDefinitions} />
</React.Suspense>
</Box>
</OffscreenFreeze>
})
10.5 条件 Dead Code Elimination
// feature() 是编译时常量,bun:bundle 在构建时消除死代码
const useVoiceIntegration = feature('VOICE_MODE')
? require('../hooks/useVoiceIntegration.js').useVoiceIntegration
: () => ({ stripTrailing: () => 0, ... })
10.6 Stable References
useSetAppState() 返回稳定引用——仅调用此 hook 的组件不会因状态变更而重渲染。
总结
Claude Code 的 UI 架构有几个核心设计原则:
- 单一状态源:
AppState是一个巨型不可变对象,所有 UI 状态集中管理 - 自定义轻量 Store:34 行实现,无外部依赖,与
useSyncExternalStore完美配合 - 分层 Context:核心状态用 Store,辅助状态用 React Context
- 自定义 Ink 引擎:完全控制渲染管线,实现终端内的富交互体验
- 防御性权限系统:每个工具调用都经过权限检查,三层处理路径
- 死代码消除:
feature()编译时常量,按构建目标裁剪功能 - 性能优先:虚拟滚动 + 增量渲染 + React Compiler + memo 策略