@devalok/shilp-sutra 0.49.4 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/AGENTS.md +12 -4
  2. package/MIGRATION.md +6 -0
  3. package/dist/_chunks/badge-group.js +12 -11
  4. package/dist/_chunks/badge-group.js.map +1 -1
  5. package/dist/_chunks/block-shell.js +1 -1
  6. package/dist/_chunks/block-shell.js.map +1 -1
  7. package/dist/_chunks/chat.js +18 -18
  8. package/dist/_chunks/chat.js.map +1 -1
  9. package/dist/_chunks/document-preview.js +1 -1
  10. package/dist/_chunks/document-preview.js.map +1 -1
  11. package/dist/_chunks/emoji-suggestion.js +1 -1
  12. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  13. package/dist/_chunks/grid-lines.js +2 -2
  14. package/dist/_chunks/grid-lines.js.map +1 -1
  15. package/dist/_chunks/image-preview.js +1 -1
  16. package/dist/_chunks/image-preview.js.map +1 -1
  17. package/dist/_chunks/mention-suggestion.js +2 -2
  18. package/dist/_chunks/mention-suggestion.js.map +1 -1
  19. package/dist/_chunks/oauth-button.js +8 -8
  20. package/dist/_chunks/oauth-button.js.map +1 -1
  21. package/dist/_chunks/shared.js +2 -2
  22. package/dist/_chunks/shared.js.map +1 -1
  23. package/dist/_chunks/success.js +5 -5
  24. package/dist/_chunks/success.js.map +1 -1
  25. package/dist/_chunks/tiptap.js +3956 -1796
  26. package/dist/_chunks/tiptap.js.map +1 -1
  27. package/dist/_chunks/tooltip.js +2 -2
  28. package/dist/_chunks/tooltip.js.map +1 -1
  29. package/dist/_chunks/tree-view.js +2 -2
  30. package/dist/_chunks/tree-view.js.map +1 -1
  31. package/dist/_chunks/use-calendar.js +3 -3
  32. package/dist/_chunks/use-calendar.js.map +1 -1
  33. package/dist/ai/block-renderer.js +1 -1
  34. package/dist/ai/block-renderer.js.map +1 -1
  35. package/dist/ai/blocks/error.js +1 -1
  36. package/dist/ai/blocks/error.js.map +1 -1
  37. package/dist/ai/command-bar.js +15 -15
  38. package/dist/ai/command-bar.js.map +1 -1
  39. package/dist/ai/conversation.js +6 -6
  40. package/dist/ai/conversation.js.map +1 -1
  41. package/dist/composed/activity-feed.js +4 -4
  42. package/dist/composed/activity-feed.js.map +1 -1
  43. package/dist/composed/avatar-group.js +6 -6
  44. package/dist/composed/avatar-group.js.map +1 -1
  45. package/dist/composed/bulk-action-bar.js +1 -1
  46. package/dist/composed/bulk-action-bar.js.map +1 -1
  47. package/dist/composed/command-palette.js +12 -12
  48. package/dist/composed/command-palette.js.map +1 -1
  49. package/dist/composed/content-card.js +1 -1
  50. package/dist/composed/content-card.js.map +1 -1
  51. package/dist/composed/date-picker/index.js +17 -17
  52. package/dist/composed/date-picker/index.js.map +1 -1
  53. package/dist/composed/deadline-indicator.js +2 -2
  54. package/dist/composed/deadline-indicator.js.map +1 -1
  55. package/dist/composed/diff.d.ts +108 -0
  56. package/dist/composed/diff.d.ts.map +1 -0
  57. package/dist/composed/diff.js +1004 -0
  58. package/dist/composed/diff.js.map +1 -0
  59. package/dist/composed/emoji-picker.js +6 -6
  60. package/dist/composed/emoji-picker.js.map +1 -1
  61. package/dist/composed/empty-state.js +2 -2
  62. package/dist/composed/empty-state.js.map +1 -1
  63. package/dist/composed/error-boundary.js +6 -6
  64. package/dist/composed/error-boundary.js.map +1 -1
  65. package/dist/composed/file-preview.js +5 -5
  66. package/dist/composed/file-preview.js.map +1 -1
  67. package/dist/composed/filter-bar.js +3 -3
  68. package/dist/composed/filter-bar.js.map +1 -1
  69. package/dist/composed/form-section.js +2 -2
  70. package/dist/composed/form-section.js.map +1 -1
  71. package/dist/composed/inline-edit.d.ts +1 -1
  72. package/dist/composed/inline-edit.d.ts.map +1 -1
  73. package/dist/composed/inline-edit.js.map +1 -1
  74. package/dist/composed/loading-skeleton.js +3 -3
  75. package/dist/composed/loading-skeleton.js.map +1 -1
  76. package/dist/composed/markdown-viewer.js +11 -11
  77. package/dist/composed/markdown-viewer.js.map +1 -1
  78. package/dist/composed/master-detail.js +1 -1
  79. package/dist/composed/master-detail.js.map +1 -1
  80. package/dist/composed/member-picker.js +2 -2
  81. package/dist/composed/member-picker.js.map +1 -1
  82. package/dist/composed/multi-select-popover.js +5 -5
  83. package/dist/composed/multi-select-popover.js.map +1 -1
  84. package/dist/composed/page-header.js +3 -3
  85. package/dist/composed/page-header.js.map +1 -1
  86. package/dist/composed/page-skeletons.js +4 -4
  87. package/dist/composed/page-skeletons.js.map +1 -1
  88. package/dist/composed/priority-indicator.js +1 -1
  89. package/dist/composed/priority-indicator.js.map +1 -1
  90. package/dist/composed/responsive-modal.js +2 -2
  91. package/dist/composed/responsive-modal.js.map +1 -1
  92. package/dist/composed/rich-chat-input.js +27 -27
  93. package/dist/composed/rich-chat-input.js.map +1 -1
  94. package/dist/composed/rich-text-editor.d.ts +81 -6
  95. package/dist/composed/rich-text-editor.d.ts.map +1 -1
  96. package/dist/composed/rich-text-editor.js +365 -210
  97. package/dist/composed/rich-text-editor.js.map +1 -1
  98. package/dist/composed/schedule-view.js +4 -4
  99. package/dist/composed/schedule-view.js.map +1 -1
  100. package/dist/shell/bottom-navbar.js +4 -4
  101. package/dist/shell/bottom-navbar.js.map +1 -1
  102. package/dist/shell/notification-center.js +13 -13
  103. package/dist/shell/notification-center.js.map +1 -1
  104. package/dist/shell/notification-preferences.js +10 -10
  105. package/dist/shell/notification-preferences.js.map +1 -1
  106. package/dist/shell/sidebar.js +9 -9
  107. package/dist/shell/sidebar.js.map +1 -1
  108. package/dist/shell/top-bar.js +8 -8
  109. package/dist/shell/top-bar.js.map +1 -1
  110. package/dist/tokens/semantic.css +6 -1
  111. package/dist/tokens/typography-semantic.css +6 -6
  112. package/dist/tokens/utilities.css +8 -0
  113. package/dist/ui/accordion.js +2 -2
  114. package/dist/ui/accordion.js.map +1 -1
  115. package/dist/ui/alert-dialog.js +4 -4
  116. package/dist/ui/alert-dialog.js.map +1 -1
  117. package/dist/ui/alert.js +1 -1
  118. package/dist/ui/alert.js.map +1 -1
  119. package/dist/ui/autocomplete.d.ts.map +1 -1
  120. package/dist/ui/autocomplete.js +11 -3
  121. package/dist/ui/autocomplete.js.map +1 -1
  122. package/dist/ui/avatar.js +5 -5
  123. package/dist/ui/avatar.js.map +1 -1
  124. package/dist/ui/badge-group.d.ts +2 -6
  125. package/dist/ui/badge-group.d.ts.map +1 -1
  126. package/dist/ui/badge-indicator.d.ts +2 -6
  127. package/dist/ui/badge-indicator.d.ts.map +1 -1
  128. package/dist/ui/badge-indicator.js +19 -18
  129. package/dist/ui/badge-indicator.js.map +1 -1
  130. package/dist/ui/badge.d.ts +2 -4
  131. package/dist/ui/badge.d.ts.map +1 -1
  132. package/dist/ui/banner.js +1 -1
  133. package/dist/ui/banner.js.map +1 -1
  134. package/dist/ui/breadcrumb.js +1 -1
  135. package/dist/ui/breadcrumb.js.map +1 -1
  136. package/dist/ui/button.js +7 -7
  137. package/dist/ui/button.js.map +1 -1
  138. package/dist/ui/card.d.ts +1 -1
  139. package/dist/ui/card.js +1 -1
  140. package/dist/ui/card.js.map +1 -1
  141. package/dist/ui/charts/gauge-chart.js +2 -2
  142. package/dist/ui/charts/gauge-chart.js.map +1 -1
  143. package/dist/ui/charts/pie-chart.js +1 -1
  144. package/dist/ui/charts/pie-chart.js.map +1 -1
  145. package/dist/ui/charts/radar-chart.d.ts +19 -1
  146. package/dist/ui/charts/radar-chart.d.ts.map +1 -1
  147. package/dist/ui/charts/radar-chart.js +80 -42
  148. package/dist/ui/charts/radar-chart.js.map +1 -1
  149. package/dist/ui/code.js +2 -2
  150. package/dist/ui/code.js.map +1 -1
  151. package/dist/ui/color-input.d.ts.map +1 -1
  152. package/dist/ui/color-input.js +164 -157
  153. package/dist/ui/color-input.js.map +1 -1
  154. package/dist/ui/combobox.d.ts.map +1 -1
  155. package/dist/ui/combobox.js +143 -142
  156. package/dist/ui/combobox.js.map +1 -1
  157. package/dist/ui/context-menu.js +6 -6
  158. package/dist/ui/context-menu.js.map +1 -1
  159. package/dist/ui/data-table-body.js +1 -1
  160. package/dist/ui/data-table-body.js.map +1 -1
  161. package/dist/ui/data-table-bulk-actions.js +1 -1
  162. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  163. package/dist/ui/data-table-card.js +1 -1
  164. package/dist/ui/data-table-card.js.map +1 -1
  165. package/dist/ui/data-table-header.js +1 -1
  166. package/dist/ui/data-table-header.js.map +1 -1
  167. package/dist/ui/data-table-pagination.js +3 -3
  168. package/dist/ui/data-table-pagination.js.map +1 -1
  169. package/dist/ui/data-table-toolbar.js +1 -1
  170. package/dist/ui/data-table-toolbar.js.map +1 -1
  171. package/dist/ui/data-table.js +1 -1
  172. package/dist/ui/data-table.js.map +1 -1
  173. package/dist/ui/dialog.js +2 -2
  174. package/dist/ui/dialog.js.map +1 -1
  175. package/dist/ui/dot.js +4 -4
  176. package/dist/ui/dot.js.map +1 -1
  177. package/dist/ui/dropdown-menu.js +6 -6
  178. package/dist/ui/dropdown-menu.js.map +1 -1
  179. package/dist/ui/file-upload.js +5 -5
  180. package/dist/ui/file-upload.js.map +1 -1
  181. package/dist/ui/form.js +1 -1
  182. package/dist/ui/form.js.map +1 -1
  183. package/dist/ui/input-otp.d.ts.map +1 -1
  184. package/dist/ui/input-otp.js +3 -3
  185. package/dist/ui/input-otp.js.map +1 -1
  186. package/dist/ui/input.js +8 -8
  187. package/dist/ui/input.js.map +1 -1
  188. package/dist/ui/label.js +1 -1
  189. package/dist/ui/label.js.map +1 -1
  190. package/dist/ui/lib/utils.d.ts.map +1 -1
  191. package/dist/ui/lib/utils.js +42 -13
  192. package/dist/ui/lib/utils.js.map +1 -1
  193. package/dist/ui/menubar.js +7 -7
  194. package/dist/ui/menubar.js.map +1 -1
  195. package/dist/ui/navigation-menu.js +1 -1
  196. package/dist/ui/navigation-menu.js.map +1 -1
  197. package/dist/ui/number-input.d.ts.map +1 -1
  198. package/dist/ui/number-input.js +6 -5
  199. package/dist/ui/number-input.js.map +1 -1
  200. package/dist/ui/pagination.js +1 -1
  201. package/dist/ui/pagination.js.map +1 -1
  202. package/dist/ui/progress-ring.js +3 -3
  203. package/dist/ui/progress-ring.js.map +1 -1
  204. package/dist/ui/progress.js +2 -2
  205. package/dist/ui/progress.js.map +1 -1
  206. package/dist/ui/segmented-control.js +3 -3
  207. package/dist/ui/segmented-control.js.map +1 -1
  208. package/dist/ui/select.d.ts.map +1 -1
  209. package/dist/ui/select.js +7 -6
  210. package/dist/ui/select.js.map +1 -1
  211. package/dist/ui/sheet.js +2 -2
  212. package/dist/ui/sheet.js.map +1 -1
  213. package/dist/ui/sidebar.js +7 -7
  214. package/dist/ui/sidebar.js.map +1 -1
  215. package/dist/ui/split-button.js +6 -6
  216. package/dist/ui/split-button.js.map +1 -1
  217. package/dist/ui/stat-card.d.ts +1 -1
  218. package/dist/ui/stat-card.js +7 -7
  219. package/dist/ui/stat-card.js.map +1 -1
  220. package/dist/ui/stepper.js +3 -3
  221. package/dist/ui/stepper.js.map +1 -1
  222. package/dist/ui/table.js +3 -3
  223. package/dist/ui/table.js.map +1 -1
  224. package/dist/ui/tabs.js +3 -3
  225. package/dist/ui/tabs.js.map +1 -1
  226. package/dist/ui/text.d.ts.map +1 -1
  227. package/dist/ui/text.js.map +1 -1
  228. package/dist/ui/textarea.d.ts.map +1 -1
  229. package/dist/ui/textarea.js +7 -6
  230. package/dist/ui/textarea.js.map +1 -1
  231. package/dist/ui/toast.js +11 -11
  232. package/dist/ui/toast.js.map +1 -1
  233. package/dist/ui/toggle.js +3 -3
  234. package/dist/ui/toggle.js.map +1 -1
  235. package/dist/ui/tooltip.js +1 -1
  236. package/dist/ui/tooltip.js.map +1 -1
  237. package/docs/components/composed/diff.md +50 -0
  238. package/docs/components/ui/data-table.md +2 -2
  239. package/llms.txt +2 -1
  240. package/mcp-manifest.json +147 -3
  241. package/package.json +8 -1
  242. package/skill/SKILL.md +1 -1
  243. package/skill/references/components.md +2 -1
package/dist/ui/toast.js CHANGED
@@ -143,11 +143,11 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
143
143
  className: "min-w-0 flex-1",
144
144
  children: [
145
145
  o && /* @__PURE__ */ c("p", {
146
- className: "text-ds-md font-semibold text-surface-fg",
146
+ className: "text-body-md font-semibold text-surface-fg",
147
147
  children: o
148
148
  }),
149
149
  u && /* @__PURE__ */ c("p", {
150
- className: n("text-ds-sm text-surface-fg-muted", o && "mt-0.5"),
150
+ className: n("text-body-sm text-surface-fg-muted", o && "mt-0.5"),
151
151
  children: u
152
152
  }),
153
153
  (d || f) && /* @__PURE__ */ l("div", {
@@ -155,12 +155,12 @@ function k({ type: a, title: o, description: u, action: d, cancel: f, duration:
155
155
  children: [d && /* @__PURE__ */ c("button", {
156
156
  type: "button",
157
157
  onClick: d.onClick,
158
- className: "text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
158
+ className: "text-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
159
159
  children: d.label
160
160
  }), f && /* @__PURE__ */ c("button", {
161
161
  type: "button",
162
162
  onClick: f.onClick,
163
- className: "text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
163
+ className: "text-body-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner",
164
164
  children: f.label
165
165
  })]
166
166
  })
@@ -302,7 +302,7 @@ function F({ file: n, onRetry: o, onRemove: s }) {
302
302
  })
303
303
  }),
304
304
  /* @__PURE__ */ c("span", {
305
- className: "min-w-0 flex-1 truncate text-ds-xs text-surface-fg",
305
+ className: "min-w-0 flex-1 truncate text-body-xs text-surface-fg",
306
306
  children: n.name
307
307
  }),
308
308
  p ? /* @__PURE__ */ l("div", {
@@ -313,17 +313,17 @@ function F({ file: n, onRetry: o, onRemove: s }) {
313
313
  value: N(n),
314
314
  className: "flex-1"
315
315
  }), n.progress !== void 0 && /* @__PURE__ */ l("span", {
316
- className: "shrink-0 text-ds-xs tabular-nums text-surface-fg-muted",
316
+ className: "shrink-0 text-caption tabular-nums text-surface-fg-muted",
317
317
  children: [n.progress, "%"]
318
318
  })]
319
319
  }) : n.status === "complete" ? /* @__PURE__ */ c("span", {
320
- className: "text-ds-xs text-success-11",
320
+ className: "text-body-xs text-success-11",
321
321
  children: "Done"
322
322
  }) : n.status === "error" ? /* @__PURE__ */ c("span", {
323
- className: "max-w-[60px] truncate text-ds-xs text-error-11",
323
+ className: "max-w-[60px] truncate text-body-xs text-error-11",
324
324
  children: n.error || "Failed"
325
325
  }) : /* @__PURE__ */ c("span", {
326
- className: "text-ds-xs text-surface-fg-muted",
326
+ className: "text-caption text-surface-fg-muted",
327
327
  children: P(n.size)
328
328
  }),
329
329
  n.status === "error" && o && /* @__PURE__ */ c("button", {
@@ -435,12 +435,12 @@ function I({ files: r, onRetry: a, onRemove: o, selfDismissId: d, showAccent: p
435
435
  })
436
436
  }, "upload-icon")
437
437
  }), /* @__PURE__ */ c("p", {
438
- className: "text-ds-md font-semibold text-surface-fg",
438
+ className: "text-body-md font-semibold text-surface-fg",
439
439
  children: T ? w > 0 ? `${b} of ${r.length} uploaded` : `${r.length} file${r.length > 1 ? "s" : ""} uploaded` : `Uploading ${r.length} file${r.length > 1 ? "s" : ""}`
440
440
  })]
441
441
  }),
442
442
  !T && /* @__PURE__ */ l("p", {
443
- className: "mt-0.5 text-ds-xs text-surface-fg-muted",
443
+ className: "mt-0.5 text-caption text-surface-fg-muted",
444
444
  children: [
445
445
  b,
446
446
  " of ",
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-ds-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-ds-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-ds-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-ds-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-ds-xs tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-ds-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-ds-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-ds-xs text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-ds-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-ds-xs text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../../src/ui/toast.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlertCircle,\n IconAlertTriangle,\n IconCheck,\n IconCircleCheck,\n IconCircleX,\n IconFile,\n IconInfoCircle,\n IconPhoto,\n IconRefresh,\n IconUpload,\n IconX,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\nimport { type ExternalToast,toast as sonnerToast } from 'sonner'\n\nimport { Icon } from './icon'\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\nimport { Progress } from './progress'\nimport { Spinner } from './spinner'\nimport { assertToasterMounted } from './toast-registry'\nimport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n\n/* ---------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------ */\n\nconst DEFAULT_DURATION = 5000\nconst UNDO_DURATION = 8000\nconst UPLOAD_COMPLETE_DELAY = 3000\n\n/* ---------------------------------------------------------------------------\n * Accent bar & icon config per toast type\n * ------------------------------------------------------------------------ */\n\nconst TOAST_TYPE_CONFIG: Record<\n ToastType,\n {\n accentClass: string\n iconClass: string\n icon: React.ForwardRefExoticComponent<any> | null\n timerBarClass: string\n }\n> = {\n message: {\n accentClass: '',\n iconClass: '',\n icon: null,\n timerBarClass: 'bg-surface-border',\n },\n success: {\n accentClass: 'bg-success-9',\n iconClass: 'text-success-11',\n icon: IconCircleCheck,\n timerBarClass: 'bg-success-9',\n },\n error: {\n accentClass: 'bg-error-9',\n iconClass: 'text-error-11',\n icon: IconCircleX,\n timerBarClass: 'bg-error-9',\n },\n warning: {\n accentClass: 'bg-warning-9',\n iconClass: 'text-warning-11',\n icon: IconAlertTriangle,\n timerBarClass: 'bg-warning-9',\n },\n info: {\n accentClass: 'bg-info-9',\n iconClass: 'text-info-11',\n icon: IconInfoCircle,\n timerBarClass: 'bg-info-9',\n },\n loading: {\n accentClass: 'bg-accent-9',\n iconClass: 'text-accent-11',\n icon: null,\n timerBarClass: 'bg-accent-9',\n },\n}\n\n/* ---------------------------------------------------------------------------\n * Timer bar — shows auto-dismiss countdown\n * ------------------------------------------------------------------------ */\n\nfunction TimerBar({\n duration,\n type,\n paused,\n}: {\n duration: number\n type: ToastType\n paused: boolean\n}) {\n const config = TOAST_TYPE_CONFIG[type]\n return (\n <div className=\"absolute bottom-0 left-0 right-0 h-ds-01 overflow-hidden\">\n <div\n className={cn(\n 'h-full w-full origin-left opacity-30',\n config.timerBarClass,\n 'motion-safe:animate-timer-bar',\n )}\n style={{\n animationDuration: `${duration}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }}\n />\n </div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Base toast UI — used by all typed toasts\n * ------------------------------------------------------------------------ */\n\nfunction ToastContent({\n type,\n title,\n description,\n action,\n cancel,\n duration = DEFAULT_DURATION,\n showTimerBar = true,\n showAccent = false,\n selfDismissId,\n}: {\n type: ToastType\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionOptions\n cancel?: ToastActionOptions\n duration?: number\n showTimerBar?: boolean\n /** Render the colored left accent bar. Off by default (status = icon + timer bar). */\n showAccent?: boolean\n /** When set, this component manages its own dismiss timer (hover-aware). */\n selfDismissId?: string | number\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const config = TOAST_TYPE_CONFIG[type]\n const StatusIcon = config.icon\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(duration)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || duration === Infinity) return\n if (paused) {\n // Pause: save remaining time\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n // Start/resume timer\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, duration, paused])\n\n // Error toasts announce assertively so screen readers interrupt;\n // warnings stay polite but atomically re-read the message.\n const isUrgent = type === 'error'\n return (\n <motion.div\n layout=\"position\"\n role={isUrgent ? 'alert' : 'status'}\n aria-live={isUrgent ? 'assertive' : 'polite'}\n aria-atomic=\"true\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint on error only — the one you must not miss.\n type === 'error' && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent (status is carried by icon + timer bar) */}\n {showAccent && config.accentClass && (\n <div\n className={cn('w-1 shrink-0 rounded-l-overlay-sm', config.accentClass)}\n />\n )}\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 items-start gap-ds-03 p-ds-04\">\n {/* Status icon — animates between spinner ↔ typed icon */}\n <AnimatePresence mode=\"wait\">\n {type === 'loading' ? (\n <motion.div\n key=\"loading\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n className=\"mt-0.5 shrink-0\"\n >\n <Spinner size=\"sm\" className=\"h-4 w-4\" />\n </motion.div>\n ) : StatusIcon ? (\n <motion.div\n key={type}\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n className=\"mt-0.5 shrink-0\"\n >\n <Icon icon={StatusIcon} size=\"sm\" className={config.iconClass} />\n </motion.div>\n ) : null}\n </AnimatePresence>\n\n {/* Text */}\n <div className=\"min-w-0 flex-1\">\n {title && (\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {title}\n </p>\n )}\n {description && (\n <p\n className={cn(\n 'text-body-sm text-surface-fg-muted',\n title && 'mt-0.5',\n )}\n >\n {description}\n </p>\n )}\n\n {/* Action / cancel buttons */}\n {(action || cancel) && (\n <div className=\"mt-ds-03 flex items-center gap-ds-03\">\n {action && (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"text-body-sm font-medium text-accent-11 underline-offset-2 hover:underline hover:bg-surface-raised-hover rounded-control-inner px-ds-02 py-ds-01 transition-[color,background-color] duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {action.label}\n </button>\n )}\n {cancel && (\n <button\n type=\"button\"\n onClick={cancel.onClick}\n className=\"text-body-sm text-surface-fg-muted hover:text-surface-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:rounded-control-inner\"\n >\n {cancel.label}\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Timer bar */}\n {showTimerBar && type !== 'loading' && (\n <TimerBar duration={duration} type={type} paused={paused} />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Upload toast UI\n * ------------------------------------------------------------------------ */\n\nconst IMAGE_EXTENSIONS = new Set([\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif',\n])\n\nfunction isImageFile(file: UploadFile): boolean {\n if (file.previewUrl) return true\n const ext = file.name.split('.').pop()?.toLowerCase() ?? ''\n return IMAGE_EXTENSIONS.has(ext)\n}\n\nfunction getProgressColor(\n status: UploadFile['status'],\n): 'accent' | 'success' | 'error' {\n switch (status) {\n case 'complete':\n return 'success'\n case 'error':\n return 'error'\n default:\n return 'accent'\n }\n}\n\nfunction getProgressValue(file: UploadFile): number | undefined {\n switch (file.status) {\n case 'pending':\n return 0\n case 'uploading':\n return file.progress ?? 0\n case 'processing':\n return undefined\n case 'complete':\n return 100\n case 'error':\n return file.progress ?? 0\n }\n}\n\nfunction formatFileSize(bytes: number): string {\n if (!bytes || bytes < 0 || !Number.isFinite(bytes)) return '0 B'\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`\n}\n\nfunction UploadFileRow({\n file,\n onRetry,\n onRemove,\n}: {\n file: UploadFile\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n}) {\n const isTerminal = file.status === 'complete' || file.status === 'error'\n const isUploading =\n file.status === 'uploading' || file.status === 'processing'\n\n return (\n <motion.div\n layout\n className=\"flex items-center gap-ds-02 py-1\"\n initial={false}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, x: -20, height: 0 }}\n transition={springs.snappy}\n >\n {/* File icon — animates between states */}\n <div className=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <AnimatePresence mode=\"wait\">\n {file.status === 'complete' ? (\n <motion.div\n key=\"complete\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"xs\" className=\"text-success-11\" />\n </motion.div>\n ) : file.status === 'error' ? (\n <motion.div\n key=\"error\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={tweens.fade}\n >\n <Icon icon={IconAlertCircle} size=\"xs\" className=\"text-error-11\" />\n </motion.div>\n ) : file.status === 'processing' ? (\n <motion.div\n key=\"processing\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Spinner size=\"sm\" className=\"h-3.5 w-3.5\" />\n </motion.div>\n ) : isImageFile(file) ? (\n <motion.div\n key=\"image\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconPhoto} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"file\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconFile} size=\"xs\" className=\"text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n {/* Filename */}\n <span className=\"min-w-0 flex-1 truncate text-body-xs text-surface-fg\">\n {file.name}\n </span>\n\n {/* Progress bar or status */}\n {isUploading ? (\n <div className=\"flex w-16 items-center gap-1\">\n <Progress\n size=\"sm\"\n color={getProgressColor(file.status)}\n value={getProgressValue(file)}\n className=\"flex-1\"\n />\n {file.progress !== undefined && (\n <span className=\"shrink-0 text-caption tabular-nums text-surface-fg-muted\">\n {file.progress}%\n </span>\n )}\n </div>\n ) : file.status === 'complete' ? (\n <span className=\"text-body-xs text-success-11\">Done</span>\n ) : file.status === 'error' ? (\n <span className=\"max-w-[60px] truncate text-body-xs text-error-11\">\n {file.error || 'Failed'}\n </span>\n ) : (\n <span className=\"text-caption text-surface-fg-muted\">\n {formatFileSize(file.size)}\n </span>\n )}\n\n {/* Actions */}\n {file.status === 'error' && onRetry && (\n <button\n type=\"button\"\n onClick={() => onRetry(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Retry ${file.name}`}\n title=\"Retry\"\n >\n <IconRefresh className=\"h-3.5 w-3.5\" />\n </button>\n )}\n {!isTerminal && onRemove && (\n <button\n type=\"button\"\n onClick={() => onRemove(file.id)}\n className=\"touch-target flex min-h-6 min-w-6 items-center justify-center rounded-control-inner text-surface-fg-muted hover:text-surface-fg\"\n aria-label={`Cancel ${file.name}`}\n title=\"Remove\"\n >\n <IconX className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </motion.div>\n )\n}\n\nfunction UploadToastContent({\n files,\n onRetry,\n onRemove,\n selfDismissId,\n showAccent = false,\n}: {\n files: UploadFile[]\n onRetry?: (fileId: string) => void\n onRemove?: (fileId: string) => void\n selfDismissId?: string | number\n /** Render the colored left accent bar. Off by default. */\n showAccent?: boolean\n}) {\n const [hovered, setHovered] = React.useState(false)\n const [focused, setFocused] = React.useState(false)\n const paused = hovered || focused\n const completeCount = files.filter((f) => f.status === 'complete').length\n const errorCount = files.filter((f) => f.status === 'error').length\n const allTerminal = files.every(\n (f) => f.status === 'complete' || f.status === 'error',\n )\n\n // Self-managed dismiss timer that pauses on hover or keyboard focus\n const remainingRef = React.useRef(UPLOAD_COMPLETE_DELAY)\n const startTimeRef = React.useRef(0)\n\n React.useEffect(() => {\n if (selfDismissId == null || !allTerminal) return\n if (paused) {\n const elapsed = Date.now() - startTimeRef.current\n remainingRef.current = Math.max(0, remainingRef.current - elapsed)\n return\n }\n startTimeRef.current = Date.now()\n const timer = setTimeout(() => {\n sonnerToast.dismiss(selfDismissId)\n }, remainingRef.current)\n return () => clearTimeout(timer)\n }, [selfDismissId, allTerminal, paused])\n\n const accentClass = allTerminal\n ? errorCount > 0\n ? 'bg-error-7'\n : 'bg-success-7'\n : 'bg-accent-9'\n\n const timerBarType: ToastType = allTerminal\n ? errorCount > 0\n ? 'error'\n : 'success'\n : 'loading'\n\n const uploadUrgent = allTerminal && errorCount > 0\n return (\n <motion.div\n layout\n role={uploadUrgent ? 'alert' : 'status'}\n aria-live={uploadUrgent ? 'assertive' : 'polite'}\n aria-label=\"File uploads\"\n className={cn(\n 'group relative flex w-full overflow-hidden rounded-overlay-sm bg-surface-overlay shadow-floating',\n // Faint surface tint when the batch ended with failures.\n uploadUrgent && 'bg-error-2',\n )}\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onFocusCapture={() => setFocused(true)}\n onBlurCapture={() => setFocused(false)}\n transition={springs.smooth}\n >\n {/* Left accent bar — opt-in via showAccent */}\n {showAccent && (\n <div className={cn('w-1 shrink-0 rounded-l-overlay-sm', accentClass)} />\n )}\n\n {/* Content */}\n <div className=\"min-w-0 flex-1 p-ds-04\">\n {/* Header — icon morphs between upload ↔ result */}\n <div className=\"flex items-center gap-ds-02\">\n <AnimatePresence mode=\"wait\">\n {allTerminal ? (\n errorCount > 0 ? (\n <motion.div\n key=\"error-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconAlertCircle} size=\"sm\" className=\"shrink-0 text-error-11\" />\n </motion.div>\n ) : (\n <motion.div\n key=\"success-icon\"\n initial={{ opacity: 0, scale: 0.5 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={{ opacity: 0, scale: 0.5 }}\n transition={springs.bouncy}\n >\n <Icon icon={IconCheck} size=\"sm\" className=\"shrink-0 text-success-11\" />\n </motion.div>\n )\n ) : (\n <motion.div\n key=\"upload-icon\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={tweens.fade}\n >\n <Icon icon={IconUpload} size=\"sm\" className=\"shrink-0 text-surface-fg-muted\" />\n </motion.div>\n )}\n </AnimatePresence>\n <p className=\"text-body-md font-semibold text-surface-fg\">\n {allTerminal\n ? errorCount > 0\n ? `${completeCount} of ${files.length} uploaded`\n : `${files.length} file${files.length > 1 ? 's' : ''} uploaded`\n : `Uploading ${files.length} file${files.length > 1 ? 's' : ''}`}\n </p>\n </div>\n {!allTerminal && (\n <p className=\"mt-0.5 text-caption text-surface-fg-muted\">\n {completeCount} of {files.length} complete\n {errorCount > 0 && ` · ${errorCount} failed`}\n </p>\n )}\n\n {/* File list */}\n <div className=\"mt-ds-02 max-h-[140px] overflow-y-auto\">\n <AnimatePresence>\n {files.map((file) => (\n <UploadFileRow\n key={file.id}\n file={file}\n onRetry={onRetry}\n onRemove={onRemove}\n />\n ))}\n </AnimatePresence>\n </div>\n\n {/* Screen reader announcement */}\n <div className=\"sr-only\" aria-live=\"polite\">\n {completeCount} of {files.length} files uploaded\n {errorCount > 0 && `, ${errorCount} failed`}\n </div>\n </div>\n\n {/* Timer bar — only after all terminal */}\n {allTerminal && (\n <TimerBar\n duration={UPLOAD_COMPLETE_DELAY}\n type={timerBarType}\n paused={false}\n />\n )}\n </motion.div>\n )\n}\n\n/* ---------------------------------------------------------------------------\n * Toast API — wraps Sonner with our styled UI\n * ------------------------------------------------------------------------ */\n\nfunction createSonnerOptions(\n options?: ToastOptions,\n duration?: number,\n): ExternalToast {\n return {\n id: options?.id,\n duration: options?.duration ?? duration ?? DEFAULT_DURATION,\n unstyled: true,\n classNames: { toast: 'w-full' },\n }\n}\n\nfunction createTypedToast(\n type: ToastType,\n message: string | React.ReactNode,\n options?: ToastOptions,\n) {\n assertToasterMounted()\n const duration = options?.duration ?? DEFAULT_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n description={options?.description}\n action={options?.action}\n cancel={options?.cancel}\n duration={duration}\n showAccent={options?.showAccent}\n />\n ),\n createSonnerOptions(options, duration),\n )\n}\n\n/**\n * Toast API — imperative toast notifications with Sonner rendering engine.\n *\n * Mount `<Toaster />` once at your root layout. Then call `toast()` from anywhere.\n *\n * @example\n * toast('Something happened')\n * toast.success('Saved!')\n * toast.error('Failed to save')\n * toast.promise(saveFn(), { loading: 'Saving...', success: 'Done!', error: 'Failed' })\n * toast.undo('Deleted', { onUndo: () => restore() })\n * toast.upload({ files, onRetry, onRemove })\n */\nfunction toast(message: string | React.ReactNode, options?: ToastOptions) {\n return createTypedToast('message', message, options)\n}\n\ntoast.message = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('message', message, options)\n}\n\ntoast.success = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('success', message, options)\n}\n\ntoast.error = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('error', message, options)\n}\n\ntoast.warning = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('warning', message, options)\n}\n\ntoast.info = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('info', message, options)\n}\n\ntoast.loading = (\n message: string | React.ReactNode,\n options?: ToastOptions,\n) => {\n return createTypedToast('loading', message, {\n ...options,\n duration: options?.duration ?? Infinity,\n })\n}\n\ntoast.promise = <T,>(\n promise: Promise<T>,\n options: {\n loading: string | React.ReactNode\n success: string | React.ReactNode | ((data: T) => string | React.ReactNode)\n error:\n | string\n | React.ReactNode\n | ((error: unknown) => string | React.ReactNode)\n },\n) => {\n const id = sonnerToast.custom(\n () => (\n <ToastContent type=\"loading\" title={options.loading} showTimerBar={false} />\n ),\n { duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n\n const replaceWith = (type: ToastType, message: React.ReactNode) => {\n // Sonner doesn't reset its auto-dismiss timer when updating a custom toast\n // in-place from duration: Infinity, so ToastContent manages its own\n // hover-aware dismiss timer via selfDismissId.\n sonnerToast.custom(\n () => (\n <ToastContent\n type={type}\n title={message}\n duration={DEFAULT_DURATION}\n selfDismissId={id}\n />\n ),\n { id, duration: Infinity, unstyled: true, classNames: { toast: 'w-full' } },\n )\n }\n\n promise\n .then((data) => {\n const message =\n typeof options.success === 'function'\n ? options.success(data)\n : options.success\n replaceWith('success', message)\n })\n .catch((error) => {\n const message =\n typeof options.error === 'function'\n ? options.error(error)\n : options.error\n replaceWith('error', message)\n })\n\n return id\n}\n\ntoast.undo = (message: string | React.ReactNode, options: ToastUndoOptions) => {\n const duration = options.duration ?? UNDO_DURATION\n return sonnerToast.custom(\n () => (\n <ToastContent\n type=\"message\"\n title={message}\n description={options.description}\n action={{ label: 'Undo', onClick: options.onUndo }}\n duration={duration}\n />\n ),\n { duration, unstyled: true, classNames: { toast: 'w-full' } },\n )\n}\n\ntoast.upload = (options: ToastUploadOptions) => {\n // Pre-determine the id so we can pass it to UploadToastContent for self-dismiss.\n const toastId = options.id ?? String(Date.now())\n\n sonnerToast.custom(\n () => (\n <UploadToastContent\n files={options.files}\n onRetry={options.onRetry}\n onRemove={options.onRemove}\n selfDismissId={toastId}\n showAccent={options.showAccent}\n />\n ),\n {\n id: toastId,\n duration: Infinity,\n unstyled: true,\n classNames: { toast: 'w-full' },\n },\n )\n\n return toastId\n}\n\ntoast.custom = (\n render: (id: string | number) => React.ReactElement,\n options?: ExternalToast,\n) => {\n return sonnerToast.custom(render, {\n ...options,\n unstyled: true,\n classNames: { toast: 'w-full', ...(options?.classNames as Record<string, string>) },\n })\n}\n\ntoast.dismiss = (id?: string | number) => {\n if (id !== undefined) {\n sonnerToast.dismiss(id)\n } else {\n sonnerToast.dismiss()\n }\n}\n\n/* ---------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------ */\n\nexport type ToastProps = ToastOptions\n\nexport {\n formatFileSize,\n TimerBar,\n toast,\n ToastContent,\n UploadFileRow,\n UploadToastContent,\n}\nexport type {\n ToastActionOptions,\n ToastOptions,\n ToastType,\n ToastUndoOptions,\n ToastUploadOptions,\n UploadFile,\n} from './toast-types'\n"],"mappings":"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"}
package/dist/ui/toggle.js CHANGED
@@ -14,9 +14,9 @@ var c = s.create(e), l = r("inline-flex items-center justify-center gap-ds-03 ro
14
14
  outline: "border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong"
15
15
  },
16
16
  size: {
17
- sm: "h-ds-sm px-ds-03 text-ds-sm",
18
- md: "h-ds-md px-ds-04 text-ds-md",
19
- lg: "h-ds-lg px-ds-05 text-ds-base"
17
+ sm: "h-ds-sm px-ds-03 text-body-sm",
18
+ md: "h-ds-md px-ds-04 text-body-md",
19
+ lg: "h-ds-lg px-ds-05 text-body-lg"
20
20
  },
21
21
  color: {
22
22
  accent: "data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11",
@@ -1 +1 @@
1
- {"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-ds-sm',\n md: 'h-ds-md px-ds-04 text-ds-md',\n lg: 'h-ds-lg px-ds-05 text-ds-base',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,CAAoB,GAErD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;EACX;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;CACT;AACF,CACF,GAEM,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,IAAK;CACxB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;CAAM,CAAC,GAAG,CAAS;CACjE,GAAI,EAAY,CAAK;AACtB,CAAA,CACF;AACD,EAAO,cAAA,EAAmC"}
1
+ {"version":3,"file":"toggle.js","names":[],"sources":["../../src/ui/toggle.tsx"],"sourcesContent":["'use client'\n\nimport * as TogglePrimitive from '@primitives/react-toggle'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { motionProps,springs } from './lib/motion'\nimport { cn } from './lib/utils'\n\nconst MotionToggleRoot = motion.create(TogglePrimitive.Root)\n\nconst toggleVariants = cva(\n 'inline-flex items-center justify-center gap-ds-03 rounded-control font-medium transition-colors duration-fast-01 ease-productive-standard focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-action-disabled',\n {\n variants: {\n variant: {\n default:\n 'bg-transparent hover:bg-surface-raised-hover text-surface-fg-muted',\n outline:\n 'border border-surface-border-strong bg-transparent hover:bg-surface-raised-hover hover:border-surface-border-strong',\n },\n size: {\n sm: 'h-ds-sm px-ds-03 text-body-sm',\n md: 'h-ds-md px-ds-04 text-body-md',\n lg: 'h-ds-lg px-ds-05 text-body-lg',\n },\n color: {\n accent: 'data-[state=on]:bg-accent-2 data-[state=on]:text-accent-11',\n error: 'data-[state=on]:bg-error-2 data-[state=on]:text-error-11',\n success: 'data-[state=on]:bg-success-2 data-[state=on]:text-success-11',\n neutral: 'data-[state=on]:bg-surface-raised-hover data-[state=on]:text-surface-fg',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'md',\n color: 'accent',\n },\n },\n)\n\nconst Toggle = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n>(({ className, variant, size, color, ...props }, ref) => (\n <MotionToggleRoot\n ref={ref}\n whileTap={{ scale: 0.95 }}\n transition={springs.snappy}\n className={cn(toggleVariants({ variant, size, color }), className)}\n {...motionProps(props)}\n />\n))\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nexport { Toggle, toggleVariants }\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAmB,EAAO,OAAO,CAAoB,GAErD,IAAiB,EACrB,qTACA;CACE,UAAU;EACR,SAAS;GACP,SACE;GACF,SACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EACA,OAAO;GACL,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS;EACX;CACF;CACA,iBAAiB;EACf,SAAS;EACT,MAAM;EACN,OAAO;CACT;AACF,CACF,GAEM,IAAS,EAAM,YAIlB,EAAE,cAAW,YAAS,SAAM,UAAO,GAAG,KAAS,MAChD,kBAAC,GAAD;CACO;CACL,UAAU,EAAE,OAAO,IAAK;CACxB,YAAY,EAAQ;CACpB,WAAW,EAAG,EAAe;EAAE;EAAS;EAAM;CAAM,CAAC,GAAG,CAAS;CACjE,GAAI,EAAY,CAAK;AACtB,CAAA,CACF;AACD,EAAO,cAAA,EAAmC"}
@@ -76,7 +76,7 @@ var y = i, b = {
76
76
  ...a.snappy,
77
77
  opacity: o.fade
78
78
  },
79
- className: s("z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating", n),
79
+ className: s("z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-body-sm text-surface-inverted-fg shadow-floating", n),
80
80
  children: c
81
81
  })
82
82
  })
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,EAAK,GAElD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;CACwB,CAAA;AACI,CAAA;AAGnC,SAAS,EAAa,EAAE,eAA2C;CAGjE,OAFoB,EAAM,WAAW,CACjC,IAAoB,kBAAA,GAAA,EAAG,YAAW,CAAA,IAEpC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;EACwB,CAAA;CACI,CAAA;AAErC;AAKA,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,GAAM,CAAC,GAEzE,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;CAAa,CAAC,GAErF,IAAe,EAAM,eAAe,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE3D,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;EAAQ,CAAA;CAC/C,CAAA,EACb,CAAA;AAElB;AACA,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,EAAE;CACZ,QAAQ,EAAE,GAAG,GAAG;CAChB,MAAM,EAAE,GAAG,EAAE;CACb,OAAO,EAAE,GAAG,GAAG;AACjB,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,CAAc,GAC1C,IAAY,EAAW,MAAS,CAAC;CAEvC,OACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IACjD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;IAAE;IAC5C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IAC9C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;IAAK;IACtD,WAAW,EACT,2IACA,CACF;IAEC;GACS,CAAA;EACY,CAAA;CACH,CAAA,EAEZ,CAAA;AAErB,CAAC;AACD,EAAe,cAAA,EAAuC"}
1
+ {"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-body-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,EAAK,GAElD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;CACwB,CAAA;AACI,CAAA;AAGnC,SAAS,EAAa,EAAE,eAA2C;CAGjE,OAFoB,EAAM,WAAW,CACjC,IAAoB,kBAAA,GAAA,EAAG,YAAW,CAAA,IAEpC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;EACwB,CAAA;CACI,CAAA;AAErC;AAKA,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,GAAM,CAAC,GAEzE,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;CAAa,CAAC,GAErF,IAAe,EAAM,eAAe,EAAE,QAAK,IAAI,CAAC,CAAI,CAAC;CAE3D,OACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;EAAQ,CAAA;CAC/C,CAAA,EACb,CAAA;AAElB;AACA,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,EAAE;CACZ,QAAQ,EAAE,GAAG,GAAG;CAChB,MAAM,EAAE,GAAG,EAAE;CACb,OAAO,EAAE,GAAG,GAAG;AACjB,GAEM,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,CAAc,GAC1C,IAAY,EAAW,MAAS,CAAC;CAEvC,OACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IACjD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;IAAE;IAC5C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;IAAU;IAC9C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;IAAK;IACtD,WAAW,EACT,6IACA,CACF;IAEC;GACS,CAAA;EACY,CAAA;CACH,CAAA,EAEZ,CAAA;AAErB,CAAC;AACD,EAAe,cAAA,EAAuC"}
@@ -0,0 +1,50 @@
1
+ # Diff
2
+
3
+ - Import: @devalok/shilp-sutra/composed/diff
4
+ - Server-safe: No
5
+ - Category: composed
6
+
7
+ ## Props
8
+ before: string (committed / old version)
9
+ after: string (pending / new version)
10
+ mode: "inline" | "split" | "fields" (unified, side-by-side, or structured JSON diff)
11
+ granularity: "line" | "word" (word = prose; ignored in fields mode)
12
+ collapseUnchanged: boolean (collapse long runs of unchanged lines behind an expander)
13
+ collapseThreshold: number (unchanged-line run length that triggers a collapse)
14
+ contextLines: number (unchanged lines kept visible each side of a collapsed run)
15
+ showSummary: boolean (+N / −N / change-count header)
16
+ beforeLabel: string (old-side column label, split mode)
17
+ afterLabel: string (new-side column label, split mode)
18
+ onAcceptHunk: (hunk) => void (adds per-change Accept control)
19
+ onRejectHunk: (hunk) => void (adds per-change Reject control)
20
+
21
+ ## Defaults
22
+ mode="inline", granularity="line", collapseUnchanged={true}, collapseThreshold={6}, contextLines={3}, showSummary={true}, beforeLabel="Committed", afterLabel="Pending"
23
+
24
+ ## Example
25
+ ```jsx
26
+ <Diff before={committed} after={pending} mode="split" />
27
+ <Diff before={a} after={b} granularity="word" /> // prose
28
+ <Diff before={jsonA} after={jsonB} mode="fields" /> // structured, diffs by key
29
+ <Diff before={a} after={b} onAcceptHunk={fn} onRejectHunk={fn} /> // review flow
30
+
31
+ // Composable — bring your own layout:
32
+ <Diff.Root before={a} after={b} mode="split">
33
+ <header><Diff.ColumnLabels /><Diff.Summary /></header>
34
+ <Diff.Body />
35
+ </Diff.Root>
36
+ ```
37
+
38
+ ## Composability
39
+ - **Wraps the headless `diff` (jsdiff) engine, owns the presentation** — same pattern as charts (d3) and data-table (TanStack). `diff` is a runtime dependency.
40
+ - **Three modes.** `inline` (unified), `split` (side-by-side with intra-line word highlights), `fields` (parses JSON both sides, diffs by key — added/removed/changed — for structured content like YAML front-matter).
41
+ - **Compound parts:** `Diff.Root` computes the diff once and provides it via context; `Diff.Summary`, `Diff.Body`, `Diff.ColumnLabels` read from it. Use the parts to place the summary in a sticky header, scroll the body, or wrap hunks in custom chrome. `useDiff()` exposes the model.
42
+ - **Review surface, not just a viewer.** Provide `onAcceptHunk` / `onRejectHunk` and each contiguous change (hunk) gets Accept/Reject controls — pair with an approve/reject footer.
43
+ - **Collapse unchanged** keeps long content readable (GitHub-style "expand N unchanged lines", animated open, reduced-motion aware).
44
+ - **Semantic colour:** additions use the `success` scale, removals the `error` scale — flips correctly in dark.
45
+
46
+ ## Gotchas
47
+ - `fields` mode requires valid JSON on both sides; it reports a clear message otherwise. For YAML, parse to an object and `JSON.stringify` before passing.
48
+ - `granularity="word"` is for prose; it renders a flowing word-level diff, not line gutters.
49
+ - Accept/Reject controls only appear when the matching handler is provided — the Diff itself doesn't mutate content; the consumer applies the decision.
50
+ - Hunk = a contiguous run of added/removed lines; `onAcceptHunk`/`onRejectHunk` receive `{ index, before, after }`.
@@ -32,7 +32,7 @@
32
32
  editable: boolean — enable double-click cell editing
33
33
  virtualRows: boolean — virtualize rows for large datasets
34
34
  columnPinning: { left?: string[], right?: string[] }
35
- defaultDensity: 'compact' | 'standard' | 'comfortable'
35
+ density: 'compact' | 'standard' | 'comfortable'
36
36
 
37
37
  ## Defaults
38
38
  pageSize=10, noResultsText="No results."
@@ -86,7 +86,7 @@ import { DataTable } from '@devalok/shilp-sutra/ui/data-table'
86
86
  - When pagination prop is provided, pagination is manual — pass total count
87
87
  - selectedIds syncs via useEffect — provide getRowId for custom row IDs
88
88
  - onRowClick does NOT fire when clicking checkboxes, buttons, links, or inputs
89
- - Use defaultDensity="compact" for Karm-style h-9 rows
89
+ - Use density="compact" for Karm-style h-9 rows
90
90
  - `virtualRows={true}` requires a bounded scroll container — unbounded height silently disables virtualization
91
91
 
92
92
  ## Changes
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @devalok/shilp-sutra
2
2
 
3
- > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.49.4.
3
+ > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.50.0.
4
4
  > Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
5
5
  > This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
6
6
 
@@ -125,6 +125,7 @@ Format: `[name](doc path): summary`. Import paths follow `@devalok/shilp-sutra/<
125
125
  - [content-card](docs/components/composed/content-card.md): **DEPRECATED (v0.44.0)** — will be removed in the next major
126
126
  - [date-picker](docs/components/composed/date-picker.md)
127
127
  - [deadline-indicator](docs/components/composed/deadline-indicator.md)
128
+ - [diff](docs/components/composed/diff.md)
128
129
  - [emoji-picker](docs/components/composed/emoji-picker.md)
129
130
  - [empty-state](docs/components/composed/empty-state.md): Note: EmptyState was server-safe prior to v0.18.0 but is NO LONGER server-safe due to Framer Motion dependency
130
131
  - [error-boundary](docs/components/composed/error-boundary.md)
package/mcp-manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "./mcp-manifest.schema.json",
3
3
  "manifestVersion": "1.2.0",
4
4
  "package": "@devalok/shilp-sutra",
5
- "packageVersion": "0.49.4",
5
+ "packageVersion": "0.50.0",
6
6
  "components": {
7
7
  "accordion": {
8
8
  "displayName": "Accordion",
@@ -2756,7 +2756,7 @@
2756
2756
  },
2757
2757
  "required": false
2758
2758
  },
2759
- "defaultDensity": {
2759
+ "density": {
2760
2760
  "type": {
2761
2761
  "name": "enum",
2762
2762
  "value": [
@@ -2798,7 +2798,7 @@
2798
2798
  "When pagination prop is provided, pagination is manual — pass total count",
2799
2799
  "selectedIds syncs via useEffect — provide getRowId for custom row IDs",
2800
2800
  "onRowClick does NOT fire when clicking checkboxes, buttons, links, or inputs",
2801
- "Use defaultDensity=\"compact\" for Karm-style h-9 rows",
2801
+ "Use density=\"compact\" for Karm-style h-9 rows",
2802
2802
  "`virtualRows={true}` requires a bounded scroll container — unbounded height silently disables virtualization"
2803
2803
  ],
2804
2804
  "changes": [
@@ -9408,6 +9408,146 @@
9408
9408
  "Relative format uses `Date.now()` at render time — does not live-update (re-render to refresh)"
9409
9409
  ]
9410
9410
  },
9411
+ "diff": {
9412
+ "displayName": "Diff",
9413
+ "tier": "composed",
9414
+ "import": "@devalok/shilp-sutra/composed/diff",
9415
+ "serverSafe": false,
9416
+ "description": "",
9417
+ "props": {
9418
+ "before": {
9419
+ "type": {
9420
+ "name": "string"
9421
+ },
9422
+ "required": false,
9423
+ "description": "committed / old version"
9424
+ },
9425
+ "after": {
9426
+ "type": {
9427
+ "name": "string"
9428
+ },
9429
+ "required": false,
9430
+ "description": "pending / new version"
9431
+ },
9432
+ "mode": {
9433
+ "type": {
9434
+ "name": "enum",
9435
+ "value": [
9436
+ "inline",
9437
+ "split",
9438
+ "fields"
9439
+ ]
9440
+ },
9441
+ "required": false,
9442
+ "description": "unified, side-by-side, or structured JSON diff",
9443
+ "defaultValue": "inline"
9444
+ },
9445
+ "granularity": {
9446
+ "type": {
9447
+ "name": "enum",
9448
+ "value": [
9449
+ "line",
9450
+ "word"
9451
+ ]
9452
+ },
9453
+ "required": false,
9454
+ "description": "word = prose; ignored in fields mode",
9455
+ "defaultValue": "line"
9456
+ },
9457
+ "collapseUnchanged": {
9458
+ "type": {
9459
+ "name": "boolean"
9460
+ },
9461
+ "required": false,
9462
+ "description": "collapse long runs of unchanged lines behind an expander",
9463
+ "defaultValue": "{true}"
9464
+ },
9465
+ "collapseThreshold": {
9466
+ "type": {
9467
+ "name": "number"
9468
+ },
9469
+ "required": false,
9470
+ "description": "unchanged-line run length that triggers a collapse",
9471
+ "defaultValue": "{6}"
9472
+ },
9473
+ "contextLines": {
9474
+ "type": {
9475
+ "name": "number"
9476
+ },
9477
+ "required": false,
9478
+ "description": "unchanged lines kept visible each side of a collapsed run",
9479
+ "defaultValue": "{3}"
9480
+ },
9481
+ "showSummary": {
9482
+ "type": {
9483
+ "name": "boolean"
9484
+ },
9485
+ "required": false,
9486
+ "description": "+N / −N / change-count header",
9487
+ "defaultValue": "{true}"
9488
+ },
9489
+ "beforeLabel": {
9490
+ "type": {
9491
+ "name": "string"
9492
+ },
9493
+ "required": false,
9494
+ "description": "old-side column label, split mode",
9495
+ "defaultValue": "Committed"
9496
+ },
9497
+ "afterLabel": {
9498
+ "type": {
9499
+ "name": "string"
9500
+ },
9501
+ "required": false,
9502
+ "description": "new-side column label, split mode",
9503
+ "defaultValue": "Pending"
9504
+ },
9505
+ "onAcceptHunk": {
9506
+ "type": {
9507
+ "name": "function",
9508
+ "raw": "(hunk) => void (adds per-change Accept control)"
9509
+ },
9510
+ "required": false
9511
+ },
9512
+ "onRejectHunk": {
9513
+ "type": {
9514
+ "name": "function",
9515
+ "raw": "(hunk) => void (adds per-change Reject control)"
9516
+ },
9517
+ "required": false
9518
+ }
9519
+ },
9520
+ "composition": {
9521
+ "notes": [
9522
+ "**Wraps the headless `diff` (jsdiff) engine, owns the presentation** — same pattern as charts (d3) and data-table (TanStack). `diff` is a runtime dependency.",
9523
+ "**Three modes.** `inline` (unified), `split` (side-by-side with intra-line word highlights), `fields` (parses JSON both sides, diffs by key — added/removed/changed — for structured content like YAML front-matter).",
9524
+ "**Compound parts:** `Diff.Root` computes the diff once and provides it via context; `Diff.Summary`, `Diff.Body`, `Diff.ColumnLabels` read from it. Use the parts to place the summary in a sticky header, scroll the body, or wrap hunks in custom chrome. `useDiff()` exposes the model.",
9525
+ "**Review surface, not just a viewer.** Provide `onAcceptHunk` / `onRejectHunk` and each contiguous change (hunk) gets Accept/Reject controls — pair with an approve/reject footer.",
9526
+ "**Collapse unchanged** keeps long content readable (GitHub-style \"expand N unchanged lines\", animated open, reduced-motion aware).",
9527
+ "**Semantic colour:** additions use the `success` scale, removals the `error` scale — flips correctly in dark."
9528
+ ]
9529
+ },
9530
+ "docPath": "docs/components/composed/diff.md",
9531
+ "defaults": {
9532
+ "mode": "inline",
9533
+ "granularity": "line",
9534
+ "collapseUnchanged": "{true}",
9535
+ "collapseThreshold": "{6}",
9536
+ "contextLines": "{3}",
9537
+ "showSummary": "{true}",
9538
+ "beforeLabel": "Committed",
9539
+ "afterLabel": "Pending"
9540
+ },
9541
+ "examples": [
9542
+ "<Diff before={committed} after={pending} mode=\"split\" />\n<Diff before={a} after={b} granularity=\"word\" /> // prose\n<Diff before={jsonA} after={jsonB} mode=\"fields\" /> // structured, diffs by key\n<Diff before={a} after={b} onAcceptHunk={fn} onRejectHunk={fn} /> // review flow\n\n// Composable — bring your own layout:\n<Diff.Root before={a} after={b} mode=\"split\">\n <header><Diff.ColumnLabels /><Diff.Summary /></header>\n <Diff.Body />\n</Diff.Root>"
9543
+ ],
9544
+ "gotchas": [
9545
+ "`fields` mode requires valid JSON on both sides; it reports a clear message otherwise. For YAML, parse to an object and `JSON.stringify` before passing.",
9546
+ "`granularity=\"word\"` is for prose; it renders a flowing word-level diff, not line gutters.",
9547
+ "Accept/Reject controls only appear when the matching handler is provided — the Diff itself doesn't mutate content; the consumer applies the decision.",
9548
+ "Hunk = a contiguous run of added/removed lines; `onAcceptHunk`/`onRejectHunk` receive `{ index, before, after }`."
9549
+ ]
9550
+ },
9411
9551
  "emoji-picker": {
9412
9552
  "displayName": "EmojiPicker",
9413
9553
  "tier": "composed",
@@ -13131,6 +13271,10 @@
13131
13271
  "name": "--color-surface-border-subtle",
13132
13272
  "value": "GrayText"
13133
13273
  },
13274
+ {
13275
+ "name": "--color-surface-border-card",
13276
+ "value": "GrayText"
13277
+ },
13134
13278
  {
13135
13279
  "name": "--color-backdrop",
13136
13280
  "value": "Canvas"