diff --git a/.changeset/auto-approve-header-light-theme.md b/.changeset/auto-approve-header-light-theme.md new file mode 100644 index 0000000000..9ba8392016 --- /dev/null +++ b/.changeset/auto-approve-header-light-theme.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix unreadable section titles on the Auto-Approve settings page when using a light VS Code color theme. Tool headers (External Directory, Bash, Read, Edit, etc.) now follow the active VS Code theme foreground color instead of always rendering in white. diff --git a/.changeset/autocomplete-model-switch-back.md b/.changeset/autocomplete-model-switch-back.md new file mode 100644 index 0000000000..19a09ec103 --- /dev/null +++ b/.changeset/autocomplete-model-switch-back.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Fix switching autocomplete model back to Codestral not persisting. diff --git a/.changeset/chat-toolbar-changes.md b/.changeset/chat-toolbar-changes.md new file mode 100644 index 0000000000..2a9d574980 --- /dev/null +++ b/.changeset/chat-toolbar-changes.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Improve the sidebar chat toolbar so the changes button collapses cleanly and keeps diff stats visible in its tooltip. diff --git a/.changeset/edit-tool-diff-split-view.md b/.changeset/edit-tool-diff-split-view.md new file mode 100644 index 0000000000..cca8699d5f --- /dev/null +++ b/.changeset/edit-tool-diff-split-view.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Open edit-tool diffs in side-by-side (split) mode by default; permission-dock expand stays unified. diff --git a/.changeset/sidebar-worktree-actions.md b/.changeset/sidebar-worktree-actions.md new file mode 100644 index 0000000000..8cab7eeb83 --- /dev/null +++ b/.changeset/sidebar-worktree-actions.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Expose sidebar session, worktree, and agent manager actions above the prompt, including quick and advanced worktree creation. diff --git a/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md b/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md index db8dc74c64..834ec18ce1 100644 --- a/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md +++ b/packages/kilo-docs/pages/code-with-ai/agents/custom-models.md @@ -84,6 +84,7 @@ All fields are optional. When a model ID matches one already in the built-in cat | `reasoning` | `boolean` | Whether the model supports extended thinking | | `temperature` | `boolean` | Whether the model supports the temperature parameter | | `attachment` | `boolean` | Whether the model supports file attachments | +| `modalities` | `object` | Optional. Supported input and output types: `{ input, output }` | | `limit` | `object` | Token limits: `{ context, output, input? }` | | `cost` | `object` | Pricing per million tokens: `{ input, output, cache_read?, cache_write? }` | | `options` | `object` | Arbitrary provider-specific model options | @@ -91,6 +92,26 @@ All fields are optional. When a model ID matches one already in the built-in cat | `provider` | `object` | Override `{ npm?, api? }` — the AI SDK package or base API URL for this model | | `variants` | `object` | Named variant configurations (e.g., different reasoning efforts) | +### Modalities (modalities) + +The `modalities` object declares which content types the model can receive and produce. It is optional — omit it to use defaults from the catalog or fallback to text-only. When `modalities` is provided, both `input` and `output` arrays are required. Each array can include `text`, `image`, `audio`, `video`, or `pdf`. + +| Sub-field | Type | Required | Description | +| --------- | ------- | ---------------- | ------------------------------------------------ | +| `input` | `array` | Yes (if present) | Content types the model accepts from the user | +| `output` | `array` | Yes (if present) | Content types the model can generate in response | + +For a standard text model that can also inspect images, use: + +```jsonc +"modalities": { + "input": ["text", "image"], + "output": ["text"] +} +``` + +If `modalities` is omitted and the model ID matches a models.dev catalog entry for that provider, Kilo uses the catalog's modalities. For completely custom models with no catalog match, Kilo defaults to text input and text output only. Set `attachment: true` alongside image, audio, video, or PDF input modalities when the provider supports sending those files as attachments. + ### Token Limits (limit) The `limit` object controls how Kilo manages the model's context window and output length. These values are specified in **tokens**. diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/chat-view-idle-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/chat-view-idle-chromium-linux.png index b3de7ce0a6..32ab1dbd8c 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/chat-view-idle-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/chat-view-idle-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63c6e23a31f570031361db39105d4283e05da923bb50757e7583c9430f805c53 -size 16379 +oid sha256:6fbf2dc34c9c1ba549244a750a5ce828089fc378fa2352645c9a2267270a0c08 +size 18021 diff --git a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/welcome-with-switcher-and-notification-chromium-linux.png b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/welcome-with-switcher-and-notification-chromium-linux.png index da8e320285..96ce67cb9a 100644 --- a/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/welcome-with-switcher-and-notification-chromium-linux.png +++ b/packages/kilo-docs/public/img/screenshot-tests/kilo-vscode/visual-regression/chat/welcome-with-switcher-and-notification-chromium-linux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:024ac7b12c5c6455d30c349dbfde0faff8d8c864761d0d79f0f9e08dbf7144e0 -size 26331 +oid sha256:d2cdb6dfc9a907b8ff98bb1982f324efa6d19e4ee98f9f690b8adbc2a0a163de +size 28994 diff --git a/packages/kilo-vscode/package.json b/packages/kilo-vscode/package.json index d888ec0d1a..fe786f1f58 100644 --- a/packages/kilo-vscode/package.json +++ b/packages/kilo-vscode/package.json @@ -98,7 +98,7 @@ { "command": "kilo-code.new.agentManagerOpen", "title": "Agent Manager", - "icon": "$(circuit-board)" + "icon": "$(organization)" }, { "command": "kilo-code.new.kiloClawOpen", @@ -751,7 +751,6 @@ }, "kilo-code.new.autocomplete.model": { "type": "string", - "default": "mistralai/codestral-2508", "enum": [ "mistralai/codestral-2508", "inception/mercury-edit" diff --git a/packages/kilo-vscode/src/DiffVirtualProvider.ts b/packages/kilo-vscode/src/DiffVirtualProvider.ts index b585ee71fd..9b66a60a0f 100644 --- a/packages/kilo-vscode/src/DiffVirtualProvider.ts +++ b/packages/kilo-vscode/src/DiffVirtualProvider.ts @@ -8,6 +8,7 @@ export interface DiffVirtualFile { after: string additions: number deletions: number + initialDiffStyle: "unified" | "split" } /** @@ -83,7 +84,7 @@ export class DiffVirtualProvider implements vscode.Disposable { private pushData(): void { if (!this.pending) return - this.post({ type: "diffVirtual.data", diff: this.pending }) + this.post({ type: "diffVirtual.data", diff: this.pending, initialDiffStyle: this.pending.initialDiffStyle }) } private post(message: Record): void { diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index f902d1290e..38fd59e385 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -45,7 +45,7 @@ import { resolveProjectDirectory } from "./project-directory" import { getBusySessionCount, seedSessionStatuses } from "./session-status" import { retry } from "./services/cli-backend/retry" import { slimPart, slimParts } from "./kilo-provider/slim-metadata" -import { handleContinueInWorktree } from "./kilo-provider/continue-worktree" +import { handleSidebarWorktreeMessage } from "./kilo-provider/sidebar-worktree" import { parseMessageFiles, type MessageFile } from "./kilo-provider/message-files" import { handleFileSearch } from "./kilo-provider/file-search" import { getTerminalContents } from "./services/terminal/context" @@ -241,6 +241,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper | ((sessionId: string, progress: (status: string, detail?: string, error?: string) => void) => Promise) | null = null + /** Handler for sidebar worktree creation — delegates to AgentManagerProvider. */ + private createWorktreeHandler: ((baseBranch?: string, branchName?: string) => Promise) | null = null + private diffVirtualProvider: import("./DiffVirtualProvider").DiffVirtualProvider | undefined private remoteService: RemoteStatusService | null = null private unsubscribeRemote: (() => void) | null = null @@ -555,6 +558,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper this.continueInWorktreeHandler = handler } + public setCreateWorktreeHandler(handler: (baseBranch?: string, branchName?: string) => Promise): void { + this.createWorktreeHandler = handler + } + public attachToWebview( webview: vscode.Webview, options?: { onBeforeMessage?: (msg: Record) => Promise | null> }, @@ -583,6 +590,20 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper await routeSuggestionWebviewMessage(this.questionCtx, message) if (await ModelState.handleMessage(message.type, message, this.client, (msg) => this.postMessage(msg))) return if (await routeAutocompleteMessage(message, (msg) => this.postMessage(msg))) return + if ( + await handleSidebarWorktreeMessage(message, { + post: (msg) => this.postMessage(msg), + openAgentManager: () => vscode.commands.executeCommand("kilo-code.new.agentManagerOpen"), + openAdvancedWorktree: () => vscode.commands.executeCommand("kilo-code.new.agentManager.advancedWorktree"), + openChanges: () => vscode.commands.executeCommand("kilo-code.new.showChanges"), + createWorktree: async (baseBranch, branchName) => { + await this.createWorktreeHandler?.(baseBranch, branchName) + }, + continueInWorktree: this.continueInWorktreeHandler ?? undefined, + }) + ) { + return + } switch (message.type) { case "webviewReady": console.log("[Kilo New] KiloProvider: ✅ webviewReady received") @@ -707,18 +728,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper case "openMarketplacePanel": vscode.commands.executeCommand("kilo-code.new.marketplaceButtonClicked", this.projectDirectory) break - case "openChanges": - vscode.commands.executeCommand("kilo-code.new.showChanges") - break case "openDiffVirtual": - this.openDiffVirtual(message.diff) - break - case "continueInWorktree": - handleContinueInWorktree({ - sessionId: message.sessionId, - handler: this.continueInWorktreeHandler ?? undefined, - post: (msg) => this.postMessage(msg), - }) + this.openDiffVirtual(message.diff, message.initialDiffStyle) break case "forkSession": handleForkSession(this.forkCtx, message.sessionId, message.messageId).catch((e) => @@ -1062,9 +1073,11 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper void vscode.env.openExternal(vscode.Uri.parse(url)) } - private openDiffVirtual(diff: unknown): void { + private openDiffVirtual(diff: unknown, initialDiffStyle?: unknown): void { if (!this.diffVirtualProvider || !diff) return - this.diffVirtualProvider.open(diff as import("./DiffVirtualProvider").DiffVirtualFile) + const d = diff as import("./DiffVirtualProvider").DiffVirtualFile + d.initialDiffStyle = initialDiffStyle === "split" ? "split" : "unified" + this.diffVirtualProvider.open(d) } /** diff --git a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts index d42e229f1f..788248d17d 100644 --- a/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts +++ b/packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts @@ -1537,6 +1537,25 @@ export class AgentManagerProvider implements Disposable { ) } + public async createFromSidebar(baseBranch?: string, branchName?: string): Promise { + this.openPanel() + const panel = this.panel + if (!panel) return + await panel.waitForReady() + await this.waitForStateReady("createFromSidebar") + await this.onCreateWorktree(baseBranch, branchName) + } + + public async openAdvancedWorktree(): Promise { + this.openPanel() + const panel = this.panel + if (!panel) return + await panel.waitForActive() + await panel.waitForReady() + await this.waitForStateReady("openAdvancedWorktree") + queueMicrotask(() => this.postToWebview({ type: "action", action: "advancedWorktree" })) + } + private handleSection(m: AgentManagerInMessage): boolean { return handleSection(this.state, m, () => this.pushState()) } diff --git a/packages/kilo-vscode/src/agent-manager/host.ts b/packages/kilo-vscode/src/agent-manager/host.ts index 1f86425194..92970ecac7 100644 --- a/packages/kilo-vscode/src/agent-manager/host.ts +++ b/packages/kilo-vscode/src/agent-manager/host.ts @@ -56,6 +56,12 @@ export interface PanelContext { /** Send a message to the webview. */ postMessage(msg: unknown): void + /** Resolve once the panel webview is ready to receive messages. */ + waitForReady(): Promise + + /** Resolve once the panel is the active editor tab. */ + waitForActive(): Promise + /** Reveal the panel. */ reveal(preserveFocus?: boolean): void diff --git a/packages/kilo-vscode/src/agent-manager/vscode-host.ts b/packages/kilo-vscode/src/agent-manager/vscode-host.ts index 06370eae2f..8b5ebfdc72 100644 --- a/packages/kilo-vscode/src/agent-manager/vscode-host.ts +++ b/packages/kilo-vscode/src/agent-manager/vscode-host.ts @@ -110,6 +110,19 @@ export class VscodeHost implements Host { postMessage(msg) { void panel.webview.postMessage(msg) }, + waitForReady() { + return provider.waitForReady() + }, + waitForActive() { + if (panel.active) return Promise.resolve() + return new Promise((resolve) => { + const sub = panel.onDidChangeViewState((e) => { + if (!e.webviewPanel.active) return + sub.dispose() + resolve() + }) + }) + }, reveal(preserveFocus) { panel.reveal(vscode.ViewColumn.One, preserveFocus ?? false) }, diff --git a/packages/kilo-vscode/src/extension.ts b/packages/kilo-vscode/src/extension.ts index 8295a079d9..fcd6ff1eb8 100644 --- a/packages/kilo-vscode/src/extension.ts +++ b/packages/kilo-vscode/src/extension.ts @@ -112,6 +112,9 @@ export function activate(context: vscode.ExtensionContext) { provider.setContinueInWorktreeHandler((sessionId, progress) => agentManagerProvider.continueFromSidebar(sessionId, progress), ) + provider.setCreateWorktreeHandler((baseBranch, branchName) => + agentManagerProvider.createFromSidebar(baseBranch, branchName), + ) // Register serializer so Agent Manager restores when VS Code restarts context.subscriptions.push( @@ -145,6 +148,9 @@ export function activate(context: vscode.ExtensionContext) { tabProvider.setContinueInWorktreeHandler((sessionId, progress) => agentManagerProvider.continueFromSidebar(sessionId, progress), ) + tabProvider.setCreateWorktreeHandler((baseBranch, branchName) => + agentManagerProvider.createFromSidebar(baseBranch, branchName), + ) tabProvider.setDiffVirtualProvider(diffVirtualProvider) tabProvider.resolveWebviewPanel(panel) tabPanels.set(panel, tabProvider) @@ -338,9 +344,9 @@ export function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand("kilo-code.new.agentManager.closeWorktree", () => { agentManagerProvider.postMessage({ type: "action", action: "closeWorktree" }) }), - vscode.commands.registerCommand("kilo-code.new.agentManager.advancedWorktree", () => { - agentManagerProvider.postMessage({ type: "action", action: "advancedWorktree" }) - }), + vscode.commands.registerCommand("kilo-code.new.agentManager.advancedWorktree", () => + agentManagerProvider.openAdvancedWorktree(), + ), ...Array.from({ length: 9 }, (_, i) => vscode.commands.registerCommand(`kilo-code.new.agentManager.jumpTo${i + 1}`, () => { agentManagerProvider.postMessage({ type: "action", action: `jumpTo${i + 1}` }) @@ -453,6 +459,9 @@ async function openKiloInNewTab( tabProvider.setContinueInWorktreeHandler((sessionId, progress) => agentManagerProvider.continueFromSidebar(sessionId, progress), ) + tabProvider.setCreateWorktreeHandler((baseBranch, branchName) => + agentManagerProvider.createFromSidebar(baseBranch, branchName), + ) tabProvider.setDiffVirtualProvider(diffVirtualProvider) tabProvider.resolveWebviewPanel(panel) tabPanels.set(panel, tabProvider) diff --git a/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts b/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts new file mode 100644 index 0000000000..a478d9b13c --- /dev/null +++ b/packages/kilo-vscode/src/kilo-provider/sidebar-worktree.ts @@ -0,0 +1,67 @@ +import { GitOps } from "../agent-manager/GitOps" +import { getWorkspaceRoot } from "../review-utils" +import { handleContinueInWorktree } from "./continue-worktree" + +interface Msg { + type: string + baseBranch?: string + branchName?: string + sessionId?: string +} + +interface Ctx { + post: (msg: unknown) => void + openAgentManager: () => Thenable + openAdvancedWorktree: () => Thenable + openChanges: () => Thenable + createWorktree?: (baseBranch?: string, branchName?: string) => Promise + continueInWorktree?: ( + sessionId: string, + progress: (status: string, detail?: string, error?: string) => void, + ) => Promise +} + +async function repo(post: Ctx["post"]) { + const root = getWorkspaceRoot() + if (!root) return + const git = new GitOps({ log: () => {} }) + const branch = await git.currentBranch(root).catch(() => "") + git.dispose() + if (!branch || branch === "HEAD") return + post({ type: "agentManager.repoInfo", branch }) +} + +export async function handleSidebarWorktreeMessage(message: Msg, ctx: Ctx) { + if (message.type === "openAgentManager") { + await ctx.openAgentManager() + return true + } + + if (message.type === "openAdvancedWorktree") { + await ctx.openAdvancedWorktree() + return true + } + + if (message.type === "agentManager.requestRepoInfo") { + await repo(ctx.post) + return true + } + + if (message.type === "agentManager.createWorktree") { + await ctx.createWorktree?.(message.baseBranch, message.branchName) + return true + } + + if (message.type === "openChanges") { + await ctx.openChanges() + return true + } + + if (message.type !== "continueInWorktree") return false + handleContinueInWorktree({ + sessionId: message.sessionId, + handler: ctx.continueInWorktree, + post: ctx.post, + }) + return true +} diff --git a/packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts b/packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts index 5d56a7aadf..1beb1485ea 100644 --- a/packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts +++ b/packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts @@ -8,7 +8,7 @@ import { AutocompleteCodeActionProvider } from "./AutocompleteCodeActionProvider import { AutocompleteInlineCompletionProvider } from "./classic-auto-complete/AutocompleteInlineCompletionProvider" import { AutocompleteTelemetry } from "./classic-auto-complete/AutocompleteTelemetry" import type { KiloConnectionService } from "../cli-backend" -import { DEFAULT_AUTOCOMPLETE_MODEL } from "../../shared/autocomplete-models" +import { getAutocompleteModel } from "../../shared/autocomplete-models" const CONFIG_SECTION = "kilo-code.new.autocomplete" @@ -27,7 +27,7 @@ function readSettings(): AutocompleteServiceSettings { enableAutoTrigger: config.get("enableAutoTrigger") ?? true, enableSmartInlineTaskKeybinding: config.get("enableSmartInlineTaskKeybinding") ?? true, enableChatAutocomplete: config.get("enableChatAutocomplete") ?? true, - model: config.get("model") ?? DEFAULT_AUTOCOMPLETE_MODEL.id, + model: getAutocompleteModel(config.get("model") ?? "").id, snoozeUntil: config.get("snoozeUntil"), } } diff --git a/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts b/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts index 51eccf7f3f..9ee596bc23 100644 --- a/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts +++ b/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts @@ -31,6 +31,19 @@ describe("autocomplete settings", () => { expect(buildAutocompleteSettingsMessage().settings.model).toBe("inception/mercury-edit") }) + it("defaults to codestral when no model is set", async () => { + const { buildAutocompleteSettingsMessage } = await import("../settings") + + expect(buildAutocompleteSettingsMessage().settings.model).toBe("mistralai/codestral-2508") + }) + + it("defaults to codestral when stored model is no longer supported", async () => { + state.set("model", "some/removed-model") + const { buildAutocompleteSettingsMessage } = await import("../settings") + + expect(buildAutocompleteSettingsMessage().settings.model).toBe("mistralai/codestral-2508") + }) + it("persists supported model updates", async () => { const post = vi.fn() const { routeAutocompleteMessage } = await import("../settings") diff --git a/packages/kilo-vscode/src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete.ts b/packages/kilo-vscode/src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete.ts index 8c1793faa3..328ba612eb 100644 --- a/packages/kilo-vscode/src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete.ts +++ b/packages/kilo-vscode/src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete.ts @@ -7,7 +7,7 @@ import { postprocessAutocompleteSuggestion } from "../classic-auto-complete/usel import { VisibleCodeTracker } from "../context/VisibleCodeTracker" import { FileIgnoreController } from "../shims/FileIgnoreController" import type { KiloConnectionService } from "../../cli-backend" -import { DEFAULT_AUTOCOMPLETE_MODEL } from "../../../shared/autocomplete-models" +import { getAutocompleteModel } from "../../../shared/autocomplete-models" import { finalizeChatSuggestion, buildChatPrefix } from "./chat-autocomplete-utils" interface ChatCompletionRequestMessage { @@ -77,7 +77,7 @@ export class ChatTextAreaAutocomplete { async getCompletion(userText: string, visibleCodeContext?: VisibleCodeContext): Promise<{ suggestion: string }> { const cfg = vscode.workspace.getConfiguration("kilo-code.new.autocomplete") - this.model.setModel(cfg.get("model") ?? DEFAULT_AUTOCOMPLETE_MODEL.id) + this.model.setModel(getAutocompleteModel(cfg.get("model") ?? "").id) const startTime = Date.now() // Build context for telemetry diff --git a/packages/kilo-vscode/src/services/autocomplete/settings.ts b/packages/kilo-vscode/src/services/autocomplete/settings.ts index 52f3883920..029d492129 100644 --- a/packages/kilo-vscode/src/services/autocomplete/settings.ts +++ b/packages/kilo-vscode/src/services/autocomplete/settings.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode" -import { AUTOCOMPLETE_MODELS, DEFAULT_AUTOCOMPLETE_MODEL } from "../../shared/autocomplete-models" +import { AUTOCOMPLETE_MODELS, getAutocompleteModel } from "../../shared/autocomplete-models" const keys = new Set(["enableAutoTrigger", "enableSmartInlineTaskKeybinding", "enableChatAutocomplete", "model"]) @@ -35,7 +35,7 @@ export function buildAutocompleteSettingsMessage() { enableAutoTrigger: config.get("enableAutoTrigger", true), enableSmartInlineTaskKeybinding: config.get("enableSmartInlineTaskKeybinding", false), enableChatAutocomplete: config.get("enableChatAutocomplete", false), - model: config.get("model", DEFAULT_AUTOCOMPLETE_MODEL.id), + model: getAutocompleteModel(config.get("model") ?? "").id, }, } } diff --git a/packages/kilo-vscode/tests/unit/autocomplete-models-sync.test.ts b/packages/kilo-vscode/tests/unit/autocomplete-models-sync.test.ts index fe97bb1fc9..e7dc565783 100644 --- a/packages/kilo-vscode/tests/unit/autocomplete-models-sync.test.ts +++ b/packages/kilo-vscode/tests/unit/autocomplete-models-sync.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from "bun:test" import { readFileSync } from "node:fs" import { join } from "node:path" -import { AUTOCOMPLETE_MODELS, DEFAULT_AUTOCOMPLETE_MODEL } from "../../src/shared/autocomplete-models" +import { AUTOCOMPLETE_MODELS } from "../../src/shared/autocomplete-models" describe("autocomplete model enum ↔ AUTOCOMPLETE_MODELS sync", () => { const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf8")) @@ -16,7 +16,7 @@ describe("autocomplete model enum ↔ AUTOCOMPLETE_MODELS sync", () => { expect(prop.enumDescriptions).toHaveLength(AUTOCOMPLETE_MODELS.length) }) - it("package.json default matches DEFAULT_AUTOCOMPLETE_MODEL", () => { - expect(prop.default).toBe(DEFAULT_AUTOCOMPLETE_MODEL.id) + it("package.json does not declare a default (VS Code strips user overrides that equal the schema default)", () => { + expect(prop.default).toBeUndefined() }) }) diff --git a/packages/kilo-vscode/tests/unit/databridge-shape.test.ts b/packages/kilo-vscode/tests/unit/databridge-shape.test.ts index ef2e16365c..be286d3408 100644 --- a/packages/kilo-vscode/tests/unit/databridge-shape.test.ts +++ b/packages/kilo-vscode/tests/unit/databridge-shape.test.ts @@ -68,7 +68,9 @@ describe("DataBridge openDiff wiring (regression guard)", () => { } it("wires openDiff to the openDiffVirtual webview message", () => { - expect(openDiffBlock()).toMatch(/postMessage\(\{\s*type:\s*["']openDiffVirtual["']\s*,\s*diff\s*\}\)/) + expect(openDiffBlock()).toMatch( + /postMessage\(\{\s*type:\s*["']openDiffVirtual["']\s*,\s*diff\s*,\s*initialDiffStyle:\s*["']split["']\s*\}\)/, + ) expect(src).toContain("onOpenDiff={openDiff}") }) }) diff --git a/packages/kilo-vscode/tests/unit/extension-arch.test.ts b/packages/kilo-vscode/tests/unit/extension-arch.test.ts index af37e1fbae..174989a261 100644 --- a/packages/kilo-vscode/tests/unit/extension-arch.test.ts +++ b/packages/kilo-vscode/tests/unit/extension-arch.test.ts @@ -17,6 +17,24 @@ 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") +function sliceBlock(source: string, start: number): string { + const open = source.indexOf("{", start) + expect(open, "block opening brace must exist").toBeGreaterThan(-1) + + const state = { depth: 0, end: -1 } + Array.from(source.slice(open)).some((ch, i) => { + if (ch === "{") state.depth++ + if (ch === "}") state.depth-- + if (state.depth !== 0) return false + state.end = open + i + return true + }) + + if (state.end > -1) return source.slice(start, state.end + 1) + + throw new Error("block closing brace not found") +} + function readSrcFiles(dir: string): string { const parts: string[] = [] for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { @@ -141,7 +159,7 @@ describe("Extension — KiloProvider handler wiring", () => { it("openKiloInNewTab wires setContinueInWorktreeHandler before resolveWebviewPanel", () => { const fn = ext.indexOf("function openKiloInNewTab") expect(fn, "openKiloInNewTab must exist").toBeGreaterThan(-1) - const body = ext.slice(fn, fn + 1500) + const body = sliceBlock(ext, fn) const handler = body.indexOf("setContinueInWorktreeHandler") const resolve = body.indexOf("resolveWebviewPanel") expect(handler, "setContinueInWorktreeHandler must be called").toBeGreaterThan(-1) @@ -152,7 +170,7 @@ describe("Extension — KiloProvider handler wiring", () => { it("TabPanel deserializer wires setContinueInWorktreeHandler before resolveWebviewPanel", () => { const serializer = ext.indexOf('"kilo-code.new.TabPanel"') expect(serializer, "TabPanel serializer must exist").toBeGreaterThan(-1) - const body = ext.slice(serializer, serializer + 800) + const body = sliceBlock(ext, serializer) const handler = body.indexOf("setContinueInWorktreeHandler") const resolve = body.indexOf("resolveWebviewPanel") expect(handler, "setContinueInWorktreeHandler must be called in deserializer").toBeGreaterThan(-1) diff --git a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx index 4071580eee..83d6f6fe49 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/AgentManagerApp.tsx @@ -1,7 +1,6 @@ -// Agent Manager root component +/** @jsxImportSource solid-js */ import { - Component, For, Show, createSignal, @@ -11,6 +10,7 @@ import { onMount, onCleanup, type Accessor, + type Component, } from "solid-js" import type { ExtensionMessage, @@ -137,14 +137,10 @@ interface ApplyState { message: string conflicts: AgentManagerApplyWorktreeDiffConflict[] } - /** Sidebar selection: LOCAL for local repo, worktree ID for a worktree, or null for an unassigned session. */ type SidebarSelection = typeof LOCAL | string | null - type SidePanel = "diff" | "pr" | null - const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.userAgent) - // Fallback keybindings before extension sends resolved ones const MAX_JUMP_INDEX = 9 @@ -179,7 +175,6 @@ function useTabScroll(activeTabs: Accessor, activeId: Accessor() const [showLeft, setShowLeft] = createSignal(false) const [showRight, setShowRight] = createSignal(false) - let scrollFrame: number | undefined const update = () => { if (scrollFrame !== undefined) return @@ -191,7 +186,6 @@ function useTabScroll(activeTabs: Accessor, activeId: Accessor { const el = ref() @@ -200,7 +194,6 @@ function useTabScroll(activeTabs: Accessor, activeId: Accessor 0 ? 60 : -60 } - // Recalculate on scroll, resize, or tab changes createEffect(() => { const el = ref() @@ -218,7 +211,6 @@ function useTabScroll(activeTabs: Accessor, activeId: Accessor { const id = activeId() const el = ref() @@ -237,7 +229,6 @@ function useTabScroll(activeTabs: Accessor, activeId: Accessor { - {t("agentManager.worktree.new")} + - - {t("agentManager.dialog.advanced")} + {t("agentManager.dialog.configureWorktree")} {parseBindingTokens(kb().advancedWorktree ?? "").map((token) => ( {token} diff --git a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx index 13f5692bf6..3e54451f4e 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx +++ b/packages/kilo-vscode/webview-ui/agent-manager/NewWorktreeDialog.tsx @@ -1,6 +1,8 @@ // New Worktree dialog — prompt, versions, model, mode, import tab -import { Component, For, Show, createSignal, createEffect, createMemo, onMount, onCleanup } from "solid-js" +/** @jsxImportSource solid-js */ + +import { type Component, For, Show, createSignal, createEffect, createMemo, onMount, onCleanup } from "solid-js" import type { AgentManagerBranchesMessage, AgentManagerImportResultMessage, BranchInfo } from "../src/types/messages" import { Dialog } from "@kilocode/kilo-ui/dialog" import { showToast } from "@kilocode/kilo-ui/toast" @@ -171,6 +173,17 @@ export const NewWorktreeDialog: Component<{ onClose: () => void; defaultBaseBran vscode.postMessage({ type: "agentManager.requestBranches" }) // Resize textarea if restoring a cached prompt if (prompt()) adjustHeight() + const focus = () => { + textareaRef?.focus({ preventScroll: true }) + const end = textareaRef?.value.length ?? 0 + textareaRef?.setSelectionRange(end, end) + } + requestAnimationFrame(() => { + focus() + requestAnimationFrame(focus) + setTimeout(focus, 0) + setTimeout(focus, 50) + }) }) const effectiveBaseBranch = () => baseBranch() ?? defaultBranch() diff --git a/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css b/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css index 368cd6863e..c001212a07 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css +++ b/packages/kilo-vscode/webview-ui/agent-manager/agent-manager.css @@ -2029,15 +2029,36 @@ body.am-wt-dragging-active * { } .am-split-menu { - min-width: 210px; + min-width: 250px; } .am-split-menu [data-slot="dropdown-menu-item"] { padding: 6px 10px; } -.am-split-menu [data-slot="dropdown-menu-separator"] { - margin: 4px 0; +.am-worktree-menu-gap { + display: inline-flex; + width: 16px; + height: 16px; + flex-shrink: 0; +} + +.am-worktree-menu-label { + display: inline-flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.am-worktree-menu-branch { + display: inline-flex; + align-items: center; + gap: 4px; + min-width: 0; +} + +.am-worktree-menu-branch strong { + font-weight: 600; } /* New Versioned Session Dialog */ diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ar.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ar.ts index 8e86eca5d9..8fe7cc66ef 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ar.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ar.ts @@ -71,7 +71,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "العنصر التالي", "agentManager.shortcuts.newWorktree": "Worktree جديد", "agentManager.shortcuts.openWorktree": "فتح Worktree", - "agentManager.shortcuts.advancedWorktree": "Worktree متقدم", + "agentManager.shortcuts.advancedWorktree": "تكوين worktree جديد", "agentManager.shortcuts.deleteWorktree": "حذف Worktree", "agentManager.shortcuts.previousTab": "علامة التبويب السابقة", "agentManager.shortcuts.nextTab": "علامة التبويب التالية", @@ -93,6 +93,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "إزالة Worktree القديم", "agentManager.dialog.openWorktree": "شجرة عمل جديدة", + "agentManager.dialog.configureWorktree": "تكوين Worktree جديد...", "agentManager.dialog.tab.new": "جديد", "agentManager.dialog.tab.import": "استيراد", "agentManager.dialog.namePlaceholder": "اسم Worktree (اختياري)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/br.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/br.ts index cdaf64ae9e..0403f4757c 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/br.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/br.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Próximo item", "agentManager.shortcuts.newWorktree": "Novo Worktree", "agentManager.shortcuts.openWorktree": "Abrir Worktree", - "agentManager.shortcuts.advancedWorktree": "Worktree avançado", + "agentManager.shortcuts.advancedWorktree": "Configurar novo worktree", "agentManager.shortcuts.deleteWorktree": "Excluir Worktree", "agentManager.shortcuts.previousTab": "Aba anterior", "agentManager.shortcuts.nextTab": "Próxima aba", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Remover Worktree obsoleto", "agentManager.dialog.openWorktree": "Novo Worktree", + "agentManager.dialog.configureWorktree": "Configurar Novo Worktree...", "agentManager.dialog.tab.new": "Novo", "agentManager.dialog.tab.import": "Importar", "agentManager.dialog.namePlaceholder": "Nome do Worktree (opcional)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/bs.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/bs.ts index bedf7a0535..83d07a4300 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/bs.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/bs.ts @@ -73,7 +73,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Sljedeći element", "agentManager.shortcuts.newWorktree": "Novi Worktree", "agentManager.shortcuts.openWorktree": "Otvori worktree", - "agentManager.shortcuts.advancedWorktree": "Napredni Worktree", + "agentManager.shortcuts.advancedWorktree": "Konfiguriši novi worktree", "agentManager.shortcuts.deleteWorktree": "Obriši Worktree", "agentManager.shortcuts.previousTab": "Prethodna kartica", "agentManager.shortcuts.nextTab": "Sljedeća kartica", @@ -95,6 +95,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Ukloni zastarjeli Worktree", "agentManager.dialog.openWorktree": "Novi worktree", + "agentManager.dialog.configureWorktree": "Konfiguriši Novi Worktree...", "agentManager.dialog.tab.new": "Novo", "agentManager.dialog.tab.import": "Uvezi", "agentManager.dialog.namePlaceholder": "Naziv Worktree-a (opcionalno)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/da.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/da.ts index beed91109b..b922b7ad48 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/da.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/da.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Næste element", "agentManager.shortcuts.newWorktree": "Nyt Worktree", "agentManager.shortcuts.openWorktree": "Åbn Worktree", - "agentManager.shortcuts.advancedWorktree": "Avanceret Worktree", + "agentManager.shortcuts.advancedWorktree": "Konfigurer nyt worktree", "agentManager.shortcuts.deleteWorktree": "Slet Worktree", "agentManager.shortcuts.previousTab": "Forrige fane", "agentManager.shortcuts.nextTab": "Næste fane", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Fjern forældet Worktree", "agentManager.dialog.openWorktree": "Ny Worktree", + "agentManager.dialog.configureWorktree": "Konfigurer Nyt Worktree...", "agentManager.dialog.tab.new": "Ny", "agentManager.dialog.tab.import": "Importér", "agentManager.dialog.namePlaceholder": "Worktree-navn (valgfrit)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/de.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/de.ts index acc48640f0..056b3365ea 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/de.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/de.ts @@ -73,7 +73,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Nächstes Element", "agentManager.shortcuts.newWorktree": "Neuer Worktree", "agentManager.shortcuts.openWorktree": "Worktree öffnen", - "agentManager.shortcuts.advancedWorktree": "Erweiterter Worktree", + "agentManager.shortcuts.advancedWorktree": "Neuen Worktree konfigurieren", "agentManager.shortcuts.deleteWorktree": "Worktree löschen", "agentManager.shortcuts.previousTab": "Vorheriger Tab", "agentManager.shortcuts.nextTab": "Nächster Tab", @@ -95,6 +95,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Veralteten Worktree entfernen", "agentManager.dialog.openWorktree": "Neuer Worktree", + "agentManager.dialog.configureWorktree": "Neuen Worktree konfigurieren...", "agentManager.dialog.tab.new": "Neu", "agentManager.dialog.tab.import": "Importieren", "agentManager.dialog.namePlaceholder": "Worktree-Name (optional)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/en.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/en.ts index bde300bd16..e564072f3a 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/en.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/en.ts @@ -77,7 +77,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Next item", "agentManager.shortcuts.newWorktree": "New worktree", "agentManager.shortcuts.openWorktree": "Open worktree", - "agentManager.shortcuts.advancedWorktree": "Advanced worktree", + "agentManager.shortcuts.advancedWorktree": "Configure new worktree", "agentManager.shortcuts.deleteWorktree": "Delete worktree", "agentManager.shortcuts.previousTab": "Previous tab", "agentManager.shortcuts.nextTab": "Next tab", @@ -122,6 +122,7 @@ export const dict = { "agentManager.dialog.createWorktree": "Create Worktree", "agentManager.dialog.removeImage": "Remove image", "agentManager.dialog.advanced": "Advanced...", + "agentManager.dialog.configureWorktree": "Configure New Worktree...", "agentManager.diff.toggle": "Toggle diff", "agentManager.diff.openFile": "Open file", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/es.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/es.ts index 6319f590a9..55fe7198a8 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/es.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/es.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Siguiente elemento", "agentManager.shortcuts.newWorktree": "Nuevo Worktree", "agentManager.shortcuts.openWorktree": "Abrir Worktree", - "agentManager.shortcuts.advancedWorktree": "Worktree avanzado", + "agentManager.shortcuts.advancedWorktree": "Configurar nuevo worktree", "agentManager.shortcuts.deleteWorktree": "Eliminar Worktree", "agentManager.shortcuts.previousTab": "Pestaña anterior", "agentManager.shortcuts.nextTab": "Siguiente pestaña", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Eliminar Worktree obsoleto", "agentManager.dialog.openWorktree": "Nuevo Worktree", + "agentManager.dialog.configureWorktree": "Configurar Nuevo Worktree...", "agentManager.dialog.tab.new": "Nuevo", "agentManager.dialog.tab.import": "Importar", "agentManager.dialog.namePlaceholder": "Nombre del Worktree (opcional)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/fr.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/fr.ts index 840d96b413..d61da449af 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/fr.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/fr.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Élément suivant", "agentManager.shortcuts.newWorktree": "Nouveau Worktree", "agentManager.shortcuts.openWorktree": "Ouvrir le worktree", - "agentManager.shortcuts.advancedWorktree": "Worktree avancé", + "agentManager.shortcuts.advancedWorktree": "Configurer un nouveau worktree", "agentManager.shortcuts.deleteWorktree": "Supprimer le Worktree", "agentManager.shortcuts.previousTab": "Onglet précédent", "agentManager.shortcuts.nextTab": "Onglet suivant", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Supprimer le Worktree obsolète", "agentManager.dialog.openWorktree": "Nouveau worktree", + "agentManager.dialog.configureWorktree": "Configurer un Nouveau Worktree...", "agentManager.dialog.tab.new": "Nouveau", "agentManager.dialog.tab.import": "Importer", "agentManager.dialog.namePlaceholder": "Nom du Worktree (optionnel)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ja.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ja.ts index 965a2fdd6a..4035c4ac95 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ja.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ja.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "次の項目", "agentManager.shortcuts.newWorktree": "新しいWorktree", "agentManager.shortcuts.openWorktree": "Worktreeを開く", - "agentManager.shortcuts.advancedWorktree": "詳細Worktree", + "agentManager.shortcuts.advancedWorktree": "新規 worktree の構成", "agentManager.shortcuts.deleteWorktree": "Worktreeを削除", "agentManager.shortcuts.previousTab": "前のタブ", "agentManager.shortcuts.nextTab": "次のタブ", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "無効な Worktree を削除", "agentManager.dialog.openWorktree": "新規ワークツリー", + "agentManager.dialog.configureWorktree": "新規 Worktree の構成...", "agentManager.dialog.tab.new": "新規", "agentManager.dialog.tab.import": "インポート", "agentManager.dialog.namePlaceholder": "Worktree名(任意)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ko.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ko.ts index 6580d9bfe7..3fa6207bd9 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ko.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ko.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "다음 항목", "agentManager.shortcuts.newWorktree": "새 Worktree", "agentManager.shortcuts.openWorktree": "Worktree 열기", - "agentManager.shortcuts.advancedWorktree": "고급 Worktree", + "agentManager.shortcuts.advancedWorktree": "새 worktree 구성", "agentManager.shortcuts.deleteWorktree": "Worktree 삭제", "agentManager.shortcuts.previousTab": "이전 탭", "agentManager.shortcuts.nextTab": "다음 탭", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "오래된 Worktree 제거", "agentManager.dialog.openWorktree": "새 워크트리", + "agentManager.dialog.configureWorktree": "새 Worktree 구성...", "agentManager.dialog.tab.new": "새로 만들기", "agentManager.dialog.tab.import": "가져오기", "agentManager.dialog.namePlaceholder": "Worktree 이름 (선택사항)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/nl.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/nl.ts index bf52dacc3e..6d71eb72fb 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/nl.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/nl.ts @@ -77,7 +77,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Volgend item", "agentManager.shortcuts.newWorktree": "Nieuwe worktree", "agentManager.shortcuts.openWorktree": "Worktree openen", - "agentManager.shortcuts.advancedWorktree": "Geavanceerde worktree", + "agentManager.shortcuts.advancedWorktree": "Nieuwe worktree configureren", "agentManager.shortcuts.deleteWorktree": "Worktree verwijderen", "agentManager.shortcuts.previousTab": "Vorig tabblad", "agentManager.shortcuts.nextTab": "Volgend tabblad", @@ -100,6 +100,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Verouderde worktree verwijderen", "agentManager.dialog.openWorktree": "Nieuwe worktree", + "agentManager.dialog.configureWorktree": "Nieuwe Worktree Configureren...", "agentManager.dialog.tab.new": "Nieuw", "agentManager.dialog.tab.import": "Importeren", "agentManager.dialog.namePlaceholder": "Worktree naam (optioneel)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/no.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/no.ts index 22ce0f348b..bd0fe91965 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/no.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/no.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Neste element", "agentManager.shortcuts.newWorktree": "Nytt Worktree", "agentManager.shortcuts.openWorktree": "Åpne Worktree", - "agentManager.shortcuts.advancedWorktree": "Avansert Worktree", + "agentManager.shortcuts.advancedWorktree": "Konfigurer nytt worktree", "agentManager.shortcuts.deleteWorktree": "Slett Worktree", "agentManager.shortcuts.previousTab": "Forrige fane", "agentManager.shortcuts.nextTab": "Neste fane", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Fjern utdatert Worktree", "agentManager.dialog.openWorktree": "Ny worktree", + "agentManager.dialog.configureWorktree": "Konfigurer Nytt Worktree...", "agentManager.dialog.tab.new": "Ny", "agentManager.dialog.tab.import": "Importer", "agentManager.dialog.namePlaceholder": "Worktree-navn (valgfritt)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/pl.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/pl.ts index 84abc9b768..d27b43d4c3 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/pl.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/pl.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Następny element", "agentManager.shortcuts.newWorktree": "Nowy Worktree", "agentManager.shortcuts.openWorktree": "Otwórz Worktree", - "agentManager.shortcuts.advancedWorktree": "Zaawansowany Worktree", + "agentManager.shortcuts.advancedWorktree": "Skonfiguruj nowe worktree", "agentManager.shortcuts.deleteWorktree": "Usuń Worktree", "agentManager.shortcuts.previousTab": "Poprzednia karta", "agentManager.shortcuts.nextTab": "Następna karta", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Usuń nieaktualny Worktree", "agentManager.dialog.openWorktree": "Nowy Worktree", + "agentManager.dialog.configureWorktree": "Skonfiguruj Nowe Worktree...", "agentManager.dialog.tab.new": "Nowy", "agentManager.dialog.tab.import": "Importuj", "agentManager.dialog.namePlaceholder": "Nazwa Worktree (opcjonalnie)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ru.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ru.ts index 96a6117c1d..c963c944c6 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/ru.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/ru.ts @@ -72,7 +72,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Следующий элемент", "agentManager.shortcuts.newWorktree": "Новый Worktree", "agentManager.shortcuts.openWorktree": "Открыть Worktree", - "agentManager.shortcuts.advancedWorktree": "Расширенный Worktree", + "agentManager.shortcuts.advancedWorktree": "Настроить новое worktree", "agentManager.shortcuts.deleteWorktree": "Удалить Worktree", "agentManager.shortcuts.previousTab": "Предыдущая вкладка", "agentManager.shortcuts.nextTab": "Следующая вкладка", @@ -94,6 +94,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Удалить устаревший Worktree", "agentManager.dialog.openWorktree": "Новый worktree", + "agentManager.dialog.configureWorktree": "Настроить новое Worktree...", "agentManager.dialog.tab.new": "Новый", "agentManager.dialog.tab.import": "Импорт", "agentManager.dialog.namePlaceholder": "Имя Worktree (необязательно)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/th.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/th.ts index 0494959ffc..506f6ba0fa 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/th.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/th.ts @@ -69,7 +69,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "รายการถัดไป", "agentManager.shortcuts.newWorktree": "Worktree ใหม่", "agentManager.shortcuts.openWorktree": "เปิด Worktree", - "agentManager.shortcuts.advancedWorktree": "Worktree ขั้นสูง", + "agentManager.shortcuts.advancedWorktree": "กำหนดค่า worktree ใหม่", "agentManager.shortcuts.deleteWorktree": "ลบ Worktree", "agentManager.shortcuts.previousTab": "แท็บก่อนหน้า", "agentManager.shortcuts.nextTab": "แท็บถัดไป", @@ -91,6 +91,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "ลบ Worktree ที่ล้าสมัย", "agentManager.dialog.openWorktree": "Worktree ใหม่", + "agentManager.dialog.configureWorktree": "กำหนดค่า Worktree ใหม่...", "agentManager.dialog.tab.new": "ใหม่", "agentManager.dialog.tab.import": "นำเข้า", "agentManager.dialog.namePlaceholder": "ชื่อ Worktree (ไม่บังคับ)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/tr.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/tr.ts index 63ced21e2c..1dac84b599 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/tr.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/tr.ts @@ -77,7 +77,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Sonraki öğe", "agentManager.shortcuts.newWorktree": "Yeni worktree", "agentManager.shortcuts.openWorktree": "Worktree aç", - "agentManager.shortcuts.advancedWorktree": "Gelişmiş worktree", + "agentManager.shortcuts.advancedWorktree": "Yeni worktree yapılandır", "agentManager.shortcuts.deleteWorktree": "Worktree'yi sil", "agentManager.shortcuts.previousTab": "Önceki sekme", "agentManager.shortcuts.nextTab": "Sonraki sekme", @@ -100,6 +100,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Eskimiş worktree'yi kaldır", "agentManager.dialog.openWorktree": "Yeni Worktree", + "agentManager.dialog.configureWorktree": "Yeni Worktree Yapılandır...", "agentManager.dialog.tab.new": "Yeni", "agentManager.dialog.tab.import": "İçe Aktar", "agentManager.dialog.namePlaceholder": "Worktree adı (isteğe bağlı)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/uk.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/uk.ts index c9038e02a9..d2d196c660 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/uk.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/uk.ts @@ -78,7 +78,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "Наступний елемент", "agentManager.shortcuts.newWorktree": "Нове робоче дерево", "agentManager.shortcuts.openWorktree": "Відкрити робоче дерево", - "agentManager.shortcuts.advancedWorktree": "Розширене робоче дерево", + "agentManager.shortcuts.advancedWorktree": "Налаштувати нове worktree", "agentManager.shortcuts.deleteWorktree": "Видалити робоче дерево", "agentManager.shortcuts.previousTab": "Попередня вкладка", "agentManager.shortcuts.nextTab": "Наступна вкладка", @@ -101,6 +101,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "Видалити застаріле робоче дерево", "agentManager.dialog.openWorktree": "Нове робоче дерево", + "agentManager.dialog.configureWorktree": "Налаштувати нове Worktree...", "agentManager.dialog.tab.new": "Нове", "agentManager.dialog.tab.import": "Імпортувати", "agentManager.dialog.namePlaceholder": "Назва робочого дерева (необов'язково)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/zh.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/zh.ts index ba08ae225b..c125c2e385 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/zh.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/zh.ts @@ -69,7 +69,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "下一项", "agentManager.shortcuts.newWorktree": "新建 Worktree", "agentManager.shortcuts.openWorktree": "打开 Worktree", - "agentManager.shortcuts.advancedWorktree": "高级 Worktree", + "agentManager.shortcuts.advancedWorktree": "配置新 worktree", "agentManager.shortcuts.deleteWorktree": "删除 Worktree", "agentManager.shortcuts.previousTab": "上一个标签页", "agentManager.shortcuts.nextTab": "下一个标签页", @@ -90,6 +90,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "移除失效 Worktree", "agentManager.dialog.openWorktree": "新建工作树", + "agentManager.dialog.configureWorktree": "配置新 Worktree...", "agentManager.dialog.tab.new": "新建", "agentManager.dialog.tab.import": "导入", "agentManager.dialog.namePlaceholder": "Worktree 名称(可选)", diff --git a/packages/kilo-vscode/webview-ui/agent-manager/i18n/zht.ts b/packages/kilo-vscode/webview-ui/agent-manager/i18n/zht.ts index 57b281eb83..4fcdc1f355 100644 --- a/packages/kilo-vscode/webview-ui/agent-manager/i18n/zht.ts +++ b/packages/kilo-vscode/webview-ui/agent-manager/i18n/zht.ts @@ -69,7 +69,7 @@ export const dict = { "agentManager.shortcuts.nextItem": "下一個項目", "agentManager.shortcuts.newWorktree": "新建 Worktree", "agentManager.shortcuts.openWorktree": "開啟 Worktree", - "agentManager.shortcuts.advancedWorktree": "進階 Worktree", + "agentManager.shortcuts.advancedWorktree": "配置新 worktree", "agentManager.shortcuts.deleteWorktree": "刪除 Worktree", "agentManager.shortcuts.previousTab": "上一個分頁", "agentManager.shortcuts.nextTab": "下一個分頁", @@ -90,6 +90,7 @@ export const dict = { "agentManager.dialog.removeStaleWorktree.confirm": "移除失效 Worktree", "agentManager.dialog.openWorktree": "新建工作樹", + "agentManager.dialog.configureWorktree": "配置新 Worktree...", "agentManager.dialog.tab.new": "新建", "agentManager.dialog.tab.import": "匯入", "agentManager.dialog.namePlaceholder": "Worktree 名稱(選填)", diff --git a/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx b/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx index 96841f7b7f..a3d288c34a 100644 --- a/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx +++ b/packages/kilo-vscode/webview-ui/diff-virtual/DiffVirtualApp.tsx @@ -32,9 +32,10 @@ const DiffVirtualContent: Component = () => { const [style, setStyle] = createSignal("unified") const handler = (event: MessageEvent) => { - const msg = event.data as { type: string; diff?: DiffVirtualFile } + const msg = event.data as { type: string; diff?: DiffVirtualFile; initialDiffStyle?: DiffStyle } if (msg?.type === "diffVirtual.data" && msg.diff) { setDiff(msg.diff) + setStyle(msg.initialDiffStyle ?? "unified") } } diff --git a/packages/kilo-vscode/webview-ui/src/App.tsx b/packages/kilo-vscode/webview-ui/src/App.tsx index ae69fe3a8d..c774defdac 100644 --- a/packages/kilo-vscode/webview-ui/src/App.tsx +++ b/packages/kilo-vscode/webview-ui/src/App.tsx @@ -134,7 +134,7 @@ export const DataBridge: Component<{ children: any }> = (props) => { } const openDiff = (diff: { file: string; before: string; after: string; additions: number; deletions: number }) => { - vscode.postMessage({ type: "openDiffVirtual", diff }) + vscode.postMessage({ type: "openDiffVirtual", diff, initialDiffStyle: "split" }) } const openUrl = (url: string) => { diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx index 7da3fa1dce..0b656f5c02 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/ChatView.tsx @@ -1,14 +1,17 @@ +/** @jsxImportSource solid-js */ + /** * ChatView component * Main chat container that combines all chat components */ -import { Component, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from "solid-js" +import { type Component, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from "solid-js" import { Button } from "@kilocode/kilo-ui/button" import { Icon } from "@kilocode/kilo-ui/icon" import { Spinner } from "@kilocode/kilo-ui/spinner" import { Tooltip } from "@kilocode/kilo-ui/tooltip" import { showToast } from "@kilocode/kilo-ui/toast" +import { DropdownMenu } from "@kilocode/kilo-ui/dropdown-menu" import { TaskHeader } from "./TaskHeader" import { MessageList } from "./MessageList" import { PromptInput } from "./PromptInput" @@ -50,6 +53,8 @@ export const ChatView: Component = (props) => { // "Continue in Worktree" state const [transferring, setTransferring] = createSignal(false) const [transferDetail, setTransferDetail] = createSignal("") + const [repoBranch, setRepoBranch] = createSignal() + let worktreeRef: HTMLDivElement | undefined // Permissions and questions scoped to this session's family (self + subagents). // Each ChatView only sees its own session tree — no cross-session leakage. @@ -98,13 +103,17 @@ export const ChatView: Component = (props) => { // Listen for "Continue in Worktree" progress messages { const labels: Record = { - capturing: "Capturing changes...", - creating: "Creating worktree...", - setup: "Running setup...", - transferring: "Transferring changes...", - forking: "Starting session...", + capturing: language.t("sidebar.session.progress.capturing"), + creating: language.t("sidebar.session.progress.creating"), + setup: language.t("sidebar.session.progress.setup"), + transferring: language.t("sidebar.session.progress.transferring"), + forking: language.t("sidebar.session.progress.forking"), } const cleanup = vscode.onMessage((msg) => { + if (msg.type === "agentManager.repoInfo") { + setRepoBranch(msg.branch) + return + } if (msg.type !== "continueInWorktreeProgress") return const m = msg as { status: string; error?: string } if (m.status === "done") { @@ -115,10 +124,10 @@ export const ChatView: Component = (props) => { if (m.status === "error") { setTransferring(false) setTransferDetail("") - showToast({ title: m.error ?? "Failed to continue in worktree" }) + showToast({ title: m.error ?? language.t("sidebar.session.progress.failed") }) return } - setTransferDetail(labels[m.status] ?? "Working...") + setTransferDetail(labels[m.status] ?? language.t("session.status.working")) }) onCleanup(cleanup) } @@ -129,6 +138,170 @@ export const ChatView: Component = (props) => { session.respondToPermission(perm.id, response, approvedAlways, deniedAlways) } + const startSession = () => window.dispatchEvent(new CustomEvent("newTaskRequest")) + + const startWorktree = () => vscode.postMessage({ type: "agentManager.createWorktree" }) + + const startWorktreeFromBranch = () => + vscode.postMessage({ type: "agentManager.createWorktree", baseBranch: repoBranch()! }) + + const openAgentManager = () => vscode.postMessage({ type: "openAgentManager" }) + + const openChanges = () => vscode.postMessage({ type: "openChanges" }) + + const moveToWorktree = () => { + if (transferring()) return + const sid = id() + if (!sid) return + setTransferring(true) + setTransferDetail(language.t("sidebar.session.progress.capturing")) + vscode.postMessage({ type: "continueInWorktree", sessionId: sid }) + } + + const worktreeTooltip = language.t("sidebar.session.newWorktree.tooltip") + + const advancedTooltip = language.t("sidebar.session.configureWorktree.tooltip") + + const moveTooltip = () => { + const stats = session.worktreeStats() + if (!stats?.files) return language.t("sidebar.session.moveToWorktree.tooltip.empty") + if (stats.files === 1) return language.t("sidebar.session.moveToWorktree.tooltip.one") + return language.t("sidebar.session.moveToWorktree.tooltip.other", { files: stats.files }) + } + + const changesTooltip = () => { + const stats = session.worktreeStats() + if (!stats?.files) return language.t("sidebar.session.showChanges.tooltip.empty") + return ( + + {stats.files === 1 ? "1 file changed" : `${stats.files} files changed`} + · + +{stats.additions} + -{stats.deletions} + Open the changes view. + + ) + } + + const showAdvancedWorktree = () => vscode.postMessage({ type: "openAdvancedWorktree" }) + + createEffect(() => { + if (!isSidebar() || !server.gitInstalled()) return + vscode.postMessage({ type: "agentManager.requestRepoInfo" }) + }) + + const renderActions = (hasChat: boolean) => ( +
+
+ + + + +
+ + + + worktreeRef?.getBoundingClientRect()}> + + + + + + + + + + + + + {language.t("agentManager.dialog.configureWorktree")} + + + + + +
+
+ + <> + + + + + + + + +
+
+ ) + return (
@@ -159,70 +332,8 @@ export const ChatView: Component = (props) => { /> )} - -
-
- - - - - - - - - - 1 ? "s" : ""} changed · +${session.worktreeStats()!.additions} -${session.worktreeStats()!.deletions}` - : "No file changes" - } - placement="top" - class="session-diff-wrapper" - > - - - -
-
+ + {renderActions(hasMessages())} = (props) => { vscode.postMessage({ type: "openDiffVirtual", diff: { ...props.filediff, before, after }, + initialDiffStyle: "unified", }) } diff --git a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx index 6d99d009be..b38204d94b 100644 --- a/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/chat/PromptInput.tsx @@ -979,7 +979,7 @@ export const PromptInput: Component = (props) => { variant="ghost" size="small" onClick={() => void handleSend()} - disabled={!canSend()} + aria-disabled={!canSend()} aria-label={language.t("prompt.action.send")} > diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx index 751dd2a1e6..5bc297fd9b 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AutoApproveTab.tsx @@ -296,7 +296,9 @@ const SimpleToolRow: Component<{ }} >
-
{toolTitle(props.id)}
+
+ {toolTitle(props.id)} +
-
{toolTitle(props.tool.id)}
+
+ {toolTitle(props.tool.id)} +
.new-task-button-wrapper + .prompt-input-container { + margin-top: 0; +} + +:root { + --session-action-radius: 6px; +} + .session-actions-row { display: flex; gap: 4px; align-items: center; + flex-wrap: nowrap; + min-height: 22px; + justify-content: flex-start; } -/* All direct children share extra space equally, but start from their content size */ .session-actions-row > * { - flex: 1 1 auto; + flex: 0 0 auto; min-width: 0; overflow: hidden; } -/* Tooltip wrappers fill their flex slot, buttons fill the tooltip wrapper */ .session-actions-row > [data-component="tooltip-trigger"] { display: flex; + flex: 0 0 auto; +} + +.session-actions-row > .session-move-changes-trigger { + margin-left: auto; } .session-actions-row > [data-component="tooltip-trigger"] > [data-component="button"] { - flex: 1; + flex: 0 0 auto; + border-radius: var(--session-action-radius); + min-height: 22px; + height: auto; + padding: 4px 6px !important; + background: transparent; + border: none; + box-shadow: none; + font-family: var(--font-family-sans); + font-size: 12px; + font-style: normal; + font-weight: var(--font-weight-medium); + line-height: normal; + letter-spacing: var(--letter-spacing-normal); + color: var(--vscode-descriptionForeground); + transition: + background-color 150ms ease, + color 150ms ease; } -/* Diff wrapper also stretches evenly (same flex as buttons) */ -.session-diff-wrapper { - display: flex !important; +.session-actions-row > [data-component="tooltip-trigger"] > [data-component="button"]:hover:not(:disabled), +.session-actions-row > [data-component="tooltip-trigger"] > [data-component="button"]:focus-visible:not(:disabled), +.session-actions-row > [data-component="tooltip-trigger"] > [data-component="button"]:active:not(:disabled) { + background: var(--surface-interactive-hover, var(--vscode-list-hoverBackground)); + color: var(--vscode-foreground); } -/* Diff stats badge — fills its flex slot, content centered */ +.session-actions-row > [data-component="tooltip-trigger"] > [data-component="button"].session-new-button:disabled { + cursor: default; +} + +.session-worktree-split { + display: flex; + flex: 0 0 auto; + min-width: 0; + align-items: stretch; + gap: 2px; + border-radius: var(--session-action-radius); + box-shadow: none; + overflow: hidden; + color: var(--vscode-descriptionForeground); + transition: + background-color 150ms ease, + color 150ms ease; +} + +.session-worktree-split:hover, +.session-worktree-split:has(.session-worktree-main:focus-visible), +.session-worktree-split:has(.session-worktree-split-arrow:focus-visible), +.session-worktree-split:has(.session-worktree-split-arrow[data-expanded]) { + color: var(--vscode-foreground); +} + +.session-worktree-split:hover > [data-component="tooltip-trigger"] > .session-worktree-main, +.session-worktree-split:hover .session-worktree-split-arrow, +.session-worktree-split:has(.session-worktree-main:focus-visible) + > [data-component="tooltip-trigger"] + > .session-worktree-main, +.session-worktree-split:has(.session-worktree-main:focus-visible) .session-worktree-split-arrow, +.session-worktree-split:has(.session-worktree-split-arrow:focus-visible) + > [data-component="tooltip-trigger"] + > .session-worktree-main, +.session-worktree-split:has(.session-worktree-split-arrow:focus-visible) .session-worktree-split-arrow, +.session-worktree-split:has(.session-worktree-split-arrow[data-expanded]) + > [data-component="tooltip-trigger"] + > .session-worktree-main, +.session-worktree-split:has(.session-worktree-split-arrow[data-expanded]) .session-worktree-split-arrow { + background: var(--surface-interactive-hover, var(--vscode-list-hoverBackground)); +} + +.session-worktree-split > [data-component="tooltip-trigger"] { + display: flex; + flex: 0 0 auto; +} + +.session-worktree-split > [data-component="tooltip-trigger"] > [data-component="button"] { + flex: 0 0 auto; + border-radius: var(--session-action-radius) 0 0 var(--session-action-radius); + background: transparent; + box-shadow: none; + color: inherit; + padding: 4px 6px !important; +} + +.session-worktree-split > [data-component="tooltip-trigger"] > [data-component="button"]:hover:not(:disabled), +.session-worktree-split > [data-component="tooltip-trigger"] > [data-component="button"]:focus-visible:not(:disabled), +.session-worktree-split > [data-component="tooltip-trigger"] > [data-component="button"]:active:not(:disabled) { + background: var(--surface-base-hover); + box-shadow: none; +} + +.session-worktree-split-arrow { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 12px; + padding: 0 3px; + border: none; + border-radius: 0 var(--session-action-radius) var(--session-action-radius) 0; + background: transparent; + color: inherit; + cursor: pointer; +} + +.session-worktree-split-arrow:focus-visible { + outline: none; + background: inherit; +} + +.session-worktree-split > [data-component="tooltip-trigger"] > [data-component="button"], +.session-worktree-split-arrow { + height: 24px; +} + +.session-worktree-main { + padding-right: 6px !important; +} + +.session-worktree-split-menu { + min-width: 250px; +} + +.session-worktree-split-menu [data-slot="dropdown-menu-item"] { + padding: 6px 10px; +} + +.session-worktree-menu-gap { + display: inline-flex; + width: 16px; + height: 16px; + flex-shrink: 0; +} + +.session-worktree-menu-label { + display: inline-flex; + align-items: center; + gap: 6px; + min-width: 0; +} + +.session-worktree-menu-branch { + display: inline-flex; + align-items: center; + gap: 4px; + min-width: 0; +} + +.session-worktree-menu-branch strong { + font-weight: 600; +} + +.session-move-action { + display: flex; + flex: 0 0 auto; + align-items: center; + justify-content: center; + gap: 5px; + border-radius: var(--session-action-radius); + color: var(--vscode-descriptionForeground); + padding: 4px 6px !important; + background: transparent; + box-shadow: none; + transition: + background-color 150ms ease, + color 150ms ease; +} + +.session-move-action:hover:not(:disabled), +.session-move-action:focus-visible:not(:disabled), +.session-move-action:active:not(:disabled) { + color: var(--vscode-foreground); + background: var(--surface-interactive-hover, var(--vscode-list-hoverBackground)); + box-shadow: none; +} + +.session-move-label { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.session-move-action [data-component="icon"], +.session-move-changes [data-component="icon"] { + opacity: 0.7; +} + +.session-move-changes { + display: inline-flex; + align-items: center; + gap: 3px; + flex: 0 0 auto; + overflow: hidden; + position: relative; + border-radius: var(--session-action-radius); + color: var(--vscode-descriptionForeground); + padding: 4px 6px !important; + background: transparent; + box-shadow: none; + transition: + background-color 150ms ease, + color 150ms ease; +} + +.session-move-changes:hover:not(:disabled), +.session-move-changes:focus-visible:not(:disabled), +.session-move-changes:active:not(:disabled) { + color: var(--vscode-foreground); + background: var(--surface-interactive-hover, var(--vscode-list-hoverBackground)); + box-shadow: none; +} + +.session-move-changes { + font-family: var(--font-family-sans); + font-size: 12px; + font-variant-numeric: tabular-nums; +} + +.session-move-changes--empty { + opacity: 0.7; +} + +.session-move-dot { + position: absolute; + top: 4px; + right: 5px; + width: 5px; + height: 5px; + border-radius: 999px; + background: #34d399; + opacity: 0; +} + +/* Diff stats badge styles retained for the inline move button stats */ .session-diff-badge { display: flex; flex: 1; @@ -73,27 +316,56 @@ color: #f87171; } -/* Collapse diff stats to icon-only when truly narrow */ -@container (max-width: 300px) { - .session-diff-add, - .session-diff-del { - display: none; +.session-changes-tooltip { + display: inline-flex; + align-items: baseline; + flex-wrap: wrap; + column-gap: 4px; + row-gap: 2px; +} + +.session-changes-tooltip-separator { + color: var(--vscode-descriptionForeground); +} + +/* Collapse diff stats to icon-only before the row wraps. */ +@container (max-width: 420px) { + .session-actions-row { + gap: 4px; + flex-wrap: wrap; } - .session-diff-badge { - padding: 4px 6px; + .session-actions-row > *, + .session-worktree-split { + flex-basis: auto; + } + + .session-move-action { + padding-right: 5px !important; + } + + .session-move-changes { + gap: 0; + width: 26px; + min-width: 26px; + padding-left: 5px !important; + padding-right: 5px !important; + } + + .session-diff-add, + .session-diff-del { + width: 0; + opacity: 0; overflow: hidden; } - /* Dot indicator on the icon when collapsed and there are changes */ - .session-diff-badge--has-changes [data-component="icon"]::after { - content: ""; - position: absolute; - top: -2px; - right: -2px; - width: 6px; - height: 6px; - border-radius: 50%; - background: #34d399; + .session-move-dot { + opacity: 1; + } +} + +@container (max-width: 420px) { + .session-actions-row > .session-move-changes-trigger { + margin-left: 0; } } diff --git a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts index f40f47a216..de4a705b9e 100644 --- a/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts +++ b/packages/kilo-vscode/webview-ui/src/types/messages/webview-messages.ts @@ -194,6 +194,14 @@ export interface OpenMarketplacePanelRequest { type: "openMarketplacePanel" } +export interface OpenAgentManagerRequest { + type: "openAgentManager" +} + +export interface OpenAdvancedWorktreeRequest { + type: "openAdvancedWorktree" +} + export interface RequestAgentsMessage { type: "requestAgents" } @@ -723,6 +731,7 @@ export interface OpenChangesRequest { export interface OpenDiffVirtualRequest { type: "openDiffVirtual" diff: PermissionFileDiff + initialDiffStyle: "unified" | "split" } export interface RetryConnectionRequest { @@ -939,6 +948,8 @@ export type WebviewMessage = | OpenVSCodeSettingsRequest | OpenConfigFileRequest | OpenMarketplacePanelRequest + | OpenAgentManagerRequest + | OpenAdvancedWorktreeRequest | OpenFileRequest | CancelLoginRequest | SetOrganizationRequest diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 0868279a56..37c07d9831 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -33,6 +33,8 @@ const icons = { compress: ``, code: ``, "code-lines": ``, + "circuit-board": ``, + organization: ``, "circle-ban-sign": ``, "edit-small-2": ``, eye: ``, @@ -121,15 +123,18 @@ export interface IconProps extends ComponentProps<"svg"> { export function Icon(props: IconProps) { const [local, others] = splitProps(props, ["name", "size", "class", "classList"]) const viewBox = () => - local.name === "magnifying-glass" || local.name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20" + // kilocode_change : added organization + local.name === "magnifying-glass" || local.name === "circuit-board" || local.name === "organization" + ? "0 0 16 16" + : "0 0 20 20" return (