@hex-core/components 1.9.0 → 1.10.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 (146) hide show
  1. package/dist/_tsup-dts-rollup.d.ts +575 -18
  2. package/dist/accordion.js.map +1 -1
  3. package/dist/alert-dialog.js.map +1 -1
  4. package/dist/alert.js.map +1 -1
  5. package/dist/arc.js.map +1 -1
  6. package/dist/attachment.js.map +1 -1
  7. package/dist/audio-player.js.map +1 -1
  8. package/dist/audio-waveform.js.map +1 -1
  9. package/dist/auth-forgot-password.js.map +1 -1
  10. package/dist/auth-reset-password.js.map +1 -1
  11. package/dist/auth-sign-in-split.js.map +1 -1
  12. package/dist/auth-sign-up-card.js.map +1 -1
  13. package/dist/auth-verify-email.js.map +1 -1
  14. package/dist/auth-verify-otp.js.map +1 -1
  15. package/dist/avatar.js.map +1 -1
  16. package/dist/badge.js.map +1 -1
  17. package/dist/branch.d.ts +2 -0
  18. package/dist/branch.js +136 -0
  19. package/dist/branch.js.map +1 -0
  20. package/dist/breadcrumb.js.map +1 -1
  21. package/dist/button.js.map +1 -1
  22. package/dist/calendar.js.map +1 -1
  23. package/dist/canvas.js.map +1 -1
  24. package/dist/card.js.map +1 -1
  25. package/dist/chain-of-thought.d.ts +3 -0
  26. package/dist/chain-of-thought.js +119 -0
  27. package/dist/chain-of-thought.js.map +1 -0
  28. package/dist/checkbox.js.map +1 -1
  29. package/dist/chord.js.map +1 -1
  30. package/dist/citation.js.map +1 -1
  31. package/dist/cloze.js.map +1 -1
  32. package/dist/cluster.js.map +1 -1
  33. package/dist/code-block-copy.js.map +1 -1
  34. package/dist/code-block.js.map +1 -1
  35. package/dist/color-picker.js.map +1 -1
  36. package/dist/combobox.js.map +1 -1
  37. package/dist/command.js.map +1 -1
  38. package/dist/compare-table.js.map +1 -1
  39. package/dist/composer.js.map +1 -1
  40. package/dist/container.js.map +1 -1
  41. package/dist/context-menu.js.map +1 -1
  42. package/dist/conversation.d.ts +3 -0
  43. package/dist/conversation.js +358 -0
  44. package/dist/conversation.js.map +1 -0
  45. package/dist/data-table.js.map +1 -1
  46. package/dist/date-picker.js.map +1 -1
  47. package/dist/deck.js.map +1 -1
  48. package/dist/dendrogram.js.map +1 -1
  49. package/dist/diagram.js.map +1 -1
  50. package/dist/dialog.js.map +1 -1
  51. package/dist/drawer.js.map +1 -1
  52. package/dist/dropdown-menu.js.map +1 -1
  53. package/dist/dropzone.js.map +1 -1
  54. package/dist/empty.js.map +1 -1
  55. package/dist/error-state.js.map +1 -1
  56. package/dist/file-tree.js.map +1 -1
  57. package/dist/flashcard.js.map +1 -1
  58. package/dist/flowchart.js.map +1 -1
  59. package/dist/form.js.map +1 -1
  60. package/dist/funnel.js.map +1 -1
  61. package/dist/gantt.js.map +1 -1
  62. package/dist/grid.js.map +1 -1
  63. package/dist/hover-card.js.map +1 -1
  64. package/dist/image-occlusion.js.map +1 -1
  65. package/dist/index.d.ts +21 -0
  66. package/dist/index.js +1011 -13
  67. package/dist/index.js.map +1 -1
  68. package/dist/inline-citation.d.ts +2 -0
  69. package/dist/inline-citation.js +108 -0
  70. package/dist/inline-citation.js.map +1 -0
  71. package/dist/input-otp.js.map +1 -1
  72. package/dist/input.js.map +1 -1
  73. package/dist/label.js.map +1 -1
  74. package/dist/loading-indicator.js.map +1 -1
  75. package/dist/loading.js.map +1 -1
  76. package/dist/markdown.d.ts +1 -0
  77. package/dist/markdown.js +784 -4
  78. package/dist/markdown.js.map +1 -1
  79. package/dist/matrix.js.map +1 -1
  80. package/dist/menubar.js.map +1 -1
  81. package/dist/message-actions.js.map +1 -1
  82. package/dist/message-list.js.map +1 -1
  83. package/dist/message.js.map +1 -1
  84. package/dist/mind-map.js.map +1 -1
  85. package/dist/multi-combobox.js.map +1 -1
  86. package/dist/navigation-menu.js.map +1 -1
  87. package/dist/org-chart.js.map +1 -1
  88. package/dist/pagination.js.map +1 -1
  89. package/dist/plan.d.ts +3 -0
  90. package/dist/plan.js +183 -0
  91. package/dist/plan.js.map +1 -0
  92. package/dist/popover.js.map +1 -1
  93. package/dist/progress.js.map +1 -1
  94. package/dist/pyramid.js.map +1 -1
  95. package/dist/quiz.js.map +1 -1
  96. package/dist/radio-group.js.map +1 -1
  97. package/dist/reasoning.js.map +1 -1
  98. package/dist/resizable.js.map +1 -1
  99. package/dist/sankey.js.map +1 -1
  100. package/dist/schemas.d.ts +8 -0
  101. package/dist/schemas.js +774 -17
  102. package/dist/schemas.js.map +1 -1
  103. package/dist/scroll-area.js.map +1 -1
  104. package/dist/select.js.map +1 -1
  105. package/dist/separator.js.map +1 -1
  106. package/dist/sequence.js.map +1 -1
  107. package/dist/sheet.js.map +1 -1
  108. package/dist/shimmer.d.ts +2 -0
  109. package/dist/shimmer.js +39 -0
  110. package/dist/shimmer.js.map +1 -0
  111. package/dist/sidebar.js.map +1 -1
  112. package/dist/skeleton.js.map +1 -1
  113. package/dist/slider.js.map +1 -1
  114. package/dist/sources.d.ts +3 -0
  115. package/dist/sources.js +164 -0
  116. package/dist/sources.js.map +1 -0
  117. package/dist/spaced-repetition.js.map +1 -1
  118. package/dist/spacer.js.map +1 -1
  119. package/dist/speech-recognition.js.map +1 -1
  120. package/dist/stack.js.map +1 -1
  121. package/dist/stepper.js.map +1 -1
  122. package/dist/suggestion.js.map +1 -1
  123. package/dist/sunburst.js.map +1 -1
  124. package/dist/switch.js.map +1 -1
  125. package/dist/table.js.map +1 -1
  126. package/dist/tabs.js.map +1 -1
  127. package/dist/tag.js.map +1 -1
  128. package/dist/task.d.ts +3 -0
  129. package/dist/task.js +189 -0
  130. package/dist/task.js.map +1 -0
  131. package/dist/terminal.js +11 -0
  132. package/dist/terminal.js.map +1 -1
  133. package/dist/textarea.js.map +1 -1
  134. package/dist/time-axis.js.map +1 -1
  135. package/dist/time-picker.js.map +1 -1
  136. package/dist/timeline.js.map +1 -1
  137. package/dist/toggle-group.js.map +1 -1
  138. package/dist/toggle.js.map +1 -1
  139. package/dist/tool-call.js +5 -6
  140. package/dist/tool-call.js.map +1 -1
  141. package/dist/toolbar.js.map +1 -1
  142. package/dist/tooltip.js.map +1 -1
  143. package/dist/tree-map.js.map +1 -1
  144. package/dist/tree.js.map +1 -1
  145. package/dist/venn.js.map +1 -1
  146. package/package.json +8 -3
package/dist/markdown.js CHANGED
@@ -1,15 +1,651 @@
1
1
  "use client";
2
- import { Streamdown } from 'streamdown';
2
+ import * as React from 'react';
3
+ import ReactMarkdown from 'react-markdown';
4
+ import rehypeRaw from 'rehype-raw';
5
+ import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
6
+ import remarkGfm from 'remark-gfm';
7
+ import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
3
8
  import { clsx } from 'clsx';
4
9
  import { twMerge } from 'tailwind-merge';
5
- import { jsx } from 'react/jsx-runtime';
10
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
11
+ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
12
+ import { visit } from 'unist-util-visit';
6
13
 
7
14
  function cn(...inputs) {
8
15
  return twMerge(clsx(inputs));
9
16
  }
17
+ var SAFE_URL_SCHEMES = ["http:", "https:", "mailto:"];
18
+ function safeUrl(raw) {
19
+ if (raw === void 0 || raw === "") return void 0;
20
+ let parsed;
21
+ try {
22
+ parsed = new URL(raw);
23
+ } catch {
24
+ return raw.includes(":") ? void 0 : raw;
25
+ }
26
+ return SAFE_URL_SCHEMES.some((scheme) => scheme === parsed.protocol) ? raw : void 0;
27
+ }
28
+ function InlineCitation({
29
+ index,
30
+ title,
31
+ url: rawUrl,
32
+ excerpt,
33
+ openDelay = 200,
34
+ className
35
+ }) {
36
+ const url = safeUrl(rawUrl);
37
+ const triggerClasses = cn(
38
+ "inline-flex select-none items-center justify-center rounded-sm bg-primary/10 px-1 py-0.5 text-[0.7em] font-mono font-semibold text-primary leading-none",
39
+ "transition-all duration-[var(--duration-normal,200ms)] ease-out",
40
+ "hover:bg-primary/15",
41
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
42
+ className
43
+ );
44
+ const trigger = url ? /* @__PURE__ */ jsxs(
45
+ "a",
46
+ {
47
+ href: url,
48
+ target: "_blank",
49
+ rel: "noreferrer noopener external",
50
+ className: triggerClasses,
51
+ "aria-label": `Source ${index}: ${title}`,
52
+ children: [
53
+ "[",
54
+ index,
55
+ "]"
56
+ ]
57
+ }
58
+ ) : (
59
+ // No URL → still focusable so keyboard users can open the popover.
60
+ // Real <button type="button"> over a styled span so the trigger
61
+ // honors space/enter activation natively.
62
+ /* @__PURE__ */ jsxs(
63
+ "button",
64
+ {
65
+ type: "button",
66
+ className: triggerClasses,
67
+ "aria-label": `Source ${index}: ${title}`,
68
+ children: [
69
+ "[",
70
+ index,
71
+ "]"
72
+ ]
73
+ }
74
+ )
75
+ );
76
+ return /* @__PURE__ */ jsxs(HoverCardPrimitive.Root, { openDelay, children: [
77
+ /* @__PURE__ */ jsx(HoverCardPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("sup", { className: "inline-block leading-none", children: trigger }) }),
78
+ /* @__PURE__ */ jsx(HoverCardPrimitive.Portal, { children: /* @__PURE__ */ jsx(
79
+ HoverCardPrimitive.Content,
80
+ {
81
+ side: "top",
82
+ align: "center",
83
+ sideOffset: 4,
84
+ className: cn(
85
+ "z-50 max-w-xs rounded-md border border-border bg-popover p-3 text-popover-foreground shadow-md",
86
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
87
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
88
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
89
+ ),
90
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
91
+ /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] text-muted-foreground", children: [
92
+ "[",
93
+ index,
94
+ "]"
95
+ ] }),
96
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium leading-snug text-foreground", children: title }),
97
+ excerpt ? /* @__PURE__ */ jsx("span", { className: "text-xs leading-relaxed text-muted-foreground", children: excerpt }) : null,
98
+ url ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-primary underline-offset-2 hover:underline", children: inferDisplayUrl(url) }) : null
99
+ ] })
100
+ }
101
+ ) })
102
+ ] });
103
+ }
104
+ function inferDisplayUrl(href) {
105
+ try {
106
+ const url = new URL(href);
107
+ return url.hostname.replace(/^www\./, "") + url.pathname;
108
+ } catch {
109
+ return href;
110
+ }
111
+ }
112
+ function Reasoning({
113
+ children,
114
+ defaultOpen = false,
115
+ durationMs,
116
+ label,
117
+ className
118
+ }) {
119
+ const headerLabel = label ?? (typeof durationMs === "number" ? formatThoughtFor(durationMs) : "Thinking");
120
+ return /* @__PURE__ */ jsxs(
121
+ CollapsiblePrimitive.Root,
122
+ {
123
+ defaultOpen,
124
+ className: cn("overflow-hidden rounded-md border-l-2 border-foreground/15 bg-muted/20", className),
125
+ children: [
126
+ /* @__PURE__ */ jsxs(
127
+ CollapsiblePrimitive.Trigger,
128
+ {
129
+ className: cn(
130
+ "group flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-muted-foreground",
131
+ "transition-all duration-[var(--duration-normal,200ms)] ease-out",
132
+ "hover:text-foreground",
133
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
134
+ ),
135
+ children: [
136
+ /* @__PURE__ */ jsx(SparkleGlyph, {}),
137
+ /* @__PURE__ */ jsx("span", { className: "font-medium italic", children: headerLabel }),
138
+ /* @__PURE__ */ jsx(Chevron, {})
139
+ ]
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsx(CollapsiblePrimitive.Content, { className: "overflow-hidden border-t border-foreground/[0.06] px-3 py-2 text-sm text-muted-foreground", children })
143
+ ]
144
+ }
145
+ );
146
+ }
147
+ function formatThoughtFor(ms) {
148
+ if (ms < 1e3) return `Thought for ${ms}ms`;
149
+ const seconds = ms / 1e3;
150
+ const formatted = seconds >= 10 ? Math.round(seconds).toString() : seconds.toFixed(1);
151
+ return `Thought for ${formatted}s`;
152
+ }
153
+ function SparkleGlyph() {
154
+ return /* @__PURE__ */ jsx(
155
+ "svg",
156
+ {
157
+ "aria-hidden": true,
158
+ viewBox: "0 0 16 16",
159
+ width: "12",
160
+ height: "12",
161
+ fill: "none",
162
+ stroke: "currentColor",
163
+ strokeWidth: "1.5",
164
+ strokeLinecap: "round",
165
+ strokeLinejoin: "round",
166
+ className: "shrink-0",
167
+ children: /* @__PURE__ */ jsx("path", { d: "M8 1.5l1.5 4 4 1.5-4 1.5L8 12.5 6.5 8.5l-4-1.5 4-1.5L8 1.5z" })
168
+ }
169
+ );
170
+ }
171
+ function Chevron() {
172
+ return /* @__PURE__ */ jsx(
173
+ "svg",
174
+ {
175
+ "aria-hidden": true,
176
+ viewBox: "0 0 16 16",
177
+ width: "12",
178
+ height: "12",
179
+ fill: "none",
180
+ stroke: "currentColor",
181
+ strokeWidth: "1.5",
182
+ strokeLinecap: "round",
183
+ strokeLinejoin: "round",
184
+ className: "ml-auto shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180",
185
+ children: /* @__PURE__ */ jsx("path", { d: "M4 6l4 4 4-4" })
186
+ }
187
+ );
188
+ }
189
+ function Citation({ title, url, page, index, className, children }) {
190
+ const baseClasses = cn(
191
+ "inline-flex items-center gap-1.5 rounded-md border border-foreground/15 bg-card px-2 py-0.5 text-xs",
192
+ "transition-all duration-[var(--duration-normal,200ms)] ease-out",
193
+ className
194
+ );
195
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
196
+ typeof index === "number" ? /* @__PURE__ */ jsxs("span", { className: "font-mono text-[10px] font-semibold text-muted-foreground", children: [
197
+ "[",
198
+ index,
199
+ "]"
200
+ ] }) : /* @__PURE__ */ jsx(DocGlyph, {}),
201
+ /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: title }),
202
+ typeof page === "number" ? /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
203
+ "p.",
204
+ page
205
+ ] }) : null,
206
+ children
207
+ ] });
208
+ if (url) {
209
+ return /* @__PURE__ */ jsx(
210
+ "a",
211
+ {
212
+ href: url,
213
+ target: "_blank",
214
+ rel: "noreferrer noopener",
215
+ className: cn(
216
+ baseClasses,
217
+ "hover:border-foreground/30 hover:bg-secondary/40 hover:shadow-sm",
218
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
219
+ ),
220
+ children: body
221
+ }
222
+ );
223
+ }
224
+ return /* @__PURE__ */ jsx("span", { className: baseClasses, children: body });
225
+ }
226
+ function DocGlyph() {
227
+ return /* @__PURE__ */ jsxs(
228
+ "svg",
229
+ {
230
+ "aria-hidden": true,
231
+ viewBox: "0 0 16 16",
232
+ width: "11",
233
+ height: "11",
234
+ fill: "none",
235
+ stroke: "currentColor",
236
+ strokeWidth: "1.5",
237
+ strokeLinecap: "round",
238
+ strokeLinejoin: "round",
239
+ className: "shrink-0 text-muted-foreground",
240
+ children: [
241
+ /* @__PURE__ */ jsx("path", { d: "M9 1.5H4.5A1.5 1.5 0 0 0 3 3v10a1.5 1.5 0 0 0 1.5 1.5h7A1.5 1.5 0 0 0 13 13V5.5L9 1.5z" }),
242
+ /* @__PURE__ */ jsx("path", { d: "M9 1.5V5.5h4" })
243
+ ]
244
+ }
245
+ );
246
+ }
247
+ function Sources({ sources, defaultOpen = true, className }) {
248
+ if (sources.length === 0) return null;
249
+ const count = sources.length;
250
+ const headerLabel = count === 1 ? "1 source" : `${count} sources`;
251
+ return /* @__PURE__ */ jsxs(
252
+ CollapsiblePrimitive.Root,
253
+ {
254
+ defaultOpen,
255
+ className: cn(
256
+ "overflow-hidden rounded-md border border-border bg-card text-card-foreground",
257
+ className
258
+ ),
259
+ children: [
260
+ /* @__PURE__ */ jsxs(
261
+ CollapsiblePrimitive.Trigger,
262
+ {
263
+ className: cn(
264
+ "group flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-muted-foreground",
265
+ "transition-all duration-[var(--duration-normal,200ms)] ease-out",
266
+ "hover:text-foreground",
267
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
268
+ ),
269
+ children: [
270
+ /* @__PURE__ */ jsx(DocStackGlyph, {}),
271
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: headerLabel }),
272
+ /* @__PURE__ */ jsx(Chevron2, {})
273
+ ]
274
+ }
275
+ ),
276
+ /* @__PURE__ */ jsx(CollapsiblePrimitive.Content, { className: "overflow-hidden border-t border-foreground/[0.06]", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 p-2", children: sources.map((s, i) => /* @__PURE__ */ jsx(
277
+ Citation,
278
+ {
279
+ title: s.title,
280
+ url: safeUrl(s.url),
281
+ page: s.page,
282
+ index: s.index ?? i + 1
283
+ },
284
+ `${s.url ?? s.title}-${i}`
285
+ )) }) })
286
+ ]
287
+ }
288
+ );
289
+ }
290
+ function DocStackGlyph() {
291
+ return /* @__PURE__ */ jsxs(
292
+ "svg",
293
+ {
294
+ "aria-hidden": true,
295
+ viewBox: "0 0 16 16",
296
+ width: "12",
297
+ height: "12",
298
+ fill: "none",
299
+ stroke: "currentColor",
300
+ strokeWidth: "1.5",
301
+ strokeLinecap: "round",
302
+ strokeLinejoin: "round",
303
+ className: "shrink-0",
304
+ children: [
305
+ /* @__PURE__ */ jsx("path", { d: "M5 3h7l1.5 1.5V13H5V3z" }),
306
+ /* @__PURE__ */ jsx("path", { d: "M3 5v8.5L4.5 15H11" })
307
+ ]
308
+ }
309
+ );
310
+ }
311
+ function Chevron2() {
312
+ return /* @__PURE__ */ jsx(
313
+ "svg",
314
+ {
315
+ "aria-hidden": true,
316
+ viewBox: "0 0 16 16",
317
+ width: "12",
318
+ height: "12",
319
+ fill: "none",
320
+ stroke: "currentColor",
321
+ strokeWidth: "1.5",
322
+ strokeLinecap: "round",
323
+ strokeLinejoin: "round",
324
+ className: "ml-auto shrink-0 transition-transform duration-200 group-data-[state=open]:rotate-180",
325
+ children: /* @__PURE__ */ jsx("path", { d: "M4 6l4 4 4-4" })
326
+ }
327
+ );
328
+ }
329
+ var STATE_LABEL = {
330
+ pending: "Pending",
331
+ running: "Running",
332
+ result: "Done",
333
+ error: "Error"
334
+ };
335
+ var STATE_CLASSES = {
336
+ pending: "bg-muted text-muted-foreground",
337
+ // `text-foreground` (12:1) survives axe's mid-pulse capture; the
338
+ // previous `text-primary` (`#7081a8` on dark `--muted`, 4.28:1) was
339
+ // intermittently flagged below AA 4.5:1 at the 10px badge size when
340
+ // `animate-pulse` caught the scan during a low-opacity frame.
341
+ running: "bg-muted text-foreground font-medium animate-pulse",
342
+ result: "bg-accent/30 text-accent-foreground",
343
+ error: "bg-destructive/15 text-destructive"
344
+ };
345
+ function ToolCall({
346
+ name,
347
+ state,
348
+ args,
349
+ result,
350
+ defaultOpen = false,
351
+ className
352
+ }) {
353
+ return /* @__PURE__ */ jsxs(
354
+ CollapsiblePrimitive.Root,
355
+ {
356
+ defaultOpen,
357
+ className: cn(
358
+ "overflow-hidden rounded-md border bg-card text-card-foreground",
359
+ "transition-all duration-[var(--duration-normal,200ms)] ease-out",
360
+ "data-[state=open]:shadow-sm",
361
+ className
362
+ ),
363
+ children: [
364
+ /* @__PURE__ */ jsxs(
365
+ CollapsiblePrimitive.Trigger,
366
+ {
367
+ className: cn(
368
+ "group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm",
369
+ "hover:bg-muted/40",
370
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
371
+ ),
372
+ children: [
373
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
374
+ /* @__PURE__ */ jsx(ToolGlyph, {}),
375
+ /* @__PURE__ */ jsx("span", { className: "truncate font-mono text-xs text-foreground", children: name }),
376
+ /* @__PURE__ */ jsx(
377
+ "span",
378
+ {
379
+ className: cn(
380
+ "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium",
381
+ STATE_CLASSES[state]
382
+ ),
383
+ children: STATE_LABEL[state]
384
+ }
385
+ )
386
+ ] }),
387
+ /* @__PURE__ */ jsx(Chevron3, {})
388
+ ]
389
+ }
390
+ ),
391
+ /* @__PURE__ */ jsxs(CollapsiblePrimitive.Content, { className: "overflow-hidden border-t bg-muted/20 px-3 py-2 text-xs", children: [
392
+ args !== void 0 ? /* @__PURE__ */ jsx(CodeSection, { label: "Arguments", value: args }) : null,
393
+ result !== void 0 ? /* @__PURE__ */ jsx(CodeSection, { label: "Result", value: result }) : null,
394
+ args === void 0 && result === void 0 ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "No arguments or result yet." }) : null
395
+ ] })
396
+ ]
397
+ }
398
+ );
399
+ }
400
+ function CodeSection({ label, value }) {
401
+ const text = typeof value === "string" ? value : safeStringify(value);
402
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1 py-1", children: [
403
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: label }),
404
+ /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto rounded bg-background/60 p-2 font-mono text-[11px] leading-snug", children: text })
405
+ ] });
406
+ }
407
+ function safeStringify(value) {
408
+ try {
409
+ return JSON.stringify(value, null, 2);
410
+ } catch {
411
+ return String(value);
412
+ }
413
+ }
414
+ function ToolGlyph() {
415
+ return /* @__PURE__ */ jsxs(
416
+ "svg",
417
+ {
418
+ "aria-hidden": true,
419
+ viewBox: "0 0 16 16",
420
+ width: "14",
421
+ height: "14",
422
+ fill: "none",
423
+ stroke: "currentColor",
424
+ strokeWidth: "1.5",
425
+ strokeLinecap: "round",
426
+ strokeLinejoin: "round",
427
+ className: "shrink-0 text-muted-foreground",
428
+ children: [
429
+ /* @__PURE__ */ jsx("path", { d: "M11.5 1.5l3 3-2.5 2.5-3-3 2.5-2.5z" }),
430
+ /* @__PURE__ */ jsx("path", { d: "M9 4l-7 7v3h3l7-7" })
431
+ ]
432
+ }
433
+ );
434
+ }
435
+ function Chevron3() {
436
+ return /* @__PURE__ */ jsx(
437
+ "svg",
438
+ {
439
+ "aria-hidden": true,
440
+ viewBox: "0 0 16 16",
441
+ width: "14",
442
+ height: "14",
443
+ fill: "none",
444
+ stroke: "currentColor",
445
+ strokeWidth: "1.5",
446
+ strokeLinecap: "round",
447
+ strokeLinejoin: "round",
448
+ className: "shrink-0 text-muted-foreground transition-transform duration-200 group-data-[state=open]:rotate-180",
449
+ children: /* @__PURE__ */ jsx("path", { d: "M4 6l4 4 4-4" })
450
+ }
451
+ );
452
+ }
453
+
454
+ // src/ai/markdown/close-unterminated.ts
455
+ function closeUnterminated(input) {
456
+ let working = input;
457
+ const suffixes = [];
458
+ if (hasUnclosedFence(working)) {
459
+ suffixes.push("\n```\n");
460
+ working = maskOpenFence(working);
461
+ }
462
+ const masked = maskEscapes(maskClosedFences(working));
463
+ if (/<!--(?![\s\S]*-->)/.test(working)) {
464
+ suffixes.push("-->");
465
+ } else if (/<[a-zA-Z][^<>]*$/.test(working)) {
466
+ suffixes.push(">");
467
+ }
468
+ if (/\[[^[\]]*\]\([^()]*$/.test(working)) {
469
+ suffixes.push(")");
470
+ } else if (/\[[^[\]]*$/.test(working)) {
471
+ suffixes.push("]");
472
+ }
473
+ let backtickOdd = 0;
474
+ for (const line of masked.split("\n")) {
475
+ const count = (line.match(/`/g) ?? []).length;
476
+ if (count % 2 === 1) backtickOdd++;
477
+ }
478
+ if (backtickOdd % 2 === 1) {
479
+ suffixes.push("`");
480
+ }
481
+ const strikeMarkers = countOutsideMaskedFences(masked, /~~/g);
482
+ if (strikeMarkers % 2 === 1) {
483
+ suffixes.push("~~");
484
+ }
485
+ const { boldOdd, italicAsteriskOdd } = countBoldItalic(masked);
486
+ if (boldOdd) suffixes.push("**");
487
+ if (italicAsteriskOdd) suffixes.push("*");
488
+ if (countUnderscoreItalic(masked) % 2 === 1) suffixes.push("_");
489
+ if (suffixes.length === 0) return input;
490
+ return input + suffixes.join("");
491
+ }
492
+ function hasUnclosedFence(input) {
493
+ const fenceRe = /^[ \t]*```/gm;
494
+ const matches = input.match(fenceRe);
495
+ return matches !== null && matches.length % 2 === 1;
496
+ }
497
+ function maskOpenFence(input) {
498
+ const fenceRe = /^[ \t]*```/gm;
499
+ const matches = [];
500
+ let m;
501
+ while ((m = fenceRe.exec(input)) !== null) {
502
+ matches.push(m);
503
+ }
504
+ if (matches.length % 2 !== 1) return input;
505
+ const lastOpener = matches[matches.length - 1];
506
+ if (!lastOpener) return input;
507
+ const start = lastOpener.index;
508
+ return input.slice(0, start) + input.slice(start).replace(/[^\n]/g, " ");
509
+ }
510
+ function maskClosedFences(input) {
511
+ const fenceRe = /^[ \t]*```/gm;
512
+ const indices = [];
513
+ let m;
514
+ while ((m = fenceRe.exec(input)) !== null) {
515
+ indices.push(m.index);
516
+ }
517
+ let out = input;
518
+ const pairCount = Math.floor(indices.length / 2);
519
+ for (let i = 0; i < pairCount; i++) {
520
+ const openIdx = indices[i * 2];
521
+ const closeIdx = indices[i * 2 + 1];
522
+ if (openIdx === void 0 || closeIdx === void 0) continue;
523
+ const closeEnd = out.indexOf("\n", closeIdx);
524
+ const regionEnd = closeEnd < 0 ? out.length : closeEnd;
525
+ const before = out.slice(0, openIdx);
526
+ const region = out.slice(openIdx, regionEnd).replace(/[^\n]/g, " ");
527
+ const after = out.slice(regionEnd);
528
+ out = before + region + after;
529
+ }
530
+ return out;
531
+ }
532
+ function countOutsideMaskedFences(masked, re) {
533
+ const matches = masked.match(re);
534
+ return matches?.length ?? 0;
535
+ }
536
+ var ESCAPABLE = "\\`*_~[](){}<>#+-.!|";
537
+ function maskEscapes(input) {
538
+ let out = "";
539
+ let i = 0;
540
+ while (i < input.length) {
541
+ if (input[i] === "\\" && i + 1 < input.length && ESCAPABLE.includes(input[i + 1] ?? "")) {
542
+ out += " ";
543
+ i += 2;
544
+ continue;
545
+ }
546
+ out += input[i];
547
+ i++;
548
+ }
549
+ return out;
550
+ }
551
+ function countBoldItalic(masked) {
552
+ let bold = 0;
553
+ let italic = 0;
554
+ let i = 0;
555
+ while (i < masked.length) {
556
+ const ch = masked[i];
557
+ if (ch !== "*") {
558
+ i++;
559
+ continue;
560
+ }
561
+ let run = 0;
562
+ while (i + run < masked.length && masked[i + run] === "*") run++;
563
+ bold += Math.floor(run / 2);
564
+ italic += run % 2;
565
+ i += run;
566
+ }
567
+ return { boldOdd: bold % 2 === 1, italicAsteriskOdd: italic % 2 === 1 };
568
+ }
569
+ function countUnderscoreItalic(masked) {
570
+ let count = 0;
571
+ for (let i = 0; i < masked.length; i++) {
572
+ if (masked[i] !== "_") continue;
573
+ const prev = i === 0 ? " " : masked[i - 1];
574
+ const next = i === masked.length - 1 ? " " : masked[i + 1];
575
+ const prevWs = prev === void 0 || /[\s]/.test(prev);
576
+ const nextWs = next === void 0 || /[\s]/.test(next);
577
+ if (prevWs || nextWs) count++;
578
+ }
579
+ return count;
580
+ }
581
+ var SUPPORTED = /* @__PURE__ */ new Set(["think"]);
582
+ function remarkAdmonitions() {
583
+ return (tree) => {
584
+ visit(tree, "blockquote", (node) => {
585
+ const marker = extractMarker(node);
586
+ if (!marker || !SUPPORTED.has(marker.type)) return;
587
+ stripMarkerText(node, marker.length);
588
+ node.data = node.data ?? {};
589
+ node.data.hProperties = {
590
+ ...node.data.hProperties ?? {},
591
+ dataAdmonition: marker.type
592
+ };
593
+ });
594
+ };
595
+ }
596
+ function extractMarker(bq) {
597
+ const firstChild = bq.children[0];
598
+ if (!firstChild || firstChild.type !== "paragraph") return null;
599
+ const firstText = firstChild.children[0];
600
+ if (!firstText || firstText.type !== "text") return null;
601
+ const match = /^\[!([a-z]+)\]\s*\n?/.exec(firstText.value);
602
+ if (!match) return null;
603
+ const type = match[1];
604
+ if (!type) return null;
605
+ return { type, length: match[0].length };
606
+ }
607
+ function stripMarkerText(bq, length) {
608
+ const firstChild = isParagraph(bq.children[0]) ? bq.children[0] : void 0;
609
+ if (!firstChild) return;
610
+ const firstText = isText(firstChild.children[0]) ? firstChild.children[0] : void 0;
611
+ if (!firstText) return;
612
+ const stripped = { ...firstText, value: firstText.value.slice(length) };
613
+ if (stripped.value.length === 0) {
614
+ firstChild.children.shift();
615
+ } else {
616
+ firstChild.children[0] = stripped;
617
+ }
618
+ }
619
+ function isParagraph(node) {
620
+ return node?.type === "paragraph";
621
+ }
622
+ function isText(node) {
623
+ return node?.type === "text";
624
+ }
625
+ var SANITIZE_SCHEMA = {
626
+ ...defaultSchema,
627
+ tagNames: [...defaultSchema.tagNames ?? [], "tool-call", "sources"],
628
+ attributes: {
629
+ ...defaultSchema.attributes ?? {},
630
+ "tool-call": ["name", "state", "args", "result"],
631
+ sources: ["data"],
632
+ blockquote: [
633
+ ...(defaultSchema.attributes ?? {}).blockquote ?? [],
634
+ "dataAdmonition"
635
+ ]
636
+ }
637
+ };
638
+ var COMPONENTS = {
639
+ code: CodeBlockSlot,
640
+ a: CitationOrLinkSlot,
641
+ blockquote: ReasoningOrQuoteSlot,
642
+ "tool-call": ToolCallSlot,
643
+ sources: SourcesSlot
644
+ };
10
645
  function Markdown({ children, className }) {
646
+ const safe = React.useMemo(() => closeUnterminated(children), [children]);
11
647
  return /* @__PURE__ */ jsx(
12
- Streamdown,
648
+ "div",
13
649
  {
14
650
  className: cn(
15
651
  "prose prose-sm max-w-none text-foreground",
@@ -18,11 +654,155 @@ function Markdown({ children, className }) {
18
654
  "prose-code:text-foreground prose-code:before:content-none prose-code:after:content-none",
19
655
  className
20
656
  ),
657
+ children: /* @__PURE__ */ jsx(
658
+ ReactMarkdown,
659
+ {
660
+ remarkPlugins: [remarkGfm, remarkAdmonitions],
661
+ rehypePlugins: [rehypeRaw, [rehypeSanitize, SANITIZE_SCHEMA]],
662
+ components: COMPONENTS,
663
+ children: safe
664
+ }
665
+ )
666
+ }
667
+ );
668
+ }
669
+ var FOOTNOTE_RE = /^(\d+)$/;
670
+ function CitationOrLinkSlot({
671
+ href,
672
+ children,
673
+ className,
674
+ node: _node,
675
+ ...rest
676
+ }) {
677
+ const text = extractText(children);
678
+ const footnote = FOOTNOTE_RE.exec(text);
679
+ if (footnote && href) {
680
+ const index = Number(footnote[1]);
681
+ return /* @__PURE__ */ jsx(InlineCitation, { index, url: href, title: inferCitationTitle(href) });
682
+ }
683
+ return /* @__PURE__ */ jsx("a", { href, className, ...rest, children });
684
+ }
685
+ function extractText(children) {
686
+ if (typeof children === "string") return children;
687
+ if (Array.isArray(children)) return children.map(extractText).join("");
688
+ return "";
689
+ }
690
+ function inferCitationTitle(href) {
691
+ try {
692
+ const url = new URL(href);
693
+ const hostname = url.hostname.replace(/^www\./, "");
694
+ return hostname.length > 0 ? hostname : href;
695
+ } catch {
696
+ return href;
697
+ }
698
+ }
699
+ function CodeBlockSlot({ className, children, node: _node, ...rest }) {
700
+ const langMatch = /language-([a-zA-Z0-9-]+)/.exec(className ?? "");
701
+ const isFenced = Boolean(langMatch) || Array.isArray(children) && children.length > 1 || String(children ?? "").includes("\n");
702
+ if (!isFenced) {
703
+ return /* @__PURE__ */ jsx(
704
+ "code",
705
+ {
706
+ className: cn(
707
+ "rounded bg-muted px-[0.3em] py-[0.15em] font-mono text-[0.85em]",
708
+ className
709
+ ),
710
+ ...rest,
711
+ children
712
+ }
713
+ );
714
+ }
715
+ return /* @__PURE__ */ jsx(
716
+ "code",
717
+ {
718
+ className: cn("font-mono text-sm", className),
719
+ "data-fenced": "true",
720
+ ...rest,
721
+ children
722
+ }
723
+ );
724
+ }
725
+ function ReasoningOrQuoteSlot({
726
+ children,
727
+ className,
728
+ node: _node,
729
+ ...rest
730
+ }) {
731
+ if (rest["data-admonition"] === "think") {
732
+ return /* @__PURE__ */ jsx(Reasoning, { children });
733
+ }
734
+ return /* @__PURE__ */ jsx(
735
+ "blockquote",
736
+ {
737
+ className: cn(
738
+ "my-4 border-l-2 border-border pl-4 italic text-muted-foreground",
739
+ className
740
+ ),
741
+ ...rest,
21
742
  children
22
743
  }
23
744
  );
24
745
  }
746
+ var VALID_TOOL_CALL_STATES = {
747
+ pending: true,
748
+ running: true,
749
+ result: true,
750
+ error: true
751
+ };
752
+ function ToolCallSlot({ name, state, args, result, children, node: _node }) {
753
+ if (!name) return /* @__PURE__ */ jsx(Fragment, { children });
754
+ const parsedState = isToolCallState(state) ? state : "result";
755
+ return /* @__PURE__ */ jsx(
756
+ ToolCall,
757
+ {
758
+ name,
759
+ state: parsedState,
760
+ args: parseJson(args),
761
+ result: parseJson(result)
762
+ }
763
+ );
764
+ }
765
+ function isToolCallState(value) {
766
+ return typeof value === "string" && value in VALID_TOOL_CALL_STATES;
767
+ }
768
+ function parseJson(raw) {
769
+ if (raw === void 0 || raw === "") return void 0;
770
+ try {
771
+ return JSON.parse(raw);
772
+ } catch {
773
+ return raw;
774
+ }
775
+ }
776
+ function SourcesSlot({ data, children, node: _node }) {
777
+ const sources = React.useMemo(() => parseSources(data), [data]);
778
+ if (!sources) return /* @__PURE__ */ jsx(Fragment, { children });
779
+ return /* @__PURE__ */ jsx(Sources, { sources });
780
+ }
781
+ function parseSources(raw) {
782
+ if (raw === void 0 || raw === "") return null;
783
+ let parsed;
784
+ try {
785
+ parsed = JSON.parse(raw);
786
+ } catch {
787
+ return null;
788
+ }
789
+ if (!Array.isArray(parsed)) return null;
790
+ const result = [];
791
+ for (const item of parsed) {
792
+ if (!item || typeof item !== "object") continue;
793
+ if (!("title" in item) || typeof item.title !== "string") continue;
794
+ const ref = { title: item.title };
795
+ if ("url" in item && typeof item.url === "string") {
796
+ const url = safeUrl(item.url);
797
+ if (url !== void 0) ref.url = url;
798
+ }
799
+ if ("page" in item && typeof item.page === "number") ref.page = item.page;
800
+ if ("index" in item && typeof item.index === "number") ref.index = item.index;
801
+ result.push(ref);
802
+ }
803
+ return result;
804
+ }
25
805
 
26
- export { Markdown };
806
+ export { Markdown, closeUnterminated };
27
807
  //# sourceMappingURL=markdown.js.map
28
808
  //# sourceMappingURL=markdown.js.map