@devalok/shilp-sutra 0.56.0 → 0.57.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 (253) hide show
  1. package/MIGRATION.md +107 -0
  2. package/dist/_chunks/badge-group.js +48 -34
  3. package/dist/_chunks/badge-group.js.map +1 -1
  4. package/dist/_chunks/chat.js +91 -75
  5. package/dist/_chunks/chat.js.map +1 -1
  6. package/dist/_chunks/document-preview.js +1 -1
  7. package/dist/_chunks/document-preview.js.map +1 -1
  8. package/dist/_chunks/emoji-suggestion.js +1 -1
  9. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  10. package/dist/_chunks/mention-suggestion.js +2 -2
  11. package/dist/_chunks/mention-suggestion.js.map +1 -1
  12. package/dist/_chunks/oauth-button.js +1 -1
  13. package/dist/_chunks/oauth-button.js.map +1 -1
  14. package/dist/_chunks/success.js +2 -2
  15. package/dist/_chunks/success.js.map +1 -1
  16. package/dist/_chunks/tree-view.js +1 -1
  17. package/dist/_chunks/tree-view.js.map +1 -1
  18. package/dist/_chunks/use-calendar.js +4 -4
  19. package/dist/_chunks/use-calendar.js.map +1 -1
  20. package/dist/ai/blocks/text.js +1 -1
  21. package/dist/ai/blocks/text.js.map +1 -1
  22. package/dist/ai/command-bar.js +9 -9
  23. package/dist/ai/command-bar.js.map +1 -1
  24. package/dist/ai/conversation.js +2 -2
  25. package/dist/ai/conversation.js.map +1 -1
  26. package/dist/composed/activity-feed.js +1 -1
  27. package/dist/composed/activity-feed.js.map +1 -1
  28. package/dist/composed/avatar-group.d.ts +7 -2
  29. package/dist/composed/avatar-group.d.ts.map +1 -1
  30. package/dist/composed/avatar-group.js +3 -3
  31. package/dist/composed/avatar-group.js.map +1 -1
  32. package/dist/composed/command-palette.js +7 -7
  33. package/dist/composed/command-palette.js.map +1 -1
  34. package/dist/composed/content-card.js +2 -2
  35. package/dist/composed/content-card.js.map +1 -1
  36. package/dist/composed/date-picker/index.js +4 -4
  37. package/dist/composed/date-picker/index.js.map +1 -1
  38. package/dist/composed/diff.d.ts +23 -2
  39. package/dist/composed/diff.d.ts.map +1 -1
  40. package/dist/composed/diff.js +341 -260
  41. package/dist/composed/diff.js.map +1 -1
  42. package/dist/composed/emoji-picker.js +3 -3
  43. package/dist/composed/emoji-picker.js.map +1 -1
  44. package/dist/composed/error-boundary.js +2 -2
  45. package/dist/composed/error-boundary.js.map +1 -1
  46. package/dist/composed/file-preview.js +1 -1
  47. package/dist/composed/file-preview.js.map +1 -1
  48. package/dist/composed/filter-bar.js +1 -1
  49. package/dist/composed/filter-bar.js.map +1 -1
  50. package/dist/composed/inline-edit.js +1 -1
  51. package/dist/composed/inline-edit.js.map +1 -1
  52. package/dist/composed/loading-skeleton.js +3 -3
  53. package/dist/composed/loading-skeleton.js.map +1 -1
  54. package/dist/composed/master-detail.js +1 -1
  55. package/dist/composed/master-detail.js.map +1 -1
  56. package/dist/composed/member-picker.js +1 -1
  57. package/dist/composed/member-picker.js.map +1 -1
  58. package/dist/composed/multi-select-popover.js +1 -1
  59. package/dist/composed/multi-select-popover.js.map +1 -1
  60. package/dist/composed/page-skeletons.js +4 -4
  61. package/dist/composed/page-skeletons.js.map +1 -1
  62. package/dist/composed/responsive-modal.js +1 -1
  63. package/dist/composed/responsive-modal.js.map +1 -1
  64. package/dist/composed/rich-chat-input.js +17 -17
  65. package/dist/composed/rich-chat-input.js.map +1 -1
  66. package/dist/composed/rich-text-editor.js +5 -5
  67. package/dist/composed/rich-text-editor.js.map +1 -1
  68. package/dist/composed/schedule-view.js +5 -5
  69. package/dist/composed/schedule-view.js.map +1 -1
  70. package/dist/hooks/index.d.ts +1 -0
  71. package/dist/hooks/index.d.ts.map +1 -1
  72. package/dist/hooks/index.js +4 -3
  73. package/dist/hooks/use-container-size.d.ts +18 -0
  74. package/dist/hooks/use-container-size.d.ts.map +1 -0
  75. package/dist/hooks/use-container-size.js +28 -0
  76. package/dist/hooks/use-container-size.js.map +1 -0
  77. package/dist/shell/app-shell.d.ts +101 -0
  78. package/dist/shell/app-shell.d.ts.map +1 -0
  79. package/dist/shell/app-shell.js +76 -0
  80. package/dist/shell/app-shell.js.map +1 -0
  81. package/dist/shell/bottom-navbar.js +2 -2
  82. package/dist/shell/bottom-navbar.js.map +1 -1
  83. package/dist/shell/index.d.ts +1 -0
  84. package/dist/shell/index.d.ts.map +1 -1
  85. package/dist/shell/index.js +6 -5
  86. package/dist/shell/notification-center.js +4 -4
  87. package/dist/shell/notification-center.js.map +1 -1
  88. package/dist/shell/notification-preferences.js +1 -1
  89. package/dist/shell/notification-preferences.js.map +1 -1
  90. package/dist/shell/top-bar.js +7 -7
  91. package/dist/shell/top-bar.js.map +1 -1
  92. package/dist/tokens/generate-scale.d.ts +49 -0
  93. package/dist/tokens/generate-scale.d.ts.map +1 -1
  94. package/dist/tokens/primitives.css +29 -26
  95. package/dist/tokens/semantic.css +98 -39
  96. package/dist/tokens/variants.css +1 -1
  97. package/dist/ui/accordion.js +1 -1
  98. package/dist/ui/accordion.js.map +1 -1
  99. package/dist/ui/alert-dialog.js +1 -1
  100. package/dist/ui/alert-dialog.js.map +1 -1
  101. package/dist/ui/alert.d.ts.map +1 -1
  102. package/dist/ui/alert.js +7 -7
  103. package/dist/ui/alert.js.map +1 -1
  104. package/dist/ui/avatar.js +3 -3
  105. package/dist/ui/avatar.js.map +1 -1
  106. package/dist/ui/badge-indicator.js +1 -1
  107. package/dist/ui/badge-indicator.js.map +1 -1
  108. package/dist/ui/badge.d.ts +47 -33
  109. package/dist/ui/badge.d.ts.map +1 -1
  110. package/dist/ui/banner.js +1 -1
  111. package/dist/ui/banner.js.map +1 -1
  112. package/dist/ui/button.js +4 -4
  113. package/dist/ui/button.js.map +1 -1
  114. package/dist/ui/card.js +4 -4
  115. package/dist/ui/card.js.map +1 -1
  116. package/dist/ui/chat/date-separator.d.ts +8 -1
  117. package/dist/ui/chat/date-separator.d.ts.map +1 -1
  118. package/dist/ui/checkbox.js +1 -1
  119. package/dist/ui/checkbox.js.map +1 -1
  120. package/dist/ui/code.js +2 -2
  121. package/dist/ui/code.js.map +1 -1
  122. package/dist/ui/color-input.js +3 -3
  123. package/dist/ui/color-input.js.map +1 -1
  124. package/dist/ui/combobox.js +4 -4
  125. package/dist/ui/combobox.js.map +1 -1
  126. package/dist/ui/context-menu.js +4 -4
  127. package/dist/ui/context-menu.js.map +1 -1
  128. package/dist/ui/data-table-body.d.ts +3 -1
  129. package/dist/ui/data-table-body.d.ts.map +1 -1
  130. package/dist/ui/data-table-body.js +93 -92
  131. package/dist/ui/data-table-body.js.map +1 -1
  132. package/dist/ui/data-table-bulk-actions.d.ts +7 -1
  133. package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
  134. package/dist/ui/data-table-bulk-actions.js +14 -8
  135. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  136. package/dist/ui/data-table-card.d.ts.map +1 -1
  137. package/dist/ui/data-table-card.js +66 -47
  138. package/dist/ui/data-table-card.js.map +1 -1
  139. package/dist/ui/data-table-context.d.ts +4 -0
  140. package/dist/ui/data-table-context.d.ts.map +1 -1
  141. package/dist/ui/data-table-context.js.map +1 -1
  142. package/dist/ui/data-table-header.d.ts.map +1 -1
  143. package/dist/ui/data-table-header.js +18 -15
  144. package/dist/ui/data-table-header.js.map +1 -1
  145. package/dist/ui/data-table-pagination.js +3 -3
  146. package/dist/ui/data-table-pagination.js.map +1 -1
  147. package/dist/ui/data-table-toolbar.d.ts +11 -1
  148. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  149. package/dist/ui/data-table-toolbar.js +7 -7
  150. package/dist/ui/data-table-toolbar.js.map +1 -1
  151. package/dist/ui/data-table.d.ts +52 -5
  152. package/dist/ui/data-table.d.ts.map +1 -1
  153. package/dist/ui/data-table.js +181 -169
  154. package/dist/ui/data-table.js.map +1 -1
  155. package/dist/ui/dialog.js +1 -1
  156. package/dist/ui/dialog.js.map +1 -1
  157. package/dist/ui/dot.js +1 -1
  158. package/dist/ui/dot.js.map +1 -1
  159. package/dist/ui/dropdown-menu.js +4 -4
  160. package/dist/ui/dropdown-menu.js.map +1 -1
  161. package/dist/ui/file-upload.js +3 -3
  162. package/dist/ui/file-upload.js.map +1 -1
  163. package/dist/ui/input-otp.js +1 -1
  164. package/dist/ui/input-otp.js.map +1 -1
  165. package/dist/ui/input.js +8 -8
  166. package/dist/ui/input.js.map +1 -1
  167. package/dist/ui/menubar.js +5 -5
  168. package/dist/ui/menubar.js.map +1 -1
  169. package/dist/ui/navigation-menu.js +1 -1
  170. package/dist/ui/navigation-menu.js.map +1 -1
  171. package/dist/ui/number-input.js +3 -3
  172. package/dist/ui/number-input.js.map +1 -1
  173. package/dist/ui/pagination.js +1 -1
  174. package/dist/ui/pagination.js.map +1 -1
  175. package/dist/ui/progress-ring.js +2 -2
  176. package/dist/ui/progress-ring.js.map +1 -1
  177. package/dist/ui/progress.d.ts.map +1 -1
  178. package/dist/ui/progress.js +16 -12
  179. package/dist/ui/progress.js.map +1 -1
  180. package/dist/ui/radio.js +1 -1
  181. package/dist/ui/radio.js.map +1 -1
  182. package/dist/ui/select.js +5 -5
  183. package/dist/ui/select.js.map +1 -1
  184. package/dist/ui/sheet.js +1 -1
  185. package/dist/ui/sheet.js.map +1 -1
  186. package/dist/ui/sidebar.js +12 -12
  187. package/dist/ui/sidebar.js.map +1 -1
  188. package/dist/ui/slider.js +1 -1
  189. package/dist/ui/slider.js.map +1 -1
  190. package/dist/ui/split-button.js +3 -3
  191. package/dist/ui/split-button.js.map +1 -1
  192. package/dist/ui/stat-card.js +1 -1
  193. package/dist/ui/stat-card.js.map +1 -1
  194. package/dist/ui/stepper.js +2 -2
  195. package/dist/ui/stepper.js.map +1 -1
  196. package/dist/ui/surface.d.ts.map +1 -1
  197. package/dist/ui/surface.js +2 -2
  198. package/dist/ui/surface.js.map +1 -1
  199. package/dist/ui/switch.js +1 -1
  200. package/dist/ui/switch.js.map +1 -1
  201. package/dist/ui/table.js +1 -1
  202. package/dist/ui/table.js.map +1 -1
  203. package/dist/ui/tabs.d.ts.map +1 -1
  204. package/dist/ui/tabs.js +1 -1
  205. package/dist/ui/tabs.js.map +1 -1
  206. package/dist/ui/textarea.js +5 -5
  207. package/dist/ui/textarea.js.map +1 -1
  208. package/dist/ui/toast.js +1 -1
  209. package/dist/ui/toast.js.map +1 -1
  210. package/dist/ui/toggle.js +3 -3
  211. package/dist/ui/toggle.js.map +1 -1
  212. package/docs/components/composed/avatar-group.md +4 -4
  213. package/docs/components/composed/diff.md +10 -2
  214. package/docs/components/composed/schedule-view.md +1 -1
  215. package/docs/components/shell/app-shell.md +132 -0
  216. package/docs/components/ui/avatar.md +1 -1
  217. package/docs/components/ui/button.md +1 -1
  218. package/docs/components/ui/chat.md +7 -1
  219. package/docs/components/ui/color-swatch.md +1 -1
  220. package/docs/components/ui/data-table-toolbar.md +6 -2
  221. package/docs/components/ui/data-table.md +41 -7
  222. package/docs/components/ui/dot.md +1 -1
  223. package/docs/components/ui/progress.md +1 -0
  224. package/docs/components/ui/surface.md +3 -3
  225. package/docs/components/ui/table.md +2 -2
  226. package/docs/recipes/install-astro.md +1 -0
  227. package/docs/recipes/install-next-app-router.md +1 -0
  228. package/docs/recipes/install-remix.md +1 -0
  229. package/docs/recipes/install-tanstack-start.md +1 -0
  230. package/docs/recipes/install-vite.md +1 -0
  231. package/llms.txt +2 -1
  232. package/make-kit/Guidelines.md +2 -2
  233. package/make-kit/components/button.md +1 -1
  234. package/make-kit/components/card.md +6 -6
  235. package/make-kit/components/surface.md +4 -4
  236. package/make-kit/foundations/color.md +4 -4
  237. package/make-kit/foundations/dark-mode.md +1 -1
  238. package/make-kit/foundations/radius.md +1 -1
  239. package/make-kit/foundations/surfaces.md +9 -9
  240. package/mcp-manifest.json +350 -32
  241. package/package.json +6 -3
  242. package/skill/SKILL.md +12 -6
  243. package/skill/references/components.md +2 -1
  244. package/skill/references/setup-astro.md +1 -0
  245. package/skill/references/setup-next-app-router.md +1 -0
  246. package/skill/references/setup-remix.md +1 -0
  247. package/skill/references/setup-tanstack-start.md +1 -0
  248. package/skill/references/setup-vite.md +1 -0
  249. package/dist/primitives/react-toast.d.ts +0 -120
  250. package/dist/ui/charts/_internal/scales.d.ts +0 -5
  251. package/dist/ui/charts/_internal/scales.d.ts.map +0 -1
  252. package/dist/ui/lib/slot.d.ts +0 -8
  253. package/dist/ui/lib/slot.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"multi-select-popover.js","names":[],"sources":["../../src/composed/multi-select-popover.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck, IconSearch } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MultiSelectItem {\n id: string\n label: string\n image?: string\n description?: string\n disabled?: boolean\n}\n\nexport interface MultiSelectGroup {\n label: string\n items: MultiSelectItem[]\n}\n\nexport interface MultiSelectPopoverProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /** Flat list of items (use `groups` for grouped rendering) */\n items?: MultiSelectItem[]\n /** Grouped items with section headers */\n groups?: MultiSelectGroup[]\n /** Currently selected item IDs */\n value: string[]\n /** Called when selection changes */\n onValueChange: (ids: string[]) => void\n /** @default 'Search...' */\n searchPlaceholder?: string\n /** Async search — replaces local filter with server results */\n onSearch?: (query: string) => Promise<MultiSelectItem[]>\n /** Debounce for async search in ms @default 300 */\n searchDebounce?: number\n /** Custom item renderer */\n renderItem?: (item: MultiSelectItem, selected: boolean) => React.ReactNode\n /** Message when no items match filter @default 'No results found' */\n emptyMessage?: string\n /** Maximum number of selections */\n maxSelections?: number\n /** Popover alignment @default 'start' */\n align?: 'start' | 'center' | 'end'\n /** Popover width */\n width?: string | number\n /** Trigger element */\n children: React.ReactNode\n}\n\n// ============================================================\n// MultiSelectPopover\n// ============================================================\n\nconst MultiSelectPopover = React.forwardRef<HTMLDivElement, MultiSelectPopoverProps>(\n (\n {\n items,\n groups,\n value,\n onValueChange,\n searchPlaceholder = 'Search...',\n onSearch,\n searchDebounce = 300,\n renderItem,\n emptyMessage = 'No results found',\n maxSelections,\n align = 'start',\n width,\n children,\n className,\n ...props\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [asyncItems, setAsyncItems] = React.useState<MultiSelectItem[] | null>(null)\n const [loading, setLoading] = React.useState(false)\n const [focusedIndex, setFocusedIndex] = React.useState(-1)\n const debounceRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // Reset search and focus when popover closes\n React.useEffect(() => {\n if (!open) {\n setSearch('')\n setAsyncItems(null)\n setFocusedIndex(-1)\n }\n }, [open])\n\n // Async search\n React.useEffect(() => {\n if (!onSearch || !open) return\n if (!search.trim()) {\n setAsyncItems(null)\n setLoading(false)\n return\n }\n setLoading(true)\n clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(() => {\n onSearch(search)\n .then((results) => setAsyncItems(results))\n .catch(() => setAsyncItems([]))\n .finally(() => setLoading(false))\n }, searchDebounce)\n return () => clearTimeout(debounceRef.current)\n }, [search, onSearch, searchDebounce, open])\n\n // Resolve visible items\n const allItems = React.useMemo(() => {\n if (asyncItems) return asyncItems\n if (groups) return groups.flatMap((g) => g.items)\n return items ?? []\n }, [items, groups, asyncItems])\n\n const filteredItems = React.useMemo(() => {\n if (onSearch) return allItems // async search handles its own filtering\n const q = search.toLowerCase()\n if (!q) return allItems\n return allItems.filter((item) => item.label.toLowerCase().includes(q))\n }, [allItems, search, onSearch])\n\n const filteredGroups = React.useMemo(() => {\n if (!groups || onSearch || asyncItems) return null\n const q = search.toLowerCase()\n return groups\n .map((g) => ({\n ...g,\n items: q ? g.items.filter((item) => item.label.toLowerCase().includes(q)) : g.items,\n }))\n .filter((g) => g.items.length > 0)\n }, [groups, search, onSearch, asyncItems])\n\n function toggle(id: string) {\n const isSelected = value.includes(id)\n if (isSelected) {\n onValueChange(value.filter((v) => v !== id))\n } else {\n if (maxSelections && value.length >= maxSelections) {\n // Replace: drop the first selected item to make room for the new one\n onValueChange([...value.slice(1), id])\n } else {\n onValueChange([...value, id])\n }\n }\n }\n\n // Reset focused index when search text changes\n \n React.useEffect(() => { setFocusedIndex(-1) }, [search])\n\n // Scroll focused item into view\n React.useEffect(() => {\n if (focusedIndex < 0) return\n const list = listRef.current\n if (!list) return\n const items = list.querySelectorAll('[data-multiselect-item]')\n items[focusedIndex]?.scrollIntoView({ block: 'nearest' })\n }, [focusedIndex])\n\n function handleSearchKeyDown(e: React.KeyboardEvent) {\n const count = filteredItems.length\n if (count === 0) return\n\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev < count - 1 ? prev + 1 : 0))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev <= 0 ? count - 1 : prev - 1))\n } else if (e.key === 'Enter' && focusedIndex >= 0) {\n e.preventDefault()\n const item = filteredItems[focusedIndex]\n if (item && !item.disabled) toggle(item.id)\n }\n }\n\n let itemCounter = 0\n\n function renderItemRow(item: MultiSelectItem) {\n const isSelected = value.includes(item.id)\n const index = itemCounter++\n const isFocused = index === focusedIndex\n return (\n <motion.button\n key={item.id}\n id={`msp-item-${index}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={item.disabled}\n data-multiselect-item=\"\"\n data-focused={isFocused ? '' : undefined}\n initial={{ opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ ...springs.snappy, delay: index * 0.02 }}\n onClick={() => toggle(item.id)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n isSelected && 'bg-accent-2 text-accent-11',\n isFocused && 'bg-surface-raised-hover',\n )}\n >\n {renderItem ? (\n <span className=\"flex-1 min-w-0\">{renderItem(item, isSelected)}</span>\n ) : (\n <>\n {item.image && (\n <img\n src={item.image}\n alt=\"\"\n className=\"h-ico-md w-ico-md rounded-pill object-cover shrink-0\"\n />\n )}\n <span className=\"flex-1 min-w-0\">\n <span className=\"block truncate text-body-md font-body text-surface-fg\">{item.label}</span>\n {item.description && (\n <span className=\"block truncate text-caption text-surface-fg-subtle\">{item.description}</span>\n )}\n </span>\n </>\n )}\n {isSelected && (\n <motion.span\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"text-accent-11\" />\n </motion.span>\n )}\n </motion.button>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen} modal={false}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <PopoverContent\n ref={ref}\n {...props}\n className={cn(\n 'border-surface-border-strong bg-surface-overlay p-0',\n className,\n )}\n style={{ width: width ?? 240 }}\n align={align}\n sideOffset={4}\n >\n {/* Search */}\n <div className=\"flex items-center gap-ds-03 border-b border-surface-border-strong px-ds-04 py-ds-03\">\n <Icon icon={IconSearch} size=\"sm\" stroke=\"light\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={handleSearchKeyDown}\n aria-label=\"Search\"\n aria-activedescendant={focusedIndex >= 0 ? `msp-item-${focusedIndex}` : undefined}\n className=\"w-full bg-transparent text-body-md font-body text-surface-fg placeholder:text-surface-fg-subtle outline-hidden\"\n />\n {loading && <Spinner size=\"sm\" />}\n </div>\n\n {/* Items */}\n <div ref={listRef} role=\"listbox\" aria-multiselectable=\"true\" className=\"max-h-[240px] overflow-y-auto py-ds-02\">\n {filteredGroups\n ? filteredGroups.map((group) => (\n <div key={group.label}>\n <div className=\"px-ds-04 py-ds-02 text-label-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {group.label}\n </div>\n {group.items.map(renderItemRow)}\n </div>\n ))\n : filteredItems.map(renderItemRow)}\n {!loading && filteredItems.length === 0 && (\n <p className=\"px-ds-04 py-ds-05 text-center text-body-sm font-body text-surface-fg-subtle\">\n {emptyMessage}\n </p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nMultiSelectPopover.displayName = 'MultiSelectPopover'\n\nexport { MultiSelectPopover }\n"],"mappings":"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"}
1
+ {"version":3,"file":"multi-select-popover.js","names":[],"sources":["../../src/composed/multi-select-popover.tsx"],"sourcesContent":["'use client'\n\nimport { IconCheck, IconSearch } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '../ui/popover'\nimport { Spinner } from '../ui/spinner'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface MultiSelectItem {\n id: string\n label: string\n image?: string\n description?: string\n disabled?: boolean\n}\n\nexport interface MultiSelectGroup {\n label: string\n items: MultiSelectItem[]\n}\n\nexport interface MultiSelectPopoverProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onSelect'> {\n /** Flat list of items (use `groups` for grouped rendering) */\n items?: MultiSelectItem[]\n /** Grouped items with section headers */\n groups?: MultiSelectGroup[]\n /** Currently selected item IDs */\n value: string[]\n /** Called when selection changes */\n onValueChange: (ids: string[]) => void\n /** @default 'Search...' */\n searchPlaceholder?: string\n /** Async search — replaces local filter with server results */\n onSearch?: (query: string) => Promise<MultiSelectItem[]>\n /** Debounce for async search in ms @default 300 */\n searchDebounce?: number\n /** Custom item renderer */\n renderItem?: (item: MultiSelectItem, selected: boolean) => React.ReactNode\n /** Message when no items match filter @default 'No results found' */\n emptyMessage?: string\n /** Maximum number of selections */\n maxSelections?: number\n /** Popover alignment @default 'start' */\n align?: 'start' | 'center' | 'end'\n /** Popover width */\n width?: string | number\n /** Trigger element */\n children: React.ReactNode\n}\n\n// ============================================================\n// MultiSelectPopover\n// ============================================================\n\nconst MultiSelectPopover = React.forwardRef<HTMLDivElement, MultiSelectPopoverProps>(\n (\n {\n items,\n groups,\n value,\n onValueChange,\n searchPlaceholder = 'Search...',\n onSearch,\n searchDebounce = 300,\n renderItem,\n emptyMessage = 'No results found',\n maxSelections,\n align = 'start',\n width,\n children,\n className,\n ...props\n },\n ref,\n ) => {\n const [open, setOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [asyncItems, setAsyncItems] = React.useState<MultiSelectItem[] | null>(null)\n const [loading, setLoading] = React.useState(false)\n const [focusedIndex, setFocusedIndex] = React.useState(-1)\n const debounceRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // Reset search and focus when popover closes\n React.useEffect(() => {\n if (!open) {\n setSearch('')\n setAsyncItems(null)\n setFocusedIndex(-1)\n }\n }, [open])\n\n // Async search\n React.useEffect(() => {\n if (!onSearch || !open) return\n if (!search.trim()) {\n setAsyncItems(null)\n setLoading(false)\n return\n }\n setLoading(true)\n clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(() => {\n onSearch(search)\n .then((results) => setAsyncItems(results))\n .catch(() => setAsyncItems([]))\n .finally(() => setLoading(false))\n }, searchDebounce)\n return () => clearTimeout(debounceRef.current)\n }, [search, onSearch, searchDebounce, open])\n\n // Resolve visible items\n const allItems = React.useMemo(() => {\n if (asyncItems) return asyncItems\n if (groups) return groups.flatMap((g) => g.items)\n return items ?? []\n }, [items, groups, asyncItems])\n\n const filteredItems = React.useMemo(() => {\n if (onSearch) return allItems // async search handles its own filtering\n const q = search.toLowerCase()\n if (!q) return allItems\n return allItems.filter((item) => item.label.toLowerCase().includes(q))\n }, [allItems, search, onSearch])\n\n const filteredGroups = React.useMemo(() => {\n if (!groups || onSearch || asyncItems) return null\n const q = search.toLowerCase()\n return groups\n .map((g) => ({\n ...g,\n items: q ? g.items.filter((item) => item.label.toLowerCase().includes(q)) : g.items,\n }))\n .filter((g) => g.items.length > 0)\n }, [groups, search, onSearch, asyncItems])\n\n function toggle(id: string) {\n const isSelected = value.includes(id)\n if (isSelected) {\n onValueChange(value.filter((v) => v !== id))\n } else {\n if (maxSelections && value.length >= maxSelections) {\n // Replace: drop the first selected item to make room for the new one\n onValueChange([...value.slice(1), id])\n } else {\n onValueChange([...value, id])\n }\n }\n }\n\n // Reset focused index when search text changes\n \n React.useEffect(() => { setFocusedIndex(-1) }, [search])\n\n // Scroll focused item into view\n React.useEffect(() => {\n if (focusedIndex < 0) return\n const list = listRef.current\n if (!list) return\n const items = list.querySelectorAll('[data-multiselect-item]')\n items[focusedIndex]?.scrollIntoView({ block: 'nearest' })\n }, [focusedIndex])\n\n function handleSearchKeyDown(e: React.KeyboardEvent) {\n const count = filteredItems.length\n if (count === 0) return\n\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev < count - 1 ? prev + 1 : 0))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setFocusedIndex((prev) => (prev <= 0 ? count - 1 : prev - 1))\n } else if (e.key === 'Enter' && focusedIndex >= 0) {\n e.preventDefault()\n const item = filteredItems[focusedIndex]\n if (item && !item.disabled) toggle(item.id)\n }\n }\n\n let itemCounter = 0\n\n function renderItemRow(item: MultiSelectItem) {\n const isSelected = value.includes(item.id)\n const index = itemCounter++\n const isFocused = index === focusedIndex\n return (\n <motion.button\n key={item.id}\n id={`msp-item-${index}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={item.disabled}\n data-multiselect-item=\"\"\n data-focused={isFocused ? '' : undefined}\n initial={{ opacity: 0, x: -8 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ ...springs.snappy, delay: index * 0.02 }}\n onClick={() => toggle(item.id)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-panel-hover',\n 'disabled:opacity-action-disabled disabled:cursor-not-allowed',\n isSelected && 'bg-accent-2 text-accent-11',\n isFocused && 'bg-surface-panel-hover',\n )}\n >\n {renderItem ? (\n <span className=\"flex-1 min-w-0\">{renderItem(item, isSelected)}</span>\n ) : (\n <>\n {item.image && (\n <img\n src={item.image}\n alt=\"\"\n className=\"h-ico-md w-ico-md rounded-pill object-cover shrink-0\"\n />\n )}\n <span className=\"flex-1 min-w-0\">\n <span className=\"block truncate text-body-md font-body text-surface-fg\">{item.label}</span>\n {item.description && (\n <span className=\"block truncate text-caption text-surface-fg-subtle\">{item.description}</span>\n )}\n </span>\n </>\n )}\n {isSelected && (\n <motion.span\n initial={{ scale: 0 }}\n animate={{ scale: 1 }}\n transition={springs.snappy}\n className=\"inline-flex shrink-0\"\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"text-accent-11\" />\n </motion.span>\n )}\n </motion.button>\n )\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen} modal={false}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <PopoverContent\n ref={ref}\n {...props}\n className={cn(\n 'border-surface-border-strong bg-surface-overlay p-0',\n className,\n )}\n style={{ width: width ?? 240 }}\n align={align}\n sideOffset={4}\n >\n {/* Search */}\n <div className=\"flex items-center gap-ds-03 border-b border-surface-border-strong px-ds-04 py-ds-03\">\n <Icon icon={IconSearch} size=\"sm\" stroke=\"light\" className=\"shrink-0 text-surface-fg-subtle\" />\n <input\n type=\"text\"\n placeholder={searchPlaceholder}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onKeyDown={handleSearchKeyDown}\n aria-label=\"Search\"\n aria-activedescendant={focusedIndex >= 0 ? `msp-item-${focusedIndex}` : undefined}\n className=\"w-full bg-transparent text-body-md font-body text-surface-fg placeholder:text-surface-fg-subtle outline-hidden\"\n />\n {loading && <Spinner size=\"sm\" />}\n </div>\n\n {/* Items */}\n <div ref={listRef} role=\"listbox\" aria-multiselectable=\"true\" className=\"max-h-[240px] overflow-y-auto py-ds-02\">\n {filteredGroups\n ? filteredGroups.map((group) => (\n <div key={group.label}>\n <div className=\"px-ds-04 py-ds-02 text-label-xs font-semibold uppercase tracking-wider text-surface-fg-subtle\">\n {group.label}\n </div>\n {group.items.map(renderItemRow)}\n </div>\n ))\n : filteredItems.map(renderItemRow)}\n {!loading && filteredItems.length === 0 && (\n <p className=\"px-ds-04 py-ds-05 text-center text-body-sm font-body text-surface-fg-subtle\">\n {emptyMessage}\n </p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nMultiSelectPopover.displayName = 'MultiSelectPopover'\n\nexport { MultiSelectPopover }\n"],"mappings":"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"}
@@ -27,7 +27,7 @@ var s = n.forwardRef(function({ className: n, label: s, ...c }, l) {
27
27
  /* @__PURE__ */ r("div", {
28
28
  className: "grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-4",
29
29
  children: Array.from({ length: 4 }).map((e, n) => /* @__PURE__ */ i("div", {
30
- className: "flex flex-col gap-ds-04 rounded-surface border border-card bg-surface-raised p-ds-05b",
30
+ className: "flex flex-col gap-ds-04 rounded-surface border border-card bg-surface-panel p-ds-05b",
31
31
  children: [
32
32
  /* @__PURE__ */ i("div", {
33
33
  className: "flex items-center justify-between",
@@ -39,7 +39,7 @@ var s = n.forwardRef(function({ className: n, label: s, ...c }, l) {
39
39
  }, `stat-${n}`))
40
40
  }),
41
41
  /* @__PURE__ */ i("div", {
42
- className: "rounded-surface border border-card bg-surface-raised p-ds-06",
42
+ className: "rounded-surface border border-card bg-surface-panel p-ds-06",
43
43
  children: [
44
44
  /* @__PURE__ */ i("div", {
45
45
  className: "mb-ds-06 flex items-center justify-between",
@@ -91,7 +91,7 @@ var c = n.forwardRef(function({ className: n, label: s, ...c }, l) {
91
91
  /* @__PURE__ */ r("div", {
92
92
  className: "grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-3",
93
93
  children: Array.from({ length: 6 }).map((e, n) => /* @__PURE__ */ i("div", {
94
- className: "flex flex-col gap-ds-05 rounded-surface border border-card bg-surface-raised p-ds-05b",
94
+ className: "flex flex-col gap-ds-05 rounded-surface border border-card bg-surface-panel p-ds-05b",
95
95
  children: [
96
96
  /* @__PURE__ */ i("div", {
97
97
  className: "flex items-center justify-between",
@@ -124,7 +124,7 @@ var l = n.forwardRef(function({ className: n, label: s, ...c }, l) {
124
124
  ref: l,
125
125
  ...a(s),
126
126
  ...c,
127
- className: e("flex h-full flex-col gap-0 rounded-surface border border-card bg-surface-raised", n),
127
+ className: e("flex h-full flex-col gap-0 rounded-surface border border-card bg-surface-panel", n),
128
128
  children: [
129
129
  /* @__PURE__ */ r(o, { label: s }),
130
130
  /* @__PURE__ */ i("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"page-skeletons.js","names":[],"sources":["../../src/composed/page-skeletons.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\n// role=status + aria-busy so AT announces the loading region (child Skeletons are\n// aria-hidden). The sr-only label carries the announcement text.\nfunction statusProps(label?: string) {\n return { role: 'status' as const, 'aria-busy': true, 'aria-label': label ?? 'Loading' }\n}\nfunction SrLabel({ label }: { label?: string }) {\n return <span className=\"sr-only\">{label ?? 'Loading'}…</span>\n}\n\n// --- Dashboard Skeleton ---\n\nexport interface DashboardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst DashboardSkeleton = React.forwardRef<HTMLDivElement, DashboardSkeletonProps>(\n function DashboardSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n <SrLabel label={label} />\n {/* Stat cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={`stat-${i}`}\n className=\"flex flex-col gap-ds-04 rounded-surface border border-card bg-surface-raised p-ds-05b\"\n >\n <div className=\"flex items-center justify-between\">\n <Skeleton className={'h-ds-04 w-ds-11'} />\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n </div>\n <Skeleton className={'h-ds-xs-plus w-ds-10'} />\n <Skeleton className={'h-ds-03 w-[112px]'} />\n </div>\n ))}\n </div>\n\n {/* Attendance calendar placeholder */}\n <div className=\"rounded-surface border border-card bg-surface-raised p-ds-06\">\n <div className=\"mb-ds-06 flex items-center justify-between\">\n <Skeleton className={'h-ds-05b w-[128px]'} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n <Skeleton className={'h-ds-05 w-[112px]'} />\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n </div>\n </div>\n {/* Weekday headers */}\n <div className=\"mb-ds-03 grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 7 }).map((_, i) => (\n <Skeleton key={`wh-${i}`} className={'mx-auto h-ds-04 w-ds-07'} />\n ))}\n </div>\n {/* Calendar grid */}\n <div className=\"grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 35 }).map((_, i) => (\n <Skeleton\n key={`cal-${i}`}\n className={'mx-auto h-ds-md w-ds-md rounded-pill'}\n />\n ))}\n </div>\n </div>\n </div>\n )\n},\n)\n\nDashboardSkeleton.displayName = 'DashboardSkeleton'\n\n// --- Project List Skeleton ---\n\nexport interface ProjectListSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst ProjectListSkeleton = React.forwardRef<HTMLDivElement, ProjectListSkeletonProps>(\n function ProjectListSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n <SrLabel label={label} />\n {/* Header */}\n <div className=\"flex flex-col gap-ds-05 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={'h-ds-06 w-[112px]'} />\n <Skeleton className={'h-ds-04 w-ds-13'} />\n </div>\n <Skeleton className={'h-ds-sm-plus w-[128px] rounded-surface'} />\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-ds-04 sm:flex-row sm:items-center\">\n <Skeleton className={'h-ds-sm-plus flex-1 rounded-surface'} />\n <div className=\"flex gap-ds-02b\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={`f-${i}`} className={'h-ds-sm w-ds-10 rounded-surface'} />\n ))}\n </div>\n </div>\n\n {/* Project cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={`proj-${i}`}\n className=\"flex flex-col gap-ds-05 rounded-surface border border-card bg-surface-raised p-ds-05b\"\n >\n {/* Top row: status + org */}\n <div className=\"flex items-center justify-between\">\n <Skeleton className={'h-ds-05b w-ds-10 rounded-pill'} />\n <Skeleton className={'h-ds-05 w-ds-11 rounded-control'} />\n </div>\n {/* Title + description */}\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={'h-ds-05 w-3/4'} />\n <Skeleton className={'h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-2/3'} />\n </div>\n {/* Bottom row: members + count */}\n <div className=\"flex items-center justify-between pt-ds-02\">\n <div className=\"flex -space-x-ds-03\">\n {Array.from({ length: 3 }).map((_, j) => (\n <Skeleton\n key={`av-${i}-${j}`}\n className={'h-ds-xs-plus w-ds-xs-plus rounded-pill'}\n />\n ))}\n </div>\n <Skeleton className={'h-ds-04 w-ds-md'} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n},\n)\n\nProjectListSkeleton.displayName = 'ProjectListSkeleton'\n\n// --- Task Detail Skeleton ---\n\nexport interface TaskDetailSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst TaskDetailSkeleton = React.forwardRef<HTMLDivElement, TaskDetailSkeletonProps>(\n function TaskDetailSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex h-full flex-col gap-0 rounded-surface border border-card bg-surface-raised\", className)}>\n <SrLabel label={label} />\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-surface-border-strong px-ds-05b py-ds-05\">\n <Skeleton className={'h-ds-05b w-[192px]'} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={'h-ds-xs-plus w-ds-xs-plus rounded-surface'} />\n <Skeleton className={'h-ds-xs-plus w-ds-xs-plus rounded-surface'} />\n </div>\n </div>\n\n {/* Property rows */}\n <div className=\"flex flex-col gap-0 border-b border-surface-border-strong px-ds-05b py-ds-05\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={`prop-${i}`}\n className=\"flex items-center gap-ds-05 py-ds-03\"\n >\n <Skeleton className={'h-ds-04 w-ds-12 shrink-0'} />\n <Skeleton className={'h-ds-06 w-[128px] rounded-control'} />\n </div>\n ))}\n </div>\n\n {/* Tabs */}\n <div className=\"border-b border-surface-border-strong px-ds-05b\">\n <div className=\"flex gap-ds-05 py-ds-04\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={`tab-${i}`} className={'h-ds-05 w-ds-10'} />\n ))}\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex flex-1 flex-col gap-ds-04 px-ds-05b py-ds-05\">\n <Skeleton className={'h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-4/5'} />\n <Skeleton className={'h-ds-04 w-3/5'} />\n <Skeleton className={'mt-ds-03 h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-2/3'} />\n </div>\n </div>\n )\n},\n)\n\nTaskDetailSkeleton.displayName = 'TaskDetailSkeleton'\n\nexport {\n DashboardSkeleton,\n ProjectListSkeleton,\n TaskDetailSkeleton,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"page-skeletons.js","names":[],"sources":["../../src/composed/page-skeletons.tsx"],"sourcesContent":["// @server-safe\nimport * as React from 'react'\n\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\n\n// role=status + aria-busy so AT announces the loading region (child Skeletons are\n// aria-hidden). The sr-only label carries the announcement text.\nfunction statusProps(label?: string) {\n return { role: 'status' as const, 'aria-busy': true, 'aria-label': label ?? 'Loading' }\n}\nfunction SrLabel({ label }: { label?: string }) {\n return <span className=\"sr-only\">{label ?? 'Loading'}…</span>\n}\n\n// --- Dashboard Skeleton ---\n\nexport interface DashboardSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst DashboardSkeleton = React.forwardRef<HTMLDivElement, DashboardSkeletonProps>(\n function DashboardSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n <SrLabel label={label} />\n {/* Stat cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={`stat-${i}`}\n className=\"flex flex-col gap-ds-04 rounded-surface border border-card bg-surface-panel p-ds-05b\"\n >\n <div className=\"flex items-center justify-between\">\n <Skeleton className={'h-ds-04 w-ds-11'} />\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n </div>\n <Skeleton className={'h-ds-xs-plus w-ds-10'} />\n <Skeleton className={'h-ds-03 w-[112px]'} />\n </div>\n ))}\n </div>\n\n {/* Attendance calendar placeholder */}\n <div className=\"rounded-surface border border-card bg-surface-panel p-ds-06\">\n <div className=\"mb-ds-06 flex items-center justify-between\">\n <Skeleton className={'h-ds-05b w-[128px]'} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n <Skeleton className={'h-ds-05 w-[112px]'} />\n <Skeleton className={'h-ds-sm w-ds-sm rounded-surface'} />\n </div>\n </div>\n {/* Weekday headers */}\n <div className=\"mb-ds-03 grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 7 }).map((_, i) => (\n <Skeleton key={`wh-${i}`} className={'mx-auto h-ds-04 w-ds-07'} />\n ))}\n </div>\n {/* Calendar grid */}\n <div className=\"grid grid-cols-7 gap-ds-03\">\n {Array.from({ length: 35 }).map((_, i) => (\n <Skeleton\n key={`cal-${i}`}\n className={'mx-auto h-ds-md w-ds-md rounded-pill'}\n />\n ))}\n </div>\n </div>\n </div>\n )\n},\n)\n\nDashboardSkeleton.displayName = 'DashboardSkeleton'\n\n// --- Project List Skeleton ---\n\nexport interface ProjectListSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst ProjectListSkeleton = React.forwardRef<HTMLDivElement, ProjectListSkeletonProps>(\n function ProjectListSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex flex-col gap-ds-06\", className)}>\n <SrLabel label={label} />\n {/* Header */}\n <div className=\"flex flex-col gap-ds-05 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={'h-ds-06 w-[112px]'} />\n <Skeleton className={'h-ds-04 w-ds-13'} />\n </div>\n <Skeleton className={'h-ds-sm-plus w-[128px] rounded-surface'} />\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-ds-04 sm:flex-row sm:items-center\">\n <Skeleton className={'h-ds-sm-plus flex-1 rounded-surface'} />\n <div className=\"flex gap-ds-02b\">\n {Array.from({ length: 4 }).map((_, i) => (\n <Skeleton key={`f-${i}`} className={'h-ds-sm w-ds-10 rounded-surface'} />\n ))}\n </div>\n </div>\n\n {/* Project cards grid */}\n <div className=\"grid grid-cols-1 gap-ds-05 sm:grid-cols-2 lg:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div\n key={`proj-${i}`}\n className=\"flex flex-col gap-ds-05 rounded-surface border border-card bg-surface-panel p-ds-05b\"\n >\n {/* Top row: status + org */}\n <div className=\"flex items-center justify-between\">\n <Skeleton className={'h-ds-05b w-ds-10 rounded-pill'} />\n <Skeleton className={'h-ds-05 w-ds-11 rounded-control'} />\n </div>\n {/* Title + description */}\n <div className=\"flex flex-col gap-ds-03\">\n <Skeleton className={'h-ds-05 w-3/4'} />\n <Skeleton className={'h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-2/3'} />\n </div>\n {/* Bottom row: members + count */}\n <div className=\"flex items-center justify-between pt-ds-02\">\n <div className=\"flex -space-x-ds-03\">\n {Array.from({ length: 3 }).map((_, j) => (\n <Skeleton\n key={`av-${i}-${j}`}\n className={'h-ds-xs-plus w-ds-xs-plus rounded-pill'}\n />\n ))}\n </div>\n <Skeleton className={'h-ds-04 w-ds-md'} />\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n},\n)\n\nProjectListSkeleton.displayName = 'ProjectListSkeleton'\n\n// --- Task Detail Skeleton ---\n\nexport interface TaskDetailSkeletonProps extends React.ComponentPropsWithoutRef<'div'> {\n /** Accessible label for the loading region. @default 'Loading' */\n label?: string\n}\n\nconst TaskDetailSkeleton = React.forwardRef<HTMLDivElement, TaskDetailSkeletonProps>(\n function TaskDetailSkeleton({ className, label, ...props }, ref) {\n return (\n <div ref={ref} {...statusProps(label)} {...props} className={cn(\"flex h-full flex-col gap-0 rounded-surface border border-card bg-surface-panel\", className)}>\n <SrLabel label={label} />\n {/* Header */}\n <div className=\"flex items-center justify-between border-b border-surface-border-strong px-ds-05b py-ds-05\">\n <Skeleton className={'h-ds-05b w-[192px]'} />\n <div className=\"flex items-center gap-ds-03\">\n <Skeleton className={'h-ds-xs-plus w-ds-xs-plus rounded-surface'} />\n <Skeleton className={'h-ds-xs-plus w-ds-xs-plus rounded-surface'} />\n </div>\n </div>\n\n {/* Property rows */}\n <div className=\"flex flex-col gap-0 border-b border-surface-border-strong px-ds-05b py-ds-05\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div\n key={`prop-${i}`}\n className=\"flex items-center gap-ds-05 py-ds-03\"\n >\n <Skeleton className={'h-ds-04 w-ds-12 shrink-0'} />\n <Skeleton className={'h-ds-06 w-[128px] rounded-control'} />\n </div>\n ))}\n </div>\n\n {/* Tabs */}\n <div className=\"border-b border-surface-border-strong px-ds-05b\">\n <div className=\"flex gap-ds-05 py-ds-04\">\n {Array.from({ length: 3 }).map((_, i) => (\n <Skeleton key={`tab-${i}`} className={'h-ds-05 w-ds-10'} />\n ))}\n </div>\n </div>\n\n {/* Tab content */}\n <div className=\"flex flex-1 flex-col gap-ds-04 px-ds-05b py-ds-05\">\n <Skeleton className={'h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-4/5'} />\n <Skeleton className={'h-ds-04 w-3/5'} />\n <Skeleton className={'mt-ds-03 h-ds-04 w-full'} />\n <Skeleton className={'h-ds-04 w-2/3'} />\n </div>\n </div>\n )\n},\n)\n\nTaskDetailSkeleton.displayName = 'TaskDetailSkeleton'\n\nexport {\n DashboardSkeleton,\n ProjectListSkeleton,\n TaskDetailSkeleton,\n}\n"],"mappings":"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"}
@@ -105,7 +105,7 @@ var k = 120, A = 500, j = p.forwardRef(({ className: o, children: s, snapPoints:
105
105
  w
106
106
  ]), G = n("fixed inset-x-0 bottom-0 z-modal flex flex-col overflow-hidden rounded-t-overlay-lg border-t border-surface-border-strong bg-surface-overlay shadow-overlay", M ? void 0 : "max-h-[90dvh]"), K = C ? /* @__PURE__ */ h(i, {
107
107
  title: "Close",
108
- className: "absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none",
108
+ className: "absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-panel-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none",
109
109
  children: [/* @__PURE__ */ m(r, {
110
110
  icon: g,
111
111
  size: "lg"
@@ -1 +1 @@
1
- {"version":3,"file":"responsive-modal.js","names":[],"sources":["../../src/composed/responsive-modal.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@primitives/react-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useDragControls, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useIsMobile } from '../hooks/use-mobile'\nimport { Icon } from '../ui/icon'\nimport { springs, tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n/**\n * ResponsiveModal — one overlay that is a centered **Dialog** on desktop (md+)\n * and a partial, content-height **bottom sheet** on mobile (drag-to-dismiss,\n * optional snap points). Built on the same accessible dialog primitive as\n * `Dialog` and `Sheet` (focus trap, Escape, scroll lock), so it is an\n * alternative-to both — not a re-roll.\n *\n * Why it exists: `DialogContent responsive` collapses to a full-screen takeover\n * on mobile (`inset-0`), which leaves dead space under short content and drops\n * the drawer affordances users expect. This composes the two correct halves and\n * owns the fiddly bits (pinned header, internal scroll body with sane height\n * caps, close-button stacking above an optional full-bleed background) so\n * consumers stop hand-rolling a `useIsMobile()` switch.\n *\n * **Parts (in composition order):**\n * - `ResponsiveModal` — open/close state (this root)\n * - `ResponsiveModalTrigger` — opens it (use `asChild`)\n * - `ResponsiveModalContent` — the switching panel (Dialog md+ / bottom sheet below)\n * - `ResponsiveModalBackground` — optional full-bleed layer painted behind content (`-z-10`)\n * - `ResponsiveModalHeader` — pinned; stays put while the body scrolls\n * - `ResponsiveModalTitle` — required for accessibility (labels the dialog)\n * - `ResponsiveModalDescription` — optional subtitle\n * - `ResponsiveModalBody` — internal scroll region (capped 85dvh desktop / 90dvh mobile)\n * - `ResponsiveModalFooter` — pinned action row\n * - `ResponsiveModalClose` — manual close (a close button is built into Content)\n *\n * @compound\n * @example\n * <ResponsiveModal>\n * <ResponsiveModalTrigger asChild><Button>Open</Button></ResponsiveModalTrigger>\n * <ResponsiveModalContent>\n * <ResponsiveModalHeader>\n * <ResponsiveModalTitle>Filters</ResponsiveModalTitle>\n * <ResponsiveModalDescription>Narrow the result set.</ResponsiveModalDescription>\n * </ResponsiveModalHeader>\n * <ResponsiveModalBody>…form fields…</ResponsiveModalBody>\n * <ResponsiveModalFooter>\n * <ResponsiveModalClose asChild><Button variant=\"soft\">Cancel</Button></ResponsiveModalClose>\n * <Button>Apply</Button>\n * </ResponsiveModalFooter>\n * </ResponsiveModalContent>\n * </ResponsiveModal>\n */\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\ntype ResponsiveModalContextValue = {\n open: boolean\n isMobile: boolean\n dismissable: boolean\n onClose: () => void\n}\nconst ResponsiveModalContext = React.createContext<ResponsiveModalContextValue>({\n open: false,\n isMobile: false,\n dismissable: true,\n onClose: () => {},\n})\nconst useResponsiveModal = () => React.useContext(ResponsiveModalContext)\n\n// ── Root ──────────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** When false, Escape/overlay/drag cannot dismiss — only an explicit Close/onOpenChange can. @default true */\n dismissable?: boolean\n}\n\nconst ResponsiveModal: React.FC<ResponsiveModalProps> = ({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n dismissable = true,\n children,\n ...props\n}) => {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n const isMobile = useIsMobile()\n\n const handleOpenChange = React.useCallback(\n (value: boolean) => {\n // A non-dismissable modal ignores the primitive's implicit close attempts\n // (Escape, outside-click); only value=true or an explicit close gets through.\n if (!value && !dismissable) return\n if (!isControlled) setUncontrolledOpen(value)\n onOpenChange?.(value)\n },\n [isControlled, onOpenChange, dismissable],\n )\n\n const contextValue = React.useMemo<ResponsiveModalContextValue>(\n () => ({ open, isMobile, dismissable, onClose: () => handleOpenChange(false) }),\n [open, isMobile, dismissable, handleOpenChange],\n )\n\n return (\n <ResponsiveModalContext.Provider value={contextValue}>\n <DialogPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props}>\n {children}\n </DialogPrimitive.Root>\n </ResponsiveModalContext.Provider>\n )\n}\nResponsiveModal.displayName = 'ResponsiveModal'\n\nconst ResponsiveModalTrigger = DialogPrimitive.Trigger\nconst ResponsiveModalClose = DialogPrimitive.Close\nconst ResponsiveModalPortal = DialogPrimitive.Portal\n\nconst ResponsiveModalOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n forceMount\n className={cn('fixed inset-0 z-overlay bg-overlay', className)}\n {...props}\n />\n))\nResponsiveModalOverlay.displayName = 'ResponsiveModalOverlay'\n\n// ── Snap points ────────────────────────────────────────────────────────────────\n// A snap point is a fraction (0,1] of the viewport height the sheet rests at.\n// Omitted → the sheet is content-height (capped at 90dvh) with a single rest at\n// its natural height. Provided → the sheet is tall enough for the largest snap\n// and rests at each fraction; dragging below the smallest past a threshold\n// dismisses (when dismissable).\n\nfunction nearestSnapIndex(currentVisibleFraction: number, snapPoints: number[]) {\n let best = 0\n let bestDist = Infinity\n for (let i = 0; i < snapPoints.length; i++) {\n const d = Math.abs(snapPoints[i] - currentVisibleFraction)\n if (d < bestDist) {\n bestDist = d\n best = i\n }\n }\n return best\n}\n\n// ── Content ──────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Mobile-only. Rest heights as ascending fractions of viewport height, e.g.\n * `[0.5, 0.9]`. Omit for a content-height sheet. The largest fraction caps the\n * sheet height. Ignored on desktop.\n */\n snapPoints?: number[]\n /** Mobile-only. Index into `snapPoints` the sheet opens at. @default last (tallest) */\n defaultSnapPoint?: number\n}\n\nconst DRAG_DISMISS_THRESHOLD_PX = 120\nconst DRAG_DISMISS_VELOCITY = 500\n\nconst ResponsiveModalContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n ResponsiveModalContentProps\n>(({ className, children, snapPoints, defaultSnapPoint, ...props }, ref) => {\n const { open, isMobile, dismissable, onClose } = useResponsiveModal()\n const reduced = useReducedMotion()\n const dragControls = useDragControls()\n\n const hasSnaps = isMobile && Array.isArray(snapPoints) && snapPoints.length > 0\n const sortedSnaps = React.useMemo(\n () => (hasSnaps ? [...snapPoints!].sort((a, b) => a - b) : []),\n [hasSnaps, snapPoints],\n )\n const maxSnap = sortedSnaps.length ? sortedSnaps[sortedSnaps.length - 1] : 1\n const initialSnap = React.useMemo(() => {\n if (!sortedSnaps.length) return 0\n const d = defaultSnapPoint ?? sortedSnaps.length - 1\n return Math.min(Math.max(d, 0), sortedSnaps.length - 1)\n }, [sortedSnaps, defaultSnapPoint])\n\n const [activeSnap, setActiveSnap] = React.useState(initialSnap)\n const [sheetHeight, setSheetHeight] = React.useState(0)\n const internalRef = React.useRef<HTMLDivElement>(null)\n\n // Reset to the opening snap each time it re-opens.\n React.useEffect(() => {\n if (open) setActiveSnap(initialSnap)\n }, [open, initialSnap])\n\n // Measure the laid-out sheet height so snap rest positions can be expressed in px.\n React.useEffect(() => {\n if (!isMobile) return\n const el = internalRef.current\n if (!el) return\n const measure = () => setSheetHeight(el.getBoundingClientRect().height)\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [isMobile, open])\n\n const composedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Rest translateY for a snap index: at the tallest snap y=0; smaller snaps push\n // the sheet down so only that fraction of viewport height shows.\n const restY = React.useCallback(\n (index: number) => {\n if (!sortedSnaps.length || !sheetHeight) return 0\n const visible = sortedSnaps[index]\n return ((maxSnap - visible) / maxSnap) * sheetHeight\n },\n [sortedSnaps, sheetHeight, maxSnap],\n )\n\n const canDrag = isMobile && dismissable && !reduced\n\n const handleDragEnd = React.useCallback(\n (\n _e: unknown,\n info: { offset: { y: number }; velocity: { y: number } },\n ) => {\n if (!sortedSnaps.length) {\n // Content-height sheet: single rest; drag down past threshold dismisses.\n if (info.offset.y > DRAG_DISMISS_THRESHOLD_PX || info.velocity.y > DRAG_DISMISS_VELOCITY) {\n onClose()\n }\n return\n }\n // Snapped sheet: project the release point onto the visible-fraction axis.\n const currentY = restY(activeSnap) + info.offset.y\n const draggedVisible = maxSnap - (currentY / sheetHeight) * maxSnap\n const belowLowest = sortedSnaps[0] - draggedVisible\n if (\n activeSnap === 0 &&\n (belowLowest > 0.15 || info.velocity.y > DRAG_DISMISS_VELOCITY)\n ) {\n onClose()\n return\n }\n setActiveSnap(nearestSnapIndex(draggedVisible, sortedSnaps))\n },\n [sortedSnaps, restY, activeSnap, maxSnap, sheetHeight, onClose],\n )\n\n // ── Desktop: centered Dialog ──\n const desktopClasses =\n 'fixed left-[50%] top-[50%] z-modal flex max-h-[85dvh] w-full max-w-lg flex-col overflow-hidden rounded-overlay-lg bg-surface-overlay shadow-overlay'\n\n // ── Mobile: bottom sheet ──\n const mobileClasses = cn(\n 'fixed inset-x-0 bottom-0 z-modal flex flex-col overflow-hidden rounded-t-overlay-lg border-t border-surface-border-strong bg-surface-overlay shadow-overlay',\n // Snap sheets are as tall as the largest snap; content sheets hug content up to 90dvh.\n hasSnaps ? undefined : 'max-h-[90dvh]',\n )\n\n const closeButton = dismissable ? (\n <DialogPrimitive.Close\n title=\"Close\"\n className=\"absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none\"\n >\n <Icon icon={IconX} size=\"lg\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n ) : null\n\n return (\n <AnimatePresence>\n {open && (\n <ResponsiveModalPortal forceMount>\n <ResponsiveModalOverlay asChild>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n />\n </ResponsiveModalOverlay>\n <DialogPrimitive.Content\n ref={composedRef}\n forceMount\n asChild\n onPointerDownOutside={dismissable ? undefined : (e) => e.preventDefault()}\n {...props}\n >\n <motion.div\n className={cn(isMobile ? mobileClasses : desktopClasses, className)}\n style={\n hasSnaps\n ? { height: `${maxSnap * 100}dvh` }\n : undefined\n }\n initial={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n animate={\n isMobile\n ? { y: hasSnaps ? restY(activeSnap) : 0 }\n : { opacity: 1, scale: 1, x: '-50%', y: '-50%' }\n }\n exit={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n transition={isMobile ? springs.smooth : { ...springs.smooth, opacity: tweens.fade }}\n drag={canDrag ? 'y' : false}\n dragListener={false}\n dragControls={dragControls}\n dragConstraints={{ top: 0 }}\n dragElastic={0.15}\n onDragEnd={canDrag ? handleDragEnd : undefined}\n >\n {/* Drag handle — the only surface that initiates a drag, so the\n scroll body below never fights the gesture (useDragControls). */}\n {isMobile && (\n <div\n className={cn('flex shrink-0 justify-center pb-ds-01 pt-ds-03', canDrag && 'cursor-grab touch-none')}\n onPointerDown={canDrag ? (e) => dragControls.start(e) : undefined}\n >\n <div className=\"h-1 w-8 rounded-pill bg-surface-border\" />\n </div>\n )}\n {closeButton}\n {children}\n </motion.div>\n </DialogPrimitive.Content>\n </ResponsiveModalPortal>\n )}\n </AnimatePresence>\n )\n})\nResponsiveModalContent.displayName = 'ResponsiveModalContent'\n\n// ── Background slot ────────────────────────────────────────────────────────────\n\n/**\n * Full-bleed layer painted behind the modal content (`-z-10`), clipped to the\n * panel's rounded corners. Render whatever you want behind the content (a\n * gradient, an aurora, an image). It is non-interactive by default.\n */\nconst ResponsiveModalBackground = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n aria-hidden\n className={cn('pointer-events-none absolute inset-0 -z-10 overflow-hidden rounded-[inherit]', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBackground.displayName = 'ResponsiveModalBackground'\n\n// ── Layout parts ───────────────────────────────────────────────────────────────\n\n/** Pinned header — sits above the scroll body and does not scroll with it. */\nconst ResponsiveModalHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col gap-ds-02b px-ds-06 pb-ds-04 pt-ds-05 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalHeader.displayName = 'ResponsiveModalHeader'\n\n/** Internal scroll region — grows to fill, scrolls when content exceeds the height cap. */\nconst ResponsiveModalBody = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('min-h-0 flex-1 overflow-y-auto px-ds-06 py-ds-02', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBody.displayName = 'ResponsiveModalBody'\n\n/** Pinned footer action row. Stacks on mobile, right-aligned inline on desktop. */\nconst ResponsiveModalFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col-reverse gap-ds-03 px-ds-06 pb-ds-06 pt-ds-04 sm:flex-row sm:justify-end',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalFooter.displayName = 'ResponsiveModalFooter'\n\nconst ResponsiveModalTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-heading-xs font-semibold text-surface-fg', className)}\n {...props}\n />\n))\nResponsiveModalTitle.displayName = 'ResponsiveModalTitle'\n\nconst ResponsiveModalDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-body-md text-surface-fg-muted', className)}\n {...props}\n />\n))\nResponsiveModalDescription.displayName = 'ResponsiveModalDescription'\n\nexport {\n ResponsiveModal,\n ResponsiveModalBackground,\n ResponsiveModalBody,\n ResponsiveModalClose,\n ResponsiveModalContent,\n ResponsiveModalDescription,\n ResponsiveModalFooter,\n ResponsiveModalHeader,\n ResponsiveModalOverlay,\n ResponsiveModalPortal,\n ResponsiveModalTitle,\n ResponsiveModalTrigger,\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"responsive-modal.js","names":[],"sources":["../../src/composed/responsive-modal.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@primitives/react-dialog'\nimport { IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useDragControls, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { useIsMobile } from '../hooks/use-mobile'\nimport { Icon } from '../ui/icon'\nimport { springs, tweens } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\n\n/**\n * ResponsiveModal — one overlay that is a centered **Dialog** on desktop (md+)\n * and a partial, content-height **bottom sheet** on mobile (drag-to-dismiss,\n * optional snap points). Built on the same accessible dialog primitive as\n * `Dialog` and `Sheet` (focus trap, Escape, scroll lock), so it is an\n * alternative-to both — not a re-roll.\n *\n * Why it exists: `DialogContent responsive` collapses to a full-screen takeover\n * on mobile (`inset-0`), which leaves dead space under short content and drops\n * the drawer affordances users expect. This composes the two correct halves and\n * owns the fiddly bits (pinned header, internal scroll body with sane height\n * caps, close-button stacking above an optional full-bleed background) so\n * consumers stop hand-rolling a `useIsMobile()` switch.\n *\n * **Parts (in composition order):**\n * - `ResponsiveModal` — open/close state (this root)\n * - `ResponsiveModalTrigger` — opens it (use `asChild`)\n * - `ResponsiveModalContent` — the switching panel (Dialog md+ / bottom sheet below)\n * - `ResponsiveModalBackground` — optional full-bleed layer painted behind content (`-z-10`)\n * - `ResponsiveModalHeader` — pinned; stays put while the body scrolls\n * - `ResponsiveModalTitle` — required for accessibility (labels the dialog)\n * - `ResponsiveModalDescription` — optional subtitle\n * - `ResponsiveModalBody` — internal scroll region (capped 85dvh desktop / 90dvh mobile)\n * - `ResponsiveModalFooter` — pinned action row\n * - `ResponsiveModalClose` — manual close (a close button is built into Content)\n *\n * @compound\n * @example\n * <ResponsiveModal>\n * <ResponsiveModalTrigger asChild><Button>Open</Button></ResponsiveModalTrigger>\n * <ResponsiveModalContent>\n * <ResponsiveModalHeader>\n * <ResponsiveModalTitle>Filters</ResponsiveModalTitle>\n * <ResponsiveModalDescription>Narrow the result set.</ResponsiveModalDescription>\n * </ResponsiveModalHeader>\n * <ResponsiveModalBody>…form fields…</ResponsiveModalBody>\n * <ResponsiveModalFooter>\n * <ResponsiveModalClose asChild><Button variant=\"soft\">Cancel</Button></ResponsiveModalClose>\n * <Button>Apply</Button>\n * </ResponsiveModalFooter>\n * </ResponsiveModalContent>\n * </ResponsiveModal>\n */\n\n// ── Context ──────────────────────────────────────────────────────────────────\n\ntype ResponsiveModalContextValue = {\n open: boolean\n isMobile: boolean\n dismissable: boolean\n onClose: () => void\n}\nconst ResponsiveModalContext = React.createContext<ResponsiveModalContextValue>({\n open: false,\n isMobile: false,\n dismissable: true,\n onClose: () => {},\n})\nconst useResponsiveModal = () => React.useContext(ResponsiveModalContext)\n\n// ── Root ──────────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {\n /** When false, Escape/overlay/drag cannot dismiss — only an explicit Close/onOpenChange can. @default true */\n dismissable?: boolean\n}\n\nconst ResponsiveModal: React.FC<ResponsiveModalProps> = ({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n dismissable = true,\n children,\n ...props\n}) => {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n const isMobile = useIsMobile()\n\n const handleOpenChange = React.useCallback(\n (value: boolean) => {\n // A non-dismissable modal ignores the primitive's implicit close attempts\n // (Escape, outside-click); only value=true or an explicit close gets through.\n if (!value && !dismissable) return\n if (!isControlled) setUncontrolledOpen(value)\n onOpenChange?.(value)\n },\n [isControlled, onOpenChange, dismissable],\n )\n\n const contextValue = React.useMemo<ResponsiveModalContextValue>(\n () => ({ open, isMobile, dismissable, onClose: () => handleOpenChange(false) }),\n [open, isMobile, dismissable, handleOpenChange],\n )\n\n return (\n <ResponsiveModalContext.Provider value={contextValue}>\n <DialogPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props}>\n {children}\n </DialogPrimitive.Root>\n </ResponsiveModalContext.Provider>\n )\n}\nResponsiveModal.displayName = 'ResponsiveModal'\n\nconst ResponsiveModalTrigger = DialogPrimitive.Trigger\nconst ResponsiveModalClose = DialogPrimitive.Close\nconst ResponsiveModalPortal = DialogPrimitive.Portal\n\nconst ResponsiveModalOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n forceMount\n className={cn('fixed inset-0 z-overlay bg-overlay', className)}\n {...props}\n />\n))\nResponsiveModalOverlay.displayName = 'ResponsiveModalOverlay'\n\n// ── Snap points ────────────────────────────────────────────────────────────────\n// A snap point is a fraction (0,1] of the viewport height the sheet rests at.\n// Omitted → the sheet is content-height (capped at 90dvh) with a single rest at\n// its natural height. Provided → the sheet is tall enough for the largest snap\n// and rests at each fraction; dragging below the smallest past a threshold\n// dismisses (when dismissable).\n\nfunction nearestSnapIndex(currentVisibleFraction: number, snapPoints: number[]) {\n let best = 0\n let bestDist = Infinity\n for (let i = 0; i < snapPoints.length; i++) {\n const d = Math.abs(snapPoints[i] - currentVisibleFraction)\n if (d < bestDist) {\n bestDist = d\n best = i\n }\n }\n return best\n}\n\n// ── Content ──────────────────────────────────────────────────────────────────\n\nexport interface ResponsiveModalContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /**\n * Mobile-only. Rest heights as ascending fractions of viewport height, e.g.\n * `[0.5, 0.9]`. Omit for a content-height sheet. The largest fraction caps the\n * sheet height. Ignored on desktop.\n */\n snapPoints?: number[]\n /** Mobile-only. Index into `snapPoints` the sheet opens at. @default last (tallest) */\n defaultSnapPoint?: number\n}\n\nconst DRAG_DISMISS_THRESHOLD_PX = 120\nconst DRAG_DISMISS_VELOCITY = 500\n\nconst ResponsiveModalContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n ResponsiveModalContentProps\n>(({ className, children, snapPoints, defaultSnapPoint, ...props }, ref) => {\n const { open, isMobile, dismissable, onClose } = useResponsiveModal()\n const reduced = useReducedMotion()\n const dragControls = useDragControls()\n\n const hasSnaps = isMobile && Array.isArray(snapPoints) && snapPoints.length > 0\n const sortedSnaps = React.useMemo(\n () => (hasSnaps ? [...snapPoints!].sort((a, b) => a - b) : []),\n [hasSnaps, snapPoints],\n )\n const maxSnap = sortedSnaps.length ? sortedSnaps[sortedSnaps.length - 1] : 1\n const initialSnap = React.useMemo(() => {\n if (!sortedSnaps.length) return 0\n const d = defaultSnapPoint ?? sortedSnaps.length - 1\n return Math.min(Math.max(d, 0), sortedSnaps.length - 1)\n }, [sortedSnaps, defaultSnapPoint])\n\n const [activeSnap, setActiveSnap] = React.useState(initialSnap)\n const [sheetHeight, setSheetHeight] = React.useState(0)\n const internalRef = React.useRef<HTMLDivElement>(null)\n\n // Reset to the opening snap each time it re-opens.\n React.useEffect(() => {\n if (open) setActiveSnap(initialSnap)\n }, [open, initialSnap])\n\n // Measure the laid-out sheet height so snap rest positions can be expressed in px.\n React.useEffect(() => {\n if (!isMobile) return\n const el = internalRef.current\n if (!el) return\n const measure = () => setSheetHeight(el.getBoundingClientRect().height)\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [isMobile, open])\n\n const composedRef = React.useCallback(\n (node: HTMLDivElement | null) => {\n internalRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\n },\n [ref],\n )\n\n // Rest translateY for a snap index: at the tallest snap y=0; smaller snaps push\n // the sheet down so only that fraction of viewport height shows.\n const restY = React.useCallback(\n (index: number) => {\n if (!sortedSnaps.length || !sheetHeight) return 0\n const visible = sortedSnaps[index]\n return ((maxSnap - visible) / maxSnap) * sheetHeight\n },\n [sortedSnaps, sheetHeight, maxSnap],\n )\n\n const canDrag = isMobile && dismissable && !reduced\n\n const handleDragEnd = React.useCallback(\n (\n _e: unknown,\n info: { offset: { y: number }; velocity: { y: number } },\n ) => {\n if (!sortedSnaps.length) {\n // Content-height sheet: single rest; drag down past threshold dismisses.\n if (info.offset.y > DRAG_DISMISS_THRESHOLD_PX || info.velocity.y > DRAG_DISMISS_VELOCITY) {\n onClose()\n }\n return\n }\n // Snapped sheet: project the release point onto the visible-fraction axis.\n const currentY = restY(activeSnap) + info.offset.y\n const draggedVisible = maxSnap - (currentY / sheetHeight) * maxSnap\n const belowLowest = sortedSnaps[0] - draggedVisible\n if (\n activeSnap === 0 &&\n (belowLowest > 0.15 || info.velocity.y > DRAG_DISMISS_VELOCITY)\n ) {\n onClose()\n return\n }\n setActiveSnap(nearestSnapIndex(draggedVisible, sortedSnaps))\n },\n [sortedSnaps, restY, activeSnap, maxSnap, sheetHeight, onClose],\n )\n\n // ── Desktop: centered Dialog ──\n const desktopClasses =\n 'fixed left-[50%] top-[50%] z-modal flex max-h-[85dvh] w-full max-w-lg flex-col overflow-hidden rounded-overlay-lg bg-surface-overlay shadow-overlay'\n\n // ── Mobile: bottom sheet ──\n const mobileClasses = cn(\n 'fixed inset-x-0 bottom-0 z-modal flex flex-col overflow-hidden rounded-t-overlay-lg border-t border-surface-border-strong bg-surface-overlay shadow-overlay',\n // Snap sheets are as tall as the largest snap; content sheets hug content up to 90dvh.\n hasSnaps ? undefined : 'max-h-[90dvh]',\n )\n\n const closeButton = dismissable ? (\n <DialogPrimitive.Close\n title=\"Close\"\n className=\"absolute right-ds-05 top-ds-05 z-10 flex min-h-ds-xs min-w-ds-xs items-center justify-center rounded-control-inner text-surface-fg-subtle transition-colors duration-fast-01 ease-productive-standard hover:bg-surface-panel-hover hover:text-surface-fg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 active:scale-90 disabled:pointer-events-none\"\n >\n <Icon icon={IconX} size=\"lg\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n ) : null\n\n return (\n <AnimatePresence>\n {open && (\n <ResponsiveModalPortal forceMount>\n <ResponsiveModalOverlay asChild>\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n />\n </ResponsiveModalOverlay>\n <DialogPrimitive.Content\n ref={composedRef}\n forceMount\n asChild\n onPointerDownOutside={dismissable ? undefined : (e) => e.preventDefault()}\n {...props}\n >\n <motion.div\n className={cn(isMobile ? mobileClasses : desktopClasses, className)}\n style={\n hasSnaps\n ? { height: `${maxSnap * 100}dvh` }\n : undefined\n }\n initial={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n animate={\n isMobile\n ? { y: hasSnaps ? restY(activeSnap) : 0 }\n : { opacity: 1, scale: 1, x: '-50%', y: '-50%' }\n }\n exit={isMobile ? { y: '100%' } : { opacity: 0, scale: 0.96, x: '-50%', y: '-50%' }}\n transition={isMobile ? springs.smooth : { ...springs.smooth, opacity: tweens.fade }}\n drag={canDrag ? 'y' : false}\n dragListener={false}\n dragControls={dragControls}\n dragConstraints={{ top: 0 }}\n dragElastic={0.15}\n onDragEnd={canDrag ? handleDragEnd : undefined}\n >\n {/* Drag handle — the only surface that initiates a drag, so the\n scroll body below never fights the gesture (useDragControls). */}\n {isMobile && (\n <div\n className={cn('flex shrink-0 justify-center pb-ds-01 pt-ds-03', canDrag && 'cursor-grab touch-none')}\n onPointerDown={canDrag ? (e) => dragControls.start(e) : undefined}\n >\n <div className=\"h-1 w-8 rounded-pill bg-surface-border\" />\n </div>\n )}\n {closeButton}\n {children}\n </motion.div>\n </DialogPrimitive.Content>\n </ResponsiveModalPortal>\n )}\n </AnimatePresence>\n )\n})\nResponsiveModalContent.displayName = 'ResponsiveModalContent'\n\n// ── Background slot ────────────────────────────────────────────────────────────\n\n/**\n * Full-bleed layer painted behind the modal content (`-z-10`), clipped to the\n * panel's rounded corners. Render whatever you want behind the content (a\n * gradient, an aurora, an image). It is non-interactive by default.\n */\nconst ResponsiveModalBackground = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n aria-hidden\n className={cn('pointer-events-none absolute inset-0 -z-10 overflow-hidden rounded-[inherit]', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBackground.displayName = 'ResponsiveModalBackground'\n\n// ── Layout parts ───────────────────────────────────────────────────────────────\n\n/** Pinned header — sits above the scroll body and does not scroll with it. */\nconst ResponsiveModalHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col gap-ds-02b px-ds-06 pb-ds-04 pt-ds-05 text-center sm:text-left',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalHeader.displayName = 'ResponsiveModalHeader'\n\n/** Internal scroll region — grows to fill, scrolls when content exceeds the height cap. */\nconst ResponsiveModalBody = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('min-h-0 flex-1 overflow-y-auto px-ds-06 py-ds-02', className)}\n {...props}\n />\n ),\n)\nResponsiveModalBody.displayName = 'ResponsiveModalBody'\n\n/** Pinned footer action row. Stacks on mobile, right-aligned inline on desktop. */\nconst ResponsiveModalFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex shrink-0 flex-col-reverse gap-ds-03 px-ds-06 pb-ds-06 pt-ds-04 sm:flex-row sm:justify-end',\n className,\n )}\n {...props}\n />\n ),\n)\nResponsiveModalFooter.displayName = 'ResponsiveModalFooter'\n\nconst ResponsiveModalTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-heading-xs font-semibold text-surface-fg', className)}\n {...props}\n />\n))\nResponsiveModalTitle.displayName = 'ResponsiveModalTitle'\n\nconst ResponsiveModalDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-body-md text-surface-fg-muted', className)}\n {...props}\n />\n))\nResponsiveModalDescription.displayName = 'ResponsiveModalDescription'\n\nexport {\n ResponsiveModal,\n ResponsiveModalBackground,\n ResponsiveModalBody,\n ResponsiveModalClose,\n ResponsiveModalContent,\n ResponsiveModalDescription,\n ResponsiveModalFooter,\n ResponsiveModalHeader,\n ResponsiveModalOverlay,\n ResponsiveModalPortal,\n ResponsiveModalTitle,\n ResponsiveModalTrigger,\n}\n"],"mappings":"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"}
@@ -77,7 +77,7 @@ var H = T.forwardRef(({ groups: e, query: t, command: n }, r) => {
77
77
  role: "option",
78
78
  "aria-selected": t === i,
79
79
  onClick: () => n(e),
80
- className: a("flex w-full items-center gap-ds-03 rounded-control px-ds-03 py-ds-02 text-left cursor-pointer", t === i ? "bg-surface-raised-hover" : "hover:bg-surface-raised-hover"),
80
+ className: a("flex w-full items-center gap-ds-03 rounded-control px-ds-03 py-ds-02 text-left cursor-pointer", t === i ? "bg-surface-panel-hover" : "hover:bg-surface-panel-hover"),
81
81
  children: [r && /* @__PURE__ */ A("span", {
82
82
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-surface-fg-muted",
83
83
  children: /* @__PURE__ */ A(r, { className: "h-4 w-4" })
@@ -272,7 +272,7 @@ function Ve({ attachments: e, onRemoveAttachment: t }) {
272
272
  },
273
273
  opacity: { duration: i.fast02 }
274
274
  },
275
- className: "flex items-center gap-ds-02 shrink-0 rounded-control bg-surface-raised px-ds-03 py-ds-01 group",
275
+ className: "flex items-center gap-ds-02 shrink-0 rounded-control bg-surface-panel px-ds-03 py-ds-01 group",
276
276
  children: [
277
277
  /* @__PURE__ */ A(s, {
278
278
  icon: ce,
@@ -461,7 +461,7 @@ var We = T.forwardRef(function({ src: e, duration: t, waveformData: n, className
461
461
  type: "button",
462
462
  onClick: ee,
463
463
  "aria-label": `Playback speed ${C}x`,
464
- className: "shrink-0 text-body-xs bg-surface-raised rounded-pill px-ds-02 py-[1px] hover:bg-surface-raised-hover tabular-nums transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2",
464
+ className: "shrink-0 text-body-xs bg-surface-panel rounded-pill px-ds-02 py-[1px] hover:bg-surface-panel-hover tabular-nums transition-colors duration-fast-01 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2",
465
465
  children: [C, "x"]
466
466
  })
467
467
  ]
@@ -485,7 +485,7 @@ function G({ onClick: e, isActive: t, disabled: n, title: r, children: i, classN
485
485
  title: r,
486
486
  "aria-label": r,
487
487
  "aria-pressed": t,
488
- className: a("inline-flex h-ds-xs-plus items-center justify-center rounded-control touch-target", !o?.includes("w-") && "w-ds-xs-plus", "transition-[color,background-color,transform] duration-fast-01 ease-productive-standard", "hover:bg-surface-raised-hover hover:text-surface-fg", "active:scale-95", "disabled:pointer-events-none disabled:opacity-action-disabled", t ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle", o),
488
+ className: a("inline-flex h-ds-xs-plus items-center justify-center rounded-control touch-target", !o?.includes("w-") && "w-ds-xs-plus", "transition-[color,background-color,transform] duration-fast-01 ease-productive-standard", "hover:bg-surface-panel-hover hover:text-surface-fg", "active:scale-95", "disabled:pointer-events-none disabled:opacity-action-disabled", t ? "bg-surface-panel-hover text-accent-11" : "text-surface-fg-subtle", o),
489
489
  children: i
490
490
  });
491
491
  }
@@ -754,7 +754,7 @@ function ot({ duration: e, analyserNode: t, maxDuration: n, onCancel: r }) {
754
754
  role: "status",
755
755
  "aria-live": "polite",
756
756
  "aria-label": "Recording voice message",
757
- className: "absolute inset-0 flex items-center gap-ds-03 px-ds-04 py-ds-03 bg-surface-raised-hover rounded-surface z-10",
757
+ className: "absolute inset-0 flex items-center gap-ds-03 px-ds-04 py-ds-03 bg-surface-panel-hover rounded-surface z-10",
758
758
  children: [
759
759
  /* @__PURE__ */ A("span", {
760
760
  className: a("w-ds-02b h-ds-02b shrink-0 rounded-pill bg-error-9", i ? "opacity-100" : c),
@@ -836,7 +836,7 @@ function st({ author: e, preview: t, onDismiss: n }) {
836
836
  }), /* @__PURE__ */ A("button", {
837
837
  type: "button",
838
838
  onClick: n,
839
- className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "text-surface-fg-subtle hover:text-surface-fg hover:bg-surface-raised-hover", "transition-colors duration-fast-01 ease-productive-standard", "active:scale-95"),
839
+ className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "text-surface-fg-subtle hover:text-surface-fg hover:bg-surface-panel-hover", "transition-colors duration-fast-01 ease-productive-standard", "active:scale-95"),
840
840
  "aria-label": "Cancel reply",
841
841
  title: "Cancel reply",
842
842
  children: /* @__PURE__ */ A(s, {
@@ -914,7 +914,7 @@ function lt({ value: e, onChange: t }) {
914
914
  /* @__PURE__ */ A("select", {
915
915
  value: n,
916
916
  onChange: (e) => a(Number(e.target.value)),
917
- className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-raised px-ds-02 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9",
917
+ className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-panel px-ds-02 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9",
918
918
  "aria-label": "Hour",
919
919
  children: Array.from({ length: 12 }, (e, t) => t + 1).map((e) => /* @__PURE__ */ A("option", {
920
920
  value: e,
@@ -928,7 +928,7 @@ function lt({ value: e, onChange: t }) {
928
928
  /* @__PURE__ */ A("select", {
929
929
  value: r - r % 5,
930
930
  onChange: (e) => o(Number(e.target.value)),
931
- className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-raised px-ds-02 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9",
931
+ className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-panel px-ds-02 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9",
932
932
  "aria-label": "Minute",
933
933
  children: Array.from({ length: 12 }, (e, t) => t * 5).map((e) => /* @__PURE__ */ A("option", {
934
934
  value: e,
@@ -975,7 +975,7 @@ function X({ value: e, onChange: t, minDate: n }) {
975
975
  value: a,
976
976
  min: s,
977
977
  onChange: o,
978
- className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-raised px-ds-03 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9"
978
+ className: "h-ds-sm rounded-control border border-surface-border-strong bg-surface-panel px-ds-03 text-body-sm text-surface-fg focus:outline-hidden focus:ring-2 focus:ring-accent-9"
979
979
  })]
980
980
  }), /* @__PURE__ */ j("div", {
981
981
  className: "flex items-center justify-between",
@@ -1323,7 +1323,7 @@ var gt = {
1323
1323
  "[&_p:last-child]:mb-0",
1324
1324
  "[&_ul]:ml-ds-04 [&_ul]:list-disc [&_ol]:ml-ds-04 [&_ol]:list-decimal",
1325
1325
  "[&_li]:text-surface-fg",
1326
- "[&_code]:rounded [&_code]:bg-surface-raised [&_code]:px-ds-02 [&_code]:py-[1px] [&_code]:text-body-md [&_code]:text-accent-11",
1326
+ "[&_code]:rounded [&_code]:bg-surface-panel [&_code]:px-ds-02 [&_code]:py-[1px] [&_code]:text-body-md [&_code]:text-accent-11",
1327
1327
  "[&_strong]:font-semibold [&_strong]:text-surface-fg",
1328
1328
  "[&_mark]:rounded-xs [&_mark]:bg-warning-3 [&_mark]:px-[2px]",
1329
1329
  "[&_a]:text-accent-11 [&_a]:underline",
@@ -1346,7 +1346,7 @@ function vt({ options: e }) {
1346
1346
  title: "More send options",
1347
1347
  "aria-label": "More send options",
1348
1348
  "aria-expanded": t,
1349
- className: a("inline-flex h-ds-xs-plus w-5 items-center justify-center rounded-control touch-target", "text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg", "transition-colors duration-fast-01 ease-productive-standard", t && "bg-surface-raised-hover text-surface-fg"),
1349
+ className: a("inline-flex h-ds-xs-plus w-5 items-center justify-center rounded-control touch-target", "text-surface-fg-subtle hover:bg-surface-panel-hover hover:text-surface-fg", "transition-colors duration-fast-01 ease-productive-standard", t && "bg-surface-panel-hover text-surface-fg"),
1350
1350
  children: /* @__PURE__ */ A(s, {
1351
1351
  icon: ae,
1352
1352
  size: "xs"
@@ -1361,7 +1361,7 @@ function vt({ options: e }) {
1361
1361
  onClick: () => {
1362
1362
  e.onSelect(), n(!1);
1363
1363
  },
1364
- className: "flex w-full items-center gap-ds-03 rounded-control px-ds-03 py-ds-02 text-body-sm text-surface-fg hover:bg-surface-raised-hover transition-colors duration-fast-01",
1364
+ className: "flex w-full items-center gap-ds-03 rounded-control px-ds-03 py-ds-02 text-body-sm text-surface-fg hover:bg-surface-panel-hover transition-colors duration-fast-01",
1365
1365
  children: [e.icon && /* @__PURE__ */ A(e.icon, { className: "h-ico-sm w-ico-sm text-surface-fg-muted" }), e.label]
1366
1366
  }, t))]
1367
1367
  })]
@@ -1403,7 +1403,7 @@ function Q({ onClick: e, isActive: t = !1, title: n, children: r }) {
1403
1403
  title: n,
1404
1404
  "aria-label": n,
1405
1405
  "aria-pressed": t,
1406
- className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-raised-hover", t ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle"),
1406
+ className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-panel-hover", t ? "bg-surface-panel-hover text-accent-11" : "text-surface-fg-subtle"),
1407
1407
  children: r
1408
1408
  });
1409
1409
  }
@@ -1716,7 +1716,7 @@ var St = T.forwardRef(function({ onSubmit: o, placeholder: u = "Type a message..
1716
1716
  /* @__PURE__ */ j("div", {
1717
1717
  role: "region",
1718
1718
  "aria-label": "Message composer",
1719
- className: a("flex-1 min-w-0 overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised-hover", "transition-[color,background-color,border-color,box-shadow] duration-fast-02 ease-productive-standard", "hover:bg-surface-raised-active", "focus-within:ring-2 focus-within:ring-accent-9 focus-within:ring-offset-2 focus-within:border-accent-9", F === "recording" && "border-error-7/30", Ke && "border-dashed border-accent-7 bg-accent-2", d && "opacity-action-disabled cursor-not-allowed"),
1719
+ className: a("flex-1 min-w-0 overflow-hidden rounded-surface border border-surface-border-interactive bg-surface-panel-hover", "transition-[color,background-color,border-color,box-shadow] duration-fast-02 ease-productive-standard", "hover:bg-surface-panel-active", "focus-within:ring-2 focus-within:ring-accent-9 focus-within:ring-offset-2 focus-within:border-accent-9", F === "recording" && "border-error-7/30", Ke && "border-dashed border-accent-7 bg-accent-2", d && "opacity-action-disabled cursor-not-allowed"),
1720
1720
  onFocus: () => Be(!0),
1721
1721
  onBlur: (e) => {
1722
1722
  e.currentTarget.contains(e.relatedTarget) || Be(!1);
@@ -1783,7 +1783,7 @@ var St = T.forwardRef(function({ onSubmit: o, placeholder: u = "Type a message..
1783
1783
  title: L ? "Hide formatting" : "Show formatting",
1784
1784
  "aria-label": L ? "Hide formatting" : "Show formatting",
1785
1785
  "aria-pressed": L,
1786
- className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-raised-hover", L ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle"),
1786
+ className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target", "transition-colors duration-fast-01 ease-productive-standard", "hover:bg-surface-panel-hover", L ? "bg-surface-panel-hover text-accent-11" : "text-surface-fg-subtle"),
1787
1787
  children: /* @__PURE__ */ A(s, {
1788
1788
  icon: Se,
1789
1789
  size: "xs"
@@ -1796,7 +1796,7 @@ var St = T.forwardRef(function({ onSubmit: o, placeholder: u = "Type a message..
1796
1796
  onClick: () => Fe((e) => !e),
1797
1797
  title: "Emoji",
1798
1798
  "aria-label": "Emoji",
1799
- className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target transition-colors duration-fast-01", Ne ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle hover:bg-surface-raised-hover hover:text-surface-fg"),
1799
+ className: a("inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control touch-target transition-colors duration-fast-01", Ne ? "bg-surface-panel-hover text-accent-11" : "text-surface-fg-subtle hover:bg-surface-panel-hover hover:text-surface-fg"),
1800
1800
  children: /* @__PURE__ */ A(s, {
1801
1801
  icon: me,
1802
1802
  size: "xs"
@@ -2066,7 +2066,7 @@ var St = T.forwardRef(function({ onSubmit: o, placeholder: u = "Type a message..
2066
2066
  })]
2067
2067
  }),
2068
2068
  le && /* @__PURE__ */ A("p", {
2069
- className: "mt-ds-02 text-center text-caption text-surface-fg-subtle/50",
2069
+ className: "mt-ds-02 text-center text-caption text-surface-fg-subtle",
2070
2070
  children: le
2071
2071
  }),
2072
2072
  P && /* @__PURE__ */ A(ft, {