@devalok/shilp-sutra 0.44.1 → 0.45.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 (131) hide show
  1. package/AGENTS.md +13 -11
  2. package/BREAKING.json +209 -27
  3. package/MIGRATION.md +34 -0
  4. package/README.md +12 -0
  5. package/dist/_chunks/badge-group.js +1 -0
  6. package/dist/_chunks/badge-group.js.map +1 -1
  7. package/dist/_chunks/block-shell.js +25 -0
  8. package/dist/_chunks/block-shell.js.map +1 -0
  9. package/dist/_chunks/chat.js +4 -2
  10. package/dist/_chunks/chat.js.map +1 -1
  11. package/dist/_chunks/document-preview.js +1 -1
  12. package/dist/_chunks/image-preview.js +4 -4
  13. package/dist/_chunks/shared.js +65 -78
  14. package/dist/_chunks/shared.js.map +1 -1
  15. package/dist/_chunks/success.js +108 -106
  16. package/dist/_chunks/success.js.map +1 -1
  17. package/dist/ai/blocks/block-shell.d.ts +19 -0
  18. package/dist/ai/blocks/block-shell.d.ts.map +1 -0
  19. package/dist/ai/blocks/block-table.d.ts.map +1 -1
  20. package/dist/ai/blocks/confirm.d.ts.map +1 -1
  21. package/dist/ai/blocks/error.d.ts.map +1 -1
  22. package/dist/ai/blocks/error.js +5 -5
  23. package/dist/ai/blocks/error.js.map +1 -1
  24. package/dist/ai/blocks/success.d.ts.map +1 -1
  25. package/dist/ai/blocks/text.d.ts.map +1 -1
  26. package/dist/ai/blocks/text.js +4 -3
  27. package/dist/ai/blocks/text.js.map +1 -1
  28. package/dist/composed/activity-feed.d.ts.map +1 -1
  29. package/dist/composed/activity-feed.js +14 -12
  30. package/dist/composed/activity-feed.js.map +1 -1
  31. package/dist/composed/file-preview/audio-preview.d.ts.map +1 -1
  32. package/dist/composed/file-preview/shared.d.ts +10 -0
  33. package/dist/composed/file-preview/shared.d.ts.map +1 -1
  34. package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
  35. package/dist/composed/file-preview.js +149 -188
  36. package/dist/composed/file-preview.js.map +1 -1
  37. package/dist/composed/master-detail.d.ts.map +1 -1
  38. package/dist/composed/master-detail.js +13 -4
  39. package/dist/composed/master-detail.js.map +1 -1
  40. package/dist/composed/schedule-view.d.ts.map +1 -1
  41. package/dist/composed/schedule-view.js +52 -39
  42. package/dist/composed/schedule-view.js.map +1 -1
  43. package/dist/shell/notification-preferences.d.ts.map +1 -1
  44. package/dist/shell/notification-preferences.js +115 -114
  45. package/dist/shell/notification-preferences.js.map +1 -1
  46. package/dist/ui/badge-group.d.ts.map +1 -1
  47. package/dist/ui/card.d.ts +66 -16
  48. package/dist/ui/card.d.ts.map +1 -1
  49. package/dist/ui/card.js +85 -60
  50. package/dist/ui/card.js.map +1 -1
  51. package/dist/ui/chat/message.d.ts.map +1 -1
  52. package/dist/ui/color-input.d.ts.map +1 -1
  53. package/dist/ui/color-input.js +18 -18
  54. package/dist/ui/color-input.js.map +1 -1
  55. package/dist/ui/data-table-body.d.ts.map +1 -1
  56. package/dist/ui/data-table-body.js +94 -64
  57. package/dist/ui/data-table-body.js.map +1 -1
  58. package/dist/ui/data-table-card.d.ts.map +1 -1
  59. package/dist/ui/data-table-card.js +50 -43
  60. package/dist/ui/data-table-card.js.map +1 -1
  61. package/dist/ui/data-table-context.d.ts +0 -4
  62. package/dist/ui/data-table-context.d.ts.map +1 -1
  63. package/dist/ui/data-table-context.js +8 -12
  64. package/dist/ui/data-table-context.js.map +1 -1
  65. package/dist/ui/data-table-header.d.ts.map +1 -1
  66. package/dist/ui/data-table-header.js +2 -2
  67. package/dist/ui/data-table-header.js.map +1 -1
  68. package/dist/ui/data-table.d.ts.map +1 -1
  69. package/dist/ui/data-table.js +185 -182
  70. package/dist/ui/data-table.js.map +1 -1
  71. package/dist/ui/index.d.ts +3 -2
  72. package/dist/ui/index.d.ts.map +1 -1
  73. package/dist/ui/index.js +59 -58
  74. package/dist/ui/index.js.map +1 -1
  75. package/dist/ui/segmented-control.js +4 -4
  76. package/dist/ui/segmented-control.js.map +1 -1
  77. package/dist/ui/separator.d.ts +6 -5
  78. package/dist/ui/separator.d.ts.map +1 -1
  79. package/dist/ui/separator.js +7 -10
  80. package/dist/ui/separator.js.map +1 -1
  81. package/dist/ui/split-button.d.ts.map +1 -1
  82. package/dist/ui/split-button.js +62 -129
  83. package/dist/ui/split-button.js.map +1 -1
  84. package/dist/ui/stat-card.d.ts +7 -0
  85. package/dist/ui/stat-card.d.ts.map +1 -1
  86. package/dist/ui/stat-card.js +144 -126
  87. package/dist/ui/stat-card.js.map +1 -1
  88. package/dist/ui/table-row-link.d.ts +45 -0
  89. package/dist/ui/table-row-link.d.ts.map +1 -0
  90. package/dist/ui/table-row-link.js +16 -0
  91. package/dist/ui/table-row-link.js.map +1 -0
  92. package/dist/ui/table.d.ts +49 -5
  93. package/dist/ui/table.d.ts.map +1 -1
  94. package/dist/ui/table.js +39 -29
  95. package/dist/ui/table.js.map +1 -1
  96. package/dist/ui/toast-types.d.ts +10 -0
  97. package/dist/ui/toast-types.d.ts.map +1 -1
  98. package/dist/ui/toast.d.ts +6 -2
  99. package/dist/ui/toast.d.ts.map +1 -1
  100. package/dist/ui/toast.js +56 -54
  101. package/dist/ui/toast.js.map +1 -1
  102. package/docs/components/composed/content-card.md +2 -0
  103. package/docs/components/ui/button.md +3 -3
  104. package/docs/components/ui/card.md +27 -10
  105. package/docs/components/ui/color-input.md +2 -2
  106. package/docs/components/ui/data-table.md +8 -1
  107. package/docs/components/ui/segmented-control.md +2 -2
  108. package/docs/components/ui/separator.md +3 -1
  109. package/docs/components/ui/split-button.md +3 -0
  110. package/docs/components/ui/stat-card.md +10 -2
  111. package/docs/components/ui/table-row-link.md +41 -0
  112. package/docs/components/ui/table.md +69 -0
  113. package/docs/components/ui/toast.md +10 -6
  114. package/docs/recipes/install-next-app-router.md +1 -1
  115. package/docs/recipes/troubleshoot.md +1 -1
  116. package/llms.txt +159 -781
  117. package/make-kit/components/card.md +54 -16
  118. package/make-kit/components/table.md +45 -7
  119. package/make-kit/foundations/spacing.md +3 -1
  120. package/make-kit/foundations/surfaces.md +1 -1
  121. package/mcp-manifest.json +12349 -0
  122. package/mcp-manifest.schema.json +232 -0
  123. package/package.json +3 -3
  124. package/skill/README.md +0 -1
  125. package/skill/SKILL.md +9 -8
  126. package/skill/references/components.md +159 -781
  127. package/skill/references/setup-next-app-router.md +1 -1
  128. package/skill/references/troubleshoot.md +1 -1
  129. package/llms-full.txt +0 -7198
  130. package/llms-quick.txt +0 -251
  131. package/skill/references/components-full.md +0 -7200
@@ -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,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 const [hoverTime, setHoverTime] = React.useState<number | null>(null)\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(e: React.MouseEvent<HTMLDivElement>) {\n if (!audioRef.current || !duration) return\n const rect = e.currentTarget.getBoundingClientRect()\n const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))\n audioRef.current.currentTime = pct * duration\n }\n\n function handleHover(e: React.MouseEvent<HTMLDivElement>) {\n if (!duration) return\n const rect = e.currentTarget.getBoundingClientRect()\n const pct = (e.clientX - rect.left) / rect.width\n setHoverTime(pct * 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\n className=\"relative h-1 w-full cursor-pointer bg-surface-sunken group/bar\"\n onClick={handleSeek}\n onMouseMove={handleHover}\n onMouseLeave={() => setHoverTime(null)}\n role=\"slider\"\n aria-label=\"Audio progress\"\n aria-valuenow={Math.round(progress)}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n >\n <div\n className=\"absolute left-0 top-0 h-full bg-accent-9 transition-[width] duration-75\"\n style={{ width: `${progress}%` }}\n />\n {/* Hover time tooltip */}\n {hoverTime !== null && (\n <div\n className=\"absolute -top-7 -translate-x-1/2 rounded-control-inner bg-surface-overlay px-1.5 py-0.5 text-[10px] font-mono text-surface-fg shadow-floating pointer-events-none\"\n style={{ left: `${(hoverTime / (duration || 1)) * 100}%` }}\n >\n {formatTime(hoverTime)}\n </div>\n )}\n {/* Scrub handle */}\n <div\n className=\"absolute top-1/2 -translate-y-1/2 h-2.5 w-2.5 rounded-pill bg-accent-9 shadow-raised opacity-0 group-hover/bar:opacity-100 transition-opacity\"\n style={{ left: `${progress}%`, marginLeft: '-5px' }}\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,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 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 // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRate 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(e: React.MouseEvent<HTMLDivElement>) {\n if (!videoRef.current || !duration) return\n const rect = e.currentTarget.getBoundingClientRect()\n const pct = (e.clientX - rect.left) / rect.width\n videoRef.current.currentTime = pct * 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 <div\n className=\"relative h-1 w-full cursor-pointer rounded-pill bg-white/30 mb-ds-03 group/progress\"\n onClick={handleSeek}\n role=\"slider\"\n aria-label=\"Video progress\"\n aria-valuenow={Math.round(progress)}\n aria-valuemin={0}\n aria-valuemax={100}\n tabIndex={0}\n >\n <div\n className=\"absolute left-0 top-0 h-full rounded-pill bg-white transition-[width] duration-75\"\n style={{ width: `${progress}%` }}\n />\n {/* Scrub handle */}\n <div\n className=\"absolute top-1/2 -translate-y-1/2 h-3 w-3 rounded-pill bg-white shadow-raised opacity-0 group-hover/progress:opacity-100 transition-opacity\"\n style={{ left: `${progress}%`, marginLeft: '-6px' }}\n />\n </div>\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-[11px] 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-[11px] 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-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 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 // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRate 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-[11px] 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-[11px] 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 +1 @@
1
- {"version":3,"file":"master-detail.d.ts","sourceRoot":"","sources":["../../src/composed/master-detail.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAA;CAChD;AAED,UAAU,qBAAsB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAE/E,UAAU,uBAAwB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAEjF,UAAU,yBAA0B,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACvF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA0DD,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,kDA8DjF;AAED,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,kDA4BrF;AAwCD,iBAAS,gBAAgB,CAAC,EACxB,QAAe,EACf,MAAM,EACN,WAAqB,EACrB,UAAiB,EACjB,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,iBAAiB,2CA4BnB;AAED,QAAA,MAAM,YAAY;;;;CAIhB,CAAA;AAEF,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,YAAY,EAAE,yBAAyB,EAAE,CAAA"}
1
+ {"version":3,"file":"master-detail.d.ts","sourceRoot":"","sources":["../../src/composed/master-detail.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAW9B;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mEAAmE;IACnE,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAC/B,mEAAmE;IACnE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAA;CAChD;AAED,UAAU,qBAAsB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAE/E,UAAU,uBAAwB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;CAAG;AAEjF,UAAU,yBAA0B,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACvF,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA0DD,iBAAS,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,qBAAqB,kDAgFjF;AAED,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,kDA4BrF;AAwCD,iBAAS,gBAAgB,CAAC,EACxB,QAAe,EACf,MAAM,EACN,WAAqB,EACrB,UAAiB,EACjB,UAAU,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,iBAAiB,2CA4BnB;AAED,QAAA,MAAM,YAAY;;;;CAIhB,CAAA;AAEF,OAAO,EAAE,YAAY,EAAE,CAAA;AACvB,YAAY,EAAE,yBAAyB,EAAE,CAAA"}
@@ -34,10 +34,19 @@ var f = i.createContext({
34
34
  });
35
35
  function p({ children: e, className: n, ...r }) {
36
36
  let { selected: o, isMobile: s, onNavigate: c, activeIndex: l, setActiveIndex: u, registerItemRef: d, setItemCount: p } = i.useContext(f), m = i.useRef([]), h = i.Children.count(e);
37
- if (i.useEffect(() => {
37
+ i.useEffect(() => {
38
38
  p(h);
39
- }, [h, p]), s && o) return null;
40
- function g(e) {
39
+ }, [h, p]);
40
+ let g = i.useMemo(() => {
41
+ let t = -1;
42
+ return i.Children.forEach(e, (e, n) => {
43
+ i.isValidElement(e) && e.props.active && (t = n);
44
+ }), t;
45
+ }, [e]);
46
+ if (i.useEffect(() => {
47
+ g >= 0 && u(g);
48
+ }, [g, u]), s && o) return null;
49
+ function _(e) {
41
50
  if (e.key === "ArrowDown") {
42
51
  e.preventDefault();
43
52
  let t = Math.min(l + 1, h - 1);
@@ -57,7 +66,7 @@ function p({ children: e, className: n, ...r }) {
57
66
  role: "listbox",
58
67
  tabIndex: -1,
59
68
  className: t("overflow-y-auto focus-visible:outline-hidden", !s && "border-r border-surface-border", n),
60
- onKeyDown: g,
69
+ onKeyDown: _,
61
70
  ...r,
62
71
  children: i.Children.map(e, (e, t) => i.isValidElement(e) ? i.cloneElement(e, {
63
72
  ref: (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"master-detail.js","names":[],"sources":["../../src/composed/master-detail.tsx"],"sourcesContent":["'use client'\n\nimport { IconArrowLeft } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * A responsive master-detail layout. On desktop, shows a fixed-width list panel\n * alongside a detail panel. Below the breakpoint, switches to a stacked view\n * with animated transitions and a back button.\n */\nexport interface MasterDetailProps extends React.HTMLAttributes<HTMLDivElement> {\n /** ID of currently selected item. null = no selection (show list on mobile). */\n selected?: string | null\n /** Called when mobile back button is pressed. */\n onBack?: () => void\n /** Master panel width on desktop. @default '280px' */\n masterWidth?: string\n /** Breakpoint below which stacked mode activates. @default 'md' */\n breakpoint?: 'sm' | 'md' | 'lg'\n /** Content to show in the detail pane when nothing is selected. */\n emptyState?: React.ReactNode\n /** Called when user presses ArrowUp/ArrowDown while the list has focus. */\n onNavigate?: (direction: 'up' | 'down') => void\n}\n\ninterface MasterDetailListProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailDetailProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\n// ============================================================\n// Breakpoint values\n// ============================================================\n\nconst breakpoints: Record<string, string> = {\n sm: '640px',\n md: '768px',\n lg: '1024px',\n}\n\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(false)\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n setMatches(mql.matches)\n const handler = (e: MediaQueryListEvent) => setMatches(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [query])\n\n return matches\n}\n\n// ============================================================\n// Context\n// ============================================================\n\ninterface MasterDetailContextValue {\n selected: string | null\n isMobile: boolean\n onBack?: () => void\n emptyState?: React.ReactNode\n onNavigate?: (direction: 'up' | 'down') => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n registerItemRef: (index: number, el: HTMLButtonElement | null) => void\n itemCount: number\n setItemCount: (count: number) => void\n}\n\nconst MasterDetailContext = React.createContext<MasterDetailContextValue>({\n selected: null,\n isMobile: false,\n activeIndex: 0,\n setActiveIndex: () => {},\n registerItemRef: () => {},\n itemCount: 0,\n setItemCount: () => {},\n})\n\n// ============================================================\n// Compound Components\n// ============================================================\n\nfunction MasterDetailList({ children, className, ...props }: MasterDetailListProps) {\n const { selected, isMobile, onNavigate, activeIndex, setActiveIndex, registerItemRef, setItemCount } = React.useContext(MasterDetailContext)\n const itemRefsLocal = React.useRef<(HTMLButtonElement | null)[]>([])\n\n // Count children and register with context\n const childCount = React.Children.count(children)\n React.useEffect(() => {\n setItemCount(childCount)\n }, [childCount, setItemCount])\n\n // On mobile, hide list when something is selected\n if (isMobile && selected) return null\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = Math.min(activeIndex + 1, childCount - 1)\n setActiveIndex(next)\n itemRefsLocal.current[next]?.focus()\n onNavigate?.('down')\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = Math.max(activeIndex - 1, 0)\n setActiveIndex(prev)\n itemRefsLocal.current[prev]?.focus()\n onNavigate?.('up')\n } else if (e.key === 'Home') {\n e.preventDefault()\n setActiveIndex(0)\n itemRefsLocal.current[0]?.focus()\n } else if (e.key === 'End') {\n e.preventDefault()\n const last = childCount - 1\n setActiveIndex(last)\n itemRefsLocal.current[last]?.focus()\n }\n }\n\n return (\n <div\n role=\"listbox\"\n tabIndex={-1}\n className={cn(\n 'overflow-y-auto focus-visible:outline-hidden',\n !isMobile && 'border-r border-surface-border',\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {React.Children.map(children, (child, i) => {\n if (!React.isValidElement(child)) return child\n return React.cloneElement(child as React.ReactElement<any>, {\n ref: (el: HTMLButtonElement | null) => {\n itemRefsLocal.current[i] = el\n registerItemRef(i, el)\n },\n tabIndex: i === activeIndex ? 0 : -1,\n })\n })}\n </div>\n )\n}\n\nfunction MasterDetailDetail({ children, className, ...props }: MasterDetailDetailProps) {\n const { selected, isMobile, onBack, emptyState } = React.useContext(MasterDetailContext)\n\n // On mobile, hide detail when nothing selected\n if (isMobile && !selected) return null\n\n return (\n <div className={cn('flex-1 overflow-y-auto', className)} {...props}>\n {isMobile && onBack && (\n <div className=\"border-b border-surface-border px-ds-04 py-ds-03\">\n <Button variant=\"ghost\" size=\"xs\" onClick={onBack} startIcon={<Icon icon={IconArrowLeft} />}>\n Back\n </Button>\n </div>\n )}\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={selected ?? 'empty'}\n initial={isMobile ? { x: 20, opacity: 0 } : false}\n animate={{ x: 0, opacity: 1 }}\n exit={isMobile ? { x: -20, opacity: 0 } : undefined}\n transition={springs.snappy}\n >\n {selected ? children : (emptyState ?? children)}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nconst MasterDetailListItem = React.forwardRef<HTMLButtonElement, MasterDetailListItemProps>(\n function MasterDetailListItem({ active = false, children, className, onKeyDown, ...props }, ref) {\n function handleKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n ;(e.target as HTMLButtonElement).click()\n }\n onKeyDown?.(e)\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n data-active={active || undefined}\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full items-center px-ds-04 py-ds-03 text-left text-ds-md font-sans text-surface-fg',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset',\n active && 'bg-accent-2 text-accent-11 font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n },\n)\n\n// ============================================================\n// MasterDetail Root\n// ============================================================\n\nfunction MasterDetailRoot({\n selected = null,\n onBack,\n masterWidth = '280px',\n breakpoint = 'md',\n emptyState,\n onNavigate,\n children,\n className,\n style,\n ...props\n}: MasterDetailProps) {\n const isMobile = useMediaQuery(`(max-width: ${breakpoints[breakpoint]})`)\n const [activeIndex, setActiveIndex] = React.useState(0)\n const [itemCount, setItemCount] = React.useState(0)\n const itemRefs = React.useRef<(HTMLButtonElement | null)[]>([])\n\n const registerItemRef = React.useCallback((index: number, el: HTMLButtonElement | null) => {\n itemRefs.current[index] = el\n }, [])\n\n return (\n <MasterDetailContext.Provider value={{ selected, isMobile, onBack, emptyState, onNavigate, activeIndex, setActiveIndex, registerItemRef, itemCount, setItemCount }}>\n <div\n className={cn(\n 'flex h-full',\n !isMobile && 'grid',\n className,\n )}\n style={{\n ...style,\n ...(isMobile ? {} : { gridTemplateColumns: `${masterWidth} 1fr` }),\n }}\n {...props}\n >\n {children}\n </div>\n </MasterDetailContext.Provider>\n )\n}\n\nconst MasterDetail = Object.assign(MasterDetailRoot, {\n List: MasterDetailList,\n Detail: MasterDetailDetail,\n ListItem: MasterDetailListItem,\n})\n\nexport { MasterDetail }\nexport type { MasterDetailListItemProps }\n"],"mappings":"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"}
1
+ {"version":3,"file":"master-detail.js","names":[],"sources":["../../src/composed/master-detail.tsx"],"sourcesContent":["'use client'\n\nimport { IconArrowLeft } from '@tabler/icons-react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n// ============================================================\n// Types\n// ============================================================\n\n/**\n * A responsive master-detail layout. On desktop, shows a fixed-width list panel\n * alongside a detail panel. Below the breakpoint, switches to a stacked view\n * with animated transitions and a back button.\n */\nexport interface MasterDetailProps extends React.HTMLAttributes<HTMLDivElement> {\n /** ID of currently selected item. null = no selection (show list on mobile). */\n selected?: string | null\n /** Called when mobile back button is pressed. */\n onBack?: () => void\n /** Master panel width on desktop. @default '280px' */\n masterWidth?: string\n /** Breakpoint below which stacked mode activates. @default 'md' */\n breakpoint?: 'sm' | 'md' | 'lg'\n /** Content to show in the detail pane when nothing is selected. */\n emptyState?: React.ReactNode\n /** Called when user presses ArrowUp/ArrowDown while the list has focus. */\n onNavigate?: (direction: 'up' | 'down') => void\n}\n\ninterface MasterDetailListProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailDetailProps extends React.HTMLAttributes<HTMLDivElement> {}\n\ninterface MasterDetailListItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\n// ============================================================\n// Breakpoint values\n// ============================================================\n\nconst breakpoints: Record<string, string> = {\n sm: '640px',\n md: '768px',\n lg: '1024px',\n}\n\nfunction useMediaQuery(query: string): boolean {\n const [matches, setMatches] = React.useState(false)\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const mql = window.matchMedia(query)\n setMatches(mql.matches)\n const handler = (e: MediaQueryListEvent) => setMatches(e.matches)\n mql.addEventListener('change', handler)\n return () => mql.removeEventListener('change', handler)\n }, [query])\n\n return matches\n}\n\n// ============================================================\n// Context\n// ============================================================\n\ninterface MasterDetailContextValue {\n selected: string | null\n isMobile: boolean\n onBack?: () => void\n emptyState?: React.ReactNode\n onNavigate?: (direction: 'up' | 'down') => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n registerItemRef: (index: number, el: HTMLButtonElement | null) => void\n itemCount: number\n setItemCount: (count: number) => void\n}\n\nconst MasterDetailContext = React.createContext<MasterDetailContextValue>({\n selected: null,\n isMobile: false,\n activeIndex: 0,\n setActiveIndex: () => {},\n registerItemRef: () => {},\n itemCount: 0,\n setItemCount: () => {},\n})\n\n// ============================================================\n// Compound Components\n// ============================================================\n\nfunction MasterDetailList({ children, className, ...props }: MasterDetailListProps) {\n const { selected, isMobile, onNavigate, activeIndex, setActiveIndex, registerItemRef, setItemCount } = React.useContext(MasterDetailContext)\n const itemRefsLocal = React.useRef<(HTMLButtonElement | null)[]>([])\n\n // Count children and register with context\n const childCount = React.Children.count(children)\n React.useEffect(() => {\n setItemCount(childCount)\n }, [childCount, setItemCount])\n\n // Keep roving focus in sync with the active/selected item, so keyboard nav\n // starts on the current selection instead of always the first row.\n const activeChildIndex = React.useMemo(() => {\n let idx = -1\n React.Children.forEach(children, (child, i) => {\n if (\n React.isValidElement(child) &&\n (child.props as { active?: boolean }).active\n ) {\n idx = i\n }\n })\n return idx\n }, [children])\n React.useEffect(() => {\n if (activeChildIndex >= 0) setActiveIndex(activeChildIndex)\n }, [activeChildIndex, setActiveIndex])\n\n // On mobile, hide list when something is selected\n if (isMobile && selected) return null\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = Math.min(activeIndex + 1, childCount - 1)\n setActiveIndex(next)\n itemRefsLocal.current[next]?.focus()\n onNavigate?.('down')\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n const prev = Math.max(activeIndex - 1, 0)\n setActiveIndex(prev)\n itemRefsLocal.current[prev]?.focus()\n onNavigate?.('up')\n } else if (e.key === 'Home') {\n e.preventDefault()\n setActiveIndex(0)\n itemRefsLocal.current[0]?.focus()\n } else if (e.key === 'End') {\n e.preventDefault()\n const last = childCount - 1\n setActiveIndex(last)\n itemRefsLocal.current[last]?.focus()\n }\n }\n\n return (\n <div\n role=\"listbox\"\n tabIndex={-1}\n className={cn(\n 'overflow-y-auto focus-visible:outline-hidden',\n !isMobile && 'border-r border-surface-border',\n className,\n )}\n onKeyDown={handleKeyDown}\n {...props}\n >\n {React.Children.map(children, (child, i) => {\n if (!React.isValidElement(child)) return child\n return React.cloneElement(child as React.ReactElement<any>, {\n ref: (el: HTMLButtonElement | null) => {\n itemRefsLocal.current[i] = el\n registerItemRef(i, el)\n },\n tabIndex: i === activeIndex ? 0 : -1,\n })\n })}\n </div>\n )\n}\n\nfunction MasterDetailDetail({ children, className, ...props }: MasterDetailDetailProps) {\n const { selected, isMobile, onBack, emptyState } = React.useContext(MasterDetailContext)\n\n // On mobile, hide detail when nothing selected\n if (isMobile && !selected) return null\n\n return (\n <div className={cn('flex-1 overflow-y-auto', className)} {...props}>\n {isMobile && onBack && (\n <div className=\"border-b border-surface-border px-ds-04 py-ds-03\">\n <Button variant=\"ghost\" size=\"xs\" onClick={onBack} startIcon={<Icon icon={IconArrowLeft} />}>\n Back\n </Button>\n </div>\n )}\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={selected ?? 'empty'}\n initial={isMobile ? { x: 20, opacity: 0 } : false}\n animate={{ x: 0, opacity: 1 }}\n exit={isMobile ? { x: -20, opacity: 0 } : undefined}\n transition={springs.snappy}\n >\n {selected ? children : (emptyState ?? children)}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nconst MasterDetailListItem = React.forwardRef<HTMLButtonElement, MasterDetailListItemProps>(\n function MasterDetailListItem({ active = false, children, className, onKeyDown, ...props }, ref) {\n function handleKeyDown(e: React.KeyboardEvent<HTMLButtonElement>) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n ;(e.target as HTMLButtonElement).click()\n }\n onKeyDown?.(e)\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"option\"\n aria-selected={active}\n data-active={active || undefined}\n onKeyDown={handleKeyDown}\n className={cn(\n 'flex w-full items-center px-ds-04 py-ds-03 text-left text-ds-md font-sans text-surface-fg',\n 'transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-7 focus-visible:ring-inset',\n active && 'bg-accent-2 text-accent-11 font-medium',\n className,\n )}\n {...props}\n >\n {children}\n </button>\n )\n },\n)\n\n// ============================================================\n// MasterDetail Root\n// ============================================================\n\nfunction MasterDetailRoot({\n selected = null,\n onBack,\n masterWidth = '280px',\n breakpoint = 'md',\n emptyState,\n onNavigate,\n children,\n className,\n style,\n ...props\n}: MasterDetailProps) {\n const isMobile = useMediaQuery(`(max-width: ${breakpoints[breakpoint]})`)\n const [activeIndex, setActiveIndex] = React.useState(0)\n const [itemCount, setItemCount] = React.useState(0)\n const itemRefs = React.useRef<(HTMLButtonElement | null)[]>([])\n\n const registerItemRef = React.useCallback((index: number, el: HTMLButtonElement | null) => {\n itemRefs.current[index] = el\n }, [])\n\n return (\n <MasterDetailContext.Provider value={{ selected, isMobile, onBack, emptyState, onNavigate, activeIndex, setActiveIndex, registerItemRef, itemCount, setItemCount }}>\n <div\n className={cn(\n 'flex h-full',\n !isMobile && 'grid',\n className,\n )}\n style={{\n ...style,\n ...(isMobile ? {} : { gridTemplateColumns: `${masterWidth} 1fr` }),\n }}\n {...props}\n >\n {children}\n </div>\n </MasterDetailContext.Provider>\n )\n}\n\nconst MasterDetail = Object.assign(MasterDetailRoot, {\n List: MasterDetailList,\n Detail: MasterDetailDetail,\n ListItem: MasterDetailListItem,\n})\n\nexport { MasterDetail }\nexport type { MasterDetailListItemProps }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-view.d.ts","sourceRoot":"","sources":["../../src/composed/schedule-view.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,EAAE,IAAI,CAAA;IACT,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;CACzE;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gEAAgE;IAChE,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;IACpB,6EAA6E;IAC7E,IAAI,EAAE,IAAI,CAAA;IACV,8CAA8C;IAC9C,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,sFAAsF;IACtF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,KAAK,IAAI,CAAA;IAC9C,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAkND,QAAA,MAAM,YAAY,0FAyDjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"schedule-view.d.ts","sourceRoot":"","sources":["../../src/composed/schedule-view.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,IAAI,CAAA;IACX,GAAG,EAAE,IAAI,CAAA;IACT,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;CACzE;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC7E,gEAAgE;IAChE,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;IACpB,6EAA6E;IAC7E,IAAI,EAAE,IAAI,CAAA;IACV,8CAA8C;IAC9C,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC7C,sFAAsF;IACtF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,KAAK,IAAI,CAAA;IAC9C,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAuOD,QAAA,MAAM,YAAY,0FAyDjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -6,24 +6,31 @@ import { motion as i } from "framer-motion";
6
6
  import { addDays as a, differenceInMinutes as o, format as s, getHours as c, getMinutes as l, isSameDay as u, isToday as d, startOfWeek as f } from "date-fns";
7
7
  //#region src/composed/schedule-view.tsx
8
8
  var p = {
9
- primary: "bg-accent-2 border-accent-7 text-accent-11",
10
- success: "bg-success-3 border-success-7 text-success-11",
11
- warning: "bg-warning-3 border-warning-7 text-warning-11",
12
- error: "bg-error-3 border-error-7 text-error-11",
13
- info: "bg-info-3 border-info-7 text-info-11",
14
- neutral: "bg-surface-raised border-surface-border-strong text-surface-fg-muted"
9
+ primary: "bg-accent-2 text-accent-11",
10
+ success: "bg-success-3 text-success-11",
11
+ warning: "bg-warning-3 text-warning-11",
12
+ error: "bg-error-3 text-error-11",
13
+ info: "bg-info-3 text-info-11",
14
+ neutral: "bg-surface-raised text-surface-fg-muted"
15
+ }, m = {
16
+ primary: "bg-accent-9",
17
+ success: "bg-success-9",
18
+ warning: "bg-warning-9",
19
+ error: "bg-error-9",
20
+ info: "bg-info-9",
21
+ neutral: "bg-surface-fg-subtle"
15
22
  };
16
- function m(e) {
23
+ function h(e) {
17
24
  return e === 0 ? "12 AM" : e < 12 ? `${e} AM` : e === 12 ? "12 PM" : `${e - 12} PM`;
18
25
  }
19
- function h(e, t, n) {
26
+ function g(e, t, n) {
20
27
  let r = (n - t) * 60, i = (c(e.start) - t) * 60 + l(e.start), a = o(e.end, e.start), s = i / r * 100, u = a / r * 100;
21
28
  return {
22
29
  top: `${s}%`,
23
30
  height: `${u}%`
24
31
  };
25
32
  }
26
- function g({ startHour: e, endHour: t }) {
33
+ function _({ startHour: e, endHour: t }) {
27
34
  let r = [];
28
35
  for (let n = e; n < t; n++) r.push(n);
29
36
  let i = 100 / (t - e);
@@ -33,60 +40,66 @@ function g({ startHour: e, endHour: t }) {
33
40
  children: r.map((e) => /* @__PURE__ */ n("div", {
34
41
  className: "text-ds-xs text-surface-fg-muted pr-ds-02 text-right",
35
42
  style: { height: `${i}%` },
36
- children: m(e)
43
+ children: h(e)
37
44
  }, e))
38
45
  });
39
46
  }
40
- function _({ date: t, events: a, startHour: o, endHour: f, slotDuration: m, onEventClick: g, onSlotClick: _, showHeader: v }) {
41
- let y = a.filter((e) => u(e.start, t)), b = (f - o) * 60, x = b / m, S = [];
42
- for (let e = 0; e < x; e++) {
47
+ function v({ date: t, events: a, startHour: o, endHour: f, slotDuration: h, onEventClick: _, onSlotClick: v, showHeader: y }) {
48
+ let b = a.filter((e) => u(e.start, t)), x = (f - o) * 60, S = x / h, C = [];
49
+ for (let e = 0; e < S; e++) {
43
50
  let n = new Date(t);
44
- n.setHours(o, e * m, 0, 0);
51
+ n.setHours(o, e * h, 0, 0);
45
52
  let r = new Date(n);
46
- r.setMinutes(r.getMinutes() + m), S.push({
53
+ r.setMinutes(r.getMinutes() + h), C.push({
47
54
  start: n,
48
55
  end: r
49
56
  });
50
57
  }
51
- let C = d(t), w = null;
52
- if (C) {
58
+ let w = d(t), T = null;
59
+ if (w) {
53
60
  let e = /* @__PURE__ */ new Date(), t = c(e), n = l(e);
54
- t >= o && t < f && (w = ((t - o) * 60 + n) / b * 100);
61
+ t >= o && t < f && (T = ((t - o) * 60 + n) / x * 100);
55
62
  }
56
63
  return /* @__PURE__ */ r("div", {
57
64
  className: "flex flex-1 flex-col min-w-[80px]",
58
- children: [v && /* @__PURE__ */ n("div", {
59
- className: e("text-center text-ds-sm font-semibold py-ds-02 border-b border-surface-border-strong", C ? "text-accent-11 bg-accent-2" : "text-surface-fg bg-surface-raised"),
65
+ children: [y && /* @__PURE__ */ n("div", {
66
+ className: e("text-center text-ds-sm font-semibold py-ds-02 border-b border-surface-border-strong", w ? "text-accent-11 bg-accent-2" : "text-surface-fg bg-surface-raised"),
60
67
  children: s(t, "EEE d")
61
68
  }), /* @__PURE__ */ r("div", {
62
69
  className: "relative flex-1",
63
70
  children: [
64
- S.map((t, r) => /* @__PURE__ */ n("button", {
71
+ C.map((t, r) => /* @__PURE__ */ n("button", {
65
72
  type: "button",
66
73
  className: e("block w-full border-b border-surface-border hover:bg-surface-raised-hover transition-colors ease-productive-standard", r % 2 == 0 ? "border-surface-border-strong" : "border-surface-border-subtle"),
67
- style: { height: `${100 / x}%` },
68
- onClick: () => _?.(t.start, t.end),
74
+ style: { height: `${100 / S}%` },
75
+ onClick: () => v?.(t.start, t.end),
69
76
  "aria-label": `${s(t.start, "h:mm a")} to ${s(t.end, "h:mm a")}`
70
77
  }, r)),
71
- y.map((t) => {
72
- let r = h(t, o, f), i = p[t.color ?? "primary"];
78
+ b.map((t) => {
79
+ let i = g(t, o, f), a = t.color ?? "primary", c = p[a], l = m[a];
73
80
  return /* @__PURE__ */ n("button", {
74
81
  type: "button",
75
- className: e("absolute left-ds-01 right-ds-01 rounded-control-inner border-l-[3px] px-ds-02 py-ds-01", "text-left text-ds-xs font-medium overflow-hidden cursor-pointer", "hover:shadow-raised hover:scale-[1.02] transition-[box-shadow,transform] duration-fast-02 ease-productive-standard", i),
76
- style: r,
82
+ className: e("absolute left-ds-01 right-ds-01 rounded-control-inner px-ds-02 py-ds-01", "text-left text-ds-xs font-medium overflow-hidden cursor-pointer", "hover:shadow-raised hover:scale-[1.02] transition-[box-shadow,transform] duration-fast-02 ease-productive-standard", c),
83
+ style: i,
77
84
  onClick: (e) => {
78
- e.stopPropagation(), g?.(t);
85
+ e.stopPropagation(), _?.(t);
79
86
  },
80
87
  "aria-label": `${t.title}, ${s(t.start, "h:mm a")} to ${s(t.end, "h:mm a")}`,
81
- children: /* @__PURE__ */ n("span", {
82
- className: "line-clamp-2",
83
- children: t.title
88
+ children: /* @__PURE__ */ r("span", {
89
+ className: "flex items-start gap-ds-02",
90
+ children: [/* @__PURE__ */ n("span", {
91
+ className: e("mt-[3px] h-ds-03 w-ds-03 shrink-0 rounded-pill", l),
92
+ "aria-hidden": "true"
93
+ }), /* @__PURE__ */ n("span", {
94
+ className: "line-clamp-2",
95
+ children: t.title
96
+ })]
84
97
  })
85
98
  }, t.id);
86
99
  }),
87
- w != null && /* @__PURE__ */ n("div", {
100
+ T != null && /* @__PURE__ */ n("div", {
88
101
  className: "absolute left-0 right-0 h-[2px] bg-error-9 z-10 pointer-events-none",
89
- style: { top: `${w}%` },
102
+ style: { top: `${T}%` },
90
103
  "aria-hidden": "true",
91
104
  children: /* @__PURE__ */ n(i.span, {
92
105
  className: "absolute -left-[5px] -top-[4px] h-[10px] w-[10px] rounded-pill bg-error-9",
@@ -105,7 +118,7 @@ function _({ date: t, events: a, startHour: o, endHour: f, slotDuration: m, onEv
105
118
  })]
106
119
  });
107
120
  }
108
- var v = t.forwardRef(({ view: i, date: o, events: c, onEventClick: l, onSlotClick: u, startHour: d = 8, endHour: p = 18, slotDuration: m = 30, className: h, ...v }, y) => {
121
+ var y = t.forwardRef(({ view: i, date: o, events: c, onEventClick: l, onSlotClick: u, startHour: d = 8, endHour: p = 18, slotDuration: m = 30, className: h, ...g }, y) => {
109
122
  let b = t.useMemo(() => {
110
123
  if (i === "day") return [o];
111
124
  let e = f(o, { weekStartsOn: 1 });
@@ -116,13 +129,13 @@ var v = t.forwardRef(({ view: i, date: o, events: c, onEventClick: l, onSlotClic
116
129
  role: "region",
117
130
  "aria-label": i === "day" ? `Schedule for ${s(o, "EEEE, MMMM d, yyyy")}` : `Week schedule starting ${s(b[0], "MMMM d, yyyy")}`,
118
131
  className: e("flex rounded-control border border-surface-border-strong bg-surface-raised overflow-hidden", "h-[480px]", h),
119
- ...v,
120
- children: [/* @__PURE__ */ n(g, {
132
+ ...g,
133
+ children: [/* @__PURE__ */ n(_, {
121
134
  startHour: d,
122
135
  endHour: p
123
136
  }), /* @__PURE__ */ n("div", {
124
137
  className: "flex flex-1 divide-x divide-surface-border overflow-x-auto",
125
- children: b.map((e) => /* @__PURE__ */ n(_, {
138
+ children: b.map((e) => /* @__PURE__ */ n(v, {
126
139
  date: e,
127
140
  events: c,
128
141
  startHour: d,
@@ -135,6 +148,6 @@ var v = t.forwardRef(({ view: i, date: o, events: c, onEventClick: l, onSlotClic
135
148
  })]
136
149
  });
137
150
  });
138
- v.displayName = "ScheduleView";
151
+ y.displayName = "ScheduleView";
139
152
  //#endregion
140
- export { v as ScheduleView };
153
+ export { y as ScheduleView };
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-view.js","names":[],"sources":["../../src/composed/schedule-view.tsx"],"sourcesContent":["'use client'\n\nimport {\n addDays,\n differenceInMinutes,\n format,\n getHours,\n getMinutes,\n isSameDay,\n isToday,\n startOfWeek,\n} from 'date-fns'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface ScheduleEvent {\n id: string\n title: string\n start: Date\n end: Date\n color?: 'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral'\n}\n\n/**\n * A day/week calendar schedule grid. Renders time-slotted events with\n * proportional height, color coding, click handlers, and a current-time indicator.\n */\nexport interface ScheduleViewProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Display mode: single-day column or full-week (7 columns). */\n view: 'day' | 'week'\n /** Current day (day view) or any date within the target week (week view). */\n date: Date\n /** Events to display on the calendar grid. */\n events: ScheduleEvent[]\n /** Called when an event block is clicked. */\n onEventClick?: (event: ScheduleEvent) => void\n /** Called when an empty time slot is clicked, with the slot's start and end times. */\n onSlotClick?: (start: Date, end: Date) => void\n /** First visible hour (default 8). */\n startHour?: number\n /** Last visible hour -- exclusive (default 18). */\n endHour?: number\n /** Slot duration in minutes (default 30). */\n slotDuration?: number\n}\n\n/* ------------------------------------------------------------------ */\n/* Color map */\n/* ------------------------------------------------------------------ */\n\nconst eventColorMap: Record<\n NonNullable<ScheduleEvent['color']>,\n string\n> = {\n primary: 'bg-accent-2 border-accent-7 text-accent-11',\n success: 'bg-success-3 border-success-7 text-success-11',\n warning: 'bg-warning-3 border-warning-7 text-warning-11',\n error: 'bg-error-3 border-error-7 text-error-11',\n info: 'bg-info-3 border-info-7 text-info-11',\n neutral: 'bg-surface-raised border-surface-border-strong text-surface-fg-muted',\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction formatHourLabel(hour: number): string {\n if (hour === 0) return '12 AM'\n if (hour < 12) return `${hour} AM`\n if (hour === 12) return '12 PM'\n return `${hour - 12} PM`\n}\n\nfunction getEventStyle(\n event: ScheduleEvent,\n startHour: number,\n endHour: number,\n) {\n const totalMinutes = (endHour - startHour) * 60\n const eventStartMinutes =\n (getHours(event.start) - startHour) * 60 + getMinutes(event.start)\n const durationMinutes = differenceInMinutes(event.end, event.start)\n\n const top = (eventStartMinutes / totalMinutes) * 100\n const height = (durationMinutes / totalMinutes) * 100\n\n return {\n top: `${top}%`,\n height: `${height}%`,\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Sub-components */\n/* ------------------------------------------------------------------ */\n\ninterface TimeColumnProps {\n startHour: number\n endHour: number\n}\n\nfunction TimeColumn({ startHour, endHour }: TimeColumnProps) {\n const hours: number[] = []\n for (let h = startHour; h < endHour; h++) {\n hours.push(h)\n }\n const slotHeight = 100 / (endHour - startHour)\n\n return (\n <div\n className=\"relative shrink-0 w-[60px] border-r border-surface-border-strong\"\n aria-hidden=\"true\"\n >\n {hours.map((hour) => (\n <div\n key={hour}\n className=\"text-ds-xs text-surface-fg-muted pr-ds-02 text-right\"\n style={{ height: `${slotHeight}%` }}\n >\n {formatHourLabel(hour)}\n </div>\n ))}\n </div>\n )\n}\n\ninterface DayColumnProps {\n date: Date\n events: ScheduleEvent[]\n startHour: number\n endHour: number\n slotDuration: number\n onEventClick?: (event: ScheduleEvent) => void\n onSlotClick?: (start: Date, end: Date) => void\n showHeader?: boolean\n}\n\nfunction DayColumn({\n date,\n events,\n startHour,\n endHour,\n slotDuration,\n onEventClick,\n onSlotClick,\n showHeader,\n}: DayColumnProps) {\n const dayEvents = events.filter((e) => isSameDay(e.start, date))\n const totalMinutes = (endHour - startHour) * 60\n const slotCount = totalMinutes / slotDuration\n\n const slots: { start: Date; end: Date }[] = []\n for (let i = 0; i < slotCount; i++) {\n const slotStart = new Date(date)\n slotStart.setHours(startHour, i * slotDuration, 0, 0)\n const slotEnd = new Date(slotStart)\n slotEnd.setMinutes(slotEnd.getMinutes() + slotDuration)\n slots.push({ start: slotStart, end: slotEnd })\n }\n\n const todayInView = isToday(date)\n\n // Current time indicator position\n let nowIndicatorTop: number | null = null\n if (todayInView) {\n const now = new Date()\n const nowHour = getHours(now)\n const nowMin = getMinutes(now)\n if (nowHour >= startHour && nowHour < endHour) {\n const nowMinutes = (nowHour - startHour) * 60 + nowMin\n nowIndicatorTop = (nowMinutes / totalMinutes) * 100\n }\n }\n\n return (\n <div className=\"flex flex-1 flex-col min-w-[80px]\">\n {showHeader && (\n <div\n className={cn(\n 'text-center text-ds-sm font-semibold py-ds-02 border-b border-surface-border-strong',\n todayInView\n ? 'text-accent-11 bg-accent-2'\n : 'text-surface-fg bg-surface-raised',\n )}\n >\n {format(date, 'EEE d')}\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* Slot grid lines */}\n {slots.map((slot, i) => (\n <button\n key={i}\n type=\"button\"\n className={cn(\n 'block w-full border-b border-surface-border hover:bg-surface-raised-hover transition-colors ease-productive-standard',\n i % 2 === 0 ? 'border-surface-border-strong' : 'border-surface-border-subtle',\n )}\n style={{ height: `${100 / slotCount}%` }}\n onClick={() => onSlotClick?.(slot.start, slot.end)}\n aria-label={`${format(slot.start, 'h:mm a')} to ${format(slot.end, 'h:mm a')}`}\n />\n ))}\n\n {/* Events */}\n {dayEvents.map((event) => {\n const style = getEventStyle(event, startHour, endHour)\n const colorClass =\n eventColorMap[event.color ?? 'primary']\n return (\n <button\n key={event.id}\n type=\"button\"\n className={cn(\n 'absolute left-ds-01 right-ds-01 rounded-control-inner border-l-[3px] px-ds-02 py-ds-01',\n 'text-left text-ds-xs font-medium overflow-hidden cursor-pointer',\n 'hover:shadow-raised hover:scale-[1.02] transition-[box-shadow,transform] duration-fast-02 ease-productive-standard',\n colorClass,\n )}\n style={style}\n onClick={(e) => {\n e.stopPropagation()\n onEventClick?.(event)\n }}\n aria-label={`${event.title}, ${format(event.start, 'h:mm a')} to ${format(event.end, 'h:mm a')}`}\n >\n <span className=\"line-clamp-2\">{event.title}</span>\n </button>\n )\n })}\n\n {/* Current time indicator */}\n {nowIndicatorTop != null && (\n <div\n className=\"absolute left-0 right-0 h-[2px] bg-error-9 z-10 pointer-events-none\"\n style={{ top: `${nowIndicatorTop}%` }}\n aria-hidden=\"true\"\n >\n <motion.span\n className=\"absolute -left-[5px] -top-[4px] h-[10px] w-[10px] rounded-pill bg-error-9\"\n animate={{ scale: [1, 1.3, 1] }}\n transition={{ repeat: Infinity, duration: 2 }}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ */\n/* ScheduleView */\n/* ------------------------------------------------------------------ */\n\nconst ScheduleView = React.forwardRef<HTMLDivElement, ScheduleViewProps>(\n (\n {\n view,\n date,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n endHour = 18,\n slotDuration = 30,\n className,\n ...props\n },\n ref,\n ) => {\n const days: Date[] = React.useMemo(() => {\n if (view === 'day') return [date]\n const weekStart = startOfWeek(date, { weekStartsOn: 1 }) // Monday\n return Array.from({ length: 7 }, (_, i) => addDays(weekStart, i))\n }, [view, date])\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={\n view === 'day'\n ? `Schedule for ${format(date, 'EEEE, MMMM d, yyyy')}`\n : `Week schedule starting ${format(days[0], 'MMMM d, yyyy')}`\n }\n className={cn(\n 'flex rounded-control border border-surface-border-strong bg-surface-raised overflow-hidden',\n 'h-[480px]',\n className,\n )}\n {...props}\n >\n <TimeColumn startHour={startHour} endHour={endHour} />\n <div className=\"flex flex-1 divide-x divide-surface-border overflow-x-auto\">\n {days.map((day) => (\n <DayColumn\n key={day.toISOString()}\n date={day}\n events={events}\n startHour={startHour}\n endHour={endHour}\n slotDuration={slotDuration}\n onEventClick={onEventClick}\n onSlotClick={onSlotClick}\n showHeader={view === 'week'}\n />\n ))}\n </div>\n </div>\n )\n },\n)\nScheduleView.displayName = 'ScheduleView'\n\nexport { ScheduleView }\n"],"mappings":"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"}
1
+ {"version":3,"file":"schedule-view.js","names":[],"sources":["../../src/composed/schedule-view.tsx"],"sourcesContent":["'use client'\n\nimport {\n addDays,\n differenceInMinutes,\n format,\n getHours,\n getMinutes,\n isSameDay,\n isToday,\n startOfWeek,\n} from 'date-fns'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface ScheduleEvent {\n id: string\n title: string\n start: Date\n end: Date\n color?: 'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral'\n}\n\n/**\n * A day/week calendar schedule grid. Renders time-slotted events with\n * proportional height, color coding, click handlers, and a current-time indicator.\n */\nexport interface ScheduleViewProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Display mode: single-day column or full-week (7 columns). */\n view: 'day' | 'week'\n /** Current day (day view) or any date within the target week (week view). */\n date: Date\n /** Events to display on the calendar grid. */\n events: ScheduleEvent[]\n /** Called when an event block is clicked. */\n onEventClick?: (event: ScheduleEvent) => void\n /** Called when an empty time slot is clicked, with the slot's start and end times. */\n onSlotClick?: (start: Date, end: Date) => void\n /** First visible hour (default 8). */\n startHour?: number\n /** Last visible hour -- exclusive (default 18). */\n endHour?: number\n /** Slot duration in minutes (default 30). */\n slotDuration?: number\n}\n\n/* ------------------------------------------------------------------ */\n/* Color map */\n/* ------------------------------------------------------------------ */\n\nconst eventColorMap: Record<\n NonNullable<ScheduleEvent['color']>,\n string\n> = {\n primary: 'bg-accent-2 text-accent-11',\n success: 'bg-success-3 text-success-11',\n warning: 'bg-warning-3 text-warning-11',\n error: 'bg-error-3 text-error-11',\n info: 'bg-info-3 text-info-11',\n neutral: 'bg-surface-raised text-surface-fg-muted',\n}\n\n// Solid category dot — the color-blind-safe, shape-based signal that replaces the\n// former left accent rail (the AI tell). Tint above carries the ambient color.\nconst eventDotMap: Record<\n NonNullable<ScheduleEvent['color']>,\n string\n> = {\n primary: 'bg-accent-9',\n success: 'bg-success-9',\n warning: 'bg-warning-9',\n error: 'bg-error-9',\n info: 'bg-info-9',\n neutral: 'bg-surface-fg-subtle',\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction formatHourLabel(hour: number): string {\n if (hour === 0) return '12 AM'\n if (hour < 12) return `${hour} AM`\n if (hour === 12) return '12 PM'\n return `${hour - 12} PM`\n}\n\nfunction getEventStyle(\n event: ScheduleEvent,\n startHour: number,\n endHour: number,\n) {\n const totalMinutes = (endHour - startHour) * 60\n const eventStartMinutes =\n (getHours(event.start) - startHour) * 60 + getMinutes(event.start)\n const durationMinutes = differenceInMinutes(event.end, event.start)\n\n const top = (eventStartMinutes / totalMinutes) * 100\n const height = (durationMinutes / totalMinutes) * 100\n\n return {\n top: `${top}%`,\n height: `${height}%`,\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Sub-components */\n/* ------------------------------------------------------------------ */\n\ninterface TimeColumnProps {\n startHour: number\n endHour: number\n}\n\nfunction TimeColumn({ startHour, endHour }: TimeColumnProps) {\n const hours: number[] = []\n for (let h = startHour; h < endHour; h++) {\n hours.push(h)\n }\n const slotHeight = 100 / (endHour - startHour)\n\n return (\n <div\n className=\"relative shrink-0 w-[60px] border-r border-surface-border-strong\"\n aria-hidden=\"true\"\n >\n {hours.map((hour) => (\n <div\n key={hour}\n className=\"text-ds-xs text-surface-fg-muted pr-ds-02 text-right\"\n style={{ height: `${slotHeight}%` }}\n >\n {formatHourLabel(hour)}\n </div>\n ))}\n </div>\n )\n}\n\ninterface DayColumnProps {\n date: Date\n events: ScheduleEvent[]\n startHour: number\n endHour: number\n slotDuration: number\n onEventClick?: (event: ScheduleEvent) => void\n onSlotClick?: (start: Date, end: Date) => void\n showHeader?: boolean\n}\n\nfunction DayColumn({\n date,\n events,\n startHour,\n endHour,\n slotDuration,\n onEventClick,\n onSlotClick,\n showHeader,\n}: DayColumnProps) {\n const dayEvents = events.filter((e) => isSameDay(e.start, date))\n const totalMinutes = (endHour - startHour) * 60\n const slotCount = totalMinutes / slotDuration\n\n const slots: { start: Date; end: Date }[] = []\n for (let i = 0; i < slotCount; i++) {\n const slotStart = new Date(date)\n slotStart.setHours(startHour, i * slotDuration, 0, 0)\n const slotEnd = new Date(slotStart)\n slotEnd.setMinutes(slotEnd.getMinutes() + slotDuration)\n slots.push({ start: slotStart, end: slotEnd })\n }\n\n const todayInView = isToday(date)\n\n // Current time indicator position\n let nowIndicatorTop: number | null = null\n if (todayInView) {\n const now = new Date()\n const nowHour = getHours(now)\n const nowMin = getMinutes(now)\n if (nowHour >= startHour && nowHour < endHour) {\n const nowMinutes = (nowHour - startHour) * 60 + nowMin\n nowIndicatorTop = (nowMinutes / totalMinutes) * 100\n }\n }\n\n return (\n <div className=\"flex flex-1 flex-col min-w-[80px]\">\n {showHeader && (\n <div\n className={cn(\n 'text-center text-ds-sm font-semibold py-ds-02 border-b border-surface-border-strong',\n todayInView\n ? 'text-accent-11 bg-accent-2'\n : 'text-surface-fg bg-surface-raised',\n )}\n >\n {format(date, 'EEE d')}\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* Slot grid lines */}\n {slots.map((slot, i) => (\n <button\n key={i}\n type=\"button\"\n className={cn(\n 'block w-full border-b border-surface-border hover:bg-surface-raised-hover transition-colors ease-productive-standard',\n i % 2 === 0 ? 'border-surface-border-strong' : 'border-surface-border-subtle',\n )}\n style={{ height: `${100 / slotCount}%` }}\n onClick={() => onSlotClick?.(slot.start, slot.end)}\n aria-label={`${format(slot.start, 'h:mm a')} to ${format(slot.end, 'h:mm a')}`}\n />\n ))}\n\n {/* Events */}\n {dayEvents.map((event) => {\n const style = getEventStyle(event, startHour, endHour)\n const eventColor = event.color ?? 'primary'\n const colorClass = eventColorMap[eventColor]\n const dotClass = eventDotMap[eventColor]\n return (\n <button\n key={event.id}\n type=\"button\"\n className={cn(\n 'absolute left-ds-01 right-ds-01 rounded-control-inner px-ds-02 py-ds-01',\n 'text-left text-ds-xs font-medium overflow-hidden cursor-pointer',\n 'hover:shadow-raised hover:scale-[1.02] transition-[box-shadow,transform] duration-fast-02 ease-productive-standard',\n colorClass,\n )}\n style={style}\n onClick={(e) => {\n e.stopPropagation()\n onEventClick?.(event)\n }}\n aria-label={`${event.title}, ${format(event.start, 'h:mm a')} to ${format(event.end, 'h:mm a')}`}\n >\n <span className=\"flex items-start gap-ds-02\">\n <span\n className={cn('mt-[3px] h-ds-03 w-ds-03 shrink-0 rounded-pill', dotClass)}\n aria-hidden=\"true\"\n />\n <span className=\"line-clamp-2\">{event.title}</span>\n </span>\n </button>\n )\n })}\n\n {/* Current time indicator */}\n {nowIndicatorTop != null && (\n <div\n className=\"absolute left-0 right-0 h-[2px] bg-error-9 z-10 pointer-events-none\"\n style={{ top: `${nowIndicatorTop}%` }}\n aria-hidden=\"true\"\n >\n <motion.span\n className=\"absolute -left-[5px] -top-[4px] h-[10px] w-[10px] rounded-pill bg-error-9\"\n animate={{ scale: [1, 1.3, 1] }}\n transition={{ repeat: Infinity, duration: 2 }}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ */\n/* ScheduleView */\n/* ------------------------------------------------------------------ */\n\nconst ScheduleView = React.forwardRef<HTMLDivElement, ScheduleViewProps>(\n (\n {\n view,\n date,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n endHour = 18,\n slotDuration = 30,\n className,\n ...props\n },\n ref,\n ) => {\n const days: Date[] = React.useMemo(() => {\n if (view === 'day') return [date]\n const weekStart = startOfWeek(date, { weekStartsOn: 1 }) // Monday\n return Array.from({ length: 7 }, (_, i) => addDays(weekStart, i))\n }, [view, date])\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={\n view === 'day'\n ? `Schedule for ${format(date, 'EEEE, MMMM d, yyyy')}`\n : `Week schedule starting ${format(days[0], 'MMMM d, yyyy')}`\n }\n className={cn(\n 'flex rounded-control border border-surface-border-strong bg-surface-raised overflow-hidden',\n 'h-[480px]',\n className,\n )}\n {...props}\n >\n <TimeColumn startHour={startHour} endHour={endHour} />\n <div className=\"flex flex-1 divide-x divide-surface-border overflow-x-auto\">\n {days.map((day) => (\n <DayColumn\n key={day.toISOString()}\n date={day}\n events={events}\n startHour={startHour}\n endHour={endHour}\n slotDuration={slotDuration}\n onEventClick={onEventClick}\n onSlotClick={onSlotClick}\n showHeader={view === 'week'}\n />\n ))}\n </div>\n </div>\n )\n },\n)\nScheduleView.displayName = 'ScheduleView'\n\nexport { ScheduleView }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"notification-preferences.d.ts","sourceRoot":"","sources":["../../src/shell/notification-preferences.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA4B9B,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IAC1D,+BAA+B;IAC/B,WAAW,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACtC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAChC,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE;QACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,OAAO,CAAA;KACf,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E,gDAAgD;IAChD,YAAY,CAAC,EAAE,CACb,UAAU,EAAE,sBAAsB,EAClC,OAAO,EAAE,MAAM,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAqBD,QAAA,MAAM,uBAAuB,kHA0P5B,CAAA;AAID,OAAO,EAAE,uBAAuB,EAAE,CAAA"}
1
+ {"version":3,"file":"notification-preferences.d.ts","sourceRoot":"","sources":["../../src/shell/notification-preferences.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA6B9B,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IAC1D,+BAA+B;IAC/B,WAAW,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACtC,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAChC,8BAA8B;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE;QACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,OAAO,CAAA;KACf,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E,gDAAgD;IAChD,YAAY,CAAC,EAAE,CACb,UAAU,EAAE,sBAAsB,EAClC,OAAO,EAAE,MAAM,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAqBD,QAAA,MAAM,uBAAuB,kHA4P5B,CAAA;AAID,OAAO,EAAE,uBAAuB,EAAE,CAAA"}