-
Notifications
You must be signed in to change notification settings - Fork 2.9k
fix(cli): switch @ completion category tabs with bare arrow keys #8576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -185,20 +185,24 @@ export const defaultKeyBindings: KeyBindingConfig = { | |
| { key: 'n', ctrl: true }, | ||
| ], | ||
| // Completion category tab switching (for the tabbed @ completion UI). | ||
| // Bound to Ctrl+arrows rather than plain arrows so the bare arrow keys keep | ||
| // moving the caret in the editable input buffer (plain arrows only switch | ||
| // tabs in modal dialogs, which have no text buffer). Alt/Option+arrows still | ||
| // perform word movement. | ||
| // Ctrl+←/→ is the primary binding but many terminals intercept it for | ||
| // word-jump. Ctrl+Tab / Ctrl+Shift+Tab are alternatives that are less | ||
| // commonly intercepted (#8069). | ||
| // Bound to the BARE arrow keys: Ctrl+←/→ was the original binding but many | ||
| // terminals intercept it for word-jump, and on macOS the system claims it | ||
| // for Mission Control, so the documented gesture was unreachable for most | ||
| // users (#8069). | ||
| // | ||
| // Tradeoff, accepted deliberately: while the `@` category tabs are visible, | ||
| // the bare arrows no longer move the caret in the input buffer — press Esc | ||
| // to dismiss the menu first. InputPrompt only renders and handles the tabs | ||
| // when they own the arrows, so search and attachment navigation keep their | ||
| // normal behavior. | ||
| // | ||
| // Modifiers are pinned false so Alt/Option+arrow word movement and any | ||
| // Ctrl+arrow terminal binding fall through untouched. | ||
| [Command.COMPLETION_TAB_LEFT]: [ | ||
| { key: 'left', shift: false, ctrl: true, command: false }, | ||
| { key: 'tab', shift: true, ctrl: true, command: false }, | ||
| { key: 'left', shift: false, ctrl: false, meta: false }, | ||
| ], | ||
|
Comment on lines
201
to
203
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Removing the Ctrl+Tab / Ctrl+Shift+Tab bindings also deleted the only thing shadowing the modifier-agnostic 中文说明[Suggestion] 删除 Ctrl+Tab / Ctrl+Shift+Tab 绑定的同时,也移除了唯一遮蔽不区分修饰键的 — qwen3.8-max via Qwen Code /review (v0.21.5) |
||
| [Command.COMPLETION_TAB_RIGHT]: [ | ||
| { key: 'right', shift: false, ctrl: true, command: false }, | ||
| { key: 'tab', shift: false, ctrl: true, command: false }, | ||
| { key: 'right', shift: false, ctrl: false, meta: false }, | ||
| ], | ||
|
|
||
| // Text input | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2709,7 +2709,7 @@ describe('InputPrompt', () => { | |||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('should NOT switch category on Ctrl+left/right when availableCategories is exactly 2', async () => { | ||||||||||||||||||||||||||||||
| it('should NOT switch category on left/right when availableCategories is exactly 2', async () => { | ||||||||||||||||||||||||||||||
| const switchCategory = vi.fn(); | ||||||||||||||||||||||||||||||
| mockedUseCommandCompletion.mockReturnValue({ | ||||||||||||||||||||||||||||||
| ...mockCommandCompletion, | ||||||||||||||||||||||||||||||
|
|
@@ -2726,18 +2726,18 @@ describe('InputPrompt', () => { | |||||||||||||||||||||||||||||
| const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5C'); // Ctrl+right arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[C'); // right arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5D'); // Ctrl+left arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[D'); // left arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // With only 2 entries (all + one real category) the tab bar is hidden, | ||||||||||||||||||||||||||||||
| // so Ctrl+arrows must not trigger category switching. | ||||||||||||||||||||||||||||||
| // so the arrows must not trigger category switching. | ||||||||||||||||||||||||||||||
| expect(switchCategory).not.toHaveBeenCalled(); | ||||||||||||||||||||||||||||||
|
Comment on lines
2734
to
2736
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The hidden-tab-bar tests assert only that switching does not happen (
Suggested change
中文说明[Suggestion] 标签栏隐藏的测试只断言了切换不会发生( — qwen3.8-max via Qwen Code /review (v0.21.7) |
||||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('should switch category on Ctrl+left/right when availableCategories > 2', async () => { | ||||||||||||||||||||||||||||||
| it('should switch category on plain arrows before Vim handling', async () => { | ||||||||||||||||||||||||||||||
| const switchCategory = vi.fn(); | ||||||||||||||||||||||||||||||
| mockedUseCommandCompletion.mockReturnValue({ | ||||||||||||||||||||||||||||||
| ...mockCommandCompletion, | ||||||||||||||||||||||||||||||
|
|
@@ -2753,23 +2753,99 @@ describe('InputPrompt', () => { | |||||||||||||||||||||||||||||
| switchCategory, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| props.buffer.setText('@'); | ||||||||||||||||||||||||||||||
| props.vimHandleInput = vi.fn().mockReturnValue(true); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5C'); // Ctrl+right arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[C'); // plain right arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
Comment on lines
+2761
to
2762
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] R2-6: The positive category-switching tests assert Fix idea: after the arrow, also assert the buffer state — e.g. cursor position / 中文说明[Suggestion] R2-6:正向的分类切换测试只断言了 修复建议:在方向键之后追加缓冲区状态断言——例如光标位置/ — qwen3.8-max via Qwen Code /review (v0.21.6) |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| expect(switchCategory).toHaveBeenCalledWith(1); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5D'); // Ctrl+left arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[D'); // plain left arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| expect(switchCategory).toHaveBeenCalledWith(-1); | ||||||||||||||||||||||||||||||
|
Comment on lines
+2766
to
+2769
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] These positive tests assert the category switched but not that the arrow was consumed — half of the PR's stated behaviour ("the arrows no longer move the caret") is unverified. Failure scenario (mutation proven at this commit): deleting both Suggested fix: after each arrow write in the expect(mockBuffer.move).not.toHaveBeenCalled();
expect(mockBuffer.handleInput).not.toHaveBeenCalled();中文说明这些正向测试断言了分类发生切换,却没有断言方向键被消费——PR 所声明行为的一半("方向键不再移动光标")未被验证。 故障场景(已在本提交上做变异验证):删除 建议修复:在 expect(mockBuffer.move).not.toHaveBeenCalled();
expect(mockBuffer.handleInput).not.toHaveBeenCalled();— qwen3.8-max via Qwen Code /review (v0.21.5) |
||||||||||||||||||||||||||||||
| expect(props.vimHandleInput).not.toHaveBeenCalled(); | ||||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('should NOT switch category on bare arrows while command search is active', async () => { | ||||||||||||||||||||||||||||||
| props.shellModeActive = false; | ||||||||||||||||||||||||||||||
| const switchCategory = vi.fn(); | ||||||||||||||||||||||||||||||
| mockedUseCommandCompletion.mockReturnValue({ | ||||||||||||||||||||||||||||||
| ...mockCommandCompletion, | ||||||||||||||||||||||||||||||
| completionMode: CompletionMode.AT, | ||||||||||||||||||||||||||||||
| showSuggestions: true, | ||||||||||||||||||||||||||||||
| suggestions: [ | ||||||||||||||||||||||||||||||
| { label: 'file.ts', value: 'file.ts', category: 'file' }, | ||||||||||||||||||||||||||||||
| { label: 'sess', value: 'sess', category: 'session' }, | ||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||
| activeSuggestionIndex: 0, | ||||||||||||||||||||||||||||||
| isPerfectMatch: false, | ||||||||||||||||||||||||||||||
| availableCategories: ['all', 'file', 'session'], | ||||||||||||||||||||||||||||||
| switchCategory, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| props.buffer.setText('@ses'); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write('\x12'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[C'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[D'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| expect(switchCategory).not.toHaveBeenCalled(); | ||||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('should NOT switch category on plain left/right when availableCategories > 2 (caret stays free)', async () => { | ||||||||||||||||||||||||||||||
| it('should hide category tabs and keep bare arrows for attachments', async () => { | ||||||||||||||||||||||||||||||
| const isWindows = process.platform === 'win32'; | ||||||||||||||||||||||||||||||
| vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(true); | ||||||||||||||||||||||||||||||
| vi.mocked(clipboardUtils.saveClipboardImage).mockResolvedValue( | ||||||||||||||||||||||||||||||
| path.join('test', 'project', '.qwen', 'tmp', 'clipboard.png'), | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| vi.mocked(clipboardUtils.cleanupOldClipboardImages).mockResolvedValue( | ||||||||||||||||||||||||||||||
| undefined, | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const switchCategory = vi.fn(); | ||||||||||||||||||||||||||||||
| mockedUseCommandCompletion.mockReturnValue({ | ||||||||||||||||||||||||||||||
| ...mockCommandCompletion, | ||||||||||||||||||||||||||||||
| completionMode: CompletionMode.AT, | ||||||||||||||||||||||||||||||
| showSuggestions: true, | ||||||||||||||||||||||||||||||
| suggestions: [{ label: 'file.ts', value: 'file.ts', category: 'file' }], | ||||||||||||||||||||||||||||||
| activeSuggestionIndex: 0, | ||||||||||||||||||||||||||||||
| isPerfectMatch: false, | ||||||||||||||||||||||||||||||
| availableCategories: ['all', 'file', 'session'], | ||||||||||||||||||||||||||||||
| switchCategory, | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| props.buffer.setText('@'); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const { stdin, lastFrame, unmount } = renderWithProviders( | ||||||||||||||||||||||||||||||
| <InputPrompt {...props} />, | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write(isWindows ? '\x1Bv' : '\x16'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[A'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[C'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[D'); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| expect(switchCategory).not.toHaveBeenCalled(); | ||||||||||||||||||||||||||||||
| expect(stripAnsi(lastFrame() ?? '')).not.toContain('(←/→ to switch)'); | ||||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('should NOT consume Ctrl+left/right for category switching (#8069)', async () => { | ||||||||||||||||||||||||||||||
| const switchCategory = vi.fn(); | ||||||||||||||||||||||||||||||
| mockedUseCommandCompletion.mockReturnValue({ | ||||||||||||||||||||||||||||||
| ...mockCommandCompletion, | ||||||||||||||||||||||||||||||
|
|
@@ -2789,13 +2865,13 @@ describe('InputPrompt', () => { | |||||||||||||||||||||||||||||
| const { stdin, unmount } = renderWithProviders(<InputPrompt {...props} />); | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| stdin.write('\x1b[C'); // plain right arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5C'); // Ctrl+right arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[D'); // plain left arrow | ||||||||||||||||||||||||||||||
| stdin.write('\x1b[1;5D'); // Ctrl+left arrow | ||||||||||||||||||||||||||||||
| await wait(); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // Plain arrows must not be hijacked for tab switching, so they remain | ||||||||||||||||||||||||||||||
| // available to move the caret in the editable buffer. | ||||||||||||||||||||||||||||||
| // Ctrl+arrows are no longer bound: terminals and macOS Mission Control | ||||||||||||||||||||||||||||||
| // intercept them, so they are left to fall through to the terminal. | ||||||||||||||||||||||||||||||
| expect(switchCategory).not.toHaveBeenCalled(); | ||||||||||||||||||||||||||||||
| unmount(); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -382,6 +382,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ | |
| ); | ||
| const showCompletionSuggestions = | ||
| completion.showSuggestions && !isHistoryRestoredText; | ||
| const categoryTabsVisible = | ||
| !exportCompletion.suggestionDisplayProps && | ||
| !commandSearchActive && | ||
| !reverseSearchActive && | ||
| !isAttachmentMode && | ||
| (completion.availableCategories?.length ?? 0) > 2; | ||
|
|
||
| // Ref so renderLineWithHighlighting (stable useCallback) can access fresh ghost text | ||
| const midInputGhostTextRef = useRef<{ | ||
|
|
@@ -1081,6 +1087,21 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ | |
| return true; | ||
| } | ||
|
|
||
| // The visible category tabs own the bare arrows, including in Vim mode. | ||
| // All other states fall through to their existing input owner. | ||
| if (showCompletionSuggestions && categoryTabsVisible) { | ||
|
Comment on lines
+1090
to
+1092
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Consuming bare Left/Right here — before Suggested fix direction: when this block consumes an arrow while vim is active, clear vim's pending operator/count first (expose a reset from 中文说明[Critical] 在此处—— 修复方向建议:当此分支在 vim 激活时消费方向键时,先清除 vim 的待命操作符/计数(从 — qwen3.8-max via Qwen Code /review (v0.21.8) |
||
| if (keyMatchers[Command.COMPLETION_TAB_RIGHT](key)) { | ||
| completion.switchCategory(1); | ||
| setExpandedSuggestionIndex(-1); | ||
| return true; | ||
| } | ||
| if (keyMatchers[Command.COMPLETION_TAB_LEFT](key)) { | ||
| completion.switchCategory(-1); | ||
| setExpandedSuggestionIndex(-1); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| if (vimHandleInput && vimHandleInput(key)) { | ||
| return true; | ||
| } | ||
|
|
@@ -1442,23 +1463,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ | |
| } | ||
|
|
||
| if (showCompletionSuggestions) { | ||
| // Category tab switching for the tabbed `@` completion UI. Only consume | ||
| // Ctrl+←/→ (per the COMPLETION_TAB_* bindings) and only when there are | ||
| // more than two tabs (at least 3 entries including 'all'). Plain ←/→ are | ||
| // never consumed here, so they always move the caret in the editable buffer. | ||
| if ((completion.availableCategories?.length ?? 0) > 2) { | ||
| if (keyMatchers[Command.COMPLETION_TAB_RIGHT](key)) { | ||
| completion.switchCategory(1); | ||
| setExpandedSuggestionIndex(-1); | ||
| return true; | ||
| } | ||
| if (keyMatchers[Command.COMPLETION_TAB_LEFT](key)) { | ||
| completion.switchCategory(-1); | ||
| setExpandedSuggestionIndex(-1); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| if (completion.suggestions.length > 1) { | ||
| const isCompletionUpKey = keyMatchers[Command.COMPLETION_UP](key); | ||
| const isCompletionDownKey = keyMatchers[Command.COMPLETION_DOWN](key); | ||
|
|
@@ -1907,6 +1911,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ | |
| exportCompletion, | ||
| isHistoryRestoredText, | ||
| showCompletionSuggestions, | ||
| categoryTabsVisible, | ||
| voiceInput, | ||
| targetDir, | ||
| ], | ||
|
|
@@ -2308,11 +2313,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({ | |
| : completion.activeCategory | ||
| } | ||
| availableCategories={ | ||
| suggestionsFromExport || | ||
| commandSearchActive || | ||
| reverseSearchActive | ||
| ? undefined | ||
| : completion.availableCategories | ||
| categoryTabsVisible ? completion.availableCategories : undefined | ||
| } | ||
|
Comment on lines
2315
to
2317
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Suggested fix: reset the completion category to 中文说明[Suggestion] 修复建议:进入附件模式时将补全分类重置为 — qwen3.8-max via Qwen Code /review (v0.21.8) |
||
| onHoverIndex={ | ||
| suggestionsFromExport ? undefined : handleSuggestionHover | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -187,12 +187,10 @@ export function SuggestionsDisplay({ | |
| </Box> | ||
| ); | ||
| })} | ||
| {/* Mention Ctrl+Tab as an alternative since many terminals | ||
| intercept Ctrl+←/→ for word-jump (#8069). */} | ||
| {/* Bare ←/→: the original Ctrl+←/→ was unreachable because terminals | ||
| and macOS Mission Control intercept it (#8069). */} | ||
| <Box marginLeft={2}> | ||
| <Text color={theme.text.secondary}> | ||
| {t('(Ctrl+Tab / Ctrl+Shift+Tab or Ctrl+←/→ to switch)')} | ||
| </Text> | ||
| <Text color={theme.text.secondary}>{t('(←/→ to switch)')}</Text> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] 中文说明[Suggestion] — qwen3.8-max via Qwen Code /review (v0.21.5) |
||
| </Box> | ||
| </Box> | ||
| )} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] The PR says "Closes #8069", but #8069 was already closed on 2026-07-31 by merged PR #8074 (the Ctrl+Tab alternative this PR removes). The live gap this PR actually fixes — Ctrl+Tab being intercepted by Warp — is tracked in still-OPEN issue #8330, and the complementary mouse-click fix is open PR #8395; neither is referenced. — Concrete cost: after merge, #8330 stays open with no cross-reference, "Closes #8069" is a no-op link, and two open PRs touching the same picker files (#8395, #8576) are not connected anywhere, risking conflicting review/merge decisions. Suggested fix: update the PR description, e.g. "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074".
中文说明
[Suggestion] 本 PR 声明 "Closes #8069",但 #8069 已于 2026-07-31 被合并的 PR #8074(即本 PR 所移除的 Ctrl+Tab 备选方案)关闭。本 PR 实际修复的活跃缺口——Ctrl+Tab 被 Warp 拦截——记录在仍开放的 issue #8330 中;互补的鼠标点击修复是开放 PR #8395;二者均未被引用。— 具体代价:合并后 #8330 仍然开放且没有交叉引用;"Closes #8069" 成为无效链接;两个触及同一补全选择器文件的开放 PR(#8395、#8576)互不关联,可能导致冲突的评审/合并决定。建议更新 PR 描述,例如 "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074"。
— qwen3.8-max via Qwen Code /review (v0.21.5)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accurate — #8069 was already closed by merged PR #8074, and the live gap this PR actually closes is #8330 (Ctrl+Tab intercepted by Warp), with the complementary mouse-click fix in #8395. Updating the PR description is a GitHub write that this automated round cannot perform (it has no GitHub credentials; the workflow owns all GitHub writes), so it must be applied by a maintainer rather than in code. Suggested description wording: "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074." No code change is required for this finding.
中文说明
确实如此—— #8069 已被合并的 PR #8074 关闭,本 PR 实际修复的活跃缺口是 #8330(Ctrl+Tab 被 Warp 拦截),互补的鼠标点击修复是 #8395。更新 PR 描述属于 GitHub 写操作,本轮自动化无法执行(没有 GitHub 凭据,所有 GitHub 写操作由工作流负责),因此需要由维护者应用而非通过代码完成。建议的描述措辞为:"Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074"。该发现无需代码改动。