import { type ModelSlug, type ProviderKind } from "@t3tools/contracts"; import { resolveSelectableModel } from "@t3tools/shared/model"; import { memo, useState } from "react"; import type { VariantProps } from "class-variance-authority"; import { type ProviderPickerKind, PROVIDER_OPTIONS } from "../../session-logic"; import { ChevronDownIcon } from "lucide-react"; import { Button, buttonVariants } from "../ui/button"; import { Menu, MenuGroup, MenuItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator as MenuDivider, MenuSub, MenuSubPopup, MenuSubTrigger, MenuTrigger, } from "../ui/menu"; import { ClaudeAI, CursorIcon, Gemini, Icon, OpenAI, OpenCodeIcon } from "../Icons"; import { cn } from "~/lib/utils"; function isAvailableProviderOption(option: (typeof PROVIDER_OPTIONS)[number]): option is { value: ProviderKind; label: string; available: true; } { return option.available; } const PROVIDER_ICON_BY_PROVIDER: Record = { codex: OpenAI, claudeAgent: ClaudeAI, cursor: CursorIcon, }; export const AVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter(isAvailableProviderOption); const UNAVAILABLE_PROVIDER_OPTIONS = PROVIDER_OPTIONS.filter((option) => !option.available); const COMING_SOON_PROVIDER_OPTIONS = [ { id: "opencode", label: "OpenCode", icon: OpenCodeIcon }, { id: "gemini", label: "Gemini", icon: Gemini }, ] as const; function providerIconClassName( provider: ProviderKind | ProviderPickerKind, fallbackClassName: string, ): string { return provider === "claudeAgent" ? "text-[#d97757]" : fallbackClassName; } export const ProviderModelPicker = memo(function ProviderModelPicker(props: { provider: ProviderKind; model: ModelSlug; lockedProvider: ProviderKind | null; modelOptionsByProvider: Record>; activeProviderIconClassName?: string; compact?: boolean; disabled?: boolean; triggerVariant?: VariantProps["variant"]; triggerClassName?: string; onProviderModelChange: (provider: ProviderKind, model: ModelSlug) => void; }) { const [isMenuOpen, setIsMenuOpen] = useState(false); const activeProvider = props.lockedProvider ?? props.provider; const selectedProviderOptions = props.modelOptionsByProvider[activeProvider]; const selectedModelLabel = selectedProviderOptions.find((option) => option.slug === props.model)?.name ?? props.model; const ProviderIcon = PROVIDER_ICON_BY_PROVIDER[activeProvider]; const handleModelChange = (provider: ProviderKind, value: string) => { if (props.disabled) return; if (!value) return; const resolvedModel = resolveSelectableModel( provider, value, props.modelOptionsByProvider[provider], ); if (!resolvedModel) return; props.onProviderModelChange(provider, resolvedModel); setIsMenuOpen(false); }; return ( { if (props.disabled) { setIsMenuOpen(false); return; } setIsMenuOpen(open); }} > } > {props.lockedProvider !== null ? ( handleModelChange(props.lockedProvider!, value)} > {props.modelOptionsByProvider[props.lockedProvider].map((modelOption) => ( setIsMenuOpen(false)} > {modelOption.name} ))} ) : ( <> {AVAILABLE_PROVIDER_OPTIONS.map((option) => { const OptionIcon = PROVIDER_ICON_BY_PROVIDER[option.value]; return ( handleModelChange(option.value, value)} > {props.modelOptionsByProvider[option.value].map((modelOption) => ( setIsMenuOpen(false)} > {modelOption.name} ))} ); })} {UNAVAILABLE_PROVIDER_OPTIONS.length > 0 && } {UNAVAILABLE_PROVIDER_OPTIONS.map((option) => { const OptionIcon = PROVIDER_ICON_BY_PROVIDER[option.value]; return ( ); })} {UNAVAILABLE_PROVIDER_OPTIONS.length === 0 && } {COMING_SOON_PROVIDER_OPTIONS.map((option) => { const OptionIcon = option.icon; return ( ); })} )} ); });