{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-bar",
  "type": "registry:ui",
  "dependencies": [
    "@tabler/icons-react",
    "ai",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "https://agent-elements.21st.dev/r/attachment-button.json",
    "https://agent-elements.21st.dev/r/file-attachment.json",
    "https://agent-elements.21st.dev/r/send-button.json",
    "https://agent-elements.21st.dev/r/suggestions.json"
  ],
  "files": [
    {
      "path": "registry/agent-elements/components/input-bar.tsx",
      "content": "\"use client\";\n\nimport { memo, useState, useCallback, useRef, useEffect } from \"react\";\nimport type { ChatStatus } from \"ai\";\nimport { cn } from \"./utils/cn\";\n\ntype InputConfig = {\n  inputBarPlaceholder: string;\n  attachmentButtonPosition: \"left\" | \"right\";\n  attachmentPreviewStyle: \"thumbnail\" | \"chip\" | \"hidden\";\n};\n\nconst DEFAULT_INPUT_CONFIG: InputConfig = {\n  inputBarPlaceholder: \"Send a message...\",\n  attachmentButtonPosition: \"left\",\n  attachmentPreviewStyle: \"thumbnail\",\n};\n\nimport {\n  IconChevronDown,\n  IconChevronUp,\n  IconMessageCircleQuestion,\n  IconX,\n} from \"@tabler/icons-react\";\nimport { SendButton } from \"./input/send-button\";\nimport { AttachmentButton } from \"./input/attachment-button\";\nimport { FileAttachment } from \"./input/file-attachment\";\nimport { useInputTyping } from \"./input/input-typing\";\nimport { QuestionPrompt } from \"./question/question-prompt\";\nimport { Suggestions, type SuggestionItem } from \"./input/suggestions\";\nimport type {\n  QuestionAnswer,\n  QuestionConfig,\n} from \"./question/question-prompt\";\n\nexport type AttachedImage = {\n  id: string;\n  filename: string;\n  url: string;\n  size?: number;\n};\n\nexport type AttachedFile = {\n  id: string;\n  filename: string;\n  size?: number;\n};\n\nexport type InputBarProps = {\n  onSend: (message: { role: \"user\"; content: string }) => void;\n  status: ChatStatus;\n  onStop: () => void;\n  placeholder?: string;\n  className?: string;\n\n  // Attachment support\n  onAttach?: () => void;\n  attachedImages?: AttachedImage[];\n  attachedFiles?: AttachedFile[];\n  onRemoveImage?: (id: string) => void;\n  onRemoveFile?: (id: string) => void;\n  onPaste?: (e: React.ClipboardEvent) => void;\n  isDragOver?: boolean;\n  /**\n   * When true (default) clicking a staged image attachment opens a\n   * fullscreen lightbox preview. Set to false to render thumbnails as\n   * plain non-interactive previews.\n   */\n  enableImagePreview?: boolean;\n\n  // Controlled mode\n  value?: string;\n  onChange?: (value: string) => void;\n  disabled?: boolean;\n  autoFocus?: boolean;\n  suggestions?:\n    | SuggestionItem[]\n    | {\n        items: SuggestionItem[];\n        className?: string;\n        itemClassName?: string;\n      };\n\n  // Typing animation\n  typingAnimation?: {\n    text: string;\n    duration: number;\n    image?: string;\n    isActive: boolean;\n    onComplete: () => void;\n  };\n\n  infoBar?: {\n    title?: string;\n    description?: string;\n    onClose?: () => void;\n    position?: \"top\" | \"bottom\";\n    /** Optional primary action rendered on the right (e.g. \"Upgrade\"). */\n    action?: {\n      label: string;\n      onClick: () => void;\n    };\n  };\n\n  questionBar?: {\n    id: string;\n    questions: QuestionConfig[];\n    questionIndex?: number;\n    totalQuestions?: number;\n    onPreviousQuestion?: () => void;\n    onNextQuestion?: () => void;\n    submitLabel?: string;\n    skipLabel?: string;\n    allowSkip?: boolean;\n    onSubmit: (answer: QuestionAnswer) => void;\n    onSkip?: () => void;\n  };\n\n  /** Content rendered on the left of the toolbar, next to the attachment button. */\n  leftActions?: React.ReactNode;\n  /** Content rendered on the right of the toolbar, before the send button. */\n  rightActions?: React.ReactNode;\n};\n\nexport const InputBar = memo(function InputBar({\n  onSend,\n  status,\n  onStop,\n  placeholder,\n  className,\n  onAttach,\n  attachedImages = [],\n  attachedFiles = [],\n  onRemoveImage,\n  onRemoveFile,\n  onPaste,\n  isDragOver,\n  enableImagePreview = true,\n  value: controlledValue,\n  onChange: controlledOnChange,\n  disabled,\n  autoFocus,\n  suggestions = [],\n  typingAnimation,\n  infoBar,\n  questionBar,\n  leftActions,\n  rightActions,\n}: InputBarProps) {\n  const [internalInput, setInternalInput] = useState(\"\");\n  const [isInfoBarOpen, setIsInfoBarOpen] = useState(true);\n  const [dismissedQuestionId, setDismissedQuestionId] = useState<string | null>(\n    null,\n  );\n  const [questionBarIndex, setQuestionBarIndex] = useState(1);\n  const isControlled = controlledValue !== undefined;\n  const input = isControlled ? controlledValue : internalInput;\n  const setInput = useCallback(\n    (v: string) => {\n      if (isControlled) {\n        controlledOnChange?.(v);\n      } else {\n        setInternalInput(v);\n      }\n    },\n    [isControlled, controlledOnChange],\n  );\n  const textareaRef = useRef<HTMLTextAreaElement>(null);\n  const config = DEFAULT_INPUT_CONFIG;\n\n  const isStreaming = status === \"streaming\" || status === \"submitted\";\n  const isTyping = typingAnimation?.isActive ?? false;\n\n  const { displayedText, showImage } = useInputTyping(\n    typingAnimation?.text ?? \"\",\n    typingAnimation?.duration ?? 2000,\n    isTyping,\n    typingAnimation?.onComplete ?? (() => {}),\n  );\n\n  const effectivePlaceholder = placeholder ?? config.inputBarPlaceholder;\n\n  const showAttach = Boolean(onAttach);\n  const attachRight = config.attachmentButtonPosition === \"right\";\n\n  // Auto-resize textarea\n  useEffect(() => {\n    const el = textareaRef.current;\n    if (!el) return;\n    el.style.height = \"0\";\n    const nextHeight = Math.min(el.scrollHeight, 120);\n    el.style.height = `${nextHeight}px`;\n    el.style.overflowY = el.scrollHeight > 120 ? \"auto\" : \"hidden\";\n    el.style.overflowX = \"hidden\";\n  }, [input]);\n\n  useEffect(() => {\n    if (!autoFocus) return;\n    textareaRef.current?.focus();\n  }, [autoFocus]);\n\n  const handleSubmit = useCallback(() => {\n    const trimmed = input.trim();\n    if (!trimmed || isStreaming || disabled) return;\n    onSend({ role: \"user\", content: trimmed });\n    setInput(\"\");\n  }, [input, isStreaming, disabled, onSend, setInput]);\n\n  const handleInfoBarClose = useCallback(() => {\n    setIsInfoBarOpen(false);\n    infoBar?.onClose?.();\n  }, [infoBar]);\n\n  const infoBarPosition = infoBar?.position ?? \"top\";\n  const shouldShowInfoBar = Boolean(\n    infoBar && (infoBar.title || infoBar.description),\n  );\n  const infoBarData = infoBar ?? {};\n\n  const infoBarNode = shouldShowInfoBar ? (\n    <div\n      className={cn(\n        \"flex items-center justify-between gap-3 px-3 h-[34px]\",\n        \"transition-all duration-150 ease-out overflow-hidden\",\n        isInfoBarOpen ? \"opacity-100 max-h-[34px]\" : \"opacity-0 max-h-0\",\n        infoBarPosition === \"top\"\n          ? \"rounded-t-an-input-border-radius\"\n          : \"rounded-b-an-input-border-radius\",\n      )}\n    >\n      <div className=\"min-w-0 truncate text-xs text-an-foreground\">\n        {infoBarData.title && (\n          <span className=\"font-medium\">{infoBarData.title}</span>\n        )}\n        {infoBarData.description && (\n          <span className=\"text-an-foreground-muted/80\">\n            {infoBarData.title\n              ? ` ${infoBarData.description}`\n              : infoBarData.description}\n          </span>\n        )}\n      </div>\n      <div className=\"flex items-center gap-1 shrink-0\">\n        {infoBarData.action && (\n          <button\n            type=\"button\"\n            onClick={infoBarData.action.onClick}\n            className=\"h-6 px-2 rounded-[4px] text-xs font-medium bg-an-primary-color text-an-send-button-color hover:bg-an-primary-color/90 active:scale-[0.98] transition-[background-color,transform] duration-150\"\n          >\n            {infoBarData.action.label}\n          </button>\n        )}\n        {infoBarData.onClose && (\n          <button\n            type=\"button\"\n            onClick={handleInfoBarClose}\n            className=\"shrink-0 inline-flex items-center justify-center w-6 h-6 rounded-md text-an-foreground-muted/70 hover:text-an-foreground hover:bg-an-background-secondary\"\n            aria-label=\"Close\"\n          >\n            <IconX className=\"w-3.5 h-3.5\" strokeWidth={2} />\n          </button>\n        )}\n      </div>\n    </div>\n  ) : null;\n\n  const shouldShowQuestionBar = Boolean(\n    questionBar && questionBar.id !== dismissedQuestionId,\n  );\n  const questionBarData = questionBar;\n  const questionSet = questionBarData?.questions ?? [];\n  const hasQuestions = questionSet.length > 0;\n  const derivedTotal = hasQuestions ? questionSet.length : 1;\n  const totalQuestions = questionBarData?.totalQuestions ?? derivedTotal;\n  const hasExternalQuestionNavigation = Boolean(\n    questionBarData?.onPreviousQuestion || questionBarData?.onNextQuestion,\n  );\n  const questionIndex = hasExternalQuestionNavigation\n    ? (questionBarData?.questionIndex ?? 1)\n    : questionBarIndex;\n  const clampedQuestionIndex = Math.max(\n    1,\n    Math.min(questionIndex, totalQuestions),\n  );\n  const activeQuestion = hasQuestions\n    ? questionSet[clampedQuestionIndex - 1]\n    : undefined;\n  const showQuestionNavigation = totalQuestions > 1;\n  const canGoPrev = clampedQuestionIndex > 1;\n  const canGoNext = clampedQuestionIndex < totalQuestions;\n\n  const handleQuestionPrevious = useCallback(() => {\n    if (!canGoPrev) return;\n    if (questionBarData?.onPreviousQuestion) {\n      questionBarData.onPreviousQuestion();\n      return;\n    }\n    setQuestionBarIndex((prev) => Math.max(1, prev - 1));\n  }, [canGoPrev, questionBarData]);\n\n  const handleQuestionNext = useCallback(() => {\n    if (!canGoNext) return;\n    if (questionBarData?.onNextQuestion) {\n      questionBarData.onNextQuestion();\n      return;\n    }\n    setQuestionBarIndex((prev) => Math.min(totalQuestions, prev + 1));\n  }, [canGoNext, questionBarData, totalQuestions]);\n\n  const questionBarNode =\n    shouldShowQuestionBar && activeQuestion ? (\n      <div\n        className={cn(\n          \"border-t border-x border-border max-w-[calc(100%-24px)] w-full mx-auto\",\n          !shouldShowInfoBar || infoBarPosition === \"bottom\"\n            ? \"rounded-t-an-input-border-radius\"\n            : null,\n        )}\n      >\n        <div className=\"h-7 border-b border-border px-3 flex items-center justify-between text-xs text-an-tool-color-muted\">\n          <div className=\"inline-flex items-center gap-1.5\">\n            <IconMessageCircleQuestion className=\"w-3.5 h-3.5\" />\n            Question\n          </div>\n          {showQuestionNavigation && (\n            <div className=\"inline-flex items-center gap-1\">\n              <button\n                type=\"button\"\n                onClick={handleQuestionPrevious}\n                disabled={!canGoPrev}\n                className=\"size-5 inline-flex items-center justify-center rounded-[4px] hover:bg-an-background-secondary disabled:opacity-40\"\n                aria-label=\"Previous question\"\n              >\n                <IconChevronUp className=\"w-3.5 h-3.5\" />\n              </button>\n              <span>\n                {clampedQuestionIndex} of {totalQuestions}\n              </span>\n              <button\n                type=\"button\"\n                onClick={handleQuestionNext}\n                disabled={!canGoNext}\n                className=\"size-5 inline-flex items-center justify-center rounded-[4px] hover:bg-an-background-secondary disabled:opacity-40\"\n                aria-label=\"Next question\"\n              >\n                <IconChevronDown className=\"w-3.5 h-3.5\" />\n              </button>\n            </div>\n          )}\n        </div>\n        <QuestionPrompt\n          key={`${clampedQuestionIndex}-${activeQuestion?.title ?? \"question\"}`}\n          questions={questionSet}\n          questionIndex={clampedQuestionIndex}\n          totalQuestions={totalQuestions}\n          submitLabel={questionBarData!.submitLabel}\n          skipLabel={questionBarData!.skipLabel}\n          allowSkip={questionBarData!.allowSkip}\n          onSubmit={(answer) => {\n            questionBarData!.onSubmit(answer);\n            setDismissedQuestionId(questionBarData!.id);\n          }}\n          onSkip={() => {\n            questionBarData!.onSkip?.();\n          }}\n        />\n      </div>\n    ) : null;\n\n  const handleKeyDown = useCallback(\n    (e: React.KeyboardEvent) => {\n      if (e.key === \"Enter\" && !e.shiftKey) {\n        e.preventDefault();\n        handleSubmit();\n      }\n    },\n    [handleSubmit],\n  );\n\n  const hasInput = input.trim().length > 0;\n  const hasContextItems = attachedImages.length > 0 || attachedFiles.length > 0;\n  const showContextItems =\n    hasContextItems && config.attachmentPreviewStyle !== \"hidden\";\n  const imageDisplayMode =\n    config.attachmentPreviewStyle === \"thumbnail\" ? \"image-only\" : \"chip\";\n\n  const handleContainerClick = useCallback((e: React.MouseEvent) => {\n    if (\n      e.target === e.currentTarget ||\n      !(e.target as HTMLElement).closest(\"button, textarea\")\n    ) {\n      textareaRef.current?.focus();\n    }\n  }, []);\n\n  const handleSuggestionSelect = useCallback(\n    (item: SuggestionItem) => {\n      if (disabled || isStreaming) return;\n      setInput(item.value ?? item.label);\n      requestAnimationFrame(() => {\n        const el = textareaRef.current;\n        if (!el) return;\n        el.focus();\n        const end = el.value.length;\n        el.setSelectionRange(end, end);\n      });\n    },\n    [disabled, isStreaming, setInput],\n  );\n\n  const suggestionItems = Array.isArray(suggestions)\n    ? suggestions\n    : (suggestions?.items ?? []);\n  const suggestionsClassName = Array.isArray(suggestions)\n    ? undefined\n    : suggestions?.className;\n  const suggestionItemClassName = Array.isArray(suggestions)\n    ? undefined\n    : suggestions?.itemClassName;\n\n  return (\n    <div className={cn(\"shrink-0 px-3 pb-3\", className)}>\n      <div className=\"mx-auto max-w-an\">\n        <div\n          className={cn(\n            \"flex flex-col gap-0\",\n            shouldShowInfoBar\n              ? \"bg-an-background-tertiary rounded-an-input-border-radius\"\n              : null,\n          )}\n        >\n          {infoBarPosition === \"top\" && infoBarNode}\n          {questionBarNode}\n          <div\n            className={cn(\n              \"relative cursor-text rounded-an-input-border-radius bg-an-input-background shadow-2xs ring-1 ring-foreground/10\",\n              isDragOver && \"ring-2 ring-an-primary-color\",\n            )}\n            onClick={handleContainerClick}\n          >\n            {/* Context items (attached images/files) */}\n            <div\n              className={cn(\n                \"grid transition-[grid-template-rows] duration-200 ease-out grid-rows-[0fr]\",\n                showContextItems && \"grid-rows-[1fr]\",\n              )}\n            >\n              <div className=\"overflow-hidden\">\n                {showContextItems && (\n                  <div className=\"flex flex-wrap items-center gap-[6px] px-an-context-padding pt-an-context-padding pb-0.5\">\n                    {attachedImages.map((img) => (\n                      <FileAttachment\n                        key={img.id}\n                        id={img.id}\n                        filename={img.filename}\n                        size={img.size}\n                        isImage\n                        url={img.url}\n                        display={imageDisplayMode}\n                        enableImagePreview={enableImagePreview}\n                        onRemove={\n                          onRemoveImage\n                            ? () => onRemoveImage(img.id)\n                            : undefined\n                        }\n                      />\n                    ))}\n                    {attachedFiles.map((file) => (\n                      <FileAttachment\n                        key={file.id}\n                        id={file.id}\n                        filename={file.filename}\n                        size={file.size}\n                        onRemove={\n                          onRemoveFile ? () => onRemoveFile(file.id) : undefined\n                        }\n                      />\n                    ))}\n                  </div>\n                )}\n              </div>\n            </div>\n\n            {/* Typing animation image */}\n            {isTyping && typingAnimation?.image && showImage && (\n              <div className=\"flex gap-2 flex-wrap px-3 pt-3\">\n                <div className=\"relative overflow-hidden shrink-0 w-16 h-16 rounded-md\">\n                  <img\n                    src={typingAnimation.image}\n                    alt=\"\"\n                    className=\"w-full h-full object-cover\"\n                  />\n                </div>\n              </div>\n            )}\n\n            {/* Text input or typing animation text */}\n            <div className=\"pt-3 pb-0 pr-3 pl-3.5 min-h-[44px]\">\n              {isTyping ? (\n                <div className=\"w-full text-[14px] leading-[1.6] text-an-foreground-muted\">\n                  <span>{displayedText}</span>\n                  <span className=\"inline-block w-[2px] h-[1em] ml-px align-text-bottom bg-an-foreground animate-an-blink\" />\n                </div>\n              ) : (\n                <>\n                  <textarea\n                    ref={textareaRef}\n                    value={input}\n                    onChange={(e) => setInput(e.target.value)}\n                    onKeyDown={handleKeyDown}\n                    onPaste={onPaste}\n                    placeholder={effectivePlaceholder}\n                    disabled={disabled}\n                    rows={1}\n                    className={cn(\n                      \"peer w-full resize-none bg-transparent border-0 outline-none text-[14px] leading-[1.6] text-an-foreground placeholder:text-an-input-placeholder-color\",\n                      \"overflow-hidden\",\n                      disabled && \"opacity-50 cursor-not-allowed\",\n                    )}\n                  />\n                  <div className=\"pointer-events-none absolute inset-0 rounded-an-input-border-radius outline-2 outline-an-input-focus-outline opacity-0 transition-opacity duration-75 peer-focus-visible:opacity-100 peer-focus:opacity-100 z-20 ease-in-out\" />\n                </>\n              )}\n            </div>\n\n            {/* Toolbar */}\n            <div className=\"flex items-center justify-between gap-3 px-2 pt-1 pb-2\">\n              <div className=\"flex items-center gap-1 min-w-0\">\n                {!attachRight && showAttach && onAttach && (\n                  <AttachmentButton onClick={onAttach} />\n                )}\n                {leftActions}\n              </div>\n              <div className=\"flex items-center gap-1\">\n                {rightActions}\n                {attachRight && showAttach && onAttach && (\n                  <AttachmentButton onClick={onAttach} />\n                )}\n                {/* Send / Stop button */}\n                <div\n                  onClick={() => {\n                    if (isStreaming) {\n                      onStop();\n                    } else if (hasInput) {\n                      handleSubmit();\n                    }\n                  }}\n                  className=\"cursor-pointer\"\n                >\n                  <SendButton\n                    state={\n                      isStreaming\n                        ? \"streaming\"\n                        : hasInput && !disabled\n                          ? \"typing\"\n                          : \"idle\"\n                    }\n                  />\n                </div>\n              </div>\n            </div>\n          </div>\n          {suggestionItems.length > 0 && (\n            <Suggestions\n              items={suggestionItems}\n              onSelect={handleSuggestionSelect}\n              disabled={disabled || isStreaming}\n              className={cn(\"mt-4 px-3\", suggestionsClassName)}\n              itemClassName={suggestionItemClassName}\n            />\n          )}\n          {infoBarPosition === \"bottom\" && infoBarNode}\n        </div>\n      </div>\n    </div>\n  );\n});\n",
      "type": "registry:ui",
      "target": "components/agent-elements/input-bar.tsx"
    },
    {
      "path": "registry/agent-elements/utils/cn.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n",
      "type": "registry:lib",
      "target": "components/agent-elements/utils/cn.ts"
    },
    {
      "path": "registry/agent-elements/components/input/input-typing.tsx",
      "content": "import { useState, useEffect, useRef } from \"react\";\n\nexport function useInputTyping(\n  text: string,\n  duration: number,\n  isActive: boolean,\n  onComplete: () => void,\n) {\n  const [visibleChars, setVisibleChars] = useState(0);\n  const [showImage, setShowImage] = useState(false);\n  const onCompleteRef = useRef(onComplete);\n  onCompleteRef.current = onComplete;\n\n  useEffect(() => {\n    if (!isActive) {\n      setVisibleChars(0);\n      setShowImage(false);\n      return;\n    }\n\n    const imageDelay = duration * 0.1;\n    const typingStart = duration * 0.15;\n    const typingDuration = duration * 0.7;\n    const charInterval = typingDuration / text.length;\n    const sendDelay = duration * 0.15;\n    const timers: ReturnType<typeof setTimeout>[] = [];\n\n    timers.push(setTimeout(() => setShowImage(true), imageDelay));\n    for (let i = 0; i < text.length; i++) {\n      timers.push(\n        setTimeout(\n          () => setVisibleChars(i + 1),\n          typingStart + charInterval * i,\n        ),\n      );\n    }\n    timers.push(\n      setTimeout(\n        () => onCompleteRef.current(),\n        typingStart + typingDuration + sendDelay,\n      ),\n    );\n\n    return () => timers.forEach(clearTimeout);\n  }, [isActive, text, duration]);\n\n  return { displayedText: text.slice(0, visibleChars), showImage };\n}\n",
      "type": "registry:ui",
      "target": "components/agent-elements/input/input-typing.tsx"
    },
    {
      "path": "registry/agent-elements/components/question/question-prompt.tsx",
      "content": "import { useEffect, useMemo, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport type QuestionOption = {\n  id: string;\n  label: string;\n  description?: string;\n};\n\nexport type QuestionConfig = {\n  kind: \"single\" | \"multi\" | \"text\";\n  title: string;\n  description?: string;\n  options?: QuestionOption[];\n  allowCustom?: boolean;\n  customLabel?: string;\n  customPlaceholder?: string;\n  minSelections?: number;\n  maxSelections?: number;\n  placeholder?: string;\n};\n\nexport type QuestionAnswer = {\n  kind: \"single\" | \"multi\" | \"text\" | \"skip\";\n  selectedIds?: string[];\n  text?: string;\n};\n\nconst QUESTION_CUSTOM_ID = \"__custom__\";\n\nfunction optionBadge(idx: number) {\n  return String.fromCharCode(65 + idx);\n}\n\nexport type QuestionPromptProps = {\n  questions: QuestionConfig[];\n  questionIndex?: number;\n  totalQuestions?: number;\n  onPreviousQuestion?: () => void;\n  onNextQuestion?: () => void;\n  initialAnswer?: QuestionAnswer;\n  /** Label for the primary action on the LAST question (default \"Send\"). */\n  submitLabel?: string;\n  /** Label for the primary action when there are more questions ahead\n   *  (default \"Next\"). The host (e.g. QuestionTool) is expected to advance\n   *  to the next question after onSubmit fires. */\n  nextLabel?: string;\n  skipLabel?: string;\n  allowSkip?: boolean;\n  onSubmit: (answer: QuestionAnswer) => void;\n  onSkip?: () => void;\n  className?: string;\n};\n\nexport function QuestionPrompt({\n  questions,\n  questionIndex = 1,\n  totalQuestions,\n  onPreviousQuestion,\n  onNextQuestion,\n  submitLabel = \"Send\",\n  nextLabel = \"Next\",\n  skipLabel = \"Skip\",\n  allowSkip = true,\n  initialAnswer,\n  onSubmit,\n  onSkip,\n  className,\n}: QuestionPromptProps) {\n  const [selectedIds, setSelectedIds] = useState<string[]>([]);\n  const [customText, setCustomText] = useState(\"\");\n  const [textValue, setTextValue] = useState(\"\");\n  const resolvedTotal = totalQuestions ?? questions.length;\n  const clampedIndex = Math.max(1, Math.min(questionIndex, resolvedTotal));\n  const activeQuestion = questions[clampedIndex - 1];\n  const customEnabled = activeQuestion?.allowCustom ?? false;\n  const showNav =\n    resolvedTotal > 1 && (!!onPreviousQuestion || !!onNextQuestion);\n  const canGoPrev = clampedIndex > 1;\n  const canGoNext = clampedIndex < resolvedTotal;\n  const isLastQuestion = clampedIndex >= resolvedTotal;\n  const primaryLabel = isLastQuestion ? submitLabel : nextLabel;\n\n  useEffect(() => {\n    if (!initialAnswer || initialAnswer.kind === \"skip\") {\n      setSelectedIds([]);\n      setCustomText(\"\");\n      setTextValue(\"\");\n      return;\n    }\n\n    if (activeQuestion?.kind === \"text\") {\n      setSelectedIds([]);\n      setCustomText(\"\");\n      setTextValue(initialAnswer.text ?? \"\");\n      return;\n    }\n\n    const nextSelected = new Set(initialAnswer.selectedIds ?? []);\n    const nextCustomText = initialAnswer.text ?? \"\";\n    if (customEnabled && nextCustomText.trim().length > 0) {\n      nextSelected.add(QUESTION_CUSTOM_ID);\n    }\n    setSelectedIds(Array.from(nextSelected));\n    setCustomText(nextCustomText);\n    setTextValue(\"\");\n  }, [\n    activeQuestion?.kind,\n    clampedIndex,\n    customEnabled,\n    initialAnswer?.kind,\n    initialAnswer?.text,\n    initialAnswer?.selectedIds?.join(\"|\"),\n  ]);\n\n  const canSubmit = useMemo(() => {\n    if (activeQuestion?.kind === \"text\") return textValue.trim().length > 0;\n\n    const selectedNonCustom = selectedIds.filter(\n      (id) => id !== QUESTION_CUSTOM_ID,\n    ).length;\n    const hasCustomText = customText.trim().length > 0;\n    const total = selectedNonCustom + (hasCustomText ? 1 : 0);\n\n    if (activeQuestion?.kind === \"single\") {\n      return total === 1;\n    }\n\n    const min = activeQuestion?.minSelections ?? 1;\n    const max = activeQuestion?.maxSelections;\n    if (total < min) return false;\n    if (typeof max === \"number\" && total > max) return false;\n    return total > 0;\n  }, [\n    activeQuestion?.kind,\n    activeQuestion?.minSelections,\n    activeQuestion?.maxSelections,\n    selectedIds,\n    customText,\n    textValue,\n  ]);\n\n  const toggleMulti = (id: string) => {\n    setSelectedIds((prev) =>\n      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],\n    );\n  };\n\n  const handleSingleSelect = (id: string) => {\n    setSelectedIds([id]);\n  };\n\n  const handleCustomTextChange = (nextValue: string) => {\n    setCustomText(nextValue);\n    if (!activeQuestion) return;\n    if (activeQuestion.kind === \"single\") {\n      setSelectedIds(nextValue.trim().length > 0 ? [QUESTION_CUSTOM_ID] : []);\n      return;\n    }\n    setSelectedIds((prev) => {\n      const hasCustom = prev.includes(QUESTION_CUSTOM_ID);\n      if (nextValue.trim().length > 0 && !hasCustom) {\n        return [...prev, QUESTION_CUSTOM_ID];\n      }\n      if (nextValue.trim().length === 0 && hasCustom) {\n        return prev.filter((id) => id !== QUESTION_CUSTOM_ID);\n      }\n      return prev;\n    });\n  };\n\n  const handleSubmit = () => {\n    if (!canSubmit || !activeQuestion) return;\n    if (activeQuestion.kind === \"text\") {\n      onSubmit({ kind: \"text\", text: textValue.trim() });\n      return;\n    }\n\n    const selectedNonCustom = selectedIds.filter(\n      (id) => id !== QUESTION_CUSTOM_ID,\n    );\n    const answerText = customText.trim() || undefined;\n    onSubmit({\n      kind: activeQuestion.kind,\n      selectedIds: selectedNonCustom,\n      text: answerText || undefined,\n    });\n  };\n\n  const handleSkip = () => {\n    onSkip?.();\n    onSubmit({ kind: \"skip\" });\n  };\n\n  if (!activeQuestion) return null;\n\n  return (\n    <div className={cn(\"px-3 py-2 space-y-2 bg-background\", className)}>\n      <div\n        className=\"flex items-center justify-between gap-px\"\n        data-total-questions={resolvedTotal}\n      >\n        <div className=\"flex items-center gap-2 text-sm text-an-tool-color\">\n          <span className=\"h-5 min-w-5 px-1 rounded-[4px] inline-flex items-center justify-center text-sm font-medium text-an-tool-color-muted\">\n            {clampedIndex}\n          </span>\n          <span>{activeQuestion.title}</span>\n        </div>\n      </div>\n\n      {activeQuestion.kind !== \"text\" &&\n        (activeQuestion.options?.length ?? 0) > 0 && (\n          <div className=\"space-y-px\">\n            {activeQuestion.options!.map((option, idx) => {\n              const checked = selectedIds.includes(option.id);\n              return (\n                <button\n                  key={option.id}\n                  type=\"button\"\n                  onClick={() => {\n                    if (activeQuestion.kind === \"single\") {\n                      handleSingleSelect(option.id);\n                      if (customEnabled) setCustomText(\"\");\n                    } else {\n                      toggleMulti(option.id);\n                    }\n                  }}\n                  className=\"w-full text-left rounded-md px-2 py-1.5 flex items-center gap-2 hover:bg-an-background-secondary -mx-2\"\n                >\n                  <span\n                    className={cn(\n                      \"h-5 min-w-5 px-1 rounded-[4px] inline-flex items-center justify-center text-sm font-medium border\",\n                      checked\n                        ? \"bg-an-primary-color text-an-send-button-color border-an-primary-color\"\n                        : \"bg-transparent text-an-tool-color-muted border-border\",\n                    )}\n                  >\n                    {optionBadge(idx)}\n                  </span>\n                  <span className=\"text-sm text-an-tool-color\">\n                    {option.label}\n                    {option.description && (\n                      <span className=\"text-an-tool-color-muted\">\n                        {\" \"}\n                        {option.description}\n                      </span>\n                    )}\n                  </span>\n                </button>\n              );\n            })}\n\n            {customEnabled && (\n              <div className=\"pt-1 flex items-center gap-2\">\n                <span\n                  className={cn(\n                    \"h-5 min-w-5 px-1 rounded-[4px] inline-flex items-center justify-center text-sm font-medium border\",\n                    selectedIds.includes(QUESTION_CUSTOM_ID)\n                      ? \"bg-an-primary-color text-an-send-button-color border-an-primary-color\"\n                      : \"bg-transparent text-an-tool-color-muted border-border\",\n                  )}\n                >\n                  {optionBadge(activeQuestion.options!.length)}\n                </span>\n                <input\n                  value={customText}\n                  onChange={(event) =>\n                    handleCustomTextChange(event.target.value)\n                  }\n                  placeholder={\n                    activeQuestion.customPlaceholder ?? \"Type your answer\"\n                  }\n                  className=\"w-full h-7 rounded-md border border-border bg-background px-2 text-sm text-an-tool-color\"\n                />\n              </div>\n            )}\n          </div>\n        )}\n\n      {activeQuestion.kind === \"text\" && (\n        <textarea\n          value={textValue}\n          onChange={(event) => setTextValue(event.target.value)}\n          placeholder={activeQuestion.placeholder ?? \"Type your answer\"}\n          rows={3}\n          className=\"w-full rounded-md border border-border bg-background px-2 py-1.5 text-sm text-an-tool-color resize-y\"\n        />\n      )}\n\n      <div\n        className={cn(\n          \"flex items-center gap-1.5\",\n          showNav ? \"justify-between\" : \"justify-end\",\n        )}\n      >\n        {showNav && (\n          <div className=\"flex items-center gap-1.5\">\n            {onPreviousQuestion && (\n              <button\n                type=\"button\"\n                onClick={onPreviousQuestion}\n                disabled={!canGoPrev}\n                className=\"h-6 px-2 rounded-[4px] text-sm text-muted-foreground hover:text-an-tool-color disabled:opacity-60\"\n              >\n                Previous\n              </button>\n            )}\n            {onNextQuestion && (\n              <button\n                type=\"button\"\n                onClick={onNextQuestion}\n                disabled={!canGoNext}\n                className=\"h-6 px-2 rounded-[4px] text-sm text-muted-foreground hover:text-an-tool-color disabled:opacity-60\"\n              >\n                Next\n              </button>\n            )}\n          </div>\n        )}\n        <div className=\"flex items-center justify-end gap-1.5\">\n          {allowSkip && (\n            <button\n              type=\"button\"\n              onClick={handleSkip}\n              className=\"h-6 px-2 rounded-[4px] text-sm text-muted-foreground hover:text-an-tool-color hover:bg-muted/50 active:scale-[0.98] transition-[background-color,color,transform] duration-150\"\n            >\n              {skipLabel}\n            </button>\n          )}\n          <button\n            type=\"button\"\n            onClick={handleSubmit}\n            disabled={!canSubmit}\n            className=\"h-6 px-2.5 rounded-[4px] text-sm font-medium bg-an-primary-color text-an-send-button-color hover:bg-an-primary-color/90 active:scale-[0.98] transition-[background-color,transform] duration-150 disabled:opacity-60 disabled:hover:bg-an-primary-color disabled:active:scale-100\"\n          >\n            {primaryLabel}\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/agent-elements/question/question-prompt.tsx"
    },
    {
      "path": "registry/agent-elements/components/image-lightbox.tsx",
      "content": "\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n  IconChevronLeft,\n  IconChevronRight,\n  IconX,\n} from \"@tabler/icons-react\";\nimport { cn } from \"./utils/cn\";\n\nexport type LightboxImage = {\n  /** Stable identifier — used for keys and to know which image is active. */\n  id: string;\n  /** Resolvable image URL (https / data: / blob:). */\n  url: string;\n  /** Optional filename used for the alt text. */\n  filename?: string;\n};\n\nexport type ImageLightboxProps = {\n  /** Whether the overlay is open. */\n  open: boolean;\n  /** Close handler — wired to overlay click, X button, and Esc key. */\n  onClose: () => void;\n  /** Full set of images for gallery navigation. */\n  images: LightboxImage[];\n  /** Index in `images` to start on. */\n  initialIndex?: number;\n};\n\n/**\n * Portal-based fullscreen image preview. Renders to `document.body` so it\n * escapes any clipping/transform/stacking context. Adapted from the\n * 21st-private-1 desktop chat — without copy/save (those are\n * desktop-API-specific).\n */\nexport function ImageLightbox({\n  open,\n  onClose,\n  images,\n  initialIndex = 0,\n}: ImageLightboxProps) {\n  const [currentIndex, setCurrentIndex] = useState(initialIndex);\n  const hasMultipleImages = images.length > 1;\n\n  // Sync the active index whenever the consumer re-opens with a new initial.\n  useEffect(() => {\n    if (open) setCurrentIndex(initialIndex);\n  }, [open, initialIndex]);\n\n  const goToPrevious = useCallback(\n    (event?: React.MouseEvent) => {\n      event?.stopPropagation();\n      setCurrentIndex((prev) => (prev > 0 ? prev - 1 : images.length - 1));\n    },\n    [images.length],\n  );\n\n  const goToNext = useCallback(\n    (event?: React.MouseEvent) => {\n      event?.stopPropagation();\n      setCurrentIndex((prev) => (prev < images.length - 1 ? prev + 1 : 0));\n    },\n    [images.length],\n  );\n\n  // Esc / arrow-key navigation. Capture phase so we beat any local handlers\n  // (e.g. an Editor that swallows Esc).\n  useEffect(() => {\n    if (!open) return;\n    const handleKeyDown = (event: KeyboardEvent) => {\n      switch (event.key) {\n        case \"Escape\":\n          event.preventDefault();\n          event.stopPropagation();\n          onClose();\n          break;\n        case \"ArrowLeft\":\n          if (hasMultipleImages) goToPrevious();\n          break;\n        case \"ArrowRight\":\n          if (hasMultipleImages) goToNext();\n          break;\n      }\n    };\n    window.addEventListener(\"keydown\", handleKeyDown, true);\n    return () =>\n      window.removeEventListener(\"keydown\", handleKeyDown, true);\n  }, [open, hasMultipleImages, onClose, goToPrevious, goToNext]);\n\n  // Lock body scroll while open so the page underneath doesn't move.\n  useEffect(() => {\n    if (!open) return;\n    const previousOverflow = document.body.style.overflow;\n    document.body.style.overflow = \"hidden\";\n    return () => {\n      document.body.style.overflow = previousOverflow;\n    };\n  }, [open]);\n\n  if (typeof document === \"undefined\") return null;\n  if (!open) return null;\n  const currentImage = images[currentIndex] ?? images[0];\n  if (!currentImage?.url) return null;\n\n  return createPortal(\n    <div\n      role=\"dialog\"\n      aria-modal=\"true\"\n      className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/90 backdrop-blur-sm\"\n      onClick={onClose}\n    >\n      <button\n        type=\"button\"\n        onClick={onClose}\n        aria-label=\"Close fullscreen (Esc)\"\n        className=\"absolute top-4 right-4 z-10 inline-flex size-9 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors\"\n      >\n        <IconX className=\"size-5\" />\n      </button>\n\n      {hasMultipleImages && (\n        <button\n          type=\"button\"\n          onClick={goToPrevious}\n          aria-label=\"Previous image (←)\"\n          className=\"absolute left-4 top-1/2 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors\"\n        >\n          <IconChevronLeft className=\"size-6\" />\n        </button>\n      )}\n\n      <img\n        src={currentImage.url}\n        alt={currentImage.filename ?? \"Image preview\"}\n        className=\"max-w-[90vw] max-h-[85vh] object-contain select-none\"\n        onClick={(event) => event.stopPropagation()}\n        draggable={false}\n      />\n\n      {hasMultipleImages && (\n        <button\n          type=\"button\"\n          onClick={goToNext}\n          aria-label=\"Next image (→)\"\n          className=\"absolute right-4 top-1/2 z-10 inline-flex size-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70 transition-colors\"\n        >\n          <IconChevronRight className=\"size-6\" />\n        </button>\n      )}\n\n      {hasMultipleImages && (\n        <div className=\"absolute bottom-6 left-1/2 -translate-x-1/2 flex flex-col items-center gap-3\">\n          <div className=\"flex gap-2\">\n            {images.map((_, idx) => (\n              <button\n                key={idx}\n                type=\"button\"\n                onClick={(event) => {\n                  event.stopPropagation();\n                  setCurrentIndex(idx);\n                }}\n                aria-label={`Go to image ${idx + 1}`}\n                className={cn(\n                  \"size-2 rounded-full transition-all\",\n                  idx === currentIndex\n                    ? \"bg-white scale-125\"\n                    : \"bg-white/40 hover:bg-white/60\",\n                )}\n              />\n            ))}\n          </div>\n          <span className=\"text-white/70 text-sm\">\n            {currentIndex + 1} / {images.length}\n          </span>\n        </div>\n      )}\n    </div>,\n    document.body,\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/agent-elements/image-lightbox.tsx"
    },
    {
      "path": "registry/agent-elements/agent-ui.css",
      "content": "/* Agent Elements tokens + utilities */\n\n/*\n * AN Agent Chat - CSS Custom Properties\n * These are the default values. Override by redefining these vars in your app CSS.\n */\n:root {\n  /* Geometry — all radii derive from --an-border-radius */\n  --an-border-radius: 16px;\n  --an-message-border-radius: var(--an-border-radius);\n  --an-input-border-radius: var(--an-border-radius);\n  --an-tool-border-radius: 10px;\n  --an-message-radius-inner-offset: 4px;\n  --an-max-width: 420px;\n\n  /* Colors - Light mode defaults */\n  --an-background: #ffffff;\n  --an-background-secondary: #f0f0f0;\n  --an-background-tertiary: #f8f8f8;\n  --an-foreground: #1a1a1a;\n  --an-foreground-muted: #737373;\n  --an-foreground-subtle: #a3a3a3;\n  --an-border-color: #e4e4e7;\n  --an-primary-color: #3b82f6;\n\n  /* User Messages */\n  --an-user-message-bg: #f5f5f5;\n  --an-user-message-text: #1a1a1a;\n\n  /* Input */\n  --an-input-background: #ffffff;\n  --an-input-border-color: #e4e4e7;\n  --an-input-color: #1a1a1a;\n  --an-input-placeholder-color: #a3a3a3;\n  --an-input-focus-outline: transparent;\n  --an-context-padding: 10px;\n\n  /* Send/Stop Buttons */\n  --an-send-button-bg: #3b82f6;\n  --an-send-button-color: #ffffff;\n\n  /* Tools */\n  --an-tool-background: #f5f5f5;\n  --an-tool-border-color: #e4e4e7;\n  --an-tool-color: #1a1a1a;\n  --an-tool-color-muted: #737373;\n\n  /* Code */\n  --an-code-background: #1e1e1e;\n  --an-code-color: #d4d4d4;\n\n  /* Diff colors */\n  --an-diff-added-bg: rgba(34, 197, 94, 0.1);\n  --an-diff-added-border: rgba(34, 197, 94, 0.5);\n  --an-diff-added-text: #15803d;\n  --an-diff-removed-bg: rgba(239, 68, 68, 0.1);\n  --an-diff-removed-border: rgba(239, 68, 68, 0.5);\n  --an-diff-removed-text: #dc2626;\n}\n\n/* Dark mode defaults */\n.dark {\n  --an-background: #0a0a0a;\n  --an-background-secondary: #242424;\n  --an-background-tertiary: #141414;\n  --an-foreground: #fafafa;\n  --an-foreground-muted: #8c8c8c;\n  --an-foreground-subtle: #71717a;\n  --an-border-color: #2a2a2a;\n  --an-primary-color: #60a5fa;\n\n  --an-user-message-bg: #1a1a1a;\n  --an-user-message-text: #fafafa;\n\n  --an-input-background: #0a0a0a;\n  --an-input-border-color: #2a2a2a;\n  --an-input-color: #fafafa;\n  --an-input-placeholder-color: #71717a;\n  --an-input-focus-outline: transparent;\n  --an-context-padding: 12px;\n\n  --an-send-button-bg: #60a5fa;\n  /* Dark mode uses a lighter primary (#60a5fa) — pair it with black text so\n     labels like \"Approve\" / send-arrow have proper contrast instead of\n     low-contrast white-on-light-blue. */\n  --an-send-button-color: #0a0a0a;\n\n  --an-tool-background: #141414;\n  --an-tool-border-color: #2a2a2a;\n  --an-tool-color: #fafafa;\n  --an-tool-color-muted: #8c8c8c;\n\n  --an-code-background: #0a0a0a;\n  --an-code-color: #d4d4d4;\n\n  --an-diff-added-bg: rgba(34, 197, 94, 0.15);\n  --an-diff-added-border: rgba(34, 197, 94, 0.4);\n  --an-diff-added-text: #4ade80;\n  --an-diff-removed-bg: rgba(239, 68, 68, 0.15);\n  --an-diff-removed-border: rgba(239, 68, 68, 0.4);\n  --an-diff-removed-text: #f87171;\n}\n\n@theme inline {\n  --color-an-background: var(--an-background);\n  --color-an-background-secondary: var(--an-background-secondary);\n  --color-an-background-tertiary: var(--an-background-tertiary);\n  --color-an-foreground: var(--an-foreground);\n  --color-an-foreground-muted: var(--an-foreground-muted);\n  --color-an-foreground-subtle: var(--an-foreground-subtle);\n  --color-an-border-color: var(--an-border-color);\n  --color-an-primary-color: var(--an-primary-color);\n  --color-an-user-message-bg: var(--an-user-message-bg);\n  --color-an-user-message-text: var(--an-user-message-text);\n  --color-an-input-background: var(--an-input-background);\n  --color-an-input-border-color: var(--an-input-border-color);\n  --color-an-input-color: var(--an-input-color);\n  --color-an-input-placeholder-color: var(--an-input-placeholder-color);\n  --color-an-input-focus-outline: var(--an-input-focus-outline);\n  --color-an-send-button-bg: var(--an-send-button-bg);\n  --color-an-send-button-color: var(--an-send-button-color);\n  --color-an-tool-background: var(--an-tool-background);\n  --color-an-tool-border-color: var(--an-tool-border-color);\n  --color-an-tool-color: var(--an-tool-color);\n  --color-an-tool-color-muted: var(--an-tool-color-muted);\n  --color-an-code-background: var(--an-code-background);\n  --color-an-code-color: var(--an-code-color);\n  --color-an-diff-added-bg: var(--an-diff-added-bg);\n  --color-an-diff-added-border: var(--an-diff-added-border);\n  --color-an-diff-added-text: var(--an-diff-added-text);\n  --color-an-diff-removed-bg: var(--an-diff-removed-bg);\n  --color-an-diff-removed-border: var(--an-diff-removed-border);\n  --color-an-diff-removed-text: var(--an-diff-removed-text);\n  --radius-an-message: var(--an-message-border-radius);\n  --radius-an-message-inner: calc(\n    var(--an-message-border-radius) - var(--an-message-radius-inner-offset)\n  );\n  --radius-an-input-border-radius: var(--an-input-border-radius);\n  --radius-an-tool-border-radius: var(--an-tool-border-radius);\n  --spacing-an-context-padding: var(--an-context-padding);\n  --max-width-an: var(--an-max-width);\n  --spacing-an-user-message-x: 14px;\n  --spacing-an-user-message-y: 10px;\n}\n\n/* TextShimmer animation */\n@keyframes an-shimmer {\n  from {\n    background-position: 100% center;\n  }\n  to {\n    background-position: 0% center;\n  }\n}\n\n@keyframes an-blink {\n  50% {\n    opacity: 0;\n  }\n}\n\n@keyframes loading-dots {\n  0%,\n  100% {\n    opacity: 0;\n  }\n  50% {\n    opacity: 1;\n  }\n}\n\n@keyframes an-ellipsis {\n  0% {\n    width: 0;\n  }\n  33% {\n    width: 0.33em;\n  }\n  66% {\n    width: 0.66em;\n  }\n  100% {\n    width: 1em;\n  }\n}\n\n.an-text-shimmer {\n  display: inline-block;\n  background-size: 250% 100%;\n  background-clip: text;\n  -webkit-background-clip: text;\n  color: transparent;\n  background-image: linear-gradient(\n    90deg,\n    var(--an-foreground-subtle, #a3a3a3) 0%,\n    var(--an-foreground-subtle, #a3a3a3) 40%,\n    var(--an-foreground-muted, #737373) 50%,\n    var(--an-foreground-subtle, #a3a3a3) 60%,\n    var(--an-foreground-subtle, #a3a3a3) 100%\n  );\n  background-repeat: no-repeat;\n}\n\n.an-text-shimmer--active {\n  animation: an-shimmer var(--an-shimmer-duration, 2s) linear infinite;\n}\n\n.an-ellipsis {\n  display: inline-block;\n  overflow: hidden;\n  width: 0;\n  vertical-align: bottom;\n  animation: an-ellipsis 1.2s steps(1, end) infinite;\n}\n\n.an-markdown pre code {\n  counter-reset: none !important;\n}\n\n.an-markdown pre code > span::before {\n  content: none !important;\n  display: none !important;\n}\n\n.an-markdown pre code > span {\n  padding-left: 0 !important;\n}\n\n/* Diff */\n.an-edit-diff,\n.an-edit-diff pre,\n.an-edit-diff code {\n  font-size: 12px;\n}\n\n.dark .an-edit-tool-card {\n  background-color: #000;\n}\n\n[data-theme=\"dark\"] .an-edit-tool-card {\n  background-color: #000;\n}\n\n.dark .an-edit-tool-card .an-edit-diff,\n.dark .an-edit-tool-card .an-edit-diff pre,\n.dark .an-edit-tool-card .an-edit-diff code {\n  background-color: #000 !important;\n}\n\n[data-theme=\"dark\"] .an-edit-tool-card .an-edit-diff,\n[data-theme=\"dark\"] .an-edit-tool-card .an-edit-diff pre,\n[data-theme=\"dark\"] .an-edit-tool-card .an-edit-diff code {\n  background-color: #000 !important;\n}\n\n/* Markdown Code Block */\n.an-markdown [data-streamdown=\"code-block\"] {\n  padding: 0;\n  border: 1px solid var(--an-border-color);\n  border-radius: var(--an-tool-border-radius);\n  background: transparent;\n  gap: 0;\n}\n\n.an-markdown [data-streamdown=\"code-block-header\"] {\n  padding: 0px 10px 0px 8px;\n  height: auto;\n  font-size: 12px;\n  height: 28px;\n  background: transparent;\n  border-bottom: 1px solid var(--color-an-tool-border-color);\n  background-color: var(--an-tool-background);\n  position: relative;\n}\n\n.an-markdown div:has(> [data-streamdown=\"code-block-actions\"]) {\n  position: absolute;\n  height: auto;\n  margin: 0;\n  top: 0;\n  right: 0;\n  height: 28px;\n  padding-right: 8px;\n}\n\n.an-markdown [data-streamdown=\"code-block-actions\"] {\n  background: transparent;\n  border: none;\n  backdrop-filter: none;\n  padding: 0;\n}\n\n.an-markdown [data-streamdown=\"code-block-actions\"] button > svg {\n  width: 14px;\n  height: 14px;\n}\n\n.an-markdown [data-streamdown=\"code-block-body\"] {\n  margin: 0;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  background: transparent;\n  border: transparent;\n  overflow-x: auto;\n}\n\n.an-markdown [data-streamdown=\"code-block\"] pre,\n.an-markdown [data-streamdown=\"code-block\"] code {\n  font-size: 12px;\n  background: transparent;\n}\n",
      "type": "registry:style",
      "target": "components/agent-elements/agent-ui.css"
    }
  ],
  "css": {
    "@import \"../components/agent-elements/agent-ui.css\"": ""
  }
}