mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-29 04:32:57 +00:00
Fix native wide-layout model selectors mounting bottom-sheet scrolling outside a bottom sheet. Keep the scrolling policy with the model browser and cover native compact, native wide, and web surfaces.
112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import {
|
|
filterAndRankModelRows,
|
|
getAllProviderModelRows,
|
|
type ProviderSelectionModelRow,
|
|
type ProviderSelectorProvider,
|
|
} from "@/provider-selection/provider-selection";
|
|
|
|
export type ModelBrowserView =
|
|
| { kind: "all" }
|
|
| { kind: "provider"; providerId: string; providerLabel: string };
|
|
|
|
export function resolveModelBrowserScrolling({
|
|
isNative,
|
|
isCompact,
|
|
}: {
|
|
isNative: boolean;
|
|
isCompact: boolean;
|
|
}): "sheet" | "independent" {
|
|
return isNative && isCompact ? "sheet" : "independent";
|
|
}
|
|
|
|
/** A profile's model reference; used to match profiles back to model rows. */
|
|
export interface ModelProfileRef {
|
|
provider: string;
|
|
modelId: string;
|
|
}
|
|
|
|
/**
|
|
* Groups profiles by `provider:modelId`, skipping profiles that name no model.
|
|
* Pure so the model browser can test it apart from the component tree.
|
|
*/
|
|
export function groupProfilesByProviderModel<T extends ModelProfileRef>(
|
|
refs: readonly T[],
|
|
): Map<string, T[]> {
|
|
const lookup = new Map<string, T[]>();
|
|
for (const ref of refs) {
|
|
const modelId = ref.modelId.trim();
|
|
if (!modelId) {
|
|
continue;
|
|
}
|
|
const key = `${ref.provider}:${modelId}`;
|
|
const existing = lookup.get(key);
|
|
if (existing) {
|
|
existing.push(ref);
|
|
} else {
|
|
lookup.set(key, [ref]);
|
|
}
|
|
}
|
|
return lookup;
|
|
}
|
|
|
|
/** What the root view shows: the provider drill-down, or ranked cross-provider results. */
|
|
export type ModelBrowserAllView =
|
|
| { kind: "browse" }
|
|
| { kind: "searchResults"; rows: ProviderSelectionModelRow[] }
|
|
| { kind: "noSearchMatches" };
|
|
|
|
export function resolveModelBrowserAllView({
|
|
providers,
|
|
normalizedQuery,
|
|
isSearchFocused,
|
|
}: {
|
|
providers: ProviderSelectorProvider[];
|
|
normalizedQuery: string;
|
|
isSearchFocused: boolean;
|
|
}): ModelBrowserAllView {
|
|
if (!normalizedQuery && !isSearchFocused) {
|
|
return { kind: "browse" };
|
|
}
|
|
const allRows = getAllProviderModelRows(providers);
|
|
const rows = normalizedQuery ? filterAndRankModelRows(allRows, normalizedQuery) : allRows;
|
|
if (rows.length === 0) {
|
|
return { kind: "noSearchMatches" };
|
|
}
|
|
return { kind: "searchResults", rows };
|
|
}
|
|
|
|
/** Where the picker lands when it opens. A sole provider skips the redundant root view. */
|
|
export function resolveInitialModelBrowserView({
|
|
providers,
|
|
selectedProvider,
|
|
selectedModel,
|
|
hasProfiles,
|
|
}: {
|
|
providers: ProviderSelectorProvider[];
|
|
selectedProvider: string;
|
|
selectedModel: string;
|
|
hasProfiles: boolean;
|
|
}): ModelBrowserView {
|
|
const singleProvider = providers.length === 1 ? providers[0] : undefined;
|
|
if (singleProvider) {
|
|
return {
|
|
kind: "provider",
|
|
providerId: singleProvider.id,
|
|
providerLabel: singleProvider.label,
|
|
};
|
|
}
|
|
|
|
if (hasProfiles) {
|
|
return { kind: "all" };
|
|
}
|
|
|
|
if (selectedProvider.length > 0 && selectedModel.length > 0) {
|
|
const provider = providers.find((entry) => entry.id === selectedProvider);
|
|
if (provider) {
|
|
return { kind: "provider", providerId: provider.id, providerLabel: provider.label };
|
|
}
|
|
}
|
|
|
|
return { kind: "all" };
|
|
}
|