158 lines · 5.7 KB · TypeScript
RawDownload
import {  type ProviderKind,  type ResolvedKeybindingsConfig,  type ServerProvider,} from "@t3tools/contracts";import { memo, useEffect, useState } from "react";import type { VariantProps } from "class-variance-authority";import { ChevronDownIcon } from "lucide-react";import { Button, buttonVariants } from "../ui/button";import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";import { cn } from "~/lib/utils";import { ModelPickerContent } from "./ModelPickerContent";import {  ModelEsque,  PROVIDER_ICON_BY_PROVIDER,  getTriggerDisplayModelLabel,  getTriggerDisplayModelName,} from "./providerIconUtils";import { setModelPickerOpen } from "../../modelPickerOpenState"; export const ProviderModelPicker = memo(function ProviderModelPicker(props: {  provider: ProviderKind;  model: string;  lockedProvider: ProviderKind | null;  providers?: ReadonlyArray<ServerProvider>;  keybindings?: ResolvedKeybindingsConfig;  modelOptionsByProvider: Record<ProviderKind, ReadonlyArray<ModelEsque>>;  activeProviderIconClassName?: string;  compact?: boolean;  disabled?: boolean;  terminalOpen?: boolean;  open?: boolean;  triggerVariant?: VariantProps<typeof buttonVariants>["variant"];  triggerClassName?: string;  onOpenChange?: (open: boolean) => void;  onProviderModelChange: (provider: ProviderKind, model: string) => void;}) {  const [uncontrolledIsMenuOpen, setUncontrolledIsMenuOpen] = useState(false);  const activeProvider = props.lockedProvider ?? props.provider;  const isMenuOpen = props.open ?? uncontrolledIsMenuOpen;  const selectedProviderOptions = props.modelOptionsByProvider[activeProvider];  // If the current slug belongs to a different provider (for example after a provider  // switch or disable), prefer the active provider's first option so the trigger icon  // and label stay in sync instead of showing a stale foreign slug.  const selectedModel =    selectedProviderOptions.find((option) => option.slug === props.model) ??    selectedProviderOptions[0];  const ProviderIcon = PROVIDER_ICON_BY_PROVIDER[activeProvider];  const triggerTitle = selectedModel ? getTriggerDisplayModelName(selectedModel) : props.model;  const triggerSubtitle = selectedModel?.subProvider;  const triggerLabel = selectedModel ? getTriggerDisplayModelLabel(selectedModel) : props.model;   const setIsMenuOpen = (open: boolean) => {    props.onOpenChange?.(open);    if (props.open === undefined) {      setUncontrolledIsMenuOpen(open);    }  };   useEffect(() => {    setModelPickerOpen(isMenuOpen);    return () => {      setModelPickerOpen(false);    };  }, [isMenuOpen]);   const handleProviderModelChange = (provider: ProviderKind, model: string) => {    if (props.disabled) return;    props.onProviderModelChange(provider, model);    setIsMenuOpen(false);  };   return (    <Popover      open={isMenuOpen}      onOpenChange={(open) => {        if (props.disabled) {          setIsMenuOpen(false);          return;        }        setIsMenuOpen(open);      }}    >      <PopoverTrigger        render={          <Button            size="sm"            variant={props.triggerVariant ?? "ghost"}            data-chat-provider-model-picker="true"            className={cn(              "min-w-0 justify-start overflow-hidden whitespace-nowrap px-2 text-muted-foreground/70 hover:text-foreground/80 [&_svg]:mx-0",              props.compact ? "max-w-42 shrink-0" : "max-w-48 shrink sm:max-w-56 sm:px-3",              props.triggerClassName,            )}            disabled={props.disabled}          />        }      >        <span          className={cn(            "flex min-w-0 w-full box-border items-center gap-2 overflow-hidden",            props.compact ? "max-w-36 sm:pl-1" : undefined,          )}        >          <ProviderIcon            aria-hidden="true"            className={cn("size-4 shrink-0", props.activeProviderIconClassName)}          />          <Tooltip>            <TooltipTrigger              render={                <span                  className={cn(                    "min-w-0 flex-1 overflow-hidden",                    triggerSubtitle                      ? "grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-1"                      : "truncate",                  )}                />              }            >              {triggerSubtitle ? (                <>                  <span className="min-w-0 truncate">{triggerSubtitle}</span>                  <span aria-hidden="true" className="shrink-0 opacity-60">                    ·                  </span>                  <span className="min-w-0 truncate">{triggerTitle}</span>                </>              ) : (                triggerTitle              )}            </TooltipTrigger>            <TooltipPopup side="top">{triggerLabel}</TooltipPopup>          </Tooltip>          <ChevronDownIcon aria-hidden="true" className="size-3 shrink-0 opacity-60" />        </span>      </PopoverTrigger>      <PopoverPopup        align="start"        className="border-0 bg-transparent p-0 shadow-none before:hidden [--viewport-inline-padding:0] *:data-[slot=popover-viewport]:p-0"      >        <ModelPickerContent          provider={props.provider}          model={props.model}          lockedProvider={props.lockedProvider}          {...(props.providers && { providers: props.providers })}          {...(props.keybindings ? { keybindings: props.keybindings } : {})}          modelOptionsByProvider={props.modelOptionsByProvider}          terminalOpen={props.terminalOpen ?? false}          onRequestClose={() => setIsMenuOpen(false)}          onProviderModelChange={handleProviderModelChange}        />      </PopoverPopup>    </Popover>  );});