@mieweb/ui 0.6.1-dev.148 → 0.6.1-dev.150

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 (82) hide show
  1. package/dist/brands/index.cjs +22 -22
  2. package/dist/brands/index.js +5 -5
  3. package/dist/chunk-2T4JU5RH.cjs +1156 -0
  4. package/dist/chunk-2T4JU5RH.cjs.map +1 -0
  5. package/dist/chunk-3SSXDWD7.js +363 -0
  6. package/dist/chunk-3SSXDWD7.js.map +1 -0
  7. package/dist/{chunk-MARLXJQO.cjs → chunk-6R2ZPDN7.cjs} +7 -7
  8. package/dist/{chunk-MARLXJQO.cjs.map → chunk-6R2ZPDN7.cjs.map} +1 -1
  9. package/dist/{chunk-WHUD3XHR.cjs → chunk-7PA26KBF.cjs} +15 -3
  10. package/dist/chunk-7PA26KBF.cjs.map +1 -0
  11. package/dist/chunk-ASLZUFH4.js +1967 -0
  12. package/dist/chunk-ASLZUFH4.js.map +1 -0
  13. package/dist/chunk-FADQVM4M.cjs +2017 -0
  14. package/dist/chunk-FADQVM4M.cjs.map +1 -0
  15. package/dist/{chunk-DFT7TYKL.cjs → chunk-H4T5T65N.cjs} +6 -3
  16. package/dist/chunk-H4T5T65N.cjs.map +1 -0
  17. package/dist/chunk-I6CY5C6A.js +12 -0
  18. package/dist/chunk-I6CY5C6A.js.map +1 -0
  19. package/dist/chunk-JFLC7SHM.cjs +35 -0
  20. package/dist/chunk-JFLC7SHM.cjs.map +1 -0
  21. package/dist/chunk-LZPPH5BW.cjs +368 -0
  22. package/dist/chunk-LZPPH5BW.cjs.map +1 -0
  23. package/dist/{chunk-3OHVUXDG.js → chunk-M7BLVBL4.js} +6 -3
  24. package/dist/chunk-M7BLVBL4.js.map +1 -0
  25. package/dist/chunk-PM2I3QKM.cjs +1419 -0
  26. package/dist/chunk-PM2I3QKM.cjs.map +1 -0
  27. package/dist/{chunk-TW6DXMSD.js → chunk-R6PBBPU3.js} +2 -2
  28. package/dist/{chunk-TW6DXMSD.js.map → chunk-R6PBBPU3.js.map} +1 -1
  29. package/dist/{chunk-33PO3J4O.js → chunk-RXY5SD3O.js} +15 -3
  30. package/dist/chunk-RXY5SD3O.js.map +1 -0
  31. package/dist/{chunk-AEGYWRSL.js → chunk-TXYTMU3K.js} +3 -3
  32. package/dist/{chunk-AEGYWRSL.js.map → chunk-TXYTMU3K.js.map} +1 -1
  33. package/dist/chunk-UHPQYBXQ.js +1124 -0
  34. package/dist/chunk-UHPQYBXQ.js.map +1 -0
  35. package/dist/chunk-XQE26F3G.js +1383 -0
  36. package/dist/chunk-XQE26F3G.js.map +1 -0
  37. package/dist/{chunk-26YNFCOC.cjs → chunk-Z6NRP4Z5.cjs} +2 -2
  38. package/dist/{chunk-26YNFCOC.cjs.map → chunk-Z6NRP4Z5.cjs.map} +1 -1
  39. package/dist/components/Dropdown/index.cjs +7 -7
  40. package/dist/components/Dropdown/index.d.cts +1 -1
  41. package/dist/components/Dropdown/index.d.ts +1 -1
  42. package/dist/components/Dropdown/index.js +1 -1
  43. package/dist/components/RichTextEditor/index.cjs +5 -5
  44. package/dist/components/RichTextEditor/index.js +2 -2
  45. package/dist/components/SuperChat/index.cjs +1319 -0
  46. package/dist/components/SuperChat/index.cjs.map +1 -0
  47. package/dist/components/SuperChat/index.d.cts +189 -0
  48. package/dist/components/SuperChat/index.d.ts +189 -0
  49. package/dist/components/SuperChat/index.js +1282 -0
  50. package/dist/components/SuperChat/index.js.map +1 -0
  51. package/dist/components/SuperChat/plugins/index.cjs +1221 -0
  52. package/dist/components/SuperChat/plugins/index.cjs.map +1 -0
  53. package/dist/components/SuperChat/plugins/index.d.cts +253 -0
  54. package/dist/components/SuperChat/plugins/index.d.ts +253 -0
  55. package/dist/components/SuperChat/plugins/index.js +1181 -0
  56. package/dist/components/SuperChat/plugins/index.js.map +1 -0
  57. package/dist/datavis.cjs +18 -362
  58. package/dist/datavis.cjs.map +1 -1
  59. package/dist/datavis.js +1 -361
  60. package/dist/datavis.js.map +1 -1
  61. package/dist/index.cjs +1297 -5412
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +44 -240
  64. package/dist/index.d.ts +44 -240
  65. package/dist/index.js +759 -5037
  66. package/dist/index.js.map +1 -1
  67. package/dist/nitroTableGrid-FWRCDE4N.js +22 -0
  68. package/dist/nitroTableGrid-FWRCDE4N.js.map +1 -0
  69. package/dist/nitroTableGrid-IY75TQJ2.cjs +44 -0
  70. package/dist/nitroTableGrid-IY75TQJ2.cjs.map +1 -0
  71. package/dist/styles.css +1 -1
  72. package/dist/tailwind-preset.cjs +4 -4
  73. package/dist/tailwind-preset.js +1 -1
  74. package/dist/types-BFFgW6qy.d.ts +240 -0
  75. package/dist/types-BzeY_kYO.d.cts +242 -0
  76. package/dist/types-BzeY_kYO.d.ts +242 -0
  77. package/dist/types-CRt5IPNL.d.cts +240 -0
  78. package/package.json +41 -4
  79. package/dist/chunk-33PO3J4O.js.map +0 -1
  80. package/dist/chunk-3OHVUXDG.js.map +0 -1
  81. package/dist/chunk-DFT7TYKL.cjs.map +0 -1
  82. package/dist/chunk-WHUD3XHR.cjs.map +0 -1
@@ -0,0 +1,1282 @@
1
+ import { TextRenderContext } from '../../chunk-I6CY5C6A.js';
2
+ export { TextRenderContext, useTextRenderContext } from '../../chunk-I6CY5C6A.js';
3
+ import { ChatBubble, MCPToolCallDisplay, AITypingIndicator, SparklesIcon, CloseIcon } from '../../chunk-XQE26F3G.js';
4
+ import { MessageComposer } from '../../chunk-UHPQYBXQ.js';
5
+ import { Dropdown, DropdownItem } from '../../chunk-M7BLVBL4.js';
6
+ import '../../chunk-PVUDXJAI.js';
7
+ import { Avatar } from '../../chunk-WO5CAOAN.js';
8
+ import { Badge } from '../../chunk-DCER2QQB.js';
9
+ import '../../chunk-T4ME7QCT.js';
10
+ import '../../chunk-OKBR6PX4.js';
11
+ import { cn } from '../../chunk-F3SOEIN2.js';
12
+ import * as React4 from 'react';
13
+ import Markdown, { defaultUrlTransform } from 'react-markdown';
14
+ import remarkGfm from 'remark-gfm';
15
+ import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
16
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
17
+ import { cva } from 'class-variance-authority';
18
+ import { Pencil, Check, Clipboard } from 'lucide-react';
19
+ import { useVirtualizer } from '@tanstack/react-virtual';
20
+
21
+ function remarkPreserveLineBreaks() {
22
+ const splitNode = (node) => {
23
+ if (!node.children) return;
24
+ const next = [];
25
+ for (const child of node.children) {
26
+ if (child.type === "text" && child.value && child.value.includes("\n")) {
27
+ const segments = child.value.split("\n");
28
+ segments.forEach((segment, i) => {
29
+ if (i > 0) next.push({ type: "break" });
30
+ if (segment) next.push({ type: "text", value: segment });
31
+ });
32
+ } else {
33
+ splitNode(child);
34
+ next.push(child);
35
+ }
36
+ }
37
+ node.children = next;
38
+ };
39
+ return (tree) => splitNode(tree);
40
+ }
41
+ function baseSanitizeSchema() {
42
+ const schema = JSON.parse(JSON.stringify(defaultSchema));
43
+ const attributes = schema.attributes ?? {};
44
+ const allowClass = (tag) => {
45
+ const existing = attributes[tag] ?? [];
46
+ const withoutClass = existing.filter(
47
+ (a) => !(Array.isArray(a) && a[0] === "className")
48
+ );
49
+ attributes[tag] = [...withoutClass, "className"];
50
+ };
51
+ allowClass("code");
52
+ allowClass("pre");
53
+ allowClass("span");
54
+ allowClass("div");
55
+ schema.attributes = attributes;
56
+ return schema;
57
+ }
58
+ function mergeSanitizeSchema(base, extra) {
59
+ if (!extra) return base;
60
+ const out = { ...base };
61
+ if (Array.isArray(extra.tagNames)) {
62
+ const a = base.tagNames ?? [];
63
+ out.tagNames = Array.from(/* @__PURE__ */ new Set([...a, ...extra.tagNames]));
64
+ }
65
+ if (extra.attributes && typeof extra.attributes === "object") {
66
+ const baseAttrs = {
67
+ ...base.attributes ?? {}
68
+ };
69
+ for (const [tag, attrs] of Object.entries(
70
+ extra.attributes
71
+ )) {
72
+ baseAttrs[tag] = [...baseAttrs[tag] ?? [], ...attrs];
73
+ }
74
+ out.attributes = baseAttrs;
75
+ }
76
+ if (extra.protocols && typeof extra.protocols === "object") {
77
+ const baseProtocols = {
78
+ ...base.protocols ?? {}
79
+ };
80
+ for (const [attr, protocols] of Object.entries(
81
+ extra.protocols
82
+ )) {
83
+ baseProtocols[attr] = Array.from(
84
+ /* @__PURE__ */ new Set([...baseProtocols[attr] ?? [], ...protocols])
85
+ );
86
+ }
87
+ out.protocols = baseProtocols;
88
+ }
89
+ for (const [k, v] of Object.entries(extra)) {
90
+ if (k === "tagNames" || k === "attributes" || k === "protocols") continue;
91
+ out[k] = v;
92
+ }
93
+ return out;
94
+ }
95
+ function baseComponents() {
96
+ return {
97
+ a: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
98
+ "a",
99
+ {
100
+ ...props,
101
+ className: cn(
102
+ "text-primary-700 hover:text-primary-800 dark:text-primary-300 underline underline-offset-2",
103
+ props.className
104
+ ),
105
+ target: props.target ?? "_blank",
106
+ rel: props.rel ?? "noopener noreferrer",
107
+ children
108
+ }
109
+ ),
110
+ pre: ({ node: _node, ...props }) => /* @__PURE__ */ jsx(
111
+ "pre",
112
+ {
113
+ ...props,
114
+ className: cn(
115
+ "overflow-x-auto rounded-lg bg-neutral-900 p-3 text-sm text-neutral-100 dark:bg-neutral-950",
116
+ props.className
117
+ )
118
+ }
119
+ ),
120
+ code: ({ node: _node, ...props }) => {
121
+ const isBlock = typeof props.className === "string" && props.className.includes("language-");
122
+ return /* @__PURE__ */ jsx(
123
+ "code",
124
+ {
125
+ ...props,
126
+ className: cn(
127
+ !isBlock && "rounded bg-neutral-200 px-1 py-0.5 text-[0.85em] dark:bg-neutral-700",
128
+ props.className
129
+ )
130
+ }
131
+ );
132
+ },
133
+ table: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("div", { className: "my-2 overflow-x-auto", children: /* @__PURE__ */ jsx(
134
+ "table",
135
+ {
136
+ ...props,
137
+ className: cn(
138
+ "w-full border-collapse text-sm [&_td]:border [&_td]:border-neutral-200 [&_td]:px-2 [&_td]:py-1 dark:[&_td]:border-neutral-700 [&_th]:border [&_th]:border-neutral-200 [&_th]:px-2 [&_th]:py-1 [&_th]:text-left dark:[&_th]:border-neutral-700",
139
+ props.className
140
+ )
141
+ }
142
+ ) }),
143
+ // Tailwind's preflight resets list markers/padding; restore them explicitly
144
+ // so bullets/numbers show without depending on the typography plugin.
145
+ ul: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("ul", { ...props, className: cn("list-disc ps-5", props.className) }),
146
+ ol: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("ol", { ...props, className: cn("list-decimal ps-5", props.className) }),
147
+ li: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("li", { ...props, className: cn("ps-1", props.className) }),
148
+ // Preflight also flattens headings to body size/weight; restore a sensible
149
+ // hierarchy without depending on the typography plugin.
150
+ h1: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
151
+ "h1",
152
+ {
153
+ ...props,
154
+ className: cn("mt-3 mb-1 text-xl font-semibold", props.className),
155
+ children
156
+ }
157
+ ),
158
+ h2: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
159
+ "h2",
160
+ {
161
+ ...props,
162
+ className: cn("mt-3 mb-1 text-lg font-semibold", props.className),
163
+ children
164
+ }
165
+ ),
166
+ h3: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
167
+ "h3",
168
+ {
169
+ ...props,
170
+ className: cn("mt-2 mb-1 text-base font-semibold", props.className),
171
+ children
172
+ }
173
+ ),
174
+ h4: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
175
+ "h4",
176
+ {
177
+ ...props,
178
+ className: cn("mt-2 mb-1 text-sm font-semibold", props.className),
179
+ children
180
+ }
181
+ ),
182
+ h5: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
183
+ "h5",
184
+ {
185
+ ...props,
186
+ className: cn("mt-2 mb-1 text-sm font-semibold", props.className),
187
+ children
188
+ }
189
+ ),
190
+ h6: ({ node: _node, children, ...props }) => /* @__PURE__ */ jsx(
191
+ "h6",
192
+ {
193
+ ...props,
194
+ className: cn(
195
+ "mt-2 mb-1 text-xs font-semibold tracking-wide uppercase",
196
+ props.className
197
+ ),
198
+ children
199
+ }
200
+ ),
201
+ hr: ({ node: _node, ...props }) => /* @__PURE__ */ jsx(
202
+ "hr",
203
+ {
204
+ ...props,
205
+ className: cn(
206
+ "my-2 border-neutral-300 dark:border-neutral-600",
207
+ props.className
208
+ )
209
+ }
210
+ )
211
+ };
212
+ }
213
+ function createMarkdownRenderer(options = {}) {
214
+ const { plugins = [], trusted = false } = options;
215
+ const remarkPlugins = [
216
+ remarkGfm,
217
+ remarkPreserveLineBreaks,
218
+ ...plugins.flatMap((p) => p.remarkPlugins ?? [])
219
+ ];
220
+ const pluginRehype = plugins.flatMap(
221
+ (p) => p.rehypePlugins ?? []
222
+ );
223
+ const sanitizeSchema = plugins.reduce(
224
+ (acc, p) => mergeSanitizeSchema(acc, p.sanitizeSchema),
225
+ baseSanitizeSchema()
226
+ );
227
+ const rehypePlugins = trusted ? pluginRehype : [...pluginRehype, [rehypeSanitize, sanitizeSchema]];
228
+ const components = plugins.reduce(
229
+ (acc, p) => ({ ...acc, ...p.components }),
230
+ baseComponents()
231
+ );
232
+ const MarkdownRenderer = (text, ctx) => /* @__PURE__ */ jsx(
233
+ MarkdownContent,
234
+ {
235
+ text,
236
+ messageId: ctx.messageId,
237
+ streaming: ctx.streaming,
238
+ remarkPlugins,
239
+ rehypePlugins,
240
+ components
241
+ },
242
+ ctx.messageId
243
+ );
244
+ return MarkdownRenderer;
245
+ }
246
+ var transformUrl = (url, key, node) => {
247
+ if (key === "src" && node.tagName === "img" && (/^data:image\//i.test(url) || /^blob:/i.test(url))) {
248
+ return url;
249
+ }
250
+ return defaultUrlTransform(url);
251
+ };
252
+ var MarkdownContent = React4.memo(function MarkdownContent2({
253
+ text,
254
+ messageId,
255
+ streaming,
256
+ remarkPlugins,
257
+ rehypePlugins,
258
+ components
259
+ }) {
260
+ return /* @__PURE__ */ jsx(TextRenderContext.Provider, { value: { messageId, streaming }, children: /* @__PURE__ */ jsx(
261
+ Markdown,
262
+ {
263
+ remarkPlugins,
264
+ rehypePlugins,
265
+ urlTransform: transformUrl,
266
+ components,
267
+ children: text
268
+ }
269
+ ) });
270
+ });
271
+ function formatTime(time) {
272
+ return new Date(time).toLocaleTimeString(void 0, {
273
+ hour: "numeric",
274
+ minute: "2-digit"
275
+ });
276
+ }
277
+ function byTime(a, b) {
278
+ return new Date(a.time).getTime() - new Date(b.time).getTime();
279
+ }
280
+ function lastActivityOf(c) {
281
+ if (c.lastActivity) return new Date(c.lastActivity).getTime();
282
+ const last = lastMessageByTime(c.thread);
283
+ return last ? new Date(last.time).getTime() : 0;
284
+ }
285
+ function escapeRegExp(value) {
286
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
287
+ }
288
+ function detectMentions(text, participants) {
289
+ const ids = [];
290
+ for (const p of participants) {
291
+ if (p.kind === "system") continue;
292
+ const token = "@" + p.name.split(" ")[0];
293
+ const pattern = new RegExp(
294
+ `(?<![\\w@])${escapeRegExp(token)}(?![\\w])`,
295
+ "i"
296
+ );
297
+ if (pattern.test(text)) ids.push(p.id);
298
+ }
299
+ return ids;
300
+ }
301
+ function lastMessageByTime(thread) {
302
+ let latest;
303
+ let latestTime = -Infinity;
304
+ for (const message of thread) {
305
+ const t = new Date(message.time).getTime();
306
+ if (t >= latestTime) {
307
+ latest = message;
308
+ latestTime = t;
309
+ }
310
+ }
311
+ return latest;
312
+ }
313
+ function ParticipantAvatar({
314
+ participant
315
+ }) {
316
+ const isAgent = participant?.kind === "agent";
317
+ const backgroundColor = participant?.color ?? (isAgent ? "var(--mieweb-primary-700, #1f2937)" : "#64748b");
318
+ return /* @__PURE__ */ jsx(
319
+ Avatar,
320
+ {
321
+ size: "xs",
322
+ src: participant?.avatar,
323
+ alt: "",
324
+ name: participant?.name ?? "?",
325
+ fallback: isAgent ? /* @__PURE__ */ jsx(SparklesIcon, { size: "sm" }) : void 0,
326
+ style: { backgroundColor },
327
+ "aria-hidden": "true",
328
+ className: "shrink-0"
329
+ }
330
+ );
331
+ }
332
+ function ReferenceChip({
333
+ reference,
334
+ linkBuilder,
335
+ onReferenceClick
336
+ }) {
337
+ const href = linkBuilder?.(reference);
338
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
339
+ /* @__PURE__ */ jsx(
340
+ Badge,
341
+ {
342
+ variant: "default",
343
+ size: "sm",
344
+ className: "rounded px-1.5 text-[10px] font-semibold tracking-wide uppercase",
345
+ children: reference.refType
346
+ }
347
+ ),
348
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: reference.title })
349
+ ] });
350
+ const className = "inline-flex max-w-full items-center gap-2 rounded-lg border border-neutral-200 bg-white px-3 py-1.5 text-sm text-neutral-700 hover:border-primary-300 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-200";
351
+ if (href) {
352
+ return /* @__PURE__ */ jsx(
353
+ "a",
354
+ {
355
+ href,
356
+ className,
357
+ onClick: () => onReferenceClick?.(reference),
358
+ children: content
359
+ }
360
+ );
361
+ }
362
+ return /* @__PURE__ */ jsx(
363
+ "button",
364
+ {
365
+ type: "button",
366
+ className,
367
+ onClick: () => onReferenceClick?.(reference),
368
+ children: content
369
+ }
370
+ );
371
+ }
372
+ var ATTACHMENT_KIND_CONFIG = {
373
+ image: { accept: "image/*", match: (t) => t.startsWith("image/") },
374
+ video: { accept: "video/*", match: (t) => t.startsWith("video/") },
375
+ audio: { accept: "audio/*", match: (t) => t.startsWith("audio/") },
376
+ pdf: { accept: "application/pdf", match: (t) => t === "application/pdf" }
377
+ };
378
+ var DEFAULT_ACCEPTED_FILE_TYPES = [
379
+ "image",
380
+ "video",
381
+ "audio",
382
+ "pdf"
383
+ ];
384
+ function acceptTokensFor(kinds = DEFAULT_ACCEPTED_FILE_TYPES) {
385
+ const source = kinds.length > 0 ? kinds : DEFAULT_ACCEPTED_FILE_TYPES;
386
+ return Array.from(
387
+ new Set(source.map((k) => ATTACHMENT_KIND_CONFIG[k].accept))
388
+ );
389
+ }
390
+ function filesToComposerAttachments(files) {
391
+ return Promise.all(
392
+ files.map(
393
+ (file, i) => new Promise((resolve, reject) => {
394
+ const reader = new window.FileReader();
395
+ reader.onload = () => {
396
+ const dataUrl = typeof reader.result === "string" ? reader.result : "";
397
+ resolve({
398
+ id: `att-${Date.now()}-${i}`,
399
+ name: file.name || `attachment-${i}`,
400
+ type: file.type || "application/octet-stream",
401
+ dataUrl
402
+ });
403
+ };
404
+ reader.onerror = () => reject(reader.error);
405
+ reader.readAsDataURL(file);
406
+ })
407
+ )
408
+ );
409
+ }
410
+ function CopyMenu({ isSelf, markdown, getHtml, getText }) {
411
+ const [open, setOpen] = React4.useState(false);
412
+ const [copied, setCopied] = React4.useState(false);
413
+ const copiedTimer = React4.useRef(
414
+ void 0
415
+ );
416
+ React4.useEffect(() => () => window.clearTimeout(copiedTimer.current), []);
417
+ const flash = () => {
418
+ setCopied(true);
419
+ window.clearTimeout(copiedTimer.current);
420
+ copiedTimer.current = setTimeout(() => setCopied(false), 1200);
421
+ };
422
+ const writeText = async (value) => {
423
+ if (!navigator.clipboard?.writeText) {
424
+ throw new Error("Clipboard API unavailable");
425
+ }
426
+ await navigator.clipboard.writeText(value);
427
+ };
428
+ const writeBoth = async () => {
429
+ const html = getHtml();
430
+ const text = markdown || getText();
431
+ try {
432
+ if (typeof window !== "undefined" && "ClipboardItem" in window && navigator.clipboard?.write) {
433
+ await navigator.clipboard.write([
434
+ new window.ClipboardItem({
435
+ "text/html": new Blob([html], { type: "text/html" }),
436
+ "text/plain": new Blob([text], { type: "text/plain" })
437
+ })
438
+ ]);
439
+ } else {
440
+ await writeText(text);
441
+ }
442
+ } catch {
443
+ await writeText(text);
444
+ }
445
+ };
446
+ const run = (fn) => {
447
+ setOpen(false);
448
+ void fn().then(flash, () => {
449
+ });
450
+ };
451
+ return /* @__PURE__ */ jsx(
452
+ "div",
453
+ {
454
+ className: cn(
455
+ "sticky bottom-2 shrink-0 self-end",
456
+ // Rich content (e.g. NITRO tables) layers internals up to z-50, so the
457
+ // open menu's stacking context must clear that.
458
+ open ? "z-[60]" : "z-10"
459
+ ),
460
+ children: /* @__PURE__ */ jsxs(
461
+ Dropdown,
462
+ {
463
+ open,
464
+ onOpenChange: setOpen,
465
+ placement: isSelf ? "top-end" : "top-start",
466
+ trigger: /* @__PURE__ */ jsx(
467
+ "button",
468
+ {
469
+ type: "button",
470
+ "data-slot": "superchat-copy-button",
471
+ "aria-label": "Copy message",
472
+ className: "rounded p-1 text-neutral-400 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 hover:text-neutral-600 focus-visible:opacity-100 dark:hover:text-neutral-200",
473
+ children: copied ? /* @__PURE__ */ jsx(Check, { size: 14, "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Clipboard, { size: 14, "aria-hidden": "true" })
474
+ }
475
+ ),
476
+ children: [
477
+ /* @__PURE__ */ jsx(DropdownItem, { onClick: () => run(writeBoth), children: /* @__PURE__ */ jsxs("span", { className: "flex flex-col items-start", children: [
478
+ /* @__PURE__ */ jsx("span", { children: "Copy" }),
479
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-normal text-neutral-400", children: "Rich text + Markdown" })
480
+ ] }) }),
481
+ /* @__PURE__ */ jsx(
482
+ DropdownItem,
483
+ {
484
+ onClick: () => run(() => writeText(markdown || getText())),
485
+ children: "Copy as Markdown"
486
+ }
487
+ ),
488
+ /* @__PURE__ */ jsx(DropdownItem, { onClick: () => run(() => writeText(getText())), children: "Copy as plain text" })
489
+ ]
490
+ }
491
+ )
492
+ }
493
+ );
494
+ }
495
+ var MessageRow = React4.memo(function MessageRow2({
496
+ message,
497
+ participant,
498
+ isSelf,
499
+ renderText,
500
+ linkBuilder,
501
+ onReferenceClick,
502
+ editable,
503
+ onMessageEdited
504
+ }) {
505
+ const streaming = message.status === "streaming";
506
+ const hasBody = !!message.text || (message.content?.length ?? 0) > 0;
507
+ const [isEditing, setIsEditing] = React4.useState(false);
508
+ const [draft, setDraft] = React4.useState(message.text ?? "");
509
+ const editRef = React4.useRef(null);
510
+ const bubbleRef = React4.useRef(null);
511
+ const autosize = React4.useCallback(() => {
512
+ const el = editRef.current;
513
+ if (!el) return;
514
+ el.style.height = "auto";
515
+ el.style.height = `${el.scrollHeight}px`;
516
+ }, []);
517
+ React4.useEffect(() => {
518
+ if (!isEditing) return;
519
+ const el = editRef.current;
520
+ if (!el) return;
521
+ el.focus();
522
+ el.setSelectionRange(el.value.length, el.value.length);
523
+ autosize();
524
+ }, [isEditing, autosize]);
525
+ if (message.type === "system") {
526
+ return /* @__PURE__ */ jsx(
527
+ "div",
528
+ {
529
+ "data-slot": "superchat-system-message",
530
+ role: "status",
531
+ className: "my-1 text-center text-xs text-neutral-500 dark:text-neutral-400",
532
+ children: message.text
533
+ }
534
+ );
535
+ }
536
+ if (message.type === "ref" && message.ref) {
537
+ return /* @__PURE__ */ jsx(
538
+ "div",
539
+ {
540
+ "data-slot": "superchat-reference",
541
+ className: cn("flex", isSelf && "justify-end"),
542
+ children: /* @__PURE__ */ jsx(
543
+ ReferenceChip,
544
+ {
545
+ reference: message.ref,
546
+ linkBuilder,
547
+ onReferenceClick
548
+ }
549
+ )
550
+ }
551
+ );
552
+ }
553
+ const accent = participant?.color;
554
+ const authorName = participant?.name ?? "Unknown";
555
+ const canEdit = !!editable && isSelf && !streaming && typeof message.text === "string" && !message.content?.length;
556
+ const startEdit = () => {
557
+ setDraft(message.text ?? "");
558
+ setIsEditing(true);
559
+ };
560
+ const cancelEdit = () => setIsEditing(false);
561
+ const saveEdit = () => {
562
+ const next = draft.trim();
563
+ if (next && next !== message.text) onMessageEdited?.(message.id, next);
564
+ setIsEditing(false);
565
+ };
566
+ const handleEditPaste = (e) => {
567
+ const files = Array.from(e.clipboardData.items).filter((item) => item.kind === "file" && item.type.startsWith("image/")).map((item) => item.getAsFile()).filter((f) => f !== null);
568
+ if (files.length === 0) return;
569
+ e.preventDefault();
570
+ const el = e.currentTarget;
571
+ const start = el.selectionStart ?? draft.length;
572
+ const end = el.selectionEnd ?? draft.length;
573
+ Promise.all(
574
+ files.map(
575
+ (file, i) => new Promise((resolve) => {
576
+ const reader = new window.FileReader();
577
+ reader.onload = () => {
578
+ const dataUrl = typeof reader.result === "string" ? reader.result : "";
579
+ const name = file.name || `pasted-image-${i + 1}.png`;
580
+ resolve(dataUrl ? `![${name}](${dataUrl})` : "");
581
+ };
582
+ reader.readAsDataURL(file);
583
+ })
584
+ )
585
+ ).then((snippets) => {
586
+ const insert = snippets.filter(Boolean).join("\n");
587
+ if (!insert) return;
588
+ el.focus();
589
+ el.setSelectionRange(start, end);
590
+ const text = `${insert}
591
+ `;
592
+ const inserted = typeof document !== "undefined" && typeof document.execCommand === "function" && document.execCommand("insertText", false, text);
593
+ if (!inserted) {
594
+ setDraft((prev) => `${prev.slice(0, start)}${text}${prev.slice(end)}`);
595
+ }
596
+ requestAnimationFrame(autosize);
597
+ });
598
+ };
599
+ const markdownSource = typeof message.text === "string" && message.text ? message.text : message.content?.map((block) => {
600
+ if (block.type === "code" && block.text) {
601
+ return `\`\`\`${block.language ?? ""}
602
+ ${block.text}
603
+ \`\`\``;
604
+ }
605
+ if ((block.type === "text" || block.type === "thinking") && block.text) {
606
+ return block.text;
607
+ }
608
+ return "";
609
+ }).filter(Boolean).join("\n\n") ?? "";
610
+ const canCopy = !isEditing && (!!message.content?.length || typeof message.text === "string" && message.text.length > 0);
611
+ return /* @__PURE__ */ jsxs(
612
+ "div",
613
+ {
614
+ "data-slot": "superchat-message",
615
+ role: "article",
616
+ "aria-label": `${authorName}, ${formatTime(message.time)}`,
617
+ className: cn(
618
+ "group flex gap-2",
619
+ isSelf ? "flex-row-reverse" : "flex-row"
620
+ ),
621
+ children: [
622
+ /* @__PURE__ */ jsx(ParticipantAvatar, { participant }),
623
+ /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 flex-col gap-1", isSelf && "items-end"), children: [
624
+ /* @__PURE__ */ jsxs(
625
+ "div",
626
+ {
627
+ "data-slot": "superchat-message-meta",
628
+ className: "flex items-baseline gap-2",
629
+ children: [
630
+ /* @__PURE__ */ jsx(
631
+ "span",
632
+ {
633
+ className: "text-xs font-medium",
634
+ style: accent ? { color: accent } : void 0,
635
+ children: authorName
636
+ }
637
+ ),
638
+ participant?.role && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-neutral-400", children: participant.role }),
639
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-neutral-400", children: formatTime(message.time) }),
640
+ message.editedAt && /* @__PURE__ */ jsx(
641
+ "span",
642
+ {
643
+ "data-slot": "superchat-edited-indicator",
644
+ className: "text-[10px] text-neutral-400",
645
+ title: `Edited ${formatTime(message.editedAt)}`,
646
+ children: "(edited)"
647
+ }
648
+ ),
649
+ canEdit && !isEditing && /* @__PURE__ */ jsx(
650
+ "button",
651
+ {
652
+ type: "button",
653
+ "data-slot": "superchat-edit-button",
654
+ onClick: startEdit,
655
+ "aria-label": "Edit message",
656
+ className: "rounded p-0.5 text-neutral-400 opacity-0 transition-opacity group-focus-within:opacity-100 group-hover:opacity-100 hover:text-neutral-600 focus-visible:opacity-100 dark:hover:text-neutral-200",
657
+ children: /* @__PURE__ */ jsx(Pencil, { size: 14, "aria-hidden": "true" })
658
+ }
659
+ )
660
+ ]
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsxs(
664
+ "div",
665
+ {
666
+ className: cn(
667
+ "flex items-center gap-1",
668
+ isSelf ? "flex-row-reverse" : "flex-row"
669
+ ),
670
+ children: [
671
+ canCopy && /* @__PURE__ */ jsx(
672
+ CopyMenu,
673
+ {
674
+ isSelf,
675
+ markdown: markdownSource,
676
+ getHtml: () => bubbleRef.current?.innerHTML ?? "",
677
+ getText: () => bubbleRef.current?.textContent ?? ""
678
+ }
679
+ ),
680
+ /* @__PURE__ */ jsx(
681
+ ChatBubble,
682
+ {
683
+ ref: bubbleRef,
684
+ "data-slot": "superchat-bubble",
685
+ variant: isSelf ? "user" : "assistant",
686
+ hasError: message.status === "error",
687
+ className: "text-sm",
688
+ children: isEditing ? /* @__PURE__ */ jsxs(
689
+ "div",
690
+ {
691
+ "data-slot": "superchat-message-editor",
692
+ className: "flex w-80 max-w-full flex-col gap-2",
693
+ children: [
694
+ /* @__PURE__ */ jsx(
695
+ "textarea",
696
+ {
697
+ value: draft,
698
+ ref: editRef,
699
+ rows: 2,
700
+ onChange: (e) => {
701
+ setDraft(e.target.value);
702
+ autosize();
703
+ },
704
+ onPaste: handleEditPaste,
705
+ onKeyDown: (e) => {
706
+ if (e.key === "Enter" && !e.shiftKey) {
707
+ e.preventDefault();
708
+ saveEdit();
709
+ } else if (e.key === "Escape") {
710
+ e.preventDefault();
711
+ cancelEdit();
712
+ }
713
+ },
714
+ "aria-label": "Edit message",
715
+ className: "focus:ring-primary-500 max-h-60 min-h-16 w-full resize-none rounded-lg border border-neutral-300 bg-white px-3 py-2 text-sm text-neutral-900 focus:ring-1 focus:outline-none dark:border-neutral-600 dark:bg-neutral-900 dark:text-white"
716
+ }
717
+ ),
718
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
719
+ /* @__PURE__ */ jsx(
720
+ "button",
721
+ {
722
+ type: "button",
723
+ onClick: cancelEdit,
724
+ className: "rounded-md px-2.5 py-1 text-xs font-medium text-neutral-200 hover:bg-white/10",
725
+ children: "Cancel"
726
+ }
727
+ ),
728
+ /* @__PURE__ */ jsx(
729
+ "button",
730
+ {
731
+ type: "button",
732
+ onClick: saveEdit,
733
+ disabled: !draft.trim() || draft.trim() === message.text,
734
+ className: "rounded-md bg-white px-2.5 py-1 text-xs font-medium text-neutral-900 hover:bg-neutral-100 disabled:opacity-40",
735
+ children: "Save"
736
+ }
737
+ )
738
+ ] })
739
+ ]
740
+ }
741
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
742
+ message.content?.map((block, i) => {
743
+ if (block.type === "tool_use" && block.toolCall) {
744
+ return /* @__PURE__ */ jsx(MCPToolCallDisplay, { toolCall: block.toolCall }, i);
745
+ }
746
+ if ((block.type === "text" || block.type === "thinking") && block.text) {
747
+ return /* @__PURE__ */ jsx(
748
+ "div",
749
+ {
750
+ className: "prose prose-sm dark:prose-invert max-w-none **:wrap-break-word",
751
+ children: renderText(block.text, {
752
+ messageId: message.id,
753
+ streaming,
754
+ role: participant?.kind === "human" ? "user" : "assistant"
755
+ })
756
+ },
757
+ i
758
+ );
759
+ }
760
+ if (block.type === "code" && block.text) {
761
+ const fenced = `\`\`\`${block.language ?? ""}
762
+ ${block.text}
763
+ \`\`\``;
764
+ return /* @__PURE__ */ jsx(
765
+ "div",
766
+ {
767
+ className: "prose prose-sm dark:prose-invert max-w-none **:wrap-break-word",
768
+ children: renderText(fenced, {
769
+ messageId: message.id,
770
+ streaming,
771
+ role: participant?.kind === "human" ? "user" : "assistant"
772
+ })
773
+ },
774
+ i
775
+ );
776
+ }
777
+ return null;
778
+ }),
779
+ message.text && /* @__PURE__ */ jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none **:wrap-break-word", children: renderText(message.text, {
780
+ messageId: message.id,
781
+ streaming,
782
+ role: participant?.kind === "human" ? "user" : "assistant"
783
+ }) }),
784
+ streaming && !hasBody && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(AITypingIndicator, {}) })
785
+ ] })
786
+ }
787
+ )
788
+ ]
789
+ }
790
+ )
791
+ ] })
792
+ ]
793
+ }
794
+ );
795
+ });
796
+ var sidebarItem = cva(
797
+ "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm transition-colors",
798
+ {
799
+ variants: {
800
+ active: {
801
+ true: "bg-primary-100 text-primary-900 dark:bg-primary-900/40 dark:text-primary-100",
802
+ false: "text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
803
+ }
804
+ },
805
+ defaultVariants: { active: false }
806
+ }
807
+ );
808
+ function VirtualThread({
809
+ items,
810
+ participantById,
811
+ currentParticipantId,
812
+ renderText,
813
+ linkBuilder,
814
+ onReferenceClick,
815
+ editable,
816
+ onMessageEdited,
817
+ order,
818
+ conversationId,
819
+ containerProps
820
+ }) {
821
+ const parentRef = React4.useRef(null);
822
+ const virtualizer = useVirtualizer({
823
+ count: items.length,
824
+ getScrollElement: () => parentRef.current,
825
+ // Rough first guess; real heights are measured via measureElement.
826
+ estimateSize: () => 88,
827
+ overscan: 10,
828
+ getItemKey: (index) => items[index]?.id ?? index
829
+ });
830
+ const count = items.length;
831
+ React4.useEffect(() => {
832
+ if (count === 0) return;
833
+ if (order === "desc") {
834
+ virtualizer.scrollToIndex(0, { align: "start" });
835
+ } else {
836
+ virtualizer.scrollToIndex(count - 1, { align: "end" });
837
+ }
838
+ }, [count, conversationId, order]);
839
+ const virtualItems = virtualizer.getVirtualItems();
840
+ return /* @__PURE__ */ jsx("div", { ...containerProps, ref: parentRef, children: /* @__PURE__ */ jsx(
841
+ "div",
842
+ {
843
+ style: {
844
+ height: virtualizer.getTotalSize(),
845
+ position: "relative",
846
+ width: "100%"
847
+ },
848
+ children: virtualItems.map((virtualRow) => {
849
+ const message = items[virtualRow.index];
850
+ if (!message) return null;
851
+ return /* @__PURE__ */ jsx(
852
+ "div",
853
+ {
854
+ "data-index": virtualRow.index,
855
+ ref: virtualizer.measureElement,
856
+ style: {
857
+ position: "absolute",
858
+ top: 0,
859
+ left: 0,
860
+ width: "100%",
861
+ transform: `translateY(${virtualRow.start}px)`
862
+ },
863
+ children: /* @__PURE__ */ jsx("div", { className: "pb-4", children: /* @__PURE__ */ jsx(
864
+ MessageRow,
865
+ {
866
+ message,
867
+ participant: participantById.get(message.participantId),
868
+ isSelf: !!currentParticipantId && message.participantId === currentParticipantId,
869
+ renderText,
870
+ linkBuilder,
871
+ onReferenceClick,
872
+ editable,
873
+ onMessageEdited
874
+ }
875
+ ) })
876
+ },
877
+ virtualRow.key
878
+ );
879
+ })
880
+ }
881
+ ) });
882
+ }
883
+ function SuperChat({
884
+ conversation,
885
+ currentParticipantId,
886
+ renderPlugins,
887
+ renderTextContent,
888
+ trustedContent,
889
+ readOnly,
890
+ acceptedFileTypes,
891
+ order = "asc",
892
+ virtualized = false,
893
+ linkBuilder,
894
+ className,
895
+ onMessageSent,
896
+ onMessageEdited,
897
+ onConversationClosed,
898
+ onReferenceClick,
899
+ onBack
900
+ }) {
901
+ const headingId = React4.useId();
902
+ const renderText = React4.useMemo(
903
+ () => renderTextContent ?? createMarkdownRenderer({
904
+ plugins: renderPlugins,
905
+ trusted: trustedContent
906
+ }),
907
+ [renderTextContent, renderPlugins, trustedContent]
908
+ );
909
+ const threadRef = React4.useRef(null);
910
+ React4.useEffect(() => {
911
+ if (virtualized) return;
912
+ const el = threadRef.current;
913
+ if (!el) return;
914
+ el.scrollTop = order === "desc" ? 0 : el.scrollHeight;
915
+ }, [conversation.thread.length, conversation.id, order, virtualized]);
916
+ const participantById = React4.useMemo(() => {
917
+ const map = /* @__PURE__ */ new Map();
918
+ conversation.participants.forEach((p) => map.set(p.id, p));
919
+ return map;
920
+ }, [conversation]);
921
+ const orderedThread = React4.useMemo(() => {
922
+ const sorted = [...conversation.thread].sort(byTime);
923
+ return order === "desc" ? sorted.reverse() : sorted;
924
+ }, [conversation, order]);
925
+ const onMessageEditedRef = React4.useRef(onMessageEdited);
926
+ onMessageEditedRef.current = onMessageEdited;
927
+ const conversationRef = React4.useRef(conversation);
928
+ conversationRef.current = conversation;
929
+ const handleMessageEdited = React4.useCallback(
930
+ (messageId, text) => {
931
+ onMessageEditedRef.current?.(messageId, text, {
932
+ conversation: conversationRef.current
933
+ });
934
+ },
935
+ []
936
+ );
937
+ const editable = !readOnly && !!onMessageEdited;
938
+ const mentionOptions = React4.useMemo(
939
+ () => conversation.participants.filter((p) => p.kind !== "system").map((p) => ({
940
+ id: p.id,
941
+ label: p.name,
942
+ description: p.role,
943
+ meta: p.kind,
944
+ icon: /* @__PURE__ */ jsx(ParticipantAvatar, { participant: p })
945
+ })),
946
+ [conversation.participants]
947
+ );
948
+ const composerAccept = React4.useMemo(
949
+ () => acceptTokensFor(acceptedFileTypes),
950
+ [acceptedFileTypes]
951
+ );
952
+ const handleComposerSend = React4.useCallback(
953
+ async (message) => {
954
+ const text = message.content;
955
+ const mentions = detectMentions(text, conversation.participants);
956
+ const attachments = await filesToComposerAttachments(
957
+ message.attachments ?? []
958
+ );
959
+ onMessageSent?.(text, { conversation, mentions, attachments });
960
+ },
961
+ [conversation, onMessageSent]
962
+ );
963
+ return /* @__PURE__ */ jsxs(
964
+ "section",
965
+ {
966
+ "data-slot": "superchat",
967
+ role: "group",
968
+ "aria-labelledby": headingId,
969
+ className: cn(
970
+ "flex min-w-0 flex-1 flex-col bg-white dark:bg-neutral-900",
971
+ className
972
+ ),
973
+ children: [
974
+ /* @__PURE__ */ jsxs(
975
+ "header",
976
+ {
977
+ "data-slot": "superchat-header",
978
+ className: "flex items-center justify-between gap-2 border-b border-neutral-200 p-3 dark:border-neutral-700",
979
+ children: [
980
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
981
+ onBack && /* @__PURE__ */ jsx(
982
+ "button",
983
+ {
984
+ type: "button",
985
+ onClick: onBack,
986
+ "aria-label": "Back to conversations",
987
+ className: "-ml-1 shrink-0 rounded-md p-1 text-neutral-500 hover:bg-neutral-100 sm:hidden dark:text-neutral-300 dark:hover:bg-neutral-800",
988
+ children: /* @__PURE__ */ jsx(
989
+ "svg",
990
+ {
991
+ viewBox: "0 0 24 24",
992
+ width: "20",
993
+ height: "20",
994
+ fill: "none",
995
+ stroke: "currentColor",
996
+ strokeWidth: "2",
997
+ strokeLinecap: "round",
998
+ strokeLinejoin: "round",
999
+ "aria-hidden": "true",
1000
+ children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
1001
+ }
1002
+ )
1003
+ }
1004
+ ),
1005
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1006
+ /* @__PURE__ */ jsx(
1007
+ "h2",
1008
+ {
1009
+ id: headingId,
1010
+ className: "truncate text-sm font-semibold text-neutral-800 dark:text-neutral-100",
1011
+ children: conversation.title
1012
+ }
1013
+ ),
1014
+ /* @__PURE__ */ jsx(
1015
+ "div",
1016
+ {
1017
+ "data-slot": "superchat-participants",
1018
+ role: "group",
1019
+ "aria-label": "Participants",
1020
+ className: "mt-0.5 flex items-center gap-1",
1021
+ children: conversation.participants.slice(0, 6).map((p) => /* @__PURE__ */ jsx("span", { role: "img", "aria-label": p.name, children: /* @__PURE__ */ jsx(ParticipantAvatar, { participant: p }) }, p.id))
1022
+ }
1023
+ )
1024
+ ] })
1025
+ ] }),
1026
+ onConversationClosed && /* @__PURE__ */ jsx(
1027
+ "button",
1028
+ {
1029
+ type: "button",
1030
+ onClick: () => onConversationClosed(conversation),
1031
+ "aria-label": "Close conversation",
1032
+ className: "rounded-md p-1 text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800",
1033
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
1034
+ }
1035
+ )
1036
+ ]
1037
+ }
1038
+ ),
1039
+ virtualized ? /* @__PURE__ */ jsx(
1040
+ VirtualThread,
1041
+ {
1042
+ items: orderedThread,
1043
+ participantById,
1044
+ currentParticipantId,
1045
+ renderText,
1046
+ linkBuilder,
1047
+ onReferenceClick,
1048
+ editable,
1049
+ onMessageEdited: handleMessageEdited,
1050
+ order,
1051
+ conversationId: conversation.id,
1052
+ containerProps: {
1053
+ "data-slot": "superchat-thread",
1054
+ role: "log",
1055
+ "aria-label": "Messages",
1056
+ "aria-live": "polite",
1057
+ // Focusable so keyboard-only users can scroll the message history.
1058
+ tabIndex: 0,
1059
+ className: "flex-1 overflow-y-auto p-4"
1060
+ }
1061
+ }
1062
+ ) : /* @__PURE__ */ jsx(
1063
+ "div",
1064
+ {
1065
+ "data-slot": "superchat-thread",
1066
+ ref: threadRef,
1067
+ role: "log",
1068
+ "aria-label": "Messages",
1069
+ "aria-live": "polite",
1070
+ tabIndex: 0,
1071
+ className: "flex-1 space-y-4 overflow-y-auto p-4",
1072
+ children: orderedThread.map((m) => /* @__PURE__ */ jsx(
1073
+ MessageRow,
1074
+ {
1075
+ message: m,
1076
+ participant: participantById.get(m.participantId),
1077
+ isSelf: !!currentParticipantId && m.participantId === currentParticipantId,
1078
+ renderText,
1079
+ linkBuilder,
1080
+ onReferenceClick,
1081
+ editable,
1082
+ onMessageEdited: handleMessageEdited
1083
+ },
1084
+ m.id
1085
+ ))
1086
+ }
1087
+ ),
1088
+ /* @__PURE__ */ jsx(
1089
+ MessageComposer,
1090
+ {
1091
+ onSend: handleComposerSend,
1092
+ disabled: readOnly,
1093
+ placeholder: readOnly ? "Read-only conversation" : "Type a message\u2026 use @ to address an agent",
1094
+ mentionOptions,
1095
+ showAttachmentPicker: !readOnly,
1096
+ showCameraButton: false,
1097
+ acceptedFileTypes: composerAccept,
1098
+ maxLength: 1e5
1099
+ }
1100
+ )
1101
+ ]
1102
+ }
1103
+ );
1104
+ }
1105
+ function SuperChatConversations({
1106
+ conversations,
1107
+ activeConversationId,
1108
+ defaultActiveConversationId,
1109
+ className,
1110
+ onConversationOpened,
1111
+ onNewConversation
1112
+ }) {
1113
+ const [internalActive, setInternalActive] = React4.useState(
1114
+ defaultActiveConversationId ?? conversations[0]?.id
1115
+ );
1116
+ const requestedId = activeConversationId ?? internalActive;
1117
+ const activeId = conversations.some((c) => c.id === requestedId) ? requestedId : conversations[0]?.id;
1118
+ const sortedConversations = React4.useMemo(
1119
+ () => [...conversations].sort((a, b) => lastActivityOf(b) - lastActivityOf(a)),
1120
+ [conversations]
1121
+ );
1122
+ const selectConversation = (c) => {
1123
+ if (activeConversationId === void 0) setInternalActive(c.id);
1124
+ onConversationOpened?.(c);
1125
+ };
1126
+ return /* @__PURE__ */ jsxs(
1127
+ "aside",
1128
+ {
1129
+ "data-slot": "superchat-conversations",
1130
+ "aria-label": "Conversations",
1131
+ className: cn(
1132
+ "flex w-64 shrink-0 flex-col border-r border-neutral-200 dark:border-neutral-700",
1133
+ className
1134
+ ),
1135
+ children: [
1136
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-3", children: [
1137
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-neutral-700 dark:text-neutral-200", children: "Conversations" }),
1138
+ onNewConversation && /* @__PURE__ */ jsx(
1139
+ "button",
1140
+ {
1141
+ type: "button",
1142
+ onClick: onNewConversation,
1143
+ "aria-label": "New conversation",
1144
+ className: "rounded-md px-2 py-1 text-lg leading-none text-neutral-500 hover:bg-neutral-100 dark:hover:bg-neutral-800",
1145
+ children: "+"
1146
+ }
1147
+ )
1148
+ ] }),
1149
+ /* @__PURE__ */ jsx(
1150
+ "div",
1151
+ {
1152
+ "data-slot": "superchat-conversation-list",
1153
+ role: "list",
1154
+ className: "flex-1 space-y-1 overflow-y-auto px-2 pb-2",
1155
+ children: sortedConversations.map((c) => {
1156
+ const last = lastMessageByTime(c.thread);
1157
+ const isActive = c.id === activeId;
1158
+ return /* @__PURE__ */ jsx("div", { role: "listitem", children: /* @__PURE__ */ jsxs(
1159
+ "button",
1160
+ {
1161
+ type: "button",
1162
+ "aria-current": isActive ? "true" : void 0,
1163
+ onClick: () => selectConversation(c),
1164
+ className: sidebarItem({ active: isActive }),
1165
+ children: [
1166
+ /* @__PURE__ */ jsxs("span", { className: "flex-1 truncate", children: [
1167
+ /* @__PURE__ */ jsx("span", { className: "block truncate font-medium", children: c.title }),
1168
+ last?.text && /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-neutral-400", children: last.text })
1169
+ ] }),
1170
+ !!c.unread && /* @__PURE__ */ jsxs("span", { className: "bg-primary-600 ml-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[10px] font-semibold text-white", children: [
1171
+ c.unread,
1172
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: " unread messages" })
1173
+ ] })
1174
+ ]
1175
+ }
1176
+ ) }, c.id);
1177
+ })
1178
+ }
1179
+ )
1180
+ ]
1181
+ }
1182
+ );
1183
+ }
1184
+ function SuperChatInbox({
1185
+ conversations,
1186
+ activeConversationId,
1187
+ defaultActiveConversationId,
1188
+ currentParticipantId,
1189
+ renderPlugins,
1190
+ renderTextContent,
1191
+ trustedContent,
1192
+ readOnly,
1193
+ acceptedFileTypes,
1194
+ order,
1195
+ virtualized,
1196
+ showSidebar = true,
1197
+ linkBuilder,
1198
+ className,
1199
+ onMessageSent,
1200
+ onMessageEdited,
1201
+ onConversationOpened,
1202
+ onConversationClosed,
1203
+ onNewConversation,
1204
+ onReferenceClick
1205
+ }) {
1206
+ const [internalActive, setInternalActive] = React4.useState(
1207
+ defaultActiveConversationId ?? conversations[0]?.id
1208
+ );
1209
+ const requestedId = activeConversationId ?? internalActive;
1210
+ const active = conversations.find((c) => c.id === requestedId) ?? conversations[0];
1211
+ const activeId = active?.id;
1212
+ const [mobileView, setMobileView] = React4.useState("list");
1213
+ const handleOpen = (c) => {
1214
+ if (activeConversationId === void 0) setInternalActive(c.id);
1215
+ setMobileView("chat");
1216
+ onConversationOpened?.(c);
1217
+ };
1218
+ return /* @__PURE__ */ jsxs(
1219
+ "div",
1220
+ {
1221
+ "data-slot": "superchat-inbox",
1222
+ role: "group",
1223
+ "aria-label": active ? `Chat: ${active.title}` : "Chat",
1224
+ className: cn(
1225
+ "flex h-full overflow-hidden rounded-xl border border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-900",
1226
+ className
1227
+ ),
1228
+ children: [
1229
+ showSidebar && /* @__PURE__ */ jsx(
1230
+ SuperChatConversations,
1231
+ {
1232
+ conversations,
1233
+ activeConversationId: activeId,
1234
+ onConversationOpened: handleOpen,
1235
+ onNewConversation,
1236
+ className: cn(
1237
+ "w-full sm:w-64",
1238
+ mobileView === "chat" && "hidden sm:flex"
1239
+ )
1240
+ }
1241
+ ),
1242
+ active ? /* @__PURE__ */ jsx(
1243
+ SuperChat,
1244
+ {
1245
+ conversation: active,
1246
+ currentParticipantId,
1247
+ renderPlugins,
1248
+ renderTextContent,
1249
+ trustedContent,
1250
+ readOnly,
1251
+ acceptedFileTypes,
1252
+ order,
1253
+ virtualized,
1254
+ linkBuilder,
1255
+ onMessageSent,
1256
+ onMessageEdited,
1257
+ onConversationClosed,
1258
+ onReferenceClick,
1259
+ onBack: showSidebar ? () => setMobileView("list") : void 0,
1260
+ className: cn(
1261
+ showSidebar && mobileView === "list" && "hidden sm:flex"
1262
+ )
1263
+ }
1264
+ ) : /* @__PURE__ */ jsx(
1265
+ "section",
1266
+ {
1267
+ "data-slot": "superchat",
1268
+ className: cn(
1269
+ "min-w-0 flex-1 items-center justify-center text-sm text-neutral-400",
1270
+ showSidebar && mobileView === "list" ? "hidden sm:flex" : "flex"
1271
+ ),
1272
+ children: "No conversation selected"
1273
+ }
1274
+ )
1275
+ ]
1276
+ }
1277
+ );
1278
+ }
1279
+
1280
+ export { SuperChat, SuperChatConversations, SuperChatInbox, createMarkdownRenderer };
1281
+ //# sourceMappingURL=index.js.map
1282
+ //# sourceMappingURL=index.js.map