From 56c33e84a350bf2acd1ce2fb20504b97c8c3412b Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Wed, 5 Aug 2026 18:19:26 -0400 Subject: [PATCH] fix(tui): keep model search order stable (#40753) --- packages/tui/src/component/dialog-model.tsx | 16 ++++++++++++---- .../tui/test/cli/cmd/tui/model-options.test.ts | 17 ++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/tui/src/component/dialog-model.tsx b/packages/tui/src/component/dialog-model.tsx index 87168f6c29a..cb1e92ae2ac 100644 --- a/packages/tui/src/component/dialog-model.tsx +++ b/packages/tui/src/component/dialog-model.tsx @@ -7,12 +7,14 @@ import { DialogVariant } from "./dialog-variant" import * as fuzzysort from "fuzzysort" import { useConnected } from "./use-connected" import { useData } from "../context/data" +import { modelPreferenceKey } from "../model-preference" export function DialogModel(props: { providerID?: string }) { const local = useLocal() const data = useData() const dialog = useDialog() const [query, setQuery] = createSignal("") + const favoritePriority = new Set(local.model.favorite().map(modelPreferenceKey)) const connected = useConnected() const providers = createMemo(() => new Map((data.location.provider.list() ?? []).map((item) => [item.id, item]))) @@ -63,14 +65,14 @@ export function DialogModel(props: { providerID?: string }) { .filter((model) => (props.providerID ? model.providerID === props.providerID : true)) .map((model) => { const provider = providers().get(model.providerID) - const favorite = favorites.some((item) => item.providerID === model.providerID && item.modelID === model.id) + const key = modelPreferenceKey({ providerID: model.providerID, modelID: model.id }) + const favorite = favorites.some((item) => modelPreferenceKey(item) === key) return { value: { providerID: model.providerID, modelID: model.id }, providerID: model.providerID, providerName: provider?.name ?? model.providerID, title: model.name, releaseDate: model.time.released, - favorite, description: favorite ? "(Favorite)" : undefined, category: connected() ? (provider?.name ?? model.providerID) : undefined, footer: free(model) ? "Free" : undefined, @@ -98,6 +100,7 @@ export function DialogModel(props: { providerID?: string }) { if (needle) { return prioritizeFavorites( fuzzysort.go(needle, modelOptions, { keys: ["title", "category"] }).map((item) => item.obj), + favoritePriority, ) } @@ -162,8 +165,13 @@ export function DialogModel(props: { providerID?: string }) { ) } -export function prioritizeFavorites(options: T[]) { - return options.toSorted((a, b) => Number(b.favorite) - Number(a.favorite)) +export function prioritizeFavorites( + options: T[], + favorites: Set, +) { + return options.toSorted( + (a, b) => Number(favorites.has(modelPreferenceKey(b.value))) - Number(favorites.has(modelPreferenceKey(a.value))), + ) } export function sortModelOptions< diff --git a/packages/tui/test/cli/cmd/tui/model-options.test.ts b/packages/tui/test/cli/cmd/tui/model-options.test.ts index 54fd1688620..0c8477a61b1 100644 --- a/packages/tui/test/cli/cmd/tui/model-options.test.ts +++ b/packages/tui/test/cli/cmd/tui/model-options.test.ts @@ -2,13 +2,16 @@ import { describe, expect, test } from "bun:test" import { prioritizeFavorites, sortModelOptions } from "../../../../src/component/dialog-model" describe("prioritizeFavorites", () => { - test("moves favorites first while preserving fuzzy result order", () => { - const prioritized = prioritizeFavorites([ - { title: "Best match", favorite: false }, - { title: "Favorite match", favorite: true }, - { title: "Second best match", favorite: false }, - { title: "Second favorite match", favorite: true }, - ]) + test("uses the favorite order captured when the dialog opened", () => { + const prioritized = prioritizeFavorites( + [ + { title: "Best match", value: { providerID: "test", modelID: "best" } }, + { title: "Favorite match", value: { providerID: "test", modelID: "favorite" } }, + { title: "Second best match", value: { providerID: "test", modelID: "second-best" } }, + { title: "Second favorite match", value: { providerID: "test", modelID: "second-favorite" } }, + ], + new Set(["test/favorite", "test/second-favorite"]), + ) expect(prioritized.map((model) => model.title)).toEqual([ "Favorite match",