feat(vscode-ide-companion): 优化 Qwen Code 聊天窗口创建逻辑

- 修改 createPanel 方法返回值类型,使其支持异步操作
- 实现聚焦当前激活编辑器的功能
- 优化多窗口创建逻辑,允许在已有 Qwen Code 窗口旁边创建新窗口
- 移除自动锁定编辑器组的功能,以支持多个 Qwen Code 标签页
- 在 UI 中添加聚焦当前文件的按钮
This commit is contained in:
yiliang114
2025-11-20 23:41:41 +08:00
parent 5a9f5e3432
commit 7d2411e72f
4 changed files with 131 additions and 33 deletions

View File

@@ -117,7 +117,7 @@ export class WebViewProvider {
} }
// Create new panel // Create new panel
const isNewPanel = this.panelManager.createPanel(); const isNewPanel = await this.panelManager.createPanel();
if (!isNewPanel) { if (!isNewPanel) {
return; // Failed to create panel return; // Failed to create panel

View File

@@ -1048,12 +1048,33 @@ export const App: React.FC = () => {
<span>{getEditModeInfo().text}</span> <span>{getEditModeInfo().text}</span>
</button> </button>
{activeFileName && ( {activeFileName && (
<span <button
className="active-file-indicator" type="button"
className="action-button active-file-indicator"
title={`Showing Qwen Code your current file selection: ${activeFileName}`} title={`Showing Qwen Code your current file selection: ${activeFileName}`}
onClick={() => {
// Request to focus/reveal the active file
vscode.postMessage({
type: 'focusActiveEditor',
data: {},
});
}}
> >
{activeFileName} <svg
</span> xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
data-slot="icon"
>
<path
fillRule="evenodd"
d="M6.28 5.22a.75.75 0 0 1 0 1.06L2.56 10l3.72 3.72a.75.75 0 0 1-1.06 1.06L.97 10.53a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Zm7.44 0a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L17.44 10l-3.72-3.72a.75.75 0 0 1 0-1.06ZM11.377 2.011a.75.75 0 0 1 .612.867l-2.5 14.5a.75.75 0 0 1-1.478-.255l2.5-14.5a.75.75 0 0 1 .866-.612Z"
clipRule="evenodd"
></path>
</svg>
<span>{activeFileName}</span>
</button>
)} )}
<div className="action-divider"></div> <div className="action-divider"></div>
<button <button

View File

@@ -141,6 +141,18 @@ export class MessageHandler {
break; break;
} }
case 'focusActiveEditor': {
// 聚焦到当前激活的编辑器
const activeEditor = vscode.window.activeTextEditor;
if (activeEditor) {
vscode.window.showTextDocument(activeEditor.document, {
viewColumn: activeEditor.viewColumn,
preserveFocus: false,
});
}
break;
}
case 'switchQwenSession': case 'switchQwenSession':
await this.handleSwitchQwenSession((data?.sessionId as string) || ''); await this.handleSwitchQwenSession((data?.sessionId as string) || '');
break; break;

View File

@@ -37,17 +37,65 @@ export class PanelManager {
* 创建新的 WebView Panel * 创建新的 WebView Panel
* @returns 是否是新创建的 Panel * @returns 是否是新创建的 Panel
*/ */
createPanel(): boolean { async createPanel(): Promise<boolean> {
if (this.panel) { if (this.panel) {
return false; // Panel already exists return false; // Panel already exists
} }
// Find if there's already a Qwen Code webview tab open and get its view column // Find if there's already a Qwen Code webview tab open and get its view column
const existingQwenViewColumn = this.findExistingQwenCodeViewColumn(); const existingQwenInfo = this.findExistingQwenCodeGroup();
// If we found an existing Qwen Code tab, open in the same view column // If we found an existing Qwen Code tab, open in the same view column
// Otherwise, open beside the active editor // Otherwise, open beside the active editor
const targetViewColumn = existingQwenViewColumn ?? vscode.ViewColumn.Beside; const targetViewColumn =
existingQwenInfo?.viewColumn ?? vscode.ViewColumn.Beside;
console.log('[PanelManager] existingQwenInfo', existingQwenInfo);
console.log('[PanelManager] targetViewColumn', targetViewColumn);
// If there's an existing Qwen Code group, ensure it's unlocked so we can add new tabs
// We try to unlock regardless of current state - if already unlocked, this is a no-op
if (existingQwenInfo?.group) {
console.log(
"[PanelManager] Found existing Qwen Code group, ensuring it's unlocked...",
);
try {
// We need to make the target group active first
// Find a Qwen Code tab in that group
const firstQwenTab = existingQwenInfo.group.tabs.find((tab) => {
const input: unknown = (tab as { input?: unknown }).input;
const isWebviewInput = (inp: unknown): inp is { viewType: string } =>
!!inp && typeof inp === 'object' && 'viewType' in inp;
return (
isWebviewInput(input) &&
input.viewType === 'mainThreadWebview-qwenCode.chat'
);
});
if (firstQwenTab) {
// Make the group active by focusing on one of its tabs
const activeTabGroup = vscode.window.tabGroups.activeTabGroup;
if (activeTabGroup !== existingQwenInfo.group) {
// Switch to the target group
await vscode.commands.executeCommand(
'workbench.action.focusFirstEditorGroup',
);
}
}
// Try to unlock the group (will be no-op if already unlocked)
await vscode.commands.executeCommand(
'workbench.action.unlockEditorGroup',
);
console.log('[PanelManager] Unlock command executed');
} catch (error) {
console.warn(
'[PanelManager] Failed to unlock group, continuing anyway:',
error,
);
// Continue anyway - the group might not be locked
}
}
this.panel = vscode.window.createWebviewPanel( this.panel = vscode.window.createWebviewPanel(
'qwenCode.chat', 'qwenCode.chat',
@@ -77,23 +125,33 @@ export class PanelManager {
} }
/** /**
* 查找已存在的 Qwen Code webview 所在的 view column * 查找已存在的 Qwen Code webview 所在的 group 和 view column
* @returns 找到的 view column如果没有则返回 undefined * @returns 找到的 group 和 view column如果没有则返回 undefined
*/ */
private findExistingQwenCodeViewColumn(): vscode.ViewColumn | undefined { private findExistingQwenCodeGroup():
const allTabs = vscode.window.tabGroups.all.flatMap((g) => g.tabs); | { group: vscode.TabGroup; viewColumn: vscode.ViewColumn }
| undefined {
for (const group of vscode.window.tabGroups.all) {
for (const tab of group.tabs) {
const input: unknown = (tab as { input?: unknown }).input;
const isWebviewInput = (inp: unknown): inp is { viewType: string } =>
!!inp && typeof inp === 'object' && 'viewType' in inp;
for (const tab of allTabs) { if (
const input: unknown = (tab as { input?: unknown }).input; isWebviewInput(input) &&
const isWebviewInput = (inp: unknown): inp is { viewType: string } => input.viewType === 'mainThreadWebview-qwenCode.chat'
!!inp && typeof inp === 'object' && 'viewType' in inp; ) {
// Found an existing Qwen Code tab
if (isWebviewInput(input) && input.viewType === 'qwenCode.chat') { console.log('[PanelManager] Found existing Qwen Code group:', {
// Found an existing Qwen Code tab, get its view column viewColumn: group.viewColumn,
const tabGroup = vscode.window.tabGroups.all.find((g) => tabCount: group.tabs.length,
g.tabs.includes(tab), isActive: group.isActive,
); });
return tabGroup?.viewColumn; return {
group,
viewColumn: group.viewColumn,
};
}
} }
} }
@@ -102,23 +160,30 @@ export class PanelManager {
/** /**
* 自动锁定编辑器组(仅在新创建 Panel 时调用) * 自动锁定编辑器组(仅在新创建 Panel 时调用)
* 注意:我们不再自动锁定 Qwen Code group以允许用户创建多个 Qwen Code tab
*/ */
async autoLockEditorGroup(): Promise<void> { async autoLockEditorGroup(): Promise<void> {
if (!this.panel) { if (!this.panel) {
return; return;
} }
console.log('[PanelManager] Auto-locking editor group for Qwen Code chat'); // We don't auto-lock anymore to allow multiple Qwen Code tabs in the same group
try { console.log(
// Reveal panel without preserving focus to make it the active group '[PanelManager] Skipping auto-lock to allow multiple Qwen Code tabs',
this.revealPanel(false); );
await vscode.commands.executeCommand('workbench.action.lockEditorGroup'); // If you want to enable auto-locking for the first tab, uncomment the following:
console.log('[PanelManager] Editor group locked successfully'); // const existingQwenInfo = this.findExistingQwenCodeGroup();
} catch (error) { // if (!existingQwenInfo) {
console.warn('[PanelManager] Failed to lock editor group:', error); // console.log('[PanelManager] First Qwen Code tab, locking editor group');
// Non-fatal error, continue anyway // try {
} // this.revealPanel(false);
// await vscode.commands.executeCommand('workbench.action.lockEditorGroup');
// console.log('[PanelManager] Editor group locked successfully');
// } catch (error) {
// console.warn('[PanelManager] Failed to lock editor group:', error);
// }
// }
} }
/** /**