Merge pull request #7081 from Kilo-Org/mark/fix-settings-panel-reload

fix(vscode): restore settings view and active tab after webview reload
This commit is contained in:
Mark IJbema
2026-03-16 13:03:07 +01:00
committed by GitHub
4 changed files with 62 additions and 7 deletions
@@ -18,6 +18,7 @@ type PanelView = "settings" | "profile"
export class SettingsEditorProvider implements vscode.Disposable {
private panels = new Map<PanelView, vscode.WebviewPanel>()
private providers = new Map<PanelView, KiloProvider>()
private tabs = new Map<PanelView, string>()
constructor(
private readonly extensionUri: vscode.Uri,
@@ -25,9 +26,15 @@ export class SettingsEditorProvider implements vscode.Disposable {
private readonly context: vscode.ExtensionContext,
) {}
openPanel(view: PanelView): void {
openPanel(view: PanelView, tab?: string): void {
if (tab) this.tabs.set(view, tab)
const existing = this.panels.get(view)
if (existing) {
if (tab) {
const provider = this.providers.get(view)
provider?.postMessage({ type: "navigate", view, tab })
}
existing.reveal(vscode.ViewColumn.One)
return
}
@@ -57,14 +64,21 @@ export class SettingsEditorProvider implements vscode.Disposable {
}
})
// Once the webview signals ready, navigate to the target view.
// Navigate to the target view on every webviewReady (including after
// "Developer: Reload Webviews" which re-creates the JS context).
const readyDisposable = panel.webview.onDidReceiveMessage((msg) => {
if (msg.type === "webviewReady") {
// Small delay to let KiloProvider's own webviewReady handler finish first
setTimeout(() => {
provider.postMessage({ type: "navigate", view })
provider.postMessage({ type: "navigate", view, tab: this.tabs.get(view) })
}, 50)
readyDisposable.dispose()
}
})
// Remember the active settings tab so it survives webview reloads.
const tabDisposable = panel.webview.onDidReceiveMessage((msg) => {
if (msg.type === "settingsTabChanged" && typeof msg.tab === "string") {
this.tabs.set(view, msg.tab)
}
})
@@ -74,9 +88,12 @@ export class SettingsEditorProvider implements vscode.Disposable {
panel.onDidDispose(() => {
console.log(`[Kilo New] ${title} panel disposed`)
closePanelDisposable.dispose()
readyDisposable.dispose()
tabDisposable.dispose()
provider.dispose()
this.panels.delete(view)
this.providers.delete(view)
this.tabs.delete(view)
})
}
+5 -1
View File
@@ -176,6 +176,7 @@ export const LanguageBridge: Component<{ children: any }> = (props) => {
// Inner app component that uses the contexts
const AppContent: Component = () => {
const [currentView, setCurrentView] = createSignal<ViewType>("newTask")
const [settingsTab, setSettingsTab] = createSignal<string | undefined>()
const [migrationReturnView, setMigrationReturnView] = createSignal<ViewType>("newTask") // legacy-migration
const session = useSession()
const server = useServer()
@@ -212,7 +213,8 @@ const AppContent: Component = () => {
handleViewAction(message.action)
}
if (message?.type === "navigate" && message.view && VALID_VIEWS.has(message.view)) {
console.log("[Kilo New] App: 🧭 navigate:", message.view)
console.log("[Kilo New] App: 🧭 navigate:", message.view, message.tab ? `tab=${message.tab}` : "")
if (message.tab) setSettingsTab(message.tab)
setCurrentView(message.view as ViewType)
}
if (message?.type === "openCloudSession" && message.sessionId) {
@@ -267,6 +269,8 @@ const AppContent: Component = () => {
</Match>
<Match when={currentView() === "settings"}>
<Settings
tab={settingsTab()}
onTabChange={setSettingsTab}
onMigrateClick={() => {
setMigrationReturnView("settings")
setCurrentView("migration")
@@ -1,6 +1,7 @@
import { Component } from "solid-js"
import { Component, createSignal, createEffect, on } from "solid-js"
import { Icon } from "@kilocode/kilo-ui/icon"
import { Tabs } from "@kilocode/kilo-ui/tabs"
import { useVSCode } from "../../context/vscode"
import { useLanguage } from "../../context/language"
import ProvidersTab from "./ProvidersTab"
import AgentBehaviourTab from "./AgentBehaviourTab"
@@ -19,12 +20,32 @@ import AboutKiloCodeTab from "./AboutKiloCodeTab"
import { useServer } from "../../context/server"
export interface SettingsProps {
tab?: string
onTabChange?: (tab: string) => void
onMigrateClick?: () => void // legacy-migration
}
const Settings: Component<SettingsProps> = (props) => {
const server = useServer()
const language = useLanguage()
const vscode = useVSCode()
const [active, setActive] = createSignal(props.tab ?? "providers")
// Sync when the parent changes the tab prop (e.g. via navigate message)
createEffect(
on(
() => props.tab,
(tab) => {
if (tab) setActive(tab)
},
),
)
const onTabChange = (tab: string) => {
setActive(tab)
props.onTabChange?.(tab)
vscode.postMessage({ type: "settingsTabChanged", tab })
}
return (
<div style={{ display: "flex", "flex-direction": "column", height: "100%" }}>
@@ -42,7 +63,13 @@ const Settings: Component<SettingsProps> = (props) => {
</div>
{/* Settings tabs */}
<Tabs orientation="vertical" variant="settings" defaultValue="providers" style={{ flex: 1, overflow: "hidden" }}>
<Tabs
orientation="vertical"
variant="settings"
value={active()}
onChange={onTabChange}
style={{ flex: 1, overflow: "hidden" }}
>
<Tabs.List>
<Tabs.Trigger value="providers">
<Icon name="providers" />
@@ -557,6 +557,7 @@ export interface DeviceAuthCancelledMessage {
export interface NavigateMessage {
type: "navigate"
view: "newTask" | "marketplace" | "history" | "cloudHistory" | "profile" | "settings" | "migration" | "subAgentViewer" // legacy-migration: "migration"
tab?: string
}
export interface ProvidersLoadedMessage {
@@ -1327,6 +1328,11 @@ export interface ResetAllSettingsRequest {
type: "resetAllSettings"
}
export interface SettingsTabChangedMessage {
type: "settingsTabChanged"
tab: string
}
export interface RequestNotificationsMessage {
type: "requestNotifications"
}
@@ -1638,6 +1644,7 @@ export type WebviewMessage =
| UpdateConfigMessage
| RequestNotificationSettingsMessage
| ResetAllSettingsRequest
| SettingsTabChangedMessage
| SyncSessionRequest
| CreateWorktreeSessionRequest
| RequestNotificationsMessage