跳转至

Deep Dive | src/components/PromptInput/PromptInput.tsx 2338 行 — 提示输入框(多模态编辑器)

重要性:⭐⭐⭐(用户输入核心 UI——30+ hooks 编排、bash 模式、history、autocomplete) 真实位置src/components/PromptInput/PromptInput.tsx2338 行角色: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)

function getInitialPasteId(messages: Message[]): number {
  // 找到最后 paste 的 id +1
}

初始 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

export default React.memo(PromptInput);

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. 阅读建议

  1. 看 Props(行 124)—— 30+ props
  2. 数 30+ hooks(grep)
  3. 跳到 useInput —— 键盘处理
  4. 跳到 onSubmit —— 提交流程
  5. 跳到 JSX 段 —— render

15. 与其他深度拆解的关系

文件 关系
REPL.tsx 调用方
hooks/useXxx.js 业务封装
components/MessageActionsBar 兄弟组件
screens/REPL.tsx 父级

16. 阅读清单

  1. ✅ 看 Props(行 124)
  2. ✅ 数 hooks
  3. ✅ 看 useInput
  4. ✅ 看 onSubmit
  5. 📌 实际用 / 输入 / 看 mode 切换

17. 练习任务

  1. 数 hooks(grep use[A-Z]\w+()—— 30+
  2. 数键盘事件(grep key\.\|case 'ctrl\|case 'meta)—— 30+
  3. 画 mode 切换图 —— default/bash/plan/voice
  4. 手写迷你 PromptInput(~50 行)—— 1 个 useInput + 1 个 submit
  5. 思考:God Component 的"提示框"能拆成小组件吗?