diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index 858b16c9fa..673f6074b7 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -45,7 +45,13 @@ "kilocode" ], "activationEvents": [ - "onWebviewPanel:kilo-code.new.AgentManagerPanel" + "onWebviewPanel:kilo-code.new.AgentManagerPanel", + "onWebviewPanel:kilo-code.new.TabPanel", + "onWebviewPanel:kilo-code.new.settingsPanel", + "onWebviewPanel:kilo-code.new.profilePanel", + "onWebviewPanel:kilo-code.new.marketplacePanel", + "onWebviewPanel:kilo-code.new.DiffViewerPanel", + "onWebviewPanel:kilo-code.new.SubAgentViewerPanel" ], "main": "./dist/extension.js", "contributes": { diff --git a/packages/kilo-vscode/src/DiffViewerProvider.ts b/packages/kilo-vscode/src/DiffViewerProvider.ts index 5650e220cc..78005a9312 100644 --- a/packages/kilo-vscode/src/DiffViewerProvider.ts +++ b/packages/kilo-vscode/src/DiffViewerProvider.ts @@ -48,21 +48,32 @@ export class DiffViewerProvider implements vscode.Disposable { return } - this.panel = vscode.window.createWebviewPanel(DiffViewerProvider.viewType, "Changes", vscode.ViewColumn.One, { + const panel = vscode.window.createWebviewPanel(DiffViewerProvider.viewType, "Changes", vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: true, localResourceRoots: [this.extensionUri], }) - this.panel.iconPath = { + this.wirePanel(panel) + } + + /** Re-wire a deserialized panel after extension restart. */ + public deserializePanel(panel: vscode.WebviewPanel): void { + this.wirePanel(panel) + } + + private wirePanel(panel: vscode.WebviewPanel): void { + this.panel = panel + + panel.iconPath = { light: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-light.svg"), dark: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-dark.svg"), } - this.panel.webview.onDidReceiveMessage((msg) => this.onMessage(msg), undefined, []) - this.panel.webview.html = this.getHtml(this.panel.webview) + panel.webview.onDidReceiveMessage((msg) => this.onMessage(msg), undefined, []) + panel.webview.html = this.getHtml(panel.webview) - this.panel.onDidDispose(() => { + panel.onDidDispose(() => { this.log("Panel disposed") this.stopDiffPolling() this.panel = undefined diff --git a/packages/kilo-vscode/src/SettingsEditorProvider.ts b/packages/kilo-vscode/src/SettingsEditorProvider.ts index c60d9ebc75..afaa819944 100644 --- a/packages/kilo-vscode/src/SettingsEditorProvider.ts +++ b/packages/kilo-vscode/src/SettingsEditorProvider.ts @@ -42,6 +42,15 @@ export class SettingsEditorProvider implements vscode.Disposable { return resolvePanelProjectDirectory(active, vscode.workspace.workspaceFolders) } + /** Extract the PanelView from a viewType string like "kilo-code.new.settingsPanel". */ + static viewFromType(type: string): PanelView | undefined { + const match = type.match(/^kilo-code\.new\.(\w+)Panel$/) + if (!match) return undefined + const view = match[1] as PanelView + if (!(view in PANEL_TITLES)) return undefined + return view + } + openPanel(view: PanelView, tab?: string): void { if (tab) this.tabs.set(view, tab) @@ -57,14 +66,31 @@ export class SettingsEditorProvider implements vscode.Disposable { return } - const title = PANEL_TITLES[view] + const panel = vscode.window.createWebviewPanel( + `kilo-code.new.${view}Panel`, + PANEL_TITLES[view], + vscode.ViewColumn.One, + { + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: [this.extensionUri], + }, + ) - const panel = vscode.window.createWebviewPanel(`kilo-code.new.${view}Panel`, title, vscode.ViewColumn.One, { - enableScripts: true, - retainContextWhenHidden: true, - localResourceRoots: [this.extensionUri], - }) + this.wirePanel(panel, view, projectDirectory) + } + /** Re-wire a deserialized panel after extension restart. */ + deserializePanel(panel: vscode.WebviewPanel): void { + const view = SettingsEditorProvider.viewFromType(panel.viewType) + if (!view) { + panel.dispose() + return + } + this.wirePanel(panel, view, this.getProjectDirectory()) + } + + private wirePanel(panel: vscode.WebviewPanel, view: PanelView, projectDirectory: string | null): void { panel.iconPath = { light: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-light.svg"), dark: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-dark.svg"), @@ -105,6 +131,7 @@ export class SettingsEditorProvider implements vscode.Disposable { this.panels.set(view, panel) this.providers.set(view, provider) + const title = PANEL_TITLES[view] panel.onDidDispose(() => { console.log(`[Kilo New] ${title} panel disposed`) closePanelDisposable.dispose() diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index 3ad1197e47..6626140174 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -66,6 +66,25 @@ export function activate(context: vscode.ExtensionContext) { }), ) + // Register serializer so "Open in Tab" restores when VS Code restarts + context.subscriptions.push( + vscode.window.registerWebviewPanelSerializer("kilo-code.new.TabPanel", { + deserializeWebviewPanel(panel: vscode.WebviewPanel) { + const tabProvider = new KiloProvider(context.extensionUri, connectionService, context) + tabProvider.resolveWebviewPanel(panel) + panel.onDidDispose( + () => { + console.log("[Kilo New] Tab panel restored from restart disposed") + tabProvider.dispose() + }, + null, + context.subscriptions, + ) + return Promise.resolve() + }, + }), + ) + // Create standalone diff viewer provider for the sidebar "Show Changes" action const diffViewerProvider = new DiffViewerProvider(context.extensionUri, connectionService) diffViewerProvider.setCommentHandler((comments) => { @@ -81,6 +100,39 @@ export function activate(context: vscode.ExtensionContext) { const subAgentViewerProvider = new SubAgentViewerProvider(context.extensionUri, connectionService, context) context.subscriptions.push(subAgentViewerProvider) + // Register serializers so settings/diff/sub-agent panels restore on restart + const settingsViews = ["settingsPanel", "profilePanel", "marketplacePanel"] as const + for (const suffix of settingsViews) { + context.subscriptions.push( + vscode.window.registerWebviewPanelSerializer(`kilo-code.new.${suffix}`, { + deserializeWebviewPanel(panel: vscode.WebviewPanel) { + settingsEditorProvider.deserializePanel(panel) + return Promise.resolve() + }, + }), + ) + } + + context.subscriptions.push( + vscode.window.registerWebviewPanelSerializer(DiffViewerProvider.viewType, { + deserializeWebviewPanel(panel: vscode.WebviewPanel) { + diffViewerProvider.deserializePanel(panel) + return Promise.resolve() + }, + }), + ) + + context.subscriptions.push( + vscode.window.registerWebviewPanelSerializer("kilo-code.new.SubAgentViewerPanel", { + deserializeWebviewPanel(panel: vscode.WebviewPanel) { + // Sub-agent viewer requires a session ID that can't be recovered + // after restart, so dispose the stale panel cleanly. + panel.dispose() + return Promise.resolve() + }, + }), + ) + // Register toolbar button command handlers context.subscriptions.push( vscode.commands.registerCommand("kilo-code.new.plusButtonClicked", () => {