From 96d2d3addb121b25d17ff79b33ee3570577aec83 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Fri, 29 May 2026 09:32:07 +0200 Subject: [PATCH] fix(vscode): preserve model preview interactions --- .../model-selector-accessibility.spec.ts | 21 ++++++++++- .../src/components/shared/ModelSelector.tsx | 35 ++++++++++++++++--- .../webview-ui/src/styles/model-selector.css | 23 +++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts b/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts index c413a11fcff..dd634e67ff9 100644 --- a/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts +++ b/packages/kilo-vscode/tests/model-selector-accessibility.spec.ts @@ -37,11 +37,28 @@ test("model selector exposes combobox relationships and active option movement", const preview = page.locator(`[id="${controls}"]`) await expect(expand).toHaveAttribute("aria-expanded", "false") await expect(preview).toHaveAttribute("aria-hidden", "true") + await expect(preview.locator("button, a, [tabindex]")).toHaveCount(0) await expand.click() const collapse = page.getByRole("button", { name: "Collapse", exact: true }) await expect(collapse).toHaveAttribute("aria-controls", controls!) await expect(collapse).toHaveAttribute("aria-expanded", "true") await expect(preview).toHaveAttribute("aria-hidden", "false") + await expect(preview.getByRole("button", { name: "Add to favorites" })).toBeVisible() +}) + +test("expanded preview waits for explicit pointer selection", async ({ page }) => { + await load(page, "shared--model-selector-accessible") + + await page.getByRole("button", { name: "Review model: Alpha" }).click() + await page.getByRole("button", { name: "Expand" }).click() + await page.getByRole("option", { name: "Bravo" }).click() + + await expect(page.getByTestId("model-selector-value")).toHaveText("alpha") + await expect(page.getByRole("combobox", { name: "Review model: Alpha. Search models" })).toBeVisible() + await expect(page.locator(".model-selector-preview")).toContainText("Bravo") + + await page.getByRole("button", { name: "Select: Bravo" }).click() + await expect(page.getByTestId("model-selector-value")).toHaveText("bravo") }) test("selected favorite remains selected when its duplicate group is collapsed", async ({ page }) => { @@ -50,9 +67,11 @@ test("selected favorite remains selected when its duplicate group is collapsed", await page.getByRole("button", { name: "Review model: Alpha" }).click() const combobox = page.getByRole("combobox", { name: "Review model: Alpha. Search models" }) const alpha = page.getByRole("option", { name: "Alpha" }) + const favorites = page.getByRole("button", { name: "Collapse Favorites" }) await expect(alpha.first()).toHaveAttribute("aria-selected", "true") + await expect.poll(() => favorites.evaluate((el) => getComputedStyle(el).borderTopStyle)).toBe("solid") - await page.getByRole("button", { name: "Collapse Favorites" }).click() + await favorites.click() await expect(alpha).toHaveCount(1) await expect(alpha).toHaveAttribute("aria-selected", "true") await expect(combobox).toHaveAttribute("aria-activedescendant", await alpha.getAttribute("id")) diff --git a/packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx b/packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx index 5533c16c748..4ae79db99d6 100644 --- a/packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx +++ b/packages/kilo-vscode/webview-ui/src/components/shared/ModelSelector.tsx @@ -427,7 +427,10 @@ export const ModelSelectorBase: Component = (props) => { // Listen for slash command trigger const onTrigger = () => setOpen(true) window.addEventListener("openModelPicker", onTrigger) - onCleanup(() => window.removeEventListener("openModelPicker", onTrigger)) + onCleanup(() => { + window.removeEventListener("openModelPicker", onTrigger) + clearTimeout(previewTimer) + }) const onEscape = (e: KeyboardEvent) => { if (!open() || e.key !== "Escape") return @@ -720,7 +723,7 @@ export const ModelSelectorBase: Component = (props) => { {(group) => { const shown = () => isGroupOpen(group.key) return ( -
+
+ + +
) }} @@ -841,7 +866,9 @@ export const ModelSelectorBase: Component = (props) => { class={`model-selector-preview${expanded() ? " model-selector-preview--visible" : ""}`} style={expanded() ? { height: `${previewHeight()}px` } : {}} > - + + +
) diff --git a/packages/kilo-vscode/webview-ui/src/styles/model-selector.css b/packages/kilo-vscode/webview-ui/src/styles/model-selector.css index 7c1a737694d..adecd84ceae 100644 --- a/packages/kilo-vscode/webview-ui/src/styles/model-selector.css +++ b/packages/kilo-vscode/webview-ui/src/styles/model-selector.css @@ -292,7 +292,7 @@ outline-offset: -1px; } -.model-selector-group-label:not(:first-child) { +.model-selector-group:not(:first-child) > .model-selector-group-label { border-top: 1px solid var(--border-weak-base, var(--vscode-panel-border)); margin-top: 4px; padding-top: 8px; @@ -445,6 +445,27 @@ opacity: 1; } +.model-selector-item-select-btn { + flex-shrink: 0; + margin-right: 12px; + padding: 2px 10px; + font-size: var(--kilo-font-size-11); + font-weight: 600; + font-family: inherit; + border-radius: 2px; + border: none; + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + opacity: 0.95; + cursor: pointer; + line-height: 1.6; + letter-spacing: 0.02em; +} + +.model-selector-item-select-btn:hover { + opacity: 1; +} + /* ============================================ Thinking Selector (uses kilo-ui Popover) ============================================ */