From 85d65a3137ecadfcbda8255bfb4a40daf1f155fb Mon Sep 17 00:00:00 2001 From: Josh Lambert Date: Sat, 18 Jul 2026 10:35:16 -0400 Subject: [PATCH] fix(vscode): preserve active editor pane --- .changeset/steady-editor-tabs.md | 5 ++ .../kilo-vscode/src/SettingsEditorProvider.ts | 4 +- packages/kilo-vscode/src/extension.ts | 47 +++++-------------- .../tests/unit/extension-arch.test.ts | 27 +++++++++++ 4 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 .changeset/steady-editor-tabs.md diff --git a/.changeset/steady-editor-tabs.md b/.changeset/steady-editor-tabs.md new file mode 100644 index 00000000000..84e5f2b0b56 --- /dev/null +++ b/.changeset/steady-editor-tabs.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Open Kilo chats, settings, and files as tabs in the selected editor pane without creating, locking, or resizing editor panes. diff --git a/packages/kilo-vscode/src/SettingsEditorProvider.ts b/packages/kilo-vscode/src/SettingsEditorProvider.ts index 94d2536dfd2..84dc741d6d2 100644 --- a/packages/kilo-vscode/src/SettingsEditorProvider.ts +++ b/packages/kilo-vscode/src/SettingsEditorProvider.ts @@ -64,7 +64,7 @@ export class SettingsEditorProvider implements vscode.Disposable { const provider = this.providers.get(view) provider?.postMessage({ type: "navigate", view, tab }) } - existing.reveal(vscode.ViewColumn.One) + existing.reveal(vscode.ViewColumn.Active) this.providers.get(view)?.postMessage({ type: "navigate", view, ...(tab ? { tab } : {}) }) return } @@ -72,7 +72,7 @@ export class SettingsEditorProvider implements vscode.Disposable { const panel = vscode.window.createWebviewPanel( `kilo-code.new.${view}Panel`, PANEL_TITLES[view], - vscode.ViewColumn.One, + vscode.ViewColumn.Active, { enableScripts: true, retainContextWhenHidden: true, diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index b4467fb347b..902f29832a8 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -592,7 +592,7 @@ export async function deactivate() { TelemetryProxy.getInstance().shutdown() } -async function openKiloInNewTab( +function openKiloInNewTab( context: vscode.ExtensionContext, connectionService: KiloConnectionService, agentManagerProvider: AgentManagerProvider, @@ -601,20 +601,16 @@ async function openKiloInNewTab( remoteService: RemoteStatusService, autoApprove: ReturnType, ) { - const lastCol = Math.max(...vscode.window.visibleTextEditors.map((e) => e.viewColumn || 0), 0) - const hasVisibleEditors = vscode.window.visibleTextEditors.length > 0 - - if (!hasVisibleEditors) { - await vscode.commands.executeCommand("workbench.action.newGroupRight") - } - - const targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two - - const panel = vscode.window.createWebviewPanel("kilo-code.new.TabPanel", EXTENSION_DISPLAY_NAME, targetCol, { - enableScripts: true, - retainContextWhenHidden: true, - localResourceRoots: [context.extensionUri], - }) + const panel = vscode.window.createWebviewPanel( + "kilo-code.new.TabPanel", + EXTENSION_DISPLAY_NAME, + vscode.ViewColumn.Active, + { + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: [context.extensionUri], + }, + ) panel.iconPath = { light: vscode.Uri.joinPath(context.extensionUri, "assets", "icons", "kilo-light.svg"), @@ -636,11 +632,6 @@ async function openKiloInNewTab( tabProvider.resolveWebviewPanel(panel) tabPanels.set(panel, tabProvider) - // Wait for the new panel to become active before locking the editor group. - // This avoids the race where VS Code hasn't switched focus yet. - await waitForWebviewPanelToBeActive(panel) - await vscode.commands.executeCommand("workbench.action.lockEditorGroup") - panel.onDidDispose( () => { console.log("[Kilo New] Tab panel disposed") @@ -671,19 +662,3 @@ function ensureCommandsSkipShell(commands: string[]): void { if (missing.length === 0) return config.update("commandsToSkipShell", [...existing, ...missing], target) } - -function waitForWebviewPanelToBeActive(panel: vscode.WebviewPanel): Promise { - if (panel.active) { - return Promise.resolve() - } - - return new Promise((resolve) => { - const disposable = panel.onDidChangeViewState((event) => { - if (!event.webviewPanel.active) { - return - } - disposable.dispose() - resolve() - }) - }) -} diff --git a/packages/kilo-vscode/tests/unit/extension-arch.test.ts b/packages/kilo-vscode/tests/unit/extension-arch.test.ts index 4ff0d758ec8..1e7ed0edebd 100644 --- a/packages/kilo-vscode/tests/unit/extension-arch.test.ts +++ b/packages/kilo-vscode/tests/unit/extension-arch.test.ts @@ -16,6 +16,7 @@ const PKG_JSON_FILE = path.join(ROOT, "package.json") const SRC_DIR = path.join(ROOT, "src") const EXTENSION_FILE = path.join(ROOT, "src/extension.ts") const KILO_PROVIDER_FILE = path.join(ROOT, "src/KiloProvider.ts") +const SETTINGS_PROVIDER_FILE = path.join(ROOT, "src/SettingsEditorProvider.ts") const VSCODE_HOST_FILE = path.join(ROOT, "src/agent-manager/vscode-host.ts") function sliceBlock(source: string, start: number): string { @@ -202,6 +203,32 @@ describe("Extension — KiloProvider handler wiring", () => { }) }) +describe("Extension — editor panel placement", () => { + const ext = fs.readFileSync(EXTENSION_FILE, "utf-8") + const settings = fs.readFileSync(SETTINGS_PROVIDER_FILE, "utf-8") + + it("opens Kilo as a tab in the active editor group", () => { + const fn = ext.indexOf("function openKiloInNewTab") + expect(fn, "openKiloInNewTab must exist").toBeGreaterThan(-1) + const body = sliceBlock(ext, fn) + + expect(body).toContain("vscode.ViewColumn.Active") + expect(body).not.toContain("visibleTextEditors") + expect(body).not.toContain("workbench.action.newGroupRight") + expect(body).not.toContain("workbench.action.lockEditorGroup") + }) + + it("opens and reveals Settings in the active editor group", () => { + const fn = settings.indexOf("openPanel(view") + expect(fn, "SettingsEditorProvider.openPanel must exist").toBeGreaterThan(-1) + const body = sliceBlock(settings, fn) + + expect(body).toContain("existing.reveal(vscode.ViewColumn.Active)") + expect(body.match(/vscode\.ViewColumn\.Active/g)).toHaveLength(2) + expect(body).not.toContain("vscode.ViewColumn.One") + }) +}) + // --------------------------------------------------------------------------- // KiloProvider — continueInWorktree error fallback //