{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "edit-tool",
  "type": "registry:ui",
  "dependencies": [
    "@pierre/diffs",
    "@tabler/icons-react",
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [
    "https://agent-elements.21st.dev/r/text-shimmer.json"
  ],
  "files": [
    {
      "path": "registry/agent-elements/components/tools/edit-tool.tsx",
      "content": "import React, { memo } from \"react\";\nimport { MultiFileDiff, type FileContents } from \"@pierre/diffs/react\";\nimport { TextShimmer } from \"../text-shimmer\";\nimport type { TimelineStep, StepState } from \"../types/timeline\";\nimport { useToolComplete } from \"../hooks/use-tool-complete\";\nimport { IconChevronDown } from \"@tabler/icons-react\";\nimport { FileExtIcon } from \"../icons/file-ext-icon\";\nimport {\n  mapToolInvocationToStep,\n  mapToolStateToStepState,\n} from \"../utils/tool-adapters\";\nimport { ToolApprovalFooter, type ToolApproval } from \"./tool-approval-footer\";\n\nexport type EditToolDiffCardProps = {\n  step: Extract<TimelineStep, { type: \"tool-call\" }>;\n  state: StepState;\n  onComplete: () => void;\n  input?: Record<string, unknown>;\n  output?: Record<string, unknown>;\n  isCollapsible?: boolean;\n  approval?: ToolApproval;\n};\n\nexport function EditToolDiffCard({\n  step,\n  state,\n  onComplete,\n  input,\n  output,\n  isCollapsible = false,\n  approval,\n}: EditToolDiffCardProps) {\n  useToolComplete(state === \"animating\", step.duration, onComplete);\n  const isPending = state === \"animating\";\n  const fileName = step.filePath?.split(\"/\").pop() ?? step.toolDetail;\n  const hasFileName = Boolean(fileName);\n  const isWrite = step.toolName === \"Write\";\n  const [themeType, setThemeType] = React.useState<\"light\" | \"dark\">(\"light\");\n  const [isExpanded, setIsExpanded] = React.useState(!isCollapsible);\n\n  React.useEffect(() => {\n    if (typeof window === \"undefined\") return;\n    const updateTheme = () => {\n      const isDark = document.documentElement.classList.contains(\"dark\");\n      setThemeType(isDark ? \"dark\" : \"light\");\n    };\n    updateTheme();\n\n    const observer = new MutationObserver(updateTheme);\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    });\n\n    return () => {\n      observer.disconnect();\n    };\n  }, []);\n\n  React.useEffect(() => {\n    setIsExpanded(!isCollapsible);\n  }, [isCollapsible]);\n\n  const diffFiles = React.useMemo(() => {\n    const fileLabel = fileName || \"file\";\n    const oldFromOutput =\n      typeof output?.old_content === \"string\" ? output.old_content : undefined;\n    const newFromOutput =\n      typeof output?.content === \"string\" ? output.content : undefined;\n    const oldFromInput =\n      !oldFromOutput && typeof input?.old_string === \"string\"\n        ? input.old_string\n        : undefined;\n    const newFromInput =\n      !newFromOutput && typeof input?.new_string === \"string\"\n        ? input.new_string\n        : undefined;\n\n    const fallbackOld = step.diffLines\n      ?.filter((line) => line.type !== \"add\")\n      .map((line) => line.content)\n      .join(\"\\n\");\n    const fallbackNew = step.diffLines\n      ?.filter((line) => line.type !== \"remove\")\n      .map((line) => line.content)\n      .join(\"\\n\");\n\n    const oldContents = oldFromInput ?? oldFromOutput ?? fallbackOld ?? \"\";\n    const newContents = newFromInput ?? newFromOutput ?? fallbackNew ?? \"\";\n\n    if (!oldContents && !newContents) return null;\n\n    const oldFile: FileContents = {\n      name: fileLabel,\n      contents: oldContents,\n    };\n    const newFile: FileContents = {\n      name: fileLabel,\n      contents: newContents,\n    };\n\n    return { oldFile, newFile };\n  }, [fileName, input, output, step.diffLines]);\n\n  const diffCssVars = React.useMemo(\n    () =>\n      themeType === \"dark\"\n        ? ({\n            \"--diffs-bg\": \"#000\",\n            \"--diffs-bg-buffer-override\": \"#000\",\n            \"--diffs-bg-context-override\": \"#000\",\n            \"--diffs-bg-hover-override\": \"#0a0a0a\",\n            \"--diffs-bg-separator-override\": \"#0f0f0f\",\n          } as React.CSSProperties)\n        : undefined,\n    [themeType],\n  );\n\n  const diffUnsafeCss = React.useMemo(\n    () =>\n      themeType === \"dark\"\n        ? `\n[data-diff],\n[data-file],\n[data-diffs-header],\n[data-error-wrapper],\n[data-virtualizer-buffer] {\n  --diffs-bg: #000;\n  --diffs-bg-buffer-override: #000;\n  --diffs-bg-context-override: #000;\n  --diffs-bg-hover-override: #0a0a0a;\n  --diffs-bg-separator-override: #0f0f0f;\n}\n`\n        : undefined,\n    [themeType],\n  );\n\n  const diffClassName =\n    \"an-edit-diff dark:bg-black dark:[--diffs-bg:#000] dark:[--diffs-bg-buffer-override:#000] dark:[--diffs-bg-context-override:#000] dark:[--diffs-bg-hover-override:#0a0a0a] dark:[--diffs-bg-separator-override:#0f0f0f]\";\n\n  return (\n    <div className=\"an-edit-tool-card rounded-an-tool-border-radius border border-an-tool-border-color bg-an-tool-background dark:bg-black overflow-hidden\">\n      <div\n        className={\n          // Explicit bg-an-tool-background so the header keeps its light-grey\n          // contrast in dark mode — the wrapper forces `dark:bg-black` for the\n          // diff body, which would otherwise bleed into the header.\n          \"flex items-center justify-between px-2.5 py-0 h-7 bg-an-tool-background \" +\n          (isPending && !diffFiles\n            ? \"\"\n            : \"border-b border-an-tool-border-color\")\n        }\n      >\n        <div className=\"flex items-center gap-1.5 min-w-0\">\n          {hasFileName && (\n            <FileExtIcon filename={fileName} className=\"w-3 h-3 shrink-0\" />\n          )}\n          {isPending && !diffFiles ? (\n            <TextShimmer as=\"span\" duration={1.2} className=\"text-xs\">\n              Generating...\n            </TextShimmer>\n          ) : isPending ? (\n            <TextShimmer as=\"span\" duration={1.2} className=\"text-xs\">\n              {isWrite ? \"Creating\" : \"Editing\"} {fileName}\n            </TextShimmer>\n          ) : (\n            <span className=\"text-xs text-an-tool-color-muted truncate\">\n              {isWrite ? \"Created\" : \"Edited\"} {fileName}\n            </span>\n          )}\n        </div>\n        {step.diffStats && !isPending && (\n          <span className=\"text-[11px] font-mono text-an-tool-color-muted inline-flex gap-2\">\n            {step.diffStats.split(\" \").map((token) => (\n              <span\n                key={token}\n                className={\n                  token.startsWith(\"+\")\n                    ? \"text-an-diff-added-text\"\n                    : token.startsWith(\"-\")\n                      ? \"text-an-diff-removed-text\"\n                      : undefined\n                }\n              >\n                {token}\n              </span>\n            ))}\n          </span>\n        )}\n      </div>\n      {diffFiles ? (\n        <div className={`${diffClassName} text-[12px]`} style={diffCssVars}>\n          <div\n            className={isCollapsible ? \"group/edit-diff relative\" : \"relative\"}\n          >\n            <div\n              className={\n                isCollapsible && !isExpanded\n                  ? \"max-h-[260px] overflow-hidden\"\n                  : undefined\n              }\n            >\n              <MultiFileDiff\n                key={themeType}\n                oldFile={diffFiles.oldFile}\n                newFile={diffFiles.newFile}\n                className={diffClassName}\n                style={diffCssVars}\n                options={{\n                  theme: { dark: \"github-dark\", light: \"github-light\" },\n                  themeType,\n                  unsafeCSS: diffUnsafeCss,\n                  diffStyle: \"unified\",\n                  disableFileHeader: true,\n                }}\n              />\n            </div>\n            {isCollapsible && (\n              <>\n                <button\n                  type=\"button\"\n                  onClick={() => setIsExpanded((prev) => !prev)}\n                  aria-label={isExpanded ? \"Hide\" : \"Show more\"}\n                  className={\n                    \"group absolute inset-x-0 bottom-0 h-16 flex items-end justify-center pb-2 text-muted-foreground \" +\n                    (isExpanded\n                      ? \"bg-transparent\"\n                      : \"bg-linear-to-b from-transparent to-background\")\n                  }\n                >\n                  <IconChevronDown\n                    className={\n                      \"w-4 h-4 transition-opacity duration-150 opacity-0 group-hover:opacity-100 \" +\n                      (isExpanded ? \"rotate-180\" : \"rotate-0\")\n                    }\n                  />\n                </button>\n              </>\n            )}\n          </div>\n        </div>\n      ) : null}\n      {approval && <ToolApprovalFooter isPending={isPending} {...approval} />}\n    </div>\n  );\n}\n\nexport type EditToolProps = {\n  part: any;\n  isCollapsible?: boolean;\n};\n\nexport const EditTool = memo(function EditTool({\n  part,\n  isCollapsible = false,\n}: EditToolProps) {\n  const approval = (part.input?.approval ?? part.args?.approval) as\n    | ToolApproval\n    | undefined;\n  const toolName = (part.type as string)?.replace(\"tool-\", \"\") || \"Edit\";\n  const step = mapToolInvocationToStep(part.toolCallId ?? part.id ?? \"edit\", {\n    toolName,\n    args: part.input ?? part.args ?? {},\n    state:\n      part.state === \"output-available\"\n        ? \"result\"\n        : part.state === \"input-streaming\"\n          ? \"partial-call\"\n          : \"call\",\n    result: part.output ?? part.result,\n  });\n  const stepState = mapToolStateToStepState(\n    part.state === \"output-available\"\n      ? \"result\"\n      : part.state === \"input-streaming\"\n        ? \"partial-call\"\n        : \"call\",\n  );\n  const noop = () => {};\n\n  return (\n    <EditToolDiffCard\n      step={step}\n      state={stepState}\n      onComplete={noop}\n      input={part.input ?? part.args}\n      output={part.output ?? part.result}\n      isCollapsible={isCollapsible}\n      approval={approval}\n    />\n  );\n});\n",
      "type": "registry:ui",
      "target": "components/agent-elements/tools/edit-tool.tsx"
    },
    {
      "path": "registry/agent-elements/types/timeline.ts",
      "content": "export type StepState = \"pending\" | \"animating\" | \"complete\";\n\nexport type TimelineStep =\n  | {\n      id: string;\n      type: \"input-typing\";\n      content: string;\n      image?: string;\n      duration: number;\n    }\n  | {\n      id: string;\n      type: \"user-message\";\n      content: string;\n      image?: string;\n    }\n  | {\n      id: string;\n      type: \"tool-call\";\n      toolName: string;\n      toolDetail: string;\n      duration: number;\n      toolVariant?: \"thinking\" | \"action\" | \"search\";\n      thoughtContent?: string;\n      searchQuery?: string;\n      searchSource?: string;\n      filePath?: string;\n      diffStats?: string;\n      diffLines?: { type: \"add\" | \"remove\" | \"context\"; content: string }[];\n      bashCommand?: string;\n      bashOutput?: string;\n      bashSuccess?: boolean;\n    }\n  | {\n      id: string;\n      type: \"assistant-stream\";\n      content: string;\n    }\n  | {\n      id: string;\n      type: \"pause\";\n      duration: number;\n    };\n\nexport type Turn = { userStep?: TimelineStep; steps: TimelineStep[] };\n",
      "type": "registry:lib",
      "target": "components/agent-elements/types/timeline.ts"
    },
    {
      "path": "registry/agent-elements/hooks/use-tool-complete.ts",
      "content": "import { useEffect, useRef } from \"react\";\n\nexport function useToolComplete(\n  isAnimating: boolean,\n  duration: number,\n  onComplete: () => void,\n) {\n  const onCompleteRef = useRef(onComplete);\n  onCompleteRef.current = onComplete;\n\n  useEffect(() => {\n    if (!isAnimating) return;\n    const t = setTimeout(() => onCompleteRef.current(), duration);\n    return () => clearTimeout(t);\n  }, [isAnimating, duration]);\n}\n",
      "type": "registry:lib",
      "target": "components/agent-elements/hooks/use-tool-complete.ts"
    },
    {
      "path": "registry/agent-elements/icons/file-ext-icon.tsx",
      "content": "export function FileExtIcon({\n  filename,\n  className,\n}: {\n  filename: string;\n  className?: string;\n}) {\n  const ext = filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n  const cls = className ?? \"w-2.5 h-2.5 shrink-0\";\n\n  if (ext === \"ts\" || ext === \"tsx\") {\n    return (\n      <svg viewBox=\"0 0 32 32\" className={cls}>\n        <path\n          d=\"M23.827,8.243A4.424,4.424,0,0,1,26.05,9.524a5.853,5.853,0,0,1,.852,1.143c.011.045-1.534,1.083-2.471,1.662-.034.023-.169-.124-.322-.35a2.014,2.014,0,0,0-1.67-1c-1.077-.074-1.771.49-1.766,1.433a1.3,1.3,0,0,0,.153.666c.237.49.677.784,2.059,1.383,2.544,1.095,3.636,1.817,4.31,2.843a5.158,5.158,0,0,1,.416,4.333,4.764,4.764,0,0,1-3.932,2.815,10.9,10.9,0,0,1-2.708-.028,6.531,6.531,0,0,1-3.616-1.884,6.278,6.278,0,0,1-.926-1.371,2.655,2.655,0,0,1,.327-.208c.158-.09.756-.434,1.32-.761L19.1,19.6l.214.312a4.771,4.771,0,0,0,1.35,1.292,3.3,3.3,0,0,0,3.458-.175,1.545,1.545,0,0,0,.2-1.974c-.276-.395-.84-.727-2.443-1.422a8.8,8.8,0,0,1-3.349-2.055,4.687,4.687,0,0,1-.976-1.777,7.116,7.116,0,0,1-.062-2.268,4.332,4.332,0,0,1,3.644-3.374A9,9,0,0,1,23.827,8.243ZM15.484,9.726l.011,1.454h-4.63V24.328H7.6V11.183H2.97V9.755A13.986,13.986,0,0,1,3.01,8.289c.017-.023,2.832-.034,6.245-.028l6.211.017Z\"\n          fill=\"#007acc\"\n        />\n      </svg>\n    );\n  }\n  if (ext === \"js\" || ext === \"mjs\" || ext === \"cjs\" || ext === \"jsx\") {\n    return (\n      <svg viewBox=\"0 0 32 32\" className={cls}>\n        <rect x=\"2\" y=\"2\" width=\"28\" height=\"28\" rx=\"1.312\" fill=\"#f5de19\" />\n        <path\n          d=\"M20.809,23.875a2.866,2.866,0,0,0,2.6,1.6c1.09,0,1.787-.545,1.787-1.3,0-.9-.715-1.222-1.916-1.747l-.658-.282c-1.9-.809-3.16-1.822-3.16-3.964,0-1.973,1.5-3.476,3.853-3.476a3.889,3.889,0,0,1,3.742,2.107L25,18.128A1.789,1.789,0,0,0,23.311,17a1.145,1.145,0,0,0-1.259,1.128c0,.789.489,1.109,1.618,1.6l.658.282c2.236.959,3.5,1.936,3.5,4.133,0,2.369-1.861,3.667-4.36,3.667a5.055,5.055,0,0,1-4.795-2.691Zm-9.295.228c.413.733.789,1.353,1.693,1.353.864,0,1.41-.338,1.41-1.653V14.856h2.631v8.982c0,2.724-1.6,3.964-3.929,3.964a4.085,4.085,0,0,1-3.947-2.4Z\"\n          fill=\"#000\"\n        />\n      </svg>\n    );\n  }\n  if (ext === \"json\" || ext === \"jsonc\") {\n    return (\n      <svg viewBox=\"0 0 32 32\" className={cls}>\n        <path\n          d=\"M4.014,14.976a2.51,2.51,0,0,0,1.567-.518A2.377,2.377,0,0,0,6.386,13l.129-1.334A5.435,5.435,0,0,1,7.6,9.071,3.157,3.157,0,0,1,10.519,8h1.316v1.86H10.937a1.257,1.257,0,0,0-1.1.465,3.405,3.405,0,0,0-.357,1.585l-.127,1.244a3.283,3.283,0,0,1-.573,1.61A2.482,2.482,0,0,1,7.6,15.537a2.482,2.482,0,0,1,1.18.773A3.283,3.283,0,0,1,9.349,17.9l.127,1.244a3.405,3.405,0,0,0,.357,1.585,1.257,1.257,0,0,0,1.1.465h.9V23.06H10.519A3.157,3.157,0,0,1,7.6,21.981a5.435,5.435,0,0,1-1.082-2.595L6.386,18.05a2.377,2.377,0,0,0-.805-1.456A2.51,2.51,0,0,0,4.014,16.07Z\"\n          fill=\"#fbc02d\"\n        />\n        <path\n          d=\"M27.986,16.07a2.51,2.51,0,0,0-1.567.518,2.377,2.377,0,0,0-.805,1.456l-.129,1.334a5.435,5.435,0,0,1-1.082,2.595A3.157,3.157,0,0,1,21.481,23.06H20.165V21.2h.9a1.257,1.257,0,0,0,1.1-.465,3.405,3.405,0,0,0,.357-1.585l.127-1.244a3.283,3.283,0,0,1,.573-1.61,2.482,2.482,0,0,1,1.18-.773,2.482,2.482,0,0,1-1.18-.773,3.283,3.283,0,0,1-.573-1.61L22.527,11.9a3.405,3.405,0,0,0-.357-1.585,1.257,1.257,0,0,0-1.1-.465h-.9V8h1.316a3.157,3.157,0,0,1,2.924,1.071,5.435,5.435,0,0,1,1.082,2.595l.129,1.334A2.377,2.377,0,0,0,26.419,14.458a2.51,2.51,0,0,0,1.567.518Z\"\n          fill=\"#fbc02d\"\n        />\n      </svg>\n    );\n  }\n\n  return null;\n}\n",
      "type": "registry:ui",
      "target": "components/agent-elements/icons/file-ext-icon.tsx"
    },
    {
      "path": "registry/agent-elements/utils/tool-adapters.ts",
      "content": "import type { TimelineStep, StepState } from \"../types/timeline\";\n\nfunction calculateDiffStatsFromPatch(\n  patches: Array<{ lines?: string[] }>,\n): string | undefined {\n  let addedLines = 0;\n  let removedLines = 0;\n\n  for (const patch of patches) {\n    if (!patch.lines) continue;\n    for (const line of patch.lines) {\n      if (line.startsWith(\"+\")) addedLines++;\n      else if (line.startsWith(\"-\")) removedLines++;\n    }\n  }\n\n  if (addedLines === 0 && removedLines === 0) return undefined;\n\n  const parts: string[] = [];\n  if (addedLines > 0) parts.push(`+${addedLines}`);\n  if (removedLines > 0) parts.push(`-${removedLines}`);\n  return parts.join(\" \");\n}\n\nfunction getDiffLinesFromPatch(\n  patches: Array<{ lines?: string[] }>,\n): { type: \"add\" | \"remove\" | \"context\"; content: string }[] {\n  const result: { type: \"add\" | \"remove\" | \"context\"; content: string }[] = [];\n\n  for (const patch of patches) {\n    if (!patch.lines) continue;\n    for (const line of patch.lines) {\n      if (line.startsWith(\"+\")) {\n        result.push({ type: \"add\", content: line.slice(1) });\n      } else if (line.startsWith(\"-\")) {\n        result.push({ type: \"remove\", content: line.slice(1) });\n      } else if (line.startsWith(\" \")) {\n        result.push({ type: \"context\", content: line.slice(1) });\n      }\n    }\n  }\n\n  return result;\n}\n\nexport function mapToolStateToStepState(\n  aiState: \"partial-call\" | \"call\" | \"result\",\n): StepState {\n  return aiState === \"result\" ? \"complete\" : \"animating\";\n}\n\nexport function mapToolNameToVariant(\n  toolName: string,\n): \"thinking\" | \"action\" | \"search\" | undefined {\n  const lower = toolName.toLowerCase();\n  if (lower === \"thinking\" || lower === \"reasoning\") return \"thinking\";\n  if (\n    lower === \"websearch\" ||\n    lower === \"web_search\" ||\n    lower === \"grep\" ||\n    lower === \"glob\" ||\n    lower === \"webfetch\" ||\n    lower === \"web_fetch\"\n  )\n    return \"search\";\n  return undefined;\n}\n\nfunction extractToolDetail(\n  toolName: string,\n  args: Record<string, any>,\n): string {\n  switch (toolName) {\n    case \"Bash\":\n      return args?.command ? String(args.command).slice(0, 80) : \"\";\n    case \"Edit\":\n    case \"Write\":\n    case \"Read\":\n      return args?.file_path\n        ? (String(args.file_path).split(\"/\").pop() ?? \"\")\n        : \"\";\n    case \"Grep\":\n      return args?.pattern ? String(args.pattern) : \"\";\n    case \"Glob\":\n      return args?.pattern ? String(args.pattern) : \"\";\n    case \"WebSearch\":\n    case \"web_search\":\n      return args?.query ? String(args.query) : \"\";\n    case \"WebFetch\":\n    case \"web_fetch\":\n      return args?.url ? String(args.url).slice(0, 60) : \"\";\n    default:\n      return \"\";\n  }\n}\n\nexport function mapToolInvocationToStep(\n  toolCallId: string,\n  toolInvocation: {\n    toolName: string;\n    args?: Record<string, any>;\n    state: \"partial-call\" | \"call\" | \"result\";\n    result?: any;\n  },\n): Extract<TimelineStep, { type: \"tool-call\" }> {\n  const { toolName, args = {}, result } = toolInvocation;\n  const displayToolName =\n    toolName === \"PlanWrite\"\n      ? \"Plan\"\n      : toolName === \"TodoWrite\"\n        ? \"Todo\"\n        : toolName;\n  const detail = extractToolDetail(toolName, args);\n\n  const step: Extract<TimelineStep, { type: \"tool-call\" }> = {\n    id: toolCallId,\n    type: \"tool-call\",\n    toolName: displayToolName,\n    toolDetail: detail,\n    duration: Number.MAX_SAFE_INTEGER,\n    toolVariant: mapToolNameToVariant(toolName),\n  };\n\n  if (toolName === \"Bash\") {\n    step.bashCommand = args?.command ? String(args.command) : undefined;\n    if (toolInvocation.state === \"result\" && result) {\n      if (typeof result === \"string\") {\n        step.bashOutput = result;\n        step.bashSuccess = true;\n      } else if (typeof result === \"object\") {\n        const stdout =\n          typeof result?.stdout === \"string\"\n            ? result.stdout\n            : typeof result?.output === \"string\"\n              ? result.output\n              : \"\";\n        const stderr = typeof result?.stderr === \"string\" ? result.stderr : \"\";\n        step.bashOutput = [stdout, stderr]\n          .filter(Boolean)\n          .join(stdout && stderr ? \"\\n\" : \"\");\n        const exitCode = result?.exitCode ?? result?.exit_code;\n        step.bashSuccess = exitCode === undefined ? true : exitCode === 0;\n      } else {\n        step.bashOutput = JSON.stringify(result);\n        step.bashSuccess = true;\n      }\n    }\n  }\n\n  if (toolName === \"Edit\" || toolName === \"Write\" || toolName === \"Read\") {\n    step.filePath = args?.file_path ? String(args.file_path) : undefined;\n  }\n\n  if (toolName === \"Write\") {\n    const content =\n      typeof result?.content === \"string\"\n        ? result.content\n        : typeof args?.content === \"string\"\n          ? args.content\n          : \"\";\n\n    if (content) {\n      const lines = content.split(\"\\n\");\n      step.diffStats = `+${lines.length}`;\n      step.diffLines = lines.map((line: string) => ({\n        type: \"add\",\n        content: line,\n      }));\n    }\n  }\n\n  if (toolName === \"Edit\" && Array.isArray(result?.structuredPatch)) {\n    step.diffStats = calculateDiffStatsFromPatch(result.structuredPatch);\n    step.diffLines = getDiffLinesFromPatch(result.structuredPatch);\n  }\n\n  if (\n    toolName === \"WebSearch\" ||\n    toolName === \"web_search\" ||\n    toolName === \"Grep\" ||\n    toolName === \"Glob\"\n  ) {\n    step.searchQuery =\n      (args?.query ?? args?.pattern)\n        ? String(args?.query ?? args?.pattern)\n        : undefined;\n    step.searchSource =\n      toolName === \"WebSearch\" || toolName === \"web_search\" ? \"web\" : \"code\";\n  }\n\n  if (\n    toolName.toLowerCase() === \"thinking\" ||\n    toolName.toLowerCase() === \"reasoning\"\n  ) {\n    step.thoughtContent =\n      typeof args?.thought === \"string\"\n        ? args.thought\n        : typeof result === \"string\"\n          ? result\n          : undefined;\n  }\n\n  return step;\n}\n",
      "type": "registry:lib",
      "target": "components/agent-elements/utils/tool-adapters.ts"
    },
    {
      "path": "registry/agent-elements/components/tools/tool-approval-footer.tsx",
      "content": "import { memo, useMemo, useState } from \"react\";\n\nexport type ToolApproval = {\n  approveLabel?: string;\n  rejectLabel?: string;\n  onApprove?: () => void;\n  onReject?: () => void;\n};\n\nexport type ToolApprovalFooterProps = ToolApproval & {\n  isPending?: boolean;\n};\n\nexport const ToolApprovalFooter = memo(function ToolApprovalFooter({\n  isPending,\n  approveLabel,\n  rejectLabel,\n  onApprove,\n  onReject,\n}: ToolApprovalFooterProps) {\n  const [decision, setDecision] = useState<\"approved\" | \"rejected\" | null>(\n    null,\n  );\n\n  const approveText =\n    decision === \"approved\" ? \"Approved\" : (approveLabel ?? \"Next\");\n  const rejectText =\n    decision === \"rejected\" ? \"Skipped\" : (rejectLabel ?? \"Skip\");\n\n  const handleApprove = () => {\n    if (decision) return;\n    setDecision(\"approved\");\n    onApprove?.();\n  };\n\n  const handleReject = () => {\n    if (decision) return;\n    setDecision(\"rejected\");\n    onReject?.();\n  };\n\n  const statusConfig = useMemo(() => {\n    if (decision === \"approved\") return { label: \"Waiting\", dots: true };\n    if (decision === \"rejected\") return { label: \"Canceled\", dots: false };\n    if (isPending) return { label: \"Starting\", dots: true };\n    // Default \"ready\" state — buttons themselves communicate the affordance,\n    // an extra \"Ready\" label just adds noise. Render an empty spacer so the\n    // buttons stay right-aligned via justify-between.\n    return null;\n  }, [decision, isPending]);\n\n  return (\n    <div className=\"flex items-center justify-between py-1 pl-3 pr-2 border-t border-border bg-an-tool-background\">\n      {statusConfig ? (\n        <span className=\"text-xs text-an-tool-color-muted\">\n          {statusConfig.label}\n          {statusConfig.dots && (\n            <span className=\"inline-flex\" aria-hidden=\"true\">\n              <span className=\"text-an-tool-color-muted animate-[loading-dots_1.4s_infinite_0.2s]\">\n                .\n              </span>\n              <span className=\"text-an-tool-color-muted animate-[loading-dots_1.4s_infinite_0.4s]\">\n                .\n              </span>\n              <span className=\"text-an-tool-color-muted animate-[loading-dots_1.4s_infinite_0.6s]\">\n                .\n              </span>\n            </span>\n          )}\n        </span>\n      ) : (\n        <span aria-hidden=\"true\" />\n      )}\n      <div className=\"flex gap-1\">\n        <button\n          type=\"button\"\n          onClick={handleReject}\n          disabled={Boolean(decision)}\n          className=\"h-5 px-1.5 rounded-[4px] text-xs text-muted-foreground hover:text-an-tool-color hover:bg-muted/50 active:scale-[0.98] transition-[background-color,color,transform] duration-150 disabled:opacity-60 disabled:hover:bg-transparent disabled:active:scale-100\"\n        >\n          {rejectText}\n        </button>\n        <button\n          type=\"button\"\n          onClick={handleApprove}\n          disabled={Boolean(decision)}\n          className=\"h-5 px-1.5 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 disabled:opacity-60 disabled:hover:bg-an-primary-color disabled:active:scale-100\"\n        >\n          {approveText}\n        </button>\n      </div>\n    </div>\n  );\n});\n",
      "type": "registry:ui",
      "target": "components/agent-elements/tools/tool-approval-footer.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/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\"": ""
  }
}