这一章解释 Claude Code 在当前快照中的终端交互层:UI 是怎么组织的,全局状态放在哪里,REPL 为什么会显得非常重。
Claude Code 的终端界面不是简单的 readline 包装,而是一套基于 React + Ink 的状态驱动应用。REPL 是主交互屏,AppState 是核心共享状态,AppStateProvider 负责把状态、设置变更和上下文能力注入整棵组件树。
- 顶层 UI 包装:src/components/App.tsx
- REPL 主屏:src/screens/REPL.tsx
- AppState 定义:src/state/AppStateStore.ts
- AppState Provider:src/state/AppState.tsx
- REPL 启动器:src/replLauncher.tsx
flowchart TD
A[App] --> B[AppStateProvider]
A --> C[StatsProvider]
A --> D[FpsMetricsProvider]
B --> E[REPL]
E --> F[PromptInput]
E --> G[Messages]
E --> H[Notifications / Dialogs]
E --> I[Tasks / Teammates]
B --> J[mcp state]
B --> K[plugins state]
B --> L[bridge / remote state]
B --> M[toolPermissionContext]
src/components/App.tsx 很简洁,但暴露了整个 UI 的基本结构:
FpsMetricsProviderStatsProviderAppStateProvider
这意味着终端 UI 至少有三类共享能力:
- 性能/FPS 指标
- 统计信息
- 统一应用状态
src/state/AppState.tsx 进一步说明了状态层的组织方式:
- 通过
createStore(...)创建状态存储。 - 在挂载时检查 bypassPermissions 是否需要被强制禁用。
- 通过
useSettingsChange(...)响应设置变更。 - 额外包裹
MailboxProvider和可能存在的VoiceProvider。
可以把它看成“UI 与非 UI 系统之间的边界层”。它既服务 React 组件,也向非 React 代码暴露 getState/setState。
src/state/AppStateStore.ts 显示,AppState 很大,但结构是有规律的。它至少包含以下几组核心状态:
mainLoopModelstatusLineTextexpandedViewviewSelectionModefooterSelectionisBriefOnly
toolPermissionContextthinkingEnabledpromptSuggestionEnablednotificationselicitation
remoteSessionUrlremoteConnectionStatusreplBridgeEnabledreplBridgeConnectedreplBridgeSessionActivereplBridgeError
mcp.clientsmcp.toolsmcp.commandsmcp.resourcesplugins.enabledplugins.disabledplugins.errors
tasksagentNameRegistryforegroundedTaskIdviewingAgentTaskIdtodos
这也解释了为什么 REPL 主屏会很重:它几乎是这些状态的汇合点。
src/screens/REPL.tsx 的 import 面非常宽,说明 REPL 不只是“文本输入框”。
从文件顶部可直接看到它要同时处理:
- 输入与键盘事件
- 搜索、高亮、消息列表
- 提示框、通知、弹窗
- hook 结果、MCP elicitation、权限请求
- IDE 集成、remote session、SSH session
- 插件、skills、任务、后台会话
- 消息流、token 预算、成本汇总
- speculative suggestion、prompt suggestion
- worktree、restore、resume、compact
换句话说,REPL 是 Claude Code 的主控制台。
命令系统中大量命令是 local-jsx 类型,例如:
/help/config/doctor/plugin/tasks
这些命令实际上是在 REPL 内部打开一个 React 视图或对话框,而不是执行一个无界面的 shell 动作。
这两个层次不要混淆:
REPL.tsx负责交互编排、UI、输入队列、消息展示、状态协同。QueryEngine.ts负责具体的一轮对话或任务提交过程中,如何组织上下文、调用模型、处理工具和累计会话状态。
一个偏“终端应用壳”,一个偏“执行引擎”。
研究 UI 时,不建议直接硬啃 REPL.tsx 全文。更高效的路径是:
- 先看 src/components/App.tsx。
- 再看 src/state/AppStateStore.ts 理解状态面。
- 再回到 src/screens/REPL.tsx 搜你关心的主题,例如
permissions、remote、plugin、tasks、query。
Claude Code 的终端 UI 是一套状态很重、模块耦合也很深的 React + Ink 应用。很多看似“命令行功能”的能力,其实都是通过 REPL 主屏和共享状态系统协调出来的。