551 lines · 17.8 KB · TypeScript
RawDownload
import {  type ProviderDriverKind,  type ProviderInstanceId,  type ProviderOptionDescriptor,  type ProviderOptionSelection,  type ScopedThreadRef,  type ServerProviderModel,} from "@t3tools/contracts";import {  applyClaudePromptEffortPrefix,  buildProviderOptionSelectionsFromDescriptors,  getProviderOptionCurrentLabel,  getProviderOptionCurrentValue,  getProviderOptionDescriptors,  isClaudeUltrathinkPrompt,} from "@t3tools/shared/model";import { memo, useCallback, useState } from "react";import type { VariantProps } from "class-variance-authority";import { ZapIcon } from "lucide-react";import { buttonVariants } from "../ui/button";import {  Menu,  MenuGroup,  MenuPopup,  MenuRadioGroup,  MenuRadioItem,  MenuSeparator as MenuDivider,  MenuTrigger,} from "../ui/menu";import { useComposerDraftStore, DraftId } from "../../composerDraftStore";import { getProviderModelCapabilities } from "../../providerModels";import { cn } from "~/lib/utils";import { Badge } from "../ui/badge";import { ComposerControl, ComposerControlChevron, ComposerControlIcon } from "./ComposerControl"; type ProviderOptions = ReadonlyArray<ProviderOptionSelection>; type TraitsPersistence =  | {      threadRef?: ScopedThreadRef;      draftId?: DraftId;      onModelOptionsChange?: never;    }  | {      threadRef?: undefined;      onModelOptionsChange: (nextOptions: ProviderOptions | undefined) => void;    }; const ULTRATHINK_PROMPT_PREFIX = "Ultrathink:\n"; function DefaultBadge() {  return (    <Badge      variant="outline"      className="inline-flex h-4 w-fit min-w-0 items-center justify-center gap-0 border-border/70 bg-muted/60 px-1.5 py-0 font-semibold text-[10px] text-muted-foreground leading-none sm:h-4"    >      Default    </Badge>  );} function replaceDescriptorCurrentValue(  descriptors: ReadonlyArray<ProviderOptionDescriptor>,  descriptorId: string,  currentValue: string | boolean | undefined,): ReadonlyArray<ProviderOptionDescriptor> {  return descriptors.map((descriptor) =>    descriptor.id !== descriptorId      ? descriptor      : descriptor.type === "boolean"        ? {            ...descriptor,            ...(typeof currentValue === "boolean" ? { currentValue } : {}),          }        : {            ...descriptor,            ...(typeof currentValue === "string" ? { currentValue } : {}),          },  );} function getDescriptorStringValue(  descriptor: Extract<ProviderOptionDescriptor, { type: "select" }> | null,): string | null {  if (!descriptor) {    return null;  }  const value = getProviderOptionCurrentValue(descriptor);  return typeof value === "string" ? value : null;} function getSelectedTraits(  provider: ProviderDriverKind,  models: ReadonlyArray<ServerProviderModel>,  model: string | null | undefined,  prompt: string,  modelOptions: ProviderOptions | null | undefined,  allowPromptInjectedEffort: boolean,) {  const caps = getProviderModelCapabilities(models, model, provider);  const descriptors = getProviderOptionDescriptors({    caps,    selections: modelOptions,  });  const selectDescriptors = descriptors.filter(    (descriptor): descriptor is Extract<ProviderOptionDescriptor, { type: "select" }> =>      descriptor.type === "select",  );  const booleanDescriptors = descriptors.filter(    (descriptor): descriptor is Extract<ProviderOptionDescriptor, { type: "boolean" }> =>      descriptor.type === "boolean",  );  const primarySelectDescriptor = selectDescriptors[0] ?? null;  const contextWindowDescriptor =    selectDescriptors.find((descriptor) => descriptor.id === "contextWindow") ?? null;  const agentDescriptor = selectDescriptors.find((descriptor) => descriptor.id === "agent") ?? null;  const fastModeDescriptor =    booleanDescriptors.find((descriptor) => descriptor.id === "fastMode") ?? null;  const thinkingDescriptor =    booleanDescriptors.find((descriptor) => descriptor.id === "thinking") ?? null;   // Prompt-controlled effort (e.g. ultrathink in prompt text)  const ultrathinkPromptControlled =    allowPromptInjectedEffort &&    (primarySelectDescriptor?.promptInjectedValues?.length ?? 0) > 0 &&    isClaudeUltrathinkPrompt(prompt);   // Check if "ultrathink" appears in the body text (not just our prefix)  const ultrathinkInBodyText =    ultrathinkPromptControlled && isClaudeUltrathinkPrompt(prompt.replace(/^Ultrathink:\s*/i, ""));  const effort =    (ultrathinkPromptControlled      ? "ultrathink"      : getDescriptorStringValue(primarySelectDescriptor)) ?? null;  const thinkingEnabled =    typeof thinkingDescriptor?.currentValue === "boolean" ? thinkingDescriptor.currentValue : null;  const contextWindow = getDescriptorStringValue(contextWindowDescriptor);  const selectedAgent = getDescriptorStringValue(agentDescriptor);  const selectedAgentLabel = agentDescriptor    ? getProviderOptionCurrentLabel(agentDescriptor)    : null;   return {    caps,    descriptors,    selectDescriptors,    booleanDescriptors,    primarySelectDescriptor,    contextWindowDescriptor,    agentDescriptor,    fastModeDescriptor,    thinkingDescriptor,    effort,    thinkingEnabled,    contextWindow,    ultrathinkPromptControlled,    ultrathinkInBodyText,    selectedAgent,    selectedAgentLabel,  };} function getTraitsSectionVisibility(input: {  provider: ProviderDriverKind;  models: ReadonlyArray<ServerProviderModel>;  model: string | null | undefined;  prompt: string;  modelOptions: ProviderOptions | null | undefined;  allowPromptInjectedEffort?: boolean;}) {  const selected = getSelectedTraits(    input.provider,    input.models,    input.model,    input.prompt,    input.modelOptions,    input.allowPromptInjectedEffort ?? true,  );   const showEffort = selected.primarySelectDescriptor !== null;  const showThinking = selected.thinkingDescriptor !== null;  const showFastMode = selected.fastModeDescriptor !== null;  const showContextWindow = selected.contextWindowDescriptor !== null;  const showAgent = selected.agentDescriptor !== null;   return {    ...selected,    showEffort,    showThinking,    showFastMode,    showContextWindow,    showAgent,    hasAnyControls: showEffort || showThinking || showFastMode || showContextWindow || showAgent,  };} export function shouldRenderTraitsControls(input: {  provider: ProviderDriverKind;  models: ReadonlyArray<ServerProviderModel>;  model: string | null | undefined;  prompt: string;  modelOptions: ProviderOptions | null | undefined;  allowPromptInjectedEffort?: boolean;}): boolean {  return getTraitsSectionVisibility(input).hasAnyControls;} export interface TraitsMenuContentProps {  provider: ProviderDriverKind;  instanceId?: ProviderInstanceId;  models: ReadonlyArray<ServerProviderModel>;  model: string | null | undefined;  prompt: string;  onPromptChange: (prompt: string) => void;  modelOptions?: ProviderOptions | null | undefined;  allowPromptInjectedEffort?: boolean;  triggerVariant?: VariantProps<typeof buttonVariants>["variant"];  triggerClassName?: string;} export const TraitsMenuContent = memo(function TraitsMenuContentImpl({  provider,  instanceId,  models,  model,  prompt,  onPromptChange,  modelOptions,  allowPromptInjectedEffort = true,  ...persistence}: TraitsMenuContentProps & TraitsPersistence) {  const setProviderModelOptions = useComposerDraftStore((store) => store.setProviderModelOptions);  const updateModelOptions = useCallback(    (nextOptions: ProviderOptions | undefined) => {      if ("onModelOptionsChange" in persistence) {        persistence.onModelOptionsChange(nextOptions);        return;      }      const threadTarget = persistence.threadRef ?? persistence.draftId;      if (!threadTarget) {        return;      }      setProviderModelOptions(threadTarget, provider, nextOptions, {        ...(instanceId ? { instanceId } : {}),        model,        persistSticky: true,      });    },    [instanceId, model, persistence, provider, setProviderModelOptions],  );  const {    descriptors,    selectDescriptors,    booleanDescriptors,    primarySelectDescriptor,    ultrathinkPromptControlled,    ultrathinkInBodyText,    hasAnyControls,  } = getTraitsSectionVisibility({    provider,    models,    model,    prompt,    modelOptions,    allowPromptInjectedEffort,  });  const updateDescriptors = (nextDescriptors: ReadonlyArray<ProviderOptionDescriptor>) => {    updateModelOptions(buildProviderOptionSelectionsFromDescriptors(nextDescriptors));  };   const handleSelectChange = (    descriptor: Extract<ProviderOptionDescriptor, { type: "select" }>,    value: string,  ) => {    if (!value) return;    if (descriptor.promptInjectedValues?.includes(value)) {      const nextPrompt =        prompt.trim().length === 0          ? ULTRATHINK_PROMPT_PREFIX          : applyClaudePromptEffortPrefix(prompt, "ultrathink");      onPromptChange(nextPrompt);      return;    }    if (ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id) return;    if (ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id) {      const stripped = prompt.replace(/^Ultrathink:\s*/i, "");      onPromptChange(stripped);    }    updateDescriptors(replaceDescriptorCurrentValue(descriptors, descriptor.id, value));  };   if (!hasAnyControls) {    return null;  }   return (    <>      {selectDescriptors.map((descriptor, index) => {        const selectedValue =          ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id            ? "ultrathink"            : (getDescriptorStringValue(descriptor) ?? "");         return (          <div key={descriptor.id}>            {index > 0 ? <MenuDivider /> : null}            <MenuGroup>              <div className="px-2 pt-1.5 pb-1 font-medium text-muted-foreground text-xs">                {descriptor.label}              </div>              {ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id ? (                <div className="px-2 pb-1.5 text-muted-foreground/80 text-xs">                  Your prompt contains &quot;ultrathink&quot; in the text. Remove it to change this                  option.                </div>              ) : null}              <MenuRadioGroup                value={selectedValue}                onValueChange={(value) => handleSelectChange(descriptor, value)}              >                {descriptor.options.map((option) => (                  <MenuRadioItem                    key={option.id}                    value={option.id}                    hideIndicator                    // Base UI keeps radio menus open by default. Close on pick so                    // the traits menu behaves like the model picker.                    closeOnClick                    disabled={ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id}                  >                    <span className="flex w-full min-w-0 flex-col">                      <span className="flex w-full min-w-0 items-center justify-between gap-3">                        <span className="min-w-0 truncate">                          {option.label}                          {option.isDefault ? (                            <>                              {" "}                              <DefaultBadge />                            </>                          ) : null}                        </span>                      </span>                      {option.description ? (                        <span className="max-w-56 text-pretty text-muted-foreground/80 text-xs">                          {option.description}                        </span>                      ) : null}                    </span>                  </MenuRadioItem>                ))}              </MenuRadioGroup>            </MenuGroup>          </div>        );      })}      {booleanDescriptors.map((descriptor, index) => {        const selectedValue = descriptor.currentValue === true ? "on" : "off";         return (          <div key={descriptor.id}>            {index > 0 || selectDescriptors.length > 0 ? <MenuDivider /> : null}            <MenuGroup>              <div className="px-2 py-1.5 font-medium text-muted-foreground text-xs">                {descriptor.label}              </div>              <MenuRadioGroup                value={selectedValue}                onValueChange={(value) => {                  updateDescriptors(                    replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"),                  );                }}              >                {(["on", "off"] as const).map((value) => (                  <MenuRadioItem key={value} value={value} hideIndicator closeOnClick>                    <span className="flex w-full min-w-0 items-center justify-between gap-3">                      <span>{value === "on" ? "On" : "Off"}</span>                    </span>                  </MenuRadioItem>                ))}              </MenuRadioGroup>            </MenuGroup>          </div>        );      })}    </>  );}); /** * Build the traits trigger's text label plus whether the fast-mode bolt should * render. Fast mode is a lightning bolt when on and nothing at all when off — * "Normal" is the near-universal case and isn't worth the horizontal space. The * one exception is when fast mode is the only trait, where a bare bolt (or bare * chevron) would leave the trigger unreadable. */export function buildTraitsTriggerDisplay(input: {  provider: ProviderDriverKind;  descriptors: ReadonlyArray<ProviderOptionDescriptor>;  primarySelectDescriptorId: string | null;  ultrathinkPromptControlled: boolean;}): { label: string; showFastModeIcon: boolean } {  let hasFastMode = false;  let fastModeEnabled = false;  const labels: Array<string> = [];  for (const descriptor of input.descriptors) {    if (descriptor.id === "fastMode" && descriptor.type === "boolean") {      hasFastMode = true;      fastModeEnabled = descriptor.currentValue === true;      continue;    }    if (      input.provider === "codex" &&      descriptor.id === "serviceTier" &&      descriptor.type === "select"    ) {      const currentValue = getProviderOptionCurrentValue(descriptor);      const fastTier = descriptor.options.find(({ label }) => label === "Fast");      if (fastTier && (currentValue === "default" || currentValue === fastTier.id)) {        hasFastMode = true;        fastModeEnabled = currentValue === fastTier.id;        continue;      }    }    const label =      input.ultrathinkPromptControlled && descriptor.id === input.primarySelectDescriptorId        ? "Ultrathink"        : descriptor.type === "boolean"          ? `${descriptor.label} ${descriptor.currentValue === true ? "On" : "Off"}`          : getProviderOptionCurrentLabel(descriptor);    if (typeof label === "string" && label.length > 0) {      labels.push(label);    }  }   // Only fall back to text when fast mode is genuinely the sole trait. Keying  // off an empty label list alone would also catch descriptors that resolved to  // no label at all, printing a bogus "Normal" for a model without fast mode.  if (labels.length === 0 && hasFastMode) {    return { label: fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false };  }  return { label: labels.join(" · "), showFastModeIcon: fastModeEnabled };} export const TraitsPicker = memo(function TraitsPicker({  provider,  instanceId,  models,  model,  prompt,  onPromptChange,  modelOptions,  allowPromptInjectedEffort = true,  triggerVariant,  triggerClassName,  ...persistence}: TraitsMenuContentProps & TraitsPersistence) {  const [isMenuOpen, setIsMenuOpen] = useState(false);  const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled } =    getTraitsSectionVisibility({      provider,      models,      model,      prompt,      modelOptions,      allowPromptInjectedEffort,    });  if (    !shouldRenderTraitsControls({      provider,      models,      model,      prompt,      modelOptions,      allowPromptInjectedEffort,    })  ) {    return null;  }   const { label: triggerLabel, showFastModeIcon } = buildTraitsTriggerDisplay({    provider,    descriptors,    primarySelectDescriptorId: primarySelectDescriptor?.id ?? null,    ultrathinkPromptControlled,  });  const fastModeIcon = showFastModeIcon ? (    <>      <ComposerControlIcon        icon={ZapIcon}        className={cn(          "fill-current opacity-80",          provider === "claudeAgent" ? "text-[#d97757]" : "text-foreground",        )}      />      <span className="sr-only">Fast mode on</span>    </>  ) : null;   const isCodexStyle = provider === "codex";

Lines 1–500 of 551.