packages/cli/src/commands/review/__fixtures__/pr-6486-comment-4942713150.md
I built this PR from source and drove the real CLI end-to-end (not just unit tests) to validate the model-toggle hotkey before merge. Sharing the results as a merge reference.
Environment
d0edcbe9 via npm run build (exit 0).tmux PTY with an isolated HOME and settings.json → model: { name: "model-a", toggleModel: "model-b", baseUrl: "…" }, OpenAI auth.| Check | Result |
|---|---|
npm run build | ✅ clean (exit 0) |
npm run typecheck (all packages) | ✅ clean |
keyBindings.test.ts + keyMatchers.test.ts | ✅ 52/52 pass |
Ctrl+F toggles model-a → model-b (header + footer + info message) | ✅ |
Repeated Ctrl+F round-trips via previousModelRef (b → a → b) | ✅ |
? shortcuts panel shows ctrl+f to toggle model; COLUMN_SPLITS renders every entry | ✅ |
Raw Ctrl+F (byte 0x06) is recognized directly — no kitty CSI-u sequence needed.
Repeated toggle round-trip (works):
Shortcuts panel (?) renders the new binding:
This is the earlier [Critical] dual-fire raised by @doudouOUC — it is still reproducible. text-buffer.ts:2663 still binds Ctrl+F → move('right'), and the toggle handler in AppContainer and the input handler in BaseTextInput are independent subscribers of the same KeypressContext.broadcast() (which has no stop-propagation). So the return in the toggle handler does not stop the text buffer from also acting. The guard added in the last round (!activePtyId && !embeddedShellFocused) fixes shell-focus collisions but not this.
Proven reproduction (see screenshot):
1234 → cursor at end.Ctrl+A → cursor to home (before 1).Ctrl+F once, then type X.1X234 — the cursor moved right by one. (Control run without Ctrl+F yields X1234, confirming Ctrl+A homed the cursor; the only difference is Ctrl+F's extra move('right').) The model also toggled to model-b in the same keypress.Impact: harmless when the cursor is already at end-of-input (the move is a no-op — which is exactly why it slips past casual testing), but it silently corrupts cursor position when toggling mid-edit. It also contradicts this PR's own doc change, which removed Ctrl+F from the cursor-right row.
Suggested fix (one line): remove else if (key.ctrl && key.name === 'f') move('right'); at packages/cli/src/ui/components/shared/text-buffer.ts:2663. Since the PR already repurposes Ctrl+F and documents → as the replacement, this simply aligns the code with the docs.
● ● Switched to model-b (cosmetic)InfoMessage already renders a ● prefix (StatusMessages.tsx:72), but the three new messages prepend another ● in the text, producing ● ● Switched to …. Every other MessageType.INFO caller in the codebase passes text without a leading bullet and relies on the renderer's prefix.
Suggested fix: drop the leading ● from the three strings in AppContainer.tsx (lines 3625, 3644, 3658).
The manual-/model invalidation path (onModelChange clearing previousModelRef) and the ⚠ Failed to switch error path were reviewed in code and covered by the matcher/binding unit tests, but not driven end-to-end — the dummy test environment has no second real model to switch to via /model. The invalidation logic reads correctly.
Cleanly scoped feature; the toggle, round-trip, header/footer wiring, and shortcut display all work. One blocker before merge: Finding 1 (Ctrl+F dual-fire) — a one-line removal of the stale text-buffer binding. Finding 2 is trivial polish. With those two changes, this is good to go. 👍
我从源码构建了此 PR,并端到端驱动真实 CLI(不仅是单元测试)来验证合并前的模型切换快捷键,结果作为合并参考分享如下。
环境
d0edcbe9 执行 npm run build(退出码 0)。tmux PTY 驱动,使用隔离的 HOME 和 settings.json → model: { name: "model-a", toggleModel: "model-b", … },OpenAI 鉴权。| 检查项 | 结果 |
|---|---|
npm run build | ✅ 通过(退出码 0) |
npm run typecheck(全部包) | ✅ 通过 |
keyBindings.test.ts + keyMatchers.test.ts | ✅ 52/52 通过 |
Ctrl+F 将 model-a → model-b(标题栏 + 底栏 + 提示消息) | ✅ |
重复按 Ctrl+F 通过 previousModelRef 往返(b → a → b) | ✅ |
? 快捷键面板显示 ctrl+f to toggle model;COLUMN_SPLITS 完整渲染所有条目 | ✅ |
原始 Ctrl+F(字节 0x06)可直接识别——无需 kitty CSI-u 序列。(截图见上方英文部分。)
这正是 @doudouOUC 之前提出的 [Critical] 双重触发 ——目前仍可复现。text-buffer.ts:2663 仍然绑定 Ctrl+F → move('right'),而 AppContainer 中的切换处理器和 BaseTextInput 中的输入处理器是同一个 KeypressContext.broadcast() 的独立订阅者(该广播没有停止传播机制)。因此切换处理器里的 return 并不能阻止文本缓冲区也执行动作。上一轮新增的守卫(!activePtyId && !embeddedShellFocused)修复了 shell 焦点冲突,但没有修复此问题。
已验证的复现步骤(见截图):
1234 → 光标在末尾。Ctrl+A → 光标移到行首(1 之前)。Ctrl+F,然后输入 X。1X234 —— 光标右移了一位。(不按 Ctrl+F 的对照实验得到 X1234,证明 Ctrl+A 确实把光标移到了行首;唯一的差别就是 Ctrl+F 额外的 move('right')。)同一次按键中模型也切换到了 model-b。影响: 当光标已在输入末尾时无害(移动是空操作——这正是它能躲过随手测试的原因),但在编辑过程中切换时会悄悄破坏光标位置。这也与本 PR 自己的文档改动矛盾(文档已将 Ctrl+F 从"右移光标"一行中移除)。
建议修复(一行): 删除 packages/cli/src/ui/components/shared/text-buffer.ts:2663 的 else if (key.ctrl && key.name === 'f') move('right');。由于该 PR 已经把 Ctrl+F 改作他用,并在文档中用 → 作为替代,这只是让代码与文档保持一致。
● ● Switched to model-b(外观问题)InfoMessage 本身已渲染 ● 前缀(StatusMessages.tsx:72),但三条新消息在文本中又加了一个 ● ,导致 ● ● Switched to …。代码库中其他所有 MessageType.INFO 调用都不带前导圆点,依赖渲染器的前缀。
建议修复: 去掉 AppContainer.tsx 中三处(第 3625、3644、3658 行)字符串的前导 ● 。
手动 /model 使 previousModelRef 失效的路径,以及 ⚠ Failed to switch 错误路径,已在代码中审阅并被匹配器/绑定单测覆盖,但未端到端驱动——因为 dummy 测试环境没有第二个真实模型可供 /model 切换。失效逻辑本身阅读下来是正确的。
功能范围清晰;切换、往返、标题栏/底栏联动、快捷键显示均正常工作。合并前有一个阻塞项:发现 1(Ctrl+F 双重触发) —— 只需删除 text-buffer 中那一行残留绑定。发现 2 是很小的打磨。改完这两处即可合并。👍