export declare const WEB_APP_TEMPLATE_FILES: {
    readonly "agent/channels/eve.ts": 'import { eveChannel } from "eve/channels/eve";\nimport { localDev, placeholderAuth, vercelOidc } from "eve/channels/auth";\n\nexport default eveChannel({\n  auth: [\n    // Lets the eve TUI and your Vercel deployments reach the deployed agent.\n    vercelOidc(),\n    // Open on localhost for `eve dev` and the REPL; ignored in production.\n    localDev(),\n    // This placeholder will not allow browser requests in production.\n    // Replace it with your app\'s auth provider, like Auth.js or Clerk,\n    // or use none() for a public demo.\n    placeholderAuth(),\n  ],\n});\n';
    readonly "app/_components/agent-chat.tsx": '"use client";\n\nimport type { UserContent } from "ai";\nimport { Client, type HandleMessageStreamEvent } from "eve/client";\nimport { useEveAgent } from "eve/react";\nimport { AlertCircleIcon } from "lucide-react";\nimport { useCallback, useRef, useState } from "react";\nimport {\n  Conversation,\n  ConversationContent,\n  ConversationScrollButton,\n} from "@/components/ai-elements/conversation";\nimport {\n  PromptInput,\n  type PromptInputMessage,\n  PromptInputSubmit,\n  PromptInputTextarea,\n} from "@/components/ai-elements/prompt-input";\nimport { cn } from "@/lib/utils";\nimport { AgentMessage } from "./agent-message";\n\nconst AGENT_NAME = "__EVE_INIT_APP_NAME__";\n\ntype AgentStatus = ReturnType<typeof useEveAgent>["status"];\ntype CancellationState = "idle" | "requested" | "cancelling";\n\ntype Cancellation = {\n  requested: boolean;\n  sentTurnId?: string;\n  turnId?: string;\n};\n\nexport function AgentChat() {\n  const [session] = useState(() =>\n    new Client({ host: "", preserveCompletedSessions: true }).session(),\n  );\n  const cancellationRef = useRef<Cancellation>({ requested: false });\n  const [cancellationError, setCancellationError] = useState<string>();\n  const [cancellationState, setCancellationState] = useState<CancellationState>("idle");\n\n  const cancelTurn = useCallback(\n    (turnId: string) => {\n      const cancellation = cancellationRef.current;\n      if (!cancellation.requested || cancellation.sentTurnId === turnId) {\n        return;\n      }\n\n      cancellation.sentTurnId = turnId;\n      setCancellationState("cancelling");\n\n      void session.cancel({ turnId }).catch((error: unknown) => {\n        if (cancellationRef.current !== cancellation) {\n          return;\n        }\n\n        cancellation.requested = false;\n        cancellation.sentTurnId = undefined;\n        setCancellationError(toErrorMessage(error));\n        setCancellationState("idle");\n      });\n    },\n    [session],\n  );\n\n  const handleEvent = useCallback(\n    (event: HandleMessageStreamEvent) => {\n      if (event.type !== "turn.started") {\n        return;\n      }\n\n      const cancellation = cancellationRef.current;\n      cancellation.turnId = event.data.turnId;\n      cancelTurn(event.data.turnId);\n    },\n    [cancelTurn],\n  );\n\n  const agent = useEveAgent({ onEvent: handleEvent, session });\n  const isBusy = agent.status === "submitted" || agent.status === "streaming";\n  const isEmpty = agent.data.messages.length === 0;\n  const errorMessage = cancellationError ?? agent.error?.message;\n  const submitStatus = isBusy && cancellationState !== "idle" ? "submitted" : agent.status;\n\n  const prepareTurn = () => {\n    cancellationRef.current = { requested: false };\n    setCancellationError(undefined);\n    setCancellationState("idle");\n  };\n\n  const requestCancellation = () => {\n    if (!isBusy || cancellationState !== "idle") {\n      return;\n    }\n\n    const cancellation = cancellationRef.current;\n    cancellation.requested = true;\n    setCancellationError(undefined);\n    setCancellationState("requested");\n\n    if (cancellation.turnId !== undefined) {\n      cancelTurn(cancellation.turnId);\n    }\n  };\n\n  const handleSubmit = async (message: PromptInputMessage) => {\n    const text = message.text.trim();\n    if ((text.length === 0 && message.files.length === 0) || isBusy) return;\n\n    prepareTurn();\n\n    if (message.files.length === 0) {\n      await agent.send({ message: text });\n      return;\n    }\n\n    const parts: UserContent = [];\n    if (text.length > 0) {\n      parts.push({ text, type: "text" });\n    }\n    for (const file of message.files) {\n      parts.push({\n        data: file.url,\n        filename: file.filename,\n        mediaType: file.mediaType,\n        type: "file",\n      });\n    }\n\n    await agent.send({ message: parts });\n  };\n\n  const composer = (\n    <PromptInput onSubmit={handleSubmit}>\n      <PromptInputTextarea placeholder="Send a message…" />\n      <PromptInputSubmit onStop={requestCancellation} status={submitStatus} />\n    </PromptInput>\n  );\n\n  return (\n    <main className="flex h-dvh flex-col overflow-hidden bg-background text-foreground">\n      {isEmpty ? null : (\n        <header className="flex h-14 shrink-0 items-center justify-center gap-3 pl-4 pr-2">\n          <span className="flex min-w-0 items-center gap-2">\n            <span className="truncate text-muted-foreground text-sm">{AGENT_NAME}</span>\n            <StatusDot status={agent.status} />\n          </span>\n        </header>\n      )}\n\n      {errorMessage ? (\n        <div className="mx-auto w-full max-w-3xl shrink-0 px-4 pt-2 sm:px-6">\n          <div className="flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2.5 text-sm">\n            <AlertCircleIcon className="mt-0.5 size-4 shrink-0 text-destructive" />\n            <div>\n              <p className="font-medium">Request failed</p>\n              <p className="mt-0.5 text-muted-foreground">{errorMessage}</p>\n            </div>\n          </div>\n        </div>\n      ) : null}\n\n      {isEmpty ? null : (\n        <Conversation className="min-h-0 flex-1">\n          <ConversationContent className="mx-auto w-full max-w-3xl gap-6 px-4 py-6 sm:px-6">\n            {agent.data.messages.map((message, index) => (\n              <AgentMessage\n                canRespond={!isBusy}\n                isStreaming={\n                  agent.status === "streaming" && index === agent.data.messages.length - 1\n                }\n                key={message.id}\n                message={message}\n                onInputResponses={(inputResponses) => {\n                  prepareTurn();\n                  return agent.send({ inputResponses });\n                }}\n              />\n            ))}\n          </ConversationContent>\n          <ConversationScrollButton />\n        </Conversation>\n      )}\n\n      <div\n        className={cn(\n          "mx-auto w-full px-4 sm:px-6",\n          isEmpty\n            ? "flex max-w-xl flex-1 flex-col items-center justify-center gap-8 pb-[10vh]"\n            : "max-w-3xl shrink-0 pb-6",\n        )}\n      >\n        {isEmpty ? (\n          <div className="flex flex-col items-center gap-3 text-center">\n            <h1 className="font-medium text-5xl tracking-tighter">{AGENT_NAME}</h1>\n          </div>\n        ) : null}\n        <div className="w-full">{composer}</div>\n      </div>\n    </main>\n  );\n}\n\nfunction toErrorMessage(error: unknown): string {\n  return error instanceof Error ? error.message : "Unable to cancel the response.";\n}\n\nfunction StatusDot({ status }: { readonly status: AgentStatus }) {\n  const isLive = status === "submitted" || status === "streaming";\n  const tone =\n    status === "error"\n      ? "bg-destructive"\n      : isLive\n        ? "bg-emerald-500"\n        : status === "ready"\n          ? "bg-muted-foreground"\n          : "bg-muted-foreground/50";\n\n  return (\n    <span className="relative flex size-1">\n      {isLive ? (\n        <span\n          className={cn(\n            "absolute inline-flex size-full animate-ping rounded-full opacity-75",\n            tone,\n          )}\n        />\n      ) : null}\n      <span className={cn("relative inline-flex size-1 rounded-full transition-colors", tone)} />\n    </span>\n  );\n}\n';
    readonly "app/_components/agent-message.tsx": '"use client";\n\nimport type {\n  EveAuthorizationPart,\n  EveDynamicToolPart,\n  EveMessage,\n  EveMessagePart,\n} from "eve/react";\nimport {\n  CheckCircleIcon,\n  ExternalLinkIcon,\n  FileIcon,\n  ImageIcon,\n  KeyRoundIcon,\n  XCircleIcon,\n} from "lucide-react";\nimport { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message";\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from "@/components/ai-elements/reasoning";\nimport {\n  Tool,\n  ToolContent,\n  ToolHeader,\n  ToolInput,\n  ToolOutput,\n} from "@/components/ai-elements/tool";\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\n\nexport type AgentInputResponse = {\n  readonly optionId?: string;\n  readonly requestId: string;\n  readonly text?: string;\n};\n\ntype EveFilePart = Extract<EveMessagePart, { type: "file" }>;\n\nexport function AgentMessage({\n  canRespond,\n  isStreaming,\n  message,\n  onInputResponses,\n}: {\n  readonly canRespond: boolean;\n  readonly isStreaming: boolean;\n  readonly message: EveMessage;\n  readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n}) {\n  const lastTextIndex = message.parts.reduce(\n    (last, part, index) => (part.type === "text" ? index : last),\n    -1,\n  );\n\n  return (\n    <Message\n      data-optimistic={message.metadata?.optimistic ? "true" : undefined}\n      from={message.role}\n    >\n      <MessageContent>\n        {message.parts.map((part, index) => (\n          <AgentMessagePart\n            canRespond={canRespond}\n            key={partKey(part, index)}\n            onInputResponses={onInputResponses}\n            part={part}\n            showCaret={isStreaming && message.role === "assistant" && index === lastTextIndex}\n          />\n        ))}\n      </MessageContent>\n    </Message>\n  );\n}\n\nfunction AgentMessagePart({\n  canRespond,\n  onInputResponses,\n  part,\n  showCaret,\n}: {\n  readonly canRespond: boolean;\n  readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n  readonly part: EveMessagePart;\n  readonly showCaret: boolean;\n}) {\n  switch (part.type) {\n    case "step-start":\n      return null;\n    case "text":\n      return (\n        <MessageResponse caret="block" isAnimating={showCaret}>\n          {part.text}\n        </MessageResponse>\n      );\n    case "reasoning":\n      return (\n        <Reasoning defaultOpen isStreaming={part.state === "streaming"}>\n          <ReasoningTrigger />\n          <ReasoningContent>{part.text}</ReasoningContent>\n        </Reasoning>\n      );\n    case "file":\n      return <AttachmentPart part={part} />;\n    case "authorization":\n      return <AuthorizationPrompt part={part} />;\n    case "dynamic-tool":\n      return (\n        <Tool\n          defaultOpen={part.state === "approval-requested" || part.state === "approval-responded"}\n        >\n          <ToolHeader\n            state={part.state}\n            title={part.toolName}\n            toolName={part.toolName}\n            type="dynamic-tool"\n          />\n          <ToolContent>\n            <ToolInput input={part.input} />\n            <InputRequestActions\n              canRespond={canRespond}\n              part={part}\n              onInputResponses={onInputResponses}\n            />\n            <ToolOutput errorText={part.errorText} output={part.output} />\n          </ToolContent>\n        </Tool>\n      );\n  }\n}\n\nfunction AttachmentPart({ part }: { readonly part: EveFilePart }) {\n  const label = part.filename ?? "Attachment";\n  const detail = [part.mediaType, formatBytes(part.size)].filter(Boolean).join(" - ");\n  const isImage = part.mediaType.startsWith("image/") && part.url !== undefined;\n  const Icon = isImage ? ImageIcon : FileIcon;\n  const body = (\n    <span className="flex max-w-sm items-center gap-3 rounded-md border bg-background/60 p-2 text-sm">\n      {isImage ? (\n        <img alt={label} className="size-12 shrink-0 rounded-sm object-cover" src={part.url} />\n      ) : (\n        <span className="flex size-10 shrink-0 items-center justify-center rounded-sm bg-muted text-muted-foreground">\n          <Icon className="size-4" />\n        </span>\n      )}\n      <span className="min-w-0 flex-1">\n        <span className="block truncate font-medium">{label}</span>\n        {detail ? <span className="block truncate text-muted-foreground">{detail}</span> : null}\n      </span>\n      {part.url ? <ExternalLinkIcon className="size-4 shrink-0 text-muted-foreground" /> : null}\n    </span>\n  );\n\n  return part.url ? (\n    <a href={part.url} rel="noreferrer" target="_blank">\n      {body}\n    </a>\n  ) : (\n    body\n  );\n}\n\nfunction AuthorizationPrompt({ part }: { readonly part: EveAuthorizationPart }) {\n  const isAuthorized = part.state === "completed" && part.outcome === "authorized";\n  const isCompleted = part.state === "completed";\n  const Icon = isAuthorized ? CheckCircleIcon : isCompleted ? XCircleIcon : KeyRoundIcon;\n  const instructions = part.authorization?.instructions;\n  const shouldShowInstructions = instructions !== undefined && instructions !== part.description;\n\n  return (\n    <div\n      className={cn(\n        "space-y-3 rounded-md border p-3",\n        isAuthorized\n          ? "border-emerald-500/30 bg-emerald-500/5"\n          : isCompleted\n            ? "border-destructive/30 bg-destructive/5"\n            : "border-blue-500/30 bg-blue-500/5",\n      )}\n    >\n      <div className="flex items-start gap-3">\n        <span\n          className={cn(\n            "mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full",\n            isAuthorized\n              ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"\n              : isCompleted\n                ? "bg-destructive/10 text-destructive"\n                : "bg-blue-500/10 text-blue-700 dark:text-blue-300",\n          )}\n        >\n          <Icon className="size-4" />\n        </span>\n        <div className="min-w-0 flex-1 space-y-2">\n          <p className="font-medium text-sm">{authorizationTitle(part)}</p>\n          <p className="text-muted-foreground text-sm">{authorizationDescription(part)}</p>\n          {shouldShowInstructions ? (\n            <p className="text-muted-foreground text-sm">{instructions}</p>\n          ) : null}\n          {part.state === "required" && part.authorization?.userCode ? (\n            <div className="flex flex-wrap items-center gap-2 text-sm">\n              <span className="text-muted-foreground">Code</span>\n              <code className="rounded-md bg-background px-2 py-1 font-mono">\n                {part.authorization.userCode}\n              </code>\n            </div>\n          ) : null}\n          {part.state === "required" && part.authorization?.url ? (\n            <Button asChild size="sm">\n              <a href={part.authorization.url} rel="noreferrer" target="_blank">\n                <ExternalLinkIcon className="size-4" />\n                Sign in with {part.displayName}\n              </a>\n            </Button>\n          ) : null}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction authorizationTitle(part: EveAuthorizationPart): string {\n  if (part.state === "required") {\n    return `Connect ${part.displayName}`;\n  }\n  if (part.outcome === "authorized") {\n    return `${part.displayName} connected`;\n  }\n  return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}`;\n}\n\nfunction authorizationDescription(part: EveAuthorizationPart): string {\n  if (part.state === "required") {\n    return part.description;\n  }\n  if (part.outcome === "authorized") {\n    return `${part.displayName} connected.`;\n  }\n  const tail = part.reason !== undefined ? ` (${part.reason})` : "";\n  return `${part.displayName} authorization ${formatAuthorizationOutcome(part.outcome)}${tail}.`;\n}\n\nfunction formatAuthorizationOutcome(outcome: NonNullable<EveAuthorizationPart["outcome"]>): string {\n  switch (outcome) {\n    case "authorized":\n      return "authorized";\n    case "declined":\n      return "declined";\n    case "failed":\n      return "failed";\n    case "timed-out":\n      return "timed out";\n  }\n}\n\nfunction formatBytes(size: number | undefined): string | undefined {\n  if (size === undefined) {\n    return undefined;\n  }\n  if (size < 1024) {\n    return `${size} B`;\n  }\n  if (size < 1024 * 1024) {\n    return `${(size / 1024).toFixed(1)} KB`;\n  }\n  return `${(size / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction InputRequestActions({\n  canRespond,\n  onInputResponses,\n  part,\n}: {\n  readonly canRespond: boolean;\n  readonly onInputResponses: (responses: readonly AgentInputResponse[]) => void | Promise<void>;\n  readonly part: EveDynamicToolPart;\n}) {\n  const inputRequest = part.toolMetadata?.eve?.inputRequest;\n  if (!inputRequest) {\n    return null;\n  }\n\n  const inputResponse = part.toolMetadata?.eve?.inputResponse;\n  const selectedOption = inputRequest.options?.find(\n    (option) => option.id === inputResponse?.optionId,\n  );\n\n  return (\n    <div className="space-y-3 rounded-md border border-yellow-500/30 bg-yellow-500/5 p-3">\n      <p className="text-muted-foreground text-sm">{inputRequest.prompt}</p>\n      {inputResponse ? (\n        <p className="font-medium text-sm">\n          Responded: {selectedOption?.label ?? inputResponse.text ?? inputResponse.optionId}\n        </p>\n      ) : (\n        <div className="flex flex-wrap gap-2">\n          {inputRequest.options?.map((option) => (\n            <Button\n              disabled={!canRespond}\n              key={option.id}\n              onClick={() => {\n                void onInputResponses([\n                  {\n                    optionId: option.id,\n                    requestId: inputRequest.requestId,\n                  },\n                ]);\n              }}\n              size="sm"\n              type="button"\n              variant={option.style === "danger" ? "destructive" : "default"}\n            >\n              {option.label}\n            </Button>\n          ))}\n        </div>\n      )}\n    </div>\n  );\n}\n\nfunction partKey(part: EveMessagePart, index: number): string {\n  switch (part.type) {\n    case "authorization":\n      return `authorization:${part.turnId}:${part.stepIndex}:${part.name}`;\n    case "dynamic-tool":\n      return part.toolCallId;\n    default:\n      return `${part.type}:${index}`;\n  }\n}\n';
    readonly "app/globals.css": '@import "tailwindcss";\n@source "../node_modules/streamdown/dist/*.js";\n\n@theme inline {\n  --color-background: var(--background);\n  --color-foreground: var(--foreground);\n  --color-card: var(--card);\n  --color-card-foreground: var(--card-foreground);\n  --color-popover: var(--popover);\n  --color-popover-foreground: var(--popover-foreground);\n  --color-primary: var(--primary);\n  --color-primary-foreground: var(--primary-foreground);\n  --color-secondary: var(--secondary);\n  --color-secondary-foreground: var(--secondary-foreground);\n  --color-muted: var(--muted);\n  --color-muted-foreground: var(--muted-foreground);\n  --color-accent: var(--accent);\n  --color-accent-foreground: var(--accent-foreground);\n  --color-destructive: var(--destructive);\n  --color-border: var(--border);\n  --color-input: var(--input);\n  --color-ring: var(--ring);\n  --radius-sm: calc(var(--radius) - 4px);\n  --radius-md: calc(var(--radius) - 2px);\n  --radius-lg: var(--radius);\n  --radius-xl: calc(var(--radius) + 4px);\n  --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;\n  --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;\n}\n\n:root {\n  color-scheme: light;\n  /* Soft neutral page with white elevated surfaces so cards/composer pop. */\n  --background: oklch(0.971 0 0);\n  --foreground: oklch(0.16 0 0);\n  --card: oklch(1 0 0);\n  --card-foreground: oklch(0.16 0 0);\n  --popover: oklch(1 0 0);\n  --popover-foreground: oklch(0.16 0 0);\n  --primary: oklch(0.19 0 0);\n  --primary-foreground: oklch(0.985 0 0);\n  --secondary: oklch(0.94 0 0);\n  --secondary-foreground: oklch(0.19 0 0);\n  --muted: oklch(0.94 0 0);\n  --muted-foreground: oklch(0.6 0 0);\n  --accent: oklch(0.94 0 0);\n  --accent-foreground: oklch(0.19 0 0);\n  --destructive: oklch(0.577 0.245 27.325);\n  --border: oklch(0.916 0 0);\n  --input: oklch(0.916 0 0);\n  --ring: oklch(0.708 0 0);\n  --radius: 0.625rem;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    color-scheme: dark;\n    --background: oklch(0.145 0 0);\n    --foreground: oklch(0.985 0 0);\n    --card: oklch(0.205 0 0);\n    --card-foreground: oklch(0.985 0 0);\n    --popover: oklch(0.205 0 0);\n    --popover-foreground: oklch(0.985 0 0);\n    --primary: oklch(0.922 0 0);\n    --primary-foreground: oklch(0.205 0 0);\n    --secondary: oklch(0.269 0 0);\n    --secondary-foreground: oklch(0.985 0 0);\n    --muted: oklch(0.269 0 0);\n    --muted-foreground: oklch(0.708 0 0);\n    --accent: oklch(0.269 0 0);\n    --accent-foreground: oklch(0.985 0 0);\n    --destructive: oklch(0.704 0.191 22.216);\n    --border: oklch(1 0 0 / 10%);\n    --input: oklch(1 0 0 / 15%);\n    --ring: oklch(0.556 0 0);\n  }\n}\n\n* {\n  border-color: var(--border);\n}\n\nhtml {\n  height: 100%;\n}\n\nbody {\n  min-height: 100%;\n  margin: 0;\n  background: var(--background);\n  font-family: var(--font-sans);\n}\n\nbutton,\ninput,\ntextarea {\n  font: inherit;\n}\n';
    readonly "app/layout.tsx": 'import type { Metadata } from "next";\nimport { Geist, Geist_Mono } from "next/font/google";\nimport type { ReactNode } from "react";\nimport { TooltipProvider } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport "./globals.css";\n\nconst sans = Geist({\n  variable: "--font-sans",\n  subsets: ["latin"],\n  weight: "variable",\n  display: "swap",\n});\n\nconst mono = Geist_Mono({\n  variable: "--font-mono",\n  subsets: ["latin"],\n  weight: "variable",\n  display: "swap",\n});\n\nexport const metadata: Metadata = {\n  title: "__EVE_INIT_APP_NAME__",\n  description: "A Next.js starter for eve agents with AI Elements.",\n};\n\nexport default function RootLayout({ children }: { readonly children: ReactNode }) {\n  return (\n    <html className={cn(sans.variable, mono.variable)} lang="en">\n      <body>\n        <TooltipProvider>{children}</TooltipProvider>\n      </body>\n    </html>\n  );\n}\n';
    readonly "app/page.tsx": 'import { AgentChat } from "@/app/_components/agent-chat";\n\nexport default function Page() {\n  return <AgentChat />;\n}\n';
    readonly "components/ai-elements/chain-of-thought.tsx": '"use client";\n\nimport { useControllableState } from "@radix-ui/react-use-controllable-state";\nimport { Badge } from "@/components/ui/badge";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport type { LucideIcon } from "lucide-react";\nimport { BrainIcon, ChevronDownIcon, DotIcon } from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport { createContext, memo, useContext, useMemo } from "react";\n\ninterface ChainOfThoughtContextValue {\n  isOpen: boolean;\n  setIsOpen: (open: boolean) => void;\n}\n\nconst ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(null);\n\nconst useChainOfThought = () => {\n  const context = useContext(ChainOfThoughtContext);\n  if (!context) {\n    throw new Error("ChainOfThought components must be used within ChainOfThought");\n  }\n  return context;\n};\n\nexport type ChainOfThoughtProps = ComponentProps<"div"> & {\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n};\n\nexport const ChainOfThought = memo(\n  ({\n    className,\n    open,\n    defaultOpen = false,\n    onOpenChange,\n    children,\n    ...props\n  }: ChainOfThoughtProps) => {\n    const [isOpen, setIsOpen] = useControllableState({\n      defaultProp: defaultOpen,\n      onChange: onOpenChange,\n      prop: open,\n    });\n\n    const chainOfThoughtContext = useMemo(() => ({ isOpen, setIsOpen }), [isOpen, setIsOpen]);\n\n    return (\n      <ChainOfThoughtContext.Provider value={chainOfThoughtContext}>\n        <div className={cn("not-prose w-full space-y-4", className)} {...props}>\n          {children}\n        </div>\n      </ChainOfThoughtContext.Provider>\n    );\n  },\n);\n\nexport type ChainOfThoughtHeaderProps = ComponentProps<typeof CollapsibleTrigger>;\n\nexport const ChainOfThoughtHeader = memo(\n  ({ className, children, ...props }: ChainOfThoughtHeaderProps) => {\n    const { isOpen, setIsOpen } = useChainOfThought();\n\n    return (\n      <Collapsible onOpenChange={setIsOpen} open={isOpen}>\n        <CollapsibleTrigger\n          className={cn(\n            "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",\n            className,\n          )}\n          {...props}\n        >\n          <BrainIcon className="size-4" />\n          <span className="flex-1 text-left">{children ?? "Chain of Thought"}</span>\n          <ChevronDownIcon\n            className={cn("size-4 transition-transform", isOpen ? "rotate-180" : "rotate-0")}\n          />\n        </CollapsibleTrigger>\n      </Collapsible>\n    );\n  },\n);\n\nexport type ChainOfThoughtStepProps = ComponentProps<"div"> & {\n  icon?: LucideIcon;\n  label: ReactNode;\n  description?: ReactNode;\n  status?: "complete" | "active" | "pending";\n};\n\nconst stepStatusStyles = {\n  active: "text-foreground",\n  complete: "text-muted-foreground",\n  pending: "text-muted-foreground/50",\n};\n\nexport const ChainOfThoughtStep = memo(\n  ({\n    className,\n    icon: Icon = DotIcon,\n    label,\n    description,\n    status = "complete",\n    children,\n    ...props\n  }: ChainOfThoughtStepProps) => (\n    <div\n      className={cn(\n        "flex gap-2 text-sm",\n        stepStatusStyles[status],\n        "fade-in-0 slide-in-from-top-2 animate-in",\n        className,\n      )}\n      {...props}\n    >\n      <div className="relative mt-0.5">\n        <Icon className="size-4" />\n        <div className="absolute top-7 bottom-0 left-1/2 -mx-px w-px bg-border" />\n      </div>\n      <div className="flex-1 space-y-2 overflow-hidden">\n        <div>{label}</div>\n        {description && <div className="text-muted-foreground text-xs">{description}</div>}\n        {children}\n      </div>\n    </div>\n  ),\n);\n\nexport type ChainOfThoughtSearchResultsProps = ComponentProps<"div">;\n\nexport const ChainOfThoughtSearchResults = memo(\n  ({ className, ...props }: ChainOfThoughtSearchResultsProps) => (\n    <div className={cn("flex flex-wrap items-center gap-2", className)} {...props} />\n  ),\n);\n\nexport type ChainOfThoughtSearchResultProps = ComponentProps<typeof Badge>;\n\nexport const ChainOfThoughtSearchResult = memo(\n  ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => (\n    <Badge\n      className={cn("gap-1 px-2 py-0.5 font-normal text-xs", className)}\n      variant="secondary"\n      {...props}\n    >\n      {children}\n    </Badge>\n  ),\n);\n\nexport type ChainOfThoughtContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport const ChainOfThoughtContent = memo(\n  ({ className, children, ...props }: ChainOfThoughtContentProps) => {\n    const { isOpen } = useChainOfThought();\n\n    return (\n      <Collapsible open={isOpen}>\n        <CollapsibleContent\n          className={cn(\n            "mt-2 space-y-3",\n            "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n            className,\n          )}\n          {...props}\n        >\n          {children}\n        </CollapsibleContent>\n      </Collapsible>\n    );\n  },\n);\n\nexport type ChainOfThoughtImageProps = ComponentProps<"div"> & {\n  caption?: string;\n};\n\nexport const ChainOfThoughtImage = memo(\n  ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => (\n    <div className={cn("mt-2 space-y-2", className)} {...props}>\n      <div className="relative flex max-h-[22rem] items-center justify-center overflow-hidden rounded-lg bg-muted p-3">\n        {children}\n      </div>\n      {caption && <p className="text-muted-foreground text-xs">{caption}</p>}\n    </div>\n  ),\n);\n\nChainOfThought.displayName = "ChainOfThought";\nChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";\nChainOfThoughtStep.displayName = "ChainOfThoughtStep";\nChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults";\nChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult";\nChainOfThoughtContent.displayName = "ChainOfThoughtContent";\nChainOfThoughtImage.displayName = "ChainOfThoughtImage";\n';
    readonly "components/ai-elements/code-block.tsx": '"use client";\n\nimport { Button } from "@/components/ui/button";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from "@/components/ui/select";\nimport { cn } from "@/lib/utils";\nimport { CheckIcon, CopyIcon } from "lucide-react";\nimport type { ComponentProps, CSSProperties, HTMLAttributes } from "react";\nimport {\n  createContext,\n  memo,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from "react";\nimport type { BundledLanguage, BundledTheme, HighlighterGeneric, ThemedToken } from "shiki";\nimport { createHighlighter } from "shiki";\n\n// Shiki uses bitflags for font styles: 1=italic, 2=bold, 4=underline\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isItalic = (fontStyle: number | undefined) => fontStyle && fontStyle & 1;\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isBold = (fontStyle: number | undefined) => fontStyle && fontStyle & 2;\nconst isUnderline = (fontStyle: number | undefined) =>\n  // oxlint-disable-next-line eslint(no-bitwise)\n  fontStyle && fontStyle & 4;\n\n// Transform tokens to include pre-computed keys to avoid noArrayIndexKey lint\ninterface KeyedToken {\n  token: ThemedToken;\n  key: string;\n}\ninterface KeyedLine {\n  tokens: KeyedToken[];\n  key: string;\n}\n\nconst addKeysToTokens = (lines: ThemedToken[][]): KeyedLine[] =>\n  lines.map((line, lineIdx) => ({\n    key: `line-${lineIdx}`,\n    tokens: line.map((token, tokenIdx) => ({\n      key: `line-${lineIdx}-${tokenIdx}`,\n      token,\n    })),\n  }));\n\n// Token rendering component\nconst TokenSpan = ({ token }: { token: ThemedToken }) => (\n  <span\n    className="dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]"\n    style={\n      {\n        backgroundColor: token.bgColor,\n        color: token.color,\n        fontStyle: isItalic(token.fontStyle) ? "italic" : undefined,\n        fontWeight: isBold(token.fontStyle) ? "bold" : undefined,\n        textDecoration: isUnderline(token.fontStyle) ? "underline" : undefined,\n        ...token.htmlStyle,\n      } as CSSProperties\n    }\n  >\n    {token.content}\n  </span>\n);\n\n// Line number styles using CSS counters\nconst LINE_NUMBER_CLASSES = cn(\n  "block",\n  "before:content-[counter(line)]",\n  "before:inline-block",\n  "before:[counter-increment:line]",\n  "before:w-8",\n  "before:mr-4",\n  "before:text-right",\n  "before:text-muted-foreground/50",\n  "before:font-mono",\n  "before:select-none",\n);\n\n// Line rendering component\nconst LineSpan = ({\n  keyedLine,\n  showLineNumbers,\n}: {\n  keyedLine: KeyedLine;\n  showLineNumbers: boolean;\n}) => (\n  <span className={showLineNumbers ? LINE_NUMBER_CLASSES : "block"}>\n    {keyedLine.tokens.length === 0\n      ? "\\n"\n      : keyedLine.tokens.map(({ token, key }) => <TokenSpan key={key} token={token} />)}\n  </span>\n);\n\n// Types\ntype CodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n  code: string;\n  language: BundledLanguage;\n  showLineNumbers?: boolean;\n};\n\ninterface TokenizedCode {\n  tokens: ThemedToken[][];\n  fg: string;\n  bg: string;\n}\n\ninterface CodeBlockContextType {\n  code: string;\n}\n\n// Context\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n  code: "",\n});\n\n// Highlighter cache (singleton per language)\nconst highlighterCache = new Map<\n  string,\n  Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>\n>();\n\n// Token cache\nconst tokensCache = new Map<string, TokenizedCode>();\n\n// Subscribers for async token updates\nconst subscribers = new Map<string, Set<(result: TokenizedCode) => void>>();\n\nconst getTokensCacheKey = (code: string, language: BundledLanguage) => {\n  const start = code.slice(0, 100);\n  const end = code.length > 100 ? code.slice(-100) : "";\n  return `${language}:${code.length}:${start}:${end}`;\n};\n\nconst getHighlighter = (\n  language: BundledLanguage,\n): Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> => {\n  const cached = highlighterCache.get(language);\n  if (cached) {\n    return cached;\n  }\n\n  const highlighterPromise = createHighlighter({\n    langs: [language],\n    themes: ["github-light", "github-dark"],\n  });\n\n  highlighterCache.set(language, highlighterPromise);\n  return highlighterPromise;\n};\n\n// Create raw tokens for immediate display while highlighting loads\nconst createRawTokens = (code: string): TokenizedCode => ({\n  bg: "transparent",\n  fg: "inherit",\n  tokens: code.split("\\n").map((line) =>\n    line === ""\n      ? []\n      : [\n          {\n            color: "inherit",\n            content: line,\n          } as ThemedToken,\n        ],\n  ),\n});\n\n// Synchronous highlight with callback for async results\nexport const highlightCode = (\n  code: string,\n  language: BundledLanguage,\n  // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-callbacks)\n  callback?: (result: TokenizedCode) => void,\n): TokenizedCode | null => {\n  const tokensCacheKey = getTokensCacheKey(code, language);\n\n  // Return cached result if available\n  const cached = tokensCache.get(tokensCacheKey);\n  if (cached) {\n    return cached;\n  }\n\n  // Subscribe callback if provided\n  if (callback) {\n    if (!subscribers.has(tokensCacheKey)) {\n      subscribers.set(tokensCacheKey, new Set());\n    }\n    subscribers.get(tokensCacheKey)?.add(callback);\n  }\n\n  // Start highlighting in background - fire-and-forget async pattern\n  getHighlighter(language)\n    // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then)\n    .then((highlighter) => {\n      const availableLangs = highlighter.getLoadedLanguages();\n      const langToUse = availableLangs.includes(language) ? language : "text";\n\n      const result = highlighter.codeToTokens(code, {\n        lang: langToUse,\n        themes: {\n          dark: "github-dark",\n          light: "github-light",\n        },\n      });\n\n      const tokenized: TokenizedCode = {\n        bg: result.bg ?? "transparent",\n        fg: result.fg ?? "inherit",\n        tokens: result.tokens,\n      };\n\n      // Cache the result\n      tokensCache.set(tokensCacheKey, tokenized);\n\n      // Notify all subscribers\n      const subs = subscribers.get(tokensCacheKey);\n      if (subs) {\n        for (const sub of subs) {\n          sub(tokenized);\n        }\n        subscribers.delete(tokensCacheKey);\n      }\n    })\n    // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then), eslint-plugin-promise(prefer-await-to-callbacks)\n    .catch((error) => {\n      console.error("Failed to highlight code:", error);\n      subscribers.delete(tokensCacheKey);\n    });\n\n  return null;\n};\n\nconst CodeBlockBody = memo(\n  ({\n    tokenized,\n    showLineNumbers,\n    className,\n  }: {\n    tokenized: TokenizedCode;\n    showLineNumbers: boolean;\n    className?: string;\n  }) => {\n    const preStyle = useMemo(\n      () => ({\n        backgroundColor: tokenized.bg,\n        color: tokenized.fg,\n      }),\n      [tokenized.bg, tokenized.fg],\n    );\n\n    const keyedLines = useMemo(() => addKeysToTokens(tokenized.tokens), [tokenized.tokens]);\n\n    return (\n      <pre\n        className={cn(\n          "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm",\n          className,\n        )}\n        style={preStyle}\n      >\n        <code\n          className={cn(\n            "font-mono text-sm",\n            showLineNumbers && "[counter-increment:line_0] [counter-reset:line]",\n          )}\n        >\n          {keyedLines.map((keyedLine) => (\n            <LineSpan key={keyedLine.key} keyedLine={keyedLine} showLineNumbers={showLineNumbers} />\n          ))}\n        </code>\n      </pre>\n    );\n  },\n  (prevProps, nextProps) =>\n    prevProps.tokenized === nextProps.tokenized &&\n    prevProps.showLineNumbers === nextProps.showLineNumbers &&\n    prevProps.className === nextProps.className,\n);\n\nCodeBlockBody.displayName = "CodeBlockBody";\n\nexport const CodeBlockContainer = ({\n  className,\n  language,\n  style,\n  ...props\n}: HTMLAttributes<HTMLDivElement> & { language: string }) => (\n  <div\n    className={cn(\n      "group relative w-full overflow-hidden rounded-md border bg-background text-foreground",\n      className,\n    )}\n    data-language={language}\n    style={{\n      containIntrinsicSize: "auto 200px",\n      contentVisibility: "auto",\n      ...style,\n    }}\n    {...props}\n  />\n);\n\nexport const CodeBlockHeader = ({\n  children,\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      "flex items-center justify-between border-b bg-muted/80 px-3 py-2 text-muted-foreground text-xs",\n      className,\n    )}\n    {...props}\n  >\n    {children}\n  </div>\n);\n\nexport const CodeBlockTitle = ({\n  children,\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n  <div className={cn("flex items-center gap-2", className)} {...props}>\n    {children}\n  </div>\n);\n\nexport const CodeBlockFilename = ({\n  children,\n  className,\n  ...props\n}: HTMLAttributes<HTMLSpanElement>) => (\n  <span className={cn("font-mono", className)} {...props}>\n    {children}\n  </span>\n);\n\nexport const CodeBlockActions = ({\n  children,\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n  <div className={cn("-my-1 -mr-1 flex items-center gap-2", className)} {...props}>\n    {children}\n  </div>\n);\n\nexport const CodeBlockContent = ({\n  code,\n  language,\n  showLineNumbers = false,\n}: {\n  code: string;\n  language: BundledLanguage;\n  showLineNumbers?: boolean;\n}) => {\n  // Memoized raw tokens for immediate display\n  const rawTokens = useMemo(() => createRawTokens(code), [code]);\n\n  // Synchronous cache lookup — avoids setState in effect for cached results\n  const syncTokens = useMemo(\n    () => highlightCode(code, language) ?? rawTokens,\n    [code, language, rawTokens],\n  );\n\n  // Async highlighting result (populated after shiki loads)\n  const [asyncTokens, setAsyncTokens] = useState<TokenizedCode | null>(null);\n  const asyncKeyRef = useRef({ code, language });\n\n  // Invalidate stale async tokens synchronously during render\n  if (asyncKeyRef.current.code !== code || asyncKeyRef.current.language !== language) {\n    asyncKeyRef.current = { code, language };\n    setAsyncTokens(null);\n  }\n\n  useEffect(() => {\n    let cancelled = false;\n\n    highlightCode(code, language, (result) => {\n      if (!cancelled) {\n        setAsyncTokens(result);\n      }\n    });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [code, language]);\n\n  const tokenized = asyncTokens ?? syncTokens;\n\n  return (\n    <div className="relative overflow-auto">\n      <CodeBlockBody showLineNumbers={showLineNumbers} tokenized={tokenized} />\n    </div>\n  );\n};\n\nexport const CodeBlock = ({\n  code,\n  language,\n  showLineNumbers = false,\n  className,\n  children,\n  ...props\n}: CodeBlockProps) => {\n  const contextValue = useMemo(() => ({ code }), [code]);\n\n  return (\n    <CodeBlockContext.Provider value={contextValue}>\n      <CodeBlockContainer className={className} language={language} {...props}>\n        {children}\n        <CodeBlockContent code={code} language={language} showLineNumbers={showLineNumbers} />\n      </CodeBlockContainer>\n    </CodeBlockContext.Provider>\n  );\n};\n\nexport type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n  onCopy?: () => void;\n  onError?: (error: Error) => void;\n  timeout?: number;\n};\n\nexport const CodeBlockCopyButton = ({\n  onCopy,\n  onError,\n  timeout = 2000,\n  children,\n  className,\n  ...props\n}: CodeBlockCopyButtonProps) => {\n  const [isCopied, setIsCopied] = useState(false);\n  const timeoutRef = useRef<number>(0);\n  const { code } = useContext(CodeBlockContext);\n\n  const copyToClipboard = useCallback(async () => {\n    if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {\n      onError?.(new Error("Clipboard API not available"));\n      return;\n    }\n\n    try {\n      if (!isCopied) {\n        await navigator.clipboard.writeText(code);\n        setIsCopied(true);\n        onCopy?.();\n        timeoutRef.current = window.setTimeout(() => setIsCopied(false), timeout);\n      }\n    } catch (error) {\n      onError?.(error as Error);\n    }\n  }, [code, onCopy, onError, timeout, isCopied]);\n\n  useEffect(\n    () => () => {\n      window.clearTimeout(timeoutRef.current);\n    },\n    [],\n  );\n\n  const Icon = isCopied ? CheckIcon : CopyIcon;\n\n  return (\n    <Button\n      className={cn("shrink-0", className)}\n      onClick={copyToClipboard}\n      size="icon"\n      variant="ghost"\n      {...props}\n    >\n      {children ?? <Icon size={14} />}\n    </Button>\n  );\n};\n\nexport type CodeBlockLanguageSelectorProps = ComponentProps<typeof Select>;\n\nexport const CodeBlockLanguageSelector = (props: CodeBlockLanguageSelectorProps) => (\n  <Select {...props} />\n);\n\nexport type CodeBlockLanguageSelectorTriggerProps = ComponentProps<typeof SelectTrigger>;\n\nexport const CodeBlockLanguageSelectorTrigger = ({\n  className,\n  ...props\n}: CodeBlockLanguageSelectorTriggerProps) => (\n  <SelectTrigger\n    className={cn("h-7 border-none bg-transparent px-2 text-xs shadow-none", className)}\n    size="sm"\n    {...props}\n  />\n);\n\nexport type CodeBlockLanguageSelectorValueProps = ComponentProps<typeof SelectValue>;\n\nexport const CodeBlockLanguageSelectorValue = (props: CodeBlockLanguageSelectorValueProps) => (\n  <SelectValue {...props} />\n);\n\nexport type CodeBlockLanguageSelectorContentProps = ComponentProps<typeof SelectContent>;\n\nexport const CodeBlockLanguageSelectorContent = ({\n  align = "end",\n  ...props\n}: CodeBlockLanguageSelectorContentProps) => <SelectContent align={align} {...props} />;\n\nexport type CodeBlockLanguageSelectorItemProps = ComponentProps<typeof SelectItem>;\n\nexport const CodeBlockLanguageSelectorItem = (props: CodeBlockLanguageSelectorItemProps) => (\n  <SelectItem {...props} />\n);\n';
    readonly "components/ai-elements/conversation.tsx": '"use client";\n\nimport { Button } from "@/components/ui/button";\nimport { cn } from "@/lib/utils";\nimport type { UIMessage } from "ai";\nimport { ArrowDownIcon, DownloadIcon } from "lucide-react";\nimport type { ComponentProps } from "react";\nimport { useCallback } from "react";\nimport { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom>;\n\nexport const Conversation = ({ className, ...props }: ConversationProps) => (\n  <StickToBottom\n    className={cn("relative flex-1 overflow-y-hidden", className)}\n    initial="smooth"\n    resize="smooth"\n    role="log"\n    {...props}\n  />\n);\n\nexport type ConversationContentProps = ComponentProps<typeof StickToBottom.Content>;\n\nexport const ConversationContent = ({ className, ...props }: ConversationContentProps) => (\n  <StickToBottom.Content className={cn("flex flex-col gap-8 p-4", className)} {...props} />\n);\n\nexport type ConversationEmptyStateProps = ComponentProps<"div"> & {\n  title?: string;\n  description?: string;\n  icon?: React.ReactNode;\n};\n\nexport const ConversationEmptyState = ({\n  className,\n  title = "No messages yet",\n  description = "Start a conversation to see messages here",\n  icon,\n  children,\n  ...props\n}: ConversationEmptyStateProps) => (\n  <div\n    className={cn(\n      "flex size-full flex-col items-center justify-center gap-3 p-8 text-center",\n      className,\n    )}\n    {...props}\n  >\n    {children ?? (\n      <>\n        {icon && <div className="text-muted-foreground">{icon}</div>}\n        <div className="space-y-1">\n          <h3 className="font-medium text-sm">{title}</h3>\n          {description && <p className="text-muted-foreground text-sm">{description}</p>}\n        </div>\n      </>\n    )}\n  </div>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n  className,\n  ...props\n}: ConversationScrollButtonProps) => {\n  const { isAtBottom, scrollToBottom } = useStickToBottomContext();\n\n  const handleScrollToBottom = useCallback(() => {\n    scrollToBottom();\n  }, [scrollToBottom]);\n\n  return (\n    !isAtBottom && (\n      <Button\n        className={cn(\n          "absolute bottom-4 left-[50%] translate-x-[-50%] rounded-full dark:bg-background dark:hover:bg-muted",\n          className,\n        )}\n        onClick={handleScrollToBottom}\n        size="icon"\n        type="button"\n        variant="outline"\n        {...props}\n      >\n        <ArrowDownIcon className="size-4" />\n      </Button>\n    )\n  );\n};\n\nconst getMessageText = (message: UIMessage): string =>\n  message.parts\n    .filter((part) => part.type === "text")\n    .map((part) => part.text)\n    .join("");\n\nexport type ConversationDownloadProps = Omit<ComponentProps<typeof Button>, "onClick"> & {\n  messages: UIMessage[];\n  filename?: string;\n  formatMessage?: (message: UIMessage, index: number) => string;\n};\n\nconst defaultFormatMessage = (message: UIMessage): string => {\n  const roleLabel = message.role.charAt(0).toUpperCase() + message.role.slice(1);\n  return `**${roleLabel}:** ${getMessageText(message)}`;\n};\n\nexport const messagesToMarkdown = (\n  messages: UIMessage[],\n  formatMessage: (message: UIMessage, index: number) => string = defaultFormatMessage,\n): string => messages.map((msg, i) => formatMessage(msg, i)).join("\\n\\n");\n\nexport const ConversationDownload = ({\n  messages,\n  filename = "conversation.md",\n  formatMessage = defaultFormatMessage,\n  className,\n  children,\n  ...props\n}: ConversationDownloadProps) => {\n  const handleDownload = useCallback(() => {\n    const markdown = messagesToMarkdown(messages, formatMessage);\n    const blob = new Blob([markdown], { type: "text/markdown" });\n    const url = URL.createObjectURL(blob);\n    const link = document.createElement("a");\n    link.href = url;\n    link.download = filename;\n    document.body.append(link);\n    link.click();\n    link.remove();\n    URL.revokeObjectURL(url);\n  }, [messages, filename, formatMessage]);\n\n  return (\n    <Button\n      className={cn(\n        "absolute top-4 right-4 rounded-full dark:bg-background dark:hover:bg-muted",\n        className,\n      )}\n      onClick={handleDownload}\n      size="icon"\n      type="button"\n      variant="outline"\n      {...props}\n    >\n      {children ?? <DownloadIcon className="size-4" />}\n    </Button>\n  );\n};\n';
    readonly "components/ai-elements/message.tsx": '"use client";\n\nimport { Button } from "@/components/ui/button";\nimport { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group";\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport { cjk } from "@streamdown/cjk";\nimport { code } from "@streamdown/code";\nimport { math } from "@streamdown/math";\nimport { mermaid } from "@streamdown/mermaid";\nimport type { UIMessage } from "ai";\nimport { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";\nimport type { ComponentProps, HTMLAttributes, ReactElement } from "react";\nimport { createContext, memo, useCallback, useContext, useEffect, useMemo, useState } from "react";\nimport { Streamdown } from "streamdown";\n\nexport type MessageProps = HTMLAttributes<HTMLDivElement> & {\n  from: UIMessage["role"];\n};\n\nexport const Message = ({ className, from, ...props }: MessageProps) => (\n  <div\n    className={cn(\n      "group flex w-full max-w-[95%] flex-col gap-2",\n      from === "user" ? "is-user ml-auto justify-end" : "is-assistant",\n      className,\n    )}\n    {...props}\n  />\n);\n\nexport type MessageContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageContent = ({ children, className, ...props }: MessageContentProps) => (\n  <div\n    className={cn(\n      "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",\n      "group-[.is-user]:ml-auto group-[.is-user]:rounded-2xl group-[.is-user]:bg-primary group-[.is-user]:px-4 group-[.is-user]:py-2.5 group-[.is-user]:text-primary-foreground",\n      "group-[.is-assistant]:w-full group-[.is-assistant]:text-foreground",\n      "group-data-[optimistic=true]:opacity-70",\n      className,\n    )}\n    {...props}\n  >\n    {children}\n  </div>\n);\n\nexport type MessageActionsProps = ComponentProps<"div">;\n\nexport const MessageActions = ({ className, children, ...props }: MessageActionsProps) => (\n  <div className={cn("flex items-center gap-1", className)} {...props}>\n    {children}\n  </div>\n);\n\nexport type MessageActionProps = ComponentProps<typeof Button> & {\n  tooltip?: string;\n  label?: string;\n};\n\nexport const MessageAction = ({\n  tooltip,\n  children,\n  label,\n  variant = "ghost",\n  size = "icon-sm",\n  ...props\n}: MessageActionProps) => {\n  const button = (\n    <Button size={size} type="button" variant={variant} {...props}>\n      {children}\n      <span className="sr-only">{label || tooltip}</span>\n    </Button>\n  );\n\n  if (tooltip) {\n    return (\n      <TooltipProvider>\n        <Tooltip>\n          <TooltipTrigger asChild>{button}</TooltipTrigger>\n          <TooltipContent>\n            <p>{tooltip}</p>\n          </TooltipContent>\n        </Tooltip>\n      </TooltipProvider>\n    );\n  }\n\n  return button;\n};\n\ninterface MessageBranchContextType {\n  currentBranch: number;\n  totalBranches: number;\n  goToPrevious: () => void;\n  goToNext: () => void;\n  branches: ReactElement[];\n  setBranches: (branches: ReactElement[]) => void;\n}\n\nconst MessageBranchContext = createContext<MessageBranchContextType | null>(null);\n\nconst useMessageBranch = () => {\n  const context = useContext(MessageBranchContext);\n\n  if (!context) {\n    throw new Error("MessageBranch components must be used within MessageBranch");\n  }\n\n  return context;\n};\n\nexport type MessageBranchProps = HTMLAttributes<HTMLDivElement> & {\n  defaultBranch?: number;\n  onBranchChange?: (branchIndex: number) => void;\n};\n\nexport const MessageBranch = ({\n  defaultBranch = 0,\n  onBranchChange,\n  className,\n  ...props\n}: MessageBranchProps) => {\n  const [currentBranch, setCurrentBranch] = useState(defaultBranch);\n  const [branches, setBranches] = useState<ReactElement[]>([]);\n\n  const handleBranchChange = useCallback(\n    (newBranch: number) => {\n      setCurrentBranch(newBranch);\n      onBranchChange?.(newBranch);\n    },\n    [onBranchChange],\n  );\n\n  const goToPrevious = useCallback(() => {\n    const newBranch = currentBranch > 0 ? currentBranch - 1 : branches.length - 1;\n    handleBranchChange(newBranch);\n  }, [currentBranch, branches.length, handleBranchChange]);\n\n  const goToNext = useCallback(() => {\n    const newBranch = currentBranch < branches.length - 1 ? currentBranch + 1 : 0;\n    handleBranchChange(newBranch);\n  }, [currentBranch, branches.length, handleBranchChange]);\n\n  const contextValue = useMemo<MessageBranchContextType>(\n    () => ({\n      branches,\n      currentBranch,\n      goToNext,\n      goToPrevious,\n      setBranches,\n      totalBranches: branches.length,\n    }),\n    [branches, currentBranch, goToNext, goToPrevious],\n  );\n\n  return (\n    <MessageBranchContext.Provider value={contextValue}>\n      <div className={cn("grid w-full gap-2 [&>div]:pb-0", className)} {...props} />\n    </MessageBranchContext.Provider>\n  );\n};\n\nexport type MessageBranchContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageBranchContent = ({ children, ...props }: MessageBranchContentProps) => {\n  const { currentBranch, setBranches, branches } = useMessageBranch();\n  const childrenArray = useMemo(\n    () => (Array.isArray(children) ? children : [children]),\n    [children],\n  );\n\n  // Use useEffect to update branches when they change\n  useEffect(() => {\n    if (branches.length !== childrenArray.length) {\n      setBranches(childrenArray);\n    }\n  }, [childrenArray, branches, setBranches]);\n\n  return childrenArray.map((branch, index) => (\n    <div\n      className={cn(\n        "grid gap-2 overflow-hidden [&>div]:pb-0",\n        index === currentBranch ? "block" : "hidden",\n      )}\n      key={branch.key}\n      {...props}\n    >\n      {branch}\n    </div>\n  ));\n};\n\nexport type MessageBranchSelectorProps = ComponentProps<typeof ButtonGroup>;\n\nexport const MessageBranchSelector = ({ className, ...props }: MessageBranchSelectorProps) => {\n  const { totalBranches } = useMessageBranch();\n\n  // Don\'t render if there\'s only one branch\n  if (totalBranches <= 1) {\n    return null;\n  }\n\n  return (\n    <ButtonGroup\n      className={cn(\n        "[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md",\n        className,\n      )}\n      orientation="horizontal"\n      {...props}\n    />\n  );\n};\n\nexport type MessageBranchPreviousProps = ComponentProps<typeof Button>;\n\nexport const MessageBranchPrevious = ({ children, ...props }: MessageBranchPreviousProps) => {\n  const { goToPrevious, totalBranches } = useMessageBranch();\n\n  return (\n    <Button\n      aria-label="Previous branch"\n      disabled={totalBranches <= 1}\n      onClick={goToPrevious}\n      size="icon-sm"\n      type="button"\n      variant="ghost"\n      {...props}\n    >\n      {children ?? <ChevronLeftIcon size={14} />}\n    </Button>\n  );\n};\n\nexport type MessageBranchNextProps = ComponentProps<typeof Button>;\n\nexport const MessageBranchNext = ({ children, ...props }: MessageBranchNextProps) => {\n  const { goToNext, totalBranches } = useMessageBranch();\n\n  return (\n    <Button\n      aria-label="Next branch"\n      disabled={totalBranches <= 1}\n      onClick={goToNext}\n      size="icon-sm"\n      type="button"\n      variant="ghost"\n      {...props}\n    >\n      {children ?? <ChevronRightIcon size={14} />}\n    </Button>\n  );\n};\n\nexport type MessageBranchPageProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const MessageBranchPage = ({ className, ...props }: MessageBranchPageProps) => {\n  const { currentBranch, totalBranches } = useMessageBranch();\n\n  return (\n    <ButtonGroupText\n      className={cn("border-none bg-transparent text-muted-foreground shadow-none", className)}\n      {...props}\n    >\n      {currentBranch + 1} of {totalBranches}\n    </ButtonGroupText>\n  );\n};\n\nexport type MessageResponseProps = ComponentProps<typeof Streamdown>;\n\nconst streamdownPlugins = { cjk, code, math, mermaid };\n\nexport const MessageResponse = memo(\n  ({ className, ...props }: MessageResponseProps) => (\n    <Streamdown\n      className={cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className)}\n      plugins={streamdownPlugins}\n      {...props}\n    />\n  ),\n  (prevProps, nextProps) =>\n    prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating,\n);\n\nMessageResponse.displayName = "MessageResponse";\n\nexport type MessageToolbarProps = ComponentProps<"div">;\n\nexport const MessageToolbar = ({ className, children, ...props }: MessageToolbarProps) => (\n  <div className={cn("mt-4 flex w-full items-center justify-between gap-4", className)} {...props}>\n    {children}\n  </div>\n);\n';
    readonly "components/ai-elements/prompt-input.tsx": '"use client";\n\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n  CommandSeparator,\n} from "@/components/ui/command";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from "@/components/ui/dropdown-menu";\nimport { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupTextarea,\n} from "@/components/ui/input-group";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from "@/components/ui/select";\nimport { Spinner } from "@/components/ui/spinner";\nimport { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";\nimport { cn } from "@/lib/utils";\nimport type { ChatStatus, FileUIPart, SourceDocumentUIPart } from "ai";\nimport { ArrowUpIcon, ImageIcon, Monitor, PlusIcon, SquareIcon, XIcon } from "lucide-react";\nimport { nanoid } from "nanoid";\nimport type {\n  ChangeEvent,\n  ChangeEventHandler,\n  ClipboardEventHandler,\n  ComponentProps,\n  FormEvent,\n  FormEventHandler,\n  HTMLAttributes,\n  KeyboardEventHandler,\n  PropsWithChildren,\n  ReactNode,\n  RefObject,\n} from "react";\nimport {\n  Children,\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from "react";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {\n  try {\n    const response = await fetch(url);\n    const blob = await response.blob();\n    // FileReader uses callback-based API, wrapping in Promise is necessary\n    // oxlint-disable-next-line eslint-plugin-promise(avoid-new)\n    return new Promise((resolve) => {\n      const reader = new FileReader();\n      // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener)\n      reader.onloadend = () => resolve(reader.result as string);\n      // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener)\n      reader.onerror = () => resolve(null);\n      reader.readAsDataURL(blob);\n    });\n  } catch {\n    return null;\n  }\n};\n\nconst captureScreenshot = async (): Promise<File | null> => {\n  if (typeof navigator === "undefined" || !navigator.mediaDevices?.getDisplayMedia) {\n    return null;\n  }\n\n  let stream: MediaStream | null = null;\n  const video = document.createElement("video");\n  video.muted = true;\n  video.playsInline = true;\n\n  try {\n    stream = await navigator.mediaDevices.getDisplayMedia({\n      audio: false,\n      video: true,\n    });\n\n    video.srcObject = stream;\n\n    // Video element uses callback-based API, wrapping in Promise is necessary\n    // oxlint-disable-next-line eslint-plugin-promise(avoid-new)\n    await new Promise<void>((resolve, reject) => {\n      // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener)\n      video.onloadedmetadata = () => resolve();\n      // oxlint-disable-next-line eslint-plugin-unicorn(prefer-add-event-listener)\n      video.onerror = () => reject(new Error("Failed to load screen stream"));\n    });\n\n    await video.play();\n\n    const width = video.videoWidth;\n    const height = video.videoHeight;\n    if (!width || !height) {\n      return null;\n    }\n\n    const canvas = document.createElement("canvas");\n    canvas.width = width;\n    canvas.height = height;\n    const context = canvas.getContext("2d");\n    if (!context) {\n      return null;\n    }\n\n    context.drawImage(video, 0, 0, width, height);\n    // canvas.toBlob uses callback-based API, wrapping in Promise is necessary\n    // oxlint-disable-next-line eslint-plugin-promise(avoid-new)\n    const blob = await new Promise<Blob | null>((resolve) => {\n      canvas.toBlob(resolve, "image/png");\n    });\n    if (!blob) {\n      return null;\n    }\n\n    const timestamp = new Date()\n      .toISOString()\n      .replaceAll(/[:.]/g, "-")\n      .replace("T", "_")\n      .replace("Z", "");\n\n    return new File([blob], `screenshot-${timestamp}.png`, {\n      lastModified: Date.now(),\n      type: "image/png",\n    });\n  } finally {\n    if (stream) {\n      for (const track of stream.getTracks()) {\n        track.stop();\n      }\n    }\n    video.pause();\n    video.srcObject = null;\n  }\n};\n\n// ============================================================================\n// Provider Context & Types\n// ============================================================================\n\nexport interface AttachmentsContext {\n  files: (FileUIPart & { id: string })[];\n  add: (files: File[] | FileList) => void;\n  remove: (id: string) => void;\n  clear: () => void;\n  openFileDialog: () => void;\n  fileInputRef: RefObject<HTMLInputElement | null>;\n}\n\nexport interface TextInputContext {\n  value: string;\n  setInput: (v: string) => void;\n  clear: () => void;\n}\n\nexport interface PromptInputControllerProps {\n  textInput: TextInputContext;\n  attachments: AttachmentsContext;\n  /** INTERNAL: Allows PromptInput to register its file textInput + "open" callback */\n  __registerFileInput: (ref: RefObject<HTMLInputElement | null>, open: () => void) => void;\n}\n\nconst PromptInputController = createContext<PromptInputControllerProps | null>(null);\nconst ProviderAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputController = () => {\n  const ctx = useContext(PromptInputController);\n  if (!ctx) {\n    throw new Error(\n      "Wrap your component inside <PromptInputProvider> to use usePromptInputController().",\n    );\n  }\n  return ctx;\n};\n\n// Optional variants (do NOT throw). Useful for dual-mode components.\nconst useOptionalPromptInputController = () => useContext(PromptInputController);\n\nexport const useProviderAttachments = () => {\n  const ctx = useContext(ProviderAttachmentsContext);\n  if (!ctx) {\n    throw new Error(\n      "Wrap your component inside <PromptInputProvider> to use useProviderAttachments().",\n    );\n  }\n  return ctx;\n};\n\nconst useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);\n\nexport type PromptInputProviderProps = PropsWithChildren<{\n  initialInput?: string;\n}>;\n\n/**\n * Optional global provider that lifts PromptInput state outside of PromptInput.\n * If you don\'t use it, PromptInput stays fully self-managed.\n */\nexport const PromptInputProvider = ({\n  initialInput: initialTextInput = "",\n  children,\n}: PromptInputProviderProps) => {\n  // ----- textInput state\n  const [textInput, setTextInput] = useState(initialTextInput);\n  const clearInput = useCallback(() => setTextInput(""), []);\n\n  // ----- attachments state (global when wrapped)\n  const [attachmentFiles, setAttachmentFiles] = useState<(FileUIPart & { id: string })[]>([]);\n  const fileInputRef = useRef<HTMLInputElement | null>(null);\n  // oxlint-disable-next-line eslint(no-empty-function)\n  const openRef = useRef<() => void>(() => {});\n\n  const add = useCallback((files: File[] | FileList) => {\n    const incoming = [...files];\n    if (incoming.length === 0) {\n      return;\n    }\n\n    setAttachmentFiles((prev) => [\n      ...prev,\n      ...incoming.map((file) => ({\n        filename: file.name,\n        id: nanoid(),\n        mediaType: file.type,\n        type: "file" as const,\n        url: URL.createObjectURL(file),\n      })),\n    ]);\n  }, []);\n\n  const remove = useCallback((id: string) => {\n    setAttachmentFiles((prev) => {\n      const found = prev.find((f) => f.id === id);\n      if (found?.url) {\n        URL.revokeObjectURL(found.url);\n      }\n      return prev.filter((f) => f.id !== id);\n    });\n  }, []);\n\n  const clear = useCallback(() => {\n    setAttachmentFiles((prev) => {\n      for (const f of prev) {\n        if (f.url) {\n          URL.revokeObjectURL(f.url);\n        }\n      }\n      return [];\n    });\n  }, []);\n\n  // Keep a ref to attachments for cleanup on unmount (avoids stale closure)\n  const attachmentsRef = useRef(attachmentFiles);\n\n  useEffect(() => {\n    attachmentsRef.current = attachmentFiles;\n  }, [attachmentFiles]);\n\n  // Cleanup blob URLs on unmount to prevent memory leaks\n  useEffect(\n    () => () => {\n      for (const f of attachmentsRef.current) {\n        if (f.url) {\n          URL.revokeObjectURL(f.url);\n        }\n      }\n    },\n    [],\n  );\n\n  const openFileDialog = useCallback(() => {\n    openRef.current?.();\n  }, []);\n\n  const attachments = useMemo<AttachmentsContext>(\n    () => ({\n      add,\n      clear,\n      fileInputRef,\n      files: attachmentFiles,\n      openFileDialog,\n      remove,\n    }),\n    [attachmentFiles, add, remove, clear, openFileDialog],\n  );\n\n  const __registerFileInput = useCallback(\n    (ref: RefObject<HTMLInputElement | null>, open: () => void) => {\n      fileInputRef.current = ref.current;\n      openRef.current = open;\n    },\n    [],\n  );\n\n  const controller = useMemo<PromptInputControllerProps>(\n    () => ({\n      __registerFileInput,\n      attachments,\n      textInput: {\n        clear: clearInput,\n        setInput: setTextInput,\n        value: textInput,\n      },\n    }),\n    [textInput, clearInput, attachments, __registerFileInput],\n  );\n\n  return (\n    <PromptInputController.Provider value={controller}>\n      <ProviderAttachmentsContext.Provider value={attachments}>\n        {children}\n      </ProviderAttachmentsContext.Provider>\n    </PromptInputController.Provider>\n  );\n};\n\n// ============================================================================\n// Component Context & Hooks\n// ============================================================================\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n  // Prefer local context (inside PromptInput) as it has validation, fall back to provider\n  const provider = useOptionalProviderAttachments();\n  const local = useContext(LocalAttachmentsContext);\n  const context = local ?? provider;\n  if (!context) {\n    throw new Error(\n      "usePromptInputAttachments must be used within a PromptInput or PromptInputProvider",\n    );\n  }\n  return context;\n};\n\n// ============================================================================\n// Referenced Sources (Local to PromptInput)\n// ============================================================================\n\nexport interface ReferencedSourcesContext {\n  sources: (SourceDocumentUIPart & { id: string })[];\n  add: (sources: SourceDocumentUIPart[] | SourceDocumentUIPart) => void;\n  remove: (id: string) => void;\n  clear: () => void;\n}\n\nexport const LocalReferencedSourcesContext = createContext<ReferencedSourcesContext | null>(null);\n\nexport const usePromptInputReferencedSources = () => {\n  const ctx = useContext(LocalReferencedSourcesContext);\n  if (!ctx) {\n    throw new Error(\n      "usePromptInputReferencedSources must be used within a LocalReferencedSourcesContext.Provider",\n    );\n  }\n  return ctx;\n};\n\nexport type PromptInputActionAddAttachmentsProps = ComponentProps<typeof DropdownMenuItem> & {\n  label?: string;\n};\n\nexport const PromptInputActionAddAttachments = ({\n  label = "Add photos or files",\n  ...props\n}: PromptInputActionAddAttachmentsProps) => {\n  const attachments = usePromptInputAttachments();\n\n  const handleSelect = useCallback(\n    (e: Event) => {\n      e.preventDefault();\n      attachments.openFileDialog();\n    },\n    [attachments],\n  );\n\n  return (\n    <DropdownMenuItem {...props} onSelect={handleSelect}>\n      <ImageIcon className="mr-2 size-4" /> {label}\n    </DropdownMenuItem>\n  );\n};\n\nexport type PromptInputActionAddScreenshotProps = ComponentProps<typeof DropdownMenuItem> & {\n  label?: string;\n};\n\nexport const PromptInputActionAddScreenshot = ({\n  label = "Take screenshot",\n  onSelect,\n  ...props\n}: PromptInputActionAddScreenshotProps) => {\n  const attachments = usePromptInputAttachments();\n\n  const handleSelect = useCallback(\n    async (event: Event) => {\n      onSelect?.(event);\n      if (event.defaultPrevented) {\n        return;\n      }\n\n      try {\n        const screenshot = await captureScreenshot();\n        if (screenshot) {\n          attachments.add([screenshot]);\n        }\n      } catch (error) {\n        if (\n          error instanceof DOMException &&\n          (error.name === "NotAllowedError" || error.name === "AbortError")\n        ) {\n          return;\n        }\n        throw error;\n      }\n    },\n    [onSelect, attachments],\n  );\n\n  return (\n    <DropdownMenuItem {...props} onSelect={handleSelect}>\n      <Monitor className="mr-2 size-4" />\n      {label}\n    </DropdownMenuItem>\n  );\n};\n\nexport interface PromptInputMessage {\n  text: string;\n  files: FileUIPart[];\n}\n\nexport type PromptInputProps = Omit<HTMLAttributes<HTMLFormElement>, "onSubmit" | "onError"> & {\n  // e.g., "image/*" or leave undefined for any\n  accept?: string;\n  multiple?: boolean;\n  // When true, accepts drops anywhere on document. Default false (opt-in).\n  globalDrop?: boolean;\n  // Render a hidden input with given name and keep it in sync for native form posts. Default false.\n  syncHiddenInput?: boolean;\n  // Minimal constraints\n  maxFiles?: number;\n  // bytes\n  maxFileSize?: number;\n  onError?: (err: { code: "max_files" | "max_file_size" | "accept"; message: string }) => void;\n  onSubmit: (\n    message: PromptInputMessage,\n    event: FormEvent<HTMLFormElement>,\n  ) => void | Promise<void>;\n};\n\nexport const PromptInput = ({\n  className,\n  accept,\n  multiple,\n  globalDrop,\n  syncHiddenInput,\n  maxFiles,\n  maxFileSize,\n  onError,\n  onSubmit,\n  children,\n  ...props\n}: PromptInputProps) => {\n  // Try to use a provider controller if present\n  const controller = useOptionalPromptInputController();\n  const usingProvider = !!controller;\n\n  // Refs\n  const inputRef = useRef<HTMLInputElement | null>(null);\n  const formRef = useRef<HTMLFormElement | null>(null);\n\n  // ----- Local attachments (only used when no provider)\n  const [items, setItems] = useState<(FileUIPart & { id: string })[]>([]);\n  const files = usingProvider ? controller.attachments.files : items;\n\n  // ----- Local referenced sources (always local to PromptInput)\n  const [referencedSources, setReferencedSources] = useState<\n    (SourceDocumentUIPart & { id: string })[]\n  >([]);\n\n  // Keep a ref to files for cleanup on unmount (avoids stale closure)\n  const filesRef = useRef(files);\n\n  useEffect(() => {\n    filesRef.current = files;\n  }, [files]);\n\n  const openFileDialogLocal = useCallback(() => {\n    inputRef.current?.click();\n  }, []);\n\n  const matchesAccept = useCallback(\n    (f: File) => {\n      if (!accept || accept.trim() === "") {\n        return true;\n      }\n\n      const patterns = accept\n        .split(",")\n        .map((s) => s.trim())\n        .filter(Boolean);\n\n      return patterns.some((pattern) => {\n        if (pattern.endsWith("/*")) {\n          // e.g: image/* -> image/\n          const prefix = pattern.slice(0, -1);\n          return f.type.startsWith(prefix);\n        }\n        return f.type === pattern;\n      });\n    },\n    [accept],\n  );\n\n  const addLocal = useCallback(\n    (fileList: File[] | FileList) => {\n      const incoming = [...fileList];\n      const accepted = incoming.filter((f) => matchesAccept(f));\n      if (incoming.length && accepted.length === 0) {\n        onError?.({\n          code: "accept",\n          message: "No files match the accepted types.",\n        });\n        return;\n      }\n      const withinSize = (f: File) => (maxFileSize ? f.size <= maxFileSize : true);\n      const sized = accepted.filter(withinSize);\n      if (accepted.length > 0 && sized.length === 0) {\n        onError?.({\n          code: "max_file_size",\n          message: "All files exceed the maximum size.",\n        });\n        return;\n      }\n\n      setItems((prev) => {\n        const capacity =\n          typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : undefined;\n        const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;\n        if (typeof capacity === "number" && sized.length > capacity) {\n          onError?.({\n            code: "max_files",\n            message: "Too many files. Some were not added.",\n          });\n        }\n        const next: (FileUIPart & { id: string })[] = [];\n        for (const file of capped) {\n          next.push({\n            filename: file.name,\n            id: nanoid(),\n            mediaType: file.type,\n            type: "file",\n            url: URL.createObjectURL(file),\n          });\n        }\n        return [...prev, ...next];\n      });\n    },\n    [matchesAccept, maxFiles, maxFileSize, onError],\n  );\n\n  const removeLocal = useCallback(\n    (id: string) =>\n      setItems((prev) => {\n        const found = prev.find((file) => file.id === id);\n        if (found?.url) {\n          URL.revokeObjectURL(found.url);\n        }\n        return prev.filter((file) => file.id !== id);\n      }),\n    [],\n  );\n\n  // Wrapper that validates files before calling provider\'s add\n  const addWithProviderValidation = useCallback(\n    (fileList: File[] | FileList) => {\n      const incoming = [...fileList];\n      const accepted = incoming.filter((f) => matchesAccept(f));\n      if (incoming.length && accepted.length === 0) {\n        onError?.({\n          code: "accept",\n          message: "No files match the accepted types.",\n        });\n        return;\n      }\n      const withinSize = (f: File) => (maxFileSize ? f.size <= maxFileSize : true);\n      const sized = accepted.filter(withinSize);\n      if (accepted.length > 0 && sized.length === 0) {\n        onError?.({\n          code: "max_file_size",\n          message: "All files exceed the maximum size.",\n        });\n        return;\n      }\n\n      const currentCount = files.length;\n      const capacity =\n        typeof maxFiles === "number" ? Math.max(0, maxFiles - currentCount) : undefined;\n      const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized;\n      if (typeof capacity === "number" && sized.length > capacity) {\n        onError?.({\n          code: "max_files",\n          message: "Too many files. Some were not added.",\n        });\n      }\n\n      if (capped.length > 0) {\n        controller?.attachments.add(capped);\n      }\n    },\n    [matchesAccept, maxFileSize, maxFiles, onError, files.length, controller],\n  );\n\n  const clearAttachments = useCallback(\n    () =>\n      usingProvider\n        ? controller?.attachments.clear()\n        : setItems((prev) => {\n            for (const file of prev) {\n              if (file.url) {\n                URL.revokeObjectURL(file.url);\n              }\n            }\n            return [];\n          }),\n    [usingProvider, controller],\n  );\n\n  const clearReferencedSources = useCallback(() => setReferencedSources([]), []);\n\n  const add = usingProvider ? addWithProviderValidation : addLocal;\n  const remove = usingProvider ? controller.attachments.remove : removeLocal;\n  const openFileDialog = usingProvider\n    ? controller.attachments.openFileDialog\n    : openFileDialogLocal;\n\n  const clear = useCallback(() => {\n    clearAttachments();\n    clearReferencedSources();\n  }, [clearAttachments, clearReferencedSources]);\n\n  // Let provider know about our hidden file input so external menus can call openFileDialog()\n  useEffect(() => {\n    if (!usingProvider) {\n      return;\n    }\n    controller.__registerFileInput(inputRef, () => inputRef.current?.click());\n  }, [usingProvider, controller]);\n\n  // Note: File input cannot be programmatically set for security reasons\n  // The syncHiddenInput prop is no longer functional\n  useEffect(() => {\n    if (syncHiddenInput && inputRef.current && files.length === 0) {\n      inputRef.current.value = "";\n    }\n  }, [files, syncHiddenInput]);\n\n  // Attach drop handlers on nearest form and document (opt-in)\n  useEffect(() => {\n    const form = formRef.current;\n    if (!form) {\n      return;\n    }\n    if (globalDrop) {\n      // when global drop is on, let the document-level handler own drops\n      return;\n    }\n\n    const onDragOver = (e: DragEvent) => {\n      if (e.dataTransfer?.types?.includes("Files")) {\n        e.preventDefault();\n      }\n    };\n    const onDrop = (e: DragEvent) => {\n      if (e.dataTransfer?.types?.includes("Files")) {\n        e.preventDefault();\n      }\n      if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n        add(e.dataTransfer.files);\n      }\n    };\n    form.addEventListener("dragover", onDragOver);\n    form.addEventListener("drop", onDrop);\n    return () => {\n      form.removeEventListener("dragover", onDragOver);\n      form.removeEventListener("drop", onDrop);\n    };\n  }, [add, globalDrop]);\n\n  useEffect(() => {\n    if (!globalDrop) {\n      return;\n    }\n\n    const onDragOver = (e: DragEvent) => {\n      if (e.dataTransfer?.types?.includes("Files")) {\n        e.preventDefault();\n      }\n    };\n    const onDrop = (e: DragEvent) => {\n      if (e.dataTransfer?.types?.includes("Files")) {\n        e.preventDefault();\n      }\n      if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n        add(e.dataTransfer.files);\n      }\n    };\n    document.addEventListener("dragover", onDragOver);\n    document.addEventListener("drop", onDrop);\n    return () => {\n      document.removeEventListener("dragover", onDragOver);\n      document.removeEventListener("drop", onDrop);\n    };\n  }, [add, globalDrop]);\n\n  useEffect(\n    () => () => {\n      if (!usingProvider) {\n        for (const f of filesRef.current) {\n          if (f.url) {\n            URL.revokeObjectURL(f.url);\n          }\n        }\n      }\n    },\n    [usingProvider],\n  );\n\n  const handleChange: ChangeEventHandler<HTMLInputElement> = useCallback(\n    (event) => {\n      if (event.currentTarget.files) {\n        add(event.currentTarget.files);\n      }\n      // Reset input value to allow selecting files that were previously removed\n      event.currentTarget.value = "";\n    },\n    [add],\n  );\n\n  const attachmentsCtx = useMemo<AttachmentsContext>(\n    () => ({\n      add,\n      clear: clearAttachments,\n      fileInputRef: inputRef,\n      files: files.map((item) => ({ ...item, id: item.id })),\n      openFileDialog,\n      remove,\n    }),\n    [files, add, remove, clearAttachments, openFileDialog],\n  );\n\n  const refsCtx = useMemo<ReferencedSourcesContext>(\n    () => ({\n      add: (incoming: SourceDocumentUIPart[] | SourceDocumentUIPart) => {\n        const array = Array.isArray(incoming) ? incoming : [incoming];\n        setReferencedSources((prev) => [...prev, ...array.map((s) => ({ ...s, id: nanoid() }))]);\n      },\n      clear: clearReferencedSources,\n      remove: (id: string) => {\n        setReferencedSources((prev) => prev.filter((s) => s.id !== id));\n      },\n      sources: referencedSources,\n    }),\n    [referencedSources, clearReferencedSources],\n  );\n\n  const handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n    async (event) => {\n      event.preventDefault();\n\n      const form = event.currentTarget;\n      const text = usingProvider\n        ? controller.textInput.value\n        : (() => {\n            const formData = new FormData(form);\n            return (formData.get("message") as string) || "";\n          })();\n\n      // Reset form immediately after capturing text to avoid race condition\n      // where user input during async blob conversion would be lost\n      if (!usingProvider) {\n        form.reset();\n      }\n\n      try {\n        // Convert blob URLs to data URLs asynchronously\n        const convertedFiles: FileUIPart[] = await Promise.all(\n          files.map(async ({ id: _id, ...item }) => {\n            if (item.url?.startsWith("blob:")) {\n              const dataUrl = await convertBlobUrlToDataUrl(item.url);\n              // If conversion failed, keep the original blob URL\n              return {\n                ...item,\n                url: dataUrl ?? item.url,\n              };\n            }\n            return item;\n          }),\n        );\n\n        const result = onSubmit({ files: convertedFiles, text }, event);\n\n        // Handle both sync and async onSubmit\n        if (result instanceof Promise) {\n          try {\n            await result;\n            clear();\n            if (usingProvider) {\n              controller.textInput.clear();\n            }\n          } catch {\n            // Don\'t clear on error - user may want to retry\n          }\n        } else {\n          // Sync function completed without throwing, clear inputs\n          clear();\n          if (usingProvider) {\n            controller.textInput.clear();\n          }\n        }\n      } catch {\n        // Don\'t clear on error - user may want to retry\n      }\n    },\n    [usingProvider, controller, files, onSubmit, clear],\n  );\n\n  // Render with or without local provider\n  const inner = (\n    <>\n      <input\n        accept={accept}\n        aria-label="Upload files"\n        className="hidden"\n        multiple={multiple}\n        onChange={handleChange}\n        ref={inputRef}\n        title="Upload files"\n        type="file"\n      />\n      <form className="w-full" onSubmit={handleSubmit} ref={formRef} {...props}>\n        <InputGroup\n          className={cn(\n            "overflow-hidden rounded-2xl bg-card shadow-sm",\n            "focus-within:border-foreground has-[[data-slot=input-group-control]:focus-visible]:border-foreground",\n            className,\n          )}\n        >\n          {children}\n        </InputGroup>\n      </form>\n    </>\n  );\n\n  const withReferencedSources = (\n    <LocalReferencedSourcesContext.Provider value={refsCtx}>\n      {inner}\n    </LocalReferencedSourcesContext.Provider>\n  );\n\n  // Always provide LocalAttachmentsContext so children get validated add function\n  return (\n    <LocalAttachmentsContext.Provider value={attachmentsCtx}>\n      {withReferencedSources}\n    </LocalAttachmentsContext.Provider>\n  );\n};\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({ className, ...props }: PromptInputBodyProps) => (\n  <div className={cn("contents", className)} {...props} />\n);\n\nexport type PromptInputTextareaProps = ComponentProps<typeof InputGroupTextarea>;\n\nexport const PromptInputTextarea = ({\n  onChange,\n  onKeyDown,\n  className,\n  placeholder = "What would you like to know?",\n  ...props\n}: PromptInputTextareaProps) => {\n  const controller = useOptionalPromptInputController();\n  const attachments = usePromptInputAttachments();\n  const [isComposing, setIsComposing] = useState(false);\n\n  const handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n    (e) => {\n      // Call the external onKeyDown handler first\n      onKeyDown?.(e);\n\n      // If the external handler prevented default, don\'t run internal logic\n      if (e.defaultPrevented) {\n        return;\n      }\n\n      if (e.key === "Enter") {\n        if (isComposing || e.nativeEvent.isComposing) {\n          return;\n        }\n        if (e.shiftKey) {\n          return;\n        }\n        e.preventDefault();\n\n        // Check if the submit button is disabled before submitting\n        const { form } = e.currentTarget;\n        const submitButton = form?.querySelector(\n          \'button[type="submit"]\',\n        ) as HTMLButtonElement | null;\n        if (submitButton?.disabled) {\n          return;\n        }\n\n        form?.requestSubmit();\n      }\n\n      // Remove last attachment when Backspace is pressed and textarea is empty\n      if (e.key === "Backspace" && e.currentTarget.value === "" && attachments.files.length > 0) {\n        e.preventDefault();\n        const lastAttachment = attachments.files.at(-1);\n        if (lastAttachment) {\n          attachments.remove(lastAttachment.id);\n        }\n      }\n    },\n    [onKeyDown, isComposing, attachments],\n  );\n\n  const handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback(\n    (event) => {\n      const items = event.clipboardData?.items;\n\n      if (!items) {\n        return;\n      }\n\n      const files: File[] = [];\n\n      for (const item of items) {\n        if (item.kind === "file") {\n          const file = item.getAsFile();\n          if (file) {\n            files.push(file);\n          }\n        }\n      }\n\n      if (files.length > 0) {\n        event.preventDefault();\n        attachments.add(files);\n      }\n    },\n    [attachments],\n  );\n\n  const handleCompositionEnd = useCallback(() => setIsComposing(false), []);\n  const handleCompositionStart = useCallback(() => setIsComposing(true), []);\n\n  const controlledProps = controller\n    ? {\n        onChange: (e: ChangeEvent<HTMLTextAreaElement>) => {\n          controller.textInput.setInput(e.currentTarget.value);\n          onChange?.(e);\n        },\n        value: controller.textInput.value,\n      }\n    : {\n        onChange,\n      };\n\n  return (\n    <InputGroupTextarea\n      className={cn("field-sizing-content max-h-48 min-h-18", className)}\n      name="message"\n      onCompositionEnd={handleCompositionEnd}\n      onCompositionStart={handleCompositionStart}\n      onKeyDown={handleKeyDown}\n      onPaste={handlePaste}\n      placeholder={placeholder}\n      {...props}\n      {...controlledProps}\n    />\n  );\n};\n\nexport type PromptInputHeaderProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;\n\nexport const PromptInputHeader = ({ className, ...props }: PromptInputHeaderProps) => (\n  <InputGroupAddon\n    align="block-end"\n    className={cn("order-first flex-wrap gap-1", className)}\n    {...props}\n  />\n);\n\nexport type PromptInputFooterProps = Omit<ComponentProps<typeof InputGroupAddon>, "align">;\n\nexport const PromptInputFooter = ({ className, ...props }: PromptInputFooterProps) => (\n  <InputGroupAddon\n    align="block-end"\n    className={cn("justify-between gap-1", className)}\n    {...props}\n  />\n);\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({ className, ...props }: PromptInputToolsProps) => (\n  <div className={cn("flex min-w-0 items-center gap-1", className)} {...props} />\n);\n\nexport type PromptInputButtonTooltip =\n  | string\n  | {\n      content: ReactNode;\n      shortcut?: string;\n      side?: ComponentProps<typeof TooltipContent>["side"];\n    };\n\nexport type PromptInputButtonProps = ComponentProps<typeof InputGroupButton> & {\n  tooltip?: PromptInputButtonTooltip;\n};\n\nexport const PromptInputButton = ({\n  variant = "ghost",\n  className,\n  size,\n  tooltip,\n  ...props\n}: PromptInputButtonProps) => {\n  const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");\n\n  const button = (\n    <InputGroupButton\n      className={cn(className)}\n      size={newSize}\n      type="button"\n      variant={variant}\n      {...props}\n    />\n  );\n\n  if (!tooltip) {\n    return button;\n  }\n\n  const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;\n  const shortcut = typeof tooltip === "string" ? undefined : tooltip.shortcut;\n  const side = typeof tooltip === "string" ? "top" : (tooltip.side ?? "top");\n\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>{button}</TooltipTrigger>\n      <TooltipContent side={side}>\n        {tooltipContent}\n        {shortcut && <span className="ml-2 text-muted-foreground">{shortcut}</span>}\n      </TooltipContent>\n    </Tooltip>\n  );\n};\n\nexport type PromptInputActionMenuProps = ComponentProps<typeof DropdownMenu>;\nexport const PromptInputActionMenu = (props: PromptInputActionMenuProps) => (\n  <DropdownMenu {...props} />\n);\n\nexport type PromptInputActionMenuTriggerProps = PromptInputButtonProps;\n\nexport const PromptInputActionMenuTrigger = ({\n  className,\n  children,\n  ...props\n}: PromptInputActionMenuTriggerProps) => (\n  <DropdownMenuTrigger asChild>\n    <PromptInputButton className={className} {...props}>\n      {children ?? <PlusIcon className="size-4" />}\n    </PromptInputButton>\n  </DropdownMenuTrigger>\n);\n\nexport type PromptInputActionMenuContentProps = ComponentProps<typeof DropdownMenuContent>;\nexport const PromptInputActionMenuContent = ({\n  className,\n  ...props\n}: PromptInputActionMenuContentProps) => (\n  <DropdownMenuContent align="start" className={cn(className)} {...props} />\n);\n\nexport type PromptInputActionMenuItemProps = ComponentProps<typeof DropdownMenuItem>;\nexport const PromptInputActionMenuItem = ({\n  className,\n  ...props\n}: PromptInputActionMenuItemProps) => <DropdownMenuItem className={cn(className)} {...props} />;\n\n// Note: Actions that perform side-effects (like opening a file dialog)\n// are provided in opt-in modules (e.g., prompt-input-attachments).\n\nexport type PromptInputSubmitProps = ComponentProps<typeof InputGroupButton> & {\n  status?: ChatStatus;\n  onStop?: () => void;\n};\n\nexport const PromptInputSubmit = ({\n  className,\n  variant = "default",\n  size = "icon-sm",\n  status,\n  onStop,\n  onClick,\n  children,\n  ...props\n}: PromptInputSubmitProps) => {\n  const isGenerating = status === "submitted" || status === "streaming";\n\n  let Icon = <ArrowUpIcon className="size-4" />;\n\n  if (status === "submitted") {\n    Icon = <Spinner />;\n  } else if (status === "streaming") {\n    Icon = <SquareIcon className="size-4" />;\n  } else if (status === "error") {\n    Icon = <XIcon className="size-4" />;\n  }\n\n  const handleClick = useCallback(\n    (e: React.MouseEvent<HTMLButtonElement>) => {\n      if (isGenerating && onStop) {\n        e.preventDefault();\n        onStop();\n        return;\n      }\n      onClick?.(e);\n    },\n    [isGenerating, onStop, onClick],\n  );\n\n  return (\n    <InputGroupButton\n      aria-label={isGenerating ? "Stop" : "Submit"}\n      className={cn("absolute right-2.5 bottom-2.5 rounded-full", className)}\n      onClick={handleClick}\n      size={size}\n      type={isGenerating && onStop ? "button" : "submit"}\n      variant={variant}\n      {...props}\n    >\n      {children ?? Icon}\n    </InputGroupButton>\n  );\n};\n\nexport type PromptInputSelectProps = ComponentProps<typeof Select>;\n\nexport const PromptInputSelect = (props: PromptInputSelectProps) => <Select {...props} />;\n\nexport type PromptInputSelectTriggerProps = ComponentProps<typeof SelectTrigger>;\n\nexport const PromptInputSelectTrigger = ({\n  className,\n  ...props\n}: PromptInputSelectTriggerProps) => (\n  <SelectTrigger\n    className={cn(\n      "border-none bg-transparent font-medium text-muted-foreground shadow-none transition-colors",\n      "hover:bg-accent hover:text-foreground aria-expanded:bg-accent aria-expanded:text-foreground",\n      className,\n    )}\n    {...props}\n  />\n);\n\nexport type PromptInputSelectContentProps = ComponentProps<typeof SelectContent>;\n\nexport const PromptInputSelectContent = ({\n  className,\n  ...props\n}: PromptInputSelectContentProps) => <SelectContent className={cn(className)} {...props} />;\n\nexport type PromptInputSelectItemProps = ComponentProps<typeof SelectItem>;\n\nexport const PromptInputSelectItem = ({ className, ...props }: PromptInputSelectItemProps) => (\n  <SelectItem className={cn(className)} {...props} />\n);\n\nexport type PromptInputSelectValueProps = ComponentProps<typeof SelectValue>;\n\nexport const PromptInputSelectValue = ({ className, ...props }: PromptInputSelectValueProps) => (\n  <SelectValue className={cn(className)} {...props} />\n);\n\nexport type PromptInputHoverCardProps = ComponentProps<typeof HoverCard>;\n\nexport const PromptInputHoverCard = ({\n  openDelay = 0,\n  closeDelay = 0,\n  ...props\n}: PromptInputHoverCardProps) => (\n  <HoverCard closeDelay={closeDelay} openDelay={openDelay} {...props} />\n);\n\nexport type PromptInputHoverCardTriggerProps = ComponentProps<typeof HoverCardTrigger>;\n\nexport const PromptInputHoverCardTrigger = (props: PromptInputHoverCardTriggerProps) => (\n  <HoverCardTrigger {...props} />\n);\n\nexport type PromptInputHoverCardContentProps = ComponentProps<typeof HoverCardContent>;\n\nexport const PromptInputHoverCardContent = ({\n  align = "start",\n  ...props\n}: PromptInputHoverCardContentProps) => <HoverCardContent align={align} {...props} />;\n\nexport type PromptInputTabsListProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTabsList = ({ className, ...props }: PromptInputTabsListProps) => (\n  <div className={cn(className)} {...props} />\n);\n\nexport type PromptInputTabProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTab = ({ className, ...props }: PromptInputTabProps) => (\n  <div className={cn(className)} {...props} />\n);\n\nexport type PromptInputTabLabelProps = HTMLAttributes<HTMLHeadingElement>;\n\nexport const PromptInputTabLabel = ({ className, ...props }: PromptInputTabLabelProps) => (\n  // Content provided via children in props\n  // oxlint-disable-next-line eslint-plugin-jsx-a11y(heading-has-content)\n  <h3 className={cn("mb-2 px-3 font-medium text-muted-foreground text-xs", className)} {...props} />\n);\n\nexport type PromptInputTabBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTabBody = ({ className, ...props }: PromptInputTabBodyProps) => (\n  <div className={cn("space-y-1", className)} {...props} />\n);\n\nexport type PromptInputTabItemProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTabItem = ({ className, ...props }: PromptInputTabItemProps) => (\n  <div\n    className={cn("flex items-center gap-2 px-3 py-2 text-xs hover:bg-accent", className)}\n    {...props}\n  />\n);\n\nexport type PromptInputCommandProps = ComponentProps<typeof Command>;\n\nexport const PromptInputCommand = ({ className, ...props }: PromptInputCommandProps) => (\n  <Command className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandInputProps = ComponentProps<typeof CommandInput>;\n\nexport const PromptInputCommandInput = ({ className, ...props }: PromptInputCommandInputProps) => (\n  <CommandInput className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandListProps = ComponentProps<typeof CommandList>;\n\nexport const PromptInputCommandList = ({ className, ...props }: PromptInputCommandListProps) => (\n  <CommandList className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandEmptyProps = ComponentProps<typeof CommandEmpty>;\n\nexport const PromptInputCommandEmpty = ({ className, ...props }: PromptInputCommandEmptyProps) => (\n  <CommandEmpty className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandGroupProps = ComponentProps<typeof CommandGroup>;\n\nexport const PromptInputCommandGroup = ({ className, ...props }: PromptInputCommandGroupProps) => (\n  <CommandGroup className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandItemProps = ComponentProps<typeof CommandItem>;\n\nexport const PromptInputCommandItem = ({ className, ...props }: PromptInputCommandItemProps) => (\n  <CommandItem className={cn(className)} {...props} />\n);\n\nexport type PromptInputCommandSeparatorProps = ComponentProps<typeof CommandSeparator>;\n\nexport const PromptInputCommandSeparator = ({\n  className,\n  ...props\n}: PromptInputCommandSeparatorProps) => <CommandSeparator className={cn(className)} {...props} />;\n';
    readonly "components/ai-elements/reasoning.tsx": '"use client";\n\nimport { useControllableState } from "@radix-ui/react-use-controllable-state";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport { cjk } from "@streamdown/cjk";\nimport { code } from "@streamdown/code";\nimport { math } from "@streamdown/math";\nimport { mermaid } from "@streamdown/mermaid";\nimport { BrainIcon, ChevronDownIcon } from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport {\n  createContext,\n  memo,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from "react";\nimport { Streamdown } from "streamdown";\n\nimport { Shimmer } from "./shimmer";\n\ninterface ReasoningContextValue {\n  isStreaming: boolean;\n  isOpen: boolean;\n  setIsOpen: (open: boolean) => void;\n  duration: number | undefined;\n}\n\nconst ReasoningContext = createContext<ReasoningContextValue | null>(null);\n\nexport const useReasoning = () => {\n  const context = useContext(ReasoningContext);\n  if (!context) {\n    throw new Error("Reasoning components must be used within Reasoning");\n  }\n  return context;\n};\n\nexport type ReasoningProps = ComponentProps<typeof Collapsible> & {\n  isStreaming?: boolean;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  duration?: number;\n};\n\nconst AUTO_CLOSE_DELAY = 1000;\nconst MS_IN_S = 1000;\n\nexport const Reasoning = memo(\n  ({\n    className,\n    isStreaming = false,\n    open,\n    defaultOpen,\n    onOpenChange,\n    duration: durationProp,\n    children,\n    ...props\n  }: ReasoningProps) => {\n    const resolvedDefaultOpen = defaultOpen ?? isStreaming;\n    // Track if defaultOpen was explicitly set to false (to prevent auto-open)\n    const isExplicitlyClosed = defaultOpen === false;\n\n    const [isOpen, setIsOpen] = useControllableState<boolean>({\n      defaultProp: resolvedDefaultOpen,\n      onChange: onOpenChange,\n      prop: open,\n    });\n    const [duration, setDuration] = useControllableState<number | undefined>({\n      defaultProp: undefined,\n      prop: durationProp,\n    });\n\n    const hasEverStreamedRef = useRef(isStreaming);\n    const [hasAutoClosed, setHasAutoClosed] = useState(false);\n    const startTimeRef = useRef<number | null>(null);\n\n    // Track when streaming starts and compute duration\n    useEffect(() => {\n      if (isStreaming) {\n        hasEverStreamedRef.current = true;\n        if (startTimeRef.current === null) {\n          startTimeRef.current = Date.now();\n        }\n      } else if (startTimeRef.current !== null) {\n        setDuration(Math.ceil((Date.now() - startTimeRef.current) / MS_IN_S));\n        startTimeRef.current = null;\n      }\n    }, [isStreaming, setDuration]);\n\n    // Auto-open when streaming starts (unless explicitly closed)\n    useEffect(() => {\n      if (isStreaming && !isOpen && !isExplicitlyClosed) {\n        setIsOpen(true);\n      }\n    }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);\n\n    // Auto-close when streaming ends (once only, and only if it ever streamed)\n    useEffect(() => {\n      if (hasEverStreamedRef.current && !isStreaming && isOpen && !hasAutoClosed) {\n        const timer = setTimeout(() => {\n          setIsOpen(false);\n          setHasAutoClosed(true);\n        }, AUTO_CLOSE_DELAY);\n\n        return () => clearTimeout(timer);\n      }\n    }, [isStreaming, isOpen, setIsOpen, hasAutoClosed]);\n\n    const handleOpenChange = useCallback(\n      (newOpen: boolean) => {\n        setIsOpen(newOpen);\n      },\n      [setIsOpen],\n    );\n\n    const contextValue = useMemo(\n      () => ({ duration, isOpen, isStreaming, setIsOpen }),\n      [duration, isOpen, isStreaming, setIsOpen],\n    );\n\n    return (\n      <ReasoningContext.Provider value={contextValue}>\n        <Collapsible\n          className={cn("not-prose mb-4 w-full", className)}\n          onOpenChange={handleOpenChange}\n          open={isOpen}\n          {...props}\n        >\n          {children}\n        </Collapsible>\n      </ReasoningContext.Provider>\n    );\n  },\n);\n\nexport type ReasoningTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {\n  getThinkingMessage?: (isStreaming: boolean, duration?: number) => ReactNode;\n};\n\nconst defaultGetThinkingMessage = (isStreaming: boolean, duration?: number) => {\n  if (isStreaming || duration === 0) {\n    return <Shimmer duration={1}>Thinking...</Shimmer>;\n  }\n  if (duration === undefined) {\n    return <p>Thought for a few seconds</p>;\n  }\n  return <p>Thought for {duration} seconds</p>;\n};\n\nexport const ReasoningTrigger = memo(\n  ({\n    className,\n    children,\n    getThinkingMessage = defaultGetThinkingMessage,\n    ...props\n  }: ReasoningTriggerProps) => {\n    const { isStreaming, isOpen, duration } = useReasoning();\n\n    return (\n      <CollapsibleTrigger\n        className={cn(\n          "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",\n          className,\n        )}\n        {...props}\n      >\n        {children ?? (\n          <>\n            <BrainIcon className="size-4" />\n            {getThinkingMessage(isStreaming, duration)}\n            <ChevronDownIcon\n              className={cn("size-4 transition-transform", isOpen ? "rotate-180" : "rotate-0")}\n            />\n          </>\n        )}\n      </CollapsibleTrigger>\n    );\n  },\n);\n\nexport type ReasoningContentProps = ComponentProps<typeof CollapsibleContent> & {\n  children: string;\n};\n\nconst streamdownPlugins = { cjk, code, math, mermaid };\n\nexport const ReasoningContent = memo(({ className, children, ...props }: ReasoningContentProps) => (\n  <CollapsibleContent\n    className={cn(\n      "mt-4 text-sm",\n      "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n      className,\n    )}\n    {...props}\n  >\n    <Streamdown plugins={streamdownPlugins}>{children}</Streamdown>\n  </CollapsibleContent>\n));\n\nReasoning.displayName = "Reasoning";\nReasoningTrigger.displayName = "ReasoningTrigger";\nReasoningContent.displayName = "ReasoningContent";\n';
    readonly "components/ai-elements/shimmer.tsx": '"use client";\n\nimport { cn } from "@/lib/utils";\nimport type { MotionProps } from "motion/react";\nimport { motion } from "motion/react";\nimport type { CSSProperties, ElementType, JSX } from "react";\nimport { memo, useMemo } from "react";\n\ntype MotionHTMLProps = MotionProps & Record<string, unknown>;\n\n// Cache motion components at module level to avoid creating during render\nconst motionComponentCache = new Map<\n  keyof JSX.IntrinsicElements,\n  React.ComponentType<MotionHTMLProps>\n>();\n\nconst getMotionComponent = (element: keyof JSX.IntrinsicElements) => {\n  let component = motionComponentCache.get(element);\n  if (!component) {\n    component = motion.create(element);\n    motionComponentCache.set(element, component);\n  }\n  return component;\n};\n\nexport interface TextShimmerProps {\n  children: string;\n  as?: ElementType;\n  className?: string;\n  duration?: number;\n  spread?: number;\n}\n\nconst ShimmerComponent = ({\n  children,\n  as: Component = "p",\n  className,\n  duration = 2,\n  spread = 2,\n}: TextShimmerProps) => {\n  const MotionComponent = getMotionComponent(Component as keyof JSX.IntrinsicElements);\n\n  const dynamicSpread = useMemo(() => (children?.length ?? 0) * spread, [children, spread]);\n\n  return (\n    <MotionComponent\n      animate={{ backgroundPosition: "0% center" }}\n      className={cn(\n        "relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent",\n        "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",\n        className,\n      )}\n      initial={{ backgroundPosition: "100% center" }}\n      style={\n        {\n          "--spread": `${dynamicSpread}px`,\n          backgroundImage:\n            "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))",\n        } as CSSProperties\n      }\n      transition={{\n        duration,\n        ease: "linear",\n        repeat: Number.POSITIVE_INFINITY,\n      }}\n    >\n      {children}\n    </MotionComponent>\n  );\n};\n\nexport const Shimmer = memo(ShimmerComponent);\n';
    readonly "components/ai-elements/tool.tsx": '"use client";\n\nimport { Badge } from "@/components/ui/badge";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";\nimport { cn } from "@/lib/utils";\nimport type { DynamicToolUIPart, ToolUIPart } from "ai";\nimport {\n  CheckCircleIcon,\n  ChevronDownIcon,\n  CircleIcon,\n  ClockIcon,\n  WrenchIcon,\n  XCircleIcon,\n} from "lucide-react";\nimport type { ComponentProps, ReactNode } from "react";\nimport { isValidElement } from "react";\n\nimport { CodeBlock } from "./code-block";\n\nexport type ToolProps = ComponentProps<typeof Collapsible>;\n\nexport const Tool = ({ className, ...props }: ToolProps) => (\n  <Collapsible\n    className={cn("group not-prose mb-4 w-full rounded-md border", className)}\n    {...props}\n  />\n);\n\nexport type ToolPart = ToolUIPart | DynamicToolUIPart;\n\nexport type ToolHeaderProps = {\n  title?: string;\n  className?: string;\n} & (\n  | { type: ToolUIPart["type"]; state: ToolUIPart["state"]; toolName?: never }\n  | {\n      type: DynamicToolUIPart["type"];\n      state: DynamicToolUIPart["state"];\n      toolName: string;\n    }\n);\n\nconst statusLabels: Record<ToolPart["state"], string> = {\n  "approval-requested": "Awaiting Approval",\n  "approval-responded": "Responded",\n  "input-available": "Running",\n  "input-streaming": "Pending",\n  "output-available": "Completed",\n  "output-denied": "Denied",\n  "output-error": "Error",\n};\n\nconst statusIcons: Record<ToolPart["state"], ReactNode> = {\n  "approval-requested": <ClockIcon className="size-4 text-yellow-600" />,\n  "approval-responded": <CheckCircleIcon className="size-4 text-blue-600" />,\n  "input-available": <ClockIcon className="size-4 animate-pulse" />,\n  "input-streaming": <CircleIcon className="size-4" />,\n  "output-available": <CheckCircleIcon className="size-4 text-green-600" />,\n  "output-denied": <XCircleIcon className="size-4 text-orange-600" />,\n  "output-error": <XCircleIcon className="size-4 text-red-600" />,\n};\n\nexport const getStatusBadge = (status: ToolPart["state"]) => (\n  <Badge className="gap-1.5 rounded-full text-xs" variant="secondary">\n    {statusIcons[status]}\n    {statusLabels[status]}\n  </Badge>\n);\n\nexport const ToolHeader = ({\n  className,\n  title,\n  type,\n  state,\n  toolName,\n  ...props\n}: ToolHeaderProps) => {\n  const derivedName = type === "dynamic-tool" ? toolName : type.split("-").slice(1).join("-");\n\n  return (\n    <CollapsibleTrigger\n      className={cn("flex w-full items-center justify-between gap-4 p-3", className)}\n      {...props}\n    >\n      <div className="flex items-center gap-2">\n        <WrenchIcon className="size-4 text-muted-foreground" />\n        <span className="font-medium text-sm">{title ?? derivedName}</span>\n        {getStatusBadge(state)}\n      </div>\n      <ChevronDownIcon className="size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-180" />\n    </CollapsibleTrigger>\n  );\n};\n\nexport type ToolContentProps = ComponentProps<typeof CollapsibleContent>;\n\nexport const ToolContent = ({ className, ...props }: ToolContentProps) => (\n  <CollapsibleContent\n    className={cn(\n      "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 space-y-4 p-4 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",\n      className,\n    )}\n    {...props}\n  />\n);\n\nexport type ToolInputProps = ComponentProps<"div"> & {\n  input: ToolPart["input"];\n};\n\nexport const ToolInput = ({ className, input, ...props }: ToolInputProps) => (\n  <div className={cn("space-y-2 overflow-hidden", className)} {...props}>\n    <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">\n      Parameters\n    </h4>\n    <div className="rounded-md bg-muted/50">\n      <CodeBlock code={JSON.stringify(input, null, 2)} language="json" />\n    </div>\n  </div>\n);\n\nexport type ToolOutputProps = ComponentProps<"div"> & {\n  output: ToolPart["output"];\n  errorText: ToolPart["errorText"];\n};\n\nexport const ToolOutput = ({ className, output, errorText, ...props }: ToolOutputProps) => {\n  if (!(output || errorText)) {\n    return null;\n  }\n\n  let Output = <div>{output as ReactNode}</div>;\n\n  if (typeof output === "object" && !isValidElement(output)) {\n    Output = <CodeBlock code={JSON.stringify(output, null, 2)} language="json" />;\n  } else if (typeof output === "string") {\n    Output = <CodeBlock code={output} language="json" />;\n  }\n\n  return (\n    <div className={cn("space-y-2", className)} {...props}>\n      <h4 className="font-medium text-muted-foreground text-xs uppercase tracking-wide">\n        {errorText ? "Error" : "Result"}\n      </h4>\n      <div\n        className={cn(\n          "overflow-x-auto rounded-md text-xs [&_table]:w-full",\n          errorText ? "bg-destructive/10 text-destructive" : "bg-muted/50 text-foreground",\n        )}\n      >\n        {errorText && <div>{errorText}</div>}\n        {Output}\n      </div>\n    </div>\n  );\n};\n';
    readonly "components/ui/badge.tsx": 'import * as React from "react";\nimport { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nconst badgeVariants = cva(\n  "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",\n  {\n    variants: {\n      variant: {\n        default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",\n        secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",\n        destructive:\n          "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",\n        outline:\n          "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",\n        ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",\n        link: "text-primary underline-offset-4 [a&]:hover:underline",\n      },\n    },\n    defaultVariants: {\n      variant: "default",\n    },\n  },\n);\n\nfunction Badge({\n  className,\n  variant = "default",\n  asChild = false,\n  ...props\n}: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot.Root : "span";\n\n  return (\n    <Comp\n      data-slot="badge"\n      data-variant={variant}\n      className={cn(badgeVariants({ variant }), className)}\n      {...props}\n    />\n  );\n}\n\nexport { Badge, badgeVariants };\n';
    readonly "components/ui/button-group.tsx": 'import { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\nimport { Separator } from "@/components/ui/separator";\n\nconst buttonGroupVariants = cva(\n  "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*=\'w-\'])]:w-fit [&>input]:flex-1",\n  {\n    variants: {\n      orientation: {\n        horizontal:\n          "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",\n        vertical:\n          "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",\n      },\n    },\n    defaultVariants: {\n      orientation: "horizontal",\n    },\n  },\n);\n\nfunction ButtonGroup({\n  className,\n  orientation,\n  ...props\n}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {\n  return (\n    <div\n      role="group"\n      data-slot="button-group"\n      data-orientation={orientation}\n      className={cn(buttonGroupVariants({ orientation }), className)}\n      {...props}\n    />\n  );\n}\n\nfunction ButtonGroupText({\n  className,\n  asChild = false,\n  ...props\n}: React.ComponentProps<"div"> & {\n  asChild?: boolean;\n}) {\n  const Comp = asChild ? Slot.Root : "div";\n\n  return (\n    <Comp\n      className={cn(\n        "flex items-center gap-2 rounded-md border bg-muted px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction ButtonGroupSeparator({\n  className,\n  orientation = "vertical",\n  ...props\n}: React.ComponentProps<typeof Separator>) {\n  return (\n    <Separator\n      data-slot="button-group-separator"\n      orientation={orientation}\n      className={cn(\n        "relative m-0! self-stretch bg-input data-[orientation=vertical]:h-auto",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };\n';
    readonly "components/ui/button.tsx": 'import * as React from "react";\nimport { cva, type VariantProps } from "class-variance-authority";\nimport { Slot } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nconst buttonVariants = cva(\n  "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4",\n  {\n    variants: {\n      variant: {\n        default: "bg-primary text-primary-foreground hover:bg-primary/90",\n        destructive:\n          "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",\n        outline:\n          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",\n        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",\n        ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",\n        link: "text-primary underline-offset-4 hover:underline",\n      },\n      size: {\n        default: "h-9 px-4 py-2 has-[>svg]:px-3",\n        xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*=\'size-\'])]:size-3",\n        sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",\n        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",\n        icon: "size-9",\n        "icon-xs": "size-6 rounded-md [&_svg:not([class*=\'size-\'])]:size-3",\n        "icon-sm": "size-8",\n        "icon-lg": "size-10",\n      },\n    },\n    defaultVariants: {\n      variant: "default",\n      size: "default",\n    },\n  },\n);\n\nfunction Button({\n  className,\n  variant = "default",\n  size = "default",\n  asChild = false,\n  ...props\n}: React.ComponentProps<"button"> &\n  VariantProps<typeof buttonVariants> & {\n    asChild?: boolean;\n  }) {\n  const Comp = asChild ? Slot.Root : "button";\n\n  return (\n    <Comp\n      data-slot="button"\n      data-variant={variant}\n      data-size={size}\n      className={cn(buttonVariants({ variant, size, className }))}\n      {...props}\n    />\n  );\n}\n\nexport { Button, buttonVariants };\n';
    readonly "components/ui/collapsible.tsx": '"use client";\n\nimport { Collapsible as CollapsiblePrimitive } from "radix-ui";\n\nfunction Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n  return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;\n}\n\nfunction CollapsibleTrigger({\n  ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n  return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;\n}\n\nfunction CollapsibleContent({\n  ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n  return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n';
    readonly "components/ui/command.tsx": '"use client";\n\nimport * as React from "react";\nimport { Command as CommandPrimitive } from "cmdk";\nimport { SearchIcon } from "lucide-react";\n\nimport { cn } from "@/lib/utils";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from "@/components/ui/dialog";\n\nfunction Command({ className, ...props }: React.ComponentProps<typeof CommandPrimitive>) {\n  return (\n    <CommandPrimitive\n      data-slot="command"\n      className={cn(\n        "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CommandDialog({\n  title = "Command Palette",\n  description = "Search for a command to run...",\n  children,\n  className,\n  showCloseButton = true,\n  ...props\n}: React.ComponentProps<typeof Dialog> & {\n  title?: string;\n  description?: string;\n  className?: string;\n  showCloseButton?: boolean;\n}) {\n  return (\n    <Dialog {...props}>\n      <DialogHeader className="sr-only">\n        <DialogTitle>{title}</DialogTitle>\n        <DialogDescription>{description}</DialogDescription>\n      </DialogHeader>\n      <DialogContent\n        className={cn("overflow-hidden p-0", className)}\n        showCloseButton={showCloseButton}\n      >\n        <Command className="**:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">\n          {children}\n        </Command>\n      </DialogContent>\n    </Dialog>\n  );\n}\n\nfunction CommandInput({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\n  return (\n    <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">\n      <SearchIcon className="size-4 shrink-0 opacity-50" />\n      <CommandPrimitive.Input\n        data-slot="command-input"\n        className={cn(\n          "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",\n          className,\n        )}\n        {...props}\n      />\n    </div>\n  );\n}\n\nfunction CommandList({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {\n  return (\n    <CommandPrimitive.List\n      data-slot="command-list"\n      className={cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className)}\n      {...props}\n    />\n  );\n}\n\nfunction CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive.Empty>) {\n  return (\n    <CommandPrimitive.Empty\n      data-slot="command-empty"\n      className="py-6 text-center text-sm"\n      {...props}\n    />\n  );\n}\n\nfunction CommandGroup({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\n  return (\n    <CommandPrimitive.Group\n      data-slot="command-group"\n      className={cn(\n        "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CommandSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\n  return (\n    <CommandPrimitive.Separator\n      data-slot="command-separator"\n      className={cn("-mx-1 h-px bg-border", className)}\n      {...props}\n    />\n  );\n}\n\nfunction CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n  return (\n    <CommandPrimitive.Item\n      data-slot="command-item"\n      className={cn(\n        "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction CommandShortcut({ className, ...props }: React.ComponentProps<"span">) {\n  return (\n    <span\n      data-slot="command-shortcut"\n      className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Command,\n  CommandDialog,\n  CommandInput,\n  CommandList,\n  CommandEmpty,\n  CommandGroup,\n  CommandItem,\n  CommandShortcut,\n  CommandSeparator,\n};\n';
    readonly "components/ui/dialog.tsx": '"use client";\n\nimport * as React from "react";\nimport { XIcon } from "lucide-react";\nimport { Dialog as DialogPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\nimport { Button } from "@/components/ui/button";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n  return <DialogPrimitive.Root data-slot="dialog" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;\n}\n\nfunction DialogOverlay({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n  return (\n    <DialogPrimitive.Overlay\n      data-slot="dialog-overlay"\n      className={cn(\n        "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DialogContent({\n  className,\n  children,\n  showCloseButton = true,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n  showCloseButton?: boolean;\n}) {\n  return (\n    <DialogPortal data-slot="dialog-portal">\n      <DialogOverlay />\n      <DialogPrimitive.Content\n        data-slot="dialog-content"\n        className={cn(\n          "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        {showCloseButton && (\n          <DialogPrimitive.Close\n            data-slot="dialog-close"\n            className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4"\n          >\n            <XIcon />\n            <span className="sr-only">Close</span>\n          </DialogPrimitive.Close>\n        )}\n      </DialogPrimitive.Content>\n    </DialogPortal>\n  );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<"div">) {\n  return (\n    <div\n      data-slot="dialog-header"\n      className={cn("flex flex-col gap-2 text-center sm:text-left", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogFooter({\n  className,\n  showCloseButton = false,\n  children,\n  ...props\n}: React.ComponentProps<"div"> & {\n  showCloseButton?: boolean;\n}) {\n  return (\n    <div\n      data-slot="dialog-footer"\n      className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}\n      {...props}\n    >\n      {children}\n      {showCloseButton && (\n        <DialogPrimitive.Close asChild>\n          <Button variant="outline">Close</Button>\n        </DialogPrimitive.Close>\n      )}\n    </div>\n  );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n  return (\n    <DialogPrimitive.Title\n      data-slot="dialog-title"\n      className={cn("text-lg leading-none font-semibold", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogDescription({\n  className,\n  ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n  return (\n    <DialogPrimitive.Description\n      data-slot="dialog-description"\n      className={cn("text-sm text-muted-foreground", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogOverlay,\n  DialogPortal,\n  DialogTitle,\n  DialogTrigger,\n};\n';
    readonly "components/ui/dropdown-menu.tsx": '"use client";\n\nimport * as React from "react";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";\nimport { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nfunction DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n  return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;\n}\n\nfunction DropdownMenuPortal({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n  return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;\n}\n\nfunction DropdownMenuTrigger({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n  return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;\n}\n\nfunction DropdownMenuContent({\n  className,\n  sideOffset = 4,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n  return (\n    <DropdownMenuPrimitive.Portal>\n      <DropdownMenuPrimitive.Content\n        data-slot="dropdown-menu-content"\n        sideOffset={sideOffset}\n        className={cn(\n          "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",\n          className,\n        )}\n        {...props}\n      />\n    </DropdownMenuPrimitive.Portal>\n  );\n}\n\nfunction DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n  return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;\n}\n\nfunction DropdownMenuItem({\n  className,\n  inset,\n  variant = "default",\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n  inset?: boolean;\n  variant?: "default" | "destructive";\n}) {\n  return (\n    <DropdownMenuPrimitive.Item\n      data-slot="dropdown-menu-item"\n      data-inset={inset}\n      data-variant={variant}\n      className={cn(\n        "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuCheckboxItem({\n  className,\n  children,\n  checked,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n  return (\n    <DropdownMenuPrimitive.CheckboxItem\n      data-slot="dropdown-menu-checkbox-item"\n      className={cn(\n        "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4",\n        className,\n      )}\n      checked={checked}\n      {...props}\n    >\n      <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">\n        <DropdownMenuPrimitive.ItemIndicator>\n          <CheckIcon className="size-4" />\n        </DropdownMenuPrimitive.ItemIndicator>\n      </span>\n      {children}\n    </DropdownMenuPrimitive.CheckboxItem>\n  );\n}\n\nfunction DropdownMenuRadioGroup({\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n  return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;\n}\n\nfunction DropdownMenuRadioItem({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n  return (\n    <DropdownMenuPrimitive.RadioItem\n      data-slot="dropdown-menu-radio-item"\n      className={cn(\n        "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4",\n        className,\n      )}\n      {...props}\n    >\n      <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">\n        <DropdownMenuPrimitive.ItemIndicator>\n          <CircleIcon className="size-2 fill-current" />\n        </DropdownMenuPrimitive.ItemIndicator>\n      </span>\n      {children}\n    </DropdownMenuPrimitive.RadioItem>\n  );\n}\n\nfunction DropdownMenuLabel({\n  className,\n  inset,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n  inset?: boolean;\n}) {\n  return (\n    <DropdownMenuPrimitive.Label\n      data-slot="dropdown-menu-label"\n      data-inset={inset}\n      className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n  return (\n    <DropdownMenuPrimitive.Separator\n      data-slot="dropdown-menu-separator"\n      className={cn("-mx-1 my-1 h-px bg-border", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {\n  return (\n    <span\n      data-slot="dropdown-menu-shortcut"\n      className={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n  return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n  className,\n  inset,\n  children,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n  inset?: boolean;\n}) {\n  return (\n    <DropdownMenuPrimitive.SubTrigger\n      data-slot="dropdown-menu-sub-trigger"\n      data-inset={inset}\n      className={cn(\n        "flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <ChevronRightIcon className="ml-auto size-4" />\n    </DropdownMenuPrimitive.SubTrigger>\n  );\n}\n\nfunction DropdownMenuSubContent({\n  className,\n  ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n  return (\n    <DropdownMenuPrimitive.SubContent\n      data-slot="dropdown-menu-sub-content"\n      className={cn(\n        "z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  DropdownMenu,\n  DropdownMenuPortal,\n  DropdownMenuTrigger,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuLabel,\n  DropdownMenuItem,\n  DropdownMenuCheckboxItem,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuSeparator,\n  DropdownMenuShortcut,\n  DropdownMenuSub,\n  DropdownMenuSubTrigger,\n  DropdownMenuSubContent,\n};\n';
    readonly "components/ui/hover-card.tsx": '"use client";\n\nimport * as React from "react";\nimport { HoverCard as HoverCardPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nfunction HoverCard({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {\n  return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;\n}\n\nfunction HoverCardTrigger({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {\n  return <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />;\n}\n\nfunction HoverCardContent({\n  className,\n  align = "center",\n  sideOffset = 4,\n  ...props\n}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {\n  return (\n    <HoverCardPrimitive.Portal data-slot="hover-card-portal">\n      <HoverCardPrimitive.Content\n        data-slot="hover-card-content"\n        align={align}\n        sideOffset={sideOffset}\n        className={cn(\n          "z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",\n          className,\n        )}\n        {...props}\n      />\n    </HoverCardPrimitive.Portal>\n  );\n}\n\nexport { HoverCard, HoverCardTrigger, HoverCardContent };\n';
    readonly "components/ui/input-group.tsx": '"use client";\n\nimport * as React from "react";\nimport { cva, type VariantProps } from "class-variance-authority";\n\nimport { cn } from "@/lib/utils";\nimport { Button } from "@/components/ui/button";\nimport { Input } from "@/components/ui/input";\nimport { Textarea } from "@/components/ui/textarea";\n\nfunction InputGroup({ className, ...props }: React.ComponentProps<"div">) {\n  return (\n    <div\n      data-slot="input-group"\n      role="group"\n      className={cn(\n        "group/input-group relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",\n        "h-9 min-w-0 has-[>textarea]:h-auto",\n\n        // Variants based on alignment.\n        "has-[>[data-align=inline-start]]:[&>input]:pl-2",\n        "has-[>[data-align=inline-end]]:[&>input]:pr-2",\n        "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",\n        "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",\n\n        // Focus state.\n        "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50",\n\n        // Error state.\n        "has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",\n\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nconst inputGroupAddonVariants = cva(\n  "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*=\'size-\'])]:size-4",\n  {\n    variants: {\n      align: {\n        "inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",\n        "inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",\n        "block-start":\n          "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",\n        "block-end":\n          "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",\n      },\n    },\n    defaultVariants: {\n      align: "inline-start",\n    },\n  },\n);\n\nfunction InputGroupAddon({\n  className,\n  align = "inline-start",\n  ...props\n}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {\n  return (\n    <div\n      role="group"\n      data-slot="input-group-addon"\n      data-align={align}\n      className={cn(inputGroupAddonVariants({ align }), className)}\n      onClick={(e) => {\n        if ((e.target as HTMLElement).closest("button")) {\n          return;\n        }\n        e.currentTarget.parentElement?.querySelector("input")?.focus();\n      }}\n      {...props}\n    />\n  );\n}\n\nconst inputGroupButtonVariants = cva("flex items-center gap-2 text-sm shadow-none", {\n  variants: {\n    size: {\n      xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*=\'size-\'])]:size-3.5",\n      sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",\n      "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",\n      "icon-sm": "size-8 p-0 has-[>svg]:p-0",\n    },\n  },\n  defaultVariants: {\n    size: "xs",\n  },\n});\n\nfunction InputGroupButton({\n  className,\n  type = "button",\n  variant = "ghost",\n  size = "xs",\n  ...props\n}: Omit<React.ComponentProps<typeof Button>, "size"> &\n  VariantProps<typeof inputGroupButtonVariants>) {\n  return (\n    <Button\n      type={type}\n      data-size={size}\n      variant={variant}\n      className={cn(inputGroupButtonVariants({ size }), className)}\n      {...props}\n    />\n  );\n}\n\nfunction InputGroupText({ className, ...props }: React.ComponentProps<"span">) {\n  return (\n    <span\n      className={cn(\n        "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction InputGroupInput({ className, ...props }: React.ComponentProps<"input">) {\n  return (\n    <Input\n      data-slot="input-group-control"\n      className={cn(\n        "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction InputGroupTextarea({ className, ...props }: React.ComponentProps<"textarea">) {\n  return (\n    <Textarea\n      data-slot="input-group-control"\n      className={cn(\n        "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupText,\n  InputGroupInput,\n  InputGroupTextarea,\n};\n';
    readonly "components/ui/input.tsx": 'import * as React from "react";\n\nimport { cn } from "@/lib/utils";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<"input">) {\n  return (\n    <input\n      type={type}\n      data-slot="input"\n      className={cn(\n        "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",\n        "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",\n        "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Input };\n';
    readonly "components/ui/select.tsx": '"use client";\n\nimport * as React from "react";\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";\nimport { Select as SelectPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nfunction Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {\n  return <SelectPrimitive.Root data-slot="select" {...props} />;\n}\n\nfunction SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>) {\n  return <SelectPrimitive.Group data-slot="select-group" {...props} />;\n}\n\nfunction SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>) {\n  return <SelectPrimitive.Value data-slot="select-value" {...props} />;\n}\n\nfunction SelectTrigger({\n  className,\n  size = "default",\n  children,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n  size?: "sm" | "default";\n}) {\n  return (\n    <SelectPrimitive.Trigger\n      data-slot="select-trigger"\n      data-size={size}\n      className={cn(\n        "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      <SelectPrimitive.Icon asChild>\n        <ChevronDownIcon className="size-4 opacity-50" />\n      </SelectPrimitive.Icon>\n    </SelectPrimitive.Trigger>\n  );\n}\n\nfunction SelectContent({\n  className,\n  children,\n  position = "item-aligned",\n  align = "center",\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n  return (\n    <SelectPrimitive.Portal>\n      <SelectPrimitive.Content\n        data-slot="select-content"\n        className={cn(\n          "relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",\n          position === "popper" &&\n            "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",\n          className,\n        )}\n        position={position}\n        align={align}\n        {...props}\n      >\n        <SelectScrollUpButton />\n        <SelectPrimitive.Viewport\n          className={cn(\n            "p-1",\n            position === "popper" &&\n              "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",\n          )}\n        >\n          {children}\n        </SelectPrimitive.Viewport>\n        <SelectScrollDownButton />\n      </SelectPrimitive.Content>\n    </SelectPrimitive.Portal>\n  );\n}\n\nfunction SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>) {\n  return (\n    <SelectPrimitive.Label\n      data-slot="select-label"\n      className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SelectItem({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n  return (\n    <SelectPrimitive.Item\n      data-slot="select-item"\n      className={cn(\n        "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",\n        className,\n      )}\n      {...props}\n    >\n      <span\n        data-slot="select-item-indicator"\n        className="absolute right-2 flex size-3.5 items-center justify-center"\n      >\n        <SelectPrimitive.ItemIndicator>\n          <CheckIcon className="size-4" />\n        </SelectPrimitive.ItemIndicator>\n      </span>\n      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n    </SelectPrimitive.Item>\n  );\n}\n\nfunction SelectSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.Separator>) {\n  return (\n    <SelectPrimitive.Separator\n      data-slot="select-separator"\n      className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SelectScrollUpButton({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n  return (\n    <SelectPrimitive.ScrollUpButton\n      data-slot="select-scroll-up-button"\n      className={cn("flex cursor-default items-center justify-center py-1", className)}\n      {...props}\n    >\n      <ChevronUpIcon className="size-4" />\n    </SelectPrimitive.ScrollUpButton>\n  );\n}\n\nfunction SelectScrollDownButton({\n  className,\n  ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n  return (\n    <SelectPrimitive.ScrollDownButton\n      data-slot="select-scroll-down-button"\n      className={cn("flex cursor-default items-center justify-center py-1", className)}\n      {...props}\n    >\n      <ChevronDownIcon className="size-4" />\n    </SelectPrimitive.ScrollDownButton>\n  );\n}\n\nexport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectLabel,\n  SelectScrollDownButton,\n  SelectScrollUpButton,\n  SelectSeparator,\n  SelectTrigger,\n  SelectValue,\n};\n';
    readonly "components/ui/separator.tsx": '"use client";\n\nimport * as React from "react";\nimport { Separator as SeparatorPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nfunction Separator({\n  className,\n  orientation = "horizontal",\n  decorative = true,\n  ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n  return (\n    <SeparatorPrimitive.Root\n      data-slot="separator"\n      decorative={decorative}\n      orientation={orientation}\n      className={cn(\n        "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Separator };\n';
    readonly "components/ui/spinner.tsx": 'import { Loader2Icon } from "lucide-react";\n\nimport { cn } from "@/lib/utils";\n\nfunction Spinner({ className, ...props }: React.ComponentProps<"svg">) {\n  return (\n    <Loader2Icon\n      role="status"\n      aria-label="Loading"\n      className={cn("size-4 animate-spin", className)}\n      {...props}\n    />\n  );\n}\n\nexport { Spinner };\n';
    readonly "components/ui/textarea.tsx": 'import * as React from "react";\n\nimport { cn } from "@/lib/utils";\n\nfunction Textarea({ className, ...props }: React.ComponentProps<"textarea">) {\n  return (\n    <textarea\n      data-slot="textarea"\n      className={cn(\n        "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Textarea };\n';
    readonly "components/ui/tooltip.tsx": '"use client";\n\nimport * as React from "react";\nimport { Tooltip as TooltipPrimitive } from "radix-ui";\n\nimport { cn } from "@/lib/utils";\n\nfunction TooltipProvider({\n  delayDuration = 0,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n  return (\n    <TooltipPrimitive.Provider\n      data-slot="tooltip-provider"\n      delayDuration={delayDuration}\n      {...props}\n    />\n  );\n}\n\nfunction Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;\n}\n\nfunction TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;\n}\n\nfunction TooltipContent({\n  className,\n  sideOffset = 0,\n  children,\n  ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n  return (\n    <TooltipPrimitive.Portal>\n      <TooltipPrimitive.Content\n        data-slot="tooltip-content"\n        sideOffset={sideOffset}\n        className={cn(\n          "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />\n      </TooltipPrimitive.Content>\n    </TooltipPrimitive.Portal>\n  );\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n';
    readonly "components.json": '{\n  "$schema": "https://ui.shadcn.com/schema.json",\n  "style": "new-york",\n  "rsc": true,\n  "tsx": true,\n  "tailwind": {\n    "config": "",\n    "css": "app/globals.css",\n    "baseColor": "neutral",\n    "cssVariables": true,\n    "prefix": ""\n  },\n  "iconLibrary": "lucide",\n  "aliases": {\n    "components": "@/components",\n    "utils": "@/lib/utils",\n    "ui": "@/components/ui",\n    "lib": "@/lib",\n    "hooks": "@/hooks"\n  },\n  "registries": {}\n}\n';
    readonly "css.d.ts": 'declare module "*.css";\n';
    readonly "lib/utils.ts": 'import { clsx, type ClassValue } from "clsx";\nimport { twMerge } from "tailwind-merge";\n\nexport function cn(...inputs: ClassValue[]): string {\n  return twMerge(clsx(inputs));\n}\n';
    readonly "next-env.d.ts": '/// <reference types="next" />\n/// <reference types="next/image-types/global" />\nimport "./.next/types/routes.d.ts";\nimport "./.next/types/root-params.d.ts";\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\n';
    readonly "next.config.ts": 'import type { NextConfig } from "next";\nimport { withEve } from "eve/next";\n\nconst nextConfig: NextConfig = {};\n\nexport default withEve(nextConfig__EVE_INIT_WITH_EVE_OPTIONS__);\n';
    readonly "postcss.config.mjs": 'const config = {\n  plugins: {\n    "@tailwindcss/postcss": {},\n  },\n};\n\nexport default config;\n';
    readonly "tsconfig.json": '{\n  "$schema": "https://json.schemastore.org/tsconfig",\n  "compilerOptions": {\n    "target": "ES2017",\n    "lib": ["dom", "dom.iterable", "esnext"],\n    "allowJs": true,\n    "skipLibCheck": true,\n    "strict": true,\n    "noEmit": true,\n    "esModuleInterop": true,\n    "module": "esnext",\n    "moduleResolution": "Bundler",\n    "resolveJsonModule": true,\n    "isolatedModules": true,\n    "jsx": "react-jsx",\n    "incremental": true,\n    "plugins": [\n      {\n        "name": "next"\n      }\n    ],\n    "paths": {\n      "@/*": ["./*"]\n    }\n  },\n  "include": [\n    "next-env.d.ts",\n    "**/*.ts",\n    "**/*.tsx",\n    ".next/types/**/*.ts",\n    ".next/dev/types/**/*.ts"\n  ],\n  "exclude": ["node_modules"]\n}\n';
};
export declare const WEB_APP_TEMPLATE_PACKAGE_JSON: {
    readonly scripts: {
        readonly build: "next build";
        readonly "build:eve": "eve build";
        readonly dev: "next dev";
        readonly "dev:eve": "eve dev";
        readonly start: "next start";
        readonly "start:eve": "eve start";
        readonly typecheck: "tsc --noEmit -p tsconfig.json";
    };
    readonly dependencies: {
        readonly "@radix-ui/react-use-controllable-state": "1.2.2";
        readonly "@shikijs/core": "4.1.0";
        readonly "@shikijs/engine-javascript": "4.1.0";
        readonly "@shikijs/engine-oniguruma": "4.1.0";
        readonly "@streamdown/cjk": "1.0.3";
        readonly "@streamdown/code": "1.1.1";
        readonly "@streamdown/math": "1.0.2";
        readonly "@streamdown/mermaid": "1.0.2";
        readonly "@tailwindcss/postcss": "4.3.0";
        readonly ai: "catalog:";
        readonly "class-variance-authority": "0.7.1";
        readonly clsx: "2.1.1";
        readonly cmdk: "1.1.1";
        readonly eve: "workspace:*";
        readonly "lucide-react": "1.16.0";
        readonly motion: "12.40.0";
        readonly nanoid: "5.1.11";
        readonly next: "catalog:";
        readonly "radix-ui": "1.4.3";
        readonly react: "catalog:";
        readonly "react-dom": "catalog:";
        readonly shiki: "4.1.0";
        readonly streamdown: "catalog:";
        readonly "tailwind-merge": "3.6.0";
        readonly tailwindcss: "4.3.0";
        readonly "use-stick-to-bottom": "1.1.4";
        readonly zod: "catalog:";
    };
    readonly devDependencies: {
        readonly "@types/node": "catalog:";
        readonly "@types/react": "catalog:";
        readonly "@types/react-dom": "catalog:";
        readonly typescript: "6.0.3";
    };
};
