From 0785ee31f0a94fc303feb3bd31574762a846bb00 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 7 May 2026 15:22:23 +0700 Subject: [PATCH] Match model description in combobox search Search now matches across model label, id, provider, and description with multi-token fuzzy matching, so "kimi zen" finds the OpenCode Zen Kimi model. --- .../components/combined-model-selector.test.ts | 16 ++++++++++++++++ .../components/combined-model-selector.utils.ts | 9 ++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/app/src/components/combined-model-selector.test.ts b/packages/app/src/components/combined-model-selector.test.ts index 7d5671795..50c57e07c 100644 --- a/packages/app/src/components/combined-model-selector.test.ts +++ b/packages/app/src/components/combined-model-selector.test.ts @@ -67,6 +67,22 @@ describe("combined model selector helpers", () => { expect(matchesSearch(rows[1], "gpt-5.4")).toBe(true); }); + it("matches across label, provider, and description with multi-token fuzzy search", () => { + const row = { + favoriteKey: "opencode:opencode-zen/kimi-k2.5", + provider: "opencode", + providerLabel: "OpenCode", + modelId: "opencode-zen/kimi-k2.5", + modelLabel: "Kimi K2.5", + description: "OpenCode Zen - kimi", + }; + + expect(matchesSearch(row, "kimi zen")).toBe(true); + expect(matchesSearch(row, "zen kimi")).toBe(true); + expect(matchesSearch(row, "k2.5 zen")).toBe(true); + expect(matchesSearch(row, "kimi gemini")).toBe(false); + }); + it("keeps the selected trigger label model-only", () => { expect(resolveProviderLabel(providerDefinitions, "codex")).toBe("Codex"); expect(buildSelectedTriggerLabel("GPT-5.4")).toBe("GPT-5.4"); diff --git a/packages/app/src/components/combined-model-selector.utils.ts b/packages/app/src/components/combined-model-selector.utils.ts index e96e5de72..9f6e806a6 100644 --- a/packages/app/src/components/combined-model-selector.utils.ts +++ b/packages/app/src/components/combined-model-selector.utils.ts @@ -48,7 +48,10 @@ export function matchesSearch(row: SelectorModelRow, normalizedQuery: string): b return true; } - return [row.modelLabel, row.modelId, row.providerLabel].some((value) => - value.toLowerCase().includes(normalizedQuery), - ); + const haystack = [row.modelLabel, row.modelId, row.providerLabel, row.description ?? ""] + .join(" ") + .toLowerCase(); + + const tokens = normalizedQuery.split(/\s+/).filter((token) => token.length > 0); + return tokens.every((token) => haystack.includes(token)); }