1import type { ModelOption, ProviderGroup } from "../../lib/modelOptions";
2import type { ProviderInstanceId } from "@t3tools/contracts";
3
4export function modelFavoriteKey(provider: ProviderInstanceId, model: string): string {
5 return `${provider}:${model}`;
6}
7
8/** Keep catalog order within each group when favorites move to the front. */
9export function favoritesFirst(
10 models: ReadonlyArray<ModelOption>,
11 favoriteKeys: ReadonlySet<string>,
12): ReadonlyArray<ModelOption> {
13 const favorites: ModelOption[] = [];
14 const others: ModelOption[] = [];
15 for (const model of models) {
16 (favoriteKeys.has(model.key) ? favorites : others).push(model);
17 }
18 return [...favorites, ...others];
19}
20
21/** Match the terms a user can actually see or recognize in the model picker. */
22export function modelMatchesCatalogQuery(input: {
23 readonly model: ModelOption;
24 readonly providerLabel: string;
25 readonly query: string;
26}): boolean {
27 const query = input.query.trim().toLocaleLowerCase();
28 if (query.length === 0) {
29 return true;
30 }
31
32 return [
33 input.model.label,
34 input.model.subtitle,
35 input.model.selection.model,
36 input.providerLabel,
37 ].some((value) => value.toLocaleLowerCase().includes(query));
38}
39
40/** Preserve staged provider options when the highlighted model is tapped again. */
41export function pendingModelAfterPress(input: {
42 readonly current: ModelOption | null;
43 readonly pressed: ModelOption;
44 readonly pressedIsApplied: boolean;
45}): ModelOption | null {
46 if (input.pressedIsApplied) {
47 return null;
48 }
49 return input.current?.key === input.pressed.key ? input.current : input.pressed;
50}
51
52/** A model can disappear while the picker is open. */
53export function canCommitPendingModel(
54 pending: ModelOption,
55 groups: ReadonlyArray<ProviderGroup>,
56): boolean {
57 return groups.some((group) =>
58 group.models.some(
59 (model) => model.key === pending.key && !model.isUnavailable && !model.isHidden,
60 ),
61 );
62}
63
64/**
65 * Primary and selected providers start open; all other catalogs start closed.
66 * A user's disclosure tap inverts that default until the picker is dismissed.
67 */
68export function providerSectionIsCollapsed(input: {
69 readonly defaultExpanded: boolean;
70 readonly hasExpansionOverride: boolean;
71 readonly isNarrowed: boolean;
72}): boolean {
73 if (input.isNarrowed) {
74 return false;
75 }
76 return input.defaultExpanded ? input.hasExpansionOverride : !input.hasExpansionOverride;
77}