@devalok/shilp-sutra 0.49.0 → 0.49.2

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 (162) hide show
  1. package/dist/_chunks/badge-group.js +41 -41
  2. package/dist/_chunks/badge-group.js.map +1 -1
  3. package/dist/_chunks/block-shell.js +1 -1
  4. package/dist/_chunks/chart-container.js +1 -1
  5. package/dist/_chunks/chat.js +6 -15
  6. package/dist/_chunks/chat.js.map +1 -1
  7. package/dist/_chunks/colors.js +2 -2
  8. package/dist/_chunks/document-preview.js +4 -4
  9. package/dist/_chunks/emoji-suggestion.js +5 -5
  10. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  11. package/dist/_chunks/emoji.js +1 -1
  12. package/dist/_chunks/field-state.js +1 -1
  13. package/dist/_chunks/grid-lines.js +1 -1
  14. package/dist/_chunks/image-preview.js +7 -7
  15. package/dist/_chunks/keybinding.js +1 -1
  16. package/dist/_chunks/link-context.js +1 -1
  17. package/dist/_chunks/mention-suggestion.js +2 -2
  18. package/dist/_chunks/mention.js +1 -1
  19. package/dist/_chunks/motion-provider.js +1 -1
  20. package/dist/_chunks/normalize-icon.js +1 -1
  21. package/dist/_chunks/oauth-button.js +2 -2
  22. package/dist/_chunks/popover.js +17 -17
  23. package/dist/_chunks/primitives.js +1 -1
  24. package/dist/_chunks/shared.js +5 -5
  25. package/dist/_chunks/success.js +5 -8
  26. package/dist/_chunks/success.js.map +1 -1
  27. package/dist/_chunks/tiptap.js +11 -11
  28. package/dist/_chunks/tooltip.js +1 -1
  29. package/dist/_chunks/tree-view.js +2 -2
  30. package/dist/_chunks/use-calendar.js +1 -1
  31. package/dist/_chunks/use-controllable-open.js +1 -1
  32. package/dist/_chunks/vendor-client.js +1 -1
  33. package/dist/_chunks/vendor-utils.js +1 -1
  34. package/dist/ai/block-renderer.js +8 -11
  35. package/dist/ai/block-renderer.js.map +1 -1
  36. package/dist/ai/blocks/error.js +1 -1
  37. package/dist/ai/blocks/index.js +2 -2
  38. package/dist/ai/blocks/text.js +1 -1
  39. package/dist/ai/command-bar.d.ts.map +1 -1
  40. package/dist/ai/command-bar.js +8 -39
  41. package/dist/ai/command-bar.js.map +1 -1
  42. package/dist/ai/conversation.js +4 -10
  43. package/dist/ai/conversation.js.map +1 -1
  44. package/dist/ai/devadoot-icon.d.ts.map +1 -1
  45. package/dist/ai/index.js +10 -41
  46. package/dist/ai/index.js.map +1 -1
  47. package/dist/composed/activity-feed.js +3 -3
  48. package/dist/composed/activity-feed.js.map +1 -1
  49. package/dist/composed/avatar-group.js +3 -3
  50. package/dist/composed/avatar-group.js.map +1 -1
  51. package/dist/composed/bulk-action-bar.js +1 -1
  52. package/dist/composed/command-palette.js +6 -6
  53. package/dist/composed/content-card.js +1 -1
  54. package/dist/composed/date-picker/index.js +10 -10
  55. package/dist/composed/emoji-picker.js +4 -4
  56. package/dist/composed/empty-state.d.ts.map +1 -1
  57. package/dist/composed/empty-state.js +23 -33
  58. package/dist/composed/empty-state.js.map +1 -1
  59. package/dist/composed/extensions/emoji-node.js +1 -1
  60. package/dist/composed/extensions/emoji-suggestion.js +1 -1
  61. package/dist/composed/file-preview.js +14 -14
  62. package/dist/composed/filter-bar.js +1 -1
  63. package/dist/composed/multi-select-popover.js +3 -3
  64. package/dist/composed/priority-indicator.js +1 -1
  65. package/dist/composed/responsive-modal.js +21 -21
  66. package/dist/composed/rich-chat-input.js +445 -445
  67. package/dist/composed/rich-chat-input.js.map +1 -1
  68. package/dist/composed/rich-text-editor.js +28 -28
  69. package/dist/composed/schedule-view.js +1 -1
  70. package/dist/composed/schedule-view.js.map +1 -1
  71. package/dist/composed/status-badge.js +1 -1
  72. package/dist/motion/index.js +2 -2
  73. package/dist/shell/bottom-navbar.js +3 -3
  74. package/dist/shell/bottom-navbar.js.map +1 -1
  75. package/dist/shell/index.js +2 -2
  76. package/dist/shell/link-context.js +2 -2
  77. package/dist/shell/notification-center.js +3 -3
  78. package/dist/shell/sidebar.js +2 -2
  79. package/dist/shell/top-bar.js +1 -1
  80. package/dist/ui/accordion.js +14 -14
  81. package/dist/ui/alert-dialog.js +31 -31
  82. package/dist/ui/alert.js +1 -1
  83. package/dist/ui/aspect-ratio.js +1 -1
  84. package/dist/ui/autocomplete.js +7 -7
  85. package/dist/ui/avatar.d.ts.map +1 -1
  86. package/dist/ui/avatar.js +52 -67
  87. package/dist/ui/avatar.js.map +1 -1
  88. package/dist/ui/badge-group.js +1 -1
  89. package/dist/ui/badge.d.ts +2 -0
  90. package/dist/ui/badge.d.ts.map +1 -1
  91. package/dist/ui/badge.js +1 -1
  92. package/dist/ui/banner.js +1 -1
  93. package/dist/ui/breadcrumb.js +1 -1
  94. package/dist/ui/button.js +9 -9
  95. package/dist/ui/button.js.map +1 -1
  96. package/dist/ui/card.js +1 -1
  97. package/dist/ui/charts/area-chart.js +6 -6
  98. package/dist/ui/charts/bar-chart.js +6 -6
  99. package/dist/ui/charts/chart-container.js +1 -1
  100. package/dist/ui/charts/gauge-chart.js +2 -2
  101. package/dist/ui/charts/index.js +2 -2
  102. package/dist/ui/charts/line-chart.js +6 -6
  103. package/dist/ui/charts/pie-chart.js +5 -5
  104. package/dist/ui/charts/radar-chart.js +5 -5
  105. package/dist/ui/charts/sparkline.js +2 -2
  106. package/dist/ui/chat/index.js +2 -2
  107. package/dist/ui/checkbox.js +5 -5
  108. package/dist/ui/collapsible.js +4 -4
  109. package/dist/ui/color-input.js +4 -4
  110. package/dist/ui/combobox.js +8 -8
  111. package/dist/ui/combobox.js.map +1 -1
  112. package/dist/ui/context-menu.js +41 -41
  113. package/dist/ui/dialog.js +25 -25
  114. package/dist/ui/dot.d.ts.map +1 -1
  115. package/dist/ui/dot.js +24 -32
  116. package/dist/ui/dot.js.map +1 -1
  117. package/dist/ui/dropdown-menu.js +51 -51
  118. package/dist/ui/hover-card.js +9 -9
  119. package/dist/ui/icon-button.js +1 -1
  120. package/dist/ui/index.js +6 -6
  121. package/dist/ui/input.js +2 -2
  122. package/dist/ui/label.js +1 -1
  123. package/dist/ui/lib/utils.js +4 -4
  124. package/dist/ui/link.js +1 -1
  125. package/dist/ui/menubar.js +53 -53
  126. package/dist/ui/navigation-menu.js +27 -27
  127. package/dist/ui/number-input.js +2 -2
  128. package/dist/ui/oauth-button/index.js +2 -2
  129. package/dist/ui/pagination.js +1 -1
  130. package/dist/ui/popover.js +2 -2
  131. package/dist/ui/progress.js +2 -2
  132. package/dist/ui/radio.js +2 -2
  133. package/dist/ui/segmented-control.js +1 -1
  134. package/dist/ui/select.js +35 -35
  135. package/dist/ui/separator.js +1 -1
  136. package/dist/ui/sheet.js +22 -22
  137. package/dist/ui/sidebar.js +2 -2
  138. package/dist/ui/skeleton.js +1 -1
  139. package/dist/ui/slider.js +8 -8
  140. package/dist/ui/slider.js.map +1 -1
  141. package/dist/ui/split-button.js +3 -3
  142. package/dist/ui/stat-card.d.ts +2 -0
  143. package/dist/ui/stat-card.d.ts.map +1 -1
  144. package/dist/ui/stat-card.js +94 -126
  145. package/dist/ui/stat-card.js.map +1 -1
  146. package/dist/ui/stat-flash.js +1 -1
  147. package/dist/ui/stepper.js +1 -1
  148. package/dist/ui/surface.js +2 -2
  149. package/dist/ui/switch.js +2 -2
  150. package/dist/ui/table-row-link.js +1 -1
  151. package/dist/ui/tabs.js +13 -13
  152. package/dist/ui/text.js +1 -1
  153. package/dist/ui/textarea.js +2 -2
  154. package/dist/ui/toggle-group.js +1 -1
  155. package/dist/ui/toggle.js +2 -2
  156. package/dist/ui/tooltip.js +16 -16
  157. package/dist/ui/tree-view/index.js +2 -2
  158. package/llms.txt +1 -1
  159. package/mcp-manifest.json +1 -1
  160. package/package.json +1 -1
  161. package/skill/SKILL.md +1 -1
  162. package/skill/references/components.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","names":[],"sources":["../../src/ai/conversation.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconCircle,\n IconCircleCheck,\n IconCircleX,\n IconLoader2,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../motion/motion-provider'\nimport { Icon } from '../ui/icon'\nimport { IconProvider } from '../ui/icon-context'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { durations,springs } from '../ui/lib/motion'\nimport { normalizeIcon } from '../ui/lib/normalize-icon'\nimport { cn } from '../ui/lib/utils'\nimport { useAICommand } from './ai-command-provider'\nimport { BlockRenderer } from './block-renderer'\nimport type {\n BlockComponentProps,\n ConversationMessage,\n ProcessingStep,\n} from './types'\n\n// ── Props ────────────────────────────────────────────────────────────────────\n\n/**\n * Renders a scrollable AI conversation thread with support for streaming\n * processing steps, custom content blocks, and action confirmations.\n */\nexport interface AIConversationProps {\n /** Ordered conversation messages (user + assistant turns). */\n messages: ConversationMessage[]\n /** When true, shows a live processing indicator below the last message. */\n isProcessing?: boolean\n /** Granular processing steps shown during the processing state. */\n processingSteps?: ProcessingStep[]\n /** Agent identity displayed as a header on assistant messages. */\n agent?: { name: string; icon?: IconInput }\n /** Called when user interacts with an action block (confirm/cancel/undo). */\n onAction?: (actionId: string, type: 'confirm' | 'cancel' | 'undo') => void\n /** Map of custom block type names to React components for extensible rendering. */\n customBlocks?: Record<string, React.ComponentType<BlockComponentProps<any>>>\n /** Max height of the conversation container (CSS value). Enables scrolling. */\n maxHeight?: string | number\n /** Auto-scroll to bottom on new messages. @default true */\n autoScroll?: boolean\n className?: string\n}\n\n// ── Step status icon ─────────────────────────────────────────────────────────\n\nfunction StepStatusIcon({ status }: { status: ProcessingStep['status'] }) {\n switch (status) {\n case 'done':\n return (\n <Icon icon={IconCircleCheck} size=\"sm\" className=\"text-success-11\" />\n )\n case 'active':\n return (\n <motion.span\n className=\"inline-flex items-center justify-center text-accent-9\"\n animate={{ rotate: 360 }}\n transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}\n aria-hidden=\"true\"\n >\n <Icon icon={IconLoader2} size=\"sm\" />\n </motion.span>\n )\n case 'error':\n return (\n <Icon icon={IconCircleX} size=\"sm\" className=\"text-error-11\" />\n )\n case 'pending':\n default:\n return (\n <Icon icon={IconCircle} size=\"sm\" className=\"text-surface-fg-subtle opacity-50\" />\n )\n }\n}\n\n// ── Agent header ─────────────────────────────────────────────────────────────\n\nfunction AgentHeader({\n name,\n icon,\n}: {\n name: string\n icon?: IconInput\n}) {\n return (\n <div className=\"flex items-center gap-ds-02b mb-ds-03\">\n {icon && <span className=\"h-4 w-4 flex items-center justify-center\"><IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider></span>}\n <span className=\"text-ds-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {name}\n </span>\n </div>\n )\n}\n\n// ── User message ─────────────────────────────────────────────────────────────\n\nfunction UserMessage({\n message,\n reducedMotion,\n}: {\n message: ConversationMessage\n reducedMotion: boolean\n}) {\n if (reducedMotion) {\n return (\n <div className=\"bg-surface-raised rounded-surface px-ds-05 py-ds-04\">\n <p className=\"text-ds-sm text-surface-fg\">{message.content}</p>\n </div>\n )\n }\n\n return (\n <motion.div\n className=\"bg-surface-raised rounded-surface px-ds-05 py-ds-04\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n >\n <p className=\"text-ds-sm text-surface-fg\">{message.content}</p>\n </motion.div>\n )\n}\n\n// ── Assistant message ────────────────────────────────────────────────────────\n\nfunction AssistantMessage({\n message,\n agent,\n onAction,\n customBlocks,\n}: {\n message: ConversationMessage\n agent: { name: string; icon?: IconInput }\n onAction?: (actionId: string, type: 'confirm' | 'cancel' | 'undo') => void\n customBlocks?: Record<string, React.ComponentType<BlockComponentProps<any>>>\n}) {\n return (\n <div>\n <AgentHeader name={agent.name} icon={agent.icon} />\n {message.blocks && (\n <BlockRenderer\n blocks={message.blocks}\n onAction={onAction}\n customBlocks={customBlocks}\n />\n )}\n </div>\n )\n}\n\n// ── Processing indicator ─────────────────────────────────────────────────────\n\nfunction ProcessingIndicator({\n steps,\n agent,\n reducedMotion,\n}: {\n steps?: ProcessingStep[]\n agent: { name: string; icon?: IconInput }\n reducedMotion: boolean\n}) {\n // Step visualization\n if (steps && steps.length > 0) {\n return (\n <div role=\"status\" aria-busy=\"true\" aria-label=\"Processing\">\n <AgentHeader name={agent.name} icon={agent.icon} />\n <div className=\"flex flex-col gap-ds-02b\">\n {steps.map((step, i) => (\n <motion.div\n key={step.id}\n className=\"flex items-center gap-ds-02b\"\n initial={reducedMotion ? undefined : { opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : { duration: durations.moderate01b, delay: i * 0.05 }\n }\n >\n <StepStatusIcon status={step.status} />\n <span\n className={cn(\n 'text-ds-sm',\n step.status === 'pending'\n ? 'text-surface-fg-subtle'\n : 'text-surface-fg',\n )}\n >\n {step.label}\n </span>\n </motion.div>\n ))}\n </div>\n </div>\n )\n }\n\n // Breathing dots\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label={`${agent.name} is processing`}\n >\n <AgentHeader name={agent.name} icon={agent.icon} />\n <div className=\"flex items-center gap-ds-02\">\n {!reducedMotion &&\n [0, 1, 2].map((i) => (\n <motion.span\n key={i}\n className=\"h-1.5 w-1.5 rounded-pill bg-accent-9\"\n animate={{ scale: [1, 1.4, 1], opacity: [0.4, 1, 0.4] }}\n transition={{\n duration: 1.4,\n repeat: Infinity,\n ease: 'easeInOut',\n delay: i * 0.16,\n }}\n />\n ))}\n <span className=\"ml-ds-02b text-ds-sm text-surface-fg-subtle\">\n {agent.name} is thinking...\n </span>\n </div>\n </div>\n )\n}\n\n// ── Scroll-to-bottom pill ────────────────────────────────────────────────────\n\nfunction ScrollToBottomPill({ onClick }: { onClick: () => void }) {\n return (\n <motion.button\n role=\"button\"\n aria-label=\"Scroll to latest response\"\n className=\"absolute bottom-ds-04 left-1/2 -translate-x-1/2 z-10 flex items-center gap-ds-02 bg-accent-9 text-accent-fg text-ds-xs font-medium rounded-pill px-ds-04 py-ds-02 shadow-floating\"\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 8 }}\n transition={springs.snappy}\n onClick={onClick}\n >\n {'\\u2193'} New response\n </motion.button>\n )\n}\n\n// ── Default agent ────────────────────────────────────────────────────────────\n\nconst DEFAULT_AGENT = { name: 'Assistant' }\n\n// ── AIConversation ───────────────────────────────────────────────────────────\n\nconst AIConversation = React.forwardRef<HTMLDivElement, AIConversationProps>(({\n messages,\n isProcessing = false,\n processingSteps,\n agent: agentProp,\n onAction,\n customBlocks,\n maxHeight,\n autoScroll = true,\n className,\n}, ref) => {\n const ctx = useAICommand()\n const { reducedMotion } = useMotion()\n\n // Resolve agent: prop > context > default\n const agent = agentProp ?? ctx?.agent ?? DEFAULT_AGENT\n\n // Resolve onAction: prop > context\n const resolvedOnAction = onAction ?? ctx?.onAction\n\n // Resolve customBlocks: prop wins over context\n const resolvedCustomBlocks = React.useMemo(() => {\n const contextBlocks = ctx?.customBlocks ?? {}\n return { ...contextBlocks, ...customBlocks }\n }, [ctx?.customBlocks, customBlocks])\n\n // ── Auto-scroll ──────────────────────────────────────────────────────────\n\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const sentinelRef = React.useRef<HTMLDivElement>(null)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const [hasNewContent, setHasNewContent] = React.useState(false)\n const prevMessageCount = React.useRef(messages.length)\n const prevProcessing = React.useRef(isProcessing)\n\n // Track if user is scrolled to bottom via IntersectionObserver\n React.useEffect(() => {\n if (!autoScroll || !sentinelRef.current) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setIsAtBottom(entry.isIntersecting)\n if (entry.isIntersecting) {\n setHasNewContent(false)\n }\n },\n {\n root: scrollRef.current,\n threshold: 0.1,\n },\n )\n\n observer.observe(sentinelRef.current)\n return () => observer.disconnect()\n }, [autoScroll])\n\n // Auto-scroll or show pill on new content\n React.useEffect(() => {\n const contentChanged =\n messages.length !== prevMessageCount.current ||\n isProcessing !== prevProcessing.current\n\n prevMessageCount.current = messages.length\n prevProcessing.current = isProcessing\n\n if (!contentChanged || !autoScroll) return\n\n if (isAtBottom) {\n sentinelRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n } else {\n setHasNewContent(true)\n }\n }, [messages, isProcessing, isAtBottom, autoScroll])\n\n const scrollToBottom = React.useCallback(() => {\n sentinelRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n setIsAtBottom(true)\n setHasNewContent(false)\n }, [])\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <div ref={ref} className={cn('relative', className)}>\n <div\n ref={scrollRef}\n className=\"flex flex-col gap-ds-05 overflow-y-auto\"\n aria-live=\"polite\"\n style={\n maxHeight\n ? {\n maxHeight:\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,\n }\n : undefined\n }\n >\n {messages.map((msg) =>\n msg.role === 'user' ? (\n <UserMessage\n key={msg.id}\n message={msg}\n reducedMotion={reducedMotion}\n />\n ) : (\n <AssistantMessage\n key={msg.id}\n message={msg}\n agent={agent}\n onAction={resolvedOnAction}\n customBlocks={resolvedCustomBlocks}\n />\n ),\n )}\n {isProcessing && (\n <ProcessingIndicator\n steps={processingSteps}\n agent={agent}\n reducedMotion={reducedMotion}\n />\n )}\n <div ref={sentinelRef} className=\"h-px\" />\n </div>\n <AnimatePresence>\n {!isAtBottom && hasNewContent && (\n <ScrollToBottomPill onClick={scrollToBottom} />\n )}\n </AnimatePresence>\n </div>\n )\n})\nAIConversation.displayName = 'AIConversation'\n\nexport { AIConversation }\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversation.js","names":[],"sources":["../../src/ai/conversation.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconCircle,\n IconCircleCheck,\n IconCircleX,\n IconLoader2,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../motion/motion-provider'\nimport { Icon } from '../ui/icon'\nimport { IconProvider } from '../ui/icon-context'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { durations,springs } from '../ui/lib/motion'\nimport { normalizeIcon } from '../ui/lib/normalize-icon'\nimport { cn } from '../ui/lib/utils'\nimport { useAICommand } from './ai-command-provider'\nimport { BlockRenderer } from './block-renderer'\nimport type {\n BlockComponentProps,\n ConversationMessage,\n ProcessingStep,\n} from './types'\n\n// ── Props ────────────────────────────────────────────────────────────────────\n\n/**\n * Renders a scrollable AI conversation thread with support for streaming\n * processing steps, custom content blocks, and action confirmations.\n */\nexport interface AIConversationProps {\n /** Ordered conversation messages (user + assistant turns). */\n messages: ConversationMessage[]\n /** When true, shows a live processing indicator below the last message. */\n isProcessing?: boolean\n /** Granular processing steps shown during the processing state. */\n processingSteps?: ProcessingStep[]\n /** Agent identity displayed as a header on assistant messages. */\n agent?: { name: string; icon?: IconInput }\n /** Called when user interacts with an action block (confirm/cancel/undo). */\n onAction?: (actionId: string, type: 'confirm' | 'cancel' | 'undo') => void\n /** Map of custom block type names to React components for extensible rendering. */\n customBlocks?: Record<string, React.ComponentType<BlockComponentProps<any>>>\n /** Max height of the conversation container (CSS value). Enables scrolling. */\n maxHeight?: string | number\n /** Auto-scroll to bottom on new messages. @default true */\n autoScroll?: boolean\n className?: string\n}\n\n// ── Step status icon ─────────────────────────────────────────────────────────\n\nfunction StepStatusIcon({ status }: { status: ProcessingStep['status'] }) {\n switch (status) {\n case 'done':\n return (\n <Icon icon={IconCircleCheck} size=\"sm\" className=\"text-success-11\" />\n )\n case 'active':\n return (\n <motion.span\n className=\"inline-flex items-center justify-center text-accent-9\"\n animate={{ rotate: 360 }}\n transition={{ repeat: Infinity, duration: 1, ease: 'linear' }}\n aria-hidden=\"true\"\n >\n <Icon icon={IconLoader2} size=\"sm\" />\n </motion.span>\n )\n case 'error':\n return (\n <Icon icon={IconCircleX} size=\"sm\" className=\"text-error-11\" />\n )\n case 'pending':\n default:\n return (\n <Icon icon={IconCircle} size=\"sm\" className=\"text-surface-fg-subtle opacity-50\" />\n )\n }\n}\n\n// ── Agent header ─────────────────────────────────────────────────────────────\n\nfunction AgentHeader({\n name,\n icon,\n}: {\n name: string\n icon?: IconInput\n}) {\n return (\n <div className=\"flex items-center gap-ds-02b mb-ds-03\">\n {icon && <span className=\"h-4 w-4 flex items-center justify-center\"><IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider></span>}\n <span className=\"text-ds-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {name}\n </span>\n </div>\n )\n}\n\n// ── User message ─────────────────────────────────────────────────────────────\n\nfunction UserMessage({\n message,\n reducedMotion,\n}: {\n message: ConversationMessage\n reducedMotion: boolean\n}) {\n if (reducedMotion) {\n return (\n <div className=\"bg-surface-raised rounded-surface px-ds-05 py-ds-04\">\n <p className=\"text-ds-sm text-surface-fg\">{message.content}</p>\n </div>\n )\n }\n\n return (\n <motion.div\n className=\"bg-surface-raised rounded-surface px-ds-05 py-ds-04\"\n initial={{ y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={springs.snappy}\n >\n <p className=\"text-ds-sm text-surface-fg\">{message.content}</p>\n </motion.div>\n )\n}\n\n// ── Assistant message ────────────────────────────────────────────────────────\n\nfunction AssistantMessage({\n message,\n agent,\n onAction,\n customBlocks,\n}: {\n message: ConversationMessage\n agent: { name: string; icon?: IconInput }\n onAction?: (actionId: string, type: 'confirm' | 'cancel' | 'undo') => void\n customBlocks?: Record<string, React.ComponentType<BlockComponentProps<any>>>\n}) {\n return (\n <div>\n <AgentHeader name={agent.name} icon={agent.icon} />\n {message.blocks && (\n <BlockRenderer\n blocks={message.blocks}\n onAction={onAction}\n customBlocks={customBlocks}\n />\n )}\n </div>\n )\n}\n\n// ── Processing indicator ─────────────────────────────────────────────────────\n\nfunction ProcessingIndicator({\n steps,\n agent,\n reducedMotion,\n}: {\n steps?: ProcessingStep[]\n agent: { name: string; icon?: IconInput }\n reducedMotion: boolean\n}) {\n // Step visualization\n if (steps && steps.length > 0) {\n return (\n <div role=\"status\" aria-busy=\"true\" aria-label=\"Processing\">\n <AgentHeader name={agent.name} icon={agent.icon} />\n <div className=\"flex flex-col gap-ds-02b\">\n {steps.map((step, i) => (\n <motion.div\n key={step.id}\n className=\"flex items-center gap-ds-02b\"\n initial={reducedMotion ? undefined : { opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : { duration: durations.moderate01b, delay: i * 0.05 }\n }\n >\n <StepStatusIcon status={step.status} />\n <span\n className={cn(\n 'text-ds-sm',\n step.status === 'pending'\n ? 'text-surface-fg-subtle'\n : 'text-surface-fg',\n )}\n >\n {step.label}\n </span>\n </motion.div>\n ))}\n </div>\n </div>\n )\n }\n\n // Breathing dots\n return (\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-label={`${agent.name} is processing`}\n >\n <AgentHeader name={agent.name} icon={agent.icon} />\n <div className=\"flex items-center gap-ds-02\">\n {!reducedMotion &&\n [0, 1, 2].map((i) => (\n <motion.span\n key={i}\n className=\"h-1.5 w-1.5 rounded-pill bg-accent-9\"\n animate={{ scale: [1, 1.4, 1], opacity: [0.4, 1, 0.4] }}\n transition={{\n duration: 1.4,\n repeat: Infinity,\n ease: 'easeInOut',\n delay: i * 0.16,\n }}\n />\n ))}\n <span className=\"ml-ds-02b text-ds-sm text-surface-fg-subtle\">\n {agent.name} is thinking...\n </span>\n </div>\n </div>\n )\n}\n\n// ── Scroll-to-bottom pill ────────────────────────────────────────────────────\n\nfunction ScrollToBottomPill({ onClick }: { onClick: () => void }) {\n return (\n <motion.button\n role=\"button\"\n aria-label=\"Scroll to latest response\"\n className=\"absolute bottom-ds-04 left-1/2 -translate-x-1/2 z-10 flex items-center gap-ds-02 bg-accent-9 text-accent-fg text-ds-xs font-medium rounded-pill px-ds-04 py-ds-02 shadow-floating\"\n initial={{ y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 8 }}\n transition={springs.snappy}\n onClick={onClick}\n >\n {'\\u2193'} New response\n </motion.button>\n )\n}\n\n// ── Default agent ────────────────────────────────────────────────────────────\n\nconst DEFAULT_AGENT = { name: 'Assistant' }\n\n// ── AIConversation ───────────────────────────────────────────────────────────\n\nconst AIConversation = React.forwardRef<HTMLDivElement, AIConversationProps>(({\n messages,\n isProcessing = false,\n processingSteps,\n agent: agentProp,\n onAction,\n customBlocks,\n maxHeight,\n autoScroll = true,\n className,\n}, ref) => {\n const ctx = useAICommand()\n const { reducedMotion } = useMotion()\n\n // Resolve agent: prop > context > default\n const agent = agentProp ?? ctx?.agent ?? DEFAULT_AGENT\n\n // Resolve onAction: prop > context\n const resolvedOnAction = onAction ?? ctx?.onAction\n\n // Resolve customBlocks: prop wins over context\n const resolvedCustomBlocks = React.useMemo(() => {\n const contextBlocks = ctx?.customBlocks ?? {}\n return { ...contextBlocks, ...customBlocks }\n }, [ctx?.customBlocks, customBlocks])\n\n // ── Auto-scroll ──────────────────────────────────────────────────────────\n\n const scrollRef = React.useRef<HTMLDivElement>(null)\n const sentinelRef = React.useRef<HTMLDivElement>(null)\n const [isAtBottom, setIsAtBottom] = React.useState(true)\n const [hasNewContent, setHasNewContent] = React.useState(false)\n const prevMessageCount = React.useRef(messages.length)\n const prevProcessing = React.useRef(isProcessing)\n\n // Track if user is scrolled to bottom via IntersectionObserver\n React.useEffect(() => {\n if (!autoScroll || !sentinelRef.current) return\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n setIsAtBottom(entry.isIntersecting)\n if (entry.isIntersecting) {\n setHasNewContent(false)\n }\n },\n {\n root: scrollRef.current,\n threshold: 0.1,\n },\n )\n\n observer.observe(sentinelRef.current)\n return () => observer.disconnect()\n }, [autoScroll])\n\n // Auto-scroll or show pill on new content\n React.useEffect(() => {\n const contentChanged =\n messages.length !== prevMessageCount.current ||\n isProcessing !== prevProcessing.current\n\n prevMessageCount.current = messages.length\n prevProcessing.current = isProcessing\n\n if (!contentChanged || !autoScroll) return\n\n if (isAtBottom) {\n sentinelRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n } else {\n setHasNewContent(true)\n }\n }, [messages, isProcessing, isAtBottom, autoScroll])\n\n const scrollToBottom = React.useCallback(() => {\n sentinelRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n setIsAtBottom(true)\n setHasNewContent(false)\n }, [])\n\n // ── Render ───────────────────────────────────────────────────────────────\n\n return (\n <div ref={ref} className={cn('relative', className)}>\n <div\n ref={scrollRef}\n className=\"flex flex-col gap-ds-05 overflow-y-auto\"\n aria-live=\"polite\"\n style={\n maxHeight\n ? {\n maxHeight:\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,\n }\n : undefined\n }\n >\n {messages.map((msg) =>\n msg.role === 'user' ? (\n <UserMessage\n key={msg.id}\n message={msg}\n reducedMotion={reducedMotion}\n />\n ) : (\n <AssistantMessage\n key={msg.id}\n message={msg}\n agent={agent}\n onAction={resolvedOnAction}\n customBlocks={resolvedCustomBlocks}\n />\n ),\n )}\n {isProcessing && (\n <ProcessingIndicator\n steps={processingSteps}\n agent={agent}\n reducedMotion={reducedMotion}\n />\n )}\n <div ref={sentinelRef} className=\"h-px\" />\n </div>\n <AnimatePresence>\n {!isAtBottom && hasNewContent && (\n <ScrollToBottomPill onClick={scrollToBottom} />\n )}\n </AnimatePresence>\n </div>\n )\n})\nAIConversation.displayName = 'AIConversation'\n\nexport { AIConversation }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"devadoot-icon.d.ts","sourceRoot":"","sources":["../../src/ai/devadoot-icon.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAA;AAEzE,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAiCD,QAAA,MAAM,YAAY,0DAIf,iBAAiB,6CAqJlB,CAAA;AAIF,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"devadoot-icon.d.ts","sourceRoot":"","sources":["../../src/ai/devadoot-icon.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAA;AAEzE,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAiCD,QAAA,MAAM,YAAY,0DAIf,iBAAiB,6CAuHlB,CAAA;AAIF,OAAO,EAAE,YAAY,EAAE,CAAA"}
package/dist/ai/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { cn as e } from "../ui/lib/utils.js";
3
- import { n as t } from "../_chunks/motion-provider.js";
3
+ import { useMotion as t } from "../_chunks/motion-provider.js";
4
4
  import { AICommandProvider as n, useAICommand as r } from "./ai-command-provider.js";
5
5
  import { CommandBar as i } from "./command-bar.js";
6
- import { a, i as o, n as s, o as c, r as l, s as u, t as d } from "../_chunks/success.js";
6
+ import { BlockTable as a, ConfirmBlock as o, DividerBlock as s, InfoBlock as c, LoadingBlock as l, StatRowBlock as u, SuccessBlock as d } from "../_chunks/success.js";
7
7
  import { AIConversation as f } from "./conversation.js";
8
8
  import * as p from "react";
9
9
  import { jsx as m, jsxs as h } from "react/jsx-runtime";
@@ -22,7 +22,7 @@ function O(e) {
22
22
  }, [t, e]), b(t, (e) => `rotate(${e}, 16, 16)`);
23
23
  }
24
24
  var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
25
- let { reducedMotion: a } = t(), o = O(n), s = p.useId(), c = p.useId();
25
+ let { reducedMotion: a } = t(), o = O(n), s = p.useId();
26
26
  if (a) return /* @__PURE__ */ m("svg", {
27
27
  viewBox: "0 0 32 32",
28
28
  width: r,
@@ -34,7 +34,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
34
34
  fill: n === "error" ? D : S
35
35
  })
36
36
  });
37
- let l = n === "error" ? {
37
+ let c = n === "error" ? {
38
38
  s1: D,
39
39
  s2: "#FF6B6B",
40
40
  s3: D
@@ -50,7 +50,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
50
50
  s1: S,
51
51
  s2: C,
52
52
  s3: S
53
- }, u = n === "processing" ? 3 : n === "error" ? 2.5 : 0;
53
+ };
54
54
  return /* @__PURE__ */ m("span", {
55
55
  className: e("relative inline-flex items-center justify-center", i),
56
56
  style: {
@@ -80,7 +80,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
80
80
  } } : n === "error" ? { x: { duration: .4 } } : { duration: .3 },
81
81
  style: { overflow: "visible" },
82
82
  children: [
83
- /* @__PURE__ */ h("defs", { children: [/* @__PURE__ */ h(v.linearGradient, {
83
+ /* @__PURE__ */ m("defs", { children: /* @__PURE__ */ h(v.linearGradient, {
84
84
  id: s,
85
85
  x1: "0",
86
86
  y1: "0",
@@ -90,7 +90,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
90
90
  children: [
91
91
  /* @__PURE__ */ m(v.stop, {
92
92
  offset: "0%",
93
- animate: { stopColor: l.s1 },
93
+ animate: { stopColor: c.s1 },
94
94
  transition: {
95
95
  duration: n === "processing" ? 3 : 5,
96
96
  repeat: Infinity,
@@ -100,7 +100,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
100
100
  }),
101
101
  /* @__PURE__ */ m(v.stop, {
102
102
  offset: "50%",
103
- animate: { stopColor: l.s2 },
103
+ animate: { stopColor: c.s2 },
104
104
  transition: {
105
105
  duration: n === "processing" ? 3 : 5,
106
106
  repeat: Infinity,
@@ -111,7 +111,7 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
111
111
  }),
112
112
  /* @__PURE__ */ m(v.stop, {
113
113
  offset: "100%",
114
- animate: { stopColor: l.s3 },
114
+ animate: { stopColor: c.s3 },
115
115
  transition: {
116
116
  duration: n === "processing" ? 3 : 5,
117
117
  repeat: Infinity,
@@ -121,37 +121,6 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
121
121
  }
122
122
  })
123
123
  ]
124
- }), /* @__PURE__ */ m("filter", {
125
- id: c,
126
- x: "-50%",
127
- y: "-50%",
128
- width: "200%",
129
- height: "200%",
130
- children: /* @__PURE__ */ m(v.feGaussianBlur, {
131
- in: "SourceGraphic",
132
- animate: { stdDeviation: u },
133
- transition: {
134
- duration: .6,
135
- ease: "easeOut"
136
- }
137
- })
138
- })] }),
139
- /* @__PURE__ */ m(g, { children: (n === "processing" || n === "error") && /* @__PURE__ */ m(v.path, {
140
- d: x,
141
- fill: `url(#${s})`,
142
- filter: `url(#${c})`,
143
- initial: { opacity: 0 },
144
- animate: { opacity: [
145
- .2,
146
- .45,
147
- .2
148
- ] },
149
- exit: { opacity: 0 },
150
- transition: { opacity: {
151
- duration: 3,
152
- repeat: Infinity,
153
- ease: "easeInOut"
154
- } }
155
124
  }) }),
156
125
  /* @__PURE__ */ m(g, { children: n === "processing" && /* @__PURE__ */ m(v.path, {
157
126
  d: x,
@@ -180,4 +149,4 @@ var k = p.memo(function({ state: n = "idle", size: r = 20, className: i }) {
180
149
  });
181
150
  k.displayName = "DevadootIcon";
182
151
  //#endregion
183
- export { n as AICommandProvider, f as AIConversation, u as BlockTable, i as CommandBar, c as ConfirmBlock, k as DevadootIcon, a as DividerBlock, o as InfoBlock, l as LoadingBlock, s as StatRowBlock, d as SuccessBlock, r as useAICommand };
152
+ export { n as AICommandProvider, f as AIConversation, a as BlockTable, i as CommandBar, o as ConfirmBlock, k as DevadootIcon, s as DividerBlock, c as InfoBlock, l as LoadingBlock, u as StatRowBlock, d as SuccessBlock, r as useAICommand };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/ai/devadoot-icon.tsx"],"sourcesContent":["'use client'\n\n/**\n * DevadootIcon -- Animated Devalok chakra for the AI command system.\n *\n * Renders the Devalok chakra SVG with **fill-first** animations:\n * - idle: gradient color breathing (pink ↔ rose, slow cycle)\n * - processing: gradient sweep (pink → purple → blue → pink cycle) + glow pulse\n * - responded: bright flash → settle to solid brand + brief scale pop\n * - error: red fill + glow pulse\n *\n * Inspired by Google Gemini's gradient-as-identity approach: the color IS\n * the animation, not just rotation/scale applied to a static shape.\n */\nimport { animate, AnimatePresence,motion, useMotionValue, useTransform } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../motion/motion-provider'\nimport { cn } from '../ui/lib/utils'\n\nexport type DevadootState = 'idle' | 'processing' | 'responded' | 'error'\n\nexport interface DevadootIconProps {\n state?: DevadootState\n size?: number\n className?: string\n}\n\n// Devalok chakra SVG path\nconst CHAKRA_PATH =\n 'M25.97,21.39c-0.9-1.85,0.08-3.95-1.72-5.39c1.76-1.44,0.8-3.55,1.69-5.39c0.05-0.12,0.04-0.25-0.02-0.35c-0.06-0.1-0.16-0.18-0.29-0.19c-2.05-0.15-3.35-2.04-5.5-1.21c-0.39-2.21-2.7-2.44-3.84-4.13c-0.08-0.1-0.19-0.16-0.31-0.16c-0.12,0-0.23,0.05-0.31,0.16c-1.14,1.69-3.43,1.92-3.82,4.13c-2.14-0.83-3.47,1.07-5.52,1.21c-0.13,0.01-0.23,0.09-0.29,0.19c-0.06,0.1-0.07,0.23-0.02,0.35c0.9,1.85-0.08,3.95,1.72,5.39c-1.76,1.44-0.8,3.55-1.69,5.39C6,21.51,6.02,21.64,6.07,21.74c0.06,0.1,0.16,0.18,0.29,0.19c2.05,0.15,3.38,2.06,5.52,1.23c0.39,2.21,2.67,2.43,3.82,4.12c0.08,0.1,0.19,0.16,0.31,0.16c0.12,0,0.23-0.05,0.31-0.16c1.14-1.69,3.42-1.92,3.81-4.13c2.14,0.83,3.48-1.07,5.53-1.22c0.13-0.01,0.23-0.09,0.29-0.19C26.01,21.64,26.02,21.51,25.97,21.39z'\n\n// Brand colors — pink ↔ purple palette, no blue\nconst BRAND_PINK = '#D33163'\nconst BRAND_ROSE = '#E8457A'\nconst BRAND_PURPLE = '#9B5DE5'\nconst BRAND_MAGENTA = '#C850C0'\nconst BRAND_BRIGHT = '#FF6B9D'\nconst ERROR_RED = '#E5383B'\n\n/**\n * Gradient rotation driver — animates the angle of the SVG gradient.\n * This creates the \"sweep\" effect: colors appear to flow across the shape.\n */\nfunction useGradientRotation(state: DevadootState) {\n const angle = useMotionValue(0)\n\n React.useEffect(() => {\n const controls = animate(angle, 360, {\n duration: state === 'processing' ? 4 : 12,\n repeat: Infinity,\n ease: 'linear',\n })\n return () => controls.stop()\n }, [angle, state])\n\n return useTransform(angle, (v) => `rotate(${v}, 16, 16)`)\n}\n\nconst DevadootIcon = React.memo(function DevadootIcon({\n state = 'idle',\n size = 20,\n className,\n}: DevadootIconProps) {\n const { reducedMotion } = useMotion()\n const gradientRotation = useGradientRotation(state)\n const gradientId = React.useId()\n const filterId = React.useId()\n\n // Static fallback for reduced motion\n if (reducedMotion) {\n return (\n <svg\n viewBox=\"0 0 32 32\"\n width={size}\n height={size}\n className={className}\n aria-hidden=\"true\"\n >\n <path\n d={CHAKRA_PATH}\n fill={state === 'error' ? ERROR_RED : BRAND_PINK}\n />\n </svg>\n )\n }\n\n // Resolve gradient stop colors based on state\n const stops =\n state === 'error'\n ? { s1: ERROR_RED, s2: '#FF6B6B', s3: ERROR_RED }\n : state === 'processing'\n ? { s1: BRAND_PINK, s2: BRAND_PURPLE, s3: BRAND_MAGENTA }\n : state === 'responded'\n ? { s1: BRAND_BRIGHT, s2: BRAND_PINK, s3: BRAND_BRIGHT }\n : { s1: BRAND_PINK, s2: BRAND_ROSE, s3: BRAND_PINK }\n\n // Glow blur intensity\n const glowBlur = state === 'processing' ? 3 : state === 'error' ? 2.5 : 0\n\n return (\n <span\n className={cn('relative inline-flex items-center justify-center', className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n >\n <motion.svg\n viewBox=\"0 0 32 32\"\n width={size}\n height={size}\n // Responded: brief pop. Error: shake. Others: no transform.\n animate={\n state === 'responded'\n ? { scale: [1.18, 1] }\n : state === 'error'\n ? { x: [0, -2, 2, -1.5, 1.5, 0] }\n : { scale: 1, x: 0 }\n }\n transition={\n state === 'responded'\n /* Near springs.bouncy (400/15/0.5) — stiffer pop for celebration feedback */\n ? { scale: { type: 'spring', stiffness: 500, damping: 15 } }\n : state === 'error'\n ? { x: { duration: 0.4 } }\n : { duration: 0.3 }\n }\n style={{ overflow: 'visible' }}\n >\n <defs>\n {/* Animated gradient — colors + rotation create the sweep */}\n <motion.linearGradient\n id={gradientId}\n x1=\"0\"\n y1=\"0\"\n x2=\"1\"\n y2=\"1\"\n gradientTransform={gradientRotation}\n >\n <motion.stop\n offset=\"0%\"\n animate={{ stopColor: stops.s1 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut' }}\n />\n <motion.stop\n offset=\"50%\"\n animate={{ stopColor: stops.s2 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut', delay: 0.5 }}\n />\n <motion.stop\n offset=\"100%\"\n animate={{ stopColor: stops.s3 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut', delay: 1 }}\n />\n </motion.linearGradient>\n\n {/* Glow filter — blur applied to shadow layer */}\n <filter id={filterId} x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\n <motion.feGaussianBlur\n in=\"SourceGraphic\"\n animate={{ stdDeviation: glowBlur }}\n transition={{ duration: 0.6, ease: 'easeOut' }}\n />\n </filter>\n </defs>\n\n {/* Layer 1: Glow shadow (blurred, semi-transparent copy behind) */}\n <AnimatePresence>\n {(state === 'processing' || state === 'error') && (\n <motion.path\n d={CHAKRA_PATH}\n fill={`url(#${gradientId})`}\n filter={`url(#${filterId})`}\n initial={{ opacity: 0 }}\n animate={{ opacity: [0.2, 0.45, 0.2] }}\n exit={{ opacity: 0 }}\n transition={{\n opacity: { duration: 3, repeat: Infinity, ease: 'easeInOut' },\n }}\n />\n )}\n </AnimatePresence>\n\n {/* Layer 2: Shimmer highlight (bright overlay that pulses) */}\n <AnimatePresence>\n {state === 'processing' && (\n <motion.path\n d={CHAKRA_PATH}\n fill=\"white\"\n initial={{ opacity: 0 }}\n animate={{ opacity: [0, 0.12, 0] }}\n exit={{ opacity: 0 }}\n transition={{\n opacity: { duration: 2.5, repeat: Infinity, ease: 'easeInOut' },\n }}\n />\n )}\n </AnimatePresence>\n\n {/* Layer 3: Main shape — gradient fill */}\n <motion.path\n d={CHAKRA_PATH}\n fill={`url(#${gradientId})`}\n // Responded: brief bright flash via fill override\n animate={\n state === 'responded'\n ? { fill: [`url(#${gradientId})`, `url(#${gradientId})`] }\n : {}\n }\n />\n </motion.svg>\n </span>\n )\n})\n\nDevadootIcon.displayName = 'DevadootIcon'\n\nexport { DevadootIcon }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/ai/devadoot-icon.tsx"],"sourcesContent":["'use client'\n\n/**\n * DevadootIcon -- Animated Devalok chakra for the AI command system.\n *\n * Renders the Devalok chakra SVG with **fill-first** animations:\n * - idle: gradient color breathing (pink ↔ rose, slow cycle)\n * - processing: gradient sweep (pink → purple → blue → pink cycle) + glow pulse\n * - responded: bright flash → settle to solid brand + brief scale pop\n * - error: red fill + glow pulse\n *\n * Inspired by Google Gemini's gradient-as-identity approach: the color IS\n * the animation, not just rotation/scale applied to a static shape.\n */\nimport { animate, AnimatePresence,motion, useMotionValue, useTransform } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useMotion } from '../motion/motion-provider'\nimport { cn } from '../ui/lib/utils'\n\nexport type DevadootState = 'idle' | 'processing' | 'responded' | 'error'\n\nexport interface DevadootIconProps {\n state?: DevadootState\n size?: number\n className?: string\n}\n\n// Devalok chakra SVG path\nconst CHAKRA_PATH =\n 'M25.97,21.39c-0.9-1.85,0.08-3.95-1.72-5.39c1.76-1.44,0.8-3.55,1.69-5.39c0.05-0.12,0.04-0.25-0.02-0.35c-0.06-0.1-0.16-0.18-0.29-0.19c-2.05-0.15-3.35-2.04-5.5-1.21c-0.39-2.21-2.7-2.44-3.84-4.13c-0.08-0.1-0.19-0.16-0.31-0.16c-0.12,0-0.23,0.05-0.31,0.16c-1.14,1.69-3.43,1.92-3.82,4.13c-2.14-0.83-3.47,1.07-5.52,1.21c-0.13,0.01-0.23,0.09-0.29,0.19c-0.06,0.1-0.07,0.23-0.02,0.35c0.9,1.85-0.08,3.95,1.72,5.39c-1.76,1.44-0.8,3.55-1.69,5.39C6,21.51,6.02,21.64,6.07,21.74c0.06,0.1,0.16,0.18,0.29,0.19c2.05,0.15,3.38,2.06,5.52,1.23c0.39,2.21,2.67,2.43,3.82,4.12c0.08,0.1,0.19,0.16,0.31,0.16c0.12,0,0.23-0.05,0.31-0.16c1.14-1.69,3.42-1.92,3.81-4.13c2.14,0.83,3.48-1.07,5.53-1.22c0.13-0.01,0.23-0.09,0.29-0.19C26.01,21.64,26.02,21.51,25.97,21.39z'\n\n// Brand colors — pink ↔ purple palette, no blue\nconst BRAND_PINK = '#D33163'\nconst BRAND_ROSE = '#E8457A'\nconst BRAND_PURPLE = '#9B5DE5'\nconst BRAND_MAGENTA = '#C850C0'\nconst BRAND_BRIGHT = '#FF6B9D'\nconst ERROR_RED = '#E5383B'\n\n/**\n * Gradient rotation driver — animates the angle of the SVG gradient.\n * This creates the \"sweep\" effect: colors appear to flow across the shape.\n */\nfunction useGradientRotation(state: DevadootState) {\n const angle = useMotionValue(0)\n\n React.useEffect(() => {\n const controls = animate(angle, 360, {\n duration: state === 'processing' ? 4 : 12,\n repeat: Infinity,\n ease: 'linear',\n })\n return () => controls.stop()\n }, [angle, state])\n\n return useTransform(angle, (v) => `rotate(${v}, 16, 16)`)\n}\n\nconst DevadootIcon = React.memo(function DevadootIcon({\n state = 'idle',\n size = 20,\n className,\n}: DevadootIconProps) {\n const { reducedMotion } = useMotion()\n const gradientRotation = useGradientRotation(state)\n const gradientId = React.useId()\n\n // Static fallback for reduced motion\n if (reducedMotion) {\n return (\n <svg\n viewBox=\"0 0 32 32\"\n width={size}\n height={size}\n className={className}\n aria-hidden=\"true\"\n >\n <path\n d={CHAKRA_PATH}\n fill={state === 'error' ? ERROR_RED : BRAND_PINK}\n />\n </svg>\n )\n }\n\n // Resolve gradient stop colors based on state\n const stops =\n state === 'error'\n ? { s1: ERROR_RED, s2: '#FF6B6B', s3: ERROR_RED }\n : state === 'processing'\n ? { s1: BRAND_PINK, s2: BRAND_PURPLE, s3: BRAND_MAGENTA }\n : state === 'responded'\n ? { s1: BRAND_BRIGHT, s2: BRAND_PINK, s3: BRAND_BRIGHT }\n : { s1: BRAND_PINK, s2: BRAND_ROSE, s3: BRAND_PINK }\n\n return (\n <span\n className={cn('relative inline-flex items-center justify-center', className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n >\n <motion.svg\n viewBox=\"0 0 32 32\"\n width={size}\n height={size}\n // Responded: brief pop. Error: shake. Others: no transform.\n animate={\n state === 'responded'\n ? { scale: [1.18, 1] }\n : state === 'error'\n ? { x: [0, -2, 2, -1.5, 1.5, 0] }\n : { scale: 1, x: 0 }\n }\n transition={\n state === 'responded'\n /* Near springs.bouncy (400/15/0.5) — stiffer pop for celebration feedback */\n ? { scale: { type: 'spring', stiffness: 500, damping: 15 } }\n : state === 'error'\n ? { x: { duration: 0.4 } }\n : { duration: 0.3 }\n }\n style={{ overflow: 'visible' }}\n >\n <defs>\n {/* Animated gradient — colors + rotation create the sweep */}\n <motion.linearGradient\n id={gradientId}\n x1=\"0\"\n y1=\"0\"\n x2=\"1\"\n y2=\"1\"\n gradientTransform={gradientRotation}\n >\n <motion.stop\n offset=\"0%\"\n animate={{ stopColor: stops.s1 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut' }}\n />\n <motion.stop\n offset=\"50%\"\n animate={{ stopColor: stops.s2 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut', delay: 0.5 }}\n />\n <motion.stop\n offset=\"100%\"\n animate={{ stopColor: stops.s3 }}\n transition={{ duration: state === 'processing' ? 3 : 5, repeat: Infinity, repeatType: 'reverse', ease: 'easeInOut', delay: 1 }}\n />\n </motion.linearGradient>\n </defs>\n\n {/* Layer 1: Shimmer highlight (bright overlay that pulses) */}\n <AnimatePresence>\n {state === 'processing' && (\n <motion.path\n d={CHAKRA_PATH}\n fill=\"white\"\n initial={{ opacity: 0 }}\n animate={{ opacity: [0, 0.12, 0] }}\n exit={{ opacity: 0 }}\n transition={{\n opacity: { duration: 2.5, repeat: Infinity, ease: 'easeInOut' },\n }}\n />\n )}\n </AnimatePresence>\n\n {/* Layer 3: Main shape — gradient fill */}\n <motion.path\n d={CHAKRA_PATH}\n fill={`url(#${gradientId})`}\n // Responded: brief bright flash via fill override\n animate={\n state === 'responded'\n ? { fill: [`url(#${gradientId})`, `url(#${gradientId})`] }\n : {}\n }\n />\n </motion.svg>\n </span>\n )\n})\n\nDevadootIcon.displayName = 'DevadootIcon'\n\nexport { DevadootIcon }\n"],"mappings":"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"}
@@ -138,7 +138,7 @@ function b({ item: e, compact: n, children: r }) {
138
138
  }
139
139
  function x({ label: n, isFirst: r }) {
140
140
  return /* @__PURE__ */ f(m.div, {
141
- initial: { opacity: 0 },
141
+ initial: !1,
142
142
  animate: { opacity: 1 },
143
143
  transition: e.fade,
144
144
  className: t("flex items-center gap-ds-03", r ? "" : "mt-ds-06", "mb-ds-03"),
@@ -191,7 +191,7 @@ var S = u.forwardRef(({ items: n, onLoadMore: r, loading: i = !1, hasMore: a = !
191
191
  }), /* @__PURE__ */ f("div", {
192
192
  className: t("relative flex flex-col", c ? "gap-1" : "gap-3"),
193
193
  children: [/* @__PURE__ */ d("div", { className: "absolute bottom-0 left-[3px] top-0 w-px bg-surface-border" }), n.items.map((t, n) => /* @__PURE__ */ d(m.div, {
194
- initial: { opacity: 0 },
194
+ initial: !1,
195
195
  animate: { opacity: 1 },
196
196
  transition: {
197
197
  ...e.fade,
@@ -202,7 +202,7 @@ var S = u.forwardRef(({ items: n, onLoadMore: r, loading: i = !1, hasMore: a = !
202
202
  })] }, n.label)) }) : /* @__PURE__ */ f("div", {
203
203
  className: t("relative flex flex-col", c ? "gap-1" : "gap-3"),
204
204
  children: [/* @__PURE__ */ d("div", { className: "absolute bottom-0 left-[3px] top-0 w-px bg-surface-border" }), O.map((t, n) => /* @__PURE__ */ d(m.div, {
205
- initial: { opacity: 0 },
205
+ initial: !1,
206
206
  animate: { opacity: 1 },
207
207
  transition: {
208
208
  ...e.fade,
@@ -1 +1 @@
1
- {"version":3,"file":"activity-feed.js","names":[],"sources":["../../src/composed/activity-feed.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { formatRelativeTime } from '../ui/lib/date-utils'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { getInitials } from './lib/string-utils'\n\nexport interface ActivityItem {\n id: string\n actor?: { name: string; image?: string }\n action: string | React.ReactNode\n timestamp: Date | string\n icon?: IconInput\n color?: 'default' | 'success' | 'warning' | 'error' | 'info'\n detail?: React.ReactNode\n}\n\nexport interface GroupLabels {\n today?: string\n yesterday?: string\n thisWeek?: string\n older?: string\n}\n\nexport interface ActivityFeedProps extends React.HTMLAttributes<HTMLDivElement> {\n items: ActivityItem[]\n onLoadMore?: () => void\n loading?: boolean\n hasMore?: boolean\n emptyState?: React.ReactNode\n compact?: boolean\n maxInitialItems?: number\n groupBy?: 'time' | 'none'\n groupLabels?: GroupLabels\n /** Custom renderer per item. Return ReactNode for custom rendering, undefined to use default ActivityEntry. */\n renderItem?: (item: ActivityItem, index: number) => React.ReactNode | undefined\n}\n\nconst dotColorMap = {\n default: 'bg-surface-fg-subtle',\n success: 'bg-success-9',\n warning: 'bg-warning-9',\n error: 'bg-error-9',\n info: 'bg-info-9',\n} as const\n\nconst DEFAULT_GROUP_LABELS: Required<GroupLabels> = {\n today: 'Today',\n yesterday: 'Yesterday',\n thisWeek: 'This Week',\n older: 'Older',\n}\n\n/**\n * Buckets items into time-based groups: today, yesterday, earlier this week (since Monday), older.\n * Skips empty groups.\n */\nexport function groupItemsByTime(\n items: ActivityItem[],\n labels?: GroupLabels,\n): { label: string; items: ActivityItem[] }[] {\n const l = { ...DEFAULT_GROUP_LABELS, ...labels }\n const now = new Date()\n\n // Start of today (midnight local)\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n // Start of yesterday\n const startOfYesterday = new Date(startOfToday.getTime() - 24 * 60 * 60 * 1000)\n // Start of this week (Monday)\n const dayOfWeek = now.getDay() // 0=Sun, 1=Mon, ...\n const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1\n const startOfWeek = new Date(\n startOfToday.getTime() - daysSinceMonday * 24 * 60 * 60 * 1000,\n )\n\n const today: ActivityItem[] = []\n const yesterday: ActivityItem[] = []\n const thisWeek: ActivityItem[] = []\n const older: ActivityItem[] = []\n\n for (const item of items) {\n const ts =\n typeof item.timestamp === 'string'\n ? new Date(item.timestamp)\n : item.timestamp\n const t = ts.getTime()\n\n if (t >= startOfToday.getTime()) {\n today.push(item)\n } else if (t >= startOfYesterday.getTime()) {\n yesterday.push(item)\n } else if (t >= startOfWeek.getTime()) {\n thisWeek.push(item)\n } else {\n older.push(item)\n }\n }\n\n const groups: { label: string; items: ActivityItem[] }[] = []\n if (today.length > 0) groups.push({ label: l.today, items: today })\n if (yesterday.length > 0) groups.push({ label: l.yesterday, items: yesterday })\n if (thisWeek.length > 0) groups.push({ label: l.thisWeek, items: thisWeek })\n if (older.length > 0) groups.push({ label: l.older, items: older })\n\n return groups\n}\n\nfunction LoadingSkeleton({ compact }: { compact: boolean }) {\n return (\n <div className={cn('flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-ds-03\">\n <Skeleton className=\"h-2 w-2 shrink-0 rounded-pill\" />\n {!compact && <Skeleton className=\"h-4 w-4 shrink-0 rounded-pill\" />}\n <Skeleton className={cn('flex-1', compact ? 'h-3' : 'h-4')} />\n <Skeleton className=\"h-3 w-12\" />\n </div>\n ))}\n </div>\n )\n}\n\nfunction ActivityEntry({\n item,\n compact,\n}: {\n item: ActivityItem\n compact: boolean\n}) {\n const [expandedDetail, setExpandedDetail] = React.useState(false)\n const detailId = React.useId()\n const color = item.color ?? 'default'\n\n const handleActionClick = () => {\n if (item.detail) {\n setExpandedDetail((prev) => !prev)\n }\n }\n\n return (\n <div className={cn('relative flex items-start', compact ? 'gap-ds-02' : 'gap-ds-03')}>\n {/* Dot */}\n <div\n className={cn(\n 'relative z-10 mt-1.5 h-2 w-2 shrink-0 rounded-pill ring-2 ring-surface-base',\n dotColorMap[color],\n )}\n />\n\n {/* Avatar (non-compact only) */}\n {!compact && item.actor && (\n <Avatar className=\"h-5 w-5 shrink-0 text-[9px]\">\n {item.actor.image && (\n <AvatarImage src={item.actor.image} alt={item.actor.name} />\n )}\n <AvatarFallback className=\"text-[9px]\">\n {getInitials(item.actor.name)}\n </AvatarFallback>\n </Avatar>\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-center justify-between gap-ds-02\">\n <div className={cn('min-w-0 flex-1 flex items-center gap-ds-01 flex-wrap', compact ? 'text-ds-xs' : 'text-ds-sm')}>\n {item.actor && (\n <span className=\"font-medium text-surface-fg\">{item.actor.name} </span>\n )}\n {item.detail && (\n <Icon icon={IconChevronRight} size=\"xs\" className={cn('shrink-0 text-surface-fg-subtle transition-transform duration-fast-02 ease-productive-standard', expandedDetail && 'rotate-90')} />\n )}\n {item.detail ? (\n <button\n type=\"button\"\n className=\"rounded-control text-surface-fg-muted hover:underline hover:bg-surface-raised focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9\"\n onClick={handleActionClick}\n aria-expanded={expandedDetail}\n aria-controls={detailId}\n >\n {item.action}\n </button>\n ) : (\n <span className=\"text-surface-fg-muted\">{item.action}</span>\n )}\n </div>\n\n <time\n className={cn(\n 'shrink-0 whitespace-nowrap text-surface-fg-subtle',\n compact ? 'text-ds-xs' : 'text-ds-sm',\n )}\n dateTime={\n (typeof item.timestamp === 'string'\n ? new Date(item.timestamp)\n : item.timestamp\n ).toISOString()\n }\n >\n {formatRelativeTime(item.timestamp)}\n </time>\n </div>\n\n {/* Expandable detail */}\n {expandedDetail && item.detail && (\n <div id={detailId} className=\"mt-ds-02 animate-in fade-in slide-in-from-top-1 text-ds-sm text-surface-fg-muted\">\n {item.detail}\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/**\n * Wraps custom renderItem content with the same dot + layout container\n * that ActivityEntry uses, keeping vertical rhythm consistent.\n */\nfunction CustomEntry({\n item,\n compact,\n children,\n}: {\n item: ActivityItem\n compact: boolean\n children: React.ReactNode\n}) {\n const color = item.color ?? 'default'\n return (\n <div className={cn('relative flex items-start', compact ? 'gap-ds-02' : 'gap-ds-03')}>\n {/* Dot — same as ActivityEntry */}\n <div\n className={cn(\n 'relative z-10 mt-1.5 h-2 w-2 shrink-0 rounded-pill ring-2 ring-surface-base',\n dotColorMap[color],\n )}\n />\n {/* Custom content in the same flex slot as ActivityEntry's content area */}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n )\n}\n\nfunction GroupHeader({\n label,\n isFirst,\n}: {\n label: string\n isFirst: boolean\n}) {\n return (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n className={cn(\n 'flex items-center gap-ds-03',\n !isFirst ? 'mt-ds-06' : '',\n 'mb-ds-03',\n )}\n >\n <hr className=\"flex-1 border-surface-border\" />\n <span className=\"bg-surface-base px-ds-03 text-ds-xs font-medium uppercase tracking-wider text-surface-fg-subtle\">\n {label}\n </span>\n <hr className=\"flex-1 border-surface-border\" />\n </motion.div>\n )\n}\n\nconst ActivityFeed = React.forwardRef<HTMLDivElement, ActivityFeedProps>(\n (\n {\n items,\n onLoadMore,\n loading = false,\n hasMore = false,\n emptyState,\n compact = false,\n maxInitialItems,\n groupBy = 'none',\n groupLabels,\n renderItem,\n className,\n ...props\n },\n ref,\n ) => {\n const [showAll, setShowAll] = React.useState(false)\n\n if (loading) {\n return (\n <div ref={ref} className={cn('relative', className)} {...props}>\n <LoadingSkeleton compact={compact} />\n </div>\n )\n }\n\n if (items.length === 0) {\n if (emptyState) {\n return (\n <div ref={ref} className={className} {...props}>\n {emptyState}\n </div>\n )\n }\n return null\n }\n\n // maxInitialItems applies to the flat list BEFORE grouping\n const truncated =\n maxInitialItems != null && !showAll && items.length > maxInitialItems\n const visibleItems = truncated ? items.slice(0, maxInitialItems) : items\n\n const useGrouping = groupBy === 'time'\n\n /** Resolve what to render for a single item, respecting renderItem. */\n const resolveEntry = (item: ActivityItem, index: number) => {\n if (renderItem) {\n const custom = renderItem(item, index)\n if (custom !== undefined) {\n return <CustomEntry item={item} compact={compact}>{custom}</CustomEntry>\n }\n }\n return <ActivityEntry item={item} compact={compact} />\n }\n\n return (\n <div ref={ref} className={cn('relative', className)} {...props}>\n {/* Items */}\n {useGrouping ? (\n <div>\n {groupItemsByTime(visibleItems, groupLabels).map((group, gi) => (\n <div key={group.label}>\n <GroupHeader label={group.label} isFirst={gi === 0} />\n {/* Timeline line scoped to this group */}\n <div className={cn('relative flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n <div className=\"absolute bottom-0 left-[3px] top-0 w-px bg-surface-border\" />\n {group.items.map((item, index) => (\n <motion.div key={item.id} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ ...tweens.fade, delay: index * 0.03 }}>\n {resolveEntry(item, index)}\n </motion.div>\n ))}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className={cn('relative flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n {/* Timeline line */}\n <div className=\"absolute bottom-0 left-[3px] top-0 w-px bg-surface-border\" />\n {visibleItems.map((item, index) => (\n <motion.div key={item.id} initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ ...tweens.fade, delay: index * 0.03 }}>\n {resolveEntry(item, index)}\n </motion.div>\n ))}\n </div>\n )}\n\n {/* Show all button */}\n {truncated && (\n <div className=\"relative mt-ds-03 flex justify-center\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setShowAll(true)}\n >\n Show all ({items.length})\n </Button>\n </div>\n )}\n\n {/* Load more button */}\n {hasMore && onLoadMore && !truncated && (\n <div className=\"relative mt-ds-03 flex justify-center\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n )}\n </div>\n )\n },\n)\nActivityFeed.displayName = 'ActivityFeed'\n\nexport { ActivityFeed }\n"],"mappings":";;;;;;;;;;;;;;AA+CA,IAAM,IAAc;CAClB,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;CACP,MAAM;CACP,EAEK,IAA8C;CAClD,OAAO;CACP,WAAW;CACX,UAAU;CACV,OAAO;CACR;AAMD,SAAgB,EACd,GACA,GAC4C;CAC5C,IAAM,IAAI;EAAE,GAAG;EAAsB,GAAG;EAAQ,EAC1C,oBAAM,IAAI,MAAM,EAGhB,IAAe,IAAI,KAAK,EAAI,aAAa,EAAE,EAAI,UAAU,EAAE,EAAI,SAAS,CAAC,EAEzE,oBAAmB,IAAI,KAAK,EAAa,SAAS,GAAG,OAAU,KAAK,IAAK,EAEzE,IAAY,EAAI,QAAQ,EACxB,IAAkB,MAAc,IAAI,IAAI,IAAY,GACpD,oBAAc,IAAI,KACtB,EAAa,SAAS,GAAG,IAAkB,KAAK,KAAK,KAAK,IAC3D,EAEK,IAAwB,EAAE,EAC1B,IAA4B,EAAE,EAC9B,IAA2B,EAAE,EAC7B,IAAwB,EAAE;AAEhC,MAAK,IAAM,KAAQ,GAAO;EAKxB,IAAM,KAHJ,OAAO,EAAK,aAAc,WACtB,IAAI,KAAK,EAAK,UAAU,GACxB,EAAK,WACE,SAAS;AAEtB,EAAI,KAAK,EAAa,SAAS,GAC7B,EAAM,KAAK,EAAK,GACP,KAAK,EAAiB,SAAS,GACxC,EAAU,KAAK,EAAK,GACX,KAAK,EAAY,SAAS,GACnC,EAAS,KAAK,EAAK,GAEnB,EAAM,KAAK,EAAK;;CAIpB,IAAM,IAAqD,EAAE;AAM7D,QALI,EAAM,SAAS,KAAG,EAAO,KAAK;EAAE,OAAO,EAAE;EAAO,OAAO;EAAO,CAAC,EAC/D,EAAU,SAAS,KAAG,EAAO,KAAK;EAAE,OAAO,EAAE;EAAW,OAAO;EAAW,CAAC,EAC3E,EAAS,SAAS,KAAG,EAAO,KAAK;EAAE,OAAO,EAAE;EAAU,OAAO;EAAU,CAAC,EACxE,EAAM,SAAS,KAAG,EAAO,KAAK;EAAE,OAAO,EAAE;EAAO,OAAO;EAAO,CAAC,EAE5D;;AAGT,SAAS,EAAgB,EAAE,cAAiC;AAC1D,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,iBAAiB,IAAU,UAAU,QAAQ;YAC7D;GAAC;GAAG;GAAG;GAAE,CAAC,KAAK,MACd,kBAAC,OAAD;GAAa,WAAU;aAAvB;IACE,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;IACrD,CAAC,KAAW,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;IACnE,kBAAC,GAAD,EAAU,WAAW,EAAG,UAAU,IAAU,QAAQ,MAAM,EAAI,CAAA;IAC9D,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA;IAC7B;KALI,EAKJ,CACN;EACE,CAAA;;AAIV,SAAS,EAAc,EACrB,SACA,cAIC;CACD,IAAM,CAAC,GAAgB,KAAqB,EAAM,SAAS,GAAM,EAC3D,IAAW,EAAM,OAAO,EACxB,IAAQ,EAAK,SAAS;AAQ5B,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,6BAA6B,IAAU,cAAc,YAAY;YAApF;GAEE,kBAAC,OAAD,EACE,WAAW,EACT,+EACA,EAAY,GACb,EACD,CAAA;GAGD,CAAC,KAAW,EAAK,SAChB,kBAAC,GAAD;IAAQ,WAAU;cAAlB,CACG,EAAK,MAAM,SACV,kBAAC,GAAD;KAAa,KAAK,EAAK,MAAM;KAAO,KAAK,EAAK,MAAM;KAAQ,CAAA,EAE9D,kBAAC,GAAD;KAAgB,WAAU;eACvB,EAAY,EAAK,MAAM,KAAK;KACd,CAAA,CACV;;GAIX,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAW,EAAG,wDAAwD,IAAU,eAAe,aAAa;gBAAjH;OACG,EAAK,SACJ,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+C,EAAK,MAAM,MAAK,IAAQ;;OAExE,EAAK,UACJ,kBAAC,GAAD;QAAM,MAAM;QAAkB,MAAK;QAAK,WAAW,EAAG,kGAAkG,KAAkB,YAAY;QAAI,CAAA;OAE3L,EAAK,SACJ,kBAAC,UAAD;QACE,MAAK;QACL,WAAU;QACV,eA1CkB;AAC9B,SAAI,EAAK,UACP,GAAmB,MAAS,CAAC,EAAK;;QAyCxB,iBAAe;QACf,iBAAe;kBAEd,EAAK;QACC,CAAA,GAET,kBAAC,QAAD;QAAM,WAAU;kBAAyB,EAAK;QAAc,CAAA;OAE1D;SAEN,kBAAC,QAAD;MACE,WAAW,EACT,qDACA,IAAU,eAAe,aAC1B;MACD,WACG,OAAO,EAAK,aAAc,WACvB,IAAI,KAAK,EAAK,UAAU,GACxB,EAAK,WACP,aAAa;gBAGhB,EAAmB,EAAK,UAAU;MAC9B,CAAA,CACH;QAGL,KAAkB,EAAK,UACtB,kBAAC,OAAD;KAAK,IAAI;KAAU,WAAU;eAC1B,EAAK;KACF,CAAA,CAEJ;;GACF;;;AAQV,SAAS,EAAY,EACnB,SACA,YACA,eAKC;CACD,IAAM,IAAQ,EAAK,SAAS;AAC5B,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,6BAA6B,IAAU,cAAc,YAAY;YAApF,CAEE,kBAAC,OAAD,EACE,WAAW,EACT,+EACA,EAAY,GACb,EACD,CAAA,EAEF,kBAAC,OAAD;GAAK,WAAU;GAAkB;GAAe,CAAA,CAC5C;;;AAIV,SAAS,EAAY,EACnB,UACA,cAIC;AACD,QACE,kBAAC,EAAO,KAAR;EACE,SAAS,EAAE,SAAS,GAAG;EACvB,SAAS,EAAE,SAAS,GAAG;EACvB,YAAY,EAAO;EACnB,WAAW,EACT,+BACC,IAAuB,KAAb,YACX,WACD;YARH;GAUE,kBAAC,MAAD,EAAI,WAAU,gCAAiC,CAAA;GAC/C,kBAAC,QAAD;IAAM,WAAU;cACb;IACI,CAAA;GACP,kBAAC,MAAD,EAAI,WAAU,gCAAiC,CAAA;GACpC;;;AAIjB,IAAM,IAAe,EAAM,YAEvB,EACE,UACA,eACA,aAAU,IACV,aAAU,IACV,eACA,aAAU,IACV,oBACA,aAAU,QACV,gBACA,eACA,cACA,GAAG,KAEL,MACG;CACH,IAAM,CAAC,GAAS,KAAc,EAAM,SAAS,GAAM;AAEnD,KAAI,EACF,QACE,kBAAC,OAAD;EAAU;EAAK,WAAW,EAAG,YAAY,EAAU;EAAE,GAAI;YACvD,kBAAC,GAAD,EAA0B,YAAW,CAAA;EACjC,CAAA;AAIV,KAAI,EAAM,WAAW,EAQnB,QAPI,IAEA,kBAAC,OAAD;EAAU;EAAgB;EAAW,GAAI;YACtC;EACG,CAAA,GAGH;CAIT,IAAM,IACJ,KAAmB,QAAQ,CAAC,KAAW,EAAM,SAAS,GAClD,IAAe,IAAY,EAAM,MAAM,GAAG,EAAgB,GAAG,GAE7D,IAAc,MAAY,QAG1B,KAAgB,GAAoB,MAAkB;AAC1D,MAAI,GAAY;GACd,IAAM,IAAS,EAAW,GAAM,EAAM;AACtC,OAAI,MAAW,KAAA,EACb,QAAO,kBAAC,GAAD;IAAmB;IAAe;cAAU;IAAqB,CAAA;;AAG5E,SAAO,kBAAC,GAAD;GAAqB;GAAe;GAAW,CAAA;;AAGxD,QACE,kBAAC,OAAD;EAAU;EAAK,WAAW,EAAG,YAAY,EAAU;EAAE,GAAI;YAAzD;GAEG,IACC,kBAAC,OAAD,EAAA,UACG,EAAiB,GAAc,EAAY,CAAC,KAAK,GAAO,MACvD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAa,OAAO,EAAM;IAAO,SAAS,MAAO;IAAK,CAAA,EAEtD,kBAAC,OAAD;IAAK,WAAW,EAAG,0BAA0B,IAAU,UAAU,QAAQ;cAAzE,CACE,kBAAC,OAAD,EAAK,WAAU,6DAA8D,CAAA,EAC5E,EAAM,MAAM,KAAK,GAAM,MACtB,kBAAC,EAAO,KAAR;KAA0B,SAAS,EAAE,SAAS,GAAG;KAAE,SAAS,EAAE,SAAS,GAAG;KAAE,YAAY;MAAE,GAAG,EAAO;MAAM,OAAO,IAAQ;MAAM;eAC5H,EAAa,GAAM,EAAM;KACf,EAFI,EAAK,GAET,CACb,CACE;MACF,EAAA,EAXI,EAAM,MAWV,CACN,EACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAW,EAAG,0BAA0B,IAAU,UAAU,QAAQ;cAAzE,CAEE,kBAAC,OAAD,EAAK,WAAU,6DAA8D,CAAA,EAC5E,EAAa,KAAK,GAAM,MACvB,kBAAC,EAAO,KAAR;KAA0B,SAAS,EAAE,SAAS,GAAG;KAAE,SAAS,EAAE,SAAS,GAAG;KAAE,YAAY;MAAE,GAAG,EAAO;MAAM,OAAO,IAAQ;MAAM;eAC5H,EAAa,GAAM,EAAM;KACf,EAFI,EAAK,GAET,CACb,CACE;;GAIP,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAW,GAAK;eAHjC;MAIC;MACY,EAAM;MAAO;MACjB;;IACL,CAAA;GAIP,KAAW,KAAc,CAAC,KACzB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,SAAS;eAAY;KAE9C,CAAA;IACL,CAAA;GAEJ;;EAGX;AACD,EAAa,cAAc"}
1
+ {"version":3,"file":"activity-feed.js","names":[],"sources":["../../src/composed/activity-feed.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronRight } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { formatRelativeTime } from '../ui/lib/date-utils'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { getInitials } from './lib/string-utils'\n\nexport interface ActivityItem {\n id: string\n actor?: { name: string; image?: string }\n action: string | React.ReactNode\n timestamp: Date | string\n icon?: IconInput\n color?: 'default' | 'success' | 'warning' | 'error' | 'info'\n detail?: React.ReactNode\n}\n\nexport interface GroupLabels {\n today?: string\n yesterday?: string\n thisWeek?: string\n older?: string\n}\n\nexport interface ActivityFeedProps extends React.HTMLAttributes<HTMLDivElement> {\n items: ActivityItem[]\n onLoadMore?: () => void\n loading?: boolean\n hasMore?: boolean\n emptyState?: React.ReactNode\n compact?: boolean\n maxInitialItems?: number\n groupBy?: 'time' | 'none'\n groupLabels?: GroupLabels\n /** Custom renderer per item. Return ReactNode for custom rendering, undefined to use default ActivityEntry. */\n renderItem?: (item: ActivityItem, index: number) => React.ReactNode | undefined\n}\n\nconst dotColorMap = {\n default: 'bg-surface-fg-subtle',\n success: 'bg-success-9',\n warning: 'bg-warning-9',\n error: 'bg-error-9',\n info: 'bg-info-9',\n} as const\n\nconst DEFAULT_GROUP_LABELS: Required<GroupLabels> = {\n today: 'Today',\n yesterday: 'Yesterday',\n thisWeek: 'This Week',\n older: 'Older',\n}\n\n/**\n * Buckets items into time-based groups: today, yesterday, earlier this week (since Monday), older.\n * Skips empty groups.\n */\nexport function groupItemsByTime(\n items: ActivityItem[],\n labels?: GroupLabels,\n): { label: string; items: ActivityItem[] }[] {\n const l = { ...DEFAULT_GROUP_LABELS, ...labels }\n const now = new Date()\n\n // Start of today (midnight local)\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n // Start of yesterday\n const startOfYesterday = new Date(startOfToday.getTime() - 24 * 60 * 60 * 1000)\n // Start of this week (Monday)\n const dayOfWeek = now.getDay() // 0=Sun, 1=Mon, ...\n const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1\n const startOfWeek = new Date(\n startOfToday.getTime() - daysSinceMonday * 24 * 60 * 60 * 1000,\n )\n\n const today: ActivityItem[] = []\n const yesterday: ActivityItem[] = []\n const thisWeek: ActivityItem[] = []\n const older: ActivityItem[] = []\n\n for (const item of items) {\n const ts =\n typeof item.timestamp === 'string'\n ? new Date(item.timestamp)\n : item.timestamp\n const t = ts.getTime()\n\n if (t >= startOfToday.getTime()) {\n today.push(item)\n } else if (t >= startOfYesterday.getTime()) {\n yesterday.push(item)\n } else if (t >= startOfWeek.getTime()) {\n thisWeek.push(item)\n } else {\n older.push(item)\n }\n }\n\n const groups: { label: string; items: ActivityItem[] }[] = []\n if (today.length > 0) groups.push({ label: l.today, items: today })\n if (yesterday.length > 0) groups.push({ label: l.yesterday, items: yesterday })\n if (thisWeek.length > 0) groups.push({ label: l.thisWeek, items: thisWeek })\n if (older.length > 0) groups.push({ label: l.older, items: older })\n\n return groups\n}\n\nfunction LoadingSkeleton({ compact }: { compact: boolean }) {\n return (\n <div className={cn('flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n {[0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-ds-03\">\n <Skeleton className=\"h-2 w-2 shrink-0 rounded-pill\" />\n {!compact && <Skeleton className=\"h-4 w-4 shrink-0 rounded-pill\" />}\n <Skeleton className={cn('flex-1', compact ? 'h-3' : 'h-4')} />\n <Skeleton className=\"h-3 w-12\" />\n </div>\n ))}\n </div>\n )\n}\n\nfunction ActivityEntry({\n item,\n compact,\n}: {\n item: ActivityItem\n compact: boolean\n}) {\n const [expandedDetail, setExpandedDetail] = React.useState(false)\n const detailId = React.useId()\n const color = item.color ?? 'default'\n\n const handleActionClick = () => {\n if (item.detail) {\n setExpandedDetail((prev) => !prev)\n }\n }\n\n return (\n <div className={cn('relative flex items-start', compact ? 'gap-ds-02' : 'gap-ds-03')}>\n {/* Dot */}\n <div\n className={cn(\n 'relative z-10 mt-1.5 h-2 w-2 shrink-0 rounded-pill ring-2 ring-surface-base',\n dotColorMap[color],\n )}\n />\n\n {/* Avatar (non-compact only) */}\n {!compact && item.actor && (\n <Avatar className=\"h-5 w-5 shrink-0 text-[9px]\">\n {item.actor.image && (\n <AvatarImage src={item.actor.image} alt={item.actor.name} />\n )}\n <AvatarFallback className=\"text-[9px]\">\n {getInitials(item.actor.name)}\n </AvatarFallback>\n </Avatar>\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <div className=\"flex items-center justify-between gap-ds-02\">\n <div className={cn('min-w-0 flex-1 flex items-center gap-ds-01 flex-wrap', compact ? 'text-ds-xs' : 'text-ds-sm')}>\n {item.actor && (\n <span className=\"font-medium text-surface-fg\">{item.actor.name} </span>\n )}\n {item.detail && (\n <Icon icon={IconChevronRight} size=\"xs\" className={cn('shrink-0 text-surface-fg-subtle transition-transform duration-fast-02 ease-productive-standard', expandedDetail && 'rotate-90')} />\n )}\n {item.detail ? (\n <button\n type=\"button\"\n className=\"rounded-control text-surface-fg-muted hover:underline hover:bg-surface-raised focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9\"\n onClick={handleActionClick}\n aria-expanded={expandedDetail}\n aria-controls={detailId}\n >\n {item.action}\n </button>\n ) : (\n <span className=\"text-surface-fg-muted\">{item.action}</span>\n )}\n </div>\n\n <time\n className={cn(\n 'shrink-0 whitespace-nowrap text-surface-fg-subtle',\n compact ? 'text-ds-xs' : 'text-ds-sm',\n )}\n dateTime={\n (typeof item.timestamp === 'string'\n ? new Date(item.timestamp)\n : item.timestamp\n ).toISOString()\n }\n >\n {formatRelativeTime(item.timestamp)}\n </time>\n </div>\n\n {/* Expandable detail */}\n {expandedDetail && item.detail && (\n <div id={detailId} className=\"mt-ds-02 animate-in fade-in slide-in-from-top-1 text-ds-sm text-surface-fg-muted\">\n {item.detail}\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/**\n * Wraps custom renderItem content with the same dot + layout container\n * that ActivityEntry uses, keeping vertical rhythm consistent.\n */\nfunction CustomEntry({\n item,\n compact,\n children,\n}: {\n item: ActivityItem\n compact: boolean\n children: React.ReactNode\n}) {\n const color = item.color ?? 'default'\n return (\n <div className={cn('relative flex items-start', compact ? 'gap-ds-02' : 'gap-ds-03')}>\n {/* Dot — same as ActivityEntry */}\n <div\n className={cn(\n 'relative z-10 mt-1.5 h-2 w-2 shrink-0 rounded-pill ring-2 ring-surface-base',\n dotColorMap[color],\n )}\n />\n {/* Custom content in the same flex slot as ActivityEntry's content area */}\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n )\n}\n\nfunction GroupHeader({\n label,\n isFirst,\n}: {\n label: string\n isFirst: boolean\n}) {\n return (\n <motion.div\n initial={false}\n animate={{ opacity: 1 }}\n transition={tweens.fade}\n className={cn(\n 'flex items-center gap-ds-03',\n !isFirst ? 'mt-ds-06' : '',\n 'mb-ds-03',\n )}\n >\n <hr className=\"flex-1 border-surface-border\" />\n <span className=\"bg-surface-base px-ds-03 text-ds-xs font-medium uppercase tracking-wider text-surface-fg-subtle\">\n {label}\n </span>\n <hr className=\"flex-1 border-surface-border\" />\n </motion.div>\n )\n}\n\nconst ActivityFeed = React.forwardRef<HTMLDivElement, ActivityFeedProps>(\n (\n {\n items,\n onLoadMore,\n loading = false,\n hasMore = false,\n emptyState,\n compact = false,\n maxInitialItems,\n groupBy = 'none',\n groupLabels,\n renderItem,\n className,\n ...props\n },\n ref,\n ) => {\n const [showAll, setShowAll] = React.useState(false)\n\n if (loading) {\n return (\n <div ref={ref} className={cn('relative', className)} {...props}>\n <LoadingSkeleton compact={compact} />\n </div>\n )\n }\n\n if (items.length === 0) {\n if (emptyState) {\n return (\n <div ref={ref} className={className} {...props}>\n {emptyState}\n </div>\n )\n }\n return null\n }\n\n // maxInitialItems applies to the flat list BEFORE grouping\n const truncated =\n maxInitialItems != null && !showAll && items.length > maxInitialItems\n const visibleItems = truncated ? items.slice(0, maxInitialItems) : items\n\n const useGrouping = groupBy === 'time'\n\n /** Resolve what to render for a single item, respecting renderItem. */\n const resolveEntry = (item: ActivityItem, index: number) => {\n if (renderItem) {\n const custom = renderItem(item, index)\n if (custom !== undefined) {\n return <CustomEntry item={item} compact={compact}>{custom}</CustomEntry>\n }\n }\n return <ActivityEntry item={item} compact={compact} />\n }\n\n return (\n <div ref={ref} className={cn('relative', className)} {...props}>\n {/* Items */}\n {useGrouping ? (\n <div>\n {groupItemsByTime(visibleItems, groupLabels).map((group, gi) => (\n <div key={group.label}>\n <GroupHeader label={group.label} isFirst={gi === 0} />\n {/* Timeline line scoped to this group */}\n <div className={cn('relative flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n <div className=\"absolute bottom-0 left-[3px] top-0 w-px bg-surface-border\" />\n {group.items.map((item, index) => (\n <motion.div key={item.id} initial={false} animate={{ opacity: 1 }} transition={{ ...tweens.fade, delay: index * 0.03 }}>\n {resolveEntry(item, index)}\n </motion.div>\n ))}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className={cn('relative flex flex-col', compact ? 'gap-1' : 'gap-3')}>\n {/* Timeline line */}\n <div className=\"absolute bottom-0 left-[3px] top-0 w-px bg-surface-border\" />\n {visibleItems.map((item, index) => (\n <motion.div key={item.id} initial={false} animate={{ opacity: 1 }} transition={{ ...tweens.fade, delay: index * 0.03 }}>\n {resolveEntry(item, index)}\n </motion.div>\n ))}\n </div>\n )}\n\n {/* Show all button */}\n {truncated && (\n <div className=\"relative mt-ds-03 flex justify-center\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setShowAll(true)}\n >\n Show all ({items.length})\n </Button>\n </div>\n )}\n\n {/* Load more button */}\n {hasMore && onLoadMore && !truncated && (\n <div className=\"relative mt-ds-03 flex justify-center\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onLoadMore}>\n Load more\n </Button>\n </div>\n )}\n </div>\n )\n },\n)\nActivityFeed.displayName = 'ActivityFeed'\n\nexport { ActivityFeed }\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { springs as e } from "../ui/lib/motion.js";
3
- import { t } from "../_chunks/vendor-utils.js";
3
+ import { cva as t } from "../_chunks/vendor-utils.js";
4
4
  import { cn as n } from "../ui/lib/utils.js";
5
5
  import { Avatar as r, AvatarFallback as i, AvatarImage as a } from "../ui/avatar.js";
6
6
  import { Tooltip as o, TooltipContent as s, TooltipProvider as c, TooltipTrigger as l } from "../ui/tooltip.js";
@@ -57,7 +57,7 @@ var g = {
57
57
  let t = L * R;
58
58
  return w === "left" ? `translateX(-${(z - 1 - e) * t}px)` : `translateX(${e * t}px)`;
59
59
  }
60
- let V = "transition-[transform,opacity] duration-300 ease-out hover:z-50 hover:scale-105 group-hover:[&:not(:hover)]:opacity-85";
60
+ let V = "transition-opacity duration-300 ease-out hover:z-50 group-hover:[&:not(:hover)]:opacity-85";
61
61
  return /* @__PURE__ */ f(c, { children: /* @__PURE__ */ p("div", {
62
62
  ref: O,
63
63
  role: "group",
@@ -132,7 +132,7 @@ var g = {
132
132
  type: "button",
133
133
  onClick: S,
134
134
  "aria-label": `${A} more members`,
135
- className: n(_({ size: y }), P, j, `flex cursor-pointer items-center justify-center bg-accent-2 font-semibold text-accent-11 ${M}`, "hover:scale-105 hover:bg-accent-3 transition-[transform,background-color] duration-300 ease-out"),
135
+ className: n(_({ size: y }), P, j, `flex cursor-pointer items-center justify-center bg-accent-2 font-semibold text-accent-11 ${M}`, "hover:bg-accent-3 transition-colors duration-300 ease-out"),
136
136
  style: {
137
137
  zIndex: 0,
138
138
  transform: B(k.length)
@@ -1 +1 @@
1
- {"version":3,"file":"avatar-group.js","names":[],"sources":["../../src/composed/avatar-group.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport type { AvatarRing } from '../ui/avatar'\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '../ui/avatar'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { getInitials } from './lib/string-utils'\n\n// Ring classes applied at group level (not via Avatar's ring prop, which adds ring-offset-2 that gets clipped by overflow-hidden)\nconst groupRingMap: Record<string, string> = {\n lead: 'ring-2 ring-accent-7 ring-offset-1 ring-offset-surface-raised',\n admin: 'ring-2 ring-warning-7 ring-offset-1 ring-offset-surface-raised',\n client: 'ring-2 ring-info-7 ring-offset-1 ring-offset-surface-raised',\n}\n\nconst avatarSizeVariants = cva(\n 'shrink-0 overflow-hidden rounded-pill border-2',\n {\n variants: {\n size: {\n xs: 'h-ds-xs w-ds-xs',\n sm: 'h-ds-sm w-ds-sm',\n md: 'h-ds-md w-ds-md',\n lg: 'h-ds-lg w-ds-lg',\n xl: 'h-ds-xl w-ds-xl',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n },\n)\n\nexport interface AvatarUser {\n name: string\n image?: string | null\n ring?: AvatarRing\n indicator?: 'lead' | 'admin' | React.ReactNode\n}\n\nexport interface AvatarGroupProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof avatarSizeVariants> {\n users: AvatarUser[]\n max?: number\n showTooltip?: boolean\n /** Border color for the group avatars. @default 'surface-raised' */\n borderColor?: 'surface-base' | 'surface-raised'\n /** Callback when the \"+N\" overflow badge is clicked */\n onOverflowClick?: () => void\n /** Custom render function for each avatar */\n renderAvatar?: (user: AvatarUser, index: number) => React.ReactNode\n /** Direction avatars expand on hover. @default 'right' */\n expandDirection?: 'left' | 'right'\n /** How much avatars spread apart on hover. @default 'default' */\n expandAmount?: 'compact' | 'default' | 'wide'\n}\n\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n users,\n max = 4,\n size,\n showTooltip = true,\n borderColor = 'surface-raised',\n onOverflowClick,\n renderAvatar,\n expandDirection = 'right',\n expandAmount = 'default',\n className,\n ...props\n },\n ref,\n ) => {\n const displayed = users.slice(0, max)\n const overflow = users.length - max\n\n const overlapMap = {\n xs: '-ml-ds-02',\n sm: '-ml-ds-02b',\n md: '-ml-ds-03',\n lg: '-ml-ds-04',\n xl: '-ml-ds-05',\n }\n const overlapClass = overlapMap[size ?? 'md']\n\n // Match Avatar fallback font sizes so the \"+N\" badge text is consistent\n const textSizeMap: Record<string, string> = {\n xs: 'text-[9px]',\n sm: 'text-ds-xs',\n md: 'text-ds-sm',\n lg: 'text-ds-md',\n xl: 'text-ds-lg',\n }\n const textSizeClass = textSizeMap[size ?? 'md']\n\n // Indicator dot scales with avatar size (matches Avatar's statusDotSizeMap)\n const indicatorDotSizeMap: Record<string, string> = {\n xs: 'h-1 w-1',\n sm: 'h-1.5 w-1.5',\n md: 'h-2 w-2',\n lg: 'h-2.5 w-2.5',\n xl: 'h-3 w-3',\n }\n const indicatorDotClass = indicatorDotSizeMap[size ?? 'md']\n\n const borderClass =\n borderColor === 'surface-base'\n ? 'border-surface-base'\n : 'border-surface-raised'\n\n const [isHovered, setIsHovered] = React.useState(false)\n\n const overlapPxMap: Record<string, number> = {\n xs: 8, sm: 10, md: 12, lg: 16, xl: 20,\n }\n const resolvedSize = size ?? 'md'\n const overlapPx = overlapPxMap[resolvedSize]\n const expandMultiplier = { compact: 0.5, default: 1, wide: 1.5 }[expandAmount]\n const totalVisible = displayed.length + (overflow > 0 ? 1 : 0)\n\n function getExpandTransform(index: number): string {\n if (!isHovered) return 'translateX(0)'\n const shift = overlapPx * expandMultiplier\n if (expandDirection === 'left') {\n return `translateX(-${(totalVisible - 1 - index) * shift}px)`\n }\n return `translateX(${index * shift}px)`\n }\n\n const spotlightClasses =\n 'transition-[transform,opacity] duration-300 ease-out hover:z-50 hover:scale-105 group-hover:[&:not(:hover)]:opacity-85'\n\n return (\n <TooltipProvider>\n <div\n ref={ref}\n role=\"group\"\n aria-label={`${users.length} team members`}\n tabIndex={0}\n className={cn('group flex items-center', className)}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onFocus={() => setIsHovered(true)}\n onBlur={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setIsHovered(false) }}\n {...props}\n >\n {displayed.map((user, index) => {\n const initials = getInitials(user.name)\n\n const key = `${user.name}-${index}`\n\n if (renderAvatar) {\n // When renderAvatar is provided, the consumer owns the Avatar entirely.\n // Wrapper is positioning-only (overlap, z-index, spotlight) — no size/border/clip\n // so the consumer's Avatar renders at its natural size without being clipped.\n return (\n <div\n key={key}\n className={cn(\n 'shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n {renderAvatar(user, index)}\n </div>\n )\n }\n\n const avatarNode = (\n <div\n key={key}\n className={cn(\n 'relative shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n <Avatar\n size={size}\n className={borderClass}\n >\n {user.image && (\n <AvatarImage src={user.image} alt={user.name} />\n )}\n <AvatarFallback\n colorSeed={user.name}\n >\n {initials}\n </AvatarFallback>\n </Avatar>\n <AnimatePresence>\n {user.indicator && (\n <motion.span\n initial={{ scale: 0, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0, opacity: 0 }}\n transition={springs.snappy}\n className={cn(\n 'absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised',\n indicatorDotClass,\n user.indicator === 'lead' ? 'bg-accent-9' :\n user.indicator === 'admin' ? 'bg-accent-9' : '',\n )}\n >\n {typeof user.indicator !== 'string' && user.indicator}\n </motion.span>\n )}\n </AnimatePresence>\n </div>\n )\n\n if (!showTooltip) return avatarNode\n\n return (\n <Tooltip key={key}>\n <TooltipTrigger asChild>{avatarNode}</TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <p className=\"text-ds-sm\">{user.name}</p>\n </TooltipContent>\n </Tooltip>\n )\n })}\n\n {overflow > 0 && (\n <Tooltip>\n <TooltipTrigger asChild>\n {onOverflowClick ? (\n <button\n type=\"button\"\n onClick={onOverflowClick}\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-pointer items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'hover:scale-105 hover:bg-accent-3 transition-[transform,background-color] duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </button>\n ) : (\n <div\n role=\"img\"\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-default items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'transition-[transform,opacity] duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </div>\n )}\n </TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <div className=\"flex flex-col gap-ds-01\">\n {users.slice(max).map((user, i) => (\n <p key={i} className=\"text-ds-sm\">{user.name}</p>\n ))}\n </div>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n </TooltipProvider>\n )\n },\n)\nAvatarGroup.displayName = 'AvatarGroup'\n\nexport { AvatarGroup }\n"],"mappings":"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"}
1
+ {"version":3,"file":"avatar-group.js","names":[],"sources":["../../src/composed/avatar-group.tsx"],"sourcesContent":["'use client'\n\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport type { AvatarRing } from '../ui/avatar'\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from '../ui/avatar'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { getInitials } from './lib/string-utils'\n\n// Ring classes applied at group level (not via Avatar's ring prop, which adds ring-offset-2 that gets clipped by overflow-hidden)\nconst groupRingMap: Record<string, string> = {\n lead: 'ring-2 ring-accent-7 ring-offset-1 ring-offset-surface-raised',\n admin: 'ring-2 ring-warning-7 ring-offset-1 ring-offset-surface-raised',\n client: 'ring-2 ring-info-7 ring-offset-1 ring-offset-surface-raised',\n}\n\nconst avatarSizeVariants = cva(\n 'shrink-0 overflow-hidden rounded-pill border-2',\n {\n variants: {\n size: {\n xs: 'h-ds-xs w-ds-xs',\n sm: 'h-ds-sm w-ds-sm',\n md: 'h-ds-md w-ds-md',\n lg: 'h-ds-lg w-ds-lg',\n xl: 'h-ds-xl w-ds-xl',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n },\n)\n\nexport interface AvatarUser {\n name: string\n image?: string | null\n ring?: AvatarRing\n indicator?: 'lead' | 'admin' | React.ReactNode\n}\n\nexport interface AvatarGroupProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof avatarSizeVariants> {\n users: AvatarUser[]\n max?: number\n showTooltip?: boolean\n /** Border color for the group avatars. @default 'surface-raised' */\n borderColor?: 'surface-base' | 'surface-raised'\n /** Callback when the \"+N\" overflow badge is clicked */\n onOverflowClick?: () => void\n /** Custom render function for each avatar */\n renderAvatar?: (user: AvatarUser, index: number) => React.ReactNode\n /** Direction avatars expand on hover. @default 'right' */\n expandDirection?: 'left' | 'right'\n /** How much avatars spread apart on hover. @default 'default' */\n expandAmount?: 'compact' | 'default' | 'wide'\n}\n\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n users,\n max = 4,\n size,\n showTooltip = true,\n borderColor = 'surface-raised',\n onOverflowClick,\n renderAvatar,\n expandDirection = 'right',\n expandAmount = 'default',\n className,\n ...props\n },\n ref,\n ) => {\n const displayed = users.slice(0, max)\n const overflow = users.length - max\n\n const overlapMap = {\n xs: '-ml-ds-02',\n sm: '-ml-ds-02b',\n md: '-ml-ds-03',\n lg: '-ml-ds-04',\n xl: '-ml-ds-05',\n }\n const overlapClass = overlapMap[size ?? 'md']\n\n // Match Avatar fallback font sizes so the \"+N\" badge text is consistent\n const textSizeMap: Record<string, string> = {\n xs: 'text-[9px]',\n sm: 'text-ds-xs',\n md: 'text-ds-sm',\n lg: 'text-ds-md',\n xl: 'text-ds-lg',\n }\n const textSizeClass = textSizeMap[size ?? 'md']\n\n // Indicator dot scales with avatar size (matches Avatar's statusDotSizeMap)\n const indicatorDotSizeMap: Record<string, string> = {\n xs: 'h-1 w-1',\n sm: 'h-1.5 w-1.5',\n md: 'h-2 w-2',\n lg: 'h-2.5 w-2.5',\n xl: 'h-3 w-3',\n }\n const indicatorDotClass = indicatorDotSizeMap[size ?? 'md']\n\n const borderClass =\n borderColor === 'surface-base'\n ? 'border-surface-base'\n : 'border-surface-raised'\n\n const [isHovered, setIsHovered] = React.useState(false)\n\n const overlapPxMap: Record<string, number> = {\n xs: 8, sm: 10, md: 12, lg: 16, xl: 20,\n }\n const resolvedSize = size ?? 'md'\n const overlapPx = overlapPxMap[resolvedSize]\n const expandMultiplier = { compact: 0.5, default: 1, wide: 1.5 }[expandAmount]\n const totalVisible = displayed.length + (overflow > 0 ? 1 : 0)\n\n function getExpandTransform(index: number): string {\n if (!isHovered) return 'translateX(0)'\n const shift = overlapPx * expandMultiplier\n if (expandDirection === 'left') {\n return `translateX(-${(totalVisible - 1 - index) * shift}px)`\n }\n return `translateX(${index * shift}px)`\n }\n\n const spotlightClasses =\n 'transition-opacity duration-300 ease-out hover:z-50 group-hover:[&:not(:hover)]:opacity-85'\n\n return (\n <TooltipProvider>\n <div\n ref={ref}\n role=\"group\"\n aria-label={`${users.length} team members`}\n tabIndex={0}\n className={cn('group flex items-center', className)}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onFocus={() => setIsHovered(true)}\n onBlur={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setIsHovered(false) }}\n {...props}\n >\n {displayed.map((user, index) => {\n const initials = getInitials(user.name)\n\n const key = `${user.name}-${index}`\n\n if (renderAvatar) {\n // When renderAvatar is provided, the consumer owns the Avatar entirely.\n // Wrapper is positioning-only (overlap, z-index, spotlight) — no size/border/clip\n // so the consumer's Avatar renders at its natural size without being clipped.\n return (\n <div\n key={key}\n className={cn(\n 'shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n {renderAvatar(user, index)}\n </div>\n )\n }\n\n const avatarNode = (\n <div\n key={key}\n className={cn(\n 'relative shrink-0 rounded-pill',\n index > 0 && overlapClass,\n spotlightClasses,\n user.ring && user.ring !== 'none' && groupRingMap[user.ring],\n )}\n style={{ zIndex: displayed.length - index, transform: getExpandTransform(index) }}\n >\n <Avatar\n size={size}\n className={borderClass}\n >\n {user.image && (\n <AvatarImage src={user.image} alt={user.name} />\n )}\n <AvatarFallback\n colorSeed={user.name}\n >\n {initials}\n </AvatarFallback>\n </Avatar>\n <AnimatePresence>\n {user.indicator && (\n <motion.span\n initial={{ scale: 0, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0, opacity: 0 }}\n transition={springs.snappy}\n className={cn(\n 'absolute top-0 right-0 rounded-pill ring-1 ring-surface-raised',\n indicatorDotClass,\n user.indicator === 'lead' ? 'bg-accent-9' :\n user.indicator === 'admin' ? 'bg-accent-9' : '',\n )}\n >\n {typeof user.indicator !== 'string' && user.indicator}\n </motion.span>\n )}\n </AnimatePresence>\n </div>\n )\n\n if (!showTooltip) return avatarNode\n\n return (\n <Tooltip key={key}>\n <TooltipTrigger asChild>{avatarNode}</TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <p className=\"text-ds-sm\">{user.name}</p>\n </TooltipContent>\n </Tooltip>\n )\n })}\n\n {overflow > 0 && (\n <Tooltip>\n <TooltipTrigger asChild>\n {onOverflowClick ? (\n <button\n type=\"button\"\n onClick={onOverflowClick}\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-pointer items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'hover:bg-accent-3 transition-colors duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </button>\n ) : (\n <div\n role=\"img\"\n aria-label={`${overflow} more members`}\n className={cn(\n avatarSizeVariants({ size }),\n borderClass,\n overlapClass,\n `flex cursor-default items-center justify-center bg-accent-2 font-semibold text-accent-11 ${textSizeClass}`,\n 'transition-[transform,opacity] duration-300 ease-out',\n )}\n style={{ zIndex: 0, transform: getExpandTransform(displayed.length) }}\n >\n +{overflow}\n </div>\n )}\n </TooltipTrigger>\n <TooltipContent\n className=\"border-surface-border-strong bg-surface-base text-surface-fg\"\n sideOffset={6}\n >\n <div className=\"flex flex-col gap-ds-01\">\n {users.slice(max).map((user, i) => (\n <p key={i} className=\"text-ds-sm\">{user.name}</p>\n ))}\n </div>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n </TooltipProvider>\n )\n },\n)\nAvatarGroup.displayName = 'AvatarGroup'\n\nexport { AvatarGroup }\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { springs as e } from "../ui/lib/motion.js";
3
3
  import { cn as t } from "../ui/lib/utils.js";
4
4
  import { Icon as n } from "../ui/icon.js";
5
- import { n as r } from "../_chunks/badge-group.js";
5
+ import { BadgeCompound as r } from "../_chunks/badge-group.js";
6
6
  import { Button as i } from "../ui/button.js";
7
7
  import * as a from "react";
8
8
  import { jsx as o, jsxs as s } from "react/jsx-runtime";
@@ -3,11 +3,11 @@ import { IconProvider as e } from "../ui/icon-context.js";
3
3
  import { springs as t, tweens as n } from "../ui/lib/motion.js";
4
4
  import { cn as r } from "../ui/lib/utils.js";
5
5
  import { Icon as i } from "../ui/icon.js";
6
- import { t as a } from "../_chunks/normalize-icon.js";
6
+ import { normalizeIcon as a } from "../_chunks/normalize-icon.js";
7
7
  import { Dialog as o, DialogContentRaw as s, DialogDescription as c, DialogOverlay as l, DialogPortal as ee, DialogTitle as u } from "../ui/dialog.js";
8
8
  import { VisuallyHidden as te } from "../ui/visually-hidden.js";
9
- import { n as ne } from "../_chunks/motion-provider.js";
10
- import { n as d, r as f, t as p } from "../_chunks/keybinding.js";
9
+ import { useMotion as ne } from "../_chunks/motion-provider.js";
10
+ import { getIsMac as d, getModifierDisplay as f, matchesKeybinding as p } from "../_chunks/keybinding.js";
11
11
  import * as m from "react";
12
12
  import { jsx as h, jsxs as g } from "react/jsx-runtime";
13
13
  import { IconArrowDown as re, IconArrowUp as ie, IconCornerDownLeft as _, IconSearch as ae } from "@tabler/icons-react";
@@ -28,7 +28,7 @@ var b = m.forwardRef(function({ groups: b = [], placeholder: x = "Search or jump
28
28
  let R = m.useCallback((e) => {
29
29
  let t = typeof e == "function" ? e(L.current) : e;
30
30
  N || F(t), E?.(t);
31
- }, [N, E]), [z, B] = m.useState(""), [V, H] = m.useState(0), U = m.useRef(null), W = m.useRef(null), G = m.useId(), K = `command-palette-listbox-${G}`, { reducedMotion: q } = ne(), J = { duration: 0 }, ue = m.useMemo(() => p(), []), Y = m.useMemo(() => {
31
+ }, [N, E]), [z, B] = m.useState(""), [V, H] = m.useState(0), U = m.useRef(null), W = m.useRef(null), G = m.useId(), K = `command-palette-listbox-${G}`, { reducedMotion: q } = ne(), J = { duration: 0 }, ue = m.useMemo(() => d(), []), Y = m.useMemo(() => {
32
32
  if (!z.trim()) return b;
33
33
  let e = z.toLowerCase();
34
34
  return b.map((t) => ({
@@ -40,7 +40,7 @@ var b = m.forwardRef(function({ groups: b = [], placeholder: x = "Search or jump
40
40
  if (D === !1) return;
41
41
  let e = Array.isArray(D) ? D : [D];
42
42
  function t(t) {
43
- for (let n of e) if (f(t, n)) {
43
+ for (let n of e) if (p(t, n)) {
44
44
  t.preventDefault(), R((e) => !e);
45
45
  return;
46
46
  }
@@ -229,7 +229,7 @@ var b = m.forwardRef(function({ groups: b = [], placeholder: x = "Search or jump
229
229
  className: "flex shrink-0 items-center gap-ds-01",
230
230
  children: y(n.shortcut).map((e, t) => /* @__PURE__ */ h("kbd", {
231
231
  className: r("inline-flex min-w-ds-05b items-center justify-center rounded border px-ds-02b py-ds-01 text-ds-xs font-medium shadow-kbd transition-colors duration-fast-02 ease-productive-standard", s ? "bg-accent-2 text-accent-11 border-accent-6" : "bg-surface-raised text-surface-fg-subtle border-surface-border-strong"),
232
- children: e === "Ctrl" || e === "ctrl" ? d(ue) : e
232
+ children: e === "Ctrl" || e === "ctrl" ? f(ue) : e
233
233
  }, t))
234
234
  }),
235
235
  /* @__PURE__ */ h(oe, { children: s && /* @__PURE__ */ h(v.span, {
@@ -1,4 +1,4 @@
1
- import { t as e } from "../_chunks/vendor-utils.js";
1
+ import { cva as e } from "../_chunks/vendor-utils.js";
2
2
  import { cn as t } from "../ui/lib/utils.js";
3
3
  import * as n from "react";
4
4
  import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn as e } from "../../ui/lib/utils.js";
3
3
  import { Icon as t } from "../../ui/icon.js";
4
- import { i as n, r, t as i } from "../../_chunks/popover.js";
5
- import { n as a, t as o } from "../../_chunks/use-calendar.js";
4
+ import { Popover as n, PopoverContent as r, PopoverTrigger as i } from "../../_chunks/popover.js";
5
+ import { CalendarGrid as a, useCalendar as o } from "../../_chunks/use-calendar.js";
6
6
  import * as s from "react";
7
7
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
8
  import { IconCalendarClock as u, IconCalendarEvent as d, IconClock as f } from "@tabler/icons-react";
@@ -206,10 +206,10 @@ var M = s.forwardRef(function({ value: o, onChange: u, placeholder: f = "Pick a
206
206
  });
207
207
  }
208
208
  };
209
- return /* @__PURE__ */ l(i, {
209
+ return /* @__PURE__ */ l(n, {
210
210
  open: S,
211
211
  onOpenChange: w,
212
- children: [/* @__PURE__ */ c(n, {
212
+ children: [/* @__PURE__ */ c(i, {
213
213
  asChild: !0,
214
214
  children: /* @__PURE__ */ l("button", {
215
215
  ref: b,
@@ -408,10 +408,10 @@ var F = s.forwardRef(function({ startDate: o, endDate: u, onChange: f, placehold
408
408
  default: return Z();
409
409
  }
410
410
  }, $ = T && T.length > 0;
411
- return /* @__PURE__ */ l(i, {
411
+ return /* @__PURE__ */ l(n, {
412
412
  open: k,
413
413
  onOpenChange: M,
414
- children: [/* @__PURE__ */ c(n, {
414
+ children: [/* @__PURE__ */ c(i, {
415
415
  asChild: !0,
416
416
  children: /* @__PURE__ */ l("button", {
417
417
  ref: O,
@@ -510,10 +510,10 @@ var R = s.forwardRef(function({ value: o, onChange: d, minDate: f, maxDate: p, d
510
510
  });
511
511
  }
512
512
  }, Q = "h-ds-sm rounded-control border border-surface-border-strong bg-surface-overlay px-ds-02 text-ds-md text-surface-fg transition-colors duration-fast-01 ease-productive-standard hover:border-surface-border-strong focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 cursor-pointer appearance-none";
513
- return /* @__PURE__ */ l(i, {
513
+ return /* @__PURE__ */ l(n, {
514
514
  open: E,
515
515
  onOpenChange: D,
516
- children: [/* @__PURE__ */ c(n, {
516
+ children: [/* @__PURE__ */ c(i, {
517
517
  asChild: !0,
518
518
  children: /* @__PURE__ */ l("button", {
519
519
  ref: T,
@@ -639,10 +639,10 @@ var V = s.forwardRef(function({ value: a, onChange: o, format: u = "12h", minute
639
639
  u,
640
640
  m
641
641
  ]), H = u === "12h" ? A : E, U = "flex flex-col gap-ds-01 overflow-y-auto max-h-[200px] px-ds-01", W = "flex h-ds-sm w-full items-center justify-center rounded-control text-ds-md transition-colors duration-fast-01 ease-productive-standard cursor-pointer", G = "bg-accent-9 text-accent-fg", K = "text-surface-fg hover:bg-surface-raised-hover";
642
- return /* @__PURE__ */ l(i, {
642
+ return /* @__PURE__ */ l(n, {
643
643
  open: w,
644
644
  onOpenChange: T,
645
- children: [/* @__PURE__ */ c(n, {
645
+ children: [/* @__PURE__ */ c(i, {
646
646
  asChild: !0,
647
647
  children: /* @__PURE__ */ l("button", {
648
648
  ref: C,