diff --git a/.changeset/fix-secondary-sidebar-nav-bar.md b/.changeset/fix-secondary-sidebar-nav-bar.md new file mode 100644 index 00000000000..070b89d5f80 --- /dev/null +++ b/.changeset/fix-secondary-sidebar-nav-bar.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix the sidebar navigation bar (New Task, History, Agent Manager, KiloClaw, Marketplace, Profile, Settings) disappearing when the Kilo Code view is docked in the Secondary Side Bar. The navigation is now rendered inside the webview itself, so it stays visible regardless of where the view is docked. diff --git a/bun.lock b/bun.lock index 0f1a05b52c8..9e1efe75bf4 100644 --- a/bun.lock +++ b/bun.lock @@ -444,6 +444,7 @@ "@opencode-ai/ui": "workspace:*", "@pierre/diffs": "catalog:", "@thisbeyond/solid-dnd": "0.7.5", + "@vscode/codicons": "^0.0.44", "@xterm/addon-clipboard": "0.2.0", "@xterm/addon-fit": "0.11.0", "@xterm/addon-unicode-graphemes": "0.4.0", diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png new file mode 100644 index 00000000000..d5298111b8c --- /dev/null +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/sidebar-top-bar-default-chromium-linux.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1bc2a28b80d8dc5aea4c03609efc13a23a2c88103e56df741155d9e794fc96f7 +size 2422 diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index 26d6af3ffad..d1aed14c8d9 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -141,41 +141,6 @@ "category": "Kilo Code", "icon": "$(settings-gear)" }, - { - "command": "kilo-code.new.sidebarTitle.plusButtonClicked", - "title": "New Task", - "icon": "$(add)" - }, - { - "command": "kilo-code.new.sidebarTitle.agentManagerOpen", - "title": "Agent Manager", - "icon": "$(organization)" - }, - { - "command": "kilo-code.new.sidebarTitle.kiloClawOpen", - "title": "KiloClaw", - "icon": "$(comment-discussion)" - }, - { - "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", - "title": "Marketplace", - "icon": "$(extensions)" - }, - { - "command": "kilo-code.new.sidebarTitle.historyButtonClicked", - "title": "History", - "icon": "$(history)" - }, - { - "command": "kilo-code.new.sidebarTitle.profileButtonClicked", - "title": "Profile", - "icon": "$(account)" - }, - { - "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", - "title": "Settings", - "icon": "$(settings-gear)" - }, { "command": "kilo-code.new.openInTab", "title": "Open in Tab", @@ -461,76 +426,12 @@ ], "menus": { "commandPalette": [ - { - "command": "kilo-code.new.sidebarTitle.plusButtonClicked", - "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.historyButtonClicked", - "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.agentManagerOpen", - "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.kiloClawOpen", - "when": "false" - }, { "command": "kilo-code.new.agentManager.advancedWorktree", "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", - "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.profileButtonClicked", - "when": "false" - }, - { - "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", - "when": "false" - } - ], - "view/title": [ - { - "command": "kilo-code.new.sidebarTitle.plusButtonClicked", - "group": "navigation@0", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.historyButtonClicked", - "group": "navigation@1", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.agentManagerOpen", - "group": "navigation@2", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.kiloClawOpen", - "group": "navigation@3", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.marketplaceButtonClicked", - "group": "navigation@4", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.profileButtonClicked", - "group": "navigation@5", - "when": "view == kilo-code.SidebarProvider" - }, - { - "command": "kilo-code.new.sidebarTitle.settingsButtonClicked", - "group": "navigation@6", - "when": "view == kilo-code.SidebarProvider" } ], + "view/title": [], "scm/title": [ { "command": "kilo-code.new.generateCommitMessage", @@ -550,26 +451,6 @@ "command": "kilo-code.new.openInTab", "group": "navigation", "when": "true" - }, - { - "command": "kilo-code.new.plusButtonClicked", - "group": "navigation@0", - "when": "activeWebviewPanelId == kilo-code.new.TabPanel" - }, - { - "command": "kilo-code.new.historyButtonClicked", - "group": "navigation@1", - "when": "activeWebviewPanelId == kilo-code.new.TabPanel" - }, - { - "command": "kilo-code.new.profileButtonClicked", - "group": "navigation@2", - "when": "activeWebviewPanelId == kilo-code.new.TabPanel" - }, - { - "command": "kilo-code.new.settingsButtonClicked", - "group": "navigation@3", - "when": "activeWebviewPanelId == kilo-code.new.TabPanel" } ], "editor/context": [ @@ -1271,6 +1152,7 @@ "@opencode-ai/ui": "workspace:*", "@pierre/diffs": "catalog:", "@thisbeyond/solid-dnd": "0.7.5", + "@vscode/codicons": "^0.0.44", "@xterm/addon-clipboard": "0.2.0", "@xterm/addon-fit": "0.11.0", "@xterm/addon-unicode-graphemes": "0.4.0", diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index 1b9a9a5244e..15e8ec95749 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -1048,6 +1048,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper openAdvancedWorktree: () => vscode.commands.executeCommand("kilo-code.new.agentManager.advancedWorktree"), openChanges: (sessionId?: string, turnId?: string) => vscode.commands.executeCommand("kilo-code.new.showChanges", { sessionId, turnId }), + openProfile: () => vscode.commands.executeCommand("kilo-code.new.profileButtonClicked"), currentSessionId: this.currentSession?.id, createWorktree: async (baseBranch, branchName) => { await this.createWorktreeHandler?.(baseBranch, branchName) @@ -4908,6 +4909,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper title: "Kilo Code", port: this.connectionService.getServerInfo()?.port, extraStyles: `.container { height: 100vh; }`, + topBar: this.opts.hideTopBar !== true, + topBarSurface: this.opts.topBarSurface === "tab" ? "tab_title" : "sidebar_title", }) } diff --git a/packages/kilo-vscode/src/SettingsEditorProvider.ts b/packages/kilo-vscode/src/SettingsEditorProvider.ts index 84dc741d6d2..f03657921a8 100644 --- a/packages/kilo-vscode/src/SettingsEditorProvider.ts +++ b/packages/kilo-vscode/src/SettingsEditorProvider.ts @@ -103,6 +103,7 @@ export class SettingsEditorProvider implements vscode.Disposable { // backend connectivity (config, providers, agents, profile, auth). const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context, { projectDirectory, + hideTopBar: true, }) if (this.remoteService) { provider.setRemoteService(this.remoteService) diff --git a/packages/kilo-vscode/src/SubAgentViewerProvider.ts b/packages/kilo-vscode/src/SubAgentViewerProvider.ts index e1693cc18bb..11dfd75e8ef 100644 --- a/packages/kilo-vscode/src/SubAgentViewerProvider.ts +++ b/packages/kilo-vscode/src/SubAgentViewerProvider.ts @@ -41,7 +41,7 @@ export class SubAgentViewerProvider implements vscode.Disposable { dark: vscode.Uri.joinPath(this.extensionUri, "assets", "icons", "kilo-dark.svg"), } - const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context) + const provider = new KiloProvider(this.extensionUri, this.connectionService, this.context, { hideTopBar: true }) // Start accepting this session's SSE events as soon as the panel subscribes. // Reasoning deltas are not persisted until the reasoning part finishes. provider.trackSession(sessionID) diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index 20f842e324b..6e6e991a3d1 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -17,7 +17,7 @@ import { ensureBackendForAutocomplete } from "./services/autocomplete/ensure-bac import { AutocompleteServiceManager } from "./services/autocomplete/AutocompleteServiceManager" import { AttentionService } from "./services/attention" import { BrowserAutomationService } from "./services/browser-automation" -import { TelemetryEventName, TelemetryProxy } from "./services/telemetry" +import { TelemetryProxy } from "./services/telemetry" import { registerCommitMessageService } from "./services/commit-message" import { registerCodeActions, registerTerminalActions, KiloCodeActionProvider } from "./services/code-actions" import { registerToggleAutoApprove } from "./commands/toggle-auto-approve" @@ -232,6 +232,7 @@ export function activate(context: vscode.ExtensionContext) { deserializeWebviewPanel(panel: vscode.WebviewPanel) { const tabProvider = new KiloProvider(context.extensionUri, connectionService, context, { tabTitle: panelTitleHandler(panel), + topBarSurface: "tab", }) tabProvider.setRemoteService(remoteService) tabProvider.setAutoApproveController(autoApprove) @@ -333,39 +334,8 @@ export function activate(context: vscode.ExtensionContext) { }), ) - // Sidebar menus use wrapper commands so this event measures real title button presses, - // not programmatic opens, shortcuts, or editor title commands. - const track = (button: string, command: string) => { - TelemetryProxy.capture(TelemetryEventName.TITLE_BUTTON_CLICKED, { - button, - surface: "sidebar_title", - }) - void vscode.commands.executeCommand(command) - } - // Register toolbar button command handlers context.subscriptions.push( - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.plusButtonClicked", () => { - track("new_task", "kilo-code.new.plusButtonClicked") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.historyButtonClicked", () => { - track("history", "kilo-code.new.historyButtonClicked") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.agentManagerOpen", () => { - track("agent_manager", "kilo-code.new.agentManagerOpen") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.kiloClawOpen", () => { - track("kiloclaw", "kilo-code.new.kiloClawOpen") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.marketplaceButtonClicked", () => { - track("marketplace", "kilo-code.new.marketplaceButtonClicked") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.profileButtonClicked", () => { - track("profile", "kilo-code.new.profileButtonClicked") - }), - vscode.commands.registerCommand("kilo-code.new.sidebarTitle.settingsButtonClicked", () => { - track("settings", "kilo-code.new.settingsButtonClicked") - }), vscode.commands.registerCommand("kilo-code.new.plusButtonClicked", () => { const tab = activeTabProvider() if (tab) tab.postMessage({ type: "action", action: "plusButtonClicked" }) @@ -627,6 +597,7 @@ function openKiloInNewTab( const tabProvider = new KiloProvider(context.extensionUri, connectionService, context, { tabTitle: panelTitleHandler(panel), + topBarSurface: "tab", }) tabProvider.setRemoteService(remoteService) tabProvider.setAutoApproveController(autoApprove) diff --git a/packages/kilo-vscode/src/kilo-provider/options.ts b/packages/kilo-vscode/src/kilo-provider/options.ts index fcd8481bbe5..0e23e9d8dbc 100644 --- a/packages/kilo-vscode/src/kilo-provider/options.ts +++ b/packages/kilo-vscode/src/kilo-provider/options.ts @@ -32,4 +32,13 @@ export type KiloProviderOptions = { * is ambiguous. */ projectQualifier?: () => { projectId: string } | undefined + /** + * Hides the in-webview sidebar top bar (New Task, History, Agent Manager, + * etc.) for dedicated single-purpose panels — Settings, Profile, and the + * Sub-Agent Viewer — where it doesn't apply and would let users navigate + * away from the panel's one job. Sidebar and "Open in Tab" leave this unset. + */ + hideTopBar?: boolean + /** Reports "Open in Tab" as the top bar's telemetry surface instead of the sidebar default. */ + topBarSurface?: "tab" } diff --git a/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts b/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts index dc4d54fe5b0..10963338214 100644 --- a/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts +++ b/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts @@ -15,6 +15,7 @@ interface Ctx { openAgentManager: () => Thenable openAdvancedWorktree: () => Thenable openChanges: (sessionId?: string, turnId?: string) => Thenable + openProfile: () => Thenable currentSessionId?: string createWorktree?: (baseBranch?: string, branchName?: string) => Promise continueInWorktree?: ( @@ -59,6 +60,11 @@ export async function handleSidebarWorktreeMessage(message: Msg, ctx: Ctx) { return true } + if (message.type === "openProfilePanel") { + await ctx.openProfile() + return true + } + if (message.type !== "continueInWorktree") return false handleContinueInWorktree({ sessionId: message.sessionId, diff --git a/packages/kilo-vscode/src/utils.ts b/packages/kilo-vscode/src/utils.ts index fba6bb74b70..23479043e8b 100644 --- a/packages/kilo-vscode/src/utils.ts +++ b/packages/kilo-vscode/src/utils.ts @@ -41,6 +41,9 @@ export function buildWebviewHtml( title: string port?: number extraStyles?: string + /** Sidebar top bar visibility and telemetry surface for the shared webview bundle (App.tsx). Unused by the Agent Manager bundle. */ + topBar?: boolean + topBarSurface?: string }, ): string { const nonce = getNonce() @@ -83,7 +86,7 @@ export function buildWebviewHtml(
- + ` diff --git a/packages/kilo-vscode/webview-ui/src/App.tsx b/packages/kilo-vscode/webview-ui/src/App.tsx index 7fa356e8482..6dc0e4da1c9 100644 --- a/packages/kilo-vscode/webview-ui/src/App.tsx +++ b/packages/kilo-vscode/webview-ui/src/App.tsx @@ -11,6 +11,7 @@ import { LocalTabsProvider, useLocalTabs } from "./context/local-tabs" import { ProviderShell } from "./context/provider-shell" import { ChatView } from "./components/chat" import { SidebarEmptyState } from "./components/chat/SidebarEmptyState" +import { SidebarTopBar } from "./components/chat/SidebarTopBar" import { registerExpandedTaskTool } from "./components/chat/TaskToolExpanded" import { registerVscodeToolOverrides } from "./components/chat/VscodeToolOverrides" @@ -308,8 +309,23 @@ const AppContent: Component = () => { setCurrentView("history")} /> ) + // Set synchronously in the webview HTML by KiloProvider so it's available + // before this component ever mounts (see buildWebviewHtml/_getHtmlForWebview). + // Dedicated single-purpose panels (Settings, Profile, Sub-Agent Viewer) set + // KILO_TOP_BAR = false since navigating away from them makes no sense. + const host = window as { KILO_TOP_BAR?: boolean; KILO_TOP_BAR_SURFACE?: string } + const showTopBar = host.KILO_TOP_BAR !== false + const topBarSurface = host.KILO_TOP_BAR_SURFACE ?? "sidebar_title" + return (
+ + handleViewAction("plusButtonClicked")} + onHistory={() => handleViewAction("historyButtonClicked")} + surface={topBarSurface} + /> + {/* legacy-migration start — state-driven overlay, independent of currentView */} void + onHistory: () => void + /** Telemetry surface — distinguishes the sidebar from the "Open in Tab" panel, which shares this component. */ + surface: string +} + +/** Codicon names used below. */ +type Codicon = "add" | "history" | "organization" | "comment-discussion" | "extensions" | "account" | "settings-gear" + +interface Action { + key: string + codicon: Codicon + button: string + run: () => void +} + +export const SidebarTopBar: Component = (props) => { + const vscode = useVSCode() + const language = useLanguage() + + // Mirrors the telemetry the native toolbar buttons used to record, so analytics aren't lost. + const track = (button: string) => + vscode.postMessage({ + type: "telemetry", + event: TelemetryEventName.TITLE_BUTTON_CLICKED, + properties: { button, surface: props.surface }, + }) + + const open = ( + type: "openAgentManager" | "openKiloClaw" | "openMarketplacePanel" | "openProfilePanel" | "openSettingsPanel", + ) => vscode.postMessage({ type }) + + const actions: (Action | "spacer")[] = [ + { key: "newTask", codicon: "add", button: "new_task", run: () => props.onNewTask() }, + { key: "history", codicon: "history", button: "history", run: () => props.onHistory() }, + { key: "agentManager", codicon: "organization", button: "agent_manager", run: () => open("openAgentManager") }, + { key: "kiloClaw", codicon: "comment-discussion", button: "kiloclaw", run: () => open("openKiloClaw") }, + { key: "marketplace", codicon: "extensions", button: "marketplace", run: () => open("openMarketplacePanel") }, + "spacer", + { key: "profile", codicon: "account", button: "profile", run: () => open("openProfilePanel") }, + { key: "settings", codicon: "settings-gear", button: "settings", run: () => open("openSettingsPanel") }, + ] + + return ( +