import { type ProviderKind, type ServerProvider } from "@t3tools/contracts"; import { memo } from "react"; import { Clock3Icon, SparklesIcon, StarIcon } from "lucide-react"; import { Gemini, GithubCopilotIcon } from "../Icons"; import { AVAILABLE_PROVIDER_OPTIONS, PROVIDER_ICON_BY_PROVIDER } from "./providerIconUtils"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; import { cn } from "~/lib/utils"; import { getProviderSnapshot } from "../../providerModels"; function describeUnavailableProvider(label: string, live: ServerProvider | undefined): string { if (!live) { return `${label} — waiting for provider status…`; } if (live.status === "ready") { return label; } const kind = live.status === "error" ? "Unavailable" : live.status === "warning" ? "Limited" : live.status === "disabled" ? "Disabled in settings" : "Not ready"; const msg = live.message?.trim(); return msg ? `${label} — ${kind}. ${msg}` : `${label} — ${kind}.`; } const SELECTED_BUTTON_CLASS = "bg-background text-foreground shadow-sm"; const SELECTED_INDICATOR_CLASS = "pointer-events-none absolute -right-1 top-1/2 z-10 h-5 w-0.5 -translate-y-1/2 rounded-l-full bg-primary"; const BADGE_BASE_CLASS = "pointer-events-none absolute -right-0.5 top-0.5 z-10 flex size-3.5 items-center justify-center rounded-full bg-transparent shadow-sm "; const NEW_BADGE_CLASS = `${BADGE_BASE_CLASS} text-amber-600 dark:text-amber-300 `; const SOON_BADGE_CLASS = `${BADGE_BASE_CLASS} text-muted-foreground `; /** Opens toward the rail so the list stays readable (not over the model names). */ const PICKER_TOOLTIP_SIDE = "left" as const; const PICKER_TOOLTIP_CLASS = "max-w-64 text-balance font-normal leading-snug"; export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: { selectedProvider: ProviderKind | "favorites"; onSelectProvider: (provider: ProviderKind | "favorites") => void; providers?: ReadonlyArray; }) { const handleProviderClick = (provider: ProviderKind | "favorites") => { props.onSelectProvider(provider); }; return (
{/* Favorites section */}
{props.selectedProvider === "favorites" &&
} handleProviderClick("favorites")} type="button" data-model-picker-provider="favorites" aria-label="Favorites" > } /> Favorites
{/* Provider buttons */} {AVAILABLE_PROVIDER_OPTIONS.map((option) => { const OptionIcon = PROVIDER_ICON_BY_PROVIDER[option.value]; const liveProvider = props.providers ? getProviderSnapshot(props.providers, option.value) : undefined; const isDisabled = !liveProvider || liveProvider.status !== "ready"; const isSelected = props.selectedProvider === option.value; const badge = option.pickerSidebarBadge; const providerTooltip = isDisabled ? describeUnavailableProvider(option.label, liveProvider) : badge === "new" ? `${option.label} — New` : option.label; const button = ( ); const trigger = isDisabled ? ( {button} ) : ( button ); return (
{isSelected &&
} {providerTooltip}
); })} {/* Gemini button (coming soon) */} } /> Gemini — Coming soon {/* Github Copilot button (coming soon) */} } /> Github Copilot — Coming soon
); });