{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool-calls-section",
  "type": "registry:ui",
  "title": "Tool Calls Section",
  "description": "An expandable section showing AI agent tool usage with icons, inputs, and outputs.",
  "registryDependencies": [
    "icons"
  ],
  "files": [
    {
      "path": "registry/new-york/ui/tool-calls-section.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useMemo, useState } from \"react\";\n\nimport { HugeiconsIcon, ArrowDown01Icon, ToolsIcon } from \"@/components/icons\";\nimport { cn } from \"@/lib/utils\";\nimport { formatToolName, getToolCategoryIcon } from \"@/lib/utils/tool-icons\";\nimport { CompactMarkdown } from \"@/registry/new-york/ui/compact-markdown\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ToolCallEntry {\n\t/** Name of the tool that was called */\n\ttool_name: string;\n\t/** Category/integration the tool belongs to (e.g., \"gmail\", \"search\", \"memory\") */\n\ttool_category: string;\n\t/** Human-readable message describing what the tool did */\n\tmessage?: string;\n\t/** Whether to show the category label (default: true) */\n\tshow_category?: boolean;\n\t/** Unique ID for this tool call */\n\ttool_call_id?: string;\n\t/** Input parameters passed to the tool */\n\tinputs?: Record<string, unknown>;\n\t/** Output/result from the tool */\n\toutput?: string;\n\t/** URL to custom icon for integrations */\n\ticon_url?: string;\n\t/** Friendly name for the integration (e.g., \"Linear\", \"Slack\") */\n\tintegration_name?: string;\n}\n\nexport interface IntegrationInfo {\n\ticonUrl?: string;\n\tname?: string;\n}\n\nexport interface ToolCallsSectionProps {\n\t/** Array of tool call entries to display */\n\ttoolCalls: ToolCallEntry[];\n\t/** Optional map of integration IDs to their info for icon/name lookup */\n\tintegrations?: Map<string, IntegrationInfo>;\n\t/** Maximum number of icons to show in the stacked display (default: 10) */\n\tmaxIconsToShow?: number;\n\t/** Whether to start with the accordion expanded (default: false) */\n\tdefaultExpanded?: boolean;\n\t/** Custom class name for the container */\n\tclassName?: string;\n\t/** Custom icon size (default: 21) */\n\ticonSize?: number;\n\t/** Custom icon renderer override */\n\trenderIcon?: (call: ToolCallEntry, size: number) => ReactNode;\n\t/** Custom content renderer override for inputs/outputs */\n\trenderContent?: (content: unknown) => ReactNode;\n}\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\ninterface ChevronIconProps {\n\tisExpanded: boolean;\n\tsize?: number;\n\tclassName?: string;\n}\n\nfunction ChevronIcon({ isExpanded, size = 18, className = \"\" }: ChevronIconProps) {\n\treturn (\n\t\t<HugeiconsIcon\n\t\t\ticon={ArrowDown01Icon}\n\t\t\tsize={size}\n\t\t\tclassName={cn(\n\t\t\t\t\"transition-transform duration-200\",\n\t\t\t\tisExpanded && \"rotate-180\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t/>\n\t);\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function ToolCallsSection({\n\ttoolCalls,\n\tintegrations,\n\tmaxIconsToShow = 10,\n\tdefaultExpanded = false,\n\tclassName,\n\ticonSize = 21,\n\trenderIcon,\n\trenderContent,\n}: ToolCallsSectionProps) {\n\tconst [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\tconst [expandedCalls, setExpandedCalls] = useState<Set<number>>(new Set());\n\n\t// Create a lookup map for custom integrations by id\n\tconst integrationLookup = useMemo(() => {\n\t\tif (integrations) return integrations;\n\t\treturn new Map<string, IntegrationInfo>();\n\t}, [integrations]);\n\n\t// Helper to get icon_url with fallback to integrations lookup\n\tconst getIconUrl = (call: ToolCallEntry): string | undefined => {\n\t\tif (call.icon_url) return call.icon_url;\n\t\tconst integration = integrationLookup.get(call.tool_category);\n\t\treturn integration?.iconUrl;\n\t};\n\n\t// Helper to get integration_name with fallback to integrations lookup\n\tconst getIntegrationName = (call: ToolCallEntry): string | undefined => {\n\t\tif (call.integration_name) return call.integration_name;\n\t\tconst integration = integrationLookup.get(call.tool_category);\n\t\treturn integration?.name;\n\t};\n\n\tconst toggleCallExpansion = (index: number) => {\n\t\tsetExpandedCalls((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(index)) next.delete(index);\n\t\t\telse next.add(index);\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tif (toolCalls.length === 0) return null;\n\n\t// Default icon renderer\n\tconst defaultRenderIcon = (call: ToolCallEntry, size: number) => {\n\t\tconst icon = getToolCategoryIcon(\n\t\t\tcall.tool_category || \"general\",\n\t\t\t{ width: size, height: size },\n\t\t\tgetIconUrl(call),\n\t\t);\n\t\treturn icon || (\n\t\t\t<div className=\"p-1 min-w-8 min-h-8 bg-zinc-200 dark:bg-zinc-800 rounded-lg text-zinc-600 dark:text-zinc-400 backdrop-blur\">\n\t\t\t\t<HugeiconsIcon icon={ToolsIcon} size={size} />\n\t\t\t</div>\n\t\t);\n\t};\n\n\tconst iconRenderer = renderIcon || defaultRenderIcon;\n\n\t// Default content renderer\n\tconst defaultRenderContent = (content: unknown) => (\n\t\t<CompactMarkdown content={content} />\n\t);\n\n\tconst contentRenderer = renderContent || defaultRenderContent;\n\n\t// Render stacked rotated icons (deduplicated by category for cleaner display)\n\tconst renderStackedIcons = () => {\n\t\tconst seenCategories = new Set<string>();\n\t\tconst uniqueIcons = toolCalls.filter((call) => {\n\t\t\tconst category = call.tool_category || \"general\";\n\t\t\tif (seenCategories.has(category)) return false;\n\t\t\tseenCategories.add(category);\n\t\t\treturn true;\n\t\t});\n\t\tconst displayIcons = uniqueIcons.slice(0, maxIconsToShow);\n\n\t\treturn (\n\t\t\t<div className=\"flex min-h-8 items-center -space-x-2\">\n\t\t\t\t{displayIcons.map((call, index) => (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={`${call.tool_name}-${index}`}\n\t\t\t\t\t\tclassName=\"relative flex min-w-8 items-center justify-center\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\trotate:\n\t\t\t\t\t\t\t\tdisplayIcons.length > 1\n\t\t\t\t\t\t\t\t\t? index % 2 === 0\n\t\t\t\t\t\t\t\t\t\t? \"8deg\"\n\t\t\t\t\t\t\t\t\t\t: \"-8deg\"\n\t\t\t\t\t\t\t\t\t: \"0deg\",\n\t\t\t\t\t\t\tzIndex: index,\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{iconRenderer(call, iconSize)}\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t\t{uniqueIcons.length > maxIconsToShow && (\n\t\t\t\t\t<div className=\"z-0 flex size-7 min-h-7 min-w-7 items-center justify-center rounded-lg bg-zinc-200 dark:bg-zinc-700/60 text-xs text-zinc-600 dark:text-zinc-500 font-normal\">\n\t\t\t\t\t\t+{uniqueIcons.length - maxIconsToShow}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t};\n\n\treturn (\n\t\t<div className={cn(\"w-fit max-w-[35rem]\", className)}>\n\t\t\t{/* Collapsible Header */}\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setIsExpanded(!isExpanded)}\n\t\t\t\tclassName=\"flex items-center gap-2 hover:text-zinc-900 dark:hover:text-white text-zinc-500 cursor-pointer py-2\"\n\t\t\t>\n\t\t\t\t{renderStackedIcons()}\n\t\t\t\t<span className=\"text-xs font-medium transition-all duration-200\">\n\t\t\t\t\tUsed {toolCalls.length} tool\n\t\t\t\t\t{toolCalls.length > 1 ? \"s\" : \"\"}\n\t\t\t\t</span>\n\t\t\t\t<ChevronIcon isExpanded={isExpanded} />\n\t\t\t</button>\n\n\t\t\t{/* Collapsible Content */}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"overflow-hidden transition-all duration-200\",\n\t\t\t\t\tisExpanded ? \"max-h-[2000px] opacity-100\" : \"max-h-0 opacity-0\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<div className=\"space-y-0 pt-1\">\n\t\t\t\t\t{toolCalls.map((call, index) => {\n\t\t\t\t\t\tconst hasCategoryText =\n\t\t\t\t\t\t\tcall.show_category !== false &&\n\t\t\t\t\t\t\tcall.tool_category &&\n\t\t\t\t\t\t\tcall.tool_category !== \"unknown\";\n\t\t\t\t\t\tconst hasDetails = call.inputs || call.output;\n\t\t\t\t\t\tconst isCallExpanded = expandedCalls.has(index);\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={`${call.tool_name}-step-${index}`}\n\t\t\t\t\t\t\t\tclassName=\"flex items-stretch gap-2\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/* Icon column with connector line */}\n\t\t\t\t\t\t\t\t<div className=\"flex flex-col items-center self-stretch\">\n\t\t\t\t\t\t\t\t\t<div className=\"min-h-8 min-w-8 flex items-center justify-center shrink-0\">\n\t\t\t\t\t\t\t\t\t\t{iconRenderer(call, iconSize)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{index < toolCalls.length - 1 && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"w-px flex-1 bg-zinc-300 dark:bg-zinc-700 min-h-4\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{/* Content column */}\n\t\t\t\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-1 group/parent\",\n\t\t\t\t\t\t\t\t\t\t\thasDetails ? \"cursor-pointer\" : \"\",\n\t\t\t\t\t\t\t\t\t\t\t!hasCategoryText ? \"pt-2\" : \"\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\tonClick={() => hasDetails && toggleCallExpansion(index)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<p\n\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\"text-xs text-zinc-600 dark:text-zinc-400 font-medium\",\n\t\t\t\t\t\t\t\t\t\t\t\thasDetails && \"group-hover/parent:text-zinc-900 dark:group-hover/parent:text-white\",\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{call.message || formatToolName(call.tool_name)}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t{hasDetails && (\n\t\t\t\t\t\t\t\t\t\t\t<ChevronIcon isExpanded={isCallExpanded} size={14} />\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t{hasCategoryText && (\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-[11px] text-zinc-400 dark:text-zinc-500 capitalize\">\n\t\t\t\t\t\t\t\t\t\t\t{getIntegrationName(call) ||\n\t\t\t\t\t\t\t\t\t\t\t\tcall.tool_category\n\t\t\t\t\t\t\t\t\t\t\t\t\t.replace(/_/g, \" \")\n\t\t\t\t\t\t\t\t\t\t\t\t\t.split(\" \")\n\t\t\t\t\t\t\t\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(word) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tword.charAt(0).toUpperCase() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tword.slice(1).toLowerCase(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t.join(\" \")}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t\t{isCallExpanded && hasDetails && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"mt-2 space-y-2 text-[11px] bg-zinc-100 dark:bg-zinc-800/50 rounded-xl p-3 mb-3 w-fit\">\n\t\t\t\t\t\t\t\t\t\t\t{call.inputs && Object.keys(call.inputs).length > 0 && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-zinc-400 dark:text-zinc-500 font-medium mb-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tInput\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{contentRenderer(call.inputs)}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t{call.output && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex flex-col\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-zinc-400 dark:text-zinc-500 font-medium mb-1\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOutput\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{contentRenderer(call.output)}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default ToolCallsSection;\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/new-york/ui/compact-markdown.tsx",
      "content": "\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nexport interface CompactMarkdownProps {\n\tcontent: unknown;\n\tclassName?: string;\n}\n\n/**\n * Helper to check if a value looks like structured data (object/array/JSON)\n */\nconst isStructuredData = (value: unknown): boolean => {\n\tif (typeof value === \"object\" && value !== null) return true;\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\treturn (\n\t\t\t(trimmed.startsWith(\"{\") && trimmed.endsWith(\"}\")) ||\n\t\t\t(trimmed.startsWith(\"[\") && trimmed.endsWith(\"]\"))\n\t\t);\n\t}\n\treturn false;\n};\n\n/**\n * Try to parse and format JSON-like strings\n */\nconst formatJsonLikeString = (str: string): string => {\n\ttry {\n\t\tconst parsed = JSON.parse(str);\n\t\treturn JSON.stringify(parsed, null, 2);\n\t} catch {\n\t\t// If it looks like truncated JSON, try to format it anyway\n\t\treturn str;\n\t}\n};\n\n/**\n * Normalize content for display\n */\nconst normalizeValue = (content: unknown): { data: unknown; isStructured: boolean } => {\n\tif (typeof content === \"object\" && content !== null) {\n\t\treturn { data: content, isStructured: true };\n\t}\n\tif (typeof content === \"string\") {\n\t\tconst trimmed = content.trim();\n\t\tconst looksLikeJson =\n\t\t\t(trimmed.startsWith(\"{\") && (trimmed.endsWith(\"}\") || trimmed.includes(\"}\"))) ||\n\t\t\t(trimmed.startsWith(\"[\") && (trimmed.endsWith(\"]\") || trimmed.includes(\"]\")));\n\t\tif (looksLikeJson) {\n\t\t\treturn { data: content, isStructured: true };\n\t\t}\n\t\treturn { data: content, isStructured: false };\n\t}\n\treturn { data: String(content), isStructured: false };\n};\n\n/**\n * Compact display for structured data and markdown content.\n * Accepts any value and automatically formats it appropriately:\n * - Objects/Arrays: Pretty-printed JSON\n * - Strings that look like JSON: Formatted with indentation (even if truncated)\n * - Other strings: Simple text rendering or markdown if react-markdown available\n */\nexport function CompactMarkdown({ content, className = \"\" }: CompactMarkdownProps) {\n\tconst { data, isStructured } = normalizeValue(content);\n\n\tconst baseClasses = \"bg-zinc-900/50 rounded-xl p-3 max-h-60 overflow-y-auto text-xs text-zinc-400 w-fit max-w-[32rem]\";\n\n\t// For structured data, render as preformatted text\n\tif (isStructured) {\n\t\tconst displayText =\n\t\t\ttypeof data === \"string\"\n\t\t\t\t? formatJsonLikeString(data)\n\t\t\t\t: JSON.stringify(data, null, 2);\n\n\t\treturn (\n\t\t\t<pre className={`${baseClasses} whitespace-pre-wrap break-words ${className}`}>\n\t\t\t\t{displayText}\n\t\t\t</pre>\n\t\t);\n\t}\n\n\t// For text content, render with basic formatting\n\tconst textContent = String(data);\n\n\treturn (\n\t\t<div className={`${baseClasses} leading-relaxed ${className}`}>\n\t\t\t{textContent}\n\t\t</div>\n\t);\n}\n\nexport default CompactMarkdown;\n",
      "type": "registry:ui"
    },
    {
      "path": "lib/utils/tool-icons.tsx",
      "content": "import type { IconSvgElement } from \"@hugeicons/react\";\nimport Image from \"next/image\";\nimport {\n\tBrain02Icon,\n\tCheckListIcon,\n\tAlarmClockIcon,\n\tComputerProgramming01Icon,\n\tFile02Icon,\n\tHugeiconsIcon,\n\tImage02Icon,\n\tInformationCircleIcon,\n\tLink01Icon,\n\tNotification03Icon,\n\tPackageOpenIcon,\n\tSourceCodeCircleIcon,\n\tSquareArrowUpRight02Icon,\n\tTarget02Icon,\n\tToolsIcon,\n} from \"@/components/icons\";\n\nexport interface IconProps {\n\tsize?: number;\n\twidth?: number;\n\theight?: number;\n\tstrokeWidth?: number;\n\tclassName?: string;\n\tcolor?: string;\n}\n\n// Category-specific icons with colors\nexport interface IconConfig {\n\ticon: IconSvgElement | string;\n\tbgColor: string;\n\tbgColorLight?: string; // Light mode background\n\ticonColor: string;\n\tisImage?: boolean;\n}\n\n/**\n * Normalize a category/integration name for icon lookup\n */\nconst normalizeCategoryName = (name: string): string => {\n\tif (!name) return \"general\";\n\treturn name\n\t\t.toLowerCase()\n\t\t.trim()\n\t\t.replace(/[\\s-]+/g, \"_\")\n\t\t.replace(/_+/g, \"_\")\n\t\t.replace(/^_|_$/g, \"\");\n};\n\n// Alias mapping for backwards compatibility\nconst iconAliases: Record<string, string> = {\n\tcalendar: \"google_calendar\",\n};\n\n// Tool category icon configs - matches gaia repo pattern\nconst iconConfigs: Record<string, IconConfig> = {\n\t// Integration icons (use images)\n\tgmail: {\n\t\ticon: \"/images/icons/gmail.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tgoogle_calendar: {\n\t\ticon: \"/images/icons/googlecalendar.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tgithub: {\n\t\ticon: \"/images/icons/github.png\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tlinear: {\n\t\ticon: \"/images/icons/linear.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tslack: {\n\t\ticon: \"/images/icons/slack.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tgoogle_docs: {\n\t\ticon: \"/images/icons/google_docs.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tgooglesheets: {\n\t\ticon: \"/images/icons/googlesheets.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tsearch: {\n\t\ticon: \"/images/icons/google.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tweather: {\n\t\ticon: \"/images/icons/weather.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tnotion: {\n\t\ticon: \"/images/icons/notion.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\ttwitter: {\n\t\ticon: \"/images/icons/twitter.webp\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tlinkedin: {\n\t\ticon: \"/images/icons/linkedin.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\treddit: {\n\t\ticon: \"/images/icons/reddit.svg\",\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t\tisImage: true,\n\t},\n\tfigma: {\n\t\ticon: \"/images/icons/figma.svg\",\n\t\tbgColor: \"bg-zinc-800\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-white\",\n\t\tisImage: true,\n\t},\n\t\n\t// Category icons (use HugeIcons components)\n\ttodos: {\n\t\ticon: CheckListIcon,\n\t\tbgColor: \"bg-emerald-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-emerald-500/20\",\n\t\ticonColor: \"text-emerald-400\",\n\t},\n\treminders: {\n\t\ticon: AlarmClockIcon,\n\t\tbgColor: \"bg-sky-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-sky-500/20\",\n\t\ticonColor: \"text-blue-400\",\n\t},\n\tdocuments: {\n\t\ticon: File02Icon,\n\t\tbgColor: \"bg-orange-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-orange-500/20\",\n\t\ticonColor: \"text-orange-400\",\n\t},\n\tdevelopment: {\n\t\ticon: SourceCodeCircleIcon,\n\t\tbgColor: \"bg-sky-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-sky-500/20\",\n\t\ticonColor: \"text-cyan-400\",\n\t},\n\tmemory: {\n\t\ticon: Brain02Icon,\n\t\tbgColor: \"bg-indigo-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-indigo-500/20\",\n\t\ticonColor: \"text-indigo-400\",\n\t},\n\tcreative: {\n\t\ticon: Image02Icon,\n\t\tbgColor: \"bg-pink-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-pink-500/20\",\n\t\ticonColor: \"text-pink-400\",\n\t},\n\tgoal_tracking: {\n\t\ticon: Target02Icon,\n\t\tbgColor: \"bg-emerald-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-emerald-500/20\",\n\t\ticonColor: \"text-emerald-400\",\n\t},\n\tnotifications: {\n\t\ticon: Notification03Icon,\n\t\tbgColor: \"bg-yellow-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-yellow-500/20\",\n\t\ticonColor: \"text-yellow-400\",\n\t},\n\tsupport: {\n\t\ticon: InformationCircleIcon,\n\t\tbgColor: \"bg-sky-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-sky-500/20\",\n\t\ticonColor: \"text-blue-400\",\n\t},\n\tgeneral: {\n\t\ticon: InformationCircleIcon,\n\t\tbgColor: \"bg-gray-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-gray-500/20\",\n\t\ticonColor: \"text-gray-400\",\n\t},\n\tintegrations: {\n\t\ticon: Link01Icon,\n\t\tbgColor: \"bg-zinc-700\",\n\t\tbgColorLight: \"bg-zinc-200\",\n\t\ticonColor: \"text-zinc-200\",\n\t},\n\t\n\t// Agent tool call categories\n\thandoff: {\n\t\ticon: SquareArrowUpRight02Icon,\n\t\tbgColor: \"bg-sky-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-sky-500/20\",\n\t\ticonColor: \"text-sky-400\",\n\t},\n\tretrieve_tools: {\n\t\ticon: PackageOpenIcon,\n\t\tbgColor: \"bg-indigo-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-indigo-500/20\",\n\t\ticonColor: \"text-indigo-400\",\n\t},\n\texecutor: {\n\t\ticon: ComputerProgramming01Icon,\n\t\tbgColor: \"bg-teal-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-teal-500/20\",\n\t\ticonColor: \"text-teal-400\",\n\t},\n\tunknown: {\n\t\ticon: ToolsIcon,\n\t\tbgColor: \"bg-zinc-500/20 backdrop-blur\",\n\t\tbgColorLight: \"bg-zinc-500/20\",\n\t\ticonColor: \"text-zinc-400\",\n\t},\n};\n\n/**\n * Get icon for a tool category with optional URL-based icon fallback.\n * Supports built-in categories and custom integration icons via iconUrl.\n */\nexport const getToolCategoryIcon = (\n\tcategory: string,\n\ticonProps: Partial<IconProps> & { showBackground?: boolean } = {},\n\ticonUrl?: string | null,\n) => {\n\tconst { showBackground = true, ...restProps } = iconProps;\n\n\tconst defaultProps = {\n\t\tsize: restProps.size || 16,\n\t\twidth: restProps.width || 20,\n\t\theight: restProps.height || 20,\n\t\tstrokeWidth: restProps.strokeWidth || 2,\n\t\tclassName: restProps.className,\n\t};\n\n\t// Normalize\n\tconst normalizedCategory = normalizeCategoryName(category);\n\n\t// Resolve aliases\n\tconst aliasedCategory =\n\t\ticonAliases[normalizedCategory] ||\n\t\ticonAliases[category] ||\n\t\tnormalizedCategory;\n\n\tconst finalCategory = normalizeCategoryName(aliasedCategory);\n\n\tlet config = iconConfigs[finalCategory];\n\n\t// Fallback search\n\tif (!config) {\n\t\tconst normalizedConfigs = Object.entries(iconConfigs);\n\t\tconst matchingConfig = normalizedConfigs.find(\n\t\t\t([key]) => normalizeCategoryName(key) === finalCategory,\n\t\t);\n\t\tif (matchingConfig) {\n\t\t\tconfig = matchingConfig[1];\n\t\t}\n\t}\n\n\t// If no predefined config found, try iconUrl fallback for custom integrations\n\tif (!config) {\n\t\tif (iconUrl) {\n\t\t\tconst iconElement = (\n\t\t\t\t<Image\n\t\t\t\t\talt={`${category} Icon`}\n\t\t\t\t\twidth={defaultProps.width}\n\t\t\t\t\theight={defaultProps.height}\n\t\t\t\t\tclassName={`${restProps.className || \"\"} aspect-square object-contain`}\n\t\t\t\t\tsrc={iconUrl}\n\t\t\t\t/>\n\t\t\t);\n\t\t\treturn showBackground ? (\n\t\t\t\t<div className=\"rounded-lg p-1 bg-zinc-700 dark:bg-zinc-700\">{iconElement}</div>\n\t\t\t) : (\n\t\t\t\ticonElement\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Render image or component icon\n\tconst iconElement = config.isImage ? (\n\t\t<Image\n\t\t\talt={`${category} Icon`}\n\t\t\twidth={defaultProps.width}\n\t\t\theight={defaultProps.height}\n\t\t\tclassName={`${restProps.className || \"\"} aspect-square object-contain`}\n\t\t\tsrc={config.icon as string}\n\t\t/>\n\t) : (\n\t\t<HugeiconsIcon\n\t\t\ticon={config.icon as IconSvgElement}\n\t\t\tsize={defaultProps.size}\n\t\t\tclassName={restProps.className || config.iconColor}\n\t\t/>\n\t);\n\n\t// Return with or without background based on showBackground prop\n\t// Using dark: prefix for proper light/dark mode support\n\treturn showBackground ? (\n\t\t<div className={`rounded-lg p-1 ${config.bgColorLight || config.bgColor} dark:${config.bgColor}`}>\n\t\t\t{iconElement}\n\t\t</div>\n\t) : (\n\t\ticonElement\n\t);\n};\n\n// Format tool names from snake_case to Title Case\nexport const formatToolName = (name: string): string => {\n\treturn name\n\t\t.split(\"_\")\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\" \");\n};\n",
      "type": "registry:lib"
    }
  ]
}