@devalok/shilp-sutra 0.28.0 → 0.30.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 (246) hide show
  1. package/dist/_chunks/badge-group.js +299 -0
  2. package/dist/_chunks/framer.js +1915 -1980
  3. package/dist/_chunks/keybinding.js +4 -5
  4. package/dist/_chunks/primitives.js +3198 -3272
  5. package/dist/_chunks/sonner.js +29 -31
  6. package/dist/_chunks/stat-row.js +110 -131
  7. package/dist/_chunks/tiptap.js +42 -78
  8. package/dist/_chunks/tree-view.js +138 -149
  9. package/dist/_chunks/typing-indicator.js +565 -0
  10. package/dist/_chunks/use-calendar.js +416 -439
  11. package/dist/_chunks/vendor-client.js +5 -5
  12. package/dist/_chunks/vendor-utils.js +5 -5
  13. package/dist/ai/block-renderer.js +22 -22
  14. package/dist/ai/blocks/loading.d.ts.map +1 -1
  15. package/dist/ai/command-bar.d.ts.map +1 -1
  16. package/dist/ai/command-bar.js +241 -263
  17. package/dist/ai/conversation.d.ts.map +1 -1
  18. package/dist/ai/conversation.js +87 -107
  19. package/dist/composed/activity-feed.d.ts +2 -0
  20. package/dist/composed/activity-feed.d.ts.map +1 -1
  21. package/dist/composed/activity-feed.js +118 -90
  22. package/dist/composed/avatar-group.d.ts +1 -0
  23. package/dist/composed/avatar-group.d.ts.map +1 -1
  24. package/dist/composed/avatar-group.js +125 -86
  25. package/dist/composed/bulk-action-bar.d.ts.map +1 -1
  26. package/dist/composed/bulk-action-bar.js +29 -28
  27. package/dist/composed/command-palette.d.ts.map +1 -1
  28. package/dist/composed/command-palette.js +99 -113
  29. package/dist/composed/content-card.js +1 -1
  30. package/dist/composed/date-picker/calendar-grid.d.ts.map +1 -1
  31. package/dist/composed/date-picker/date-picker.d.ts.map +1 -1
  32. package/dist/composed/date-picker/date-range-picker.d.ts.map +1 -1
  33. package/dist/composed/date-picker/date-time-picker.d.ts.map +1 -1
  34. package/dist/composed/date-picker/time-picker.d.ts.map +1 -1
  35. package/dist/composed/deadline-indicator.d.ts.map +1 -1
  36. package/dist/composed/deadline-indicator.js +29 -28
  37. package/dist/composed/error-boundary.d.ts.map +1 -1
  38. package/dist/composed/error-boundary.js +30 -27
  39. package/dist/composed/extensions/file-attachment.d.ts.map +1 -1
  40. package/dist/composed/file-preview.d.ts.map +1 -1
  41. package/dist/composed/file-preview.js +261 -271
  42. package/dist/composed/filter-bar.d.ts.map +1 -1
  43. package/dist/composed/filter-bar.js +49 -48
  44. package/dist/composed/form-section.d.ts.map +1 -1
  45. package/dist/composed/form-section.js +12 -11
  46. package/dist/composed/global-loading.js +1 -1
  47. package/dist/composed/index.d.ts +1 -1
  48. package/dist/composed/index.d.ts.map +1 -1
  49. package/dist/composed/index.js +63 -63
  50. package/dist/composed/inline-edit.d.ts.map +1 -1
  51. package/dist/composed/inline-edit.js +55 -54
  52. package/dist/composed/markdown-viewer.d.ts.map +1 -1
  53. package/dist/composed/markdown-viewer.js +44 -43
  54. package/dist/composed/master-detail.d.ts.map +1 -1
  55. package/dist/composed/master-detail.js +35 -34
  56. package/dist/composed/multi-select-popover.d.ts.map +1 -1
  57. package/dist/composed/multi-select-popover.js +73 -73
  58. package/dist/composed/page-header.d.ts.map +1 -1
  59. package/dist/composed/page-header.js +42 -35
  60. package/dist/composed/priority-indicator.d.ts.map +1 -1
  61. package/dist/composed/priority-indicator.js +37 -36
  62. package/dist/composed/rich-text-editor.d.ts +3 -0
  63. package/dist/composed/rich-text-editor.d.ts.map +1 -1
  64. package/dist/composed/rich-text-editor.js +295 -311
  65. package/dist/composed/schedule-view.js +62 -62
  66. package/dist/composed/status-badge.d.ts +4 -2
  67. package/dist/composed/status-badge.d.ts.map +1 -1
  68. package/dist/composed/status-badge.js +58 -45
  69. package/dist/shell/app-command-palette.d.ts.map +1 -1
  70. package/dist/shell/app-command-palette.js +93 -93
  71. package/dist/shell/bottom-navbar.d.ts.map +1 -1
  72. package/dist/shell/bottom-navbar.js +21 -20
  73. package/dist/shell/index.js +18 -18
  74. package/dist/shell/notification-center.d.ts.map +1 -1
  75. package/dist/shell/notification-center.js +96 -95
  76. package/dist/shell/notification-preferences.d.ts.map +1 -1
  77. package/dist/shell/notification-preferences.js +82 -85
  78. package/dist/shell/sidebar.js +59 -60
  79. package/dist/shell/top-bar.d.ts.map +1 -1
  80. package/dist/shell/top-bar.js +103 -103
  81. package/dist/tailwind/index.cjs +37 -4
  82. package/dist/tailwind/preset.d.ts.map +1 -1
  83. package/dist/tailwind/preset.js +38 -5
  84. package/dist/tokens/primitives.css +20 -0
  85. package/dist/tokens/semantic.css +74 -7
  86. package/dist/ui/accordion.d.ts +5 -2
  87. package/dist/ui/accordion.d.ts.map +1 -1
  88. package/dist/ui/accordion.js +44 -39
  89. package/dist/ui/alert-dialog.js +57 -57
  90. package/dist/ui/alert.d.ts +1 -1
  91. package/dist/ui/alert.d.ts.map +1 -1
  92. package/dist/ui/alert.js +30 -29
  93. package/dist/ui/aspect-ratio.js +4 -4
  94. package/dist/ui/autocomplete.js +56 -56
  95. package/dist/ui/avatar.js +2 -2
  96. package/dist/ui/badge-group.d.ts +22 -0
  97. package/dist/ui/badge-group.d.ts.map +1 -0
  98. package/dist/ui/badge-group.js +8 -0
  99. package/dist/ui/badge-indicator.d.ts +32 -0
  100. package/dist/ui/badge-indicator.d.ts.map +1 -0
  101. package/dist/ui/badge-indicator.js +54 -0
  102. package/dist/ui/badge.d.ts +27 -24
  103. package/dist/ui/badge.d.ts.map +1 -1
  104. package/dist/ui/badge.js +13 -129
  105. package/dist/ui/banner.d.ts +1 -1
  106. package/dist/ui/banner.d.ts.map +1 -1
  107. package/dist/ui/banner.js +27 -26
  108. package/dist/ui/breadcrumb.d.ts.map +1 -1
  109. package/dist/ui/breadcrumb.js +37 -36
  110. package/dist/ui/button-group.d.ts +12 -6
  111. package/dist/ui/button-group.d.ts.map +1 -1
  112. package/dist/ui/button-group.js +18 -18
  113. package/dist/ui/button-processing.d.ts +16 -0
  114. package/dist/ui/button-processing.d.ts.map +1 -0
  115. package/dist/ui/button-processing.js +77 -0
  116. package/dist/ui/button.d.ts +50 -25
  117. package/dist/ui/button.d.ts.map +1 -1
  118. package/dist/ui/button.js +243 -127
  119. package/dist/ui/card.js +20 -21
  120. package/dist/ui/charts/index.js +499 -508
  121. package/dist/ui/chat/date-separator.d.ts +12 -0
  122. package/dist/ui/chat/date-separator.d.ts.map +1 -0
  123. package/dist/ui/chat/index.d.ts +9 -0
  124. package/dist/ui/chat/index.d.ts.map +1 -0
  125. package/dist/ui/chat/index.js +12 -0
  126. package/dist/ui/chat/message-input.d.ts +16 -0
  127. package/dist/ui/chat/message-input.d.ts.map +1 -0
  128. package/dist/ui/chat/message-list.d.ts +24 -0
  129. package/dist/ui/chat/message-list.d.ts.map +1 -0
  130. package/dist/ui/chat/message.d.ts +108 -0
  131. package/dist/ui/chat/message.d.ts.map +1 -0
  132. package/dist/ui/chat/system-message.d.ts +11 -0
  133. package/dist/ui/chat/system-message.d.ts.map +1 -0
  134. package/dist/ui/chat/typing-indicator.d.ts +14 -0
  135. package/dist/ui/chat/typing-indicator.d.ts.map +1 -0
  136. package/dist/ui/chat/unread-separator.d.ts +12 -0
  137. package/dist/ui/chat/unread-separator.d.ts.map +1 -0
  138. package/dist/ui/checkbox.js +18 -18
  139. package/dist/ui/chip.d.ts +13 -62
  140. package/dist/ui/chip.d.ts.map +1 -1
  141. package/dist/ui/chip.js +10 -109
  142. package/dist/ui/collapsible.js +4 -4
  143. package/dist/ui/color-input.js +134 -134
  144. package/dist/ui/color-swatch.js +11 -11
  145. package/dist/ui/combobox.d.ts.map +1 -1
  146. package/dist/ui/combobox.js +74 -80
  147. package/dist/ui/context-menu.d.ts.map +1 -1
  148. package/dist/ui/context-menu.js +86 -85
  149. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  150. package/dist/ui/data-table-toolbar.js +51 -57
  151. package/dist/ui/data-table.d.ts.map +1 -1
  152. package/dist/ui/data-table.js +268 -296
  153. package/dist/ui/devalok-grain.d.ts +81 -0
  154. package/dist/ui/devalok-grain.d.ts.map +1 -0
  155. package/dist/ui/devalok-grain.js +69 -0
  156. package/dist/ui/dialog.d.ts.map +1 -1
  157. package/dist/ui/dialog.js +73 -72
  158. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  159. package/dist/ui/dropdown-menu.js +93 -92
  160. package/dist/ui/file-upload.d.ts.map +1 -1
  161. package/dist/ui/file-upload.js +82 -82
  162. package/dist/ui/hover-card.js +29 -29
  163. package/dist/ui/icon-button.d.ts +7 -7
  164. package/dist/ui/icon-button.d.ts.map +1 -1
  165. package/dist/ui/icon-context.d.ts +15 -0
  166. package/dist/ui/icon-context.d.ts.map +1 -0
  167. package/dist/ui/icon-context.js +20 -0
  168. package/dist/ui/icon-group.d.ts +22 -0
  169. package/dist/ui/icon-group.d.ts.map +1 -0
  170. package/dist/ui/icon-group.js +32 -0
  171. package/dist/ui/icon.d.ts +57 -0
  172. package/dist/ui/icon.d.ts.map +1 -0
  173. package/dist/ui/icon.js +122 -0
  174. package/dist/ui/index.d.ts +8 -1
  175. package/dist/ui/index.d.ts.map +1 -1
  176. package/dist/ui/index.js +351 -329
  177. package/dist/ui/input-otp.d.ts.map +1 -1
  178. package/dist/ui/input-otp.js +21 -20
  179. package/dist/ui/input.d.ts +32 -11
  180. package/dist/ui/input.d.ts.map +1 -1
  181. package/dist/ui/input.js +149 -44
  182. package/dist/ui/lib/motion.d.ts +2 -0
  183. package/dist/ui/lib/motion.d.ts.map +1 -1
  184. package/dist/ui/lib/motion.js +13 -11
  185. package/dist/ui/lib/utils.js +4 -4
  186. package/dist/ui/menubar.d.ts.map +1 -1
  187. package/dist/ui/menubar.js +87 -86
  188. package/dist/ui/navigation-menu.d.ts.map +1 -1
  189. package/dist/ui/navigation-menu.js +23 -28
  190. package/dist/ui/number-input.d.ts.map +1 -1
  191. package/dist/ui/number-input.js +54 -53
  192. package/dist/ui/pagination.d.ts.map +1 -1
  193. package/dist/ui/pagination.js +45 -44
  194. package/dist/ui/popover.js +28 -28
  195. package/dist/ui/progress.d.ts +3 -1
  196. package/dist/ui/progress.d.ts.map +1 -1
  197. package/dist/ui/progress.js +43 -39
  198. package/dist/ui/search-input.d.ts.map +1 -1
  199. package/dist/ui/search-input.js +47 -60
  200. package/dist/ui/segmented-control.js +1 -1
  201. package/dist/ui/select.d.ts.map +1 -1
  202. package/dist/ui/select.js +54 -53
  203. package/dist/ui/sheet.d.ts.map +1 -1
  204. package/dist/ui/sheet.js +46 -45
  205. package/dist/ui/sidebar.d.ts.map +1 -1
  206. package/dist/ui/sidebar.js +196 -193
  207. package/dist/ui/skeleton.js +1 -1
  208. package/dist/ui/spinner.js +74 -74
  209. package/dist/ui/stat-card.d.ts.map +1 -1
  210. package/dist/ui/stat-card.js +85 -86
  211. package/dist/ui/switch.d.ts +3 -0
  212. package/dist/ui/switch.d.ts.map +1 -1
  213. package/dist/ui/switch.js +41 -27
  214. package/dist/ui/tabs.js +43 -43
  215. package/dist/ui/text.js +1 -1
  216. package/dist/ui/textarea.js +10 -10
  217. package/dist/ui/toast.d.ts.map +1 -1
  218. package/dist/ui/toast.js +169 -169
  219. package/dist/ui/toggle.js +4 -4
  220. package/dist/ui/tooltip.js +40 -40
  221. package/dist/ui/tree-view/tree-item.d.ts.map +1 -1
  222. package/docs/components/_header.md +4 -4
  223. package/docs/components/composed/activity-feed.md +7 -0
  224. package/docs/components/composed/avatar-group.md +8 -5
  225. package/docs/components/composed/rich-text-editor.md +6 -0
  226. package/docs/components/composed/status-badge.md +14 -1
  227. package/docs/components/ui/accordion.md +5 -2
  228. package/docs/components/ui/badge-group.md +38 -0
  229. package/docs/components/ui/badge-indicator.md +40 -0
  230. package/docs/components/ui/badge.md +36 -5
  231. package/docs/components/ui/button-processing.md +15 -0
  232. package/docs/components/ui/button.md +40 -11
  233. package/docs/components/ui/chat.md +214 -0
  234. package/docs/components/ui/data-table.md +3 -0
  235. package/docs/components/ui/devalok-grain.md +55 -0
  236. package/docs/components/ui/icon-button.md +12 -5
  237. package/docs/components/ui/icon-context.md +38 -0
  238. package/docs/components/ui/icon-group.md +36 -0
  239. package/docs/components/ui/icon.md +47 -0
  240. package/docs/components/ui/input.md +32 -6
  241. package/docs/components/ui/progress.md +5 -0
  242. package/docs/components/ui/spinner.md +3 -0
  243. package/docs/components/ui/switch.md +13 -0
  244. package/llms-full.txt +672 -40
  245. package/llms.txt +46 -21
  246. package/package.json +7 -2
package/llms-full.txt CHANGED
@@ -5,7 +5,7 @@
5
5
  > All variant values and props verified from source CVA definitions.
6
6
  >
7
7
  > Package: @devalok/shilp-sutra
8
- > Version: 0.28.0
8
+ > Version: 0.30.0
9
9
 
10
10
  ---
11
11
 
@@ -54,15 +54,15 @@ Color tokens use OKLCH (perceptually uniform) with 12 functional steps per palet
54
54
  Semantic layer:
55
55
  - Accent (swappable): --color-accent-{1-12} + --color-accent-fg
56
56
  - Secondary: --color-secondary-{1-12} + --color-secondary-fg
57
- - Surface: --color-surface-{1-4} + --color-surface-fg / fg-muted / fg-subtle / border / border-strong
58
- - Border mapping: light mode border=step5, border-strong=step6; dark mode border=step3, border-strong=step4
59
- - Shell chrome (sidebar, topbar, bottom nav) uses surface-2 for elevation above surface-1 app background
57
+ - Surface: --color-surface-{base,raised,sunken} + --color-surface-fg / fg-muted / fg-subtle / border / border-subtle
58
+ - Border mapping: light mode border=step5, border-subtle=step3; dark mode border=step3, border-subtle=step2
59
+ - Shell chrome (sidebar, topbar, bottom nav) uses surface-sunken with brand tint for recessed chrome
60
60
  - Status: --color-{error,success,warning,info}-{3,7,9,11}
61
61
  - Category: --color-category-{teal,amber,slate,indigo,cyan,orange,emerald}
62
62
 
63
63
  Consumer rebranding: Override accent scale CSS vars or use generateScale(options) utility.
64
64
 
65
- Tailwind utilities: accent-1..12, secondary-1..12, surface-1..4, plus fg/border variants.
65
+ Tailwind utilities: accent-1..12, secondary-1..12, surface-base/raised/sunken, plus fg/border variants.
66
66
 
67
67
  ### Toast Setup Pattern
68
68
 
@@ -108,11 +108,11 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
108
108
  ## Compound Components
109
109
  Accordion (root)
110
110
  AccordionItem (value: string, REQUIRED)
111
- AccordionTrigger (clickable header, chevron auto-renders)
111
+ AccordionTrigger (clickable header, chevron auto-renders; chevronPosition: "left" | "right")
112
112
  AccordionContent (collapsible body)
113
113
 
114
114
  ## Defaults
115
- none (type is required)
115
+ none (type is required); AccordionTrigger: chevronPosition="right"
116
116
 
117
117
  ## Example
118
118
  ```jsx
@@ -129,6 +129,9 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
129
129
  - collapsible only works with type="single"
130
130
 
131
131
  ## Changes
132
+ ### v0.29.0
133
+ - **Added** `chevronPosition` prop on AccordionTrigger: `"left"` | `"right"` (default: `"right"`) — controls which side the chevron icon renders
134
+
132
135
  ### v0.18.0
133
136
  - **Changed** Accordion content fade animation migrated to Framer Motion (height animation still uses CSS keyframes)
134
137
 
@@ -380,13 +383,25 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
380
383
  - Category: ui
381
384
 
382
385
  ## Props
383
- variant: "subtle" | "solid" | "outline" | "secondary" (alias->subtle) | "destructive" (alias->solid+error)
384
- color: "default" | "info" | "success" | "error" | "warning" | "brand" | "accent" | "teal" | "amber" | "slate" | "indigo" | "cyan" | "orange" | "emerald"
386
+ variant: "subtle" | "solid" | "outline" | "soft"
387
+ color: "default" | "accent" | "error" | "success" | "warning" | "info" | "neutral" | "teal" | "amber" | "slate" | "indigo" | "cyan" | "orange" | "emerald" | "custom"
385
388
  size: "xs" | "sm" | "md" | "lg"
386
- dot: boolean (shows leading dot indicator)
387
- onDismiss: () => void (shows X button when provided)
389
+ startIcon: ReactElement | null
390
+ endIcon: ReactElement | null
391
+ dot: boolean (shows animated leading dot indicator)
392
+ onClick: () => void (makes badge interactive as button)
393
+ selected: boolean (toggle state — shows check icon when true)
394
+ disabled: boolean
395
+ onDismiss: () => void (shows dismiss X button)
396
+ maxWidth: number (enables truncation with title tooltip)
397
+ circle: boolean (square aspect-ratio, centered content)
398
+ asChild: boolean
388
399
  children: ReactNode
389
400
 
401
+ ## Compound Components
402
+ Badge.Indicator — status indicator sub-component
403
+ Badge.Group — layout wrapper for badge collections
404
+
390
405
  ## Defaults
391
406
  variant="subtle", color="default", size="md"
392
407
 
@@ -394,13 +409,32 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
394
409
  ```jsx
395
410
  <Badge variant="solid" color="success">Active</Badge>
396
411
  <Badge color="teal" onDismiss={() => removeFilter('teal')}>Teal team</Badge>
412
+ <Badge onClick={() => toggle()} selected={isSelected}>Filterable</Badge>
413
+ <Badge color="custom" style={{ '--badge-color': '#8b5cf6' }}>Custom</Badge>
397
414
  ```
398
415
 
399
416
  ## Gotchas
400
417
  - DO NOT use variant="destructive" — use variant="solid" color="error"
401
- - Badge is display-only; for interactive tags use Chip
418
+ - Badge is now interactive when `onClick` is provided (renders as `<button>`)
419
+ - When both `onClick` and `onDismiss` are provided, renders as `div[role="button"]` to avoid nested buttons
420
+ - Chip is deprecated — use Badge with `onClick` for interactive tags
421
+ - `color="custom"` requires `--badge-color` CSS variable (and optionally `--badge-fg-color` for solid variant)
402
422
 
403
423
  ## Changes
424
+ ### v0.29.0
425
+ - **Changed** (BREAKING) v2 rewrite — Badge is now a full interactive component
426
+ - **Added** `soft` variant (tinted bg, no border — completes the 4-variant set: subtle/solid/outline/soft)
427
+ - **Added** `custom` color with CSS variable `--badge-color` (and `--badge-fg-color` for solid)
428
+ - **Added** Interactive mode: `onClick` makes Badge a clickable button, `selected` shows animated check icon
429
+ - **Added** `disabled` prop with reduced opacity and pointer-events-none
430
+ - **Added** `startIcon` / `endIcon` props (auto-sized per badge size)
431
+ - **Added** `maxWidth` prop for truncation with title tooltip
432
+ - **Added** `circle` prop for square aspect-ratio badges
433
+ - **Added** `asChild` prop for Slot composition
434
+ - **Added** `Badge.Indicator` and `Badge.Group` compound sub-components
435
+ - **Deprecated** Chip component — use Badge with `onClick` instead
436
+ - **Changed** Dot animation now uses Framer Motion spring entrance + continuous pulse
437
+
404
438
  ### v0.18.0
405
439
  - **Changed** Pulse-ring animation migrated to Framer Motion
406
440
  - **Fixed** Accent color variants — `text-accent-9` changed to `text-accent-11`, `border-accent-9` changed to `border-accent-7`
@@ -421,6 +455,84 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
421
455
 
422
456
  ### v0.1.0
423
457
  - **Added** Initial release
458
+ # BadgeGroup
459
+
460
+ - Import: @devalok/shilp-sutra/ui (as Badge.Group)
461
+ - Server-safe: Yes
462
+ - Category: ui
463
+
464
+ ## Props
465
+ max: number — Show at most N badges, rest collapsed into "+N" overflow
466
+ gap: "tight" | "default" | "loose" (default: "default")
467
+ size: BadgeProps["size"] — Size passed to the overflow "+N" badge
468
+ onOverflowClick: () => void — Click handler for the overflow "+N" badge
469
+ className: string
470
+ children: ReactNode (REQUIRED)
471
+
472
+ ## Defaults
473
+ gap="default", max=undefined (show all), size="sm" (overflow badge)
474
+
475
+ ## Example
476
+ ```jsx
477
+ <Badge.Group max={3} size="sm" onOverflowClick={() => setShowAll(true)}>
478
+ <Badge>React</Badge>
479
+ <Badge>TypeScript</Badge>
480
+ <Badge>Tailwind</Badge>
481
+ <Badge>Vite</Badge>
482
+ <Badge>Vitest</Badge>
483
+ </Badge.Group>
484
+ {/* Renders: React, TypeScript, Tailwind, +2 */}
485
+ ```
486
+
487
+ ## Gotchas
488
+ - Overflow badge is always `variant="outline" color="neutral"`
489
+ - `size` only applies to the overflow badge — child badges keep their own size
490
+ - Gap values: tight=4px, default=6px, loose=8px
491
+ - Without `onOverflowClick`, the overflow badge is not interactive
492
+
493
+ ## Changes
494
+ ### v0.29.0
495
+ - **Added** Initial release — badge grouping with overflow collapse and click handler
496
+ # BadgeIndicator
497
+
498
+ - Import: @devalok/shilp-sutra/ui (as Badge.Indicator)
499
+ - Server-safe: No
500
+ - Category: ui
501
+
502
+ ## Props
503
+ count: number — Numeric value to display
504
+ max: number (default: 99) — Cap display at this value, shows "99+" when exceeded
505
+ dot: boolean (default: false) — Show a small dot instead of count
506
+ color: "error" | "success" | "warning" | "accent" | "info" (default: "error")
507
+ invisible: boolean (default: false) — Hide the indicator while keeping layout
508
+ showZero: boolean (default: false) — Show indicator when count is 0
509
+ placement: "top-right" | "top-left" | "bottom-right" | "bottom-left" (default: "top-right")
510
+ className: string
511
+ children: ReactNode (REQUIRED) — The element to attach the indicator to
512
+
513
+ ## Defaults
514
+ max=99, dot=false, color="error", invisible=false, showZero=false, placement="top-right"
515
+
516
+ ## Example
517
+ ```jsx
518
+ <Badge.Indicator count={5}>
519
+ <IconButton icon={IconBell} variant="ghost" />
520
+ </Badge.Indicator>
521
+
522
+ <Badge.Indicator dot color="success">
523
+ <Avatar src={user.avatar} fallback={user.name} />
524
+ </Badge.Indicator>
525
+ ```
526
+
527
+ ## Gotchas
528
+ - Wraps children in `position: relative` span — the indicator is absolutely positioned
529
+ - Animation uses spring physics, respects `prefers-reduced-motion`
530
+ - When `count > max`, displays `${max}+` (e.g., "99+")
531
+ - `invisible` keeps the layout but hides the dot/count (useful for transitions)
532
+
533
+ ## Changes
534
+ ### v0.29.0
535
+ - **Added** Initial release — animated notification indicator with count, dot, placement, and color
424
536
  # Banner
425
537
 
426
538
  - Import: @devalok/shilp-sutra/ui/banner
@@ -507,37 +619,66 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
507
619
  - Category: ui
508
620
 
509
621
  ## Props
510
- variant: "solid" | "default" (alias->solid) | "outline" | "ghost" | "link" | "destructive" (alias->solid+error)
511
- color: "default" | "error"
512
- size: "sm" | "md" | "lg" | "icon" | "icon-sm" | "icon-md" | "icon-lg"
513
- startIcon: ReactNode
514
- endIcon: ReactNode
622
+ variant: "solid" | "soft" | "outline" | "ghost" | "link" | "default" (alias->solid) | "destructive" (alias->solid+error)
623
+ color: "accent" | "error" | "success" | "warning" | "neutral" | "default" (alias->accent)
624
+ size: "xs" | "sm" | "md" | "lg" | "compact-xs" | "compact-sm" | "compact-md" | "icon" | "icon-xs" | "icon-sm" | "icon-md" | "icon-lg"
625
+ weight: "semibold" | "normal"
626
+ shape: "default" | "pill"
627
+ startIcon: ReactElement (use <Icon icon={...} />) | null
628
+ endIcon: ReactElement (use <Icon icon={...} />) | null
515
629
  loading: boolean (disables button, shows spinner)
516
630
  loadingPosition: "start" | "end" | "center" (default: "start")
517
631
  fullWidth: boolean
518
632
  asChild: boolean
519
- onClickAsync: (e: MouseEvent) => Promise<void> (auto loading->success/error->idle)
633
+ processing: boolean | 'ambient' | 'working' | 'urgent' (marching ants SVG border)
634
+ processingColor: 'accent' | 'error' | 'success' | 'warning' | 'neutral' (override animation color)
635
+ processingDisabled: boolean (disable button during processing, default: true)
636
+ onClickAsync: (e: MouseEvent) => Promise<void> (auto loading->success/error->idle, auto-activates processing)
520
637
  asyncFeedbackDuration: number (ms, default 1500)
521
638
 
522
639
  ## Defaults
523
- variant="solid", color="default", size="md"
640
+ variant="solid", color="accent", weight="semibold", size="md", shape="default"
524
641
 
525
642
  ## Example
526
643
  ```jsx
527
- <Button variant="solid" color="error" startIcon={<IconTrash />} loading={isDeleting}>
644
+ <Button variant="solid" color="error" startIcon={<Icon icon={IconTrash} />} loading={isDeleting}>
528
645
  Delete project
529
646
  </Button>
647
+ <Button variant="soft" color="success" startIcon={<Icon icon={IconCheck} />}>Approved</Button>
648
+ <Button variant="soft" color="warning" size="compact-sm" shape="pill">Overdue</Button>
530
649
  ```
531
650
 
532
651
  ## Gotchas
533
652
  - DO NOT use variant="destructive" — use variant="solid" color="error"
534
653
  - DO NOT use variant="secondary" — use variant="outline" or variant="ghost"
535
654
  - DO NOT use size="default" — use size="md"
536
- - DO NOT use color="danger" — use color="error"
537
- - Inherits variant/color/size from ButtonGroup context if present
538
- - onClickAsync overrides onClick and loading when active
655
+ - DO NOT use color="danger" or color="default" — use color="error" or color="accent"
656
+ - startIcon/endIcon now expect `<Icon icon={...} />` wrapper, not bare icon components
657
+ - Inherits variant/color/size/weight/shape from ButtonGroup context if present
658
+ - onClickAsync overrides onClick and loading when active; also auto-activates processing='working' during loading phase
659
+ - processing forces soft variant so marching ants pop against the background
660
+ - processingDisabled=true (default) makes button aria-disabled and pointer-events-none during processing
661
+ - Grain children (DevalokGrain) are auto-separated and rendered as direct button children for absolute positioning
539
662
 
540
663
  ## Changes
664
+ ### v0.29.0
665
+ - **Added** `soft` variant — tinted background, colored text (new middle ground between solid and ghost)
666
+ - **Added** 5 color options: `accent` (default), `error`, `success`, `warning`, `neutral` (replaces old `default`/`error`-only axis)
667
+ - **Added** `shape` prop: `"default"` | `"pill"` (rounded-full with extra horizontal padding)
668
+ - **Added** compact sizes: `compact-xs`, `compact-sm`, `compact-md` (height-less inline buttons)
669
+ - **Added** `xs` size and `icon-xs` size
670
+ - **Added** `weight` prop: `"semibold"` (default) | `"normal"` for lighter labels
671
+ - **Changed** `startIcon`/`endIcon` now accept `<Icon icon={...} />` wrapper (auto-sized via IconProvider context per button size)
672
+ - **Changed** Default color is now `"accent"` (was `"default"`)
673
+ - **Changed** Solid hover adds tinted shadows per color (e.g., `hover:shadow-brand`, `hover:shadow-error`)
674
+ - **Changed** Icon slots get negative-margin inset to tighten padding against button edges
675
+ - **Added** DevalokGrain support — grain children are auto-separated and rendered for texture overlays
676
+ - **Added** `processing` prop — marching ants SVG border while content stays visible (`"ambient"` (3s) | `"working"` (2s) | `"urgent"` (1s) | boolean). Forces soft variant so ants pop.
677
+ - **Added** `processingColor` — override processing animation color independently of button color
678
+ - **Added** `processingDisabled` — disable button during processing (default: true). Set false for cancel-by-click patterns.
679
+ - **Added** Auto-processing during `onClickAsync` — loading phase auto-activates `processing='working'` when no explicit `processing` prop is set
680
+ - **Added** Always-on layout animation — smooth width/height transitions via Framer Motion FLIP
681
+
541
682
  ### v0.22.0
542
683
  - **Changed** Active/pressed scale from `0.97` to `0.95` for snappier press feedback.
543
684
  - **Fixed** Ghost/outline hover not fading — `transition-transform` in base overrode `transition-colors` from variant. Combined into single `transition-[color,background-color,border-color,box-shadow,transform]`.
@@ -592,6 +733,21 @@ Note: getFormFieldA11y() was removed in favor of useFormField() hook.
592
733
 
593
734
  ### v0.1.0
594
735
  - **Added** Initial release
736
+ # ButtonProcessing (Internal)
737
+
738
+ - Import: Internal — not exported from barrel. Used only by Button.
739
+ - Server-safe: No
740
+ - Category: ui
741
+
742
+ ## Description
743
+
744
+ Internal component that renders the processing animation overlay for Button. Consumers use the `processing` prop on Button, not this component directly.
745
+
746
+ See `Button` docs for the public API.
747
+
748
+ ## Changes
749
+ ### v0.29.0
750
+ - **Added** Initial release — marching ants processing overlay (SVG dashed rect with animated stroke-dashoffset). Speed tiers: ambient (3s), working (2s), urgent (1s). Color maps to step-11 tokens for visibility on all variants.
595
751
  # Card
596
752
 
597
753
  - Import: @devalok/shilp-sutra/ui/card
@@ -683,6 +839,220 @@ import { BarChart } from '@devalok/shilp-sutra/ui/charts'
683
839
 
684
840
  ### v0.1.0
685
841
  - **Added** Initial release
842
+ # Chat
843
+
844
+ - Import: @devalok/shilp-sutra/ui/chat
845
+ - Server-safe: No (MessageList, Message, MessageInput, TypingIndicator use Framer Motion); DateSeparator and UnreadSeparator are server-safe
846
+ - Category: ui
847
+
848
+ Seven primitives for building chat interfaces: MessageList, Message (compound), SystemMessage, MessageInput, DateSeparator, UnreadSeparator, TypingIndicator.
849
+
850
+ ---
851
+
852
+ ## MessageList
853
+
854
+ Scrollable container with auto-scroll, load-more, empty state, and "N new" floating pill.
855
+
856
+ ### Props
857
+ autoScroll: boolean — auto-scroll to bottom on new content (default: true)
858
+ newMessageCount: number — count shown in floating pill (default: 0)
859
+ onScrollToBottom: () => void — called when user clicks the "N new" pill
860
+ onLoadMore: () => void — called when user scrolls near top
861
+ isLoadingMore: boolean — shows spinner at top (default: false)
862
+ emptySlot: ReactNode — content when there are no children
863
+ headerSlot: ReactNode — content above the scroll container (e.g. channel name)
864
+ scrollToBottomSlot: ReactNode — reserved slot for custom scroll-to-bottom button
865
+ children: ReactNode (REQUIRED)
866
+
867
+ ### Example
868
+ ```jsx
869
+ <MessageList autoScroll onLoadMore={loadMore} isLoadingMore={loading} newMessageCount={3} onScrollToBottom={markRead}>
870
+ {messages.map(m => <Message key={m.id}>...</Message>)}
871
+ </MessageList>
872
+ ```
873
+
874
+ ---
875
+
876
+ ## Message (Compound)
877
+
878
+ Compound component: `Message`, `Message.Avatar`, `Message.Content`, `Message.Author`, `Message.Body`, `Message.EditableBody`, `Message.Reactions`, `Message.Actions`, `Message.Action`.
879
+
880
+ ### Message (root) Props
881
+ variant: "flat" | "bubble" (default: "flat")
882
+ placement: "start" | "end" (default: "start")
883
+ highlight: "mention" | "internal"
884
+ grouped: boolean — hides avatar/author for consecutive messages (default: false)
885
+ deleted: boolean — renders deleted placeholder (default: false)
886
+ deletedText: string (default: "This message was deleted")
887
+
888
+ ### Message.Avatar Props
889
+ src: string | null — avatar image URL
890
+ fallback: string — initials text
891
+ icon: ReactNode — custom icon instead of avatar
892
+ size: "sm" | "md" (default: "md")
893
+ children: ReactNode — fully custom avatar slot
894
+
895
+ ### Message.Author Props
896
+ name: string (REQUIRED)
897
+ badge: ReactNode
898
+ timestamp: Date
899
+ formattedTimestamp: string — pre-formatted timestamp (overrides timestamp)
900
+ timestampFormat: (date: Date) => string — custom formatter
901
+
902
+ ### Message.Body Props
903
+ children: ReactNode (REQUIRED)
904
+
905
+ ### Message.EditableBody Props
906
+ content: string (REQUIRED)
907
+ onSave: (newContent: string) => void (REQUIRED)
908
+ onCancel: () => void
909
+ canEdit: boolean (default: false)
910
+ renderContent: (content: string) => ReactNode — custom render for display mode
911
+
912
+ ### Message.Reactions Props
913
+ reactions: { emoji: string; count: number; reacted: boolean }[] (REQUIRED)
914
+ onReact: (emoji: string) => void (REQUIRED)
915
+
916
+ ### Message.Content Props
917
+ children: ReactNode (REQUIRED)
918
+ className: string
919
+
920
+ ### Message.Actions Props
921
+ children: ReactNode (REQUIRED)
922
+ delay: number — hover reveal delay in ms (default: 100)
923
+
924
+ ### Message.Action Props
925
+ icon: IconProps["icon"] (REQUIRED) — pass the Tabler component reference, e.g. `IconReply` (not `<IconReply />`)
926
+ label: string (REQUIRED)
927
+ onClick: () => void (REQUIRED)
928
+ variant: "default" | "danger" (default: "default")
929
+
930
+ ### Example
931
+ ```jsx
932
+ <Message variant="flat" highlight="mention">
933
+ <Message.Avatar src={user.photo} fallback="JD" />
934
+ <Message.Content>
935
+ <Message.Author name="Jane Doe" timestamp={new Date()} badge={<Badge>Admin</Badge>} />
936
+ <Message.Body>Hello, world!</Message.Body>
937
+ <Message.Reactions reactions={reactions} onReact={handleReact} />
938
+ <Message.Actions>
939
+ <Message.Action icon={IconReply} label="Reply" onClick={handleReply} />
940
+ <Message.Action icon={IconTrash} label="Delete" onClick={handleDelete} variant="danger" />
941
+ </Message.Actions>
942
+ </Message.Content>
943
+ </Message>
944
+ ```
945
+
946
+ ---
947
+
948
+ ## SystemMessage
949
+
950
+ Inline system event or alert message (e.g. "Alice joined the channel").
951
+
952
+ ### Props
953
+ icon: ReactNode — custom icon
954
+ timestamp: string — ISO timestamp string
955
+ variant: "event" | "alert" (default: "event")
956
+ children: ReactNode (REQUIRED)
957
+
958
+ ### Example
959
+ ```jsx
960
+ <SystemMessage>Alice joined the channel</SystemMessage>
961
+ <SystemMessage variant="alert" timestamp="2026-03-26T10:00:00Z">Connection lost</SystemMessage>
962
+ ```
963
+
964
+ ---
965
+
966
+ ## MessageInput
967
+
968
+ Auto-resizing textarea with send/stop buttons, streaming support, and slot-based extensibility.
969
+
970
+ ### Props
971
+ onSubmit: (text: string) => void (REQUIRED)
972
+ placeholder: string (default: "Type a message...")
973
+ disabled: boolean (default: false)
974
+ isStreaming: boolean — shows stop button instead of send (default: false)
975
+ onCancel: () => void — called when stop button is clicked
976
+ leadingSlot: ReactNode — content before the textarea (e.g. attachment button)
977
+ trailingSlot: ReactNode — content after the send button
978
+ disclaimer: string — centered text below the input (e.g. "AI can make mistakes")
979
+ sendIcon: ReactNode — custom send icon
980
+
981
+ ### Example
982
+ ```jsx
983
+ <MessageInput
984
+ onSubmit={handleSend}
985
+ isStreaming={streaming}
986
+ onCancel={handleStop}
987
+ disclaimer="AI can make mistakes"
988
+ />
989
+ ```
990
+
991
+ ---
992
+
993
+ ## DateSeparator
994
+
995
+ Horizontal rule with a formatted date label.
996
+
997
+ ### Props
998
+ date: Date | string (REQUIRED)
999
+ format: (date: Date) => string — custom date formatter
1000
+ className: string
1001
+
1002
+ ### Example
1003
+ ```jsx
1004
+ <DateSeparator date={new Date()} />
1005
+ <DateSeparator date="2026-03-25" format={(d) => d.toLocaleDateString()} />
1006
+ ```
1007
+
1008
+ ---
1009
+
1010
+ ## UnreadSeparator
1011
+
1012
+ Accent-colored horizontal rule marking the unread boundary.
1013
+
1014
+ ### Props
1015
+ label: string (default: "NEW")
1016
+ count: number — prepended to label (e.g. "5 NEW")
1017
+ className: string
1018
+
1019
+ ### Example
1020
+ ```jsx
1021
+ <UnreadSeparator />
1022
+ <UnreadSeparator count={5} />
1023
+ ```
1024
+
1025
+ ---
1026
+
1027
+ ## TypingIndicator
1028
+
1029
+ Animated bouncing dots with a text description of who is typing.
1030
+
1031
+ ### Props
1032
+ users: { name: string; image?: string }[] (REQUIRED)
1033
+ className: string
1034
+
1035
+ ### Example
1036
+ ```jsx
1037
+ <TypingIndicator users={[{ name: 'Alice' }]} />
1038
+ <TypingIndicator users={[{ name: 'Alice' }, { name: 'Bob' }]} />
1039
+ ```
1040
+
1041
+ ---
1042
+
1043
+ ## Gotchas
1044
+ - MessageList uses `role="log"` with `aria-live="polite"` — screen readers announce new messages
1045
+ - Message entrance animations use Framer Motion springs — AnimatePresence wraps children in MessageList
1046
+ - `grouped` hides avatar and author — use for consecutive messages from the same user
1047
+ - MessageInput sends on Enter (Shift+Enter for newline) — textarea auto-resizes up to 160px
1048
+ - TypingIndicator renders nothing when `users` is empty
1049
+ - Message.Actions toolbar is hidden by default (opacity-0) — it reveals on hover of the parent Message root via `group-hover/message`. Only works when Actions is inside a Message root.
1050
+ - Message.Content is the flex column wrapper for Author + Body — required for proper layout in flat variant
1051
+ - DateSeparator's default formatter shows "Today", "Yesterday", or "Mon DD" / "Mon DD, YYYY"
1052
+
1053
+ ## Changes
1054
+ ### v0.29.0
1055
+ - **Added** Initial release — 7 chat primitives (MessageList, Message, SystemMessage, MessageInput, DateSeparator, UnreadSeparator, TypingIndicator)
686
1056
  # Checkbox
687
1057
 
688
1058
  - Import: @devalok/shilp-sutra/ui/checkbox
@@ -1140,6 +1510,9 @@ import { DataTable } from '@devalok/shilp-sutra/ui/data-table'
1140
1510
  - Use defaultDensity="compact" for Karm-style h-9 rows
1141
1511
 
1142
1512
  ## Changes
1513
+ ### v0.29.0
1514
+ - **Fixed** Controlled selection infinite re-render loop — inline `getRowId` callback caused `onSelectionChange` effect to fire every render, creating a setState cycle with `selectedIds`. Now uses a stable ref for `getRowId`.
1515
+
1143
1516
  ### v0.16.1
1144
1517
  - **Fixed** `serverPagination` object reference in `useCallback` dependency caused stale closure — now uses stable ref for `onPageChange`
1145
1518
  - **Fixed** `onSelectionChange` effect fired every render due to `table` in dependency array — now derives selected rows directly
@@ -1208,6 +1581,61 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1208
1581
 
1209
1582
  ### v0.1.0
1210
1583
  - **Added** Initial release
1584
+ # DevalokGrain
1585
+
1586
+ - Import: @devalok/shilp-sutra/ui/devalok-grain
1587
+ - Server-safe: No
1588
+ - Category: ui
1589
+
1590
+ ## Props
1591
+ intensity: "subtle" | "medium" | "heavy" — grain intensity level
1592
+ surface: "solid" | "soft" — affects noise opacity ('solid' for filled backgrounds, 'soft' for tinted/muted)
1593
+ sheen: boolean — inner highlight (top-lit emboss) for premium 3D feel
1594
+ animated: boolean — fade-in entrance animation on mount
1595
+ hoverIntensify: boolean — increase grain visibility on parent hover (requires parent `group` class)
1596
+ tint: string — CSS color for the directional gradient (e.g. "oklch(0.55 0.19 360)", "var(--color-accent-9)")
1597
+
1598
+ ## Defaults
1599
+ intensity: "subtle"
1600
+ surface: "solid"
1601
+ sheen: false
1602
+ animated: false
1603
+ hoverIntensify: false
1604
+ tint: undefined (no gradient, noise texture only)
1605
+
1606
+ ## Example
1607
+ ```jsx
1608
+ {/* Inside a Button (Button already has relative/overflow-hidden/isolate): */}
1609
+ <Button>
1610
+ <DevalokGrain />
1611
+ Save changes
1612
+ </Button>
1613
+
1614
+ {/* Inside a Card: */}
1615
+ <Card className="relative overflow-hidden isolate">
1616
+ <DevalokGrain surface="soft" />
1617
+ Card content
1618
+ </Card>
1619
+
1620
+ {/* Heavy grain with tint on a hero section: */}
1621
+ <div className="relative overflow-hidden isolate rounded-ds-lg bg-accent-9 p-8">
1622
+ <DevalokGrain intensity="heavy" tint="oklch(0.55 0.19 360)" />
1623
+ <h1 className="relative z-[2]">Hero</h1>
1624
+ </div>
1625
+ ```
1626
+
1627
+ ## Gotchas
1628
+ - Parent element MUST have `relative overflow-hidden isolate` for the grain to render correctly
1629
+ - The grain layers are absolute-positioned at `z-[1]` — content that should appear above must use `z-[2]` or higher
1630
+ - Uses `rounded-[inherit]` to match parent border-radius automatically
1631
+ - Renders `aria-hidden="true"` — purely decorative
1632
+ - Without `tint`, only the noise texture renders (no directional gradient)
1633
+ - `hoverIntensify` requires the parent to have a `group` class for `group-hover:` to work
1634
+ - Respects `prefers-reduced-motion` — entrance animation disabled when user prefers reduced motion
1635
+
1636
+ ## Changes
1637
+ ### v0.29.0
1638
+ - **Added** Initial release — brand noise texture with directional gradient, sheen, animation, and hover intensification
1211
1639
  # Dialog
1212
1640
 
1213
1641
  - Import: @devalok/shilp-sutra/ui/dialog
@@ -1434,6 +1862,53 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1434
1862
 
1435
1863
  ### v0.1.0
1436
1864
  - **Added** Initial release
1865
+ # Icon
1866
+
1867
+ - Import: @devalok/shilp-sutra/ui (barrel export)
1868
+ - Server-safe: No
1869
+ - Category: ui
1870
+
1871
+ ## Props
1872
+ icon: ForwardRefExoticComponent (REQUIRED — Tabler icon or any ForwardRef SVG icon component)
1873
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" — reads from IconContext if not set
1874
+ stroke: "light" | "regular" | "bold" — reads from IconContext if not set
1875
+ label: string — accessible label (renders <title> + sets aria-label; without it, icon is aria-hidden)
1876
+ animate: "spin" | "pulse" | "bounce" | "draw" | "none" | { rotate?: number; scale?: number }
1877
+ state: "idle" | "loading" | "success" | "error" — delegates to Spinner (bare variant)
1878
+ className: string
1879
+
1880
+ ## Defaults
1881
+ size: "md" (from context or fallback)
1882
+ stroke: "regular" (from context or fallback)
1883
+ state: undefined (no state machine)
1884
+ animate: undefined (static render)
1885
+
1886
+ ## Example
1887
+ ```jsx
1888
+ <Icon icon={IconPlus} />
1889
+ <Icon icon={IconPlus} size="xs" stroke="light" />
1890
+ <Icon icon={IconPlus} label="Add item" />
1891
+ <Icon icon={IconPlus} animate="spin" />
1892
+ <Icon icon={IconCheck} animate="draw" />
1893
+ <Icon icon={IconPlus} state="loading" />
1894
+ <Icon icon={IconPlus} state="success" />
1895
+ ```
1896
+
1897
+ ## Gotchas
1898
+ - Without `label`, the icon renders `aria-hidden="true"` (decorative)
1899
+ - With `label`, the icon renders `role="img"` with `aria-label` and a `<title>` element
1900
+ - **Priority rule:** If both `state` and `animate` are set, `state` wins
1901
+ - `state="loading"` renders a bare Spinner; `state="success"` / `state="error"` render animated checkmark/cross
1902
+ - Size tiers map to pixel values: xs=14, sm=16, md=18, lg=20, xl=24, 2xl=32
1903
+ - Stroke weight varies by size tier (lighter strokes on smaller icons)
1904
+ - Reads size/stroke from IconContext (provided by Button, IconGroup, etc.); explicit props override context
1905
+ - `animate="draw"` works with IconCheck, IconX, and CircleCheck only — other icons fall back to static render
1906
+ - Respects `prefers-reduced-motion` — animations disabled when user prefers reduced motion
1907
+
1908
+ ## Changes
1909
+ ### v0.29.0
1910
+ - **Added** Initial release — context-aware Icon wrapper with size tiers, stroke weights, accessibility, animations, state machine
1911
+ - **Added** `animate="draw"` — SVG path-draw animation using `pathLength`. Draws check/X strokes progressively (0.35s easeOut). Respects `prefers-reduced-motion`.
1437
1912
  # IconButton
1438
1913
 
1439
1914
  - Import: @devalok/shilp-sutra/ui/icon-button
@@ -1441,12 +1916,12 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1441
1916
  - Category: ui
1442
1917
 
1443
1918
  ## Props
1444
- icon: ReactNode (REQUIRED)
1919
+ icon: ReactElement (REQUIRED — use <Icon icon={...} />)
1445
1920
  aria-label: string (REQUIRED — WCAG AA mandatory)
1446
1921
  shape: "square" | "circle"
1447
1922
  size: "sm" | "md" | "lg"
1448
- variant: same as Button (solid, outline, ghost, link)
1449
- color: same as Button (default, error)
1923
+ variant: same as Button (solid, soft, outline, ghost, link)
1924
+ color: same as Button (accent, error, success, warning, neutral)
1450
1925
  loading: boolean
1451
1926
  disabled: boolean
1452
1927
 
@@ -1456,17 +1931,98 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1456
1931
 
1457
1932
  ## Example
1458
1933
  ```jsx
1459
- <IconButton icon={<IconEdit />} variant="ghost" aria-label="Edit item" />
1460
- <IconButton icon={<IconX />} shape="circle" variant="ghost" size="sm" aria-label="Close" />
1934
+ <IconButton icon={<Icon icon={IconEdit} />} variant="ghost" aria-label="Edit item" />
1935
+ <IconButton icon={<Icon icon={IconX} />} shape="circle" variant="ghost" size="sm" aria-label="Close" />
1936
+ <IconButton icon={<Icon icon={IconTrash} />} variant="solid" color="error" aria-label="Delete" />
1461
1937
  ```
1462
1938
 
1463
1939
  ## Gotchas
1464
1940
  - aria-label is enforced by TypeScript — you MUST provide it
1465
1941
  - Prefer IconButton over Button with size="icon-*" for icon-only buttons
1942
+ - `icon` prop should use `<Icon icon={...} />` wrapper (auto-sized via Button's IconProvider)
1466
1943
 
1467
1944
  ## Changes
1945
+ ### v0.29.0
1946
+ - **Changed** `icon` prop now expects `<Icon icon={...} />` wrapper (auto-sized via Button's IconProvider context)
1947
+ - **Changed** Inherits all new Button v2 colors: `accent`, `error`, `success`, `warning`, `neutral` (was `default`/`error` only)
1948
+ - **Changed** Inherits Button v2 variants including new `soft` variant
1949
+
1468
1950
  ### v0.1.0
1469
1951
  - **Added** Initial release
1952
+ # IconContext
1953
+
1954
+ - Import: @devalok/shilp-sutra/ui (barrel export)
1955
+ - Server-safe: No
1956
+ - Category: ui
1957
+
1958
+ ## Exports
1959
+ IconContext — React.Context<IconContextValue>
1960
+ IconProvider — Provider component (props: size?, stroke?, children)
1961
+ useIconContext() — Hook returning { size?, stroke? }
1962
+ IconSize — Type: "xs" | "sm" | "md" | "lg" | "xl" | "2xl"
1963
+ IconStroke — Type: "light" | "regular" | "bold"
1964
+
1965
+ ## IconProvider Props
1966
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl"
1967
+ stroke: "light" | "regular" | "bold"
1968
+ children: ReactNode (REQUIRED)
1969
+
1970
+ ## Example
1971
+ ```jsx
1972
+ import { IconProvider, useIconContext } from '@devalok/shilp-sutra/ui'
1973
+
1974
+ <IconProvider size="sm" stroke="bold">
1975
+ <MyCustomIconComponent />
1976
+ </IconProvider>
1977
+
1978
+ // Inside MyCustomIconComponent:
1979
+ const { size, stroke } = useIconContext()
1980
+ ```
1981
+
1982
+ ## Gotchas
1983
+ - Used internally by IconGroup and Button to propagate icon sizing to children
1984
+ - If no provider is present, `useIconContext()` returns `{}` (empty object) — consumers should fall back to defaults
1985
+ - Value is memoized — safe for frequent re-renders
1986
+
1987
+ ## Changes
1988
+ ### v0.29.0
1989
+ - **Added** Initial release — React context for propagating icon size and stroke weight
1990
+ # IconGroup
1991
+
1992
+ - Import: @devalok/shilp-sutra/ui (barrel export)
1993
+ - Server-safe: No
1994
+ - Category: ui
1995
+
1996
+ ## Props
1997
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" — propagated to children via IconContext
1998
+ stroke: "light" | "regular" | "bold" — propagated to children via IconContext
1999
+ gap: "tight" | "default" | "loose" — flex gap between icons
2000
+ label: string — accessible label (used as aria-label when role="toolbar")
2001
+ role: "toolbar" — optional, set for toolbar patterns
2002
+ className: string
2003
+ children: ReactNode (REQUIRED)
2004
+
2005
+ ## Defaults
2006
+ gap: "default"
2007
+ role: undefined (no ARIA role)
2008
+
2009
+ ## Example
2010
+ ```jsx
2011
+ <IconGroup size="sm" gap="tight" role="toolbar" label="Formatting">
2012
+ <Icon icon={IconBold} label="Bold" />
2013
+ <Icon icon={IconItalic} label="Italic" />
2014
+ <Icon icon={IconUnderline} label="Underline" />
2015
+ </IconGroup>
2016
+ ```
2017
+
2018
+ ## Gotchas
2019
+ - Wraps children in an IconProvider — all child Icons inherit size/stroke from the group
2020
+ - `label` is only applied as `aria-label` when `role="toolbar"` is set
2021
+ - Gap values: tight=2px, default=4px, loose=8px
2022
+
2023
+ ## Changes
2024
+ ### v0.29.0
2025
+ - **Added** Initial release — icon grouping with shared context, toolbar ARIA support
1470
2026
  # Input
1471
2027
 
1472
2028
  - Import: @devalok/shilp-sutra/ui/input
@@ -1474,10 +2030,17 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1474
2030
  - Category: ui
1475
2031
 
1476
2032
  ## Props
1477
- size: "sm" | "md" | "lg"
2033
+ size: "xs" | "sm" | "md" | "lg"
1478
2034
  state: InputState
1479
- startIcon: ReactNode
1480
- endIcon: ReactNode
2035
+ startSection: ReactNode (icon or content in the leading slot)
2036
+ endSection: ReactNode (icon or content in the trailing slot)
2037
+ startSectionClickable: boolean (enables pointer events on start section)
2038
+ endSectionClickable: boolean (enables pointer events on end section)
2039
+ startSectionType: 'icon' | 'label' (section display type — auto-inferred from content)
2040
+ endSectionType: 'icon' | 'label' (section display type — auto-inferred from content)
2041
+ wrapperClassName: string (classes for the wrapper div — border, bg, ring)
2042
+ startIcon: ReactNode (@deprecated — use startSection)
2043
+ endIcon: ReactNode (@deprecated — use endSection)
1481
2044
  (plus all standard HTML input attributes except native "size")
1482
2045
 
1483
2046
  ## Types
@@ -1488,17 +2051,36 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1488
2051
 
1489
2052
  ## Example
1490
2053
  ```jsx
1491
- <Input type="email" placeholder="you@example.com" state="error" startIcon={<IconMail />} />
2054
+ <Input type="email" placeholder="you@example.com" state="error" startSection={<Icon icon={IconMail} />} />
2055
+ <Input size="xs" placeholder="Quick search" startSection={<Icon icon={IconSearch} />} />
2056
+ <Input startSection="https://" startSectionType="label" placeholder="example.com" />
2057
+ <Input endSection=".00" endSectionType="label" startSection={<Icon icon={IconCurrencyDollar} />} placeholder="0" />
1492
2058
  ```
1493
2059
 
1494
2060
  ## Gotchas
1495
2061
  - HTML native "size" attribute is excluded — use CSS width instead
1496
2062
  - state="error" sets aria-invalid automatically
1497
2063
  - Inside FormField: auto-inherits state, aria-describedby, aria-required from context (explicit props override)
1498
- - Resting border is border-subtle (soft); focus ring is `ring-1 ring-accent-7` (v0.12.0)
1499
- - All sizes (sm, md, lg) use text-ds-md (14px) font size only affects height and padding (v0.15.0)
2064
+ - `className` targets the `<input>` element; use `wrapperClassName` for border/bg/ring overrides
2065
+ - Focus ring is on the wrapper container (focus-within), not the input itself
2066
+ - Icons in startSection/endSection are auto-sized via IconProvider per input size
2067
+ - Sections are `pointer-events-none` by default — set `startSectionClickable`/`endSectionClickable` for interactive sections
2068
+ - Section type is auto-inferred: strings default to `'label'` (tinted bg + border), React elements default to `'icon'` (fixed-width centered). Override with `startSectionType`/`endSectionType`.
1500
2069
 
1501
2070
  ## Changes
2071
+ ### v0.29.0
2072
+ - **Changed** v2 rewrite: container-first architecture with wrapper div holding focus ring
2073
+ - **Added** `xs` size (28px height)
2074
+ - **Added** `startSection` / `endSection` props replacing `startIcon` / `endIcon` (deprecated but still work)
2075
+ - **Added** `startSectionClickable` / `endSectionClickable` props for interactive sections
2076
+ - **Added** `wrapperClassName` prop for styling the wrapper div (border, bg, ring)
2077
+ - **Changed** Focus ring now on wrapper via `focus-within` (container-level ring, not input-level)
2078
+ - **Changed** Icons auto-sized via `IconProvider` context per input size
2079
+ - **Deprecated** `startIcon` / `endIcon` — use `startSection` / `endSection`
2080
+ - **Added** `startSectionType` / `endSectionType` props — `'icon'` (fixed-width centered cell) or `'label'` (tinted background with border separator). Auto-inferred from content type (strings → label, React elements → icon).
2081
+ - **Changed** Sections use flexbox layout for consistent alignment
2082
+ - **Deprecated** `inputVariants` export — use `inputWrapperVariants` (semantics changed to target wrapper)
2083
+
1502
2084
  ### v0.15.0
1503
2085
  - **Changed** `lg` size font changed from `text-ds-lg` (18px) to `text-ds-md` (14px) — all input sizes now use 14px for consistency
1504
2086
  - **Changed** `md` size font standardized to `text-ds-md` (14px) from mixed values
@@ -1835,6 +2417,7 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1835
2417
  value: number (0-100) — omit for indeterminate
1836
2418
  size: "sm" | "md" | "lg" (track height)
1837
2419
  color: "default" | "success" | "warning" | "error" (indicator color)
2420
+ autoColor: boolean (auto-shifts color by value: 0-59=default, 60-84=warning, 85-100=success, >100=error)
1838
2421
  showLabel: boolean (shows percentage text)
1839
2422
  indicatorClassName: string
1840
2423
 
@@ -1850,8 +2433,12 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
1850
2433
 
1851
2434
  ## Gotchas
1852
2435
  - Omit value (or pass undefined) for indeterminate animation
2436
+ - `autoColor` overrides `color` when `value` is set — do not pass both unless you want autoColor to win
1853
2437
 
1854
2438
  ## Changes
2439
+ ### v0.29.0
2440
+ - **Added** `autoColor` prop — automatically shifts indicator color based on value thresholds (0-59 default, 60-84 warning, 85-100 success, >100 error)
2441
+
1855
2442
  ### v0.1.0
1856
2443
  - **Added** Initial release with `size`, `color`, `indeterminate` variants and optional label slot
1857
2444
  # ProgressRing
@@ -2362,6 +2949,9 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
2362
2949
  - No longer server-safe as of v0.18.0 (uses Framer Motion)
2363
2950
 
2364
2951
  ## Changes
2952
+ ### v0.29.0
2953
+ - **Changed** `bare` variant spinning state now uses `currentColor` instead of `var(--color-accent-9)` — inherits text color from parent for seamless embedding in buttons/toolbars
2954
+
2365
2955
  ### v0.18.0
2366
2956
  - **Changed** (BREAKING) Complete rewrite with Framer Motion arc animation and state transitions
2367
2957
  - **Added** `state` prop: 'spinning' | 'success' | 'error'
@@ -2565,16 +3155,29 @@ import { DataTableToolbar } from '@devalok/shilp-sutra/ui/data-table-toolbar'
2565
3155
  onCheckedChange: (checked: boolean) => void
2566
3156
  error: boolean (shows red border/bg)
2567
3157
  disabled: boolean
3158
+ size: "sm" | "md" | "lg"
3159
+ color: "accent" | "success" | "warning"
3160
+ thumbIcon: ReactNode (icon rendered inside the thumb)
3161
+
3162
+ ## Defaults
3163
+ size="md", color="accent"
2568
3164
 
2569
3165
  ## Example
2570
3166
  ```jsx
2571
3167
  <Switch checked={enabled} onCheckedChange={setEnabled} />
3168
+ <Switch size="lg" color="success" thumbIcon={<IconCheck size={14} />} />
2572
3169
  ```
2573
3170
 
2574
3171
  ## Gotchas
2575
3172
  - Use error prop for validation states (matches Checkbox API)
3173
+ - `error` overrides `color` — when error is true, checked state always uses error-9
2576
3174
 
2577
3175
  ## Changes
3176
+ ### v0.29.0
3177
+ - **Added** `size` prop: `"sm"` (18px track) | `"md"` (24px, default) | `"lg"` (28px track)
3178
+ - **Added** `color` prop: `"accent"` (default) | `"success"` | `"warning"` for checked-state color
3179
+ - **Added** `thumbIcon` prop — renders any ReactNode inside the thumb circle (e.g., check icon)
3180
+
2578
3181
  ### v0.18.0
2579
3182
  - **Changed** Migrated to Framer Motion spring thumb animation
2580
3183
  - **Fixed** Added visible border on unchecked state (`border-surface-border-strong`) — was borderless, making unchecked state hard to see
@@ -3108,6 +3711,7 @@ export default function RootLayout({ children }) {
3108
3711
  maxInitialItems: number — truncate with "Show all (N)" toggle
3109
3712
  groupBy?: 'time' | 'none' — group items by time buckets (today, yesterday, this week, older)
3110
3713
  groupLabels?: GroupLabels — custom labels for time groups: { today?, yesterday?, thisWeek?, older? }
3714
+ renderItem?: (item: ActivityItem, index: number) => ReactNode | undefined — custom renderer per item; return undefined to fall back to default ActivityEntry
3111
3715
 
3112
3716
  ## Defaults
3113
3717
  loading=false, compact=false, hasMore=false, groupBy='none'
@@ -3135,8 +3739,14 @@ export default function RootLayout({ children }) {
3135
3739
  - `maxInitialItems` truncates with a "Show all (N)" toggle button
3136
3740
  - `maxInitialItems` applies to the flat list BEFORE grouping — items are sliced first, then grouped
3137
3741
  - Empty time groups are automatically skipped
3742
+ - `renderItem` receives the item and index; return `undefined` to use the default ActivityEntry rendering
3743
+ - Custom `renderItem` content is wrapped in the same dot + layout container as default entries for consistent vertical rhythm
3138
3744
 
3139
3745
  ## Changes
3746
+ ### v0.29.0
3747
+ - **Added** `renderItem` prop — custom render function per item; return ReactNode for custom rendering, return `undefined` to fall back to default ActivityEntry
3748
+ - **Added** Internal `CustomEntry` wrapper that keeps dot + layout consistent with default entries
3749
+
3140
3750
  ### v0.20.0
3141
3751
  - **Added** `groupBy="time"` prop — groups items into Today, Yesterday, This Week, Older with section headers
3142
3752
  - **Added** `groupLabels` prop for custom group label text
@@ -3158,7 +3768,7 @@ export default function RootLayout({ children }) {
3158
3768
  max: number (default: 4, overflow shows "+N" badge)
3159
3769
  size: "xs" | "sm" | "md" | "lg" | "xl"
3160
3770
  showTooltip: boolean (default: true)
3161
- borderColor: "surface-1" | "surface-2" (default: "surface-2") — overlap border color
3771
+ borderColor: "surface-base" | "surface-raised" (default: "surface-raised") — overlap border color
3162
3772
  onOverflowClick: () => void — makes the "+N" badge interactive (button)
3163
3773
  renderAvatar: (user: AvatarUser, index: number) => ReactNode — custom avatar render
3164
3774
  expandDirection: "left" | "right" (default: "right") — direction group expands on hover
@@ -3168,9 +3778,10 @@ export default function RootLayout({ children }) {
3168
3778
  name: string (REQUIRED)
3169
3779
  image?: string | null
3170
3780
  ring?: "none" | "lead" | "admin" | "client" — role ring per user in group
3781
+ indicator?: "lead" | "admin" | ReactNode — small dot indicator at top-right of avatar
3171
3782
 
3172
3783
  ## Defaults
3173
- size="md", max=4, showTooltip=true, borderColor="surface-2", expandDirection="right", expandAmount="default"
3784
+ size="md", max=4, showTooltip=true, borderColor="surface-raised", expandDirection="right", expandAmount="default"
3174
3785
 
3175
3786
  ## Example
3176
3787
  ```jsx
@@ -3181,7 +3792,7 @@ export default function RootLayout({ children }) {
3181
3792
  ]}
3182
3793
  max={3}
3183
3794
  size="md"
3184
- borderColor="surface-1"
3795
+ borderColor="surface-base"
3185
3796
  onOverflowClick={() => setShowAll(true)}
3186
3797
  />
3187
3798
  ```
@@ -3191,10 +3802,13 @@ export default function RootLayout({ children }) {
3191
3802
  - Users beyond `max` are collapsed into a "+N" overflow badge
3192
3803
  - Missing `image` falls back to initials derived from `name`
3193
3804
  - Hover expand animation uses CSS `group-hover` — parent must not clip overflow
3194
- - `borderColor` should match the surface context the group sits on (e.g., `surface-1` on cards)
3805
+ - `borderColor` should match the surface the group sits on (`surface-base` on page bg, `surface-raised` on cards)
3195
3806
  - `renderAvatar` wrapper is positioning-only — pass `size` directly to your Avatar (do NOT use `className="h-full w-full"`)
3196
3807
 
3197
3808
  ## Changes
3809
+ ### v0.29.0
3810
+ - **Added** `indicator` prop on AvatarUser: `"lead"` (warning dot) | `"admin"` (accent dot) | ReactNode (custom indicator) — animated dot at the top-right corner of each avatar
3811
+
3198
3812
  ### v0.22.3
3199
3813
  - **Fixed** `renderAvatar` wrapper no longer clips consumer Avatar content (removed overflow-hidden, border, and size classes from wrapper)
3200
3814
  - **Fixed** Removed redundant text-size classes from wrapper — Avatar handles font scaling internally
@@ -3203,7 +3817,6 @@ export default function RootLayout({ children }) {
3203
3817
  - **Added** `xs` and `xl` size variants
3204
3818
  - **Added** `borderColor` prop for overlap border matching surface context
3205
3819
  - **Added** `onOverflowClick` prop making the overflow badge an interactive button
3206
- - **Added** `overflowContent` prop for popover content on overflow click
3207
3820
  - **Added** `renderAvatar` prop for custom per-avatar rendering
3208
3821
  - **Added** `AvatarUser.ring` field for per-user role rings in groups
3209
3822
 
@@ -4225,8 +4838,11 @@ Exports: RichTextEditor, RichTextViewer
4225
4838
  onFileUpload?: (file: File) => Promise<{ url: string; name: string; size: number }> — enables file attachments
4226
4839
  mentions?: MentionItem[] — static list for @mention autocomplete
4227
4840
  onMentionSearch?: (query: string) => Promise<MentionItem[]> — async search, takes precedence over static mentions
4841
+ toolbar?: ToolbarItem[] — whitelist of toolbar items to show. Omit to show all.
4228
4842
  onMentionSelect?: (item: MentionItem) => void — called when a mention is selected
4229
4843
 
4844
+ ToolbarItem: 'bold' | 'italic' | 'underline' | 'strike' | 'highlight' | 'h2' | 'h3' | 'blockquote' | 'bulletList' | 'orderedList' | 'taskList' | 'codeBlock' | 'link' | 'image' | 'file' | 'hr' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'emoji' | 'undo' | 'redo'
4845
+
4230
4846
  MentionItem: { id: string; label: string; avatar?: string }
4231
4847
 
4232
4848
  ### RichTextViewer
@@ -4259,6 +4875,9 @@ MentionItem: { id: string; label: string; avatar?: string }
4259
4875
  - Features: bold, italic, underline, strikethrough, highlight, headings, blockquote, lists, task lists, code, links, images, file attachments, mentions, emoji, text alignment, horizontal rule
4260
4876
 
4261
4877
  ## Changes
4878
+ ### v0.30.0
4879
+ - **Added** `toolbar` prop — whitelist of `ToolbarItem` names to control which toolbar buttons appear. Dividers render only between groups that have visible items. `ToolbarItem` type exported from barrel.
4880
+
4262
4881
  ### v0.18.0
4263
4882
  - **Fixed** Use ref to track internal changes, prevent update loop
4264
4883
 
@@ -4357,11 +4976,13 @@ Event colors: "primary" | "success" | "warning" | "error" | "info" | "neutral"
4357
4976
  Note: StatusBadge was server-safe prior to v0.18.0 but is NO LONGER server-safe due to Framer Motion dependency.
4358
4977
 
4359
4978
  ## Props
4360
- status: "active" | "pending" | "approved" | "rejected" | "completed" | "blocked" | "cancelled" | "draft"
4979
+ status: "active" | "pending" | "approved" | "rejected" | "completed" | "blocked" | "in-progress" | "review" | "cancelled" | "draft"
4361
4980
  color: "success" | "warning" | "error" | "info" | "neutral" (overrides status styling when set)
4362
4981
  size: "sm" | "md"
4363
4982
  label: string (auto-derived from status/color if omitted)
4364
4983
  hideDot: boolean (default: false)
4984
+ onClick: () => void (makes badge clickable — renders as button with chevron)
4985
+ icon: ReactNode (custom trailing icon — replaces default chevron when clickable)
4365
4986
 
4366
4987
  ## Defaults
4367
4988
  size="md", hideDot=false
@@ -4370,15 +4991,26 @@ Note: StatusBadge was server-safe prior to v0.18.0 but is NO LONGER server-safe
4370
4991
  ## Example
4371
4992
  ```jsx
4372
4993
  <StatusBadge status="active" />
4994
+ <StatusBadge status="in-progress" />
4995
+ <StatusBadge status="review" label="Needs Review" />
4373
4996
  <StatusBadge color="warning" label="In Review" size="sm" />
4997
+ <StatusBadge status="active" onClick={() => openStatusPicker()} />
4374
4998
  ```
4375
4999
 
4376
5000
  ## Gotchas
4377
5001
  - When `color` is set, it takes priority over `status` for styling
4378
5002
  - Props use a discriminated union — pass either `status` or `color`, not both
4379
5003
  - As of v0.18.0, StatusBadge is NOT server-safe (Framer Motion dependency)
5004
+ - When `onClick` is provided, badge renders as a `<button>` with a trailing chevron icon
5005
+ - Pass `icon` to replace the default chevron with a custom trailing icon
4380
5006
 
4381
5007
  ## Changes
5008
+ ### v0.29.0
5009
+ - **Added** `in-progress` status (accent/blue styling)
5010
+ - **Added** `review` status (info styling)
5011
+ - **Added** `onClick` prop — renders as a `<button>` with trailing chevron and hover opacity
5012
+ - **Added** `icon` prop — custom trailing icon (replaces the default chevron when clickable)
5013
+
4382
5014
  ### v0.18.0
4383
5015
  - **Changed** No longer server-safe due to Framer Motion dependency
4384
5016