mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix(vscode-ide-companion): 修复新建会话按钮,在同一 view column 创建新 tab
问题: - 之前的实现会复用现有 panel 并清空当前会话 - 期望行为是在同一 view column(不创建分屏)中创建新的 VS Code tab 解决方案: 1. 修改 qwenCode.openNewChatTab 命令 - 总是创建新的 WebviewProvider 和 WebviewPanel - PanelManager 的 findExistingQwenCodeViewColumn() 确保在同一 column 打开 2. 修改 MessageHandler 中的 openNewChatTab 处理 - 调用 VS Code 命令创建新 panel/tab 3. 移除不再需要的 createNewSession 方法 效果: - 点击新建会话按钮会在同一 view column 中创建新的 VS Code tab - 类似 Claude Code 的交互方式 - 不会创建新的分屏 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -364,6 +364,20 @@ export class WebViewProvider {
|
|||||||
// Register dispose handler
|
// Register dispose handler
|
||||||
this.panelManager.registerDisposeHandler(this.disposables);
|
this.panelManager.registerDisposeHandler(this.disposables);
|
||||||
|
|
||||||
|
// Listen for active editor changes and notify WebView
|
||||||
|
const editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
|
||||||
|
(editor) => {
|
||||||
|
const fileName = editor?.document.uri.fsPath
|
||||||
|
? getFileName(editor.document.uri.fsPath)
|
||||||
|
: null;
|
||||||
|
this.sendMessageToWebView({
|
||||||
|
type: 'activeEditorChanged',
|
||||||
|
data: { fileName },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.disposables.push(editorChangeDisposable);
|
||||||
|
|
||||||
// Capture the tab reference on restore
|
// Capture the tab reference on restore
|
||||||
this.panelManager.captureTab();
|
this.panelManager.captureTab();
|
||||||
|
|
||||||
|
|||||||
@@ -189,17 +189,10 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
vscode.commands.registerCommand('qwenCode.openNewChatTab', () => {
|
vscode.commands.registerCommand('qwenCode.openNewChatTab', () => {
|
||||||
// Check if there's already an open chat panel
|
// Always create a new WebviewPanel (tab) in the same view column
|
||||||
if (webViewProviders.length > 0) {
|
// The PanelManager will find existing Qwen Code tabs and open in the same column
|
||||||
const lastProvider = webViewProviders[webViewProviders.length - 1];
|
const provider = createWebViewProvider();
|
||||||
// Reveal the existing panel and create a new session within it
|
provider.show();
|
||||||
lastProvider.show();
|
|
||||||
lastProvider.createNewSession();
|
|
||||||
} else {
|
|
||||||
// Create first chat tab
|
|
||||||
const provider = createWebViewProvider();
|
|
||||||
provider.show();
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
vscode.commands.registerCommand('qwenCode.clearAuthCache', async () => {
|
vscode.commands.registerCommand('qwenCode.clearAuthCache', async () => {
|
||||||
await authStateManager.clearAuthState();
|
await authStateManager.clearAuthState();
|
||||||
|
|||||||
@@ -430,8 +430,6 @@ button {
|
|||||||
=========================== */
|
=========================== */
|
||||||
.input-form {
|
.input-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
/* gap: var(--app-spacing-medium); */
|
|
||||||
/* padding: 16px; */
|
|
||||||
background-color: var(--app-primary-background);
|
background-color: var(--app-primary-background);
|
||||||
border-top: 1px solid var(--app-primary-border-color);
|
border-top: 1px solid var(--app-primary-border-color);
|
||||||
}
|
}
|
||||||
@@ -626,8 +624,6 @@ button {
|
|||||||
color: var(--app-input-foreground);
|
color: var(--app-input-foreground);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// max-width: 680px;
|
|
||||||
// margin: 0 auto;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
@@ -644,7 +640,7 @@ button {
|
|||||||
|
|
||||||
.input-form:focus-within {
|
.input-form:focus-within {
|
||||||
border-color: var(--app-qwen-orange);
|
border-color: var(--app-qwen-orange);
|
||||||
box-shadow: 0 0 0 1px var(--app-qwen-orange), 0 1px 2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 2px color-mix(in srgb,var(--app-qwen-orange),transparent 80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Banner area - for warnings/messages */
|
/* Banner area - for warnings/messages */
|
||||||
|
|||||||
@@ -612,15 +612,9 @@ export const App: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleNewQwenSession = () => {
|
const handleNewQwenSession = () => {
|
||||||
vscode.postMessage({ type: 'newQwenSession', data: {} });
|
// Send message to open a new chat tab
|
||||||
|
vscode.postMessage({ type: 'openNewChatTab', data: {} });
|
||||||
setShowSessionSelector(false);
|
setShowSessionSelector(false);
|
||||||
setCurrentSessionId(null);
|
|
||||||
setCurrentSessionTitle('Past Conversations'); // Reset title to default
|
|
||||||
// Clear messages in UI
|
|
||||||
setMessages([]);
|
|
||||||
setCurrentStreamContent('');
|
|
||||||
setPlanEntries([]); // Clear plan entries
|
|
||||||
setToolCalls(new Map()); // Clear tool calls
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Time ago formatter (matching Claude Code)
|
// Time ago formatter (matching Claude Code)
|
||||||
@@ -1053,6 +1047,14 @@ export const App: React.FC = () => {
|
|||||||
{getEditModeInfo().icon}
|
{getEditModeInfo().icon}
|
||||||
<span>{getEditModeInfo().text}</span>
|
<span>{getEditModeInfo().text}</span>
|
||||||
</button>
|
</button>
|
||||||
|
{activeFileName && (
|
||||||
|
<span
|
||||||
|
className="active-file-indicator"
|
||||||
|
title={`Showing Qwen Code your current file selection: ${activeFileName}`}
|
||||||
|
>
|
||||||
|
{activeFileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<div className="action-divider"></div>
|
<div className="action-divider"></div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1096,14 +1098,6 @@ export const App: React.FC = () => {
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div className="input-actions-spacer"></div>
|
<div className="input-actions-spacer"></div>
|
||||||
{activeFileName && (
|
|
||||||
<span
|
|
||||||
className="active-file-indicator"
|
|
||||||
title={`Showing Qwen Code your current file selection: ${activeFileName}`}
|
|
||||||
>
|
|
||||||
{activeFileName}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="send-button-icon"
|
className="send-button-icon"
|
||||||
|
|||||||
@@ -170,8 +170,8 @@ export class MessageHandler {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'openNewChatTab':
|
case 'openNewChatTab':
|
||||||
// Create a new session in the current panel instead of opening a new panel
|
// Create a new WebviewPanel (tab) in the same view column
|
||||||
await this.handleNewQwenSession();
|
await vscode.commands.executeCommand('qwenCode.openNewChatTab');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -42,12 +42,19 @@ export class PanelManager {
|
|||||||
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
|
||||||
|
const existingQwenViewColumn = this.findExistingQwenCodeViewColumn();
|
||||||
|
|
||||||
|
// If we found an existing Qwen Code tab, open in the same view column
|
||||||
|
// Otherwise, open beside the active editor
|
||||||
|
const targetViewColumn = existingQwenViewColumn ?? vscode.ViewColumn.Beside;
|
||||||
|
|
||||||
this.panel = vscode.window.createWebviewPanel(
|
this.panel = vscode.window.createWebviewPanel(
|
||||||
'qwenCode.chat',
|
'qwenCode.chat',
|
||||||
'Qwen Code Chat',
|
'Qwen Code',
|
||||||
{
|
{
|
||||||
viewColumn: vscode.ViewColumn.Beside, // Open on right side of active editor
|
viewColumn: targetViewColumn,
|
||||||
preserveFocus: true, // Don't steal focus from editor
|
preserveFocus: false, // Focus the new tab
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableScripts: true,
|
enableScripts: true,
|
||||||
@@ -69,6 +76,30 @@ export class PanelManager {
|
|||||||
return true; // New panel created
|
return true; // New panel created
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找已存在的 Qwen Code webview 所在的 view column
|
||||||
|
* @returns 找到的 view column,如果没有则返回 undefined
|
||||||
|
*/
|
||||||
|
private findExistingQwenCodeViewColumn(): vscode.ViewColumn | undefined {
|
||||||
|
const allTabs = vscode.window.tabGroups.all.flatMap((g) => g.tabs);
|
||||||
|
|
||||||
|
for (const tab of allTabs) {
|
||||||
|
const input: unknown = (tab as { input?: unknown }).input;
|
||||||
|
const isWebviewInput = (inp: unknown): inp is { viewType: string } =>
|
||||||
|
!!inp && typeof inp === 'object' && 'viewType' in inp;
|
||||||
|
|
||||||
|
if (isWebviewInput(input) && input.viewType === 'qwenCode.chat') {
|
||||||
|
// Found an existing Qwen Code tab, get its view column
|
||||||
|
const tabGroup = vscode.window.tabGroups.all.find((g) =>
|
||||||
|
g.tabs.includes(tab),
|
||||||
|
);
|
||||||
|
return tabGroup?.viewColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自动锁定编辑器组(仅在新创建 Panel 时调用)
|
* 自动锁定编辑器组(仅在新创建 Panel 时调用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class WebViewContent {
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${panel.webview.cspSource}; script-src ${panel.webview.cspSource}; style-src ${panel.webview.cspSource} 'unsafe-inline';">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${panel.webview.cspSource}; script-src ${panel.webview.cspSource}; style-src ${panel.webview.cspSource} 'unsafe-inline';">
|
||||||
<title>Qwen Code Chat</title>
|
<title>Qwen Code</title>
|
||||||
</head>
|
</head>
|
||||||
<body data-extension-uri="${safeExtensionUri}">
|
<body data-extension-uri="${safeExtensionUri}">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.empty-state-logo-image {
|
.empty-state-logo-image {
|
||||||
width: 120px;
|
width: 60px;
|
||||||
height: 120px;
|
height: 60px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,11 @@ export const EmptyState: React.FC = () => {
|
|||||||
<div className="empty-state-content">
|
<div className="empty-state-content">
|
||||||
{/* Qwen Logo */}
|
{/* Qwen Logo */}
|
||||||
<div className="empty-state-logo">
|
<div className="empty-state-logo">
|
||||||
{iconUri && (
|
<img
|
||||||
<img
|
src={iconUri}
|
||||||
src={iconUri}
|
alt="Qwen Logo"
|
||||||
alt="Qwen Logo"
|
className="empty-state-logo-image"
|
||||||
className="empty-state-logo-image"
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="empty-state-text">
|
<div className="empty-state-text">
|
||||||
<div className="empty-state-title">
|
<div className="empty-state-title">
|
||||||
What to do first? Ask about this codebase or we can start writing
|
What to do first? Ask about this codebase or we can start writing
|
||||||
|
|||||||
Reference in New Issue
Block a user