@devalok/shilp-sutra 0.55.1 → 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 (270) hide show
  1. package/BREAKING.json +13 -0
  2. package/MIGRATION.md +163 -0
  3. package/README.md +16 -1
  4. package/dist/_chunks/badge-group.js +92 -78
  5. package/dist/_chunks/badge-group.js.map +1 -1
  6. package/dist/_chunks/chat.js +91 -75
  7. package/dist/_chunks/chat.js.map +1 -1
  8. package/dist/_chunks/document-preview.js +1 -1
  9. package/dist/_chunks/document-preview.js.map +1 -1
  10. package/dist/_chunks/emoji-suggestion.js +32 -30
  11. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  12. package/dist/_chunks/mention-suggestion.js +36 -35
  13. package/dist/_chunks/mention-suggestion.js.map +1 -1
  14. package/dist/_chunks/oauth-button.js +1 -1
  15. package/dist/_chunks/oauth-button.js.map +1 -1
  16. package/dist/_chunks/primitives-internal.js +437 -537
  17. package/dist/_chunks/primitives-internal.js.map +1 -1
  18. package/dist/_chunks/success.js +2 -2
  19. package/dist/_chunks/success.js.map +1 -1
  20. package/dist/_chunks/tree-view.js +1 -1
  21. package/dist/_chunks/tree-view.js.map +1 -1
  22. package/dist/_chunks/use-calendar.js +4 -4
  23. package/dist/_chunks/use-calendar.js.map +1 -1
  24. package/dist/ai/blocks/text.js +1 -1
  25. package/dist/ai/blocks/text.js.map +1 -1
  26. package/dist/ai/command-bar.js +9 -9
  27. package/dist/ai/command-bar.js.map +1 -1
  28. package/dist/ai/conversation.js +2 -2
  29. package/dist/ai/conversation.js.map +1 -1
  30. package/dist/composed/activity-feed.js +1 -1
  31. package/dist/composed/activity-feed.js.map +1 -1
  32. package/dist/composed/avatar-group.d.ts +7 -2
  33. package/dist/composed/avatar-group.d.ts.map +1 -1
  34. package/dist/composed/avatar-group.js +3 -3
  35. package/dist/composed/avatar-group.js.map +1 -1
  36. package/dist/composed/command-palette.js +7 -7
  37. package/dist/composed/command-palette.js.map +1 -1
  38. package/dist/composed/content-card.js +20 -20
  39. package/dist/composed/content-card.js.map +1 -1
  40. package/dist/composed/date-picker/index.js +4 -4
  41. package/dist/composed/date-picker/index.js.map +1 -1
  42. package/dist/composed/diff.d.ts +23 -2
  43. package/dist/composed/diff.d.ts.map +1 -1
  44. package/dist/composed/diff.js +341 -260
  45. package/dist/composed/diff.js.map +1 -1
  46. package/dist/composed/emoji-picker.js +3 -3
  47. package/dist/composed/emoji-picker.js.map +1 -1
  48. package/dist/composed/error-boundary.js +2 -2
  49. package/dist/composed/error-boundary.js.map +1 -1
  50. package/dist/composed/extensions/emoji-node.js +8 -7
  51. package/dist/composed/extensions/emoji-node.js.map +1 -1
  52. package/dist/composed/file-preview.js +1 -1
  53. package/dist/composed/file-preview.js.map +1 -1
  54. package/dist/composed/filter-bar.js +1 -1
  55. package/dist/composed/filter-bar.js.map +1 -1
  56. package/dist/composed/inline-edit.js +1 -1
  57. package/dist/composed/inline-edit.js.map +1 -1
  58. package/dist/composed/loading-skeleton.js +3 -3
  59. package/dist/composed/loading-skeleton.js.map +1 -1
  60. package/dist/composed/master-detail.js +1 -1
  61. package/dist/composed/master-detail.js.map +1 -1
  62. package/dist/composed/member-picker.js +1 -1
  63. package/dist/composed/member-picker.js.map +1 -1
  64. package/dist/composed/multi-select-popover.js +1 -1
  65. package/dist/composed/multi-select-popover.js.map +1 -1
  66. package/dist/composed/page-skeletons.js +4 -4
  67. package/dist/composed/page-skeletons.js.map +1 -1
  68. package/dist/composed/responsive-modal.js +1 -1
  69. package/dist/composed/responsive-modal.js.map +1 -1
  70. package/dist/composed/rich-chat-input.js +549 -540
  71. package/dist/composed/rich-chat-input.js.map +1 -1
  72. package/dist/composed/rich-text-editor.js +206 -198
  73. package/dist/composed/rich-text-editor.js.map +1 -1
  74. package/dist/composed/schedule-view.js +5 -5
  75. package/dist/composed/schedule-view.js.map +1 -1
  76. package/dist/hooks/index.d.ts +1 -0
  77. package/dist/hooks/index.d.ts.map +1 -1
  78. package/dist/hooks/index.js +4 -3
  79. package/dist/hooks/use-container-size.d.ts +18 -0
  80. package/dist/hooks/use-container-size.d.ts.map +1 -0
  81. package/dist/hooks/use-container-size.js +28 -0
  82. package/dist/hooks/use-container-size.js.map +1 -0
  83. package/dist/shell/app-shell.d.ts +101 -0
  84. package/dist/shell/app-shell.d.ts.map +1 -0
  85. package/dist/shell/app-shell.js +76 -0
  86. package/dist/shell/app-shell.js.map +1 -0
  87. package/dist/shell/bottom-navbar.js +2 -2
  88. package/dist/shell/bottom-navbar.js.map +1 -1
  89. package/dist/shell/index.d.ts +1 -0
  90. package/dist/shell/index.d.ts.map +1 -1
  91. package/dist/shell/index.js +6 -5
  92. package/dist/shell/notification-center.js +4 -4
  93. package/dist/shell/notification-center.js.map +1 -1
  94. package/dist/shell/notification-preferences.js +1 -1
  95. package/dist/shell/notification-preferences.js.map +1 -1
  96. package/dist/shell/top-bar.js +7 -7
  97. package/dist/shell/top-bar.js.map +1 -1
  98. package/dist/tokens/generate-scale.d.ts +49 -0
  99. package/dist/tokens/generate-scale.d.ts.map +1 -1
  100. package/dist/tokens/primitives.css +29 -26
  101. package/dist/tokens/semantic.css +98 -39
  102. package/dist/tokens/variants.css +1 -1
  103. package/dist/ui/accordion.js +1 -1
  104. package/dist/ui/accordion.js.map +1 -1
  105. package/dist/ui/alert-dialog.js +1 -1
  106. package/dist/ui/alert-dialog.js.map +1 -1
  107. package/dist/ui/alert.d.ts.map +1 -1
  108. package/dist/ui/alert.js +36 -36
  109. package/dist/ui/alert.js.map +1 -1
  110. package/dist/ui/avatar.js +44 -44
  111. package/dist/ui/avatar.js.map +1 -1
  112. package/dist/ui/badge-indicator.js +1 -1
  113. package/dist/ui/badge-indicator.js.map +1 -1
  114. package/dist/ui/badge.d.ts +47 -33
  115. package/dist/ui/badge.d.ts.map +1 -1
  116. package/dist/ui/banner.js +26 -26
  117. package/dist/ui/banner.js.map +1 -1
  118. package/dist/ui/button.js +35 -35
  119. package/dist/ui/button.js.map +1 -1
  120. package/dist/ui/card.js +42 -42
  121. package/dist/ui/card.js.map +1 -1
  122. package/dist/ui/chat/date-separator.d.ts +8 -1
  123. package/dist/ui/chat/date-separator.d.ts.map +1 -1
  124. package/dist/ui/checkbox.js +1 -1
  125. package/dist/ui/checkbox.js.map +1 -1
  126. package/dist/ui/code.js +2 -2
  127. package/dist/ui/code.js.map +1 -1
  128. package/dist/ui/color-input.js +3 -3
  129. package/dist/ui/color-input.js.map +1 -1
  130. package/dist/ui/combobox.js +139 -139
  131. package/dist/ui/combobox.js.map +1 -1
  132. package/dist/ui/context-menu.js +4 -4
  133. package/dist/ui/context-menu.js.map +1 -1
  134. package/dist/ui/data-table-body.d.ts +3 -1
  135. package/dist/ui/data-table-body.d.ts.map +1 -1
  136. package/dist/ui/data-table-body.js +93 -92
  137. package/dist/ui/data-table-body.js.map +1 -1
  138. package/dist/ui/data-table-bulk-actions.d.ts +7 -1
  139. package/dist/ui/data-table-bulk-actions.d.ts.map +1 -1
  140. package/dist/ui/data-table-bulk-actions.js +14 -8
  141. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  142. package/dist/ui/data-table-card.d.ts.map +1 -1
  143. package/dist/ui/data-table-card.js +66 -47
  144. package/dist/ui/data-table-card.js.map +1 -1
  145. package/dist/ui/data-table-context.d.ts +4 -0
  146. package/dist/ui/data-table-context.d.ts.map +1 -1
  147. package/dist/ui/data-table-context.js.map +1 -1
  148. package/dist/ui/data-table-header.d.ts.map +1 -1
  149. package/dist/ui/data-table-header.js +18 -15
  150. package/dist/ui/data-table-header.js.map +1 -1
  151. package/dist/ui/data-table-pagination.js +3 -3
  152. package/dist/ui/data-table-pagination.js.map +1 -1
  153. package/dist/ui/data-table-toolbar.d.ts +11 -1
  154. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  155. package/dist/ui/data-table-toolbar.js +7 -7
  156. package/dist/ui/data-table-toolbar.js.map +1 -1
  157. package/dist/ui/data-table.d.ts +52 -5
  158. package/dist/ui/data-table.d.ts.map +1 -1
  159. package/dist/ui/data-table.js +181 -169
  160. package/dist/ui/data-table.js.map +1 -1
  161. package/dist/ui/dialog.js +1 -1
  162. package/dist/ui/dialog.js.map +1 -1
  163. package/dist/ui/dot.js +13 -13
  164. package/dist/ui/dot.js.map +1 -1
  165. package/dist/ui/dropdown-menu.js +4 -4
  166. package/dist/ui/dropdown-menu.js.map +1 -1
  167. package/dist/ui/file-upload.js +3 -3
  168. package/dist/ui/file-upload.js.map +1 -1
  169. package/dist/ui/input-otp.js +1 -1
  170. package/dist/ui/input-otp.js.map +1 -1
  171. package/dist/ui/input.js +30 -30
  172. package/dist/ui/input.js.map +1 -1
  173. package/dist/ui/lib/utils.js +2 -1
  174. package/dist/ui/lib/utils.js.map +1 -1
  175. package/dist/ui/menubar.js +5 -5
  176. package/dist/ui/menubar.js.map +1 -1
  177. package/dist/ui/navigation-menu.js +1 -1
  178. package/dist/ui/navigation-menu.js.map +1 -1
  179. package/dist/ui/number-input.js +36 -36
  180. package/dist/ui/number-input.js.map +1 -1
  181. package/dist/ui/pagination.js +1 -1
  182. package/dist/ui/pagination.js.map +1 -1
  183. package/dist/ui/progress-ring.js +2 -2
  184. package/dist/ui/progress-ring.js.map +1 -1
  185. package/dist/ui/progress.d.ts.map +1 -1
  186. package/dist/ui/progress.js +66 -62
  187. package/dist/ui/progress.js.map +1 -1
  188. package/dist/ui/radio.js +1 -1
  189. package/dist/ui/radio.js.map +1 -1
  190. package/dist/ui/select.js +61 -61
  191. package/dist/ui/select.js.map +1 -1
  192. package/dist/ui/sheet.js +47 -47
  193. package/dist/ui/sheet.js.map +1 -1
  194. package/dist/ui/sidebar.js +132 -132
  195. package/dist/ui/sidebar.js.map +1 -1
  196. package/dist/ui/skeleton.js +39 -39
  197. package/dist/ui/slider.js +18 -18
  198. package/dist/ui/slider.js.map +1 -1
  199. package/dist/ui/split-button.js +3 -3
  200. package/dist/ui/split-button.js.map +1 -1
  201. package/dist/ui/stat-card.js +1 -1
  202. package/dist/ui/stat-card.js.map +1 -1
  203. package/dist/ui/stepper.js +2 -2
  204. package/dist/ui/stepper.js.map +1 -1
  205. package/dist/ui/surface.d.ts.map +1 -1
  206. package/dist/ui/surface.js +11 -11
  207. package/dist/ui/surface.js.map +1 -1
  208. package/dist/ui/switch.js +1 -1
  209. package/dist/ui/switch.js.map +1 -1
  210. package/dist/ui/table.js +1 -1
  211. package/dist/ui/table.js.map +1 -1
  212. package/dist/ui/tabs.d.ts.map +1 -1
  213. package/dist/ui/tabs.js +48 -48
  214. package/dist/ui/tabs.js.map +1 -1
  215. package/dist/ui/text.js +8 -8
  216. package/dist/ui/textarea.js +17 -17
  217. package/dist/ui/textarea.js.map +1 -1
  218. package/dist/ui/toast.js +1 -1
  219. package/dist/ui/toast.js.map +1 -1
  220. package/dist/ui/toggle.js +15 -15
  221. package/dist/ui/toggle.js.map +1 -1
  222. package/docs/components/composed/avatar-group.md +4 -4
  223. package/docs/components/composed/diff.md +10 -2
  224. package/docs/components/composed/schedule-view.md +1 -1
  225. package/docs/components/shell/app-shell.md +132 -0
  226. package/docs/components/ui/avatar.md +1 -1
  227. package/docs/components/ui/button.md +1 -1
  228. package/docs/components/ui/chat.md +7 -1
  229. package/docs/components/ui/color-swatch.md +1 -1
  230. package/docs/components/ui/data-table-toolbar.md +6 -2
  231. package/docs/components/ui/data-table.md +41 -7
  232. package/docs/components/ui/dot.md +1 -1
  233. package/docs/components/ui/progress.md +1 -0
  234. package/docs/components/ui/surface.md +3 -3
  235. package/docs/components/ui/table.md +2 -2
  236. package/docs/recipes/install-astro.md +3 -2
  237. package/docs/recipes/install-next-app-router.md +3 -2
  238. package/docs/recipes/install-remix.md +3 -2
  239. package/docs/recipes/install-tanstack-start.md +3 -2
  240. package/docs/recipes/install-vite.md +3 -2
  241. package/docs/recipes/troubleshoot.md +26 -5
  242. package/llms.txt +2 -1
  243. package/make-kit/Guidelines.md +2 -2
  244. package/make-kit/components/button.md +1 -1
  245. package/make-kit/components/card.md +6 -6
  246. package/make-kit/components/surface.md +4 -4
  247. package/make-kit/foundations/color.md +4 -4
  248. package/make-kit/foundations/dark-mode.md +1 -1
  249. package/make-kit/foundations/radius.md +1 -1
  250. package/make-kit/foundations/surfaces.md +9 -9
  251. package/mcp-manifest.json +373 -34
  252. package/package.json +43 -13
  253. package/skill/SKILL.md +12 -6
  254. package/skill/references/components.md +2 -1
  255. package/skill/references/setup-astro.md +3 -2
  256. package/skill/references/setup-next-app-router.md +3 -2
  257. package/skill/references/setup-remix.md +3 -2
  258. package/skill/references/setup-tanstack-start.md +3 -2
  259. package/skill/references/setup-vite.md +3 -2
  260. package/skill/references/troubleshoot.md +26 -5
  261. package/dist/_chunks/tiptap.js +0 -18809
  262. package/dist/_chunks/tiptap.js.map +0 -1
  263. package/dist/_chunks/vendor-utils.js +0 -1766
  264. package/dist/_chunks/vendor-utils.js.map +0 -1
  265. package/dist/primitives/react-toast.d.ts +0 -120
  266. package/dist/ui/charts/_internal/scales.d.ts +0 -5
  267. package/dist/ui/charts/_internal/scales.d.ts.map +0 -1
  268. package/dist/ui/lib/slot.d.ts +0 -8
  269. package/dist/ui/lib/slot.d.ts.map +0 -1
  270. package/scripts/welcome.mjs +0 -312
@@ -1 +1 @@
1
- {"version":3,"file":"notification-preferences.js","names":[],"sources":["../../src/shell/notification-preferences.tsx"],"sourcesContent":["'use client'\n\n/**\n * NotificationPreferences -- Manage notification rules per channel/project.\n *\n * Props-driven: accepts preferences array, projects, and callbacks.\n * No internal fetching -- the consumer is responsible for data loading.\n */\nimport { IconBell, IconMessage,IconPlus, IconTrash } from '@tabler/icons-react'\nimport * as React from 'react'\nimport { useState } from 'react'\n\nimport { Button } from '../ui/button'\nimport { Card, CardContent,CardHeader, CardTitle } from '../ui/card'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../ui/dialog'\nimport { Icon } from '../ui/icon'\nimport { IconButton } from '../ui/icon-button'\nimport { cn } from '../ui/lib/utils'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../ui/select'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface NotificationPreference {\n id: string\n userId?: string\n projectId: string | null\n channel: string\n minTier: string\n muted: boolean\n}\n\nexport interface NotificationProject {\n id: string\n title: string\n}\n\nexport interface NotificationPreferencesProps\n extends Omit<React.ComponentPropsWithRef<'div'>, 'onSave'> {\n /** Current preference rules */\n preferences?: NotificationPreference[]\n /** Available projects for scoping rules */\n projects?: NotificationProject[]\n /** Whether data is loading */\n isLoading?: boolean\n /** Called to add or update a preference */\n onSave?: (preference: {\n projectId: string | null\n channel: string\n minTier: string\n muted: boolean\n }) => void | Promise<void>\n /** Called to toggle mute on a preference */\n onToggleMute?: (preference: NotificationPreference) => void | Promise<void>\n /** Called to update the tier on a preference */\n onUpdateTier?: (\n preference: NotificationPreference,\n newTier: string,\n ) => void | Promise<void>\n /** Called to delete a preference */\n onDelete?: (preferenceId: string) => void | Promise<void>\n /** Additional className */\n className?: string\n}\n\n// -----------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------\n\nconst CHANNEL_LABELS: Record<string, { label: string; icon: React.ReactNode }> = {\n IN_APP: { label: 'In-App', icon: <Icon icon={IconBell} size=\"sm\" /> },\n GOOGLE_CHAT: { label: 'Google Chat', icon: <Icon icon={IconMessage} size=\"sm\" /> },\n}\n\nconst TIER_LABELS: Record<string, string> = {\n INFO: 'All (Info+)',\n IMPORTANT: 'Important+',\n CRITICAL: 'Critical only',\n}\n\n// -----------------------------------------------------------------------\n// NotificationPreferences\n// -----------------------------------------------------------------------\n\nconst NotificationPreferences = React.forwardRef<HTMLDivElement, NotificationPreferencesProps>(\n function NotificationPreferences(\n {\n preferences = [],\n projects = [],\n isLoading = false,\n onSave,\n onToggleMute,\n onUpdateTier,\n onDelete,\n className,\n ...props\n },\n ref,\n ) {\n const [showAddDialog, setShowAddDialog] = useState(false)\n const [newProjectId, setNewProjectId] = useState<string>('global')\n const [newChannel, setNewChannel] = useState<string>('IN_APP')\n const [newMinTier, setNewMinTier] = useState<string>('INFO')\n const [newMuted, setNewMuted] = useState(false)\n const [isSaving, setIsSaving] = useState(false)\n\n const handleAdd = async () => {\n if (!onSave) return\n setIsSaving(true)\n try {\n await onSave({\n projectId: newProjectId === 'global' ? null : newProjectId,\n channel: newChannel,\n minTier: newMinTier,\n muted: newMuted,\n })\n setShowAddDialog(false)\n resetForm()\n } catch (error) {\n console.error('[Preferences] Failed to save:', error)\n } finally {\n setIsSaving(false)\n }\n }\n\n const resetForm = () => {\n setNewProjectId('global')\n setNewChannel('IN_APP')\n setNewMinTier('INFO')\n setNewMuted(false)\n }\n\n const getProjectName = (projectId: string | null) => {\n if (!projectId) return 'Global (all projects)'\n const project = projects.find((p) => p.id === projectId)\n return project?.title || 'Unknown project'\n }\n\n return (\n <>\n <Card ref={ref} className={className} {...(props as Omit<typeof props, 'color'>)}>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle className=\"text-body-md font-semibold\">\n Notification Preferences\n </CardTitle>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setShowAddDialog(true)}\n >\n <Icon icon={IconPlus} size=\"sm\" className=\"mr-ds-02b\" />\n Add Rule\n </Button>\n </CardHeader>\n <CardContent>\n {isLoading ? (\n <div className=\"flex items-center justify-center py-ds-07\">\n <Spinner />\n </div>\n ) : preferences.length === 0 ? (\n <div className=\"py-ds-07 text-center\">\n <p className=\"text-body-md text-surface-fg-subtle\">\n No custom preferences set. All notifications are delivered by\n default.\n </p>\n </div>\n ) : (\n <div className=\"flex flex-col\">\n {preferences.map((pref, i) => {\n const channelInfo =\n CHANNEL_LABELS[pref.channel] || CHANNEL_LABELS.IN_APP\n return (\n <div\n key={pref.id}\n className={cn(\n 'flex items-center gap-ds-05 py-ds-04',\n i < preferences.length - 1 &&\n 'border-b border-surface-border-strong',\n )}\n >\n {/* Channel icon */}\n <div className=\"flex h-ds-sm w-ds-sm shrink-0 items-center justify-center rounded-surface bg-surface-raised\">\n <span className=\"[&>svg]:h-ico-sm [&>svg]:w-ico-sm text-surface-fg-muted\" aria-hidden=\"true\">{channelInfo.icon}</span>\n </div>\n\n {/* Info */}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-body-md font-medium text-surface-fg\">\n {channelInfo.label}\n </p>\n <p className=\"text-body-sm text-surface-fg-subtle\">\n {getProjectName(pref.projectId)}\n </p>\n </div>\n\n {/* Min tier */}\n <Select\n value={pref.minTier}\n onValueChange={(v) => onUpdateTier?.(pref, v)}\n >\n <SelectTrigger\n aria-label={`Minimum notification tier for ${channelInfo.label} on ${getProjectName(pref.projectId)}`}\n className=\"h-ds-xs-plus w-[130px] text-body-sm\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(TIER_LABELS).map(([key, label]) => (\n <SelectItem key={key} value={key}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {/* Muted toggle */}\n <div className=\"flex items-center gap-ds-02b\">\n <span className=\"text-body-sm text-surface-fg-subtle\">\n {pref.muted ? 'Muted' : 'Active'}\n </span>\n <Switch\n aria-label={`${pref.muted ? 'Unmute' : 'Mute'} ${channelInfo.label} for ${getProjectName(pref.projectId)}`}\n checked={!pref.muted}\n onCheckedChange={() => onToggleMute?.(pref)}\n />\n </div>\n\n {/* Delete */}\n <IconButton\n aria-label={`Delete ${channelInfo.label} notification rule`}\n icon={<Icon icon={IconTrash} />}\n variant=\"ghost\"\n color=\"error\"\n size=\"sm\"\n onClick={() => onDelete?.(pref.id)}\n className=\"shrink-0\"\n />\n </div>\n )\n })}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Add Preference Dialog */}\n <Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>Add Notification Rule</DialogTitle>\n <DialogDescription>\n Customize how you receive notifications for a specific channel\n and project.\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-ds-05 pt-ds-03\">\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-scope\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Scope\n </label>\n <Select value={newProjectId} onValueChange={setNewProjectId}>\n <SelectTrigger id=\"pref-scope\">\n <SelectValue placeholder=\"Select scope\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"global\">\n Global (all projects)\n </SelectItem>\n {projects.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-channel\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Channel\n </label>\n <Select value={newChannel} onValueChange={setNewChannel}>\n <SelectTrigger id=\"pref-channel\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"IN_APP\">In-App</SelectItem>\n <SelectItem value=\"GOOGLE_CHAT\">Google Chat</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-min-tier\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Minimum Tier\n </label>\n <Select value={newMinTier} onValueChange={setNewMinTier}>\n <SelectTrigger id=\"pref-min-tier\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(TIER_LABELS).map(([key, label]) => (\n <SelectItem key={key} value={key}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"pref-muted\" className=\"text-body-md text-surface-fg\">\n Mute this channel\n </label>\n <Switch id=\"pref-muted\" checked={newMuted} onCheckedChange={setNewMuted} />\n </div>\n\n <div className=\"flex justify-end gap-ds-03 pt-ds-03\">\n <Button\n variant=\"ghost\"\n onClick={() => {\n setShowAddDialog(false)\n resetForm()\n }}\n >\n Cancel\n </Button>\n <Button\n onClick={handleAdd}\n disabled={isSaving}\n >\n {isSaving ? 'Saving...' : 'Save Rule'}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n )\n },\n)\n\nNotificationPreferences.displayName = 'NotificationPreferences'\n\nexport { NotificationPreferences }\n"],"mappings":"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"}
1
+ {"version":3,"file":"notification-preferences.js","names":[],"sources":["../../src/shell/notification-preferences.tsx"],"sourcesContent":["'use client'\n\n/**\n * NotificationPreferences -- Manage notification rules per channel/project.\n *\n * Props-driven: accepts preferences array, projects, and callbacks.\n * No internal fetching -- the consumer is responsible for data loading.\n */\nimport { IconBell, IconMessage,IconPlus, IconTrash } from '@tabler/icons-react'\nimport * as React from 'react'\nimport { useState } from 'react'\n\nimport { Button } from '../ui/button'\nimport { Card, CardContent,CardHeader, CardTitle } from '../ui/card'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../ui/dialog'\nimport { Icon } from '../ui/icon'\nimport { IconButton } from '../ui/icon-button'\nimport { cn } from '../ui/lib/utils'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../ui/select'\nimport { Spinner } from '../ui/spinner'\nimport { Switch } from '../ui/switch'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface NotificationPreference {\n id: string\n userId?: string\n projectId: string | null\n channel: string\n minTier: string\n muted: boolean\n}\n\nexport interface NotificationProject {\n id: string\n title: string\n}\n\nexport interface NotificationPreferencesProps\n extends Omit<React.ComponentPropsWithRef<'div'>, 'onSave'> {\n /** Current preference rules */\n preferences?: NotificationPreference[]\n /** Available projects for scoping rules */\n projects?: NotificationProject[]\n /** Whether data is loading */\n isLoading?: boolean\n /** Called to add or update a preference */\n onSave?: (preference: {\n projectId: string | null\n channel: string\n minTier: string\n muted: boolean\n }) => void | Promise<void>\n /** Called to toggle mute on a preference */\n onToggleMute?: (preference: NotificationPreference) => void | Promise<void>\n /** Called to update the tier on a preference */\n onUpdateTier?: (\n preference: NotificationPreference,\n newTier: string,\n ) => void | Promise<void>\n /** Called to delete a preference */\n onDelete?: (preferenceId: string) => void | Promise<void>\n /** Additional className */\n className?: string\n}\n\n// -----------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------\n\nconst CHANNEL_LABELS: Record<string, { label: string; icon: React.ReactNode }> = {\n IN_APP: { label: 'In-App', icon: <Icon icon={IconBell} size=\"sm\" /> },\n GOOGLE_CHAT: { label: 'Google Chat', icon: <Icon icon={IconMessage} size=\"sm\" /> },\n}\n\nconst TIER_LABELS: Record<string, string> = {\n INFO: 'All (Info+)',\n IMPORTANT: 'Important+',\n CRITICAL: 'Critical only',\n}\n\n// -----------------------------------------------------------------------\n// NotificationPreferences\n// -----------------------------------------------------------------------\n\nconst NotificationPreferences = React.forwardRef<HTMLDivElement, NotificationPreferencesProps>(\n function NotificationPreferences(\n {\n preferences = [],\n projects = [],\n isLoading = false,\n onSave,\n onToggleMute,\n onUpdateTier,\n onDelete,\n className,\n ...props\n },\n ref,\n ) {\n const [showAddDialog, setShowAddDialog] = useState(false)\n const [newProjectId, setNewProjectId] = useState<string>('global')\n const [newChannel, setNewChannel] = useState<string>('IN_APP')\n const [newMinTier, setNewMinTier] = useState<string>('INFO')\n const [newMuted, setNewMuted] = useState(false)\n const [isSaving, setIsSaving] = useState(false)\n\n const handleAdd = async () => {\n if (!onSave) return\n setIsSaving(true)\n try {\n await onSave({\n projectId: newProjectId === 'global' ? null : newProjectId,\n channel: newChannel,\n minTier: newMinTier,\n muted: newMuted,\n })\n setShowAddDialog(false)\n resetForm()\n } catch (error) {\n console.error('[Preferences] Failed to save:', error)\n } finally {\n setIsSaving(false)\n }\n }\n\n const resetForm = () => {\n setNewProjectId('global')\n setNewChannel('IN_APP')\n setNewMinTier('INFO')\n setNewMuted(false)\n }\n\n const getProjectName = (projectId: string | null) => {\n if (!projectId) return 'Global (all projects)'\n const project = projects.find((p) => p.id === projectId)\n return project?.title || 'Unknown project'\n }\n\n return (\n <>\n <Card ref={ref} className={className} {...(props as Omit<typeof props, 'color'>)}>\n <CardHeader className=\"flex flex-row items-center justify-between\">\n <CardTitle className=\"text-body-md font-semibold\">\n Notification Preferences\n </CardTitle>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setShowAddDialog(true)}\n >\n <Icon icon={IconPlus} size=\"sm\" className=\"mr-ds-02b\" />\n Add Rule\n </Button>\n </CardHeader>\n <CardContent>\n {isLoading ? (\n <div className=\"flex items-center justify-center py-ds-07\">\n <Spinner />\n </div>\n ) : preferences.length === 0 ? (\n <div className=\"py-ds-07 text-center\">\n <p className=\"text-body-md text-surface-fg-subtle\">\n No custom preferences set. All notifications are delivered by\n default.\n </p>\n </div>\n ) : (\n <div className=\"flex flex-col\">\n {preferences.map((pref, i) => {\n const channelInfo =\n CHANNEL_LABELS[pref.channel] || CHANNEL_LABELS.IN_APP\n return (\n <div\n key={pref.id}\n className={cn(\n 'flex items-center gap-ds-05 py-ds-04',\n i < preferences.length - 1 &&\n 'border-b border-surface-border-strong',\n )}\n >\n {/* Channel icon */}\n <div className=\"flex h-ds-sm w-ds-sm shrink-0 items-center justify-center rounded-surface bg-surface-panel\">\n <span className=\"[&>svg]:h-ico-sm [&>svg]:w-ico-sm text-surface-fg-muted\" aria-hidden=\"true\">{channelInfo.icon}</span>\n </div>\n\n {/* Info */}\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-body-md font-medium text-surface-fg\">\n {channelInfo.label}\n </p>\n <p className=\"text-body-sm text-surface-fg-subtle\">\n {getProjectName(pref.projectId)}\n </p>\n </div>\n\n {/* Min tier */}\n <Select\n value={pref.minTier}\n onValueChange={(v) => onUpdateTier?.(pref, v)}\n >\n <SelectTrigger\n aria-label={`Minimum notification tier for ${channelInfo.label} on ${getProjectName(pref.projectId)}`}\n className=\"h-ds-xs-plus w-[130px] text-body-sm\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(TIER_LABELS).map(([key, label]) => (\n <SelectItem key={key} value={key}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {/* Muted toggle */}\n <div className=\"flex items-center gap-ds-02b\">\n <span className=\"text-body-sm text-surface-fg-subtle\">\n {pref.muted ? 'Muted' : 'Active'}\n </span>\n <Switch\n aria-label={`${pref.muted ? 'Unmute' : 'Mute'} ${channelInfo.label} for ${getProjectName(pref.projectId)}`}\n checked={!pref.muted}\n onCheckedChange={() => onToggleMute?.(pref)}\n />\n </div>\n\n {/* Delete */}\n <IconButton\n aria-label={`Delete ${channelInfo.label} notification rule`}\n icon={<Icon icon={IconTrash} />}\n variant=\"ghost\"\n color=\"error\"\n size=\"sm\"\n onClick={() => onDelete?.(pref.id)}\n className=\"shrink-0\"\n />\n </div>\n )\n })}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Add Preference Dialog */}\n <Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>Add Notification Rule</DialogTitle>\n <DialogDescription>\n Customize how you receive notifications for a specific channel\n and project.\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-ds-05 pt-ds-03\">\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-scope\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Scope\n </label>\n <Select value={newProjectId} onValueChange={setNewProjectId}>\n <SelectTrigger id=\"pref-scope\">\n <SelectValue placeholder=\"Select scope\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"global\">\n Global (all projects)\n </SelectItem>\n {projects.map((p) => (\n <SelectItem key={p.id} value={p.id}>\n {p.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-channel\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Channel\n </label>\n <Select value={newChannel} onValueChange={setNewChannel}>\n <SelectTrigger id=\"pref-channel\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"IN_APP\">In-App</SelectItem>\n <SelectItem value=\"GOOGLE_CHAT\">Google Chat</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-col gap-ds-02b\">\n <label htmlFor=\"pref-min-tier\" className=\"text-body-sm font-medium text-surface-fg-muted\">\n Minimum Tier\n </label>\n <Select value={newMinTier} onValueChange={setNewMinTier}>\n <SelectTrigger id=\"pref-min-tier\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {Object.entries(TIER_LABELS).map(([key, label]) => (\n <SelectItem key={key} value={key}>\n {label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <label htmlFor=\"pref-muted\" className=\"text-body-md text-surface-fg\">\n Mute this channel\n </label>\n <Switch id=\"pref-muted\" checked={newMuted} onCheckedChange={setNewMuted} />\n </div>\n\n <div className=\"flex justify-end gap-ds-03 pt-ds-03\">\n <Button\n variant=\"ghost\"\n onClick={() => {\n setShowAddDialog(false)\n resetForm()\n }}\n >\n Cancel\n </Button>\n <Button\n onClick={handleAdd}\n disabled={isSaving}\n >\n {isSaving ? 'Saving...' : 'Save Rule'}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n )\n },\n)\n\nNotificationPreferences.displayName = 'NotificationPreferences'\n\nexport { NotificationPreferences }\n"],"mappings":"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"}
@@ -17,7 +17,7 @@ var T = _.forwardRef(({ className: e, children: n, ...r }, i) => {
17
17
  return /* @__PURE__ */ y("header", {
18
18
  ...r,
19
19
  ref: i,
20
- className: t("z-sticky w-full border-b border-surface-border-strong bg-surface-chrome px-ds-05 py-ds-04 md:px-ds-06", a ? "grid grid-cols-[1fr_auto_1fr] items-center" : "flex items-center", e),
20
+ className: t("z-sticky w-full border-b border-surface-border-strong bg-surface-base px-ds-05 py-ds-04 md:px-ds-06", a ? "grid grid-cols-[1fr_auto_1fr] items-center" : "flex items-center", e),
21
21
  children: n
22
22
  });
23
23
  });
@@ -60,7 +60,7 @@ var j = _.forwardRef(({ icon: n, tooltip: r, className: i, ...a }, s) => /* @__P
60
60
  ref: s,
61
61
  type: "button",
62
62
  "aria-label": r,
63
- className: t("flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-card-strong bg-surface-raised-hover text-surface-fg-muted transition-[color,background-color,border-color,transform] hover:bg-surface-raised-active active:scale-90 duration-fast-01 ease-productive-standard", i),
63
+ className: t("flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-card-strong bg-surface-panel-hover text-surface-fg-muted transition-[color,background-color,border-color,transform] hover:bg-surface-panel-active active:scale-90 duration-fast-01 ease-productive-standard", i),
64
64
  ...a,
65
65
  children: /* @__PURE__ */ y("span", {
66
66
  className: "[&>svg]:h-ico-sm [&>svg]:w-ico-sm",
@@ -100,7 +100,7 @@ var N = _.forwardRef(({ user: T, onNavigate: E, onLogout: D, userMenuItems: O, c
100
100
  src: T.image,
101
101
  alt: T.name
102
102
  }) : null, /* @__PURE__ */ y(i, {
103
- className: "bg-surface-raised-hover text-surface-fg",
103
+ className: "bg-surface-panel-hover text-surface-fg",
104
104
  children: T.name?.charAt(0).toUpperCase()
105
105
  })]
106
106
  })
@@ -128,7 +128,7 @@ var N = _.forwardRef(({ user: T, onNavigate: E, onLogout: D, userMenuItems: O, c
128
128
  })]
129
129
  }),
130
130
  /* @__PURE__ */ b(l, {
131
- className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised",
131
+ className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover",
132
132
  onClick: () => E?.("/profile"),
133
133
  children: [/* @__PURE__ */ y(n, {
134
134
  icon: w,
@@ -147,7 +147,7 @@ var N = _.forwardRef(({ user: T, onNavigate: E, onLogout: D, userMenuItems: O, c
147
147
  info: "text-info-11"
148
148
  }[n.color] ?? "text-surface-fg-muted" : "text-surface-fg-muted";
149
149
  return /* @__PURE__ */ b(_.Fragment, { children: [n.separator && /* @__PURE__ */ y(u, { className: "bg-surface-border" }), /* @__PURE__ */ b(l, {
150
- className: t("flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised", n.disabled && "pointer-events-none opacity-action-disabled"),
150
+ className: t("flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover", n.disabled && "pointer-events-none opacity-action-disabled"),
151
151
  disabled: n.disabled,
152
152
  onClick: () => {
153
153
  n.disabled || (n.onClick ? n.onClick() : n.href && E?.(n.href));
@@ -172,7 +172,7 @@ var N = _.forwardRef(({ user: T, onNavigate: E, onLogout: D, userMenuItems: O, c
172
172
  })] }, n.label + r);
173
173
  }),
174
174
  /* @__PURE__ */ b(l, {
175
- className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised",
175
+ className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover",
176
176
  onClick: M,
177
177
  children: [j === "dark" ? /* @__PURE__ */ y(n, {
178
178
  icon: C,
@@ -188,7 +188,7 @@ var N = _.forwardRef(({ user: T, onNavigate: E, onLogout: D, userMenuItems: O, c
188
188
  })]
189
189
  }),
190
190
  D && /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(u, { className: "bg-surface-border" }), /* @__PURE__ */ b(l, {
191
- className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised",
191
+ className: "flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover",
192
192
  onClick: D,
193
193
  children: [/* @__PURE__ */ y(n, {
194
194
  icon: x,
@@ -1 +1 @@
1
- {"version":3,"file":"top-bar.js","names":[],"sources":["../../src/shell/top-bar.tsx"],"sourcesContent":["'use client'\n\n/**\n * TopBar v2 — Composition-based application top bar.\n *\n * Subcomponents: TopBar.Left, TopBar.Center, TopBar.Right,\n * TopBar.Section, TopBar.IconButton, TopBar.Title, TopBar.UserMenu.\n */\nimport { IconLogout, IconMoon, IconSun,IconUser } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { Icon } from '../ui/icon'\nimport { IconProvider } from '../ui/icon-context'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { normalizeIcon } from '../ui/lib/normalize-icon'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface TopBarUser {\n name: string\n email?: string\n image?: string | null\n}\n\nexport interface UserMenuItem {\n /** Display label */\n label: string\n /** Optional icon. Accepts any `IconInput`. */\n icon?: IconInput\n /** Navigate via onNavigate when clicked */\n href?: string\n /** Custom click handler */\n onClick?: () => void\n /** Render a separator before this item */\n separator?: boolean\n /** Text color semantic token (e.g. \"error\") — maps to text-{color} */\n color?: string\n /** Badge content — string for count, true for dot indicator */\n badge?: string | boolean\n /** Whether the item is disabled */\n disabled?: boolean\n}\n\n// -----------------------------------------------------------------------\n// TopBar (root)\n// -----------------------------------------------------------------------\n\ninterface TopBarRootProps extends React.HTMLAttributes<HTMLElement> {\n children: React.ReactNode\n className?: string\n}\n\nconst TopBarRoot = React.forwardRef<HTMLElement, TopBarRootProps>(\n ({ className, children, ...props }, ref) => {\n // Detect if a Center zone is present to switch to grid layout\n const hasCenter = React.Children.toArray(children).some(\n (child) => React.isValidElement(child) && child.type === TopBarCenter,\n )\n\n return (\n <header\n {...props}\n ref={ref}\n className={cn(\n 'z-sticky w-full border-b border-surface-border-strong bg-surface-chrome px-ds-05 py-ds-04 md:px-ds-06',\n hasCenter\n ? 'grid grid-cols-[1fr_auto_1fr] items-center'\n : 'flex items-center',\n className,\n )}\n >\n {children}\n </header>\n )\n },\n)\nTopBarRoot.displayName = 'TopBar'\n\n// -----------------------------------------------------------------------\n// TopBar.Left\n// -----------------------------------------------------------------------\n\ninterface TopBarLeftProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarLeft = React.forwardRef<HTMLDivElement, TopBarLeftProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center gap-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarLeft.displayName = 'TopBar.Left'\n\n// -----------------------------------------------------------------------\n// TopBar.Center\n// -----------------------------------------------------------------------\n\ninterface TopBarCenterProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarCenter = React.forwardRef<HTMLDivElement, TopBarCenterProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center justify-center px-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarCenter.displayName = 'TopBar.Center'\n\n// -----------------------------------------------------------------------\n// TopBar.Right\n// -----------------------------------------------------------------------\n\ninterface TopBarRightProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarRight = React.forwardRef<HTMLDivElement, TopBarRightProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('ml-auto flex items-center gap-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarRight.displayName = 'TopBar.Right'\n\n// -----------------------------------------------------------------------\n// TopBar.Section\n// -----------------------------------------------------------------------\n\nconst gapMap = {\n tight: 'gap-ds-02',\n default: 'gap-ds-04',\n loose: 'gap-ds-06',\n} as const\n\ninterface TopBarSectionProps extends React.HTMLAttributes<HTMLDivElement> {\n gap?: keyof typeof gapMap\n children: React.ReactNode\n}\n\nconst TopBarSection = React.forwardRef<HTMLDivElement, TopBarSectionProps>(\n ({ gap = 'default', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center', gapMap[gap], className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarSection.displayName = 'TopBar.Section'\n\n// -----------------------------------------------------------------------\n// TopBar.IconButton\n// -----------------------------------------------------------------------\n\ninterface TopBarIconButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n icon: IconInput\n tooltip: string\n}\n\nconst TopBarIconButton = React.forwardRef<\n HTMLButtonElement,\n TopBarIconButtonProps\n>(({ icon, tooltip, className, ...props }, ref) => (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n aria-label={tooltip}\n className={cn(\n 'flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-card-strong bg-surface-raised-hover text-surface-fg-muted transition-[color,background-color,border-color,transform] hover:bg-surface-raised-active active:scale-90 duration-fast-01 ease-productive-standard',\n className,\n )}\n {...props}\n >\n <span className=\"[&>svg]:h-ico-sm [&>svg]:w-ico-sm\" aria-hidden=\"true\">\n <IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider>\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" align=\"center\">\n {tooltip}\n </TooltipContent>\n </Tooltip>\n))\nTopBarIconButton.displayName = 'TopBar.IconButton'\n\n// -----------------------------------------------------------------------\n// TopBar.Title\n// -----------------------------------------------------------------------\n\ninterface TopBarTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {\n children: React.ReactNode\n}\n\nconst TopBarTitle = React.forwardRef<HTMLHeadingElement, TopBarTitleProps>(\n ({ className, children, ...props }, ref) => (\n <h2\n ref={ref}\n className={cn(\n 'hidden text-heading-xs text-surface-fg md:block',\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n ),\n)\nTopBarTitle.displayName = 'TopBar.Title'\n\n// -----------------------------------------------------------------------\n// TopBar.UserMenu\n// -----------------------------------------------------------------------\n\ninterface TopBarUserMenuProps {\n user: TopBarUser\n onNavigate?: (path: string) => void\n onLogout?: () => void\n userMenuItems?: UserMenuItem[]\n className?: string\n}\n\nconst TopBarUserMenu = React.forwardRef<HTMLButtonElement, TopBarUserMenuProps>(\n ({ user, onNavigate, onLogout, userMenuItems, className }, ref) => {\n const { colorMode, toggleColorMode } = useColorMode()\n\n return (\n <DropdownMenu>\n <Tooltip>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={cn('flex items-center gap-ds-03 outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2', className)}\n >\n <Avatar className=\"h-ds-sm-plus w-ds-sm-plus cursor-pointer\">\n {user.image ? (\n <AvatarImage src={user.image} alt={user.name} />\n ) : null}\n <AvatarFallback className=\"bg-surface-raised-hover text-surface-fg\">\n {user.name?.charAt(0).toUpperCase()}\n </AvatarFallback>\n </Avatar>\n </button>\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" align=\"center\">\n User Menu\n </TooltipContent>\n </Tooltip>\n\n <DropdownMenuContent\n className=\"w-[200px] rounded-overlay-lg bg-surface-overlay p-0 shadow-floating\"\n sideOffset={8}\n align=\"end\"\n >\n {/* User Info */}\n <div className=\"border-b border-surface-border-strong px-ds-05 py-ds-04\">\n <p className=\"text-body-md text-surface-fg\">{user.name}</p>\n {user.email && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-body-sm text-surface-fg-subtle\">\n {user.email}\n </TruncatedText>\n )}\n </div>\n\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised\"\n onClick={() => onNavigate?.('/profile')}\n >\n <Icon icon={IconUser} size=\"sm\" className=\"text-surface-fg-muted\" />\n <span className=\"text-body-md text-surface-fg-muted\">Profile</span>\n </DropdownMenuItem>\n\n {/* Custom user menu items */}\n {userMenuItems?.map((item, index) => {\n const colorMap: Record<string, string> = {\n error: 'text-error-11',\n success: 'text-success-11',\n warning: 'text-warning-11',\n info: 'text-info-11',\n }\n const textColor = item.color\n ? (colorMap[item.color] ?? 'text-surface-fg-muted')\n : 'text-surface-fg-muted'\n return (\n <React.Fragment key={item.label + index}>\n {item.separator && (\n <DropdownMenuSeparator className=\"bg-surface-border\" />\n )}\n <DropdownMenuItem\n className={cn(\n 'flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised',\n item.disabled && 'pointer-events-none opacity-action-disabled',\n )}\n disabled={item.disabled}\n onClick={() => {\n if (item.disabled) return\n if (item.onClick) item.onClick()\n else if (item.href) onNavigate?.(item.href)\n }}\n >\n {item.icon && (\n <span\n className={cn(\n '[&>svg]:h-ico-sm [&>svg]:w-ico-sm',\n textColor,\n )}\n >\n <IconProvider size=\"sm\">{normalizeIcon(item.icon)}</IconProvider>\n </span>\n )}\n <span className={cn('text-body-md', textColor)}>\n {item.label}\n </span>\n {item.badge != null &&\n item.badge !== false &&\n (typeof item.badge === 'string' ? (\n <span className=\"ml-auto inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-pill bg-error-9 px-ds-02 text-[10px] font-semibold leading-none text-error-fg\">\n {item.badge}\n </span>\n ) : (\n <span className=\"ml-auto h-[8px] w-[8px] rounded-pill bg-error-9\" />\n ))}\n </DropdownMenuItem>\n </React.Fragment>\n )\n })}\n\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised\"\n onClick={toggleColorMode}\n >\n {colorMode === 'dark' ? (\n <Icon icon={IconSun} size=\"sm\" className=\"text-surface-fg-muted\" />\n ) : (\n <Icon icon={IconMoon} size=\"sm\" className=\"text-surface-fg-muted\" />\n )}\n <span className=\"text-body-md text-surface-fg-muted\">\n {colorMode === 'dark' ? 'Light Mode' : 'Dark Mode'}\n </span>\n </DropdownMenuItem>\n\n {onLogout && (\n <>\n <DropdownMenuSeparator className=\"bg-surface-border\" />\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-raised\"\n onClick={onLogout}\n >\n <Icon icon={IconLogout} size=\"sm\" className=\"text-error-11\" />\n <span className=\"text-body-md text-error-11\">Logout</span>\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n },\n)\nTopBarUserMenu.displayName = 'TopBar.UserMenu'\n\n// -----------------------------------------------------------------------\n// Compound export\n// -----------------------------------------------------------------------\n\nexport type TopBarProps = TopBarRootProps\n\nconst TopBar = Object.assign(TopBarRoot, {\n Left: TopBarLeft,\n Center: TopBarCenter,\n Right: TopBarRight,\n Section: TopBarSection,\n IconButton: TopBarIconButton,\n Title: TopBarTitle,\n UserMenu: TopBarUserMenu,\n})\n\nexport { TopBar }\n"],"mappings":"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"}
1
+ {"version":3,"file":"top-bar.js","names":[],"sources":["../../src/shell/top-bar.tsx"],"sourcesContent":["'use client'\n\n/**\n * TopBar v2 — Composition-based application top bar.\n *\n * Subcomponents: TopBar.Left, TopBar.Center, TopBar.Right,\n * TopBar.Section, TopBar.IconButton, TopBar.Title, TopBar.UserMenu.\n */\nimport { IconLogout, IconMoon, IconSun,IconUser } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../ui/dropdown-menu'\nimport { Icon } from '../ui/icon'\nimport { IconProvider } from '../ui/icon-context'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { normalizeIcon } from '../ui/lib/normalize-icon'\nimport { cn } from '../ui/lib/utils'\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '../ui/tooltip'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface TopBarUser {\n name: string\n email?: string\n image?: string | null\n}\n\nexport interface UserMenuItem {\n /** Display label */\n label: string\n /** Optional icon. Accepts any `IconInput`. */\n icon?: IconInput\n /** Navigate via onNavigate when clicked */\n href?: string\n /** Custom click handler */\n onClick?: () => void\n /** Render a separator before this item */\n separator?: boolean\n /** Text color semantic token (e.g. \"error\") — maps to text-{color} */\n color?: string\n /** Badge content — string for count, true for dot indicator */\n badge?: string | boolean\n /** Whether the item is disabled */\n disabled?: boolean\n}\n\n// -----------------------------------------------------------------------\n// TopBar (root)\n// -----------------------------------------------------------------------\n\ninterface TopBarRootProps extends React.HTMLAttributes<HTMLElement> {\n children: React.ReactNode\n className?: string\n}\n\nconst TopBarRoot = React.forwardRef<HTMLElement, TopBarRootProps>(\n ({ className, children, ...props }, ref) => {\n // Detect if a Center zone is present to switch to grid layout\n const hasCenter = React.Children.toArray(children).some(\n (child) => React.isValidElement(child) && child.type === TopBarCenter,\n )\n\n return (\n <header\n {...props}\n ref={ref}\n className={cn(\n 'z-sticky w-full border-b border-surface-border-strong bg-surface-base px-ds-05 py-ds-04 md:px-ds-06',\n hasCenter\n ? 'grid grid-cols-[1fr_auto_1fr] items-center'\n : 'flex items-center',\n className,\n )}\n >\n {children}\n </header>\n )\n },\n)\nTopBarRoot.displayName = 'TopBar'\n\n// -----------------------------------------------------------------------\n// TopBar.Left\n// -----------------------------------------------------------------------\n\ninterface TopBarLeftProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarLeft = React.forwardRef<HTMLDivElement, TopBarLeftProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center gap-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarLeft.displayName = 'TopBar.Left'\n\n// -----------------------------------------------------------------------\n// TopBar.Center\n// -----------------------------------------------------------------------\n\ninterface TopBarCenterProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarCenter = React.forwardRef<HTMLDivElement, TopBarCenterProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center justify-center px-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarCenter.displayName = 'TopBar.Center'\n\n// -----------------------------------------------------------------------\n// TopBar.Right\n// -----------------------------------------------------------------------\n\ninterface TopBarRightProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode\n}\n\nconst TopBarRight = React.forwardRef<HTMLDivElement, TopBarRightProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('ml-auto flex items-center gap-ds-04', className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarRight.displayName = 'TopBar.Right'\n\n// -----------------------------------------------------------------------\n// TopBar.Section\n// -----------------------------------------------------------------------\n\nconst gapMap = {\n tight: 'gap-ds-02',\n default: 'gap-ds-04',\n loose: 'gap-ds-06',\n} as const\n\ninterface TopBarSectionProps extends React.HTMLAttributes<HTMLDivElement> {\n gap?: keyof typeof gapMap\n children: React.ReactNode\n}\n\nconst TopBarSection = React.forwardRef<HTMLDivElement, TopBarSectionProps>(\n ({ gap = 'default', className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center', gapMap[gap], className)}\n {...props}\n >\n {children}\n </div>\n ),\n)\nTopBarSection.displayName = 'TopBar.Section'\n\n// -----------------------------------------------------------------------\n// TopBar.IconButton\n// -----------------------------------------------------------------------\n\ninterface TopBarIconButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n icon: IconInput\n tooltip: string\n}\n\nconst TopBarIconButton = React.forwardRef<\n HTMLButtonElement,\n TopBarIconButtonProps\n>(({ icon, tooltip, className, ...props }, ref) => (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n aria-label={tooltip}\n className={cn(\n 'flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-card-strong bg-surface-panel-hover text-surface-fg-muted transition-[color,background-color,border-color,transform] hover:bg-surface-panel-active active:scale-90 duration-fast-01 ease-productive-standard',\n className,\n )}\n {...props}\n >\n <span className=\"[&>svg]:h-ico-sm [&>svg]:w-ico-sm\" aria-hidden=\"true\">\n <IconProvider size=\"sm\">{normalizeIcon(icon)}</IconProvider>\n </span>\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" align=\"center\">\n {tooltip}\n </TooltipContent>\n </Tooltip>\n))\nTopBarIconButton.displayName = 'TopBar.IconButton'\n\n// -----------------------------------------------------------------------\n// TopBar.Title\n// -----------------------------------------------------------------------\n\ninterface TopBarTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {\n children: React.ReactNode\n}\n\nconst TopBarTitle = React.forwardRef<HTMLHeadingElement, TopBarTitleProps>(\n ({ className, children, ...props }, ref) => (\n <h2\n ref={ref}\n className={cn(\n 'hidden text-heading-xs text-surface-fg md:block',\n className,\n )}\n {...props}\n >\n {children}\n </h2>\n ),\n)\nTopBarTitle.displayName = 'TopBar.Title'\n\n// -----------------------------------------------------------------------\n// TopBar.UserMenu\n// -----------------------------------------------------------------------\n\ninterface TopBarUserMenuProps {\n user: TopBarUser\n onNavigate?: (path: string) => void\n onLogout?: () => void\n userMenuItems?: UserMenuItem[]\n className?: string\n}\n\nconst TopBarUserMenu = React.forwardRef<HTMLButtonElement, TopBarUserMenuProps>(\n ({ user, onNavigate, onLogout, userMenuItems, className }, ref) => {\n const { colorMode, toggleColorMode } = useColorMode()\n\n return (\n <DropdownMenu>\n <Tooltip>\n <TooltipTrigger asChild>\n <DropdownMenuTrigger asChild>\n <button\n ref={ref}\n type=\"button\"\n className={cn('flex items-center gap-ds-03 outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2', className)}\n >\n <Avatar className=\"h-ds-sm-plus w-ds-sm-plus cursor-pointer\">\n {user.image ? (\n <AvatarImage src={user.image} alt={user.name} />\n ) : null}\n <AvatarFallback className=\"bg-surface-panel-hover text-surface-fg\">\n {user.name?.charAt(0).toUpperCase()}\n </AvatarFallback>\n </Avatar>\n </button>\n </DropdownMenuTrigger>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" align=\"center\">\n User Menu\n </TooltipContent>\n </Tooltip>\n\n <DropdownMenuContent\n className=\"w-[200px] rounded-overlay-lg bg-surface-overlay p-0 shadow-floating\"\n sideOffset={8}\n align=\"end\"\n >\n {/* User Info */}\n <div className=\"border-b border-surface-border-strong px-ds-05 py-ds-04\">\n <p className=\"text-body-md text-surface-fg\">{user.name}</p>\n {user.email && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-body-sm text-surface-fg-subtle\">\n {user.email}\n </TruncatedText>\n )}\n </div>\n\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover\"\n onClick={() => onNavigate?.('/profile')}\n >\n <Icon icon={IconUser} size=\"sm\" className=\"text-surface-fg-muted\" />\n <span className=\"text-body-md text-surface-fg-muted\">Profile</span>\n </DropdownMenuItem>\n\n {/* Custom user menu items */}\n {userMenuItems?.map((item, index) => {\n const colorMap: Record<string, string> = {\n error: 'text-error-11',\n success: 'text-success-11',\n warning: 'text-warning-11',\n info: 'text-info-11',\n }\n const textColor = item.color\n ? (colorMap[item.color] ?? 'text-surface-fg-muted')\n : 'text-surface-fg-muted'\n return (\n <React.Fragment key={item.label + index}>\n {item.separator && (\n <DropdownMenuSeparator className=\"bg-surface-border\" />\n )}\n <DropdownMenuItem\n className={cn(\n 'flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover',\n item.disabled && 'pointer-events-none opacity-action-disabled',\n )}\n disabled={item.disabled}\n onClick={() => {\n if (item.disabled) return\n if (item.onClick) item.onClick()\n else if (item.href) onNavigate?.(item.href)\n }}\n >\n {item.icon && (\n <span\n className={cn(\n '[&>svg]:h-ico-sm [&>svg]:w-ico-sm',\n textColor,\n )}\n >\n <IconProvider size=\"sm\">{normalizeIcon(item.icon)}</IconProvider>\n </span>\n )}\n <span className={cn('text-body-md', textColor)}>\n {item.label}\n </span>\n {item.badge != null &&\n item.badge !== false &&\n (typeof item.badge === 'string' ? (\n <span className=\"ml-auto inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-pill bg-error-9 px-ds-02 text-[10px] font-semibold leading-none text-error-fg\">\n {item.badge}\n </span>\n ) : (\n <span className=\"ml-auto h-[8px] w-[8px] rounded-pill bg-error-9\" />\n ))}\n </DropdownMenuItem>\n </React.Fragment>\n )\n })}\n\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover\"\n onClick={toggleColorMode}\n >\n {colorMode === 'dark' ? (\n <Icon icon={IconSun} size=\"sm\" className=\"text-surface-fg-muted\" />\n ) : (\n <Icon icon={IconMoon} size=\"sm\" className=\"text-surface-fg-muted\" />\n )}\n <span className=\"text-body-md text-surface-fg-muted\">\n {colorMode === 'dark' ? 'Light Mode' : 'Dark Mode'}\n </span>\n </DropdownMenuItem>\n\n {onLogout && (\n <>\n <DropdownMenuSeparator className=\"bg-surface-border\" />\n <DropdownMenuItem\n className=\"flex w-full cursor-pointer items-center gap-ds-03 px-ds-05 py-ds-04 hover:bg-surface-panel-hover\"\n onClick={onLogout}\n >\n <Icon icon={IconLogout} size=\"sm\" className=\"text-error-11\" />\n <span className=\"text-body-md text-error-11\">Logout</span>\n </DropdownMenuItem>\n </>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n },\n)\nTopBarUserMenu.displayName = 'TopBar.UserMenu'\n\n// -----------------------------------------------------------------------\n// Compound export\n// -----------------------------------------------------------------------\n\nexport type TopBarProps = TopBarRootProps\n\nconst TopBar = Object.assign(TopBarRoot, {\n Left: TopBarLeft,\n Center: TopBarCenter,\n Right: TopBarRight,\n Section: TopBarSection,\n IconButton: TopBarIconButton,\n Title: TopBarTitle,\n UserMenu: TopBarUserMenu,\n})\n\nexport { TopBar }\n"],"mappings":"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"}
@@ -12,6 +12,29 @@ export interface ScaleOptions {
12
12
  peakChroma: number;
13
13
  /** When true, keeps chroma very low across all steps */
14
14
  isNeutral?: boolean;
15
+ /**
16
+ * Per-ramp lightness corrections for the solid steps, in OKLCH L units.
17
+ *
18
+ * Steps 9 and 10 are the solid fills that carry text. The generic curve does
19
+ * not clear WCAG for every hue — green and blue at the default dark L of 0.63
20
+ * are too light under white text — so the ramps used as semantic intents carry
21
+ * a hand-verified correction.
22
+ *
23
+ * Discovered 2026-08-18: without these, `generateScale` could not reproduce the
24
+ * palette in `primitives.css`. Regenerating a ramp silently reverted the
25
+ * contrast fix on pink, red, green, blue and neutral.
26
+ *
27
+ * Decorative ramps (purple, teal, amber, slate, indigo, cyan, orange, emerald)
28
+ * need no correction — they never sit under text.
29
+ */
30
+ correction?: {
31
+ /** Added to step 9's light-mode L */
32
+ light9?: number;
33
+ /** Added to step 9's dark-mode L */
34
+ dark9?: number;
35
+ /** Added to step 10's dark-mode L */
36
+ dark10?: number;
37
+ };
15
38
  }
16
39
  export interface ScaleStep {
17
40
  /** 1-based step number */
@@ -24,10 +47,20 @@ export interface Scale {
24
47
  dark: ScaleStep[];
25
48
  }
26
49
  export declare function generateScale(options: ScaleOptions): Scale;
50
+ /**
51
+ * Ramp seeds. The five ramps that back semantic intents carry `correction`
52
+ * values on steps 9/10 — the solid fills that sit under text. Verified against
53
+ * `primitives.css` by `figma-plugin/verify-parity.mjs`; without them the
54
+ * generator does not reproduce the shipped palette.
55
+ */
27
56
  export declare const BRAND_PALETTES: {
28
57
  readonly pink: {
29
58
  readonly hue: 360;
30
59
  readonly peakChroma: 0.19;
60
+ readonly correction: {
61
+ readonly dark9: -0.09;
62
+ readonly dark10: -0.09;
63
+ };
31
64
  };
32
65
  readonly purple: {
33
66
  readonly hue: 300;
@@ -37,14 +70,26 @@ export declare const BRAND_PALETTES: {
37
70
  readonly hue: 350;
38
71
  readonly peakChroma: 0.01;
39
72
  readonly isNeutral: true;
73
+ readonly correction: {
74
+ readonly light9: -0.01;
75
+ };
40
76
  };
41
77
  readonly red: {
42
78
  readonly hue: 25;
43
79
  readonly peakChroma: 0.18;
80
+ readonly correction: {
81
+ readonly dark9: -0.09;
82
+ readonly dark10: -0.09;
83
+ };
44
84
  };
45
85
  readonly green: {
46
86
  readonly hue: 145;
47
87
  readonly peakChroma: 0.14;
88
+ readonly correction: {
89
+ readonly light9: -0.03;
90
+ readonly dark9: -0.09;
91
+ readonly dark10: -0.09;
92
+ };
48
93
  };
49
94
  readonly yellow: {
50
95
  readonly hue: 85;
@@ -53,6 +98,10 @@ export declare const BRAND_PALETTES: {
53
98
  readonly blue: {
54
99
  readonly hue: 240;
55
100
  readonly peakChroma: 0.12;
101
+ readonly correction: {
102
+ readonly dark9: -0.09;
103
+ readonly dark10: -0.09;
104
+ };
56
105
  };
57
106
  readonly teal: {
58
107
  readonly hue: 175;
@@ -1 +1 @@
1
- {"version":3,"file":"generate-scale.d.ts","sourceRoot":"","sources":["../../src/tokens/generate-scale.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,IAAI,EAAE,SAAS,EAAE,CAAA;CAClB;AAyGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAsC1D;AAMD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAesB,CAAA"}
1
+ {"version":3,"file":"generate-scale.d.ts","sourceRoot":"","sources":["../../src/tokens/generate-scale.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE;QACX,qCAAqC;QACrC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,oCAAoC;QACpC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,qCAAqC;QACrC,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF;AAED,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,IAAI,EAAE,SAAS,EAAE,CAAA;CAClB;AAyGD,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CA0C1D;AAMD;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAesB,CAAA"}
@@ -51,24 +51,26 @@
51
51
  --neutral-0: #ffffff;
52
52
  /* Sunken well. Chroma follows the neutral cadence (was H:360 C:0.008 — an
53
53
  outlier that read visibly pink on sunken surfaces). */
54
- --color-surface-0: oklch(0.945 0.0013 350);
55
- --neutral-1: oklch(0.99 0.0003 350);
56
- --neutral-2: oklch(0.97 0.0008 350);
57
- --neutral-3: oklch(0.93 0.0018 350);
58
- --neutral-4: oklch(0.89 0.0029 350);
59
- --neutral-5: oklch(0.84 0.0042 350);
60
- --neutral-6: oklch(0.78 0.0053 350);
61
- --neutral-7: oklch(0.7 0.0074 350);
62
- --neutral-8: oklch(0.62 0.0089 350);
54
+ /* Sunken wells. Sits below the page and clears panel-hover; see
55
+ docs/plans/2026-08-26-surface-model-rebuild.md */
56
+ --neutral-sunken: oklch(0.95 0 0);
57
+ --neutral-1: oklch(0.99 0 0);
58
+ --neutral-2: oklch(0.97 0 0);
59
+ --neutral-3: oklch(0.93 0 0);
60
+ --neutral-4: oklch(0.89 0 0);
61
+ --neutral-5: oklch(0.84 0 0);
62
+ --neutral-6: oklch(0.78 0 0);
63
+ --neutral-7: oklch(0.7 0 0);
64
+ --neutral-8: oklch(0.62 0 0);
63
65
  /* 0.55 → 0.54: at 0.55 this measured 4.472:1 on --neutral-2 (the light
64
66
  surface-base), just under WCAG AA's 4.5 for normal text. 0.54 gives
65
67
  4.664:1. The intermediate 0.545 also clears at 4.567, but with only
66
68
  0.067 of headroom — a later tweak to surface-base would silently push it
67
69
  back under, which is how the previous attempt at this fix landed short. */
68
- --neutral-9: oklch(0.54 0.01 350);
69
- --neutral-10: oklch(0.5 0.01 350);
70
- --neutral-11: oklch(0.43 0.0074 350);
71
- --neutral-12: oklch(0.32 0.0042 350);
70
+ --neutral-9: oklch(0.54 0 0);
71
+ --neutral-10: oklch(0.5 0 0);
72
+ --neutral-11: oklch(0.43 0 0);
73
+ --neutral-12: oklch(0.32 0 0);
72
74
 
73
75
  /* ── Red — H:25 ── */
74
76
  --red-1: oklch(0.99 0.0047 25);
@@ -271,19 +273,20 @@
271
273
  /* ── Neutral — Warm grays — H:350 ── */
272
274
  /* Sunken well (deep). Chroma follows the neutral cadence — was H:360 C:0.008,
273
275
  an outlier that read tinted rather than neutral. */
274
- --color-surface-0: oklch(0.07 0.0004 350);
275
- --neutral-1: oklch(0.11 0.0002 350);
276
- --neutral-2: oklch(0.17 0.0007 350);
277
- --neutral-3: oklch(0.23 0.0019 350);
278
- --neutral-4: oklch(0.29 0.0029 350);
279
- --neutral-5: oklch(0.34 0.0038 350);
280
- --neutral-6: oklch(0.38 0.0048 350);
281
- --neutral-7: oklch(0.44 0.0062 350);
282
- --neutral-8: oklch(0.53 0.0086 350);
283
- --neutral-9: oklch(0.63 0.01 350);
284
- --neutral-10: oklch(0.58 0.01 350);
285
- --neutral-11: oklch(0.76 0.0062 350);
286
- --neutral-12: oklch(0.88 0.0024 350);
276
+ /* Dark has no room below the page, so a well simply IS the page. */
277
+ --neutral-sunken: oklch(0.147 0 0);
278
+ --neutral-1: oklch(0.147 0 0);
279
+ --neutral-2: oklch(0.207 0 0);
280
+ --neutral-3: oklch(0.267 0 0);
281
+ --neutral-4: oklch(0.327 0 0);
282
+ --neutral-5: oklch(0.377 0 0);
283
+ --neutral-6: oklch(0.417 0 0);
284
+ --neutral-7: oklch(0.477 0 0);
285
+ --neutral-8: oklch(0.567 0 0);
286
+ --neutral-9: oklch(0.667 0 0);
287
+ --neutral-10: oklch(0.617 0 0);
288
+ --neutral-11: oklch(0.797 0 0);
289
+ --neutral-12: oklch(0.917 0 0);
287
290
 
288
291
  /* ── Red — H:25 ── */
289
292
  --red-1: oklch(0.11 0.0047 25);