@humain/ui 2.2.3 → 2.2.5

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 (116) hide show
  1. package/dist/components/templates/AppShell/AppShell.d.ts +1 -1
  2. package/dist/components/templates/AppSidebar/AppSidebar.constants.d.ts +1 -1
  3. package/dist/index11.js +4 -4
  4. package/dist/index11.js.map +1 -1
  5. package/dist/index110.js +4 -4
  6. package/dist/index110.js.map +1 -1
  7. package/dist/index134.js +4 -4
  8. package/dist/index134.js.map +1 -1
  9. package/dist/index135.js +2 -2
  10. package/dist/index135.js.map +1 -1
  11. package/dist/index151.js +3 -3
  12. package/dist/index151.js.map +1 -1
  13. package/dist/index152.js +2 -2
  14. package/dist/index152.js.map +1 -1
  15. package/dist/index155.js +1 -1
  16. package/dist/index155.js.map +1 -1
  17. package/dist/index173.js +1 -1
  18. package/dist/index173.js.map +1 -1
  19. package/dist/index184.js +1 -1
  20. package/dist/index184.js.map +1 -1
  21. package/dist/index188.js +4 -4
  22. package/dist/index188.js.map +1 -1
  23. package/dist/index201.js +1 -1
  24. package/dist/index201.js.map +1 -1
  25. package/dist/index217.js +1 -1
  26. package/dist/index217.js.map +1 -1
  27. package/dist/index223.js.map +1 -1
  28. package/dist/index225.js +3 -3
  29. package/dist/index225.js.map +1 -1
  30. package/dist/index228.js +1 -1
  31. package/dist/index228.js.map +1 -1
  32. package/dist/index24.js +1 -1
  33. package/dist/index24.js.map +1 -1
  34. package/dist/index25.js +4 -4
  35. package/dist/index25.js.map +1 -1
  36. package/dist/index39.js +2 -2
  37. package/dist/index39.js.map +1 -1
  38. package/dist/index5.js +8 -8
  39. package/dist/index5.js.map +1 -1
  40. package/dist/styles.css +1 -1
  41. package/dist/wc/AIScrollButton.js +8 -8
  42. package/dist/wc/AIScrollButton.js.map +1 -1
  43. package/dist/wc/AISource.js +46 -46
  44. package/dist/wc/AISource.js.map +1 -1
  45. package/dist/wc/AIToolCall.js +9 -9
  46. package/dist/wc/AIToolCall.js.map +1 -1
  47. package/dist/wc/AppSidebar.constants.js +1 -1
  48. package/dist/wc/AppSidebar.constants.js.map +1 -1
  49. package/dist/wc/Badge.js +9 -9
  50. package/dist/wc/Badge.js.map +1 -1
  51. package/dist/wc/Checkbox.js +22 -22
  52. package/dist/wc/Checkbox.js.map +1 -1
  53. package/dist/wc/Pagination.js +4 -4
  54. package/dist/wc/Pagination.js.map +1 -1
  55. package/dist/wc/Switch.js +23 -23
  56. package/dist/wc/Switch.js.map +1 -1
  57. package/dist/wc/Tabs.js +110 -110
  58. package/dist/wc/Tabs.js.map +1 -1
  59. package/dist/wc/Tooltip.js +28 -28
  60. package/dist/wc/Tooltip.js.map +1 -1
  61. package/dist/wc/button.js +33 -33
  62. package/dist/wc/button.js.map +1 -1
  63. package/dist/wc/hf-alert.js +10 -10
  64. package/dist/wc/hf-alert.js.map +1 -1
  65. package/dist/wc/hf-app-shell.js.map +1 -1
  66. package/dist/wc/hf-app-sidebar.js +3 -3
  67. package/dist/wc/hf-app-sidebar.js.map +1 -1
  68. package/dist/wc/hf-nav-account-menu.js +8 -8
  69. package/dist/wc/hf-nav-account-menu.js.map +1 -1
  70. package/dist/wc/hf-steps.js +8 -8
  71. package/dist/wc/hf-steps.js.map +1 -1
  72. package/dist/wc/markdown-renderer.js +5 -5
  73. package/dist/wc/markdown-renderer.js.map +1 -1
  74. package/dist/wc/selection-group.js +6 -6
  75. package/dist/wc/selection-group.js.map +1 -1
  76. package/dist/wc-react/AIScrollButton.js +18 -18
  77. package/dist/wc-react/AIScrollButton.js.map +1 -1
  78. package/dist/wc-react/AISource.js +8 -8
  79. package/dist/wc-react/AISource.js.map +1 -1
  80. package/dist/wc-react/AIToolCall.js +42 -42
  81. package/dist/wc-react/AIToolCall.js.map +1 -1
  82. package/dist/wc-react/AppSidebar.constants.js +1 -1
  83. package/dist/wc-react/AppSidebar.constants.js.map +1 -1
  84. package/dist/wc-react/Badge.js +16 -16
  85. package/dist/wc-react/Badge.js.map +1 -1
  86. package/dist/wc-react/Checkbox.js +14 -14
  87. package/dist/wc-react/Checkbox.js.map +1 -1
  88. package/dist/wc-react/Pagination.js +130 -130
  89. package/dist/wc-react/Pagination.js.map +1 -1
  90. package/dist/wc-react/Switch.js +2 -2
  91. package/dist/wc-react/Switch.js.map +1 -1
  92. package/dist/wc-react/Tabs.js +38 -38
  93. package/dist/wc-react/Tabs.js.map +1 -1
  94. package/dist/wc-react/Tooltip.js +22 -22
  95. package/dist/wc-react/Tooltip.js.map +1 -1
  96. package/dist/wc-react/button.js +8 -8
  97. package/dist/wc-react/button.js.map +1 -1
  98. package/dist/wc-react/hf-alert.js +28 -28
  99. package/dist/wc-react/hf-alert.js.map +1 -1
  100. package/dist/wc-react/hf-app-shell.js.map +1 -1
  101. package/dist/wc-react/hf-app-sidebar.js +3 -3
  102. package/dist/wc-react/hf-app-sidebar.js.map +1 -1
  103. package/dist/wc-react/hf-nav-account-menu.js +7 -7
  104. package/dist/wc-react/hf-nav-account-menu.js.map +1 -1
  105. package/dist/wc-react/hf-steps.js +16 -16
  106. package/dist/wc-react/hf-steps.js.map +1 -1
  107. package/dist/wc-react/markdown-renderer.js +5 -5
  108. package/dist/wc-react/markdown-renderer.js.map +1 -1
  109. package/dist/wc-react/selection-group.js +1 -1
  110. package/dist/wc-react/selection-group.js.map +1 -1
  111. package/package.json +1 -1
  112. package/skills/humain-ui/SKILL.md +1 -1
  113. package/skills/humain-ui-wc/SKILL.md +1 -1
  114. package/skills/humain-ui-wc.skill +0 -0
  115. package/skills/humain-ui.skill +0 -0
  116. package/src/tokens.css +10 -2
@@ -1 +1 @@
1
- {"version":3,"file":"index135.js","names":[],"sources":["../src/components/ui/tabs.tsx"],"sourcesContent":["'use client';\n\nimport { Tabs as TabsPrimitive } from '@base-ui/react/tabs';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// TabsList Variants\n// ============================================\n\nconst tabsListVariants = cva(\n 'group/tabs-list inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col',\n {\n variants: {\n variant: {\n // Primary variants\n boxed:\n 'bg-muted rounded-lg p-[3px] overflow-clip group-data-[orientation=vertical]/tabs:gap-1',\n pill: 'bg-muted rounded-full p-[3px] overflow-clip group-data-[orientation=vertical]/tabs:gap-1',\n bordered:\n 'gap-3 bg-transparent relative pb-0 w-full rounded-none overflow-x-auto after:absolute after:bottom-0 after:inset-x-0 after:h-px after:bg-border after:z-0 group-data-[orientation=vertical]/tabs:overflow-x-visible group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:inset-x-auto group-data-[orientation=vertical]/tabs:after:left-0 group-data-[orientation=vertical]/tabs:after:w-px group-data-[orientation=vertical]/tabs:after:h-auto group-data-[orientation=vertical]/tabs:w-fit',\n lifted:\n 'gap-0 bg-transparent relative w-full rounded-none overflow-x-auto after:absolute after:bottom-0 after:inset-x-0 after:h-px after:bg-border after:z-0 group-data-[orientation=vertical]/tabs:overflow-x-visible',\n },\n size: {\n sm: '',\n md: '',\n lg: '',\n },\n align: {\n start: '',\n center: '',\n end: '',\n stretch: '',\n },\n borderPosition: {\n bottom: '',\n top: '',\n },\n },\n compoundVariants: [\n // Size applies container height for boxed/pill variants\n {\n variant: 'boxed',\n size: 'sm',\n className: 'group-data-[orientation=horizontal]/tabs:h-[38px]',\n },\n {\n variant: 'boxed',\n size: 'md',\n className: 'group-data-[orientation=horizontal]/tabs:h-[42px]',\n },\n {\n variant: 'boxed',\n size: 'lg',\n className: 'group-data-[orientation=horizontal]/tabs:h-[46px]',\n },\n {\n variant: 'pill',\n size: 'sm',\n className: 'group-data-[orientation=horizontal]/tabs:h-[38px]',\n },\n {\n variant: 'pill',\n size: 'md',\n className: 'group-data-[orientation=horizontal]/tabs:h-[42px]',\n },\n {\n variant: 'pill',\n size: 'lg',\n className: 'group-data-[orientation=horizontal]/tabs:h-[46px]',\n },\n // Alignment for bordered variant\n { variant: 'bordered', align: 'start', className: 'justify-start' },\n { variant: 'bordered', align: 'center', className: 'justify-center' },\n { variant: 'bordered', align: 'end', className: 'justify-end' },\n { variant: 'bordered', align: 'stretch', className: 'justify-start' },\n // Alignment for lifted variant\n { variant: 'lifted', align: 'start', className: 'justify-start' },\n { variant: 'lifted', align: 'center', className: 'justify-center' },\n { variant: 'lifted', align: 'end', className: 'justify-end' },\n { variant: 'lifted', align: 'stretch', className: 'justify-start' },\n // Border position top for bordered/lifted\n {\n variant: 'bordered',\n borderPosition: 'top',\n className: 'after:bottom-auto after:top-0',\n },\n {\n variant: 'lifted',\n borderPosition: 'top',\n className: 'after:bottom-auto after:top-0',\n },\n ],\n defaultVariants: {\n variant: 'boxed',\n size: 'md',\n align: 'start',\n borderPosition: 'bottom',\n },\n },\n);\n\ntype TabsListVariantProps = VariantProps<typeof tabsListVariants>;\n\ninterface TabsListProps extends TabsPrimitive.List.Props, TabsListVariantProps {\n /** Enable gradient styling on active tab */\n gradient?: boolean;\n}\n\n// ============================================\n// TabsTrigger Variants\n// ============================================\n\nconst tabsTriggerVariants = cva([\n // Base\n 'relative inline-flex items-center justify-center whitespace-nowrap',\n 'gap-1.5 border border-transparent font-medium',\n 'transition-[color,background-color,border-color,box-shadow,opacity] duration-200 ease-smooth motion-reduce:transition-none',\n 'group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start',\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:outline-1',\n 'disabled:pointer-events-none disabled:text-muted-foreground disabled:opacity-70',\n '[&_svg]:pointer-events-none [&_svg]:shrink-0',\n 'text-foreground',\n\n // Size: sm\n 'group-data-[size=sm]/tabs-list:h-9 group-data-[size=sm]/tabs-list:px-2 group-data-[size=sm]/tabs-list:py-1 group-data-[size=sm]/tabs-list:text-sm',\n \"group-data-[size=sm]/tabs-list:[&_svg:not([class*='size-'])]:size-4\",\n // Size: md\n 'group-data-[size=md]/tabs-list:h-10 group-data-[size=md]/tabs-list:px-2.5 group-data-[size=md]/tabs-list:py-[5px] group-data-[size=md]/tabs-list:text-sm',\n \"group-data-[size=md]/tabs-list:[&_svg:not([class*='size-'])]:size-4\",\n // Size: lg\n 'group-data-[size=lg]/tabs-list:h-11 group-data-[size=lg]/tabs-list:px-3 group-data-[size=lg]/tabs-list:py-2 group-data-[size=lg]/tabs-list:text-base',\n \"group-data-[size=lg]/tabs-list:[&_svg:not([class*='size-'])]:size-5\",\n\n // Boxed variant\n 'group-data-[variant=boxed]/tabs-list:rounded-[10px] group-data-[variant=boxed]/tabs-list:flex-1 group-data-[variant=boxed]/tabs-list:h-[calc(100%-1px)]',\n 'group-data-[variant=boxed]/tabs-list:data-active:bg-secondary group-data-[variant=boxed]/tabs-list:data-active:shadow-sm group-data-[variant=boxed]/tabs-list:data-active:text-foreground',\n 'group-data-[variant=boxed]/tabs-list:hover:bg-primary-foreground dark:group-data-[variant=boxed]/tabs-list:hover:bg-input/30',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=boxed]/tabs-list:flex-initial group-data-[orientation=vertical]/tabs:group-data-[variant=boxed]/tabs-list:justify-center',\n\n // Pill variant\n 'group-data-[variant=pill]/tabs-list:rounded-full group-data-[variant=pill]/tabs-list:flex-1 group-data-[variant=pill]/tabs-list:h-[calc(100%-1px)]',\n 'group-data-[variant=pill]/tabs-list:data-active:bg-background group-data-[variant=pill]/tabs-list:data-active:shadow-sm group-data-[variant=pill]/tabs-list:data-active:text-foreground',\n 'group-data-[variant=pill]/tabs-list:hover:bg-primary-foreground dark:group-data-[variant=pill]/tabs-list:hover:bg-input/30',\n 'dark:group-data-[variant=pill]/tabs-list:data-active:bg-accent dark:group-data-[variant=pill]/tabs-list:data-active:text-accent-foreground dark:group-data-[variant=pill]/tabs-list:data-active:border-input',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=pill]/tabs-list:flex-initial group-data-[orientation=vertical]/tabs:group-data-[variant=pill]/tabs-list:justify-center',\n\n // Bordered variant\n 'group-data-[variant=bordered]/tabs-list:bg-transparent group-data-[variant=bordered]/tabs-list:rounded-none',\n 'group-data-[variant=bordered]/tabs-list:pb-2.5 group-data-[variant=bordered]/tabs-list:flex-initial',\n 'group-data-[variant=bordered]/tabs-list:data-active:bg-background group-data-[variant=bordered]/tabs-list:data-active:text-primary group-data-[variant=bordered]/tabs-list:data-active:shadow-none',\n 'group-data-[variant=bordered]/tabs-list:group-data-[align=stretch]/tabs-list:flex-1',\n 'group-data-[variant=bordered]/tabs-list:after:bg-primary/90 group-data-[variant=bordered]/tabs-list:after:absolute group-data-[variant=bordered]/tabs-list:after:bottom-0 group-data-[variant=bordered]/tabs-list:after:inset-x-0 group-data-[variant=bordered]/tabs-list:after:h-0.5 group-data-[variant=bordered]/tabs-list:after:z-10 group-data-[variant=bordered]/tabs-list:after:opacity-0 group-data-[variant=bordered]/tabs-list:after:transition-opacity group-data-[variant=bordered]/tabs-list:after:duration-200 group-data-[variant=bordered]/tabs-list:after:ease-out',\n 'group-data-[variant=bordered]/tabs-list:data-active:after:opacity-100',\n 'group-data-[variant=bordered]/tabs-list:hover:after:opacity-100 group-data-[variant=bordered]/tabs-list:hover:after:bg-primary/20',\n 'group-data-[variant=bordered]/tabs-list:data-active:hover:after:bg-primary/90',\n 'group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:after:bottom-auto group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:after:top-0',\n 'group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:pb-0 group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:pt-2.5',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:inset-x-auto group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:inset-y-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:left-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:w-0.5 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:h-auto',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:pb-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:pl-2.5',\n\n // Lifted variant — browser-tab style with curved corner connectors\n 'group-data-[variant=lifted]/tabs-list:flex-initial',\n 'group-data-[variant=lifted]/tabs-list:rounded-t-md',\n 'group-data-[variant=lifted]/tabs-list:border-b group-data-[variant=lifted]/tabs-list:border-b-border',\n 'group-data-[variant=lifted]/tabs-list:text-foreground',\n 'group-data-[variant=lifted]/tabs-list:data-active:bg-background',\n 'group-data-[variant=lifted]/tabs-list:data-active:text-primary',\n 'group-data-[variant=lifted]/tabs-list:data-active:border group-data-[variant=lifted]/tabs-list:data-active:border-border',\n 'group-data-[variant=lifted]/tabs-list:data-active:border-b-0 group-data-[variant=lifted]/tabs-list:data-active:border-b-background',\n 'group-data-[variant=lifted]/tabs-list:data-active:rounded-t-[10px]',\n 'group-data-[variant=lifted]/tabs-list:data-active:mb-[-1px] group-data-[variant=lifted]/tabs-list:data-active:z-10',\n 'group-data-[variant=lifted]/tabs-list:data-active:before:absolute group-data-[variant=lifted]/tabs-list:data-active:before:bottom-0 group-data-[variant=lifted]/tabs-list:data-active:before:left-[-10px] group-data-[variant=lifted]/tabs-list:data-active:before:size-[10px] group-data-[variant=lifted]/tabs-list:data-active:before:rounded-br-[10px] group-data-[variant=lifted]/tabs-list:data-active:before:border-b group-data-[variant=lifted]/tabs-list:data-active:before:border-r group-data-[variant=lifted]/tabs-list:data-active:before:border-border group-data-[variant=lifted]/tabs-list:data-active:before:bg-background group-data-[variant=lifted]/tabs-list:data-active:before:shadow-[2px_2px_0_0_var(--background)]',\n 'group-data-[variant=lifted]/tabs-list:data-active:after:absolute group-data-[variant=lifted]/tabs-list:data-active:after:bottom-0 group-data-[variant=lifted]/tabs-list:data-active:after:right-[-10px] group-data-[variant=lifted]/tabs-list:data-active:after:size-[10px] group-data-[variant=lifted]/tabs-list:data-active:after:rounded-bl-[10px] group-data-[variant=lifted]/tabs-list:data-active:after:border-b group-data-[variant=lifted]/tabs-list:data-active:after:border-l group-data-[variant=lifted]/tabs-list:data-active:after:border-border group-data-[variant=lifted]/tabs-list:data-active:after:bg-background group-data-[variant=lifted]/tabs-list:data-active:after:shadow-[-2px_2px_0_0_var(--background)]',\n 'group-data-[variant=lifted]/tabs-list:group-data-[align=stretch]/tabs-list:flex-1',\n\n // Gradient support (uses brand tokens)\n 'group-data-[variant=boxed]/tabs-list:group-data-[gradient]/tabs-list:data-active:bg-[linear-gradient(100deg,var(--air-100)/0.1,var(--primary)/0.1)] group-data-[variant=boxed]/tabs-list:group-data-[gradient]/tabs-list:data-active:text-primary',\n 'group-data-[variant=pill]/tabs-list:group-data-[gradient]/tabs-list:data-active:bg-[linear-gradient(98deg,var(--air-100),var(--primary))] group-data-[variant=pill]/tabs-list:group-data-[gradient]/tabs-list:data-active:text-primary-foreground',\n\n // Hide pseudo-element underline when TabsIndicator is used\n 'group-has-[[data-slot=tabs-indicator]]/tabs-list:after:hidden',\n]);\n\n// ============================================\n// Components\n// ============================================\n\nconst Tabs = React.forwardRef<HTMLDivElement, TabsPrimitive.Root.Props>(\n ({ className, orientation = 'horizontal', ...props }, ref) => (\n <TabsPrimitive.Root\n ref={ref}\n data-slot=\"tabs\"\n data-orientation={orientation}\n className={cn(\n 'gap-2 group/tabs flex data-[orientation=horizontal]:flex-col',\n className,\n )}\n {...props}\n />\n ),\n);\n\nconst TabsList = React.forwardRef<HTMLDivElement, TabsListProps>(\n (\n {\n className,\n variant = 'boxed',\n size = 'md',\n align = 'start',\n borderPosition = 'bottom',\n gradient,\n ...props\n },\n ref,\n ) => {\n return (\n <TabsPrimitive.List\n ref={ref}\n data-slot=\"tabs-list\"\n data-variant={variant}\n data-align={align}\n data-size={size}\n data-border-position={borderPosition}\n {...(gradient ? { 'data-gradient': '' } : {})}\n className={cn(\n tabsListVariants({ variant, size, align, borderPosition }),\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nconst TabsTrigger = React.forwardRef<\n HTMLButtonElement,\n TabsPrimitive.Tab.Props\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Tab\n ref={ref}\n data-slot=\"tabs-trigger\"\n className={cn(tabsTriggerVariants(), className)}\n {...props}\n />\n));\n\nconst TabsContent = React.forwardRef<HTMLDivElement, TabsPrimitive.Panel.Props>(\n ({ className, ...props }, ref) => (\n <TabsPrimitive.Panel\n ref={ref}\n data-slot=\"tabs-content\"\n className={cn(\n 'flex-1 focus-visible:outline-none delight-fade-in',\n className,\n )}\n {...props}\n />\n ),\n);\n\n/**\n * Animated indicator that slides to the active tab position.\n * Use with `variant=\"bordered\"` on TabsList.\n *\n * @example\n * ```tsx\n * <TabsList variant=\"bordered\">\n * <TabsTrigger value=\"tab1\">Tab 1</TabsTrigger>\n * <TabsTrigger value=\"tab2\">Tab 2</TabsTrigger>\n * <TabsIndicator />\n * </TabsList>\n * ```\n */\nconst TabsIndicator = React.forwardRef<\n HTMLSpanElement,\n TabsPrimitive.Indicator.Props\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Indicator\n ref={ref}\n data-slot=\"tabs-indicator\"\n className={cn(\n 'absolute bottom-0 left-0 h-0.5 z-10 bg-primary/90',\n 'w-[var(--active-tab-width)]',\n // Width must be layout-animated (Base UI constraint); transform is GPU-composited\n 'transition-[transform,width] duration-300 ease-smooth motion-reduce:transition-none',\n className,\n )}\n style={{\n transform: 'translateX(var(--active-tab-left, 0px))',\n }}\n {...props}\n />\n));\n\nTabs.displayName = 'Tabs';\nTabsList.displayName = 'TabsList';\nTabsTrigger.displayName = 'TabsTrigger';\nTabsContent.displayName = 'TabsContent';\nTabsIndicator.displayName = 'TabsIndicator';\n\nexport {\n Tabs,\n TabsContent,\n TabsIndicator,\n TabsList,\n TabsTrigger,\n tabsListVariants,\n tabsTriggerVariants,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index135.js","names":[],"sources":["../src/components/ui/tabs.tsx"],"sourcesContent":["'use client';\n\nimport { Tabs as TabsPrimitive } from '@base-ui/react/tabs';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\n\n// ============================================\n// TabsList Variants\n// ============================================\n\nconst tabsListVariants = cva(\n 'group/tabs-list inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col',\n {\n variants: {\n variant: {\n // Primary variants\n boxed:\n 'bg-muted rounded-lg p-[3px] overflow-clip group-data-[orientation=vertical]/tabs:gap-1',\n pill: 'bg-muted rounded-full p-[3px] overflow-clip group-data-[orientation=vertical]/tabs:gap-1',\n bordered:\n 'gap-3 bg-transparent relative pb-0 w-full rounded-none overflow-x-auto after:absolute after:bottom-0 after:inset-x-0 after:h-px after:bg-border after:z-0 group-data-[orientation=vertical]/tabs:overflow-x-visible group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:inset-x-auto group-data-[orientation=vertical]/tabs:after:left-0 group-data-[orientation=vertical]/tabs:after:w-px group-data-[orientation=vertical]/tabs:after:h-auto group-data-[orientation=vertical]/tabs:w-fit',\n lifted:\n 'gap-0 bg-transparent relative w-full rounded-none overflow-x-auto after:absolute after:bottom-0 after:inset-x-0 after:h-px after:bg-border after:z-0 group-data-[orientation=vertical]/tabs:overflow-x-visible',\n },\n size: {\n sm: '',\n md: '',\n lg: '',\n },\n align: {\n start: '',\n center: '',\n end: '',\n stretch: '',\n },\n borderPosition: {\n bottom: '',\n top: '',\n },\n },\n compoundVariants: [\n // Size applies container height for boxed/pill variants\n {\n variant: 'boxed',\n size: 'sm',\n className: 'group-data-[orientation=horizontal]/tabs:h-[38px]',\n },\n {\n variant: 'boxed',\n size: 'md',\n className: 'group-data-[orientation=horizontal]/tabs:h-[42px]',\n },\n {\n variant: 'boxed',\n size: 'lg',\n className: 'group-data-[orientation=horizontal]/tabs:h-[46px]',\n },\n {\n variant: 'pill',\n size: 'sm',\n className: 'group-data-[orientation=horizontal]/tabs:h-[38px]',\n },\n {\n variant: 'pill',\n size: 'md',\n className: 'group-data-[orientation=horizontal]/tabs:h-[42px]',\n },\n {\n variant: 'pill',\n size: 'lg',\n className: 'group-data-[orientation=horizontal]/tabs:h-[46px]',\n },\n // Alignment for bordered variant\n { variant: 'bordered', align: 'start', className: 'justify-start' },\n { variant: 'bordered', align: 'center', className: 'justify-center' },\n { variant: 'bordered', align: 'end', className: 'justify-end' },\n { variant: 'bordered', align: 'stretch', className: 'justify-start' },\n // Alignment for lifted variant\n { variant: 'lifted', align: 'start', className: 'justify-start' },\n { variant: 'lifted', align: 'center', className: 'justify-center' },\n { variant: 'lifted', align: 'end', className: 'justify-end' },\n { variant: 'lifted', align: 'stretch', className: 'justify-start' },\n // Border position top for bordered/lifted\n {\n variant: 'bordered',\n borderPosition: 'top',\n className: 'after:bottom-auto after:top-0',\n },\n {\n variant: 'lifted',\n borderPosition: 'top',\n className: 'after:bottom-auto after:top-0',\n },\n ],\n defaultVariants: {\n variant: 'boxed',\n size: 'md',\n align: 'start',\n borderPosition: 'bottom',\n },\n },\n);\n\ntype TabsListVariantProps = VariantProps<typeof tabsListVariants>;\n\ninterface TabsListProps extends TabsPrimitive.List.Props, TabsListVariantProps {\n /** Enable gradient styling on active tab */\n gradient?: boolean;\n}\n\n// ============================================\n// TabsTrigger Variants\n// ============================================\n\nconst tabsTriggerVariants = cva([\n // Base\n 'relative inline-flex items-center justify-center whitespace-nowrap',\n 'gap-1.5 border border-transparent font-medium',\n 'transition-[color,background-color,border-color,box-shadow,opacity] duration-200 ease-smooth motion-reduce:transition-none',\n 'group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start',\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:outline-1',\n 'disabled:pointer-events-none disabled:text-muted-foreground disabled:opacity-70',\n '[&_svg]:pointer-events-none [&_svg]:shrink-0',\n 'text-foreground',\n\n // Size: sm\n 'group-data-[size=sm]/tabs-list:h-9 group-data-[size=sm]/tabs-list:px-2 group-data-[size=sm]/tabs-list:py-1 group-data-[size=sm]/tabs-list:text-sm',\n \"group-data-[size=sm]/tabs-list:[&_svg:not([class*='size-'])]:size-4\",\n // Size: md\n 'group-data-[size=md]/tabs-list:h-10 group-data-[size=md]/tabs-list:px-2.5 group-data-[size=md]/tabs-list:py-[5px] group-data-[size=md]/tabs-list:text-sm',\n \"group-data-[size=md]/tabs-list:[&_svg:not([class*='size-'])]:size-4\",\n // Size: lg\n 'group-data-[size=lg]/tabs-list:h-11 group-data-[size=lg]/tabs-list:px-3 group-data-[size=lg]/tabs-list:py-2 group-data-[size=lg]/tabs-list:text-base',\n \"group-data-[size=lg]/tabs-list:[&_svg:not([class*='size-'])]:size-5\",\n\n // Boxed variant\n 'group-data-[variant=boxed]/tabs-list:rounded-[10px] group-data-[variant=boxed]/tabs-list:flex-1 group-data-[variant=boxed]/tabs-list:h-[calc(100%-1px)]',\n 'group-data-[variant=boxed]/tabs-list:data-active:bg-secondary group-data-[variant=boxed]/tabs-list:data-active:shadow-sm group-data-[variant=boxed]/tabs-list:data-active:text-foreground',\n 'group-data-[variant=boxed]/tabs-list:hover:bg-accent',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=boxed]/tabs-list:flex-initial group-data-[orientation=vertical]/tabs:group-data-[variant=boxed]/tabs-list:justify-center',\n\n // Pill variant\n 'group-data-[variant=pill]/tabs-list:rounded-full group-data-[variant=pill]/tabs-list:flex-1 group-data-[variant=pill]/tabs-list:h-[calc(100%-1px)]',\n 'group-data-[variant=pill]/tabs-list:data-active:bg-background group-data-[variant=pill]/tabs-list:data-active:shadow-sm group-data-[variant=pill]/tabs-list:data-active:text-foreground',\n 'group-data-[variant=pill]/tabs-list:hover:bg-accent',\n 'dark:group-data-[variant=pill]/tabs-list:data-active:bg-accent dark:group-data-[variant=pill]/tabs-list:data-active:text-accent-foreground dark:group-data-[variant=pill]/tabs-list:data-active:border-input',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=pill]/tabs-list:flex-initial group-data-[orientation=vertical]/tabs:group-data-[variant=pill]/tabs-list:justify-center',\n\n // Bordered variant\n 'group-data-[variant=bordered]/tabs-list:bg-transparent group-data-[variant=bordered]/tabs-list:rounded-none',\n 'group-data-[variant=bordered]/tabs-list:pb-2.5 group-data-[variant=bordered]/tabs-list:flex-initial',\n 'group-data-[variant=bordered]/tabs-list:data-active:bg-background group-data-[variant=bordered]/tabs-list:data-active:text-primary group-data-[variant=bordered]/tabs-list:data-active:shadow-none',\n 'group-data-[variant=bordered]/tabs-list:group-data-[align=stretch]/tabs-list:flex-1',\n 'group-data-[variant=bordered]/tabs-list:after:bg-primary/90 group-data-[variant=bordered]/tabs-list:after:absolute group-data-[variant=bordered]/tabs-list:after:bottom-0 group-data-[variant=bordered]/tabs-list:after:inset-x-0 group-data-[variant=bordered]/tabs-list:after:h-0.5 group-data-[variant=bordered]/tabs-list:after:z-10 group-data-[variant=bordered]/tabs-list:after:opacity-0 group-data-[variant=bordered]/tabs-list:after:transition-opacity group-data-[variant=bordered]/tabs-list:after:duration-200 group-data-[variant=bordered]/tabs-list:after:ease-out',\n 'group-data-[variant=bordered]/tabs-list:data-active:after:opacity-100',\n 'group-data-[variant=bordered]/tabs-list:hover:after:opacity-100 group-data-[variant=bordered]/tabs-list:hover:after:bg-primary/20',\n 'group-data-[variant=bordered]/tabs-list:data-active:hover:after:bg-primary/90',\n 'group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:after:bottom-auto group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:after:top-0',\n 'group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:pb-0 group-data-[variant=bordered]/tabs-list:group-data-[border-position=top]/tabs-list:pt-2.5',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:inset-x-auto group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:inset-y-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:left-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:w-0.5 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:after:h-auto',\n 'group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:pb-0 group-data-[orientation=vertical]/tabs:group-data-[variant=bordered]/tabs-list:pl-2.5',\n\n // Lifted variant — browser-tab style with curved corner connectors\n 'group-data-[variant=lifted]/tabs-list:flex-initial',\n 'group-data-[variant=lifted]/tabs-list:rounded-t-md',\n 'group-data-[variant=lifted]/tabs-list:border-b group-data-[variant=lifted]/tabs-list:border-b-border',\n 'group-data-[variant=lifted]/tabs-list:text-foreground',\n 'group-data-[variant=lifted]/tabs-list:data-active:bg-background',\n 'group-data-[variant=lifted]/tabs-list:data-active:text-primary',\n 'group-data-[variant=lifted]/tabs-list:data-active:border group-data-[variant=lifted]/tabs-list:data-active:border-border',\n 'group-data-[variant=lifted]/tabs-list:data-active:border-b-0 group-data-[variant=lifted]/tabs-list:data-active:border-b-background',\n 'group-data-[variant=lifted]/tabs-list:data-active:rounded-t-[10px]',\n 'group-data-[variant=lifted]/tabs-list:data-active:mb-[-1px] group-data-[variant=lifted]/tabs-list:data-active:z-10',\n 'group-data-[variant=lifted]/tabs-list:data-active:before:absolute group-data-[variant=lifted]/tabs-list:data-active:before:bottom-0 group-data-[variant=lifted]/tabs-list:data-active:before:left-[-10px] group-data-[variant=lifted]/tabs-list:data-active:before:size-[10px] group-data-[variant=lifted]/tabs-list:data-active:before:rounded-br-[10px] group-data-[variant=lifted]/tabs-list:data-active:before:border-b group-data-[variant=lifted]/tabs-list:data-active:before:border-r group-data-[variant=lifted]/tabs-list:data-active:before:border-border group-data-[variant=lifted]/tabs-list:data-active:before:bg-background group-data-[variant=lifted]/tabs-list:data-active:before:shadow-[2px_2px_0_0_var(--background)]',\n 'group-data-[variant=lifted]/tabs-list:data-active:after:absolute group-data-[variant=lifted]/tabs-list:data-active:after:bottom-0 group-data-[variant=lifted]/tabs-list:data-active:after:right-[-10px] group-data-[variant=lifted]/tabs-list:data-active:after:size-[10px] group-data-[variant=lifted]/tabs-list:data-active:after:rounded-bl-[10px] group-data-[variant=lifted]/tabs-list:data-active:after:border-b group-data-[variant=lifted]/tabs-list:data-active:after:border-l group-data-[variant=lifted]/tabs-list:data-active:after:border-border group-data-[variant=lifted]/tabs-list:data-active:after:bg-background group-data-[variant=lifted]/tabs-list:data-active:after:shadow-[-2px_2px_0_0_var(--background)]',\n 'group-data-[variant=lifted]/tabs-list:group-data-[align=stretch]/tabs-list:flex-1',\n\n // Gradient support (uses brand tokens)\n 'group-data-[variant=boxed]/tabs-list:group-data-[gradient]/tabs-list:data-active:bg-[linear-gradient(100deg,var(--air-100)/0.1,var(--primary)/0.1)] group-data-[variant=boxed]/tabs-list:group-data-[gradient]/tabs-list:data-active:text-primary',\n 'group-data-[variant=pill]/tabs-list:group-data-[gradient]/tabs-list:data-active:bg-[linear-gradient(98deg,var(--air-100),var(--primary))] group-data-[variant=pill]/tabs-list:group-data-[gradient]/tabs-list:data-active:text-primary-foreground',\n\n // Hide pseudo-element underline when TabsIndicator is used\n 'group-has-[[data-slot=tabs-indicator]]/tabs-list:after:hidden',\n]);\n\n// ============================================\n// Components\n// ============================================\n\nconst Tabs = React.forwardRef<HTMLDivElement, TabsPrimitive.Root.Props>(\n ({ className, orientation = 'horizontal', ...props }, ref) => (\n <TabsPrimitive.Root\n ref={ref}\n data-slot=\"tabs\"\n data-orientation={orientation}\n className={cn(\n 'gap-2 group/tabs flex data-[orientation=horizontal]:flex-col',\n className,\n )}\n {...props}\n />\n ),\n);\n\nconst TabsList = React.forwardRef<HTMLDivElement, TabsListProps>(\n (\n {\n className,\n variant = 'boxed',\n size = 'md',\n align = 'start',\n borderPosition = 'bottom',\n gradient,\n ...props\n },\n ref,\n ) => {\n return (\n <TabsPrimitive.List\n ref={ref}\n data-slot=\"tabs-list\"\n data-variant={variant}\n data-align={align}\n data-size={size}\n data-border-position={borderPosition}\n {...(gradient ? { 'data-gradient': '' } : {})}\n className={cn(\n tabsListVariants({ variant, size, align, borderPosition }),\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nconst TabsTrigger = React.forwardRef<\n HTMLButtonElement,\n TabsPrimitive.Tab.Props\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Tab\n ref={ref}\n data-slot=\"tabs-trigger\"\n className={cn(tabsTriggerVariants(), className)}\n {...props}\n />\n));\n\nconst TabsContent = React.forwardRef<HTMLDivElement, TabsPrimitive.Panel.Props>(\n ({ className, ...props }, ref) => (\n <TabsPrimitive.Panel\n ref={ref}\n data-slot=\"tabs-content\"\n className={cn(\n 'flex-1 focus-visible:outline-none delight-fade-in',\n className,\n )}\n {...props}\n />\n ),\n);\n\n/**\n * Animated indicator that slides to the active tab position.\n * Use with `variant=\"bordered\"` on TabsList.\n *\n * @example\n * ```tsx\n * <TabsList variant=\"bordered\">\n * <TabsTrigger value=\"tab1\">Tab 1</TabsTrigger>\n * <TabsTrigger value=\"tab2\">Tab 2</TabsTrigger>\n * <TabsIndicator />\n * </TabsList>\n * ```\n */\nconst TabsIndicator = React.forwardRef<\n HTMLSpanElement,\n TabsPrimitive.Indicator.Props\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Indicator\n ref={ref}\n data-slot=\"tabs-indicator\"\n className={cn(\n 'absolute bottom-0 left-0 h-0.5 z-10 bg-primary/90',\n 'w-[var(--active-tab-width)]',\n // Width must be layout-animated (Base UI constraint); transform is GPU-composited\n 'transition-[transform,width] duration-300 ease-smooth motion-reduce:transition-none',\n className,\n )}\n style={{\n transform: 'translateX(var(--active-tab-left, 0px))',\n }}\n {...props}\n />\n));\n\nTabs.displayName = 'Tabs';\nTabsList.displayName = 'TabsList';\nTabsTrigger.displayName = 'TabsTrigger';\nTabsContent.displayName = 'TabsContent';\nTabsIndicator.displayName = 'TabsIndicator';\n\nexport {\n Tabs,\n TabsContent,\n TabsIndicator,\n TabsList,\n TabsTrigger,\n tabsListVariants,\n tabsTriggerVariants,\n};\n"],"mappings":"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"}
package/dist/index151.js CHANGED
@@ -68,7 +68,7 @@ function renderRenderOnlyComponent(language, content) {
68
68
  }
69
69
  function renderInteractiveError({ language, inverted, message }) {
70
70
  return /* @__PURE__ */ jsxs("div", {
71
- className: cn("my-4 p-4 rounded-lg", inverted ? "border border-primary-foreground/30 bg-primary-foreground/10 text-primary-foreground text-sm" : "border border-destructive bg-destructive/20 text-destructive text-sm"),
71
+ className: cn("my-4 p-4 rounded-lg", inverted ? "border border-primary-foreground/30 bg-background/10 text-primary-foreground text-sm" : "border border-destructive bg-destructive/20 text-destructive text-sm"),
72
72
  role: "alert",
73
73
  "data-slot": "ai-interactive-error",
74
74
  children: [
@@ -98,7 +98,7 @@ function renderCodeBlock({ code, language, inverted }) {
98
98
  });
99
99
  if (!inverted) return block;
100
100
  return /* @__PURE__ */ jsx("div", {
101
- className: "my-4 [&_[data-slot=ai-code-block]]:bg-primary-foreground/10 [&_[data-slot=ai-code-block]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]]:bg-primary-foreground/5 [&_[data-slot=ai-code-block-header]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]_span]:!text-primary-foreground/60 [&_[data-slot=ai-code-block-header]_button]:!text-primary-foreground/60",
101
+ className: "my-4 [&_[data-slot=ai-code-block]]:bg-background/10 [&_[data-slot=ai-code-block]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]]:bg-background/5 [&_[data-slot=ai-code-block-header]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]_span]:!text-primary-foreground/60 [&_[data-slot=ai-code-block-header]_button]:!text-primary-foreground/60",
102
102
  style: INVERTED_SUGAR_HIGH_STYLE,
103
103
  children: block
104
104
  });
@@ -107,7 +107,7 @@ function createCodeComponent(variant = "default") {
107
107
  const inv = variant === "inverted";
108
108
  return function UnifiedCodeBlock({ className, children }) {
109
109
  if (!className?.includes("language-")) return /* @__PURE__ */ jsx("code", {
110
- className: cn("px-1.5 py-0.5 rounded text-sm font-mono transition-colors", "break-words [overflow-wrap:anywhere]", inv ? "bg-primary-foreground/15 text-primary-foreground" : "bg-accent text-foreground", className),
110
+ className: cn("px-1.5 py-0.5 rounded text-sm font-mono transition-colors", "break-words [overflow-wrap:anywhere]", inv ? "bg-background/15 text-primary-foreground" : "bg-accent text-foreground", className),
111
111
  children
112
112
  });
113
113
  const match = /language-(\w+)/.exec(className || "");
@@ -1 +1 @@
1
- {"version":3,"file":"index151.js","names":[],"sources":["../src/components/organisms/ai/_shared/code-handler.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { INVERTED_SUGAR_HIGH_STYLE } from '@/lib/syntax-highlight';\nimport { cn } from '@/lib/utils';\nimport { validateInteractiveSchema } from './schema-validation';\nimport type { MarkdownVariant } from './types';\n\n// =============================================================================\n// Lazy-loaded renderers\n// =============================================================================\n\nconst LazyAIMermaidDiagram = React.lazy(\n () => import('@/components/organisms/ai/AIMermaidDiagram'),\n);\nconst LazyAIAsciiDiagram = React.lazy(\n () => import('@/components/organisms/ai/AIAsciiDiagram'),\n);\nconst LazyAIChart = React.lazy(\n () => import('@/components/organisms/ai/AIChart'),\n);\nconst LazyAITable = React.lazy(\n () => import('@/components/organisms/ai/AITable'),\n);\nconst LazyAIForm = React.lazy(() => import('@/components/organisms/ai/AIForm'));\nconst LazyAISelect = React.lazy(\n () => import('@/components/organisms/ai/AISelect'),\n);\nconst LazyAICodeBlock = React.lazy(() =>\n import('@/components/organisms/ai/AICodeBlock').then((module) => ({\n default: module.AICodeBlock,\n })),\n);\n\n// =============================================================================\n// Loading skeleton for lazy components\n// =============================================================================\n\nfunction LazyLoadingSkeleton() {\n return (\n <div className=\"my-4 flex items-center justify-center p-8 rounded-xl border border-border bg-muted animate-pulse\">\n <div className=\"h-4 w-32 bg-accent rounded\" />\n </div>\n );\n}\n\n// =============================================================================\n// Interactive languages (fire onInteraction events)\n// =============================================================================\n\nconst INTERACTIVE_LANGUAGES = ['chart', 'select', 'table', 'form'] as const;\ntype InteractiveLanguage = (typeof INTERACTIVE_LANGUAGES)[number];\n\nfunction isInteractiveLanguage(\n language: string,\n): language is InteractiveLanguage {\n return INTERACTIVE_LANGUAGES.includes(language as InteractiveLanguage);\n}\n\n// =============================================================================\n// Render-only languages (visual output, no interaction events)\n// =============================================================================\n\nconst RENDER_ONLY_LANGUAGES = ['mermaid', 'ascii'] as const;\ntype RenderOnlyLanguage = (typeof RENDER_ONLY_LANGUAGES)[number];\n\nfunction isRenderOnlyLanguage(\n language: string,\n): language is RenderOnlyLanguage {\n return RENDER_ONLY_LANGUAGES.includes(language as RenderOnlyLanguage);\n}\n\n// =============================================================================\n// Component lookup maps\n// =============================================================================\n\ntype InteractiveComponentProps = {\n schema: unknown;\n};\n\ntype CodeRendererEntry = {\n Component: React.LazyExoticComponent<React.ComponentType<{ code: string }>>;\n prop: 'code';\n};\n\ntype ContentRendererEntry = {\n Component: React.LazyExoticComponent<\n React.ComponentType<{ content: string }>\n >;\n prop: 'content';\n};\n\ntype RenderOnlyComponentEntry = CodeRendererEntry | ContentRendererEntry;\n\nconst INTERACTIVE_COMPONENTS: Record<\n InteractiveLanguage,\n React.LazyExoticComponent<React.ComponentType<InteractiveComponentProps>>\n> = {\n chart: LazyAIChart as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n select: LazyAISelect as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n table: LazyAITable as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n form: LazyAIForm as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n};\n\nconst RENDER_ONLY_COMPONENTS: Record<\n RenderOnlyLanguage,\n RenderOnlyComponentEntry\n> = {\n mermaid: {\n Component: LazyAIMermaidDiagram as React.LazyExoticComponent<\n React.ComponentType<{ code: string }>\n >,\n prop: 'code',\n },\n ascii: {\n Component: LazyAIAsciiDiagram as React.LazyExoticComponent<\n React.ComponentType<{ content: string }>\n >,\n prop: 'content',\n },\n};\n\nconst LOADING_FALLBACK = <LazyLoadingSkeleton />;\n\n// =============================================================================\n// Render helpers\n// =============================================================================\n\nfunction renderInteractiveComponent(\n language: InteractiveLanguage,\n schema: unknown,\n): React.ReactElement {\n const Component = INTERACTIVE_COMPONENTS[language];\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component schema={schema} />\n </React.Suspense>\n );\n}\n\nfunction renderRenderOnlyComponent(\n language: RenderOnlyLanguage,\n content: string,\n): React.ReactElement {\n const { Component, prop } = RENDER_ONLY_COMPONENTS[language];\n if (prop === 'code') {\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component code={content} />\n </React.Suspense>\n );\n }\n\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component content={content} />\n </React.Suspense>\n );\n}\n\nfunction renderInteractiveError({\n language,\n inverted,\n message,\n}: {\n language: InteractiveLanguage;\n inverted: boolean;\n message: string;\n}): React.ReactElement {\n return (\n <div\n className={cn(\n 'my-4 p-4 rounded-lg',\n inverted\n ? 'border border-primary-foreground/30 bg-primary-foreground/10 text-primary-foreground text-sm'\n : 'border border-destructive bg-destructive/20 text-destructive text-sm',\n )}\n role=\"alert\"\n data-slot=\"ai-interactive-error\"\n >\n <span className=\"font-semibold\">Invalid {language} schema:</span>{' '}\n {message}\n </div>\n );\n}\n\nfunction renderCodeBlock({\n code,\n language,\n inverted,\n}: {\n code: string;\n language: string;\n inverted: boolean;\n}): React.ReactElement {\n const block = (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <LazyAICodeBlock\n code={code}\n language={language}\n showHeader={true}\n showCopyButton={true}\n className={inverted ? undefined : 'my-4'}\n />\n </React.Suspense>\n );\n\n if (!inverted) {\n return block;\n }\n\n return (\n <div\n className=\"my-4 [&_[data-slot=ai-code-block]]:bg-primary-foreground/10 [&_[data-slot=ai-code-block]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]]:bg-primary-foreground/5 [&_[data-slot=ai-code-block-header]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]_span]:!text-primary-foreground/60 [&_[data-slot=ai-code-block-header]_button]:!text-primary-foreground/60\"\n style={INVERTED_SUGAR_HIGH_STYLE}\n >\n {block}\n </div>\n );\n}\n\n// =============================================================================\n// Unified code component for react-markdown\n// =============================================================================\n\nfunction createCodeComponent(variant: MarkdownVariant = 'default'): React.FC<{\n className?: string;\n children?: React.ReactNode;\n}> {\n const inv = variant === 'inverted';\n\n return function UnifiedCodeBlock({ className, children }) {\n const isInline = !className?.includes('language-');\n\n if (isInline) {\n return (\n <code\n className={cn(\n 'px-1.5 py-0.5 rounded text-sm font-mono transition-colors',\n 'break-words [overflow-wrap:anywhere]',\n inv\n ? 'bg-primary-foreground/15 text-primary-foreground'\n : 'bg-accent text-foreground',\n className,\n )}\n >\n {children}\n </code>\n );\n }\n\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : 'plaintext';\n const content = String(children).replace(/\\n$/, '');\n\n if (isRenderOnlyLanguage(language)) {\n return renderRenderOnlyComponent(language, content);\n }\n\n if (isInteractiveLanguage(language)) {\n try {\n const schema = JSON.parse(content);\n const validation = validateInteractiveSchema(language, schema);\n\n if (!validation.valid) {\n return renderInteractiveError({\n language,\n inverted: inv,\n message:\n 'The configuration does not match the expected schema shape.',\n });\n }\n\n return renderInteractiveComponent(language, validation.schema);\n } catch {\n return renderInteractiveError({\n language,\n inverted: inv,\n message:\n 'Unable to parse the configuration. Please check the JSON syntax.',\n });\n }\n }\n\n return renderCodeBlock({ code: content, language, inverted: inv });\n };\n}\n\nexport { createCodeComponent, LazyLoadingSkeleton };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index151.js","names":[],"sources":["../src/components/organisms/ai/_shared/code-handler.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { INVERTED_SUGAR_HIGH_STYLE } from '@/lib/syntax-highlight';\nimport { cn } from '@/lib/utils';\nimport { validateInteractiveSchema } from './schema-validation';\nimport type { MarkdownVariant } from './types';\n\n// =============================================================================\n// Lazy-loaded renderers\n// =============================================================================\n\nconst LazyAIMermaidDiagram = React.lazy(\n () => import('@/components/organisms/ai/AIMermaidDiagram'),\n);\nconst LazyAIAsciiDiagram = React.lazy(\n () => import('@/components/organisms/ai/AIAsciiDiagram'),\n);\nconst LazyAIChart = React.lazy(\n () => import('@/components/organisms/ai/AIChart'),\n);\nconst LazyAITable = React.lazy(\n () => import('@/components/organisms/ai/AITable'),\n);\nconst LazyAIForm = React.lazy(() => import('@/components/organisms/ai/AIForm'));\nconst LazyAISelect = React.lazy(\n () => import('@/components/organisms/ai/AISelect'),\n);\nconst LazyAICodeBlock = React.lazy(() =>\n import('@/components/organisms/ai/AICodeBlock').then((module) => ({\n default: module.AICodeBlock,\n })),\n);\n\n// =============================================================================\n// Loading skeleton for lazy components\n// =============================================================================\n\nfunction LazyLoadingSkeleton() {\n return (\n <div className=\"my-4 flex items-center justify-center p-8 rounded-xl border border-border bg-muted animate-pulse\">\n <div className=\"h-4 w-32 bg-accent rounded\" />\n </div>\n );\n}\n\n// =============================================================================\n// Interactive languages (fire onInteraction events)\n// =============================================================================\n\nconst INTERACTIVE_LANGUAGES = ['chart', 'select', 'table', 'form'] as const;\ntype InteractiveLanguage = (typeof INTERACTIVE_LANGUAGES)[number];\n\nfunction isInteractiveLanguage(\n language: string,\n): language is InteractiveLanguage {\n return INTERACTIVE_LANGUAGES.includes(language as InteractiveLanguage);\n}\n\n// =============================================================================\n// Render-only languages (visual output, no interaction events)\n// =============================================================================\n\nconst RENDER_ONLY_LANGUAGES = ['mermaid', 'ascii'] as const;\ntype RenderOnlyLanguage = (typeof RENDER_ONLY_LANGUAGES)[number];\n\nfunction isRenderOnlyLanguage(\n language: string,\n): language is RenderOnlyLanguage {\n return RENDER_ONLY_LANGUAGES.includes(language as RenderOnlyLanguage);\n}\n\n// =============================================================================\n// Component lookup maps\n// =============================================================================\n\ntype InteractiveComponentProps = {\n schema: unknown;\n};\n\ntype CodeRendererEntry = {\n Component: React.LazyExoticComponent<React.ComponentType<{ code: string }>>;\n prop: 'code';\n};\n\ntype ContentRendererEntry = {\n Component: React.LazyExoticComponent<\n React.ComponentType<{ content: string }>\n >;\n prop: 'content';\n};\n\ntype RenderOnlyComponentEntry = CodeRendererEntry | ContentRendererEntry;\n\nconst INTERACTIVE_COMPONENTS: Record<\n InteractiveLanguage,\n React.LazyExoticComponent<React.ComponentType<InteractiveComponentProps>>\n> = {\n chart: LazyAIChart as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n select: LazyAISelect as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n table: LazyAITable as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n form: LazyAIForm as React.LazyExoticComponent<\n React.ComponentType<InteractiveComponentProps>\n >,\n};\n\nconst RENDER_ONLY_COMPONENTS: Record<\n RenderOnlyLanguage,\n RenderOnlyComponentEntry\n> = {\n mermaid: {\n Component: LazyAIMermaidDiagram as React.LazyExoticComponent<\n React.ComponentType<{ code: string }>\n >,\n prop: 'code',\n },\n ascii: {\n Component: LazyAIAsciiDiagram as React.LazyExoticComponent<\n React.ComponentType<{ content: string }>\n >,\n prop: 'content',\n },\n};\n\nconst LOADING_FALLBACK = <LazyLoadingSkeleton />;\n\n// =============================================================================\n// Render helpers\n// =============================================================================\n\nfunction renderInteractiveComponent(\n language: InteractiveLanguage,\n schema: unknown,\n): React.ReactElement {\n const Component = INTERACTIVE_COMPONENTS[language];\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component schema={schema} />\n </React.Suspense>\n );\n}\n\nfunction renderRenderOnlyComponent(\n language: RenderOnlyLanguage,\n content: string,\n): React.ReactElement {\n const { Component, prop } = RENDER_ONLY_COMPONENTS[language];\n if (prop === 'code') {\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component code={content} />\n </React.Suspense>\n );\n }\n\n return (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <Component content={content} />\n </React.Suspense>\n );\n}\n\nfunction renderInteractiveError({\n language,\n inverted,\n message,\n}: {\n language: InteractiveLanguage;\n inverted: boolean;\n message: string;\n}): React.ReactElement {\n return (\n <div\n className={cn(\n 'my-4 p-4 rounded-lg',\n inverted\n ? 'border border-primary-foreground/30 bg-background/10 text-primary-foreground text-sm'\n : 'border border-destructive bg-destructive/20 text-destructive text-sm',\n )}\n role=\"alert\"\n data-slot=\"ai-interactive-error\"\n >\n <span className=\"font-semibold\">Invalid {language} schema:</span>{' '}\n {message}\n </div>\n );\n}\n\nfunction renderCodeBlock({\n code,\n language,\n inverted,\n}: {\n code: string;\n language: string;\n inverted: boolean;\n}): React.ReactElement {\n const block = (\n <React.Suspense fallback={LOADING_FALLBACK}>\n <LazyAICodeBlock\n code={code}\n language={language}\n showHeader={true}\n showCopyButton={true}\n className={inverted ? undefined : 'my-4'}\n />\n </React.Suspense>\n );\n\n if (!inverted) {\n return block;\n }\n\n return (\n <div\n className=\"my-4 [&_[data-slot=ai-code-block]]:bg-background/10 [&_[data-slot=ai-code-block]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]]:bg-background/5 [&_[data-slot=ai-code-block-header]]:border-primary-foreground/20 [&_[data-slot=ai-code-block-header]_span]:!text-primary-foreground/60 [&_[data-slot=ai-code-block-header]_button]:!text-primary-foreground/60\"\n style={INVERTED_SUGAR_HIGH_STYLE}\n >\n {block}\n </div>\n );\n}\n\n// =============================================================================\n// Unified code component for react-markdown\n// =============================================================================\n\nfunction createCodeComponent(variant: MarkdownVariant = 'default'): React.FC<{\n className?: string;\n children?: React.ReactNode;\n}> {\n const inv = variant === 'inverted';\n\n return function UnifiedCodeBlock({ className, children }) {\n const isInline = !className?.includes('language-');\n\n if (isInline) {\n return (\n <code\n className={cn(\n 'px-1.5 py-0.5 rounded text-sm font-mono transition-colors',\n 'break-words [overflow-wrap:anywhere]',\n inv\n ? 'bg-background/15 text-primary-foreground'\n : 'bg-accent text-foreground',\n className,\n )}\n >\n {children}\n </code>\n );\n }\n\n const match = /language-(\\w+)/.exec(className || '');\n const language = match ? match[1] : 'plaintext';\n const content = String(children).replace(/\\n$/, '');\n\n if (isRenderOnlyLanguage(language)) {\n return renderRenderOnlyComponent(language, content);\n }\n\n if (isInteractiveLanguage(language)) {\n try {\n const schema = JSON.parse(content);\n const validation = validateInteractiveSchema(language, schema);\n\n if (!validation.valid) {\n return renderInteractiveError({\n language,\n inverted: inv,\n message:\n 'The configuration does not match the expected schema shape.',\n });\n }\n\n return renderInteractiveComponent(language, validation.schema);\n } catch {\n return renderInteractiveError({\n language,\n inverted: inv,\n message:\n 'Unable to parse the configuration. Please check the JSON syntax.',\n });\n }\n }\n\n return renderCodeBlock({ code: content, language, inverted: inv });\n };\n}\n\nexport { createCodeComponent, LazyLoadingSkeleton };\n"],"mappings":"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"}
package/dist/index152.js CHANGED
@@ -38,7 +38,7 @@ var variantColors = {
38
38
  },
39
39
  bgMuted: {
40
40
  default: "bg-muted",
41
- inverted: "bg-primary-foreground/20"
41
+ inverted: "bg-background/20"
42
42
  }
43
43
  };
44
44
  function vc(variant, token) {
@@ -106,7 +106,7 @@ function createMarkdownComponents(variant) {
106
106
  ...props
107
107
  }),
108
108
  table: ({ className, children, ...props }) => /* @__PURE__ */ jsx("section", {
109
- className: "overflow-x-auto my-4 [overflow-wrap:normal] [&_a]:break-normal [&_a]:[overflow-wrap:normal] [&_code]:break-normal [&_code]:[overflow-wrap:normal] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
109
+ className: "not-prose overflow-x-auto my-4 [overflow-wrap:normal] [&_a]:break-normal [&_a]:[overflow-wrap:normal] [&_code]:break-normal [&_code]:[overflow-wrap:normal] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
110
110
  "aria-label": "Data table",
111
111
  tabIndex: 0,
112
112
  children: /* @__PURE__ */ jsx("table", {
@@ -1 +1 @@
1
- {"version":3,"file":"index152.js","names":[],"sources":["../src/components/organisms/ai/_shared/markdown-renderer.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport type { PluggableList } from 'unified';\nimport { cn } from '@/lib/utils';\nimport { createCodeComponent } from './code-handler';\n\nconst remarkPlugins: PluggableList = [remarkGfm, remarkMath];\nconst rehypePlugins: PluggableList = [[rehypeKatex, { output: 'mathml' }]];\nconst markdownOverflowWrapClasses =\n 'min-w-0 max-w-full break-words [overflow-wrap:anywhere]';\n\n// =============================================================================\n// Variant-aware color mapping\n// =============================================================================\n\nimport type { MarkdownVariant } from './types';\n\nexport type { MarkdownVariant } from './types';\n\nconst variantColors = {\n heading: { default: 'text-foreground', inverted: 'text-primary-foreground' },\n body: {\n default: 'text-secondary-foreground',\n inverted: 'text-primary-foreground/90',\n },\n link: { default: 'text-primary', inverted: 'text-primary-foreground' },\n muted: {\n default: 'text-muted-foreground',\n inverted: 'text-primary-foreground/70',\n },\n faint: {\n default: 'text-text-quaternary',\n inverted: 'text-primary-foreground/50',\n },\n border: {\n default: 'border-border',\n inverted: 'border-primary-foreground/25',\n },\n bgMuted: { default: 'bg-muted', inverted: 'bg-primary-foreground/20' },\n} as const;\n\ntype ColorToken = keyof typeof variantColors;\n\nfunction vc(variant: MarkdownVariant, token: ColorToken): string {\n return variantColors[token][variant];\n}\n\n// =============================================================================\n// Create variant-aware component overrides for react-markdown\n// =============================================================================\n\nfunction createMarkdownComponents(\n variant: MarkdownVariant,\n): Partial<Components> {\n return {\n h1: ({ className, children, ...props }) => (\n <h1\n className={cn(\n 'text-display-xs font-bold mt-6 mb-4',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h1>\n ),\n h2: ({ className, children, ...props }) => (\n <h2\n className={cn(\n 'text-xl font-semibold mt-5 mb-3',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n ),\n h3: ({ className, children, ...props }) => (\n <h3\n className={cn(\n 'text-lg font-semibold mt-4 mb-2',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h3>\n ),\n h4: ({ className, children, ...props }) => (\n <h4\n className={cn(\n 'text-base font-semibold mt-3 mb-2',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h4>\n ),\n p: ({ className, children, ...props }) => (\n <p\n className={cn(\n 'text-base leading-relaxed mb-4',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </p>\n ),\n ul: ({ className, children, ...props }) => (\n <ul\n className={cn(\n 'list-disc list-inside mb-4 space-y-1',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </ul>\n ),\n ol: ({ className, children, ...props }) => (\n <ol\n className={cn(\n 'list-decimal list-inside mb-4 space-y-1',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </ol>\n ),\n li: ({ className, children, ...props }) => (\n <li\n className={cn(\n 'leading-relaxed',\n markdownOverflowWrapClasses,\n className,\n )}\n {...props}\n >\n {children}\n </li>\n ),\n a: ({ className, children, href, ...props }) => (\n <a\n href={href}\n className={cn(\n 'underline underline-offset-2 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring rounded-sm',\n markdownOverflowWrapClasses,\n vc(variant, 'link'),\n variant === 'default' && 'hover:text-primary',\n variant === 'inverted' && 'hover:text-primary-foreground',\n className,\n )}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n {...props}\n >\n {children}\n </a>\n ),\n blockquote: ({ className, children, ...props }) => (\n <blockquote\n className={cn(\n 'border-s-4 ps-4 py-2 my-4 italic',\n markdownOverflowWrapClasses,\n vc(variant, 'muted'),\n vc(variant, 'border'),\n className,\n )}\n {...props}\n >\n {children}\n </blockquote>\n ),\n code: createCodeComponent(variant),\n pre: ({ children }) => <>{children}</>,\n hr: ({ className, ...props }) => (\n <hr className={cn('my-6', vc(variant, 'border'), className)} {...props} />\n ),\n table: ({ className, children, ...props }) => (\n <section\n className=\"overflow-x-auto my-4 [overflow-wrap:normal] [&_a]:break-normal [&_a]:[overflow-wrap:normal] [&_code]:break-normal [&_code]:[overflow-wrap:normal] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n aria-label=\"Data table\"\n // biome-ignore lint/a11y/noNoninteractiveTabindex: Focusable scroll region satisfies axe scrollable-region-focusable.\n tabIndex={0}\n >\n <table\n className={cn(\n 'min-w-full border-collapse border [overflow-wrap:normal]',\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </table>\n </section>\n ),\n thead: ({ className, children, ...props }) => (\n <thead\n className={cn(\n variant === 'default' ? vc(variant, 'bgMuted') : '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </thead>\n ),\n tbody: ({ className, children, ...props }) => (\n <tbody\n className={cn(\n variant === 'default'\n ? 'divide-y divide-border'\n : 'divide-y divide-primary-foreground/20',\n className,\n )}\n {...props}\n >\n {children}\n </tbody>\n ),\n tr: ({ children, ...props }) => <tr {...props}>{children}</tr>,\n th: ({ className, children, ...props }) => (\n <th\n className={cn(\n 'px-4 py-2 text-start text-sm font-semibold border',\n '[overflow-wrap:normal]',\n vc(variant, 'body'),\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </th>\n ),\n td: ({ className, children, ...props }) => (\n <td\n className={cn(\n 'px-4 py-2 text-sm border',\n '[overflow-wrap:normal]',\n vc(variant, 'body'),\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </td>\n ),\n strong: ({ className, children, ...props }) => (\n <strong\n className={cn(\n 'font-semibold',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </strong>\n ),\n em: ({ className, children, ...props }) => (\n <em className={cn(markdownOverflowWrapClasses, className)} {...props}>\n {children}\n </em>\n ),\n del: ({ className, children, ...props }) => (\n <del\n className={cn(\n 'line-through',\n markdownOverflowWrapClasses,\n vc(variant, 'faint'),\n className,\n )}\n {...props}\n >\n {children}\n </del>\n ),\n img: ({ className, alt, src, ...props }) => (\n <img\n src={src}\n alt={alt}\n className={cn('max-w-full h-auto rounded-lg my-4', className)}\n loading=\"lazy\"\n {...props}\n />\n ),\n };\n}\n\n// =============================================================================\n// Component cache (at most 2 entries: 'default' and 'inverted')\n// =============================================================================\n\nconst componentCache = new Map<MarkdownVariant, Partial<Components>>();\n\nfunction getComponents(variant: MarkdownVariant): Partial<Components> {\n let cached = componentCache.get(variant);\n if (!cached) {\n cached = createMarkdownComponents(variant);\n componentCache.set(variant, cached);\n }\n return cached;\n}\n\n// =============================================================================\n// MarkdownRenderer\n// =============================================================================\n\nexport interface MarkdownRendererProps {\n /** Markdown content to render */\n children: string;\n /** Additional class name */\n className?: string;\n /** Color variant — 'inverted' for white text on colored backgrounds */\n variant?: MarkdownVariant;\n}\n\nconst MarkdownRenderer = React.memo(function MarkdownRenderer({\n children,\n className,\n variant = 'default',\n}: MarkdownRendererProps) {\n const components = getComponents(variant);\n\n return (\n <div\n data-slot=\"ai-markdown\"\n className={cn(\n 'prose prose-gray max-w-none',\n markdownOverflowWrapClasses,\n className,\n )}\n >\n <ReactMarkdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={rehypePlugins}\n components={components}\n >\n {children}\n </ReactMarkdown>\n </div>\n );\n});\n\nMarkdownRenderer.displayName = 'MarkdownRenderer';\n\nexport { MarkdownRenderer };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index152.js","names":[],"sources":["../src/components/organisms/ai/_shared/markdown-renderer.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport ReactMarkdown, { type Components } from 'react-markdown';\nimport rehypeKatex from 'rehype-katex';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport type { PluggableList } from 'unified';\nimport { cn } from '@/lib/utils';\nimport { createCodeComponent } from './code-handler';\n\nconst remarkPlugins: PluggableList = [remarkGfm, remarkMath];\nconst rehypePlugins: PluggableList = [[rehypeKatex, { output: 'mathml' }]];\nconst markdownOverflowWrapClasses =\n 'min-w-0 max-w-full break-words [overflow-wrap:anywhere]';\n\n// =============================================================================\n// Variant-aware color mapping\n// =============================================================================\n\nimport type { MarkdownVariant } from './types';\n\nexport type { MarkdownVariant } from './types';\n\nconst variantColors = {\n heading: { default: 'text-foreground', inverted: 'text-primary-foreground' },\n body: {\n default: 'text-secondary-foreground',\n inverted: 'text-primary-foreground/90',\n },\n link: { default: 'text-primary', inverted: 'text-primary-foreground' },\n muted: {\n default: 'text-muted-foreground',\n inverted: 'text-primary-foreground/70',\n },\n faint: {\n default: 'text-text-quaternary',\n inverted: 'text-primary-foreground/50',\n },\n border: {\n default: 'border-border',\n inverted: 'border-primary-foreground/25',\n },\n bgMuted: { default: 'bg-muted', inverted: 'bg-background/20' },\n} as const;\n\ntype ColorToken = keyof typeof variantColors;\n\nfunction vc(variant: MarkdownVariant, token: ColorToken): string {\n return variantColors[token][variant];\n}\n\n// =============================================================================\n// Create variant-aware component overrides for react-markdown\n// =============================================================================\n\nfunction createMarkdownComponents(\n variant: MarkdownVariant,\n): Partial<Components> {\n return {\n h1: ({ className, children, ...props }) => (\n <h1\n className={cn(\n 'text-display-xs font-bold mt-6 mb-4',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h1>\n ),\n h2: ({ className, children, ...props }) => (\n <h2\n className={cn(\n 'text-xl font-semibold mt-5 mb-3',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n ),\n h3: ({ className, children, ...props }) => (\n <h3\n className={cn(\n 'text-lg font-semibold mt-4 mb-2',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h3>\n ),\n h4: ({ className, children, ...props }) => (\n <h4\n className={cn(\n 'text-base font-semibold mt-3 mb-2',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </h4>\n ),\n p: ({ className, children, ...props }) => (\n <p\n className={cn(\n 'text-base leading-relaxed mb-4',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </p>\n ),\n ul: ({ className, children, ...props }) => (\n <ul\n className={cn(\n 'list-disc list-inside mb-4 space-y-1',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </ul>\n ),\n ol: ({ className, children, ...props }) => (\n <ol\n className={cn(\n 'list-decimal list-inside mb-4 space-y-1',\n markdownOverflowWrapClasses,\n vc(variant, 'body'),\n className,\n )}\n {...props}\n >\n {children}\n </ol>\n ),\n li: ({ className, children, ...props }) => (\n <li\n className={cn(\n 'leading-relaxed',\n markdownOverflowWrapClasses,\n className,\n )}\n {...props}\n >\n {children}\n </li>\n ),\n a: ({ className, children, href, ...props }) => (\n <a\n href={href}\n className={cn(\n 'underline underline-offset-2 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring rounded-sm',\n markdownOverflowWrapClasses,\n vc(variant, 'link'),\n variant === 'default' && 'hover:text-primary',\n variant === 'inverted' && 'hover:text-primary-foreground',\n className,\n )}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n {...props}\n >\n {children}\n </a>\n ),\n blockquote: ({ className, children, ...props }) => (\n <blockquote\n className={cn(\n 'border-s-4 ps-4 py-2 my-4 italic',\n markdownOverflowWrapClasses,\n vc(variant, 'muted'),\n vc(variant, 'border'),\n className,\n )}\n {...props}\n >\n {children}\n </blockquote>\n ),\n code: createCodeComponent(variant),\n pre: ({ children }) => <>{children}</>,\n hr: ({ className, ...props }) => (\n <hr className={cn('my-6', vc(variant, 'border'), className)} {...props} />\n ),\n table: ({ className, children, ...props }) => (\n <section\n className=\"not-prose overflow-x-auto my-4 [overflow-wrap:normal] [&_a]:break-normal [&_a]:[overflow-wrap:normal] [&_code]:break-normal [&_code]:[overflow-wrap:normal] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n aria-label=\"Data table\"\n // biome-ignore lint/a11y/noNoninteractiveTabindex: Focusable scroll region satisfies axe scrollable-region-focusable.\n tabIndex={0}\n >\n <table\n className={cn(\n 'min-w-full border-collapse border [overflow-wrap:normal]',\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </table>\n </section>\n ),\n thead: ({ className, children, ...props }) => (\n <thead\n className={cn(\n variant === 'default' ? vc(variant, 'bgMuted') : '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </thead>\n ),\n tbody: ({ className, children, ...props }) => (\n <tbody\n className={cn(\n variant === 'default'\n ? 'divide-y divide-border'\n : 'divide-y divide-primary-foreground/20',\n className,\n )}\n {...props}\n >\n {children}\n </tbody>\n ),\n tr: ({ children, ...props }) => <tr {...props}>{children}</tr>,\n th: ({ className, children, ...props }) => (\n <th\n className={cn(\n 'px-4 py-2 text-start text-sm font-semibold border',\n '[overflow-wrap:normal]',\n vc(variant, 'body'),\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </th>\n ),\n td: ({ className, children, ...props }) => (\n <td\n className={cn(\n 'px-4 py-2 text-sm border',\n '[overflow-wrap:normal]',\n vc(variant, 'body'),\n vc(variant, 'border'),\n variant === 'inverted' && '!bg-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </td>\n ),\n strong: ({ className, children, ...props }) => (\n <strong\n className={cn(\n 'font-semibold',\n markdownOverflowWrapClasses,\n vc(variant, 'heading'),\n className,\n )}\n {...props}\n >\n {children}\n </strong>\n ),\n em: ({ className, children, ...props }) => (\n <em className={cn(markdownOverflowWrapClasses, className)} {...props}>\n {children}\n </em>\n ),\n del: ({ className, children, ...props }) => (\n <del\n className={cn(\n 'line-through',\n markdownOverflowWrapClasses,\n vc(variant, 'faint'),\n className,\n )}\n {...props}\n >\n {children}\n </del>\n ),\n img: ({ className, alt, src, ...props }) => (\n <img\n src={src}\n alt={alt}\n className={cn('max-w-full h-auto rounded-lg my-4', className)}\n loading=\"lazy\"\n {...props}\n />\n ),\n };\n}\n\n// =============================================================================\n// Component cache (at most 2 entries: 'default' and 'inverted')\n// =============================================================================\n\nconst componentCache = new Map<MarkdownVariant, Partial<Components>>();\n\nfunction getComponents(variant: MarkdownVariant): Partial<Components> {\n let cached = componentCache.get(variant);\n if (!cached) {\n cached = createMarkdownComponents(variant);\n componentCache.set(variant, cached);\n }\n return cached;\n}\n\n// =============================================================================\n// MarkdownRenderer\n// =============================================================================\n\nexport interface MarkdownRendererProps {\n /** Markdown content to render */\n children: string;\n /** Additional class name */\n className?: string;\n /** Color variant — 'inverted' for white text on colored backgrounds */\n variant?: MarkdownVariant;\n}\n\nconst MarkdownRenderer = React.memo(function MarkdownRenderer({\n children,\n className,\n variant = 'default',\n}: MarkdownRendererProps) {\n const components = getComponents(variant);\n\n return (\n <div\n data-slot=\"ai-markdown\"\n className={cn(\n 'prose prose-gray max-w-none',\n markdownOverflowWrapClasses,\n className,\n )}\n >\n <ReactMarkdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={rehypePlugins}\n components={components}\n >\n {children}\n </ReactMarkdown>\n </div>\n );\n});\n\nMarkdownRenderer.displayName = 'MarkdownRenderer';\n\nexport { MarkdownRenderer };\n"],"mappings":"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"}
package/dist/index155.js CHANGED
@@ -170,7 +170,7 @@ function AIScrollButton({ scrollRef, containerRef, threshold = 50, onClick, visi
170
170
  stiffness: 500,
171
171
  damping: 30
172
172
  },
173
- className: "absolute -top-1 -end-1 flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full bg-destructive text-primary-foreground text-xs font-medium motion-reduce:transition-none",
173
+ className: "absolute -top-1 -end-1 flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full bg-destructive text-destructive-foreground text-xs font-medium motion-reduce:transition-none",
174
174
  "aria-label": `${unreadCount > 99 ? "99+" : unreadCount} new messages`,
175
175
  children: unreadCount > 99 ? "99+" : unreadCount
176
176
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"index155.js","names":[],"sources":["../src/components/organisms/ai/AIScrollButton/AIScrollButton.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowDown } 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 scrollButtonVariants = cva(\n 'fixed flex items-center justify-center shadow-lg transition-all',\n {\n variants: {\n variant: {\n default: 'bg-background hover:bg-muted border border-border',\n primary: 'bg-primary/10 hover:bg-primary/90 text-primary-foreground',\n ghost:\n 'bg-card/80 hover:bg-background supports-backdrop-filter:backdrop-blur-sm border border-border',\n },\n size: {\n sm: 'size-8 rounded-full',\n md: 'size-10 rounded-full',\n lg: 'size-12 rounded-xl',\n },\n position: {\n 'bottom-center':\n 'bottom-4 start-1/2 -translate-x-1/2 rtl:translate-x-1/2',\n 'bottom-right': 'bottom-4 end-4',\n 'bottom-left': 'bottom-4 start-4',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n position: 'bottom-center',\n },\n },\n);\n\n// =============================================================================\n// useScrollToBottom Hook\n// =============================================================================\n\nexport interface UseScrollToBottomOptions {\n /** Threshold in pixels from bottom to consider \"at bottom\" */\n threshold?: number;\n /** Scroll behavior */\n behavior?: ScrollBehavior;\n /** Enable auto-scroll mode: tracks whether auto-scroll should fire on content changes */\n autoScroll?: boolean;\n}\n\nexport interface UseScrollToBottomReturn {\n /** Whether the user is at the bottom of the container */\n isAtBottom: boolean;\n /** Scroll to bottom function */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Ref to attach to the scrollable container */\n containerRef: React.RefObject<HTMLDivElement | null>;\n /** Ref to attach to the scroll anchor element */\n anchorRef: React.RefObject<HTMLDivElement | null>;\n /** Whether auto-scroll should fire (only meaningful when autoScroll option is true) */\n shouldAutoScroll: boolean;\n /** Call when user scrolls manually to disable auto-scroll (re-enables when user scrolls back to bottom) */\n handleUserScroll: () => void;\n /** Trigger an auto-scroll if shouldAutoScroll is true (uses requestAnimationFrame) */\n triggerAutoScroll: () => void;\n}\n\nexport function useScrollToBottom({\n threshold = 50,\n behavior = 'smooth',\n autoScroll: autoScrollEnabled = false,\n}: UseScrollToBottomOptions = {}): UseScrollToBottomReturn {\n const containerRef = React.useRef<HTMLDivElement>(null);\n const anchorRef = React.useRef<HTMLDivElement>(null);\n const [isAtBottom, setIsAtBottom] = React.useState(true);\n const [shouldAutoScroll, setShouldAutoScroll] = React.useState(true);\n\n const scrollToBottom = React.useCallback(\n (overrideBehavior?: ScrollBehavior) => {\n const scrollBehavior = overrideBehavior ?? behavior;\n if (anchorRef.current) {\n anchorRef.current.scrollIntoView({ behavior: scrollBehavior });\n } else if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n },\n [behavior],\n );\n\n const checkIsAtBottom = React.useCallback(() => {\n const container = containerRef.current;\n if (!container) return true;\n const { scrollTop, scrollHeight, clientHeight } = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n return distanceFromBottom <= threshold;\n }, [threshold]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleScroll = () => {\n const atBottom = checkIsAtBottom();\n setIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n if (autoScrollEnabled && atBottom) {\n setShouldAutoScroll((prev) => prev || true);\n }\n };\n\n handleScroll();\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n return () => container.removeEventListener('scroll', handleScroll);\n }, [checkIsAtBottom, autoScrollEnabled]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const OwnerResizeObserver =\n container.ownerDocument.defaultView?.ResizeObserver;\n if (!OwnerResizeObserver) return;\n\n const resizeObserver = new OwnerResizeObserver(() => {\n const atBottom = checkIsAtBottom();\n setIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n });\n\n resizeObserver.observe(container);\n return () => resizeObserver.disconnect();\n }, [checkIsAtBottom]);\n\n const handleUserScroll = React.useCallback(() => {\n if (!checkIsAtBottom()) {\n setShouldAutoScroll(false);\n }\n }, [checkIsAtBottom]);\n\n const triggerAutoScroll = React.useCallback(() => {\n if (autoScrollEnabled && shouldAutoScroll) {\n const ownerWindow =\n anchorRef.current?.ownerDocument.defaultView ??\n containerRef.current?.ownerDocument.defaultView;\n ownerWindow?.requestAnimationFrame(() => {\n scrollToBottom('smooth');\n });\n }\n }, [autoScrollEnabled, shouldAutoScroll, scrollToBottom]);\n\n return {\n isAtBottom,\n scrollToBottom,\n containerRef,\n anchorRef,\n shouldAutoScroll,\n handleUserScroll,\n triggerAutoScroll,\n };\n}\n\n// =============================================================================\n// Main AIScrollButton Component\n// =============================================================================\n\nexport interface AIScrollButtonProps\n extends VariantProps<typeof scrollButtonVariants> {\n /** Target scroll element ref */\n scrollRef?: React.RefObject<HTMLElement | null>;\n /** Scrollable container ref */\n containerRef?: React.RefObject<HTMLElement | null>;\n /** Threshold in pixels from bottom to show button */\n threshold?: number;\n /** Click handler (defaults to scrolling to bottom) */\n onClick?: () => void;\n /** Whether to show the button (controlled) */\n visible?: boolean;\n /** Label for accessibility */\n label?: string;\n /** Show unread count badge */\n unreadCount?: number;\n /** Additional class name */\n className?: string;\n}\n\nfunction getIconSize(size: AIScrollButtonProps['size']): string {\n switch (size) {\n case 'sm':\n return 'size-4';\n case 'lg':\n return 'size-6';\n default:\n return 'size-5';\n }\n}\n\nfunction AIScrollButton({\n scrollRef,\n containerRef,\n threshold = 50,\n onClick,\n visible: controlledVisible,\n variant,\n size,\n position,\n label = 'Scroll to bottom',\n unreadCount,\n className,\n}: AIScrollButtonProps) {\n const [internalVisible, setInternalVisible] = React.useState(false);\n\n // Track scroll position if containerRef is provided and no controlled visibility\n React.useEffect(() => {\n if (controlledVisible !== undefined) return;\n\n const container = containerRef?.current;\n if (!container) return;\n\n const handleScroll = () => {\n const { scrollTop, scrollHeight, clientHeight } = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n setInternalVisible(distanceFromBottom > threshold);\n };\n\n handleScroll();\n container.addEventListener('scroll', handleScroll, { passive: true });\n return () => container.removeEventListener('scroll', handleScroll);\n }, [containerRef, threshold, controlledVisible]);\n\n const isVisible = controlledVisible ?? internalVisible;\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (scrollRef?.current) {\n scrollRef.current.scrollIntoView({ behavior: 'smooth' });\n } else if (containerRef?.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n };\n\n const iconSize = getIconSize(size);\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-scroll-button\"\n initial={{ opacity: 0, y: 20, scale: 0.9 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 20, scale: 0.9 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n scrollButtonVariants({ variant, size, position }),\n 'motion-reduce:transition-none',\n className,\n )}\n >\n <Button\n appearance=\"ghost\"\n variant=\"secondary\"\n size=\"icon\"\n onClick={handleClick}\n className=\"size-full rounded-full\"\n aria-label={label}\n >\n <ArrowDown\n className={cn(\n iconSize,\n variant === 'primary'\n ? 'text-primary-foreground'\n : 'text-muted-foreground',\n )}\n />\n </Button>\n\n {/* Unread count badge */}\n {unreadCount !== undefined && unreadCount > 0 && (\n <motion.div\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={{ type: 'spring', stiffness: 500, damping: 30 }}\n className=\"absolute -top-1 -end-1 flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full bg-destructive text-primary-foreground text-xs font-medium motion-reduce:transition-none\"\n aria-label={`${unreadCount > 99 ? '99+' : unreadCount} new messages`}\n >\n {unreadCount > 99 ? '99+' : unreadCount}\n </motion.div>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAIScrollButton.displayName = 'AIScrollButton';\n\n// =============================================================================\n// Scroll Anchor Component\n// =============================================================================\n\nexport interface AIScrollAnchorProps {\n /** Ref to expose for scrolling */\n ref?: React.Ref<HTMLDivElement>;\n /** Additional class name */\n className?: string;\n}\n\nconst AIScrollAnchor = React.forwardRef<HTMLDivElement, AIScrollAnchorProps>(\n ({ className }, ref) => {\n return (\n <div ref={ref} className={cn('h-px w-full', className)} aria-hidden />\n );\n },\n);\n\nAIScrollAnchor.displayName = 'AIScrollAnchor';\n\nexport { AIScrollAnchor, AIScrollButton, scrollButtonVariants };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index155.js","names":[],"sources":["../src/components/organisms/ai/AIScrollButton/AIScrollButton.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority';\nimport { ArrowDown } 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 scrollButtonVariants = cva(\n 'fixed flex items-center justify-center shadow-lg transition-all',\n {\n variants: {\n variant: {\n default: 'bg-background hover:bg-muted border border-border',\n primary: 'bg-primary/10 hover:bg-primary/90 text-primary-foreground',\n ghost:\n 'bg-card/80 hover:bg-background supports-backdrop-filter:backdrop-blur-sm border border-border',\n },\n size: {\n sm: 'size-8 rounded-full',\n md: 'size-10 rounded-full',\n lg: 'size-12 rounded-xl',\n },\n position: {\n 'bottom-center':\n 'bottom-4 start-1/2 -translate-x-1/2 rtl:translate-x-1/2',\n 'bottom-right': 'bottom-4 end-4',\n 'bottom-left': 'bottom-4 start-4',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n position: 'bottom-center',\n },\n },\n);\n\n// =============================================================================\n// useScrollToBottom Hook\n// =============================================================================\n\nexport interface UseScrollToBottomOptions {\n /** Threshold in pixels from bottom to consider \"at bottom\" */\n threshold?: number;\n /** Scroll behavior */\n behavior?: ScrollBehavior;\n /** Enable auto-scroll mode: tracks whether auto-scroll should fire on content changes */\n autoScroll?: boolean;\n}\n\nexport interface UseScrollToBottomReturn {\n /** Whether the user is at the bottom of the container */\n isAtBottom: boolean;\n /** Scroll to bottom function */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Ref to attach to the scrollable container */\n containerRef: React.RefObject<HTMLDivElement | null>;\n /** Ref to attach to the scroll anchor element */\n anchorRef: React.RefObject<HTMLDivElement | null>;\n /** Whether auto-scroll should fire (only meaningful when autoScroll option is true) */\n shouldAutoScroll: boolean;\n /** Call when user scrolls manually to disable auto-scroll (re-enables when user scrolls back to bottom) */\n handleUserScroll: () => void;\n /** Trigger an auto-scroll if shouldAutoScroll is true (uses requestAnimationFrame) */\n triggerAutoScroll: () => void;\n}\n\nexport function useScrollToBottom({\n threshold = 50,\n behavior = 'smooth',\n autoScroll: autoScrollEnabled = false,\n}: UseScrollToBottomOptions = {}): UseScrollToBottomReturn {\n const containerRef = React.useRef<HTMLDivElement>(null);\n const anchorRef = React.useRef<HTMLDivElement>(null);\n const [isAtBottom, setIsAtBottom] = React.useState(true);\n const [shouldAutoScroll, setShouldAutoScroll] = React.useState(true);\n\n const scrollToBottom = React.useCallback(\n (overrideBehavior?: ScrollBehavior) => {\n const scrollBehavior = overrideBehavior ?? behavior;\n if (anchorRef.current) {\n anchorRef.current.scrollIntoView({ behavior: scrollBehavior });\n } else if (containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n },\n [behavior],\n );\n\n const checkIsAtBottom = React.useCallback(() => {\n const container = containerRef.current;\n if (!container) return true;\n const { scrollTop, scrollHeight, clientHeight } = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n return distanceFromBottom <= threshold;\n }, [threshold]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleScroll = () => {\n const atBottom = checkIsAtBottom();\n setIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n if (autoScrollEnabled && atBottom) {\n setShouldAutoScroll((prev) => prev || true);\n }\n };\n\n handleScroll();\n\n container.addEventListener('scroll', handleScroll, { passive: true });\n return () => container.removeEventListener('scroll', handleScroll);\n }, [checkIsAtBottom, autoScrollEnabled]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const OwnerResizeObserver =\n container.ownerDocument.defaultView?.ResizeObserver;\n if (!OwnerResizeObserver) return;\n\n const resizeObserver = new OwnerResizeObserver(() => {\n const atBottom = checkIsAtBottom();\n setIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n });\n\n resizeObserver.observe(container);\n return () => resizeObserver.disconnect();\n }, [checkIsAtBottom]);\n\n const handleUserScroll = React.useCallback(() => {\n if (!checkIsAtBottom()) {\n setShouldAutoScroll(false);\n }\n }, [checkIsAtBottom]);\n\n const triggerAutoScroll = React.useCallback(() => {\n if (autoScrollEnabled && shouldAutoScroll) {\n const ownerWindow =\n anchorRef.current?.ownerDocument.defaultView ??\n containerRef.current?.ownerDocument.defaultView;\n ownerWindow?.requestAnimationFrame(() => {\n scrollToBottom('smooth');\n });\n }\n }, [autoScrollEnabled, shouldAutoScroll, scrollToBottom]);\n\n return {\n isAtBottom,\n scrollToBottom,\n containerRef,\n anchorRef,\n shouldAutoScroll,\n handleUserScroll,\n triggerAutoScroll,\n };\n}\n\n// =============================================================================\n// Main AIScrollButton Component\n// =============================================================================\n\nexport interface AIScrollButtonProps\n extends VariantProps<typeof scrollButtonVariants> {\n /** Target scroll element ref */\n scrollRef?: React.RefObject<HTMLElement | null>;\n /** Scrollable container ref */\n containerRef?: React.RefObject<HTMLElement | null>;\n /** Threshold in pixels from bottom to show button */\n threshold?: number;\n /** Click handler (defaults to scrolling to bottom) */\n onClick?: () => void;\n /** Whether to show the button (controlled) */\n visible?: boolean;\n /** Label for accessibility */\n label?: string;\n /** Show unread count badge */\n unreadCount?: number;\n /** Additional class name */\n className?: string;\n}\n\nfunction getIconSize(size: AIScrollButtonProps['size']): string {\n switch (size) {\n case 'sm':\n return 'size-4';\n case 'lg':\n return 'size-6';\n default:\n return 'size-5';\n }\n}\n\nfunction AIScrollButton({\n scrollRef,\n containerRef,\n threshold = 50,\n onClick,\n visible: controlledVisible,\n variant,\n size,\n position,\n label = 'Scroll to bottom',\n unreadCount,\n className,\n}: AIScrollButtonProps) {\n const [internalVisible, setInternalVisible] = React.useState(false);\n\n // Track scroll position if containerRef is provided and no controlled visibility\n React.useEffect(() => {\n if (controlledVisible !== undefined) return;\n\n const container = containerRef?.current;\n if (!container) return;\n\n const handleScroll = () => {\n const { scrollTop, scrollHeight, clientHeight } = container;\n const distanceFromBottom = scrollHeight - scrollTop - clientHeight;\n setInternalVisible(distanceFromBottom > threshold);\n };\n\n handleScroll();\n container.addEventListener('scroll', handleScroll, { passive: true });\n return () => container.removeEventListener('scroll', handleScroll);\n }, [containerRef, threshold, controlledVisible]);\n\n const isVisible = controlledVisible ?? internalVisible;\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (scrollRef?.current) {\n scrollRef.current.scrollIntoView({ behavior: 'smooth' });\n } else if (containerRef?.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight;\n }\n };\n\n const iconSize = getIconSize(size);\n\n return (\n <AnimatePresence>\n {isVisible && (\n <motion.div\n data-slot=\"ai-scroll-button\"\n initial={{ opacity: 0, y: 20, scale: 0.9 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n exit={{ opacity: 0, y: 20, scale: 0.9 }}\n transition={{ duration: 0.2, ease: [0.4, 0, 0.2, 1] }}\n className={cn(\n scrollButtonVariants({ variant, size, position }),\n 'motion-reduce:transition-none',\n className,\n )}\n >\n <Button\n appearance=\"ghost\"\n variant=\"secondary\"\n size=\"icon\"\n onClick={handleClick}\n className=\"size-full rounded-full\"\n aria-label={label}\n >\n <ArrowDown\n className={cn(\n iconSize,\n variant === 'primary'\n ? 'text-primary-foreground'\n : 'text-muted-foreground',\n )}\n />\n </Button>\n\n {/* Unread count badge */}\n {unreadCount !== undefined && unreadCount > 0 && (\n <motion.div\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={{ type: 'spring', stiffness: 500, damping: 30 }}\n className=\"absolute -top-1 -end-1 flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full bg-destructive text-destructive-foreground text-xs font-medium motion-reduce:transition-none\"\n aria-label={`${unreadCount > 99 ? '99+' : unreadCount} new messages`}\n >\n {unreadCount > 99 ? '99+' : unreadCount}\n </motion.div>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n );\n}\n\nAIScrollButton.displayName = 'AIScrollButton';\n\n// =============================================================================\n// Scroll Anchor Component\n// =============================================================================\n\nexport interface AIScrollAnchorProps {\n /** Ref to expose for scrolling */\n ref?: React.Ref<HTMLDivElement>;\n /** Additional class name */\n className?: string;\n}\n\nconst AIScrollAnchor = React.forwardRef<HTMLDivElement, AIScrollAnchorProps>(\n ({ className }, ref) => {\n return (\n <div ref={ref} className={cn('h-px w-full', className)} aria-hidden />\n );\n },\n);\n\nAIScrollAnchor.displayName = 'AIScrollAnchor';\n\nexport { AIScrollAnchor, AIScrollButton, scrollButtonVariants };\n"],"mappings":"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"}
package/dist/index173.js CHANGED
@@ -9,7 +9,7 @@ var sourceVariants = cva("inline-flex items-center gap-1.5 transition-colors cur
9
9
  variants: { variant: {
10
10
  default: "text-sm text-primary hover:text-primary underline underline-offset-2 rounded-sm",
11
11
  pill: "px-2.5 py-1 rounded-full bg-accent hover:bg-surface-2 text-xs text-secondary-foreground delight-selection-press",
12
- minimal: "text-xs text-text-quaternary hover:text-secondary-foreground rounded-sm"
12
+ minimal: "text-xs text-secondary-foreground hover:text-foreground rounded-sm"
13
13
  } },
14
14
  defaultVariants: { variant: "default" }
15
15
  });
@@ -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-text-quaternary hover:text-secondary-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":"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"}
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":"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"}
package/dist/index184.js CHANGED
@@ -52,7 +52,7 @@ function AIToolCallHeader({ toolName, status, isOpen, onToggle, className }) {
52
52
  className: "text-sm font-medium text-foreground",
53
53
  children: toolName
54
54
  }), /* @__PURE__ */ jsx("span", {
55
- className: "text-xs text-text-quaternary",
55
+ className: "text-xs text-secondary-foreground",
56
56
  children: {
57
57
  pending: "Pending",
58
58
  running: "Running...",
@@ -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-text-quaternary\">\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":"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"}
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":"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"}
package/dist/index188.js CHANGED
@@ -10,10 +10,10 @@ var tooltipVariants = cva("data-open:animate-in data-open:fade-in-0 data-open:zo
10
10
  variant: {
11
11
  primary: "bg-foreground text-background",
12
12
  secondary: "bg-secondary text-secondary-foreground border border-border shadow-sm",
13
- info: "bg-info text-primary-foreground",
14
- success: "bg-success text-primary-foreground",
15
- warning: "bg-warning text-primary-foreground",
16
- destructive: "bg-destructive text-primary-foreground"
13
+ info: "bg-info text-info-foreground",
14
+ success: "bg-success text-success-foreground",
15
+ warning: "bg-warning text-warning-foreground",
16
+ destructive: "bg-destructive text-destructive-foreground"
17
17
  },
18
18
  shape: {
19
19
  default: "rounded-md",