@inui/design-system 0.0.0-stage → 1.22.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 (312) hide show
  1. package/README.md +360 -2
  2. package/dist/components/ui/accordion.d.ts +44 -0
  3. package/dist/components/ui/accordion.d.ts.map +1 -0
  4. package/dist/components/ui/accordion.js +86 -0
  5. package/dist/components/ui/accordion.js.map +1 -0
  6. package/dist/components/ui/ai-avatar.d.ts +46 -0
  7. package/dist/components/ui/ai-avatar.d.ts.map +1 -0
  8. package/dist/components/ui/ai-avatar.js +43 -0
  9. package/dist/components/ui/ai-avatar.js.map +1 -0
  10. package/dist/components/ui/ai-label.d.ts +118 -0
  11. package/dist/components/ui/ai-label.d.ts.map +1 -0
  12. package/dist/components/ui/ai-label.js +184 -0
  13. package/dist/components/ui/ai-label.js.map +1 -0
  14. package/dist/components/ui/ai-sparkle.d.ts +91 -0
  15. package/dist/components/ui/ai-sparkle.d.ts.map +1 -0
  16. package/dist/components/ui/ai-sparkle.js +200 -0
  17. package/dist/components/ui/ai-sparkle.js.map +1 -0
  18. package/dist/components/ui/alert-dialog.d.ts +79 -0
  19. package/dist/components/ui/alert-dialog.d.ts.map +1 -0
  20. package/dist/components/ui/alert-dialog.js +147 -0
  21. package/dist/components/ui/alert-dialog.js.map +1 -0
  22. package/dist/components/ui/alert.d.ts +71 -0
  23. package/dist/components/ui/alert.d.ts.map +1 -0
  24. package/dist/components/ui/alert.js +108 -0
  25. package/dist/components/ui/alert.js.map +1 -0
  26. package/dist/components/ui/aspect-ratio.d.ts +30 -0
  27. package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
  28. package/dist/components/ui/aspect-ratio.js +40 -0
  29. package/dist/components/ui/aspect-ratio.js.map +1 -0
  30. package/dist/components/ui/attachment.d.ts +74 -0
  31. package/dist/components/ui/attachment.d.ts.map +1 -0
  32. package/dist/components/ui/attachment.js +142 -0
  33. package/dist/components/ui/attachment.js.map +1 -0
  34. package/dist/components/ui/avatar.d.ts +82 -0
  35. package/dist/components/ui/avatar.d.ts.map +1 -0
  36. package/dist/components/ui/avatar.js +123 -0
  37. package/dist/components/ui/avatar.js.map +1 -0
  38. package/dist/components/ui/badge.d.ts +130 -0
  39. package/dist/components/ui/badge.d.ts.map +1 -0
  40. package/dist/components/ui/badge.js +226 -0
  41. package/dist/components/ui/badge.js.map +1 -0
  42. package/dist/components/ui/breadcrumb.d.ts +43 -0
  43. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  44. package/dist/components/ui/breadcrumb.js +98 -0
  45. package/dist/components/ui/breadcrumb.js.map +1 -0
  46. package/dist/components/ui/browser-tabs.d.ts +218 -0
  47. package/dist/components/ui/browser-tabs.d.ts.map +1 -0
  48. package/dist/components/ui/browser-tabs.js +528 -0
  49. package/dist/components/ui/browser-tabs.js.map +1 -0
  50. package/dist/components/ui/bubble.d.ts +80 -0
  51. package/dist/components/ui/bubble.d.ts.map +1 -0
  52. package/dist/components/ui/bubble.js +235 -0
  53. package/dist/components/ui/bubble.js.map +1 -0
  54. package/dist/components/ui/button-group.d.ts +60 -0
  55. package/dist/components/ui/button-group.d.ts.map +1 -0
  56. package/dist/components/ui/button-group.js +89 -0
  57. package/dist/components/ui/button-group.js.map +1 -0
  58. package/dist/components/ui/button.d.ts +157 -0
  59. package/dist/components/ui/button.d.ts.map +1 -0
  60. package/dist/components/ui/button.js +144 -0
  61. package/dist/components/ui/button.js.map +1 -0
  62. package/dist/components/ui/calendar.d.ts +57 -0
  63. package/dist/components/ui/calendar.d.ts.map +1 -0
  64. package/dist/components/ui/calendar.js +163 -0
  65. package/dist/components/ui/calendar.js.map +1 -0
  66. package/dist/components/ui/card.d.ts +96 -0
  67. package/dist/components/ui/card.d.ts.map +1 -0
  68. package/dist/components/ui/card.js +131 -0
  69. package/dist/components/ui/card.js.map +1 -0
  70. package/dist/components/ui/carousel.d.ts +86 -0
  71. package/dist/components/ui/carousel.d.ts.map +1 -0
  72. package/dist/components/ui/carousel.js +207 -0
  73. package/dist/components/ui/carousel.js.map +1 -0
  74. package/dist/components/ui/checkbox.d.ts +54 -0
  75. package/dist/components/ui/checkbox.d.ts.map +1 -0
  76. package/dist/components/ui/checkbox.js +78 -0
  77. package/dist/components/ui/checkbox.js.map +1 -0
  78. package/dist/components/ui/collapsible.d.ts +45 -0
  79. package/dist/components/ui/collapsible.d.ts.map +1 -0
  80. package/dist/components/ui/collapsible.js +65 -0
  81. package/dist/components/ui/collapsible.js.map +1 -0
  82. package/dist/components/ui/combobox.d.ts +111 -0
  83. package/dist/components/ui/combobox.d.ts.map +1 -0
  84. package/dist/components/ui/combobox.js +264 -0
  85. package/dist/components/ui/combobox.js.map +1 -0
  86. package/dist/components/ui/command.d.ts +71 -0
  87. package/dist/components/ui/command.d.ts.map +1 -0
  88. package/dist/components/ui/command.js +133 -0
  89. package/dist/components/ui/command.js.map +1 -0
  90. package/dist/components/ui/context-menu.d.ts +70 -0
  91. package/dist/components/ui/context-menu.d.ts.map +1 -0
  92. package/dist/components/ui/context-menu.js +163 -0
  93. package/dist/components/ui/context-menu.js.map +1 -0
  94. package/dist/components/ui/count-badge.d.ts +40 -0
  95. package/dist/components/ui/count-badge.d.ts.map +1 -0
  96. package/dist/components/ui/count-badge.js +43 -0
  97. package/dist/components/ui/count-badge.js.map +1 -0
  98. package/dist/components/ui/data-table.d.ts +1026 -0
  99. package/dist/components/ui/data-table.d.ts.map +1 -0
  100. package/dist/components/ui/data-table.js +3423 -0
  101. package/dist/components/ui/data-table.js.map +1 -0
  102. package/dist/components/ui/dialog.d.ts +73 -0
  103. package/dist/components/ui/dialog.d.ts.map +1 -0
  104. package/dist/components/ui/dialog.js +145 -0
  105. package/dist/components/ui/dialog.js.map +1 -0
  106. package/dist/components/ui/direction.d.ts +35 -0
  107. package/dist/components/ui/direction.d.ts.map +1 -0
  108. package/dist/components/ui/direction.js +35 -0
  109. package/dist/components/ui/direction.js.map +1 -0
  110. package/dist/components/ui/drawer.d.ts +53 -0
  111. package/dist/components/ui/drawer.d.ts.map +1 -0
  112. package/dist/components/ui/drawer.js +115 -0
  113. package/dist/components/ui/drawer.js.map +1 -0
  114. package/dist/components/ui/dropdown-menu.d.ts +75 -0
  115. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  116. package/dist/components/ui/dropdown-menu.js +173 -0
  117. package/dist/components/ui/dropdown-menu.js.map +1 -0
  118. package/dist/components/ui/dropzone.d.ts +141 -0
  119. package/dist/components/ui/dropzone.d.ts.map +1 -0
  120. package/dist/components/ui/dropzone.js +257 -0
  121. package/dist/components/ui/dropzone.js.map +1 -0
  122. package/dist/components/ui/empty.d.ts +94 -0
  123. package/dist/components/ui/empty.d.ts.map +1 -0
  124. package/dist/components/ui/empty.js +149 -0
  125. package/dist/components/ui/empty.js.map +1 -0
  126. package/dist/components/ui/field.d.ts +122 -0
  127. package/dist/components/ui/field.d.ts.map +1 -0
  128. package/dist/components/ui/field.js +254 -0
  129. package/dist/components/ui/field.js.map +1 -0
  130. package/dist/components/ui/hover-card.d.ts +51 -0
  131. package/dist/components/ui/hover-card.d.ts.map +1 -0
  132. package/dist/components/ui/hover-card.js +101 -0
  133. package/dist/components/ui/hover-card.js.map +1 -0
  134. package/dist/components/ui/input-group.d.ts +62 -0
  135. package/dist/components/ui/input-group.d.ts.map +1 -0
  136. package/dist/components/ui/input-group.js +115 -0
  137. package/dist/components/ui/input-group.js.map +1 -0
  138. package/dist/components/ui/input-otp.d.ts +52 -0
  139. package/dist/components/ui/input-otp.d.ts.map +1 -0
  140. package/dist/components/ui/input-otp.js +89 -0
  141. package/dist/components/ui/input-otp.js.map +1 -0
  142. package/dist/components/ui/input.d.ts +47 -0
  143. package/dist/components/ui/input.d.ts.map +1 -0
  144. package/dist/components/ui/input.js +47 -0
  145. package/dist/components/ui/input.js.map +1 -0
  146. package/dist/components/ui/item.d.ts +74 -0
  147. package/dist/components/ui/item.d.ts.map +1 -0
  148. package/dist/components/ui/item.js +176 -0
  149. package/dist/components/ui/item.js.map +1 -0
  150. package/dist/components/ui/kbd.d.ts +41 -0
  151. package/dist/components/ui/kbd.d.ts.map +1 -0
  152. package/dist/components/ui/kbd.js +58 -0
  153. package/dist/components/ui/kbd.js.map +1 -0
  154. package/dist/components/ui/key-value-pair.d.ts +56 -0
  155. package/dist/components/ui/key-value-pair.d.ts.map +1 -0
  156. package/dist/components/ui/key-value-pair.js +110 -0
  157. package/dist/components/ui/key-value-pair.js.map +1 -0
  158. package/dist/components/ui/label.d.ts +83 -0
  159. package/dist/components/ui/label.d.ts.map +1 -0
  160. package/dist/components/ui/label.js +85 -0
  161. package/dist/components/ui/label.js.map +1 -0
  162. package/dist/components/ui/marker.d.ts +54 -0
  163. package/dist/components/ui/marker.d.ts.map +1 -0
  164. package/dist/components/ui/marker.js +79 -0
  165. package/dist/components/ui/marker.js.map +1 -0
  166. package/dist/components/ui/menubar.d.ts +72 -0
  167. package/dist/components/ui/menubar.d.ts.map +1 -0
  168. package/dist/components/ui/menubar.js +174 -0
  169. package/dist/components/ui/menubar.js.map +1 -0
  170. package/dist/components/ui/message-scroller.d.ts +53 -0
  171. package/dist/components/ui/message-scroller.d.ts.map +1 -0
  172. package/dist/components/ui/message-scroller.js +95 -0
  173. package/dist/components/ui/message-scroller.js.map +1 -0
  174. package/dist/components/ui/message.d.ts +63 -0
  175. package/dist/components/ui/message.d.ts.map +1 -0
  176. package/dist/components/ui/message.js +92 -0
  177. package/dist/components/ui/message.js.map +1 -0
  178. package/dist/components/ui/meta-list.d.ts +55 -0
  179. package/dist/components/ui/meta-list.d.ts.map +1 -0
  180. package/dist/components/ui/meta-list.js +75 -0
  181. package/dist/components/ui/meta-list.js.map +1 -0
  182. package/dist/components/ui/native-select.d.ts +51 -0
  183. package/dist/components/ui/native-select.d.ts.map +1 -0
  184. package/dist/components/ui/native-select.js +75 -0
  185. package/dist/components/ui/native-select.js.map +1 -0
  186. package/dist/components/ui/navigation-menu.d.ts +56 -0
  187. package/dist/components/ui/navigation-menu.d.ts.map +1 -0
  188. package/dist/components/ui/navigation-menu.js +116 -0
  189. package/dist/components/ui/navigation-menu.js.map +1 -0
  190. package/dist/components/ui/page-header.d.ts +223 -0
  191. package/dist/components/ui/page-header.d.ts.map +1 -0
  192. package/dist/components/ui/page-header.js +233 -0
  193. package/dist/components/ui/page-header.js.map +1 -0
  194. package/dist/components/ui/pagination.d.ts +68 -0
  195. package/dist/components/ui/pagination.d.ts.map +1 -0
  196. package/dist/components/ui/pagination.js +145 -0
  197. package/dist/components/ui/pagination.js.map +1 -0
  198. package/dist/components/ui/popover.d.ts +61 -0
  199. package/dist/components/ui/popover.d.ts.map +1 -0
  200. package/dist/components/ui/popover.js +144 -0
  201. package/dist/components/ui/popover.js.map +1 -0
  202. package/dist/components/ui/progress.d.ts +35 -0
  203. package/dist/components/ui/progress.d.ts.map +1 -0
  204. package/dist/components/ui/progress.js +46 -0
  205. package/dist/components/ui/progress.js.map +1 -0
  206. package/dist/components/ui/questionnaire.d.ts +92 -0
  207. package/dist/components/ui/questionnaire.d.ts.map +1 -0
  208. package/dist/components/ui/questionnaire.js +277 -0
  209. package/dist/components/ui/questionnaire.js.map +1 -0
  210. package/dist/components/ui/radio-group.d.ts +50 -0
  211. package/dist/components/ui/radio-group.d.ts.map +1 -0
  212. package/dist/components/ui/radio-group.js +65 -0
  213. package/dist/components/ui/radio-group.js.map +1 -0
  214. package/dist/components/ui/resizable.d.ts +44 -0
  215. package/dist/components/ui/resizable.d.ts.map +1 -0
  216. package/dist/components/ui/resizable.js +73 -0
  217. package/dist/components/ui/resizable.js.map +1 -0
  218. package/dist/components/ui/scroll-area.d.ts +55 -0
  219. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  220. package/dist/components/ui/scroll-area.js +72 -0
  221. package/dist/components/ui/scroll-area.js.map +1 -0
  222. package/dist/components/ui/select.d.ts +274 -0
  223. package/dist/components/ui/select.d.ts.map +1 -0
  224. package/dist/components/ui/select.js +609 -0
  225. package/dist/components/ui/select.js.map +1 -0
  226. package/dist/components/ui/separator.d.ts +31 -0
  227. package/dist/components/ui/separator.d.ts.map +1 -0
  228. package/dist/components/ui/separator.js +38 -0
  229. package/dist/components/ui/separator.js.map +1 -0
  230. package/dist/components/ui/sheet.d.ts +59 -0
  231. package/dist/components/ui/sheet.d.ts.map +1 -0
  232. package/dist/components/ui/sheet.js +128 -0
  233. package/dist/components/ui/sheet.js.map +1 -0
  234. package/dist/components/ui/sidebar.d.ts +166 -0
  235. package/dist/components/ui/sidebar.d.ts.map +1 -0
  236. package/dist/components/ui/sidebar.js +512 -0
  237. package/dist/components/ui/sidebar.js.map +1 -0
  238. package/dist/components/ui/skeleton.d.ts +35 -0
  239. package/dist/components/ui/skeleton.d.ts.map +1 -0
  240. package/dist/components/ui/skeleton.js +46 -0
  241. package/dist/components/ui/skeleton.js.map +1 -0
  242. package/dist/components/ui/slider.d.ts +39 -0
  243. package/dist/components/ui/slider.d.ts.map +1 -0
  244. package/dist/components/ui/slider.js +75 -0
  245. package/dist/components/ui/slider.js.map +1 -0
  246. package/dist/components/ui/sonner.d.ts +56 -0
  247. package/dist/components/ui/sonner.d.ts.map +1 -0
  248. package/dist/components/ui/sonner.js +96 -0
  249. package/dist/components/ui/sonner.js.map +1 -0
  250. package/dist/components/ui/spinner.d.ts +37 -0
  251. package/dist/components/ui/spinner.d.ts.map +1 -0
  252. package/dist/components/ui/spinner.js +51 -0
  253. package/dist/components/ui/spinner.js.map +1 -0
  254. package/dist/components/ui/stepper.d.ts +84 -0
  255. package/dist/components/ui/stepper.d.ts.map +1 -0
  256. package/dist/components/ui/stepper.js +145 -0
  257. package/dist/components/ui/stepper.js.map +1 -0
  258. package/dist/components/ui/switch.d.ts +36 -0
  259. package/dist/components/ui/switch.d.ts.map +1 -0
  260. package/dist/components/ui/switch.js +44 -0
  261. package/dist/components/ui/switch.js.map +1 -0
  262. package/dist/components/ui/table.d.ts +80 -0
  263. package/dist/components/ui/table.d.ts.map +1 -0
  264. package/dist/components/ui/table.js +130 -0
  265. package/dist/components/ui/table.js.map +1 -0
  266. package/dist/components/ui/tabs.d.ts +87 -0
  267. package/dist/components/ui/tabs.d.ts.map +1 -0
  268. package/dist/components/ui/tabs.js +128 -0
  269. package/dist/components/ui/tabs.js.map +1 -0
  270. package/dist/components/ui/textarea.d.ts +35 -0
  271. package/dist/components/ui/textarea.d.ts.map +1 -0
  272. package/dist/components/ui/textarea.js +38 -0
  273. package/dist/components/ui/textarea.js.map +1 -0
  274. package/dist/components/ui/time-picker.d.ts +58 -0
  275. package/dist/components/ui/time-picker.d.ts.map +1 -0
  276. package/dist/components/ui/time-picker.js +188 -0
  277. package/dist/components/ui/time-picker.js.map +1 -0
  278. package/dist/components/ui/toggle-group.d.ts +52 -0
  279. package/dist/components/ui/toggle-group.d.ts.map +1 -0
  280. package/dist/components/ui/toggle-group.js +94 -0
  281. package/dist/components/ui/toggle-group.js.map +1 -0
  282. package/dist/components/ui/toggle.d.ts +50 -0
  283. package/dist/components/ui/toggle.d.ts.map +1 -0
  284. package/dist/components/ui/toggle.js +70 -0
  285. package/dist/components/ui/toggle.js.map +1 -0
  286. package/dist/components/ui/tooltip.d.ts +51 -0
  287. package/dist/components/ui/tooltip.d.ts.map +1 -0
  288. package/dist/components/ui/tooltip.js +78 -0
  289. package/dist/components/ui/tooltip.js.map +1 -0
  290. package/dist/hooks/use-dark-mode.d.ts +2 -0
  291. package/dist/hooks/use-dark-mode.d.ts.map +1 -0
  292. package/dist/hooks/use-dark-mode.js +36 -0
  293. package/dist/hooks/use-dark-mode.js.map +1 -0
  294. package/dist/hooks/use-mobile.d.ts +2 -0
  295. package/dist/hooks/use-mobile.d.ts.map +1 -0
  296. package/dist/hooks/use-mobile.js +20 -0
  297. package/dist/hooks/use-mobile.js.map +1 -0
  298. package/dist/index.d.ts +88 -0
  299. package/dist/index.d.ts.map +1 -0
  300. package/dist/index.js +77 -0
  301. package/dist/lib/scope.d.ts +64 -0
  302. package/dist/lib/scope.d.ts.map +1 -0
  303. package/dist/lib/scope.js +74 -0
  304. package/dist/lib/scope.js.map +1 -0
  305. package/dist/lib/utils.d.ts +3 -0
  306. package/dist/lib/utils.d.ts.map +1 -0
  307. package/dist/lib/utils.js +11 -0
  308. package/dist/lib/utils.js.map +1 -0
  309. package/dist/styles.css +2 -0
  310. package/dist/theme-tokens.css +537 -0
  311. package/dist/theme.css +417 -0
  312. package/package.json +103 -3
@@ -0,0 +1,118 @@
1
+ /**
2
+ * AILabel — the accessible entry point into an AI explanation.
3
+ *
4
+ * A thin composite over Popover: `AILabelTrigger` is a real, labelled
5
+ * `Button` — a transparent `variant="ghost"` button with a visible
6
+ * `[sparkle] AI` label, not `AISparkle` alone (which is `aria-hidden` and
7
+ * purely a brand mark — see its own file) and not an icon-only button
8
+ * either. Deliberately `ghost`, not `variant="ai"`'s filled AI Chip look
9
+ * (Button's own AI Chip recipe) — the two need to read apart: the Chip is
10
+ * an action, this is a marker/explainability trigger, and a filled pill
11
+ * here would make them visually indistinguishable. The marker has visible
12
+ * "AI" text, not just an accessible name, so the tag itself is the
13
+ * explainability entry point a sighted user can find without hovering.
14
+ * Opens `AILabelContent`, a Popover panel holding a short,
15
+ * first-layer explanation.
16
+ *
17
+ * Marks genuine AI involvement only, never decoration — see manifest.json's
18
+ * `ai_presence` convention for the full rule and every current AI-treatment
19
+ * recipe. This is specifically the "why am I seeing this" affordance; other
20
+ * recipes (Button variant="ai", the AI Card outline, DataTable's row tint)
21
+ * mark AI presence visually but don't need to explain anything on their own.
22
+ */
23
+ import * as React from "react";
24
+ import { Button } from "../../components/ui/button";
25
+ import { Popover, PopoverContent } from "../../components/ui/popover";
26
+ declare function AILabel({ edited, onRevert, revertLabel, ...props }: React.ComponentProps<typeof Popover> & {
27
+ /** True once the user has changed the AI-filled value away from what AI produced. Morphs AILabelTrigger into a ghost undo button (calling `onRevert`) and hides AILabelContent — one slot, two states. */
28
+ edited?: boolean;
29
+ /** Restores the original AI-filled value. Required for the undo button to render; without it `edited` just hides the marker. */
30
+ onRevert?: () => void;
31
+ /** Accessible name of the undo button; make it specific when there's more than one AI-filled field on the page. */
32
+ revertLabel?: string;
33
+ }): React.JSX.Element;
34
+ type AILabelTriggerBaseProps = Omit<React.ComponentProps<typeof Button>, "variant" | "size" | "children" | "asChild" | "aria-label">;
35
+ type AILabelTriggerSize = "default" | "sm";
36
+ type AILabelTriggerProps = AILabelTriggerBaseProps & {
37
+ size?: AILabelTriggerSize;
38
+ } & ({
39
+ iconOnly: true;
40
+ "aria-label": string;
41
+ } | {
42
+ iconOnly?: false;
43
+ "aria-label"?: string;
44
+ });
45
+ declare function AILabelTrigger({ className, size, iconOnly, ...props }: AILabelTriggerProps): React.JSX.Element | null;
46
+ declare function AILabelContent({ className, ...props }: React.ComponentProps<typeof PopoverContent>): React.JSX.Element | null;
47
+ export { AILabel, AILabelTrigger, AILabelContent };
48
+ /**
49
+ * WHEN TO USE
50
+ * - Any specific AI-generated content or suggestion where a user might reasonably ask "why am I seeing this" — a DataTable AI-tinted row, an AI Card summary, an inline AI suggestion in a form
51
+ * - As the accessible explainability entry point required alongside any other AI-presence styling (Button variant="ai", the AI Card outline, DataTable's row tint) — those mark presence, this explains it
52
+ *
53
+ * NOT WHEN
54
+ * - Marking AI presence with no explanation to give — use plain AISparkle or Button variant="ai" instead; don't add AILabel just to decorate something as AI-flavored
55
+ * - A full walkthrough or multi-step explanation — use a Dialog instead; AILabel's popover is for a short, first-layer summary, not a document
56
+ *
57
+ * KEY RULES
58
+ * - size: "default" | "sm" (default) — the marker's height step, matched to the surrounding size/density; both
59
+ * render the visible `[sparkle] AI` text at Button's matching text size. These are the only two to pick from by default.
60
+ * - iconOnly: drops the "AI" text and leaves just the sparkle, square, at that same size step. OPT-IN ONLY — use it
61
+ * solely when a designer explicitly asks for an icon-only marker, never as an automatic "tight space" fallback
62
+ * (DataTable keeps the visible text at every density). Icon-only is a shape, not a size — same two-axis split as
63
+ * Button's own `size` + `iconOnly`.
64
+ * - Without `iconOnly` no `aria-label` is needed — the visible "AI" text is the accessible name; pass one yourself only
65
+ * for a more specific name. With `iconOnly` there's no visible text, so `aria-label` is required — enforced at the
66
+ * type level, same as Button.
67
+ * - AISparkle inside the trigger stays `aria-hidden` (its own default) — the chip's visible "AI" text is what's
68
+ * announced, or with `iconOnly` the required `aria-label`.
69
+ * - An AI-filled value the user can override is the `edited`/`onRevert` pair on AILabel itself,
70
+ * not a hand-rolled swap: pass `edited={value !== aiValue}` and `onRevert={() => setValue(aiValue)}`,
71
+ * and while edited the trigger morphs into a ghost `Undo2Icon` button at the matching icon size
72
+ * (same footprint, no layout shift) that restores the AI value, and AILabelContent stops
73
+ * rendering — nothing left to explain, only something to restore. `revertLabel` names the undo
74
+ * button (default "Undo edit, restore AI value"); make it specific with several AI-filled fields
75
+ * on one page. Input and Textarea (in the label row, beside FieldLabel), Select (beside the trigger)
76
+ * and Checkbox/RadioGroup (beside the option or legend) all use exactly this — see the AI/* stories.
77
+ * - For a second, deeper layer of explanation, nest Collapsible/CollapsibleTrigger/CollapsibleContent inside AILabelContent (e.g. a "Show more" link) rather than opening a second popover — keep the deeper detail inside the same explanation surface, revealed only on request
78
+ * - PopoverTitle/PopoverDescription inside AILabelContent wire up aria-labelledby/aria-describedby automatically, same as any other Popover
79
+ *
80
+ * USAGE
81
+ * <AILabel>
82
+ * <AILabelTrigger />
83
+ * <AILabelContent>
84
+ * <PopoverHeader>
85
+ * <PopoverTitle>Why you're seeing this</PopoverTitle>
86
+ * </PopoverHeader>
87
+ * <PopoverDescription>
88
+ * Flagged because this patient's last visit was over 90 days ago.
89
+ * </PopoverDescription>
90
+ * </AILabelContent>
91
+ * </AILabel>
92
+ *
93
+ * // Icon-only — only when a designer explicitly asks for it; aria-label required
94
+ * <AILabelTrigger iconOnly aria-label="AI-suggested row" />
95
+ *
96
+ * // AI-filled value the user can override: marker while untouched, undo once edited
97
+ * <AILabel edited={value !== AI_VALUE} onRevert={() => setValue(AI_VALUE)}>
98
+ * <AILabelTrigger aria-label="AI-filled: why this value?" />
99
+ * <AILabelContent>...</AILabelContent>
100
+ * </AILabel>
101
+ *
102
+ * // With a second, deeper explanation layer
103
+ * <AILabelContent>
104
+ * <PopoverHeader>
105
+ * <PopoverTitle>Why you're seeing this</PopoverTitle>
106
+ * </PopoverHeader>
107
+ * <PopoverDescription>Flagged for overdue follow-up.</PopoverDescription>
108
+ * <Collapsible>
109
+ * <CollapsibleTrigger asChild>
110
+ * <Button variant="link" size="sm">Show more</Button>
111
+ * </CollapsibleTrigger>
112
+ * <CollapsibleContent className="text-sm text-muted-foreground">
113
+ * Based on the care-gap model, last-visit date, and current risk score.
114
+ * </CollapsibleContent>
115
+ * </Collapsible>
116
+ * </AILabelContent>
117
+ */
118
+ //# sourceMappingURL=ai-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-label.d.ts","sourceRoot":"","sources":["../../../src/components/ui/ai-label.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EACL,OAAO,EACP,cAAc,EAEf,MAAM,yBAAyB,CAAA;AAchC,iBAAS,OAAO,CAAC,EACf,MAAc,EACd,QAAQ,EACR,WAA2C,EAC3C,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,GAAG;IACxC,0MAA0M;IAC1M,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gIAAgI;IAChI,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,mHAAmH;IACnH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAUA;AAED,KAAK,uBAAuB,GAAG,IAAI,CACjC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EACnC,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAC3D,CAAA;AAsBD,KAAK,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAAA;AAK1C,KAAK,mBAAmB,GAAG,uBAAuB,GAAG;IACnD,IAAI,CAAC,EAAE,kBAAkB,CAAA;CAC1B,GAAG,CACE;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAC9C,CAAA;AAKH,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,IAAW,EACX,QAAgB,EAChB,GAAG,KAAK,EACT,EAAE,mBAAmB,4BAiFrB;AAED,iBAAS,cAAc,CAAC,EACtB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,4BAW7C;AAED,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAA;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG"}
@@ -0,0 +1,184 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import { AISparkle } from "./ai-sparkle.js";
3
+ import { Button } from "./button.js";
4
+ import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
5
+ import * as React from "react";
6
+ import { Undo2Icon } from "lucide-react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/ui/ai-label.tsx
9
+ /**
10
+ * AILabel — the accessible entry point into an AI explanation.
11
+ *
12
+ * A thin composite over Popover: `AILabelTrigger` is a real, labelled
13
+ * `Button` — a transparent `variant="ghost"` button with a visible
14
+ * `[sparkle] AI` label, not `AISparkle` alone (which is `aria-hidden` and
15
+ * purely a brand mark — see its own file) and not an icon-only button
16
+ * either. Deliberately `ghost`, not `variant="ai"`'s filled AI Chip look
17
+ * (Button's own AI Chip recipe) — the two need to read apart: the Chip is
18
+ * an action, this is a marker/explainability trigger, and a filled pill
19
+ * here would make them visually indistinguishable. The marker has visible
20
+ * "AI" text, not just an accessible name, so the tag itself is the
21
+ * explainability entry point a sighted user can find without hovering.
22
+ * Opens `AILabelContent`, a Popover panel holding a short,
23
+ * first-layer explanation.
24
+ *
25
+ * Marks genuine AI involvement only, never decoration — see manifest.json's
26
+ * `ai_presence` convention for the full rule and every current AI-treatment
27
+ * recipe. This is specifically the "why am I seeing this" affordance; other
28
+ * recipes (Button variant="ai", the AI Card outline, DataTable's row tint)
29
+ * mark AI presence visually but don't need to explain anything on their own.
30
+ */
31
+ var AILabelContext = React.createContext({
32
+ edited: false,
33
+ revertLabel: "Undo edit, restore AI value"
34
+ });
35
+ function AILabel({ edited = false, onRevert, revertLabel = "Undo edit, restore AI value", ...props }) {
36
+ const context = React.useMemo(() => ({
37
+ edited,
38
+ onRevert,
39
+ revertLabel
40
+ }), [
41
+ edited,
42
+ onRevert,
43
+ revertLabel
44
+ ]);
45
+ return /* @__PURE__ */ jsx(AILabelContext.Provider, {
46
+ value: context,
47
+ children: /* @__PURE__ */ jsx(Popover, {
48
+ "data-slot": "ai-label",
49
+ ...props
50
+ })
51
+ });
52
+ }
53
+ var BUTTON_SIZE_BY_AILABEL_TRIGGER_SIZE = {
54
+ default: "default",
55
+ sm: "xs"
56
+ };
57
+ function AILabelTrigger({ className, size = "sm", iconOnly = false, ...props }) {
58
+ const { edited, onRevert, revertLabel } = React.useContext(AILabelContext);
59
+ const buttonSize = BUTTON_SIZE_BY_AILABEL_TRIGGER_SIZE[size];
60
+ if (edited) {
61
+ if (!onRevert) return null;
62
+ const rest = Object.fromEntries(Object.entries(props).filter(([key]) => key !== "aria-label"));
63
+ return /* @__PURE__ */ jsx(Button, {
64
+ type: "button",
65
+ variant: "ghost",
66
+ size: buttonSize,
67
+ iconOnly: true,
68
+ "aria-label": revertLabel,
69
+ "data-slot": "ai-label-revert",
70
+ className,
71
+ onClick: onRevert,
72
+ ...rest,
73
+ children: /* @__PURE__ */ jsx(Undo2Icon, {})
74
+ });
75
+ }
76
+ if (iconOnly) {
77
+ const { "aria-label": ariaLabel, ...rest } = props;
78
+ return /* @__PURE__ */ jsx(PopoverTrigger, {
79
+ asChild: true,
80
+ children: /* @__PURE__ */ jsx(Button, {
81
+ variant: "ghost",
82
+ size: buttonSize,
83
+ iconOnly: true,
84
+ "aria-label": ariaLabel,
85
+ className,
86
+ ...rest,
87
+ children: /* @__PURE__ */ jsx(AISparkle, {})
88
+ })
89
+ });
90
+ }
91
+ return /* @__PURE__ */ jsx(PopoverTrigger, {
92
+ asChild: true,
93
+ children: /* @__PURE__ */ jsxs(Button, {
94
+ variant: "ghost",
95
+ size: buttonSize,
96
+ className,
97
+ ...props,
98
+ children: [/* @__PURE__ */ jsx(AISparkle, { "data-icon": "inline-start" }), "AI"]
99
+ })
100
+ });
101
+ }
102
+ function AILabelContent({ className, ...props }) {
103
+ const { edited } = React.useContext(AILabelContext);
104
+ if (edited) return null;
105
+ return /* @__PURE__ */ jsx(PopoverContent, {
106
+ "data-slot": "ai-label-content",
107
+ className: cn("w-80", className),
108
+ ...props
109
+ });
110
+ }
111
+ /**
112
+ * WHEN TO USE
113
+ * - Any specific AI-generated content or suggestion where a user might reasonably ask "why am I seeing this" — a DataTable AI-tinted row, an AI Card summary, an inline AI suggestion in a form
114
+ * - As the accessible explainability entry point required alongside any other AI-presence styling (Button variant="ai", the AI Card outline, DataTable's row tint) — those mark presence, this explains it
115
+ *
116
+ * NOT WHEN
117
+ * - Marking AI presence with no explanation to give — use plain AISparkle or Button variant="ai" instead; don't add AILabel just to decorate something as AI-flavored
118
+ * - A full walkthrough or multi-step explanation — use a Dialog instead; AILabel's popover is for a short, first-layer summary, not a document
119
+ *
120
+ * KEY RULES
121
+ * - size: "default" | "sm" (default) — the marker's height step, matched to the surrounding size/density; both
122
+ * render the visible `[sparkle] AI` text at Button's matching text size. These are the only two to pick from by default.
123
+ * - iconOnly: drops the "AI" text and leaves just the sparkle, square, at that same size step. OPT-IN ONLY — use it
124
+ * solely when a designer explicitly asks for an icon-only marker, never as an automatic "tight space" fallback
125
+ * (DataTable keeps the visible text at every density). Icon-only is a shape, not a size — same two-axis split as
126
+ * Button's own `size` + `iconOnly`.
127
+ * - Without `iconOnly` no `aria-label` is needed — the visible "AI" text is the accessible name; pass one yourself only
128
+ * for a more specific name. With `iconOnly` there's no visible text, so `aria-label` is required — enforced at the
129
+ * type level, same as Button.
130
+ * - AISparkle inside the trigger stays `aria-hidden` (its own default) — the chip's visible "AI" text is what's
131
+ * announced, or with `iconOnly` the required `aria-label`.
132
+ * - An AI-filled value the user can override is the `edited`/`onRevert` pair on AILabel itself,
133
+ * not a hand-rolled swap: pass `edited={value !== aiValue}` and `onRevert={() => setValue(aiValue)}`,
134
+ * and while edited the trigger morphs into a ghost `Undo2Icon` button at the matching icon size
135
+ * (same footprint, no layout shift) that restores the AI value, and AILabelContent stops
136
+ * rendering — nothing left to explain, only something to restore. `revertLabel` names the undo
137
+ * button (default "Undo edit, restore AI value"); make it specific with several AI-filled fields
138
+ * on one page. Input and Textarea (in the label row, beside FieldLabel), Select (beside the trigger)
139
+ * and Checkbox/RadioGroup (beside the option or legend) all use exactly this — see the AI/* stories.
140
+ * - For a second, deeper layer of explanation, nest Collapsible/CollapsibleTrigger/CollapsibleContent inside AILabelContent (e.g. a "Show more" link) rather than opening a second popover — keep the deeper detail inside the same explanation surface, revealed only on request
141
+ * - PopoverTitle/PopoverDescription inside AILabelContent wire up aria-labelledby/aria-describedby automatically, same as any other Popover
142
+ *
143
+ * USAGE
144
+ * <AILabel>
145
+ * <AILabelTrigger />
146
+ * <AILabelContent>
147
+ * <PopoverHeader>
148
+ * <PopoverTitle>Why you're seeing this</PopoverTitle>
149
+ * </PopoverHeader>
150
+ * <PopoverDescription>
151
+ * Flagged because this patient's last visit was over 90 days ago.
152
+ * </PopoverDescription>
153
+ * </AILabelContent>
154
+ * </AILabel>
155
+ *
156
+ * // Icon-only — only when a designer explicitly asks for it; aria-label required
157
+ * <AILabelTrigger iconOnly aria-label="AI-suggested row" />
158
+ *
159
+ * // AI-filled value the user can override: marker while untouched, undo once edited
160
+ * <AILabel edited={value !== AI_VALUE} onRevert={() => setValue(AI_VALUE)}>
161
+ * <AILabelTrigger aria-label="AI-filled: why this value?" />
162
+ * <AILabelContent>...</AILabelContent>
163
+ * </AILabel>
164
+ *
165
+ * // With a second, deeper explanation layer
166
+ * <AILabelContent>
167
+ * <PopoverHeader>
168
+ * <PopoverTitle>Why you're seeing this</PopoverTitle>
169
+ * </PopoverHeader>
170
+ * <PopoverDescription>Flagged for overdue follow-up.</PopoverDescription>
171
+ * <Collapsible>
172
+ * <CollapsibleTrigger asChild>
173
+ * <Button variant="link" size="sm">Show more</Button>
174
+ * </CollapsibleTrigger>
175
+ * <CollapsibleContent className="text-sm text-muted-foreground">
176
+ * Based on the care-gap model, last-visit date, and current risk score.
177
+ * </CollapsibleContent>
178
+ * </Collapsible>
179
+ * </AILabelContent>
180
+ */
181
+ //#endregion
182
+ export { AILabel, AILabelContent, AILabelTrigger };
183
+
184
+ //# sourceMappingURL=ai-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-label.js","names":[],"sources":["../../../src/components/ui/ai-label.tsx"],"sourcesContent":["/**\n * AILabel — the accessible entry point into an AI explanation.\n *\n * A thin composite over Popover: `AILabelTrigger` is a real, labelled\n * `Button` — a transparent `variant=\"ghost\"` button with a visible\n * `[sparkle] AI` label, not `AISparkle` alone (which is `aria-hidden` and\n * purely a brand mark — see its own file) and not an icon-only button\n * either. Deliberately `ghost`, not `variant=\"ai\"`'s filled AI Chip look\n * (Button's own AI Chip recipe) — the two need to read apart: the Chip is\n * an action, this is a marker/explainability trigger, and a filled pill\n * here would make them visually indistinguishable. The marker has visible\n * \"AI\" text, not just an accessible name, so the tag itself is the\n * explainability entry point a sighted user can find without hovering.\n * Opens `AILabelContent`, a Popover panel holding a short,\n * first-layer explanation.\n *\n * Marks genuine AI involvement only, never decoration — see manifest.json's\n * `ai_presence` convention for the full rule and every current AI-treatment\n * recipe. This is specifically the \"why am I seeing this\" affordance; other\n * recipes (Button variant=\"ai\", the AI Card outline, DataTable's row tint)\n * mark AI presence visually but don't need to explain anything on their own.\n */\n\nimport * as React from \"react\"\nimport { Undo2Icon } from \"lucide-react\"\n\nimport { AISparkle } from \"@/components/ui/ai-sparkle\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\"\nimport { cn } from \"@/lib/utils\"\n\n// `edited`/`onRevert` live on the root, not the trigger, so the trigger\n// AND the content both react from one place: while `edited`, the trigger\n// morphs into a ghost undo button and the content stops rendering — the\n// explanation is for AI's value, and once the user has overridden it there\n// is nothing left to explain, only something to restore.\nconst AILabelContext = React.createContext<{\n edited: boolean\n onRevert?: () => void\n revertLabel: string\n}>({ edited: false, revertLabel: \"Undo edit, restore AI value\" })\n\nfunction AILabel({\n edited = false,\n onRevert,\n revertLabel = \"Undo edit, restore AI value\",\n ...props\n}: React.ComponentProps<typeof Popover> & {\n /** True once the user has changed the AI-filled value away from what AI produced. Morphs AILabelTrigger into a ghost undo button (calling `onRevert`) and hides AILabelContent — one slot, two states. */\n edited?: boolean\n /** Restores the original AI-filled value. Required for the undo button to render; without it `edited` just hides the marker. */\n onRevert?: () => void\n /** Accessible name of the undo button; make it specific when there's more than one AI-filled field on the page. */\n revertLabel?: string\n}) {\n const context = React.useMemo(\n () => ({ edited, onRevert, revertLabel }),\n [edited, onRevert, revertLabel]\n )\n return (\n <AILabelContext.Provider value={context}>\n <Popover data-slot=\"ai-label\" {...props} />\n </AILabelContext.Provider>\n )\n}\n\ntype AILabelTriggerBaseProps = Omit<\n React.ComponentProps<typeof Button>,\n \"variant\" | \"size\" | \"children\" | \"asChild\" | \"aria-label\"\n>\n\n// Two axes, same as Button: `size` (\"default\" | \"sm\") is how tall the\n// marker is; `iconOnly` drops the visible \"AI\" text and leaves just the\n// sparkle, square, at that same size step. Icon-only is opt-in only — for\n// when a designer explicitly asks for it; the visible-text marker is the\n// default at every density. Like Button's own `iconOnly`, it needs an\n// `aria-label` since there's no text left for a screen reader to fall back\n// on; with the visible \"AI\" text, `aria-label` stays optional (only for a\n// more specific name than \"AI\").\n//\n// `sm` renders at Button's `xs` step (24px, h-6), not Button's own `sm`\n// (28px, h-7) — AILabelTrigger's \"sm\" is its own size name, not a literal\n// passthrough of Button's. 24px is a deliberate floor, not a shrink for\n// its own sake: it's the exact minimum WCAG 2.5.8 (AA) requires (24×24 CSS\n// px), which the marker clears on both axes since the visible \"AI\" text\n// keeps its rendered width well over 24px regardless of height — so this\n// is legal at every step down to here, just with no margin left below it.\n// Matching this to the fixed-height label row in Field/FieldLabel and\n// Field/FieldLegend (their own `aiLabel` slot) is exactly why this exists:\n// one component-level default instead of a `className=\"h-6\"` override at\n// every call site.\ntype AILabelTriggerSize = \"default\" | \"sm\"\nconst BUTTON_SIZE_BY_AILABEL_TRIGGER_SIZE = {\n default: \"default\",\n sm: \"xs\",\n} as const\ntype AILabelTriggerProps = AILabelTriggerBaseProps & {\n size?: AILabelTriggerSize\n} & (\n | { iconOnly: true; \"aria-label\": string }\n | { iconOnly?: false; \"aria-label\"?: string }\n )\n\n// The undo button is the same size step as the marker it replaces, just\n// square (`iconOnly`) — so the slot keeps its footprint when they swap.\n\nfunction AILabelTrigger({\n className,\n size = \"sm\",\n iconOnly = false,\n ...props\n}: AILabelTriggerProps) {\n const { edited, onRevert, revertLabel } = React.useContext(AILabelContext)\n const buttonSize = BUTTON_SIZE_BY_AILABEL_TRIGGER_SIZE[size]\n\n // Overridden by the user: same slot, same footprint (the matching icon-*\n // size, so the row doesn't shift when the two states swap), but a plain\n // ghost undo button instead of the marker — and NOT a PopoverTrigger, so\n // there's no popover to open. `onRevert` is what makes this an undo;\n // without it there's nothing to restore, so the slot just renders empty.\n if (edited) {\n if (!onRevert) return null\n // Drop the marker's own aria-label — the undo button has its own name.\n const rest = Object.fromEntries(\n Object.entries(props).filter(([key]) => key !== \"aria-label\")\n )\n return (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size={buttonSize}\n iconOnly\n aria-label={revertLabel}\n data-slot=\"ai-label-revert\"\n className={className}\n onClick={onRevert}\n {...(rest as Omit<\n React.ComponentProps<typeof Button>,\n \"size\" | \"aria-label\"\n >)}\n >\n <Undo2Icon />\n </Button>\n )\n }\n\n // variant=\"ghost\", not \"ai\" — a filled AI Chip (Button's own AI Chip\n // recipe: variant=\"ai\" rounded-full) is visually near-identical to this,\n // and the two need to read apart: the Chip is an action, this is a\n // marker/explainability trigger. Visible \"AI\" text is the accessible\n // name (WCAG 2.5.3 label-in-name) — pass aria-label yourself only to say\n // something more specific than \"AI\". With `iconOnly` there's no visible\n // text at all (just the sparkle), so `aria-label` is required the same\n // way Button's own `iconOnly` requires it. Two explicit branches (not a\n // single computed `iconOnly`) because Button's prop type is a\n // discriminated union keyed on the literal, which a boolean can't satisfy.\n if (iconOnly) {\n const { \"aria-label\": ariaLabel, ...rest } = props as {\n \"aria-label\": string\n }\n return (\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size={buttonSize}\n iconOnly\n aria-label={ariaLabel}\n className={className}\n {...rest}\n >\n <AISparkle />\n </Button>\n </PopoverTrigger>\n )\n }\n\n return (\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size={buttonSize}\n className={className}\n {...(props as Omit<\n React.ComponentProps<typeof Button>,\n \"size\" | \"iconOnly\" | \"asChild\"\n >)}\n >\n <AISparkle data-icon=\"inline-start\" />\n AI\n </Button>\n </PopoverTrigger>\n )\n}\n\nfunction AILabelContent({\n className,\n ...props\n}: React.ComponentProps<typeof PopoverContent>) {\n const { edited } = React.useContext(AILabelContext)\n // Nothing to explain once the user has overridden the value.\n if (edited) return null\n return (\n <PopoverContent\n data-slot=\"ai-label-content\"\n className={cn(\"w-80\", className)}\n {...props}\n />\n )\n}\n\nexport { AILabel, AILabelTrigger, AILabelContent }\n\n/**\n * WHEN TO USE\n * - Any specific AI-generated content or suggestion where a user might reasonably ask \"why am I seeing this\" — a DataTable AI-tinted row, an AI Card summary, an inline AI suggestion in a form\n * - As the accessible explainability entry point required alongside any other AI-presence styling (Button variant=\"ai\", the AI Card outline, DataTable's row tint) — those mark presence, this explains it\n *\n * NOT WHEN\n * - Marking AI presence with no explanation to give — use plain AISparkle or Button variant=\"ai\" instead; don't add AILabel just to decorate something as AI-flavored\n * - A full walkthrough or multi-step explanation — use a Dialog instead; AILabel's popover is for a short, first-layer summary, not a document\n *\n * KEY RULES\n * - size: \"default\" | \"sm\" (default) — the marker's height step, matched to the surrounding size/density; both\n * render the visible `[sparkle] AI` text at Button's matching text size. These are the only two to pick from by default.\n * - iconOnly: drops the \"AI\" text and leaves just the sparkle, square, at that same size step. OPT-IN ONLY — use it\n * solely when a designer explicitly asks for an icon-only marker, never as an automatic \"tight space\" fallback\n * (DataTable keeps the visible text at every density). Icon-only is a shape, not a size — same two-axis split as\n * Button's own `size` + `iconOnly`.\n * - Without `iconOnly` no `aria-label` is needed — the visible \"AI\" text is the accessible name; pass one yourself only\n * for a more specific name. With `iconOnly` there's no visible text, so `aria-label` is required — enforced at the\n * type level, same as Button.\n * - AISparkle inside the trigger stays `aria-hidden` (its own default) — the chip's visible \"AI\" text is what's\n * announced, or with `iconOnly` the required `aria-label`.\n * - An AI-filled value the user can override is the `edited`/`onRevert` pair on AILabel itself,\n * not a hand-rolled swap: pass `edited={value !== aiValue}` and `onRevert={() => setValue(aiValue)}`,\n * and while edited the trigger morphs into a ghost `Undo2Icon` button at the matching icon size\n * (same footprint, no layout shift) that restores the AI value, and AILabelContent stops\n * rendering — nothing left to explain, only something to restore. `revertLabel` names the undo\n * button (default \"Undo edit, restore AI value\"); make it specific with several AI-filled fields\n * on one page. Input and Textarea (in the label row, beside FieldLabel), Select (beside the trigger)\n * and Checkbox/RadioGroup (beside the option or legend) all use exactly this — see the AI/* stories.\n * - For a second, deeper layer of explanation, nest Collapsible/CollapsibleTrigger/CollapsibleContent inside AILabelContent (e.g. a \"Show more\" link) rather than opening a second popover — keep the deeper detail inside the same explanation surface, revealed only on request\n * - PopoverTitle/PopoverDescription inside AILabelContent wire up aria-labelledby/aria-describedby automatically, same as any other Popover\n *\n * USAGE\n * <AILabel>\n * <AILabelTrigger />\n * <AILabelContent>\n * <PopoverHeader>\n * <PopoverTitle>Why you're seeing this</PopoverTitle>\n * </PopoverHeader>\n * <PopoverDescription>\n * Flagged because this patient's last visit was over 90 days ago.\n * </PopoverDescription>\n * </AILabelContent>\n * </AILabel>\n *\n * // Icon-only — only when a designer explicitly asks for it; aria-label required\n * <AILabelTrigger iconOnly aria-label=\"AI-suggested row\" />\n *\n * // AI-filled value the user can override: marker while untouched, undo once edited\n * <AILabel edited={value !== AI_VALUE} onRevert={() => setValue(AI_VALUE)}>\n * <AILabelTrigger aria-label=\"AI-filled: why this value?\" />\n * <AILabelContent>...</AILabelContent>\n * </AILabel>\n *\n * // With a second, deeper explanation layer\n * <AILabelContent>\n * <PopoverHeader>\n * <PopoverTitle>Why you're seeing this</PopoverTitle>\n * </PopoverHeader>\n * <PopoverDescription>Flagged for overdue follow-up.</PopoverDescription>\n * <Collapsible>\n * <CollapsibleTrigger asChild>\n * <Button variant=\"link\" size=\"sm\">Show more</Button>\n * </CollapsibleTrigger>\n * <CollapsibleContent className=\"text-sm text-muted-foreground\">\n * Based on the care-gap model, last-visit date, and current risk score.\n * </CollapsibleContent>\n * </Collapsible>\n * </AILabelContent>\n */\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAM,iBAAiB,MAAM,cAI1B;CAAE,QAAQ;CAAO,aAAa;AAA8B,CAAC;AAEhE,SAAS,QAAQ,EACf,SAAS,OACT,UACA,cAAc,+BACd,GAAG,SAQF;CACD,MAAM,UAAU,MAAM,eACb;EAAE;EAAQ;EAAU;CAAY,IACvC;EAAC;EAAQ;EAAU;CAAW,CAChC;CACA,OACE,oBAAC,eAAe,UAAhB;EAAyB,OAAO;EAC9B,UAAA,oBAAC,SAAD;GAAS,aAAU;GAAW,GAAI;EAAQ,CAAA;CACnB,CAAA;AAE7B;AA4BA,IAAM,sCAAsC;CAC1C,SAAS;CACT,IAAI;AACN;AAWA,SAAS,eAAe,EACtB,WACA,OAAO,MACP,WAAW,OACX,GAAG,SACmB;CACtB,MAAM,EAAE,QAAQ,UAAU,gBAAgB,MAAM,WAAW,cAAc;CACzE,MAAM,aAAa,oCAAoC;CAOvD,IAAI,QAAQ;EACV,IAAI,CAAC,UAAU,OAAO;EAEtB,MAAM,OAAO,OAAO,YAClB,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,SAAS,QAAQ,YAAY,CAC9D;EACA,OACE,oBAAC,QAAD;GACE,MAAK;GACL,SAAQ;GACR,MAAM;GACN,UAAA;GACA,cAAY;GACZ,aAAU;GACC;GACX,SAAS;GACT,GAAK;GAKL,UAAA,oBAAC,WAAD,CAAY,CAAA;EACN,CAAA;CAEZ;CAYA,IAAI,UAAU;EACZ,MAAM,EAAE,cAAc,WAAW,GAAG,SAAS;EAG7C,OACE,oBAAC,gBAAD;GAAgB,SAAA;GACd,UAAA,oBAAC,QAAD;IACE,SAAQ;IACR,MAAM;IACN,UAAA;IACA,cAAY;IACD;IACX,GAAI;IAEJ,UAAA,oBAAC,WAAD,CAAY,CAAA;GACN,CAAA;EACM,CAAA;CAEpB;CAEA,OACE,oBAAC,gBAAD;EAAgB,SAAA;EACd,UAAA,qBAAC,QAAD;GACE,SAAQ;GACR,MAAM;GACK;GACX,GAAK;GAJP,UAAA,CASE,oBAAC,WAAD,EAAW,aAAU,eAAgB,CAAA,GAAC,IAEhC;;CACM,CAAA;AAEpB;AAEA,SAAS,eAAe,EACtB,WACA,GAAG,SAC2C;CAC9C,MAAM,EAAE,WAAW,MAAM,WAAW,cAAc;CAElD,IAAI,QAAQ,OAAO;CACnB,OACE,oBAAC,gBAAD;EACE,aAAU;EACV,WAAW,GAAG,QAAQ,SAAS;EAC/B,GAAI;CACL,CAAA;AAEL"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * AISparkle — the single AI-identity mark ("Sara") for all AI-generated
3
+ * content and affordances: one gradient 4-pointed star (pink→orange, washed
4
+ * with yellow), per the AI Identity tokens in theme.css. Path/gradient/shine
5
+ * geometry is a hand-authored SVG — not lucide-react, since the two-layer
6
+ * gradient fill and animated states aren't expressible with a plain stroked
7
+ * icon.
8
+ *
9
+ * `size` is optional by design rather than a fixed pixel width/height —
10
+ * omit it to inherit whatever ambient icon sizing applies (e.g. a Button's
11
+ * `[&_svg]` rule), or set it for a token-derived size when used standalone.
12
+ *
13
+ * Purely a brand/identity mark — `aria-hidden` by default (see below) — not
14
+ * an accessible trigger for anything. It marks genuine AI involvement only,
15
+ * never decoration; see manifest.json's `ai_presence` convention for the
16
+ * full rule and every current AI-treatment recipe. When the surface needs
17
+ * an actual accessible entry point into an explanation (not just a visual
18
+ * marker), that's AILabel's job, not this icon's.
19
+ */
20
+ import * as React from "react";
21
+ import { type VariantProps } from "class-variance-authority";
22
+ declare const aiSparkleSizeVariants: (props?: ({
23
+ size?: "sm" | "md" | "lg" | null | undefined;
24
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
25
+ declare const aiSparkleStateVariants: (props?: ({
26
+ state?: "default" | "listening" | "short-processing" | "long-processing" | null | undefined;
27
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
28
+ declare function AISparkle({ className, size, state, tone, ...props }: React.ComponentProps<"svg"> & VariantProps<typeof aiSparkleSizeVariants> & VariantProps<typeof aiSparkleStateVariants> & {
29
+ /**
30
+ * "gradient" (default) — the branded two-layer pink/orange/yellow fill.
31
+ * "mono" — a flat `fill="currentColor"` star, no gradient defs, no
32
+ * long-processing shine (that needs the gradient's own white-wash
33
+ * layer). For placing the mark on top of an already-colored surface
34
+ * (e.g. AIAvatar's gradient background) where the full brand gradient
35
+ * would read as muddy — set the icon's own text color instead.
36
+ */
37
+ tone?: "gradient" | "mono";
38
+ }): React.JSX.Element;
39
+ export { AISparkle, aiSparkleSizeVariants, aiSparkleStateVariants };
40
+ /**
41
+ * WHEN TO USE
42
+ * - AI message avatars, empty-state headers, agent badges, copilot-assisted
43
+ * actions, AI-generated content markers.
44
+ * - Compose it as a Button's leading icon (`data-icon="inline-start"`) or a
45
+ * small corner accent on an icon-only trigger.
46
+ *
47
+ * NOT WHEN
48
+ * - Generic decorative sparkle unrelated to AI — use lucide's `Sparkles`.
49
+ * - A loading spinner — use Spinner; AISparkle's `state` communicates AI
50
+ * activity specifically, not generic async work.
51
+ * - Animated backgrounds or large decorative use — this is an identity mark,
52
+ * not a motion asset.
53
+ *
54
+ * KEY RULES
55
+ * - size: sm | md | lg — optional. Leave it unset when composing inside Button
56
+ * (its `[&_svg]` sizing takes over, same as any other icon) so it inherits
57
+ * the ambient icon size; set it explicitly for a token-derived size
58
+ * (size-4/5/6) when used standalone.
59
+ * - state: default | listening | short-processing | long-processing (default
60
+ * "default") — each maps to its own keyframe animation in
61
+ * src/styles/ai-sparkle.css (imported once via index.css, not per-usage):
62
+ * listening bobs vertically, short-processing does one full rotation then
63
+ * holds, long-processing pulses in scale AND sweeps a diagonal shine
64
+ * across the star, clipped to its silhouette. These are custom keyframes,
65
+ * not Tailwind's animate-bounce/spin/pulse — the motion curves and hold
66
+ * points are specific to this mark. The shine's -50px..50px translate is
67
+ * calibrated directly for this component's own 80x80 viewBox + 120x120
68
+ * rect — no extra scaling wrapper needed. All three loop `infinite` for as
69
+ * long as the state is active (not just long-processing — a slow
70
+ * short-processing step or a long listening session runs past 5s just as
71
+ * easily), so `ai-sparkle.css` falls every one of them back to the static
72
+ * resting mark under `prefers-reduced-motion` (SC 2.2.2, Level A).
73
+ * - tone: gradient (default) | mono — gradient is the fixed brand fill, driven
74
+ * by the `--color-ai-grad-*` / `--color-ai-wash*` tokens in theme.css, no
75
+ * color props of its own. mono drops to a flat `fill="currentColor"` star
76
+ * (no gradient defs, no long-processing shine) — set the icon's own text
77
+ * color instead when it needs to sit on top of an already-colored surface
78
+ * (e.g. AIAvatar's gradient background) where the full brand gradient
79
+ * would read as muddy.
80
+ * - `aria-hidden` by default, matching every other decorative icon in this
81
+ * system — pair it with real text, not as a standalone control.
82
+ *
83
+ * USAGE
84
+ * <Button variant="ai">
85
+ * <AISparkle data-icon="inline-start" />
86
+ * Summarize
87
+ * </Button>
88
+ *
89
+ * <AISparkle size="lg" state="short-processing" />
90
+ */
91
+ //# sourceMappingURL=ai-sparkle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sparkle.d.ts","sourceRoot":"","sources":["../../../src/components/ui/ai-sparkle.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAOjE,QAAA,MAAM,qBAAqB;;8EAQzB,CAAA;AAEF,QAAA,MAAM,sBAAsB;;8EAY1B,CAAA;AAEF,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,IAAI,EACJ,KAAiB,EACjB,IAAiB,EACjB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAC5B,YAAY,CAAC,OAAO,qBAAqB,CAAC,GAC1C,YAAY,CAAC,OAAO,sBAAsB,CAAC,GAAG;IAC5C;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAAA;CAC3B,qBA0FF;AAED,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG"}
@@ -0,0 +1,200 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import * as React from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { cva } from "class-variance-authority";
5
+ //#region src/components/ui/ai-sparkle.tsx
6
+ /**
7
+ * AISparkle — the single AI-identity mark ("Sara") for all AI-generated
8
+ * content and affordances: one gradient 4-pointed star (pink→orange, washed
9
+ * with yellow), per the AI Identity tokens in theme.css. Path/gradient/shine
10
+ * geometry is a hand-authored SVG — not lucide-react, since the two-layer
11
+ * gradient fill and animated states aren't expressible with a plain stroked
12
+ * icon.
13
+ *
14
+ * `size` is optional by design rather than a fixed pixel width/height —
15
+ * omit it to inherit whatever ambient icon sizing applies (e.g. a Button's
16
+ * `[&_svg]` rule), or set it for a token-derived size when used standalone.
17
+ *
18
+ * Purely a brand/identity mark — `aria-hidden` by default (see below) — not
19
+ * an accessible trigger for anything. It marks genuine AI involvement only,
20
+ * never decoration; see manifest.json's `ai_presence` convention for the
21
+ * full rule and every current AI-treatment recipe. When the surface needs
22
+ * an actual accessible entry point into an explanation (not just a visual
23
+ * marker), that's AILabel's job, not this icon's.
24
+ */
25
+ var STAR_PATH = "M40 5C40 24.33 55.67 40 75 40C55.67 40 40 55.67 40 75C40 55.67 24.33 40 5 40C24.33 40 40 24.33 40 5Z";
26
+ var aiSparkleSizeVariants = cva("", { variants: { size: {
27
+ sm: "size-4",
28
+ md: "size-5",
29
+ lg: "size-6"
30
+ } } });
31
+ var aiSparkleStateVariants = cva("", {
32
+ variants: { state: {
33
+ default: "",
34
+ listening: "mmds-ai-sparkle-listening",
35
+ "short-processing": "mmds-ai-sparkle-short-processing",
36
+ "long-processing": "mmds-ai-sparkle-long-processing"
37
+ } },
38
+ defaultVariants: { state: "default" }
39
+ });
40
+ function AISparkle({ className, size, state = "default", tone = "gradient", ...props }) {
41
+ const id = React.useId();
42
+ return /* @__PURE__ */ jsx("svg", {
43
+ "data-slot": "ai-sparkle",
44
+ "data-state": state,
45
+ "data-tone": tone,
46
+ xmlns: "http://www.w3.org/2000/svg",
47
+ viewBox: "0 0 80 80",
48
+ fill: "none",
49
+ className: cn("shrink-0", size && aiSparkleSizeVariants({ size }), aiSparkleStateVariants({ state }), className),
50
+ "aria-hidden": "true",
51
+ ...props,
52
+ children: tone === "mono" ? /* @__PURE__ */ jsx("path", {
53
+ d: STAR_PATH,
54
+ fill: "currentColor"
55
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
56
+ /* @__PURE__ */ jsxs("defs", { children: [
57
+ /* @__PURE__ */ jsxs("linearGradient", {
58
+ id: `${id}-grad`,
59
+ x1: "17.1875",
60
+ y1: "12.1875",
61
+ x2: "62.0313",
62
+ y2: "65",
63
+ gradientUnits: "userSpaceOnUse",
64
+ children: [/* @__PURE__ */ jsx("stop", {
65
+ stopColor: "var(--color-ai-grad-from)",
66
+ stopOpacity: "var(--color-ai-grad-from-alpha)"
67
+ }), /* @__PURE__ */ jsx("stop", {
68
+ offset: "1",
69
+ stopColor: "var(--color-ai-grad-to)"
70
+ })]
71
+ }),
72
+ /* @__PURE__ */ jsxs("linearGradient", {
73
+ id: `${id}-wash`,
74
+ x1: "17.1875",
75
+ y1: "12.1875",
76
+ x2: "62.0313",
77
+ y2: "65",
78
+ gradientUnits: "userSpaceOnUse",
79
+ children: [/* @__PURE__ */ jsx("stop", {
80
+ stopColor: "var(--color-ai-wash)",
81
+ stopOpacity: "0"
82
+ }), /* @__PURE__ */ jsx("stop", {
83
+ offset: "1",
84
+ stopColor: "var(--color-ai-wash)"
85
+ })]
86
+ }),
87
+ state === "long-processing" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("clipPath", {
88
+ id: `${id}-clip`,
89
+ children: /* @__PURE__ */ jsx("path", { d: STAR_PATH })
90
+ }), /* @__PURE__ */ jsxs("linearGradient", {
91
+ id: `${id}-shine`,
92
+ x1: "0",
93
+ y1: "0",
94
+ x2: "1",
95
+ y2: "1",
96
+ children: [
97
+ /* @__PURE__ */ jsx("stop", {
98
+ offset: "0",
99
+ stopColor: "#fff",
100
+ stopOpacity: "0"
101
+ }),
102
+ /* @__PURE__ */ jsx("stop", {
103
+ offset: "0.4",
104
+ stopColor: "#fff",
105
+ stopOpacity: "0"
106
+ }),
107
+ /* @__PURE__ */ jsx("stop", {
108
+ offset: "0.5",
109
+ stopColor: "#fff",
110
+ stopOpacity: "0.55"
111
+ }),
112
+ /* @__PURE__ */ jsx("stop", {
113
+ offset: "0.6",
114
+ stopColor: "#fff",
115
+ stopOpacity: "0"
116
+ }),
117
+ /* @__PURE__ */ jsx("stop", {
118
+ offset: "1",
119
+ stopColor: "#fff",
120
+ stopOpacity: "0"
121
+ })
122
+ ]
123
+ })] })
124
+ ] }),
125
+ /* @__PURE__ */ jsx("path", {
126
+ d: STAR_PATH,
127
+ fill: `url(#${id}-grad)`
128
+ }),
129
+ /* @__PURE__ */ jsx("path", {
130
+ d: STAR_PATH,
131
+ fill: `url(#${id}-wash)`,
132
+ opacity: "var(--color-ai-wash-alpha)"
133
+ }),
134
+ state === "long-processing" && /* @__PURE__ */ jsx("rect", {
135
+ x: "-20",
136
+ y: "-20",
137
+ width: "120",
138
+ height: "120",
139
+ fill: `url(#${id}-shine)`,
140
+ clipPath: `url(#${id}-clip)`,
141
+ className: "mmds-ai-sparkle-shine"
142
+ })
143
+ ] })
144
+ });
145
+ }
146
+ /**
147
+ * WHEN TO USE
148
+ * - AI message avatars, empty-state headers, agent badges, copilot-assisted
149
+ * actions, AI-generated content markers.
150
+ * - Compose it as a Button's leading icon (`data-icon="inline-start"`) or a
151
+ * small corner accent on an icon-only trigger.
152
+ *
153
+ * NOT WHEN
154
+ * - Generic decorative sparkle unrelated to AI — use lucide's `Sparkles`.
155
+ * - A loading spinner — use Spinner; AISparkle's `state` communicates AI
156
+ * activity specifically, not generic async work.
157
+ * - Animated backgrounds or large decorative use — this is an identity mark,
158
+ * not a motion asset.
159
+ *
160
+ * KEY RULES
161
+ * - size: sm | md | lg — optional. Leave it unset when composing inside Button
162
+ * (its `[&_svg]` sizing takes over, same as any other icon) so it inherits
163
+ * the ambient icon size; set it explicitly for a token-derived size
164
+ * (size-4/5/6) when used standalone.
165
+ * - state: default | listening | short-processing | long-processing (default
166
+ * "default") — each maps to its own keyframe animation in
167
+ * src/styles/ai-sparkle.css (imported once via index.css, not per-usage):
168
+ * listening bobs vertically, short-processing does one full rotation then
169
+ * holds, long-processing pulses in scale AND sweeps a diagonal shine
170
+ * across the star, clipped to its silhouette. These are custom keyframes,
171
+ * not Tailwind's animate-bounce/spin/pulse — the motion curves and hold
172
+ * points are specific to this mark. The shine's -50px..50px translate is
173
+ * calibrated directly for this component's own 80x80 viewBox + 120x120
174
+ * rect — no extra scaling wrapper needed. All three loop `infinite` for as
175
+ * long as the state is active (not just long-processing — a slow
176
+ * short-processing step or a long listening session runs past 5s just as
177
+ * easily), so `ai-sparkle.css` falls every one of them back to the static
178
+ * resting mark under `prefers-reduced-motion` (SC 2.2.2, Level A).
179
+ * - tone: gradient (default) | mono — gradient is the fixed brand fill, driven
180
+ * by the `--color-ai-grad-*` / `--color-ai-wash*` tokens in theme.css, no
181
+ * color props of its own. mono drops to a flat `fill="currentColor"` star
182
+ * (no gradient defs, no long-processing shine) — set the icon's own text
183
+ * color instead when it needs to sit on top of an already-colored surface
184
+ * (e.g. AIAvatar's gradient background) where the full brand gradient
185
+ * would read as muddy.
186
+ * - `aria-hidden` by default, matching every other decorative icon in this
187
+ * system — pair it with real text, not as a standalone control.
188
+ *
189
+ * USAGE
190
+ * <Button variant="ai">
191
+ * <AISparkle data-icon="inline-start" />
192
+ * Summarize
193
+ * </Button>
194
+ *
195
+ * <AISparkle size="lg" state="short-processing" />
196
+ */
197
+ //#endregion
198
+ export { AISparkle, aiSparkleSizeVariants, aiSparkleStateVariants };
199
+
200
+ //# sourceMappingURL=ai-sparkle.js.map