feat(app): improvements to model search (#34954)
This commit is contained in:
parent
458ec7b372
commit
a9144eccf8
3 changed files with 42 additions and 13 deletions
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { matchesModelSearch } from "./dialog-select-model-search"
|
||||||
|
|
||||||
|
describe("matchesModelSearch", () => {
|
||||||
|
test("matches model names across separators", () => {
|
||||||
|
expect(matchesModelSearch("gpt 5", ["GPT-5.5"])).toBe(true)
|
||||||
|
expect(matchesModelSearch("gpt-5", ["GPT-5.5"])).toBe(true)
|
||||||
|
expect(matchesModelSearch("gpt5", ["GPT-5.5"])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("matches any searchable model field", () => {
|
||||||
|
expect(matchesModelSearch("open ai", ["GPT-5.5", "gpt-5.5", "OpenAI"])).toBe(true)
|
||||||
|
expect(matchesModelSearch("gpt 5", ["GPT-5.5", "gpt-5.5", "OpenAI"])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("does not match unrelated searches", () => {
|
||||||
|
expect(matchesModelSearch("claude", ["GPT-5.5", "gpt-5.5", "OpenAI"])).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
18
packages/app/src/components/dialog-select-model-search.ts
Normal file
18
packages/app/src/components/dialog-select-model-search.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
export const normalizeModelSearch = (value: string) =>
|
||||||
|
value
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^\p{Letter}\p{Number}]+/gu, " ")
|
||||||
|
.trim()
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
|
||||||
|
export const compactModelSearch = (value: string) => normalizeModelSearch(value).replaceAll(" ", "")
|
||||||
|
|
||||||
|
export const matchesModelSearch = (query: string, values: string[]) => {
|
||||||
|
const search = normalizeModelSearch(query)
|
||||||
|
if (!search) return true
|
||||||
|
|
||||||
|
const compactSearch = compactModelSearch(query)
|
||||||
|
return values.some(
|
||||||
|
(value) => normalizeModelSearch(value).includes(search) || compactModelSearch(value).includes(compactSearch),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
|
||||||
import { ModelTooltip } from "./model-tooltip"
|
import { ModelTooltip } from "./model-tooltip"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
|
import { matchesModelSearch } from "./dialog-select-model-search"
|
||||||
|
|
||||||
const isFree = (provider: string, cost: { input: number } | undefined) =>
|
const isFree = (provider: string, cost: { input: number } | undefined) =>
|
||||||
provider === "opencode" && (!cost || cost.input === 0)
|
provider === "opencode" && (!cost || cost.input === 0)
|
||||||
|
|
@ -242,13 +243,10 @@ export function ModelSelectorPopoverV2(props: {
|
||||||
.filter((item) => (props.provider ? item.provider.id === props.provider : true)),
|
.filter((item) => (props.provider ? item.provider.id === props.provider : true)),
|
||||||
)
|
)
|
||||||
const models = createMemo(() => {
|
const models = createMemo(() => {
|
||||||
const search = store.search.trim().toLowerCase()
|
const search = store.search.trim()
|
||||||
const filtered = search
|
const filtered = search
|
||||||
? allModels().filter(
|
? allModels().filter(
|
||||||
(item) =>
|
(item) => matchesModelSearch(search, [item.name, item.id, item.provider.name]),
|
||||||
item.name.toLowerCase().includes(search) ||
|
|
||||||
item.id.toLowerCase().includes(search) ||
|
|
||||||
item.provider.name.toLowerCase().includes(search),
|
|
||||||
)
|
)
|
||||||
: allModels()
|
: allModels()
|
||||||
|
|
||||||
|
|
@ -333,16 +331,10 @@ export function ModelSelectorPopoverV2(props: {
|
||||||
queueMicrotask(() => activeItem()?.scrollIntoView({ block: "nearest" }))
|
queueMicrotask(() => activeItem()?.scrollIntoView({ block: "nearest" }))
|
||||||
}
|
}
|
||||||
const setSearch = (value: string) => {
|
const setSearch = (value: string) => {
|
||||||
const search = value.trim().toLowerCase()
|
const search = value.trim()
|
||||||
const first = [...allModels()]
|
const first = [...allModels()]
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
.find(
|
.find((item) => matchesModelSearch(search, [item.name, item.id, item.provider.name]))
|
||||||
(item) =>
|
|
||||||
!search ||
|
|
||||||
item.name.toLowerCase().includes(search) ||
|
|
||||||
item.id.toLowerCase().includes(search) ||
|
|
||||||
item.provider.name.toLowerCase().includes(search),
|
|
||||||
)
|
|
||||||
setStore({ search: value, active: first ? modelKey(first) : manageKey })
|
setStore({ search: value, active: first ? modelKey(first) : manageKey })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue