🐛 fix(vscode): 修好单个会话 tab 从看板点回时不反向选中所属工单

Claude-Session: https://claude.ai/code/session_01FAA5YEyR7fzWj7gYQiLSqy
This commit is contained in:
cruldra committed 2026-08-26 13:42:30 +08:00
1 parent eee0170a63
commit 06fb5ecec2
4 files changed
+157 -77

No files matched your search

+9 -26
View File
@@ -118,7 +118,7 @@ export function findExistingTerminal(panel: KanbanWebviewPanel, expectedName: st
* no-op (user has to press Enter to spawn one).
*/
export function handleSessionFocus(panel: KanbanWebviewPanel, issueNumber: number): void {
// 如果这次 session/focus 是 onDidChangeActiveTerminal 反选触发的回路
// 如果这次 session/focus 是终端 tab 切换反选触发的回路
// (而不是用户主动点卡片切换工单),跳过优先级跳转,否则会把用户刚刚
// 点的"审查 tab"弹回到优先级更高的"实施 tab"。
const REVERSE_LOOP_WINDOW_MS = 200
@@ -135,10 +135,8 @@ export function handleSessionFocus(panel: KanbanWebviewPanel, issueNumber: numbe
// side-map is the only way to find it by issueNumber.
const newIssueTerm = panel.newIssueTerminals.get(issueNumber)
if (newIssueTerm && newIssueTerm.exitStatus === undefined) {
if (newIssueTerm !== window.activeTerminal) {
panel.programmaticTabReveal = newIssueTerm
if (newIssueTerm !== window.activeTerminal)
newIssueTerm.show(true)
}
return
}
// Priority 1-4: 实施 > 规划 > 审查 > 测试. Match by terminal.name since we know
@@ -152,12 +150,10 @@ export function handleSessionFocus(panel: KanbanWebviewPanel, issueNumber: numbe
for (const name of namePriority) {
const term = findExistingTerminal(panel, name)
if (term) {
// 已经是 active terminal 就不重复 show(避免无谓的回声事件);
// 否则记下程序化 reveal,让随后的 active 变更被吞掉,不回环。
if (term !== window.activeTerminal) {
panel.programmaticTabReveal = term
// preserveFocus 只在分组内切 tab、不激活分组,因此不会被当成
// 用户切到终端而反向选中;已是 active terminal 则无需重复 show。
if (term !== window.activeTerminal)
term.show(true)
}
return
}
}
@@ -165,27 +161,14 @@ export function handleSessionFocus(panel: KanbanWebviewPanel, issueNumber: numbe
}
/**
* 反向选中:用户在 column 2 切换 terminal tab 时,从 terminal.name 解析
* issueNumber 并通知 webview 选中对应工单。
* 反向选中:用户把焦点切到 column 2 的某个 terminal tab 时,从 terminal.name
* 解析 issueNumber 并通知 webview 选中对应工单。去重与回声抑制由调用方的
* `FocusedTerminalTracker` 负责,这里每次调用都视为一次真实切换。
*
* 终端名按 `issue-${N}-(规划|实施|审查)` 命名;`issue-new-${nonce}-...`
* 终端名按 `issue-${N}-(规划|实施|审查|测试)` 命名;`issue-new-${nonce}-...`
* 是新建工单流程的占位 tab,没有 issue number,跳过。
*/
export function handleActiveTerminalChanged(panel: KanbanWebviewPanel, terminal: Terminal): void {
// Dedupe by reference: OSC title rewrites / shell prompt updates fire
// onDidChangeTabs repeatedly for the same terminal. Skip if it's the
// same reference we just handled.
// 吞掉「我们自己 show() 引发的」active 变更回声:handleSessionFocus 在程序化
// show 前把目标终端记到 programmaticTabReveal,这里命中就 return,不反向选中,
// 从根上打断 选中→聚焦→选中 的死循环(基于引用,不依赖脆弱的时间窗)。
// 每次调用都清空,因此只压制紧随其后的那一次回声,用户真实点 tab 不受影响。
const reveal = panel.programmaticTabReveal
panel.programmaticTabReveal = undefined
if (terminal === reveal)
return
if (terminal === panel.lastActiveTerminalRef)
return
panel.lastActiveTerminalRef = terminal
// Primary: `issue-${N}-(规划|实施|审查|测试)` — the steady-state naming.
const m = terminal.name.match(/^issue-(\d+)-(规划|实施|审查|测试)/)
if (m) {