Deep Dive | src/components/PromptInput/PromptInput.tsx 2338 行 — 提示输入框(多模态编辑器)¶
重要性:⭐⭐⭐(用户输入核心 UI——30+ hooks 编排、bash 模式、history、autocomplete) 真实位置:
src/components/PromptInput/PromptInput.tsx(2338 行) 角色:REPL 主输入框——多行编辑、bash 模式、history 搜索、@ 提及、命令补全、模型切换、附件预览 关联:topics/deep-dive-repl-screen.md(调用方)、Ink 文档
1. 文件全景¶
PromptInput.tsx (2338 行)
│
├── 行 1-120 :imports + 3 个内部 hook 懒加载
│
├── 行 124-191:Props type (~67 行)
│
├── 行 192-194:3 个常量
│ ├── PROMPT_FOOTER_LINES (5)
│ ├── MIN_INPUT_VIEWPORT_LINES (3)
│
├── 行 194-2302:**PromptInput 主组件(~2108 行)** — God Component
│
├── 行 2303-2327:getInitialPasteId (helper)
│
├── 行 2328-2337:buildBorderText (helper)
│
└── 行 2338 :export default React.memo(PromptInput)
核心洞察:整个文件就是一个 2108 行的 React 组件 + 2 个 helper。
2. Props 接口(67 行)¶
type Props = {
// 30+ props
debug: boolean
ideSelection?: IDESelection
hasSuppressedDialogs: boolean
isLocalJSXCommandActive: boolean
getToolUseContext: GetToolUseContextFn
toolPermissionContext: ...
setToolPermissionContext: ...
apiKeyStatus: ...
commands: Command[]
agents: ...
isLoading: boolean
onExit: () => void
verbose: boolean
messages: Message[]
onAutoUpdaterResult: ...
autoUpdaterResult: ...
input: string
onInputChange: (v: string) => void
mode: InputMode
onModeChange: (m: InputMode) => void
stashedPrompt: ...
setStashedPrompt: ...
submitCount: number
onShowMessageSelector: ...
onMessageActionsEnter: ...
mcpClients: ...
pastedContents: ...
setPastedContents: ...
vimMode: ...
setVimMode: ...
showBashesDialog: ...
setShowBashesDialog: ...
onSubmit: (text, mode) => Promise<void>
onAgentSubmit: ...
isSearchingHistory: boolean
setIsSearchingHistory: ...
helpOpen: boolean
setHelpOpen: ...
insertTextRef: ...
voiceInterimRange: ...
}
30+ props —— 与 REPL 深度集成。
3. 30+ 唯一 Hooks¶
useAppState, useAppStateStore, useSetAppState
useState, useEffect, useMemo, useCallback, useRef, useSyncExternalStore
useInput (Ink)
useInputBuffer // 输入缓冲
useArrowKeyHistory // ↑↓ 历史
useHistorySearch // Ctrl+R 历史搜索
useDoublePress // 双击检测(如 Esc Esc)
useKeybinding, useKeybindings // 快捷键
useOptionalKeybindingContext
usePromptSuggestion // 补全
usePromptInputPlaceholder // placeholder
useCommandQueue // 命令队列
useIsModalOverlayActive // 模态检测
useMaybeTruncateInput // 截断(防长输入)
useBuddyNotification // 助手
useCoordinatorTaskCount // 协调任务数
useIdeAtMentioned // IDE @ 提及
useMainLoopModel // 主模型
useNotifications // 通知
useSetPromptOverlayDialog // 覆盖对话框
useShowFastIconHint // Fast 模式图标提示
useSwarmBanner // swarm 横幅
30+ 唯一 hooks —— 输入框功能的全部。
4. 主组件 6 段结构¶
PromptInput({...}) (行 194-2302, ~2108 行)
│
├── A. 初始化 / state 段 (~200 行)
│ ├── constants / mode detection
│ ├── local state (vim mode, paste id, ...)
│ └── input buffer hook
│
├── B. 业务 hook 段 (~400 行)
│ ├── history search
│ ├── command suggestion
│ ├── placeholder
│ ├── notifications
│ └── ... 30+ hooks
│
├── C. 事件处理段 (~600 行)
│ ├── onSubmit (主提交)
│ ├── onKeyEvent (键盘)
│ ├── onPaste
│ ├── onBashCommand
│ ├── onModeSwitch
│ └── ... 50+ handlers
│
├── D. 派生数据段 (~400 行)
│ ├── visible commands
│ ├── history navigation
│ ├── placeholder text
│ └── fast icon
│
├── E. JSX 段 (~500 行)
│ ├── Box layout
│ ├── Input area
│ ├── Suggestions
│ ├── Footer
│ └── Dialogs
│
└── F. React.memo 包装 (行 2338)
~2108 行 单组件。
5. useInput —— 30+ 键盘事件¶
useInput((input, key) => {
// 处理所有键盘事件
// Ctrl+C, Ctrl+D, Enter, Esc, Tab, ↑↓, Ctrl+R, ...
}, { isActive: ... });
30+ 键盘组合 —— - Enter 提交 - Shift+Enter 换行 - Tab 补全 - Esc 取消 - ↑↓ 历史 - Ctrl+R 历史搜索 - Ctrl+C 中断 - Ctrl+D 退出 - Ctrl+L 清屏 - vim mode 的 h/j/k/l/w/b/e/0/$ ...
6. 3 个 Mode¶
InputMode:
- default —— 普通
- bash —— bash 命令(! 触发)
- plan —— 计划模式
- voice —— 语音输入
- ... 还有
onModeChange 切换 mode —— 影响快捷键 / placeholder / 提交行为。
7. getInitialPasteId — 25 行(行 2303)¶
初始 paste id —— 给新的 paste 一个唯一 id。
8. buildBorderText — 10 行(行 2328)¶
function buildBorderText(showFastIcon, showFastIconHint, fastModeCooldown): BorderTextOptions | undefined {
// 边框文字(fast 模式提示)
}
边框提示 —— 显示 fast 模式状态。
9. 关键设计模式¶
9.1 God Component¶
2108 行单组件 —— 与 REPL.tsx 一样的"编排"模式。
9.2 30+ Hooks 编排¶
业务逻辑在 hooks —— PromptInput 只编排。
9.3 useInput 集中处理¶
所有键盘事件 走一个 useInput 回调 —— 集中管理。
9.4 模式切换¶
多种 InputMode —— default / bash / plan / voice —— 切换影响行为。
9.5 历史搜索¶
useHistorySearch —— Ctrl+R 模糊搜索。
9.6 双击检测¶
useDoublePress —— Esc Esc 是常见操作(清空 / 取消)。
9.7 Vim 模式¶
vimMode state —— 切换 vim 风格输入。
9.8 @ 提及¶
useIdeAtMentioned —— IDE 中选中的代码 → @ 提及。
9.9 React.memo¶
memo 包装 —— props 不变不重渲染(REPL 重渲染频繁)。
9.10 附件预览¶
显示已 paste 的内容、文件、附件。
9.11 多行编辑¶
支持 Shift+Enter 换行 —— 复杂文本输入。
9.12 提示词 + 补全¶
usePromptSuggestion —— 智能补全。
10. 复杂度分析¶
| 维度 | 数字 |
|---|---|
| 总行数 | 2338 |
| 主组件 | 1 (2108 行) |
| Helper | 2 (35 行) |
| Props | 30+ |
| 唯一 Hooks | 30+ |
| InputMode | 3+ |
| 键盘事件 | 30+ |
11. 性能特征¶
11.1 输入响应¶
- 按键 → render:< 16ms(一帧 60fps)
- 大输入:~16-50ms(deferredValue)
- React.memo:减少重渲染
11.2 优化¶
- React.memo
- useDeferredValue
- useInputBuffer
- useMaybeTruncateInput
12. 与其他文件的关系¶
PromptInput.tsx
├──→ hooks/useInputBuffer.js
├──→ hooks/useHistorySearch.js
├──→ hooks/useArrowKeyHistory.js
├──→ hooks/usePromptSuggestion.js
├──→ hooks/useKeybindings.js
├──→ hooks/useNotifications.js
├──→ ...
├──→ components/MessageActionsBar
├──→ components/SkillSuggestion
├──→ ink/...
└──→ screens/REPL.tsx (调用方)
13. 关键洞察¶
13.1 30+ hooks 编排¶
业务 100% 在 hooks —— PromptInput 只 render + 编排。
13.2 useInput 集中键盘¶
所有键盘 走一个回调 —— 集中管理 + 优先级。
13.3 React.memo 关键¶
REPL 重渲染频繁 —— 必须 memo。
13.4 多 mode 切换¶
default / bash / plan / voice —— 不同 mode 不同行为。
13.5 Vim 模式¶
额外模式 —— 高级用户友好。
13.6 历史搜索 Ctrl+R¶
Linux 风格 —— 工程师习惯。
13.7 双击 Esc 检测¶
Esc Esc 模式 —— vim 用户习惯。
13.8 useMaybeTruncateInput¶
防超长输入 —— 保护主线程。
13.9 IDE @ 提及¶
IDE 集成 —— 选中的代码直接 @ 提及。
13.10 useSyncExternalStore¶
外部 store 订阅 —— 不会用 useState 不一致。
14. 阅读建议¶
- 看 Props(行 124)—— 30+ props
- 数 30+ hooks(grep)
- 跳到 useInput —— 键盘处理
- 跳到 onSubmit —— 提交流程
- 跳到 JSX 段 —— render
15. 与其他深度拆解的关系¶
| 文件 | 关系 |
|---|---|
REPL.tsx |
调用方 |
hooks/useXxx.js |
业务封装 |
components/MessageActionsBar |
兄弟组件 |
screens/REPL.tsx |
父级 |
16. 阅读清单¶
- ✅ 看 Props(行 124)
- ✅ 数 hooks
- ✅ 看 useInput
- ✅ 看 onSubmit
- 📌 实际用 / 输入 / 看 mode 切换
17. 练习任务¶
- 数 hooks(grep
use[A-Z]\w+()—— 30+ - 数键盘事件(grep
key\.\|case 'ctrl\|case 'meta)—— 30+ - 画 mode 切换图 —— default/bash/plan/voice
- 手写迷你 PromptInput(~50 行)—— 1 个 useInput + 1 个 submit
- 思考:God Component 的"提示框"能拆成小组件吗?