@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,1181 @@
1
+ import { useTextRenderContext } from '../../../chunk-I6CY5C6A.js';
2
+ import { LightboxModal } from '../../../chunk-ASLZUFH4.js';
3
+ import '../../../chunk-UHPQYBXQ.js';
4
+ import { cn } from '../../../chunk-F3SOEIN2.js';
5
+ import * as React2 from 'react';
6
+ import rehypeHighlight from 'rehype-highlight';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+ import rehypeKatex from 'rehype-katex';
9
+ import remarkMath from 'remark-math';
10
+ import { createPortal } from 'react-dom';
11
+
12
+ function CopyablePre({
13
+ node: _node,
14
+ ...props
15
+ }) {
16
+ const ref = React2.useRef(null);
17
+ const [copied, setCopied] = React2.useState(false);
18
+ const onCopy = React2.useCallback(() => {
19
+ const text = ref.current?.innerText ?? "";
20
+ if (!text) return;
21
+ const copy = navigator.clipboard?.writeText(text);
22
+ if (!copy) return;
23
+ void copy.then(
24
+ () => {
25
+ setCopied(true);
26
+ window.setTimeout(() => setCopied(false), 1500);
27
+ },
28
+ () => {
29
+ }
30
+ );
31
+ }, []);
32
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "superchat-code", className: "group relative my-2", children: [
33
+ /* @__PURE__ */ jsx(
34
+ "button",
35
+ {
36
+ type: "button",
37
+ onClick: onCopy,
38
+ "aria-label": copied ? "Copied" : "Copy code",
39
+ className: "absolute top-2 right-2 rounded-md bg-neutral-700/80 px-2 py-1 text-xs text-neutral-100 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-neutral-600 focus-visible:opacity-100",
40
+ children: copied ? "Copied" : "Copy"
41
+ }
42
+ ),
43
+ /* @__PURE__ */ jsx(
44
+ "pre",
45
+ {
46
+ ...props,
47
+ ref,
48
+ className: cn(
49
+ "overflow-x-auto rounded-lg bg-neutral-900 p-3 text-sm text-neutral-100 dark:bg-neutral-950",
50
+ props.className
51
+ )
52
+ }
53
+ )
54
+ ] });
55
+ }
56
+ function createCodePlugin() {
57
+ return {
58
+ name: "code",
59
+ // `detect: false` + ignoreMissing keeps it predictable for streamed output.
60
+ rehypePlugins: [[rehypeHighlight, { detect: false, ignoreMissing: true }]],
61
+ components: {
62
+ pre: CopyablePre
63
+ },
64
+ // `.hljs-*` token classes are already permitted by the base schema's
65
+ // broadened `className` allow-list on code/pre/span.
66
+ sanitizeSchema: {
67
+ attributes: {
68
+ code: ["className"],
69
+ span: ["className"]
70
+ }
71
+ }
72
+ };
73
+ }
74
+ var KATEX_TAGS = [
75
+ "span",
76
+ "svg",
77
+ "path",
78
+ "line",
79
+ "math",
80
+ "semantics",
81
+ "annotation",
82
+ "mrow",
83
+ "mi",
84
+ "mo",
85
+ "mn",
86
+ "ms",
87
+ "mtext",
88
+ "msup",
89
+ "msub",
90
+ "msubsup",
91
+ "mfrac",
92
+ "msqrt",
93
+ "mroot",
94
+ "mover",
95
+ "munder",
96
+ "munderover",
97
+ "mtable",
98
+ "mtr",
99
+ "mtd",
100
+ "mspace",
101
+ "mpadded",
102
+ "mphantom",
103
+ "menclose",
104
+ "mstyle"
105
+ ];
106
+ function createMathPlugin() {
107
+ return {
108
+ name: "math",
109
+ remarkPlugins: [remarkMath],
110
+ // `output: 'htmlAndMathml'` (KaTeX default) gives accessible MathML too.
111
+ rehypePlugins: [[rehypeKatex, { throwOnError: false }]],
112
+ sanitizeSchema: {
113
+ tagNames: KATEX_TAGS,
114
+ attributes: {
115
+ "*": ["className"],
116
+ span: ["className", "style", "ariaHidden"],
117
+ svg: [
118
+ "xmlns",
119
+ "width",
120
+ "height",
121
+ "viewBox",
122
+ "preserveAspectRatio",
123
+ "style"
124
+ ],
125
+ path: ["d"],
126
+ line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth"],
127
+ math: ["xmlns", "display"],
128
+ annotation: ["encoding"]
129
+ }
130
+ }
131
+ };
132
+ }
133
+ var GENUI_TAG = "genui-widget";
134
+ function textOf(node) {
135
+ if (node.type === "text") return node.value ?? "";
136
+ return (node.children ?? []).map(textOf).join("");
137
+ }
138
+ function isGenuiPre(node) {
139
+ if (node.tagName !== "pre") return false;
140
+ const code = node.children?.find((c) => c.tagName === "code");
141
+ const className = code?.properties?.className;
142
+ const classes = Array.isArray(className) ? className : [className];
143
+ return classes.some(
144
+ (c) => typeof c === "string" && (c === "language-genui" || c === "genui")
145
+ );
146
+ }
147
+ function rehypeGenui() {
148
+ return (tree) => {
149
+ const walk = (node) => {
150
+ if (!node.children) return;
151
+ node.children = node.children.map((child) => {
152
+ if (isGenuiPre(child)) {
153
+ const code = child.children?.find((c) => c.tagName === "code");
154
+ const raw = code ? textOf(code) : "";
155
+ return {
156
+ type: "element",
157
+ tagName: GENUI_TAG,
158
+ properties: {},
159
+ children: [{ type: "text", value: raw }]
160
+ };
161
+ }
162
+ walk(child);
163
+ return child;
164
+ });
165
+ };
166
+ walk(tree);
167
+ };
168
+ }
169
+ function PendingCard({ label }) {
170
+ return /* @__PURE__ */ jsxs(
171
+ "div",
172
+ {
173
+ "data-slot": "superchat-genui-pending",
174
+ className: "my-2 flex items-center gap-2 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2 text-sm text-neutral-600 dark:border-neutral-700 dark:bg-neutral-800/50 dark:text-neutral-300",
175
+ children: [
176
+ /* @__PURE__ */ jsx(
177
+ "span",
178
+ {
179
+ className: "h-3 w-3 animate-spin rounded-full border-2 border-neutral-400 border-t-transparent",
180
+ "aria-hidden": "true"
181
+ }
182
+ ),
183
+ label
184
+ ]
185
+ }
186
+ );
187
+ }
188
+ function InertFallback({ raw }) {
189
+ return /* @__PURE__ */ jsx(
190
+ "pre",
191
+ {
192
+ "data-slot": "superchat-genui-fallback",
193
+ className: "my-2 overflow-x-auto rounded-lg bg-neutral-900 p-3 text-sm text-neutral-100 dark:bg-neutral-950",
194
+ children: /* @__PURE__ */ jsx("code", { children: raw })
195
+ }
196
+ );
197
+ }
198
+ function ErrorCard({ message }) {
199
+ return /* @__PURE__ */ jsx(
200
+ "div",
201
+ {
202
+ "data-slot": "superchat-genui-error",
203
+ role: "alert",
204
+ className: "my-2 rounded-lg border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-700 dark:bg-red-900/30 dark:text-red-300",
205
+ children: message
206
+ }
207
+ );
208
+ }
209
+ function parsePayload(raw) {
210
+ const trimmed = raw.trim();
211
+ if (!trimmed) return { ok: false };
212
+ try {
213
+ const value = JSON.parse(trimmed);
214
+ if (!value || typeof value.widget !== "string") return { ok: false };
215
+ return { ok: true, value };
216
+ } catch {
217
+ return { ok: false };
218
+ }
219
+ }
220
+ async function validate(schema, data) {
221
+ if (!schema) return { ok: true, value: data };
222
+ const result = await schema["~standard"].validate(data);
223
+ if ("issues" in result && result.issues) {
224
+ return {
225
+ ok: false,
226
+ message: result.issues.map((i) => i.message).join("; ") || "Invalid widget payload"
227
+ };
228
+ }
229
+ return { ok: true, value: result.value };
230
+ }
231
+ function usePrefetchTrigger(policy, ref) {
232
+ const [ready, setReady] = React2.useState(policy === "eager");
233
+ React2.useEffect(() => {
234
+ if (ready) return;
235
+ if (policy === "idle") {
236
+ const requestIdle = window.requestIdleCallback;
237
+ const cancelIdle = window.cancelIdleCallback;
238
+ let idleId = null;
239
+ let timeoutId = null;
240
+ if (requestIdle) {
241
+ idleId = requestIdle(() => setReady(true));
242
+ } else {
243
+ timeoutId = window.setTimeout(() => setReady(true), 200);
244
+ }
245
+ return () => {
246
+ if (idleId !== null && cancelIdle) cancelIdle(idleId);
247
+ if (timeoutId !== null) window.clearTimeout(timeoutId);
248
+ };
249
+ }
250
+ const el = ref.current;
251
+ if (!el || typeof IntersectionObserver === "undefined") {
252
+ setReady(true);
253
+ return;
254
+ }
255
+ const obs = new IntersectionObserver((entries) => {
256
+ if (entries.some((e) => e.isIntersecting)) {
257
+ setReady(true);
258
+ obs.disconnect();
259
+ }
260
+ });
261
+ obs.observe(el);
262
+ return () => obs.disconnect();
263
+ }, [policy, ready, ref]);
264
+ return ready;
265
+ }
266
+ function GenUIBlock({ raw, registry }) {
267
+ const { messageId, streaming } = useTextRenderContext();
268
+ const placeholderRef = React2.useRef(null);
269
+ const parsed = React2.useMemo(() => parsePayload(raw), [raw]);
270
+ const entry = parsed.ok ? registry[parsed.value.widget] : void 0;
271
+ const policy = entry?.prefetch ?? (parsed.ok ? parsed.value.prefetch : void 0) ?? "visible";
272
+ const codeReady = usePrefetchTrigger(policy, placeholderRef);
273
+ const [Loaded, setLoaded] = React2.useState(null);
274
+ const [validated, setValidated] = React2.useState(null);
275
+ React2.useEffect(() => {
276
+ if (!entry || !codeReady) return;
277
+ let active = true;
278
+ void entry.component().then((m) => {
279
+ if (active) setLoaded(() => m.default);
280
+ }).catch(() => {
281
+ if (active)
282
+ setValidated({ ok: false, message: "Failed to load widget" });
283
+ });
284
+ return () => {
285
+ active = false;
286
+ };
287
+ }, [entry, codeReady]);
288
+ React2.useEffect(() => {
289
+ if (!entry || !parsed.ok || streaming) return;
290
+ let active = true;
291
+ const props = parsed.value.props;
292
+ void (async () => {
293
+ const result = await validate(entry.schema, props);
294
+ if (!active) return;
295
+ if (result.ok && entry.prefetchData) {
296
+ try {
297
+ await entry.prefetchData(result.value);
298
+ } catch {
299
+ }
300
+ }
301
+ if (active) setValidated(result);
302
+ })();
303
+ return () => {
304
+ active = false;
305
+ };
306
+ }, [entry, parsed, streaming]);
307
+ if (!parsed.ok) {
308
+ if (streaming) {
309
+ return /* @__PURE__ */ jsx("div", { ref: placeholderRef, children: /* @__PURE__ */ jsx(PendingCard, { label: "Preparing widget\u2026" }) });
310
+ }
311
+ return /* @__PURE__ */ jsx(InertFallback, { raw });
312
+ }
313
+ if (parsed.ok && !entry) return /* @__PURE__ */ jsx(InertFallback, { raw });
314
+ if (streaming || !Loaded || !validated) {
315
+ return /* @__PURE__ */ jsx("div", { ref: placeholderRef, children: /* @__PURE__ */ jsx(PendingCard, { label: "Preparing widget\u2026" }) });
316
+ }
317
+ if (!validated.ok) return /* @__PURE__ */ jsx(ErrorCard, { message: validated.message });
318
+ const meta = {
319
+ name: parsed.value.widget,
320
+ version: parsed.value.version,
321
+ messageId,
322
+ streaming
323
+ };
324
+ return /* @__PURE__ */ jsx(Loaded, { data: validated.value, meta });
325
+ }
326
+ function createGenUIPlugin(registry) {
327
+ const GenUIWidgetComponent = (props) => {
328
+ const raw = React2.Children.toArray(props.children).filter((c) => typeof c === "string").join("");
329
+ return /* @__PURE__ */ jsx(GenUIBlock, { raw, registry });
330
+ };
331
+ return {
332
+ name: "genui",
333
+ rehypePlugins: [rehypeGenui],
334
+ components: {
335
+ [GENUI_TAG]: GenUIWidgetComponent
336
+ },
337
+ sanitizeSchema: {
338
+ tagNames: [GENUI_TAG]
339
+ }
340
+ };
341
+ }
342
+ var MERMAID_TAG = "mermaid-diagram";
343
+ function textOf2(node) {
344
+ if (node.type === "text") return node.value ?? "";
345
+ return (node.children ?? []).map(textOf2).join("");
346
+ }
347
+ function isMermaidPre(node) {
348
+ if (node.tagName !== "pre") return false;
349
+ const code = node.children?.find((c) => c.tagName === "code");
350
+ const className = code?.properties?.className;
351
+ const classes = Array.isArray(className) ? className : [className];
352
+ return classes.some(
353
+ (c) => typeof c === "string" && (c === "language-mermaid" || c === "mermaid")
354
+ );
355
+ }
356
+ function rehypeMermaid() {
357
+ return (tree) => {
358
+ const walk = (node) => {
359
+ if (!node.children) return;
360
+ node.children = node.children.map((child) => {
361
+ if (isMermaidPre(child)) {
362
+ const code = child.children?.find((c) => c.tagName === "code");
363
+ const raw = code ? textOf2(code) : "";
364
+ return {
365
+ type: "element",
366
+ tagName: MERMAID_TAG,
367
+ properties: {},
368
+ children: [{ type: "text", value: raw }]
369
+ };
370
+ }
371
+ walk(child);
372
+ return child;
373
+ });
374
+ };
375
+ walk(tree);
376
+ };
377
+ }
378
+ function PendingCard2({ label }) {
379
+ return /* @__PURE__ */ jsxs(
380
+ "div",
381
+ {
382
+ "data-slot": "superchat-mermaid-pending",
383
+ className: "my-2 flex items-center gap-2 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2 text-sm text-neutral-600 dark:border-neutral-700 dark:bg-neutral-800/50 dark:text-neutral-300",
384
+ children: [
385
+ /* @__PURE__ */ jsx(
386
+ "span",
387
+ {
388
+ className: "h-3 w-3 animate-spin rounded-full border-2 border-neutral-400 border-t-transparent",
389
+ "aria-hidden": "true"
390
+ }
391
+ ),
392
+ label
393
+ ]
394
+ }
395
+ );
396
+ }
397
+ function InertFallback2({ raw }) {
398
+ return /* @__PURE__ */ jsx(
399
+ "pre",
400
+ {
401
+ "data-slot": "superchat-mermaid-fallback",
402
+ className: "my-2 overflow-x-auto rounded-lg bg-neutral-900 p-3 text-sm text-neutral-100 dark:bg-neutral-950",
403
+ children: /* @__PURE__ */ jsx("code", { children: raw })
404
+ }
405
+ );
406
+ }
407
+ var mermaidReady = null;
408
+ var mermaidTheme = null;
409
+ function loadMermaid(dark) {
410
+ const theme = dark ? "dark" : "default";
411
+ if (!mermaidReady || mermaidTheme !== theme) {
412
+ mermaidReady = (mermaidReady ?? import('mermaid').then(({ default: mermaid }) => mermaid)).then((mermaid) => {
413
+ mermaidTheme = theme;
414
+ mermaid.initialize({
415
+ startOnLoad: false,
416
+ securityLevel: "strict",
417
+ theme
418
+ });
419
+ return mermaid;
420
+ });
421
+ }
422
+ return mermaidReady;
423
+ }
424
+ var mermaidSeq = 0;
425
+ function MermaidDiagram({ code }) {
426
+ const { streaming } = useTextRenderContext();
427
+ const [svg, setSvg] = React2.useState(null);
428
+ const [failed, setFailed] = React2.useState(false);
429
+ const idRef = React2.useRef(`superchat-mermaid-${mermaidSeq += 1}`);
430
+ const source = code.trim();
431
+ React2.useEffect(() => {
432
+ if (streaming || !source) return;
433
+ let active = true;
434
+ setSvg(null);
435
+ setFailed(false);
436
+ const dark = typeof document !== "undefined" && document.documentElement.classList.contains("dark");
437
+ void loadMermaid(dark).then((mermaid) => mermaid.render(idRef.current, source)).then(({ svg: rendered }) => {
438
+ if (active) setSvg(rendered);
439
+ }).catch(() => {
440
+ if (active) setFailed(true);
441
+ });
442
+ return () => {
443
+ active = false;
444
+ };
445
+ }, [source, streaming]);
446
+ if (!source && !streaming) return /* @__PURE__ */ jsx(InertFallback2, { raw: code });
447
+ if (failed) return /* @__PURE__ */ jsx(InertFallback2, { raw: code });
448
+ if (streaming || svg === null) {
449
+ return /* @__PURE__ */ jsx(PendingCard2, { label: "Rendering diagram\u2026" });
450
+ }
451
+ return /* @__PURE__ */ jsx(
452
+ "div",
453
+ {
454
+ "data-slot": "superchat-mermaid",
455
+ className: "my-2 overflow-x-auto",
456
+ dangerouslySetInnerHTML: { __html: svg }
457
+ }
458
+ );
459
+ }
460
+ function createMermaidPlugin() {
461
+ const MermaidComponent = (props) => {
462
+ const raw = React2.Children.toArray(props.children).filter((c) => typeof c === "string").join("");
463
+ return /* @__PURE__ */ jsx(MermaidDiagram, { code: raw });
464
+ };
465
+ return {
466
+ name: "mermaid",
467
+ rehypePlugins: [rehypeMermaid],
468
+ components: {
469
+ [MERMAID_TAG]: MermaidComponent
470
+ },
471
+ sanitizeSchema: {
472
+ tagNames: [MERMAID_TAG]
473
+ }
474
+ };
475
+ }
476
+ function filenameFromUrl(url, alt) {
477
+ if (alt && alt.trim()) return alt.trim();
478
+ try {
479
+ const path = new URL(url, "http://localhost").pathname;
480
+ const last = path.split("/").filter(Boolean).pop();
481
+ return last || "image";
482
+ } catch {
483
+ return "image";
484
+ }
485
+ }
486
+ function ZoomableImage({
487
+ node: _node,
488
+ ...props
489
+ }) {
490
+ const [open, setOpen] = React2.useState(false);
491
+ const src = typeof props.src === "string" ? props.src : "";
492
+ const alt = typeof props.alt === "string" ? props.alt : "";
493
+ const attachment = React2.useMemo(() => {
494
+ if (!src) return null;
495
+ return {
496
+ id: src,
497
+ type: "image",
498
+ url: src,
499
+ filename: filenameFromUrl(src, alt),
500
+ size: 0,
501
+ mimeType: "image/*",
502
+ state: "uploaded",
503
+ alt
504
+ };
505
+ }, [src, alt]);
506
+ if (!src) return null;
507
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
508
+ /* @__PURE__ */ jsx(
509
+ "button",
510
+ {
511
+ type: "button",
512
+ onClick: () => setOpen(true),
513
+ "aria-label": alt ? `View image: ${alt}` : "View image",
514
+ className: "focus-visible:ring-primary-500 my-2 inline-block cursor-zoom-in rounded-lg focus-visible:ring-2 focus-visible:outline-none",
515
+ children: /* @__PURE__ */ jsx(
516
+ "img",
517
+ {
518
+ ...props,
519
+ alt,
520
+ className: cn(
521
+ "max-h-80 max-w-full rounded-lg object-contain",
522
+ props.className
523
+ )
524
+ }
525
+ )
526
+ }
527
+ ),
528
+ open && typeof document !== "undefined" && createPortal(
529
+ /* @__PURE__ */ jsx(
530
+ LightboxModal,
531
+ {
532
+ attachment,
533
+ onClose: () => setOpen(false)
534
+ }
535
+ ),
536
+ document.body
537
+ )
538
+ ] });
539
+ }
540
+ function createImagePlugin() {
541
+ return {
542
+ name: "image",
543
+ components: {
544
+ img: ZoomableImage
545
+ },
546
+ // Allow inline (`data:`) and object-URL (`blob:`) image sources in addition
547
+ // to the default http/https, so pasted/attached images render. These are
548
+ // safe for `<img>` (scripts in SVG loaded via `src` do not execute).
549
+ sanitizeSchema: {
550
+ protocols: { src: ["data", "blob"] }
551
+ }
552
+ };
553
+ }
554
+ var NitroTableGrid = React2.lazy(() => import('../../../nitroTableGrid-FWRCDE4N.js'));
555
+ function textOf3(node) {
556
+ if (node.type === "text") return node.value ?? "";
557
+ return (node.children ?? []).map(textOf3).join("");
558
+ }
559
+ function rowsOf(parent) {
560
+ return (parent?.children ?? []).filter((c) => c.tagName === "tr");
561
+ }
562
+ function cellsOf(row, tag) {
563
+ return (row.children ?? []).filter((c) => c.tagName === tag);
564
+ }
565
+ function parseTable(node) {
566
+ if (!node || node.tagName !== "table") return null;
567
+ const thead = node.children?.find((c) => c.tagName === "thead");
568
+ const tbody = node.children?.find((c) => c.tagName === "tbody");
569
+ const headerRow = rowsOf(thead)[0];
570
+ if (!headerRow) return null;
571
+ const headers = cellsOf(headerRow, "th").map((c) => textOf3(c).trim());
572
+ if (headers.length === 0) return null;
573
+ const seen = /* @__PURE__ */ new Map();
574
+ const keys = headers.map((h) => {
575
+ const base = h || "column";
576
+ const count = seen.get(base) ?? 0;
577
+ seen.set(base, count + 1);
578
+ return count === 0 ? base : `${base} (${count + 1})`;
579
+ });
580
+ const rows = rowsOf(tbody).map((tr) => {
581
+ const cells = cellsOf(tr, "td");
582
+ const row = {};
583
+ keys.forEach((key, i) => {
584
+ row[key] = cells[i] ? textOf3(cells[i]).trim() : "";
585
+ });
586
+ return row;
587
+ });
588
+ return { headers: keys, rows };
589
+ }
590
+ function HtmlTable({
591
+ node: _node,
592
+ ...props
593
+ }) {
594
+ return /* @__PURE__ */ jsx("div", { className: "my-2 overflow-x-auto", children: /* @__PURE__ */ jsx(
595
+ "table",
596
+ {
597
+ ...props,
598
+ className: cn(
599
+ "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",
600
+ props.className
601
+ )
602
+ }
603
+ ) });
604
+ }
605
+ var GridErrorBoundary = class extends React2.Component {
606
+ state = { failed: false };
607
+ static getDerivedStateFromError() {
608
+ return { failed: true };
609
+ }
610
+ render() {
611
+ if (this.state.failed) return this.props.fallback;
612
+ return this.props.children;
613
+ }
614
+ };
615
+ function createNitroTablePlugin() {
616
+ const NitroTable = (props) => {
617
+ const node = props.node;
618
+ const fallback = /* @__PURE__ */ jsx(
619
+ HtmlTable,
620
+ {
621
+ ...props
622
+ }
623
+ );
624
+ const parsed = React2.useMemo(() => parseTable(node), [node]);
625
+ if (!parsed) return fallback;
626
+ return /* @__PURE__ */ jsx(GridErrorBoundary, { fallback, children: /* @__PURE__ */ jsx(React2.Suspense, { fallback, children: /* @__PURE__ */ jsx(NitroTableGrid, { headers: parsed.headers, rows: parsed.rows }) }) });
627
+ };
628
+ return {
629
+ name: "nitro-table",
630
+ components: {
631
+ table: NitroTable
632
+ }
633
+ };
634
+ }
635
+
636
+ // src/components/SuperChat/render/attachmentCache.ts
637
+ var DB_NAME = "mieweb-superchat";
638
+ var STORE = "attachments";
639
+ var META_STORE = "attachments_meta";
640
+ var DB_VERSION = 2;
641
+ var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
642
+ var maxBytes = DEFAULT_MAX_BYTES;
643
+ function hasIndexedDB() {
644
+ return typeof window !== "undefined" && !!window.indexedDB;
645
+ }
646
+ function openDB() {
647
+ if (!hasIndexedDB()) return Promise.resolve(null);
648
+ return new Promise((resolve) => {
649
+ let req;
650
+ try {
651
+ req = window.indexedDB.open(DB_NAME, DB_VERSION);
652
+ } catch {
653
+ resolve(null);
654
+ return;
655
+ }
656
+ req.onupgradeneeded = () => {
657
+ const db = req.result;
658
+ if (!db.objectStoreNames.contains(STORE)) {
659
+ db.createObjectStore(STORE, { keyPath: "id" });
660
+ }
661
+ if (!db.objectStoreNames.contains(META_STORE)) {
662
+ db.createObjectStore(META_STORE, { keyPath: "id" });
663
+ }
664
+ };
665
+ req.onsuccess = () => resolve(req.result);
666
+ req.onerror = () => resolve(null);
667
+ });
668
+ }
669
+ function tx(db, mode) {
670
+ return db.transaction(STORE, mode).objectStore(STORE);
671
+ }
672
+ function readAllMeta(db) {
673
+ return new Promise((resolve) => {
674
+ try {
675
+ const req = db.transaction(META_STORE, "readonly").objectStore(META_STORE).getAll();
676
+ req.onsuccess = () => resolve(req.result ?? []);
677
+ req.onerror = () => resolve([]);
678
+ } catch {
679
+ resolve([]);
680
+ }
681
+ });
682
+ }
683
+ async function touch(id) {
684
+ const db = await openDB();
685
+ if (!db) return;
686
+ await new Promise((resolve) => {
687
+ try {
688
+ const store = db.transaction(META_STORE, "readwrite").objectStore(META_STORE);
689
+ const getReq = store.get(id);
690
+ getReq.onsuccess = () => {
691
+ const meta = getReq.result;
692
+ if (meta) {
693
+ meta.lastAccessed = Date.now();
694
+ store.put(meta);
695
+ }
696
+ resolve();
697
+ };
698
+ getReq.onerror = () => resolve();
699
+ } catch {
700
+ resolve();
701
+ } finally {
702
+ db.close();
703
+ }
704
+ });
705
+ }
706
+ async function prune() {
707
+ const db = await openDB();
708
+ if (!db) return;
709
+ try {
710
+ const metas = await readAllMeta(db);
711
+ let total = metas.reduce((sum, m) => sum + (m.size || 0), 0);
712
+ if (total <= maxBytes) return;
713
+ const byAge = metas.slice().sort((a, b) => (a.lastAccessed || 0) - (b.lastAccessed || 0));
714
+ const victims = [];
715
+ for (const meta of byAge) {
716
+ if (total <= maxBytes) break;
717
+ if (metas.length - victims.length <= 1) break;
718
+ victims.push(meta.id);
719
+ total -= meta.size || 0;
720
+ }
721
+ if (!victims.length) return;
722
+ await new Promise((resolve) => {
723
+ try {
724
+ const t = db.transaction([STORE, META_STORE], "readwrite");
725
+ const blobs = t.objectStore(STORE);
726
+ const meta = t.objectStore(META_STORE);
727
+ for (const id of victims) {
728
+ blobs.delete(id);
729
+ meta.delete(id);
730
+ }
731
+ t.oncomplete = () => resolve();
732
+ t.onerror = () => resolve();
733
+ } catch {
734
+ resolve();
735
+ }
736
+ });
737
+ } finally {
738
+ db.close();
739
+ }
740
+ }
741
+ function dataUrlToBlob(dataUrl) {
742
+ const match = /^data:([^;,]*)(;base64)?,([\s\S]*)$/.exec(dataUrl);
743
+ if (!match) return null;
744
+ const mime = match[1] || "application/octet-stream";
745
+ const isBase64 = Boolean(match[2]);
746
+ const data = match[3];
747
+ try {
748
+ if (isBase64) {
749
+ const binary = window.atob(data);
750
+ const bytes = new Uint8Array(binary.length);
751
+ for (let i = 0; i < binary.length; i += 1) {
752
+ bytes[i] = binary.charCodeAt(i);
753
+ }
754
+ return new window.Blob([bytes], { type: mime });
755
+ }
756
+ return new window.Blob([decodeURIComponent(data)], { type: mime });
757
+ } catch {
758
+ return null;
759
+ }
760
+ }
761
+ var attachmentCache = {
762
+ /** Whether a persistent IndexedDB store is available in this environment. */
763
+ isAvailable: hasIndexedDB,
764
+ /**
765
+ * Tune the cache. `maxBytes` is the eviction budget (default 100 MB); when a
766
+ * `put` pushes the total past it, least-recently-used entries are dropped
767
+ * until it fits. Set `Infinity` to disable eviction.
768
+ */
769
+ configure(options) {
770
+ if (typeof options.maxBytes === "number" && options.maxBytes >= 0) {
771
+ maxBytes = options.maxBytes;
772
+ }
773
+ },
774
+ /** Total bytes currently held by the cache (0 when unavailable). */
775
+ async usage() {
776
+ const db = await openDB();
777
+ if (!db) return 0;
778
+ try {
779
+ const metas = await readAllMeta(db);
780
+ return metas.reduce((sum, m) => sum + (m.size || 0), 0);
781
+ } finally {
782
+ db.close();
783
+ }
784
+ },
785
+ /**
786
+ * Persist an attachment's bytes by id. Pass a `blob` or a `dataUrl`. Resolves
787
+ * to `true` when stored, `false` if unavailable or the input was unusable.
788
+ * Writing past the configured `maxBytes` evicts least-recently-used entries.
789
+ */
790
+ async put(input) {
791
+ const db = await openDB();
792
+ if (!db) return false;
793
+ const blob = input.blob ?? (input.dataUrl ? dataUrlToBlob(input.dataUrl) : null);
794
+ if (!blob) {
795
+ db.close();
796
+ return false;
797
+ }
798
+ const now = Date.now();
799
+ const entry = {
800
+ id: input.id,
801
+ name: input.name,
802
+ type: input.type || blob.type || "application/octet-stream",
803
+ blob,
804
+ size: blob.size,
805
+ cachedAt: now,
806
+ lastAccessed: now
807
+ };
808
+ const stored = await new Promise((resolve) => {
809
+ try {
810
+ const t = db.transaction([STORE, META_STORE], "readwrite");
811
+ t.objectStore(STORE).put(entry);
812
+ t.objectStore(META_STORE).put({
813
+ id: entry.id,
814
+ size: entry.size,
815
+ lastAccessed: now
816
+ });
817
+ t.oncomplete = () => resolve(true);
818
+ t.onerror = () => resolve(false);
819
+ } catch {
820
+ resolve(false);
821
+ } finally {
822
+ db.close();
823
+ }
824
+ });
825
+ if (stored) await prune();
826
+ return stored;
827
+ },
828
+ /** Read a cached attachment (blob + metadata) by id, or `undefined`. */
829
+ async get(id) {
830
+ const db = await openDB();
831
+ if (!db) return void 0;
832
+ const entry = await new Promise((resolve) => {
833
+ try {
834
+ const req = tx(db, "readonly").get(id);
835
+ req.onsuccess = () => resolve(req.result ?? void 0);
836
+ req.onerror = () => resolve(void 0);
837
+ } catch {
838
+ resolve(void 0);
839
+ } finally {
840
+ db.close();
841
+ }
842
+ });
843
+ if (entry) void touch(id);
844
+ return entry;
845
+ },
846
+ /**
847
+ * Resolve a fresh `blob:` object URL for a cached attachment, or `undefined`
848
+ * if it isn't cached. **The caller owns the URL** and must
849
+ * `URL.revokeObjectURL` it when done (the {@link useAttachmentUrl} hook does
850
+ * this automatically).
851
+ */
852
+ async getObjectURL(id) {
853
+ const entry = await attachmentCache.get(id);
854
+ if (!entry) return void 0;
855
+ try {
856
+ return window.URL.createObjectURL(entry.blob);
857
+ } catch {
858
+ return void 0;
859
+ }
860
+ },
861
+ /** Remove a cached attachment by id. */
862
+ async delete(id) {
863
+ const db = await openDB();
864
+ if (!db) return;
865
+ await new Promise((resolve) => {
866
+ try {
867
+ const t = db.transaction([STORE, META_STORE], "readwrite");
868
+ t.objectStore(STORE).delete(id);
869
+ t.objectStore(META_STORE).delete(id);
870
+ t.oncomplete = () => resolve();
871
+ t.onerror = () => resolve();
872
+ } catch {
873
+ resolve();
874
+ } finally {
875
+ db.close();
876
+ }
877
+ });
878
+ },
879
+ /** Drop every cached attachment. */
880
+ async clear() {
881
+ const db = await openDB();
882
+ if (!db) return;
883
+ await new Promise((resolve) => {
884
+ try {
885
+ const t = db.transaction([STORE, META_STORE], "readwrite");
886
+ t.objectStore(STORE).clear();
887
+ t.objectStore(META_STORE).clear();
888
+ t.oncomplete = () => resolve();
889
+ t.onerror = () => resolve();
890
+ } catch {
891
+ resolve();
892
+ } finally {
893
+ db.close();
894
+ }
895
+ });
896
+ }
897
+ };
898
+ var ATTACHMENT_TAG = "superchat-attachment";
899
+ var ATTACHMENT_FENCE = "superchat-attachment";
900
+ function attachmentMarkdown(payload) {
901
+ return ["```superchat-attachment", JSON.stringify(payload), "```"].join("\n");
902
+ }
903
+ function useAttachmentUrl(id, fallbackSrc) {
904
+ const [url, setUrl] = React2.useState(void 0);
905
+ const [status, setStatus] = React2.useState("idle");
906
+ React2.useEffect(() => {
907
+ let active = true;
908
+ let created;
909
+ if (!id && !fallbackSrc) {
910
+ setUrl(void 0);
911
+ setStatus("idle");
912
+ return;
913
+ }
914
+ setStatus("loading");
915
+ void (async () => {
916
+ if (id) {
917
+ const cached = await attachmentCache.getObjectURL(id);
918
+ if (!active) {
919
+ if (cached) window.URL.revokeObjectURL(cached);
920
+ return;
921
+ }
922
+ if (cached) {
923
+ created = cached;
924
+ setUrl(cached);
925
+ setStatus("ready");
926
+ return;
927
+ }
928
+ }
929
+ if (!active) return;
930
+ setUrl(fallbackSrc);
931
+ setStatus(fallbackSrc ? "ready" : "missing");
932
+ })();
933
+ return () => {
934
+ active = false;
935
+ if (created) window.URL.revokeObjectURL(created);
936
+ };
937
+ }, [id, fallbackSrc]);
938
+ return { url, status };
939
+ }
940
+ var ICON_PROPS = {
941
+ width: 20,
942
+ height: 20,
943
+ viewBox: "0 0 24 24",
944
+ fill: "none",
945
+ stroke: "currentColor",
946
+ strokeWidth: 1.75,
947
+ strokeLinecap: "round",
948
+ strokeLinejoin: "round",
949
+ "aria-hidden": true
950
+ };
951
+ function FileGlyph() {
952
+ return /* @__PURE__ */ jsxs("svg", { ...ICON_PROPS, children: [
953
+ /* @__PURE__ */ jsx("path", { d: "M14 3v4a1 1 0 0 0 1 1h4" }),
954
+ /* @__PURE__ */ jsx("path", { d: "M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2Z" })
955
+ ] });
956
+ }
957
+ function DownloadGlyph() {
958
+ return /* @__PURE__ */ jsxs("svg", { ...ICON_PROPS, width: 16, height: 16, children: [
959
+ /* @__PURE__ */ jsx("path", { d: "M12 3v12" }),
960
+ /* @__PURE__ */ jsx("path", { d: "m7 12 5 5 5-5" }),
961
+ /* @__PURE__ */ jsx("path", { d: "M5 21h14" })
962
+ ] });
963
+ }
964
+ function kindOf(type) {
965
+ if (type.startsWith("image/")) return "image";
966
+ if (type.startsWith("video/")) return "video";
967
+ if (type.startsWith("audio/")) return "audio";
968
+ if (type === "application/pdf") return "pdf";
969
+ return "file";
970
+ }
971
+ function FileChip({
972
+ name,
973
+ url,
974
+ unavailable
975
+ }) {
976
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
977
+ /* @__PURE__ */ jsx("span", { className: "text-neutral-500 dark:text-neutral-400", children: /* @__PURE__ */ jsx(FileGlyph, {}) }),
978
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: name }),
979
+ url && !unavailable ? /* @__PURE__ */ jsx("span", { className: "text-neutral-400", children: /* @__PURE__ */ jsx(DownloadGlyph, {}) }) : null
980
+ ] });
981
+ const className = cn(
982
+ "my-2 flex max-w-sm items-center gap-2 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2 dark:border-neutral-700 dark:bg-neutral-800",
983
+ unavailable && "opacity-60"
984
+ );
985
+ if (url && !unavailable) {
986
+ return /* @__PURE__ */ jsx(
987
+ "a",
988
+ {
989
+ href: url,
990
+ download: name,
991
+ target: "_blank",
992
+ rel: "noopener noreferrer",
993
+ title: name,
994
+ className: cn(
995
+ className,
996
+ "hover:border-neutral-300 hover:bg-neutral-100 dark:hover:border-neutral-600 dark:hover:bg-neutral-700"
997
+ ),
998
+ children: content
999
+ }
1000
+ );
1001
+ }
1002
+ return /* @__PURE__ */ jsxs("div", { className, title: name, children: [
1003
+ content,
1004
+ unavailable ? /* @__PURE__ */ jsx("span", { className: "text-xs whitespace-nowrap text-neutral-400", children: "unavailable offline" }) : null
1005
+ ] });
1006
+ }
1007
+ function AttachmentBlock({ payload }) {
1008
+ const { id, type, name, src } = payload;
1009
+ const { url, status } = useAttachmentUrl(id || void 0, src);
1010
+ const kind = kindOf(type);
1011
+ React2.useEffect(() => {
1012
+ if (!id || !src || !attachmentCache.isAvailable()) return;
1013
+ let active = true;
1014
+ void attachmentCache.get(id).then((existing) => {
1015
+ if (active && !existing) {
1016
+ void attachmentCache.put({ id, name, type, dataUrl: src });
1017
+ }
1018
+ });
1019
+ return () => {
1020
+ active = false;
1021
+ };
1022
+ }, [id, src, name, type]);
1023
+ if (status === "loading") {
1024
+ return /* @__PURE__ */ jsx(
1025
+ "div",
1026
+ {
1027
+ "data-slot": "superchat-attachment",
1028
+ className: "my-2 h-10 max-w-sm animate-pulse rounded-lg bg-neutral-100 dark:bg-neutral-800",
1029
+ "aria-label": `Loading ${name}`
1030
+ }
1031
+ );
1032
+ }
1033
+ if (!url) {
1034
+ return /* @__PURE__ */ jsx("div", { "data-slot": "superchat-attachment", children: /* @__PURE__ */ jsx(FileChip, { name, unavailable: true }) });
1035
+ }
1036
+ let body;
1037
+ switch (kind) {
1038
+ case "image":
1039
+ body = /* @__PURE__ */ jsx(
1040
+ "img",
1041
+ {
1042
+ src: url,
1043
+ alt: name,
1044
+ className: "max-h-80 max-w-full rounded-lg object-contain"
1045
+ }
1046
+ );
1047
+ break;
1048
+ case "video":
1049
+ body = /* @__PURE__ */ jsx(
1050
+ "video",
1051
+ {
1052
+ src: url,
1053
+ controls: true,
1054
+ className: "max-h-80 max-w-full rounded-lg bg-black",
1055
+ children: /* @__PURE__ */ jsx("track", { kind: "captions" })
1056
+ }
1057
+ );
1058
+ break;
1059
+ case "audio":
1060
+ body = /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
1061
+ /* @__PURE__ */ jsx("div", { className: "mb-1 truncate text-xs text-neutral-500 dark:text-neutral-400", children: name }),
1062
+ /* @__PURE__ */ jsx("audio", { src: url, controls: true, className: "w-full", children: /* @__PURE__ */ jsx("track", { kind: "captions" }) })
1063
+ ] });
1064
+ break;
1065
+ case "pdf":
1066
+ body = /* @__PURE__ */ jsxs("div", { className: "max-w-2xl overflow-hidden rounded-lg border border-neutral-200 dark:border-neutral-700", children: [
1067
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-neutral-200 bg-neutral-50 px-3 py-1.5 dark:border-neutral-700 dark:bg-neutral-800", children: [
1068
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm", title: name, children: name }),
1069
+ /* @__PURE__ */ jsxs(
1070
+ "a",
1071
+ {
1072
+ href: url,
1073
+ download: name,
1074
+ target: "_blank",
1075
+ rel: "noopener noreferrer",
1076
+ className: "text-primary-700 hover:text-primary-800 dark:text-primary-300 inline-flex items-center gap-1 text-xs whitespace-nowrap",
1077
+ children: [
1078
+ /* @__PURE__ */ jsx(DownloadGlyph, {}),
1079
+ "Open"
1080
+ ]
1081
+ }
1082
+ )
1083
+ ] }),
1084
+ /* @__PURE__ */ jsx("iframe", { src: url, title: name, className: "h-96 w-full bg-white" })
1085
+ ] });
1086
+ break;
1087
+ default:
1088
+ body = /* @__PURE__ */ jsx(FileChip, { name, url });
1089
+ }
1090
+ return /* @__PURE__ */ jsx("div", { "data-slot": "superchat-attachment", children: body });
1091
+ }
1092
+ function textOf4(node) {
1093
+ if (node.type === "text") return node.value ?? "";
1094
+ return (node.children ?? []).map(textOf4).join("");
1095
+ }
1096
+ function isAttachmentPre(node) {
1097
+ if (node.tagName !== "pre") return false;
1098
+ const code = node.children?.find((c) => c.tagName === "code");
1099
+ const className = code?.properties?.className;
1100
+ const classes = Array.isArray(className) ? className : [className];
1101
+ return classes.some(
1102
+ (c) => typeof c === "string" && (c === `language-${ATTACHMENT_FENCE}` || c === ATTACHMENT_FENCE)
1103
+ );
1104
+ }
1105
+ function rehypeAttachment() {
1106
+ return (tree) => {
1107
+ const walk = (node) => {
1108
+ if (!node.children) return;
1109
+ node.children = node.children.map((child) => {
1110
+ if (isAttachmentPre(child)) {
1111
+ const code = child.children?.find((c) => c.tagName === "code");
1112
+ const raw = code ? textOf4(code) : "";
1113
+ return {
1114
+ type: "element",
1115
+ tagName: ATTACHMENT_TAG,
1116
+ properties: {},
1117
+ children: [{ type: "text", value: raw }]
1118
+ };
1119
+ }
1120
+ walk(child);
1121
+ return child;
1122
+ });
1123
+ };
1124
+ walk(tree);
1125
+ };
1126
+ }
1127
+ function childText(children) {
1128
+ return React2.Children.toArray(children).map((c) => typeof c === "string" ? c : "").join("");
1129
+ }
1130
+ function sanitizeSrc(src) {
1131
+ if (!src) return void 0;
1132
+ if (/^(?:blob:|https?:\/\/)/i.test(src)) return src;
1133
+ const dataMatch = /^data:([\w.+-]+\/[\w.+-]+)?[;,]/i.exec(src);
1134
+ if (dataMatch) {
1135
+ const mime = (dataMatch[1] ?? "text/plain").toLowerCase();
1136
+ const EXECUTABLE_MIME = /^(?:text\/html|application\/xhtml\+xml|image\/svg\+xml|text\/xml|application\/xml)$/;
1137
+ return EXECUTABLE_MIME.test(mime) ? void 0 : src;
1138
+ }
1139
+ return void 0;
1140
+ }
1141
+ function parsePayload2(raw) {
1142
+ try {
1143
+ const parsed = JSON.parse(raw);
1144
+ if (!parsed || typeof parsed.type !== "string") return null;
1145
+ const src = typeof parsed.src === "string" ? sanitizeSrc(parsed.src) : void 0;
1146
+ if (!parsed.id && !src) return null;
1147
+ return {
1148
+ id: typeof parsed.id === "string" ? parsed.id : void 0,
1149
+ type: parsed.type,
1150
+ name: typeof parsed.name === "string" ? parsed.name : "attachment",
1151
+ src
1152
+ };
1153
+ } catch {
1154
+ return null;
1155
+ }
1156
+ }
1157
+ function AttachmentElement({
1158
+ node: _node,
1159
+ children
1160
+ }) {
1161
+ const payload = parsePayload2(childText(children));
1162
+ if (!payload) return null;
1163
+ return /* @__PURE__ */ jsx(AttachmentBlock, { payload });
1164
+ }
1165
+ function createAttachmentPlugin() {
1166
+ return {
1167
+ name: "attachment",
1168
+ rehypePlugins: [rehypeAttachment],
1169
+ components: {
1170
+ [ATTACHMENT_TAG]: AttachmentElement
1171
+ },
1172
+ // The payload rides as a text child; only the custom tag needs allow-listing.
1173
+ sanitizeSchema: {
1174
+ tagNames: [ATTACHMENT_TAG]
1175
+ }
1176
+ };
1177
+ }
1178
+
1179
+ export { ATTACHMENT_FENCE, ATTACHMENT_TAG, GENUI_TAG, MERMAID_TAG, attachmentCache, attachmentMarkdown, createAttachmentPlugin, createCodePlugin, createGenUIPlugin, createImagePlugin, createMathPlugin, createMermaidPlugin, createNitroTablePlugin, useAttachmentUrl };
1180
+ //# sourceMappingURL=index.js.map
1181
+ //# sourceMappingURL=index.js.map