@nocoo/basalt 2.0.3 → 2.1.2

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 (295) hide show
  1. package/README.md +134 -15
  2. package/ai/COMPATIBILITY.md +180 -0
  3. package/ai/INTEGRATION.md +1760 -0
  4. package/ai/RECIPES.md +161 -0
  5. package/ai/USAGE.md +99 -0
  6. package/ai/registry.json +38099 -0
  7. package/ai/sources.json +10 -0
  8. package/dist/assets/brand-mark.d.ts +1 -0
  9. package/dist/assets/brand-mark.js +7 -0
  10. package/dist/assets/brand-mark.js.map +1 -0
  11. package/dist/charts/area.d.ts +58 -5
  12. package/dist/charts/area.js +20 -12
  13. package/dist/charts/area.js.map +1 -1
  14. package/dist/charts/bar.d.ts +54 -5
  15. package/dist/charts/bar.js +16 -10
  16. package/dist/charts/bar.js.map +1 -1
  17. package/dist/charts/bullet.d.ts +18 -1
  18. package/dist/charts/bullet.js +6 -3
  19. package/dist/charts/bullet.js.map +1 -1
  20. package/dist/charts/charts.d.ts +58 -5
  21. package/dist/charts/charts.js +13 -2
  22. package/dist/charts/charts.js.map +1 -1
  23. package/dist/charts/config.d.ts +7 -0
  24. package/dist/charts/config.js +5 -1
  25. package/dist/charts/config.js.map +1 -1
  26. package/dist/charts/custom-chart.d.ts +58 -5
  27. package/dist/charts/custom-chart.js +13 -2
  28. package/dist/charts/custom-chart.js.map +1 -1
  29. package/dist/charts/donut.d.ts +18 -1
  30. package/dist/charts/donut.js +6 -3
  31. package/dist/charts/donut.js.map +1 -1
  32. package/dist/charts/frame.d.ts +40 -3
  33. package/dist/charts/frame.js +55 -28
  34. package/dist/charts/frame.js.map +1 -1
  35. package/dist/charts/funnel.d.ts +18 -1
  36. package/dist/charts/funnel.js +5 -1
  37. package/dist/charts/funnel.js.map +1 -1
  38. package/dist/charts/gauge.d.ts +18 -1
  39. package/dist/charts/gauge.js +38 -20
  40. package/dist/charts/gauge.js.map +1 -1
  41. package/dist/charts/grouped-bar.d.ts +54 -5
  42. package/dist/charts/grouped-bar.js +16 -10
  43. package/dist/charts/grouped-bar.js.map +1 -1
  44. package/dist/charts/heatmap-calendar.d.ts +61 -0
  45. package/dist/charts/heatmap-calendar.js +287 -96
  46. package/dist/charts/heatmap-calendar.js.map +1 -1
  47. package/dist/charts/heatmap-matrix.d.ts +99 -0
  48. package/dist/charts/heatmap-matrix.js +249 -0
  49. package/dist/charts/heatmap-matrix.js.map +1 -0
  50. package/dist/charts/line.d.ts +66 -5
  51. package/dist/charts/line.js +37 -30
  52. package/dist/charts/line.js.map +1 -1
  53. package/dist/charts/palette.d.ts +1 -0
  54. package/dist/charts/palette.js +7 -1
  55. package/dist/charts/palette.js.map +1 -1
  56. package/dist/charts/radar.d.ts +18 -1
  57. package/dist/charts/radar.js +26 -19
  58. package/dist/charts/radar.js.map +1 -1
  59. package/dist/charts/sankey.d.ts +18 -1
  60. package/dist/charts/sankey.js +4 -1
  61. package/dist/charts/sankey.js.map +1 -1
  62. package/dist/charts/series.d.ts +5 -7
  63. package/dist/charts/series.js.map +1 -1
  64. package/dist/charts/slot-bar.d.ts +38 -6
  65. package/dist/charts/slot-bar.js +5 -2
  66. package/dist/charts/slot-bar.js.map +1 -1
  67. package/dist/charts/sparkline.d.ts +35 -5
  68. package/dist/charts/sparkline.js +22 -18
  69. package/dist/charts/sparkline.js.map +1 -1
  70. package/dist/charts/stacked-bar.d.ts +58 -5
  71. package/dist/charts/stacked-bar.js +19 -12
  72. package/dist/charts/stacked-bar.js.map +1 -1
  73. package/dist/charts/stat-card.d.ts +51 -1
  74. package/dist/charts/stat-card.js +66 -39
  75. package/dist/charts/stat-card.js.map +1 -1
  76. package/dist/charts/timeline.d.ts +3 -0
  77. package/dist/charts/timeline.js +4 -3
  78. package/dist/charts/timeline.js.map +1 -1
  79. package/dist/charts/timeseries.d.ts +58 -5
  80. package/dist/charts/timeseries.js +13 -2
  81. package/dist/charts/timeseries.js.map +1 -1
  82. package/dist/charts/tooltip.d.ts +57 -0
  83. package/dist/charts/tooltip.js +119 -2
  84. package/dist/charts/tooltip.js.map +1 -1
  85. package/dist/components/accordion.d.ts +117 -2
  86. package/dist/components/accordion.js +20 -13
  87. package/dist/components/accordion.js.map +1 -1
  88. package/dist/components/alert-dialog.d.ts +131 -13
  89. package/dist/components/alert-dialog.js +12 -9
  90. package/dist/components/alert-dialog.js.map +1 -1
  91. package/dist/components/app-header.js +18 -17
  92. package/dist/components/app-header.js.map +1 -1
  93. package/dist/components/app-shell.js +15 -14
  94. package/dist/components/app-shell.js.map +1 -1
  95. package/dist/components/autocomplete.d.ts +4 -0
  96. package/dist/components/autocomplete.js.map +1 -1
  97. package/dist/components/avatar.d.ts +76 -3
  98. package/dist/components/avatar.js.map +1 -1
  99. package/dist/components/badge.d.ts +15 -1
  100. package/dist/components/badge.js +13 -12
  101. package/dist/components/badge.js.map +1 -1
  102. package/dist/components/banner.d.ts +88 -2
  103. package/dist/components/banner.js +41 -40
  104. package/dist/components/banner.js.map +1 -1
  105. package/dist/components/basalt-mark.js +14 -8
  106. package/dist/components/basalt-mark.js.map +1 -1
  107. package/dist/components/battery-meter.d.ts +10 -0
  108. package/dist/components/battery-meter.js +56 -0
  109. package/dist/components/battery-meter.js.map +1 -0
  110. package/dist/components/breadcrumbs.d.ts +27 -7
  111. package/dist/components/breadcrumbs.js +21 -20
  112. package/dist/components/breadcrumbs.js.map +1 -1
  113. package/dist/components/button.d.ts +48 -2
  114. package/dist/components/button.js +87 -36
  115. package/dist/components/button.js.map +1 -1
  116. package/dist/components/chat-bubble.d.ts +7 -0
  117. package/dist/components/chat-bubble.js +14 -13
  118. package/dist/components/chat-bubble.js.map +1 -1
  119. package/dist/components/chat-composer.d.ts +7 -3
  120. package/dist/components/chat-composer.js.map +1 -1
  121. package/dist/components/chat-header.d.ts +6 -1
  122. package/dist/components/chat-header.js +17 -16
  123. package/dist/components/chat-header.js.map +1 -1
  124. package/dist/components/chat-inbox.d.ts +7 -2
  125. package/dist/components/chat-inbox.js.map +1 -1
  126. package/dist/components/checkbox.js +84 -53
  127. package/dist/components/checkbox.js.map +1 -1
  128. package/dist/components/clipboard-text.d.ts +13 -2
  129. package/dist/components/clipboard-text.js +4 -4
  130. package/dist/components/clipboard-text.js.map +1 -1
  131. package/dist/components/collapsible.d.ts +58 -15
  132. package/dist/components/collapsible.js.map +1 -1
  133. package/dist/components/command-palette.d.ts +151 -61
  134. package/dist/components/command-palette.js.map +1 -1
  135. package/dist/components/confirm-dialog.d.ts +1 -1
  136. package/dist/components/confirm-dialog.js +6 -4
  137. package/dist/components/confirm-dialog.js.map +1 -1
  138. package/dist/components/context-menu.d.ts +210 -2
  139. package/dist/components/context-menu.js +7 -4
  140. package/dist/components/context-menu.js.map +1 -1
  141. package/dist/components/data-table.d.ts +40 -2
  142. package/dist/components/data-table.js +168 -120
  143. package/dist/components/data-table.js.map +1 -1
  144. package/dist/components/date-picker.d.ts +53 -2
  145. package/dist/components/date-picker.js +387 -219
  146. package/dist/components/date-picker.js.map +1 -1
  147. package/dist/components/delete-resource.d.ts +7 -1
  148. package/dist/components/delete-resource.js +22 -18
  149. package/dist/components/delete-resource.js.map +1 -1
  150. package/dist/components/description-list.js +16 -15
  151. package/dist/components/description-list.js.map +1 -1
  152. package/dist/components/dialog.d.ts +141 -12
  153. package/dist/components/dialog.js +42 -38
  154. package/dist/components/dialog.js.map +1 -1
  155. package/dist/components/dock.d.ts +1 -1
  156. package/dist/components/dock.js +39 -58
  157. package/dist/components/dock.js.map +1 -1
  158. package/dist/components/dropdown-menu.d.ts +229 -15
  159. package/dist/components/dropdown-menu.js +9 -6
  160. package/dist/components/dropdown-menu.js.map +1 -1
  161. package/dist/components/editable-nav-item.d.ts +33 -0
  162. package/dist/components/editable-nav-item.js +90 -0
  163. package/dist/components/editable-nav-item.js.map +1 -0
  164. package/dist/components/empty.d.ts +22 -2
  165. package/dist/components/empty.js +22 -13
  166. package/dist/components/empty.js.map +1 -1
  167. package/dist/components/field.js +31 -30
  168. package/dist/components/field.js.map +1 -1
  169. package/dist/components/file-dropzone.d.ts +37 -0
  170. package/dist/components/file-dropzone.js +103 -0
  171. package/dist/components/file-dropzone.js.map +1 -0
  172. package/dist/components/filter-bar.d.ts +34 -0
  173. package/dist/components/filter-bar.js +52 -0
  174. package/dist/components/filter-bar.js.map +1 -0
  175. package/dist/components/flow.js +19 -18
  176. package/dist/components/flow.js.map +1 -1
  177. package/dist/components/grid.d.ts +3 -1
  178. package/dist/components/grid.js.map +1 -1
  179. package/dist/components/hover-card.d.ts +111 -1
  180. package/dist/components/hover-card.js +9 -6
  181. package/dist/components/hover-card.js.map +1 -1
  182. package/dist/components/icon-picker.d.ts +34 -0
  183. package/dist/components/icon-picker.js +77 -0
  184. package/dist/components/icon-picker.js.map +1 -0
  185. package/dist/components/inline-editable.d.ts +46 -0
  186. package/dist/components/inline-editable.js +124 -0
  187. package/dist/components/inline-editable.js.map +1 -0
  188. package/dist/components/input-area.js +3 -3
  189. package/dist/components/input-group.js +7 -7
  190. package/dist/components/input.js +3 -3
  191. package/dist/components/label.js +18 -17
  192. package/dist/components/label.js.map +1 -1
  193. package/dist/components/layer-card.d.ts +10 -2
  194. package/dist/components/layer-card.js +71 -68
  195. package/dist/components/layer-card.js.map +1 -1
  196. package/dist/components/loader.d.ts +10 -2
  197. package/dist/components/loader.js.map +1 -1
  198. package/dist/components/menu-bar.d.ts +174 -6
  199. package/dist/components/menu-bar.js +33 -29
  200. package/dist/components/menu-bar.js.map +1 -1
  201. package/dist/components/meter.d.ts +27 -2
  202. package/dist/components/meter.js.map +1 -1
  203. package/dist/components/multi-select.d.ts +64 -0
  204. package/dist/components/multi-select.js +218 -0
  205. package/dist/components/multi-select.js.map +1 -0
  206. package/dist/components/navigation-menu.d.ts +88 -5
  207. package/dist/components/navigation-menu.js.map +1 -1
  208. package/dist/components/overlay.js +6 -5
  209. package/dist/components/overlay.js.map +1 -1
  210. package/dist/components/page-header.js +23 -21
  211. package/dist/components/page-header.js.map +1 -1
  212. package/dist/components/pagination.js +7 -7
  213. package/dist/components/popover.d.ts +136 -16
  214. package/dist/components/popover.js +32 -28
  215. package/dist/components/popover.js.map +1 -1
  216. package/dist/components/radio.js +38 -37
  217. package/dist/components/radio.js.map +1 -1
  218. package/dist/components/resource-list.d.ts +26 -1
  219. package/dist/components/resource-list.js +28 -15
  220. package/dist/components/resource-list.js.map +1 -1
  221. package/dist/components/responsive-master-detail.d.ts +27 -0
  222. package/dist/components/responsive-master-detail.js +85 -0
  223. package/dist/components/responsive-master-detail.js.map +1 -0
  224. package/dist/components/section-rule.js +23 -22
  225. package/dist/components/section-rule.js.map +1 -1
  226. package/dist/components/select.d.ts +27 -3
  227. package/dist/components/select.js +11 -11
  228. package/dist/components/select.js.map +1 -1
  229. package/dist/components/sheet.d.ts +115 -11
  230. package/dist/components/sheet.js +37 -30
  231. package/dist/components/sheet.js.map +1 -1
  232. package/dist/components/sidebar.d.ts +109 -17
  233. package/dist/components/sidebar.js +112 -111
  234. package/dist/components/sidebar.js.map +1 -1
  235. package/dist/components/skeleton-line.d.ts +22 -2
  236. package/dist/components/skeleton-line.js.map +1 -1
  237. package/dist/components/slider.d.ts +61 -1
  238. package/dist/components/slider.js +31 -15
  239. package/dist/components/slider.js.map +1 -1
  240. package/dist/components/stat-strip.js +16 -15
  241. package/dist/components/stat-strip.js.map +1 -1
  242. package/dist/components/switch.js +78 -47
  243. package/dist/components/switch.js.map +1 -1
  244. package/dist/components/table-of-contents.js +18 -17
  245. package/dist/components/table-of-contents.js.map +1 -1
  246. package/dist/components/table-pager.js +20 -19
  247. package/dist/components/table-pager.js.map +1 -1
  248. package/dist/components/table.d.ts +9 -3
  249. package/dist/components/table.js +19 -18
  250. package/dist/components/table.js.map +1 -1
  251. package/dist/components/tabs.js +29 -22
  252. package/dist/components/tabs.js.map +1 -1
  253. package/dist/components/tag-badge.d.ts +58 -0
  254. package/dist/components/tag-badge.js +72 -0
  255. package/dist/components/tag-badge.js.map +1 -0
  256. package/dist/components/tag-color-picker.d.ts +20 -0
  257. package/dist/components/tag-color-picker.js +42 -0
  258. package/dist/components/tag-color-picker.js.map +1 -0
  259. package/dist/components/text.js +17 -16
  260. package/dist/components/text.js.map +1 -1
  261. package/dist/components/toast.d.ts +173 -3
  262. package/dist/components/toast.js +1 -1
  263. package/dist/components/toast.js.map +1 -1
  264. package/dist/components/toggle-group.d.ts +88 -2
  265. package/dist/components/toggle-group.js +32 -31
  266. package/dist/components/toggle-group.js.map +1 -1
  267. package/dist/components/toggle.d.ts +35 -2
  268. package/dist/components/toggle.js +12 -11
  269. package/dist/components/toggle.js.map +1 -1
  270. package/dist/components/tooltip.d.ts +144 -6
  271. package/dist/components/tooltip.js +9 -6
  272. package/dist/components/tooltip.js.map +1 -1
  273. package/dist/components/typeahead-field.js +193 -132
  274. package/dist/components/typeahead-field.js.map +1 -1
  275. package/dist/components/upload-queue.d.ts +51 -0
  276. package/dist/components/upload-queue.js +116 -0
  277. package/dist/components/upload-queue.js.map +1 -0
  278. package/dist/providers/accent.d.ts +46 -2
  279. package/dist/providers/accent.js +358 -172
  280. package/dist/providers/accent.js.map +1 -1
  281. package/dist/providers/link.d.ts +14 -4
  282. package/dist/providers/link.js.map +1 -1
  283. package/dist/providers/theme.d.ts +38 -2
  284. package/dist/providers/theme.js +160 -27
  285. package/dist/providers/theme.js.map +1 -1
  286. package/dist/styles/base.css +83 -0
  287. package/dist/styles/standalone.css +699 -120
  288. package/dist/styles/tailwind.css +21 -7
  289. package/dist/styles/tokens.css +145 -91
  290. package/dist/utils/control-surface.d.ts +2 -1
  291. package/dist/utils/control-surface.js +4 -4
  292. package/dist/utils/control-surface.js.map +1 -1
  293. package/dist/utils/selection-indicator.js +2 -2
  294. package/dist/utils/selection-indicator.js.map +1 -1
  295. package/package.json +8 -4
@@ -1,9 +1,29 @@
1
1
  import type { ReactNode } from "react";
2
- export declare function Breadcrumbs({ items, className, }: {
3
- items: {
4
- href?: string;
5
- label: ReactNode;
6
- icon?: ReactNode;
7
- }[];
2
+ export interface BreadcrumbItem {
3
+ /**
4
+ * Navigation target URL. When omitted, item renders as unlinked text (and only receives aria-current="page" if it is the final item).
5
+ */
6
+ href?: string;
7
+ /**
8
+ * Content label rendered inside the breadcrumb segment.
9
+ */
10
+ label: ReactNode;
11
+ /**
12
+ * Optional leading icon rendered before the label.
13
+ */
14
+ icon?: ReactNode;
15
+ }
16
+ export interface BreadcrumbsProps {
17
+ /**
18
+ * List of breadcrumb segments ({ label: ReactNode, href?: string, icon?: ReactNode }[]) in hierarchical order from root to current page.
19
+ * Only the final item in the array without an href receives aria-current="page".
20
+ * Interactive items render via Link, delegating routing to LinkProvider when configured.
21
+ * Component only accepts items and className without native HTML rest attribute forwarding or forwarded ref.
22
+ */
23
+ items: BreadcrumbItem[];
24
+ /**
25
+ * Optional class name applied to the outer nav element.
26
+ */
8
27
  className?: string;
9
- }): import("react").JSX.Element;
28
+ }
29
+ export declare function Breadcrumbs({ items, className }: BreadcrumbsProps): import("react").JSX.Element;
@@ -1,38 +1,39 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { Link as t } from "./link.js";
4
- import { ChevronRight as n } from "lucide-react";
5
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
3
+ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
+ import { Link as n } from "./link.js";
5
+ import { ChevronRight as r } from "lucide-react";
6
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
6
7
  //#region src/components/breadcrumbs.tsx
7
- var a = "text-sm font-normal";
8
- function o({ items: o, className: s }) {
9
- return /* @__PURE__ */ r("nav", {
8
+ var o = "text-sm font-normal";
9
+ function s({ items: s, className: c }) {
10
+ return /* @__PURE__ */ i("nav", {
10
11
  "aria-label": "Breadcrumb",
11
- className: e("flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground", s),
12
- children: o.map((s, c) => {
13
- let l = c === o.length - 1 && !s.href, u = s.icon ? /* @__PURE__ */ i("span", {
12
+ className: e(t, "flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground", c),
13
+ children: s.map((t, c) => {
14
+ let l = c === s.length - 1 && !t.href, u = t.icon ? /* @__PURE__ */ a("span", {
14
15
  className: "inline-flex items-center gap-1",
15
- children: [s.icon, s.label]
16
- }) : s.label;
17
- return /* @__PURE__ */ i("span", {
16
+ children: [t.icon, t.label]
17
+ }) : t.label;
18
+ return /* @__PURE__ */ a("span", {
18
19
  className: "flex items-center gap-1",
19
- children: [c > 0 ? /* @__PURE__ */ r(n, {
20
+ children: [c > 0 ? /* @__PURE__ */ i(r, {
20
21
  className: "size-3",
21
22
  "aria-hidden": "true"
22
- }) : null, s.href ? /* @__PURE__ */ r(t, {
23
- href: s.href,
24
- className: e(a, "text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground"),
23
+ }) : null, t.href ? /* @__PURE__ */ i(n, {
24
+ href: t.href,
25
+ className: e(o, "text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground"),
25
26
  children: u
26
- }) : /* @__PURE__ */ r("span", {
27
+ }) : /* @__PURE__ */ i("span", {
27
28
  "aria-current": l ? "page" : void 0,
28
- className: e(a, l ? "text-basalt-foreground" : "text-basalt-muted-foreground"),
29
+ className: e(o, l ? "text-basalt-foreground" : "text-basalt-muted-foreground"),
29
30
  children: u
30
31
  })]
31
- }, `${String(s.label)}-${c}`);
32
+ }, `${String(t.label)}-${c}`);
32
33
  })
33
34
  });
34
35
  }
35
36
  //#endregion
36
- export { o as Breadcrumbs };
37
+ export { s as Breadcrumbs };
37
38
 
38
39
  //# sourceMappingURL=breadcrumbs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumbs.js","names":[],"sources":["../../src/components/breadcrumbs.tsx"],"sourcesContent":["import { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Link } from \"./link\";\n\nconst crumbTypeClass = \"text-sm font-normal\";\n\nexport function Breadcrumbs({\n\titems,\n\tclassName,\n}: {\n\titems: { href?: string; label: ReactNode; icon?: ReactNode }[];\n\tclassName?: string;\n}) {\n\treturn (\n\t\t<nav\n\t\t\taria-label=\"Breadcrumb\"\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{items.map((item, index) => {\n\t\t\t\tconst current = index === items.length - 1 && !item.href;\n\t\t\t\tconst label = item.icon ? (\n\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t{item.icon}\n\t\t\t\t\t\t{item.label}\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\titem.label\n\t\t\t\t);\n\t\t\t\treturn (\n\t\t\t\t\t<span key={`${String(item.label)}-${index}`} className=\"flex items-center gap-1\">\n\t\t\t\t\t\t{index > 0 ? <ChevronRight className=\"size-3\" aria-hidden=\"true\" /> : null}\n\t\t\t\t\t\t{item.href ? (\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\thref={item.href}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\t\"text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\taria-current={current ? \"page\" : undefined}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\tcurrent ? \"text-basalt-foreground\" : \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</span>\n\t\t\t\t);\n\t\t\t})}\n\t\t</nav>\n\t);\n}\n"],"mappings":";;;;;;AAKA,IAAM,IAAiB;AAEvB,SAAgB,EAAY,EAC3B,UACA,gBAIE;CACF,OACC,kBAAC,OAAD;EACC,cAAW;EACX,WAAW,EACV,4EACA,CACD;EAEC,UAAA,EAAM,KAAK,GAAM,MAAU;GAC3B,IAAM,IAAU,MAAU,EAAM,SAAS,KAAK,CAAC,EAAK,MAC9C,IAAQ,EAAK,OAClB,kBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA,CACE,EAAK,MACL,EAAK,KACD;GAEN,CAAA,IAAA,EAAK;GAEN,OACC,kBAAC,QAAD;IAA6C,WAAU;IAAvD,UAAA,CACE,IAAQ,IAAI,kBAAC,GAAD;KAAc,WAAU;KAAS,eAAY;IAAQ,CAAA,IAAI,MACrE,EAAK,OACL,kBAAC,GAAD;KACC,MAAM,EAAK;KACX,WAAW,EACV,GACA,0FACD;KAEC,UAAA;IACI,CAAA,IAEN,kBAAC,QAAD;KACC,gBAAc,IAAU,SAAS,KAAA;KACjC,WAAW,EACV,GACA,IAAU,2BAA2B,8BACtC;KAEC,UAAA;IACI,CAAA,CAEF;GAvBK,GAAA,GAAG,OAAO,EAAK,KAAK,EAAE,GAAG,GAuB9B;EAER,CAAC;CACG,CAAA;AAEP"}
1
+ {"version":3,"file":"breadcrumbs.js","names":[],"sources":["../../src/components/breadcrumbs.tsx"],"sourcesContent":["import { ChevronRight } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { Link } from \"./link\";\n\nconst crumbTypeClass = \"text-sm font-normal\";\n\nexport interface BreadcrumbItem {\n\t/**\n\t * Navigation target URL. When omitted, item renders as unlinked text (and only receives aria-current=\"page\" if it is the final item).\n\t */\n\thref?: string;\n\t/**\n\t * Content label rendered inside the breadcrumb segment.\n\t */\n\tlabel: ReactNode;\n\t/**\n\t * Optional leading icon rendered before the label.\n\t */\n\ticon?: ReactNode;\n}\n\nexport interface BreadcrumbsProps {\n\t/**\n\t * List of breadcrumb segments ({ label: ReactNode, href?: string, icon?: ReactNode }[]) in hierarchical order from root to current page.\n\t * Only the final item in the array without an href receives aria-current=\"page\".\n\t * Interactive items render via Link, delegating routing to LinkProvider when configured.\n\t * Component only accepts items and className without native HTML rest attribute forwarding or forwarded ref.\n\t */\n\titems: BreadcrumbItem[];\n\t/**\n\t * Optional class name applied to the outer nav element.\n\t */\n\tclassName?: string;\n}\n\nexport function Breadcrumbs({ items, className }: BreadcrumbsProps) {\n\treturn (\n\t\t<nav\n\t\t\taria-label=\"Breadcrumb\"\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex items-center gap-1 text-sm font-normal text-basalt-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{items.map((item, index) => {\n\t\t\t\tconst current = index === items.length - 1 && !item.href;\n\t\t\t\tconst label = item.icon ? (\n\t\t\t\t\t<span className=\"inline-flex items-center gap-1\">\n\t\t\t\t\t\t{item.icon}\n\t\t\t\t\t\t{item.label}\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\titem.label\n\t\t\t\t);\n\t\t\t\treturn (\n\t\t\t\t\t<span key={`${String(item.label)}-${index}`} className=\"flex items-center gap-1\">\n\t\t\t\t\t\t{index > 0 ? <ChevronRight className=\"size-3\" aria-hidden=\"true\" /> : null}\n\t\t\t\t\t\t{item.href ? (\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\thref={item.href}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\t\"text-basalt-muted-foreground no-underline transition-colors hover:text-basalt-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\taria-current={current ? \"page\" : undefined}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\tcrumbTypeClass,\n\t\t\t\t\t\t\t\t\tcurrent ? \"text-basalt-foreground\" : \"text-basalt-muted-foreground\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</span>\n\t\t\t\t);\n\t\t\t})}\n\t\t</nav>\n\t);\n}\n"],"mappings":";;;;;;;AAMA,IAAM,IAAiB;AA+BvB,SAAgB,EAAY,EAAE,UAAO,gBAA+B;CACnE,OACC,kBAAC,OAAD;EACC,cAAW;EACX,WAAW,EACV,GACA,4EACA,CACD;EAEC,UAAA,EAAM,KAAK,GAAM,MAAU;GAC3B,IAAM,IAAU,MAAU,EAAM,SAAS,KAAK,CAAC,EAAK,MAC9C,IAAQ,EAAK,OAClB,kBAAC,QAAD;IAAM,WAAU;IAAhB,UAAA,CACE,EAAK,MACL,EAAK,KACD;GAEN,CAAA,IAAA,EAAK;GAEN,OACC,kBAAC,QAAD;IAA6C,WAAU;IAAvD,UAAA,CACE,IAAQ,IAAI,kBAAC,GAAD;KAAc,WAAU;KAAS,eAAY;IAAQ,CAAA,IAAI,MACrE,EAAK,OACL,kBAAC,GAAD;KACC,MAAM,EAAK;KACX,WAAW,EACV,GACA,0FACD;KAEC,UAAA;IACI,CAAA,IAEN,kBAAC,QAAD;KACC,gBAAc,IAAU,SAAS,KAAA;KACjC,WAAW,EACV,GACA,IAAU,2BAA2B,8BACtC;KAEC,UAAA;IACI,CAAA,CAEF;GAvBK,GAAA,GAAG,OAAO,EAAK,KAAK,EAAE,GAAG,GAuB9B;EAER,CAAC;CACG,CAAA;AAEP"}
@@ -4,13 +4,59 @@ declare const buttonVariants: (props?: ({
4
4
  variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary" | null | undefined;
5
5
  size?: "default" | "icon" | "lg" | "sm" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
- export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
7
+ export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type">, Omit<VariantProps<typeof buttonVariants>, "variant" | "size"> {
8
+ /**
9
+ * Visual style variant.
10
+ * @default "default"
11
+ */
12
+ variant?: VariantProps<typeof buttonVariants>["variant"];
13
+ /**
14
+ * Sizing preset.
15
+ * @default "default"
16
+ */
17
+ size?: VariantProps<typeof buttonVariants>["size"];
18
+ /**
19
+ * Pass control to child element slot.
20
+ *
21
+ * Note: Child element must forward props and ref in all states. When disabled or loading,
22
+ * asChild suppresses click, keyboard activation (Enter/Space), and pointer handlers on both
23
+ * child and parent while retaining browser default Tab navigation without trapping focus.
24
+ * To preserve single DOM node layout and avoid unexpected child layout shifts, no internal
25
+ * spinner is inserted in asChild loading mode; visual disabled state is expressed via aria-disabled.
26
+ *
27
+ * @default false
28
+ */
8
29
  asChild?: boolean;
30
+ /**
31
+ * Button type attribute.
32
+ * @default "button"
33
+ */
34
+ type?: React.ButtonHTMLAttributes<HTMLButtonElement>["type"];
35
+ /**
36
+ * Display loading spinner and disable interactions.
37
+ * @default false
38
+ */
9
39
  loading?: boolean;
40
+ /**
41
+ * Optional leading icon slot.
42
+ */
10
43
  icon?: React.ReactNode;
11
44
  }
12
45
  export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
13
- export interface LinkButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, VariantProps<typeof buttonVariants> {
46
+ export interface LinkButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorElement>, Omit<VariantProps<typeof buttonVariants>, "variant" | "size"> {
47
+ /**
48
+ * Visual style variant.
49
+ * @default "default"
50
+ */
51
+ variant?: VariantProps<typeof buttonVariants>["variant"];
52
+ /**
53
+ * Sizing preset.
54
+ * @default "default"
55
+ */
56
+ size?: VariantProps<typeof buttonVariants>["size"];
57
+ /**
58
+ * Optional leading icon slot.
59
+ */
14
60
  icon?: React.ReactNode;
15
61
  }
16
62
  export declare const LinkButton: React.ForwardRefExoticComponent<LinkButtonProps & React.RefAttributes<HTMLAnchorElement>>;
@@ -1,13 +1,14 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { FOCUS_RING as t } from "./overlay.js";
4
- import * as n from "react";
5
- import { Slot as r } from "@radix-ui/react-slot";
6
- import { cva as i } from "class-variance-authority";
7
- import { Loader2 as a } from "lucide-react";
8
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
3
+ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
+ import { FOCUS_RING as n } from "./overlay.js";
5
+ import * as r from "react";
6
+ import { Slot as i } from "@radix-ui/react-slot";
7
+ import { cva as a } from "class-variance-authority";
8
+ import { Loader2 as o } from "lucide-react";
9
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
9
10
  //#region src/components/button.tsx
10
- var c = i(`inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${t} disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
11
+ var l = a(`${t} inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${n} disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
11
12
  variants: {
12
13
  variant: {
13
14
  default: "bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90",
@@ -28,46 +29,96 @@ var c = i(`inline-flex items-center justify-center gap-2 rounded-basalt-md text-
28
29
  variant: "default",
29
30
  size: "default"
30
31
  }
31
- }), l = n.forwardRef(({ className: t, variant: n, size: i, asChild: l = !1, type: u = "button", loading: d = !1, icon: f, children: p, disabled: m, ...h }, g) => {
32
- let _ = l ? r : "button";
33
- if (l) return /* @__PURE__ */ o(_, {
34
- className: e(c({
35
- variant: n,
36
- size: i
37
- }), t),
38
- ref: g,
39
- ...h,
40
- children: p
41
- });
42
- let v = d ? /* @__PURE__ */ o(a, {
43
- className: "animate-basalt-spin",
32
+ }), u = r.forwardRef(({ className: t, variant: n, size: a, asChild: u = !1, type: d = "button", loading: f = !1, icon: p, children: m, disabled: h, ...g }, _) => {
33
+ let v = !!(h || f);
34
+ if (u) {
35
+ if (v && r.isValidElement(m)) {
36
+ let o = m, c = typeof o.type == "string" && o.type.toLowerCase() === "button", u = (e) => {
37
+ e.preventDefault(), e.stopPropagation(), "stopImmediatePropagation" in e && typeof e.stopImmediatePropagation == "function" && e.stopImmediatePropagation();
38
+ }, d = (e) => {
39
+ e.key === "Enter" || e.key === " " || e.key === "Spacebar" ? (e.preventDefault(), e.stopPropagation(), typeof e.nativeEvent?.stopImmediatePropagation == "function" && e.nativeEvent.stopImmediatePropagation()) : (e.stopPropagation(), typeof e.nativeEvent?.stopImmediatePropagation == "function" && e.nativeEvent.stopImmediatePropagation());
40
+ }, p = {
41
+ onClick: u,
42
+ onClickCapture: u,
43
+ onKeyDown: d,
44
+ onKeyDownCapture: d,
45
+ onKeyUp: d,
46
+ onKeyUpCapture: d,
47
+ onKeyPress: d,
48
+ onKeyPressCapture: d,
49
+ onMouseDown: u,
50
+ onMouseDownCapture: u,
51
+ onMouseUp: u,
52
+ onMouseUpCapture: u,
53
+ onPointerDown: u,
54
+ onPointerDownCapture: u,
55
+ onPointerUp: u,
56
+ onPointerUpCapture: u,
57
+ onAuxClick: u,
58
+ onAuxClickCapture: u
59
+ }, { disabled: h, ...v } = o.props, y = r.cloneElement(o, {
60
+ ...v,
61
+ ...p,
62
+ ...c ? { disabled: !0 } : {},
63
+ "aria-disabled": !0,
64
+ "aria-busy": f ? !0 : void 0,
65
+ tabIndex: -1
66
+ }), b = {
67
+ ...g,
68
+ ...p,
69
+ "aria-disabled": !0,
70
+ "aria-busy": f ? "true" : void 0,
71
+ tabIndex: -1
72
+ };
73
+ return c && (b.disabled = !0), /* @__PURE__ */ s(i, {
74
+ className: e(l({
75
+ variant: n,
76
+ size: a
77
+ }), t),
78
+ ref: _,
79
+ ...b,
80
+ children: y
81
+ });
82
+ }
83
+ return /* @__PURE__ */ s(i, {
84
+ className: e(l({
85
+ variant: n,
86
+ size: a
87
+ }), t),
88
+ ref: _,
89
+ ...g,
90
+ children: m
91
+ });
92
+ }
93
+ let y = f ? /* @__PURE__ */ s(o, {
94
+ className: "animate-basalt-spin motion-reduce:animate-none",
44
95
  "aria-hidden": "true"
45
- }) : f;
46
- return /* @__PURE__ */ s("button", {
47
- className: e(c({
96
+ }) : p;
97
+ return /* @__PURE__ */ c("button", {
98
+ className: e(l({
48
99
  variant: n,
49
- size: i
100
+ size: a
50
101
  }), t),
51
- ref: g,
52
- type: u,
53
- disabled: m || d,
54
- "aria-busy": d || void 0,
55
- ...h,
56
- children: [v, p]
102
+ ref: _,
103
+ type: d,
104
+ disabled: v,
105
+ "aria-busy": f || void 0,
106
+ ...g,
107
+ children: [y, m]
57
108
  });
58
109
  });
59
- l.displayName = "Button";
60
- var u = n.forwardRef(({ className: t, variant: n, size: r, icon: i, children: a, ...o }, l) => /* @__PURE__ */ s("a", {
61
- ref: l,
62
- className: e(c({
110
+ u.displayName = "Button";
111
+ var d = r.forwardRef(({ className: t, variant: n, size: r, icon: i, children: a, ...o }, s) => /* @__PURE__ */ c("a", {
112
+ ref: s,
113
+ className: e(l({
63
114
  variant: n,
64
115
  size: r
65
116
  }), t),
66
117
  ...o,
67
118
  children: [i, a]
68
119
  }));
69
- u.displayName = "LinkButton";
120
+ d.displayName = "LinkButton";
70
121
  //#endregion
71
- export { l as Button, u as LinkButton, c as buttonVariants };
122
+ export { u as Button, d as LinkButton, l as buttonVariants };
72
123
 
73
124
  //# sourceMappingURL=button.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","names":[],"sources":["../../src/components/button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { FOCUS_RING } from \"./overlay\";\n\nconst buttonVariants = cva(\n\t`inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90\",\n\t\t\t\tsecondary: \"bg-basalt-control text-basalt-foreground hover:bg-basalt-accent\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"bg-basalt-destructive text-basalt-destructive-foreground hover:bg-basalt-destructive/90\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-basalt-border bg-basalt-control hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tghost: \"hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tlink: \"text-basalt-primary underline-offset-4 hover:underline\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-9 px-4 py-2\",\n\t\t\t\tsm: \"h-8 rounded-basalt-md px-3 text-xs\",\n\t\t\t\tlg: \"h-10 rounded-basalt-md px-6\",\n\t\t\t\ticon: \"h-9 w-9\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t},\n\t},\n);\n\nexport interface ButtonProps\n\textends React.ButtonHTMLAttributes<HTMLButtonElement>,\n\t\tVariantProps<typeof buttonVariants> {\n\tasChild?: boolean;\n\tloading?: boolean;\n\ticon?: React.ReactNode;\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tvariant,\n\t\t\tsize,\n\t\t\tasChild = false,\n\t\t\ttype = \"button\",\n\t\t\tloading = false,\n\t\t\ticon,\n\t\t\tchildren,\n\t\t\tdisabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst Comp = asChild ? Slot : \"button\";\n\t\tif (asChild) {\n\t\t\treturn (\n\t\t\t\t<Comp className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</Comp>\n\t\t\t);\n\t\t}\n\t\tconst iconNode = loading ? (\n\t\t\t<Loader2 className=\"animate-basalt-spin\" aria-hidden=\"true\" />\n\t\t) : (\n\t\t\ticon\n\t\t);\n\t\treturn (\n\t\t\t<button\n\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\tref={ref}\n\t\t\t\ttype={type}\n\t\t\t\tdisabled={disabled || loading}\n\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{iconNode}\n\t\t\t\t{children}\n\t\t\t</button>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport interface LinkButtonProps\n\textends React.AnchorHTMLAttributes<HTMLAnchorElement>,\n\t\tVariantProps<typeof buttonVariants> {\n\ticon?: React.ReactNode;\n}\n\nexport const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>(\n\t({ className, variant, size, icon, children, ...props }, ref) => (\n\t\t<a ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>\n\t\t\t{icon}\n\t\t\t{children}\n\t\t</a>\n\t),\n);\nLinkButton.displayName = \"LinkButton\";\n\nexport { buttonVariants };\n"],"mappings":";;;;;;;;;AAOA,IAAM,IAAiB,EACtB,yGAAyG,EAAW,gHACpH;CACC,UAAU;EACT,SAAS;GACR,SAAS;GACT,WAAW;GACX,aACC;GACD,SACC;GACD,OAAO;GACP,MAAM;EACP;EACA,MAAM;GACL,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACP;CACD;CACA,iBAAiB;EAChB,SAAS;EACT,MAAM;CACP;AACD,CACD,GAUa,IAAS,EAAM,YAE1B,EACC,cACA,YACA,SACA,aAAU,IACV,UAAO,UACP,aAAU,IACV,SACA,aACA,aACA,GAAG,KAEJ,MACI;CACJ,IAAM,IAAO,IAAU,IAAO;CAC9B,IAAI,GACH,OACC,kBAAC,GAAD;EAAM,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EAAQ;EAAK,GAAI;EAC/E;CACI,CAAA;CAGR,IAAM,IAAW,IAChB,kBAAC,GAAD;EAAS,WAAU;EAAsB,eAAY;CAAQ,CAAA,IAE7D;CAED,OACC,kBAAC,UAAD;EACC,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EACrD;EACC;EACN,UAAU,KAAY;EACtB,aAAW,KAAW,KAAA;EACtB,GAAI;EANL,UAAA,CAQE,GACA,CACM;;AAEV,CACD;AACA,EAAO,cAAc;AAQrB,IAAa,IAAa,EAAM,YAC9B,EAAE,cAAW,YAAS,SAAM,SAAM,aAAU,GAAG,KAAS,MACxD,kBAAC,KAAD;CAAQ;CAAK,WAAW,EAAG,EAAe;EAAE;EAAS;CAAK,CAAC,GAAG,CAAS;CAAG,GAAI;CAA9E,UAAA,CACE,GACA,CACC;AAEL,CAAA,CAAA;AACA,EAAW,cAAc"}
1
+ {"version":3,"file":"button.js","names":[],"sources":["../../src/components/button.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\nimport { FOCUS_RING } from \"./overlay\";\n\nconst buttonVariants = cva(\n\t`${BASALT_UI_CLASS} inline-flex items-center justify-center gap-2 rounded-basalt-md text-sm font-medium transition-colors ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-50 aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-basalt-primary text-basalt-primary-foreground hover:bg-basalt-primary/90\",\n\t\t\t\tsecondary: \"bg-basalt-control text-basalt-foreground hover:bg-basalt-accent\",\n\t\t\t\tdestructive:\n\t\t\t\t\t\"bg-basalt-destructive text-basalt-destructive-foreground hover:bg-basalt-destructive/90\",\n\t\t\t\toutline:\n\t\t\t\t\t\"border border-basalt-border bg-basalt-control hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tghost: \"hover:bg-basalt-accent hover:text-basalt-accent-foreground\",\n\t\t\t\tlink: \"text-basalt-primary underline-offset-4 hover:underline\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-9 px-4 py-2\",\n\t\t\t\tsm: \"h-8 rounded-basalt-md px-3 text-xs\",\n\t\t\t\tlg: \"h-10 rounded-basalt-md px-6\",\n\t\t\t\ticon: \"h-9 w-9\",\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t},\n\t},\n);\n\nexport interface ButtonProps\n\textends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"type\">,\n\t\tOmit<VariantProps<typeof buttonVariants>, \"variant\" | \"size\"> {\n\t/**\n\t * Visual style variant.\n\t * @default \"default\"\n\t */\n\tvariant?: VariantProps<typeof buttonVariants>[\"variant\"];\n\t/**\n\t * Sizing preset.\n\t * @default \"default\"\n\t */\n\tsize?: VariantProps<typeof buttonVariants>[\"size\"];\n\t/**\n\t * Pass control to child element slot.\n\t *\n\t * Note: Child element must forward props and ref in all states. When disabled or loading,\n\t * asChild suppresses click, keyboard activation (Enter/Space), and pointer handlers on both\n\t * child and parent while retaining browser default Tab navigation without trapping focus.\n\t * To preserve single DOM node layout and avoid unexpected child layout shifts, no internal\n\t * spinner is inserted in asChild loading mode; visual disabled state is expressed via aria-disabled.\n\t *\n\t * @default false\n\t */\n\tasChild?: boolean;\n\t/**\n\t * Button type attribute.\n\t * @default \"button\"\n\t */\n\ttype?: React.ButtonHTMLAttributes<HTMLButtonElement>[\"type\"];\n\t/**\n\t * Display loading spinner and disable interactions.\n\t * @default false\n\t */\n\tloading?: boolean;\n\t/**\n\t * Optional leading icon slot.\n\t */\n\ticon?: React.ReactNode;\n}\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tvariant,\n\t\t\tsize,\n\t\t\tasChild = false,\n\t\t\ttype = \"button\",\n\t\t\tloading = false,\n\t\t\ticon,\n\t\t\tchildren,\n\t\t\tdisabled,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst isDisabled = Boolean(disabled || loading);\n\n\t\tif (asChild) {\n\t\t\tif (isDisabled && React.isValidElement(children)) {\n\t\t\t\tconst childElement = children as React.ReactElement<Record<string, unknown>>;\n\t\t\t\tconst isNativeButton =\n\t\t\t\t\ttypeof childElement.type === \"string\" && childElement.type.toLowerCase() === \"button\";\n\n\t\t\t\tconst blockActivationEvent = (event: React.SyntheticEvent | Event) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\tif (\n\t\t\t\t\t\t\"stopImmediatePropagation\" in event &&\n\t\t\t\t\t\ttypeof event.stopImmediatePropagation === \"function\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst handleKeyEvent = (event: React.KeyboardEvent) => {\n\t\t\t\t\tif (event.key === \"Enter\" || event.key === \" \" || event.key === \"Spacebar\") {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\tif (typeof event.nativeEvent?.stopImmediatePropagation === \"function\") {\n\t\t\t\t\t\t\tevent.nativeEvent.stopImmediatePropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// For non-activation keys like Tab, suppress user handlers from running while preserving browser default navigation\n\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\tif (typeof event.nativeEvent?.stopImmediatePropagation === \"function\") {\n\t\t\t\t\t\t\tevent.nativeEvent.stopImmediatePropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst blockedHandlers = {\n\t\t\t\t\tonClick: blockActivationEvent,\n\t\t\t\t\tonClickCapture: blockActivationEvent,\n\t\t\t\t\tonKeyDown: handleKeyEvent,\n\t\t\t\t\tonKeyDownCapture: handleKeyEvent,\n\t\t\t\t\tonKeyUp: handleKeyEvent,\n\t\t\t\t\tonKeyUpCapture: handleKeyEvent,\n\t\t\t\t\tonKeyPress: handleKeyEvent,\n\t\t\t\t\tonKeyPressCapture: handleKeyEvent,\n\t\t\t\t\tonMouseDown: blockActivationEvent,\n\t\t\t\t\tonMouseDownCapture: blockActivationEvent,\n\t\t\t\t\tonMouseUp: blockActivationEvent,\n\t\t\t\t\tonMouseUpCapture: blockActivationEvent,\n\t\t\t\t\tonPointerDown: blockActivationEvent,\n\t\t\t\t\tonPointerDownCapture: blockActivationEvent,\n\t\t\t\t\tonPointerUp: blockActivationEvent,\n\t\t\t\t\tonPointerUpCapture: blockActivationEvent,\n\t\t\t\t\tonAuxClick: blockActivationEvent,\n\t\t\t\t\tonAuxClickCapture: blockActivationEvent,\n\t\t\t\t};\n\n\t\t\t\tconst { disabled: _ignoredChildDisabled, ...sanitizedProps } = childElement.props;\n\n\t\t\t\tconst sanitizedChild = React.cloneElement(childElement, {\n\t\t\t\t\t...sanitizedProps,\n\t\t\t\t\t...blockedHandlers,\n\t\t\t\t\t...(isNativeButton ? { disabled: true } : {}),\n\t\t\t\t\t\"aria-disabled\": true,\n\t\t\t\t\t\"aria-busy\": loading ? true : undefined,\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t});\n\n\t\t\t\tconst slotProps: Record<string, unknown> = {\n\t\t\t\t\t...props,\n\t\t\t\t\t...blockedHandlers,\n\t\t\t\t\t\"aria-disabled\": true,\n\t\t\t\t\t\"aria-busy\": loading ? \"true\" : undefined,\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t};\n\t\t\t\tif (isNativeButton) {\n\t\t\t\t\tslotProps.disabled = true;\n\t\t\t\t}\n\n\t\t\t\treturn (\n\t\t\t\t\t<Slot\n\t\t\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\t{...slotProps}\n\t\t\t\t\t>\n\t\t\t\t\t\t{sanitizedChild}\n\t\t\t\t\t</Slot>\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn (\n\t\t\t\t<Slot className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</Slot>\n\t\t\t);\n\t\t}\n\n\t\tconst iconNode = loading ? (\n\t\t\t<Loader2 className=\"animate-basalt-spin motion-reduce:animate-none\" aria-hidden=\"true\" />\n\t\t) : (\n\t\t\ticon\n\t\t);\n\t\treturn (\n\t\t\t<button\n\t\t\t\tclassName={cn(buttonVariants({ variant, size }), className)}\n\t\t\t\tref={ref}\n\t\t\t\ttype={type}\n\t\t\t\tdisabled={isDisabled}\n\t\t\t\taria-busy={loading || undefined}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{iconNode}\n\t\t\t\t{children}\n\t\t\t</button>\n\t\t);\n\t},\n);\nButton.displayName = \"Button\";\n\nexport interface LinkButtonProps\n\textends React.AnchorHTMLAttributes<HTMLAnchorElement>,\n\t\tOmit<VariantProps<typeof buttonVariants>, \"variant\" | \"size\"> {\n\t/**\n\t * Visual style variant.\n\t * @default \"default\"\n\t */\n\tvariant?: VariantProps<typeof buttonVariants>[\"variant\"];\n\t/**\n\t * Sizing preset.\n\t * @default \"default\"\n\t */\n\tsize?: VariantProps<typeof buttonVariants>[\"size\"];\n\t/**\n\t * Optional leading icon slot.\n\t */\n\ticon?: React.ReactNode;\n}\n\nexport const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>(\n\t({ className, variant, size, icon, children, ...props }, ref) => (\n\t\t<a ref={ref} className={cn(buttonVariants({ variant, size }), className)} {...props}>\n\t\t\t{icon}\n\t\t\t{children}\n\t\t</a>\n\t),\n);\nLinkButton.displayName = \"LinkButton\";\n\nexport { buttonVariants };\n"],"mappings":";;;;;;;;;;AAQA,IAAM,IAAiB,EACtB,GAAG,EAAgB,yGAAyG,EAAW,yIACvI;CACC,UAAU;EACT,SAAS;GACR,SAAS;GACT,WAAW;GACX,aACC;GACD,SACC;GACD,OAAO;GACP,MAAM;EACP;EACA,MAAM;GACL,SAAS;GACT,IAAI;GACJ,IAAI;GACJ,MAAM;EACP;CACD;CACA,iBAAiB;EAChB,SAAS;EACT,MAAM;CACP;AACD,CACD,GA2Ca,IAAS,EAAM,YAE1B,EACC,cACA,YACA,SACA,aAAU,IACV,UAAO,UACP,aAAU,IACV,SACA,aACA,aACA,GAAG,KAEJ,MACI;CACJ,IAAM,IAAa,GAAQ,KAAY;CAEvC,IAAI,GAAS;EACZ,IAAI,KAAc,EAAM,eAAe,CAAQ,GAAG;GACjD,IAAM,IAAe,GACf,IACL,OAAO,EAAa,QAAS,YAAY,EAAa,KAAK,YAAY,MAAM,UAExE,KAAwB,MAAwC;IAGrE,AAFA,EAAM,eAAe,GACrB,EAAM,gBAAgB,GAErB,8BAA8B,KAC9B,OAAO,EAAM,4BAA6B,cAE1C,EAAM,yBAAyB;GAEjC,GAEM,KAAkB,MAA+B;IACtD,AAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,OAAO,EAAM,QAAQ,cAC/D,EAAM,eAAe,GACrB,EAAM,gBAAgB,GAClB,OAAO,EAAM,aAAa,4BAA6B,cAC1D,EAAM,YAAY,yBAAyB,MAI5C,EAAM,gBAAgB,GAClB,OAAO,EAAM,aAAa,4BAA6B,cAC1D,EAAM,YAAY,yBAAyB;GAG9C,GAEM,IAAkB;IACvB,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,kBAAkB;IAClB,SAAS;IACT,gBAAgB;IAChB,YAAY;IACZ,mBAAmB;IACnB,aAAa;IACb,oBAAoB;IACpB,WAAW;IACX,kBAAkB;IAClB,eAAe;IACf,sBAAsB;IACtB,aAAa;IACb,oBAAoB;IACpB,YAAY;IACZ,mBAAmB;GACpB,GAEM,EAAE,UAAU,GAAuB,GAAG,MAAmB,EAAa,OAEtE,IAAiB,EAAM,aAAa,GAAc;IACvD,GAAG;IACH,GAAG;IACH,GAAI,IAAiB,EAAE,UAAU,GAAK,IAAI,CAAC;IAC3C,iBAAiB;IACjB,aAAa,IAAU,KAAO,KAAA;IAC9B,UAAU;GACX,CAAC,GAEK,IAAqC;IAC1C,GAAG;IACH,GAAG;IACH,iBAAiB;IACjB,aAAa,IAAU,SAAS,KAAA;IAChC,UAAU;GACX;GAKA,OAJI,MACH,EAAU,WAAW,KAIrB,kBAAC,GAAD;IACC,WAAW,EAAG,EAAe;KAAE;KAAS;IAAK,CAAC,GAAG,CAAS;IACrD;IACL,GAAI;IAEH,UAAA;GACI,CAAA;EAER;EAEA,OACC,kBAAC,GAAD;GAAM,WAAW,EAAG,EAAe;IAAE;IAAS;GAAK,CAAC,GAAG,CAAS;GAAQ;GAAK,GAAI;GAC/E;EACI,CAAA;CAER;CAEA,IAAM,IAAW,IAChB,kBAAC,GAAD;EAAS,WAAU;EAAiD,eAAY;CAAQ,CAAA,IAExF;CAED,OACC,kBAAC,UAAD;EACC,WAAW,EAAG,EAAe;GAAE;GAAS;EAAK,CAAC,GAAG,CAAS;EACrD;EACC;EACN,UAAU;EACV,aAAW,KAAW,KAAA;EACtB,GAAI;EANL,UAAA,CAQE,GACA,CACM;;AAEV,CACD;AACA,EAAO,cAAc;AAqBrB,IAAa,IAAa,EAAM,YAC9B,EAAE,cAAW,YAAS,SAAM,SAAM,aAAU,GAAG,KAAS,MACxD,kBAAC,KAAD;CAAQ;CAAK,WAAW,EAAG,EAAe;EAAE;EAAS;CAAK,CAAC,GAAG,CAAS;CAAG,GAAI;CAA9E,UAAA,CACE,GACA,CACC;AAEL,CAAA,CAAA;AACA,EAAW,cAAc"}
@@ -11,7 +11,14 @@ export interface ChatBubbleProps {
11
11
  * @default false
12
12
  */
13
13
  streaming?: boolean;
14
+ /**
15
+ * Optional class name applied to the message bubble.
16
+ * Component only accepts variant, streaming, className, and children without native HTML rest forwarding or forwarded ref.
17
+ */
14
18
  className?: string;
19
+ /**
20
+ * Message content.
21
+ */
15
22
  children: ReactNode;
16
23
  }
17
24
  export declare function ChatBubble({ variant, streaming, className, children, }: ChatBubbleProps): import("react").JSX.Element;
@@ -1,25 +1,26 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
5
  //#region src/components/chat-bubble.tsx
5
- function r({ variant: r = "assistant", streaming: i = !1, className: a, children: o }) {
6
- if (r === "system") return /* @__PURE__ */ t("p", {
7
- className: e("px-1 text-center text-[11px] text-basalt-muted-foreground", a),
8
- children: o
6
+ function i({ variant: i = "assistant", streaming: a = !1, className: o, children: s }) {
7
+ if (i === "system") return /* @__PURE__ */ n("p", {
8
+ className: e(t, "px-1 text-center text-[11px] text-basalt-muted-foreground", o),
9
+ children: s
9
10
  });
10
- let s = r === "user";
11
- return /* @__PURE__ */ t("div", {
12
- className: e("flex w-full", s ? "justify-end" : "justify-start"),
13
- children: /* @__PURE__ */ n("div", {
14
- className: e("max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm", s ? "rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground" : "rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50", a),
15
- children: [o, i ? /* @__PURE__ */ t("span", {
16
- className: "mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle",
11
+ let c = i === "user";
12
+ return /* @__PURE__ */ n("div", {
13
+ className: e(t, "flex w-full", c ? "justify-end" : "justify-start"),
14
+ children: /* @__PURE__ */ r("div", {
15
+ className: e("max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm", c ? "rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground" : "rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50", o),
16
+ children: [s, a ? /* @__PURE__ */ n("span", {
17
+ className: "mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle motion-reduce:animate-none",
17
18
  "aria-hidden": !0
18
19
  }) : null]
19
20
  })
20
21
  });
21
22
  }
22
23
  //#endregion
23
- export { r as ChatBubble };
24
+ export { i as ChatBubble };
24
25
 
25
26
  //# sourceMappingURL=chat-bubble.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-bubble.js","names":[],"sources":["../../src/components/chat-bubble.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport type ChatBubbleVariant = \"assistant\" | \"system\" | \"user\";\n\nexport interface ChatBubbleProps {\n\t/**\n\t * Alignment and fill.\n\t * @default \"assistant\"\n\t */\n\tvariant?: ChatBubbleVariant;\n\t/**\n\t * Show a streaming caret after the body.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\tclassName?: string;\n\tchildren: ReactNode;\n}\n\nexport function ChatBubble({\n\tvariant = \"assistant\",\n\tstreaming = false,\n\tclassName,\n\tchildren,\n}: ChatBubbleProps) {\n\tif (variant === \"system\") {\n\t\treturn (\n\t\t\t<p className={cn(\"px-1 text-center text-[11px] text-basalt-muted-foreground\", className)}>\n\t\t\t\t{children}\n\t\t\t</p>\n\t\t);\n\t}\n\tconst user = variant === \"user\";\n\treturn (\n\t\t<div className={cn(\"flex w-full\", user ? \"justify-end\" : \"justify-start\")}>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm\",\n\t\t\t\t\tuser\n\t\t\t\t\t\t? \"rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground\"\n\t\t\t\t\t\t: \"rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle\"\n\t\t\t\t\t\taria-hidden\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;AAoBA,SAAgB,EAAW,EAC1B,aAAU,aACV,eAAY,IACZ,cACA,eACmB;CACnB,IAAI,MAAY,UACf,OACC,kBAAC,KAAD;EAAG,WAAW,EAAG,6DAA6D,CAAS;EACrF;CACC,CAAA;CAGL,IAAM,IAAO,MAAY;CACzB,OACC,kBAAC,OAAD;EAAK,WAAW,EAAG,eAAe,IAAO,gBAAgB,eAAe;EACvE,UAAA,kBAAC,OAAD;GACC,WAAW,EACV,uDACA,IACG,+EACA,qGACH,CACD;GAPD,UAAA,CASE,GACA,IACA,kBAAC,QAAD;IACC,WAAU;IACV,eAAA;GACA,CAAA,IACE,IACA;;CACD,CAAA;AAEP"}
1
+ {"version":3,"file":"chat-bubble.js","names":[],"sources":["../../src/components/chat-bubble.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\nexport type ChatBubbleVariant = \"assistant\" | \"system\" | \"user\";\n\nexport interface ChatBubbleProps {\n\t/**\n\t * Alignment and fill.\n\t * @default \"assistant\"\n\t */\n\tvariant?: ChatBubbleVariant;\n\t/**\n\t * Show a streaming caret after the body.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\t/**\n\t * Optional class name applied to the message bubble.\n\t * Component only accepts variant, streaming, className, and children without native HTML rest forwarding or forwarded ref.\n\t */\n\tclassName?: string;\n\t/**\n\t * Message content.\n\t */\n\tchildren: ReactNode;\n}\n\nexport function ChatBubble({\n\tvariant = \"assistant\",\n\tstreaming = false,\n\tclassName,\n\tchildren,\n}: ChatBubbleProps) {\n\tif (variant === \"system\") {\n\t\treturn (\n\t\t\t<p\n\t\t\t\tclassName={cn(\n\t\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\t\"px-1 text-center text-[11px] text-basalt-muted-foreground\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</p>\n\t\t);\n\t}\n\tconst user = variant === \"user\";\n\treturn (\n\t\t<div className={cn(BASALT_UI_CLASS, \"flex w-full\", user ? \"justify-end\" : \"justify-start\")}>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"max-w-[92%] px-3.5 py-2 text-sm leading-5 shadow-sm\",\n\t\t\t\t\tuser\n\t\t\t\t\t\t? \"rounded-2xl rounded-br-md bg-basalt-primary text-basalt-primary-foreground\"\n\t\t\t\t\t\t: \"rounded-2xl rounded-bl-md bg-basalt-secondary text-basalt-foreground ring-1 ring-basalt-border/50\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"mt-1 inline-block h-3 w-1.5 animate-pulse rounded-sm bg-basalt-primary/70 align-middle motion-reduce:animate-none\"\n\t\t\t\t\t\taria-hidden\n\t\t\t\t\t/>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;AA4BA,SAAgB,EAAW,EAC1B,aAAU,aACV,eAAY,IACZ,cACA,eACmB;CACnB,IAAI,MAAY,UACf,OACC,kBAAC,KAAD;EACC,WAAW,EACV,GACA,6DACA,CACD;EAEC;CACC,CAAA;CAGL,IAAM,IAAO,MAAY;CACzB,OACC,kBAAC,OAAD;EAAK,WAAW,EAAG,GAAiB,eAAe,IAAO,gBAAgB,eAAe;EACxF,UAAA,kBAAC,OAAD;GACC,WAAW,EACV,uDACA,IACG,+EACA,qGACH,CACD;GAPD,UAAA,CASE,GACA,IACA,kBAAC,QAAD;IACC,WAAU;IACV,eAAA;GACA,CAAA,IACE,IACA;;CACD,CAAA;AAEP"}
@@ -5,7 +5,7 @@ export interface ChatComposerProps {
5
5
  */
6
6
  disabled?: boolean;
7
7
  /**
8
- * Replace send with stop.
8
+ * Replace send with stop. When streaming is true, the stop button renders and triggers onCancel.
9
9
  * @default false
10
10
  */
11
11
  streaming?: boolean;
@@ -19,11 +19,11 @@ export interface ChatComposerProps {
19
19
  */
20
20
  placeholder?: string;
21
21
  /**
22
- * Called with trimmed text on send.
22
+ * Called with trimmed text on send. Operates on internal draft state and does not wait for a Promise (caller manages asynchronous errors).
23
23
  */
24
24
  onSend: (text: string) => void;
25
25
  /**
26
- * Called when the stop control is pressed.
26
+ * Called when the stop control is pressed while streaming is active.
27
27
  */
28
28
  onCancel?: () => void;
29
29
  /**
@@ -36,6 +36,10 @@ export interface ChatComposerProps {
36
36
  * @default "Stop generating"
37
37
  */
38
38
  cancelLabel?: string;
39
+ /**
40
+ * Optional class name applied to the form container.
41
+ * Component manages internal draft/height state and does not forward native form rest attributes or ref.
42
+ */
39
43
  className?: string;
40
44
  }
41
45
  export declare function ChatComposer({ disabled, streaming, label, placeholder, onSend, onCancel, sendLabel, cancelLabel, className, }: ChatComposerProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-composer.js","names":[],"sources":["../../src/components/chat-composer.tsx"],"sourcesContent":["import { ArrowUp, Square } from \"lucide-react\";\nimport { type FormEvent, type KeyboardEvent, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./button\";\nimport { InputArea } from \"./input-area\";\n\nexport interface ChatComposerProps {\n\t/**\n\t * Disable the field and send.\n\t * @default false\n\t */\n\tdisabled?: boolean;\n\t/**\n\t * Replace send with stop.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\t/**\n\t * Field accessible name.\n\t * @default \"Message\"\n\t */\n\tlabel?: string;\n\t/**\n\t * Placeholder when empty.\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Called with trimmed text on send.\n\t */\n\tonSend: (text: string) => void;\n\t/**\n\t * Called when the stop control is pressed.\n\t */\n\tonCancel?: () => void;\n\t/**\n\t * Send control accessible name.\n\t * @default \"Send message\"\n\t */\n\tsendLabel?: string;\n\t/**\n\t * Stop control accessible name.\n\t * @default \"Stop generating\"\n\t */\n\tcancelLabel?: string;\n\tclassName?: string;\n}\n\nexport function ChatComposer({\n\tdisabled = false,\n\tstreaming = false,\n\tlabel = \"Message\",\n\tplaceholder,\n\tonSend,\n\tonCancel,\n\tsendLabel = \"Send message\",\n\tcancelLabel = \"Stop generating\",\n\tclassName,\n}: ChatComposerProps) {\n\tconst [value, setValue] = useState(\"\");\n\tconst ref = useRef<HTMLTextAreaElement>(null);\n\tconst composingRef = useRef(false);\n\n\tconst resize = () => {\n\t\tconst el = ref.current;\n\t\tif (!el) {\n\t\t\treturn;\n\t\t}\n\t\tel.style.height = \"0px\";\n\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t};\n\n\tconst submit = () => {\n\t\tconst text = value.trim();\n\t\tif (!text || disabled || streaming) {\n\t\t\treturn;\n\t\t}\n\t\tonSend(text);\n\t\tsetValue(\"\");\n\t\trequestAnimationFrame(() => {\n\t\t\tconst el = ref.current;\n\t\t\tif (el) {\n\t\t\t\tel.style.height = \"0px\";\n\t\t\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t\t\t\tel.focus();\n\t\t\t}\n\t\t});\n\t};\n\n\tconst onSubmit = (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (composingRef.current || event.nativeEvent.isComposing) {\n\t\t\treturn;\n\t\t}\n\t\tif (event.key !== \"Enter\" || event.shiftKey) {\n\t\t\treturn;\n\t\t}\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\treturn (\n\t\t<form\n\t\t\tclassName={cn(\"border-t border-basalt-border/50 bg-basalt-card p-3\", className)}\n\t\t\tonSubmit={onSubmit}\n\t\t>\n\t\t\t<div className=\"flex items-end gap-2 rounded-2xl bg-basalt-secondary p-2 ring-1 ring-basalt-border/50\">\n\t\t\t\t<InputArea\n\t\t\t\t\tref={ref}\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tsetValue(event.target.value);\n\t\t\t\t\t\trequestAnimationFrame(resize);\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionStart={() => {\n\t\t\t\t\t\tcomposingRef.current = true;\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionEnd={() => {\n\t\t\t\t\t\tcomposingRef.current = false;\n\t\t\t\t\t}}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\trows={1}\n\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tclassName=\"max-h-40 min-h-[40px] flex-1 resize-none border-0 bg-transparent p-2 shadow-none ring-0\"\n\t\t\t\t/>\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tonClick={onCancel}\n\t\t\t\t\t\tdisabled={!onCancel}\n\t\t\t\t\t\taria-label={cancelLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Square className=\"h-3.5 w-3.5 fill-current\" strokeWidth={0} />\n\t\t\t\t\t</Button>\n\t\t\t\t) : (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tdisabled={disabled || !value.trim()}\n\t\t\t\t\t\taria-label={sendLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowUp className=\"h-4 w-4\" strokeWidth={2.25} />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n"],"mappings":";;;;;;;;AA+CA,SAAgB,EAAa,EAC5B,cAAW,IACX,eAAY,IACZ,WAAQ,WACR,gBACA,WACA,aACA,eAAY,gBACZ,iBAAc,mBACd,gBACqB;CACrB,IAAM,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,IAAM,EAA4B,IAAI,GACtC,IAAe,EAAO,EAAK,GAE3B,UAAe;EACpB,IAAM,IAAK,EAAI;EACV,MAGL,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE;CACrD,GAEM,UAAe;EACpB,IAAM,IAAO,EAAM,KAAK;EACpB,CAAC,KAAQ,KAAY,MAGzB,EAAO,CAAI,GACX,EAAS,EAAE,GACX,4BAA4B;GAC3B,IAAM,IAAK,EAAI;GACf,AAAI,MACH,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE,KACpD,EAAG,MAAM;EAEX,CAAC;CACF;CAkBA,OACC,kBAAC,QAAD;EACC,WAAW,EAAG,uDAAuD,CAAS;EACpE,WAnBM,MAAqB;GAEtC,AADA,EAAM,eAAe,GACrB,EAAO;EACR;EAkBE,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,GAAD;IACM;IACE;IACP,WAAW,MAAU;KAEpB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,sBAAsB,CAAM;IAC7B;IACA,0BAA0B;KACzB,EAAa,UAAU;IACxB;IACA,wBAAwB;KACvB,EAAa,UAAU;IACxB;IACW,YA9BI,MAA8C;KAC5D,EAAa,WAAW,EAAM,YAAY,eAG1C,EAAM,QAAQ,WAAW,EAAM,aAGnC,EAAM,eAAe,GACrB,EAAO;IACR;IAsBI,MAAM;IACO;IACH;IACV,cAAY;IACZ,WAAU;GACV,CAAA,GACA,IACA,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,SAAQ;IACR,SAAS;IACT,UAAU,CAAC;IACX,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAQ,WAAU;KAA2B,aAAa;IAAI,CAAA;GACvD,CAAA,IAER,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,UAAU,KAAY,CAAC,EAAM,KAAK;IAClC,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAS,WAAU;KAAU,aAAa;IAAO,CAAA;GAC1C,CAAA,CAEL;;CACA,CAAA;AAER"}
1
+ {"version":3,"file":"chat-composer.js","names":[],"sources":["../../src/components/chat-composer.tsx"],"sourcesContent":["import { ArrowUp, Square } from \"lucide-react\";\nimport { type FormEvent, type KeyboardEvent, useRef, useState } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./button\";\nimport { InputArea } from \"./input-area\";\n\nexport interface ChatComposerProps {\n\t/**\n\t * Disable the field and send.\n\t * @default false\n\t */\n\tdisabled?: boolean;\n\t/**\n\t * Replace send with stop. When streaming is true, the stop button renders and triggers onCancel.\n\t * @default false\n\t */\n\tstreaming?: boolean;\n\t/**\n\t * Field accessible name.\n\t * @default \"Message\"\n\t */\n\tlabel?: string;\n\t/**\n\t * Placeholder when empty.\n\t */\n\tplaceholder?: string;\n\t/**\n\t * Called with trimmed text on send. Operates on internal draft state and does not wait for a Promise (caller manages asynchronous errors).\n\t */\n\tonSend: (text: string) => void;\n\t/**\n\t * Called when the stop control is pressed while streaming is active.\n\t */\n\tonCancel?: () => void;\n\t/**\n\t * Send control accessible name.\n\t * @default \"Send message\"\n\t */\n\tsendLabel?: string;\n\t/**\n\t * Stop control accessible name.\n\t * @default \"Stop generating\"\n\t */\n\tcancelLabel?: string;\n\t/**\n\t * Optional class name applied to the form container.\n\t * Component manages internal draft/height state and does not forward native form rest attributes or ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatComposer({\n\tdisabled = false,\n\tstreaming = false,\n\tlabel = \"Message\",\n\tplaceholder,\n\tonSend,\n\tonCancel,\n\tsendLabel = \"Send message\",\n\tcancelLabel = \"Stop generating\",\n\tclassName,\n}: ChatComposerProps) {\n\tconst [value, setValue] = useState(\"\");\n\tconst ref = useRef<HTMLTextAreaElement>(null);\n\tconst composingRef = useRef(false);\n\n\tconst resize = () => {\n\t\tconst el = ref.current;\n\t\tif (!el) {\n\t\t\treturn;\n\t\t}\n\t\tel.style.height = \"0px\";\n\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t};\n\n\tconst submit = () => {\n\t\tconst text = value.trim();\n\t\tif (!text || disabled || streaming) {\n\t\t\treturn;\n\t\t}\n\t\tonSend(text);\n\t\tsetValue(\"\");\n\t\trequestAnimationFrame(() => {\n\t\t\tconst el = ref.current;\n\t\t\tif (el) {\n\t\t\t\tel.style.height = \"0px\";\n\t\t\t\tel.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n\t\t\t\tel.focus();\n\t\t\t}\n\t\t});\n\t};\n\n\tconst onSubmit = (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (composingRef.current || event.nativeEvent.isComposing) {\n\t\t\treturn;\n\t\t}\n\t\tif (event.key !== \"Enter\" || event.shiftKey) {\n\t\t\treturn;\n\t\t}\n\t\tevent.preventDefault();\n\t\tsubmit();\n\t};\n\n\treturn (\n\t\t<form\n\t\t\tclassName={cn(\"border-t border-basalt-border/50 bg-basalt-card p-3\", className)}\n\t\t\tonSubmit={onSubmit}\n\t\t>\n\t\t\t<div className=\"flex items-end gap-2 rounded-2xl bg-basalt-secondary p-2 ring-1 ring-basalt-border/50\">\n\t\t\t\t<InputArea\n\t\t\t\t\tref={ref}\n\t\t\t\t\tvalue={value}\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tsetValue(event.target.value);\n\t\t\t\t\t\trequestAnimationFrame(resize);\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionStart={() => {\n\t\t\t\t\t\tcomposingRef.current = true;\n\t\t\t\t\t}}\n\t\t\t\t\tonCompositionEnd={() => {\n\t\t\t\t\t\tcomposingRef.current = false;\n\t\t\t\t\t}}\n\t\t\t\t\tonKeyDown={onKeyDown}\n\t\t\t\t\trows={1}\n\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\tdisabled={disabled}\n\t\t\t\t\taria-label={label}\n\t\t\t\t\tclassName=\"max-h-40 min-h-[40px] flex-1 resize-none border-0 bg-transparent p-2 shadow-none ring-0\"\n\t\t\t\t/>\n\t\t\t\t{streaming ? (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tonClick={onCancel}\n\t\t\t\t\t\tdisabled={!onCancel}\n\t\t\t\t\t\taria-label={cancelLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Square className=\"h-3.5 w-3.5 fill-current\" strokeWidth={0} />\n\t\t\t\t\t</Button>\n\t\t\t\t) : (\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\tdisabled={disabled || !value.trim()}\n\t\t\t\t\t\taria-label={sendLabel}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowUp className=\"h-4 w-4\" strokeWidth={2.25} />\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n"],"mappings":";;;;;;;;AAmDA,SAAgB,EAAa,EAC5B,cAAW,IACX,eAAY,IACZ,WAAQ,WACR,gBACA,WACA,aACA,eAAY,gBACZ,iBAAc,mBACd,gBACqB;CACrB,IAAM,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,IAAM,EAA4B,IAAI,GACtC,IAAe,EAAO,EAAK,GAE3B,UAAe;EACpB,IAAM,IAAK,EAAI;EACV,MAGL,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE;CACrD,GAEM,UAAe;EACpB,IAAM,IAAO,EAAM,KAAK;EACpB,CAAC,KAAQ,KAAY,MAGzB,EAAO,CAAI,GACX,EAAS,EAAE,GACX,4BAA4B;GAC3B,IAAM,IAAK,EAAI;GACf,AAAI,MACH,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAI,EAAG,cAAc,GAAG,EAAE,KACpD,EAAG,MAAM;EAEX,CAAC;CACF;CAkBA,OACC,kBAAC,QAAD;EACC,WAAW,EAAG,uDAAuD,CAAS;EACpE,WAnBM,MAAqB;GAEtC,AADA,EAAM,eAAe,GACrB,EAAO;EACR;EAkBE,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAAC,GAAD;IACM;IACE;IACP,WAAW,MAAU;KAEpB,AADA,EAAS,EAAM,OAAO,KAAK,GAC3B,sBAAsB,CAAM;IAC7B;IACA,0BAA0B;KACzB,EAAa,UAAU;IACxB;IACA,wBAAwB;KACvB,EAAa,UAAU;IACxB;IACW,YA9BI,MAA8C;KAC5D,EAAa,WAAW,EAAM,YAAY,eAG1C,EAAM,QAAQ,WAAW,EAAM,aAGnC,EAAM,eAAe,GACrB,EAAO;IACR;IAsBI,MAAM;IACO;IACH;IACV,cAAY;IACZ,WAAU;GACV,CAAA,GACA,IACA,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,SAAQ;IACR,SAAS;IACT,UAAU,CAAC;IACX,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAQ,WAAU;KAA2B,aAAa;IAAI,CAAA;GACvD,CAAA,IAER,kBAAC,GAAD;IACC,MAAK;IACL,MAAK;IACL,UAAU,KAAY,CAAC,EAAM,KAAK;IAClC,cAAY;IAEZ,UAAA,kBAAC,GAAD;KAAS,WAAU;KAAU,aAAa;IAAO,CAAA;GAC1C,CAAA,CAEL;;CACA,CAAA;AAER"}
@@ -1,5 +1,5 @@
1
1
  import type { HTMLAttributes, ReactNode } from "react";
2
- export interface ChatHeaderProps extends Omit<HTMLAttributes<HTMLElement>, "title"> {
2
+ export interface ChatHeaderProps extends Omit<HTMLAttributes<HTMLElement>, "title" | "children" | "className"> {
3
3
  /**
4
4
  * Conversation title.
5
5
  */
@@ -16,5 +16,10 @@ export interface ChatHeaderProps extends Omit<HTMLAttributes<HTMLElement>, "titl
16
16
  * Trailing actions.
17
17
  */
18
18
  children?: ReactNode;
19
+ /**
20
+ * Optional class name applied to the header element.
21
+ * Component inherits native header HTMLAttributes on the root element but does not forward ref.
22
+ */
23
+ className?: string;
19
24
  }
20
25
  export declare function ChatHeader({ title, subtitle, leading, children, className, ...props }: ChatHeaderProps): import("react").JSX.Element;
@@ -1,33 +1,34 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { BASALT_UI_CLASS as t } from "../utils/control-surface.js";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
5
  //#region src/components/chat-header.tsx
5
- function r({ title: r, subtitle: i, leading: a, children: o, className: s, ...c }) {
6
- return /* @__PURE__ */ n("header", {
7
- className: e("flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5", s),
8
- ...c,
9
- children: [/* @__PURE__ */ n("div", {
6
+ function i({ title: i, subtitle: a, leading: o, children: s, className: c, ...l }) {
7
+ return /* @__PURE__ */ r("header", {
8
+ className: e(t, "flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5", c),
9
+ ...l,
10
+ children: [/* @__PURE__ */ r("div", {
10
11
  className: "flex min-w-0 items-center gap-2",
11
- children: [a ? /* @__PURE__ */ t("span", {
12
+ children: [o ? /* @__PURE__ */ n("span", {
12
13
  className: "flex h-8 w-8 items-center justify-center",
13
- children: a
14
- }) : null, /* @__PURE__ */ n("div", {
14
+ children: o
15
+ }) : null, /* @__PURE__ */ r("div", {
15
16
  className: "min-w-0",
16
- children: [/* @__PURE__ */ t("p", {
17
+ children: [/* @__PURE__ */ n("p", {
17
18
  className: "truncate text-sm font-semibold tracking-tight",
18
- children: r
19
- }), i ? /* @__PURE__ */ t("p", {
20
- className: "truncate text-[11px] text-basalt-muted-foreground",
21
19
  children: i
20
+ }), a ? /* @__PURE__ */ n("p", {
21
+ className: "truncate text-[11px] text-basalt-muted-foreground",
22
+ children: a
22
23
  }) : null]
23
24
  })]
24
- }), o ? /* @__PURE__ */ t("div", {
25
+ }), s ? /* @__PURE__ */ n("div", {
25
26
  className: "flex items-center gap-1",
26
- children: o
27
+ children: s
27
28
  }) : null]
28
29
  });
29
30
  }
30
31
  //#endregion
31
- export { r as ChatHeader };
32
+ export { i as ChatHeader };
32
33
 
33
34
  //# sourceMappingURL=chat-header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-header.js","names":[],"sources":["../../src/components/chat-header.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ChatHeaderProps extends Omit<HTMLAttributes<HTMLElement>, \"title\"> {\n\t/**\n\t * Conversation title.\n\t */\n\ttitle: ReactNode;\n\t/**\n\t * Secondary line under the title.\n\t */\n\tsubtitle?: ReactNode;\n\t/**\n\t * Leading mark, usually an icon.\n\t */\n\tleading?: ReactNode;\n\t/**\n\t * Trailing actions.\n\t */\n\tchildren?: ReactNode;\n}\n\nexport function ChatHeader({\n\ttitle,\n\tsubtitle,\n\tleading,\n\tchildren,\n\tclassName,\n\t...props\n}: ChatHeaderProps) {\n\treturn (\n\t\t<header\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t{leading ? (\n\t\t\t\t\t<span className=\"flex h-8 w-8 items-center justify-center\">{leading}</span>\n\t\t\t\t) : null}\n\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t<p className=\"truncate text-sm font-semibold tracking-tight\">{title}</p>\n\t\t\t\t\t{subtitle ? (\n\t\t\t\t\t\t<p className=\"truncate text-[11px] text-basalt-muted-foreground\">{subtitle}</p>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{children ? <div className=\"flex items-center gap-1\">{children}</div> : null}\n\t\t</header>\n\t);\n}\n"],"mappings":";;;;AAsBA,SAAgB,EAAW,EAC1B,UACA,aACA,YACA,aACA,cACA,GAAG,KACgB;CACnB,OACC,kBAAC,UAAD;EACC,WAAW,EACV,wFACA,CACD;EACA,GAAI;EALL,UAAA,CAOC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,IACA,kBAAC,QAAD;IAAM,WAAU;IAA4C,UAAA;GAAc,CAAA,IACvE,MACJ,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACC,kBAAC,KAAD;KAAG,WAAU;KAAiD,UAAA;IAAS,CAAA,GACtE,IACA,kBAAC,KAAD;KAAG,WAAU;KAAqD,UAAA;IAAY,CAAA,IAC3E,IACA;GACD,CAAA,CAAA;EACJ,CAAA,GAAA,IAAW,kBAAC,OAAD;GAAK,WAAU;GAA2B;EAAc,CAAA,IAAI,IACjE;;AAEV"}
1
+ {"version":3,"file":"chat-header.js","names":[],"sources":["../../src/components/chat-header.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { BASALT_UI_CLASS } from \"../utils/control-surface\";\n\nexport interface ChatHeaderProps\n\textends Omit<HTMLAttributes<HTMLElement>, \"title\" | \"children\" | \"className\"> {\n\t/**\n\t * Conversation title.\n\t */\n\ttitle: ReactNode;\n\t/**\n\t * Secondary line under the title.\n\t */\n\tsubtitle?: ReactNode;\n\t/**\n\t * Leading mark, usually an icon.\n\t */\n\tleading?: ReactNode;\n\t/**\n\t * Trailing actions.\n\t */\n\tchildren?: ReactNode;\n\t/**\n\t * Optional class name applied to the header element.\n\t * Component inherits native header HTMLAttributes on the root element but does not forward ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatHeader({\n\ttitle,\n\tsubtitle,\n\tleading,\n\tchildren,\n\tclassName,\n\t...props\n}: ChatHeaderProps) {\n\treturn (\n\t\t<header\n\t\t\tclassName={cn(\n\t\t\t\tBASALT_UI_CLASS,\n\t\t\t\t\"flex items-center justify-between gap-2 border-b border-basalt-border/50 px-3 py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t{leading ? (\n\t\t\t\t\t<span className=\"flex h-8 w-8 items-center justify-center\">{leading}</span>\n\t\t\t\t) : null}\n\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t<p className=\"truncate text-sm font-semibold tracking-tight\">{title}</p>\n\t\t\t\t\t{subtitle ? (\n\t\t\t\t\t\t<p className=\"truncate text-[11px] text-basalt-muted-foreground\">{subtitle}</p>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{children ? <div className=\"flex items-center gap-1\">{children}</div> : null}\n\t\t</header>\n\t);\n}\n"],"mappings":";;;;;AA6BA,SAAgB,EAAW,EAC1B,UACA,aACA,YACA,aACA,cACA,GAAG,KACgB;CACnB,OACC,kBAAC,UAAD;EACC,WAAW,EACV,GACA,wFACA,CACD;EACA,GAAI;EANL,UAAA,CAQC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACE,IACA,kBAAC,QAAD;IAAM,WAAU;IAA4C,UAAA;GAAc,CAAA,IACvE,MACJ,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACC,kBAAC,KAAD;KAAG,WAAU;KAAiD,UAAA;IAAS,CAAA,GACtE,IACA,kBAAC,KAAD;KAAG,WAAU;KAAqD,UAAA;IAAY,CAAA,IAC3E,IACA;GACD,CAAA,CAAA;EACJ,CAAA,GAAA,IAAW,kBAAC,OAAD;GAAK,WAAU;GAA2B;EAAc,CAAA,IAAI,IACjE;;AAEV"}
@@ -11,9 +11,9 @@ export interface ChatInboxItem {
11
11
  /** Optional leading mark. */
12
12
  leading?: ReactNode;
13
13
  }
14
- export interface ChatInboxProps extends Omit<HTMLAttributes<HTMLElement>, "onSelect"> {
14
+ export interface ChatInboxProps extends Omit<HTMLAttributes<HTMLElement>, "onSelect" | "className"> {
15
15
  /**
16
- * Threads to list.
16
+ * Threads to list ({ id: string, title: string, preview?: string, time?: string, leading?: ReactNode }[]).
17
17
  */
18
18
  items: readonly ChatInboxItem[];
19
19
  /**
@@ -24,5 +24,10 @@ export interface ChatInboxProps extends Omit<HTMLAttributes<HTMLElement>, "onSel
24
24
  * Called when a thread is chosen.
25
25
  */
26
26
  onSelect: (id: string) => void;
27
+ /**
28
+ * Optional class name applied to the navigation element.
29
+ * Component inherits native nav HTMLAttributes on the root element but does not forward ref.
30
+ */
31
+ className?: string;
27
32
  }
28
33
  export declare function ChatInbox({ items, activeId, onSelect, className, ...props }: ChatInboxProps): import("react").JSX.Element;