@smartspace/chat-ui 1.14.0-main.ff8a2ab → 1.14.2-main.10a17d6

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.
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { createPortal } from 'react-dom';
7
7
  import { useQuery, queryOptions, useQueryClient, useMutation, skipToken } from '@tanstack/react-query';
8
8
  import { toast } from 'sonner';
9
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
- import { Editor, rootCtx, defaultValueCtx, editorViewOptionsCtx, editorViewCtx, serializerCtx, SchemaReady, nodeViewCtx, markViewCtx, schemaCtx, prosePluginsCtx, nodesCtx } from '@milkdown/core';
10
+ import { Editor, rootCtx, defaultValueCtx, editorViewOptionsCtx, editorViewCtx, serializerCtx, parserCtx, SchemaReady, nodeViewCtx, markViewCtx, schemaCtx, prosePluginsCtx, nodesCtx } from '@milkdown/core';
11
11
  import { history } from '@milkdown/kit/plugin/history';
12
12
  import { clipboard } from '@milkdown/plugin-clipboard';
13
13
  import { listenerCtx, listener } from '@milkdown/plugin-listener';
@@ -424,6 +424,29 @@ var fileTag = $node("fileTag", () => ({
424
424
  var MAX_IFRAME_HEIGHT = 5e3;
425
425
  var HEIGHT_MESSAGE = "ss-html-preview-height";
426
426
  var ERROR_MESSAGE = "ss-html-preview-error";
427
+ var SNAPSHOT_REQUEST = "ss-html-preview-snapshot";
428
+ var SNAPSHOT_RESULT = "ss-html-preview-snapshot-result";
429
+ var SS_MARKDOWN_CLIPBOARD_TYPE = "web text/markdown";
430
+ var MARKDOWN_MARKER_PREFIX = "<!--ss-md:";
431
+ var MARKDOWN_MARKER_SUFFIX = "-->";
432
+ var MARKDOWN_MARKER_RE = /<!--ss-md:([A-Za-z0-9+/=]+)-->/;
433
+ function encodeMarkdownMarker(markdown) {
434
+ try {
435
+ const b64 = btoa(unescape(encodeURIComponent(markdown)));
436
+ return `${MARKDOWN_MARKER_PREFIX}${b64}${MARKDOWN_MARKER_SUFFIX}`;
437
+ } catch {
438
+ return "";
439
+ }
440
+ }
441
+ function extractMarkdownMarker(html4) {
442
+ const match = html4.match(MARKDOWN_MARKER_RE);
443
+ if (!match) return null;
444
+ try {
445
+ return decodeURIComponent(escape(atob(match[1])));
446
+ } catch {
447
+ return null;
448
+ }
449
+ }
427
450
  var HEIGHT_REPORTER_SCRIPT = `
428
451
  <script>(function(){
429
452
  try {
@@ -448,6 +471,48 @@ var HEIGHT_REPORTER_SCRIPT = `
448
471
  window.addEventListener('unhandledrejection', function(ev){
449
472
  reportError(ev && ev.reason && ev.reason.message);
450
473
  });
474
+ // Snapshot responder. The parent can't read this sandboxed (no
475
+ // allow-same-origin) document, so when it asks for a snapshot we rasterize
476
+ // our own <canvas> elements via toDataURL and post just the PNG strings
477
+ // back out \u2014 strings cross the sandbox boundary fine. Used by the message
478
+ // "smart copy" so client-side charts (Chart.js etc.) paste into Word as
479
+ // images instead of dead <canvas>/<script> source.
480
+ window.addEventListener('message', function(ev){
481
+ var d = ev && ev.data;
482
+ if (!d || d.type !== ${JSON.stringify(SNAPSHOT_REQUEST)}) return;
483
+ var images = [];
484
+ try {
485
+ var canvases = document.querySelectorAll('canvas');
486
+ for (var i = 0; i < canvases.length; i++) {
487
+ var c = canvases[i];
488
+ // Skip zero-area canvases (off-screen / not yet drawn).
489
+ if (!c.width || !c.height) continue;
490
+ try {
491
+ var rect = c.getBoundingClientRect();
492
+ images.push({
493
+ dataUrl: c.toDataURL('image/png'),
494
+ width: c.width,
495
+ height: c.height,
496
+ // On-screen CSS size, independent of devicePixelRatio scaling, so
497
+ // the pasted <img> renders at the chart's intended size rather than
498
+ // 2x on a retina display. Falls back to the pixel size.
499
+ cssWidth: Math.round(rect.width) || c.width,
500
+ cssHeight: Math.round(rect.height) || c.height
501
+ });
502
+ } catch (e) {
503
+ // Tainted canvas (cross-origin image drawn without CORS) \u2014 skip it;
504
+ // the host falls back to inlining the static HTML source.
505
+ }
506
+ }
507
+ } catch (e) {}
508
+ try {
509
+ parent.postMessage({
510
+ type: ${JSON.stringify(SNAPSHOT_RESULT)},
511
+ id: d.id,
512
+ images: images
513
+ }, '*');
514
+ } catch (e) {}
515
+ });
451
516
  var lastSent = -1;
452
517
  function send(){
453
518
  try {
@@ -533,6 +598,38 @@ async function copyText(text6) {
533
598
  return false;
534
599
  }
535
600
  }
601
+ var snapshotSeq = 0;
602
+ function snapshotIframe(iframe, timeoutMs = 1500) {
603
+ return new Promise((resolve) => {
604
+ const win = iframe.contentWindow;
605
+ if (!win || typeof window === "undefined") {
606
+ resolve([]);
607
+ return;
608
+ }
609
+ const id = `snap-${++snapshotSeq}`;
610
+ let settled = false;
611
+ const finish = (images) => {
612
+ if (settled) return;
613
+ settled = true;
614
+ window.removeEventListener("message", onMessage);
615
+ clearTimeout(timer);
616
+ resolve(images);
617
+ };
618
+ const onMessage = (event) => {
619
+ if (event.source !== win) return;
620
+ const data = event.data;
621
+ if (!data || data.type !== SNAPSHOT_RESULT || data.id !== id) return;
622
+ finish(Array.isArray(data.images) ? data.images : []);
623
+ };
624
+ const timer = setTimeout(() => finish([]), timeoutMs);
625
+ window.addEventListener("message", onMessage);
626
+ try {
627
+ win.postMessage({ type: SNAPSHOT_REQUEST, id }, "*");
628
+ } catch {
629
+ finish([]);
630
+ }
631
+ });
632
+ }
536
633
 
537
634
  // src/shared/markdown/extensions/htmlPreview.ts
538
635
  var PREVIEW_LANGUAGES = /* @__PURE__ */ new Set(["html"]);
@@ -756,7 +853,7 @@ var mention = $node("mention", () => ({
756
853
  match: (node2) => node2.type.name === "mention",
757
854
  runner: (state, node2) => {
758
855
  const { id, label } = node2.attrs;
759
- state.addNode("mention", void 0, `${id}|${label}`);
856
+ state.addNode("text", void 0, label || `@${id}`);
760
857
  }
761
858
  }
762
859
  }));
@@ -982,6 +1079,7 @@ function EditorInner({
982
1079
  const [_isDragging, setIsDragging] = useState(false);
983
1080
  const viewRef = useRef(null);
984
1081
  const serializerRef = useRef(null);
1082
+ const parserRef = useRef(null);
985
1083
  function guessImageExt(mime) {
986
1084
  const t = (mime || "").toLowerCase();
987
1085
  if (t === "image/jpeg") return "jpg";
@@ -1100,6 +1198,10 @@ function EditorInner({
1100
1198
  serializerRef.current = anyCtx.get(serializerCtx);
1101
1199
  } catch {
1102
1200
  }
1201
+ try {
1202
+ parserRef.current = anyCtx.get(parserCtx);
1203
+ } catch {
1204
+ }
1103
1205
  const handle2 = () => {
1104
1206
  try {
1105
1207
  if (enableMentions) updateMentionFromView();
@@ -1189,6 +1291,19 @@ function EditorInner({
1189
1291
  const tr = view.state.tr.replaceWith(from, to, node2);
1190
1292
  view.dispatch(tr.scrollIntoView());
1191
1293
  }
1294
+ function insertMarkdownAtSelection(view, markdown) {
1295
+ const parser = parserRef.current;
1296
+ if (!parser) return false;
1297
+ try {
1298
+ const doc = parser(markdown);
1299
+ if (!doc) return false;
1300
+ const slice = new Slice(doc.content, 0, 0);
1301
+ view.dispatch(view.state.tr.replaceSelection(slice).scrollIntoView());
1302
+ return true;
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ }
1192
1307
  async function insertUploadedFilesIntoEditor(files) {
1193
1308
  const view = viewRef.current;
1194
1309
  if (!isEditable || !view) return false;
@@ -1461,6 +1576,17 @@ function EditorInner({
1461
1576
  },
1462
1577
  onPasteCapture: (e) => {
1463
1578
  try {
1579
+ const view = viewRef.current;
1580
+ if (isEditable && view) {
1581
+ const ssMarkdown = extractMarkdownMarker(
1582
+ e.clipboardData?.getData("text/html") ?? ""
1583
+ ) ?? e.clipboardData?.getData(SS_MARKDOWN_CLIPBOARD_TYPE);
1584
+ if (ssMarkdown && insertMarkdownAtSelection(view, ssMarkdown)) {
1585
+ e.preventDefault();
1586
+ e.stopPropagation();
1587
+ return;
1588
+ }
1589
+ }
1464
1590
  const items = e.clipboardData?.items;
1465
1591
  if (!items) return;
1466
1592
  const imageFiles = [];
@@ -3576,6 +3702,12 @@ var useThreadIsRunning = (workspaceId, threadId) => {
3576
3702
  return !!optimistic || !!(detailThread ?? listThread)?.isFlowRunning;
3577
3703
  };
3578
3704
 
3705
+ // src/shared/utils/randomUUID.ts
3706
+ function randomUUID() {
3707
+ const cryptoObj = globalThis?.crypto;
3708
+ return typeof cryptoObj?.randomUUID === "function" ? cryptoObj.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`;
3709
+ }
3710
+
3579
3711
  // src/domains/messages/enums.ts
3580
3712
  var MessageValueType = /* @__PURE__ */ ((MessageValueType2) => {
3581
3713
  MessageValueType2["OUTPUT"] = "Output";
@@ -3623,10 +3755,10 @@ function useSendMessage() {
3623
3755
  if (!threadId) throw new Error("Thread ID is required");
3624
3756
  if (!workspaceId) throw new Error("Workspace ID is required");
3625
3757
  const optimistic = {
3626
- id: `temp-${crypto.randomUUID()}`,
3758
+ id: `temp-${randomUUID()}`,
3627
3759
  values: [
3628
3760
  {
3629
- id: `temp-${crypto.randomUUID()}-prompt`,
3761
+ id: `temp-${randomUUID()}-prompt`,
3630
3762
  type: "Input" /* INPUT */,
3631
3763
  name: "prompt",
3632
3764
  value: contentList,
@@ -3637,7 +3769,7 @@ function useSendMessage() {
3637
3769
  },
3638
3770
  ...files?.length ? [
3639
3771
  {
3640
- id: `temp-${crypto.randomUUID()}-files`,
3772
+ id: `temp-${randomUUID()}-files`,
3641
3773
  type: "Input" /* INPUT */,
3642
3774
  name: "files",
3643
3775
  value: files,
@@ -3649,7 +3781,7 @@ function useSendMessage() {
3649
3781
  ] : [],
3650
3782
  ...variables && Object.keys(variables).length ? [
3651
3783
  {
3652
- id: `temp-${crypto.randomUUID()}-vars`,
3784
+ id: `temp-${randomUUID()}-vars`,
3653
3785
  type: "Input" /* INPUT */,
3654
3786
  name: "variables",
3655
3787
  value: variables,
@@ -3718,8 +3850,9 @@ function useAddInputToMessage() {
3718
3850
  const { userId, displayName: userName } = useChatIdentity();
3719
3851
  const service = useChatService();
3720
3852
  const addInputToMessageMutation = useMutation({
3721
- mutationFn: async ({ threadId, messageId, name, value, channels }) => {
3722
- if (!threadId) throw new Error("Thread ID is required");
3853
+ onMutate: async ({ threadId, messageId, name, value, channels }) => {
3854
+ await qc.cancelQueries({ queryKey: messagesKeys.list(threadId) });
3855
+ const previousMessages = qc.getQueryData(messagesKeys.list(threadId)) ?? [];
3723
3856
  qc.setQueryData(
3724
3857
  messagesKeys.list(threadId),
3725
3858
  (old = []) => old.map(
@@ -3728,7 +3861,7 @@ function useAddInputToMessage() {
3728
3861
  values: [
3729
3862
  ...m.values ?? [],
3730
3863
  {
3731
- id: `temp-${Date.now()}-add`,
3864
+ id: `temp-${randomUUID()}-add`,
3732
3865
  type: "Input" /* INPUT */,
3733
3866
  name,
3734
3867
  value,
@@ -3741,7 +3874,10 @@ function useAddInputToMessage() {
3741
3874
  } : m
3742
3875
  )
3743
3876
  );
3744
- await qc.cancelQueries({ queryKey: messagesKeys.list(threadId) });
3877
+ return { previousMessages };
3878
+ },
3879
+ mutationFn: async ({ threadId, messageId, name, value, channels }) => {
3880
+ if (!threadId) throw new Error("Thread ID is required");
3745
3881
  return await service.addInputToMessage({
3746
3882
  messageId,
3747
3883
  name,
@@ -3755,11 +3891,13 @@ function useAddInputToMessage() {
3755
3891
  (old = []) => reconcileWithMessage(old, message, "replace")
3756
3892
  );
3757
3893
  },
3758
- onError: (_e, { threadId }) => {
3759
- qc.setQueryData(
3760
- messagesKeys.list(threadId),
3761
- (old = []) => old.filter((m) => !m.optimistic)
3762
- );
3894
+ onError: (_e, { threadId }, context) => {
3895
+ if (context) {
3896
+ qc.setQueryData(
3897
+ messagesKeys.list(threadId),
3898
+ context.previousMessages
3899
+ );
3900
+ }
3763
3901
  toast.error("There was an error posting your form input");
3764
3902
  },
3765
3903
  retry: false
@@ -18708,17 +18846,29 @@ function CodeBlock({ language, source }) {
18708
18846
  /* @__PURE__ */ jsx("pre", { ...language ? { "data-language": language } : {}, children: /* @__PURE__ */ jsx("code", { className: codeClass, children: source }) })
18709
18847
  ] });
18710
18848
  }
18849
+ var STREAM_SETTLE_MS = 250;
18711
18850
  function HtmlPreview({ source }) {
18712
18851
  const [showingPreview, setShowingPreview] = useState(true);
18713
18852
  const [copyLabel, setCopyLabel] = useState(
18714
18853
  "Copy"
18715
18854
  );
18716
18855
  const [iframeHeight, setIframeHeight] = useState(null);
18856
+ const [settledSource, setSettledSource] = useState(source);
18857
+ const [measured, setMeasured] = useState(false);
18858
+ const loading = showingPreview && (source !== settledSource || !measured);
18717
18859
  const iframeRef = useRef(null);
18718
18860
  const rafIdRef = useRef(null);
18719
18861
  const pendingHeightRef = useRef(null);
18720
18862
  const copyResetTimerRef = useRef(null);
18721
- const srcdoc = injectHeightReporter(source);
18863
+ const srcdoc = injectHeightReporter(settledSource);
18864
+ useEffect(() => {
18865
+ if (source === settledSource) return;
18866
+ const id = setTimeout(() => setSettledSource(source), STREAM_SETTLE_MS);
18867
+ return () => clearTimeout(id);
18868
+ }, [source, settledSource]);
18869
+ useEffect(() => {
18870
+ setMeasured(false);
18871
+ }, [settledSource]);
18722
18872
  useEffect(() => {
18723
18873
  ensureGlobalListener();
18724
18874
  }, []);
@@ -18733,6 +18883,7 @@ function HtmlPreview({ source }) {
18733
18883
  pendingHeightRef.current = null;
18734
18884
  if (next2 <= 0) return;
18735
18885
  setIframeHeight(next2);
18886
+ setMeasured(true);
18736
18887
  };
18737
18888
  const scheduleHeight = (height) => {
18738
18889
  pendingHeightRef.current = height;
@@ -18824,19 +18975,35 @@ function HtmlPreview({ source }) {
18824
18975
  )
18825
18976
  ] })
18826
18977
  ] }),
18827
- /* @__PURE__ */ jsx(
18828
- "iframe",
18978
+ /* @__PURE__ */ jsxs(
18979
+ "div",
18829
18980
  {
18830
- ref: iframeRef,
18831
- className: "ss-code-block__iframe",
18832
- sandbox: "allow-scripts",
18833
- loading: "lazy",
18834
- title: "HTML preview",
18835
- srcDoc: srcdoc,
18981
+ className: "ss-code-block__preview",
18836
18982
  style: {
18837
18983
  display: showingPreview ? "block" : "none",
18838
- ...iframeHeight != null ? { height: `${iframeHeight}px` } : {}
18839
- }
18984
+ ...loading ? { minHeight: 180 } : {}
18985
+ },
18986
+ children: [
18987
+ loading && /* @__PURE__ */ jsxs("div", { className: "ss-code-block__loading", children: [
18988
+ /* @__PURE__ */ jsx("div", { className: "ss-code-block__spinner" }),
18989
+ /* @__PURE__ */ jsx("span", { children: "Rendering preview\u2026" })
18990
+ ] }),
18991
+ /* @__PURE__ */ jsx(
18992
+ "iframe",
18993
+ {
18994
+ ref: iframeRef,
18995
+ className: "ss-code-block__iframe",
18996
+ sandbox: "allow-scripts",
18997
+ loading: "lazy",
18998
+ title: "HTML preview",
18999
+ srcDoc: srcdoc,
19000
+ style: {
19001
+ opacity: loading ? 0 : 1,
19002
+ ...loading ? { position: "absolute", inset: 0, height: "100%" } : iframeHeight != null ? { height: `${iframeHeight}px` } : {}
19003
+ }
19004
+ }
19005
+ )
19006
+ ]
18840
19007
  }
18841
19008
  ),
18842
19009
  /* @__PURE__ */ jsx(
@@ -19275,23 +19442,115 @@ function getUserPhotoUrl(userId) {
19275
19442
  if (!base2) return void 0;
19276
19443
  return `${base2}/users/${userId}/photo`;
19277
19444
  }
19445
+
19446
+ // src/messages/smartCopy.ts
19447
+ async function copyMessageRich(container, markdown) {
19448
+ try {
19449
+ const canRichWrite = typeof ClipboardItem !== "undefined" && !!navigator.clipboard?.write;
19450
+ if (!canRichWrite) return copyText(markdown);
19451
+ const { html: html4, images } = await buildHtmlPayload(container);
19452
+ const htmlWithMarker = `${html4}${encodeMarkdownMarker(markdown)}`;
19453
+ const parts = {
19454
+ "text/html": new Blob([htmlWithMarker], { type: "text/html" }),
19455
+ "text/plain": new Blob([markdown], { type: "text/plain" })
19456
+ };
19457
+ if (images.length === 1) {
19458
+ const pngBlob = await dataUrlToBlob(images[0].dataUrl);
19459
+ if (pngBlob) parts["image/png"] = pngBlob;
19460
+ }
19461
+ const withCustom = {
19462
+ ...parts,
19463
+ [SS_MARKDOWN_CLIPBOARD_TYPE]: new Blob([markdown], {
19464
+ type: SS_MARKDOWN_CLIPBOARD_TYPE
19465
+ })
19466
+ };
19467
+ if (await tryWrite(withCustom)) return true;
19468
+ if (await tryWrite(parts)) return true;
19469
+ return copyText(markdown);
19470
+ } catch {
19471
+ return copyText(markdown);
19472
+ }
19473
+ }
19474
+ async function tryWrite(parts) {
19475
+ try {
19476
+ await navigator.clipboard.write([new ClipboardItem(parts)]);
19477
+ return true;
19478
+ } catch {
19479
+ return false;
19480
+ }
19481
+ }
19482
+ async function buildHtmlPayload(container) {
19483
+ const liveBlocks = Array.from(
19484
+ container.querySelectorAll(".ss-code-block--previewable")
19485
+ );
19486
+ const snapshots = await Promise.all(
19487
+ liveBlocks.map((block) => {
19488
+ const iframe = block.querySelector("iframe");
19489
+ return iframe ? snapshotIframe(iframe) : Promise.resolve([]);
19490
+ })
19491
+ );
19492
+ const clone = container.cloneNode(true);
19493
+ const cloneBlocks = Array.from(
19494
+ clone.querySelectorAll(".ss-code-block--previewable")
19495
+ );
19496
+ const allImages = [];
19497
+ cloneBlocks.forEach((block, i) => {
19498
+ const images = snapshots[i] ?? [];
19499
+ if (images.length > 0) {
19500
+ allImages.push(...images);
19501
+ const frag = block.ownerDocument.createElement("div");
19502
+ for (const img of images) {
19503
+ const el = block.ownerDocument.createElement("img");
19504
+ el.src = img.dataUrl;
19505
+ el.setAttribute("width", String(img.cssWidth));
19506
+ el.setAttribute("height", String(img.cssHeight));
19507
+ el.style.maxWidth = "100%";
19508
+ frag.appendChild(el);
19509
+ }
19510
+ block.replaceWith(frag);
19511
+ } else {
19512
+ const source = block.querySelector("code")?.textContent ?? "";
19513
+ const div = block.ownerDocument.createElement("div");
19514
+ div.innerHTML = stripScripts(source);
19515
+ block.replaceWith(div);
19516
+ }
19517
+ });
19518
+ clone.querySelectorAll(
19519
+ "iframe, button, .ss-code-block__header, .ss-code-block__copy, .ss-code-block__toggle"
19520
+ ).forEach((el) => el.remove());
19521
+ const html4 = `<meta charset="utf-8">${clone.innerHTML}`;
19522
+ return { html: html4, images: allImages };
19523
+ }
19524
+ function stripScripts(html4) {
19525
+ return html4.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "");
19526
+ }
19527
+ async function dataUrlToBlob(dataUrl) {
19528
+ try {
19529
+ const res = await fetch(dataUrl);
19530
+ return await res.blob();
19531
+ } catch {
19532
+ return null;
19533
+ }
19534
+ }
19278
19535
  var RESET_DELAY = 1e3;
19279
19536
  function ChatMessageCopyButton({
19280
- content
19537
+ content,
19538
+ contentRef
19281
19539
  }) {
19282
19540
  const [state, setState] = useState("idle" /* IDLE */);
19283
19541
  const handleCopy = async () => {
19284
19542
  if (!content) return;
19285
19543
  const textToCopy = content.filter((item) => !!item.text).map((item) => item.text).join("\n");
19286
19544
  if (!textToCopy) return;
19287
- try {
19288
- await navigator.clipboard.writeText(textToCopy);
19545
+ const container = contentRef?.current;
19546
+ const ok3 = container ? await copyMessageRich(container, textToCopy) : await copyText(textToCopy);
19547
+ if (ok3) {
19289
19548
  setState("success" /* SUCCESS */);
19290
19549
  setTimeout(() => {
19291
19550
  setState("idle" /* IDLE */);
19292
19551
  }, RESET_DELAY);
19293
- } catch (err) {
19294
- console.error("Failed to copy text:", err);
19552
+ } else {
19553
+ console.error("Failed to copy message content");
19295
19554
  }
19296
19555
  };
19297
19556
  const getIcon = () => {
@@ -19579,6 +19838,7 @@ var MessageBubble = (props) => {
19579
19838
  } = props;
19580
19839
  const [responseFormData, setResponseFormData] = useState(userInput);
19581
19840
  const [responseFormValid, setResponseFormValid] = useState(false);
19841
+ const contentRef = useRef(null);
19582
19842
  const isBotResponse = type === "Output" /* OUTPUT */;
19583
19843
  const showForm = userOutput;
19584
19844
  useEffect(() => {
@@ -19616,12 +19876,12 @@ var MessageBubble = (props) => {
19616
19876
  /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: createdAt ? parseDateTime(createdAt, "Do MMMM YYYY, h:mm a") : "" })
19617
19877
  ] })
19618
19878
  ] }),
19619
- /* @__PURE__ */ jsx(ChatMessageCopyButton, { content })
19879
+ /* @__PURE__ */ jsx(ChatMessageCopyButton, { content, contentRef })
19620
19880
  ]
19621
19881
  }
19622
19882
  ),
19623
19883
  /* @__PURE__ */ jsxs("div", { className: cn(isBotResponse ? "p-4" : "px-4 py-2"), children: [
19624
- contentIsList && content?.map(
19884
+ /* @__PURE__ */ jsx("div", { ref: contentRef, children: contentIsList && content?.map(
19625
19885
  (item, i) => item.text ? /* @__PURE__ */ jsx(
19626
19886
  "div",
19627
19887
  {
@@ -19630,7 +19890,7 @@ var MessageBubble = (props) => {
19630
19890
  },
19631
19891
  `content-${i}`
19632
19892
  ) : item.image ? /* @__PURE__ */ jsx("div", { className: "mb-3 last:mb-0", children: /* @__PURE__ */ jsx(ChatMessageImage, { image: item.image }) }, `image-${i}`) : null
19633
- ),
19893
+ ) }),
19634
19894
  files.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ss-chat-message__attachments mt-4 space-y-2", children: [
19635
19895
  /* @__PURE__ */ jsx("h4", { className: "text-xs font-semibold text-muted-foreground mb-1", children: "Attachments" }),
19636
19896
  files.map((file, idx) => {
@@ -20284,6 +20544,7 @@ var computeAvatar = (name, fallback) => {
20284
20544
  function mapMentionUserDtoToModel(dto) {
20285
20545
  return {
20286
20546
  id: dto.id,
20547
+ userId: dto.userId,
20287
20548
  displayName: dto.displayName ?? "",
20288
20549
  initials: getInitials(dto.displayName ?? "")
20289
20550
  };
@@ -20326,6 +20587,6 @@ function mapWorkspaceDtoToModel(dto) {
20326
20587
  }
20327
20588
  var mapWorkspacesDtoToModels = (arr) => arr.map(mapWorkspaceDtoToModel);
20328
20589
 
20329
- export { ChatProvider, ChatVariablesForm, DRAFT_THREAD_PREFIX, DateFromApi, MarkdownEditor, MessageComposer, MessageList, MessageListSkeleton, MessageMarkdown, MessageValueType, NEW_THREAD_ID, THREAD_LIST_PAGE_SIZE, applyDeltaToMessage, applyThreadToCache, createDraftThreadId, createThreadId, downloadFileBlobOptions, filesKeys, flowRunsKeys, getModelIcon, getThreadPlaceholderFromListCache, getUserPhotoUrl, invalidateWorkspaceThreadLists, isDraftThreadId, mapFileInfoDtoToModel, mapMentionUserDtoToModel, mapMessageDtoToModel, mapMessageErrorDtoToModel, mapMessageValueDtoToModel, mapMessagesDtoToModels, mapSignalRThreadSummaryToModel, mapThreadDtoToModel, mapThreadsResponseDtoToModel, mapWorkspaceDtoToModel, mapWorkspacesDtoToModels, markDraftThreadId, messagesKeys, messagesListOptions, messagesMutationsKeys, modelsKeys, parseDateTime, parseDateTimeHuman, setThreadOptimisticRunning, setThreadRunningInLists, taggableUsersOptions, threadDetailOptions, threadsKeys, unmarkDraftThreadId, useAddInputToMessage, useChatContext, useChatIdentity, useChatService, useDownloadFileBlobQuery, useFileMutations, useFlowRunVariables, useMessages, useModels, useSendMessage, useTaggableWorkspaceUsers, useThread, useThreadIsRunning, useUpdateFlowRunVariable, useWorkspace, utcDate, workspaceDetailOptions, workspaceKeys };
20590
+ export { ChatProvider, ChatVariablesForm, DRAFT_THREAD_PREFIX, DateFromApi, MarkdownEditor, MessageComposer, MessageList, MessageListSkeleton, MessageMarkdown, MessageValueType, NEW_THREAD_ID, THREAD_LIST_PAGE_SIZE, applyDeltaToMessage, applyThreadToCache, createDraftThreadId, createThreadId, downloadFileBlobOptions, filesKeys, flowRunsKeys, getModelIcon, getThreadPlaceholderFromListCache, getUserPhotoUrl, invalidateWorkspaceThreadLists, isDraftThreadId, mapFileInfoDtoToModel, mapMentionUserDtoToModel, mapMessageDtoToModel, mapMessageErrorDtoToModel, mapMessageValueDtoToModel, mapMessagesDtoToModels, mapSignalRThreadSummaryToModel, mapThreadDtoToModel, mapThreadsResponseDtoToModel, mapWorkspaceDtoToModel, mapWorkspacesDtoToModels, markDraftThreadId, messagesKeys, messagesListOptions, messagesMutationsKeys, modelsKeys, parseDateTime, parseDateTimeHuman, randomUUID, setThreadOptimisticRunning, setThreadRunningInLists, taggableUsersOptions, threadDetailOptions, threadsKeys, unmarkDraftThreadId, useAddInputToMessage, useChatContext, useChatIdentity, useChatService, useDownloadFileBlobQuery, useFileMutations, useFlowRunVariables, useMessages, useModels, useSendMessage, useTaggableWorkspaceUsers, useThread, useThreadIsRunning, useUpdateFlowRunVariable, useWorkspace, utcDate, workspaceDetailOptions, workspaceKeys };
20330
20591
  //# sourceMappingURL=index.js.map
20331
20592
  //# sourceMappingURL=index.js.map