From c46b40ac1799bbbfae159b1d57205dc4903eff1b Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 09:07:59 +0000 Subject: [PATCH 1/7] fix(vscode): align autocomplete model selector --- .changeset/autocomplete-model-picker.md | 5 ++ packages/kilo-vscode/src/KiloProvider.ts | 3 + .../autocomplete/__tests__/settings.spec.ts | 31 +++------- .../src/services/autocomplete/settings.ts | 31 ++-------- .../unit/autocomplete-model-selector.test.ts | 20 +++++++ .../components/settings/AutocompleteTab.tsx | 40 ++++--------- .../src/components/settings/ModelsTab.tsx | 44 +++++--------- .../settings/autocomplete-model-selector.ts | 12 ++++ .../src/components/shared/ModelSelector.tsx | 15 ++++- .../webview-ui/src/context/config.tsx | 60 ++++++++++++++++--- .../webview-ui/src/stories/StoryProviders.tsx | 14 ++++- .../src/stories/settings.stories.tsx | 26 ++++++++ .../src/types/messages/webview-messages.ts | 7 --- 13 files changed, 184 insertions(+), 124 deletions(-) create mode 100644 .changeset/autocomplete-model-picker.md create mode 100644 packages/kilo-vscode/tests/unit/autocomplete-model-selector.test.ts create mode 100644 packages/kilo-vscode/webview-ui/src/components/settings/autocomplete-model-selector.ts diff --git a/.changeset/autocomplete-model-picker.md b/.changeset/autocomplete-model-picker.md new file mode 100644 index 00000000000..a24ba091f4d --- /dev/null +++ b/.changeset/autocomplete-model-picker.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show the autocomplete model selector with the same picker layout as other model selectors and save changes from the settings save bar. diff --git a/packages/kilo-vscode/src/KiloProvider.ts b/packages/kilo-vscode/src/KiloProvider.ts index f4ad6dd913b..a85dbd0b978 100644 --- a/packages/kilo-vscode/src/KiloProvider.ts +++ b/packages/kilo-vscode/src/KiloProvider.ts @@ -61,6 +61,7 @@ import { abortSession } from "./kilo-provider/abort" import { buildAutocompleteSettingsMessage, routeAutocompleteMessage, + validAutocompleteSetting, watchAutocompleteConfig, } from "./services/autocomplete/settings" import * as ModelState from "./kilo-provider/model-state" @@ -2860,8 +2861,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper */ private async handleUpdateSetting(key: string, value: unknown): Promise { const { section, leaf } = buildSettingPath(key) + if (section === "autocomplete" && !validAutocompleteSetting(leaf, value)) return const config = vscode.workspace.getConfiguration(`kilo-code.new${section ? `.${section}` : ""}`) await config.update(leaf, value, vscode.ConfigurationTarget.Global) + if (section === "autocomplete") this.postMessage(buildAutocompleteSettingsMessage()) } /** 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 9ee596bc23b..4cc2eb0a535 100644 --- a/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts +++ b/packages/kilo-vscode/src/services/autocomplete/__tests__/settings.spec.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest" const state = new Map() -const update = vi.fn(async (key: string, value: unknown) => { +const update = vi.fn((key: string, value: unknown) => { state.set(key, value) }) @@ -44,36 +44,21 @@ describe("autocomplete settings", () => { expect(buildAutocompleteSettingsMessage().settings.model).toBe("mistralai/codestral-2508") }) - it("persists supported model updates", async () => { - const post = vi.fn() - const { routeAutocompleteMessage } = await import("../settings") + it("validates supported model updates", async () => { + const { validAutocompleteSetting } = await import("../settings") - await routeAutocompleteMessage( - { type: "updateAutocompleteSetting", key: "model", value: "inception/mercury-edit" }, - post, - ) - - expect(update).toHaveBeenCalledWith("model", "inception/mercury-edit", 1) - expect(post).toHaveBeenCalledWith(expect.objectContaining({ type: "autocompleteSettingsLoaded" })) + expect(validAutocompleteSetting("model", "inception/mercury-edit")).toBe(true) }) it("rejects unsupported model updates", async () => { - const post = vi.fn() - const { routeAutocompleteMessage } = await import("../settings") - - await routeAutocompleteMessage({ type: "updateAutocompleteSetting", key: "model", value: "other/model" }, post) + const { validAutocompleteSetting } = await import("../settings") - expect(update).not.toHaveBeenCalled() - expect(post).not.toHaveBeenCalled() + expect(validAutocompleteSetting("model", "other/model")).toBe(false) }) it("rejects non-boolean toggle updates", async () => { - const post = vi.fn() - const { routeAutocompleteMessage } = await import("../settings") - - await routeAutocompleteMessage({ type: "updateAutocompleteSetting", key: "enableAutoTrigger", value: "true" }, post) + const { validAutocompleteSetting } = await import("../settings") - expect(update).not.toHaveBeenCalled() - expect(post).not.toHaveBeenCalled() + expect(validAutocompleteSetting("enableAutoTrigger", "true")).toBe(false) }) }) diff --git a/packages/kilo-vscode/src/services/autocomplete/settings.ts b/packages/kilo-vscode/src/services/autocomplete/settings.ts index 029d4921291..9ec341406dc 100644 --- a/packages/kilo-vscode/src/services/autocomplete/settings.ts +++ b/packages/kilo-vscode/src/services/autocomplete/settings.ts @@ -1,12 +1,8 @@ import * as vscode from "vscode" import { AUTOCOMPLETE_MODELS, getAutocompleteModel } from "../../shared/autocomplete-models" -const keys = new Set(["enableAutoTrigger", "enableSmartInlineTaskKeybinding", "enableChatAutocomplete", "model"]) - type Message = { type: string - key?: unknown - value?: unknown } type Post = (msg: unknown) => void @@ -17,13 +13,6 @@ export async function routeAutocompleteMessage(message: Message, post: Post): Pr return true } - if (message.type === "updateAutocompleteSetting") { - if (await update(message.key, message.value)) { - post(buildAutocompleteSettingsMessage()) - } - return true - } - return false } @@ -49,23 +38,15 @@ export function watchAutocompleteConfig(post: Post): vscode.Disposable { }) } -async function update(key: unknown, value: unknown) { - if (typeof key !== "string") return false - if (!keys.has(key)) return false - if (!valid(key, value)) return false - - await vscode.workspace - .getConfiguration("kilo-code.new.autocomplete") - .update(key, value, vscode.ConfigurationTarget.Global) - - return true -} - -function valid(key: string, value: unknown) { +export function validAutocompleteSetting(key: string, value: unknown) { if (key === "model") { if (typeof value !== "string") return false return AUTOCOMPLETE_MODELS.some((m) => m.id === value) } - return typeof value === "boolean" + if (key === "enableAutoTrigger") return typeof value === "boolean" + if (key === "enableSmartInlineTaskKeybinding") return typeof value === "boolean" + if (key === "enableChatAutocomplete") return typeof value === "boolean" + + return false } diff --git a/packages/kilo-vscode/tests/unit/autocomplete-model-selector.test.ts b/packages/kilo-vscode/tests/unit/autocomplete-model-selector.test.ts new file mode 100644 index 00000000000..1996041e917 --- /dev/null +++ b/packages/kilo-vscode/tests/unit/autocomplete-model-selector.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest" +import { + AUTOCOMPLETE_PROVIDER_ID, + AUTOCOMPLETE_PROVIDER_NAME, + AUTOCOMPLETE_SELECTOR_MODELS, +} from "../../webview-ui/src/components/settings/autocomplete-model-selector" +import { AUTOCOMPLETE_MODELS } from "../../src/shared/autocomplete-models" + +describe("autocomplete model selector", () => { + it("shows only Kilo Gateway autocomplete models", () => { + expect(AUTOCOMPLETE_SELECTOR_MODELS).toEqual( + AUTOCOMPLETE_MODELS.map((m) => ({ + id: m.id, + name: m.label, + providerID: AUTOCOMPLETE_PROVIDER_ID, + providerName: AUTOCOMPLETE_PROVIDER_NAME, + })), + ) + }) +}) diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/AutocompleteTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/AutocompleteTab.tsx index 072c5e0a925..f8cf320fd38 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/AutocompleteTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/AutocompleteTab.tsx @@ -1,37 +1,21 @@ -import { Component, createSignal, onCleanup } from "solid-js" +import { Component } from "solid-js" import { Switch } from "@kilocode/kilo-ui/switch" import { Card } from "@kilocode/kilo-ui/card" -import { useVSCode } from "../../context/vscode" +import { useConfig } from "../../context/config" import { useLanguage } from "../../context/language" -import type { ExtensionMessage } from "../../types/messages" import SettingsRow from "./SettingsRow" const AutocompleteTab: Component = () => { - const vscode = useVSCode() + const { settings, updateSetting } = useConfig() const language = useLanguage() - const [enableAutoTrigger, setEnableAutoTrigger] = createSignal(true) - const [enableSmartInlineTaskKeybinding, setEnableSmartInlineTaskKeybinding] = createSignal(false) - const [enableChatAutocomplete, setEnableChatAutocomplete] = createSignal(false) + const enabled = (key: string, fallback: boolean) => Boolean(settings()[key] ?? fallback) - const unsubscribe = vscode.onMessage((message: ExtensionMessage) => { - if (message.type !== "autocompleteSettingsLoaded") { - return - } - setEnableAutoTrigger(message.settings.enableAutoTrigger) - setEnableSmartInlineTaskKeybinding(message.settings.enableSmartInlineTaskKeybinding) - setEnableChatAutocomplete(message.settings.enableChatAutocomplete) - }) - - onCleanup(unsubscribe) - - vscode.postMessage({ type: "requestAutocompleteSettings" }) - - const updateSetting = ( + const save = ( key: "enableAutoTrigger" | "enableSmartInlineTaskKeybinding" | "enableChatAutocomplete", value: boolean, ) => { - vscode.postMessage({ type: "updateAutocompleteSetting", key, value }) + updateSetting(`autocomplete.${key}`, value) } return ( @@ -42,8 +26,8 @@ const AutocompleteTab: Component = () => { description={language.t("settings.autocomplete.autoTrigger.description")} > updateSetting("enableAutoTrigger", checked)} + checked={enabled("autocomplete.enableAutoTrigger", true)} + onChange={(checked) => save("enableAutoTrigger", checked)} hideLabel > {language.t("settings.autocomplete.autoTrigger.title")} @@ -55,8 +39,8 @@ const AutocompleteTab: Component = () => { description={language.t("settings.autocomplete.smartKeybinding.description")} > updateSetting("enableSmartInlineTaskKeybinding", checked)} + checked={enabled("autocomplete.enableSmartInlineTaskKeybinding", false)} + onChange={(checked) => save("enableSmartInlineTaskKeybinding", checked)} hideLabel > {language.t("settings.autocomplete.smartKeybinding.title")} @@ -69,8 +53,8 @@ const AutocompleteTab: Component = () => { last > updateSetting("enableChatAutocomplete", checked)} + checked={enabled("autocomplete.enableChatAutocomplete", false)} + onChange={(checked) => save("enableChatAutocomplete", checked)} hideLabel > {language.t("settings.autocomplete.chatAutocomplete.title")} diff --git a/packages/kilo-vscode/webview-ui/src/components/settings/ModelsTab.tsx b/packages/kilo-vscode/webview-ui/src/components/settings/ModelsTab.tsx index 745e533ddf1..c961e2d943b 100644 --- a/packages/kilo-vscode/webview-ui/src/components/settings/ModelsTab.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/settings/ModelsTab.tsx @@ -1,31 +1,20 @@ -import { Component, For, createMemo, createSignal, onCleanup } from "solid-js" +import { Component, For, createMemo } from "solid-js" import { Card } from "@kilocode/kilo-ui/card" -import { Select } from "@kilocode/kilo-ui/select" import { useConfig } from "../../context/config" import { useLanguage } from "../../context/language" import { useSession } from "../../context/session" -import { useVSCode } from "../../context/vscode" import { parseModelString } from "../../../../src/shared/provider-model" -import { AUTOCOMPLETE_MODELS, DEFAULT_AUTOCOMPLETE_MODEL } from "../../../../src/shared/autocomplete-models" +import { DEFAULT_AUTOCOMPLETE_MODEL } from "../../../../src/shared/autocomplete-models" import { ModelSelectorBase } from "../shared/ModelSelector" import SettingsRow from "./SettingsRow" -import type { ExtensionMessage } from "../../types/messages" +import { AUTOCOMPLETE_PROVIDER_ID, AUTOCOMPLETE_SELECTOR_MODELS } from "./autocomplete-model-selector" const ModelsTab: Component = () => { - const { config, updateConfig } = useConfig() + const { config, settings, updateConfig, updateSetting } = useConfig() const language = useLanguage() const session = useSession() - const vscode = useVSCode() - const [autocompleteModel, setAutocompleteModel] = createSignal(DEFAULT_AUTOCOMPLETE_MODEL.id) - - const unsubscribe = vscode.onMessage((message: ExtensionMessage) => { - if (message.type === "autocompleteSettingsLoaded") { - setAutocompleteModel(message.settings.model) - } - }) - onCleanup(unsubscribe) - vscode.postMessage({ type: "requestAutocompleteSettings" }) + const autocompleteModel = () => String(settings()["autocomplete.model"] ?? DEFAULT_AUTOCOMPLETE_MODEL.id) function handleModelSelect(configKey: "model" | "small_model") { return (providerID: string, modelID: string) => { @@ -49,6 +38,11 @@ const ModelsTab: Component = () => { } } + function handleAutocompleteModelSelect(providerID: string, modelID: string) { + if (providerID !== AUTOCOMPLETE_PROVIDER_ID || !modelID) return + updateSetting("autocomplete.model", modelID) + } + return (
@@ -82,18 +76,12 @@ const ModelsTab: Component = () => { description={language.t("settings.autocomplete.model.description")} last > -