@flamingo-stack/openframe-frontend-core 0.0.586 → 0.0.587-1973.5528.1

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 (161) hide show
  1. package/dist/chat-protocol/events.d.ts +8 -2
  2. package/dist/chat-protocol/events.d.ts.map +1 -1
  3. package/dist/chat-protocol/index.cjs +188 -0
  4. package/dist/chat-protocol/index.cjs.map +1 -1
  5. package/dist/chat-protocol/index.js +188 -0
  6. package/dist/chat-protocol/index.js.map +1 -1
  7. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
  8. package/dist/chat-protocol/source-metadata.d.ts +5 -0
  9. package/dist/chat-protocol/source-metadata.d.ts.map +1 -0
  10. package/dist/{chunk-BMORFCT2.cjs → chunk-3UJMZADA.cjs} +43 -43
  11. package/dist/{chunk-BMORFCT2.cjs.map → chunk-3UJMZADA.cjs.map} +1 -1
  12. package/dist/{chunk-D7YISHXC.js → chunk-4MVGFYRE.js} +534 -190
  13. package/dist/chunk-4MVGFYRE.js.map +1 -0
  14. package/dist/{chunk-3WR5R6H6.cjs → chunk-4S7GMJYA.cjs} +21 -21
  15. package/dist/{chunk-3WR5R6H6.cjs.map → chunk-4S7GMJYA.cjs.map} +1 -1
  16. package/dist/{chunk-EAYMX22G.cjs → chunk-5VCDP6UA.cjs} +10 -10
  17. package/dist/{chunk-EAYMX22G.cjs.map → chunk-5VCDP6UA.cjs.map} +1 -1
  18. package/dist/{chunk-XMTGLQSD.js → chunk-5X7AJ67N.js} +3 -3
  19. package/dist/{chunk-OZNFPQT5.js → chunk-7A6J4JTW.js} +3 -3
  20. package/dist/{chunk-QZYYNBMI.cjs → chunk-7COFWOCJ.cjs} +22 -22
  21. package/dist/{chunk-QZYYNBMI.cjs.map → chunk-7COFWOCJ.cjs.map} +1 -1
  22. package/dist/{chunk-6ZNWY6WW.cjs → chunk-AIUIIGO3.cjs} +24 -24
  23. package/dist/{chunk-6ZNWY6WW.cjs.map → chunk-AIUIIGO3.cjs.map} +1 -1
  24. package/dist/{chunk-M36XSGZV.cjs → chunk-BLC4JOFC.cjs} +15 -15
  25. package/dist/{chunk-M36XSGZV.cjs.map → chunk-BLC4JOFC.cjs.map} +1 -1
  26. package/dist/{chunk-NISAGFEM.cjs → chunk-DSXD77KX.cjs} +3 -3
  27. package/dist/{chunk-NISAGFEM.cjs.map → chunk-DSXD77KX.cjs.map} +1 -1
  28. package/dist/{chunk-P2CKLIOM.cjs → chunk-DVMQV3M4.cjs} +69 -69
  29. package/dist/{chunk-P2CKLIOM.cjs.map → chunk-DVMQV3M4.cjs.map} +1 -1
  30. package/dist/{chunk-S7YXLZNG.js → chunk-E2QFZDCP.js} +5 -5
  31. package/dist/{chunk-G45RGWHF.cjs → chunk-EKKR5ZFB.cjs} +887 -543
  32. package/dist/chunk-EKKR5ZFB.cjs.map +1 -0
  33. package/dist/{chunk-L4WLYGB4.js → chunk-FDWGO244.js} +2 -2
  34. package/dist/{chunk-3JVYM4ZN.js → chunk-JQO47UHC.js} +5 -5
  35. package/dist/{chunk-C3MRLZUI.cjs → chunk-JR37UTA2.cjs} +33 -33
  36. package/dist/{chunk-C3MRLZUI.cjs.map → chunk-JR37UTA2.cjs.map} +1 -1
  37. package/dist/{chunk-LV6CORVJ.js → chunk-KQWI25NP.js} +2 -2
  38. package/dist/{chunk-V7ZJTUV2.cjs → chunk-NEI7XMCM.cjs} +9 -9
  39. package/dist/{chunk-V7ZJTUV2.cjs.map → chunk-NEI7XMCM.cjs.map} +1 -1
  40. package/dist/{chunk-YEQAA6I4.js → chunk-OW3FUJ2I.js} +3 -3
  41. package/dist/{chunk-PLMJVA2H.js → chunk-OZRT3ZI3.js} +3 -3
  42. package/dist/{chunk-XMK24INA.cjs → chunk-QKAUBYU5.cjs} +3 -3
  43. package/dist/{chunk-XMK24INA.cjs.map → chunk-QKAUBYU5.cjs.map} +1 -1
  44. package/dist/{chunk-6EZGJZVH.js → chunk-QSO24MEK.js} +2 -2
  45. package/dist/{chunk-DQUEVRDU.js → chunk-QYDIVL4X.js} +6 -6
  46. package/dist/{chunk-3HYHYKNL.js → chunk-RE3KVI2Q.js} +5 -5
  47. package/dist/{chunk-KLWJRGNN.js → chunk-RQ5PYSRJ.js} +4 -4
  48. package/dist/{chunk-NYLWMAHP.cjs → chunk-S2UY2C4A.cjs} +10 -10
  49. package/dist/{chunk-NYLWMAHP.cjs.map → chunk-S2UY2C4A.cjs.map} +1 -1
  50. package/dist/{chunk-SSGM2NKA.cjs → chunk-SCVIBVFS.cjs} +4 -4
  51. package/dist/{chunk-SSGM2NKA.cjs.map → chunk-SCVIBVFS.cjs.map} +1 -1
  52. package/dist/{chunk-4VZFJKPT.cjs → chunk-ULOMJH7C.cjs} +5 -5
  53. package/dist/{chunk-4VZFJKPT.cjs.map → chunk-ULOMJH7C.cjs.map} +1 -1
  54. package/dist/{chunk-5OIXAV4P.js → chunk-UODM3HJC.js} +3 -3
  55. package/dist/{chunk-A6ETQEWK.cjs → chunk-UYHETUX3.cjs} +159 -159
  56. package/dist/{chunk-A6ETQEWK.cjs.map → chunk-UYHETUX3.cjs.map} +1 -1
  57. package/dist/{chunk-2Y5UT5VF.js → chunk-WFWWQO2I.js} +2 -2
  58. package/dist/{chunk-YYC7V7YC.js → chunk-ZRFSUCAH.js} +2 -2
  59. package/dist/components/case-studies/index.cjs +9 -9
  60. package/dist/components/case-studies/index.js +3 -3
  61. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  62. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  63. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  64. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  65. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +4 -1
  66. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  67. package/dist/components/chat/hooks/use-slash-commands.d.ts +3 -3
  68. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -1
  69. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +2 -28
  70. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  71. package/dist/components/chat/index.cjs +2 -2
  72. package/dist/components/chat/index.js +1 -1
  73. package/dist/components/chat/remark-strip-citations.d.ts.map +1 -1
  74. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  75. package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
  76. package/dist/components/chat/types/component.types.d.ts +4 -0
  77. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  78. package/dist/components/chat/types/message.types.d.ts +35 -1
  79. package/dist/components/chat/types/message.types.d.ts.map +1 -1
  80. package/dist/components/chat/types/unified-chat-state.types.d.ts +3 -3
  81. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  82. package/dist/components/chat/utils/chat-source-metadata.d.ts +5 -0
  83. package/dist/components/chat/utils/chat-source-metadata.d.ts.map +1 -0
  84. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  85. package/dist/components/contact/index.cjs +4 -4
  86. package/dist/components/contact/index.js +3 -3
  87. package/dist/components/docs/index.cjs +7 -7
  88. package/dist/components/docs/index.js +6 -6
  89. package/dist/components/embeds/index.cjs +4 -4
  90. package/dist/components/embeds/index.js +3 -3
  91. package/dist/components/faq/index.cjs +4 -4
  92. package/dist/components/faq/index.js +3 -3
  93. package/dist/components/features/index.cjs +3 -3
  94. package/dist/components/features/index.js +2 -2
  95. package/dist/components/help-center-pages/index.cjs +23 -23
  96. package/dist/components/help-center-pages/index.js +14 -14
  97. package/dist/components/index.cjs +36 -36
  98. package/dist/components/index.js +13 -13
  99. package/dist/components/meeting-scheduler/index.cjs +21 -21
  100. package/dist/components/meeting-scheduler/index.js +2 -2
  101. package/dist/components/navigation/index.cjs +6 -6
  102. package/dist/components/navigation/index.js +5 -5
  103. package/dist/components/onboarding-guides/index.cjs +6 -6
  104. package/dist/components/onboarding-guides/index.js +5 -5
  105. package/dist/components/tickets/index.cjs +7 -7
  106. package/dist/components/tickets/index.js +6 -6
  107. package/dist/components/ui/hover-dropdown.d.ts.map +1 -1
  108. package/dist/components/ui/index.cjs +3 -3
  109. package/dist/components/ui/index.js +2 -2
  110. package/dist/index.cjs +7 -7
  111. package/dist/index.js +6 -6
  112. package/dist/utils/index.cjs.map +1 -1
  113. package/dist/utils/index.js.map +1 -1
  114. package/package.json +2 -1
  115. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +130 -0
  116. package/src/chat-protocol/__tests__/source-metadata.test.ts +74 -0
  117. package/src/chat-protocol/events.ts +9 -1
  118. package/src/chat-protocol/nats-decoder.ts +7 -0
  119. package/src/chat-protocol/source-metadata.ts +202 -0
  120. package/src/components/chat/__tests__/chat-message-list.test.tsx +70 -0
  121. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +86 -0
  122. package/src/components/chat/__tests__/embeddable-chat-sources.test.tsx +411 -0
  123. package/src/components/chat/__tests__/remark-strip-citations.test.ts +52 -0
  124. package/src/components/chat/__tests__/tool-execution-display.test.tsx +93 -0
  125. package/src/components/chat/chat-message-enhanced.tsx +28 -13
  126. package/src/components/chat/chat-message-list.tsx +27 -22
  127. package/src/components/chat/embeddable-chat.tsx +55 -36
  128. package/src/components/chat/entity-cards/__tests__/dispatch-video-refs.test.tsx +74 -0
  129. package/src/components/chat/entity-cards/dispatch.tsx +86 -17
  130. package/src/components/chat/hooks/__tests__/use-slash-commands.test.ts +64 -0
  131. package/src/components/chat/hooks/use-nats-chat-adapter.ts +7 -0
  132. package/src/components/chat/hooks/use-slash-commands.ts +9 -5
  133. package/src/components/chat/hooks/use-sse-chat-adapter.ts +2 -29
  134. package/src/components/chat/remark-strip-citations.ts +5 -3
  135. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +56 -0
  136. package/src/components/chat/stream/chat-stream-reducer.ts +31 -0
  137. package/src/components/chat/tool-execution-display.tsx +5 -2
  138. package/src/components/chat/types/component.types.ts +4 -0
  139. package/src/components/chat/types/message.types.ts +39 -0
  140. package/src/components/chat/types/unified-chat-state.types.ts +4 -3
  141. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +57 -0
  142. package/src/components/chat/utils/chat-source-metadata.ts +19 -0
  143. package/src/components/chat/utils/process-historical-messages.ts +19 -1
  144. package/src/components/ui/hover-dropdown.tsx +8 -3
  145. package/dist/chunk-D7YISHXC.js.map +0 -1
  146. package/dist/chunk-G45RGWHF.cjs.map +0 -1
  147. /package/dist/{chunk-XMTGLQSD.js.map → chunk-5X7AJ67N.js.map} +0 -0
  148. /package/dist/{chunk-OZNFPQT5.js.map → chunk-7A6J4JTW.js.map} +0 -0
  149. /package/dist/{chunk-S7YXLZNG.js.map → chunk-E2QFZDCP.js.map} +0 -0
  150. /package/dist/{chunk-L4WLYGB4.js.map → chunk-FDWGO244.js.map} +0 -0
  151. /package/dist/{chunk-3JVYM4ZN.js.map → chunk-JQO47UHC.js.map} +0 -0
  152. /package/dist/{chunk-LV6CORVJ.js.map → chunk-KQWI25NP.js.map} +0 -0
  153. /package/dist/{chunk-YEQAA6I4.js.map → chunk-OW3FUJ2I.js.map} +0 -0
  154. /package/dist/{chunk-PLMJVA2H.js.map → chunk-OZRT3ZI3.js.map} +0 -0
  155. /package/dist/{chunk-6EZGJZVH.js.map → chunk-QSO24MEK.js.map} +0 -0
  156. /package/dist/{chunk-DQUEVRDU.js.map → chunk-QYDIVL4X.js.map} +0 -0
  157. /package/dist/{chunk-3HYHYKNL.js.map → chunk-RE3KVI2Q.js.map} +0 -0
  158. /package/dist/{chunk-KLWJRGNN.js.map → chunk-RQ5PYSRJ.js.map} +0 -0
  159. /package/dist/{chunk-5OIXAV4P.js.map → chunk-UODM3HJC.js.map} +0 -0
  160. /package/dist/{chunk-2Y5UT5VF.js.map → chunk-WFWWQO2I.js.map} +0 -0
  161. /package/dist/{chunk-YYC7V7YC.js.map → chunk-ZRFSUCAH.js.map} +0 -0
@@ -1156,6 +1156,8 @@ import { forwardRef as forwardRef6 } from "react";
1156
1156
  var MESSAGE_TYPE = {
1157
1157
  TEXT: "TEXT",
1158
1158
  THINKING: "THINKING",
1159
+ GUIDE: "GUIDE",
1160
+ SOURCES: "SOURCES",
1159
1161
  ASK: "ASK",
1160
1162
  EXECUTING_TOOL: "EXECUTING_TOOL",
1161
1163
  EXECUTED_TOOL: "EXECUTED_TOOL",
@@ -6812,17 +6814,18 @@ var remarkMentionChips = () => {
6812
6814
  // src/components/chat/remark-strip-citations.ts
6813
6815
  import { visit as visit3 } from "unist-util-visit";
6814
6816
  var CITATION_REGEX = /(^|\s)\[\d{1,2}(?:\s*,\s*\d{1,2})*\](?=[\s.,;:!?)\]]|$)/g;
6817
+ var CITATION_LIST_REGEX = /(^|\s)(?:\[\d{1,2}(?:\s*,\s*\d{1,2})*\]\s*,\s*)+(?:(?:and|or)\s+)?\[\d{1,2}(?:\s*,\s*\d{1,2})*\](?=[\s.,;:!?)\]]|$)/g;
6815
6818
  var remarkStripCitations = () => {
6816
6819
  return (tree) => {
6817
6820
  visit3(tree, "text", (node, index, parent) => {
6818
6821
  if (!parent || typeof index !== "number") return;
6819
6822
  if (!node.value || !node.value.includes("[")) return;
6820
- const withoutCitations = node.value.replace(CITATION_REGEX, "$1");
6823
+ const withoutCitationLists = node.value.replace(CITATION_LIST_REGEX, "$1");
6824
+ const withoutCitations = withoutCitationLists.replace(CITATION_REGEX, "$1");
6821
6825
  if (withoutCitations === node.value) return;
6822
6826
  parent.children.splice(index, 1, {
6823
6827
  ...node,
6824
- // Collapse doubled spaces the removal can leave mid-sentence.
6825
- value: withoutCitations.replace(/ {2,}/g, " ")
6828
+ value: withoutCitations.replace(/\s+([.,;:!?])/g, "$1").replace(/ {2,}/g, " ")
6826
6829
  });
6827
6830
  });
6828
6831
  };
@@ -6854,6 +6857,8 @@ var ToolExecutionDisplay = forwardRef15(
6854
6857
  }, [message.parameters, variant]);
6855
6858
  const hasResult = isExecuted && typeof message.result === "string" && message.result.length > 0;
6856
6859
  const hasBody = argEntries.length > 0 || hasResult || isExecuting;
6860
+ const isConciseRemoteRead = !message.integratedToolType && !!message.toolTitle?.trim() && argEntries.length === 0 && !hasResult;
6861
+ const isStaticRow = isClientView || isConciseRemoteRead;
6857
6862
  const renderStatusIcon = () => {
6858
6863
  if (isExecuting) return /* @__PURE__ */ jsx34(DotsLoaderIcon, { size: 16, className: "text-ods-text-secondary" });
6859
6864
  if (isExecuted && message.success === true) return /* @__PURE__ */ jsx34(CheckCircleIcon, { className: "h-4 w-4 text-ods-success" });
@@ -6873,7 +6878,7 @@ var ToolExecutionDisplay = forwardRef15(
6873
6878
  }
6874
6879
  ),
6875
6880
  /* @__PURE__ */ jsx34("div", { className: "flex h-5 w-5 shrink-0 items-center justify-center", children: renderStatusIcon() }),
6876
- !isClientView && /* @__PURE__ */ jsx34("div", { className: "flex h-5 w-5 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx34(ExpandChevron, { expanded }) })
6881
+ !isStaticRow && /* @__PURE__ */ jsx34("div", { className: "flex h-5 w-5 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx34(ExpandChevron, { expanded }) })
6877
6882
  ] });
6878
6883
  return /* @__PURE__ */ jsx34(
6879
6884
  "div",
@@ -6886,7 +6891,7 @@ var ToolExecutionDisplay = forwardRef15(
6886
6891
  className
6887
6892
  ),
6888
6893
  ...props,
6889
- children: isClientView ? (
6894
+ children: isStaticRow ? (
6890
6895
  // Client (Fae end-user): static, non-expandable row — no chevron, no
6891
6896
  // body. Just the explanation + status; command/args/result are hidden.
6892
6897
  /* @__PURE__ */ jsx34("div", { className: "flex w-full items-start gap-[var(--spacing-system-xs)] p-[var(--spacing-system-s)] text-left", children: headerContent })
@@ -6980,6 +6985,7 @@ var ChatMessageEnhanced = forwardRef16(
6980
6985
  renderContextItem,
6981
6986
  renderMention,
6982
6987
  renderEntityCard,
6988
+ refs,
6983
6989
  onAskSelect,
6984
6990
  NavLinkAnchor,
6985
6991
  ...props
@@ -6988,6 +6994,10 @@ var ChatMessageEnhanced = forwardRef16(
6988
6994
  const isError = role === "error";
6989
6995
  const authorType = authorTypeProp ?? (isUser ? "user" : assistantType === "mingo" ? "mingo" : "fae");
6990
6996
  const hasMarkerSupport = !!renderEntityCard;
6997
+ const refsByKey = useMemo8(
6998
+ () => new Map((refs ?? []).map((reference) => [`${reference.type}:${reference.id}`, reference])),
6999
+ [refs]
7000
+ );
6991
7001
  const segments = useMemo8(() => normalizeContent(content), [content]);
6992
7002
  const askRuns = useMemo8(() => groupAskRuns(segments), [segments]);
6993
7003
  const hasMentionSupport = !!renderMention;
@@ -7036,14 +7046,15 @@ var ChatMessageEnhanced = forwardRef16(
7036
7046
  const key = `${cardType}:${cardId}`;
7037
7047
  usedKeys.add(key);
7038
7048
  let entry = cache.get(key);
7039
- if (!entry || entry.render !== render) {
7040
- const refForRender = {
7041
- type: cardType,
7042
- id: cardId,
7043
- title: cardId,
7044
- url: null
7045
- };
7046
- entry = { render, node: render(refForRender) };
7049
+ const enrichedRef = refsByKey.get(key);
7050
+ const refForRender = enrichedRef ?? {
7051
+ type: cardType,
7052
+ id: cardId,
7053
+ title: cardId,
7054
+ url: null
7055
+ };
7056
+ if (!entry || entry.render !== render || entry.reference !== refForRender) {
7057
+ entry = { render, reference: refForRender, node: render(refForRender) };
7047
7058
  cache.set(key, entry);
7048
7059
  }
7049
7060
  const rendered = entry.node;
@@ -7077,7 +7088,7 @@ var ChatMessageEnhanced = forwardRef16(
7077
7088
  }
7078
7089
  });
7079
7090
  return { inlineByKey, partsBySegment, usedKeys };
7080
- }, [hasMarkerSupport, renderEntityCard, segments]);
7091
+ }, [hasMarkerSupport, refsByKey, renderEntityCard, segments]);
7081
7092
  useEffect6(() => {
7082
7093
  const cache = renderedCardNodeCache.current;
7083
7094
  const usedKeys = renderingPlan?.usedKeys;
@@ -7304,7 +7315,7 @@ var MemoizedChatMessageEnhanced = memo2(ChatMessageEnhanced, (prevProps, nextPro
7304
7315
  // message (it's set once on the optimistic send and never mutated).
7305
7316
  prevProps.contextItems === nextProps.contextItems && prevProps.resolveContextIcon === nextProps.resolveContextIcon && prevProps.renderContextItem === nextProps.renderContextItem && // Host keeps this stable (module const / useCallback), so reference
7306
7317
  // equality holds across streaming chunks.
7307
- prevProps.renderMention === nextProps.renderMention && prevProps.renderEntityCard === nextProps.renderEntityCard && // Same stability contract as the renderers above: hosts pass a `useCallback`
7318
+ prevProps.renderMention === nextProps.renderMention && prevProps.renderEntityCard === nextProps.renderEntityCard && prevProps.refs === nextProps.refs && // Same stability contract as the renderers above: hosts pass a `useCallback`
7308
7319
  // (EmbeddableChat passes its memoized `handleSend`), so this holds across
7309
7320
  // streaming chunks instead of re-rendering every ask card per chunk.
7310
7321
  prevProps.onAskSelect === nextProps.onAskSelect && prevProps.NavLinkAnchor === nextProps.NavLinkAnchor;
@@ -7313,6 +7324,7 @@ MemoizedChatMessageEnhanced.displayName = "MemoizedChatMessageEnhanced";
7313
7324
 
7314
7325
  // src/components/chat/chat-message-list.tsx
7315
7326
  import {
7327
+ Fragment as Fragment11,
7316
7328
  useRef as useRef5,
7317
7329
  useState as useState14,
7318
7330
  useCallback as useCallback5,
@@ -7516,6 +7528,7 @@ var ChatMessageList = forwardRef17(
7516
7528
  renderContextItem,
7517
7529
  renderMention,
7518
7530
  onAskSelect,
7531
+ renderAfterMessage,
7519
7532
  NavLinkAnchor,
7520
7533
  ...props
7521
7534
  }, ref) => {
@@ -7872,31 +7885,34 @@ var ChatMessageList = forwardRef17(
7872
7885
  if (message.hidden) return null;
7873
7886
  const isEmptyPendingTurn = index === messages.length - 1 && isTyping && message.role === "assistant" && !hasNonEmptyContent(message.content) && (!Array.isArray(message.content) || message.content.every((s) => s.type === "text"));
7874
7887
  if (isEmptyPendingTurn) return null;
7875
- return /* @__PURE__ */ jsx38(
7876
- MemoizedChatMessageEnhanced,
7877
- {
7878
- ref: getRegisterMessageEl(message.id),
7879
- role: message.role,
7880
- name: message.name,
7881
- content: message.content,
7882
- timestamp: message.timestamp,
7883
- isTyping: index === messages.length - 1 && isTyping && message.role === "assistant",
7884
- avatar: showAvatars ? message.avatar : null,
7885
- showAvatar: showAvatars,
7886
- assistantType: message.assistantType || assistantType,
7887
- approvalVariant,
7888
- authorType: message.authorType,
7889
- assistantIcon: message.role !== "user" ? assistantIcon : void 0,
7890
- contextItems: message.contextItems,
7891
- resolveContextIcon,
7892
- renderContextItem,
7893
- renderMention,
7894
- renderEntityCard,
7895
- onAskSelect: index > lastUserMessageIndex ? onAskSelect : void 0,
7896
- NavLinkAnchor
7897
- },
7898
- message.id
7899
- );
7888
+ return /* @__PURE__ */ jsxs28(Fragment11, { children: [
7889
+ /* @__PURE__ */ jsx38(
7890
+ MemoizedChatMessageEnhanced,
7891
+ {
7892
+ ref: getRegisterMessageEl(message.id),
7893
+ role: message.role,
7894
+ name: message.name,
7895
+ content: message.content,
7896
+ timestamp: message.timestamp,
7897
+ isTyping: index === messages.length - 1 && isTyping && message.role === "assistant",
7898
+ avatar: showAvatars ? message.avatar : null,
7899
+ showAvatar: showAvatars,
7900
+ assistantType: message.assistantType || assistantType,
7901
+ approvalVariant,
7902
+ authorType: message.authorType,
7903
+ assistantIcon: message.role !== "user" ? assistantIcon : void 0,
7904
+ contextItems: message.contextItems,
7905
+ resolveContextIcon,
7906
+ renderContextItem,
7907
+ renderMention,
7908
+ renderEntityCard,
7909
+ refs: message.refs,
7910
+ onAskSelect: index > lastUserMessageIndex ? onAskSelect : void 0,
7911
+ NavLinkAnchor
7912
+ }
7913
+ ),
7914
+ renderAfterMessage?.(message, index)
7915
+ ] }, message.id);
7900
7916
  })
7901
7917
  ]
7902
7918
  }
@@ -8074,7 +8090,7 @@ function EntityIcon({
8074
8090
  }
8075
8091
 
8076
8092
  // src/components/chat/quick-action-chip.tsx
8077
- import { Fragment as Fragment11, jsx as jsx42, jsxs as jsxs30 } from "react/jsx-runtime";
8093
+ import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs30 } from "react/jsx-runtime";
8078
8094
  var AGENT_ACCENTS = { fae: "pink", mingo: "cyan" };
8079
8095
  function getAgentAccent(slug) {
8080
8096
  return slug === "fae" || slug === "mingo" ? AGENT_ACCENTS[slug] : void 0;
@@ -8102,7 +8118,7 @@ function renderQuickActionIcon(icon) {
8102
8118
  }
8103
8119
  function composeChipLabel(label, lozenge) {
8104
8120
  if (!lozenge) return label;
8105
- return /* @__PURE__ */ jsxs30(Fragment11, { children: [
8121
+ return /* @__PURE__ */ jsxs30(Fragment12, { children: [
8106
8122
  /* @__PURE__ */ jsx42(
8107
8123
  "span",
8108
8124
  {
@@ -8139,7 +8155,7 @@ function QuickActionChipSkeleton({
8139
8155
  size: size2,
8140
8156
  className,
8141
8157
  icon: icon ? /* @__PURE__ */ jsx42(ChipSkelBar, { className: size2 === "large" ? "block size-5" : "block size-4" }) : void 0,
8142
- label: /* @__PURE__ */ jsxs30(Fragment11, { children: [
8158
+ label: /* @__PURE__ */ jsxs30(Fragment12, { children: [
8143
8159
  lozenge && /* @__PURE__ */ jsx42(ChipSkelBar, { className: "mr-2 inline-block h-[16px] w-[26px] translate-y-[2px]" }),
8144
8160
  /* @__PURE__ */ jsx42(ChipSkelBar, { className: "inline-block h-[0.9em] translate-y-[0.08em]", style: { width: `${labelCh}ch` } })
8145
8161
  ] })
@@ -8229,7 +8245,7 @@ import {
8229
8245
  useState as useState16,
8230
8246
  useSyncExternalStore as useSyncExternalStore2
8231
8247
  } from "react";
8232
- import { Fragment as Fragment12, jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
8248
+ import { Fragment as Fragment13, jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
8233
8249
  function normalizeFades(fade) {
8234
8250
  if (fade === void 0) return [];
8235
8251
  return typeof fade === "string" ? [fade] : fade;
@@ -8317,7 +8333,7 @@ function MarqueeWallFades({
8317
8333
  fadeColor = "var(--color-bg)",
8318
8334
  fadeSize
8319
8335
  }) {
8320
- return /* @__PURE__ */ jsx43(Fragment12, { children: normalizeFades(fade).map((edge) => {
8336
+ return /* @__PURE__ */ jsx43(Fragment13, { children: normalizeFades(fade).map((edge) => {
8321
8337
  const size2 = (typeof fadeSize === "object" && fadeSize !== null ? fadeSize[edge] : fadeSize) ?? DEFAULT_FADE_SIZE[edge];
8322
8338
  const px = typeof size2 === "number" ? `${size2}px` : size2;
8323
8339
  const isY = edge === "top" || edge === "bottom";
@@ -9406,7 +9422,7 @@ function MingoChatHistory({
9406
9422
  }
9407
9423
 
9408
9424
  // src/components/chat/mingo-welcome.tsx
9409
- import { Fragment as Fragment13, jsx as jsx52, jsxs as jsxs39 } from "react/jsx-runtime";
9425
+ import { Fragment as Fragment14, jsx as jsx52, jsxs as jsxs39 } from "react/jsx-runtime";
9410
9426
  var DEFAULT_SUBTITLE = "Ready to help with your technical tasks. What can I do for you?";
9411
9427
  var DEFAULT_PROMO = {
9412
9428
  title: "New to OpenFrame?",
@@ -9473,7 +9489,7 @@ function MingoWelcome({
9473
9489
  /* @__PURE__ */ jsx52("p", { className: "text-body-sm text-ods-text-secondary", children: "Something went wrong reaching the server. Check your connection and try again." })
9474
9490
  ] }),
9475
9491
  onRetry && /* @__PURE__ */ jsx52(Button, { variant: "outline", size: "small", leftIcon: /* @__PURE__ */ jsx52(Refresh01RightIcon, {}), onClick: onRetry, children: "Try again" })
9476
- ] }) : isLoadingHistory ? /* @__PURE__ */ jsx52(MingoChatHistorySkeleton, {}) : /* @__PURE__ */ jsx52(Fragment13, { children: /* @__PURE__ */ jsxs39("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
9492
+ ] }) : isLoadingHistory ? /* @__PURE__ */ jsx52(MingoChatHistorySkeleton, {}) : /* @__PURE__ */ jsx52(Fragment14, { children: /* @__PURE__ */ jsxs39("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
9477
9493
  /* @__PURE__ */ jsx52(
9478
9494
  "div",
9479
9495
  {
@@ -10070,7 +10086,7 @@ var ModalFooter = forwardRef20(({ children, className }, ref) => /* @__PURE__ */
10070
10086
  ModalFooter.displayName = "ModalV2Footer";
10071
10087
 
10072
10088
  // src/components/chat/mingo-chat-modals.tsx
10073
- import { Fragment as Fragment14, jsx as jsx58, jsxs as jsxs45 } from "react/jsx-runtime";
10089
+ import { Fragment as Fragment15, jsx as jsx58, jsxs as jsxs45 } from "react/jsx-runtime";
10074
10090
  var footerBtn = "flex-1 min-w-0 rounded-md px-[var(--spacing-system-m)] py-[var(--spacing-system-sf)] text-h3 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent disabled:opacity-50 disabled:cursor-not-allowed";
10075
10091
  var cancelBtn = cn(footerBtn, "border border-ods-border bg-transparent text-ods-text-primary hover:bg-ods-bg-hover");
10076
10092
  var saveBtn = cn(footerBtn, "bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover");
@@ -10141,7 +10157,7 @@ function ChatDialogModals({
10141
10157
  setRestoreTarget,
10142
10158
  onConfirmRestore
10143
10159
  }) {
10144
- return /* @__PURE__ */ jsxs45(Fragment14, { children: [
10160
+ return /* @__PURE__ */ jsxs45(Fragment15, { children: [
10145
10161
  /* @__PURE__ */ jsx58(
10146
10162
  RenameChatModal,
10147
10163
  {
@@ -10347,7 +10363,7 @@ function ChatPanelHeaderMobile({
10347
10363
  }
10348
10364
 
10349
10365
  // src/components/chat/chat-panel-header.tsx
10350
- import { Fragment as Fragment15, jsx as jsx62, jsxs as jsxs48 } from "react/jsx-runtime";
10366
+ import { Fragment as Fragment16, jsx as jsx62, jsxs as jsxs48 } from "react/jsx-runtime";
10351
10367
  function ChatPanelHeader({
10352
10368
  showBack = false,
10353
10369
  title,
@@ -10374,7 +10390,7 @@ function ChatPanelHeader({
10374
10390
  onRename && { id: "rename", label: "Rename chat", onClick: onRename },
10375
10391
  onArchive && { id: "archive", label: "Archive chat", onClick: onArchive }
10376
10392
  ].filter(Boolean);
10377
- return /* @__PURE__ */ jsxs48(Fragment15, { children: [
10393
+ return /* @__PURE__ */ jsxs48(Fragment16, { children: [
10378
10394
  !compact && /* @__PURE__ */ jsx62(
10379
10395
  ChatPanelHeaderMobile,
10380
10396
  {
@@ -10512,7 +10528,7 @@ function stripChatAttachmentMarkdown(text) {
10512
10528
  }
10513
10529
 
10514
10530
  // src/components/chat/chat-attachment-bar.tsx
10515
- import { Fragment as Fragment16, jsx as jsx63, jsxs as jsxs49 } from "react/jsx-runtime";
10531
+ import { Fragment as Fragment17, jsx as jsx63, jsxs as jsxs49 } from "react/jsx-runtime";
10516
10532
  var CHAT_ATTACHMENT_MIME_TYPES = [
10517
10533
  "image/jpeg",
10518
10534
  "image/png",
@@ -10542,7 +10558,7 @@ function ChatAttachmentAddButton({
10542
10558
  if (!canAddMore) return;
10543
10559
  fileInputRef.current?.click();
10544
10560
  };
10545
- return /* @__PURE__ */ jsxs49(Fragment16, { children: [
10561
+ return /* @__PURE__ */ jsxs49(Fragment17, { children: [
10546
10562
  /* @__PURE__ */ jsx63(
10547
10563
  "input",
10548
10564
  {
@@ -11089,11 +11105,11 @@ function ChatComposer({
11089
11105
  }
11090
11106
 
11091
11107
  // src/components/chat/chat-archive-page.tsx
11092
- import { Fragment as Fragment17, jsx as jsx68, jsxs as jsxs53 } from "react/jsx-runtime";
11108
+ import { Fragment as Fragment18, jsx as jsx68, jsxs as jsxs53 } from "react/jsx-runtime";
11093
11109
  function ChatArchivePage(props) {
11094
11110
  const { dialogs, onSelectDialog, isLoading = false, isFetching = false, hasMore = false, onLoadMore } = props;
11095
11111
  const suppressEmptyStateWhileFetching = isFetching;
11096
- return /* @__PURE__ */ jsxs53(Fragment17, { children: [
11112
+ return /* @__PURE__ */ jsxs53(Fragment18, { children: [
11097
11113
  !props.embedded && /* @__PURE__ */ jsx68(
11098
11114
  ChatPanelHeaderMobile,
11099
11115
  {
@@ -11199,7 +11215,7 @@ var ChatSidebarSkeleton = forwardRef23(
11199
11215
  ChatSidebarSkeleton.displayName = "ChatSidebarSkeleton";
11200
11216
 
11201
11217
  // src/components/chat/chat-sidebar.tsx
11202
- import { Fragment as Fragment18, jsx as jsx70, jsxs as jsxs55 } from "react/jsx-runtime";
11218
+ import { Fragment as Fragment19, jsx as jsx70, jsxs as jsxs55 } from "react/jsx-runtime";
11203
11219
  var DialogListItem = forwardRef24(
11204
11220
  ({ className, dialog, isActive, onDialogSelect, onClick, ...props }, ref) => {
11205
11221
  const handleClick = (e) => {
@@ -11336,7 +11352,7 @@ var ChatSidebar = forwardRef24(
11336
11352
  },
11337
11353
  dialog.id
11338
11354
  )),
11339
- hasNextPage && /* @__PURE__ */ jsx70("div", { ref: loadMoreRef, children: isFetchingNextPage && /* @__PURE__ */ jsxs55(Fragment18, { children: [
11355
+ hasNextPage && /* @__PURE__ */ jsx70("div", { ref: loadMoreRef, children: isFetchingNextPage && /* @__PURE__ */ jsxs55(Fragment19, { children: [
11340
11356
  /* @__PURE__ */ jsx70(DialogListItemSkeleton, {}),
11341
11357
  /* @__PURE__ */ jsx70(DialogListItemSkeleton, {}),
11342
11358
  /* @__PURE__ */ jsx70(DialogListItemSkeleton, {})
@@ -11581,7 +11597,7 @@ function captionsEntityTypeForDocType(docType) {
11581
11597
  }
11582
11598
 
11583
11599
  // src/components/layout/page-heading.tsx
11584
- import { Fragment as Fragment19, jsx as jsx73, jsxs as jsxs56 } from "react/jsx-runtime";
11600
+ import { Fragment as Fragment20, jsx as jsx73, jsxs as jsxs56 } from "react/jsx-runtime";
11585
11601
  var PAGE_HEADING_CLASS = "text-h1 text-ods-text-primary";
11586
11602
  var SECTION_HEADING_CLASS = "text-h2 text-ods-text-primary";
11587
11603
  var DESCRIPTION_CLASS = "mt-6 max-w-[640px] font-body text-[16px] md:text-[18px] leading-[24px] md:leading-[28px] text-ods-text-secondary";
@@ -11595,7 +11611,7 @@ function PageHeading({
11595
11611
  const headingClass = className ? `${PAGE_HEADING_CLASS} ${className}` : PAGE_HEADING_CLASS;
11596
11612
  const descClass = descriptionClassName ? `${DESCRIPTION_CLASS} ${descriptionClassName}` : DESCRIPTION_CLASS;
11597
11613
  const hasDescription = description != null && description !== "" && typeof description !== "boolean";
11598
- return /* @__PURE__ */ jsxs56(Fragment19, { children: [
11614
+ return /* @__PURE__ */ jsxs56(Fragment20, { children: [
11599
11615
  /* @__PURE__ */ jsx73(Tag2, { className: headingClass, children }),
11600
11616
  hasDescription && /* @__PURE__ */ jsx73("p", { className: descClass, children: description })
11601
11617
  ] });
@@ -12776,7 +12792,7 @@ function CardHitLayer({
12776
12792
 
12777
12793
  // src/components/features/cards-strip.tsx
12778
12794
  import React5, { useCallback as useCallback12, useEffect as useEffect20, useMemo as useMemo21, useRef as useRef16, useState as useState26 } from "react";
12779
- import { Fragment as Fragment20, jsx as jsx79, jsxs as jsxs59 } from "react/jsx-runtime";
12795
+ import { Fragment as Fragment21, jsx as jsx79, jsxs as jsxs59 } from "react/jsx-runtime";
12780
12796
  var TRACK_GAP_PX = 16;
12781
12797
  var CHEVRON_SUPPRESS_MS = 4e3;
12782
12798
  var USER_SCROLL_SUPPRESS_MS = 3e3;
@@ -13153,7 +13169,7 @@ function CardsStrip(props) {
13153
13169
  ) })
13154
13170
  }
13155
13171
  ),
13156
- showChevrons && overflows && /* @__PURE__ */ jsxs59(Fragment20, { children: [
13172
+ showChevrons && overflows && /* @__PURE__ */ jsxs59(Fragment21, { children: [
13157
13173
  /* @__PURE__ */ jsx79(
13158
13174
  Button,
13159
13175
  {
@@ -13187,7 +13203,7 @@ function CardsStrip(props) {
13187
13203
 
13188
13204
  // src/components/features/video-hover-preview.tsx
13189
13205
  import { useEffect as useEffect21, useRef as useRef17, useState as useState27 } from "react";
13190
- import { Fragment as Fragment21, jsx as jsx80, jsxs as jsxs60 } from "react/jsx-runtime";
13206
+ import { Fragment as Fragment22, jsx as jsx80, jsxs as jsxs60 } from "react/jsx-runtime";
13191
13207
  function VideoHoverPreviewSurface({
13192
13208
  url,
13193
13209
  posterUrl = null,
@@ -13226,7 +13242,7 @@ function VideoHoverPreviewSurface({
13226
13242
  const showMedia = continuation || (gateControlled ? playerMounted : isNear);
13227
13243
  const { src: posterSrc, onError: onPosterError } = useCoverImageFallback(posterUrl);
13228
13244
  const facadeSkipped = preload === "none" && !posterSrc;
13229
- return /* @__PURE__ */ jsx80("div", { ref: rootElRef, className: cn("absolute inset-0", className), inert: isClone || void 0, children: showMedia ? /* @__PURE__ */ jsxs60(Fragment21, { children: [
13245
+ return /* @__PURE__ */ jsx80("div", { ref: rootElRef, className: cn("absolute inset-0", className), inert: isClone || void 0, children: showMedia ? /* @__PURE__ */ jsxs60(Fragment22, { children: [
13230
13246
  posterSrc ? /* @__PURE__ */ jsx80(
13231
13247
  next_image_default,
13232
13248
  {
@@ -13275,7 +13291,7 @@ function VideoHoverPreviewSurface({
13275
13291
  }
13276
13292
 
13277
13293
  // src/components/features/video-ratio-tabs.tsx
13278
- import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs61 } from "react/jsx-runtime";
13294
+ import { Fragment as Fragment23, jsx as jsx81, jsxs as jsxs61 } from "react/jsx-runtime";
13279
13295
  var TAB_TRIGGER_CLASS = "rounded-none border-b-2 border-transparent data-[state=active]:border-ods-accent data-[state=active]:bg-transparent data-[state=active]:shadow-none px-4 py-2 text-h6 text-ods-text-secondary data-[state=active]:text-ods-text-primary";
13280
13296
  var RATIO_GRID_CLASS = {
13281
13297
  portrait: "grid grid-cols-2 md:grid-cols-3 gap-4",
@@ -13296,7 +13312,7 @@ function RatioTabs({ groups, defaultTab, className = "" }) {
13296
13312
  const activeTabs = RATIO_TAB_CONFIG.filter((t) => groups[t.key].count > 0);
13297
13313
  if (activeTabs.length <= 1) {
13298
13314
  const active = activeTabs[0];
13299
- return active ? /* @__PURE__ */ jsx81(Fragment22, { children: groups[active.key].render() }) : null;
13315
+ return active ? /* @__PURE__ */ jsx81(Fragment23, { children: groups[active.key].render() }) : null;
13300
13316
  }
13301
13317
  const firstTab = defaultTab && groups[defaultTab].count > 0 ? defaultTab : activeTabs[0].key;
13302
13318
  return /* @__PURE__ */ jsxs61(Tabs, { defaultValue: firstTab, className: `w-full ${className}`, children: [
@@ -13339,7 +13355,7 @@ function groupByAspectRatio(items, getAspectRatio) {
13339
13355
  }
13340
13356
 
13341
13357
  // src/components/features/video-bites-strip.tsx
13342
- import { Fragment as Fragment23, jsx as jsx82, jsxs as jsxs62 } from "react/jsx-runtime";
13358
+ import { Fragment as Fragment24, jsx as jsx82, jsxs as jsxs62 } from "react/jsx-runtime";
13343
13359
  function VideoBitesStrip({
13344
13360
  bites,
13345
13361
  title = DEFAULT_VIDEO_BITES_TITLE,
@@ -13452,7 +13468,7 @@ function VideoBiteCard({
13452
13468
  if (isTouch && !isActive) activate();
13453
13469
  };
13454
13470
  const titleClass = "text-h6 text-ods-text-primary";
13455
- const overlayContent = /* @__PURE__ */ jsxs62(Fragment23, { children: [
13471
+ const overlayContent = /* @__PURE__ */ jsxs62(Fragment24, { children: [
13456
13472
  titleEditable ? (
13457
13473
  // Textarea, not input: the public title wraps to two lines
13458
13474
  // (line-clamp-2) and the editor must render identically. rows=1 +
@@ -13524,7 +13540,7 @@ function VideoBiteCard({
13524
13540
  // the bottom strip, so the card centre (where the play glyph sits) was
13525
13541
  // not clickable and showed no pointer cursor even though the glyph
13526
13542
  // accented on hover. One interactive element, no a11y duplication.
13527
- /* @__PURE__ */ jsxs62(Fragment23, { children: [
13543
+ /* @__PURE__ */ jsxs62(Fragment24, { children: [
13528
13544
  /* @__PURE__ */ jsx82(
13529
13545
  CardHitLayer,
13530
13546
  {
@@ -13570,7 +13586,7 @@ function VideoBiteCard({
13570
13586
  }
13571
13587
 
13572
13588
  // src/components/features/entity-video-section.tsx
13573
- import { Fragment as Fragment24, jsx as jsx83, jsxs as jsxs63 } from "react/jsx-runtime";
13589
+ import { Fragment as Fragment25, jsx as jsx83, jsxs as jsxs63 } from "react/jsx-runtime";
13574
13590
  function EntityVideoSection({
13575
13591
  mainVideoUrl,
13576
13592
  youtubeUrl,
@@ -13606,7 +13622,7 @@ function EntityVideoSection({
13606
13622
  }
13607
13623
  const fullVideoUrl = youtubeUrl || mainVideoUrl || null;
13608
13624
  const fullVideoKind = youtubeUrl ? "youtube" : "auto";
13609
- return /* @__PURE__ */ jsxs63(Fragment24, { children: [
13625
+ return /* @__PURE__ */ jsxs63(Fragment25, { children: [
13610
13626
  hasVideo && (fullVideoUrl && hasHighlight ? /* @__PURE__ */ jsxs63(Tabs, { defaultValue: "full-video", className: "w-full", children: [
13611
13627
  /* @__PURE__ */ jsxs63(TabsList, { className: "inline-flex h-auto justify-start gap-0 rounded-none bg-transparent p-0", children: [
13612
13628
  /* @__PURE__ */ jsx83(
@@ -13719,7 +13735,7 @@ function ChatVideoEntityCard({ chatRef }) {
13719
13735
  // src/components/chat/entity-cards/github-activity-card.tsx
13720
13736
  import { GitPullRequest, Eye, ExternalLink } from "lucide-react";
13721
13737
  import React6 from "react";
13722
- import { Fragment as Fragment25, jsx as jsx85, jsxs as jsxs64 } from "react/jsx-runtime";
13738
+ import { Fragment as Fragment26, jsx as jsx85, jsxs as jsxs64 } from "react/jsx-runtime";
13723
13739
  function parseRepoFromUrl(url) {
13724
13740
  if (!url) return null;
13725
13741
  try {
@@ -13854,7 +13870,7 @@ function GitHubActivityCard({ item, variant = "compact", className, anchorProps
13854
13870
  /* @__PURE__ */ jsx85("span", { className: "whitespace-nowrap", children: dateText }, "date")
13855
13871
  );
13856
13872
  const href = safeHref(item.url);
13857
- const body = /* @__PURE__ */ jsxs64(Fragment25, { children: [
13873
+ const body = /* @__PURE__ */ jsxs64(Fragment26, { children: [
13858
13874
  /* @__PURE__ */ jsx85("span", { className: COMPACT_CARD_ICON_SLOT, children: kindIcon(kind, "h-5 w-5") }),
13859
13875
  /* @__PURE__ */ jsxs64("span", { className: COMPACT_CARD_TEXT_COL, children: [
13860
13876
  /* @__PURE__ */ jsxs64("span", { className: `${COMPACT_CARD_TITLE_ROW} flex-nowrap gap-1.5`, children: [
@@ -13903,7 +13919,7 @@ function GitHubActivityCardSkeleton({
13903
13919
  // src/components/chat/entity-cards/slack-message-card.tsx
13904
13920
  import { ExternalLink as ExternalLink2, Hash } from "lucide-react";
13905
13921
  import React7 from "react";
13906
- import { Fragment as Fragment26, jsx as jsx86, jsxs as jsxs65 } from "react/jsx-runtime";
13922
+ import { Fragment as Fragment27, jsx as jsx86, jsxs as jsxs65 } from "react/jsx-runtime";
13907
13923
  function parseChannelFromUrl(url) {
13908
13924
  if (!url) return null;
13909
13925
  try {
@@ -13945,7 +13961,7 @@ function SlackMessageCard({ item, variant = "compact", className, anchorProps })
13945
13961
  /* @__PURE__ */ jsx86("span", { className: "whitespace-nowrap", children: dateText }, "date")
13946
13962
  );
13947
13963
  const href = safeHref(item.url);
13948
- const body = /* @__PURE__ */ jsxs65(Fragment26, { children: [
13964
+ const body = /* @__PURE__ */ jsxs65(Fragment27, { children: [
13949
13965
  /* @__PURE__ */ jsx86("span", { className: COMPACT_CARD_ICON_SLOT, children: /* @__PURE__ */ jsx86(SlackIcon, { className: "h-5 w-5" }) }),
13950
13966
  /* @__PURE__ */ jsxs65("span", { className: COMPACT_CARD_TEXT_COL, children: [
13951
13967
  /* @__PURE__ */ jsx86("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx86("span", { className: COMPACT_CARD_TITLE, children: item.title }) }),
@@ -13989,7 +14005,7 @@ function SlackMessageCardSkeleton({
13989
14005
  // src/components/chat/entity-cards/hubspot-ticket-card.tsx
13990
14006
  import { ExternalLink as ExternalLink3 } from "lucide-react";
13991
14007
  import React8 from "react";
13992
- import { Fragment as Fragment27, jsx as jsx87, jsxs as jsxs66 } from "react/jsx-runtime";
14008
+ import { Fragment as Fragment28, jsx as jsx87, jsxs as jsxs66 } from "react/jsx-runtime";
13993
14009
  function formatToken(token) {
13994
14010
  if (!token) return null;
13995
14011
  const lower = token.toLowerCase().replace(/_/g, " ");
@@ -14031,7 +14047,7 @@ function HubspotTicketCard({ item, variant = "compact", className, anchorProps }
14031
14047
  /* @__PURE__ */ jsx87("span", { className: "whitespace-nowrap", children: dateText }, "date")
14032
14048
  );
14033
14049
  const href = safeHref(item.url);
14034
- const body = /* @__PURE__ */ jsxs66(Fragment27, { children: [
14050
+ const body = /* @__PURE__ */ jsxs66(Fragment28, { children: [
14035
14051
  /* @__PURE__ */ jsxs66("span", { className: COMPACT_CARD_TEXT_COL, children: [
14036
14052
  /* @__PURE__ */ jsxs66("span", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [
14037
14053
  /* @__PURE__ */ jsx87("span", { className: `${COMPACT_CARD_TITLE} min-w-0 shrink`, children: item.title }),
@@ -14105,9 +14121,9 @@ function HubspotTicketCardSkeleton({
14105
14121
 
14106
14122
  // src/components/chat/entity-cards/data-room-doc-card.tsx
14107
14123
  import { ExternalLink as ExternalLink4 } from "lucide-react";
14108
- import { Fragment as Fragment28, jsx as jsx88, jsxs as jsxs67 } from "react/jsx-runtime";
14124
+ import { Fragment as Fragment29, jsx as jsx88, jsxs as jsxs67 } from "react/jsx-runtime";
14109
14125
  function DataRoomDocCard({ item, className, badgeText, anchorProps }) {
14110
- const body = /* @__PURE__ */ jsxs67(Fragment28, { children: [
14126
+ const body = /* @__PURE__ */ jsxs67(Fragment29, { children: [
14111
14127
  /* @__PURE__ */ jsxs67("span", { className: COMPACT_CARD_TEXT_COL, children: [
14112
14128
  /* @__PURE__ */ jsxs67("span", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [
14113
14129
  /* @__PURE__ */ jsx88("span", { className: `${COMPACT_CARD_TITLE} min-w-0 shrink`, children: item.title }),
@@ -14145,10 +14161,10 @@ function DataRoomDocCardSkeleton({ className }) {
14145
14161
 
14146
14162
  // src/components/chat/entity-cards/campaign-card-admin.tsx
14147
14163
  import { Megaphone as Megaphone3 } from "lucide-react";
14148
- import { Fragment as Fragment29, jsx as jsx89, jsxs as jsxs68 } from "react/jsx-runtime";
14164
+ import { Fragment as Fragment30, jsx as jsx89, jsxs as jsxs68 } from "react/jsx-runtime";
14149
14165
  function CampaignCardAdmin({ campaign, className, anchorProps }) {
14150
14166
  const goalsCount = campaign.goals?.length || 0;
14151
- const innerChildren = /* @__PURE__ */ jsxs68(Fragment29, { children: [
14167
+ const innerChildren = /* @__PURE__ */ jsxs68(Fragment30, { children: [
14152
14168
  /* @__PURE__ */ jsx89("span", { className: COMPACT_CARD_ICON_SLOT, children: /* @__PURE__ */ jsx89(Megaphone3, { className: "h-5 w-5" }) }),
14153
14169
  /* @__PURE__ */ jsxs68("span", { className: COMPACT_CARD_TEXT_COL, children: [
14154
14170
  /* @__PURE__ */ jsx89("span", { className: COMPACT_CARD_TITLE_ROW, children: /* @__PURE__ */ jsx89("span", { className: COMPACT_CARD_TITLE, children: campaign.name }) }),
@@ -14178,11 +14194,11 @@ function CampaignCardAdminSkeleton({ className }) {
14178
14194
 
14179
14195
  // src/components/chat/entity-cards/generic-entity-card.tsx
14180
14196
  import { ExternalLink as ExternalLink5 } from "lucide-react";
14181
- import { Fragment as Fragment30, jsx as jsx90, jsxs as jsxs69 } from "react/jsx-runtime";
14197
+ import { Fragment as Fragment31, jsx as jsx90, jsxs as jsxs69 } from "react/jsx-runtime";
14182
14198
  function GenericEntityCard({ item, className, anchorProps }) {
14183
14199
  const href = safeHref(item.url);
14184
14200
  const dateText = formatDateUTC(item.dateUpdated, { fallback: "", timezone: "local" });
14185
- const body = /* @__PURE__ */ jsxs69(Fragment30, { children: [
14201
+ const body = /* @__PURE__ */ jsxs69(Fragment31, { children: [
14186
14202
  /* @__PURE__ */ jsxs69("span", { className: COMPACT_CARD_TEXT_COL, children: [
14187
14203
  /* @__PURE__ */ jsxs69("span", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [
14188
14204
  /* @__PURE__ */ jsx90("span", { className: `${COMPACT_CARD_TITLE} min-w-0 shrink`, children: item.title }),
@@ -14235,6 +14251,190 @@ function GenericEntityCardSkeleton({ className }) {
14235
14251
  // src/components/chat/entity-cards/dispatch.tsx
14236
14252
  import React9 from "react";
14237
14253
 
14254
+ // src/chat-protocol/source-metadata.ts
14255
+ var CARD_REFERENCE = /^\[card:\/\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_-]+)\]$/;
14256
+ var SOURCE_METADATA_MATCHED = "sourceMetadataMatched";
14257
+ function isSourceMetadataCardRef(ref) {
14258
+ return ref.metadata?.[SOURCE_METADATA_MATCHED] === true;
14259
+ }
14260
+ function requiredString(value) {
14261
+ if (typeof value !== "string") return null;
14262
+ const normalized = value.trim();
14263
+ return normalized ? normalized : null;
14264
+ }
14265
+ function optionalString(value) {
14266
+ return requiredString(value) ?? void 0;
14267
+ }
14268
+ function httpsUrl(value) {
14269
+ const candidate = optionalString(value);
14270
+ if (!candidate) return void 0;
14271
+ try {
14272
+ return new URL(candidate).protocol === "https:" ? candidate : void 0;
14273
+ } catch {
14274
+ return void 0;
14275
+ }
14276
+ }
14277
+ function youtubeId(value) {
14278
+ const candidate = optionalString(value);
14279
+ if (!candidate) return void 0;
14280
+ if (/^[A-Za-z0-9_-]{11}$/.test(candidate)) return candidate;
14281
+ const url = httpsUrl(candidate);
14282
+ if (!url) return void 0;
14283
+ const parsed = new URL(url);
14284
+ const hostname = parsed.hostname.toLowerCase();
14285
+ const youtubeHost = hostname === "youtu.be" || hostname === "youtube.com" || hostname.endsWith(".youtube.com");
14286
+ if (!youtubeHost) return void 0;
14287
+ const pathId = hostname === "youtu.be" ? parsed.pathname.split("/").filter(Boolean)[0] : parsed.pathname.match(/^\/(?:embed|v|shorts)\/([A-Za-z0-9_-]{11})(?:\/|$)/)?.[1];
14288
+ const id = pathId ?? parsed.searchParams.get("v") ?? void 0;
14289
+ return id && /^[A-Za-z0-9_-]{11}$/.test(id) ? id : void 0;
14290
+ }
14291
+ function nullableString(value) {
14292
+ return value === null ? null : optionalString(value);
14293
+ }
14294
+ function normalizeSourceItems(value) {
14295
+ if (!Array.isArray(value)) return void 0;
14296
+ const items = value.flatMap((candidate) => {
14297
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return [];
14298
+ const item = candidate;
14299
+ const id = requiredString(item.id);
14300
+ const documentType = requiredString(item.documentType);
14301
+ const name = requiredString(item.name);
14302
+ if (!id || !documentType || !name) return [];
14303
+ const externalUrl = httpsUrl(item.externalUrl);
14304
+ const targetPlatform = nullableString(item.targetPlatform);
14305
+ const path = nullableString(item.path);
14306
+ return [
14307
+ {
14308
+ id,
14309
+ documentType,
14310
+ name,
14311
+ ...externalUrl ? { externalUrl } : {},
14312
+ ...targetPlatform !== void 0 ? { targetPlatform } : {},
14313
+ ...path !== void 0 ? { path } : {}
14314
+ }
14315
+ ];
14316
+ });
14317
+ return items.length > 0 ? items : void 0;
14318
+ }
14319
+ function normalizeSources(value) {
14320
+ if (!Array.isArray(value)) return [];
14321
+ const seen = /* @__PURE__ */ new Set();
14322
+ const sources = [];
14323
+ for (const candidate of value) {
14324
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
14325
+ const source = candidate;
14326
+ const index = source.index;
14327
+ const name = requiredString(source.name);
14328
+ const path = requiredString(source.path);
14329
+ const documentType = requiredString(source.documentType);
14330
+ const validIndex = typeof index === "number" && Number.isFinite(index) && Number.isInteger(index) && index > 0 && !seen.has(index);
14331
+ if (!validIndex || !name || !path || !documentType) continue;
14332
+ seen.add(index);
14333
+ const externalUrl = httpsUrl(source.externalUrl);
14334
+ const targetPlatform = nullableString(source.targetPlatform);
14335
+ const id = optionalString(source.id);
14336
+ const sourceRepo = optionalString(source.sourceRepo);
14337
+ const label = optionalString(source.label);
14338
+ const items = normalizeSourceItems(source.items);
14339
+ sources.push({
14340
+ index,
14341
+ name,
14342
+ path,
14343
+ documentType,
14344
+ ...externalUrl ? { externalUrl } : {},
14345
+ ...targetPlatform !== void 0 ? { targetPlatform } : {},
14346
+ ...id ? { id } : {},
14347
+ ...sourceRepo ? { sourceRepo } : {},
14348
+ ...label ? { label } : {},
14349
+ ...items ? { items } : {}
14350
+ });
14351
+ }
14352
+ return sources;
14353
+ }
14354
+ function normalizeVideoMetadata(value) {
14355
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
14356
+ const input = value;
14357
+ const metadata = {};
14358
+ for (const key of ["videoUrl", "highlightVideoUrl", "videoPoster", "highlightVideoPoster"]) {
14359
+ const url = httpsUrl(input[key]);
14360
+ if (url) metadata[key] = url;
14361
+ }
14362
+ const id = youtubeId(input.youtubeUrl);
14363
+ if (id) metadata.youtubeUrl = id;
14364
+ return metadata;
14365
+ }
14366
+ function normalizeVideos(value) {
14367
+ if (!Array.isArray(value)) return [];
14368
+ return value.flatMap((candidate) => {
14369
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return [];
14370
+ const video = candidate;
14371
+ const match = typeof video.ref === "string" ? CARD_REFERENCE.exec(video.ref) : null;
14372
+ if (!match || match[1] !== "video") return [];
14373
+ const id = match[2];
14374
+ const title = optionalString(video.title) ?? id;
14375
+ const url = httpsUrl(video.url) ?? null;
14376
+ const sourceRepo = optionalString(video.sourceRepo);
14377
+ const metadata = normalizeVideoMetadata(video.metadata);
14378
+ if (url) {
14379
+ const idFromUrl = youtubeId(url);
14380
+ if (idFromUrl) {
14381
+ metadata.youtubeUrl = idFromUrl;
14382
+ delete metadata.videoUrl;
14383
+ } else {
14384
+ metadata.videoUrl = url;
14385
+ delete metadata.youtubeUrl;
14386
+ }
14387
+ }
14388
+ return [
14389
+ {
14390
+ type: "video",
14391
+ id,
14392
+ title,
14393
+ url,
14394
+ ...sourceRepo ? { sourceRepo } : {},
14395
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
14396
+ }
14397
+ ];
14398
+ });
14399
+ }
14400
+ function normalizeCards(value, sources) {
14401
+ if (!Array.isArray(value)) return [];
14402
+ return value.flatMap((candidate) => {
14403
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return [];
14404
+ const card = candidate;
14405
+ const match = typeof card.ref === "string" ? CARD_REFERENCE.exec(card.ref) : null;
14406
+ if (!match || match[1] === "video") return [];
14407
+ const type = requiredString(card.entityType);
14408
+ const id = requiredString(card.entityId);
14409
+ if (!type || !id || type !== match[1] || id !== match[2]) return [];
14410
+ const source = sources.find((item) => item.id === id && item.documentType === type);
14411
+ const ref = {
14412
+ type,
14413
+ id,
14414
+ title: source?.name ?? id,
14415
+ url: source?.externalUrl ?? null,
14416
+ ...source?.sourceRepo ? { sourceRepo: source.sourceRepo } : {},
14417
+ ...source?.targetPlatform !== void 0 ? { targetPlatform: source.targetPlatform } : {},
14418
+ ...source?.path ? { metadata: { path: source.path, [SOURCE_METADATA_MATCHED]: true } } : {}
14419
+ };
14420
+ return [ref];
14421
+ });
14422
+ }
14423
+ function sourceMetadataEvent(payload) {
14424
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
14425
+ const metadata = payload;
14426
+ const sources = normalizeSources(metadata.sources);
14427
+ const refs = [...normalizeVideos(metadata.videos), ...normalizeCards(metadata.cards, sources)];
14428
+ const refsByIdentity = /* @__PURE__ */ new Map();
14429
+ refs.forEach((ref) => {
14430
+ const identity = `${ref.type}:${ref.id}`;
14431
+ if (!refsByIdentity.has(identity)) refsByIdentity.set(identity, ref);
14432
+ });
14433
+ const uniqueRefs = Array.from(refsByIdentity.values());
14434
+ if (sources.length === 0 && uniqueRefs.length === 0) return null;
14435
+ return { type: "sources", sources, ...uniqueRefs.length > 0 ? { refs: uniqueRefs } : {} };
14436
+ }
14437
+
14238
14438
  // src/utils/faq-anchor.ts
14239
14439
  function faqSectionSlug(section) {
14240
14440
  return "faq-" + section.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -14295,7 +14495,7 @@ function useChatCardItem(type, id) {
14295
14495
  }
14296
14496
 
14297
14497
  // src/components/chat/mingo-info-card.tsx
14298
- import { Fragment as Fragment31, jsx as jsx91, jsxs as jsxs70 } from "react/jsx-runtime";
14498
+ import { Fragment as Fragment32, jsx as jsx91, jsxs as jsxs70 } from "react/jsx-runtime";
14299
14499
  function CardMedia({ imageSrc, imageAlt, icon }) {
14300
14500
  if (imageSrc) {
14301
14501
  return /* @__PURE__ */ jsxs70("span", { className: "relative block size-10 shrink-0", children: [
@@ -14358,7 +14558,7 @@ function MingoInfoCard({
14358
14558
  const hasMenu = !!menuGroups && menuGroups.length > 0;
14359
14559
  const isAnchor = !!anchorProps;
14360
14560
  const isInteractive = isAnchor || !!onClick;
14361
- const content = /* @__PURE__ */ jsxs70(Fragment31, { children: [
14561
+ const content = /* @__PURE__ */ jsxs70(Fragment32, { children: [
14362
14562
  hasMedia && /* @__PURE__ */ jsx91(CardMedia, { imageSrc, imageAlt, icon }),
14363
14563
  /* @__PURE__ */ jsxs70("span", { className: "flex min-w-0 flex-1 flex-col justify-center", children: [
14364
14564
  /* @__PURE__ */ jsx91("span", { className: "truncate text-ods-text-primary text-h4", children: title }),
@@ -14715,7 +14915,7 @@ function DeletedDataCard({ title, entityLabel, recoveryNote, className }) {
14715
14915
  }
14716
14916
 
14717
14917
  // src/components/chat/entity-cards/dispatch.tsx
14718
- import { Fragment as Fragment32, jsx as jsx93, jsxs as jsxs72 } from "react/jsx-runtime";
14918
+ import { Fragment as Fragment33, jsx as jsx93, jsxs as jsxs72 } from "react/jsx-runtime";
14719
14919
  function githubKindIcon(kind) {
14720
14920
  switch (kind) {
14721
14921
  case "pull_request":
@@ -14814,6 +15014,14 @@ function hubspotStatusToVariant(status) {
14814
15014
  if (s === "OPEN") return "warning";
14815
15015
  return "grey";
14816
15016
  }
15017
+ function validatedHttpsUrl(value) {
15018
+ if (typeof value !== "string") return null;
15019
+ try {
15020
+ return new URL(value).protocol === "https:" ? value : null;
15021
+ } catch {
15022
+ return null;
15023
+ }
15024
+ }
14817
15025
  function decodeVideoMarkerId(id) {
14818
15026
  const mux = id.match(/^mux-([A-Za-z0-9_-]+)$/);
14819
15027
  if (mux) {
@@ -14826,8 +15034,8 @@ function decodeVideoMarkerId(id) {
14826
15034
  try {
14827
15035
  const b64 = mp4[1].replace(/-/g, "+").replace(/_/g, "/");
14828
15036
  const url = atob(b64);
14829
- if (!/^https:\/\//.test(url)) return null;
14830
- return { videoUrl: url };
15037
+ const safeUrl = validatedHttpsUrl(url);
15038
+ return safeUrl ? { videoUrl: safeUrl } : null;
14831
15039
  } catch {
14832
15040
  return null;
14833
15041
  }
@@ -14835,6 +15043,26 @@ function decodeVideoMarkerId(id) {
14835
15043
  if (/^[A-Za-z0-9_-]{11}$/.test(id)) return { youtubeUrl: id };
14836
15044
  return null;
14837
15045
  }
15046
+ function validatedVideoMetadata(chatRef) {
15047
+ const input = chatRef.metadata ?? {};
15048
+ const metadata = {};
15049
+ for (const key of ["videoUrl", "highlightVideoUrl", "videoPoster", "highlightVideoPoster"]) {
15050
+ const url = validatedHttpsUrl(input[key]);
15051
+ if (url) metadata[key] = url;
15052
+ }
15053
+ const youtubeUrl = input.youtubeUrl;
15054
+ if (typeof youtubeUrl === "string" && /^[A-Za-z0-9_-]{11}$/.test(youtubeUrl)) {
15055
+ metadata.youtubeUrl = youtubeUrl;
15056
+ }
15057
+ const safeUrl = validatedHttpsUrl(chatRef.url);
15058
+ if (safeUrl) {
15059
+ const youtubeId2 = extractYouTubeId(safeUrl);
15060
+ delete metadata.videoUrl;
15061
+ delete metadata.youtubeUrl;
15062
+ metadata[youtubeId2 ? "youtubeUrl" : "videoUrl"] = youtubeId2 ?? safeUrl;
15063
+ }
15064
+ return Object.keys(metadata).length > 0 ? metadata : null;
15065
+ }
14838
15066
  function itemVideoMetadata(item) {
14839
15067
  const pick = (...vals) => {
14840
15068
  for (const v of vals) if (typeof v === "string" && v.length > 0) return v;
@@ -15318,7 +15546,7 @@ function RoadmapChatCard({
15318
15546
  }
15319
15547
  );
15320
15548
  }
15321
- function refHydratedEntry(docType, label, renderRef) {
15549
+ function refHydratedEntry(docType, label, renderRef, renderFromSourceMetadata = false) {
15322
15550
  return {
15323
15551
  label,
15324
15552
  bareInline: true,
@@ -15326,7 +15554,19 @@ function refHydratedEntry(docType, label, renderRef) {
15326
15554
  noComposedHref: true,
15327
15555
  fallbackHref: (item) => item?.url ?? null,
15328
15556
  skeleton: () => /* @__PURE__ */ jsx93(MingoInfoCardSkeleton, {}),
15329
- render: (item, chatRef, opts) => renderRef(fetchedItemDisplayRef(item, chatRef), opts)
15557
+ render: (item, chatRef, opts) => renderRef(fetchedItemDisplayRef(item, chatRef), opts),
15558
+ renderFromSourceMetadata
15559
+ };
15560
+ }
15561
+ function sourceMetadataCardItem(chatRef) {
15562
+ return {
15563
+ type: chatRef.type,
15564
+ id: chatRef.id,
15565
+ title: chatRef.title,
15566
+ url: chatRef.url,
15567
+ targetPlatform: chatRef.targetPlatform,
15568
+ sourceRepo: chatRef.sourceRepo,
15569
+ metadata: chatRef.metadata
15330
15570
  };
15331
15571
  }
15332
15572
  var FINANCIAL_CARD_CONFIGS = {
@@ -15478,8 +15718,18 @@ var CHAT_CARD_REGISTRY = {
15478
15718
  // config's searchFilter is deliberately fail-closed there. Same
15479
15719
  // ChatRef item shape (`buildHubspotTicketSelfRef` SSOT), same preset.
15480
15720
  hubspot_ticket_self: refHydratedEntry("hubspot_ticket_self", "HubSpot ticket (self)", (displayRef, opts) => /* @__PURE__ */ jsx93(HubspotTicketChatCard, { chatRef: displayRef, isNewTab: opts.isNewTab, discuss: opts.discuss })),
15481
- data_room_doc: refHydratedEntry("data_room_doc", "Data-room doc", (displayRef, opts) => /* @__PURE__ */ jsx93(DataRoomDocChatCard, { chatRef: displayRef, isNewTab: opts.isNewTab, discuss: opts.discuss })),
15482
- markdown: refHydratedEntry("markdown", "Doc page (markdown)", (displayRef, opts) => /* @__PURE__ */ jsx93(DataRoomDocChatCard, { chatRef: displayRef, isNewTab: opts.isNewTab, discuss: opts.discuss })),
15721
+ data_room_doc: refHydratedEntry(
15722
+ "data_room_doc",
15723
+ "Data-room doc",
15724
+ (displayRef, opts) => /* @__PURE__ */ jsx93(DataRoomDocChatCard, { chatRef: displayRef, isNewTab: opts.isNewTab, discuss: opts.discuss }),
15725
+ true
15726
+ ),
15727
+ markdown: refHydratedEntry(
15728
+ "markdown",
15729
+ "Doc page (markdown)",
15730
+ (displayRef, opts) => /* @__PURE__ */ jsx93(DataRoomDocChatCard, { chatRef: displayRef, isNewTab: opts.isNewTab, discuss: opts.discuss }),
15731
+ true
15732
+ ),
15483
15733
  // Body-synthesized video ids (`shortVideoId` content hashes / YouTube
15484
15734
  // ids) have no backing table row to fetch — ref-only by nature.
15485
15735
  video: {
@@ -15717,7 +15967,7 @@ function ChatCardLoader({
15717
15967
  children
15718
15968
  }
15719
15969
  );
15720
- const finish = (node) => entry.bareInline ? /* @__PURE__ */ jsx93(Fragment32, { children: navWrap(node) }) : /* @__PURE__ */ jsx93(
15970
+ const finish = (node) => entry.bareInline ? /* @__PURE__ */ jsx93(Fragment33, { children: navWrap(node) }) : /* @__PURE__ */ jsx93(
15721
15971
  ChatCardWithDiscuss,
15722
15972
  {
15723
15973
  chatRef: finalChatRef,
@@ -15730,8 +15980,12 @@ function ChatCardLoader({
15730
15980
  if (!fetchEntry) {
15731
15981
  return finish(entry.render(void 0, finalChatRef, renderOpts));
15732
15982
  }
15733
- if (isLoading) return /* @__PURE__ */ jsx93(Fragment32, { children: fetchEntry.skeleton?.() ?? null });
15983
+ if (isLoading) return /* @__PURE__ */ jsx93(Fragment33, { children: fetchEntry.skeleton?.() ?? null });
15734
15984
  if (!item) {
15985
+ const hasSourceMetadata = fetchEntry.renderFromSourceMetadata && isSourceMetadataCardRef(chatRef) && typeof finalChatRef.metadata?.path === "string";
15986
+ if (hasSourceMetadata) {
15987
+ return finish(fetchEntry.render(sourceMetadataCardItem(finalChatRef), finalChatRef, renderOpts));
15988
+ }
15735
15989
  if (isError) {
15736
15990
  return /* @__PURE__ */ jsx93(CardLoadFailure, { label: entry.label, id: finalChatRef.id, detail: "Failed to load" });
15737
15991
  }
@@ -15772,13 +16026,15 @@ function renderChatInlineEntityCard(chatRef, options = {}) {
15772
16026
  }
15773
16027
  );
15774
16028
  if (chatRef.type === "video") {
16029
+ const enrichedMetadata = validatedVideoMetadata(chatRef);
15775
16030
  const decoded = decodeVideoMarkerId(chatRef.id);
15776
- if (decoded) {
16031
+ const playableMetadata = enrichedMetadata ?? decoded;
16032
+ if (playableMetadata) {
15777
16033
  const displayRef = {
15778
16034
  ...chatRef,
15779
16035
  title: chatRef.title && chatRef.title !== chatRef.id ? chatRef.title : "Video",
15780
- url: chatRef.url ?? decoded.videoUrl ?? (decoded.youtubeUrl ? `https://www.youtube.com/watch?v=${decoded.youtubeUrl}` : null),
15781
- metadata: { ...chatRef.metadata ?? {}, ...decoded }
16036
+ url: chatRef.url ?? (typeof playableMetadata.videoUrl === "string" ? playableMetadata.videoUrl : null) ?? (typeof playableMetadata.youtubeUrl === "string" ? `https://www.youtube.com/watch?v=${playableMetadata.youtubeUrl}` : null),
16037
+ metadata: playableMetadata
15782
16038
  };
15783
16039
  return /* @__PURE__ */ jsx93(BlockCard, { inline: /* @__PURE__ */ jsx93(ChatInlineVideoPill, { chatRef: displayRef }), children: /* @__PURE__ */ jsx93(ChatVideoEntityCard, { chatRef: displayRef }) });
15784
16040
  }
@@ -16777,14 +17033,17 @@ function buildNatsWsUrl(apiBaseUrl, options) {
16777
17033
  // src/components/chat/hooks/use-slash-commands.ts
16778
17034
  import { useQuery as useQuery2 } from "@tanstack/react-query";
16779
17035
  import { useEffect as useEffect26, useState as useState32 } from "react";
17036
+ var COMMANDS_MAX_RESULTS = 10;
16780
17037
  async function fetchSlashCommands(prefix, signal, commandsUrl) {
16781
17038
  try {
16782
17039
  const url = new URL(commandsUrl, window.location.origin);
16783
- if (prefix) url.searchParams.set("q", prefix);
16784
17040
  const res = await embedAuthedFetch(url.toString(), { signal, headers: {} });
16785
17041
  if (!res.ok) return [];
16786
17042
  const data = await res.json();
16787
- return data.commands ?? [];
17043
+ const commands = data.commands ?? [];
17044
+ const normalizedPrefix = prefix.trim().toLowerCase();
17045
+ if (!normalizedPrefix) return commands;
17046
+ return commands.filter(({ id }) => id.startsWith(normalizedPrefix)).slice(0, COMMANDS_MAX_RESULTS);
16788
17047
  } catch (err) {
16789
17048
  if (err?.name === "AbortError") throw err;
16790
17049
  console.warn("[chat] slash-commands fetch failed, showing none:", err);
@@ -17877,6 +18136,23 @@ function approvalDisplaysInline(approvalType, displayApprovalTypes) {
17877
18136
  return !displayApprovalTypes || displayApprovalTypes.includes(approvalType);
17878
18137
  }
17879
18138
 
18139
+ // src/components/chat/utils/chat-source-metadata.ts
18140
+ function mergeChatSources(current, incoming) {
18141
+ const sourcesByIndex = new Map((current ?? []).map((source) => [source.index, source]));
18142
+ incoming.forEach((source) => {
18143
+ if (!sourcesByIndex.has(source.index)) sourcesByIndex.set(source.index, source);
18144
+ });
18145
+ return Array.from(sourcesByIndex.values());
18146
+ }
18147
+ function mergeChatRefs(current, incoming) {
18148
+ const refsByIdentity = new Map((current ?? []).map((ref) => [`${ref.type}:${ref.id}`, ref]));
18149
+ incoming.forEach((ref) => {
18150
+ const identity = `${ref.type}:${ref.id}`;
18151
+ if (!refsByIdentity.has(identity)) refsByIdentity.set(identity, ref);
18152
+ });
18153
+ return Array.from(refsByIdentity.values());
18154
+ }
18155
+
17880
18156
  // src/components/chat/stream/message-mutations.ts
17881
18157
  function nextId(role) {
17882
18158
  return `${role}-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
@@ -18736,6 +19012,8 @@ function createChatStreamReducer(options = {}) {
18736
19012
  const sourcesMap = /* @__PURE__ */ new Map();
18737
19013
  let sendCount = 0;
18738
19014
  let sseCurrentText = "";
19015
+ let pendingSources;
19016
+ let pendingRefs;
18739
19017
  let stateCache = null;
18740
19018
  function invalidate() {
18741
19019
  stateCache = null;
@@ -18797,6 +19075,17 @@ function createChatStreamReducer(options = {}) {
18797
19075
  }
18798
19076
  setMessagesInternal(stampTrailingAssistantSeq(updateTrailingAssistant(messages, segments), seq));
18799
19077
  }
19078
+ function attachPendingSourceMetadata() {
19079
+ if (!pendingSources && !pendingRefs) return;
19080
+ const last = messages[messages.length - 1];
19081
+ if (!last || last.role !== "assistant") return;
19082
+ const next = {
19083
+ ...last,
19084
+ ...pendingSources ? { sources: pendingSources } : {},
19085
+ ...pendingRefs ? { refs: pendingRefs } : {}
19086
+ };
19087
+ setMessagesInternal([...messages.slice(0, -1), next]);
19088
+ }
18800
19089
  function applyStreamStartToState() {
18801
19090
  setPhaseInternal("streaming");
18802
19091
  const adoptTail = adoptTrailingAssistant;
@@ -18915,6 +19204,8 @@ function createChatStreamReducer(options = {}) {
18915
19204
  }
18916
19205
  switch (event.type) {
18917
19206
  case "turn-start": {
19207
+ pendingSources = void 0;
19208
+ pendingRefs = void 0;
18918
19209
  isInStream = true;
18919
19210
  hasEverStreamed = true;
18920
19211
  turnStartedAt = Date.now();
@@ -18929,6 +19220,14 @@ function createChatStreamReducer(options = {}) {
18929
19220
  emit("onStreamEnd");
18930
19221
  setPhaseInternal("idle");
18931
19222
  accumulator.resetSegments();
19223
+ pendingSources = void 0;
19224
+ pendingRefs = void 0;
19225
+ break;
19226
+ }
19227
+ case "sources": {
19228
+ pendingSources = mergeChatSources(pendingSources, event.sources);
19229
+ if (event.refs) pendingRefs = mergeChatRefs(pendingRefs, event.refs);
19230
+ attachPendingSourceMetadata();
18932
19231
  break;
18933
19232
  }
18934
19233
  case "metadata": {
@@ -18957,10 +19256,12 @@ function createChatStreamReducer(options = {}) {
18957
19256
  if (isInStream || !hasEverStreamed) {
18958
19257
  emitSegments(segments);
18959
19258
  applySegmentsToState(segments, withSeqMeta(void 0));
19259
+ attachPendingSourceMetadata();
18960
19260
  } else {
18961
19261
  const delta = [{ type: kind, text: event.text }];
18962
19262
  emitSegments(delta, { append: true });
18963
19263
  applySegmentsToState(delta, withSeqMeta({ append: true }));
19264
+ attachPendingSourceMetadata();
18964
19265
  }
18965
19266
  break;
18966
19267
  }
@@ -20648,6 +20949,11 @@ function decodeNatsChunk(chunk) {
20648
20949
  return { type: "thinking-delta", text: data.text, ...seq };
20649
20950
  }
20650
20951
  return null;
20952
+ case MESSAGE_TYPE.GUIDE:
20953
+ case MESSAGE_TYPE.SOURCES: {
20954
+ const event = sourceMetadataEvent(data.payload);
20955
+ return event ? { ...event, ...seq } : null;
20956
+ }
20651
20957
  // An ask card is only an ask card with something to pick: a question and at
20652
20958
  // least one option. Anything less is dropped rather than rendered as an
20653
20959
  // empty card the user can't answer — the backend already falls back to a
@@ -20999,6 +21305,9 @@ function decodeHistoricalMessageData(data) {
20999
21305
  return { type: "thinking-delta", text: data.text };
21000
21306
  }
21001
21307
  return null;
21308
+ case MESSAGE_TYPE.GUIDE:
21309
+ case MESSAGE_TYPE.SOURCES:
21310
+ return decodeNatsChunk({ type: data.type, payload: data.payload });
21002
21311
  // Same completeness gate as the live decoder (`decodeNatsChunk`): a
21003
21312
  // persisted row without a question or without options is not a card the
21004
21313
  // user can answer, so it replays as nothing rather than as empty chrome.
@@ -21354,6 +21663,8 @@ function processHistoricalMessages(messages, options = {}) {
21354
21663
  let currentAssistantTimestamp = null;
21355
21664
  let lastAssistantId = null;
21356
21665
  let currentAssistantStreamSeq;
21666
+ let currentAssistantSources;
21667
+ let currentAssistantRefs;
21357
21668
  const flushAssistantMessage = () => {
21358
21669
  const idToUse = lastAssistantId || currentAssistantId;
21359
21670
  if (idToUse && accumulator.hasContent()) {
@@ -21366,6 +21677,8 @@ function processHistoricalMessages(messages, options = {}) {
21366
21677
  authorType: assistantType,
21367
21678
  timestamp: currentAssistantTimestamp || /* @__PURE__ */ new Date(),
21368
21679
  avatar: assistantAvatar,
21680
+ ...currentAssistantSources ? { sources: currentAssistantSources } : {},
21681
+ ...currentAssistantRefs ? { refs: currentAssistantRefs } : {},
21369
21682
  ...currentAssistantStreamSeq !== void 0 ? { streamSeq: currentAssistantStreamSeq } : {}
21370
21683
  });
21371
21684
  accumulator.resetSegments();
@@ -21374,6 +21687,8 @@ function processHistoricalMessages(messages, options = {}) {
21374
21687
  currentAssistantTimestamp = null;
21375
21688
  lastAssistantId = null;
21376
21689
  currentAssistantStreamSeq = void 0;
21690
+ currentAssistantSources = void 0;
21691
+ currentAssistantRefs = void 0;
21377
21692
  };
21378
21693
  messages.forEach((msg, index) => {
21379
21694
  if (chatTypeFilter && msg.chatType !== chatTypeFilter) return;
@@ -21417,6 +21732,11 @@ function processHistoricalMessages(messages, options = {}) {
21417
21732
  messageDataArray.forEach((data) => {
21418
21733
  const event = decodeHistoricalMessageData(data);
21419
21734
  if (!event) return;
21735
+ if (event.type === "sources") {
21736
+ currentAssistantSources = mergeChatSources(currentAssistantSources, event.sources);
21737
+ if (event.refs) currentAssistantRefs = mergeChatRefs(currentAssistantRefs, event.refs);
21738
+ return;
21739
+ }
21420
21740
  applyHistoryEvent(
21421
21741
  event,
21422
21742
  accumulator,
@@ -21509,13 +21829,17 @@ function mapProcessedToUnified(processed) {
21509
21829
  id: m.id,
21510
21830
  role: m.role,
21511
21831
  content: "",
21512
- segments: m.content
21832
+ segments: m.content,
21833
+ ...m.sources ? { sources: m.sources } : {},
21834
+ ...m.refs ? { refs: m.refs } : {}
21513
21835
  });
21514
21836
  } else {
21515
21837
  out.push({
21516
21838
  id: m.id,
21517
21839
  role: m.role,
21518
- content: m.content
21840
+ content: m.content,
21841
+ ...m.sources ? { sources: m.sources } : {},
21842
+ ...m.refs ? { refs: m.refs } : {}
21519
21843
  });
21520
21844
  }
21521
21845
  }
@@ -22799,7 +23123,7 @@ DrawerFooter.displayName = "DrawerFooter";
22799
23123
 
22800
23124
  // src/components/ui/hover-dropdown.tsx
22801
23125
  import { useState as useState43, useRef as useRef32, useCallback as useCallback24 } from "react";
22802
- import { Fragment as Fragment33, jsx as jsx96, jsxs as jsxs74 } from "react/jsx-runtime";
23126
+ import { Fragment as Fragment34, jsx as jsx96, jsxs as jsxs74 } from "react/jsx-runtime";
22803
23127
  function DefaultAnchor({ href, className, children }) {
22804
23128
  return /* @__PURE__ */ jsx96("a", { href, className, children });
22805
23129
  }
@@ -22813,6 +23137,7 @@ function HoverDropdown({
22813
23137
  }) {
22814
23138
  const [open, setOpen] = useState43(false);
22815
23139
  const timeoutRef = useRef32(void 0);
23140
+ const clickPinnedRef = useRef32(false);
22816
23141
  const containerRef = useRef32(null);
22817
23142
  const panelRef = useRef32(null);
22818
23143
  useIsomorphicLayoutEffect(() => {
@@ -22847,14 +23172,17 @@ function HoverDropdown({
22847
23172
  setOpen(true);
22848
23173
  }, []);
22849
23174
  const hide = useCallback24(() => {
23175
+ if (clickPinnedRef.current) return;
22850
23176
  timeoutRef.current = setTimeout(() => setOpen(false), hideDelay);
22851
23177
  }, [hideDelay]);
22852
23178
  const toggle = useCallback24((e) => {
22853
23179
  e.stopPropagation();
22854
23180
  clearTimeout(timeoutRef.current);
22855
- setOpen((prev) => !prev);
23181
+ const pinned = !clickPinnedRef.current;
23182
+ clickPinnedRef.current = pinned;
23183
+ setOpen(pinned);
22856
23184
  }, []);
22857
- if (items.length === 0) return /* @__PURE__ */ jsx96(Fragment33, { children });
23185
+ if (items.length === 0) return /* @__PURE__ */ jsx96(Fragment34, { children });
22858
23186
  const renderAnchorFn = renderAnchor ?? DefaultAnchor;
22859
23187
  return /* @__PURE__ */ jsxs74("div", { ref: containerRef, className: "relative inline-flex", onMouseEnter: show, onMouseLeave: hide, onClick: toggle, children: [
22860
23188
  children,
@@ -22864,7 +23192,7 @@ function HoverDropdown({
22864
23192
  className: `fixed z-[9999] max-h-[420px] min-w-[220px] max-w-[340px] overflow-y-auto rounded-lg border border-ods-border bg-ods-card p-1 shadow-xl ${className || ""}`,
22865
23193
  ref: panelRef,
22866
23194
  children: items.map((item, i) => {
22867
- const content = /* @__PURE__ */ jsxs74(Fragment33, { children: [
23195
+ const content = /* @__PURE__ */ jsxs74(Fragment34, { children: [
22868
23196
  item.icon && /* @__PURE__ */ jsx96("span", { className: "flex-shrink-0 [&_svg]:size-3.5", children: item.icon }),
22869
23197
  /* @__PURE__ */ jsx96("span", { className: "min-w-0 flex-1 truncate", children: item.label })
22870
23198
  ] });
@@ -23084,7 +23412,7 @@ function useChatDialogManager({
23084
23412
  }
23085
23413
 
23086
23414
  // src/components/chat/embeddable-chat.tsx
23087
- import { Fragment as Fragment34, jsx as jsx97, jsxs as jsxs75 } from "react/jsx-runtime";
23415
+ import { Fragment as Fragment35, jsx as jsx97, jsxs as jsxs75 } from "react/jsx-runtime";
23088
23416
  var DRAWER_DEFAULT_WIDTH_RATIO = 0.5;
23089
23417
  var DRAWER_WIDTH_KEY = "mingo-chat-width-v3";
23090
23418
  var DRAWER_DEFAULT_WIDTH_PX = 750;
@@ -23258,7 +23586,7 @@ function SourceChip({
23258
23586
  );
23259
23587
  }
23260
23588
  const displayName = src.name || formatRelativePath(src.path);
23261
- const chipBody = (label) => /* @__PURE__ */ jsxs75(Fragment34, { children: [
23589
+ const chipBody = (label) => /* @__PURE__ */ jsxs75(Fragment35, { children: [
23262
23590
  icon,
23263
23591
  /* @__PURE__ */ jsxs75("span", { className: "max-w-[160px] truncate", children: [
23264
23592
  "[",
@@ -23329,21 +23657,34 @@ function SourceChips({
23329
23657
  }) {
23330
23658
  const [expanded, setExpanded] = useState45(false);
23331
23659
  if (cited.length === 0) {
23332
- const fallback = uncited.slice(0, FALLBACK_TOP_RETRIEVED);
23333
- if (fallback.length === 0) return null;
23660
+ const visible = expanded ? uncited : uncited.slice(0, FALLBACK_TOP_RETRIEVED);
23661
+ const hiddenCount2 = Math.max(0, uncited.length - FALLBACK_TOP_RETRIEVED);
23662
+ if (visible.length === 0) return null;
23334
23663
  return /* @__PURE__ */ jsxs75("div", { className: "mt-2 flex flex-col gap-1.5 border-t border-ods-border pt-2", children: [
23335
23664
  /* @__PURE__ */ jsx97("span", { className: "uppercase text-ods-text-muted text-h6", children: "Top retrieved sources" }),
23336
- /* @__PURE__ */ jsx97("div", { className: "flex flex-wrap gap-1.5", children: fallback.map((src) => /* @__PURE__ */ jsx97(
23337
- SourceChip,
23338
- {
23339
- src,
23340
- baseRoute,
23341
- chipBasePlatform,
23342
- onClose,
23343
- onDiscuss
23344
- },
23345
- src.index
23346
- )) })
23665
+ /* @__PURE__ */ jsxs75("div", { className: `flex flex-wrap gap-1.5 ${expanded ? "max-h-[200px] overflow-y-auto overscroll-contain" : ""}`, children: [
23666
+ visible.map((src) => /* @__PURE__ */ jsx97(
23667
+ SourceChip,
23668
+ {
23669
+ src,
23670
+ baseRoute,
23671
+ chipBasePlatform,
23672
+ onClose,
23673
+ onDiscuss
23674
+ },
23675
+ src.index
23676
+ )),
23677
+ hiddenCount2 > 0 && /* @__PURE__ */ jsx97(
23678
+ "button",
23679
+ {
23680
+ onClick: () => setExpanded(!expanded),
23681
+ className: "inline-flex cursor-pointer items-center gap-1 rounded border border-ods-accent bg-ods-card px-2 py-0.5 text-ods-accent transition-colors text-h6 hover:bg-ods-accent/10",
23682
+ "aria-expanded": expanded,
23683
+ "aria-label": expanded ? "Show fewer sources" : `Show ${hiddenCount2} additional retrieved sources`,
23684
+ children: expanded ? "Show less" : `+${hiddenCount2} more retrieved ${hiddenCount2 === 1 ? "source" : "sources"}`
23685
+ }
23686
+ )
23687
+ ] })
23347
23688
  ] });
23348
23689
  }
23349
23690
  const hiddenCount = uncited.length;
@@ -23770,6 +24111,10 @@ function EmbeddableChatInner({
23770
24111
  // explicitly — see `Message.hidden` and `chat-message-list`'s skip.
23771
24112
  ...m.hidden ? { hidden: true } : {},
23772
24113
  ...m.scrollAnchor ? { scrollAnchor: m.scrollAnchor } : {},
24114
+ // Preserve same-turn rich metadata. Source chips and entity-card
24115
+ // renderers consume it after this raw-to-Message projection.
24116
+ ...m.sources && m.sources.length > 0 ? { sources: m.sources } : {},
24117
+ ...m.refs && m.refs.length > 0 ? { refs: m.refs } : {},
23773
24118
  // Forward attached context items so the user bubble renders its chips.
23774
24119
  ...m.contextItems && m.contextItems.length > 0 ? { contextItems: m.contextItems } : {}
23775
24120
  };
@@ -23921,21 +24266,32 @@ function EmbeddableChatInner({
23921
24266
  });
23922
24267
  return out;
23923
24268
  }, [commandsById, enabledSet]);
23924
- const lastAssistantMsg = [...rawMessages].reverse().find((m) => m.role === "assistant");
23925
- const lastSources = useMemo29(() => {
23926
- if (chatLoading) return void 0;
23927
- const sources = lastAssistantMsg?.sources;
23928
- if (!sources || sources.length === 0) return void 0;
23929
- const content = lastAssistantMsg?.content || "";
23930
- const citationOrder = [...content.matchAll(/\[(\d+)\]/g)].map((m) => parseInt(m[1], 10));
23931
- const seenOrder = /* @__PURE__ */ new Map();
23932
- citationOrder.forEach((idx) => {
23933
- if (!seenOrder.has(idx)) seenOrder.set(idx, seenOrder.size);
23934
- });
23935
- const cited = sources.filter((s) => seenOrder.has(s.index)).sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
23936
- const uncited = sources.filter((s) => !seenOrder.has(s.index));
23937
- return { cited, uncited };
23938
- }, [lastAssistantMsg, chatLoading]);
24269
+ const renderSourcesAfterMessage = useCallback26(
24270
+ (message, index) => {
24271
+ if (message.role !== "assistant" || !message.sources?.length) return null;
24272
+ if (chatLoading && index === messages.length - 1) return null;
24273
+ const content = typeof message.content === "string" ? message.content : message.content.filter((segment) => segment.type === "text").map((segment) => segment.text).join("\n");
24274
+ const citationOrder = [...content.matchAll(/\[(\d+)\]/g)].map((m) => parseInt(m[1], 10));
24275
+ const seenOrder = /* @__PURE__ */ new Map();
24276
+ citationOrder.forEach((idx) => {
24277
+ if (!seenOrder.has(idx)) seenOrder.set(idx, seenOrder.size);
24278
+ });
24279
+ const cited = message.sources.filter((s) => seenOrder.has(s.index)).sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0));
24280
+ const uncited = message.sources.filter((s) => !seenOrder.has(s.index));
24281
+ return /* @__PURE__ */ jsx97("div", { className: "flex-shrink-0 pb-2", children: /* @__PURE__ */ jsx97(
24282
+ SourceChips,
24283
+ {
24284
+ cited,
24285
+ uncited,
24286
+ baseRoute: resolvedBaseRoute,
24287
+ chipBasePlatform,
24288
+ onClose: handleNavigationClose,
24289
+ onDiscuss: discussRef
24290
+ }
24291
+ ) });
24292
+ },
24293
+ [chatLoading, messages.length, resolvedBaseRoute, chipBasePlatform, handleNavigationClose, discussRef]
24294
+ );
23939
24295
  const [portalHost, setPortalHost] = useState45(null);
23940
24296
  const [panelBoundary, setPanelBoundary] = useState45(null);
23941
24297
  const [panelWidth, setPanelWidth] = useState45(0);
@@ -24091,7 +24447,7 @@ function EmbeddableChatInner({
24091
24447
  // renders in the rail below); the chat block is untouched.
24092
24448
  // Back is a full-height leading cell, matching the other
24093
24449
  // header cells (not a small inline chevron).
24094
- /* @__PURE__ */ jsxs75(Fragment34, { children: [
24450
+ /* @__PURE__ */ jsxs75(Fragment35, { children: [
24095
24451
  /* @__PURE__ */ jsx97(ChatHeaderIconButton, { divider: "right", onClick: closeArchive, "aria-label": "Back to chats", children: /* @__PURE__ */ jsx97(Chevron02LeftIcon, { size: 24 }) }),
24096
24452
  /* @__PURE__ */ jsx97("div", { className: "flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]", children: /* @__PURE__ */ jsx97("span", { className: "min-w-0 flex-1 truncate text-ods-text-primary text-h3", children: "Chat Archive" }) })
24097
24453
  ] })
@@ -24100,7 +24456,7 @@ function EmbeddableChatInner({
24100
24456
  // search + archive controls. When search is open the
24101
24457
  // inline field takes over the title area in place
24102
24458
  // (Figma 116:51217) and the magnifier toggle is hidden.
24103
- /* @__PURE__ */ jsxs75(Fragment34, { children: [
24459
+ /* @__PURE__ */ jsxs75(Fragment35, { children: [
24104
24460
  railSearchOpen && mingoCaps.onSearchChange ? /* @__PURE__ */ jsx97(
24105
24461
  ChatHeaderSearchField,
24106
24462
  {
@@ -24108,7 +24464,7 @@ function EmbeddableChatInner({
24108
24464
  onSearchChange: mingoCaps.onSearchChange,
24109
24465
  onCollapse: () => setRailSearchOpen(false)
24110
24466
  }
24111
- ) : /* @__PURE__ */ jsxs75(Fragment34, { children: [
24467
+ ) : /* @__PURE__ */ jsxs75(Fragment35, { children: [
24112
24468
  /* @__PURE__ */ jsx97("div", { className: "flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]", children: /* @__PURE__ */ jsx97("span", { className: "min-w-0 flex-1 truncate text-ods-text-primary text-h3", children: "Current Chats" }) }),
24113
24469
  mingoCaps.onSearchChange && /* @__PURE__ */ jsx97(
24114
24470
  ChatHeaderIconButton,
@@ -24247,61 +24603,49 @@ function EmbeddableChatInner({
24247
24603
  onRetry: reloadDialogs
24248
24604
  }
24249
24605
  )
24250
- ) : /* @__PURE__ */ jsxs75(Fragment34, { children: [
24606
+ ) : /* @__PURE__ */ jsxs75(Fragment35, { children: [
24251
24607
  /* @__PURE__ */ jsx97("div", { ref: galleryPanelRef, className: "flex min-h-0 flex-1 flex-col p-[var(--spacing-system-m)]", children: /* @__PURE__ */ jsx97(
24252
24608
  "div",
24253
24609
  {
24254
24610
  className: "flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0",
24255
- children: hasConversation ? /* @__PURE__ */ jsxs75("div", { className: "flex min-h-0 flex-1 flex-col", children: [
24256
- messagesInitialLoading ? (
24257
- // Match the real list's `fullWidth` layout so swapping
24258
- // skeleton → bubbles doesn't shift the column. The panel
24259
- // wrapper above already pads (`p-[var(--spacing-system-m)]`),
24260
- // so the skeleton sits flush (no inner px/pb).
24261
- //
24262
- // In `previewMode` the chat lives in a fixed-height host box
24263
- // (e.g. the Mingo hero demo). The default bottom-anchored
24264
- // skeleton overflows a short panel and grows its OWN
24265
- // scrollbar — ugly. `fill` top-anchors + clips overflow so
24266
- // it reads as "the whole panel is loading" and never scrolls.
24267
- /* @__PURE__ */ jsx97(ChatMessageListSkeleton, { fullWidth: true, fill: previewMode, className: "flex-1" })
24268
- ) : /* @__PURE__ */ jsx97(
24269
- ChatMessageList,
24270
- {
24271
- messages,
24272
- isTyping: chatLoading,
24273
- pendingApprovals,
24274
- autoScroll: !previewMode,
24275
- pinBottom: previewMode,
24276
- overscrollContain: !previewMode,
24277
- assistantType: "mingo",
24278
- assistantIcon: mingoAssistantIcon,
24279
- renderEntityCard,
24280
- resolveContextIcon,
24281
- renderContextItem,
24282
- renderMention,
24283
- onAskSelect: chatLoading || previewMode ? void 0 : handleAskSelect,
24284
- NavLinkAnchor: NavLinkAnchorViaRuntime,
24285
- className: previewMode ? "pointer-events-auto flex-1" : "flex-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
24286
- contentClassName: "max-w-none pb-0",
24287
- fullWidth: true,
24288
- hasNextPage: hasMoreMessages,
24289
- isFetchingNextPage: isMessagesLoading,
24290
- onLoadMore: loadMoreMessages
24291
- }
24292
- ),
24293
- lastSources && (lastSources.cited.length > 0 || lastSources.uncited.length > 0) && !chatLoading && /* @__PURE__ */ jsx97("div", { className: "flex-shrink-0 pb-2", children: /* @__PURE__ */ jsx97(
24294
- SourceChips,
24295
- {
24296
- cited: lastSources.cited,
24297
- uncited: lastSources.uncited,
24298
- baseRoute: resolvedBaseRoute,
24299
- chipBasePlatform,
24300
- onClose: handleNavigationClose,
24301
- onDiscuss: discussRef
24302
- }
24303
- ) })
24304
- ] }) : activeMode === "mingo" ? (
24611
+ children: hasConversation ? /* @__PURE__ */ jsx97("div", { className: "flex min-h-0 flex-1 flex-col", children: messagesInitialLoading ? (
24612
+ // Match the real list's `fullWidth` layout so swapping
24613
+ // skeleton → bubbles doesn't shift the column. The panel
24614
+ // wrapper above already pads (`p-[var(--spacing-system-m)]`),
24615
+ // so the skeleton sits flush (no inner px/pb).
24616
+ //
24617
+ // In `previewMode` the chat lives in a fixed-height host box
24618
+ // (e.g. the Mingo hero demo). The default bottom-anchored
24619
+ // skeleton overflows a short panel and grows its OWN
24620
+ // scrollbar — ugly. `fill` top-anchors + clips overflow so
24621
+ // it reads as "the whole panel is loading" and never scrolls.
24622
+ /* @__PURE__ */ jsx97(ChatMessageListSkeleton, { fullWidth: true, fill: previewMode, className: "flex-1" })
24623
+ ) : /* @__PURE__ */ jsx97(
24624
+ ChatMessageList,
24625
+ {
24626
+ messages,
24627
+ isTyping: chatLoading,
24628
+ pendingApprovals,
24629
+ autoScroll: !previewMode,
24630
+ pinBottom: previewMode,
24631
+ overscrollContain: !previewMode,
24632
+ assistantType: "mingo",
24633
+ assistantIcon: mingoAssistantIcon,
24634
+ renderEntityCard,
24635
+ resolveContextIcon,
24636
+ renderContextItem,
24637
+ renderMention,
24638
+ renderAfterMessage: renderSourcesAfterMessage,
24639
+ onAskSelect: chatLoading || previewMode ? void 0 : handleAskSelect,
24640
+ NavLinkAnchor: NavLinkAnchorViaRuntime,
24641
+ className: previewMode ? "pointer-events-auto flex-1" : "flex-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
24642
+ contentClassName: "max-w-none pb-0",
24643
+ fullWidth: true,
24644
+ hasNextPage: hasMoreMessages,
24645
+ isFetchingNextPage: isMessagesLoading,
24646
+ onLoadMore: loadMoreMessages
24647
+ }
24648
+ ) }) : activeMode === "mingo" ? (
24305
24649
  /* Figma node 7532:222444 — default (Mingo-mode) empty state:
24306
24650
  centred greeting + capability grid + Guide-chat promo +
24307
24651
  quick-action chips. Guide mode keeps the slash-command
@@ -24474,7 +24818,7 @@ function EmbeddableChatInner({
24474
24818
  ]
24475
24819
  }
24476
24820
  ) }) });
24477
- return /* @__PURE__ */ jsxs75(Fragment34, { children: [
24821
+ return /* @__PURE__ */ jsxs75(Fragment35, { children: [
24478
24822
  showInternalTrigger && !shellLess && /* @__PURE__ */ jsx97(
24479
24823
  "div",
24480
24824
  {
@@ -24863,4 +25207,4 @@ export {
24863
25207
  buildStreamFrames,
24864
25208
  EmbeddableChat
24865
25209
  };
24866
- //# sourceMappingURL=chunk-D7YISHXC.js.map
25210
+ //# sourceMappingURL=chunk-4MVGFYRE.js.map