wip(vscode-ide-companion): 实现 quick win 功能

- 将 WebView 调整到编辑器右侧
- 添加 ChatHeader 组件,实现会话下拉菜单
- 替换模态框为紧凑型下拉菜单
- 更新会话切换逻辑,显示当前标题
- 清理旧的会话选择器样式
基于 Claude Code v2.0.43 UI 分析实现。
This commit is contained in:
yiliang114
2025-11-19 00:16:45 +08:00
parent 729a3d0ab3
commit 732220e651
52 changed files with 16502 additions and 1420 deletions

View File

@@ -0,0 +1,315 @@
# 自动锁定编辑器组功能实现
## 概述
参考 Claude Code 的实现Qwen Code VSCode 扩展现在支持自动锁定编辑器组功能,确保 AI 助手界面保持稳定,不会被其他编辑器替换或意外关闭。
## 实现原理
### 1. VSCode 锁定组机制
**VSCode 源码分析**`src/vs/workbench/browser/parts/editor/editor.contribution.ts:558-566`
```typescript
// Lock Group: only on auxiliary window and when group is unlocked
appendEditorToolItem(
{
id: LOCK_GROUP_COMMAND_ID,
title: localize('lockEditorGroup', 'Lock Group'),
icon: Codicon.unlock,
},
ContextKeyExpr.and(
IsAuxiliaryEditorPartContext,
ActiveEditorGroupLockedContext.toNegated(),
),
CLOSE_ORDER - 1, // immediately to the left of close action
);
```
**关键条件**
- `IsAuxiliaryEditorPartContext`: 当前是辅助窗口的编辑器组
- `ActiveEditorGroupLockedContext.toNegated()`: 当前组未锁定
### 2. Claude Code 的实现方式
Claude Code 在创建 webview panel 时会检测是否在新列中打开:
```typescript
context.subscriptions.push(
vscode.commands.registerCommand(
'claude-vscode.editor.open',
async (param1, param2) => {
context.globalState.update('lastClaudeLocation', 1);
let { startedInNewColumn } = webviewProvider.createPanel(param1, param2);
// 如果在新列中打开,则自动锁定编辑器组
if (startedInNewColumn) {
await vscode.commands.executeCommand(
'workbench.action.lockEditorGroup',
);
}
},
),
);
```
### 3. Qwen Code 的实现
**文件位置**: `packages/vscode-ide-companion/src/WebViewProvider.ts:101-153`
```typescript
async show(): Promise<void> {
// Track if we're creating a new panel in a new column
let startedInNewColumn = false;
if (this.panel) {
// If panel already exists, just reveal it (no lock needed)
this.revealPanelTab(true);
this.capturePanelTab();
return;
}
// Mark that we're creating a new panel
startedInNewColumn = true;
this.panel = vscode.window.createWebviewPanel(
'qwenCode.chat',
'Qwen Code Chat',
{
viewColumn: vscode.ViewColumn.Beside, // Open on right side of active editor
preserveFocus: true, // Don't steal focus from editor
},
{
enableScripts: true,
retainContextWhenHidden: true,
localResourceRoots: [vscode.Uri.joinPath(this.extensionUri, 'dist')],
},
);
// Capture the Tab that corresponds to our WebviewPanel (Claude-style)
this.capturePanelTab();
// Auto-lock editor group when opened in new column (Claude Code style)
if (startedInNewColumn) {
console.log('[WebViewProvider] Auto-locking editor group for Qwen Code chat');
try {
// Reveal panel without preserving focus to make it the active group
// This ensures the lock command targets the correct editor group
this.revealPanelTab(false);
await vscode.commands.executeCommand('workbench.action.lockEditorGroup');
console.log('[WebViewProvider] Editor group locked successfully');
} catch (error) {
console.warn('[WebViewProvider] Failed to lock editor group:', error);
// Non-fatal error, continue anyway
}
} else {
// For existing panel, reveal with preserving focus
this.revealPanelTab(true);
}
// Continue with panel setup...
}
```
### 关键修复preserveFocus 问题
**问题发现**
- 最初实现中,`createWebviewPanel` 使用了 `preserveFocus: true`
- 这导致焦点保留在左边的编辑器组,左边的组仍然是**活动组activeGroup**
- 执行 `workbench.action.lockEditorGroup` 时,命令默认作用于活动组
- 结果:**错误地锁定了左边的编辑器组**,而不是 webview 所在的组
**错误的执行流程**
```
1. createWebviewPanel() 创建新组
└─> preserveFocus: true 保持焦点在左边
└─> activeGroup 仍然是左边的编辑器组
2. executeCommand("workbench.action.lockEditorGroup")
└─> resolveCommandsContext() 使用 activeGroup
└─> activeGroup = 左边的编辑器组 ❌
└─> 错误地锁定了左边的组
```
**修复方案**
1. 在执行锁定命令之前,调用 `this.revealPanelTab(false)`
2. 这会让 webview panel 获得焦点并成为活动组
3. 然后执行锁定命令就会锁定正确的组
**修复后的执行流程**
```
1. createWebviewPanel() 创建新组
└─> preserveFocus: true 保持焦点在左边
2. revealPanelTab(false) 激活 webview 组
└─> webview 组成为 activeGroup
3. executeCommand("workbench.action.lockEditorGroup")
└─> resolveCommandsContext() 使用 activeGroup
└─> activeGroup = webview 所在的组 ✓
└─> 正确锁定 webview 所在的组
```
## 执行流程
```
1. 用户打开 Qwen Code chat
2. 调用 WebViewProvider.show()
3. 检查是否已有 panel
- 有:直接 reveal不执行锁定
- 无:创建新 panel设置 startedInNewColumn = true
4. 创建 webview panel
- viewColumn: ViewColumn.Beside
- preserveFocus: true (不抢夺焦点,保持在编辑器)
5. 捕获 Tab 引用
- 调用 capturePanelTab() 保存 Tab 对象
6. 执行自动锁定startedInNewColumn === true
- 调用 revealPanelTab(false) 激活 webview 组
- webview 所在的组成为活动组activeGroup
- 执行命令: workbench.action.lockEditorGroup
- 命令作用于活动组,正确锁定 webview 组
7. 编辑器组被锁定
- ActiveEditorGroupLockedContext 变为 true
- 工具栏显示"解锁组"按钮(锁定图标)
- webview 保持在固定位置
```
## 功能效果
### 锁定前
- ❌ 用户可以拖拽 Qwen Code panel 到其他位置
- ❌ 其他编辑器可能替换 Qwen Code panel
- ❌ 容易意外关闭整个编辑器组
### 锁定后
- ✅ Qwen Code panel 保持在固定位置
- ✅ 编辑器组不会被其他操作影响
- ✅ 工具栏显示"锁定组"按钮,用户可以手动解锁
- ✅ 类似侧边栏的稳定行为
## 设计优势
1. **防止意外操作**
- 锁定后用户不能轻易拖拽或关闭 AI 助手界面
- 减少误操作导致的工作流中断
2. **保持固定位置**
- AI 助手界面始终在用户期望的位置
- 符合"AI 助手作为辅助工具"的定位
3. **用户可控**
- 自动锁定提供默认保护
- 用户仍可以通过工具栏解锁按钮手动解锁
- 平衡了便利性和灵活性
4. **一致的用户体验**
- 与 Claude Code 保持一致的交互模式
- 用户无需学习新的行为模式
## 错误处理
```typescript
try {
await vscode.commands.executeCommand('workbench.action.lockEditorGroup');
console.log('[WebViewProvider] Editor group locked successfully');
} catch (error) {
console.warn('[WebViewProvider] Failed to lock editor group:', error);
// Non-fatal error, continue anyway
}
```
**设计考虑**
- 锁定失败不影响 panel 的正常功能
- 记录警告日志便于调试
- 优雅降级,不中断用户工作流
## 配置选项(可选扩展)
如果需要让用户控制是否自动锁定,可以添加配置项:
```typescript
// 在 package.json 中添加配置
"qwenCode.autoLockEditorGroup": {
"type": "boolean",
"default": true,
"description": "Automatically lock the editor group when opening Qwen Code chat"
}
// 在代码中检查配置
const config = vscode.workspace.getConfiguration('qwenCode');
const autoLock = config.get<boolean>('autoLockEditorGroup', true);
if (startedInNewColumn && autoLock) {
await vscode.commands.executeCommand('workbench.action.lockEditorGroup');
}
```
## 测试场景
### 场景 1: 首次打开 Qwen Code
1. 打开 VSCode没有 Qwen Code panel
2. 执行命令打开 Qwen Code chat
3. **预期**: Panel 在新列中打开,编辑器组自动锁定
### 场景 2: 已有 Qwen Code panel
1. Qwen Code panel 已打开
2. 切换到其他编辑器
3. 再次打开 Qwen Code chat
4. **预期**: Panel 被 reveal不重复锁定
### 场景 3: 手动解锁后
1. Qwen Code panel 已锁定
2. 用户点击工具栏解锁按钮
3. 编辑器组被解锁
4. **预期**: 用户可以自由操作编辑器组
### 场景 4: 关闭并重新打开
1. Qwen Code panel 已打开并锁定
2. 用户关闭 panel
3. 再次打开 Qwen Code chat
4. **预期**: 新 panel 在新列打开,自动锁定
## 兼容性
- ✅ VSCode 1.85+(支持 `workbench.action.lockEditorGroup` 命令)
- ✅ 所有操作系统Windows, macOS, Linux
- ✅ 不影响现有功能
- ✅ 向后兼容旧版本 VSCode锁定失败时优雅降级
## 相关 VSCode 命令
| 命令 | 功能 |
| ---------------------------------------- | -------------------- |
| `workbench.action.lockEditorGroup` | 锁定当前编辑器组 |
| `workbench.action.unlockEditorGroup` | 解锁当前编辑器组 |
| `workbench.action.toggleEditorGroupLock` | 切换编辑器组锁定状态 |
## 总结
通过模仿 Claude Code 的实现Qwen Code 现在提供了:
1. ✅ 自动锁定编辑器组功能
2. ✅ 与 Claude Code 一致的用户体验
3. ✅ 稳定的 AI 助手界面位置
4. ✅ 优雅的错误处理
这个功能显著提升了用户体验,让 AI 助手界面更加稳定可靠!