@humain/ui 2.2.6 → 2.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/index101.js +2 -2
  2. package/dist/index101.js.map +1 -1
  3. package/dist/index103.js +3 -3
  4. package/dist/index103.js.map +1 -1
  5. package/dist/index108.js +1 -1
  6. package/dist/index108.js.map +1 -1
  7. package/dist/index117.js +1 -1
  8. package/dist/index117.js.map +1 -1
  9. package/dist/index122.js +6 -6
  10. package/dist/index122.js.map +1 -1
  11. package/dist/index126.js +1 -1
  12. package/dist/index126.js.map +1 -1
  13. package/dist/index130.js +2 -2
  14. package/dist/index130.js.map +1 -1
  15. package/dist/index146.js +4 -4
  16. package/dist/index146.js.map +1 -1
  17. package/dist/index152.js +1 -1
  18. package/dist/index152.js.map +1 -1
  19. package/dist/index154.js +2 -2
  20. package/dist/index154.js.map +1 -1
  21. package/dist/index166.js +1 -1
  22. package/dist/index166.js.map +1 -1
  23. package/dist/index168.js +1 -1
  24. package/dist/index168.js.map +1 -1
  25. package/dist/index169.js +1 -1
  26. package/dist/index169.js.map +1 -1
  27. package/dist/index173.js +2 -2
  28. package/dist/index173.js.map +1 -1
  29. package/dist/index175.js +4 -4
  30. package/dist/index175.js.map +1 -1
  31. package/dist/index176.js +2 -2
  32. package/dist/index176.js.map +1 -1
  33. package/dist/index177.js +2 -2
  34. package/dist/index177.js.map +1 -1
  35. package/dist/index184.js +1 -1
  36. package/dist/index184.js.map +1 -1
  37. package/dist/index191.js +3 -3
  38. package/dist/index191.js.map +1 -1
  39. package/dist/index192.js +2 -2
  40. package/dist/index192.js.map +1 -1
  41. package/dist/index201.js +2 -2
  42. package/dist/index201.js.map +1 -1
  43. package/dist/index204.js +4 -4
  44. package/dist/index204.js.map +1 -1
  45. package/dist/index228.js +1 -1
  46. package/dist/index228.js.map +1 -1
  47. package/dist/index229.js +1 -1
  48. package/dist/index229.js.map +1 -1
  49. package/dist/index24.js +2 -2
  50. package/dist/index24.js.map +1 -1
  51. package/dist/index5.js +1 -1
  52. package/dist/index5.js.map +1 -1
  53. package/dist/styles.css +1 -1
  54. package/dist/wc/AICodeBlock.js +41 -41
  55. package/dist/wc/AICodeBlock.js.map +1 -1
  56. package/dist/wc/AIMermaidDiagram.js +7 -7
  57. package/dist/wc/AIMermaidDiagram.js.map +1 -1
  58. package/dist/wc/AIMessage.js +6 -6
  59. package/dist/wc/AIMessage.js.map +1 -1
  60. package/dist/wc/AISource.js +2 -2
  61. package/dist/wc/AISource.js.map +1 -1
  62. package/dist/wc/AIToolCall.js +1 -1
  63. package/dist/wc/AIToolCall.js.map +1 -1
  64. package/dist/wc/Breadcrumb.js +43 -43
  65. package/dist/wc/Breadcrumb.js.map +1 -1
  66. package/dist/wc/DropdownMenu.js +9 -9
  67. package/dist/wc/DropdownMenu.js.map +1 -1
  68. package/dist/wc/Kbd.js +22 -22
  69. package/dist/wc/Kbd.js.map +1 -1
  70. package/dist/wc/button.js +7 -7
  71. package/dist/wc/button.js.map +1 -1
  72. package/dist/wc/close-button.js +19 -19
  73. package/dist/wc/close-button.js.map +1 -1
  74. package/dist/wc/hf-ai-prompt-suggestion.js +14 -14
  75. package/dist/wc/hf-ai-prompt-suggestion.js.map +1 -1
  76. package/dist/wc/hf-ai-reasoning-block.js +13 -13
  77. package/dist/wc/hf-ai-reasoning-block.js.map +1 -1
  78. package/dist/wc/hf-ai-steps.js +53 -53
  79. package/dist/wc/hf-ai-steps.js.map +1 -1
  80. package/dist/wc/hf-ai-system-message.js +26 -26
  81. package/dist/wc/hf-ai-system-message.js.map +1 -1
  82. package/dist/wc/hf-code-snippet.js +35 -35
  83. package/dist/wc/hf-code-snippet.js.map +1 -1
  84. package/dist/wc/hf-command-menu.js +39 -39
  85. package/dist/wc/hf-command-menu.js.map +1 -1
  86. package/dist/wc/hf-data-table.js +3 -3
  87. package/dist/wc/hf-data-table.js.map +1 -1
  88. package/dist/wc/hf-display-value.js +50 -50
  89. package/dist/wc/hf-display-value.js.map +1 -1
  90. package/dist/wc/hf-nav-account-menu.js +5 -5
  91. package/dist/wc/hf-nav-account-menu.js.map +1 -1
  92. package/dist/wc/hf-rich-textarea.js +1 -1
  93. package/dist/wc/hf-rich-textarea.js.map +1 -1
  94. package/dist/wc/hf-steps.js +8 -8
  95. package/dist/wc/hf-steps.js.map +1 -1
  96. package/dist/wc/markdown-renderer.js +1 -1
  97. package/dist/wc/markdown-renderer.js.map +1 -1
  98. package/dist/wc/selection-group.js +26 -26
  99. package/dist/wc/selection-group.js.map +1 -1
  100. package/dist/wc/syntax-highlight.js +4 -4
  101. package/dist/wc/syntax-highlight.js.map +1 -1
  102. package/dist/wc/workflow-step-renderer.js +51 -51
  103. package/dist/wc/workflow-step-renderer.js.map +1 -1
  104. package/dist/wc-react/AICodeBlock.js +17 -17
  105. package/dist/wc-react/AICodeBlock.js.map +1 -1
  106. package/dist/wc-react/AIMermaidDiagram.js +16 -16
  107. package/dist/wc-react/AIMermaidDiagram.js.map +1 -1
  108. package/dist/wc-react/AIMessage.js +7 -7
  109. package/dist/wc-react/AIMessage.js.map +1 -1
  110. package/dist/wc-react/AISource.js +41 -41
  111. package/dist/wc-react/AISource.js.map +1 -1
  112. package/dist/wc-react/AIToolCall.js +6 -6
  113. package/dist/wc-react/AIToolCall.js.map +1 -1
  114. package/dist/wc-react/Breadcrumb.js +18 -18
  115. package/dist/wc-react/Breadcrumb.js.map +1 -1
  116. package/dist/wc-react/DropdownMenu.js +27 -27
  117. package/dist/wc-react/DropdownMenu.js.map +1 -1
  118. package/dist/wc-react/Kbd.js +17 -17
  119. package/dist/wc-react/Kbd.js.map +1 -1
  120. package/dist/wc-react/button.js +12 -12
  121. package/dist/wc-react/button.js.map +1 -1
  122. package/dist/wc-react/close-button.js +19 -19
  123. package/dist/wc-react/close-button.js.map +1 -1
  124. package/dist/wc-react/hf-ai-prompt-suggestion.js +1 -1
  125. package/dist/wc-react/hf-ai-prompt-suggestion.js.map +1 -1
  126. package/dist/wc-react/hf-ai-reasoning-block.js +1 -1
  127. package/dist/wc-react/hf-ai-reasoning-block.js.map +1 -1
  128. package/dist/wc-react/hf-ai-steps.js +33 -33
  129. package/dist/wc-react/hf-ai-steps.js.map +1 -1
  130. package/dist/wc-react/hf-ai-system-message.js +9 -9
  131. package/dist/wc-react/hf-ai-system-message.js.map +1 -1
  132. package/dist/wc-react/hf-code-snippet.js +3 -3
  133. package/dist/wc-react/hf-code-snippet.js.map +1 -1
  134. package/dist/wc-react/hf-command-menu.js +56 -56
  135. package/dist/wc-react/hf-command-menu.js.map +1 -1
  136. package/dist/wc-react/hf-data-table.js +3 -3
  137. package/dist/wc-react/hf-data-table.js.map +1 -1
  138. package/dist/wc-react/hf-display-value.js +58 -58
  139. package/dist/wc-react/hf-display-value.js.map +1 -1
  140. package/dist/wc-react/hf-nav-account-menu.js +7 -7
  141. package/dist/wc-react/hf-nav-account-menu.js.map +1 -1
  142. package/dist/wc-react/hf-rich-textarea.js +5 -5
  143. package/dist/wc-react/hf-rich-textarea.js.map +1 -1
  144. package/dist/wc-react/hf-steps.js +2 -2
  145. package/dist/wc-react/hf-steps.js.map +1 -1
  146. package/dist/wc-react/markdown-renderer.js +1 -1
  147. package/dist/wc-react/markdown-renderer.js.map +1 -1
  148. package/dist/wc-react/selection-group.js +7 -7
  149. package/dist/wc-react/selection-group.js.map +1 -1
  150. package/dist/wc-react/syntax-highlight.js +4 -4
  151. package/dist/wc-react/syntax-highlight.js.map +1 -1
  152. package/dist/wc-react/workflow-step-renderer.js +44 -44
  153. package/dist/wc-react/workflow-step-renderer.js.map +1 -1
  154. package/package.json +1 -1
  155. package/skills/humain-ui/SKILL.md +1 -1
  156. package/skills/humain-ui-wc/SKILL.md +1 -1
  157. package/skills/humain-ui-wc.skill +0 -0
  158. package/skills/humain-ui.skill +0 -0
  159. package/src/tokens.css +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index169.js","names":[],"sources":["../src/components/organisms/ai/AIReasoningBlock/AIReasoningBlock.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ChevronDown, ChevronRight, Lightbulb } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { MarkdownRenderer } from '@/components/organisms/ai/_shared/markdown-renderer';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst reasoningBlockVariants = cva(\n 'overflow-hidden rounded-xl border transition-colors',\n {\n variants: {\n variant: {\n default: 'border-border bg-muted',\n thinking: 'border-primary bg-primary/10',\n subtle: 'border-border bg-background',\n },\n state: {\n idle: '',\n streaming: 'border-primary',\n },\n },\n defaultVariants: {\n variant: 'default',\n state: 'idle',\n },\n },\n);\n\n// =============================================================================\n// Thinking Indicator\n// =============================================================================\n\ninterface ThinkingIndicatorProps {\n className?: string;\n}\n\nfunction ThinkingIndicator({ className }: ThinkingIndicatorProps) {\n return (\n <div\n className={cn('flex items-center gap-1', className)}\n role=\"status\"\n aria-label=\"Thinking\"\n >\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0,\n }}\n />\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0.2,\n }}\n />\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0.4,\n }}\n />\n </div>\n );\n}\n\n// =============================================================================\n// Reasoning Trigger (Header)\n// =============================================================================\n\nexport interface AIReasoningTriggerProps {\n /** Title text */\n title?: string;\n /** Whether the block is expanded */\n isOpen: boolean;\n /** Callback to toggle open state */\n onToggle: () => void;\n /** Whether AI is currently streaming/thinking */\n isStreaming?: boolean;\n /** Duration text to display */\n duration?: string;\n /** ID of the controlled content panel */\n contentId?: string;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningTrigger({\n title = 'Reasoning',\n isOpen,\n onToggle,\n isStreaming = false,\n duration,\n contentId,\n className,\n}: AIReasoningTriggerProps) {\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isOpen}\n aria-controls={contentId}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 transition-colors hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring rounded-xl',\n className,\n )}\n >\n <div className=\"flex items-center gap-2\">\n <Lightbulb\n className={cn(\n 'size-4',\n isStreaming ? 'text-primary' : 'text-text-quaternary',\n )}\n aria-hidden=\"true\"\n />\n <span className=\"text-sm font-medium text-foreground\">{title}</span>\n {isStreaming && <ThinkingIndicator />}\n {duration && !isStreaming && (\n <span className=\"text-xs text-muted-foreground\">{duration}</span>\n )}\n </div>\n {isOpen ? (\n <ChevronDown\n className=\"size-4 text-muted-foreground transition-transform\"\n aria-hidden=\"true\"\n />\n ) : (\n <ChevronRight\n className=\"size-4 text-muted-foreground rtl:rotate-180 transition-transform\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n}\n\nAIReasoningTrigger.displayName = 'AIReasoningTrigger';\n\n// =============================================================================\n// Reasoning Content\n// =============================================================================\n\nexport interface AIReasoningContentProps {\n /** Content to display (can be markdown string or ReactNode) */\n children: React.ReactNode;\n /** Whether to render as markdown */\n markdown?: boolean;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningContent({\n children,\n markdown = true,\n className,\n}: AIReasoningContentProps) {\n return (\n <div className={cn('px-4 pb-4', className)}>\n {markdown && typeof children === 'string' ? (\n <MarkdownRenderer className=\"text-sm text-secondary-foreground\">\n {children}\n </MarkdownRenderer>\n ) : (\n <div className=\"text-sm text-secondary-foreground\">{children}</div>\n )}\n </div>\n );\n}\n\nAIReasoningContent.displayName = 'AIReasoningContent';\n\n// =============================================================================\n// Main AIReasoningBlock Component\n// =============================================================================\n\nexport interface AIReasoningBlockProps\n extends VariantProps<typeof reasoningBlockVariants> {\n /** Reasoning content (markdown string or ReactNode) */\n children: React.ReactNode;\n /** Title for the reasoning block */\n title?: string;\n /** Controlled open state */\n open?: boolean;\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Whether AI is currently streaming/thinking */\n isStreaming?: boolean;\n /** Whether to render content as markdown */\n markdown?: boolean;\n /** Duration text to display */\n duration?: string;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningBlock({\n children,\n title = 'Reasoning',\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n isStreaming = false,\n markdown = true,\n duration,\n variant,\n className,\n}: AIReasoningBlockProps) {\n const contentId = React.useId();\n\n const [isOpen, setIsOpen] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <div\n data-slot=\"ai-reasoning-block\"\n className={cn(\n reasoningBlockVariants({\n variant: isStreaming ? 'thinking' : variant,\n state: isStreaming ? 'streaming' : 'idle',\n }),\n className,\n )}\n >\n <AIReasoningTrigger\n title={title}\n isOpen={isOpen}\n onToggle={handleToggle}\n isStreaming={isStreaming}\n duration={duration}\n contentId={contentId}\n />\n\n <AnimatePresence>\n {isOpen && (\n <motion.section\n id={contentId}\n aria-label={title}\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2 }}\n className=\"overflow-hidden\"\n >\n <AIReasoningContent markdown={markdown}>\n {children}\n </AIReasoningContent>\n </motion.section>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\nAIReasoningBlock.displayName = 'AIReasoningBlock';\n\n// =============================================================================\n// Thinking Bar (Minimal variant)\n// =============================================================================\n\nexport interface AIThinkingBarProps {\n /** Text to display */\n text?: string;\n /** Whether thinking is active */\n isThinking?: boolean;\n /** Action button content */\n action?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIThinkingBar({\n text = 'Thinking...',\n isThinking = true,\n action,\n className,\n}: AIThinkingBarProps) {\n if (!isThinking) return null;\n\n return (\n <motion.div\n data-slot=\"ai-thinking-bar\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n className={cn(\n 'flex items-center justify-between px-4 py-2 rounded-lg bg-primary/10 border border-primary motion-reduce:animate-none',\n className,\n )}\n >\n <div className=\"flex items-center gap-2\">\n <Lightbulb className=\"size-4 text-primary\" aria-hidden=\"true\" />\n <span className=\"text-sm text-primary\">{text}</span>\n <ThinkingIndicator />\n </div>\n {action}\n </motion.div>\n );\n}\n\nAIThinkingBar.displayName = 'AIThinkingBar';\n\nexport {\n AIReasoningBlock,\n AIReasoningContent,\n AIReasoningTrigger,\n AIThinkingBar,\n reasoningBlockVariants,\n ThinkingIndicator,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index169.js","names":[],"sources":["../src/components/organisms/ai/AIReasoningBlock/AIReasoningBlock.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ChevronDown, ChevronRight, Lightbulb } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { MarkdownRenderer } from '@/components/organisms/ai/_shared/markdown-renderer';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst reasoningBlockVariants = cva(\n 'overflow-hidden rounded-xl border transition-colors',\n {\n variants: {\n variant: {\n default: 'border-border bg-muted',\n thinking: 'border-primary bg-primary/10',\n subtle: 'border-border bg-background',\n },\n state: {\n idle: '',\n streaming: 'border-primary',\n },\n },\n defaultVariants: {\n variant: 'default',\n state: 'idle',\n },\n },\n);\n\n// =============================================================================\n// Thinking Indicator\n// =============================================================================\n\ninterface ThinkingIndicatorProps {\n className?: string;\n}\n\nfunction ThinkingIndicator({ className }: ThinkingIndicatorProps) {\n return (\n <div\n className={cn('flex items-center gap-1', className)}\n role=\"status\"\n aria-label=\"Thinking\"\n >\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0,\n }}\n />\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0.2,\n }}\n />\n <motion.span\n className=\"size-1.5 rounded-full bg-primary motion-reduce:animate-none\"\n animate={{ opacity: [0.3, 1, 0.3] }}\n transition={{\n duration: 1.5,\n repeat: Number.POSITIVE_INFINITY,\n delay: 0.4,\n }}\n />\n </div>\n );\n}\n\n// =============================================================================\n// Reasoning Trigger (Header)\n// =============================================================================\n\nexport interface AIReasoningTriggerProps {\n /** Title text */\n title?: string;\n /** Whether the block is expanded */\n isOpen: boolean;\n /** Callback to toggle open state */\n onToggle: () => void;\n /** Whether AI is currently streaming/thinking */\n isStreaming?: boolean;\n /** Duration text to display */\n duration?: string;\n /** ID of the controlled content panel */\n contentId?: string;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningTrigger({\n title = 'Reasoning',\n isOpen,\n onToggle,\n isStreaming = false,\n duration,\n contentId,\n className,\n}: AIReasoningTriggerProps) {\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isOpen}\n aria-controls={contentId}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 transition-colors hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring rounded-xl',\n className,\n )}\n >\n <div className=\"flex items-center gap-2\">\n <Lightbulb\n className={cn(\n 'size-4',\n isStreaming ? 'text-primary' : 'text-muted-foreground',\n )}\n aria-hidden=\"true\"\n />\n <span className=\"text-sm font-medium text-foreground\">{title}</span>\n {isStreaming && <ThinkingIndicator />}\n {duration && !isStreaming && (\n <span className=\"text-xs text-muted-foreground\">{duration}</span>\n )}\n </div>\n {isOpen ? (\n <ChevronDown\n className=\"size-4 text-muted-foreground transition-transform\"\n aria-hidden=\"true\"\n />\n ) : (\n <ChevronRight\n className=\"size-4 text-muted-foreground rtl:rotate-180 transition-transform\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n}\n\nAIReasoningTrigger.displayName = 'AIReasoningTrigger';\n\n// =============================================================================\n// Reasoning Content\n// =============================================================================\n\nexport interface AIReasoningContentProps {\n /** Content to display (can be markdown string or ReactNode) */\n children: React.ReactNode;\n /** Whether to render as markdown */\n markdown?: boolean;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningContent({\n children,\n markdown = true,\n className,\n}: AIReasoningContentProps) {\n return (\n <div className={cn('px-4 pb-4', className)}>\n {markdown && typeof children === 'string' ? (\n <MarkdownRenderer className=\"text-sm text-secondary-foreground\">\n {children}\n </MarkdownRenderer>\n ) : (\n <div className=\"text-sm text-secondary-foreground\">{children}</div>\n )}\n </div>\n );\n}\n\nAIReasoningContent.displayName = 'AIReasoningContent';\n\n// =============================================================================\n// Main AIReasoningBlock Component\n// =============================================================================\n\nexport interface AIReasoningBlockProps\n extends VariantProps<typeof reasoningBlockVariants> {\n /** Reasoning content (markdown string or ReactNode) */\n children: React.ReactNode;\n /** Title for the reasoning block */\n title?: string;\n /** Controlled open state */\n open?: boolean;\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Whether AI is currently streaming/thinking */\n isStreaming?: boolean;\n /** Whether to render content as markdown */\n markdown?: boolean;\n /** Duration text to display */\n duration?: string;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIReasoningBlock({\n children,\n title = 'Reasoning',\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n isStreaming = false,\n markdown = true,\n duration,\n variant,\n className,\n}: AIReasoningBlockProps) {\n const contentId = React.useId();\n\n const [isOpen, setIsOpen] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <div\n data-slot=\"ai-reasoning-block\"\n className={cn(\n reasoningBlockVariants({\n variant: isStreaming ? 'thinking' : variant,\n state: isStreaming ? 'streaming' : 'idle',\n }),\n className,\n )}\n >\n <AIReasoningTrigger\n title={title}\n isOpen={isOpen}\n onToggle={handleToggle}\n isStreaming={isStreaming}\n duration={duration}\n contentId={contentId}\n />\n\n <AnimatePresence>\n {isOpen && (\n <motion.section\n id={contentId}\n aria-label={title}\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2 }}\n className=\"overflow-hidden\"\n >\n <AIReasoningContent markdown={markdown}>\n {children}\n </AIReasoningContent>\n </motion.section>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\nAIReasoningBlock.displayName = 'AIReasoningBlock';\n\n// =============================================================================\n// Thinking Bar (Minimal variant)\n// =============================================================================\n\nexport interface AIThinkingBarProps {\n /** Text to display */\n text?: string;\n /** Whether thinking is active */\n isThinking?: boolean;\n /** Action button content */\n action?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AIThinkingBar({\n text = 'Thinking...',\n isThinking = true,\n action,\n className,\n}: AIThinkingBarProps) {\n if (!isThinking) return null;\n\n return (\n <motion.div\n data-slot=\"ai-thinking-bar\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n className={cn(\n 'flex items-center justify-between px-4 py-2 rounded-lg bg-primary/10 border border-primary motion-reduce:animate-none',\n className,\n )}\n >\n <div className=\"flex items-center gap-2\">\n <Lightbulb className=\"size-4 text-primary\" aria-hidden=\"true\" />\n <span className=\"text-sm text-primary\">{text}</span>\n <ThinkingIndicator />\n </div>\n {action}\n </motion.div>\n );\n}\n\nAIThinkingBar.displayName = 'AIThinkingBar';\n\nexport {\n AIReasoningBlock,\n AIReasoningContent,\n AIReasoningTrigger,\n AIThinkingBar,\n reasoningBlockVariants,\n ThinkingIndicator,\n};\n"],"mappings":"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"}
package/dist/index173.js CHANGED
@@ -95,7 +95,7 @@ function AISourceContent({ source, className }) {
95
95
  className: "text-sm font-medium text-foreground truncate",
96
96
  children: source.title || domain
97
97
  }), /* @__PURE__ */ jsx("p", {
98
- className: "text-xs text-text-quaternary truncate",
98
+ className: "text-xs text-secondary-foreground truncate",
99
99
  children: domain
100
100
  })]
101
101
  })]
@@ -150,7 +150,7 @@ function AISourceList({ sources, maxVisible = 3, showAll = false, onShowMore, cl
150
150
  className: cn("flex flex-wrap items-center gap-2", className),
151
151
  children: [
152
152
  /* @__PURE__ */ jsx("span", {
153
- className: "text-xs text-text-quaternary",
153
+ className: "text-xs text-secondary-foreground",
154
154
  children: "Sources:"
155
155
  }),
156
156
  visibleSources.map((source, index) => /* @__PURE__ */ jsx("span", {
@@ -1 +1 @@
1
- {"version":3,"file":"index173.js","names":[],"sources":["../src/components/organisms/ai/AISource/AISource.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ExternalLink, Globe } from 'lucide-react';\nimport * as React from 'react';\nimport { Tooltip, TooltipPopup, TooltipTrigger } from '@/components/ui/tooltip';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst sourceVariants = cva(\n 'inline-flex items-center gap-1.5 transition-colors cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',\n {\n variants: {\n variant: {\n default:\n 'text-sm text-primary hover:text-primary underline underline-offset-2 rounded-sm',\n pill: 'px-2.5 py-1 rounded-full bg-accent hover:bg-surface-2 text-xs text-secondary-foreground delight-selection-press',\n minimal:\n 'text-xs text-secondary-foreground hover:text-foreground rounded-sm',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nconst DEFAULT_MAX_VISIBLE_SOURCES = 3;\n\nfunction getMaxVisibleSources(maxVisible: number): number {\n return Number.isFinite(maxVisible) && maxVisible >= 0\n ? Math.floor(maxVisible)\n : DEFAULT_MAX_VISIBLE_SOURCES;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SourceData {\n /** Source URL */\n url: string;\n /** Display title */\n title?: string;\n /** Source description */\n description?: string;\n /** Favicon URL */\n favicon?: string;\n /** Site name */\n siteName?: string;\n}\n\n// =============================================================================\n// Source Favicon\n// =============================================================================\n\ninterface SourceFaviconProps {\n url: string;\n favicon?: string;\n className?: string;\n}\n\nfunction SourceFavicon({ url, favicon, className }: SourceFaviconProps) {\n const [hasError, setHasError] = React.useState(false);\n\n // Generate favicon URL from domain if not provided\n const faviconUrl = React.useMemo(() => {\n if (favicon) return favicon;\n try {\n const domain = new URL(url).hostname;\n return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;\n } catch {\n return null;\n }\n }, [url, favicon]);\n\n if (!faviconUrl || hasError) {\n return <Globe className={cn('size-4 text-muted-foreground', className)} />;\n }\n\n return (\n <img\n src={faviconUrl}\n alt=\"\"\n className={cn(\n 'size-4 rounded delight-fade-in motion-reduce:!animate-none',\n className,\n )}\n onError={() => setHasError(true)}\n />\n );\n}\n\nSourceFavicon.displayName = 'SourceFavicon';\n\n// =============================================================================\n// Source Trigger (Clickable element)\n// =============================================================================\n\nexport interface AISourceTriggerProps\n extends VariantProps<typeof sourceVariants> {\n /** Source data */\n source: SourceData;\n /** Whether to show favicon */\n showFavicon?: boolean;\n /** Additional class name */\n className?: string;\n /** Click handler */\n onClick?: () => void;\n}\n\nfunction AISourceTrigger({\n source,\n variant,\n showFavicon = true,\n className,\n onClick,\n}: AISourceTriggerProps) {\n const displayText = source.title || source.siteName || source.url;\n\n const sharedClassName = cn(sourceVariants({ variant }), className);\n const sharedChildren = (\n <>\n {showFavicon && (\n <SourceFavicon url={source.url} favicon={source.favicon} />\n )}\n <span className=\"truncate max-w-[200px]\">{displayText}</span>\n {variant === 'default' && (\n <ExternalLink className=\"size-3 shrink-0\" aria-hidden=\"true\" />\n )}\n </>\n );\n\n // Render as <a> when navigating to URL (proper semantics)\n if (!onClick) {\n return (\n <a\n data-slot=\"ai-source-trigger\"\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={sharedClassName}\n aria-label={`Source: ${displayText}`}\n >\n {sharedChildren}\n </a>\n );\n }\n\n // Render as <button> when custom click handler is provided\n return (\n <button\n type=\"button\"\n data-slot=\"ai-source-trigger\"\n onClick={onClick}\n className={sharedClassName}\n aria-label={`Source: ${displayText}`}\n >\n {sharedChildren}\n </button>\n );\n}\n\nAISourceTrigger.displayName = 'AISourceTrigger';\n\n// =============================================================================\n// Source Content (Tooltip/Popover content)\n// =============================================================================\n\nexport interface AISourceContentProps {\n /** Source data */\n source: SourceData;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISourceContent({ source, className }: AISourceContentProps) {\n const domain = React.useMemo(() => {\n try {\n return new URL(source.url).hostname;\n } catch {\n return source.url;\n }\n }, [source.url]);\n\n return (\n <div className={cn('p-3 max-w-xs space-y-2', className)}>\n <div className=\"flex items-start gap-2\">\n <SourceFavicon\n url={source.url}\n favicon={source.favicon}\n className=\"mt-0.5\"\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {source.title || domain}\n </p>\n <p className=\"text-xs text-text-quaternary truncate\">{domain}</p>\n </div>\n </div>\n {source.description && (\n <p className=\"text-xs text-muted-foreground line-clamp-2\">\n {source.description}\n </p>\n )}\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-primary hover:text-primary\"\n >\n Visit source\n <ExternalLink className=\"size-3\" />\n </a>\n </div>\n );\n}\n\nAISourceContent.displayName = 'AISourceContent';\n\n// =============================================================================\n// Main AISource Component (with built-in tooltip)\n// =============================================================================\n\nexport interface AISourceProps extends VariantProps<typeof sourceVariants> {\n /** Source data */\n source: SourceData;\n /** Whether to show favicon */\n showFavicon?: boolean;\n /** Whether to show tooltip on hover */\n showTooltip?: boolean;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISource({\n source,\n variant,\n showFavicon = true,\n showTooltip = true,\n className,\n}: AISourceProps) {\n if (!showTooltip) {\n return (\n <span data-slot=\"ai-source\">\n <AISourceTrigger\n source={source}\n variant={variant}\n showFavicon={showFavicon}\n className={className}\n />\n </span>\n );\n }\n\n return (\n <Tooltip>\n <TooltipTrigger\n render={\n <span data-slot=\"ai-source\">\n <AISourceTrigger\n source={source}\n variant={variant}\n showFavicon={showFavicon}\n className={className}\n />\n </span>\n }\n />\n <TooltipPopup side=\"top\" className=\"p-0\">\n <AISourceContent source={source} />\n </TooltipPopup>\n </Tooltip>\n );\n}\n\nAISource.displayName = 'AISource';\n\n// =============================================================================\n// Source List Component\n// =============================================================================\n\nexport interface AISourceListProps {\n /** Array of sources */\n sources: SourceData[];\n /** Maximum sources to show before truncating */\n maxVisible?: number;\n /** Whether to show expanded list */\n showAll?: boolean;\n /** Callback when \"show more\" is clicked */\n onShowMore?: () => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISourceList({\n sources,\n maxVisible = 3,\n showAll = false,\n onShowMore,\n className,\n}: AISourceListProps) {\n const safeMaxVisible = getMaxVisibleSources(maxVisible);\n const visibleSources = showAll ? sources : sources.slice(0, safeMaxVisible);\n const remainingCount = Math.max(0, sources.length - safeMaxVisible);\n\n return (\n <nav\n data-slot=\"ai-source-list\"\n aria-label=\"Sources\"\n className={cn('flex flex-wrap items-center gap-2', className)}\n >\n <span className=\"text-xs text-text-quaternary\">Sources:</span>\n {visibleSources.map((source, index) => (\n <span\n key={`${source.url}-${index}`}\n className=\"delight-stagger motion-reduce:!animate-none\"\n style={\n { '--delight-delay': `${index * 50}ms` } as React.CSSProperties\n }\n >\n <AISource source={source} variant=\"pill\" />\n </span>\n ))}\n {!showAll && remainingCount > 0 && (\n <button\n type=\"button\"\n onClick={onShowMore}\n aria-label={`Show ${remainingCount} more sources`}\n className=\"px-2.5 py-1 rounded-full bg-accent hover:bg-surface-2 text-xs text-secondary-foreground transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring delight-selection-press\"\n >\n +{remainingCount} more\n </button>\n )}\n </nav>\n );\n}\n\nAISourceList.displayName = 'AISourceList';\n\nexport {\n AISource,\n AISourceContent,\n AISourceList,\n AISourceTrigger,\n SourceFavicon,\n sourceVariants,\n};\n"],"mappings":";;;;;;;AAUA,IAAM,iBAAiB,IACrB,uJACA;CACE,UAAU,EACR,SAAS;EACP,SACE;EACF,MAAM;EACN,SACE;CACJ,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CACF;AAEA,IAAM,8BAA8B;AAEpC,SAAS,qBAAqB,YAA4B;CACxD,OAAO,OAAO,SAAS,UAAU,KAAK,cAAc,IAChD,KAAK,MAAM,UAAU,IACrB;AACN;AA6BA,SAAS,cAAc,EAAE,KAAK,SAAS,aAAiC;CACtE,MAAM,CAAC,UAAU,eAAe,QAAM,SAAS,KAAK;CAGpD,MAAM,aAAa,QAAM,cAAc;EACrC,IAAI,SAAS,OAAO;EACpB,IAAI;GAEF,OAAO,6CADQ,IAAI,IAAI,GAAG,CAAC,CAAC,SAC+B;EAC7D,QAAQ;GACN,OAAO;EACT;CACF,GAAG,CAAC,KAAK,OAAO,CAAC;CAEjB,IAAI,CAAC,cAAc,UACjB,OAAO,oBAAC,OAAD,EAAO,WAAW,GAAG,gCAAgC,SAAS,EAAI,CAAA;CAG3E,OACE,oBAAC,OAAD;EACE,KAAK;EACL,KAAI;EACJ,WAAW,GACT,8DACA,SACF;EACA,eAAe,YAAY,IAAI;CAChC,CAAA;AAEL;AAEA,cAAc,cAAc;AAkB5B,SAAS,gBAAgB,EACvB,QACA,SACA,cAAc,MACd,WACA,WACuB;CACvB,MAAM,cAAc,OAAO,SAAS,OAAO,YAAY,OAAO;CAE9D,MAAM,kBAAkB,GAAG,eAAe,EAAE,QAAQ,CAAC,GAAG,SAAS;CACjE,MAAM,iBACJ,qBAAA,UAAA,EAAA,UAAA;EACG,eACC,oBAAC,eAAD;GAAe,KAAK,OAAO;GAAK,SAAS,OAAO;EAAU,CAAA;EAE5D,oBAAC,QAAD;GAAM,WAAU;aAA0B;EAAkB,CAAA;EAC3D,YAAY,aACX,oBAAC,cAAD;GAAc,WAAU;GAAkB,eAAY;EAAQ,CAAA;CAEhE,EAAA,CAAA;CAIJ,IAAI,CAAC,SACH,OACE,oBAAC,KAAD;EACE,aAAU;EACV,MAAM,OAAO;EACb,QAAO;EACP,KAAI;EACJ,WAAW;EACX,cAAY,WAAW;YAEtB;CACA,CAAA;CAKP,OACE,oBAAC,UAAD;EACE,MAAK;EACL,aAAU;EACD;EACT,WAAW;EACX,cAAY,WAAW;YAEtB;CACK,CAAA;AAEZ;AAEA,gBAAgB,cAAc;AAa9B,SAAS,gBAAgB,EAAE,QAAQ,aAAmC;CACpE,MAAM,SAAS,QAAM,cAAc;EACjC,IAAI;GACF,OAAO,IAAI,IAAI,OAAO,GAAG,CAAC,CAAC;EAC7B,QAAQ;GACN,OAAO,OAAO;EAChB;CACF,GAAG,CAAC,OAAO,GAAG,CAAC;CAEf,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,0BAA0B,SAAS;YAAtD;GACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,eAAD;KACE,KAAK,OAAO;KACZ,SAAS,OAAO;KAChB,WAAU;IACX,CAAA,GACD,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,KAAD;MAAG,WAAU;gBACV,OAAO,SAAS;KAChB,CAAA,GACH,oBAAC,KAAD;MAAG,WAAU;gBAAyC;KAAU,CAAA,CAC7D;MACF;;GACJ,OAAO,eACN,oBAAC,KAAD;IAAG,WAAU;cACV,OAAO;GACP,CAAA;GAEL,qBAAC,KAAD;IACE,MAAM,OAAO;IACb,QAAO;IACP,KAAI;IACJ,WAAU;cAJZ,CAKC,gBAEC,oBAAC,cAAD,EAAc,WAAU,SAAU,CAAA,CACjC;;EACA;;AAET;AAEA,gBAAgB,cAAc;AAiB9B,SAAS,SAAS,EAChB,QACA,SACA,cAAc,MACd,cAAc,MACd,aACgB;CAChB,IAAI,CAAC,aACH,OACE,oBAAC,QAAD;EAAM,aAAU;YACd,oBAAC,iBAAD;GACU;GACC;GACI;GACF;EACZ,CAAA;CACG,CAAA;CAIV,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD,EACE,QACE,oBAAC,QAAD;EAAM,aAAU;YACd,oBAAC,iBAAD;GACU;GACC;GACI;GACF;EACZ,CAAA;CACG,CAAA,EAET,CAAA,GACD,oBAAC,cAAD;EAAc,MAAK;EAAM,WAAU;YACjC,oBAAC,iBAAD,EAAyB,OAAS,CAAA;CACtB,CAAA,CACP,EAAA,CAAA;AAEb;AAEA,SAAS,cAAc;AAmBvB,SAAS,aAAa,EACpB,SACA,aAAa,GACb,UAAU,OACV,YACA,aACoB;CACpB,MAAM,iBAAiB,qBAAqB,UAAU;CACtD,MAAM,iBAAiB,UAAU,UAAU,QAAQ,MAAM,GAAG,cAAc;CAC1E,MAAM,iBAAiB,KAAK,IAAI,GAAG,QAAQ,SAAS,cAAc;CAElE,OACE,qBAAC,OAAD;EACE,aAAU;EACV,cAAW;EACX,WAAW,GAAG,qCAAqC,SAAS;YAH9D;GAKE,oBAAC,QAAD;IAAM,WAAU;cAA+B;GAAc,CAAA;GAC5D,eAAe,KAAK,QAAQ,UAC3B,oBAAC,QAAD;IAEE,WAAU;IACV,OACE,EAAE,mBAAmB,GAAG,QAAQ,GAAG,IAAI;cAGzC,oBAAC,UAAD;KAAkB;KAAQ,SAAQ;IAAQ,CAAA;GACtC,GAPC,GAAG,OAAO,IAAI,GAAG,OAOlB,CACP;GACA,CAAC,WAAW,iBAAiB,KAC5B,qBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,cAAY,QAAQ,eAAe;IACnC,WAAU;cAJZ;KAKC;KACG;KAAe;IACX;;EAEP;;AAET;AAEA,aAAa,cAAc"}
1
+ {"version":3,"file":"index173.js","names":[],"sources":["../src/components/organisms/ai/AISource/AISource.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ExternalLink, Globe } from 'lucide-react';\nimport * as React from 'react';\nimport { Tooltip, TooltipPopup, TooltipTrigger } from '@/components/ui/tooltip';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst sourceVariants = cva(\n 'inline-flex items-center gap-1.5 transition-colors cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',\n {\n variants: {\n variant: {\n default:\n 'text-sm text-primary hover:text-primary underline underline-offset-2 rounded-sm',\n pill: 'px-2.5 py-1 rounded-full bg-accent hover:bg-surface-2 text-xs text-secondary-foreground delight-selection-press',\n minimal:\n 'text-xs text-secondary-foreground hover:text-foreground rounded-sm',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nconst DEFAULT_MAX_VISIBLE_SOURCES = 3;\n\nfunction getMaxVisibleSources(maxVisible: number): number {\n return Number.isFinite(maxVisible) && maxVisible >= 0\n ? Math.floor(maxVisible)\n : DEFAULT_MAX_VISIBLE_SOURCES;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SourceData {\n /** Source URL */\n url: string;\n /** Display title */\n title?: string;\n /** Source description */\n description?: string;\n /** Favicon URL */\n favicon?: string;\n /** Site name */\n siteName?: string;\n}\n\n// =============================================================================\n// Source Favicon\n// =============================================================================\n\ninterface SourceFaviconProps {\n url: string;\n favicon?: string;\n className?: string;\n}\n\nfunction SourceFavicon({ url, favicon, className }: SourceFaviconProps) {\n const [hasError, setHasError] = React.useState(false);\n\n // Generate favicon URL from domain if not provided\n const faviconUrl = React.useMemo(() => {\n if (favicon) return favicon;\n try {\n const domain = new URL(url).hostname;\n return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;\n } catch {\n return null;\n }\n }, [url, favicon]);\n\n if (!faviconUrl || hasError) {\n return <Globe className={cn('size-4 text-muted-foreground', className)} />;\n }\n\n return (\n <img\n src={faviconUrl}\n alt=\"\"\n className={cn(\n 'size-4 rounded delight-fade-in motion-reduce:!animate-none',\n className,\n )}\n onError={() => setHasError(true)}\n />\n );\n}\n\nSourceFavicon.displayName = 'SourceFavicon';\n\n// =============================================================================\n// Source Trigger (Clickable element)\n// =============================================================================\n\nexport interface AISourceTriggerProps\n extends VariantProps<typeof sourceVariants> {\n /** Source data */\n source: SourceData;\n /** Whether to show favicon */\n showFavicon?: boolean;\n /** Additional class name */\n className?: string;\n /** Click handler */\n onClick?: () => void;\n}\n\nfunction AISourceTrigger({\n source,\n variant,\n showFavicon = true,\n className,\n onClick,\n}: AISourceTriggerProps) {\n const displayText = source.title || source.siteName || source.url;\n\n const sharedClassName = cn(sourceVariants({ variant }), className);\n const sharedChildren = (\n <>\n {showFavicon && (\n <SourceFavicon url={source.url} favicon={source.favicon} />\n )}\n <span className=\"truncate max-w-[200px]\">{displayText}</span>\n {variant === 'default' && (\n <ExternalLink className=\"size-3 shrink-0\" aria-hidden=\"true\" />\n )}\n </>\n );\n\n // Render as <a> when navigating to URL (proper semantics)\n if (!onClick) {\n return (\n <a\n data-slot=\"ai-source-trigger\"\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={sharedClassName}\n aria-label={`Source: ${displayText}`}\n >\n {sharedChildren}\n </a>\n );\n }\n\n // Render as <button> when custom click handler is provided\n return (\n <button\n type=\"button\"\n data-slot=\"ai-source-trigger\"\n onClick={onClick}\n className={sharedClassName}\n aria-label={`Source: ${displayText}`}\n >\n {sharedChildren}\n </button>\n );\n}\n\nAISourceTrigger.displayName = 'AISourceTrigger';\n\n// =============================================================================\n// Source Content (Tooltip/Popover content)\n// =============================================================================\n\nexport interface AISourceContentProps {\n /** Source data */\n source: SourceData;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISourceContent({ source, className }: AISourceContentProps) {\n const domain = React.useMemo(() => {\n try {\n return new URL(source.url).hostname;\n } catch {\n return source.url;\n }\n }, [source.url]);\n\n return (\n <div className={cn('p-3 max-w-xs space-y-2', className)}>\n <div className=\"flex items-start gap-2\">\n <SourceFavicon\n url={source.url}\n favicon={source.favicon}\n className=\"mt-0.5\"\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-foreground truncate\">\n {source.title || domain}\n </p>\n <p className=\"text-xs text-secondary-foreground truncate\">{domain}</p>\n </div>\n </div>\n {source.description && (\n <p className=\"text-xs text-muted-foreground line-clamp-2\">\n {source.description}\n </p>\n )}\n <a\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 text-xs text-primary hover:text-primary\"\n >\n Visit source\n <ExternalLink className=\"size-3\" />\n </a>\n </div>\n );\n}\n\nAISourceContent.displayName = 'AISourceContent';\n\n// =============================================================================\n// Main AISource Component (with built-in tooltip)\n// =============================================================================\n\nexport interface AISourceProps extends VariantProps<typeof sourceVariants> {\n /** Source data */\n source: SourceData;\n /** Whether to show favicon */\n showFavicon?: boolean;\n /** Whether to show tooltip on hover */\n showTooltip?: boolean;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISource({\n source,\n variant,\n showFavicon = true,\n showTooltip = true,\n className,\n}: AISourceProps) {\n if (!showTooltip) {\n return (\n <span data-slot=\"ai-source\">\n <AISourceTrigger\n source={source}\n variant={variant}\n showFavicon={showFavicon}\n className={className}\n />\n </span>\n );\n }\n\n return (\n <Tooltip>\n <TooltipTrigger\n render={\n <span data-slot=\"ai-source\">\n <AISourceTrigger\n source={source}\n variant={variant}\n showFavicon={showFavicon}\n className={className}\n />\n </span>\n }\n />\n <TooltipPopup side=\"top\" className=\"p-0\">\n <AISourceContent source={source} />\n </TooltipPopup>\n </Tooltip>\n );\n}\n\nAISource.displayName = 'AISource';\n\n// =============================================================================\n// Source List Component\n// =============================================================================\n\nexport interface AISourceListProps {\n /** Array of sources */\n sources: SourceData[];\n /** Maximum sources to show before truncating */\n maxVisible?: number;\n /** Whether to show expanded list */\n showAll?: boolean;\n /** Callback when \"show more\" is clicked */\n onShowMore?: () => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISourceList({\n sources,\n maxVisible = 3,\n showAll = false,\n onShowMore,\n className,\n}: AISourceListProps) {\n const safeMaxVisible = getMaxVisibleSources(maxVisible);\n const visibleSources = showAll ? sources : sources.slice(0, safeMaxVisible);\n const remainingCount = Math.max(0, sources.length - safeMaxVisible);\n\n return (\n <nav\n data-slot=\"ai-source-list\"\n aria-label=\"Sources\"\n className={cn('flex flex-wrap items-center gap-2', className)}\n >\n <span className=\"text-xs text-secondary-foreground\">Sources:</span>\n {visibleSources.map((source, index) => (\n <span\n key={`${source.url}-${index}`}\n className=\"delight-stagger motion-reduce:!animate-none\"\n style={\n { '--delight-delay': `${index * 50}ms` } as React.CSSProperties\n }\n >\n <AISource source={source} variant=\"pill\" />\n </span>\n ))}\n {!showAll && remainingCount > 0 && (\n <button\n type=\"button\"\n onClick={onShowMore}\n aria-label={`Show ${remainingCount} more sources`}\n className=\"px-2.5 py-1 rounded-full bg-accent hover:bg-surface-2 text-xs text-secondary-foreground transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring delight-selection-press\"\n >\n +{remainingCount} more\n </button>\n )}\n </nav>\n );\n}\n\nAISourceList.displayName = 'AISourceList';\n\nexport {\n AISource,\n AISourceContent,\n AISourceList,\n AISourceTrigger,\n SourceFavicon,\n sourceVariants,\n};\n"],"mappings":"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"}
package/dist/index175.js CHANGED
@@ -7,7 +7,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { AnimatePresence, motion, useReducedMotion } from "motion/react";
8
8
  //#region src/components/organisms/ai/_shared/workflow-step-renderer.tsx
9
9
  var workflowStepItemStatusClasses = {
10
- pending: "text-muted-foreground",
10
+ pending: "text-secondary-foreground",
11
11
  running: "text-primary",
12
12
  suspended: "text-warning",
13
13
  completed: "text-success",
@@ -192,7 +192,7 @@ function WorkflowStepItem({ step, index, isLast, appearance = "contained", itemS
192
192
  children: step.contextLabel
193
193
  }),
194
194
  /* @__PURE__ */ jsx("span", {
195
- className: cn("text-sm font-medium", step.status === "pending" ? "text-text-quaternary" : "text-foreground"),
195
+ className: cn("text-sm font-medium", step.status === "pending" ? "text-secondary-foreground" : "text-foreground"),
196
196
  children: step.title
197
197
  }),
198
198
  /* @__PURE__ */ jsx("span", {
@@ -200,12 +200,12 @@ function WorkflowStepItem({ step, index, isLast, appearance = "contained", itemS
200
200
  children: getWorkflowStepStatusLabel(step.status)
201
201
  }),
202
202
  step.durationMs !== void 0 && /* @__PURE__ */ jsx("span", {
203
- className: "text-xs text-muted-foreground",
203
+ className: "text-xs text-secondary-foreground",
204
204
  children: formatAIDuration(step.durationMs)
205
205
  })
206
206
  ]
207
207
  }), step.description && /* @__PURE__ */ jsx("p", {
208
- className: "mt-0.5 text-xs text-text-quaternary",
208
+ className: "mt-0.5 text-xs text-secondary-foreground",
209
209
  children: step.description
210
210
  })]
211
211
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"index175.js","names":[],"sources":["../src/components/organisms/ai/_shared/workflow-step-renderer.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Loader,\n Pause,\n} from 'lucide-react';\nimport { AnimatePresence, motion, useReducedMotion } from 'motion/react';\nimport * as React from 'react';\nimport { assertNever, cn } from '@/lib/utils';\nimport { formatAIDuration } from './format-ai-duration';\n\nexport type WorkflowStepStatus =\n | 'pending'\n | 'running'\n | 'suspended'\n | 'completed'\n | 'error';\n\nexport type WorkflowStepAppearance = 'contained' | 'embedded' | 'timeline';\n\nexport interface WorkflowStepViewModel {\n id: string;\n title: string;\n description?: string;\n contextLabel?: string;\n status: WorkflowStepStatus;\n content?: React.ReactNode;\n durationMs?: number;\n error?: string;\n}\n\nexport interface WorkflowStepRendererProps {\n steps: readonly WorkflowStepViewModel[];\n appearance?: WorkflowStepAppearance;\n itemSlot?: string;\n expandable?: boolean;\n activeStepId?: string;\n expandedStepIds?: readonly string[];\n defaultExpandedStepIds?: readonly string[];\n onExpandedStepIdsChange?: (ids: readonly string[]) => void;\n className?: string;\n}\n\ninterface PrivateWorkflowStepRendererProps extends WorkflowStepRendererProps {\n renderItems?: boolean;\n}\n\nconst workflowStepItemStatusClasses = {\n pending: 'text-muted-foreground',\n running: 'text-primary',\n suspended: 'text-warning',\n completed: 'text-success',\n error: 'text-destructive',\n} as const satisfies Record<WorkflowStepStatus, string>;\n\nconst workflowStepIconStatusClasses = {\n pending: 'bg-accent text-muted-foreground',\n running: 'bg-primary/20 text-primary',\n suspended: 'bg-warning/20 text-warning',\n completed: 'bg-success/20 text-success',\n error: 'bg-destructive/20 text-destructive',\n} as const satisfies Record<WorkflowStepStatus, string>;\n\nexport const workflowStepItemVariants = cva(\n 'flex items-start gap-3 px-4 py-3 transition-colors motion-reduce:transition-none',\n {\n variants: {\n status: workflowStepItemStatusClasses,\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nexport const workflowStepIconVariants = cva(\n 'size-5 shrink-0 flex items-center justify-center rounded-full',\n {\n variants: {\n status: workflowStepIconStatusClasses,\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst workflowStepAppearanceConfig = {\n contained: {\n rootClassName: 'relative',\n hasDivider: true,\n hasConnector: false,\n },\n embedded: {\n rootClassName: 'relative',\n hasDivider: true,\n hasConnector: false,\n },\n timeline: {\n rootClassName: 'relative',\n hasDivider: false,\n hasConnector: true,\n },\n} as const satisfies Record<\n WorkflowStepAppearance,\n { rootClassName: string; hasDivider: boolean; hasConnector: boolean }\n>;\n\nexport function normalizeExpandedWorkflowStepIds(\n ids: readonly string[],\n steps: readonly WorkflowStepViewModel[],\n): string[] {\n const requested = new Set(ids);\n return steps.flatMap((step) => (requested.has(step.id) ? [step.id] : []));\n}\n\nfunction areWorkflowStepIdsEqual(\n left: readonly string[],\n right: readonly string[],\n): boolean {\n return (\n left.length === right.length &&\n left.every((id, index) => id === right[index])\n );\n}\n\nfunction isIterableWorkflowStepContent(\n content: React.ReactNode,\n): content is Iterable<React.ReactNode> {\n return (\n typeof content === 'object' &&\n content !== null &&\n Symbol.iterator in content &&\n typeof (content as Iterable<React.ReactNode>)[Symbol.iterator] ===\n 'function'\n );\n}\n\nfunction isIteratorWorkflowStepContent(\n content: unknown,\n): content is Iterator<React.ReactNode> {\n return (\n typeof (content as Partial<Iterator<React.ReactNode>>).next === 'function'\n );\n}\n\ninterface NormalizedWorkflowStepContent {\n content: React.ReactNode;\n hasRenderableContent: boolean;\n changed: boolean;\n}\n\nconst normalizedOneShotWorkflowStepContent = new WeakMap<\n object,\n NormalizedWorkflowStepContent\n>();\n\nfunction normalizeIterableWorkflowStepContent(\n content: Iterable<React.ReactNode>,\n iterator: Iterator<React.ReactNode>,\n): NormalizedWorkflowStepContent {\n const normalizedChildren: React.ReactNode[] = [];\n let hasRenderableContent = false;\n let changed = !Array.isArray(content);\n\n for (let result = iterator.next(); !result.done; result = iterator.next()) {\n const normalizedChild = normalizeWorkflowStepContent(result.value);\n normalizedChildren.push(normalizedChild.content);\n hasRenderableContent ||= normalizedChild.hasRenderableContent;\n changed ||= normalizedChild.changed;\n }\n\n return {\n content: changed ? normalizedChildren : content,\n hasRenderableContent,\n changed,\n };\n}\n\nfunction normalizeWorkflowStepContent(\n content: React.ReactNode,\n): NormalizedWorkflowStepContent {\n if (\n content === null ||\n content === undefined ||\n typeof content === 'boolean' ||\n content === ''\n ) {\n return { content, hasRenderableContent: false, changed: false };\n }\n\n if (\n typeof content === 'string' ||\n typeof content === 'number' ||\n typeof content === 'bigint'\n ) {\n return { content, hasRenderableContent: true, changed: false };\n }\n\n if (React.isValidElement<{ children?: React.ReactNode }>(content)) {\n if (content.type !== React.Fragment) {\n return { content, hasRenderableContent: true, changed: false };\n }\n\n const normalizedChildren = normalizeWorkflowStepContent(\n content.props.children,\n );\n return {\n content: normalizedChildren.changed\n ? React.cloneElement(content, undefined, normalizedChildren.content)\n : content,\n hasRenderableContent: normalizedChildren.hasRenderableContent,\n changed: normalizedChildren.changed,\n };\n }\n\n if (isIterableWorkflowStepContent(content)) {\n const iterator = content[Symbol.iterator]();\n const oneShotCacheKey = isIteratorWorkflowStepContent(iterator)\n ? (iterator as object)\n : null;\n\n if (oneShotCacheKey !== null) {\n const cached = normalizedOneShotWorkflowStepContent.get(oneShotCacheKey);\n if (cached !== undefined) return cached;\n }\n\n const normalized = normalizeIterableWorkflowStepContent(content, iterator);\n if (oneShotCacheKey !== null) {\n normalizedOneShotWorkflowStepContent.set(oneShotCacheKey, normalized);\n }\n return normalized;\n }\n\n return { content, hasRenderableContent: true, changed: false };\n}\n\nfunction getNonBlankWorkflowStepError(\n error: string | undefined,\n): string | undefined {\n return error?.trim() ? error : undefined;\n}\n\nfunction getWorkflowStepStatusLabel(status: WorkflowStepStatus): string {\n switch (status) {\n case 'pending':\n return 'Pending';\n case 'running':\n return 'Running';\n case 'suspended':\n return 'Paused';\n case 'completed':\n return 'Completed';\n case 'error':\n return 'Error';\n default:\n return assertNever(status);\n }\n}\n\nexport interface WorkflowStepStatusIconProps {\n status: WorkflowStepStatus;\n index: number;\n className?: string;\n}\n\nexport function WorkflowStepStatusIcon({\n status,\n index,\n className,\n}: WorkflowStepStatusIconProps) {\n const iconClass = cn(workflowStepIconVariants({ status }), className);\n\n switch (status) {\n case 'pending':\n return (\n <div data-slot=\"workflow-step-icon\" className={iconClass}>\n <span className=\"text-xs font-medium\">{index + 1}</span>\n </div>\n );\n case 'running':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(\n iconClass,\n 'delight-status-pulse motion-reduce:!animate-none',\n )}\n >\n <Loader className=\"size-3 animate-spin motion-reduce:animate-none\" />\n </div>\n );\n case 'suspended':\n return (\n <div data-slot=\"workflow-step-icon\" className={iconClass}>\n <Pause className=\"size-3\" />\n </div>\n );\n case 'completed':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(\n iconClass,\n 'delight-check-bounce motion-reduce:!animate-none',\n )}\n >\n <Check className=\"size-3\" />\n </div>\n );\n case 'error':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(iconClass, 'delight-shake motion-reduce:!animate-none')}\n >\n <AlertCircle className=\"size-3\" />\n </div>\n );\n default:\n return assertNever(status);\n }\n}\n\nWorkflowStepStatusIcon.displayName = 'WorkflowStepStatusIcon';\n\nexport interface WorkflowStepItemProps {\n step: WorkflowStepViewModel;\n index: number;\n isLast: boolean;\n appearance?: WorkflowStepAppearance;\n itemSlot?: string;\n expandable?: boolean;\n expanded: boolean;\n active?: boolean;\n onExpandedChange: () => void;\n}\n\nexport function WorkflowStepItem({\n step,\n index,\n isLast,\n appearance = 'contained',\n itemSlot = 'workflow-step-item',\n expandable = true,\n expanded,\n active = false,\n onExpandedChange,\n}: WorkflowStepItemProps) {\n const contentId = React.useId();\n const errorDescriptionId = React.useId();\n const normalizedContent = normalizeWorkflowStepContent(step.content);\n const hasContent = normalizedContent.hasRenderableContent;\n const error = getNonBlankWorkflowStepError(step.error);\n const hasError = error !== undefined;\n const hasExpandableDetails = hasContent || hasError;\n const isDisclosureControl = hasExpandableDetails && expandable;\n const prefersReducedMotion = useReducedMotion();\n const appearanceConfig = workflowStepAppearanceConfig[appearance];\n const disclosureTransition = {\n duration: prefersReducedMotion ? 0 : 0.2,\n };\n const stepContent = (\n <>\n <WorkflowStepStatusIcon status={step.status} index={index} />\n\n <div className=\"min-w-0 flex-1 text-start\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {step.contextLabel && (\n <span\n data-slot=\"workflow-step-context\"\n className=\"rounded-full border border-border px-2 py-0.5 text-[0.6875rem] font-medium leading-none text-secondary-foreground\"\n >\n {step.contextLabel}\n </span>\n )}\n <span\n className={cn(\n 'text-sm font-medium',\n step.status === 'pending'\n ? 'text-text-quaternary'\n : 'text-foreground',\n )}\n >\n {step.title}\n </span>\n <span className=\"text-xs text-secondary-foreground\">\n {getWorkflowStepStatusLabel(step.status)}\n </span>\n {step.durationMs !== undefined && (\n <span className=\"text-xs text-muted-foreground\">\n {formatAIDuration(step.durationMs)}\n </span>\n )}\n </div>\n {step.description && (\n <p className=\"mt-0.5 text-xs text-text-quaternary\">\n {step.description}\n </p>\n )}\n </div>\n\n {isDisclosureControl && (\n <div className=\"shrink-0\">\n {expanded ? (\n <ChevronDown className=\"size-4 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-4 text-muted-foreground rtl:rotate-180\" />\n )}\n </div>\n )}\n </>\n );\n\n return (\n <div\n data-slot={itemSlot}\n data-step-status={step.status}\n data-appearance={appearance}\n className={cn(\n appearanceConfig.rootClassName,\n appearanceConfig.hasDivider && !isLast && 'border-b border-border',\n )}\n >\n {isDisclosureControl ? (\n <button\n type=\"button\"\n onClick={onExpandedChange}\n aria-current={active ? 'step' : undefined}\n aria-expanded={expanded}\n aria-controls={contentId}\n aria-describedby={hasError ? errorDescriptionId : undefined}\n className={cn(\n workflowStepItemVariants({ status: step.status }),\n 'w-full cursor-pointer hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n )}\n >\n {stepContent}\n </button>\n ) : (\n <div\n aria-current={active ? 'step' : undefined}\n aria-describedby={hasError ? errorDescriptionId : undefined}\n className={cn(\n workflowStepItemVariants({ status: step.status }),\n 'w-full',\n )}\n >\n {stepContent}\n </div>\n )}\n\n <AnimatePresence initial={false}>\n {expanded && hasExpandableDetails && (\n <motion.div\n id={contentId}\n data-slot=\"workflow-step-panel\"\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={disclosureTransition}\n className=\"overflow-hidden motion-reduce:transition-none\"\n >\n <div className=\"px-4 pb-3 ps-12\">\n {hasError && (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <p className=\"text-sm text-destructive\">{error}</p>\n </div>\n </div>\n )}\n {hasContent && (\n <div className={hasError ? 'mt-3' : undefined}>\n {normalizedContent.content}\n </div>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {hasError && (\n <span id={errorDescriptionId} className=\"sr-only\">\n {error}\n </span>\n )}\n\n {appearanceConfig.hasConnector && !isLast && (\n <div\n data-slot=\"workflow-step-connector\"\n aria-hidden=\"true\"\n className=\"absolute top-8 -bottom-3 start-6 w-px bg-border\"\n />\n )}\n </div>\n );\n}\n\nWorkflowStepItem.displayName = 'WorkflowStepItem';\n\nexport function WorkflowStepRenderer({\n steps,\n appearance = 'contained',\n itemSlot = 'workflow-step-item',\n expandable = true,\n activeStepId,\n expandedStepIds: controlledExpandedStepIds,\n defaultExpandedStepIds = [],\n onExpandedStepIdsChange,\n className,\n renderItems = true,\n}: PrivateWorkflowStepRendererProps) {\n const isControlled = controlledExpandedStepIds !== undefined;\n const controlledSnapshot = isControlled\n ? [...controlledExpandedStepIds]\n : undefined;\n const [uncontrolledExpandedStepIds, setUncontrolledExpandedStepIds] =\n React.useState<readonly string[]>(() =>\n normalizeExpandedWorkflowStepIds(defaultExpandedStepIds, steps),\n );\n const lastControlledExpandedStepIdsRef = React.useRef<readonly string[]>(\n controlledSnapshot ?? [],\n );\n const wasControlledRef = React.useRef(isControlled);\n const rawExpandedStepIds = isControlled\n ? controlledExpandedStepIds\n : uncontrolledExpandedStepIds;\n const visibleExpandedStepIds = normalizeExpandedWorkflowStepIds(\n rawExpandedStepIds,\n steps,\n );\n const expandedSet = new Set(visibleExpandedStepIds);\n\n React.useLayoutEffect(() => {\n if (controlledSnapshot !== undefined) {\n lastControlledExpandedStepIdsRef.current = controlledSnapshot;\n }\n }, [controlledSnapshot]);\n\n React.useLayoutEffect(() => {\n const wasControlled = wasControlledRef.current;\n wasControlledRef.current = isControlled;\n if (isControlled) return;\n\n setUncontrolledExpandedStepIds((current) => {\n const source = wasControlled\n ? lastControlledExpandedStepIdsRef.current\n : current;\n const reconciled = normalizeExpandedWorkflowStepIds(source, steps);\n return areWorkflowStepIdsEqual(reconciled, current)\n ? current\n : reconciled;\n });\n }, [isControlled, steps]);\n\n const toggleStep = (id: string) => {\n const next = new Set(visibleExpandedStepIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n const normalizedNext = normalizeExpandedWorkflowStepIds([...next], steps);\n if (!isControlled) {\n setUncontrolledExpandedStepIds([...normalizedNext]);\n }\n onExpandedStepIdsChange?.([...normalizedNext]);\n };\n\n if (!renderItems) return null;\n\n return (\n <ol\n data-slot=\"workflow-step-list\"\n data-appearance={appearance}\n className={cn('m-0 list-none p-0', className)}\n >\n {steps.map((step, index) => (\n <li key={step.id}>\n <WorkflowStepItem\n step={step}\n index={index}\n isLast={index === steps.length - 1}\n appearance={appearance}\n itemSlot={itemSlot}\n expandable={expandable}\n expanded={expandedSet.has(step.id)}\n active={activeStepId === step.id}\n onExpandedChange={() => toggleStep(step.id)}\n />\n </li>\n ))}\n </ol>\n );\n}\n\nWorkflowStepRenderer.displayName = 'WorkflowStepRenderer';\n"],"mappings":";;;;;;;;AAkDA,IAAM,gCAAgC;CACpC,SAAS;CACT,SAAS;CACT,WAAW;CACX,WAAW;CACX,OAAO;AACT;AAEA,IAAM,gCAAgC;CACpC,SAAS;CACT,SAAS;CACT,WAAW;CACX,WAAW;CACX,OAAO;AACT;AAEA,IAAa,2BAA2B,IACtC,oFACA;CACE,UAAU,EACR,QAAQ,8BACV;CACA,iBAAiB,EACf,QAAQ,UACV;AACF,CACF;AAEA,IAAa,2BAA2B,IACtC,iEACA;CACE,UAAU,EACR,QAAQ,8BACV;CACA,iBAAiB,EACf,QAAQ,UACV;AACF,CACF;AAEA,IAAM,+BAA+B;CACnC,WAAW;EACT,eAAe;EACf,YAAY;EACZ,cAAc;CAChB;CACA,UAAU;EACR,eAAe;EACf,YAAY;EACZ,cAAc;CAChB;CACA,UAAU;EACR,eAAe;EACf,YAAY;EACZ,cAAc;CAChB;AACF;AAKA,SAAgB,iCACd,KACA,OACU;CACV,MAAM,YAAY,IAAI,IAAI,GAAG;CAC7B,OAAO,MAAM,SAAS,SAAU,UAAU,IAAI,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAE;AAC1E;AAEA,SAAS,wBACP,MACA,OACS;CACT,OACE,KAAK,WAAW,MAAM,UACtB,KAAK,OAAO,IAAI,UAAU,OAAO,MAAM,MAAM;AAEjD;AAEA,SAAS,8BACP,SACsC;CACtC,OACE,OAAO,YAAY,YACnB,YAAY,QACZ,OAAO,YAAY,WACnB,OAAQ,QAAsC,OAAO,cACnD;AAEN;AAEA,SAAS,8BACP,SACsC;CACtC,OACE,OAAQ,QAA+C,SAAS;AAEpE;AAQA,IAAM,uDAAuC,IAAI,QAG/C;AAEF,SAAS,qCACP,SACA,UAC+B;CAC/B,MAAM,qBAAwC,CAAC;CAC/C,IAAI,uBAAuB;CAC3B,IAAI,UAAU,CAAC,MAAM,QAAQ,OAAO;CAEpC,KAAK,IAAI,SAAS,SAAS,KAAK,GAAG,CAAC,OAAO,MAAM,SAAS,SAAS,KAAK,GAAG;EACzE,MAAM,kBAAkB,6BAA6B,OAAO,KAAK;EACjE,mBAAmB,KAAK,gBAAgB,OAAO;EAC/C,yBAAyB,gBAAgB;EACzC,YAAY,gBAAgB;CAC9B;CAEA,OAAO;EACL,SAAS,UAAU,qBAAqB;EACxC;EACA;CACF;AACF;AAEA,SAAS,6BACP,SAC+B;CAC/B,IACE,YAAY,QACZ,YAAY,KAAA,KACZ,OAAO,YAAY,aACnB,YAAY,IAEZ,OAAO;EAAE;EAAS,sBAAsB;EAAO,SAAS;CAAM;CAGhE,IACE,OAAO,YAAY,YACnB,OAAO,YAAY,YACnB,OAAO,YAAY,UAEnB,OAAO;EAAE;EAAS,sBAAsB;EAAM,SAAS;CAAM;CAG/D,IAAI,QAAM,eAA+C,OAAO,GAAG;EACjE,IAAI,QAAQ,SAAS,QAAM,UACzB,OAAO;GAAE;GAAS,sBAAsB;GAAM,SAAS;EAAM;EAG/D,MAAM,qBAAqB,6BACzB,QAAQ,MAAM,QAChB;EACA,OAAO;GACL,SAAS,mBAAmB,UACxB,QAAM,aAAa,SAAS,KAAA,GAAW,mBAAmB,OAAO,IACjE;GACJ,sBAAsB,mBAAmB;GACzC,SAAS,mBAAmB;EAC9B;CACF;CAEA,IAAI,8BAA8B,OAAO,GAAG;EAC1C,MAAM,WAAW,QAAQ,OAAO,SAAS,CAAC;EAC1C,MAAM,kBAAkB,8BAA8B,QAAQ,IACzD,WACD;EAEJ,IAAI,oBAAoB,MAAM;GAC5B,MAAM,SAAS,qCAAqC,IAAI,eAAe;GACvE,IAAI,WAAW,KAAA,GAAW,OAAO;EACnC;EAEA,MAAM,aAAa,qCAAqC,SAAS,QAAQ;EACzE,IAAI,oBAAoB,MACtB,qCAAqC,IAAI,iBAAiB,UAAU;EAEtE,OAAO;CACT;CAEA,OAAO;EAAE;EAAS,sBAAsB;EAAM,SAAS;CAAM;AAC/D;AAEA,SAAS,6BACP,OACoB;CACpB,OAAO,OAAO,KAAK,IAAI,QAAQ,KAAA;AACjC;AAEA,SAAS,2BAA2B,QAAoC;CACtE,QAAQ,QAAR;EACE,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,SACE,OAAO,YAAY,MAAM;CAC7B;AACF;AAQA,SAAgB,uBAAuB,EACrC,QACA,OACA,aAC8B;CAC9B,MAAM,YAAY,GAAG,yBAAyB,EAAE,OAAO,CAAC,GAAG,SAAS;CAEpE,QAAQ,QAAR;EACE,KAAK,WACH,OACE,oBAAC,OAAD;GAAK,aAAU;GAAqB,WAAW;aAC7C,oBAAC,QAAD;IAAM,WAAU;cAAuB,QAAQ;GAAQ,CAAA;EACpD,CAAA;EAET,KAAK,WACH,OACE,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GACT,WACA,kDACF;aAEA,oBAAC,QAAD,EAAQ,WAAU,iDAAkD,CAAA;EACjE,CAAA;EAET,KAAK,aACH,OACE,oBAAC,OAAD;GAAK,aAAU;GAAqB,WAAW;aAC7C,oBAAC,OAAD,EAAO,WAAU,SAAU,CAAA;EACxB,CAAA;EAET,KAAK,aACH,OACE,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GACT,WACA,kDACF;aAEA,oBAAC,OAAD,EAAO,WAAU,SAAU,CAAA;EACxB,CAAA;EAET,KAAK,SACH,OACE,oBAAC,OAAD;GACE,aAAU;GACV,WAAW,GAAG,WAAW,2CAA2C;aAEpE,oBAAC,aAAD,EAAa,WAAU,SAAU,CAAA;EAC9B,CAAA;EAET,SACE,OAAO,YAAY,MAAM;CAC7B;AACF;AAEA,uBAAuB,cAAc;AAcrC,SAAgB,iBAAiB,EAC/B,MACA,OACA,QACA,aAAa,aACb,WAAW,sBACX,aAAa,MACb,UACA,SAAS,OACT,oBACwB;CACxB,MAAM,YAAY,QAAM,MAAM;CAC9B,MAAM,qBAAqB,QAAM,MAAM;CACvC,MAAM,oBAAoB,6BAA6B,KAAK,OAAO;CACnE,MAAM,aAAa,kBAAkB;CACrC,MAAM,QAAQ,6BAA6B,KAAK,KAAK;CACrD,MAAM,WAAW,UAAU,KAAA;CAC3B,MAAM,uBAAuB,cAAc;CAC3C,MAAM,sBAAsB,wBAAwB;CACpD,MAAM,uBAAuB,iBAAiB;CAC9C,MAAM,mBAAmB,6BAA6B;CACtD,MAAM,uBAAuB,EAC3B,UAAU,uBAAuB,IAAI,GACvC;CACA,MAAM,cACJ,qBAAA,UAAA,EAAA,UAAA;EACE,oBAAC,wBAAD;GAAwB,QAAQ,KAAK;GAAe;EAAQ,CAAA;EAE5D,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACG,KAAK,gBACJ,oBAAC,QAAD;MACE,aAAU;MACV,WAAU;gBAET,KAAK;KACF,CAAA;KAER,oBAAC,QAAD;MACE,WAAW,GACT,uBACA,KAAK,WAAW,YACZ,yBACA,iBACN;gBAEC,KAAK;KACF,CAAA;KACN,oBAAC,QAAD;MAAM,WAAU;gBACb,2BAA2B,KAAK,MAAM;KACnC,CAAA;KACL,KAAK,eAAe,KAAA,KACnB,oBAAC,QAAD;MAAM,WAAU;gBACb,iBAAiB,KAAK,UAAU;KAC7B,CAAA;IAEL;OACJ,KAAK,eACJ,oBAAC,KAAD;IAAG,WAAU;cACV,KAAK;GACL,CAAA,CAEF;;EAEJ,uBACC,oBAAC,OAAD;GAAK,WAAU;aACZ,WACC,oBAAC,aAAD,EAAa,WAAU,+BAAgC,CAAA,IAEvD,oBAAC,cAAD,EAAc,WAAU,8CAA+C,CAAA;EAEtE,CAAA;CAEP,EAAA,CAAA;CAGJ,OACE,qBAAC,OAAD;EACE,aAAW;EACX,oBAAkB,KAAK;EACvB,mBAAiB;EACjB,WAAW,GACT,iBAAiB,eACjB,iBAAiB,cAAc,CAAC,UAAU,wBAC5C;YAPF;GASG,sBACC,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,gBAAc,SAAS,SAAS,KAAA;IAChC,iBAAe;IACf,iBAAe;IACf,oBAAkB,WAAW,qBAAqB,KAAA;IAClD,WAAW,GACT,yBAAyB,EAAE,QAAQ,KAAK,OAAO,CAAC,GAChD,6HACF;cAEC;GACK,CAAA,IAER,oBAAC,OAAD;IACE,gBAAc,SAAS,SAAS,KAAA;IAChC,oBAAkB,WAAW,qBAAqB,KAAA;IAClD,WAAW,GACT,yBAAyB,EAAE,QAAQ,KAAK,OAAO,CAAC,GAChD,QACF;cAEC;GACE,CAAA;GAGP,oBAAC,iBAAD;IAAiB,SAAS;cACvB,YAAY,wBACX,oBAAC,OAAO,KAAR;KACE,IAAI;KACJ,aAAU;KACV,SAAS;MAAE,QAAQ;MAAG,SAAS;KAAE;KACjC,SAAS;MAAE,QAAQ;MAAQ,SAAS;KAAE;KACtC,MAAM;MAAE,QAAQ;MAAG,SAAS;KAAE;KAC9B,YAAY;KACZ,WAAU;eAEV,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,YACC,oBAAC,OAAD;OAAK,WAAU;iBACb,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,oBAAC,aAAD,EAAa,WAAU,0CAA2C,CAAA,GAClE,oBAAC,KAAD;SAAG,WAAU;mBAA4B;QAAS,CAAA,CAC/C;;MACF,CAAA,GAEN,cACC,oBAAC,OAAD;OAAK,WAAW,WAAW,SAAS,KAAA;iBACjC,kBAAkB;MAChB,CAAA,CAEJ;;IACK,CAAA;GAEC,CAAA;GAEhB,YACC,oBAAC,QAAD;IAAM,IAAI;IAAoB,WAAU;cACrC;GACG,CAAA;GAGP,iBAAiB,gBAAgB,CAAC,UACjC,oBAAC,OAAD;IACE,aAAU;IACV,eAAY;IACZ,WAAU;GACX,CAAA;EAEA;;AAET;AAEA,iBAAiB,cAAc;AAE/B,SAAgB,qBAAqB,EACnC,OACA,aAAa,aACb,WAAW,sBACX,aAAa,MACb,cACA,iBAAiB,2BACjB,yBAAyB,CAAC,GAC1B,yBACA,WACA,cAAc,QACqB;CACnC,MAAM,eAAe,8BAA8B,KAAA;CACnD,MAAM,qBAAqB,eACvB,CAAC,GAAG,yBAAyB,IAC7B,KAAA;CACJ,MAAM,CAAC,6BAA6B,kCAClC,QAAM,eACJ,iCAAiC,wBAAwB,KAAK,CAChE;CACF,MAAM,mCAAmC,QAAM,OAC7C,sBAAsB,CAAC,CACzB;CACA,MAAM,mBAAmB,QAAM,OAAO,YAAY;CAIlD,MAAM,yBAAyB,iCAHJ,eACvB,4BACA,6BAGF,KACF;CACA,MAAM,cAAc,IAAI,IAAI,sBAAsB;CAElD,QAAM,sBAAsB;EAC1B,IAAI,uBAAuB,KAAA,GACzB,iCAAiC,UAAU;CAE/C,GAAG,CAAC,kBAAkB,CAAC;CAEvB,QAAM,sBAAsB;EAC1B,MAAM,gBAAgB,iBAAiB;EACvC,iBAAiB,UAAU;EAC3B,IAAI,cAAc;EAElB,gCAAgC,YAAY;GAI1C,MAAM,aAAa,iCAHJ,gBACX,iCAAiC,UACjC,SACwD,KAAK;GACjE,OAAO,wBAAwB,YAAY,OAAO,IAC9C,UACA;EACN,CAAC;CACH,GAAG,CAAC,cAAc,KAAK,CAAC;CAExB,MAAM,cAAc,OAAe;EACjC,MAAM,OAAO,IAAI,IAAI,sBAAsB;EAC3C,IAAI,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,EAAE;OAC3B,KAAK,IAAI,EAAE;EAChB,MAAM,iBAAiB,iCAAiC,CAAC,GAAG,IAAI,GAAG,KAAK;EACxE,IAAI,CAAC,cACH,+BAA+B,CAAC,GAAG,cAAc,CAAC;EAEpD,0BAA0B,CAAC,GAAG,cAAc,CAAC;CAC/C;CAEA,IAAI,CAAC,aAAa,OAAO;CAEzB,OACE,oBAAC,MAAD;EACE,aAAU;EACV,mBAAiB;EACjB,WAAW,GAAG,qBAAqB,SAAS;YAE3C,MAAM,KAAK,MAAM,UAChB,oBAAC,MAAD,EAAA,UACE,oBAAC,kBAAD;GACQ;GACC;GACP,QAAQ,UAAU,MAAM,SAAS;GACrB;GACF;GACE;GACZ,UAAU,YAAY,IAAI,KAAK,EAAE;GACjC,QAAQ,iBAAiB,KAAK;GAC9B,wBAAwB,WAAW,KAAK,EAAE;EAC3C,CAAA,EACC,GAZK,KAAK,EAYV,CACL;CACC,CAAA;AAER;AAEA,qBAAqB,cAAc"}
1
+ {"version":3,"file":"index175.js","names":[],"sources":["../src/components/organisms/ai/_shared/workflow-step-renderer.tsx"],"sourcesContent":["import { cva } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Loader,\n Pause,\n} from 'lucide-react';\nimport { AnimatePresence, motion, useReducedMotion } from 'motion/react';\nimport * as React from 'react';\nimport { assertNever, cn } from '@/lib/utils';\nimport { formatAIDuration } from './format-ai-duration';\n\nexport type WorkflowStepStatus =\n | 'pending'\n | 'running'\n | 'suspended'\n | 'completed'\n | 'error';\n\nexport type WorkflowStepAppearance = 'contained' | 'embedded' | 'timeline';\n\nexport interface WorkflowStepViewModel {\n id: string;\n title: string;\n description?: string;\n contextLabel?: string;\n status: WorkflowStepStatus;\n content?: React.ReactNode;\n durationMs?: number;\n error?: string;\n}\n\nexport interface WorkflowStepRendererProps {\n steps: readonly WorkflowStepViewModel[];\n appearance?: WorkflowStepAppearance;\n itemSlot?: string;\n expandable?: boolean;\n activeStepId?: string;\n expandedStepIds?: readonly string[];\n defaultExpandedStepIds?: readonly string[];\n onExpandedStepIdsChange?: (ids: readonly string[]) => void;\n className?: string;\n}\n\ninterface PrivateWorkflowStepRendererProps extends WorkflowStepRendererProps {\n renderItems?: boolean;\n}\n\nconst workflowStepItemStatusClasses = {\n pending: 'text-secondary-foreground',\n running: 'text-primary',\n suspended: 'text-warning',\n completed: 'text-success',\n error: 'text-destructive',\n} as const satisfies Record<WorkflowStepStatus, string>;\n\nconst workflowStepIconStatusClasses = {\n pending: 'bg-accent text-muted-foreground',\n running: 'bg-primary/20 text-primary',\n suspended: 'bg-warning/20 text-warning',\n completed: 'bg-success/20 text-success',\n error: 'bg-destructive/20 text-destructive',\n} as const satisfies Record<WorkflowStepStatus, string>;\n\nexport const workflowStepItemVariants = cva(\n 'flex items-start gap-3 px-4 py-3 transition-colors motion-reduce:transition-none',\n {\n variants: {\n status: workflowStepItemStatusClasses,\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nexport const workflowStepIconVariants = cva(\n 'size-5 shrink-0 flex items-center justify-center rounded-full',\n {\n variants: {\n status: workflowStepIconStatusClasses,\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst workflowStepAppearanceConfig = {\n contained: {\n rootClassName: 'relative',\n hasDivider: true,\n hasConnector: false,\n },\n embedded: {\n rootClassName: 'relative',\n hasDivider: true,\n hasConnector: false,\n },\n timeline: {\n rootClassName: 'relative',\n hasDivider: false,\n hasConnector: true,\n },\n} as const satisfies Record<\n WorkflowStepAppearance,\n { rootClassName: string; hasDivider: boolean; hasConnector: boolean }\n>;\n\nexport function normalizeExpandedWorkflowStepIds(\n ids: readonly string[],\n steps: readonly WorkflowStepViewModel[],\n): string[] {\n const requested = new Set(ids);\n return steps.flatMap((step) => (requested.has(step.id) ? [step.id] : []));\n}\n\nfunction areWorkflowStepIdsEqual(\n left: readonly string[],\n right: readonly string[],\n): boolean {\n return (\n left.length === right.length &&\n left.every((id, index) => id === right[index])\n );\n}\n\nfunction isIterableWorkflowStepContent(\n content: React.ReactNode,\n): content is Iterable<React.ReactNode> {\n return (\n typeof content === 'object' &&\n content !== null &&\n Symbol.iterator in content &&\n typeof (content as Iterable<React.ReactNode>)[Symbol.iterator] ===\n 'function'\n );\n}\n\nfunction isIteratorWorkflowStepContent(\n content: unknown,\n): content is Iterator<React.ReactNode> {\n return (\n typeof (content as Partial<Iterator<React.ReactNode>>).next === 'function'\n );\n}\n\ninterface NormalizedWorkflowStepContent {\n content: React.ReactNode;\n hasRenderableContent: boolean;\n changed: boolean;\n}\n\nconst normalizedOneShotWorkflowStepContent = new WeakMap<\n object,\n NormalizedWorkflowStepContent\n>();\n\nfunction normalizeIterableWorkflowStepContent(\n content: Iterable<React.ReactNode>,\n iterator: Iterator<React.ReactNode>,\n): NormalizedWorkflowStepContent {\n const normalizedChildren: React.ReactNode[] = [];\n let hasRenderableContent = false;\n let changed = !Array.isArray(content);\n\n for (let result = iterator.next(); !result.done; result = iterator.next()) {\n const normalizedChild = normalizeWorkflowStepContent(result.value);\n normalizedChildren.push(normalizedChild.content);\n hasRenderableContent ||= normalizedChild.hasRenderableContent;\n changed ||= normalizedChild.changed;\n }\n\n return {\n content: changed ? normalizedChildren : content,\n hasRenderableContent,\n changed,\n };\n}\n\nfunction normalizeWorkflowStepContent(\n content: React.ReactNode,\n): NormalizedWorkflowStepContent {\n if (\n content === null ||\n content === undefined ||\n typeof content === 'boolean' ||\n content === ''\n ) {\n return { content, hasRenderableContent: false, changed: false };\n }\n\n if (\n typeof content === 'string' ||\n typeof content === 'number' ||\n typeof content === 'bigint'\n ) {\n return { content, hasRenderableContent: true, changed: false };\n }\n\n if (React.isValidElement<{ children?: React.ReactNode }>(content)) {\n if (content.type !== React.Fragment) {\n return { content, hasRenderableContent: true, changed: false };\n }\n\n const normalizedChildren = normalizeWorkflowStepContent(\n content.props.children,\n );\n return {\n content: normalizedChildren.changed\n ? React.cloneElement(content, undefined, normalizedChildren.content)\n : content,\n hasRenderableContent: normalizedChildren.hasRenderableContent,\n changed: normalizedChildren.changed,\n };\n }\n\n if (isIterableWorkflowStepContent(content)) {\n const iterator = content[Symbol.iterator]();\n const oneShotCacheKey = isIteratorWorkflowStepContent(iterator)\n ? (iterator as object)\n : null;\n\n if (oneShotCacheKey !== null) {\n const cached = normalizedOneShotWorkflowStepContent.get(oneShotCacheKey);\n if (cached !== undefined) return cached;\n }\n\n const normalized = normalizeIterableWorkflowStepContent(content, iterator);\n if (oneShotCacheKey !== null) {\n normalizedOneShotWorkflowStepContent.set(oneShotCacheKey, normalized);\n }\n return normalized;\n }\n\n return { content, hasRenderableContent: true, changed: false };\n}\n\nfunction getNonBlankWorkflowStepError(\n error: string | undefined,\n): string | undefined {\n return error?.trim() ? error : undefined;\n}\n\nfunction getWorkflowStepStatusLabel(status: WorkflowStepStatus): string {\n switch (status) {\n case 'pending':\n return 'Pending';\n case 'running':\n return 'Running';\n case 'suspended':\n return 'Paused';\n case 'completed':\n return 'Completed';\n case 'error':\n return 'Error';\n default:\n return assertNever(status);\n }\n}\n\nexport interface WorkflowStepStatusIconProps {\n status: WorkflowStepStatus;\n index: number;\n className?: string;\n}\n\nexport function WorkflowStepStatusIcon({\n status,\n index,\n className,\n}: WorkflowStepStatusIconProps) {\n const iconClass = cn(workflowStepIconVariants({ status }), className);\n\n switch (status) {\n case 'pending':\n return (\n <div data-slot=\"workflow-step-icon\" className={iconClass}>\n <span className=\"text-xs font-medium\">{index + 1}</span>\n </div>\n );\n case 'running':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(\n iconClass,\n 'delight-status-pulse motion-reduce:!animate-none',\n )}\n >\n <Loader className=\"size-3 animate-spin motion-reduce:animate-none\" />\n </div>\n );\n case 'suspended':\n return (\n <div data-slot=\"workflow-step-icon\" className={iconClass}>\n <Pause className=\"size-3\" />\n </div>\n );\n case 'completed':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(\n iconClass,\n 'delight-check-bounce motion-reduce:!animate-none',\n )}\n >\n <Check className=\"size-3\" />\n </div>\n );\n case 'error':\n return (\n <div\n data-slot=\"workflow-step-icon\"\n className={cn(iconClass, 'delight-shake motion-reduce:!animate-none')}\n >\n <AlertCircle className=\"size-3\" />\n </div>\n );\n default:\n return assertNever(status);\n }\n}\n\nWorkflowStepStatusIcon.displayName = 'WorkflowStepStatusIcon';\n\nexport interface WorkflowStepItemProps {\n step: WorkflowStepViewModel;\n index: number;\n isLast: boolean;\n appearance?: WorkflowStepAppearance;\n itemSlot?: string;\n expandable?: boolean;\n expanded: boolean;\n active?: boolean;\n onExpandedChange: () => void;\n}\n\nexport function WorkflowStepItem({\n step,\n index,\n isLast,\n appearance = 'contained',\n itemSlot = 'workflow-step-item',\n expandable = true,\n expanded,\n active = false,\n onExpandedChange,\n}: WorkflowStepItemProps) {\n const contentId = React.useId();\n const errorDescriptionId = React.useId();\n const normalizedContent = normalizeWorkflowStepContent(step.content);\n const hasContent = normalizedContent.hasRenderableContent;\n const error = getNonBlankWorkflowStepError(step.error);\n const hasError = error !== undefined;\n const hasExpandableDetails = hasContent || hasError;\n const isDisclosureControl = hasExpandableDetails && expandable;\n const prefersReducedMotion = useReducedMotion();\n const appearanceConfig = workflowStepAppearanceConfig[appearance];\n const disclosureTransition = {\n duration: prefersReducedMotion ? 0 : 0.2,\n };\n const stepContent = (\n <>\n <WorkflowStepStatusIcon status={step.status} index={index} />\n\n <div className=\"min-w-0 flex-1 text-start\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {step.contextLabel && (\n <span\n data-slot=\"workflow-step-context\"\n className=\"rounded-full border border-border px-2 py-0.5 text-[0.6875rem] font-medium leading-none text-secondary-foreground\"\n >\n {step.contextLabel}\n </span>\n )}\n <span\n className={cn(\n 'text-sm font-medium',\n step.status === 'pending'\n ? 'text-secondary-foreground'\n : 'text-foreground',\n )}\n >\n {step.title}\n </span>\n <span className=\"text-xs text-secondary-foreground\">\n {getWorkflowStepStatusLabel(step.status)}\n </span>\n {step.durationMs !== undefined && (\n <span className=\"text-xs text-secondary-foreground\">\n {formatAIDuration(step.durationMs)}\n </span>\n )}\n </div>\n {step.description && (\n <p className=\"mt-0.5 text-xs text-secondary-foreground\">\n {step.description}\n </p>\n )}\n </div>\n\n {isDisclosureControl && (\n <div className=\"shrink-0\">\n {expanded ? (\n <ChevronDown className=\"size-4 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-4 text-muted-foreground rtl:rotate-180\" />\n )}\n </div>\n )}\n </>\n );\n\n return (\n <div\n data-slot={itemSlot}\n data-step-status={step.status}\n data-appearance={appearance}\n className={cn(\n appearanceConfig.rootClassName,\n appearanceConfig.hasDivider && !isLast && 'border-b border-border',\n )}\n >\n {isDisclosureControl ? (\n <button\n type=\"button\"\n onClick={onExpandedChange}\n aria-current={active ? 'step' : undefined}\n aria-expanded={expanded}\n aria-controls={contentId}\n aria-describedby={hasError ? errorDescriptionId : undefined}\n className={cn(\n workflowStepItemVariants({ status: step.status }),\n 'w-full cursor-pointer hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n )}\n >\n {stepContent}\n </button>\n ) : (\n <div\n aria-current={active ? 'step' : undefined}\n aria-describedby={hasError ? errorDescriptionId : undefined}\n className={cn(\n workflowStepItemVariants({ status: step.status }),\n 'w-full',\n )}\n >\n {stepContent}\n </div>\n )}\n\n <AnimatePresence initial={false}>\n {expanded && hasExpandableDetails && (\n <motion.div\n id={contentId}\n data-slot=\"workflow-step-panel\"\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={disclosureTransition}\n className=\"overflow-hidden motion-reduce:transition-none\"\n >\n <div className=\"px-4 pb-3 ps-12\">\n {hasError && (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-3\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <p className=\"text-sm text-destructive\">{error}</p>\n </div>\n </div>\n )}\n {hasContent && (\n <div className={hasError ? 'mt-3' : undefined}>\n {normalizedContent.content}\n </div>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n\n {hasError && (\n <span id={errorDescriptionId} className=\"sr-only\">\n {error}\n </span>\n )}\n\n {appearanceConfig.hasConnector && !isLast && (\n <div\n data-slot=\"workflow-step-connector\"\n aria-hidden=\"true\"\n className=\"absolute top-8 -bottom-3 start-6 w-px bg-border\"\n />\n )}\n </div>\n );\n}\n\nWorkflowStepItem.displayName = 'WorkflowStepItem';\n\nexport function WorkflowStepRenderer({\n steps,\n appearance = 'contained',\n itemSlot = 'workflow-step-item',\n expandable = true,\n activeStepId,\n expandedStepIds: controlledExpandedStepIds,\n defaultExpandedStepIds = [],\n onExpandedStepIdsChange,\n className,\n renderItems = true,\n}: PrivateWorkflowStepRendererProps) {\n const isControlled = controlledExpandedStepIds !== undefined;\n const controlledSnapshot = isControlled\n ? [...controlledExpandedStepIds]\n : undefined;\n const [uncontrolledExpandedStepIds, setUncontrolledExpandedStepIds] =\n React.useState<readonly string[]>(() =>\n normalizeExpandedWorkflowStepIds(defaultExpandedStepIds, steps),\n );\n const lastControlledExpandedStepIdsRef = React.useRef<readonly string[]>(\n controlledSnapshot ?? [],\n );\n const wasControlledRef = React.useRef(isControlled);\n const rawExpandedStepIds = isControlled\n ? controlledExpandedStepIds\n : uncontrolledExpandedStepIds;\n const visibleExpandedStepIds = normalizeExpandedWorkflowStepIds(\n rawExpandedStepIds,\n steps,\n );\n const expandedSet = new Set(visibleExpandedStepIds);\n\n React.useLayoutEffect(() => {\n if (controlledSnapshot !== undefined) {\n lastControlledExpandedStepIdsRef.current = controlledSnapshot;\n }\n }, [controlledSnapshot]);\n\n React.useLayoutEffect(() => {\n const wasControlled = wasControlledRef.current;\n wasControlledRef.current = isControlled;\n if (isControlled) return;\n\n setUncontrolledExpandedStepIds((current) => {\n const source = wasControlled\n ? lastControlledExpandedStepIdsRef.current\n : current;\n const reconciled = normalizeExpandedWorkflowStepIds(source, steps);\n return areWorkflowStepIdsEqual(reconciled, current)\n ? current\n : reconciled;\n });\n }, [isControlled, steps]);\n\n const toggleStep = (id: string) => {\n const next = new Set(visibleExpandedStepIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n const normalizedNext = normalizeExpandedWorkflowStepIds([...next], steps);\n if (!isControlled) {\n setUncontrolledExpandedStepIds([...normalizedNext]);\n }\n onExpandedStepIdsChange?.([...normalizedNext]);\n };\n\n if (!renderItems) return null;\n\n return (\n <ol\n data-slot=\"workflow-step-list\"\n data-appearance={appearance}\n className={cn('m-0 list-none p-0', className)}\n >\n {steps.map((step, index) => (\n <li key={step.id}>\n <WorkflowStepItem\n step={step}\n index={index}\n isLast={index === steps.length - 1}\n appearance={appearance}\n itemSlot={itemSlot}\n expandable={expandable}\n expanded={expandedSet.has(step.id)}\n active={activeStepId === step.id}\n onExpandedChange={() => toggleStep(step.id)}\n />\n </li>\n ))}\n </ol>\n );\n}\n\nWorkflowStepRenderer.displayName = 'WorkflowStepRenderer';\n"],"mappings":"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"}
package/dist/index176.js CHANGED
@@ -15,7 +15,7 @@ var stepsContainerVariants = cva("overflow-hidden rounded-xl border", {
15
15
  });
16
16
  var stepItemVariants = cva("flex items-start gap-3 px-4 py-3 transition-colors", {
17
17
  variants: { status: {
18
- pending: "text-muted-foreground",
18
+ pending: "text-secondary-foreground",
19
19
  running: "text-primary",
20
20
  completed: "text-success",
21
21
  error: "text-destructive"
@@ -118,7 +118,7 @@ function AISteps({ steps, title, variant, collapsible = true, defaultExpanded =
118
118
  children: title || "Steps"
119
119
  }),
120
120
  /* @__PURE__ */ jsxs("span", {
121
- className: "text-xs text-text-quaternary",
121
+ className: "text-xs text-secondary-foreground",
122
122
  children: [
123
123
  "(",
124
124
  completedCount,
@@ -1 +1 @@
1
- {"version":3,"file":"index176.js","names":[],"sources":["../src/components/organisms/ai/AISteps/AISteps.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Clock,\n} from 'lucide-react';\nimport { AnimatePresence, motion, useReducedMotion } from 'motion/react';\nimport * as React from 'react';\nimport { cn } from '@/lib/utils';\nimport {\n WorkflowStepItem,\n WorkflowStepRenderer,\n type WorkflowStepStatus,\n WorkflowStepStatusIcon,\n type WorkflowStepViewModel,\n} from '../_shared/workflow-step-renderer';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst stepsContainerVariants = cva('overflow-hidden rounded-xl border', {\n variants: {\n variant: {\n default: 'border-border bg-background',\n subtle: 'border-border bg-muted',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n});\n\nconst stepItemVariants = cva(\n 'flex items-start gap-3 px-4 py-3 transition-colors',\n {\n variants: {\n status: {\n pending: 'text-muted-foreground',\n running: 'text-primary',\n completed: 'text-success',\n error: 'text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst stepIconVariants = cva(\n 'size-5 shrink-0 flex items-center justify-center rounded-full',\n {\n variants: {\n status: {\n pending: 'bg-accent text-muted-foreground',\n running: 'bg-primary/20 text-primary',\n completed: 'bg-success/20 text-success',\n error: 'bg-destructive/20 text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type StepStatus = 'pending' | 'running' | 'completed' | 'error';\n\nexport interface StepData {\n /** Unique step ID */\n id: string;\n /** Step title */\n title: string;\n /** Step description */\n description?: string;\n /** Step status */\n status: StepStatus;\n /** Optional content to show when expanded */\n content?: React.ReactNode;\n /** Duration in ms (for completed steps) */\n duration?: number;\n /** Error message if status is error */\n error?: string;\n}\n\ntype AIStepsWorkflowStatus = Exclude<WorkflowStepStatus, 'suspended'>;\n\nconst WORKFLOW_STATUS_BY_STEP_STATUS = {\n pending: 'pending',\n running: 'running',\n completed: 'completed',\n error: 'error',\n} as const satisfies Record<StepStatus, AIStepsWorkflowStatus>;\n\nfunction toWorkflowStepStatus(status: StepStatus): AIStepsWorkflowStatus {\n return WORKFLOW_STATUS_BY_STEP_STATUS[status];\n}\n\nfunction toWorkflowStep(step: StepData): WorkflowStepViewModel {\n const error = step.error?.trim() ? step.error : undefined;\n\n return {\n id: step.id,\n title: step.title,\n description: step.description,\n status: toWorkflowStepStatus(step.status),\n content: error === undefined ? step.content : undefined,\n durationMs: step.status === 'completed' ? step.duration : undefined,\n error,\n };\n}\n\n// =============================================================================\n// Step Icon Component\n// =============================================================================\n\ninterface StepIconProps {\n status: StepStatus;\n index: number;\n className?: string;\n}\n\nfunction StepIcon({ status, index, className }: StepIconProps) {\n return (\n <WorkflowStepStatusIcon\n status={toWorkflowStepStatus(status)}\n index={index}\n className={className}\n />\n );\n}\n\nStepIcon.displayName = 'StepIcon';\n\n// =============================================================================\n// Single Step Item\n// =============================================================================\n\ninterface AIStepItemProps {\n step: StepData;\n index: number;\n isLast: boolean;\n expandable?: boolean;\n defaultExpanded?: boolean;\n}\n\nfunction AIStepItem({\n step,\n index,\n isLast,\n expandable = true,\n defaultExpanded = false,\n}: AIStepItemProps) {\n const [expanded, setExpanded] = React.useState(defaultExpanded);\n\n return (\n <WorkflowStepItem\n step={toWorkflowStep(step)}\n index={index}\n isLast={isLast}\n appearance=\"contained\"\n itemSlot=\"ai-step-item\"\n expandable={expandable}\n expanded={expanded}\n onExpandedChange={() => setExpanded((value) => !value)}\n />\n );\n}\n\nAIStepItem.displayName = 'AIStepItem';\n\n// =============================================================================\n// Main AISteps Component\n// =============================================================================\n\nexport interface AIStepsProps\n extends VariantProps<typeof stepsContainerVariants> {\n /** Array of steps */\n steps: StepData[];\n /** Title for the steps container */\n title?: string;\n /** Whether the container is collapsible */\n collapsible?: boolean;\n /** Whether the container is expanded by default */\n defaultExpanded?: boolean;\n /** Whether individual steps are expandable */\n stepsExpandable?: boolean;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * AISteps displays a multi-step progress indicator for AI workflows.\n * Shows step status (pending, running, complete, error) with expandable details and timing.\n *\n * @llms Multi-step AI progress with status icons, timing, expandable details\n * @example\n * ```tsx\n * <AISteps\n * title=\"Processing\"\n * steps={[\n * { label: 'Analyzing', status: 'complete', duration: '2s' },\n * { label: 'Generating', status: 'running' },\n * ]}\n * />\n * ```\n */\nexport function AISteps({\n steps,\n title,\n variant,\n collapsible = true,\n defaultExpanded = true,\n stepsExpandable = true,\n className,\n}: AIStepsProps) {\n const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);\n const prefersReducedMotion = useReducedMotion();\n\n // Calculate summary stats\n const completedCount = steps.filter((s) => s.status === 'completed').length;\n const totalCount = steps.length;\n const hasError = steps.some((s) => s.status === 'error');\n const isRunning = steps.some((s) => s.status === 'running');\n\n return (\n <section\n data-slot=\"ai-steps\"\n aria-label={title || 'Steps'}\n className={cn(stepsContainerVariants({ variant }), className)}\n >\n {/* Header */}\n {(title || collapsible) && (\n <button\n type=\"button\"\n onClick={() => collapsible && setIsExpanded(!isExpanded)}\n disabled={!collapsible}\n aria-expanded={collapsible ? isExpanded : undefined}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 border-b border-border',\n collapsible && 'hover:bg-muted cursor-pointer',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n )}\n >\n <div className=\"flex items-center gap-2\">\n {isRunning && (\n <Clock className=\"size-4 text-primary animate-pulse motion-reduce:animate-none\" />\n )}\n {hasError && !isRunning && (\n <AlertCircle className=\"size-4 text-destructive\" />\n )}\n {!isRunning && !hasError && completedCount === totalCount && (\n <Check className=\"size-4 text-success\" />\n )}\n <span className=\"text-sm font-medium text-foreground\">\n {title || 'Steps'}\n </span>\n <span className=\"text-xs text-text-quaternary\">\n ({completedCount}/{totalCount})\n </span>\n </div>\n\n {collapsible && (\n <div className=\"shrink-0\">\n {isExpanded ? (\n <ChevronDown className=\"size-4 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-4 text-muted-foreground rtl:rotate-180\" />\n )}\n </div>\n )}\n </button>\n )}\n\n {/* Steps List */}\n <AnimatePresence initial={false}>\n {isExpanded && (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: prefersReducedMotion ? 0 : 0.2 }}\n className=\"overflow-hidden\"\n >\n <WorkflowStepRenderer\n appearance=\"contained\"\n steps={steps.map(toWorkflowStep)}\n itemSlot=\"ai-step-item\"\n expandable={stepsExpandable}\n />\n </motion.div>\n )}\n </AnimatePresence>\n </section>\n );\n}\n\nAISteps.displayName = 'AISteps';\n\nexport {\n AIStepItem,\n StepIcon,\n stepIconVariants,\n stepItemVariants,\n stepsContainerVariants,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index176.js","names":[],"sources":["../src/components/organisms/ai/AISteps/AISteps.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Clock,\n} from 'lucide-react';\nimport { AnimatePresence, motion, useReducedMotion } from 'motion/react';\nimport * as React from 'react';\nimport { cn } from '@/lib/utils';\nimport {\n WorkflowStepItem,\n WorkflowStepRenderer,\n type WorkflowStepStatus,\n WorkflowStepStatusIcon,\n type WorkflowStepViewModel,\n} from '../_shared/workflow-step-renderer';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst stepsContainerVariants = cva('overflow-hidden rounded-xl border', {\n variants: {\n variant: {\n default: 'border-border bg-background',\n subtle: 'border-border bg-muted',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n});\n\nconst stepItemVariants = cva(\n 'flex items-start gap-3 px-4 py-3 transition-colors',\n {\n variants: {\n status: {\n pending: 'text-secondary-foreground',\n running: 'text-primary',\n completed: 'text-success',\n error: 'text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst stepIconVariants = cva(\n 'size-5 shrink-0 flex items-center justify-center rounded-full',\n {\n variants: {\n status: {\n pending: 'bg-accent text-muted-foreground',\n running: 'bg-primary/20 text-primary',\n completed: 'bg-success/20 text-success',\n error: 'bg-destructive/20 text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type StepStatus = 'pending' | 'running' | 'completed' | 'error';\n\nexport interface StepData {\n /** Unique step ID */\n id: string;\n /** Step title */\n title: string;\n /** Step description */\n description?: string;\n /** Step status */\n status: StepStatus;\n /** Optional content to show when expanded */\n content?: React.ReactNode;\n /** Duration in ms (for completed steps) */\n duration?: number;\n /** Error message if status is error */\n error?: string;\n}\n\ntype AIStepsWorkflowStatus = Exclude<WorkflowStepStatus, 'suspended'>;\n\nconst WORKFLOW_STATUS_BY_STEP_STATUS = {\n pending: 'pending',\n running: 'running',\n completed: 'completed',\n error: 'error',\n} as const satisfies Record<StepStatus, AIStepsWorkflowStatus>;\n\nfunction toWorkflowStepStatus(status: StepStatus): AIStepsWorkflowStatus {\n return WORKFLOW_STATUS_BY_STEP_STATUS[status];\n}\n\nfunction toWorkflowStep(step: StepData): WorkflowStepViewModel {\n const error = step.error?.trim() ? step.error : undefined;\n\n return {\n id: step.id,\n title: step.title,\n description: step.description,\n status: toWorkflowStepStatus(step.status),\n content: error === undefined ? step.content : undefined,\n durationMs: step.status === 'completed' ? step.duration : undefined,\n error,\n };\n}\n\n// =============================================================================\n// Step Icon Component\n// =============================================================================\n\ninterface StepIconProps {\n status: StepStatus;\n index: number;\n className?: string;\n}\n\nfunction StepIcon({ status, index, className }: StepIconProps) {\n return (\n <WorkflowStepStatusIcon\n status={toWorkflowStepStatus(status)}\n index={index}\n className={className}\n />\n );\n}\n\nStepIcon.displayName = 'StepIcon';\n\n// =============================================================================\n// Single Step Item\n// =============================================================================\n\ninterface AIStepItemProps {\n step: StepData;\n index: number;\n isLast: boolean;\n expandable?: boolean;\n defaultExpanded?: boolean;\n}\n\nfunction AIStepItem({\n step,\n index,\n isLast,\n expandable = true,\n defaultExpanded = false,\n}: AIStepItemProps) {\n const [expanded, setExpanded] = React.useState(defaultExpanded);\n\n return (\n <WorkflowStepItem\n step={toWorkflowStep(step)}\n index={index}\n isLast={isLast}\n appearance=\"contained\"\n itemSlot=\"ai-step-item\"\n expandable={expandable}\n expanded={expanded}\n onExpandedChange={() => setExpanded((value) => !value)}\n />\n );\n}\n\nAIStepItem.displayName = 'AIStepItem';\n\n// =============================================================================\n// Main AISteps Component\n// =============================================================================\n\nexport interface AIStepsProps\n extends VariantProps<typeof stepsContainerVariants> {\n /** Array of steps */\n steps: StepData[];\n /** Title for the steps container */\n title?: string;\n /** Whether the container is collapsible */\n collapsible?: boolean;\n /** Whether the container is expanded by default */\n defaultExpanded?: boolean;\n /** Whether individual steps are expandable */\n stepsExpandable?: boolean;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * AISteps displays a multi-step progress indicator for AI workflows.\n * Shows step status (pending, running, complete, error) with expandable details and timing.\n *\n * @llms Multi-step AI progress with status icons, timing, expandable details\n * @example\n * ```tsx\n * <AISteps\n * title=\"Processing\"\n * steps={[\n * { label: 'Analyzing', status: 'complete', duration: '2s' },\n * { label: 'Generating', status: 'running' },\n * ]}\n * />\n * ```\n */\nexport function AISteps({\n steps,\n title,\n variant,\n collapsible = true,\n defaultExpanded = true,\n stepsExpandable = true,\n className,\n}: AIStepsProps) {\n const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);\n const prefersReducedMotion = useReducedMotion();\n\n // Calculate summary stats\n const completedCount = steps.filter((s) => s.status === 'completed').length;\n const totalCount = steps.length;\n const hasError = steps.some((s) => s.status === 'error');\n const isRunning = steps.some((s) => s.status === 'running');\n\n return (\n <section\n data-slot=\"ai-steps\"\n aria-label={title || 'Steps'}\n className={cn(stepsContainerVariants({ variant }), className)}\n >\n {/* Header */}\n {(title || collapsible) && (\n <button\n type=\"button\"\n onClick={() => collapsible && setIsExpanded(!isExpanded)}\n disabled={!collapsible}\n aria-expanded={collapsible ? isExpanded : undefined}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 border-b border-border',\n collapsible && 'hover:bg-muted cursor-pointer',\n 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n )}\n >\n <div className=\"flex items-center gap-2\">\n {isRunning && (\n <Clock className=\"size-4 text-primary animate-pulse motion-reduce:animate-none\" />\n )}\n {hasError && !isRunning && (\n <AlertCircle className=\"size-4 text-destructive\" />\n )}\n {!isRunning && !hasError && completedCount === totalCount && (\n <Check className=\"size-4 text-success\" />\n )}\n <span className=\"text-sm font-medium text-foreground\">\n {title || 'Steps'}\n </span>\n <span className=\"text-xs text-secondary-foreground\">\n ({completedCount}/{totalCount})\n </span>\n </div>\n\n {collapsible && (\n <div className=\"shrink-0\">\n {isExpanded ? (\n <ChevronDown className=\"size-4 text-muted-foreground\" />\n ) : (\n <ChevronRight className=\"size-4 text-muted-foreground rtl:rotate-180\" />\n )}\n </div>\n )}\n </button>\n )}\n\n {/* Steps List */}\n <AnimatePresence initial={false}>\n {isExpanded && (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: prefersReducedMotion ? 0 : 0.2 }}\n className=\"overflow-hidden\"\n >\n <WorkflowStepRenderer\n appearance=\"contained\"\n steps={steps.map(toWorkflowStep)}\n itemSlot=\"ai-step-item\"\n expandable={stepsExpandable}\n />\n </motion.div>\n )}\n </AnimatePresence>\n </section>\n );\n}\n\nAISteps.displayName = 'AISteps';\n\nexport {\n AIStepItem,\n StepIcon,\n stepIconVariants,\n stepItemVariants,\n stepsContainerVariants,\n};\n"],"mappings":"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"}
package/dist/index177.js CHANGED
@@ -33,7 +33,7 @@ var systemMessageIconVariants = cva("shrink-0 mt-0.5", {
33
33
  success: "text-success",
34
34
  warning: "text-warning",
35
35
  error: "text-destructive",
36
- neutral: "text-text-quaternary"
36
+ neutral: "text-muted-foreground"
37
37
  },
38
38
  size: {
39
39
  sm: "size-4",
@@ -168,7 +168,7 @@ function AISystemMessageBanner({ children, variant = "info", dismissible = false
168
168
  children: [
169
169
  icon || /* @__PURE__ */ jsx(IconComponent, {
170
170
  "aria-hidden": "true",
171
- className: cn("size-4 shrink-0", variant === "info" && "text-primary", variant === "success" && "text-success", variant === "warning" && "text-warning", variant === "error" && "text-destructive", variant === "neutral" && "text-text-quaternary")
171
+ className: cn("size-4 shrink-0", variant === "info" && "text-primary", variant === "success" && "text-success", variant === "warning" && "text-warning", variant === "error" && "text-destructive", variant === "neutral" && "text-muted-foreground")
172
172
  }),
173
173
  /* @__PURE__ */ jsx("span", {
174
174
  className: "text-sm",
@@ -1 +1 @@
1
- {"version":3,"file":"index177.js","names":[],"sources":["../src/components/organisms/ai/AISystemMessage/AISystemMessage.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { AlertCircle, AlertTriangle, CheckCircle, Info, X } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { Button } from '@/components/atoms/buttons/Button';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst systemMessageVariants = cva(\n 'flex items-start gap-3 px-4 py-3 rounded-xl border',\n {\n variants: {\n variant: {\n info: 'bg-primary/10 border-primary text-primary',\n success: 'bg-success/10 border-success/50 text-success',\n warning: 'bg-warning/10 border-warning/50 text-warning',\n error: 'bg-destructive/10 border-destructive/50 text-destructive',\n neutral: 'bg-muted border-border text-secondary-foreground',\n },\n size: {\n sm: 'px-3 py-2 text-xs',\n md: 'px-4 py-3 text-sm',\n lg: 'px-5 py-4 text-base',\n },\n },\n defaultVariants: {\n variant: 'info',\n size: 'md',\n },\n },\n);\n\nconst systemMessageIconVariants = cva('shrink-0 mt-0.5', {\n variants: {\n variant: {\n info: 'text-primary',\n success: 'text-success',\n warning: 'text-warning',\n error: 'text-destructive',\n neutral: 'text-text-quaternary',\n },\n size: {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n },\n },\n defaultVariants: {\n variant: 'info',\n size: 'md',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type SystemMessageVariant =\n | 'info'\n | 'success'\n | 'warning'\n | 'error'\n | 'neutral';\n\n// =============================================================================\n// Icon Map\n// =============================================================================\n\nconst iconMap = {\n info: Info,\n success: CheckCircle,\n warning: AlertTriangle,\n error: AlertCircle,\n neutral: Info,\n};\n\n// =============================================================================\n// Main AISystemMessage Component\n// =============================================================================\n\nexport interface AISystemMessageProps\n extends VariantProps<typeof systemMessageVariants> {\n /** Message content */\n children: React.ReactNode;\n /** Title for the message */\n title?: string;\n /** Whether the message can be dismissed */\n dismissible?: boolean;\n /** Callback when dismissed */\n onDismiss?: () => void;\n /** Custom icon */\n icon?: React.ReactNode;\n /** Hide the icon */\n hideIcon?: boolean;\n /** Actions to display */\n actions?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISystemMessage({\n children,\n title,\n variant = 'info',\n size = 'md',\n dismissible = false,\n onDismiss,\n icon,\n hideIcon = false,\n actions,\n className,\n}: AISystemMessageProps) {\n const [isVisible, setIsVisible] = React.useState(true);\n\n const handleDismiss = () => {\n setIsVisible(false);\n onDismiss?.();\n };\n\n const IconComponent = iconMap[variant || 'info'];\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-system-message\"\n role=\"status\"\n aria-live=\"polite\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10, height: 0, marginBottom: 0 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n systemMessageVariants({ variant, size }),\n 'motion-reduce:transition-none',\n className,\n )}\n >\n {/* Icon */}\n {!hideIcon && (\n <div\n className={systemMessageIconVariants({ variant, size })}\n aria-hidden=\"true\"\n >\n {icon || <IconComponent className=\"size-full\" />}\n </div>\n )}\n\n {/* Content */}\n <div className=\"flex-1 min-w-0\">\n {title && <p className=\"font-medium mb-0.5\">{title}</p>}\n <div className={cn(!title && 'leading-snug')}>{children}</div>\n\n {/* Actions */}\n {actions && (\n <div className=\"flex items-center gap-2 mt-2\">{actions}</div>\n )}\n </div>\n\n {/* Dismiss button */}\n {dismissible && (\n <Button\n appearance=\"ghost\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={handleDismiss}\n aria-label=\"Dismiss message\"\n className=\"size-6 p-0 shrink-0 -mt-0.5 -me-1 hover:bg-foreground/5\"\n >\n <X className=\"size-4\" />\n </Button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAISystemMessage.displayName = 'AISystemMessage';\n\n// =============================================================================\n// System Message Banner (Full-width variant)\n// =============================================================================\n\nexport interface AISystemMessageBannerProps\n extends VariantProps<typeof systemMessageVariants> {\n /** Message content */\n children: React.ReactNode;\n /** Whether the banner can be dismissed */\n dismissible?: boolean;\n /** Callback when dismissed */\n onDismiss?: () => void;\n /** Custom icon */\n icon?: React.ReactNode;\n /** Action button */\n action?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISystemMessageBanner({\n children,\n variant = 'info',\n dismissible = false,\n onDismiss,\n icon,\n action,\n className,\n}: AISystemMessageBannerProps) {\n const [isVisible, setIsVisible] = React.useState(true);\n\n const handleDismiss = () => {\n setIsVisible(false);\n onDismiss?.();\n };\n\n const IconComponent = iconMap[variant || 'info'];\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-system-message-banner\"\n role=\"status\"\n aria-live=\"polite\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n 'w-full px-4 py-2 flex items-center justify-center gap-3 motion-reduce:transition-none',\n variant === 'info' && 'bg-primary/10 text-primary',\n variant === 'success' && 'bg-success/10 text-success',\n variant === 'warning' && 'bg-warning/10 text-warning',\n variant === 'error' && 'bg-destructive/10 text-destructive',\n variant === 'neutral' && 'bg-muted text-secondary-foreground',\n className,\n )}\n >\n <div className=\"flex items-center gap-2 flex-1 justify-center\">\n {icon || (\n <IconComponent\n aria-hidden=\"true\"\n className={cn(\n 'size-4 shrink-0',\n variant === 'info' && 'text-primary',\n variant === 'success' && 'text-success',\n variant === 'warning' && 'text-warning',\n variant === 'error' && 'text-destructive',\n variant === 'neutral' && 'text-text-quaternary',\n )}\n />\n )}\n <span className=\"text-sm\">{children}</span>\n {action}\n </div>\n\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n aria-label=\"Dismiss banner\"\n className=\"p-1 rounded hover:bg-foreground/5 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X className=\"size-4\" />\n </button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAISystemMessageBanner.displayName = 'AISystemMessageBanner';\n\nexport {\n AISystemMessage,\n AISystemMessageBanner,\n systemMessageIconVariants,\n systemMessageVariants,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index177.js","names":[],"sources":["../src/components/organisms/ai/AISystemMessage/AISystemMessage.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { AlertCircle, AlertTriangle, CheckCircle, Info, X } from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { Button } from '@/components/atoms/buttons/Button';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst systemMessageVariants = cva(\n 'flex items-start gap-3 px-4 py-3 rounded-xl border',\n {\n variants: {\n variant: {\n info: 'bg-primary/10 border-primary text-primary',\n success: 'bg-success/10 border-success/50 text-success',\n warning: 'bg-warning/10 border-warning/50 text-warning',\n error: 'bg-destructive/10 border-destructive/50 text-destructive',\n neutral: 'bg-muted border-border text-secondary-foreground',\n },\n size: {\n sm: 'px-3 py-2 text-xs',\n md: 'px-4 py-3 text-sm',\n lg: 'px-5 py-4 text-base',\n },\n },\n defaultVariants: {\n variant: 'info',\n size: 'md',\n },\n },\n);\n\nconst systemMessageIconVariants = cva('shrink-0 mt-0.5', {\n variants: {\n variant: {\n info: 'text-primary',\n success: 'text-success',\n warning: 'text-warning',\n error: 'text-destructive',\n neutral: 'text-muted-foreground',\n },\n size: {\n sm: 'size-4',\n md: 'size-5',\n lg: 'size-6',\n },\n },\n defaultVariants: {\n variant: 'info',\n size: 'md',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type SystemMessageVariant =\n | 'info'\n | 'success'\n | 'warning'\n | 'error'\n | 'neutral';\n\n// =============================================================================\n// Icon Map\n// =============================================================================\n\nconst iconMap = {\n info: Info,\n success: CheckCircle,\n warning: AlertTriangle,\n error: AlertCircle,\n neutral: Info,\n};\n\n// =============================================================================\n// Main AISystemMessage Component\n// =============================================================================\n\nexport interface AISystemMessageProps\n extends VariantProps<typeof systemMessageVariants> {\n /** Message content */\n children: React.ReactNode;\n /** Title for the message */\n title?: string;\n /** Whether the message can be dismissed */\n dismissible?: boolean;\n /** Callback when dismissed */\n onDismiss?: () => void;\n /** Custom icon */\n icon?: React.ReactNode;\n /** Hide the icon */\n hideIcon?: boolean;\n /** Actions to display */\n actions?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISystemMessage({\n children,\n title,\n variant = 'info',\n size = 'md',\n dismissible = false,\n onDismiss,\n icon,\n hideIcon = false,\n actions,\n className,\n}: AISystemMessageProps) {\n const [isVisible, setIsVisible] = React.useState(true);\n\n const handleDismiss = () => {\n setIsVisible(false);\n onDismiss?.();\n };\n\n const IconComponent = iconMap[variant || 'info'];\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-system-message\"\n role=\"status\"\n aria-live=\"polite\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10, height: 0, marginBottom: 0 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n systemMessageVariants({ variant, size }),\n 'motion-reduce:transition-none',\n className,\n )}\n >\n {/* Icon */}\n {!hideIcon && (\n <div\n className={systemMessageIconVariants({ variant, size })}\n aria-hidden=\"true\"\n >\n {icon || <IconComponent className=\"size-full\" />}\n </div>\n )}\n\n {/* Content */}\n <div className=\"flex-1 min-w-0\">\n {title && <p className=\"font-medium mb-0.5\">{title}</p>}\n <div className={cn(!title && 'leading-snug')}>{children}</div>\n\n {/* Actions */}\n {actions && (\n <div className=\"flex items-center gap-2 mt-2\">{actions}</div>\n )}\n </div>\n\n {/* Dismiss button */}\n {dismissible && (\n <Button\n appearance=\"ghost\"\n variant=\"secondary\"\n size=\"sm\"\n onClick={handleDismiss}\n aria-label=\"Dismiss message\"\n className=\"size-6 p-0 shrink-0 -mt-0.5 -me-1 hover:bg-foreground/5\"\n >\n <X className=\"size-4\" />\n </Button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAISystemMessage.displayName = 'AISystemMessage';\n\n// =============================================================================\n// System Message Banner (Full-width variant)\n// =============================================================================\n\nexport interface AISystemMessageBannerProps\n extends VariantProps<typeof systemMessageVariants> {\n /** Message content */\n children: React.ReactNode;\n /** Whether the banner can be dismissed */\n dismissible?: boolean;\n /** Callback when dismissed */\n onDismiss?: () => void;\n /** Custom icon */\n icon?: React.ReactNode;\n /** Action button */\n action?: React.ReactNode;\n /** Additional class name */\n className?: string;\n}\n\nfunction AISystemMessageBanner({\n children,\n variant = 'info',\n dismissible = false,\n onDismiss,\n icon,\n action,\n className,\n}: AISystemMessageBannerProps) {\n const [isVisible, setIsVisible] = React.useState(true);\n\n const handleDismiss = () => {\n setIsVisible(false);\n onDismiss?.();\n };\n\n const IconComponent = iconMap[variant || 'info'];\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-system-message-banner\"\n role=\"status\"\n aria-live=\"polite\"\n initial={{ opacity: 0, height: 0 }}\n animate={{ opacity: 1, height: 'auto' }}\n exit={{ opacity: 0, height: 0 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n 'w-full px-4 py-2 flex items-center justify-center gap-3 motion-reduce:transition-none',\n variant === 'info' && 'bg-primary/10 text-primary',\n variant === 'success' && 'bg-success/10 text-success',\n variant === 'warning' && 'bg-warning/10 text-warning',\n variant === 'error' && 'bg-destructive/10 text-destructive',\n variant === 'neutral' && 'bg-muted text-secondary-foreground',\n className,\n )}\n >\n <div className=\"flex items-center gap-2 flex-1 justify-center\">\n {icon || (\n <IconComponent\n aria-hidden=\"true\"\n className={cn(\n 'size-4 shrink-0',\n variant === 'info' && 'text-primary',\n variant === 'success' && 'text-success',\n variant === 'warning' && 'text-warning',\n variant === 'error' && 'text-destructive',\n variant === 'neutral' && 'text-muted-foreground',\n )}\n />\n )}\n <span className=\"text-sm\">{children}</span>\n {action}\n </div>\n\n {dismissible && (\n <button\n type=\"button\"\n onClick={handleDismiss}\n aria-label=\"Dismiss banner\"\n className=\"p-1 rounded hover:bg-foreground/5 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X className=\"size-4\" />\n </button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAISystemMessageBanner.displayName = 'AISystemMessageBanner';\n\nexport {\n AISystemMessage,\n AISystemMessageBanner,\n systemMessageIconVariants,\n systemMessageVariants,\n};\n"],"mappings":"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"}
package/dist/index184.js CHANGED
@@ -75,7 +75,7 @@ function ToolCallSection({ title, icon, children, className }) {
75
75
  return /* @__PURE__ */ jsxs("div", {
76
76
  className: cn("space-y-2", className),
77
77
  children: [/* @__PURE__ */ jsxs("div", {
78
- className: "flex items-center gap-2 text-xs font-medium text-text-quaternary uppercase tracking-wide",
78
+ className: "flex items-center gap-2 text-xs font-medium text-secondary-foreground uppercase tracking-wide",
79
79
  children: [icon, title]
80
80
  }), children]
81
81
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index184.js","names":[],"sources":["../src/components/organisms/ai/AIToolCall/AIToolCall.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Clock,\n Code,\n Wrench,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { AICodeBlock } from '@/components/organisms/ai/AICodeBlock';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst toolCallVariants = cva(\n 'overflow-hidden rounded-xl border transition-colors',\n {\n variants: {\n status: {\n pending: 'border-border bg-muted',\n running: 'border-primary bg-primary/10',\n success: 'border-success/50 bg-success/10',\n error: 'border-destructive/50 bg-destructive/10',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst statusIconVariants = cva('size-4', {\n variants: {\n status: {\n pending: 'text-muted-foreground',\n running: 'text-primary animate-spin motion-reduce:animate-none',\n success: 'text-success',\n error: 'text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error';\n\nexport interface ToolCallPart {\n /** Unique tool call ID */\n toolCallId: string;\n /** Tool name */\n toolName: string;\n /** Tool input/arguments */\n args?: Record<string, unknown>;\n /** Tool output/result */\n result?: unknown;\n /** Tool status */\n status?: ToolCallStatus;\n /** Error message if status is error */\n error?: string;\n}\n\n// =============================================================================\n// Status Icon Component\n// =============================================================================\n\ninterface StatusIconProps {\n status: ToolCallStatus;\n className?: string;\n}\n\nfunction StatusIcon({ status, className }: StatusIconProps) {\n const iconClass = cn(statusIconVariants({ status }), className);\n\n switch (status) {\n case 'running':\n return <Clock className={iconClass} />;\n case 'success':\n return (\n <Check\n className={cn(\n iconClass,\n 'delight-check-bounce motion-reduce:!animate-none',\n )}\n />\n );\n case 'error':\n return (\n <AlertCircle\n className={cn(iconClass, 'delight-shake motion-reduce:!animate-none')}\n />\n );\n default:\n return <Wrench className={iconClass} />;\n }\n}\n\nStatusIcon.displayName = 'StatusIcon';\n\n// =============================================================================\n// Tool Call Header\n// =============================================================================\n\ninterface AIToolCallHeaderProps {\n toolName: string;\n status: ToolCallStatus;\n isOpen: boolean;\n onToggle: () => void;\n className?: string;\n}\n\nfunction AIToolCallHeader({\n toolName,\n status,\n isOpen,\n onToggle,\n className,\n}: AIToolCallHeaderProps) {\n const statusLabel = {\n pending: 'Pending',\n running: 'Running...',\n success: 'Completed',\n error: 'Failed',\n };\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isOpen}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 transition-colors hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n className,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <StatusIcon status={status} aria-hidden=\"true\" />\n <div className=\"flex flex-col items-start\">\n <span className=\"text-sm font-medium text-foreground\">\n {toolName}\n </span>\n <span className=\"text-xs text-secondary-foreground\">\n {statusLabel[status]}\n </span>\n </div>\n </div>\n {isOpen ? (\n <ChevronDown\n className=\"size-4 text-muted-foreground\"\n aria-hidden=\"true\"\n />\n ) : (\n <ChevronRight\n className=\"size-4 text-muted-foreground rtl:rotate-180\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n}\n\nAIToolCallHeader.displayName = 'AIToolCallHeader';\n\n// =============================================================================\n// Tool Call Section\n// =============================================================================\n\ninterface ToolCallSectionProps {\n title: string;\n icon: React.ReactNode;\n children: React.ReactNode;\n className?: string;\n}\n\nfunction ToolCallSection({\n title,\n icon,\n children,\n className,\n}: ToolCallSectionProps) {\n return (\n <div className={cn('space-y-2', className)}>\n <div className=\"flex items-center gap-2 text-xs font-medium text-text-quaternary uppercase tracking-wide\">\n {icon}\n {title}\n </div>\n {children}\n </div>\n );\n}\n\nToolCallSection.displayName = 'ToolCallSection';\n\n// =============================================================================\n// Main AIToolCall Component\n// =============================================================================\n\nexport interface AIToolCallProps extends VariantProps<typeof toolCallVariants> {\n /** Tool call data */\n toolPart: ToolCallPart;\n /** Whether to expand by default */\n defaultOpen?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === 'string') return value;\n return JSON.stringify(value, null, 2);\n}\n\nfunction AIToolCall({\n toolPart,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n className,\n}: AIToolCallProps) {\n const [isOpen, setIsOpen] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const status = toolPart.status || 'pending';\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <div\n data-slot=\"ai-tool-call\"\n className={cn(toolCallVariants({ status }), className)}\n >\n <AIToolCallHeader\n toolName={toolPart.toolName}\n status={status}\n isOpen={isOpen}\n onToggle={handleToggle}\n />\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2 }}\n className=\"overflow-hidden\"\n >\n <div className=\"px-4 pb-4 space-y-4\">\n {/* Tool Call ID */}\n <div className=\"text-xs text-muted-foreground font-mono\">\n ID: {toolPart.toolCallId}\n </div>\n\n {/* Input Section */}\n {toolPart.args && Object.keys(toolPart.args).length > 0 && (\n <ToolCallSection\n title=\"Input\"\n icon={<Code className=\"size-3\" />}\n >\n <AICodeBlock\n code={formatValue(toolPart.args)}\n language=\"json\"\n showHeader={false}\n showCopyButton={true}\n size=\"sm\"\n />\n </ToolCallSection>\n )}\n\n {/* Output Section */}\n {toolPart.result !== undefined && (\n <ToolCallSection\n title=\"Output\"\n icon={<Code className=\"size-3\" />}\n >\n <AICodeBlock\n code={formatValue(toolPart.result)}\n language=\"json\"\n showHeader={false}\n showCopyButton={true}\n size=\"sm\"\n />\n </ToolCallSection>\n )}\n\n {/* Error Section */}\n {toolPart.error && (\n <div className=\"p-3 rounded-lg bg-destructive/20 border border-destructive/50\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"size-4 text-destructive mt-0.5 shrink-0\" />\n <p className=\"text-sm text-destructive\">{toolPart.error}</p>\n </div>\n </div>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\nAIToolCall.displayName = 'AIToolCall';\n\n// =============================================================================\n// Tool Call List Component\n// =============================================================================\n\nexport interface AIToolCallListProps {\n /** Array of tool calls */\n toolCalls: readonly ToolCallPart[];\n /** Whether to expand all by default */\n defaultAllOpen?: boolean;\n /** Tool-call IDs to expand initially in uncontrolled mode */\n defaultOpenToolCallIds?: readonly string[];\n /** Controlled expanded tool-call IDs */\n openToolCallIds?: readonly string[];\n /** Called with expanded IDs in current tool-call order */\n onOpenToolCallIdsChange?: (ids: readonly string[]) => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction normalizeOpenToolCallIds(\n ids: readonly string[],\n toolCalls: readonly ToolCallPart[],\n): string[] {\n const requested = new Set(ids);\n return toolCalls.flatMap((toolCall) =>\n requested.has(toolCall.toolCallId) ? [toolCall.toolCallId] : [],\n );\n}\n\nfunction areToolCallIdsEqual(\n left: readonly string[],\n right: readonly string[],\n): boolean {\n return (\n left.length === right.length &&\n left.every((id, index) => id === right[index])\n );\n}\n\nfunction AIToolCallList({\n toolCalls,\n defaultAllOpen = false,\n defaultOpenToolCallIds = [],\n openToolCallIds: controlledOpenToolCallIds,\n onOpenToolCallIdsChange,\n className,\n}: AIToolCallListProps) {\n const isControlled = controlledOpenToolCallIds !== undefined;\n const [uncontrolledOpenToolCallIds, setUncontrolledOpenToolCallIds] =\n React.useState<readonly string[]>(() =>\n normalizeOpenToolCallIds(\n defaultAllOpen\n ? toolCalls.map((toolCall) => toolCall.toolCallId)\n : defaultOpenToolCallIds,\n toolCalls,\n ),\n );\n const rawOpenToolCallIds = isControlled\n ? controlledOpenToolCallIds\n : uncontrolledOpenToolCallIds;\n const visibleOpenToolCallIds = normalizeOpenToolCallIds(\n rawOpenToolCallIds,\n toolCalls,\n );\n const openSet = new Set(visibleOpenToolCallIds);\n\n React.useLayoutEffect(() => {\n if (isControlled) return;\n setUncontrolledOpenToolCallIds((current) => {\n const normalized = normalizeOpenToolCallIds(current, toolCalls);\n return areToolCallIdsEqual(current, normalized) ? current : normalized;\n });\n }, [isControlled, toolCalls]);\n\n const setToolCallOpen = (toolCallId: string, open: boolean) => {\n const next = new Set(visibleOpenToolCallIds);\n if (open) next.add(toolCallId);\n else next.delete(toolCallId);\n const normalized = normalizeOpenToolCallIds([...next], toolCalls);\n if (!isControlled) {\n setUncontrolledOpenToolCallIds(normalized);\n }\n onOpenToolCallIdsChange?.([...normalized]);\n };\n\n return (\n <div data-slot=\"ai-tool-call-list\" className={cn('space-y-2', className)}>\n {toolCalls.map((toolCall) => (\n <AIToolCall\n key={toolCall.toolCallId}\n toolPart={toolCall}\n open={openSet.has(toolCall.toolCallId)}\n onOpenChange={(open) => setToolCallOpen(toolCall.toolCallId, open)}\n />\n ))}\n </div>\n );\n}\n\nAIToolCallList.displayName = 'AIToolCallList';\n\nexport {\n AIToolCall,\n AIToolCallHeader,\n AIToolCallList,\n StatusIcon,\n statusIconVariants,\n ToolCallSection,\n toolCallVariants,\n};\n"],"mappings":";;;;;;;;;AAoBA,IAAM,mBAAmB,IACvB,uDACA;CACE,UAAU,EACR,QAAQ;EACN,SAAS;EACT,SAAS;EACT,SAAS;EACT,OAAO;CACT,EACF;CACA,iBAAiB,EACf,QAAQ,UACV;AACF,CACF;AAEA,IAAM,qBAAqB,IAAI,UAAU;CACvC,UAAU,EACR,QAAQ;EACN,SAAS;EACT,SAAS;EACT,SAAS;EACT,OAAO;CACT,EACF;CACA,iBAAiB,EACf,QAAQ,UACV;AACF,CAAC;AAgCD,SAAS,WAAW,EAAE,QAAQ,aAA8B;CAC1D,MAAM,YAAY,GAAG,mBAAmB,EAAE,OAAO,CAAC,GAAG,SAAS;CAE9D,QAAQ,QAAR;EACE,KAAK,WACH,OAAO,oBAAC,OAAD,EAAO,WAAW,UAAY,CAAA;EACvC,KAAK,WACH,OACE,oBAAC,OAAD,EACE,WAAW,GACT,WACA,kDACF,EACD,CAAA;EAEL,KAAK,SACH,OACE,oBAAC,aAAD,EACE,WAAW,GAAG,WAAW,2CAA2C,EACrE,CAAA;EAEL,SACE,OAAO,oBAAC,QAAD,EAAQ,WAAW,UAAY,CAAA;CAC1C;AACF;AAEA,WAAW,cAAc;AAczB,SAAS,iBAAiB,EACxB,UACA,QACA,QACA,UACA,aACwB;CAQxB,OACE,qBAAC,UAAD;EACE,MAAK;EACL,SAAS;EACT,iBAAe;EACf,WAAW,GACT,qLACA,SACF;YAPF,CASE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,YAAD;IAAoB;IAAQ,eAAY;GAAQ,CAAA,GAChD,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,QAAD;KAAM,WAAU;eACb;IACG,CAAA,GACN,oBAAC,QAAD;KAAM,WAAU;eACb;MAvBT,SAAS;MACT,SAAS;MACT,SAAS;MACT,OAAO;KAoBE,EAAY;IACT,CAAA,CACH;KACF;MACJ,SACC,oBAAC,aAAD;GACE,WAAU;GACV,eAAY;EACb,CAAA,IAED,oBAAC,cAAD;GACE,WAAU;GACV,eAAY;EACb,CAAA,CAEG;;AAEZ;AAEA,iBAAiB,cAAc;AAa/B,SAAS,gBAAgB,EACvB,OACA,MACA,UACA,aACuB;CACvB,OACE,qBAAC,OAAD;EAAK,WAAW,GAAG,aAAa,SAAS;YAAzC,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,MACA,KACE;MACJ,QACE;;AAET;AAEA,gBAAgB,cAAc;AAmB9B,SAAS,YAAY,OAAwB;CAC3C,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AACtC;AAEA,SAAS,WAAW,EAClB,UACA,cAAc,OACd,MAAM,gBACN,cACA,aACkB;CAClB,MAAM,CAAC,QAAQ,aAAa,qBAAqB;EAC/C,OAAO;EACP,cAAc;EACd,UAAU;CACZ,CAAC;CAED,MAAM,SAAS,SAAS,UAAU;CAElC,MAAM,qBAAqB;EACzB,UAAU,CAAC,MAAM;CACnB;CAEA,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,iBAAiB,EAAE,OAAO,CAAC,GAAG,SAAS;YAFvD,CAIE,oBAAC,kBAAD;GACE,UAAU,SAAS;GACX;GACA;GACR,UAAU;EACX,CAAA,GAED,oBAAC,iBAAD,EAAA,UACG,UACC,oBAAC,OAAO,KAAR;GACE,SAAS;IAAE,QAAQ;IAAG,SAAS;GAAE;GACjC,SAAS;IAAE,QAAQ;IAAQ,SAAS;GAAE;GACtC,MAAM;IAAE,QAAQ;IAAG,SAAS;GAAE;GAC9B,YAAY,EAAE,UAAU,GAAI;GAC5B,WAAU;aAEV,qBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CAAyD,QAClD,SAAS,UACX;;KAGJ,SAAS,QAAQ,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KACpD,oBAAC,iBAAD;MACE,OAAM;MACN,MAAM,oBAAC,MAAD,EAAM,WAAU,SAAU,CAAA;gBAEhC,oBAAC,aAAD;OACE,MAAM,YAAY,SAAS,IAAI;OAC/B,UAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,MAAK;MACN,CAAA;KACc,CAAA;KAIlB,SAAS,WAAW,KAAA,KACnB,oBAAC,iBAAD;MACE,OAAM;MACN,MAAM,oBAAC,MAAD,EAAM,WAAU,SAAU,CAAA;gBAEhC,oBAAC,aAAD;OACE,MAAM,YAAY,SAAS,MAAM;OACjC,UAAS;OACT,YAAY;OACZ,gBAAgB;OAChB,MAAK;MACN,CAAA;KACc,CAAA;KAIlB,SAAS,SACR,oBAAC,OAAD;MAAK,WAAU;gBACb,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,oBAAC,aAAD,EAAa,WAAU,0CAA2C,CAAA,GAClE,oBAAC,KAAD;QAAG,WAAU;kBAA4B,SAAS;OAAS,CAAA,CACxD;;KACF,CAAA;IAEJ;;EACK,CAAA,EAEC,CAAA,CACd;;AAET;AAEA,WAAW,cAAc;AAqBzB,SAAS,yBACP,KACA,WACU;CACV,MAAM,YAAY,IAAI,IAAI,GAAG;CAC7B,OAAO,UAAU,SAAS,aACxB,UAAU,IAAI,SAAS,UAAU,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC,CAChE;AACF;AAEA,SAAS,oBACP,MACA,OACS;CACT,OACE,KAAK,WAAW,MAAM,UACtB,KAAK,OAAO,IAAI,UAAU,OAAO,MAAM,MAAM;AAEjD;AAEA,SAAS,eAAe,EACtB,WACA,iBAAiB,OACjB,yBAAyB,CAAC,GAC1B,iBAAiB,2BACjB,yBACA,aACsB;CACtB,MAAM,eAAe,8BAA8B,KAAA;CACnD,MAAM,CAAC,6BAA6B,kCAClC,QAAM,eACJ,yBACE,iBACI,UAAU,KAAK,aAAa,SAAS,UAAU,IAC/C,wBACJ,SACF,CACF;CAIF,MAAM,yBAAyB,yBAHJ,eACvB,4BACA,6BAGF,SACF;CACA,MAAM,UAAU,IAAI,IAAI,sBAAsB;CAE9C,QAAM,sBAAsB;EAC1B,IAAI,cAAc;EAClB,gCAAgC,YAAY;GAC1C,MAAM,aAAa,yBAAyB,SAAS,SAAS;GAC9D,OAAO,oBAAoB,SAAS,UAAU,IAAI,UAAU;EAC9D,CAAC;CACH,GAAG,CAAC,cAAc,SAAS,CAAC;CAE5B,MAAM,mBAAmB,YAAoB,SAAkB;EAC7D,MAAM,OAAO,IAAI,IAAI,sBAAsB;EAC3C,IAAI,MAAM,KAAK,IAAI,UAAU;OACxB,KAAK,OAAO,UAAU;EAC3B,MAAM,aAAa,yBAAyB,CAAC,GAAG,IAAI,GAAG,SAAS;EAChE,IAAI,CAAC,cACH,+BAA+B,UAAU;EAE3C,0BAA0B,CAAC,GAAG,UAAU,CAAC;CAC3C;CAEA,OACE,oBAAC,OAAD;EAAK,aAAU;EAAoB,WAAW,GAAG,aAAa,SAAS;YACpE,UAAU,KAAK,aACd,oBAAC,YAAD;GAEE,UAAU;GACV,MAAM,QAAQ,IAAI,SAAS,UAAU;GACrC,eAAe,SAAS,gBAAgB,SAAS,YAAY,IAAI;EAClE,GAJM,SAAS,UAIf,CACF;CACE,CAAA;AAET;AAEA,eAAe,cAAc"}
1
+ {"version":3,"file":"index184.js","names":[],"sources":["../src/components/organisms/ai/AIToolCall/AIToolCall.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n AlertCircle,\n Check,\n ChevronDown,\n ChevronRight,\n Clock,\n Code,\n Wrench,\n} from 'lucide-react';\nimport { AnimatePresence, motion } from 'motion/react';\nimport * as React from 'react';\nimport { AICodeBlock } from '@/components/organisms/ai/AICodeBlock';\nimport { useControllableState } from '@/hooks/use-controllable-state';\nimport { cn } from '@/lib/utils';\n\n// =============================================================================\n// Variants\n// =============================================================================\n\nconst toolCallVariants = cva(\n 'overflow-hidden rounded-xl border transition-colors',\n {\n variants: {\n status: {\n pending: 'border-border bg-muted',\n running: 'border-primary bg-primary/10',\n success: 'border-success/50 bg-success/10',\n error: 'border-destructive/50 bg-destructive/10',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n },\n);\n\nconst statusIconVariants = cva('size-4', {\n variants: {\n status: {\n pending: 'text-muted-foreground',\n running: 'text-primary animate-spin motion-reduce:animate-none',\n success: 'text-success',\n error: 'text-destructive',\n },\n },\n defaultVariants: {\n status: 'pending',\n },\n});\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error';\n\nexport interface ToolCallPart {\n /** Unique tool call ID */\n toolCallId: string;\n /** Tool name */\n toolName: string;\n /** Tool input/arguments */\n args?: Record<string, unknown>;\n /** Tool output/result */\n result?: unknown;\n /** Tool status */\n status?: ToolCallStatus;\n /** Error message if status is error */\n error?: string;\n}\n\n// =============================================================================\n// Status Icon Component\n// =============================================================================\n\ninterface StatusIconProps {\n status: ToolCallStatus;\n className?: string;\n}\n\nfunction StatusIcon({ status, className }: StatusIconProps) {\n const iconClass = cn(statusIconVariants({ status }), className);\n\n switch (status) {\n case 'running':\n return <Clock className={iconClass} />;\n case 'success':\n return (\n <Check\n className={cn(\n iconClass,\n 'delight-check-bounce motion-reduce:!animate-none',\n )}\n />\n );\n case 'error':\n return (\n <AlertCircle\n className={cn(iconClass, 'delight-shake motion-reduce:!animate-none')}\n />\n );\n default:\n return <Wrench className={iconClass} />;\n }\n}\n\nStatusIcon.displayName = 'StatusIcon';\n\n// =============================================================================\n// Tool Call Header\n// =============================================================================\n\ninterface AIToolCallHeaderProps {\n toolName: string;\n status: ToolCallStatus;\n isOpen: boolean;\n onToggle: () => void;\n className?: string;\n}\n\nfunction AIToolCallHeader({\n toolName,\n status,\n isOpen,\n onToggle,\n className,\n}: AIToolCallHeaderProps) {\n const statusLabel = {\n pending: 'Pending',\n running: 'Running...',\n success: 'Completed',\n error: 'Failed',\n };\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n aria-expanded={isOpen}\n className={cn(\n 'flex items-center justify-between w-full px-4 py-3 transition-colors hover:bg-foreground/5 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n className,\n )}\n >\n <div className=\"flex items-center gap-3\">\n <StatusIcon status={status} aria-hidden=\"true\" />\n <div className=\"flex flex-col items-start\">\n <span className=\"text-sm font-medium text-foreground\">\n {toolName}\n </span>\n <span className=\"text-xs text-secondary-foreground\">\n {statusLabel[status]}\n </span>\n </div>\n </div>\n {isOpen ? (\n <ChevronDown\n className=\"size-4 text-muted-foreground\"\n aria-hidden=\"true\"\n />\n ) : (\n <ChevronRight\n className=\"size-4 text-muted-foreground rtl:rotate-180\"\n aria-hidden=\"true\"\n />\n )}\n </button>\n );\n}\n\nAIToolCallHeader.displayName = 'AIToolCallHeader';\n\n// =============================================================================\n// Tool Call Section\n// =============================================================================\n\ninterface ToolCallSectionProps {\n title: string;\n icon: React.ReactNode;\n children: React.ReactNode;\n className?: string;\n}\n\nfunction ToolCallSection({\n title,\n icon,\n children,\n className,\n}: ToolCallSectionProps) {\n return (\n <div className={cn('space-y-2', className)}>\n <div className=\"flex items-center gap-2 text-xs font-medium text-secondary-foreground uppercase tracking-wide\">\n {icon}\n {title}\n </div>\n {children}\n </div>\n );\n}\n\nToolCallSection.displayName = 'ToolCallSection';\n\n// =============================================================================\n// Main AIToolCall Component\n// =============================================================================\n\nexport interface AIToolCallProps extends VariantProps<typeof toolCallVariants> {\n /** Tool call data */\n toolPart: ToolCallPart;\n /** Whether to expand by default */\n defaultOpen?: boolean;\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === 'string') return value;\n return JSON.stringify(value, null, 2);\n}\n\nfunction AIToolCall({\n toolPart,\n defaultOpen = false,\n open: controlledOpen,\n onOpenChange,\n className,\n}: AIToolCallProps) {\n const [isOpen, setIsOpen] = useControllableState({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const status = toolPart.status || 'pending';\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <div\n data-slot=\"ai-tool-call\"\n className={cn(toolCallVariants({ status }), className)}\n >\n <AIToolCallHeader\n toolName={toolPart.toolName}\n status={status}\n isOpen={isOpen}\n onToggle={handleToggle}\n />\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n initial={{ height: 0, opacity: 0 }}\n animate={{ height: 'auto', opacity: 1 }}\n exit={{ height: 0, opacity: 0 }}\n transition={{ duration: 0.2 }}\n className=\"overflow-hidden\"\n >\n <div className=\"px-4 pb-4 space-y-4\">\n {/* Tool Call ID */}\n <div className=\"text-xs text-muted-foreground font-mono\">\n ID: {toolPart.toolCallId}\n </div>\n\n {/* Input Section */}\n {toolPart.args && Object.keys(toolPart.args).length > 0 && (\n <ToolCallSection\n title=\"Input\"\n icon={<Code className=\"size-3\" />}\n >\n <AICodeBlock\n code={formatValue(toolPart.args)}\n language=\"json\"\n showHeader={false}\n showCopyButton={true}\n size=\"sm\"\n />\n </ToolCallSection>\n )}\n\n {/* Output Section */}\n {toolPart.result !== undefined && (\n <ToolCallSection\n title=\"Output\"\n icon={<Code className=\"size-3\" />}\n >\n <AICodeBlock\n code={formatValue(toolPart.result)}\n language=\"json\"\n showHeader={false}\n showCopyButton={true}\n size=\"sm\"\n />\n </ToolCallSection>\n )}\n\n {/* Error Section */}\n {toolPart.error && (\n <div className=\"p-3 rounded-lg bg-destructive/20 border border-destructive/50\">\n <div className=\"flex items-start gap-2\">\n <AlertCircle className=\"size-4 text-destructive mt-0.5 shrink-0\" />\n <p className=\"text-sm text-destructive\">{toolPart.error}</p>\n </div>\n </div>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n );\n}\n\nAIToolCall.displayName = 'AIToolCall';\n\n// =============================================================================\n// Tool Call List Component\n// =============================================================================\n\nexport interface AIToolCallListProps {\n /** Array of tool calls */\n toolCalls: readonly ToolCallPart[];\n /** Whether to expand all by default */\n defaultAllOpen?: boolean;\n /** Tool-call IDs to expand initially in uncontrolled mode */\n defaultOpenToolCallIds?: readonly string[];\n /** Controlled expanded tool-call IDs */\n openToolCallIds?: readonly string[];\n /** Called with expanded IDs in current tool-call order */\n onOpenToolCallIdsChange?: (ids: readonly string[]) => void;\n /** Additional class name */\n className?: string;\n}\n\nfunction normalizeOpenToolCallIds(\n ids: readonly string[],\n toolCalls: readonly ToolCallPart[],\n): string[] {\n const requested = new Set(ids);\n return toolCalls.flatMap((toolCall) =>\n requested.has(toolCall.toolCallId) ? [toolCall.toolCallId] : [],\n );\n}\n\nfunction areToolCallIdsEqual(\n left: readonly string[],\n right: readonly string[],\n): boolean {\n return (\n left.length === right.length &&\n left.every((id, index) => id === right[index])\n );\n}\n\nfunction AIToolCallList({\n toolCalls,\n defaultAllOpen = false,\n defaultOpenToolCallIds = [],\n openToolCallIds: controlledOpenToolCallIds,\n onOpenToolCallIdsChange,\n className,\n}: AIToolCallListProps) {\n const isControlled = controlledOpenToolCallIds !== undefined;\n const [uncontrolledOpenToolCallIds, setUncontrolledOpenToolCallIds] =\n React.useState<readonly string[]>(() =>\n normalizeOpenToolCallIds(\n defaultAllOpen\n ? toolCalls.map((toolCall) => toolCall.toolCallId)\n : defaultOpenToolCallIds,\n toolCalls,\n ),\n );\n const rawOpenToolCallIds = isControlled\n ? controlledOpenToolCallIds\n : uncontrolledOpenToolCallIds;\n const visibleOpenToolCallIds = normalizeOpenToolCallIds(\n rawOpenToolCallIds,\n toolCalls,\n );\n const openSet = new Set(visibleOpenToolCallIds);\n\n React.useLayoutEffect(() => {\n if (isControlled) return;\n setUncontrolledOpenToolCallIds((current) => {\n const normalized = normalizeOpenToolCallIds(current, toolCalls);\n return areToolCallIdsEqual(current, normalized) ? current : normalized;\n });\n }, [isControlled, toolCalls]);\n\n const setToolCallOpen = (toolCallId: string, open: boolean) => {\n const next = new Set(visibleOpenToolCallIds);\n if (open) next.add(toolCallId);\n else next.delete(toolCallId);\n const normalized = normalizeOpenToolCallIds([...next], toolCalls);\n if (!isControlled) {\n setUncontrolledOpenToolCallIds(normalized);\n }\n onOpenToolCallIdsChange?.([...normalized]);\n };\n\n return (\n <div data-slot=\"ai-tool-call-list\" className={cn('space-y-2', className)}>\n {toolCalls.map((toolCall) => (\n <AIToolCall\n key={toolCall.toolCallId}\n toolPart={toolCall}\n open={openSet.has(toolCall.toolCallId)}\n onOpenChange={(open) => setToolCallOpen(toolCall.toolCallId, open)}\n />\n ))}\n </div>\n );\n}\n\nAIToolCallList.displayName = 'AIToolCallList';\n\nexport {\n AIToolCall,\n AIToolCallHeader,\n AIToolCallList,\n StatusIcon,\n statusIconVariants,\n ToolCallSection,\n toolCallVariants,\n};\n"],"mappings":"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"}