641 lines · 20.7 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,  normalizeModelSlug,} 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>; const SAVED_OPTION_LABELS: Readonly<Record<string, string>> = {  agent: "Agent",  effort: "Effort",  reasoningEffort: "Reasoning effort",  variant: "Variant",}; function savedOptionLabel(id: string): string {  return (    SAVED_OPTION_LABELS[id] ??    id.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (character) => character.toUpperCase())  );} /** Read-only descriptors for saved values whose OpenCode model metadata is unavailable. */export function buildUnavailableModelOptionDescriptors(  selections: ProviderOptions | null | undefined,): ReadonlyArray<ProviderOptionDescriptor> {  return (selections ?? []).map((selection) =>    typeof selection.value === "boolean"      ? {          id: selection.id,          label: savedOptionLabel(selection.id),          type: "boolean" as const,          currentValue: selection.value,        }      : {          id: selection.id,          label: savedOptionLabel(selection.id),          type: "select" as const,          options: [{ id: selection.value, label: selection.value }],          currentValue: selection.value,        },  );} 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,  planModeEnabled: boolean,) {  const caps = getProviderModelCapabilities(models, model, provider, planModeEnabled);  const modelIsUnavailable =    provider === "opencode" &&    !models.some((candidate) => candidate.slug === normalizeModelSlug(model, provider));  const descriptors = modelIsUnavailable    ? buildUnavailableModelOptionDescriptors(        planModeEnabled          ? modelOptions          : modelOptions?.filter((option) => option.id !== "agent" || option.value !== "plan"),      )    : 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,    modelIsUnavailable,  };} function getTraitsSectionVisibility(input: {  provider: ProviderDriverKind;  models: ReadonlyArray<ServerProviderModel>;  model: string | null | undefined;  prompt: string;  modelOptions: ProviderOptions | null | undefined;  allowPromptInjectedEffort?: boolean;  planModeEnabled: boolean;}) {  const selected = getSelectedTraits(    input.provider,    input.models,    input.model,    input.prompt,    input.modelOptions,    input.allowPromptInjectedEffort ?? true,    input.planModeEnabled,  );   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 ||      (selected.modelIsUnavailable && selected.descriptors.length > 0),  };} export function shouldRenderTraitsControls(input: {  provider: ProviderDriverKind;  models: ReadonlyArray<ServerProviderModel>;  model: string | null | undefined;  prompt: string;  modelOptions: ProviderOptions | null | undefined;  allowPromptInjectedEffort?: boolean;  planModeEnabled: 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;  planModeEnabled: boolean;  triggerVariant?: VariantProps<typeof buttonVariants>["variant"];  triggerClassName?: string;} export const TraitsMenuContent = memo(function TraitsMenuContentImpl({  provider,  instanceId,  models,  model,  prompt,  onPromptChange,  modelOptions,  allowPromptInjectedEffort = true,  planModeEnabled,  ...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,    modelIsUnavailable,  } = getTraitsSectionVisibility({    provider,    models,    model,    prompt,    modelOptions,    allowPromptInjectedEffort,    planModeEnabled,  });  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;  }   if (modelIsUnavailable) {    return (      <>        {descriptors.map((descriptor, index) => {          const value = getProviderOptionCurrentLabel(descriptor);          if (!value) return null;          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>                <div className="px-2 pb-1.5 text-muted-foreground/80 text-xs">{value}</div>              </MenuGroup>            </div>          );        })}      </>    );  }   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. Claude and Cursor expose fast mode as a boolean, while Codex exposes * it through the Standard/Fast service tiers. In either form, fast mode is a * lightning bolt when on and nothing at all when off. 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 fastModeFallbackLabel: string | null = null;  let fastModeEnabled = false;  const labels: Array<string> = [];  for (const descriptor of input.descriptors) {    if (descriptor.id === "fastMode" && descriptor.type === "boolean") {      fastModeEnabled = descriptor.currentValue === true;      fastModeFallbackLabel = fastModeEnabled ? "Fast" : "Normal";      continue;    }    if (      input.provider === "codex" &&      descriptor.id === "serviceTier" &&      descriptor.type === "select"    ) {

Lines 1–500 of 641.