mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
fix(vscode): preserve model preview interactions
This commit is contained in:
@@ -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"))
|
||||
|
||||
@@ -427,7 +427,10 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (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<ModelSelectorBaseProps> = (props) => {
|
||||
{(group) => {
|
||||
const shown = () => isGroupOpen(group.key)
|
||||
return (
|
||||
<div role="group" aria-labelledby={groupID(group.key)}>
|
||||
<div class="model-selector-group" role="group" aria-labelledby={groupID(group.key)}>
|
||||
<button
|
||||
id={groupID(group.key)}
|
||||
type="button"
|
||||
@@ -756,6 +759,7 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (props) => {
|
||||
const preActive = () => isPreActive(row.key)
|
||||
const starred = () => favoriteKeys().has(modelKey(model.providerID, model.id))
|
||||
const showProvider = () => row.kind === "favorite"
|
||||
const showSelect = () => expanded() && preActive() && !isActive(model)
|
||||
const starLabel = () =>
|
||||
`${starred() ? language.t("model.favorite.remove") : language.t("model.favorite.add")}: ${sanitizeName(model.name)}`
|
||||
return (
|
||||
@@ -772,7 +776,18 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (props) => {
|
||||
class={`model-selector-item${(hovered() && !pointer()) || preActive() ? " keyboard-focused" : ""}${hovered() || preActive() ? " selected" : ""}${chosen(row) ? " active" : ""}`}
|
||||
role="option"
|
||||
aria-selected={chosen(row)}
|
||||
onClick={() => selectRow(row)}
|
||||
onClick={() => {
|
||||
if (!expanded()) {
|
||||
selectRow(row)
|
||||
return
|
||||
}
|
||||
setRow(row.key)
|
||||
setPreviewKey(row.key)
|
||||
searchRef?.focus()
|
||||
}}
|
||||
onDblClick={() => {
|
||||
if (expanded()) selectRow(row)
|
||||
}}
|
||||
onMouseMove={() => {
|
||||
setPointer(true)
|
||||
}}
|
||||
@@ -821,6 +836,16 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (props) => {
|
||||
<Icon name={starred() ? "star-filled" : "star"} size="small" />
|
||||
</button>
|
||||
</Show>
|
||||
<Show when={showSelect()}>
|
||||
<button
|
||||
type="button"
|
||||
class="model-selector-item-select-btn"
|
||||
aria-label={`${language.t("dialog.model.select")}: ${sanitizeName(model.name)}`}
|
||||
onClick={() => selectRow(row)}
|
||||
>
|
||||
{language.t("dialog.model.select")}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
@@ -841,7 +866,9 @@ export const ModelSelectorBase: Component<ModelSelectorBaseProps> = (props) => {
|
||||
class={`model-selector-preview${expanded() ? " model-selector-preview--visible" : ""}`}
|
||||
style={expanded() ? { height: `${previewHeight()}px` } : {}}
|
||||
>
|
||||
<ModelPreview model={previewModel() ?? activeModel() ?? null} />
|
||||
<Show when={expanded()}>
|
||||
<ModelPreview model={previewModel() ?? activeModel() ?? null} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
============================================ */
|
||||
|
||||
Reference in New Issue
Block a user