refactor(cosh): unify escape key handling in appcontainer#437
refactor(cosh): unify escape key handling in appcontainer#437kongld-mq wants to merge 4 commits into
Conversation
Signed-off-by: konglidong <kld02270359@alibaba-inc.com>
|
Important Installation incomplete: to start using Gemini Code Assist, please ask the organization owner(s) to visit the Gemini Code Assist Admin Console and sign the Terms of Services. |
samchu-zsl
left a comment
There was a problem hiding this comment.
Summary
重构方向正确(把分散的 ESC 处理集中到 AppContainer),但实现存在一个阻塞性缺陷:useKeypress 基于 KeypressContext 广播订阅(for (const handler of subscribers) handler(key),无 stopPropagation),ESC 会被 AppContainer 与 InputPrompt 两个订阅者并行处理。InputPrompt 在 shellMode / completion / shellCompletion / reverseSearch / commandSearch 等特定上下文分支里 return,只是结束自身 handler,并不能阻止 AppContainer 同一事件里看到 buffer.text.length > 0 而进入 double-press clear 流程。结果用户在这些场景按一次 ESC 退出 context 后,会看到「Press Esc again to clear」提示,再按一次 ESC 时输入被误清空。
🔴 Blockers
packages/cli/src/ui/AppContainer.tsx:1287-1338— 新 ESC handler 未感知 InputPrompt 的特殊 context 状态,双订阅者冲突导致退出 shellMode / 关闭 completion / 取消 reverseSearch 后再按 ESC 会误清空输入。
🟡 Major
packages/cli/src/ui/AppContainer.tsx:1315— PR description 写「500ms 内按 ESC 第二次」,但实际CTRL_EXIT_PROMPT_DURATION_MS = 1000,双击窗口扩大了一倍,与文档预期不符。packages/cli/src/ui/components/InputPrompt.test.tsx:1629— 移除了 InputPrompt 侧两个双击清空测试,但 AppContainer 侧未补回等效单测;新逻辑(双击清空 / 空 buffer 单击取消 / timer 超时 / embeddedShellFocused 跳过)全部未覆盖。
验证依据
packages/cli/src/ui/contexts/KeypressContext.tsx:385-387:按键事件以for (const handler of subscribers)方式广播到所有订阅者,handler 返回值不被消费,不存在 stopPropagation 语义。packages/cli/src/ui/hooks/useKeypress.ts:订阅/取消订阅,纯 subscriber 模式。packages/cli/src/ui/AppContainer.tsx:1419:useKeypress(handleGlobalKeypress, { isActive: true })— AppContainer 始终激活。packages/cli/src/ui/components/InputPrompt.tsx:745:useKeypress(handleInput, { isActive: !isEmbeddedShellFocused })— 非嵌入 shell 时激活;与 AppContainer 在普通输入场景下并行。packages/cli/src/ui/components/InputPrompt.tsx:388-424:ESC 分支 reverseSearch / commandSearch / shellMode / shellCompletion / completion 只return,未、也无法阻止 AppContainer 的 handler 执行。
建议修复方向
- 把 InputPrompt 内剩余的 ESC 特殊 context 处理一并迁到 AppContainer(通过 UIState/UIActions context 暴露
reverseSearchActive / commandSearchActive / completion.showSuggestions / shellCompletion.showSuggestions及对应 reset 动作),让 AppContainer 的 ESC handler 先判断是否命中特殊 context,命中则调用对应 reset 并return,否则走统一双层语义。 - 或反向:在 AppContainer 的 ESC handler 起始处读取这些 uiState 标志,命中任一则直接
return,把这些场景的处理权显式让回 InputPrompt。 - 任选其一后补齐 AppContainer 的 ESC 单测,并对齐 PR description 的双击窗口(500 vs 1000ms,二选一)。
Scope 说明
变更范围与 PR title 一致,均集中在 packages/cli/src/ui/ 下 7 个文件;无夹带改动。CHANGELOG / PR description Type of Change 等工程纪律项已按作者约定豁免,不在本次评论中提出。
51ccc8c to
83642db
Compare
…rchitecture - UIStateContext.tsx: add reverseSearchActive, commandSearchActive, completionShowSuggestions, shellCompletionShowSuggestions - UIActionsContext.tsx: add set/reset/register functions for state management - AppContainer.tsx: unified ESC handler for special context states - InputPrompt.tsx: consume state from UIState/UIActions context, remove local ESC handler, register reset callbacks - InputPrompt.test.tsx: update mocks, simplify ESC-related tests - test-utils/render.tsx: add UIStateContext/UIActionsContext mock providers ESC handler priority order: reverseSearch → commandSearch → shellMode → completion → shellCompletion → double-press clear Resolves ESC multiple-trigger issue caused by KeypressContext broadcast mechanism
…riginal design - Introduce ESC_DOUBLE_PRESS_WINDOW_MS constant (500ms) separate from CTRL_EXIT_PROMPT_DURATION_MS (1000ms) - Export constants for test verification - Add tests to verify ESC window stays at 500ms, preventing accidental regression to 1000ms The ESC double press window should be 500ms per qwen-code original design, not 1000ms like Ctrl+C/D exit prompt. This fixes the inconsistency between PR description (500ms) and actual implementation (1000ms).
Add test coverage for ESC double-press clear behavior: 1. Verify ESC_DOUBLE_PRESS_WINDOW_MS is 500ms (matching qwen-code original design) 2. Document expected ESC handler behavior: - First ESC: show prompt, set escapePressedOnce, start timer - Second ESC within window: clear buffer, reset state - ESC after timeout: restart first-press flow - ESC with empty buffer + streaming: cancel ongoing request - ESC with embeddedShellFocused: no action - ESC with special contexts (shellMode/reverseSearch/completion): handle context first, not double-press clear
Description
Consolidate Escape key behavior to improve UX and prevent state conflicts:
This refactoring resolves state conflicts caused by having two independent ESC handling systems (escapePressedOnce in AppContainer and escPressCount in InputPrompt). All ESC double-press clear logic is now centralized in AppContainer, providing a cleaner architecture and more predictable behavior.
Type of Change
Scope
cosh(copilot-shell)sec-core(agent-sec-core)skill(os-skills)sight(agentsight)tokenless(tokenless)Checklist
cosh: Lint passes, type check passes, and tests passsec-core(Rust):cargo clippy -- -D warningsandcargo fmt --checkpasssec-core(Python): Ruff format and pytest passskill: Skill directory structure is valid and shell scripts pass syntax checksight:cargo clippy -- -D warningsandcargo fmt --checkpasstokenless:cargo clippy -- -D warningsandcargo fmt --checkpasspackage-lock.json/Cargo.lock)Testing
cd src/copilot-shell,npm run format,npm run lint,npm run test
有输入时按 ESC 的行为
无输入时按 ESC 的行为
Additional Notes