mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat(vscode-ide-companion): enhance panel manager group tracking and cleanup
- Add panelGroupViewColumn tracking for precise group identification - Implement improved tab capture mechanism with delayed execution - Add robust group cleanup logic to close empty locked columns - Implement focusGroupByColumn helper for reliable group focusing - Add proper error handling and fallback mechanisms for group operations
This commit is contained in:
@@ -13,6 +13,10 @@ import * as vscode from 'vscode';
|
|||||||
export class PanelManager {
|
export class PanelManager {
|
||||||
private panel: vscode.WebviewPanel | null = null;
|
private panel: vscode.WebviewPanel | null = null;
|
||||||
private panelTab: vscode.Tab | null = null;
|
private panelTab: vscode.Tab | null = null;
|
||||||
|
// Best-effort tracking of the group (by view column) that currently hosts
|
||||||
|
// the Qwen webview. We update this when creating/revealing the panel and
|
||||||
|
// whenever we can capture the Tab from the tab model.
|
||||||
|
private panelGroupViewColumn: vscode.ViewColumn | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private extensionUri: vscode.Uri,
|
private extensionUri: vscode.Uri,
|
||||||
@@ -64,6 +68,8 @@ export class PanelManager {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// Track the group column hosting this panel
|
||||||
|
this.panelGroupViewColumn = existingGroup.viewColumn;
|
||||||
} else {
|
} else {
|
||||||
// If no existing Qwen Code group, create a new group to the right of the active editor group
|
// If no existing Qwen Code group, create a new group to the right of the active editor group
|
||||||
try {
|
try {
|
||||||
@@ -114,6 +120,9 @@ export class PanelManager {
|
|||||||
|
|
||||||
// Lock the group after creation
|
// Lock the group after creation
|
||||||
await this.autoLockEditorGroup();
|
await this.autoLockEditorGroup();
|
||||||
|
|
||||||
|
// Track the newly created group's column
|
||||||
|
this.panelGroupViewColumn = newGroupColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set panel icon to Qwen logo
|
// Set panel icon to Qwen logo
|
||||||
@@ -123,6 +132,10 @@ export class PanelManager {
|
|||||||
'icon.png',
|
'icon.png',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Try to capture Tab info shortly after creation so we can track the
|
||||||
|
// precise group even if the user later drags the tab between groups.
|
||||||
|
this.captureTab();
|
||||||
|
|
||||||
return true; // New panel created
|
return true; // New panel created
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +229,19 @@ export class PanelManager {
|
|||||||
return !!(sameViewType || sameLabel);
|
return !!(sameViewType || sameLabel);
|
||||||
});
|
});
|
||||||
this.panelTab = match ?? null;
|
this.panelTab = match ?? null;
|
||||||
|
// Update last-known group column if we can read it from the captured tab
|
||||||
|
try {
|
||||||
|
const groupViewColumn = (
|
||||||
|
this.panelTab as unknown as {
|
||||||
|
group?: { viewColumn?: vscode.ViewColumn };
|
||||||
|
}
|
||||||
|
)?.group?.viewColumn;
|
||||||
|
if (groupViewColumn != null) {
|
||||||
|
this.panelGroupViewColumn = groupViewColumn as vscode.ViewColumn;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Best effort only; ignore if the API shape differs
|
||||||
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,15 +256,94 @@ export class PanelManager {
|
|||||||
|
|
||||||
this.panel.onDidDispose(
|
this.panel.onDidDispose(
|
||||||
() => {
|
() => {
|
||||||
|
// Capture the group we intend to clean up before we clear fields
|
||||||
|
const targetColumn =
|
||||||
|
// Prefer the group from the captured tab if available
|
||||||
|
((
|
||||||
|
this.panelTab as unknown as {
|
||||||
|
group?: { viewColumn?: vscode.ViewColumn };
|
||||||
|
}
|
||||||
|
)?.group?.viewColumn as vscode.ViewColumn | undefined) ??
|
||||||
|
// Fall back to our last-known group column
|
||||||
|
this.panelGroupViewColumn ??
|
||||||
|
undefined;
|
||||||
|
|
||||||
this.panel = null;
|
this.panel = null;
|
||||||
this.panelTab = null;
|
this.panelTab = null;
|
||||||
this.onPanelDispose();
|
this.onPanelDispose();
|
||||||
|
|
||||||
|
// After VS Code updates its tab model, check if that group is now
|
||||||
|
// empty (and typically locked for Qwen). If so, close the group to
|
||||||
|
// avoid leaving an empty locked column when the user closes Qwen.
|
||||||
|
if (targetColumn != null) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const groups = vscode.window.tabGroups.all;
|
||||||
|
const group = groups.find((g) => g.viewColumn === targetColumn);
|
||||||
|
// If the group that hosted Qwen is now empty, close it to avoid
|
||||||
|
// leaving an empty locked column around. VS Code's stable API
|
||||||
|
// does not expose the lock state on TabGroup, so we only check
|
||||||
|
// for emptiness here.
|
||||||
|
if (group && group.tabs.length === 0) {
|
||||||
|
// Focus the group we want to close
|
||||||
|
await this.focusGroupByColumn(targetColumn);
|
||||||
|
// Try closeGroup first; fall back to removeActiveEditorGroup
|
||||||
|
try {
|
||||||
|
await vscode.commands.executeCommand(
|
||||||
|
'workbench.action.closeGroup',
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
await vscode.commands.executeCommand(
|
||||||
|
'workbench.action.removeActiveEditorGroup',
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(
|
||||||
|
'[PanelManager] Failed to close empty group after Qwen panel disposed:',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(
|
||||||
|
'[PanelManager] Error while trying to close empty Qwen group:',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
disposables,
|
disposables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focus the editor group at the given view column by stepping left/right.
|
||||||
|
* This avoids depending on Nth-group focus commands that may not exist.
|
||||||
|
*/
|
||||||
|
private async focusGroupByColumn(target: vscode.ViewColumn): Promise<void> {
|
||||||
|
const maxHops = 20; // safety guard for unusual layouts
|
||||||
|
let hops = 0;
|
||||||
|
while (
|
||||||
|
vscode.window.tabGroups.activeTabGroup.viewColumn !== target &&
|
||||||
|
hops < maxHops
|
||||||
|
) {
|
||||||
|
const current = vscode.window.tabGroups.activeTabGroup.viewColumn;
|
||||||
|
if (current < target) {
|
||||||
|
await vscode.commands.executeCommand(
|
||||||
|
'workbench.action.focusRightGroup',
|
||||||
|
);
|
||||||
|
} else if (current > target) {
|
||||||
|
await vscode.commands.executeCommand('workbench.action.focusLeftGroup');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
hops++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the view state change event handler
|
* Register the view state change event handler
|
||||||
* @param disposables Array used to store Disposable objects
|
* @param disposables Array used to store Disposable objects
|
||||||
|
|||||||
Reference in New Issue
Block a user