@devalok/shilp-sutra 0.49.4 → 0.50.0

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 (243) hide show
  1. package/AGENTS.md +12 -4
  2. package/MIGRATION.md +6 -0
  3. package/dist/_chunks/badge-group.js +12 -11
  4. package/dist/_chunks/badge-group.js.map +1 -1
  5. package/dist/_chunks/block-shell.js +1 -1
  6. package/dist/_chunks/block-shell.js.map +1 -1
  7. package/dist/_chunks/chat.js +18 -18
  8. package/dist/_chunks/chat.js.map +1 -1
  9. package/dist/_chunks/document-preview.js +1 -1
  10. package/dist/_chunks/document-preview.js.map +1 -1
  11. package/dist/_chunks/emoji-suggestion.js +1 -1
  12. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  13. package/dist/_chunks/grid-lines.js +2 -2
  14. package/dist/_chunks/grid-lines.js.map +1 -1
  15. package/dist/_chunks/image-preview.js +1 -1
  16. package/dist/_chunks/image-preview.js.map +1 -1
  17. package/dist/_chunks/mention-suggestion.js +2 -2
  18. package/dist/_chunks/mention-suggestion.js.map +1 -1
  19. package/dist/_chunks/oauth-button.js +8 -8
  20. package/dist/_chunks/oauth-button.js.map +1 -1
  21. package/dist/_chunks/shared.js +2 -2
  22. package/dist/_chunks/shared.js.map +1 -1
  23. package/dist/_chunks/success.js +5 -5
  24. package/dist/_chunks/success.js.map +1 -1
  25. package/dist/_chunks/tiptap.js +3956 -1796
  26. package/dist/_chunks/tiptap.js.map +1 -1
  27. package/dist/_chunks/tooltip.js +2 -2
  28. package/dist/_chunks/tooltip.js.map +1 -1
  29. package/dist/_chunks/tree-view.js +2 -2
  30. package/dist/_chunks/tree-view.js.map +1 -1
  31. package/dist/_chunks/use-calendar.js +3 -3
  32. package/dist/_chunks/use-calendar.js.map +1 -1
  33. package/dist/ai/block-renderer.js +1 -1
  34. package/dist/ai/block-renderer.js.map +1 -1
  35. package/dist/ai/blocks/error.js +1 -1
  36. package/dist/ai/blocks/error.js.map +1 -1
  37. package/dist/ai/command-bar.js +15 -15
  38. package/dist/ai/command-bar.js.map +1 -1
  39. package/dist/ai/conversation.js +6 -6
  40. package/dist/ai/conversation.js.map +1 -1
  41. package/dist/composed/activity-feed.js +4 -4
  42. package/dist/composed/activity-feed.js.map +1 -1
  43. package/dist/composed/avatar-group.js +6 -6
  44. package/dist/composed/avatar-group.js.map +1 -1
  45. package/dist/composed/bulk-action-bar.js +1 -1
  46. package/dist/composed/bulk-action-bar.js.map +1 -1
  47. package/dist/composed/command-palette.js +12 -12
  48. package/dist/composed/command-palette.js.map +1 -1
  49. package/dist/composed/content-card.js +1 -1
  50. package/dist/composed/content-card.js.map +1 -1
  51. package/dist/composed/date-picker/index.js +17 -17
  52. package/dist/composed/date-picker/index.js.map +1 -1
  53. package/dist/composed/deadline-indicator.js +2 -2
  54. package/dist/composed/deadline-indicator.js.map +1 -1
  55. package/dist/composed/diff.d.ts +108 -0
  56. package/dist/composed/diff.d.ts.map +1 -0
  57. package/dist/composed/diff.js +1004 -0
  58. package/dist/composed/diff.js.map +1 -0
  59. package/dist/composed/emoji-picker.js +6 -6
  60. package/dist/composed/emoji-picker.js.map +1 -1
  61. package/dist/composed/empty-state.js +2 -2
  62. package/dist/composed/empty-state.js.map +1 -1
  63. package/dist/composed/error-boundary.js +6 -6
  64. package/dist/composed/error-boundary.js.map +1 -1
  65. package/dist/composed/file-preview.js +5 -5
  66. package/dist/composed/file-preview.js.map +1 -1
  67. package/dist/composed/filter-bar.js +3 -3
  68. package/dist/composed/filter-bar.js.map +1 -1
  69. package/dist/composed/form-section.js +2 -2
  70. package/dist/composed/form-section.js.map +1 -1
  71. package/dist/composed/inline-edit.d.ts +1 -1
  72. package/dist/composed/inline-edit.d.ts.map +1 -1
  73. package/dist/composed/inline-edit.js.map +1 -1
  74. package/dist/composed/loading-skeleton.js +3 -3
  75. package/dist/composed/loading-skeleton.js.map +1 -1
  76. package/dist/composed/markdown-viewer.js +11 -11
  77. package/dist/composed/markdown-viewer.js.map +1 -1
  78. package/dist/composed/master-detail.js +1 -1
  79. package/dist/composed/master-detail.js.map +1 -1
  80. package/dist/composed/member-picker.js +2 -2
  81. package/dist/composed/member-picker.js.map +1 -1
  82. package/dist/composed/multi-select-popover.js +5 -5
  83. package/dist/composed/multi-select-popover.js.map +1 -1
  84. package/dist/composed/page-header.js +3 -3
  85. package/dist/composed/page-header.js.map +1 -1
  86. package/dist/composed/page-skeletons.js +4 -4
  87. package/dist/composed/page-skeletons.js.map +1 -1
  88. package/dist/composed/priority-indicator.js +1 -1
  89. package/dist/composed/priority-indicator.js.map +1 -1
  90. package/dist/composed/responsive-modal.js +2 -2
  91. package/dist/composed/responsive-modal.js.map +1 -1
  92. package/dist/composed/rich-chat-input.js +27 -27
  93. package/dist/composed/rich-chat-input.js.map +1 -1
  94. package/dist/composed/rich-text-editor.d.ts +81 -6
  95. package/dist/composed/rich-text-editor.d.ts.map +1 -1
  96. package/dist/composed/rich-text-editor.js +365 -210
  97. package/dist/composed/rich-text-editor.js.map +1 -1
  98. package/dist/composed/schedule-view.js +4 -4
  99. package/dist/composed/schedule-view.js.map +1 -1
  100. package/dist/shell/bottom-navbar.js +4 -4
  101. package/dist/shell/bottom-navbar.js.map +1 -1
  102. package/dist/shell/notification-center.js +13 -13
  103. package/dist/shell/notification-center.js.map +1 -1
  104. package/dist/shell/notification-preferences.js +10 -10
  105. package/dist/shell/notification-preferences.js.map +1 -1
  106. package/dist/shell/sidebar.js +9 -9
  107. package/dist/shell/sidebar.js.map +1 -1
  108. package/dist/shell/top-bar.js +8 -8
  109. package/dist/shell/top-bar.js.map +1 -1
  110. package/dist/tokens/semantic.css +6 -1
  111. package/dist/tokens/typography-semantic.css +6 -6
  112. package/dist/tokens/utilities.css +8 -0
  113. package/dist/ui/accordion.js +2 -2
  114. package/dist/ui/accordion.js.map +1 -1
  115. package/dist/ui/alert-dialog.js +4 -4
  116. package/dist/ui/alert-dialog.js.map +1 -1
  117. package/dist/ui/alert.js +1 -1
  118. package/dist/ui/alert.js.map +1 -1
  119. package/dist/ui/autocomplete.d.ts.map +1 -1
  120. package/dist/ui/autocomplete.js +11 -3
  121. package/dist/ui/autocomplete.js.map +1 -1
  122. package/dist/ui/avatar.js +5 -5
  123. package/dist/ui/avatar.js.map +1 -1
  124. package/dist/ui/badge-group.d.ts +2 -6
  125. package/dist/ui/badge-group.d.ts.map +1 -1
  126. package/dist/ui/badge-indicator.d.ts +2 -6
  127. package/dist/ui/badge-indicator.d.ts.map +1 -1
  128. package/dist/ui/badge-indicator.js +19 -18
  129. package/dist/ui/badge-indicator.js.map +1 -1
  130. package/dist/ui/badge.d.ts +2 -4
  131. package/dist/ui/badge.d.ts.map +1 -1
  132. package/dist/ui/banner.js +1 -1
  133. package/dist/ui/banner.js.map +1 -1
  134. package/dist/ui/breadcrumb.js +1 -1
  135. package/dist/ui/breadcrumb.js.map +1 -1
  136. package/dist/ui/button.js +7 -7
  137. package/dist/ui/button.js.map +1 -1
  138. package/dist/ui/card.d.ts +1 -1
  139. package/dist/ui/card.js +1 -1
  140. package/dist/ui/card.js.map +1 -1
  141. package/dist/ui/charts/gauge-chart.js +2 -2
  142. package/dist/ui/charts/gauge-chart.js.map +1 -1
  143. package/dist/ui/charts/pie-chart.js +1 -1
  144. package/dist/ui/charts/pie-chart.js.map +1 -1
  145. package/dist/ui/charts/radar-chart.d.ts +19 -1
  146. package/dist/ui/charts/radar-chart.d.ts.map +1 -1
  147. package/dist/ui/charts/radar-chart.js +80 -42
  148. package/dist/ui/charts/radar-chart.js.map +1 -1
  149. package/dist/ui/code.js +2 -2
  150. package/dist/ui/code.js.map +1 -1
  151. package/dist/ui/color-input.d.ts.map +1 -1
  152. package/dist/ui/color-input.js +164 -157
  153. package/dist/ui/color-input.js.map +1 -1
  154. package/dist/ui/combobox.d.ts.map +1 -1
  155. package/dist/ui/combobox.js +143 -142
  156. package/dist/ui/combobox.js.map +1 -1
  157. package/dist/ui/context-menu.js +6 -6
  158. package/dist/ui/context-menu.js.map +1 -1
  159. package/dist/ui/data-table-body.js +1 -1
  160. package/dist/ui/data-table-body.js.map +1 -1
  161. package/dist/ui/data-table-bulk-actions.js +1 -1
  162. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  163. package/dist/ui/data-table-card.js +1 -1
  164. package/dist/ui/data-table-card.js.map +1 -1
  165. package/dist/ui/data-table-header.js +1 -1
  166. package/dist/ui/data-table-header.js.map +1 -1
  167. package/dist/ui/data-table-pagination.js +3 -3
  168. package/dist/ui/data-table-pagination.js.map +1 -1
  169. package/dist/ui/data-table-toolbar.js +1 -1
  170. package/dist/ui/data-table-toolbar.js.map +1 -1
  171. package/dist/ui/data-table.js +1 -1
  172. package/dist/ui/data-table.js.map +1 -1
  173. package/dist/ui/dialog.js +2 -2
  174. package/dist/ui/dialog.js.map +1 -1
  175. package/dist/ui/dot.js +4 -4
  176. package/dist/ui/dot.js.map +1 -1
  177. package/dist/ui/dropdown-menu.js +6 -6
  178. package/dist/ui/dropdown-menu.js.map +1 -1
  179. package/dist/ui/file-upload.js +5 -5
  180. package/dist/ui/file-upload.js.map +1 -1
  181. package/dist/ui/form.js +1 -1
  182. package/dist/ui/form.js.map +1 -1
  183. package/dist/ui/input-otp.d.ts.map +1 -1
  184. package/dist/ui/input-otp.js +3 -3
  185. package/dist/ui/input-otp.js.map +1 -1
  186. package/dist/ui/input.js +8 -8
  187. package/dist/ui/input.js.map +1 -1
  188. package/dist/ui/label.js +1 -1
  189. package/dist/ui/label.js.map +1 -1
  190. package/dist/ui/lib/utils.d.ts.map +1 -1
  191. package/dist/ui/lib/utils.js +42 -13
  192. package/dist/ui/lib/utils.js.map +1 -1
  193. package/dist/ui/menubar.js +7 -7
  194. package/dist/ui/menubar.js.map +1 -1
  195. package/dist/ui/navigation-menu.js +1 -1
  196. package/dist/ui/navigation-menu.js.map +1 -1
  197. package/dist/ui/number-input.d.ts.map +1 -1
  198. package/dist/ui/number-input.js +6 -5
  199. package/dist/ui/number-input.js.map +1 -1
  200. package/dist/ui/pagination.js +1 -1
  201. package/dist/ui/pagination.js.map +1 -1
  202. package/dist/ui/progress-ring.js +3 -3
  203. package/dist/ui/progress-ring.js.map +1 -1
  204. package/dist/ui/progress.js +2 -2
  205. package/dist/ui/progress.js.map +1 -1
  206. package/dist/ui/segmented-control.js +3 -3
  207. package/dist/ui/segmented-control.js.map +1 -1
  208. package/dist/ui/select.d.ts.map +1 -1
  209. package/dist/ui/select.js +7 -6
  210. package/dist/ui/select.js.map +1 -1
  211. package/dist/ui/sheet.js +2 -2
  212. package/dist/ui/sheet.js.map +1 -1
  213. package/dist/ui/sidebar.js +7 -7
  214. package/dist/ui/sidebar.js.map +1 -1
  215. package/dist/ui/split-button.js +6 -6
  216. package/dist/ui/split-button.js.map +1 -1
  217. package/dist/ui/stat-card.d.ts +1 -1
  218. package/dist/ui/stat-card.js +7 -7
  219. package/dist/ui/stat-card.js.map +1 -1
  220. package/dist/ui/stepper.js +3 -3
  221. package/dist/ui/stepper.js.map +1 -1
  222. package/dist/ui/table.js +3 -3
  223. package/dist/ui/table.js.map +1 -1
  224. package/dist/ui/tabs.js +3 -3
  225. package/dist/ui/tabs.js.map +1 -1
  226. package/dist/ui/text.d.ts.map +1 -1
  227. package/dist/ui/text.js.map +1 -1
  228. package/dist/ui/textarea.d.ts.map +1 -1
  229. package/dist/ui/textarea.js +7 -6
  230. package/dist/ui/textarea.js.map +1 -1
  231. package/dist/ui/toast.js +11 -11
  232. package/dist/ui/toast.js.map +1 -1
  233. package/dist/ui/toggle.js +3 -3
  234. package/dist/ui/toggle.js.map +1 -1
  235. package/dist/ui/tooltip.js +1 -1
  236. package/dist/ui/tooltip.js.map +1 -1
  237. package/docs/components/composed/diff.md +50 -0
  238. package/docs/components/ui/data-table.md +2 -2
  239. package/llms.txt +2 -1
  240. package/mcp-manifest.json +147 -3
  241. package/package.json +8 -1
  242. package/skill/SKILL.md +1 -1
  243. package/skill/references/components.md +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-ds-xs font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n // Kept current every render so the mount-time keydown handler (empty deps)\n // reads the live rate instead of the stale initial 1x. See #91.\n const playbackRateRef = React.useRef(playbackRate)\n playbackRateRef.current = playbackRate\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRateRef.current as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-ds-sm font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-ds-sm font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-body-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-caption font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n // Kept current every render so the mount-time keydown handler (empty deps)\n // reads the live rate instead of the stale initial 1x. See #91.\n const playbackRateRef = React.useRef(playbackRate)\n playbackRateRef.current = playbackRate\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRateRef.current as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-body-sm font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-body-sm font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-body-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAwB,EAAa,EAAE,QAAK,aAAU,cAAgF;CACpI,IAAM,IAAW,EAAM,OAAyB,IAAI,GAC9C,IAAe,EAAM,OAAuB,IAAI,GAChD,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,CAAC,GAAU,KAAe,EAAM,SAAS,CAAC,GAC1C,CAAC,GAAU,KAAe,EAAM,SAAS,CAAC,GAC1C,CAAC,GAAa,KAAkB,EAAM,SAAS,CAAC,GAChD,CAAC,GAAQ,KAAa,EAAM,SAAS,CAAC,GACtC,CAAC,GAAO,KAAY,EAAM,SAAS,EAAK,GACxC,CAAC,GAAO,KAAY,EAAM,SAAS,EAAK;CAG9C,EAAM,gBAAgB;EACpB,SAAS,EAAc,GAAkB;GACvC,IAAM,IAAK,EAAa;GACxB,IAAI,CAAC,KAAM,CAAC,EAAG,SAAS,SAAS,aAAa,KAAK,SAAS,kBAAkB,GAAI;GAClF,IAAM,IAAI,EAAS;GACd,OAEL,QAAQ,EAAE,KAAV;IACE,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,GAAW,OACL,IAAM,EAAE,MAAM,IAAQ,EAAE,KAAK,GAC1B,CAAC,EACT;KACD;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,EAAE,YAAY,GAAG,EAAE,cAAc,CAAC;KAC3D;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,CAAC;KAC7C;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,GAAS,OACP,EAAE,QAAQ,CAAC,GACJ,CAAC,EACT;KACD;GACJ;EACF;EAEA,OADA,SAAS,iBAAiB,WAAW,CAAa,SACrC,SAAS,oBAAoB,WAAW,CAAa;CAEpE,GAAG,CAAC,CAAC;CAEL,SAAS,IAAa;EACf,EAAS,YACV,IAAS,EAAS,QAAQ,MAAM,IAC/B,EAAS,QAAQ,KAAK,GAC3B,EAAW,CAAC,CAAO;CACrB;CAEA,SAAS,EAAW,GAAsB;EACpC,CAAC,EAAS,WAAW,CAAC,MAC1B,EAAS,QAAQ,cAAe,IAAe,MAAO;CACxD;CAEA,SAAS,IAAa;EACpB,IAAI,CAAC,EAAS,SAAS;EACvB,IAAM,IAAO,CAAC;EAEd,AADA,EAAS,CAAI,GACb,EAAS,QAAQ,QAAQ;CAC3B;CAIA,OAFI,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;CAAM,CAAA,IAGzE,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;EAAkE,UAAU;YAA9G;GAEE,kBAAC,SAAD;IACE,KAAK;IACL,KAAK;IACL,oBAAoB;KACb,EAAS,YACd,EAAe,EAAS,QAAQ,WAAW,GACvC,EAAS,QAAQ,YAAU,EAAa,EAAS,QAAQ,cAAc,EAAS,QAAQ,WAAY,GAAG;IAC7G;IACA,wBAAwB,EAAY,EAAS,SAAS,YAAY,CAAC;IACnE,eAAe,EAAW,EAAK;IAC/B,eAAe;KAAkB,AAAhB,EAAS,EAAI,GAAG,IAAU,sBAAsB;IAAE;GACpE,CAAA;GAGD,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,OAAO;KACP,KAAK;KACL,MAAM;KACN,eAAe;KACf,MAAK;KACL,WAAU;IACX,CAAA;GACE,CAAA;GAGL,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,SAAQ;MACR,MAAK;MACL,SAAS;MACT,cAAY,IAAU,UAAU;MAChC,OAAO,IAAU,UAAU;MAC3B,WAAU;gBAET,IACC,kBAAC,GAAD;OAAM,MAAM;OAAiB,MAAK;MAAM,CAAA,IAExC,kBAAC,GAAD;OAAM,MAAM;OAAgB,MAAK;OAAK,WAAU;MAAU,CAAA;KAEtD,CAAA;KAGR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,KACC,kBAAC,GAAD;OAAe,IAAG;OAAI,MAAK;OAAS,WAAU;iBAC3C;MACY,CAAA,GAEjB,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAW,CAAW;QAAE;QAAI,EAAW,CAAQ;OAC/C;QACA;;KAGL,kBAAC,GAAD;MACU;MACD;MACP,iBAAiB,MAAM;OAGrB,AAFA,EAAU,CAAC,GACP,EAAS,YAAS,EAAS,QAAQ,SAAS,IAC5C,IAAI,KAAK,MACX,EAAS,EAAK,GACV,EAAS,YAAS,EAAS,QAAQ,QAAQ;MAEnD;MACA,cAAc;MACd,SAAQ;KACT,CAAA;IACE;;EACF;;AAET;;;AClJA,SAAwB,EAAa,EAAE,QAAK,aAAU,cAA+E;CACnI,IAAM,CAAC,GAAQ,KAAa,EAAM,SAAS,EAAK,GAC1C,CAAC,GAAO,KAAY,EAAM,SAAS,EAAK;CAY9C,OATA,EAAM,gBAAgB;EACpB,IAAM,IAAQ,iBAAiB;GAC7B,AAAK,MAAU,EAAS,EAAI,GAAG,IAAU,iBAAiB;EAC5D,GAAG,IAAK;EACR,aAAa,aAAa,CAAK;CACjC,GAAG,CAAC,GAAQ,CAAO,CAAC,GAEhB,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;CAAM,CAAA,IAGzE,kBAAC,OAAD;EAAK,WAAU;EAAkD,OAAO,EAAE,aAAa,SAAS;YAAhG,CACG,CAAC,KAAU,kBAAC,GAAD,EAAU,WAAU,mBAAoB,CAAA,GACpD,kBAAC,EAAO,QAAR;GACE,KAAK;GACL,OAAM;GACN,cAAc,EAAU,EAAI;GAC5B,eAAe;IAAkB,AAAhB,EAAS,EAAI,GAAG,IAAU,sBAAsB;GAAE;GACnE,SAAS,EAAE,SAAS,EAAE;GACtB,SAAS,EAAE,SAAS,KAAe;GACnC,YAAY,EAAO;GACnB,WAAU;GACV,iBAAA;EACD,CAAA,CACE;;AAET;;;ACzBA,IAAM,IAAiB;CAAC;CAAK;CAAM;CAAG;CAAM;CAAK;AAAC;AAElD,SAAwB,EAAa,EAAE,QAAK,cAA6D;CACvG,IAAM,IAAW,EAAM,OAAyB,IAAI,GAC9C,IAAe,EAAM,OAAuB,IAAI,GAChD,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,GAC5C,CAAC,GAAU,KAAe,EAAM,SAAS,CAAC,GAC1C,CAAC,GAAU,KAAe,EAAM,SAAS,CAAC,GAC1C,CAAC,GAAa,KAAkB,EAAM,SAAS,CAAC,GAChD,CAAC,GAAO,KAAY,EAAM,SAAS,EAAK,GACxC,CAAC,GAAO,KAAY,EAAM,SAAS,EAAK,GACxC,CAAC,GAAc,KAAmB,EAAM,SAAS,EAAI,GACrD,CAAC,GAAc,KAAmB,EAAM,SAAS,CAAC,GAGlD,IAAkB,EAAM,OAAO,CAAY;CACjD,EAAgB,UAAU;CAC1B,IAAM,IAAY,EAAM,OAAkD,KAAA,CAAS;CAOnF,AALA,EAAM,sBACS,aAAa,EAAU,OAAO,GAC1C,CAAC,CAAC,GAGL,EAAM,gBAAgB;EACpB,SAAS,EAAc,GAAkB;GACvC,IAAM,IAAK,EAAa;GACxB,IAAI,CAAC,KAAM,CAAC,EAAG,SAAS,SAAS,aAAa,KAAK,SAAS,kBAAkB,GAAI;GAClF,IAAM,IAAI,EAAS;GACd,OAEL,QAAQ,EAAE,KAAV;IACE,KAAK;IACL,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,GAAW,OACL,IAAM,EAAE,MAAM,IAAQ,EAAE,KAAK,GAC1B,CAAC,EACT;KACD;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,EAAE,UAAU,EAAE,cAAc,CAAC;KACtD;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,CAAC;KAC7C;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,GAAG,EAAE,cAAc,EAAE;KAC9C;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,cAAc,KAAK,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE;KACvD;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,GAAS,OACP,EAAE,QAAQ,CAAC,GACJ,CAAC,EACT;KACD;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAE,oBAAoB;KACtB;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAkB,CAAC;KACnB;IACF,KAAK;KAEH,AADA,EAAE,eAAe,GACjB,EAAkB,EAAE;KACpB;GACJ;EACF;EAEA,OADA,SAAS,iBAAiB,WAAW,CAAa,SACrC,SAAS,oBAAoB,WAAW,CAAa;CACpE,GAAG,CAAC,CAAC;CAEL,SAAS,EAAkB,GAAmB;EAC5C,IAAM,IAAM,EAAe,QAAQ,EAAgB,OAAwC,GAErF,IAAO,EADG,KAAK,IAAI,GAAG,KAAK,IAAI,EAAe,SAAS,GAAG,IAAM,CAAS,CACnD;EAE5B,AADA,EAAgB,CAAI,GAChB,EAAS,YAAS,EAAS,QAAQ,eAAe;CACxD;CAEA,SAAS,IAAa;EACf,EAAS,YACV,IAAS,EAAS,QAAQ,MAAM,IAC/B,EAAS,QAAQ,KAAK,GAC3B,EAAW,CAAC,CAAO;CACrB;CAEA,SAAS,EAAW,GAAsB;EACpC,CAAC,EAAS,WAAW,CAAC,MAC1B,EAAS,QAAQ,cAAe,IAAe,MAAO;CACxD;CAEA,SAAS,IAAkB;EAGzB,AAFA,EAAgB,EAAI,GACpB,aAAa,EAAU,OAAO,GAC1B,MACF,EAAU,UAAU,iBAAiB,EAAgB,EAAK,GAAG,GAAI;CAErE;CAIA,OAFI,IAAc,kBAAC,GAAD;EAAe,SAAQ;EAA4B;CAAM,CAAA,IAGzE,kBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,aAAa;EACb,oBAAoB,KAAW,EAAgB,EAAK;EACpD,UAAU;YALZ;GAQE,kBAAC,SAAD;IACE,KAAK;IACL,KAAK;IACL,WAAU;IACV,SAAS;IACF;IACP,oBAAoB;KACb,EAAS,YACd,EAAe,EAAS,QAAQ,WAAW,GACvC,EAAS,QAAQ,YAAU,EAAa,EAAS,QAAQ,cAAc,EAAS,QAAQ,WAAY,GAAG;IAC7G;IACA,wBAAwB,EAAY,EAAS,SAAS,YAAY,CAAC;IACnE,eAAe;KAAqB,AAAnB,EAAW,EAAK,GAAG,EAAgB,EAAI;IAAE;IAC1D,eAAe;KAAkB,AAAhB,EAAS,EAAI,GAAG,IAAU,sBAAsB;IAAE;IACnE,aAAA;GACD,CAAA;GAGA,CAAC,KACA,kBAAC,EAAO,QAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;IAAI;IAClC,SAAS;KAAE,SAAS;KAAG,OAAO;IAAE;IAChC,YAAY,EAAQ;IACpB,SAAS;IACT,WAAU;IACV,cAAW;cAEX,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAM,MAAM;MAAgB,MAAK;MAAK,WAAU;KAA0B,CAAA;IACvE,CAAA;GACQ,CAAA;GAIjB,kBAAC,GAAD,EAAA,UACG,KAAgB,IAAW,KAC1B,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,GAAG;IAAG;IAC7B,SAAS;KAAE,SAAS;KAAG,GAAG;IAAE;IAC5B,MAAM;KAAE,SAAS;KAAG,GAAG;IAAG;IAC1B,YAAY,EAAO;IACnB,WAAU;cALZ,CAQE,kBAAC,GAAD;KACE,WAAU;KACV,OAAO;KACP,KAAK;KACL,MAAM;KACN,eAAe;KACf,MAAK;KACL,WAAU;IACX,CAAA,GAGD,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OAAQ,SAAS;OAAY,WAAU;OAAiC,cAAY,IAAU,UAAU;OAAQ,OAAO,IAAU,UAAU;iBACxI,IAAU,kBAAC,GAAD;QAAM,MAAM;QAAiB,MAAK;OAAM,CAAA,IAAI,kBAAC,GAAD;QAAM,MAAM;QAAgB,MAAK;OAAM,CAAA;MACxF,CAAA;MACR,kBAAC,GAAD;OACE,QAAQ;OACD;OACP,iBAAiB,MAAM;QAErB,AADI,EAAS,YAAS,EAAS,QAAQ,SAAS,IAC5C,IAAI,KAAK,MAAS,EAAS,EAAK,GAAO,EAAS,YAAS,EAAS,QAAQ,QAAQ;OACxF;OACA,oBAAoB;QAAoB,AAAlB,EAAS,CAAC,CAAK,GAAO,EAAS,YAAS,EAAS,QAAQ,QAAQ,CAAC;OAAM;OAC9F,SAAQ;MACT,CAAA;MACD,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAW,CAAW;QAAE;QAAI,EAAW,CAAQ;OAC5C;;MACN,kBAAC,OAAD,EAAK,WAAU,SAAU,CAAA;MACzB,kBAAC,UAAD;OACE,eAAe,EAAkB,CAAC;OAClC,WAAU;OACV,cAAY,mBAAmB,EAAa;iBAH9C,CAKG,GAAa,GACR;;MACR,kBAAC,UAAD;OACE,eAAe,EAAS,SAAS,oBAAoB;OACrD,WAAU;OACV,cAAW;OACX,OAAM;iBAEN,kBAAC,GAAD;QAAM,MAAM;QAAc,MAAK;OAAM,CAAA;MAC/B,CAAA;KACL;MACK;MAEC,CAAA;EACd;;AAET;;;ACzNA,IAAM,IAAmB,EAAM,WAAW,OAAO,8BAA+B,GAC1E,IAAsB,EAAM,WAAW,OAAO,iCAAkC;AA+BtF,SAAS,EAAW,GAAa,GAA6B;CAC5D,IAAI,GAAU;EACZ,IAAI,EAAS,WAAW,QAAQ,GAAG,OAAO;EAC1C,IAAI,MAAa,mBAAmB,OAAO;EAC3C,IAAI,EAAS,WAAW,QAAQ,GAAG,OAAO;EAC1C,IAAI,EAAS,WAAW,QAAQ,GAAG,OAAO;CAC5C;CAEA,IAAM,IAAM,EAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,YAAY;CAU5D,OATI;EAAC;EAAO;EAAQ;EAAO;EAAO;EAAQ;EAAO;EAAQ;CAAK,CAAC,CAAC,SAAS,KAAO,EAAE,IAAU,UACxF,MAAQ,QAAc,QACtB;EAAC;EAAO;EAAQ;EAAO;CAAK,CAAC,CAAC,SAAS,KAAO,EAAE,IAAU,UAC1D;EAAC;EAAO;EAAO;EAAO;EAAO;CAAM,CAAC,CAAC,SAAS,KAAO,EAAE,IAAU,UAEjE,EAAI,SAAS,WAAW,KAAK,EAAI,SAAS,UAAU,KAAK,EAAI,SAAS,aAAa,KAAK,EAAI,SAAS,UAAU,KAAK,EAAI,SAAS,WAAW,IACvI,UAGF;AACT;AAEA,SAAS,EAAY,GAAqB;CACxC,IAAM,IAAU,EAAI,MAAM,iDAAiD;CAC3E,IAAI,GAAS,OAAO,iCAAiC,EAAQ;CAE7D,IAAM,IAAa,EAAI,MAAM,mBAAmB;CAChD,IAAI,GAAY,OAAO,kCAAkC,EAAW;CAEpE,IAAI,EAAI,SAAS,WAAW,GAAG,OAAO,oDAAoD,mBAAmB,CAAG;CAEhH,IAAM,IAAY,EAAI,MAAM,2BAA2B;CAGvD,OAFI,IAAkB,8BAA8B,EAAU,OAEvD;AACT;AAMA,SAAS,EAAY,EACnB,QACA,MAAM,GACN,aACA,QACA,iBAAc,GACd,aACA,aACA,YACA,cACA,GAAG,KACgB;CACnB,IAAM,IAAO,KAAY,EAAW,GAAK,CAAQ,GAC3C,IAAW,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA;CAEpE,OACE,kBAAC,OAAD;EAAK,WAAW,EAAG,2BAA2B,CAAS;EAAG,GAAI;YAA9D;IAEI,KAAY,MACZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,KACC,kBAAC,GAAD;KAAe,MAAK;KAAS,WAAU;eACpC;IACY,CAAA,GAEhB,KAAY,kBAAC,GAAD;KAAO,SAAQ;KAAS,MAAK;eAAM;IAAgB,CAAA,CAC7D;;GAIN,MAAS,WACR,kBAAC,EAAM,UAAP;IAA0B;cACxB,kBAAC,GAAD;KAAuB;KAAU;KAAc;IAAU,CAAA;GAC3C,CAAA;GAEjB,MAAS,SACR,kBAAC,EAAM,UAAP;IAA0B;cACxB,kBAAC,GAAD;KAA0B;KAAkB;KAAsB;IAAU,CAAA;GAC9D,CAAA;GAEjB,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAc;GAAU,CAAA;GAC/D,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAe;IAAmB;GAAU,CAAA;GACnF,MAAS,WAAW,kBAAC,GAAD;IAAmB;IAAK,UAAU,EAAY,CAAG;IAAY;GAAU,CAAA;GAG3F,MAAS,WACR,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,WAAW,kBAAC,GAAD,EAAM,MAAM,EAAe,CAAA;KAAG,SAAA;eACzE,kBAAC,KAAD;MAAG,MAAM;MAAK,UAAA;MAAS,QAAO;MAAS,KAAI;gBAAsB;KAAW,CAAA;IACtE,CAAA;GACL,CAAA;EAEJ;;AAET"}
@@ -71,9 +71,9 @@ function b({ label: i, value: a, onValueChange: o, options: s, className: c }) {
71
71
  children: /* @__PURE__ */ m("button", {
72
72
  type: "button",
73
73
  className: t("flex items-center justify-between gap-ds-02 whitespace-nowrap rounded-control border bg-surface-raised-hover text-surface-fg", "hover:bg-surface-raised-active transition-colors duration-fast-01 ease-productive-standard", {
74
- xs: "h-ds-xs-plus text-ds-sm px-ds-02",
75
- sm: "h-ds-sm text-ds-sm px-ds-03",
76
- md: "h-ds-md text-ds-md px-ds-04"
74
+ xs: "h-ds-xs-plus text-body-sm px-ds-02",
75
+ sm: "h-ds-sm text-body-sm px-ds-03",
76
+ md: "h-ds-md text-body-md px-ds-04"
77
77
  }[l], u > 0 ? "border-accent-7" : "border-surface-border-strong", "w-40", c),
78
78
  children: [/* @__PURE__ */ m("span", {
79
79
  className: "flex min-w-0 items-center gap-ds-02",
@@ -1 +1 @@
1
- {"version":3,"file":"filter-bar.js","names":[],"sources":["../../src/composed/filter-bar.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { SearchInput } from '../ui/search-input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../ui/select'\nimport { type MultiSelectItem,MultiSelectPopover } from './multi-select-popover'\n\n// ============================================================\n// Context\n// ============================================================\n\ntype FilterBarSize = 'xs' | 'sm' | 'md'\n\nconst FilterBarContext = React.createContext<{ size: FilterBarSize }>({ size: 'sm' })\n\n// ============================================================\n// FilterBar\n// ============================================================\n\n/**\n * A toolbar for filtering lists/tables. Composes a search input, slot-based\n * filter controls (Select, MultiSelect), and an optional \"Clear all\" button.\n * Size is propagated to all child controls via context.\n */\nexport interface FilterBarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Controlled search input value. Omit `onSearchChange` to hide the search input entirely. */\n searchValue?: string\n /** Called when the search input changes. If not provided, the search input is hidden. */\n onSearchChange?: (value: string) => void\n searchPlaceholder?: string\n /** When provided, a \"Clear all\" button appears that calls this handler. */\n onClearAll?: () => void\n /** Size propagated to all child controls. @default 'sm' */\n size?: FilterBarSize\n}\n\nconst FilterBar = React.forwardRef<HTMLDivElement, FilterBarProps>(({\n searchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n onClearAll,\n size = 'sm',\n children,\n className,\n ...props\n}, ref) => {\n return (\n <FilterBarContext.Provider value={{ size }}>\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center gap-ds-03', className)}\n role=\"toolbar\"\n aria-label=\"Filters\"\n {...props}\n >\n {onSearchChange && (\n <SearchInput\n size={size}\n value={searchValue}\n onChange={(e) => onSearchChange(e.target.value)}\n onClear={searchValue ? () => onSearchChange('') : undefined}\n placeholder={searchPlaceholder}\n className=\"w-48\"\n />\n )}\n {children}\n {onClearAll && (\n <Button variant=\"ghost\" size={size} onClick={onClearAll}>\n Clear all\n </Button>\n )}\n </div>\n </FilterBarContext.Provider>\n )\n})\nFilterBar.displayName = 'FilterBar'\n\n// ============================================================\n// FilterSelect\n// ============================================================\n\nexport interface FilterSelectProps {\n label: string\n value: string\n onValueChange: (value: string) => void\n options: { value: string; label: string }[]\n /** Label for the \"all\" option @default 'All' */\n allLabel?: string\n className?: string\n}\n\nfunction FilterSelect({\n label,\n value,\n onValueChange,\n options,\n allLabel = 'All',\n className,\n}: FilterSelectProps) {\n const { size } = React.useContext(FilterBarContext)\n const isFiltered = value !== '' && value !== 'all'\n\n return (\n <Select value={value} onValueChange={onValueChange}>\n <SelectTrigger\n size={size}\n className={cn('w-40', isFiltered && 'border-accent-7', className)}\n >\n <SelectValue placeholder={label} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{allLabel}</SelectItem>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n\n// ============================================================\n// FilterMultiSelect\n// ============================================================\n\nexport interface FilterMultiSelectProps {\n label: string\n value: string[]\n onValueChange: (values: string[]) => void\n options: { value: string; label: string }[]\n className?: string\n}\n\nfunction FilterMultiSelect({\n label,\n value,\n onValueChange,\n options,\n className,\n}: FilterMultiSelectProps) {\n const { size } = React.useContext(FilterBarContext)\n const count = value.length\n\n const items: MultiSelectItem[] = React.useMemo(\n () => options.map((o) => ({ id: o.value, label: o.label })),\n [options],\n )\n\n // Trigger button sized by FilterBar context\n const triggerSizeClasses: Record<string, string> = {\n xs: 'h-ds-xs-plus text-ds-sm px-ds-02',\n sm: 'h-ds-sm text-ds-sm px-ds-03',\n md: 'h-ds-md text-ds-md px-ds-04',\n }\n\n return (\n <MultiSelectPopover\n items={items}\n value={value}\n onValueChange={onValueChange}\n searchPlaceholder={`Search ${label.toLowerCase()}...`}\n >\n <button\n type=\"button\"\n className={cn(\n 'flex items-center justify-between gap-ds-02 whitespace-nowrap rounded-control border bg-surface-raised-hover text-surface-fg',\n 'hover:bg-surface-raised-active transition-colors duration-fast-01 ease-productive-standard',\n triggerSizeClasses[size],\n count > 0 ? 'border-accent-7' : 'border-surface-border-strong',\n 'w-40',\n className,\n )}\n >\n <span className=\"flex min-w-0 items-center gap-ds-02\">\n <span className=\"truncate\">{label}</span>\n {count > 0 && (\n <motion.span\n key={count}\n initial={{ scale: 0.8 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Badge size=\"xs\" variant=\"solid\" className=\"ml-ds-01\">\n {count}\n </Badge>\n </motion.span>\n )}\n </span>\n <Icon icon={IconChevronDown} size=\"xs\" className=\"opacity-50 shrink-0\" />\n </button>\n </MultiSelectPopover>\n )\n}\n\nexport { FilterBar, FilterMultiSelect,FilterSelect }\n"],"mappings":";;;;;;;;;;;;;;AA2BA,IAAM,IAAmB,EAAM,cAAuC,EAAE,MAAM,KAAK,CAAC,GAuB9E,IAAY,EAAM,YAA4C,EAClE,gBACA,mBACA,uBAAoB,aACpB,eACA,UAAO,MACP,aACA,cACA,GAAG,KACF,MAEC,kBAAC,EAAiB,UAAlB;CAA2B,OAAO,EAAE,QAAK;WACvC,kBAAC,OAAD;EACO;EACL,WAAW,EAAG,yCAAyC,CAAS;EAChE,MAAK;EACL,cAAW;EACX,GAAI;YALN;GAOG,KACC,kBAAC,GAAD;IACQ;IACN,OAAO;IACP,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;IAC9C,SAAS,UAAoB,EAAe,EAAE,IAAI,KAAA;IAClD,aAAa;IACb,WAAU;GACX,CAAA;GAEF;GACA,KACC,kBAAC,GAAD;IAAQ,SAAQ;IAAc;IAAM,SAAS;cAAY;GAEjD,CAAA;EAEP;;AACoB,CAAA,CAE9B;AACD,EAAU,cAAc;AAgBxB,SAAS,EAAa,EACpB,UACA,UACA,kBACA,YACA,cAAW,OACX,gBACoB;CACpB,IAAM,EAAE,YAAS,EAAM,WAAW,CAAgB;CAGlD,OACE,kBAAC,GAAD;EAAe;EAAsB;YAArC,CACE,kBAAC,GAAD;GACQ;GACN,WAAW,EAAG,QAND,MAAU,MAAM,MAAU,SAMH,mBAAmB,CAAS;aAEhE,kBAAC,GAAD,EAAa,aAAa,EAAQ,CAAA;EACrB,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAY,OAAM;aAAO;EAAqB,CAAA,GAC7C,EAAQ,KAAK,MACZ,kBAAC,GAAD;GAA0B,OAAO,EAAE;aAChC,EAAE;EACO,GAFK,EAAE,KAEP,CACb,CACY,EAAA,CAAA,CACT;;AAEZ;AAcA,SAAS,EAAkB,EACzB,UACA,UACA,kBACA,YACA,gBACyB;CACzB,IAAM,EAAE,YAAS,EAAM,WAAW,CAAgB,GAC5C,IAAQ,EAAM;CAcpB,OACE,kBAAC,GAAD;EACS,OAdsB,EAAM,cAC/B,EAAQ,KAAK,OAAO;GAAE,IAAI,EAAE;GAAO,OAAO,EAAE;EAAM,EAAE,GAC1D,CAAC,CAAO,CAYC;EACA;EACQ;EACf,mBAAmB,UAAU,EAAM,YAAY,EAAE;YAEjD,kBAAC,UAAD;GACE,MAAK;GACL,WAAW,EACT,gIACA,8FACA;IAjBN,IAAI;IACJ,IAAI;IACJ,IAAI;GAeE,EAAmB,IACnB,IAAQ,IAAI,oBAAoB,gCAChC,QACA,CACF;aATF,CAWE,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAY;IAAY,CAAA,GACvC,IAAQ,KACP,kBAAC,EAAO,MAAR;KAEE,SAAS,EAAE,OAAO,GAAI;KACtB,SAAS,EAAE,OAAO,EAAE;KACpB,YAAY,EAAQ;KACpB,WAAU;eAEV,kBAAC,GAAD;MAAO,MAAK;MAAK,SAAQ;MAAQ,WAAU;gBACxC;KACI,CAAA;IACI,GATN,CASM,CAEX;OACN,kBAAC,GAAD;IAAM,MAAM;IAAiB,MAAK;IAAK,WAAU;GAAuB,CAAA,CAClE;;CACU,CAAA;AAExB"}
1
+ {"version":3,"file":"filter-bar.js","names":[],"sources":["../../src/composed/filter-bar.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { SearchInput } from '../ui/search-input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../ui/select'\nimport { type MultiSelectItem,MultiSelectPopover } from './multi-select-popover'\n\n// ============================================================\n// Context\n// ============================================================\n\ntype FilterBarSize = 'xs' | 'sm' | 'md'\n\nconst FilterBarContext = React.createContext<{ size: FilterBarSize }>({ size: 'sm' })\n\n// ============================================================\n// FilterBar\n// ============================================================\n\n/**\n * A toolbar for filtering lists/tables. Composes a search input, slot-based\n * filter controls (Select, MultiSelect), and an optional \"Clear all\" button.\n * Size is propagated to all child controls via context.\n */\nexport interface FilterBarProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Controlled search input value. Omit `onSearchChange` to hide the search input entirely. */\n searchValue?: string\n /** Called when the search input changes. If not provided, the search input is hidden. */\n onSearchChange?: (value: string) => void\n searchPlaceholder?: string\n /** When provided, a \"Clear all\" button appears that calls this handler. */\n onClearAll?: () => void\n /** Size propagated to all child controls. @default 'sm' */\n size?: FilterBarSize\n}\n\nconst FilterBar = React.forwardRef<HTMLDivElement, FilterBarProps>(({\n searchValue,\n onSearchChange,\n searchPlaceholder = 'Search...',\n onClearAll,\n size = 'sm',\n children,\n className,\n ...props\n}, ref) => {\n return (\n <FilterBarContext.Provider value={{ size }}>\n <div\n ref={ref}\n className={cn('flex flex-wrap items-center gap-ds-03', className)}\n role=\"toolbar\"\n aria-label=\"Filters\"\n {...props}\n >\n {onSearchChange && (\n <SearchInput\n size={size}\n value={searchValue}\n onChange={(e) => onSearchChange(e.target.value)}\n onClear={searchValue ? () => onSearchChange('') : undefined}\n placeholder={searchPlaceholder}\n className=\"w-48\"\n />\n )}\n {children}\n {onClearAll && (\n <Button variant=\"ghost\" size={size} onClick={onClearAll}>\n Clear all\n </Button>\n )}\n </div>\n </FilterBarContext.Provider>\n )\n})\nFilterBar.displayName = 'FilterBar'\n\n// ============================================================\n// FilterSelect\n// ============================================================\n\nexport interface FilterSelectProps {\n label: string\n value: string\n onValueChange: (value: string) => void\n options: { value: string; label: string }[]\n /** Label for the \"all\" option @default 'All' */\n allLabel?: string\n className?: string\n}\n\nfunction FilterSelect({\n label,\n value,\n onValueChange,\n options,\n allLabel = 'All',\n className,\n}: FilterSelectProps) {\n const { size } = React.useContext(FilterBarContext)\n const isFiltered = value !== '' && value !== 'all'\n\n return (\n <Select value={value} onValueChange={onValueChange}>\n <SelectTrigger\n size={size}\n className={cn('w-40', isFiltered && 'border-accent-7', className)}\n >\n <SelectValue placeholder={label} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{allLabel}</SelectItem>\n {options.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n\n// ============================================================\n// FilterMultiSelect\n// ============================================================\n\nexport interface FilterMultiSelectProps {\n label: string\n value: string[]\n onValueChange: (values: string[]) => void\n options: { value: string; label: string }[]\n className?: string\n}\n\nfunction FilterMultiSelect({\n label,\n value,\n onValueChange,\n options,\n className,\n}: FilterMultiSelectProps) {\n const { size } = React.useContext(FilterBarContext)\n const count = value.length\n\n const items: MultiSelectItem[] = React.useMemo(\n () => options.map((o) => ({ id: o.value, label: o.label })),\n [options],\n )\n\n // Trigger button sized by FilterBar context\n const triggerSizeClasses: Record<string, string> = {\n xs: 'h-ds-xs-plus text-body-sm px-ds-02',\n sm: 'h-ds-sm text-body-sm px-ds-03',\n md: 'h-ds-md text-body-md px-ds-04',\n }\n\n return (\n <MultiSelectPopover\n items={items}\n value={value}\n onValueChange={onValueChange}\n searchPlaceholder={`Search ${label.toLowerCase()}...`}\n >\n <button\n type=\"button\"\n className={cn(\n 'flex items-center justify-between gap-ds-02 whitespace-nowrap rounded-control border bg-surface-raised-hover text-surface-fg',\n 'hover:bg-surface-raised-active transition-colors duration-fast-01 ease-productive-standard',\n triggerSizeClasses[size],\n count > 0 ? 'border-accent-7' : 'border-surface-border-strong',\n 'w-40',\n className,\n )}\n >\n <span className=\"flex min-w-0 items-center gap-ds-02\">\n <span className=\"truncate\">{label}</span>\n {count > 0 && (\n <motion.span\n key={count}\n initial={{ scale: 0.8 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Badge size=\"xs\" variant=\"solid\" className=\"ml-ds-01\">\n {count}\n </Badge>\n </motion.span>\n )}\n </span>\n <Icon icon={IconChevronDown} size=\"xs\" className=\"opacity-50 shrink-0\" />\n </button>\n </MultiSelectPopover>\n )\n}\n\nexport { FilterBar, FilterMultiSelect,FilterSelect }\n"],"mappings":";;;;;;;;;;;;;;AA2BA,IAAM,IAAmB,EAAM,cAAuC,EAAE,MAAM,KAAK,CAAC,GAuB9E,IAAY,EAAM,YAA4C,EAClE,gBACA,mBACA,uBAAoB,aACpB,eACA,UAAO,MACP,aACA,cACA,GAAG,KACF,MAEC,kBAAC,EAAiB,UAAlB;CAA2B,OAAO,EAAE,QAAK;WACvC,kBAAC,OAAD;EACO;EACL,WAAW,EAAG,yCAAyC,CAAS;EAChE,MAAK;EACL,cAAW;EACX,GAAI;YALN;GAOG,KACC,kBAAC,GAAD;IACQ;IACN,OAAO;IACP,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;IAC9C,SAAS,UAAoB,EAAe,EAAE,IAAI,KAAA;IAClD,aAAa;IACb,WAAU;GACX,CAAA;GAEF;GACA,KACC,kBAAC,GAAD;IAAQ,SAAQ;IAAc;IAAM,SAAS;cAAY;GAEjD,CAAA;EAEP;;AACoB,CAAA,CAE9B;AACD,EAAU,cAAc;AAgBxB,SAAS,EAAa,EACpB,UACA,UACA,kBACA,YACA,cAAW,OACX,gBACoB;CACpB,IAAM,EAAE,YAAS,EAAM,WAAW,CAAgB;CAGlD,OACE,kBAAC,GAAD;EAAe;EAAsB;YAArC,CACE,kBAAC,GAAD;GACQ;GACN,WAAW,EAAG,QAND,MAAU,MAAM,MAAU,SAMH,mBAAmB,CAAS;aAEhE,kBAAC,GAAD,EAAa,aAAa,EAAQ,CAAA;EACrB,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAY,OAAM;aAAO;EAAqB,CAAA,GAC7C,EAAQ,KAAK,MACZ,kBAAC,GAAD;GAA0B,OAAO,EAAE;aAChC,EAAE;EACO,GAFK,EAAE,KAEP,CACb,CACY,EAAA,CAAA,CACT;;AAEZ;AAcA,SAAS,EAAkB,EACzB,UACA,UACA,kBACA,YACA,gBACyB;CACzB,IAAM,EAAE,YAAS,EAAM,WAAW,CAAgB,GAC5C,IAAQ,EAAM;CAcpB,OACE,kBAAC,GAAD;EACS,OAdsB,EAAM,cAC/B,EAAQ,KAAK,OAAO;GAAE,IAAI,EAAE;GAAO,OAAO,EAAE;EAAM,EAAE,GAC1D,CAAC,CAAO,CAYC;EACA;EACQ;EACf,mBAAmB,UAAU,EAAM,YAAY,EAAE;YAEjD,kBAAC,UAAD;GACE,MAAK;GACL,WAAW,EACT,gIACA,8FACA;IAjBN,IAAI;IACJ,IAAI;IACJ,IAAI;GAeE,EAAmB,IACnB,IAAQ,IAAI,oBAAoB,gCAChC,QACA,CACF;aATF,CAWE,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAY;IAAY,CAAA,GACvC,IAAQ,KACP,kBAAC,EAAO,MAAR;KAEE,SAAS,EAAE,OAAO,GAAI;KACtB,SAAS,EAAE,OAAO,EAAE;KACpB,YAAY,EAAQ;KACpB,WAAU;eAEV,kBAAC,GAAD;MAAO,MAAK;MAAK,SAAQ;MAAQ,WAAU;gBACxC;KACI,CAAA;IACI,GATN,CASM,CAEX;OACN,kBAAC,GAAD;IAAM,MAAM;IAAiB,MAAK;IAAK,WAAU;GAAuB,CAAA,CAClE;;CACU,CAAA;AAExB"}
@@ -12,10 +12,10 @@ var d = o.forwardRef(({ title: d, description: f, collapsible: p = !1, defaultOp
12
12
  let y = /* @__PURE__ */ c("div", {
13
13
  className: "flex flex-col gap-ds-01",
14
14
  children: [/* @__PURE__ */ s("span", {
15
- className: "text-ds-md font-semibold text-surface-fg font-sans",
15
+ className: "text-body-md font-semibold text-surface-fg font-sans",
16
16
  children: d
17
17
  }), f && /* @__PURE__ */ s("span", {
18
- className: "text-ds-sm text-surface-fg-muted font-sans",
18
+ className: "text-body-sm text-surface-fg-muted font-sans",
19
19
  children: f
20
20
  })]
21
21
  }), [b, x] = o.useState(m);
@@ -1 +1 @@
1
- {"version":3,"file":"form-section.js","names":[],"sources":["../../src/composed/form-section.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface FormSectionProps extends React.HTMLAttributes<HTMLDivElement> {\n title: string\n description?: string\n collapsible?: boolean\n /** @default true */\n defaultOpen?: boolean\n}\n\n// ============================================================\n// FormSection\n// ============================================================\n\nconst FormSection = React.forwardRef<HTMLDivElement, FormSectionProps>(({\n title,\n description,\n collapsible = false,\n defaultOpen = true,\n children,\n className,\n ...props\n}, ref) => {\n const header = (\n <div className=\"flex flex-col gap-ds-01\">\n <span className=\"text-ds-md font-semibold text-surface-fg font-sans\">\n {title}\n </span>\n {description && (\n <span className=\"text-ds-sm text-surface-fg-muted font-sans\">\n {description}\n </span>\n )}\n </div>\n )\n\n const [isOpen, setIsOpen] = React.useState(defaultOpen)\n\n if (collapsible) {\n return (\n <Collapsible defaultOpen={defaultOpen} onOpenChange={setIsOpen} className={cn('space-y-ds-04', className)} {...props}>\n <CollapsibleTrigger className=\"flex w-full items-center justify-between py-ds-02 group\">\n {header}\n <motion.span\n animate={{ rotate: isOpen ? 180 : 0 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon icon={IconChevronDown} size=\"sm\" className=\"text-surface-fg-muted\" />\n </motion.span>\n </CollapsibleTrigger>\n <div className=\"border-b border-surface-border-subtle\" />\n <CollapsibleContent>\n <div className=\"space-y-ds-04 pt-ds-02\">\n {children}\n </div>\n </CollapsibleContent>\n </Collapsible>\n )\n }\n\n return (\n <div ref={ref} className={cn('space-y-ds-04', className)} {...props}>\n {header}\n <div className=\"border-b border-surface-border-subtle\" />\n <div className=\"space-y-ds-04\">\n {children}\n </div>\n </div>\n )\n})\nFormSection.displayName = 'FormSection'\n\nexport { FormSection }\n"],"mappings":";;;;;;;;;;AA2BA,IAAM,IAAc,EAAM,YAA8C,EACtE,UACA,gBACA,iBAAc,IACd,iBAAc,IACd,aACA,cACA,GAAG,KACF,MAAQ;CACT,IAAM,IACJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aACb;EACG,CAAA,GACL,KACC,kBAAC,QAAD;GAAM,WAAU;aACb;EACG,CAAA,CAEL;KAGD,CAAC,GAAQ,KAAa,EAAM,SAAS,CAAW;CAyBtD,OAvBI,IAEA,kBAAC,GAAD;EAA0B;EAAa,cAAc;EAAW,WAAW,EAAG,iBAAiB,CAAS;EAAG,GAAI;YAA/G;GACE,kBAAC,GAAD;IAAoB,WAAU;cAA9B,CACG,GACD,kBAAC,EAAO,MAAR;KACE,SAAS,EAAE,QAAQ,IAAS,MAAM,EAAE;KACpC,YAAY,EAAQ;KACpB,WAAU;eAEV,kBAAC,GAAD;MAAM,MAAM;MAAiB,MAAK;MAAK,WAAU;KAAyB,CAAA;IAC/D,CAAA,CACK;;GACpB,kBAAC,OAAD,EAAK,WAAU,wCAAyC,CAAA;GACxD,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;IAAK,WAAU;IACZ;GACE,CAAA,EACa,CAAA;EACT;MAKf,kBAAC,OAAD;EAAU;EAAK,WAAW,EAAG,iBAAiB,CAAS;EAAG,GAAI;YAA9D;GACG;GACD,kBAAC,OAAD,EAAK,WAAU,wCAAyC,CAAA;GACxD,kBAAC,OAAD;IAAK,WAAU;IACZ;GACE,CAAA;EACF;;AAET,CAAC;AACD,EAAY,cAAc"}
1
+ {"version":3,"file":"form-section.js","names":[],"sources":["../../src/composed/form-section.tsx"],"sourcesContent":["'use client'\n\nimport { IconChevronDown } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface FormSectionProps extends React.HTMLAttributes<HTMLDivElement> {\n title: string\n description?: string\n collapsible?: boolean\n /** @default true */\n defaultOpen?: boolean\n}\n\n// ============================================================\n// FormSection\n// ============================================================\n\nconst FormSection = React.forwardRef<HTMLDivElement, FormSectionProps>(({\n title,\n description,\n collapsible = false,\n defaultOpen = true,\n children,\n className,\n ...props\n}, ref) => {\n const header = (\n <div className=\"flex flex-col gap-ds-01\">\n <span className=\"text-body-md font-semibold text-surface-fg font-sans\">\n {title}\n </span>\n {description && (\n <span className=\"text-body-sm text-surface-fg-muted font-sans\">\n {description}\n </span>\n )}\n </div>\n )\n\n const [isOpen, setIsOpen] = React.useState(defaultOpen)\n\n if (collapsible) {\n return (\n <Collapsible defaultOpen={defaultOpen} onOpenChange={setIsOpen} className={cn('space-y-ds-04', className)} {...props}>\n <CollapsibleTrigger className=\"flex w-full items-center justify-between py-ds-02 group\">\n {header}\n <motion.span\n animate={{ rotate: isOpen ? 180 : 0 }}\n transition={springs.snappy}\n className=\"inline-flex\"\n >\n <Icon icon={IconChevronDown} size=\"sm\" className=\"text-surface-fg-muted\" />\n </motion.span>\n </CollapsibleTrigger>\n <div className=\"border-b border-surface-border-subtle\" />\n <CollapsibleContent>\n <div className=\"space-y-ds-04 pt-ds-02\">\n {children}\n </div>\n </CollapsibleContent>\n </Collapsible>\n )\n }\n\n return (\n <div ref={ref} className={cn('space-y-ds-04', className)} {...props}>\n {header}\n <div className=\"border-b border-surface-border-subtle\" />\n <div className=\"space-y-ds-04\">\n {children}\n </div>\n </div>\n )\n})\nFormSection.displayName = 'FormSection'\n\nexport { FormSection }\n"],"mappings":";;;;;;;;;;AA2BA,IAAM,IAAc,EAAM,YAA8C,EACtE,UACA,gBACA,iBAAc,IACd,iBAAc,IACd,aACA,cACA,GAAG,KACF,MAAQ;CACT,IAAM,IACJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aACb;EACG,CAAA,GACL,KACC,kBAAC,QAAD;GAAM,WAAU;aACb;EACG,CAAA,CAEL;KAGD,CAAC,GAAQ,KAAa,EAAM,SAAS,CAAW;CAyBtD,OAvBI,IAEA,kBAAC,GAAD;EAA0B;EAAa,cAAc;EAAW,WAAW,EAAG,iBAAiB,CAAS;EAAG,GAAI;YAA/G;GACE,kBAAC,GAAD;IAAoB,WAAU;cAA9B,CACG,GACD,kBAAC,EAAO,MAAR;KACE,SAAS,EAAE,QAAQ,IAAS,MAAM,EAAE;KACpC,YAAY,EAAQ;KACpB,WAAU;eAEV,kBAAC,GAAD;MAAM,MAAM;MAAiB,MAAK;MAAK,WAAU;KAAyB,CAAA;IAC/D,CAAA,CACK;;GACpB,kBAAC,OAAD,EAAK,WAAU,wCAAyC,CAAA;GACxD,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;IAAK,WAAU;IACZ;GACE,CAAA,EACa,CAAA;EACT;MAKf,kBAAC,OAAD;EAAU;EAAK,WAAW,EAAG,iBAAiB,CAAS;EAAG,GAAI;YAA9D;GACG;GACD,kBAAC,OAAD,EAAK,WAAU,wCAAyC,CAAA;GACxD,kBAAC,OAAD;IAAK,WAAU;IACZ;GACE,CAAA;EACF;;AAET,CAAC;AACD,EAAY,cAAc"}
@@ -4,7 +4,7 @@ export interface InlineEditProps extends Omit<React.HTMLAttributes<HTMLDivElemen
4
4
  value: string;
5
5
  onSave: (newValue: string) => void | Promise<void>;
6
6
  placeholder?: string;
7
- /** CSS class applied to the editable text (e.g. 'text-ds-lg font-semibold') */
7
+ /** CSS class applied to the editable text (e.g. 'text-heading-xs font-semibold') */
8
8
  textClassName?: string;
9
9
  readOnly?: boolean;
10
10
  maxLength?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"inline-edit.d.ts","sourceRoot":"","sources":["../../src/composed/inline-edit.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC3F,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAUD,QAAA,MAAM,UAAU,wFAyJd,CAAA;AAGF,OAAO,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"inline-edit.d.ts","sourceRoot":"","sources":["../../src/composed/inline-edit.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC3F,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAUD,QAAA,MAAM,UAAU,wFAyJd,CAAA;AAGF,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"inline-edit.js","names":[],"sources":["../../src/composed/inline-edit.tsx"],"sourcesContent":["'use client'\n\nimport { IconPencil } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface InlineEditProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSave'> {\n value: string\n onSave: (newValue: string) => void | Promise<void>\n placeholder?: string\n /** CSS class applied to the editable text (e.g. 'text-ds-lg font-semibold') */\n textClassName?: string\n readOnly?: boolean\n maxLength?: number\n /** External saving state — shows spinner */\n saving?: boolean\n}\n\n// ============================================================\n// InlineEdit — contentEditable approach\n//\n// No mode switch. No input field. The text IS the editor.\n// Click → cursor appears in the text. Type. Enter saves. Escape reverts.\n// Like Notion, Linear, Figma layer names.\n// ============================================================\n\nconst InlineEdit = React.forwardRef<HTMLDivElement, InlineEditProps>(({\n value,\n onSave,\n placeholder = 'Click to edit',\n textClassName,\n readOnly = false,\n maxLength,\n saving: savingProp = false,\n className,\n // aria-label / aria-labelledby apply to the editable textbox (the inner\n // span), not the outer wrapper div. Intercept them from ...props so they\n // land on the role=\"textbox\" element that actually needs the name.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}, ref) => {\n const [saving, setSaving] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const editRef = React.useRef<HTMLSpanElement>(null)\n const snapshotRef = React.useRef(value)\n\n const isSaving = savingProp || saving\n const isEmpty = !value\n\n // Sync DOM text when value changes externally (and not focused)\n React.useEffect(() => {\n if (!focused && editRef.current) {\n editRef.current.textContent = value\n }\n }, [value, focused])\n\n function handleFocus() {\n if (readOnly || isSaving) return\n setFocused(true)\n snapshotRef.current = value\n\n // Select all text on focus (like clicking a file name in Finder)\n requestAnimationFrame(() => {\n if (!editRef.current) return\n const range = document.createRange()\n range.selectNodeContents(editRef.current)\n const sel = window.getSelection()\n sel?.removeAllRanges()\n sel?.addRange(range)\n })\n }\n\n async function commit() {\n if (!editRef.current) return\n setFocused(false)\n const raw = editRef.current.textContent ?? ''\n const trimmed = raw.trim()\n\n // Enforce maxLength\n const final = maxLength ? trimmed.slice(0, maxLength) : trimmed\n\n // Reset DOM to clean value\n editRef.current.textContent = final || value\n\n if (final === value) return\n const result = onSave(final)\n if (result instanceof Promise) {\n setSaving(true)\n try {\n await result\n } catch {\n // Revert on error\n if (editRef.current) editRef.current.textContent = value\n } finally {\n setSaving(false)\n }\n }\n }\n\n function cancel() {\n if (!editRef.current) return\n editRef.current.textContent = snapshotRef.current\n setFocused(false)\n editRef.current.blur()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Escape') {\n e.preventDefault()\n cancel()\n } else if (e.key === 'Enter') {\n e.preventDefault()\n commit()\n }\n }\n\n function handleInput() {\n if (!editRef.current || !maxLength) return\n const text = editRef.current.textContent ?? ''\n if (text.length > maxLength) {\n editRef.current.textContent = text.slice(0, maxLength)\n // Move cursor to end\n const range = document.createRange()\n range.selectNodeContents(editRef.current)\n range.collapse(false)\n const sel = window.getSelection()\n sel?.removeAllRanges()\n sel?.addRange(range)\n }\n }\n\n // Prevent paste from inserting rich content\n function handlePaste(e: React.ClipboardEvent) {\n e.preventDefault()\n const text = e.clipboardData.getData('text/plain')\n document.execCommand('insertText', false, text)\n }\n\n return (\n <div ref={ref} className={cn('group inline-flex items-center gap-ds-02', className)} {...props}>\n <span\n ref={editRef}\n role={readOnly ? undefined : 'textbox'}\n contentEditable={!readOnly && !isSaving}\n suppressContentEditableWarning\n tabIndex={readOnly ? undefined : 0}\n // Fall back to the placeholder when no explicit label is supplied —\n // screen readers need _something_ on role=\"textbox\".\n aria-label={readOnly ? undefined : (ariaLabel ?? (ariaLabelledBy ? undefined : placeholder))}\n aria-labelledby={readOnly ? undefined : ariaLabelledBy}\n onFocus={handleFocus}\n onBlur={commit}\n onKeyDown={handleKeyDown}\n onInput={handleInput}\n onPaste={handlePaste}\n spellCheck={focused}\n className={cn(\n 'font-sans text-surface-fg outline-hidden',\n textClassName,\n // Idle: subtle hover hint\n !readOnly && !focused && 'cursor-text rounded-control-inner -mx-ds-01 px-ds-01 hover:bg-surface-raised-hover transition-colors duration-fast-01',\n // Focused: subtle underline to indicate editing\n focused && 'rounded-control-inner -mx-ds-01 px-ds-01 bg-surface-raised-hover ring-1 ring-accent-7',\n // Empty: show placeholder styling\n isEmpty && !focused && 'text-surface-fg-subtle italic',\n // Read-only\n readOnly && 'cursor-default',\n )}\n data-placeholder={placeholder}\n >\n {value || (focused ? '' : placeholder)}\n </span>\n {!readOnly && !focused && !isSaving && (\n <Icon icon={IconPencil} size=\"xs\" className=\"text-surface-fg-subtle opacity-0 group-hover:opacity-100 transition-opacity duration-fast-01 shrink-0\" />\n )}\n {isSaving && <Spinner size=\"sm\" />}\n </div>\n )\n})\nInlineEdit.displayName = 'InlineEdit'\n\nexport { InlineEdit }\n"],"mappings":"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"}
1
+ {"version":3,"file":"inline-edit.js","names":[],"sources":["../../src/composed/inline-edit.tsx"],"sourcesContent":["'use client'\n\nimport { IconPencil } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface InlineEditProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSave'> {\n value: string\n onSave: (newValue: string) => void | Promise<void>\n placeholder?: string\n /** CSS class applied to the editable text (e.g. 'text-heading-xs font-semibold') */\n textClassName?: string\n readOnly?: boolean\n maxLength?: number\n /** External saving state — shows spinner */\n saving?: boolean\n}\n\n// ============================================================\n// InlineEdit — contentEditable approach\n//\n// No mode switch. No input field. The text IS the editor.\n// Click → cursor appears in the text. Type. Enter saves. Escape reverts.\n// Like Notion, Linear, Figma layer names.\n// ============================================================\n\nconst InlineEdit = React.forwardRef<HTMLDivElement, InlineEditProps>(({\n value,\n onSave,\n placeholder = 'Click to edit',\n textClassName,\n readOnly = false,\n maxLength,\n saving: savingProp = false,\n className,\n // aria-label / aria-labelledby apply to the editable textbox (the inner\n // span), not the outer wrapper div. Intercept them from ...props so they\n // land on the role=\"textbox\" element that actually needs the name.\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}, ref) => {\n const [saving, setSaving] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const editRef = React.useRef<HTMLSpanElement>(null)\n const snapshotRef = React.useRef(value)\n\n const isSaving = savingProp || saving\n const isEmpty = !value\n\n // Sync DOM text when value changes externally (and not focused)\n React.useEffect(() => {\n if (!focused && editRef.current) {\n editRef.current.textContent = value\n }\n }, [value, focused])\n\n function handleFocus() {\n if (readOnly || isSaving) return\n setFocused(true)\n snapshotRef.current = value\n\n // Select all text on focus (like clicking a file name in Finder)\n requestAnimationFrame(() => {\n if (!editRef.current) return\n const range = document.createRange()\n range.selectNodeContents(editRef.current)\n const sel = window.getSelection()\n sel?.removeAllRanges()\n sel?.addRange(range)\n })\n }\n\n async function commit() {\n if (!editRef.current) return\n setFocused(false)\n const raw = editRef.current.textContent ?? ''\n const trimmed = raw.trim()\n\n // Enforce maxLength\n const final = maxLength ? trimmed.slice(0, maxLength) : trimmed\n\n // Reset DOM to clean value\n editRef.current.textContent = final || value\n\n if (final === value) return\n const result = onSave(final)\n if (result instanceof Promise) {\n setSaving(true)\n try {\n await result\n } catch {\n // Revert on error\n if (editRef.current) editRef.current.textContent = value\n } finally {\n setSaving(false)\n }\n }\n }\n\n function cancel() {\n if (!editRef.current) return\n editRef.current.textContent = snapshotRef.current\n setFocused(false)\n editRef.current.blur()\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'Escape') {\n e.preventDefault()\n cancel()\n } else if (e.key === 'Enter') {\n e.preventDefault()\n commit()\n }\n }\n\n function handleInput() {\n if (!editRef.current || !maxLength) return\n const text = editRef.current.textContent ?? ''\n if (text.length > maxLength) {\n editRef.current.textContent = text.slice(0, maxLength)\n // Move cursor to end\n const range = document.createRange()\n range.selectNodeContents(editRef.current)\n range.collapse(false)\n const sel = window.getSelection()\n sel?.removeAllRanges()\n sel?.addRange(range)\n }\n }\n\n // Prevent paste from inserting rich content\n function handlePaste(e: React.ClipboardEvent) {\n e.preventDefault()\n const text = e.clipboardData.getData('text/plain')\n document.execCommand('insertText', false, text)\n }\n\n return (\n <div ref={ref} className={cn('group inline-flex items-center gap-ds-02', className)} {...props}>\n <span\n ref={editRef}\n role={readOnly ? undefined : 'textbox'}\n contentEditable={!readOnly && !isSaving}\n suppressContentEditableWarning\n tabIndex={readOnly ? undefined : 0}\n // Fall back to the placeholder when no explicit label is supplied —\n // screen readers need _something_ on role=\"textbox\".\n aria-label={readOnly ? undefined : (ariaLabel ?? (ariaLabelledBy ? undefined : placeholder))}\n aria-labelledby={readOnly ? undefined : ariaLabelledBy}\n onFocus={handleFocus}\n onBlur={commit}\n onKeyDown={handleKeyDown}\n onInput={handleInput}\n onPaste={handlePaste}\n spellCheck={focused}\n className={cn(\n 'font-sans text-surface-fg outline-hidden',\n textClassName,\n // Idle: subtle hover hint\n !readOnly && !focused && 'cursor-text rounded-control-inner -mx-ds-01 px-ds-01 hover:bg-surface-raised-hover transition-colors duration-fast-01',\n // Focused: subtle underline to indicate editing\n focused && 'rounded-control-inner -mx-ds-01 px-ds-01 bg-surface-raised-hover ring-1 ring-accent-7',\n // Empty: show placeholder styling\n isEmpty && !focused && 'text-surface-fg-subtle italic',\n // Read-only\n readOnly && 'cursor-default',\n )}\n data-placeholder={placeholder}\n >\n {value || (focused ? '' : placeholder)}\n </span>\n {!readOnly && !focused && !isSaving && (\n <Icon icon={IconPencil} size=\"xs\" className=\"text-surface-fg-subtle opacity-0 group-hover:opacity-100 transition-opacity duration-fast-01 shrink-0\" />\n )}\n {isSaving && <Spinner size=\"sm\" />}\n </div>\n )\n})\nInlineEdit.displayName = 'InlineEdit'\n\nexport { InlineEdit }\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ var a = n.forwardRef(function({ className: n, ...a }, o) {
7
7
  return /* @__PURE__ */ i("div", {
8
8
  ref: o,
9
9
  ...a,
10
- className: e("rounded-surface border border-surface-border-strong bg-surface-raised p-ds-05b", n),
10
+ className: e("rounded-surface border border-card-strong bg-surface-raised p-ds-05b", n),
11
11
  children: [
12
12
  /* @__PURE__ */ i("div", {
13
13
  className: "flex items-center justify-between pb-ds-05",
@@ -33,7 +33,7 @@ var o = n.forwardRef(function({ rows: n = 5, columns: a = 4, className: o, ...s
33
33
  return /* @__PURE__ */ i("div", {
34
34
  ref: c,
35
35
  ...s,
36
- className: e("overflow-hidden rounded-surface border border-surface-border-strong", o),
36
+ className: e("overflow-hidden rounded-surface border border-card-strong", o),
37
37
  children: [/* @__PURE__ */ r("div", {
38
38
  className: "flex items-center gap-ds-05 border-b border-surface-border-strong bg-surface-raised px-ds-05 py-ds-04",
39
39
  children: Array.from({ length: a }).map((n, i) => /* @__PURE__ */ r(t, { className: e("h-ds-04 bg-surface-raised-hover", i === 0 ? "w-ds-13" : "flex-1") }, `head-${i}`))
@@ -63,7 +63,7 @@ var s = n.forwardRef(function({ columns: n = 4, cardsPerColumn: a = 3, className
63
63
  ]
64
64
  }), /* @__PURE__ */ r(t, { className: "h-ico-sm w-ico-sm rounded bg-surface-raised-hover" })]
65
65
  }), Array.from({ length: a }).map((e, o) => /* @__PURE__ */ i("div", {
66
- className: "rounded-surface border border-surface-border-strong bg-surface-raised p-ds-04",
66
+ className: "rounded-surface border border-card-strong bg-surface-raised p-ds-04",
67
67
  style: { animationDelay: `${(n * a + o) * 50}ms` },
68
68
  children: [/* @__PURE__ */ i("div", {
69
69
  className: "space-y-ds-03",
@@ -1 +1 @@
1
- {"version":3,"file":"loading-skeleton.js","names":[],"sources":["../../src/composed/loading-skeleton.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\nexport interface CardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n className?: string\n}\n\nconst CardSkeleton = React.forwardRef<HTMLDivElement, CardSkeletonProps>(\n function CardSkeleton({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n className={cn(\n 'rounded-surface border border-surface-border-strong bg-surface-raised p-ds-05b',\n className,\n )}\n >\n <div className=\"flex items-center justify-between pb-ds-05\">\n <Skeleton className=\"h-ds-05 w-[128px] bg-surface-raised-hover\" />\n <Skeleton className=\"h-ico-sm w-ico-sm rounded bg-surface-raised-hover\" />\n </div>\n <div className=\"space-y-ds-04\">\n <Skeleton className=\"h-ds-04 w-full bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-4/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-3/5 bg-surface-raised-hover\" />\n </div>\n <div className=\"flex items-center gap-ds-03 pt-ds-05\">\n <Skeleton className=\"h-ds-xs w-ds-xs rounded-pill bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-ds-11 bg-surface-raised-hover\" />\n </div>\n </div>\n )\n},\n)\n\nCardSkeleton.displayName = 'CardSkeleton'\n\nexport interface TableSkeletonProps extends CardSkeletonProps {\n rows?: number\n columns?: number\n}\n\nconst TableSkeleton = React.forwardRef<HTMLDivElement, TableSkeletonProps>(\n function TableSkeleton({\n rows = 5,\n columns = 4,\n className,\n ...props\n}, ref) {\n return (\n <div\n ref={ref}\n {...props}\n className={cn(\n 'overflow-hidden rounded-surface border border-surface-border-strong',\n className,\n )}\n >\n {/* Header */}\n <div className=\"flex items-center gap-ds-05 border-b border-surface-border-strong bg-surface-raised px-ds-05 py-ds-04\">\n {Array.from({ length: columns }).map((_, i) => (\n <Skeleton\n key={`head-${i}`}\n className={cn(\n 'h-ds-04 bg-surface-raised-hover',\n i === 0 ? 'w-ds-13' : 'flex-1',\n )}\n />\n ))}\n </div>\n\n {/* Rows */}\n {Array.from({ length: rows }).map((_, rowIndex) => (\n <div\n key={`row-${rowIndex}`}\n className={cn(\n 'flex items-center gap-ds-05 px-ds-05 py-ds-04',\n rowIndex < rows - 1 && 'border-b border-surface-border-strong',\n )}\n style={{ animationDelay: `${rowIndex * 50}ms` }}\n >\n {Array.from({ length: columns }).map((_, colIndex) => (\n <Skeleton\n key={`cell-${rowIndex}-${colIndex}`}\n className={cn(\n 'h-ds-04 bg-surface-raised-hover',\n colIndex === 0 ? 'w-ds-13' : 'flex-1',\n )}\n />\n ))}\n </div>\n ))}\n </div>\n )\n},\n)\n\nTableSkeleton.displayName = 'TableSkeleton'\n\nexport interface BoardSkeletonProps extends CardSkeletonProps {\n columns?: number\n cardsPerColumn?: number\n}\n\nconst BoardSkeleton = React.forwardRef<HTMLDivElement, BoardSkeletonProps>(\n function BoardSkeleton({\n columns = 4,\n cardsPerColumn = 3,\n className,\n ...props\n}, ref) {\n return (\n <div ref={ref} {...props} className={cn('flex gap-ds-05 overflow-x-auto', className)}>\n {Array.from({ length: columns }).map((_, colIndex) => (\n <div\n key={`col-${colIndex}`}\n className=\"flex w-[272px] shrink-0 flex-col gap-ds-03\"\n >\n {/* Column header */}\n <div className=\"flex items-center justify-between px-ds-02 py-ds-03\">\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className=\"h-ds-04 w-ds-04 rounded bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-ds-11 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-05 w-ds-05b rounded-pill bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ico-sm w-ico-sm rounded bg-surface-raised-hover\" />\n </div>\n\n {/* Column cards */}\n {Array.from({ length: cardsPerColumn }).map((_, cardIndex) => (\n <div\n key={`card-${colIndex}-${cardIndex}`}\n className=\"rounded-surface border border-surface-border-strong bg-surface-raised p-ds-04\"\n style={{ animationDelay: `${(colIndex * cardsPerColumn + cardIndex) * 50}ms` }}\n >\n <div className=\"space-y-ds-03\">\n <Skeleton className=\"h-ds-04 w-4/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-3/5 bg-surface-raised-hover\" />\n </div>\n <div className=\"flex items-center justify-between pt-ds-04\">\n <div className=\"flex items-center gap-ds-02b\">\n <Skeleton className=\"h-ico-md w-ico-md rounded-pill bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-03 w-ds-10 bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ds-05 w-ds-lg rounded-pill bg-surface-raised-hover\" />\n </div>\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n},\n)\n\nBoardSkeleton.displayName = 'BoardSkeleton'\n\nexport interface ListSkeletonProps extends CardSkeletonProps {\n rows?: number\n showAvatar?: boolean\n}\n\nconst ListSkeleton = React.forwardRef<HTMLDivElement, ListSkeletonProps>(\n function ListSkeleton({\n rows = 6,\n showAvatar = true,\n className,\n ...props\n}, ref) {\n return (\n <div ref={ref} {...props} className={cn('flex flex-col', className)}>\n {Array.from({ length: rows }).map((_, i) => (\n <div\n key={`list-${i}`}\n className={cn(\n 'flex items-center gap-ds-04 py-ds-04',\n i < rows - 1 && 'border-b border-surface-border-strong',\n )}\n style={{ animationDelay: `${i * 50}ms` }}\n >\n {showAvatar && (\n <Skeleton className=\"h-ds-sm w-ds-sm shrink-0 rounded-pill bg-surface-raised-hover\" />\n )}\n <div className=\"flex flex-1 flex-col gap-ds-02b\">\n <Skeleton className=\"h-ds-04 w-2/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-03 w-3/5 bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ds-05b w-[56px] rounded-pill bg-surface-raised-hover\" />\n </div>\n ))}\n </div>\n )\n},\n)\n\nListSkeleton.displayName = 'ListSkeleton'\n\nexport { BoardSkeleton, CardSkeleton, ListSkeleton,TableSkeleton }\n"],"mappings":"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"}
1
+ {"version":3,"file":"loading-skeleton.js","names":[],"sources":["../../src/composed/loading-skeleton.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\nexport interface CardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n className?: string\n}\n\nconst CardSkeleton = React.forwardRef<HTMLDivElement, CardSkeletonProps>(\n function CardSkeleton({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n className={cn(\n 'rounded-surface border border-card-strong bg-surface-raised p-ds-05b',\n className,\n )}\n >\n <div className=\"flex items-center justify-between pb-ds-05\">\n <Skeleton className=\"h-ds-05 w-[128px] bg-surface-raised-hover\" />\n <Skeleton className=\"h-ico-sm w-ico-sm rounded bg-surface-raised-hover\" />\n </div>\n <div className=\"space-y-ds-04\">\n <Skeleton className=\"h-ds-04 w-full bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-4/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-3/5 bg-surface-raised-hover\" />\n </div>\n <div className=\"flex items-center gap-ds-03 pt-ds-05\">\n <Skeleton className=\"h-ds-xs w-ds-xs rounded-pill bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-ds-11 bg-surface-raised-hover\" />\n </div>\n </div>\n )\n},\n)\n\nCardSkeleton.displayName = 'CardSkeleton'\n\nexport interface TableSkeletonProps extends CardSkeletonProps {\n rows?: number\n columns?: number\n}\n\nconst TableSkeleton = React.forwardRef<HTMLDivElement, TableSkeletonProps>(\n function TableSkeleton({\n rows = 5,\n columns = 4,\n className,\n ...props\n}, ref) {\n return (\n <div\n ref={ref}\n {...props}\n className={cn(\n 'overflow-hidden rounded-surface border border-card-strong',\n className,\n )}\n >\n {/* Header */}\n <div className=\"flex items-center gap-ds-05 border-b border-surface-border-strong bg-surface-raised px-ds-05 py-ds-04\">\n {Array.from({ length: columns }).map((_, i) => (\n <Skeleton\n key={`head-${i}`}\n className={cn(\n 'h-ds-04 bg-surface-raised-hover',\n i === 0 ? 'w-ds-13' : 'flex-1',\n )}\n />\n ))}\n </div>\n\n {/* Rows */}\n {Array.from({ length: rows }).map((_, rowIndex) => (\n <div\n key={`row-${rowIndex}`}\n className={cn(\n 'flex items-center gap-ds-05 px-ds-05 py-ds-04',\n rowIndex < rows - 1 && 'border-b border-surface-border-strong',\n )}\n style={{ animationDelay: `${rowIndex * 50}ms` }}\n >\n {Array.from({ length: columns }).map((_, colIndex) => (\n <Skeleton\n key={`cell-${rowIndex}-${colIndex}`}\n className={cn(\n 'h-ds-04 bg-surface-raised-hover',\n colIndex === 0 ? 'w-ds-13' : 'flex-1',\n )}\n />\n ))}\n </div>\n ))}\n </div>\n )\n},\n)\n\nTableSkeleton.displayName = 'TableSkeleton'\n\nexport interface BoardSkeletonProps extends CardSkeletonProps {\n columns?: number\n cardsPerColumn?: number\n}\n\nconst BoardSkeleton = React.forwardRef<HTMLDivElement, BoardSkeletonProps>(\n function BoardSkeleton({\n columns = 4,\n cardsPerColumn = 3,\n className,\n ...props\n}, ref) {\n return (\n <div ref={ref} {...props} className={cn('flex gap-ds-05 overflow-x-auto', className)}>\n {Array.from({ length: columns }).map((_, colIndex) => (\n <div\n key={`col-${colIndex}`}\n className=\"flex w-[272px] shrink-0 flex-col gap-ds-03\"\n >\n {/* Column header */}\n <div className=\"flex items-center justify-between px-ds-02 py-ds-03\">\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className=\"h-ds-04 w-ds-04 rounded bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-ds-11 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-05 w-ds-05b rounded-pill bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ico-sm w-ico-sm rounded bg-surface-raised-hover\" />\n </div>\n\n {/* Column cards */}\n {Array.from({ length: cardsPerColumn }).map((_, cardIndex) => (\n <div\n key={`card-${colIndex}-${cardIndex}`}\n className=\"rounded-surface border border-card-strong bg-surface-raised p-ds-04\"\n style={{ animationDelay: `${(colIndex * cardsPerColumn + cardIndex) * 50}ms` }}\n >\n <div className=\"space-y-ds-03\">\n <Skeleton className=\"h-ds-04 w-4/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-04 w-3/5 bg-surface-raised-hover\" />\n </div>\n <div className=\"flex items-center justify-between pt-ds-04\">\n <div className=\"flex items-center gap-ds-02b\">\n <Skeleton className=\"h-ico-md w-ico-md rounded-pill bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-03 w-ds-10 bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ds-05 w-ds-lg rounded-pill bg-surface-raised-hover\" />\n </div>\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n},\n)\n\nBoardSkeleton.displayName = 'BoardSkeleton'\n\nexport interface ListSkeletonProps extends CardSkeletonProps {\n rows?: number\n showAvatar?: boolean\n}\n\nconst ListSkeleton = React.forwardRef<HTMLDivElement, ListSkeletonProps>(\n function ListSkeleton({\n rows = 6,\n showAvatar = true,\n className,\n ...props\n}, ref) {\n return (\n <div ref={ref} {...props} className={cn('flex flex-col', className)}>\n {Array.from({ length: rows }).map((_, i) => (\n <div\n key={`list-${i}`}\n className={cn(\n 'flex items-center gap-ds-04 py-ds-04',\n i < rows - 1 && 'border-b border-surface-border-strong',\n )}\n style={{ animationDelay: `${i * 50}ms` }}\n >\n {showAvatar && (\n <Skeleton className=\"h-ds-sm w-ds-sm shrink-0 rounded-pill bg-surface-raised-hover\" />\n )}\n <div className=\"flex flex-1 flex-col gap-ds-02b\">\n <Skeleton className=\"h-ds-04 w-2/5 bg-surface-raised-hover\" />\n <Skeleton className=\"h-ds-03 w-3/5 bg-surface-raised-hover\" />\n </div>\n <Skeleton className=\"h-ds-05b w-[56px] rounded-pill bg-surface-raised-hover\" />\n </div>\n ))}\n </div>\n )\n},\n)\n\nListSkeleton.displayName = 'ListSkeleton'\n\nexport { BoardSkeleton, CardSkeleton, ListSkeleton,TableSkeleton }\n"],"mappings":"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"}
@@ -52,7 +52,7 @@ function m({ language: t, code: n, mb: i }) {
52
52
  }, []), !s) return /* @__PURE__ */ o("div", {
53
53
  className: e("group relative", i),
54
54
  children: [/* @__PURE__ */ a("pre", {
55
- className: "bg-surface-sunken rounded-control p-ds-04 overflow-x-auto text-ds-sm font-mono",
55
+ className: "bg-surface-sunken rounded-control p-ds-04 overflow-x-auto text-body-sm font-mono",
56
56
  children: /* @__PURE__ */ a("code", { children: n })
57
57
  }), /* @__PURE__ */ a(p, { code: n })]
58
58
  });
@@ -65,7 +65,7 @@ function m({ language: t, code: n, mb: i }) {
65
65
  customStyle: {
66
66
  margin: 0,
67
67
  borderRadius: "var(--radius-ds-md)",
68
- fontSize: "var(--text-ds-sm)",
68
+ fontSize: "var(--text-body-sm)",
69
69
  padding: "var(--spacing-ds-04)"
70
70
  },
71
71
  children: n
@@ -86,7 +86,7 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
86
86
  let r = d(f(t));
87
87
  return /* @__PURE__ */ o("h1", {
88
88
  id: r,
89
- className: e("group", n ? "text-ds-md" : "text-ds-lg", "font-semibold text-surface-fg", _, g),
89
+ className: e("group", n ? "text-body-md" : "text-heading-xs", "font-semibold text-surface-fg", _, g),
90
90
  children: [/* @__PURE__ */ a("a", {
91
91
  href: `#${r}`,
92
92
  className: "opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline",
@@ -99,7 +99,7 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
99
99
  let r = d(f(t));
100
100
  return /* @__PURE__ */ o("h2", {
101
101
  id: r,
102
- className: e("group", "text-ds-md font-semibold text-surface-fg", n ? "mt-ds-03" : "mt-ds-04", g),
102
+ className: e("group", "text-body-md font-semibold text-surface-fg", n ? "mt-ds-03" : "mt-ds-04", g),
103
103
  children: [/* @__PURE__ */ a("a", {
104
104
  href: `#${r}`,
105
105
  className: "opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline",
@@ -112,7 +112,7 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
112
112
  let n = d(f(t));
113
113
  return /* @__PURE__ */ o("h3", {
114
114
  id: n,
115
- className: e("group", "text-ds-md font-semibold text-surface-fg", "mt-ds-03", g),
115
+ className: e("group", "text-body-md font-semibold text-surface-fg", "mt-ds-03", g),
116
116
  children: [/* @__PURE__ */ a("a", {
117
117
  href: `#${n}`,
118
118
  className: "opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline",
@@ -122,7 +122,7 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
122
122
  });
123
123
  },
124
124
  p: ({ children: t }) => /* @__PURE__ */ a("p", {
125
- className: e("text-ds-md text-surface-fg leading-ds-relaxed", g),
125
+ className: e("text-body-md text-surface-fg leading-ds-relaxed", g),
126
126
  children: t
127
127
  }),
128
128
  a: ({ href: e, children: t }) => /* @__PURE__ */ a("a", {
@@ -143,7 +143,7 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
143
143
  });
144
144
  }
145
145
  return /* @__PURE__ */ a("code", {
146
- className: "bg-surface-sunken rounded-control-inner px-1.5 py-0.5 text-ds-sm font-mono text-surface-fg",
146
+ className: "bg-surface-sunken rounded-control-inner px-1.5 py-0.5 text-body-sm font-mono text-surface-fg",
147
147
  ...n,
148
148
  children: t
149
149
  });
@@ -154,11 +154,11 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
154
154
  children: t
155
155
  }),
156
156
  ul: ({ children: t }) => /* @__PURE__ */ a("ul", {
157
- className: e("list-disc pl-ds-06 text-ds-md text-surface-fg space-y-ds-01", g),
157
+ className: e("list-disc pl-ds-06 text-body-md text-surface-fg space-y-ds-01", g),
158
158
  children: t
159
159
  }),
160
160
  ol: ({ children: t }) => /* @__PURE__ */ a("ol", {
161
- className: e("list-decimal pl-ds-06 text-ds-md text-surface-fg space-y-ds-01", g),
161
+ className: e("list-decimal pl-ds-06 text-body-md text-surface-fg space-y-ds-01", g),
162
162
  children: t
163
163
  }),
164
164
  table: ({ children: t }) => /* @__PURE__ */ a("div", {
@@ -169,11 +169,11 @@ var h = r.forwardRef(({ content: t, compact: n = !1, allowHtml: r = !1, linkTarg
169
169
  })
170
170
  }),
171
171
  th: ({ children: e }) => /* @__PURE__ */ a("th", {
172
- className: "border border-surface-border-subtle px-ds-03 py-ds-02 text-left text-ds-sm font-semibold bg-surface-sunken",
172
+ className: "border border-surface-border-subtle px-ds-03 py-ds-02 text-left text-body-sm font-semibold bg-surface-sunken",
173
173
  children: e
174
174
  }),
175
175
  td: ({ children: e }) => /* @__PURE__ */ a("td", {
176
- className: "border border-surface-border-subtle px-ds-03 py-ds-02 text-ds-sm",
176
+ className: "border border-surface-border-subtle px-ds-03 py-ds-02 text-body-sm",
177
177
  children: e
178
178
  }),
179
179
  hr: () => /* @__PURE__ */ a("hr", { className: e("border-surface-border-subtle", n ? "my-ds-03" : "my-ds-04") }),
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-viewer.js","names":[],"sources":["../../src/composed/markdown-viewer.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck,IconCopy } from '@tabler/icons-react'\nimport * as React from 'react'\nimport Markdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\n\n// react-syntax-highlighter is lazy-loaded in CodeBlock component below\n\n// ============================================================\n// Heading helpers\n// ============================================================\n\nfunction slugify(text: string): string {\n return text.toLowerCase().replace(/[^\\w]+/g, '-').replace(/(^-|-$)/g, '')\n}\n\n/** Recursively extract text content from React children */\nfunction extractText(node: React.ReactNode): string {\n if (typeof node === 'string') return node\n if (typeof node === 'number') return String(node)\n if (!node) return ''\n if (Array.isArray(node)) return node.map(extractText).join('')\n if (typeof node === 'object' && 'props' in node) {\n return extractText((node as React.ReactElement<{ children?: React.ReactNode }>).props.children)\n }\n return ''\n}\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MarkdownViewerProps extends React.HTMLAttributes<HTMLDivElement> {\n content: string\n /** Tighter spacing for inline use */\n compact?: boolean\n /** Allow raw HTML in markdown @default false */\n allowHtml?: boolean\n /** Link target @default '_blank' */\n linkTarget?: string\n}\n\n// ============================================================\n// MarkdownViewer\n// ============================================================\n\n// Small copy-to-clipboard button for code blocks\nfunction CopyButton({ code }: { code: string }) {\n const [copied, setCopied] = React.useState(false)\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n React.useEffect(() => () => clearTimeout(timerRef.current), [])\n\n function handleCopy() {\n navigator.clipboard.writeText(code).then(() => {\n setCopied(true)\n timerRef.current = setTimeout(() => setCopied(false), 2000)\n })\n }\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n className=\"absolute top-ds-02 right-ds-02 opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={handleCopy}\n aria-label={copied ? 'Copied' : 'Copy code'}\n >\n {copied ? <Icon icon={IconCheck} size=\"xs\" /> : <Icon icon={IconCopy} size=\"xs\" />}\n </Button>\n )\n}\n\n// Syntax-highlighted code block with lazy-loaded highlighter\nfunction CodeBlock({ language, code, mb }: { language: string; code: string; mb: string }) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const [modules, setModules] = React.useState<{ Highlighter: any; style: any } | null>(null)\n\n React.useEffect(() => {\n let cancelled = false\n Promise.all([\n import('react-syntax-highlighter'),\n import('react-syntax-highlighter/dist/esm/styles/prism/one-dark'),\n ]).then(([hlMod, styleMod]) => {\n if (!cancelled) {\n setModules({ Highlighter: hlMod.Prism ?? hlMod.default, style: styleMod.default })\n }\n }).catch(() => { /* fallback to plain pre */ })\n return () => { cancelled = true }\n }, [])\n\n if (!modules) {\n return (\n <div className={cn('group relative', mb)}>\n <pre className=\"bg-surface-sunken rounded-control p-ds-04 overflow-x-auto text-ds-sm font-mono\">\n <code>{code}</code>\n </pre>\n <CopyButton code={code} />\n </div>\n )\n }\n\n const { Highlighter, style } = modules\n return (\n <div className={cn('group relative', mb)}>\n <Highlighter\n language={language}\n style={style}\n customStyle={{\n margin: 0,\n borderRadius: 'var(--radius-ds-md)',\n fontSize: 'var(--text-ds-sm)',\n padding: 'var(--spacing-ds-04)',\n }}\n >\n {code}\n </Highlighter>\n <CopyButton code={code} />\n </div>\n )\n}\n\nconst MarkdownViewer = React.forwardRef<HTMLDivElement, MarkdownViewerProps>(({\n content,\n compact = false,\n allowHtml = false,\n linkTarget = '_blank',\n className,\n ...props\n}, ref) => {\n const mb = compact ? 'mb-ds-02' : 'mb-ds-03'\n const mt = compact ? 'mt-ds-03' : 'mt-ds-05'\n\n return (\n <div ref={ref} className={cn('font-sans text-surface-fg', className)} {...props}>\n <Markdown\n remarkPlugins={[remarkGfm]}\n skipHtml={!allowHtml}\n components={{\n h1: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h1 id={slug} className={cn('group', compact ? 'text-ds-md' : 'text-ds-lg', 'font-semibold text-surface-fg', mt, mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h1>\n )\n },\n h2: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h2 id={slug} className={cn('group', 'text-ds-md font-semibold text-surface-fg', compact ? 'mt-ds-03' : 'mt-ds-04', mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h2>\n )\n },\n h3: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h3 id={slug} className={cn('group', 'text-ds-md font-semibold text-surface-fg', 'mt-ds-03', mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h3>\n )\n },\n p: ({ children }) => (\n <p className={cn('text-ds-md text-surface-fg leading-ds-relaxed', mb)}>\n {children}\n </p>\n ),\n a: ({ href, children }) => (\n <a\n href={href}\n target={linkTarget}\n rel={linkTarget === '_blank' ? 'noopener noreferrer' : undefined}\n className=\"text-accent-11 hover:underline\"\n >\n {children}\n </a>\n ),\n code: ({ className: codeClassName, children, ...codeProps }) => {\n const langMatch = codeClassName ? /language-(\\w+)/.exec(codeClassName) : null\n if (langMatch) {\n const lang = langMatch[1]\n const codeString = String(children).replace(/\\n$/, '')\n return <CodeBlock language={lang} code={codeString} mb={mb} />\n }\n return (\n <code className=\"bg-surface-sunken rounded-control-inner px-1.5 py-0.5 text-ds-sm font-mono text-surface-fg\" {...codeProps}>\n {children}\n </code>\n )\n },\n pre: ({ children }) => (\n <>{children}</>\n ),\n blockquote: ({ children }) => (\n <blockquote className={cn('border-l-2 border-surface-border-subtle pl-ds-04 text-surface-fg-muted italic', mb)}>\n {children}\n </blockquote>\n ),\n ul: ({ children }) => (\n <ul className={cn('list-disc pl-ds-06 text-ds-md text-surface-fg space-y-ds-01', mb)}>\n {children}\n </ul>\n ),\n ol: ({ children }) => (\n <ol className={cn('list-decimal pl-ds-06 text-ds-md text-surface-fg space-y-ds-01', mb)}>\n {children}\n </ol>\n ),\n table: ({ children }) => (\n <div className={cn('overflow-x-auto', mb)}>\n <table className=\"w-full border-collapse\">\n {children}\n </table>\n </div>\n ),\n th: ({ children }) => (\n <th className=\"border border-surface-border-subtle px-ds-03 py-ds-02 text-left text-ds-sm font-semibold bg-surface-sunken\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"border border-surface-border-subtle px-ds-03 py-ds-02 text-ds-sm\">\n {children}\n </td>\n ),\n hr: () => <hr className={cn('border-surface-border-subtle', compact ? 'my-ds-03' : 'my-ds-04')} />,\n img: ({ src, alt }) => (\n <img src={src} alt={alt ?? ''} className=\"rounded-control max-w-full\" loading=\"lazy\" />\n ),\n }}\n >\n {content}\n </Markdown>\n </div>\n )\n})\nMarkdownViewer.displayName = 'MarkdownViewer'\n\nexport { MarkdownViewer }\n"],"mappings":"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"}
1
+ {"version":3,"file":"markdown-viewer.js","names":[],"sources":["../../src/composed/markdown-viewer.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck,IconCopy } from '@tabler/icons-react'\nimport * as React from 'react'\nimport Markdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\n\n// react-syntax-highlighter is lazy-loaded in CodeBlock component below\n\n// ============================================================\n// Heading helpers\n// ============================================================\n\nfunction slugify(text: string): string {\n return text.toLowerCase().replace(/[^\\w]+/g, '-').replace(/(^-|-$)/g, '')\n}\n\n/** Recursively extract text content from React children */\nfunction extractText(node: React.ReactNode): string {\n if (typeof node === 'string') return node\n if (typeof node === 'number') return String(node)\n if (!node) return ''\n if (Array.isArray(node)) return node.map(extractText).join('')\n if (typeof node === 'object' && 'props' in node) {\n return extractText((node as React.ReactElement<{ children?: React.ReactNode }>).props.children)\n }\n return ''\n}\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MarkdownViewerProps extends React.HTMLAttributes<HTMLDivElement> {\n content: string\n /** Tighter spacing for inline use */\n compact?: boolean\n /** Allow raw HTML in markdown @default false */\n allowHtml?: boolean\n /** Link target @default '_blank' */\n linkTarget?: string\n}\n\n// ============================================================\n// MarkdownViewer\n// ============================================================\n\n// Small copy-to-clipboard button for code blocks\nfunction CopyButton({ code }: { code: string }) {\n const [copied, setCopied] = React.useState(false)\n const timerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n React.useEffect(() => () => clearTimeout(timerRef.current), [])\n\n function handleCopy() {\n navigator.clipboard.writeText(code).then(() => {\n setCopied(true)\n timerRef.current = setTimeout(() => setCopied(false), 2000)\n })\n }\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n className=\"absolute top-ds-02 right-ds-02 opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={handleCopy}\n aria-label={copied ? 'Copied' : 'Copy code'}\n >\n {copied ? <Icon icon={IconCheck} size=\"xs\" /> : <Icon icon={IconCopy} size=\"xs\" />}\n </Button>\n )\n}\n\n// Syntax-highlighted code block with lazy-loaded highlighter\nfunction CodeBlock({ language, code, mb }: { language: string; code: string; mb: string }) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const [modules, setModules] = React.useState<{ Highlighter: any; style: any } | null>(null)\n\n React.useEffect(() => {\n let cancelled = false\n Promise.all([\n import('react-syntax-highlighter'),\n import('react-syntax-highlighter/dist/esm/styles/prism/one-dark'),\n ]).then(([hlMod, styleMod]) => {\n if (!cancelled) {\n setModules({ Highlighter: hlMod.Prism ?? hlMod.default, style: styleMod.default })\n }\n }).catch(() => { /* fallback to plain pre */ })\n return () => { cancelled = true }\n }, [])\n\n if (!modules) {\n return (\n <div className={cn('group relative', mb)}>\n <pre className=\"bg-surface-sunken rounded-control p-ds-04 overflow-x-auto text-body-sm font-mono\">\n <code>{code}</code>\n </pre>\n <CopyButton code={code} />\n </div>\n )\n }\n\n const { Highlighter, style } = modules\n return (\n <div className={cn('group relative', mb)}>\n <Highlighter\n language={language}\n style={style}\n customStyle={{\n margin: 0,\n borderRadius: 'var(--radius-ds-md)',\n fontSize: 'var(--text-body-sm)',\n padding: 'var(--spacing-ds-04)',\n }}\n >\n {code}\n </Highlighter>\n <CopyButton code={code} />\n </div>\n )\n}\n\nconst MarkdownViewer = React.forwardRef<HTMLDivElement, MarkdownViewerProps>(({\n content,\n compact = false,\n allowHtml = false,\n linkTarget = '_blank',\n className,\n ...props\n}, ref) => {\n const mb = compact ? 'mb-ds-02' : 'mb-ds-03'\n const mt = compact ? 'mt-ds-03' : 'mt-ds-05'\n\n return (\n <div ref={ref} className={cn('font-sans text-surface-fg', className)} {...props}>\n <Markdown\n remarkPlugins={[remarkGfm]}\n skipHtml={!allowHtml}\n components={{\n h1: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h1 id={slug} className={cn('group', compact ? 'text-body-md' : 'text-heading-xs', 'font-semibold text-surface-fg', mt, mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h1>\n )\n },\n h2: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h2 id={slug} className={cn('group', 'text-body-md font-semibold text-surface-fg', compact ? 'mt-ds-03' : 'mt-ds-04', mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h2>\n )\n },\n h3: ({ children }) => {\n const slug = slugify(extractText(children))\n return (\n <h3 id={slug} className={cn('group', 'text-body-md font-semibold text-surface-fg', 'mt-ds-03', mb)}>\n <a href={`#${slug}`} className=\"opacity-0 group-hover:opacity-100 text-surface-fg-subtle mr-ds-02 no-underline\" aria-hidden=\"true\">\n #\n </a>\n {children}\n </h3>\n )\n },\n p: ({ children }) => (\n <p className={cn('text-body-md text-surface-fg leading-ds-relaxed', mb)}>\n {children}\n </p>\n ),\n a: ({ href, children }) => (\n <a\n href={href}\n target={linkTarget}\n rel={linkTarget === '_blank' ? 'noopener noreferrer' : undefined}\n className=\"text-accent-11 hover:underline\"\n >\n {children}\n </a>\n ),\n code: ({ className: codeClassName, children, ...codeProps }) => {\n const langMatch = codeClassName ? /language-(\\w+)/.exec(codeClassName) : null\n if (langMatch) {\n const lang = langMatch[1]\n const codeString = String(children).replace(/\\n$/, '')\n return <CodeBlock language={lang} code={codeString} mb={mb} />\n }\n return (\n <code className=\"bg-surface-sunken rounded-control-inner px-1.5 py-0.5 text-body-sm font-mono text-surface-fg\" {...codeProps}>\n {children}\n </code>\n )\n },\n pre: ({ children }) => (\n <>{children}</>\n ),\n blockquote: ({ children }) => (\n <blockquote className={cn('border-l-2 border-surface-border-subtle pl-ds-04 text-surface-fg-muted italic', mb)}>\n {children}\n </blockquote>\n ),\n ul: ({ children }) => (\n <ul className={cn('list-disc pl-ds-06 text-body-md text-surface-fg space-y-ds-01', mb)}>\n {children}\n </ul>\n ),\n ol: ({ children }) => (\n <ol className={cn('list-decimal pl-ds-06 text-body-md text-surface-fg space-y-ds-01', mb)}>\n {children}\n </ol>\n ),\n table: ({ children }) => (\n <div className={cn('overflow-x-auto', mb)}>\n <table className=\"w-full border-collapse\">\n {children}\n </table>\n </div>\n ),\n th: ({ children }) => (\n <th className=\"border border-surface-border-subtle px-ds-03 py-ds-02 text-left text-body-sm font-semibold bg-surface-sunken\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"border border-surface-border-subtle px-ds-03 py-ds-02 text-body-sm\">\n {children}\n </td>\n ),\n hr: () => <hr className={cn('border-surface-border-subtle', compact ? 'my-ds-03' : 'my-ds-04')} />,\n img: ({ src, alt }) => (\n <img src={src} alt={alt ?? ''} className=\"rounded-control max-w-full\" loading=\"lazy\" />\n ),\n }}\n >\n {content}\n </Markdown>\n </div>\n )\n})\nMarkdownViewer.displayName = 'MarkdownViewer'\n\nexport { MarkdownViewer }\n"],"mappings":"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"}
@@ -122,7 +122,7 @@ var h = i.forwardRef(function({ active: e = !1, children: n, className: r, onKey
122
122
  "aria-selected": e,
123
123
  "data-active": e || void 0,
124
124
  onKeyDown: c,
125
- className: t("flex w-full items-center px-ds-04 py-ds-03 text-left text-ds-md font-sans text-surface-fg", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-raised-hover", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset", e && "bg-accent-2 text-accent-11 font-medium", r),
125
+ className: t("flex w-full items-center px-ds-04 py-ds-03 text-left text-body-md font-sans text-surface-fg", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-raised-hover", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset", e && "bg-accent-2 text-accent-11 font-medium", r),
126
126
  ...o,
127
127
  children: n
128
128
  });