@nocoo/basalt 2.2.0 → 3.0.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 (319) hide show
  1. package/README.md +2 -0
  2. package/ai/COMPATIBILITY.md +11 -1
  3. package/ai/DESIGN.md +342 -0
  4. package/ai/INTEGRATION.md +179 -128
  5. package/ai/RECIPES.md +51 -26
  6. package/ai/USAGE.md +4 -4
  7. package/ai/registry.json +2836 -270
  8. package/dist/charts/config.d.ts +11 -11
  9. package/dist/charts/config.js +4 -4
  10. package/dist/charts/config.js.map +1 -1
  11. package/dist/charts/date-navigation.js +10 -10
  12. package/dist/charts/date-navigation.js.map +1 -1
  13. package/dist/charts/donut.js +1 -1
  14. package/dist/charts/donut.js.map +1 -1
  15. package/dist/charts/frame.d.ts +1 -1
  16. package/dist/charts/frame.js +4 -4
  17. package/dist/charts/frame.js.map +1 -1
  18. package/dist/charts/gauge.js +5 -5
  19. package/dist/charts/gauge.js.map +1 -1
  20. package/dist/charts/heatmap-calendar.js +11 -11
  21. package/dist/charts/heatmap-calendar.js.map +1 -1
  22. package/dist/charts/heatmap-matrix.js +8 -8
  23. package/dist/charts/heatmap-matrix.js.map +1 -1
  24. package/dist/charts/item-list.js +1 -1
  25. package/dist/charts/item-list.js.map +1 -1
  26. package/dist/charts/legend.js +19 -19
  27. package/dist/charts/legend.js.map +1 -1
  28. package/dist/charts/palette.js +2 -2
  29. package/dist/charts/palette.js.map +1 -1
  30. package/dist/charts/slot-bar.js +2 -2
  31. package/dist/charts/slot-bar.js.map +1 -1
  32. package/dist/charts/sparkline.js +38 -23
  33. package/dist/charts/sparkline.js.map +1 -1
  34. package/dist/charts/stat-card.d.ts +2 -2
  35. package/dist/charts/stat-card.js +61 -58
  36. package/dist/charts/stat-card.js.map +1 -1
  37. package/dist/charts/timeline.js +18 -9
  38. package/dist/charts/timeline.js.map +1 -1
  39. package/dist/charts/tooltip.js +16 -16
  40. package/dist/charts/tooltip.js.map +1 -1
  41. package/dist/components/accordion.js +1 -1
  42. package/dist/components/accordion.js.map +1 -1
  43. package/dist/components/alert-dialog.js +4 -4
  44. package/dist/components/alert-dialog.js.map +1 -1
  45. package/dist/components/app-header.js +8 -8
  46. package/dist/components/app-header.js.map +1 -1
  47. package/dist/components/app-shell.js +1 -1
  48. package/dist/components/app-shell.js.map +1 -1
  49. package/dist/components/approval-card.d.ts +13 -0
  50. package/dist/components/approval-card.js +161 -0
  51. package/dist/components/approval-card.js.map +1 -0
  52. package/dist/components/avatar.d.ts +12 -0
  53. package/dist/components/avatar.js +39 -18
  54. package/dist/components/avatar.js.map +1 -1
  55. package/dist/components/badge.js +8 -8
  56. package/dist/components/badge.js.map +1 -1
  57. package/dist/components/banner.d.ts +2 -2
  58. package/dist/components/banner.js +9 -8
  59. package/dist/components/banner.js.map +1 -1
  60. package/dist/components/basalt-mark.js +1 -1
  61. package/dist/components/basalt-mark.js.map +1 -1
  62. package/dist/components/battery-meter.js +5 -5
  63. package/dist/components/battery-meter.js.map +1 -1
  64. package/dist/components/breadcrumbs.js +6 -6
  65. package/dist/components/breadcrumbs.js.map +1 -1
  66. package/dist/components/button.js +10 -10
  67. package/dist/components/button.js.map +1 -1
  68. package/dist/components/chat-bubble.js +4 -3
  69. package/dist/components/chat-bubble.js.map +1 -1
  70. package/dist/components/chat-composer.d.ts +21 -3
  71. package/dist/components/chat-composer.js +126 -60
  72. package/dist/components/chat-composer.js.map +1 -1
  73. package/dist/components/chat-header.js +6 -6
  74. package/dist/components/chat-header.js.map +1 -1
  75. package/dist/components/chat-inbox.js +34 -30
  76. package/dist/components/chat-inbox.js.map +1 -1
  77. package/dist/components/chat-markdown.d.ts +8 -0
  78. package/dist/components/chat-markdown.js +122 -0
  79. package/dist/components/chat-markdown.js.map +1 -0
  80. package/dist/components/chat-message.d.ts +26 -0
  81. package/dist/components/chat-message.js +145 -0
  82. package/dist/components/chat-message.js.map +1 -0
  83. package/dist/components/checkbox.js +11 -11
  84. package/dist/components/checkbox.js.map +1 -1
  85. package/dist/components/clipboard-text.js +3 -3
  86. package/dist/components/clipboard-text.js.map +1 -1
  87. package/dist/components/code.d.ts +22 -20
  88. package/dist/components/code.js +94 -32
  89. package/dist/components/code.js.map +1 -1
  90. package/dist/components/collapsible.js +8 -4
  91. package/dist/components/collapsible.js.map +1 -1
  92. package/dist/components/command-palette.js +73 -47
  93. package/dist/components/command-palette.js.map +1 -1
  94. package/dist/components/context-cards.d.ts +15 -0
  95. package/dist/components/context-cards.js +148 -0
  96. package/dist/components/context-cards.js.map +1 -0
  97. package/dist/components/context-menu.d.ts +1 -1
  98. package/dist/components/context-menu.js +18 -14
  99. package/dist/components/context-menu.js.map +1 -1
  100. package/dist/components/data-table.d.ts +8 -2
  101. package/dist/components/data-table.js +161 -106
  102. package/dist/components/data-table.js.map +1 -1
  103. package/dist/components/date-picker.js +13 -13
  104. package/dist/components/date-picker.js.map +1 -1
  105. package/dist/components/delete-resource.js +1 -1
  106. package/dist/components/delete-resource.js.map +1 -1
  107. package/dist/components/description-list.js +3 -3
  108. package/dist/components/description-list.js.map +1 -1
  109. package/dist/components/dialog.d.ts +2 -2
  110. package/dist/components/dialog.js +8 -8
  111. package/dist/components/dialog.js.map +1 -1
  112. package/dist/components/diff-table.d.ts +15 -0
  113. package/dist/components/diff-table.js +146 -0
  114. package/dist/components/diff-table.js.map +1 -0
  115. package/dist/components/dock.js +2 -2
  116. package/dist/components/dock.js.map +1 -1
  117. package/dist/components/dropdown-menu.js +23 -18
  118. package/dist/components/dropdown-menu.js.map +1 -1
  119. package/dist/components/editable-nav-item.js +63 -58
  120. package/dist/components/editable-nav-item.js.map +1 -1
  121. package/dist/components/empty.js +6 -6
  122. package/dist/components/empty.js.map +1 -1
  123. package/dist/components/fab.js +1 -1
  124. package/dist/components/fab.js.map +1 -1
  125. package/dist/components/field.js +3 -3
  126. package/dist/components/field.js.map +1 -1
  127. package/dist/components/file-dropzone.js +7 -7
  128. package/dist/components/file-dropzone.js.map +1 -1
  129. package/dist/components/filter-bar.js +6 -6
  130. package/dist/components/filter-bar.js.map +1 -1
  131. package/dist/components/flow.js +4 -3
  132. package/dist/components/flow.js.map +1 -1
  133. package/dist/components/grid.d.ts +3 -1
  134. package/dist/components/grid.js +15 -8
  135. package/dist/components/grid.js.map +1 -1
  136. package/dist/components/hover-card.js +1 -1
  137. package/dist/components/hover-card.js.map +1 -1
  138. package/dist/components/icon-picker.d.ts +8 -2
  139. package/dist/components/icon-picker.js +75 -47
  140. package/dist/components/icon-picker.js.map +1 -1
  141. package/dist/components/inline-editable.js +6 -6
  142. package/dist/components/inline-editable.js.map +1 -1
  143. package/dist/components/input-area.js +3 -3
  144. package/dist/components/input-area.js.map +1 -1
  145. package/dist/components/input-group.js +5 -5
  146. package/dist/components/input-group.js.map +1 -1
  147. package/dist/components/input.js +4 -4
  148. package/dist/components/input.js.map +1 -1
  149. package/dist/components/label.js +3 -3
  150. package/dist/components/label.js.map +1 -1
  151. package/dist/components/layer-card.d.ts +9 -4
  152. package/dist/components/layer-card.js +72 -67
  153. package/dist/components/layer-card.js.map +1 -1
  154. package/dist/components/loader.d.ts +16 -10
  155. package/dist/components/loader.js +46 -53
  156. package/dist/components/loader.js.map +1 -1
  157. package/dist/components/loading-screen.js +3 -3
  158. package/dist/components/loading-screen.js.map +1 -1
  159. package/dist/components/menu-bar.js +32 -27
  160. package/dist/components/menu-bar.js.map +1 -1
  161. package/dist/components/meter.d.ts +5 -5
  162. package/dist/components/meter.js +25 -12
  163. package/dist/components/meter.js.map +1 -1
  164. package/dist/components/multi-select.js +66 -62
  165. package/dist/components/multi-select.js.map +1 -1
  166. package/dist/components/navigation-menu.d.ts +2 -2
  167. package/dist/components/navigation-menu.js +22 -8
  168. package/dist/components/navigation-menu.js.map +1 -1
  169. package/dist/components/overlay.js +2 -2
  170. package/dist/components/overlay.js.map +1 -1
  171. package/dist/components/page-header.d.ts +3 -1
  172. package/dist/components/page-header.js +12 -12
  173. package/dist/components/page-header.js.map +1 -1
  174. package/dist/components/pagination.js +3 -3
  175. package/dist/components/pagination.js.map +1 -1
  176. package/dist/components/popover.js +18 -23
  177. package/dist/components/popover.js.map +1 -1
  178. package/dist/components/prompt-bar.d.ts +16 -0
  179. package/dist/components/prompt-bar.js +49 -0
  180. package/dist/components/prompt-bar.js.map +1 -0
  181. package/dist/components/radio.js +9 -9
  182. package/dist/components/radio.js.map +1 -1
  183. package/dist/components/recommendation-card.d.ts +13 -0
  184. package/dist/components/recommendation-card.js +123 -0
  185. package/dist/components/recommendation-card.js.map +1 -0
  186. package/dist/components/resource-list.js +1 -1
  187. package/dist/components/resource-list.js.map +1 -1
  188. package/dist/components/responsive-master-detail.js +4 -4
  189. package/dist/components/responsive-master-detail.js.map +1 -1
  190. package/dist/components/scroll-area.js +3 -3
  191. package/dist/components/scroll-area.js.map +1 -1
  192. package/dist/components/section-rule.d.ts +5 -3
  193. package/dist/components/section-rule.js +12 -12
  194. package/dist/components/section-rule.js.map +1 -1
  195. package/dist/components/segment-control.js +3 -3
  196. package/dist/components/segment-control.js.map +1 -1
  197. package/dist/components/select.js +47 -32
  198. package/dist/components/select.js.map +1 -1
  199. package/dist/components/sensitive-input.js +17 -19
  200. package/dist/components/sensitive-input.js.map +1 -1
  201. package/dist/components/sheet.d.ts +4 -4
  202. package/dist/components/sheet.js +11 -10
  203. package/dist/components/sheet.js.map +1 -1
  204. package/dist/components/sidebar.js +146 -137
  205. package/dist/components/sidebar.js.map +1 -1
  206. package/dist/components/skeleton-line.d.ts +1 -1
  207. package/dist/components/skeleton-line.js +2 -2
  208. package/dist/components/skeleton-line.js.map +1 -1
  209. package/dist/components/slider.js +2 -2
  210. package/dist/components/slider.js.map +1 -1
  211. package/dist/components/stat-strip.js +5 -4
  212. package/dist/components/stat-strip.js.map +1 -1
  213. package/dist/components/switch.js +6 -6
  214. package/dist/components/switch.js.map +1 -1
  215. package/dist/components/table-of-contents.js +5 -5
  216. package/dist/components/table-of-contents.js.map +1 -1
  217. package/dist/components/table-pager.js +2 -2
  218. package/dist/components/table-pager.js.map +1 -1
  219. package/dist/components/table.js +4 -4
  220. package/dist/components/table.js.map +1 -1
  221. package/dist/components/tabs.js +7 -6
  222. package/dist/components/tabs.js.map +1 -1
  223. package/dist/components/tag-badge.d.ts +10 -10
  224. package/dist/components/tag-badge.js +11 -11
  225. package/dist/components/tag-badge.js.map +1 -1
  226. package/dist/components/tag-color-picker.d.ts +8 -1
  227. package/dist/components/tag-color-picker.js +33 -25
  228. package/dist/components/tag-color-picker.js.map +1 -1
  229. package/dist/components/text.js +5 -5
  230. package/dist/components/text.js.map +1 -1
  231. package/dist/components/theme-toggle.js +1 -1
  232. package/dist/components/theme-toggle.js.map +1 -1
  233. package/dist/components/thinking.d.ts +16 -0
  234. package/dist/components/thinking.js +85 -0
  235. package/dist/components/thinking.js.map +1 -0
  236. package/dist/components/toast.js +25 -20
  237. package/dist/components/toast.js.map +1 -1
  238. package/dist/components/toggle-group.js +6 -5
  239. package/dist/components/toggle-group.js.map +1 -1
  240. package/dist/components/toggle.js +6 -6
  241. package/dist/components/toggle.js.map +1 -1
  242. package/dist/components/tool-chips.d.ts +12 -0
  243. package/dist/components/tool-chips.js +138 -0
  244. package/dist/components/tool-chips.js.map +1 -0
  245. package/dist/components/toolbar.js +3 -3
  246. package/dist/components/toolbar.js.map +1 -1
  247. package/dist/components/tooltip.js +1 -1
  248. package/dist/components/tooltip.js.map +1 -1
  249. package/dist/components/typeahead-field.js +59 -56
  250. package/dist/components/typeahead-field.js.map +1 -1
  251. package/dist/components/upload-queue.js +15 -14
  252. package/dist/components/upload-queue.js.map +1 -1
  253. package/dist/models/agent-feedback.d.ts +44 -0
  254. package/dist/models/agent-feedback.js +28 -0
  255. package/dist/models/agent-feedback.js.map +1 -0
  256. package/dist/models/avatar.d.ts +2 -0
  257. package/dist/models/avatar.js +15 -0
  258. package/dist/models/avatar.js.map +1 -0
  259. package/dist/models/code.d.ts +6 -0
  260. package/dist/models/code.js +24 -0
  261. package/dist/models/code.js.map +1 -0
  262. package/dist/models/context-cards.d.ts +13 -0
  263. package/dist/models/context-cards.js +17 -0
  264. package/dist/models/context-cards.js.map +1 -0
  265. package/dist/models/diff-table.d.ts +12 -0
  266. package/dist/models/diff-table.js +9 -0
  267. package/dist/models/diff-table.js.map +1 -0
  268. package/dist/models/recommendation.d.ts +26 -0
  269. package/dist/models/recommendation.js +23 -0
  270. package/dist/models/recommendation.js.map +1 -0
  271. package/dist/styles/base.css +14 -2
  272. package/dist/styles/standalone.css +1865 -929
  273. package/dist/styles/tailwind.css +179 -13
  274. package/dist/styles/tokens.css +679 -20
  275. package/dist/utils/cn.js +25 -4
  276. package/dist/utils/cn.js.map +1 -1
  277. package/dist/utils/control-surface.d.ts +1 -1
  278. package/dist/utils/control-surface.js +1 -1
  279. package/dist/utils/control-surface.js.map +1 -1
  280. package/dist/utils/navigation.d.ts +3 -0
  281. package/dist/utils/navigation.js +8 -0
  282. package/dist/utils/navigation.js.map +1 -0
  283. package/dist/utils/selection-indicator.d.ts +1 -1
  284. package/dist/utils/selection-indicator.js +49 -72
  285. package/dist/utils/selection-indicator.js.map +1 -1
  286. package/dist/utils/use-hover-highlight.d.ts +4 -0
  287. package/dist/utils/use-hover-highlight.js +80 -0
  288. package/dist/utils/use-hover-highlight.js.map +1 -0
  289. package/dist/viewmodels/use-approval-card.d.ts +20 -0
  290. package/dist/viewmodels/use-approval-card.js +83 -0
  291. package/dist/viewmodels/use-approval-card.js.map +1 -0
  292. package/dist/viewmodels/use-chat-composer.d.ts +16 -0
  293. package/dist/viewmodels/use-chat-composer.js +34 -0
  294. package/dist/viewmodels/use-chat-composer.js.map +1 -0
  295. package/dist/viewmodels/use-chat-message.d.ts +16 -0
  296. package/dist/viewmodels/use-chat-message.js +26 -0
  297. package/dist/viewmodels/use-chat-message.js.map +1 -0
  298. package/dist/viewmodels/use-code-block.d.ts +8 -0
  299. package/dist/viewmodels/use-code-block.js +38 -0
  300. package/dist/viewmodels/use-code-block.js.map +1 -0
  301. package/dist/viewmodels/use-context-cards.d.ts +17 -0
  302. package/dist/viewmodels/use-context-cards.js +18 -0
  303. package/dist/viewmodels/use-context-cards.js.map +1 -0
  304. package/dist/viewmodels/use-diff-table.d.ts +16 -0
  305. package/dist/viewmodels/use-diff-table.js +42 -0
  306. package/dist/viewmodels/use-diff-table.js.map +1 -0
  307. package/dist/viewmodels/use-loader.d.ts +4 -0
  308. package/dist/viewmodels/use-loader.js +22 -0
  309. package/dist/viewmodels/use-loader.js.map +1 -0
  310. package/dist/viewmodels/use-recommendation-card.d.ts +17 -0
  311. package/dist/viewmodels/use-recommendation-card.js +41 -0
  312. package/dist/viewmodels/use-recommendation-card.js.map +1 -0
  313. package/dist/viewmodels/use-thinking.d.ts +13 -0
  314. package/dist/viewmodels/use-thinking.js +18 -0
  315. package/dist/viewmodels/use-thinking.js.map +1 -0
  316. package/dist/viewmodels/use-tool-chips.d.ts +10 -0
  317. package/dist/viewmodels/use-tool-chips.js +20 -0
  318. package/dist/viewmodels/use-tool-chips.js.map +1 -0
  319. package/package.json +3 -1
@@ -1,73 +1,139 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
3
  import { Button as t } from "./button.js";
4
- import { InputArea as n } from "./input-area.js";
5
- import { useRef as r, useState as i } from "react";
6
- import { ArrowUp as a, Square as o } from "lucide-react";
7
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
4
+ import { useChatComposer as n } from "../viewmodels/use-chat-composer.js";
5
+ import { InputArea as r } from "./input-area.js";
6
+ import { useId as i, useRef as a } from "react";
7
+ import { ArrowUp as o, Paperclip as s, Square as c, X as l } from "lucide-react";
8
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
8
9
  //#region src/components/chat-composer.tsx
9
- function l({ disabled: l = !1, streaming: u = !1, label: d = "Message", placeholder: f, onSend: p, onCancel: m, sendLabel: h = "Send message", cancelLabel: g = "Stop generating", className: _ }) {
10
- let [v, y] = i(""), b = r(null), x = r(!1), S = () => {
11
- let e = b.current;
12
- e && (e.style.height = "0px", e.style.height = `${Math.min(e.scrollHeight, 160)}px`);
13
- }, C = () => {
14
- let e = v.trim();
15
- !e || l || u || (p(e), y(""), requestAnimationFrame(() => {
16
- S(), b.current?.focus();
17
- }));
10
+ function p({ disabled: p = !1, streaming: m = !1, label: h = "Message", placeholder: g, onSend: _, onCancel: v, sendLabel: y = "Send message", cancelLabel: b = "Stop generating", className: x, value: S, defaultValue: C, onValueChange: w, toolbar: T, attachments: E = [], onRemoveAttachment: D, onFilesSelect: O, accept: k }) {
11
+ let A = n({
12
+ value: S,
13
+ defaultValue: C,
14
+ onValueChange: w,
15
+ onSend: _,
16
+ disabled: p,
17
+ streaming: m,
18
+ hasAttachments: E.length > 0
19
+ }), j = a(null), M = a(null), N = i(), P = a(!1), F = async () => {
20
+ await A.send() && j.current?.focus();
18
21
  };
19
- return /* @__PURE__ */ s("form", {
20
- className: e("border-t border-basalt-border/50 bg-basalt-card p-3", _),
22
+ return /* @__PURE__ */ f("form", {
23
+ className: e("basalt-ui shrink-0", x),
21
24
  onSubmit: (e) => {
22
- e.preventDefault(), C();
25
+ e.preventDefault(), F();
23
26
  },
24
- children: /* @__PURE__ */ c("div", {
25
- className: "flex items-end gap-2 rounded-2xl bg-basalt-secondary p-2 ring-1 ring-basalt-border/50",
26
- children: [/* @__PURE__ */ s(n, {
27
- ref: b,
28
- value: v,
29
- onChange: (e) => {
30
- y(e.target.value), requestAnimationFrame(S);
31
- },
32
- onCompositionStart: () => {
33
- x.current = !0;
34
- },
35
- onCompositionEnd: () => {
36
- x.current = !1;
37
- },
38
- onKeyDown: (e) => {
39
- x.current || e.nativeEvent.isComposing || e.key !== "Enter" || e.shiftKey || (e.preventDefault(), C());
40
- },
41
- rows: 1,
42
- placeholder: f,
43
- disabled: l,
44
- "aria-label": d,
45
- className: "max-h-40 min-h-[40px] flex-1 resize-none border-0 bg-transparent p-2 shadow-none ring-0"
46
- }), u ? /* @__PURE__ */ s(t, {
47
- type: "button",
48
- size: "icon",
49
- variant: "secondary",
50
- onClick: m,
51
- disabled: !m,
52
- "aria-label": g,
53
- children: /* @__PURE__ */ s(o, {
54
- className: "h-3.5 w-3.5 fill-current",
55
- strokeWidth: 0
27
+ children: [/* @__PURE__ */ f("div", {
28
+ className: "rounded-basalt-lg bg-basalt-control p-basalt-space-lg ring-1 ring-basalt-border focus-within:ring-basalt-ring",
29
+ children: [
30
+ E.length > 0 && /* @__PURE__ */ d("ul", {
31
+ "aria-label": "Attachments",
32
+ className: "mb-basalt-space-lg flex flex-wrap gap-basalt-space-md",
33
+ children: E.map((e) => /* @__PURE__ */ f("li", {
34
+ className: "flex min-w-0 max-w-full items-center gap-basalt-space-sm rounded-basalt-md bg-basalt-secondary py-basalt-space-sm pl-basalt-space-lg pr-basalt-space-sm text-basalt-sm",
35
+ children: [
36
+ /* @__PURE__ */ d(s, {
37
+ "aria-hidden": "true",
38
+ className: "size-basalt-icon shrink-0"
39
+ }),
40
+ /* @__PURE__ */ d("span", {
41
+ className: "truncate",
42
+ children: e.name
43
+ }),
44
+ D && /* @__PURE__ */ d(t, {
45
+ type: "button",
46
+ size: "icon",
47
+ variant: "ghost",
48
+ disabled: p || A.pending || m,
49
+ className: "size-basalt-5",
50
+ "aria-label": `Remove ${e.name}`,
51
+ onClick: () => D(e.id),
52
+ children: /* @__PURE__ */ d(l, {})
53
+ })
54
+ ]
55
+ }, e.id))
56
+ }),
57
+ /* @__PURE__ */ d(r, {
58
+ ref: j,
59
+ value: A.value,
60
+ onChange: (e) => A.update(e.target.value),
61
+ onCompositionStart: () => {
62
+ P.current = !0;
63
+ },
64
+ onCompositionEnd: () => {
65
+ P.current = !1;
66
+ },
67
+ onKeyDown: (e) => {
68
+ P.current || e.nativeEvent.isComposing || e.keyCode === 229 || e.key !== "Enter" || e.shiftKey || (e.preventDefault(), F());
69
+ },
70
+ rows: 1,
71
+ placeholder: g,
72
+ disabled: p || A.pending,
73
+ "aria-label": h,
74
+ "aria-describedby": A.error ? N : void 0,
75
+ "aria-description": "Enter to send, Shift+Enter for a new line",
76
+ className: "basalt-chat-input min-h-[calc(1lh+var(--basalt-space-2))] resize-none overflow-y-auto border-0 bg-transparent px-basalt-space-sm py-basalt-space-sm text-basalt-base leading-[var(--basalt-line-body)] shadow-none ring-0 outline-none"
77
+ }),
78
+ /* @__PURE__ */ f("div", {
79
+ className: "mt-basalt-content-gap flex items-center justify-between gap-basalt-content-gap",
80
+ children: [/* @__PURE__ */ f("div", {
81
+ className: "flex min-w-0 flex-1 flex-wrap items-center gap-basalt-space-sm",
82
+ children: [O && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("input", {
83
+ ref: M,
84
+ type: "file",
85
+ multiple: !0,
86
+ accept: k,
87
+ hidden: !0,
88
+ onChange: (e) => {
89
+ O(Array.from(e.target.files ?? [])), e.target.value = "";
90
+ }
91
+ }), /* @__PURE__ */ d(t, {
92
+ type: "button",
93
+ size: "icon",
94
+ variant: "ghost",
95
+ "aria-label": "Attach files",
96
+ disabled: p || A.pending || m,
97
+ onClick: () => M.current?.click(),
98
+ children: /* @__PURE__ */ d(s, {})
99
+ })] }), /* @__PURE__ */ d("fieldset", {
100
+ disabled: p || m || A.pending,
101
+ className: "contents",
102
+ children: T
103
+ })]
104
+ }), m ? /* @__PURE__ */ d(t, {
105
+ type: "button",
106
+ size: "icon",
107
+ variant: "secondary",
108
+ onClick: v,
109
+ disabled: p || !v,
110
+ "aria-label": b,
111
+ children: /* @__PURE__ */ d(c, {
112
+ className: "h-basalt-3_5 w-basalt-3_5 fill-current",
113
+ strokeWidth: 0
114
+ })
115
+ }) : /* @__PURE__ */ d(t, {
116
+ type: "submit",
117
+ size: "icon",
118
+ disabled: !A.canSend,
119
+ loading: A.pending,
120
+ "aria-label": y,
121
+ children: /* @__PURE__ */ d(o, {
122
+ className: "h-basalt-4 w-basalt-4",
123
+ strokeWidth: 2.25
124
+ })
125
+ })]
56
126
  })
57
- }) : /* @__PURE__ */ s(t, {
58
- type: "submit",
59
- size: "icon",
60
- disabled: l || !v.trim(),
61
- "aria-label": h,
62
- children: /* @__PURE__ */ s(a, {
63
- className: "h-4 w-4",
64
- strokeWidth: 2.25
65
- })
66
- })]
67
- })
127
+ ]
128
+ }), A.error && /* @__PURE__ */ d("p", {
129
+ id: N,
130
+ role: "alert",
131
+ className: "mt-basalt-content-gap text-basalt-sm text-basalt-danger",
132
+ children: A.error
133
+ })]
68
134
  });
69
135
  }
70
136
  //#endregion
71
- export { l as ChatComposer };
137
+ export { p as ChatComposer };
72
138
 
73
139
  //# sourceMappingURL=chat-composer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-composer.js","names":["ChatComposer","value","setValue","useState","ref","useRef","composingRef","resize","el","submit","text","disabled","streaming","onSend","cn","className","event","InputArea","label","Button","onCancel","cancelLabel","Square","sendLabel","ArrowUp"],"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\tresize();\n\t\t\tref.current?.focus();\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,SAAgBA,EAAa,EAC5B,cAAW,IACX,eAAY,IACZ,WAAQ,WACR,gBACA,WACA,aACA,eAAY,gBACZ,iBAAc,mBACd,gBACqB;CACrB,IAAM,CAACC,GAAOC,KAAYC,EAAS,EAAE,GAC/BC,IAAMC,EAA4B,IAAI,GACtCC,IAAeD,EAAO,EAAK,GAE3BE,UAAe;EACpB,IAAMC,IAAKJ,EAAI;EACf,AAAKI,MAGL,EAAG,MAAM,SAAS,OAClB,EAAG,MAAM,SAAS,GAAG,KAAK,IAAIA,EAAG,cAAc,GAAG,EAAE;CACrD,GAEMC,UAAe;EACpB,IAAMC,IAAOT,EAAM,KAAK;EACxB,AAAI,CAACS,KAAQC,KAAYC,MAGzBC,EAAOH,CAAI,GACXR,EAAS,EAAE,GACX,4BAA4B;GAE3B,AADAK,EAAO,GACPH,EAAI,SAAS,MAAM;EACpB,CAAC;CACF;CAkBA,OACC,kBAAC,QAAD;EACC,WAAWU,EAAG,uDAAuDC,CAAS;EACpE,WAnBM,MAAqB;GAEtC,AADAC,EAAM,eAAe,GACrBP,EAAO;EACR;EAkBE,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACC,kBAACQ,GAAD;IACM;IACE;IACP,WAAW,MAAU;KAEpB,AADAf,EAASc,EAAM,OAAO,KAAK,GAC3B,sBAAsBT,CAAM;IAC7B;IACA,0BAA0B;KACzB,EAAa,UAAU;IACxB;IACA,wBAAwB;KACvB,EAAa,UAAU;IACxB;IACW,YA9BI,MAA8C;KAChE,AAAID,EAAa,WAAWU,EAAM,YAAY,eAG1CA,EAAM,QAAQ,WAAWA,EAAM,aAGnCA,EAAM,eAAe,GACrBP,EAAO;IACR;IAsBI,MAAM;IACO;IACH;IACV,cAAYS;IACZ,WAAU;GACV,CAAA,GACAN,IACA,kBAACO,GAAD;IACC,MAAK;IACL,MAAK;IACL,SAAQ;IACR,SAASC;IACT,UAAU,CAACA;IACX,cAAYC;IAEZ,UAAA,kBAACC,GAAD;KAAQ,WAAU;KAA2B,aAAa;IAAI,CAAA;GACvD,CAAA,IAER,kBAACH,GAAD;IACC,MAAK;IACL,MAAK;IACL,UAAUR,KAAY,CAACV,EAAM,KAAK;IAClC,cAAYsB;IAEZ,UAAA,kBAACC,GAAD;KAAS,WAAU;KAAU,aAAa;IAAO,CAAA;GAC1C,CAAA,CAEL;;CACA,CAAA;AAER"}
1
+ {"version":3,"file":"chat-composer.js","names":["_Fragment","Fragment$1","ChatComposer","vm","useChatComposer","attachments","ref","useRef","fileInput","errorId","useId","composingRef","submit","cn","className","event","Paperclip","file","onRemoveAttachment","Button","disabled","streaming","X","InputArea","label","onFilesSelect","toolbar","onCancel","cancelLabel","Square","sendLabel","ArrowUp"],"sources":["../../src/components/chat-composer.tsx"],"sourcesContent":["import { ArrowUp, Paperclip, Square, X } from \"lucide-react\";\nimport { type FormEvent, type KeyboardEvent, type ReactNode, useId, useRef } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { useChatComposer } from \"../viewmodels/use-chat-composer\";\nimport { Button } from \"./button\";\nimport { InputArea } from \"./input-area\";\n\nexport interface ChatComposerProps {\n\t/** Controlled draft; keep one draft per conversation in the application. */\n\tvalue?: string;\n\t/** Initial draft when uncontrolled. */\n\tdefaultValue?: string;\n\tonValueChange?: (value: string) => void;\n\t/** Model, tool and context controls arranged before Send. */\n\ttoolbar?: ReactNode;\n\t/** Caller-owned attachments; this component never uploads files. */\n\tattachments?: readonly ChatComposerAttachment[];\n\tonRemoveAttachment?: (id: string) => void;\n\t/** Local file selection callback. File validation/upload belong to the application. */\n\tonFilesSelect?: (files: File[]) => void;\n\taccept?: string;\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. Async failure preserves draft and attachments for retry.\n\t */\n\tonSend: (text: string) => void | Promise<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 type ChatComposerAttachment = { id: string; name: string };\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\tvalue,\n\tdefaultValue,\n\tonValueChange,\n\ttoolbar,\n\tattachments = [],\n\tonRemoveAttachment,\n\tonFilesSelect,\n\taccept,\n}: ChatComposerProps) {\n\tconst vm = useChatComposer({\n\t\tvalue,\n\t\tdefaultValue,\n\t\tonValueChange,\n\t\tonSend,\n\t\tdisabled,\n\t\tstreaming,\n\t\thasAttachments: attachments.length > 0,\n\t});\n\tconst ref = useRef<HTMLTextAreaElement>(null);\n\tconst fileInput = useRef<HTMLInputElement>(null);\n\tconst errorId = useId();\n\tconst composingRef = useRef(false);\n\tconst submit = async () => {\n\t\tif (await vm.send()) ref.current?.focus();\n\t};\n\n\tconst onSubmit = (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tvoid submit();\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n\t\tif (composingRef.current || event.nativeEvent.isComposing || event.keyCode === 229) {\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\tvoid submit();\n\t};\n\n\treturn (\n\t\t<form className={cn(\"basalt-ui shrink-0\", className)} onSubmit={onSubmit}>\n\t\t\t<div className=\"rounded-basalt-lg bg-basalt-control p-basalt-space-lg ring-1 ring-basalt-border focus-within:ring-basalt-ring\">\n\t\t\t\t{attachments.length > 0 && (\n\t\t\t\t\t<ul\n\t\t\t\t\t\taria-label=\"Attachments\"\n\t\t\t\t\t\tclassName=\"mb-basalt-space-lg flex flex-wrap gap-basalt-space-md\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{attachments.map((file) => (\n\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\tkey={file.id}\n\t\t\t\t\t\t\t\tclassName=\"flex min-w-0 max-w-full items-center gap-basalt-space-sm rounded-basalt-md bg-basalt-secondary py-basalt-space-sm pl-basalt-space-lg pr-basalt-space-sm text-basalt-sm\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Paperclip aria-hidden=\"true\" className=\"size-basalt-icon shrink-0\" />\n\t\t\t\t\t\t\t\t<span className=\"truncate\">{file.name}</span>\n\t\t\t\t\t\t\t\t{onRemoveAttachment && (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tdisabled={disabled || vm.pending || streaming}\n\t\t\t\t\t\t\t\t\t\tclassName=\"size-basalt-5\"\n\t\t\t\t\t\t\t\t\t\taria-label={`Remove ${file.name}`}\n\t\t\t\t\t\t\t\t\t\tonClick={() => onRemoveAttachment(file.id)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<X />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t)}\n\t\t\t\t<InputArea\n\t\t\t\t\tref={ref}\n\t\t\t\t\tvalue={vm.value}\n\t\t\t\t\tonChange={(event) => vm.update(event.target.value)}\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 || vm.pending}\n\t\t\t\t\taria-label={label}\n\t\t\t\t\taria-describedby={vm.error ? errorId : undefined}\n\t\t\t\t\taria-description=\"Enter to send, Shift+Enter for a new line\"\n\t\t\t\t\tclassName=\"basalt-chat-input min-h-[calc(1lh+var(--basalt-space-2))] resize-none overflow-y-auto border-0 bg-transparent px-basalt-space-sm py-basalt-space-sm text-basalt-base leading-[var(--basalt-line-body)] shadow-none ring-0 outline-none\"\n\t\t\t\t/>\n\t\t\t\t<div className=\"mt-basalt-content-gap flex items-center justify-between gap-basalt-content-gap\">\n\t\t\t\t\t<div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-basalt-space-sm\">\n\t\t\t\t\t\t{onFilesSelect && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\tref={fileInput}\n\t\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\t\taccept={accept}\n\t\t\t\t\t\t\t\t\thidden\n\t\t\t\t\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\t\t\t\t\tonFilesSelect(Array.from(event.target.files ?? []));\n\t\t\t\t\t\t\t\t\t\tevent.target.value = \"\";\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\taria-label=\"Attach files\"\n\t\t\t\t\t\t\t\t\tdisabled={disabled || vm.pending || streaming}\n\t\t\t\t\t\t\t\t\tonClick={() => fileInput.current?.click()}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Paperclip />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<fieldset disabled={disabled || streaming || vm.pending} className=\"contents\">\n\t\t\t\t\t\t\t{toolbar}\n\t\t\t\t\t\t</fieldset>\n\t\t\t\t\t</div>\n\t\t\t\t\t{streaming ? (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tonClick={onCancel}\n\t\t\t\t\t\t\tdisabled={disabled || !onCancel}\n\t\t\t\t\t\t\taria-label={cancelLabel}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Square className=\"h-basalt-3_5 w-basalt-3_5 fill-current\" strokeWidth={0} />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\tdisabled={!vm.canSend}\n\t\t\t\t\t\t\tloading={vm.pending}\n\t\t\t\t\t\t\taria-label={sendLabel}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowUp className=\"h-basalt-4 w-basalt-4\" strokeWidth={2.25} />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{vm.error && (\n\t\t\t\t<p\n\t\t\t\t\tid={errorId}\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tclassName=\"mt-basalt-content-gap text-basalt-sm text-basalt-danger\"\n\t\t\t\t>\n\t\t\t\t\t{vm.error}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t</form>\n\t);\n}\n"],"mappings":"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"}
@@ -5,25 +5,25 @@ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
5
  //#region src/components/chat-header.tsx
6
6
  function i({ title: i, subtitle: a, leading: o, children: s, className: c, ...l }) {
7
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),
8
+ className: e(t, "flex items-center justify-between gap-basalt-content-gap border-b border-basalt-border/50 px-basalt-panel-x py-basalt-panel-y leading-[var(--basalt-line-body)]", c),
9
9
  ...l,
10
10
  children: [/* @__PURE__ */ r("div", {
11
- className: "flex min-w-0 items-center gap-2",
11
+ className: "flex min-w-0 items-center gap-basalt-content-gap",
12
12
  children: [o ? /* @__PURE__ */ n("span", {
13
- className: "flex h-8 w-8 items-center justify-center",
13
+ className: "flex h-basalt-8 w-basalt-8 items-center justify-center",
14
14
  children: o
15
15
  }) : null, /* @__PURE__ */ r("div", {
16
16
  className: "min-w-0",
17
17
  children: [/* @__PURE__ */ n("p", {
18
- className: "truncate text-sm font-semibold tracking-tight",
18
+ className: "truncate text-basalt-base font-semibold tracking-tight",
19
19
  children: i
20
20
  }), a ? /* @__PURE__ */ n("p", {
21
- className: "truncate text-[11px] text-basalt-muted-foreground",
21
+ className: "truncate text-basalt-xs text-basalt-muted-foreground",
22
22
  children: a
23
23
  }) : null]
24
24
  })]
25
25
  }), s ? /* @__PURE__ */ n("div", {
26
- className: "flex items-center gap-1",
26
+ className: "flex items-center gap-basalt-space-sm",
27
27
  children: s
28
28
  }) : null]
29
29
  });
@@ -1 +1 @@
1
- {"version":3,"file":"chat-header.js","names":["ChatHeader","props","cn","BASALT_UI_CLASS","className","leading","title","subtitle","children"],"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,SAAgBA,EAAW,EAC1B,UACA,aACA,YACA,aACA,cACA,GAAGC,KACgB;CACnB,OACC,kBAAC,UAAD;EACC,WAAWC,EACVC,GACA,wFACAC,CACD;EACA,GAAIH;EANL,UAAA,CAQC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACEI,IACA,kBAAC,QAAD;IAAM,WAAU;IAA4C,UAAAA;GAAc,CAAA,IACvE,MACJ,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACC,kBAAC,KAAD;KAAG,WAAU;KAAiD,UAAAC;IAAS,CAAA,GACtEC,IACA,kBAAC,KAAD;KAAG,WAAU;KAAqD,UAAAA;IAAY,CAAA,IAC3E,IACA;GACD,CAAA,CAAA;EACJ,CAAA,GAAAC,IAAW,kBAAC,OAAD;GAAK,WAAU;GAA2B;EAAc,CAAA,IAAI,IACjE;;AAEV"}
1
+ {"version":3,"file":"chat-header.js","names":["ChatHeader","props","cn","BASALT_UI_CLASS","className","leading","title","subtitle","children"],"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-basalt-content-gap border-b border-basalt-border/50 px-basalt-panel-x py-basalt-panel-y leading-[var(--basalt-line-body)]\",\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-basalt-content-gap\">\n\t\t\t\t{leading ? (\n\t\t\t\t\t<span className=\"flex h-basalt-8 w-basalt-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-basalt-base font-semibold tracking-tight\">{title}</p>\n\t\t\t\t\t{subtitle ? (\n\t\t\t\t\t\t<p className=\"truncate text-basalt-xs 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-basalt-space-sm\">{children}</div> : null}\n\t\t</header>\n\t);\n}\n"],"mappings":";;;;;AA6BA,SAAgBA,EAAW,EAC1B,UACA,aACA,YACA,aACA,cACA,GAAGC,KACgB;CACnB,OACC,kBAAC,UAAD;EACC,WAAWC,EACVC,GACA,mKACAC,CACD;EACA,GAAIH;EANL,UAAA,CAQC,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA,CACEI,IACA,kBAAC,QAAD;IAAM,WAAU;IAA0D,UAAAA;GAAc,CAAA,IACrF,MACJ,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACC,kBAAC,KAAD;KAAG,WAAU;KAA0D,UAAAC;IAAS,CAAA,GAC/EC,IACA,kBAAC,KAAD;KAAG,WAAU;KAAwD,UAAAA;IAAY,CAAA,IAC9E,IACA;GACD,CAAA,CAAA;EACJ,CAAA,GAAAC,IAAW,kBAAC,OAAD;GAAK,WAAU;GAAyC;EAAc,CAAA,IAAI,IAC/E;;AAEV"}
@@ -1,44 +1,48 @@
1
1
  "use client";
2
2
  import { cn as e } from "../utils/cn.js";
3
- import { Button as t } from "./button.js";
4
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
3
+ import { useHoverHighlight as t } from "../utils/use-hover-highlight.js";
4
+ import { NAV_ITEM_CLASS as n, NAV_LIST_CLASS as r } from "../utils/navigation.js";
5
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
6
  //#region src/components/chat-inbox.tsx
6
- function i({ items: i, activeId: a, onSelect: o, className: s, ...c }) {
7
- return /* @__PURE__ */ n("nav", {
8
- className: e("flex min-h-0 flex-col gap-0.5 overflow-y-auto p-2", s),
9
- ...c,
10
- children: i.map((i) => {
11
- let s = i.id === a;
12
- return /* @__PURE__ */ r(t, {
7
+ function o({ items: o, activeId: s, onSelect: c, className: l, ...u }) {
8
+ let d = t();
9
+ return /* @__PURE__ */ i("nav", {
10
+ ref: d,
11
+ className: e(r, "min-h-0 flex-col overflow-y-auto overscroll-y-contain", l),
12
+ ...u,
13
+ children: o.map((t) => {
14
+ let r = t.id === s;
15
+ return /* @__PURE__ */ a("button", {
13
16
  type: "button",
14
- variant: "ghost",
15
- "aria-current": s ? "true" : void 0,
16
- onClick: () => o(i.id),
17
- className: e("h-auto w-full justify-start gap-2 rounded-basalt-md px-2 py-2 text-left", s ? "bg-basalt-accent" : ""),
18
- children: [i.leading ? /* @__PURE__ */ n("span", {
19
- className: "flex h-8 w-8 shrink-0 items-center justify-center",
20
- children: i.leading
21
- }) : null, /* @__PURE__ */ r("span", {
17
+ "aria-current": r ? "true" : void 0,
18
+ "data-basalt-hover-item": "",
19
+ "data-hover-selected": r,
20
+ onClick: () => c(t.id),
21
+ className: e(n, "w-full justify-start"),
22
+ children: [t.leading ? /* @__PURE__ */ i("span", {
23
+ className: "flex h-basalt-8 w-basalt-8 shrink-0 items-center justify-center",
24
+ children: t.leading
25
+ }) : null, /* @__PURE__ */ a("span", {
22
26
  className: "min-w-0 flex-1",
23
- children: [/* @__PURE__ */ r("span", {
24
- className: "flex items-baseline justify-between gap-2",
25
- children: [/* @__PURE__ */ n("span", {
26
- className: "truncate text-sm font-medium text-basalt-foreground",
27
- children: i.title
28
- }), i.time ? /* @__PURE__ */ n("span", {
29
- className: "shrink-0 text-[11px] text-basalt-muted-foreground",
30
- children: i.time
27
+ children: [/* @__PURE__ */ a("span", {
28
+ className: "flex items-baseline justify-between gap-basalt-row-gap",
29
+ children: [/* @__PURE__ */ i("span", {
30
+ className: "truncate text-basalt-base font-medium text-basalt-foreground",
31
+ children: t.title
32
+ }), t.time ? /* @__PURE__ */ i("span", {
33
+ className: "shrink-0 text-basalt-xs text-basalt-muted-foreground",
34
+ children: t.time
31
35
  }) : null]
32
- }), i.preview ? /* @__PURE__ */ n("span", {
33
- className: "mt-0.5 block truncate text-xs text-basalt-muted-foreground",
34
- children: i.preview
36
+ }), t.preview ? /* @__PURE__ */ i("span", {
37
+ className: "mt-basalt-space-xs block truncate text-basalt-sm text-basalt-muted-foreground",
38
+ children: t.preview
35
39
  }) : null]
36
40
  })]
37
- }, i.id);
41
+ }, t.id);
38
42
  })
39
43
  });
40
44
  }
41
45
  //#endregion
42
- export { i as ChatInbox };
46
+ export { o as ChatInbox };
43
47
 
44
48
  //# sourceMappingURL=chat-inbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-inbox.js","names":["ChatInbox","props","cn","className","items","active","item","activeId","Button","onSelect"],"sources":["../../src/components/chat-inbox.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { Button } from \"./button\";\n\nexport interface ChatInboxItem {\n\t/** Stable id for selection. */\n\tid: string;\n\t/** Thread title. */\n\ttitle: string;\n\t/** Last-message preview. */\n\tpreview?: string;\n\t/** Timestamp label. */\n\ttime?: string;\n\t/** Optional leading mark. */\n\tleading?: ReactNode;\n}\n\nexport interface ChatInboxProps\n\textends Omit<HTMLAttributes<HTMLElement>, \"onSelect\" | \"className\"> {\n\t/**\n\t * Threads to list ({ id: string, title: string, preview?: string, time?: string, leading?: ReactNode }[]).\n\t */\n\titems: readonly ChatInboxItem[];\n\t/**\n\t * Selected thread id.\n\t */\n\tactiveId?: string;\n\t/**\n\t * Called when a thread is chosen.\n\t */\n\tonSelect: (id: string) => void;\n\t/**\n\t * Optional class name applied to the navigation element.\n\t * Component inherits native nav HTMLAttributes on the root element but does not forward ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatInbox({ items, activeId, onSelect, className, ...props }: ChatInboxProps) {\n\treturn (\n\t\t<nav className={cn(\"flex min-h-0 flex-col gap-0.5 overflow-y-auto p-2\", className)} {...props}>\n\t\t\t{items.map((item) => {\n\t\t\t\tconst active = item.id === activeId;\n\t\t\t\treturn (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\taria-current={active ? \"true\" : undefined}\n\t\t\t\t\t\tonClick={() => onSelect(item.id)}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"h-auto w-full justify-start gap-2 rounded-basalt-md px-2 py-2 text-left\",\n\t\t\t\t\t\t\tactive ? \"bg-basalt-accent\" : \"\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.leading ? (\n\t\t\t\t\t\t\t<span className=\"flex h-8 w-8 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t\t\t{item.leading}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t<span className=\"flex items-baseline justify-between gap-2\">\n\t\t\t\t\t\t\t\t<span className=\"truncate text-sm font-medium text-basalt-foreground\">\n\t\t\t\t\t\t\t\t\t{item.title}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{item.time ? (\n\t\t\t\t\t\t\t\t\t<span className=\"shrink-0 text-[11px] text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{item.time}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{item.preview ? (\n\t\t\t\t\t\t\t\t<span className=\"mt-0.5 block truncate text-xs text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{item.preview}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Button>\n\t\t\t\t);\n\t\t\t})}\n\t\t</nav>\n\t);\n}\n"],"mappings":";;;;;AAsCA,SAAgBA,EAAU,EAAE,UAAO,aAAU,aAAU,cAAW,GAAGC,KAAyB;CAC7F,OACC,kBAAC,OAAD;EAAK,WAAWC,EAAG,qDAAqDC,CAAS;EAAG,GAAIF;EACtF,UAAAG,EAAM,KAAK,MAAS;GACpB,IAAMC,IAASC,EAAK,OAAOC;GAC3B,OACC,kBAACC,GAAD;IAEC,MAAK;IACL,SAAQ;IACR,gBAAcH,IAAS,SAAS,KAAA;IAChC,eAAeI,EAASH,EAAK,EAAE;IAC/B,WAAWJ,EACV,2EACAG,IAAS,qBAAqB,EAC/B;IATD,UAAA,CAWEC,EAAK,UACL,kBAAC,QAAD;KAAM,WAAU;KACd,UAAAA,EAAK;IACD,CAAA,IACH,MACJ,kBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CACC,kBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA,CACC,kBAAC,QAAD;OAAM,WAAU;OACd,UAAAA,EAAK;MACD,CAAA,GACLA,EAAK,OACL,kBAAC,QAAD;OAAM,WAAU;OACd,UAAAA,EAAK;MACD,CAAA,IACH,IACC;KACL,CAAA,GAAAA,EAAK,UACL,kBAAC,QAAD;MAAM,WAAU;MACd,UAAAA,EAAK;KACD,CAAA,IACH,IACC;IACC,CAAA,CAAA;GAhCF,GAAAA,EAAK,EAgCH;EAEV,CAAC;CACG,CAAA;AAEP"}
1
+ {"version":3,"file":"chat-inbox.js","names":["ChatInbox","props","highlightRef","useHoverHighlight","cn","NAV_LIST_CLASS","className","items","active","item","activeId","onSelect","NAV_ITEM_CLASS"],"sources":["../../src/components/chat-inbox.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../utils/cn\";\nimport { NAV_ITEM_CLASS, NAV_LIST_CLASS } from \"../utils/navigation\";\nimport { useHoverHighlight } from \"../utils/use-hover-highlight\";\n\nexport interface ChatInboxItem {\n\t/** Stable id for selection. */\n\tid: string;\n\t/** Thread title. */\n\ttitle: string;\n\t/** Last-message preview. */\n\tpreview?: string;\n\t/** Timestamp label. */\n\ttime?: string;\n\t/** Optional leading mark. */\n\tleading?: ReactNode;\n}\n\nexport interface ChatInboxProps\n\textends Omit<HTMLAttributes<HTMLElement>, \"onSelect\" | \"className\"> {\n\t/**\n\t * Threads to list ({ id: string, title: string, preview?: string, time?: string, leading?: ReactNode }[]).\n\t */\n\titems: readonly ChatInboxItem[];\n\t/**\n\t * Selected thread id.\n\t */\n\tactiveId?: string;\n\t/**\n\t * Called when a thread is chosen.\n\t */\n\tonSelect: (id: string) => void;\n\t/**\n\t * Optional class name applied to the navigation element.\n\t * Component inherits native nav HTMLAttributes on the root element but does not forward ref.\n\t */\n\tclassName?: string;\n}\n\nexport function ChatInbox({ items, activeId, onSelect, className, ...props }: ChatInboxProps) {\n\tconst highlightRef = useHoverHighlight();\n\treturn (\n\t\t<nav\n\t\t\tref={highlightRef}\n\t\t\tclassName={cn(\n\t\t\t\tNAV_LIST_CLASS,\n\t\t\t\t\"min-h-0 flex-col overflow-y-auto overscroll-y-contain\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{items.map((item) => {\n\t\t\t\tconst active = item.id === activeId;\n\t\t\t\treturn (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\taria-current={active ? \"true\" : undefined}\n\t\t\t\t\t\tdata-basalt-hover-item=\"\"\n\t\t\t\t\t\tdata-hover-selected={active}\n\t\t\t\t\t\tonClick={() => onSelect(item.id)}\n\t\t\t\t\t\tclassName={cn(NAV_ITEM_CLASS, \"w-full justify-start\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t{item.leading ? (\n\t\t\t\t\t\t\t<span className=\"flex h-basalt-8 w-basalt-8 shrink-0 items-center justify-center\">\n\t\t\t\t\t\t\t\t{item.leading}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t<span className=\"flex items-baseline justify-between gap-basalt-row-gap\">\n\t\t\t\t\t\t\t\t<span className=\"truncate text-basalt-base font-medium text-basalt-foreground\">\n\t\t\t\t\t\t\t\t\t{item.title}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{item.time ? (\n\t\t\t\t\t\t\t\t\t<span className=\"shrink-0 text-basalt-xs text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{item.time}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{item.preview ? (\n\t\t\t\t\t\t\t\t<span className=\"mt-basalt-space-xs block truncate text-basalt-sm text-basalt-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{item.preview}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t);\n\t\t\t})}\n\t\t</nav>\n\t);\n}\n"],"mappings":";;;;;;AAuCA,SAAgBA,EAAU,EAAE,UAAO,aAAU,aAAU,cAAW,GAAGC,KAAyB;CAC7F,IAAMC,IAAeC,EAAkB;CACvC,OACC,kBAAC,OAAD;EACC,KAAKD;EACL,WAAWE,EACVC,GACA,yDACAC,CACD;EACA,GAAIL;EAEH,UAAAM,EAAM,KAAK,MAAS;GACpB,IAAMC,IAASC,EAAK,OAAOC;GAC3B,OACC,kBAAC,UAAD;IAEC,MAAK;IACL,gBAAcF,IAAS,SAAS,KAAA;IAChC,0BAAuB;IACvB,uBAAqBA;IACrB,eAAeG,EAASF,EAAK,EAAE;IAC/B,WAAWL,EAAGQ,GAAgB,sBAAsB;IAPrD,UAAA,CASEH,EAAK,UACL,kBAAC,QAAD;KAAM,WAAU;KACd,UAAAA,EAAK;IACD,CAAA,IACH,MACJ,kBAAC,QAAD;KAAM,WAAU;KAAhB,UAAA,CACC,kBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA,CACC,kBAAC,QAAD;OAAM,WAAU;OACd,UAAAA,EAAK;MACD,CAAA,GACLA,EAAK,OACL,kBAAC,QAAD;OAAM,WAAU;OACd,UAAAA,EAAK;MACD,CAAA,IACH,IACC;KACL,CAAA,GAAAA,EAAK,UACL,kBAAC,QAAD;MAAM,WAAU;MACd,UAAAA,EAAK;KACD,CAAA,IACH,IACC;IACC,CAAA,CAAA;GA9BF,GAAAA,EAAK,EA8BH;EAEV,CAAC;CACG,CAAA;AAEP"}
@@ -0,0 +1,8 @@
1
+ export interface ChatMarkdownProps {
2
+ /** Untrusted Markdown. Raw HTML is rendered as text, never injected. */
3
+ content: string;
4
+ /** Animate newly appended prose words; disable for static output. */
5
+ streaming?: boolean;
6
+ className?: string;
7
+ }
8
+ export declare function ChatMarkdown({ content, streaming, className }: ChatMarkdownProps): import("react").JSX.Element;
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import { cn as e } from "../utils/cn.js";
3
+ import { contextSourceHref as t } from "../models/context-cards.js";
4
+ import { CodeHighlighted as n } from "./code.js";
5
+ import { Fragment as r, useMemo as i } from "react";
6
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
7
+ import { decodeHTML as s } from "entities";
8
+ import { marked as c } from "marked";
9
+ //#region src/components/chat-markdown.tsx
10
+ function l(i, c = !1) {
11
+ return i.map((i, u) => {
12
+ let d = i, f;
13
+ switch (d.type) {
14
+ case "space":
15
+ case "def": return null;
16
+ case "heading": {
17
+ let e = `h${Math.min(6, d.depth + 1)}`;
18
+ f = /* @__PURE__ */ a(e, {
19
+ className: "font-semibold text-basalt-lg",
20
+ children: l(d.tokens, c)
21
+ });
22
+ break;
23
+ }
24
+ case "paragraph":
25
+ f = /* @__PURE__ */ a("p", { children: l(d.tokens, c) });
26
+ break;
27
+ case "text":
28
+ f = d.tokens ? l(d.tokens, c) : c ? s(d.text).split(/(\s+)/).map((e, t) => /\S/.test(e) ? /* @__PURE__ */ a("span", {
29
+ className: "basalt-chat-word",
30
+ children: e
31
+ }, t) : e) : s(d.text);
32
+ break;
33
+ case "strong":
34
+ f = /* @__PURE__ */ a("strong", { children: l(d.tokens, c) });
35
+ break;
36
+ case "em":
37
+ f = /* @__PURE__ */ a("em", { children: l(d.tokens, c) });
38
+ break;
39
+ case "del":
40
+ f = /* @__PURE__ */ a("del", { children: l(d.tokens, c) });
41
+ break;
42
+ case "codespan":
43
+ f = /* @__PURE__ */ a("code", {
44
+ className: "rounded-basalt-sm bg-basalt-accent px-basalt-space-sm py-basalt-space-xs font-mono text-basalt-sm",
45
+ children: d.text
46
+ });
47
+ break;
48
+ case "code":
49
+ f = /* @__PURE__ */ a(n, {
50
+ code: d.text,
51
+ title: d.lang || "text",
52
+ lineNumbers: !0
53
+ });
54
+ break;
55
+ case "blockquote":
56
+ f = /* @__PURE__ */ a("blockquote", {
57
+ className: "border-l-2 border-basalt-border pl-basalt-space-lg text-basalt-muted-foreground",
58
+ children: l(d.tokens, c)
59
+ });
60
+ break;
61
+ case "list": {
62
+ let t = d.ordered ? "ol" : "ul";
63
+ f = /* @__PURE__ */ a(t, {
64
+ start: d.ordered ? Number(d.start) || 1 : void 0,
65
+ className: e("space-y-basalt-space-sm pl-basalt-space-lg", d.ordered ? "list-decimal" : "list-disc"),
66
+ children: d.items.map((e, t) => /* @__PURE__ */ o("li", { children: [e.task && /* @__PURE__ */ a("span", {
67
+ "aria-label": e.checked ? "Complete" : "Incomplete",
68
+ role: "img",
69
+ children: e.checked ? "☑ " : "☐ "
70
+ }), l(e.tokens, c)] }, `${u}-${t}`))
71
+ });
72
+ break;
73
+ }
74
+ case "link":
75
+ case "image": {
76
+ let e = t(s(d.href)), n = d.type === "link" ? l(d.tokens, c) : s(d.text);
77
+ f = e ? /* @__PURE__ */ a("a", {
78
+ href: e,
79
+ target: "_blank",
80
+ rel: "noopener noreferrer",
81
+ className: "text-basalt-primary underline underline-offset-4",
82
+ children: n
83
+ }) : n;
84
+ break;
85
+ }
86
+ case "table":
87
+ f = /* @__PURE__ */ a("div", {
88
+ className: "max-w-full overflow-x-auto",
89
+ children: /* @__PURE__ */ o("table", {
90
+ className: "w-full border-collapse text-left text-basalt-sm",
91
+ children: [/* @__PURE__ */ a("thead", { children: /* @__PURE__ */ a("tr", { children: d.header.map((e, t) => /* @__PURE__ */ a("th", {
92
+ className: "whitespace-nowrap border-b border-basalt-border px-basalt-space-lg py-basalt-space-md",
93
+ children: l(e.tokens, c)
94
+ }, t)) }) }), /* @__PURE__ */ a("tbody", { children: d.rows.map((e, t) => /* @__PURE__ */ a("tr", { children: e.map((e, t) => /* @__PURE__ */ a("td", {
95
+ className: "border-b border-basalt-border px-basalt-space-lg py-basalt-space-md",
96
+ children: l(e.tokens, c)
97
+ }, t)) }, t)) })]
98
+ })
99
+ });
100
+ break;
101
+ case "br":
102
+ f = /* @__PURE__ */ a("br", {});
103
+ break;
104
+ case "hr":
105
+ f = /* @__PURE__ */ a("hr", { className: "border-basalt-border" });
106
+ break;
107
+ default: f = "text" in d ? String(d.text) : d.raw;
108
+ }
109
+ return /* @__PURE__ */ a(r, { children: f }, u);
110
+ });
111
+ }
112
+ function u({ content: t, streaming: n = !1, className: r }) {
113
+ let o = i(() => l(c.lexer(t, { gfm: !0 }), n), [t, n]);
114
+ return /* @__PURE__ */ a("div", {
115
+ className: e("basalt-ui min-w-0 space-y-basalt-space-lg break-words text-basalt-base leading-[var(--basalt-line-relaxed)] [&_p]:whitespace-pre-wrap", r),
116
+ children: o
117
+ });
118
+ }
119
+ //#endregion
120
+ export { u as ChatMarkdown };
121
+
122
+ //# sourceMappingURL=chat-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-markdown.js","names":["nodes","tokens","token","raw","node","Heading","streaming","decodeHTML","word","i","CodeHighlighted","List","cn","item","index","href","contextSourceHref","text","cell","row","j","Fragment","ChatMarkdown","rendered","useMemo","marked","content","className"],"sources":["../../src/components/chat-markdown.tsx"],"sourcesContent":["import { decodeHTML } from \"entities\";\nimport { type MarkedToken, marked, type Token } from \"marked\";\nimport { Fragment, type ReactNode, useMemo } from \"react\";\nimport { contextSourceHref } from \"../models/context-cards\";\nimport { cn } from \"../utils/cn\";\nimport { CodeHighlighted } from \"./code\";\n\nexport interface ChatMarkdownProps {\n\t/** Untrusted Markdown. Raw HTML is rendered as text, never injected. */\n\tcontent: string;\n\t/** Animate newly appended prose words; disable for static output. */\n\tstreaming?: boolean;\n\tclassName?: string;\n}\n\nfunction nodes(tokens: readonly Token[], streaming = false): ReactNode {\n\treturn tokens.map((raw, index) => {\n\t\tconst token = raw as MarkedToken;\n\t\tlet node: ReactNode;\n\t\tswitch (token.type) {\n\t\t\tcase \"space\":\n\t\t\tcase \"def\":\n\t\t\t\treturn null;\n\t\t\tcase \"heading\": {\n\t\t\t\tconst Heading = `h${Math.min(6, token.depth + 1)}` as \"h2\";\n\t\t\t\tnode = (\n\t\t\t\t\t<Heading className=\"font-semibold text-basalt-lg\">\n\t\t\t\t\t\t{nodes(token.tokens, streaming)}\n\t\t\t\t\t</Heading>\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"paragraph\":\n\t\t\t\tnode = <p>{nodes(token.tokens, streaming)}</p>;\n\t\t\t\tbreak;\n\t\t\tcase \"text\":\n\t\t\t\tnode = token.tokens\n\t\t\t\t\t? nodes(token.tokens, streaming)\n\t\t\t\t\t: streaming\n\t\t\t\t\t\t? decodeHTML(token.text)\n\t\t\t\t\t\t\t\t.split(/(\\s+)/)\n\t\t\t\t\t\t\t\t.map((word, i) =>\n\t\t\t\t\t\t\t\t\t/\\S/.test(word) ? (\n\t\t\t\t\t\t\t\t\t\t<span key={i} className=\"basalt-chat-word\">\n\t\t\t\t\t\t\t\t\t\t\t{word}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\tword\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: decodeHTML(token.text);\n\t\t\t\tbreak;\n\t\t\tcase \"strong\":\n\t\t\t\tnode = <strong>{nodes(token.tokens, streaming)}</strong>;\n\t\t\t\tbreak;\n\t\t\tcase \"em\":\n\t\t\t\tnode = <em>{nodes(token.tokens, streaming)}</em>;\n\t\t\t\tbreak;\n\t\t\tcase \"del\":\n\t\t\t\tnode = <del>{nodes(token.tokens, streaming)}</del>;\n\t\t\t\tbreak;\n\t\t\tcase \"codespan\":\n\t\t\t\tnode = (\n\t\t\t\t\t<code className=\"rounded-basalt-sm bg-basalt-accent px-basalt-space-sm py-basalt-space-xs font-mono text-basalt-sm\">\n\t\t\t\t\t\t{token.text}\n\t\t\t\t\t</code>\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"code\":\n\t\t\t\tnode = <CodeHighlighted code={token.text} title={token.lang || \"text\"} lineNumbers />;\n\t\t\t\tbreak;\n\t\t\tcase \"blockquote\":\n\t\t\t\tnode = (\n\t\t\t\t\t<blockquote className=\"border-l-2 border-basalt-border pl-basalt-space-lg text-basalt-muted-foreground\">\n\t\t\t\t\t\t{nodes(token.tokens, streaming)}\n\t\t\t\t\t</blockquote>\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"list\": {\n\t\t\t\tconst List = token.ordered ? \"ol\" : \"ul\";\n\t\t\t\tnode = (\n\t\t\t\t\t<List\n\t\t\t\t\t\tstart={token.ordered ? Number(token.start) || 1 : undefined}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"space-y-basalt-space-sm pl-basalt-space-lg\",\n\t\t\t\t\t\t\ttoken.ordered ? \"list-decimal\" : \"list-disc\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{token.items.map((item, i) => (\n\t\t\t\t\t\t\t<li key={`${index}-${i}`}>\n\t\t\t\t\t\t\t\t{item.task && (\n\t\t\t\t\t\t\t\t\t<span aria-label={item.checked ? \"Complete\" : \"Incomplete\"} role=\"img\">\n\t\t\t\t\t\t\t\t\t\t{item.checked ? \"☑ \" : \"☐ \"}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{nodes(item.tokens, streaming)}\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</List>\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"link\":\n\t\t\tcase \"image\": {\n\t\t\t\tconst href = contextSourceHref(decodeHTML(token.href));\n\t\t\t\tconst text =\n\t\t\t\t\ttoken.type === \"link\" ? nodes(token.tokens, streaming) : decodeHTML(token.text);\n\t\t\t\tnode = href ? (\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={href}\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tclassName=\"text-basalt-primary underline underline-offset-4\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{text}\n\t\t\t\t\t</a>\n\t\t\t\t) : (\n\t\t\t\t\ttext\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"table\":\n\t\t\t\tnode = (\n\t\t\t\t\t<div className=\"max-w-full overflow-x-auto\">\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-left text-basalt-sm\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t{token.header.map((cell, i) => (\n\t\t\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"whitespace-nowrap border-b border-basalt-border px-basalt-space-lg py-basalt-space-md\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{nodes(cell.tokens, streaming)}\n\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{token.rows.map((row, i) => (\n\t\t\t\t\t\t\t\t\t<tr key={i}>\n\t\t\t\t\t\t\t\t\t\t{row.map((cell, j) => (\n\t\t\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\t\t\tkey={j}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-b border-basalt-border px-basalt-space-lg py-basalt-space-md\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{nodes(cell.tokens, streaming)}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase \"br\":\n\t\t\t\tnode = <br />;\n\t\t\t\tbreak;\n\t\t\tcase \"hr\":\n\t\t\t\tnode = <hr className=\"border-basalt-border\" />;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tnode = \"text\" in token ? String(token.text) : token.raw;\n\t\t}\n\t\treturn <Fragment key={index}>{node}</Fragment>;\n\t});\n}\n\nexport function ChatMarkdown({ content, streaming = false, className }: ChatMarkdownProps) {\n\tconst rendered = useMemo(\n\t\t() => nodes(marked.lexer(content, { gfm: true }), streaming),\n\t\t[content, streaming],\n\t);\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"basalt-ui min-w-0 space-y-basalt-space-lg break-words text-basalt-base leading-[var(--basalt-line-relaxed)] [&_p]:whitespace-pre-wrap\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{rendered}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ChatMessageProps {
3
+ variant: "user" | "assistant";
4
+ content: string;
5
+ streaming?: boolean;
6
+ /** Thinking, tool calls and other trace content before the response. */
7
+ trace?: ReactNode;
8
+ /** Approval or result content after the response; use sources for citations. */
9
+ children?: ReactNode;
10
+ author?: string;
11
+ onEdit?: () => void;
12
+ onRegenerate?: () => void;
13
+ feedback?: "up" | "down";
14
+ onFeedback?: (value: "up" | "down") => void;
15
+ /** Compact source disclosure beside message actions. No card wrapper is added. */
16
+ sources?: readonly {
17
+ id: string;
18
+ name: string;
19
+ type?: string;
20
+ href?: string;
21
+ }[];
22
+ /** Source disclosure label. @default "Sources" */
23
+ sourcesLabel?: string;
24
+ className?: string;
25
+ }
26
+ export declare function ChatMessage({ variant, content, streaming, trace, children, author, onEdit, onRegenerate, feedback, onFeedback, className, sources, sourcesLabel, }: ChatMessageProps): import("react").JSX.Element;