@seed-ship/mcp-ui-solid 6.19.1 → 6.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/CHANGELOG.md +519 -0
  2. package/README.md +791 -1
  3. package/dist/adapters/connector.cjs +15 -4
  4. package/dist/adapters/connector.cjs.map +1 -1
  5. package/dist/adapters/connector.d.ts +38 -0
  6. package/dist/adapters/connector.d.ts.map +1 -1
  7. package/dist/adapters/connector.js +15 -4
  8. package/dist/adapters/connector.js.map +1 -1
  9. package/dist/adapters/index.d.ts +4 -3
  10. package/dist/adapters/index.d.ts.map +1 -1
  11. package/dist/adapters/macro-run.cjs +25 -13
  12. package/dist/adapters/macro-run.cjs.map +1 -1
  13. package/dist/adapters/macro-run.d.ts +37 -2
  14. package/dist/adapters/macro-run.d.ts.map +1 -1
  15. package/dist/adapters/macro-run.js +25 -13
  16. package/dist/adapters/macro-run.js.map +1 -1
  17. package/dist/adapters.cjs +2 -0
  18. package/dist/adapters.cjs.map +1 -1
  19. package/dist/adapters.js +4 -2
  20. package/dist/components/ActionGroupRenderer.cjs +3 -1
  21. package/dist/components/ActionGroupRenderer.cjs.map +1 -1
  22. package/dist/components/ActionGroupRenderer.d.ts.map +1 -1
  23. package/dist/components/ActionGroupRenderer.js +3 -1
  24. package/dist/components/ActionGroupRenderer.js.map +1 -1
  25. package/dist/components/AgentCard.cjs +11 -8
  26. package/dist/components/AgentCard.cjs.map +1 -1
  27. package/dist/components/AgentCard.d.ts.map +1 -1
  28. package/dist/components/AgentCard.js +11 -8
  29. package/dist/components/AgentCard.js.map +1 -1
  30. package/dist/components/AgentHandoff.cjs +6 -1
  31. package/dist/components/AgentHandoff.cjs.map +1 -1
  32. package/dist/components/AgentHandoff.d.ts.map +1 -1
  33. package/dist/components/AgentHandoff.js +6 -1
  34. package/dist/components/AgentHandoff.js.map +1 -1
  35. package/dist/components/ArtifactRenderer.cjs +25 -6
  36. package/dist/components/ArtifactRenderer.cjs.map +1 -1
  37. package/dist/components/ArtifactRenderer.d.ts.map +1 -1
  38. package/dist/components/ArtifactRenderer.js +25 -6
  39. package/dist/components/ArtifactRenderer.js.map +1 -1
  40. package/dist/components/AutocompleteDropdown.cjs +30 -24
  41. package/dist/components/AutocompleteDropdown.cjs.map +1 -1
  42. package/dist/components/AutocompleteDropdown.d.ts.map +1 -1
  43. package/dist/components/AutocompleteDropdown.js +30 -24
  44. package/dist/components/AutocompleteDropdown.js.map +1 -1
  45. package/dist/components/AutocompleteFormField.cjs +3 -1
  46. package/dist/components/AutocompleteFormField.cjs.map +1 -1
  47. package/dist/components/AutocompleteFormField.d.ts.map +1 -1
  48. package/dist/components/AutocompleteFormField.js +4 -2
  49. package/dist/components/AutocompleteFormField.js.map +1 -1
  50. package/dist/components/BriefingDiff.cjs +45 -39
  51. package/dist/components/BriefingDiff.cjs.map +1 -1
  52. package/dist/components/BriefingDiff.d.ts.map +1 -1
  53. package/dist/components/BriefingDiff.js +45 -39
  54. package/dist/components/BriefingDiff.js.map +1 -1
  55. package/dist/components/CarouselRenderer.cjs +8 -2
  56. package/dist/components/CarouselRenderer.cjs.map +1 -1
  57. package/dist/components/CarouselRenderer.d.ts.map +1 -1
  58. package/dist/components/CarouselRenderer.js +8 -2
  59. package/dist/components/CarouselRenderer.js.map +1 -1
  60. package/dist/components/ChartJSRenderer.cjs +60 -36
  61. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  62. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  63. package/dist/components/ChartJSRenderer.js +61 -37
  64. package/dist/components/ChartJSRenderer.js.map +1 -1
  65. package/dist/components/ChatPrompt.cjs +15 -9
  66. package/dist/components/ChatPrompt.cjs.map +1 -1
  67. package/dist/components/ChatPrompt.d.ts.map +1 -1
  68. package/dist/components/ChatPrompt.js +15 -9
  69. package/dist/components/ChatPrompt.js.map +1 -1
  70. package/dist/components/CodeBlockRenderer.cjs +34 -16
  71. package/dist/components/CodeBlockRenderer.cjs.map +1 -1
  72. package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
  73. package/dist/components/CodeBlockRenderer.js +34 -16
  74. package/dist/components/CodeBlockRenderer.js.map +1 -1
  75. package/dist/components/DataPreviewSection.cjs +91 -44
  76. package/dist/components/DataPreviewSection.cjs.map +1 -1
  77. package/dist/components/DataPreviewSection.d.ts.map +1 -1
  78. package/dist/components/DataPreviewSection.js +92 -45
  79. package/dist/components/DataPreviewSection.js.map +1 -1
  80. package/dist/components/DegradedFallback.cjs +20 -16
  81. package/dist/components/DegradedFallback.cjs.map +1 -1
  82. package/dist/components/DegradedFallback.d.ts.map +1 -1
  83. package/dist/components/DegradedFallback.js +20 -16
  84. package/dist/components/DegradedFallback.js.map +1 -1
  85. package/dist/components/FooterRenderer.cjs +21 -17
  86. package/dist/components/FooterRenderer.cjs.map +1 -1
  87. package/dist/components/FooterRenderer.d.ts.map +1 -1
  88. package/dist/components/FooterRenderer.js +21 -17
  89. package/dist/components/FooterRenderer.js.map +1 -1
  90. package/dist/components/FormFieldRenderer.cjs +116 -93
  91. package/dist/components/FormFieldRenderer.cjs.map +1 -1
  92. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  93. package/dist/components/FormFieldRenderer.js +116 -93
  94. package/dist/components/FormFieldRenderer.js.map +1 -1
  95. package/dist/components/FormRenderer.cjs +55 -31
  96. package/dist/components/FormRenderer.cjs.map +1 -1
  97. package/dist/components/FormRenderer.d.ts.map +1 -1
  98. package/dist/components/FormRenderer.js +55 -31
  99. package/dist/components/FormRenderer.js.map +1 -1
  100. package/dist/components/GenerativeUIErrorBoundary.cjs +20 -15
  101. package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
  102. package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
  103. package/dist/components/GenerativeUIErrorBoundary.js +20 -15
  104. package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
  105. package/dist/components/GraphRenderer.cjs +56 -33
  106. package/dist/components/GraphRenderer.cjs.map +1 -1
  107. package/dist/components/GraphRenderer.d.ts.map +1 -1
  108. package/dist/components/GraphRenderer.js +56 -33
  109. package/dist/components/GraphRenderer.js.map +1 -1
  110. package/dist/components/ImageGalleryRenderer.cjs +13 -4
  111. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  112. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  113. package/dist/components/ImageGalleryRenderer.js +13 -4
  114. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  115. package/dist/components/LightboxOverlay.cjs +16 -8
  116. package/dist/components/LightboxOverlay.cjs.map +1 -1
  117. package/dist/components/LightboxOverlay.d.ts.map +1 -1
  118. package/dist/components/LightboxOverlay.js +17 -9
  119. package/dist/components/LightboxOverlay.js.map +1 -1
  120. package/dist/components/MapRenderer.cjs +59 -29
  121. package/dist/components/MapRenderer.cjs.map +1 -1
  122. package/dist/components/MapRenderer.d.ts +10 -2
  123. package/dist/components/MapRenderer.d.ts.map +1 -1
  124. package/dist/components/MapRenderer.js +60 -30
  125. package/dist/components/MapRenderer.js.map +1 -1
  126. package/dist/components/ModalRenderer.cjs +4 -1
  127. package/dist/components/ModalRenderer.cjs.map +1 -1
  128. package/dist/components/ModalRenderer.d.ts.map +1 -1
  129. package/dist/components/ModalRenderer.js +5 -2
  130. package/dist/components/ModalRenderer.js.map +1 -1
  131. package/dist/components/ScratchpadPanel.cjs +578 -535
  132. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  133. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  134. package/dist/components/ScratchpadPanel.js +578 -535
  135. package/dist/components/ScratchpadPanel.js.map +1 -1
  136. package/dist/components/StreamingUIRenderer.cjs +92 -28
  137. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  138. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  139. package/dist/components/StreamingUIRenderer.js +92 -28
  140. package/dist/components/StreamingUIRenderer.js.map +1 -1
  141. package/dist/components/UIResourceRenderer.cjs +456 -363
  142. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  143. package/dist/components/UIResourceRenderer.d.ts +21 -0
  144. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  145. package/dist/components/UIResourceRenderer.js +458 -365
  146. package/dist/components/UIResourceRenderer.js.map +1 -1
  147. package/dist/components/VerifiedText.cjs +39 -20
  148. package/dist/components/VerifiedText.cjs.map +1 -1
  149. package/dist/components/VerifiedText.d.ts +2 -1
  150. package/dist/components/VerifiedText.d.ts.map +1 -1
  151. package/dist/components/VerifiedText.js +39 -20
  152. package/dist/components/VerifiedText.js.map +1 -1
  153. package/dist/components/VideoRenderer.cjs +10 -5
  154. package/dist/components/VideoRenderer.cjs.map +1 -1
  155. package/dist/components/VideoRenderer.d.ts.map +1 -1
  156. package/dist/components/VideoRenderer.js +10 -5
  157. package/dist/components/VideoRenderer.js.map +1 -1
  158. package/dist/components/chart-data-table.cjs +38 -17
  159. package/dist/components/chart-data-table.cjs.map +1 -1
  160. package/dist/components/chart-data-table.d.ts +30 -1
  161. package/dist/components/chart-data-table.d.ts.map +1 -1
  162. package/dist/components/chart-data-table.js +38 -17
  163. package/dist/components/chart-data-table.js.map +1 -1
  164. package/dist/context/MCPActionContext.cjs +3 -1
  165. package/dist/context/MCPActionContext.cjs.map +1 -1
  166. package/dist/context/MCPActionContext.d.ts.map +1 -1
  167. package/dist/context/MCPActionContext.js +3 -1
  168. package/dist/context/MCPActionContext.js.map +1 -1
  169. package/dist/context/MCPUIStringsContext.cjs +285 -2
  170. package/dist/context/MCPUIStringsContext.cjs.map +1 -1
  171. package/dist/context/MCPUIStringsContext.d.ts +600 -3
  172. package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
  173. package/dist/context/MCPUIStringsContext.js +285 -2
  174. package/dist/context/MCPUIStringsContext.js.map +1 -1
  175. package/dist/hooks/index.d.ts +2 -2
  176. package/dist/hooks/index.d.ts.map +1 -1
  177. package/dist/hooks/useStreamingUI.cjs +28 -10
  178. package/dist/hooks/useStreamingUI.cjs.map +1 -1
  179. package/dist/hooks/useStreamingUI.d.ts +37 -0
  180. package/dist/hooks/useStreamingUI.d.ts.map +1 -1
  181. package/dist/hooks/useStreamingUI.js +28 -10
  182. package/dist/hooks/useStreamingUI.js.map +1 -1
  183. package/dist/hooks.cjs +1 -0
  184. package/dist/hooks.cjs.map +1 -1
  185. package/dist/hooks.js +2 -1
  186. package/dist/index.cjs +23 -8
  187. package/dist/index.cjs.map +1 -1
  188. package/dist/index.d.cts +9 -3
  189. package/dist/index.d.ts +9 -3
  190. package/dist/index.d.ts.map +1 -1
  191. package/dist/index.js +18 -3
  192. package/dist/index.js.map +1 -1
  193. package/dist/services/validation.cjs +41 -16
  194. package/dist/services/validation.cjs.map +1 -1
  195. package/dist/services/validation.d.ts +47 -2
  196. package/dist/services/validation.d.ts.map +1 -1
  197. package/dist/services/validation.js +41 -16
  198. package/dist/services/validation.js.map +1 -1
  199. package/dist/utils/degraded-projections.cjs +37 -12
  200. package/dist/utils/degraded-projections.cjs.map +1 -1
  201. package/dist/utils/degraded-projections.d.ts +39 -3
  202. package/dist/utils/degraded-projections.d.ts.map +1 -1
  203. package/dist/utils/degraded-projections.js +37 -12
  204. package/dist/utils/degraded-projections.js.map +1 -1
  205. package/dist/utils/format-string.cjs +10 -0
  206. package/dist/utils/format-string.cjs.map +1 -0
  207. package/dist/utils/format-string.d.ts +26 -0
  208. package/dist/utils/format-string.d.ts.map +1 -0
  209. package/dist/utils/format-string.js +10 -0
  210. package/dist/utils/format-string.js.map +1 -0
  211. package/dist/validation.cjs +3 -0
  212. package/dist/validation.cjs.map +1 -1
  213. package/dist/validation.d.cts +2 -1
  214. package/dist/validation.d.ts +2 -1
  215. package/dist/validation.d.ts.map +1 -1
  216. package/dist/validation.js +4 -1
  217. package/package.json +1 -1
  218. package/src/adapters/connector.ts +58 -4
  219. package/src/adapters/index.ts +12 -2
  220. package/src/adapters/macro-run.ts +71 -11
  221. package/src/components/ActionGroupRenderer.tsx +3 -1
  222. package/src/components/AgentCard.tsx +27 -9
  223. package/src/components/AgentHandoff.tsx +4 -1
  224. package/src/components/ArtifactRenderer.tsx +17 -5
  225. package/src/components/AutocompleteDropdown.tsx +8 -6
  226. package/src/components/AutocompleteFormField.tsx +5 -1
  227. package/src/components/BriefingDiff.tsx +11 -3
  228. package/src/components/CarouselRenderer.tsx +4 -2
  229. package/src/components/ChartJSRenderer.tsx +18 -9
  230. package/src/components/ChatPrompt.tsx +13 -8
  231. package/src/components/CodeBlockRenderer.tsx +12 -10
  232. package/src/components/DataPreviewSection.tsx +92 -26
  233. package/src/components/DegradedFallback.tsx +4 -2
  234. package/src/components/FooterRenderer.tsx +3 -1
  235. package/src/components/FormFieldRenderer.tsx +34 -19
  236. package/src/components/FormRenderer.tsx +38 -10
  237. package/src/components/GenerativeUIErrorBoundary.tsx +8 -3
  238. package/src/components/GraphRenderer.tsx +31 -16
  239. package/src/components/ImageGalleryRenderer.tsx +8 -4
  240. package/src/components/LightboxOverlay.tsx +6 -4
  241. package/src/components/MapRenderer.tsx +57 -20
  242. package/src/components/ModalRenderer.tsx +3 -1
  243. package/src/components/ScratchpadPanel.tsx +95 -44
  244. package/src/components/StreamingUIRenderer.tsx +41 -11
  245. package/src/components/UIResourceRenderer.tsx +145 -63
  246. package/src/components/VerifiedText.tsx +19 -9
  247. package/src/components/VideoRenderer.tsx +6 -4
  248. package/src/components/chart-data-table.ts +68 -14
  249. package/src/context/MCPActionContext.tsx +4 -1
  250. package/src/context/MCPUIStringsContext.tsx +1004 -1
  251. package/src/hooks/index.ts +2 -1
  252. package/src/hooks/useStreamingUI.ts +64 -10
  253. package/src/index.ts +23 -0
  254. package/src/services/validation.ts +93 -16
  255. package/src/utils/degraded-projections.ts +75 -12
  256. package/src/utils/format-string.ts +33 -0
  257. package/src/validation.ts +5 -0
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
- var _tmpl$ = /* @__PURE__ */ web.template(`<div class="verified-text text-sm leading-relaxed"><!$><!/><!$><!/>`), _tmpl$2 = /* @__PURE__ */ web.template(`<span>`), _tmpl$3 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300"title="Verified against source data"><!$><!/><span class="text-xs opacity-70"aria-label=verified>&#x2705;`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs">[non v&eacute;rifi&eacute;]`), _tmpl$5 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help"><!$><!/><span class=text-xs aria-label=unverified>&#x26A0;&#xFE0F;`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"><div class="flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden"><div class="h-full rounded-full transition-all"></div></div><span><!$><!/>% verified (<!$><!/> unverified)`);
5
+ const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
6
+ const formatString = require("../utils/format-string.cjs");
7
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="verified-text text-sm leading-relaxed"><!$><!/><!$><!/>`), _tmpl$2 = /* @__PURE__ */ web.template(`<span>`), _tmpl$3 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300"><!$><!/><span class="text-xs opacity-70">&#x2705;`), _tmpl$4 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs">`), _tmpl$5 = /* @__PURE__ */ web.template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help"><!$><!/><span class=text-xs>&#x26A0;&#xFE0F;`), _tmpl$6 = /* @__PURE__ */ web.template(`<div class="mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"><div class="flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden"><div class="h-full rounded-full transition-all"></div></div><span><!$><!/> <!$><!/>`);
6
8
  function buildAnnotatedSegments(text, validation) {
7
9
  const hallucinatedPositions = /* @__PURE__ */ new Map();
8
10
  for (const h of validation.hallucinated) {
@@ -63,6 +65,7 @@ function buildAnnotatedSegments(text, validation) {
63
65
  return segments;
64
66
  }
65
67
  function VerifiedText(props) {
68
+ const strings = MCPUIStringsContext.useMCPUIStrings();
66
69
  const mode = () => props.mode || "highlight";
67
70
  const segments = solidJs.createMemo(() => {
68
71
  if (!props.validation || props.validation.valid) {
@@ -89,42 +92,55 @@ function VerifiedText(props) {
89
92
  }
90
93
  if (seg.type === "verified") {
91
94
  return (() => {
92
- var _el$7 = web.getNextElement(_tmpl$3), _el$9 = _el$7.firstChild, [_el$0, _co$3] = web.getNextMarker(_el$9.nextSibling);
93
- _el$0.nextSibling;
95
+ var _el$7 = web.getNextElement(_tmpl$3), _el$9 = _el$7.firstChild, [_el$0, _co$3] = web.getNextMarker(_el$9.nextSibling), _el$8 = _el$0.nextSibling;
94
96
  web.insert(_el$7, () => seg.content, _el$0, _co$3);
97
+ web.effect((_p$) => {
98
+ var _v$ = strings.verifiedTitle, _v$2 = strings.verifiedAria;
99
+ _v$ !== _p$.e && web.setAttribute(_el$7, "title", _p$.e = _v$);
100
+ _v$2 !== _p$.t && web.setAttribute(_el$8, "aria-label", _p$.t = _v$2);
101
+ return _p$;
102
+ }, {
103
+ e: void 0,
104
+ t: void 0
105
+ });
95
106
  return _el$7;
96
107
  })();
97
108
  }
98
109
  const h = seg.item;
99
110
  const tooltipText = () => {
100
111
  if (h.closest != null && h.distance != null) {
101
- return `Not found in source data. Closest: ${h.closest} (${Math.round(h.distance * 100)}% off)`;
112
+ return formatString.formatMCPUIString(strings.verifiedNotFoundClosest, {
113
+ closest: h.closest,
114
+ pct: Math.round(h.distance * 100)
115
+ });
102
116
  }
103
- return "Not found in source data";
117
+ return strings.verifiedNotFound;
104
118
  };
105
119
  if (mode() === "strip") {
106
120
  return (() => {
107
121
  var _el$1 = web.getNextElement(_tmpl$4);
122
+ web.insert(_el$1, () => strings.verifiedStripLabel);
108
123
  web.effect(() => web.setAttribute(_el$1, "title", tooltipText()));
109
124
  return _el$1;
110
125
  })();
111
126
  }
112
127
  return (() => {
113
- var _el$10 = web.getNextElement(_tmpl$5), _el$12 = _el$10.firstChild, [_el$13, _co$4] = web.getNextMarker(_el$12.nextSibling);
114
- _el$13.nextSibling;
128
+ var _el$10 = web.getNextElement(_tmpl$5), _el$12 = _el$10.firstChild, [_el$13, _co$4] = web.getNextMarker(_el$12.nextSibling), _el$11 = _el$13.nextSibling;
115
129
  _el$10.$$click = () => {
116
130
  var _a;
117
131
  return (_a = props.onHallucinationClick) == null ? void 0 : _a.call(props, h);
118
132
  };
119
133
  web.insert(_el$10, () => seg.content, _el$13, _co$4);
120
134
  web.effect((_p$) => {
121
- var _v$ = tooltipText(), _v$2 = props.onHallucinationClick ? "button" : void 0;
122
- _v$ !== _p$.e && web.setAttribute(_el$10, "title", _p$.e = _v$);
123
- _v$2 !== _p$.t && web.setAttribute(_el$10, "role", _p$.t = _v$2);
135
+ var _v$3 = tooltipText(), _v$4 = props.onHallucinationClick ? "button" : void 0, _v$5 = strings.unverifiedAria;
136
+ _v$3 !== _p$.e && web.setAttribute(_el$10, "title", _p$.e = _v$3);
137
+ _v$4 !== _p$.t && web.setAttribute(_el$10, "role", _p$.t = _v$4);
138
+ _v$5 !== _p$.a && web.setAttribute(_el$11, "aria-label", _p$.a = _v$5);
124
139
  return _p$;
125
140
  }, {
126
141
  e: void 0,
127
- t: void 0
142
+ t: void 0,
143
+ a: void 0
128
144
  });
129
145
  web.runHydrationEvents();
130
146
  return _el$10;
@@ -134,16 +150,19 @@ function VerifiedText(props) {
134
150
  web.insert(_el$, (() => {
135
151
  var _c$ = web.memo(() => !!(props.validation && !props.validation.valid));
136
152
  return () => _c$() && (() => {
137
- var _el$14 = web.getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$15.nextSibling, _el$20 = _el$17.firstChild, [_el$21, _co$5] = web.getNextMarker(_el$20.nextSibling), _el$18 = _el$21.nextSibling, _el$22 = _el$18.nextSibling, [_el$23, _co$6] = web.getNextMarker(_el$22.nextSibling);
138
- _el$23.nextSibling;
139
- web.insert(_el$17, () => Math.round(props.validation.confidence * 100), _el$21, _co$5);
140
- web.insert(_el$17, () => props.validation.hallucinated.length, _el$23, _co$6);
153
+ var _el$14 = web.getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$15.nextSibling, _el$19 = _el$17.firstChild, [_el$20, _co$5] = web.getNextMarker(_el$19.nextSibling), _el$18 = _el$20.nextSibling, _el$21 = _el$18.nextSibling, [_el$22, _co$6] = web.getNextMarker(_el$21.nextSibling);
154
+ web.insert(_el$17, () => formatString.formatMCPUIString(strings.verifiedConfidence, {
155
+ pct: Math.round(props.validation.confidence * 100)
156
+ }), _el$20, _co$5);
157
+ web.insert(_el$17, () => formatString.formatMCPUIString(strings.verifiedUnverifiedCount, {
158
+ count: props.validation.hallucinated.length
159
+ }), _el$22, _co$6);
141
160
  web.effect((_p$) => {
142
- var _v$3 = !!(props.validation.confidence >= 0.8), _v$4 = !!(props.validation.confidence >= 0.5 && props.validation.confidence < 0.8), _v$5 = !!(props.validation.confidence < 0.5), _v$6 = `${Math.round(props.validation.confidence * 100)}%`;
143
- _v$3 !== _p$.e && _el$16.classList.toggle("bg-green-500", _p$.e = _v$3);
144
- _v$4 !== _p$.t && _el$16.classList.toggle("bg-amber-500", _p$.t = _v$4);
145
- _v$5 !== _p$.a && _el$16.classList.toggle("bg-red-500", _p$.a = _v$5);
146
- _v$6 !== _p$.o && web.setStyleProperty(_el$16, "width", _p$.o = _v$6);
161
+ var _v$6 = !!(props.validation.confidence >= 0.8), _v$7 = !!(props.validation.confidence >= 0.5 && props.validation.confidence < 0.8), _v$8 = !!(props.validation.confidence < 0.5), _v$9 = `${Math.round(props.validation.confidence * 100)}%`;
162
+ _v$6 !== _p$.e && _el$16.classList.toggle("bg-green-500", _p$.e = _v$6);
163
+ _v$7 !== _p$.t && _el$16.classList.toggle("bg-amber-500", _p$.t = _v$7);
164
+ _v$8 !== _p$.a && _el$16.classList.toggle("bg-red-500", _p$.a = _v$8);
165
+ _v$9 !== _p$.o && web.setStyleProperty(_el$16, "width", _p$.o = _v$9);
147
166
  return _p$;
148
167
  }, {
149
168
  e: void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"VerifiedText.cjs","sources":["../../src/components/VerifiedText.tsx"],"sourcesContent":["/**\n * VerifiedText — renders LLM text with inline verification badges\n * v3.1.0: Highlights verified vs hallucinated numbers\n *\n * @experimental\n *\n * Modes:\n * - highlight: ✅/⚠️ badges next to numbers (default)\n * - strip: replaces hallucinated numbers with [non vérifié]\n * - annotate: tooltip on hover with closest source number\n */\n\nimport { createMemo, For } from 'solid-js'\nimport type { DataValidation, HallucinatedNumber } from '../types/chat-bus'\n\nexport interface VerifiedTextProps {\n text: string\n validation: DataValidation\n /** Display mode (default: 'highlight') */\n mode?: 'highlight' | 'strip' | 'annotate'\n /** Callback when a hallucinated number is clicked */\n onHallucinationClick?: (item: HallucinatedNumber) => void\n}\n\ninterface TextSegment {\n type: 'text' | 'verified' | 'hallucinated'\n content: string\n item?: HallucinatedNumber\n}\n\n/**\n * Build annotated segments by splitting text at number positions.\n * Verified numbers get ✅, hallucinated get ⚠️.\n */\nfunction buildAnnotatedSegments(text: string, validation: DataValidation): TextSegment[] {\n // Build position maps\n const hallucinatedPositions = new Map<number, HallucinatedNumber>()\n for (const h of validation.hallucinated) {\n hallucinatedPositions.set(h.position, h)\n }\n\n const verifiedPositions = new Set<number>()\n for (const n of validation.llmNumbers) {\n if (!hallucinatedPositions.has(n.position)) {\n verifiedPositions.add(n.position)\n }\n }\n\n // Re-extract number lengths from text for precise splitting\n const numberRegex = /\\d[\\d\\s,.]*\\d|\\d+/g\n const matches: Array<{ start: number; end: number }> = []\n let m: RegExpExecArray | null\n while ((m = numberRegex.exec(text)) !== null) {\n matches.push({ start: m.index, end: m.index + m[0].length })\n }\n\n // Build a lookup from position → match\n const matchByPosition = new Map<number, { start: number; end: number }>()\n for (const match of matches) {\n matchByPosition.set(match.start, match)\n }\n\n // Build segments\n const segments: TextSegment[] = []\n let cursor = 0\n\n for (const num of validation.llmNumbers) {\n const match = matchByPosition.get(num.position)\n if (!match) continue\n\n // Text before this number\n if (match.start > cursor) {\n segments.push({ type: 'text', content: text.slice(cursor, match.start) })\n }\n\n const numberText = text.slice(match.start, match.end)\n const hallucinated = hallucinatedPositions.get(num.position)\n\n if (hallucinated) {\n segments.push({ type: 'hallucinated', content: numberText, item: hallucinated })\n } else {\n segments.push({ type: 'verified', content: numberText })\n }\n\n cursor = match.end\n }\n\n // Remaining text\n if (cursor < text.length) {\n segments.push({ type: 'text', content: text.slice(cursor) })\n }\n\n return segments\n}\n\nexport function VerifiedText(props: VerifiedTextProps) {\n const mode = () => props.mode || 'highlight'\n\n const segments = createMemo<TextSegment[]>(() => {\n if (!props.validation || props.validation.valid) {\n return [{ type: 'text' as const, content: props.text }]\n }\n return buildAnnotatedSegments(props.text, props.validation)\n })\n\n return (\n <div class=\"verified-text text-sm leading-relaxed\">\n <For each={segments()}>\n {(seg) => {\n if (seg.type === 'text') {\n return <span>{seg.content}</span>\n }\n\n if (seg.type === 'verified') {\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300\"\n title=\"Verified against source data\"\n >\n {seg.content}\n <span class=\"text-xs opacity-70\" aria-label=\"verified\">&#x2705;</span>\n </span>\n )\n }\n\n // hallucinated\n const h = seg.item!\n const tooltipText = () => {\n if (h.closest != null && h.distance != null) {\n return `Not found in source data. Closest: ${h.closest} (${Math.round(h.distance * 100)}% off)`\n }\n return 'Not found in source data'\n }\n\n if (mode() === 'strip') {\n return (\n <span\n class=\"inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs\"\n title={tooltipText()}\n >\n [non v&eacute;rifi&eacute;]\n </span>\n )\n }\n\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help\"\n title={tooltipText()}\n onClick={() => props.onHallucinationClick?.(h)}\n role={props.onHallucinationClick ? 'button' : undefined}\n >\n {seg.content}\n <span class=\"text-xs\" aria-label=\"unverified\">&#x26A0;&#xFE0F;</span>\n </span>\n )\n }}\n </For>\n\n {/* Confidence bar */}\n {props.validation && !props.validation.valid && (\n <div class=\"mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400\">\n <div class=\"flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden\">\n <div\n class=\"h-full rounded-full transition-all\"\n classList={{\n 'bg-green-500': props.validation.confidence >= 0.8,\n 'bg-amber-500': props.validation.confidence >= 0.5 && props.validation.confidence < 0.8,\n 'bg-red-500': props.validation.confidence < 0.5,\n }}\n style={{ width: `${Math.round(props.validation.confidence * 100)}%` }}\n />\n </div>\n <span>\n {Math.round(props.validation.confidence * 100)}% verified\n ({props.validation.hallucinated.length} unverified)\n </span>\n </div>\n )}\n </div>\n )\n}\n"],"names":["buildAnnotatedSegments","text","validation","hallucinatedPositions","Map","h","hallucinated","set","position","verifiedPositions","Set","n","llmNumbers","has","add","numberRegex","matches","m","exec","push","start","index","end","length","matchByPosition","match","segments","cursor","num","get","type","content","slice","numberText","item","VerifiedText","props","mode","createMemo","valid","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_co$","_$getNextMarker","nextSibling","_el$4","_el$5","_co$2","_$insert","_$createComponent","For","each","children","seg","_el$6","_tmpl$2","_el$7","_tmpl$3","_el$9","_el$0","_co$3","tooltipText","closest","distance","Math","round","_el$1","_tmpl$4","_$effect","_$setAttribute","_el$10","_tmpl$5","_el$12","_el$13","_co$4","$$click","onHallucinationClick","_p$","_v$","_v$2","undefined","e","t","_$runHydrationEvents","_c$","_$memo","_el$14","_tmpl$6","_el$15","_el$16","_el$17","_el$20","_el$21","_co$5","_el$18","_el$22","_el$23","_co$6","confidence","_v$3","_v$4","_v$5","_v$6","classList","toggle","a","o","_$setStyleProperty","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"VerifiedText.cjs","sources":["../../src/components/VerifiedText.tsx"],"sourcesContent":["/**\n * VerifiedText — renders LLM text with inline verification badges\n * v3.1.0: Highlights verified vs hallucinated numbers\n *\n * @experimental\n *\n * Modes:\n * - highlight: ✅/⚠️ badges next to numbers (default)\n * - strip: replaces hallucinated numbers with the `verifiedStripLabel`\n * chrome string ('[unverified]' by default)\n * - annotate: tooltip on hover with closest source number\n */\n\nimport { createMemo, For } from 'solid-js'\nimport type { DataValidation, HallucinatedNumber } from '../types/chat-bus'\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface VerifiedTextProps {\n text: string\n validation: DataValidation\n /** Display mode (default: 'highlight') */\n mode?: 'highlight' | 'strip' | 'annotate'\n /** Callback when a hallucinated number is clicked */\n onHallucinationClick?: (item: HallucinatedNumber) => void\n}\n\ninterface TextSegment {\n type: 'text' | 'verified' | 'hallucinated'\n content: string\n item?: HallucinatedNumber\n}\n\n/**\n * Build annotated segments by splitting text at number positions.\n * Verified numbers get ✅, hallucinated get ⚠️.\n */\nfunction buildAnnotatedSegments(text: string, validation: DataValidation): TextSegment[] {\n // Build position maps\n const hallucinatedPositions = new Map<number, HallucinatedNumber>()\n for (const h of validation.hallucinated) {\n hallucinatedPositions.set(h.position, h)\n }\n\n const verifiedPositions = new Set<number>()\n for (const n of validation.llmNumbers) {\n if (!hallucinatedPositions.has(n.position)) {\n verifiedPositions.add(n.position)\n }\n }\n\n // Re-extract number lengths from text for precise splitting\n const numberRegex = /\\d[\\d\\s,.]*\\d|\\d+/g\n const matches: Array<{ start: number; end: number }> = []\n let m: RegExpExecArray | null\n while ((m = numberRegex.exec(text)) !== null) {\n matches.push({ start: m.index, end: m.index + m[0].length })\n }\n\n // Build a lookup from position → match\n const matchByPosition = new Map<number, { start: number; end: number }>()\n for (const match of matches) {\n matchByPosition.set(match.start, match)\n }\n\n // Build segments\n const segments: TextSegment[] = []\n let cursor = 0\n\n for (const num of validation.llmNumbers) {\n const match = matchByPosition.get(num.position)\n if (!match) continue\n\n // Text before this number\n if (match.start > cursor) {\n segments.push({ type: 'text', content: text.slice(cursor, match.start) })\n }\n\n const numberText = text.slice(match.start, match.end)\n const hallucinated = hallucinatedPositions.get(num.position)\n\n if (hallucinated) {\n segments.push({ type: 'hallucinated', content: numberText, item: hallucinated })\n } else {\n segments.push({ type: 'verified', content: numberText })\n }\n\n cursor = match.end\n }\n\n // Remaining text\n if (cursor < text.length) {\n segments.push({ type: 'text', content: text.slice(cursor) })\n }\n\n return segments\n}\n\nexport function VerifiedText(props: VerifiedTextProps) {\n const strings = useMCPUIStrings()\n const mode = () => props.mode || 'highlight'\n\n const segments = createMemo<TextSegment[]>(() => {\n if (!props.validation || props.validation.valid) {\n return [{ type: 'text' as const, content: props.text }]\n }\n return buildAnnotatedSegments(props.text, props.validation)\n })\n\n return (\n <div class=\"verified-text text-sm leading-relaxed\">\n <For each={segments()}>\n {(seg) => {\n if (seg.type === 'text') {\n return <span>{seg.content}</span>\n }\n\n if (seg.type === 'verified') {\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300\"\n title={strings.verifiedTitle}\n >\n {seg.content}\n <span class=\"text-xs opacity-70\" aria-label={strings.verifiedAria}>&#x2705;</span>\n </span>\n )\n }\n\n // hallucinated\n const h = seg.item!\n const tooltipText = () => {\n if (h.closest != null && h.distance != null) {\n return formatMCPUIString(strings.verifiedNotFoundClosest, {\n closest: h.closest,\n pct: Math.round(h.distance * 100),\n })\n }\n return strings.verifiedNotFound\n }\n\n if (mode() === 'strip') {\n return (\n <span\n class=\"inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs\"\n title={tooltipText()}\n >\n {strings.verifiedStripLabel}\n </span>\n )\n }\n\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help\"\n title={tooltipText()}\n onClick={() => props.onHallucinationClick?.(h)}\n role={props.onHallucinationClick ? 'button' : undefined}\n >\n {seg.content}\n <span class=\"text-xs\" aria-label={strings.unverifiedAria}>&#x26A0;&#xFE0F;</span>\n </span>\n )\n }}\n </For>\n\n {/* Confidence bar */}\n {props.validation && !props.validation.valid && (\n <div class=\"mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400\">\n <div class=\"flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden\">\n <div\n class=\"h-full rounded-full transition-all\"\n classList={{\n 'bg-green-500': props.validation.confidence >= 0.8,\n 'bg-amber-500': props.validation.confidence >= 0.5 && props.validation.confidence < 0.8,\n 'bg-red-500': props.validation.confidence < 0.5,\n }}\n style={{ width: `${Math.round(props.validation.confidence * 100)}%` }}\n />\n </div>\n <span>\n {formatMCPUIString(strings.verifiedConfidence, {\n pct: Math.round(props.validation.confidence * 100),\n })}{' '}\n {formatMCPUIString(strings.verifiedUnverifiedCount, {\n count: props.validation.hallucinated.length,\n })}\n </span>\n </div>\n )}\n </div>\n )\n}\n"],"names":["buildAnnotatedSegments","text","validation","hallucinatedPositions","Map","h","hallucinated","set","position","verifiedPositions","Set","n","llmNumbers","has","add","numberRegex","matches","m","exec","push","start","index","end","length","matchByPosition","match","segments","cursor","num","get","type","content","slice","numberText","item","VerifiedText","props","strings","useMCPUIStrings","mode","createMemo","valid","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_co$","_$getNextMarker","nextSibling","_el$4","_el$5","_co$2","_$insert","_$createComponent","For","each","children","seg","_el$6","_tmpl$2","_el$7","_tmpl$3","_el$9","_el$0","_co$3","_el$8","_$effect","_p$","_v$","verifiedTitle","_v$2","verifiedAria","e","_$setAttribute","t","undefined","tooltipText","closest","distance","formatMCPUIString","verifiedNotFoundClosest","pct","Math","round","verifiedNotFound","_el$1","_tmpl$4","verifiedStripLabel","_el$10","_tmpl$5","_el$12","_el$13","_co$4","_el$11","$$click","onHallucinationClick","_v$3","_v$4","_v$5","unverifiedAria","a","_$runHydrationEvents","_c$","_$memo","_el$14","_tmpl$6","_el$15","_el$16","_el$17","_el$19","_el$20","_co$5","_el$18","_el$21","_el$22","_co$6","verifiedConfidence","confidence","verifiedUnverifiedCount","count","_v$6","_v$7","_v$8","_v$9","classList","toggle","o","_$setStyleProperty","_$delegateEvents"],"mappings":";;;;;;;AAoCA,SAASA,uBAAuBC,MAAcC,YAA2C;AAEvF,QAAMC,4CAA4BC,IAAAA;AAClC,aAAWC,KAAKH,WAAWI,cAAc;AACvCH,0BAAsBI,IAAIF,EAAEG,UAAUH,CAAC;AAAA,EACzC;AAEA,QAAMI,wCAAwBC,IAAAA;AAC9B,aAAWC,KAAKT,WAAWU,YAAY;AACrC,QAAI,CAACT,sBAAsBU,IAAIF,EAAEH,QAAQ,GAAG;AAC1CC,wBAAkBK,IAAIH,EAAEH,QAAQ;AAAA,IAClC;AAAA,EACF;AAGA,QAAMO,cAAc;AACpB,QAAMC,UAAiD,CAAA;AACvD,MAAIC;AACJ,UAAQA,IAAIF,YAAYG,KAAKjB,IAAI,OAAO,MAAM;AAC5Ce,YAAQG,KAAK;AAAA,MAAEC,OAAOH,EAAEI;AAAAA,MAAOC,KAAKL,EAAEI,QAAQJ,EAAE,CAAC,EAAEM;AAAAA,IAAAA,CAAQ;AAAA,EAC7D;AAGA,QAAMC,sCAAsBpB,IAAAA;AAC5B,aAAWqB,SAAST,SAAS;AAC3BQ,oBAAgBjB,IAAIkB,MAAML,OAAOK,KAAK;AAAA,EACxC;AAGA,QAAMC,WAA0B,CAAA;AAChC,MAAIC,SAAS;AAEb,aAAWC,OAAO1B,WAAWU,YAAY;AACvC,UAAMa,QAAQD,gBAAgBK,IAAID,IAAIpB,QAAQ;AAC9C,QAAI,CAACiB,MAAO;AAGZ,QAAIA,MAAML,QAAQO,QAAQ;AACxBD,eAASP,KAAK;AAAA,QAAEW,MAAM;AAAA,QAAQC,SAAS9B,KAAK+B,MAAML,QAAQF,MAAML,KAAK;AAAA,MAAA,CAAG;AAAA,IAC1E;AAEA,UAAMa,aAAahC,KAAK+B,MAAMP,MAAML,OAAOK,MAAMH,GAAG;AACpD,UAAMhB,eAAeH,sBAAsB0B,IAAID,IAAIpB,QAAQ;AAE3D,QAAIF,cAAc;AAChBoB,eAASP,KAAK;AAAA,QAAEW,MAAM;AAAA,QAAgBC,SAASE;AAAAA,QAAYC,MAAM5B;AAAAA,MAAAA,CAAc;AAAA,IACjF,OAAO;AACLoB,eAASP,KAAK;AAAA,QAAEW,MAAM;AAAA,QAAYC,SAASE;AAAAA,MAAAA,CAAY;AAAA,IACzD;AAEAN,aAASF,MAAMH;AAAAA,EACjB;AAGA,MAAIK,SAAS1B,KAAKsB,QAAQ;AACxBG,aAASP,KAAK;AAAA,MAAEW,MAAM;AAAA,MAAQC,SAAS9B,KAAK+B,MAAML,MAAM;AAAA,IAAA,CAAG;AAAA,EAC7D;AAEA,SAAOD;AACT;AAEO,SAASS,aAAaC,OAA0B;AACrD,QAAMC,UAAUC,oBAAAA,gBAAAA;AAChB,QAAMC,OAAOA,MAAMH,MAAMG,QAAQ;AAEjC,QAAMb,WAAWc,QAAAA,WAA0B,MAAM;AAC/C,QAAI,CAACJ,MAAMlC,cAAckC,MAAMlC,WAAWuC,OAAO;AAC/C,aAAO,CAAC;AAAA,QAAEX,MAAM;AAAA,QAAiBC,SAASK,MAAMnC;AAAAA,MAAAA,CAAM;AAAA,IACxD;AACA,WAAOD,uBAAuBoC,MAAMnC,MAAMmC,MAAMlC,UAAU;AAAA,EAC5D,CAAC;AAED,UAAA,MAAA;AAAA,QAAAwC,OAAAC,IAAAA,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAA,CAAAC,OAAAC,IAAA,IAAAC,IAAAA,cAAAJ,MAAAK,WAAA,GAAAC,QAAAJ,MAAAG,aAAA,CAAAE,OAAAC,KAAA,IAAAJ,IAAAA,cAAAE,MAAAD,WAAA;AAAAI,eAAAZ,MAAAa,IAAAA,gBAEKC,aAAG;AAAA,MAAA,IAACC,OAAI;AAAA,eAAE/B,SAAAA;AAAAA,MAAU;AAAA,MAAAgC,UACjBC,CAAAA,QAAQ;AACR,YAAIA,IAAI7B,SAAS,QAAQ;AACvB,kBAAA,MAAA;AAAA,gBAAA8B,QAAAjB,IAAAA,eAAAkB,OAAA;AAAAP,gBAAAA,OAAAM,OAAA,MAAcD,IAAI5B,OAAO;AAAA,mBAAA6B;AAAAA,UAAA,GAAA;AAAA,QAC3B;AAEA,YAAID,IAAI7B,SAAS,YAAY;AAC3B,kBAAA,MAAA;AAAA,gBAAAgC,QAAAnB,IAAAA,eAAAoB,OAAA,GAAAC,QAAAF,MAAAhB,YAAA,CAAAmB,OAAAC,KAAA,IAAAjB,IAAAA,cAAAe,MAAAd,WAAA,GAAAiB,QAAAF,MAAAf;AAAAI,gBAAAA,OAAAQ,OAAA,MAKKH,IAAI5B,SAAOkC,OAAAC,KAAA;AAAAE,gBAAAA,OAAAC,CAAAA,QAAA;AAAA,kBAAAC,MAFLjC,QAAQkC,eAAaC,OAGiBnC,QAAQoC;AAAYH,sBAAAD,IAAAK,KAAAC,IAAAA,aAAAb,OAAA,SAAAO,IAAAK,IAAAJ,GAAA;AAAAE,uBAAAH,IAAAO,KAAAD,IAAAA,aAAAR,OAAA,cAAAE,IAAAO,IAAAJ,IAAA;AAAA,qBAAAH;AAAAA,YAAA,GAAA;AAAA,cAAAK,GAAAG;AAAAA,cAAAD,GAAAC;AAAAA,YAAAA,CAAA;AAAA,mBAAAf;AAAAA,UAAA,GAAA;AAAA,QAGvE;AAGA,cAAMzD,IAAIsD,IAAIzB;AACd,cAAM4C,cAAcA,MAAM;AACxB,cAAIzE,EAAE0E,WAAW,QAAQ1E,EAAE2E,YAAY,MAAM;AAC3C,mBAAOC,aAAAA,kBAAkB5C,QAAQ6C,yBAAyB;AAAA,cACxDH,SAAS1E,EAAE0E;AAAAA,cACXI,KAAKC,KAAKC,MAAMhF,EAAE2E,WAAW,GAAG;AAAA,YAAA,CACjC;AAAA,UACH;AACA,iBAAO3C,QAAQiD;AAAAA,QACjB;AAEA,YAAI/C,KAAAA,MAAW,SAAS;AACtB,kBAAA,MAAA;AAAA,gBAAAgD,QAAA5C,IAAAA,eAAA6C,OAAA;AAAAlC,gBAAAA,OAAAiC,OAAA,MAKKlD,QAAQoD,kBAAkB;AAAArB,gBAAAA,aAAAO,IAAAA,aAAAY,OAAA,SAFpBT,YAAAA,CAAa,CAAA;AAAA,mBAAAS;AAAAA,UAAA,GAAA;AAAA,QAK1B;AAEA,gBAAA,MAAA;AAAA,cAAAG,SAAA/C,IAAAA,eAAAgD,OAAA,GAAAC,SAAAF,OAAA5C,YAAA,CAAA+C,QAAAC,KAAA,IAAA7C,IAAAA,cAAA2C,OAAA1C,WAAA,GAAA6C,SAAAF,OAAA3C;AAAAwC,iBAAAM,UAIa,MAAA;;AAAM5D,+BAAM6D,yBAAN7D,+BAA6B/B;AAAAA;AAAEiD,cAAAA,OAAAoC,QAAA,MAG7C/B,IAAI5B,SAAO8D,QAAAC,KAAA;AAAA1B,cAAAA,OAAAC,CAAAA,QAAA;AAAA,gBAAA6B,OAJLpB,eAAaqB,OAEd/D,MAAM6D,uBAAuB,WAAWpB,QAASuB,OAGrB/D,QAAQgE;AAAcH,qBAAA7B,IAAAK,KAAAC,IAAAA,aAAAe,QAAA,SAAArB,IAAAK,IAAAwB,IAAA;AAAAC,qBAAA9B,IAAAO,KAAAD,IAAAA,aAAAe,QAAA,QAAArB,IAAAO,IAAAuB,IAAA;AAAAC,qBAAA/B,IAAAiC,KAAA3B,IAAAA,aAAAoB,QAAA,cAAA1B,IAAAiC,IAAAF,IAAA;AAAA,mBAAA/B;AAAAA,UAAA,GAAA;AAAA,YAAAK,GAAAG;AAAAA,YAAAD,GAAAC;AAAAA,YAAAyB,GAAAzB;AAAAA,UAAAA,CAAA;AAAA0B,iCAAAA;AAAA,iBAAAb;AAAAA,QAAA,GAAA;AAAA,MAG9D;AAAA,IAAA,CAAC,GAAA3C,OAAAC,IAAA;AAAAM,QAAAA,OAAAZ,OAAA,MAAA;AAAA,UAAA8D,MAAAC,SAAA,MAAA,CAAA,EAIFrE,MAAMlC,cAAc,CAACkC,MAAMlC,WAAWuC,MAAK;AAAA,aAAA,MAA3C+D,IAAAA,MAAA,MAAA;AAAA,YAAAE,SAAA/D,IAAAA,eAAAgE,OAAA,GAAAC,SAAAF,OAAA5D,YAAA+D,SAAAD,OAAA9D,YAAAgE,SAAAF,OAAA1D,aAAA6D,SAAAD,OAAAhE,YAAA,CAAAkE,QAAAC,KAAA,IAAAhE,IAAAA,cAAA8D,OAAA7D,WAAA,GAAAgE,SAAAF,OAAA9D,aAAAiE,SAAAD,OAAAhE,aAAA,CAAAkE,QAAAC,KAAA,IAAApE,IAAAA,cAAAkE,OAAAjE,WAAA;AAAAI,YAAAA,OAAAwD,QAAA,MAcM7B,+BAAkB5C,QAAQiF,oBAAoB;AAAA,UAC7CnC,KAAKC,KAAKC,MAAMjD,MAAMlC,WAAWqH,aAAa,GAAG;AAAA,QAAA,CAClD,GAACP,QAAAC,KAAA;AAAA3D,YAAAA,OAAAwD,QAAA,MACD7B,+BAAkB5C,QAAQmF,yBAAyB;AAAA,UAClDC,OAAOrF,MAAMlC,WAAWI,aAAaiB;AAAAA,QAAAA,CACtC,GAAC6F,QAAAC,KAAA;AAAAjD,YAAAA,OAAAC,CAAAA,QAAA;AAAA,cAAAqD,UAbkBtF,MAAMlC,WAAWqH,cAAc,MAAGI,OAAA,CAAA,EAClCvF,MAAMlC,WAAWqH,cAAc,OAAOnF,MAAMlC,WAAWqH,aAAa,MAAGK,UACzExF,MAAMlC,WAAWqH,aAAa,MAAGM,OAEjC,GAAGzC,KAAKC,MAAMjD,MAAMlC,WAAWqH,aAAa,GAAG,CAAC;AAAGG,mBAAArD,IAAAK,KAAAmC,OAAAiB,UAAAC,OAAA,gBAAA1D,IAAAK,IAAAgD,IAAA;AAAAC,mBAAAtD,IAAAO,KAAAiC,OAAAiB,UAAAC,OAAA,gBAAA1D,IAAAO,IAAA+C,IAAA;AAAAC,mBAAAvD,IAAAiC,KAAAO,OAAAiB,UAAAC,OAAA,cAAA1D,IAAAiC,IAAAsB,IAAA;AAAAC,mBAAAxD,IAAA2D,KAAAC,IAAAA,iBAAApB,QAAA,SAAAxC,IAAA2D,IAAAH,IAAA;AAAA,iBAAAxD;AAAAA,QAAA,GAAA;AAAA,UAAAK,GAAAG;AAAAA,UAAAD,GAAAC;AAAAA,UAAAyB,GAAAzB;AAAAA,UAAAmD,GAAAnD;AAAAA,QAAAA,CAAA;AAAA,eAAA6B;AAAAA,MAAA,GAAA;AAAA,IAY1E,GAAA,GAAAtD,OAAAC,KAAA;AAAA,WAAAX;AAAAA,EAAA,GAAA;AAGP;AAACwF,IAAAA,eAAA,CAAA,OAAA,CAAA;;"}
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * Modes:
8
8
  * - highlight: ✅/⚠️ badges next to numbers (default)
9
- * - strip: replaces hallucinated numbers with [non vérifié]
9
+ * - strip: replaces hallucinated numbers with the `verifiedStripLabel`
10
+ * chrome string ('[unverified]' by default)
10
11
  * - annotate: tooltip on hover with closest source number
11
12
  */
12
13
  import type { DataValidation, HallucinatedNumber } from '../types/chat-bus';
@@ -1 +1 @@
1
- {"version":3,"file":"VerifiedText.d.ts","sourceRoot":"","sources":["../../src/components/VerifiedText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,cAAc,CAAA;IAC1B,0CAA0C;IAC1C,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,CAAA;IACzC,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAA;CAC1D;AAyED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCAsFpD"}
1
+ {"version":3,"file":"VerifiedText.d.ts","sourceRoot":"","sources":["../../src/components/VerifiedText.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAG3E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,cAAc,CAAA;IAC1B,0CAA0C;IAC1C,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,CAAA;IACzC,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAA;CAC1D;AAyED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCA8FpD"}
@@ -1,6 +1,8 @@
1
1
  import { getNextElement, getNextMarker, insert, createComponent, effect, setAttribute, runHydrationEvents, memo, setStyleProperty, template, delegateEvents } from "solid-js/web";
2
2
  import { createMemo, For } from "solid-js";
3
- var _tmpl$ = /* @__PURE__ */ template(`<div class="verified-text text-sm leading-relaxed"><!$><!/><!$><!/>`), _tmpl$2 = /* @__PURE__ */ template(`<span>`), _tmpl$3 = /* @__PURE__ */ template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300"title="Verified against source data"><!$><!/><span class="text-xs opacity-70"aria-label=verified>&#x2705;`), _tmpl$4 = /* @__PURE__ */ template(`<span class="inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs">[non v&eacute;rifi&eacute;]`), _tmpl$5 = /* @__PURE__ */ template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help"><!$><!/><span class=text-xs aria-label=unverified>&#x26A0;&#xFE0F;`), _tmpl$6 = /* @__PURE__ */ template(`<div class="mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"><div class="flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden"><div class="h-full rounded-full transition-all"></div></div><span><!$><!/>% verified (<!$><!/> unverified)`);
3
+ import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
4
+ import { formatMCPUIString } from "../utils/format-string.js";
5
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="verified-text text-sm leading-relaxed"><!$><!/><!$><!/>`), _tmpl$2 = /* @__PURE__ */ template(`<span>`), _tmpl$3 = /* @__PURE__ */ template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300"><!$><!/><span class="text-xs opacity-70">&#x2705;`), _tmpl$4 = /* @__PURE__ */ template(`<span class="inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs">`), _tmpl$5 = /* @__PURE__ */ template(`<span class="inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help"><!$><!/><span class=text-xs>&#x26A0;&#xFE0F;`), _tmpl$6 = /* @__PURE__ */ template(`<div class="mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400"><div class="flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden"><div class="h-full rounded-full transition-all"></div></div><span><!$><!/> <!$><!/>`);
4
6
  function buildAnnotatedSegments(text, validation) {
5
7
  const hallucinatedPositions = /* @__PURE__ */ new Map();
6
8
  for (const h of validation.hallucinated) {
@@ -61,6 +63,7 @@ function buildAnnotatedSegments(text, validation) {
61
63
  return segments;
62
64
  }
63
65
  function VerifiedText(props) {
66
+ const strings = useMCPUIStrings();
64
67
  const mode = () => props.mode || "highlight";
65
68
  const segments = createMemo(() => {
66
69
  if (!props.validation || props.validation.valid) {
@@ -87,42 +90,55 @@ function VerifiedText(props) {
87
90
  }
88
91
  if (seg.type === "verified") {
89
92
  return (() => {
90
- var _el$7 = getNextElement(_tmpl$3), _el$9 = _el$7.firstChild, [_el$0, _co$3] = getNextMarker(_el$9.nextSibling);
91
- _el$0.nextSibling;
93
+ var _el$7 = getNextElement(_tmpl$3), _el$9 = _el$7.firstChild, [_el$0, _co$3] = getNextMarker(_el$9.nextSibling), _el$8 = _el$0.nextSibling;
92
94
  insert(_el$7, () => seg.content, _el$0, _co$3);
95
+ effect((_p$) => {
96
+ var _v$ = strings.verifiedTitle, _v$2 = strings.verifiedAria;
97
+ _v$ !== _p$.e && setAttribute(_el$7, "title", _p$.e = _v$);
98
+ _v$2 !== _p$.t && setAttribute(_el$8, "aria-label", _p$.t = _v$2);
99
+ return _p$;
100
+ }, {
101
+ e: void 0,
102
+ t: void 0
103
+ });
93
104
  return _el$7;
94
105
  })();
95
106
  }
96
107
  const h = seg.item;
97
108
  const tooltipText = () => {
98
109
  if (h.closest != null && h.distance != null) {
99
- return `Not found in source data. Closest: ${h.closest} (${Math.round(h.distance * 100)}% off)`;
110
+ return formatMCPUIString(strings.verifiedNotFoundClosest, {
111
+ closest: h.closest,
112
+ pct: Math.round(h.distance * 100)
113
+ });
100
114
  }
101
- return "Not found in source data";
115
+ return strings.verifiedNotFound;
102
116
  };
103
117
  if (mode() === "strip") {
104
118
  return (() => {
105
119
  var _el$1 = getNextElement(_tmpl$4);
120
+ insert(_el$1, () => strings.verifiedStripLabel);
106
121
  effect(() => setAttribute(_el$1, "title", tooltipText()));
107
122
  return _el$1;
108
123
  })();
109
124
  }
110
125
  return (() => {
111
- var _el$10 = getNextElement(_tmpl$5), _el$12 = _el$10.firstChild, [_el$13, _co$4] = getNextMarker(_el$12.nextSibling);
112
- _el$13.nextSibling;
126
+ var _el$10 = getNextElement(_tmpl$5), _el$12 = _el$10.firstChild, [_el$13, _co$4] = getNextMarker(_el$12.nextSibling), _el$11 = _el$13.nextSibling;
113
127
  _el$10.$$click = () => {
114
128
  var _a;
115
129
  return (_a = props.onHallucinationClick) == null ? void 0 : _a.call(props, h);
116
130
  };
117
131
  insert(_el$10, () => seg.content, _el$13, _co$4);
118
132
  effect((_p$) => {
119
- var _v$ = tooltipText(), _v$2 = props.onHallucinationClick ? "button" : void 0;
120
- _v$ !== _p$.e && setAttribute(_el$10, "title", _p$.e = _v$);
121
- _v$2 !== _p$.t && setAttribute(_el$10, "role", _p$.t = _v$2);
133
+ var _v$3 = tooltipText(), _v$4 = props.onHallucinationClick ? "button" : void 0, _v$5 = strings.unverifiedAria;
134
+ _v$3 !== _p$.e && setAttribute(_el$10, "title", _p$.e = _v$3);
135
+ _v$4 !== _p$.t && setAttribute(_el$10, "role", _p$.t = _v$4);
136
+ _v$5 !== _p$.a && setAttribute(_el$11, "aria-label", _p$.a = _v$5);
122
137
  return _p$;
123
138
  }, {
124
139
  e: void 0,
125
- t: void 0
140
+ t: void 0,
141
+ a: void 0
126
142
  });
127
143
  runHydrationEvents();
128
144
  return _el$10;
@@ -132,16 +148,19 @@ function VerifiedText(props) {
132
148
  insert(_el$, (() => {
133
149
  var _c$ = memo(() => !!(props.validation && !props.validation.valid));
134
150
  return () => _c$() && (() => {
135
- var _el$14 = getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$15.nextSibling, _el$20 = _el$17.firstChild, [_el$21, _co$5] = getNextMarker(_el$20.nextSibling), _el$18 = _el$21.nextSibling, _el$22 = _el$18.nextSibling, [_el$23, _co$6] = getNextMarker(_el$22.nextSibling);
136
- _el$23.nextSibling;
137
- insert(_el$17, () => Math.round(props.validation.confidence * 100), _el$21, _co$5);
138
- insert(_el$17, () => props.validation.hallucinated.length, _el$23, _co$6);
151
+ var _el$14 = getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$15.nextSibling, _el$19 = _el$17.firstChild, [_el$20, _co$5] = getNextMarker(_el$19.nextSibling), _el$18 = _el$20.nextSibling, _el$21 = _el$18.nextSibling, [_el$22, _co$6] = getNextMarker(_el$21.nextSibling);
152
+ insert(_el$17, () => formatMCPUIString(strings.verifiedConfidence, {
153
+ pct: Math.round(props.validation.confidence * 100)
154
+ }), _el$20, _co$5);
155
+ insert(_el$17, () => formatMCPUIString(strings.verifiedUnverifiedCount, {
156
+ count: props.validation.hallucinated.length
157
+ }), _el$22, _co$6);
139
158
  effect((_p$) => {
140
- var _v$3 = !!(props.validation.confidence >= 0.8), _v$4 = !!(props.validation.confidence >= 0.5 && props.validation.confidence < 0.8), _v$5 = !!(props.validation.confidence < 0.5), _v$6 = `${Math.round(props.validation.confidence * 100)}%`;
141
- _v$3 !== _p$.e && _el$16.classList.toggle("bg-green-500", _p$.e = _v$3);
142
- _v$4 !== _p$.t && _el$16.classList.toggle("bg-amber-500", _p$.t = _v$4);
143
- _v$5 !== _p$.a && _el$16.classList.toggle("bg-red-500", _p$.a = _v$5);
144
- _v$6 !== _p$.o && setStyleProperty(_el$16, "width", _p$.o = _v$6);
159
+ var _v$6 = !!(props.validation.confidence >= 0.8), _v$7 = !!(props.validation.confidence >= 0.5 && props.validation.confidence < 0.8), _v$8 = !!(props.validation.confidence < 0.5), _v$9 = `${Math.round(props.validation.confidence * 100)}%`;
160
+ _v$6 !== _p$.e && _el$16.classList.toggle("bg-green-500", _p$.e = _v$6);
161
+ _v$7 !== _p$.t && _el$16.classList.toggle("bg-amber-500", _p$.t = _v$7);
162
+ _v$8 !== _p$.a && _el$16.classList.toggle("bg-red-500", _p$.a = _v$8);
163
+ _v$9 !== _p$.o && setStyleProperty(_el$16, "width", _p$.o = _v$9);
145
164
  return _p$;
146
165
  }, {
147
166
  e: void 0,
@@ -1 +1 @@
1
- {"version":3,"file":"VerifiedText.js","sources":["../../src/components/VerifiedText.tsx"],"sourcesContent":["/**\n * VerifiedText — renders LLM text with inline verification badges\n * v3.1.0: Highlights verified vs hallucinated numbers\n *\n * @experimental\n *\n * Modes:\n * - highlight: ✅/⚠️ badges next to numbers (default)\n * - strip: replaces hallucinated numbers with [non vérifié]\n * - annotate: tooltip on hover with closest source number\n */\n\nimport { createMemo, For } from 'solid-js'\nimport type { DataValidation, HallucinatedNumber } from '../types/chat-bus'\n\nexport interface VerifiedTextProps {\n text: string\n validation: DataValidation\n /** Display mode (default: 'highlight') */\n mode?: 'highlight' | 'strip' | 'annotate'\n /** Callback when a hallucinated number is clicked */\n onHallucinationClick?: (item: HallucinatedNumber) => void\n}\n\ninterface TextSegment {\n type: 'text' | 'verified' | 'hallucinated'\n content: string\n item?: HallucinatedNumber\n}\n\n/**\n * Build annotated segments by splitting text at number positions.\n * Verified numbers get ✅, hallucinated get ⚠️.\n */\nfunction buildAnnotatedSegments(text: string, validation: DataValidation): TextSegment[] {\n // Build position maps\n const hallucinatedPositions = new Map<number, HallucinatedNumber>()\n for (const h of validation.hallucinated) {\n hallucinatedPositions.set(h.position, h)\n }\n\n const verifiedPositions = new Set<number>()\n for (const n of validation.llmNumbers) {\n if (!hallucinatedPositions.has(n.position)) {\n verifiedPositions.add(n.position)\n }\n }\n\n // Re-extract number lengths from text for precise splitting\n const numberRegex = /\\d[\\d\\s,.]*\\d|\\d+/g\n const matches: Array<{ start: number; end: number }> = []\n let m: RegExpExecArray | null\n while ((m = numberRegex.exec(text)) !== null) {\n matches.push({ start: m.index, end: m.index + m[0].length })\n }\n\n // Build a lookup from position → match\n const matchByPosition = new Map<number, { start: number; end: number }>()\n for (const match of matches) {\n matchByPosition.set(match.start, match)\n }\n\n // Build segments\n const segments: TextSegment[] = []\n let cursor = 0\n\n for (const num of validation.llmNumbers) {\n const match = matchByPosition.get(num.position)\n if (!match) continue\n\n // Text before this number\n if (match.start > cursor) {\n segments.push({ type: 'text', content: text.slice(cursor, match.start) })\n }\n\n const numberText = text.slice(match.start, match.end)\n const hallucinated = hallucinatedPositions.get(num.position)\n\n if (hallucinated) {\n segments.push({ type: 'hallucinated', content: numberText, item: hallucinated })\n } else {\n segments.push({ type: 'verified', content: numberText })\n }\n\n cursor = match.end\n }\n\n // Remaining text\n if (cursor < text.length) {\n segments.push({ type: 'text', content: text.slice(cursor) })\n }\n\n return segments\n}\n\nexport function VerifiedText(props: VerifiedTextProps) {\n const mode = () => props.mode || 'highlight'\n\n const segments = createMemo<TextSegment[]>(() => {\n if (!props.validation || props.validation.valid) {\n return [{ type: 'text' as const, content: props.text }]\n }\n return buildAnnotatedSegments(props.text, props.validation)\n })\n\n return (\n <div class=\"verified-text text-sm leading-relaxed\">\n <For each={segments()}>\n {(seg) => {\n if (seg.type === 'text') {\n return <span>{seg.content}</span>\n }\n\n if (seg.type === 'verified') {\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300\"\n title=\"Verified against source data\"\n >\n {seg.content}\n <span class=\"text-xs opacity-70\" aria-label=\"verified\">&#x2705;</span>\n </span>\n )\n }\n\n // hallucinated\n const h = seg.item!\n const tooltipText = () => {\n if (h.closest != null && h.distance != null) {\n return `Not found in source data. Closest: ${h.closest} (${Math.round(h.distance * 100)}% off)`\n }\n return 'Not found in source data'\n }\n\n if (mode() === 'strip') {\n return (\n <span\n class=\"inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs\"\n title={tooltipText()}\n >\n [non v&eacute;rifi&eacute;]\n </span>\n )\n }\n\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help\"\n title={tooltipText()}\n onClick={() => props.onHallucinationClick?.(h)}\n role={props.onHallucinationClick ? 'button' : undefined}\n >\n {seg.content}\n <span class=\"text-xs\" aria-label=\"unverified\">&#x26A0;&#xFE0F;</span>\n </span>\n )\n }}\n </For>\n\n {/* Confidence bar */}\n {props.validation && !props.validation.valid && (\n <div class=\"mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400\">\n <div class=\"flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden\">\n <div\n class=\"h-full rounded-full transition-all\"\n classList={{\n 'bg-green-500': props.validation.confidence >= 0.8,\n 'bg-amber-500': props.validation.confidence >= 0.5 && props.validation.confidence < 0.8,\n 'bg-red-500': props.validation.confidence < 0.5,\n }}\n style={{ width: `${Math.round(props.validation.confidence * 100)}%` }}\n />\n </div>\n <span>\n {Math.round(props.validation.confidence * 100)}% verified\n ({props.validation.hallucinated.length} unverified)\n </span>\n </div>\n )}\n </div>\n )\n}\n"],"names":["buildAnnotatedSegments","text","validation","hallucinatedPositions","Map","h","hallucinated","set","position","verifiedPositions","Set","n","llmNumbers","has","add","numberRegex","matches","m","exec","push","start","index","end","length","matchByPosition","match","segments","cursor","num","get","type","content","slice","numberText","item","VerifiedText","props","mode","createMemo","valid","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_co$","_$getNextMarker","nextSibling","_el$4","_el$5","_co$2","_$insert","_$createComponent","For","each","children","seg","_el$6","_tmpl$2","_el$7","_tmpl$3","_el$9","_el$0","_co$3","tooltipText","closest","distance","Math","round","_el$1","_tmpl$4","_$effect","_$setAttribute","_el$10","_tmpl$5","_el$12","_el$13","_co$4","$$click","onHallucinationClick","_p$","_v$","_v$2","undefined","e","t","_$runHydrationEvents","_c$","_$memo","_el$14","_tmpl$6","_el$15","_el$16","_el$17","_el$20","_el$21","_co$5","_el$18","_el$22","_el$23","_co$6","confidence","_v$3","_v$4","_v$5","_v$6","classList","toggle","a","o","_$setStyleProperty","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"VerifiedText.js","sources":["../../src/components/VerifiedText.tsx"],"sourcesContent":["/**\n * VerifiedText — renders LLM text with inline verification badges\n * v3.1.0: Highlights verified vs hallucinated numbers\n *\n * @experimental\n *\n * Modes:\n * - highlight: ✅/⚠️ badges next to numbers (default)\n * - strip: replaces hallucinated numbers with the `verifiedStripLabel`\n * chrome string ('[unverified]' by default)\n * - annotate: tooltip on hover with closest source number\n */\n\nimport { createMemo, For } from 'solid-js'\nimport type { DataValidation, HallucinatedNumber } from '../types/chat-bus'\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface VerifiedTextProps {\n text: string\n validation: DataValidation\n /** Display mode (default: 'highlight') */\n mode?: 'highlight' | 'strip' | 'annotate'\n /** Callback when a hallucinated number is clicked */\n onHallucinationClick?: (item: HallucinatedNumber) => void\n}\n\ninterface TextSegment {\n type: 'text' | 'verified' | 'hallucinated'\n content: string\n item?: HallucinatedNumber\n}\n\n/**\n * Build annotated segments by splitting text at number positions.\n * Verified numbers get ✅, hallucinated get ⚠️.\n */\nfunction buildAnnotatedSegments(text: string, validation: DataValidation): TextSegment[] {\n // Build position maps\n const hallucinatedPositions = new Map<number, HallucinatedNumber>()\n for (const h of validation.hallucinated) {\n hallucinatedPositions.set(h.position, h)\n }\n\n const verifiedPositions = new Set<number>()\n for (const n of validation.llmNumbers) {\n if (!hallucinatedPositions.has(n.position)) {\n verifiedPositions.add(n.position)\n }\n }\n\n // Re-extract number lengths from text for precise splitting\n const numberRegex = /\\d[\\d\\s,.]*\\d|\\d+/g\n const matches: Array<{ start: number; end: number }> = []\n let m: RegExpExecArray | null\n while ((m = numberRegex.exec(text)) !== null) {\n matches.push({ start: m.index, end: m.index + m[0].length })\n }\n\n // Build a lookup from position → match\n const matchByPosition = new Map<number, { start: number; end: number }>()\n for (const match of matches) {\n matchByPosition.set(match.start, match)\n }\n\n // Build segments\n const segments: TextSegment[] = []\n let cursor = 0\n\n for (const num of validation.llmNumbers) {\n const match = matchByPosition.get(num.position)\n if (!match) continue\n\n // Text before this number\n if (match.start > cursor) {\n segments.push({ type: 'text', content: text.slice(cursor, match.start) })\n }\n\n const numberText = text.slice(match.start, match.end)\n const hallucinated = hallucinatedPositions.get(num.position)\n\n if (hallucinated) {\n segments.push({ type: 'hallucinated', content: numberText, item: hallucinated })\n } else {\n segments.push({ type: 'verified', content: numberText })\n }\n\n cursor = match.end\n }\n\n // Remaining text\n if (cursor < text.length) {\n segments.push({ type: 'text', content: text.slice(cursor) })\n }\n\n return segments\n}\n\nexport function VerifiedText(props: VerifiedTextProps) {\n const strings = useMCPUIStrings()\n const mode = () => props.mode || 'highlight'\n\n const segments = createMemo<TextSegment[]>(() => {\n if (!props.validation || props.validation.valid) {\n return [{ type: 'text' as const, content: props.text }]\n }\n return buildAnnotatedSegments(props.text, props.validation)\n })\n\n return (\n <div class=\"verified-text text-sm leading-relaxed\">\n <For each={segments()}>\n {(seg) => {\n if (seg.type === 'text') {\n return <span>{seg.content}</span>\n }\n\n if (seg.type === 'verified') {\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-300\"\n title={strings.verifiedTitle}\n >\n {seg.content}\n <span class=\"text-xs opacity-70\" aria-label={strings.verifiedAria}>&#x2705;</span>\n </span>\n )\n }\n\n // hallucinated\n const h = seg.item!\n const tooltipText = () => {\n if (h.closest != null && h.distance != null) {\n return formatMCPUIString(strings.verifiedNotFoundClosest, {\n closest: h.closest,\n pct: Math.round(h.distance * 100),\n })\n }\n return strings.verifiedNotFound\n }\n\n if (mode() === 'strip') {\n return (\n <span\n class=\"inline-flex items-center px-1 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-700 dark:text-amber-300 italic text-xs\"\n title={tooltipText()}\n >\n {strings.verifiedStripLabel}\n </span>\n )\n }\n\n return (\n <span\n class=\"inline-flex items-center gap-0.5 px-0.5 rounded bg-amber-50 dark:bg-amber-900/20 text-amber-800 dark:text-amber-300 cursor-help\"\n title={tooltipText()}\n onClick={() => props.onHallucinationClick?.(h)}\n role={props.onHallucinationClick ? 'button' : undefined}\n >\n {seg.content}\n <span class=\"text-xs\" aria-label={strings.unverifiedAria}>&#x26A0;&#xFE0F;</span>\n </span>\n )\n }}\n </For>\n\n {/* Confidence bar */}\n {props.validation && !props.validation.valid && (\n <div class=\"mt-2 flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400\">\n <div class=\"flex-1 h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden\">\n <div\n class=\"h-full rounded-full transition-all\"\n classList={{\n 'bg-green-500': props.validation.confidence >= 0.8,\n 'bg-amber-500': props.validation.confidence >= 0.5 && props.validation.confidence < 0.8,\n 'bg-red-500': props.validation.confidence < 0.5,\n }}\n style={{ width: `${Math.round(props.validation.confidence * 100)}%` }}\n />\n </div>\n <span>\n {formatMCPUIString(strings.verifiedConfidence, {\n pct: Math.round(props.validation.confidence * 100),\n })}{' '}\n {formatMCPUIString(strings.verifiedUnverifiedCount, {\n count: props.validation.hallucinated.length,\n })}\n </span>\n </div>\n )}\n </div>\n )\n}\n"],"names":["buildAnnotatedSegments","text","validation","hallucinatedPositions","Map","h","hallucinated","set","position","verifiedPositions","Set","n","llmNumbers","has","add","numberRegex","matches","m","exec","push","start","index","end","length","matchByPosition","match","segments","cursor","num","get","type","content","slice","numberText","item","VerifiedText","props","strings","useMCPUIStrings","mode","createMemo","valid","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","_co$","_$getNextMarker","nextSibling","_el$4","_el$5","_co$2","_$insert","_$createComponent","For","each","children","seg","_el$6","_tmpl$2","_el$7","_tmpl$3","_el$9","_el$0","_co$3","_el$8","_$effect","_p$","_v$","verifiedTitle","_v$2","verifiedAria","e","_$setAttribute","t","undefined","tooltipText","closest","distance","formatMCPUIString","verifiedNotFoundClosest","pct","Math","round","verifiedNotFound","_el$1","_tmpl$4","verifiedStripLabel","_el$10","_tmpl$5","_el$12","_el$13","_co$4","_el$11","$$click","onHallucinationClick","_v$3","_v$4","_v$5","unverifiedAria","a","_$runHydrationEvents","_c$","_$memo","_el$14","_tmpl$6","_el$15","_el$16","_el$17","_el$19","_el$20","_co$5","_el$18","_el$21","_el$22","_co$6","verifiedConfidence","confidence","verifiedUnverifiedCount","count","_v$6","_v$7","_v$8","_v$9","classList","toggle","o","_$setStyleProperty","_$delegateEvents"],"mappings":"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"}
@@ -3,7 +3,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const web = require("solid-js/web");
4
4
  const solidJs = require("solid-js");
5
5
  const ExpandableWrapper = require("./ExpandableWrapper.cjs");
6
- var _tmpl$ = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ web.template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions>Your browser does not support the video tag.`);
6
+ const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
7
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ web.template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions><!$><!/>`);
7
8
  function parseVideoUrl(url) {
8
9
  const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
9
10
  if (youtubeMatch) {
@@ -24,6 +25,7 @@ function parseVideoUrl(url) {
24
25
  };
25
26
  }
26
27
  const VideoRenderer = (props) => {
28
+ const strings = MCPUIStringsContext.useMCPUIStrings();
27
29
  const params = () => {
28
30
  var _a;
29
31
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
@@ -84,13 +86,15 @@ const VideoRenderer = (props) => {
84
86
  return web.createComponent(ExpandableWrapper.ExpandableWrapper, {
85
87
  get title() {
86
88
  var _a;
87
- return ((_a = params()) == null ? void 0 : _a.title) || "Video";
89
+ return ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle;
88
90
  },
89
91
  get copyData() {
90
92
  var _a;
91
93
  return ((_a = params()) == null ? void 0 : _a.url) || "";
92
94
  },
93
- copyLabel: "Copy video URL",
95
+ get copyLabel() {
96
+ return strings.videoCopy;
97
+ },
94
98
  get toolbarVariant() {
95
99
  return props.toolbarVariant;
96
100
  },
@@ -115,8 +119,9 @@ const VideoRenderer = (props) => {
115
119
  return (
116
120
  // Direct video file
117
121
  (() => {
118
- var _el$10 = web.getNextElement(_tmpl$5);
122
+ var _el$10 = web.getNextElement(_tmpl$5), _el$11 = _el$10.firstChild, _el$12 = _el$11.nextSibling, [_el$13, _co$3] = web.getNextMarker(_el$12.nextSibling);
119
123
  _el$10.addEventListener("error", handleVideoError);
124
+ web.insert(_el$10, () => strings.videoUnsupported, _el$13, _co$3);
120
125
  web.effect((_p$) => {
121
126
  var _a, _b, _c, _d, _e, _f;
122
127
  var _v$5 = (_a = params()) == null ? void 0 : _a.url, _v$6 = (_b = params()) == null ? void 0 : _b.poster, _v$7 = (_c = params()) == null ? void 0 : _c.autoplay, _v$8 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$9 = (_e = params()) == null ? void 0 : _e.loop, _v$0 = (_f = params()) == null ? void 0 : _f.muted;
@@ -143,7 +148,7 @@ const VideoRenderer = (props) => {
143
148
  var _el$5 = web.getNextElement(_tmpl$2);
144
149
  web.effect((_p$) => {
145
150
  var _a;
146
- var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
151
+ var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle;
147
152
  _v$ !== _p$.e && web.setAttribute(_el$5, "src", _p$.e = _v$);
148
153
  _v$2 !== _p$.t && web.setAttribute(_el$5, "title", _p$.t = _v$2);
149
154
  return _p$;
@@ -1 +1 @@
1
- {"version":3,"file":"VideoRenderer.cjs","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || 'Video'}\n copyData={params()?.url || ''}\n copyLabel=\"Copy video URL\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n Your browser does not support the video tag.\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || 'Video'}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","addEventListener","_$effect","_p$","_v$5","_v$6","poster","_v$7","_v$8","_v$9","_v$0","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_v$3","_v$4","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
1
+ {"version":3,"file":"VideoRenderer.cjs","sources":["../../src/components/VideoRenderer.tsx"],"sourcesContent":["/**\n * VideoRenderer - Video embed component\n * Sprint 5: Media Components\n *\n * Supports YouTube, Vimeo, and direct video files\n */\n\nimport { Component, createMemo, Show } from 'solid-js'\nimport type { UIComponent, VideoComponentParams } from '../types'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface VideoRendererProps {\n /**\n * UIComponent containing video params\n */\n component?: UIComponent\n\n /**\n * Direct video params (alternative to component)\n */\n params?: VideoComponentParams\n\n /**\n * Error callback\n */\n onError?: (error: Error) => void\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Video provider type\n */\ntype VideoProvider = 'youtube' | 'vimeo' | 'direct'\n\n/**\n * Parsed video info\n */\ninterface VideoInfo {\n provider: VideoProvider\n videoId?: string\n}\n\n/**\n * Extract video ID and provider from URL\n */\nfunction parseVideoUrl(url: string): VideoInfo {\n // YouTube patterns:\n // - youtube.com/watch?v=VIDEO_ID\n // - youtube.com/embed/VIDEO_ID\n // - youtube.com/v/VIDEO_ID\n // - youtu.be/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/\n )\n if (youtubeMatch) {\n return { provider: 'youtube', videoId: youtubeMatch[1] }\n }\n\n // Vimeo patterns:\n // - vimeo.com/VIDEO_ID\n // - player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/(?:vimeo\\.com\\/|player\\.vimeo\\.com\\/video\\/)(\\d+)/)\n if (vimeoMatch) {\n return { provider: 'vimeo', videoId: vimeoMatch[1] }\n }\n\n // Direct video file\n return { provider: 'direct' }\n}\n\nexport const VideoRenderer: Component<VideoRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const params = () => props.params || (props.component?.params as VideoComponentParams)\n const isExpanded = useExpanded()\n\n const videoInfo = createMemo(() => parseVideoUrl(params()?.url || ''))\n\n const embedUrl = createMemo(() => {\n const info = videoInfo()\n const p = params()\n\n if (!p?.url) return null\n\n switch (info.provider) {\n case 'youtube': {\n const ytParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n controls: p.controls !== false ? '1' : '0',\n loop: p.loop ? '1' : '0',\n mute: p.muted ? '1' : '0',\n })\n if (p.startTime) {\n ytParams.set('start', String(p.startTime))\n }\n // Use youtube-nocookie.com for privacy\n return `https://www.youtube-nocookie.com/embed/${info.videoId}?${ytParams}`\n }\n\n case 'vimeo': {\n const vParams = new URLSearchParams({\n autoplay: p.autoplay ? '1' : '0',\n loop: p.loop ? '1' : '0',\n muted: p.muted ? '1' : '0',\n })\n return `https://player.vimeo.com/video/${info.videoId}?${vParams}`\n }\n\n default:\n return null\n }\n })\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '4:3':\n return 'aspect-[4/3]'\n case '21:9':\n return 'aspect-[21/9]'\n default:\n return 'aspect-video' // 16:9\n }\n }\n\n const handleVideoError = (e: Event) => {\n const error = new Error('Video failed to load')\n console.error('Video error:', e)\n props.onError?.(error)\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.videoTitle}\n copyData={params()?.url || ''}\n copyLabel={strings.videoCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Title */}\n <Show when={params()?.title}>\n <div class=\"px-4 py-3 border-b border-gray-200 dark:border-gray-700\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Video Container — when expanded, fill remaining space (override aspect ratio) */}\n <div class={`relative bg-black ${isExpanded() ? 'flex-1 min-h-0' : aspectClass()}`}>\n <Show\n when={embedUrl()}\n fallback={\n // Direct video file\n <video\n src={params()?.url}\n poster={params()?.poster}\n autoplay={params()?.autoplay}\n controls={params()?.controls !== false}\n loop={params()?.loop}\n muted={params()?.muted}\n playsinline\n class=\"absolute inset-0 w-full h-full object-contain\"\n onError={handleVideoError}\n >\n <track kind=\"captions\" />\n {strings.videoUnsupported}\n </video>\n }\n >\n {/* YouTube/Vimeo embed */}\n <iframe\n src={embedUrl()!}\n title={params()?.title || strings.videoTitle}\n class=\"absolute inset-0 w-full h-full\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n allowfullscreen\n loading=\"lazy\"\n />\n </Show>\n </div>\n\n {/* Caption */}\n <Show when={params()?.caption}>\n <div class=\"px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <p class=\"text-sm text-gray-600 dark:text-gray-400\">{params()!.caption}</p>\n </div>\n </Show>\n </div>\n </ExpandableWrapper>\n )\n}\n\n/**\n * Check if a URL is from a supported video provider\n */\nexport function isSupportedVideoUrl(url: string): boolean {\n const info = parseVideoUrl(url)\n return info.provider !== 'direct' || url.match(/\\.(mp4|webm|ogg|mov)$/i) !== null\n}\n\n/**\n * Get video provider from URL\n */\nexport function getVideoProvider(url: string): VideoProvider {\n return parseVideoUrl(url).provider\n}\n"],"names":["parseVideoUrl","url","youtubeMatch","match","provider","videoId","vimeoMatch","VideoRenderer","props","strings","useMCPUIStrings","params","component","isExpanded","useExpanded","videoInfo","createMemo","embedUrl","info","p","ytParams","URLSearchParams","autoplay","controls","loop","mute","muted","startTime","set","String","vParams","aspectClass","aspectRatio","handleVideoError","e","error","Error","console","onError","_$createComponent","ExpandableWrapper","title","videoTitle","copyData","copyLabel","videoCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$4","_el$8","firstChild","_el$9","_co$","_$getNextMarker","nextSibling","_el$4","_el$0","_el$1","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","fallback","_el$10","_tmpl$5","_el$11","_el$12","_el$13","_co$3","addEventListener","videoUnsupported","_$effect","_p$","_v$5","_v$6","poster","_v$7","_v$8","_v$9","_v$0","_$setAttribute","t","a","_$setProperty","o","i","n","undefined","_el$5","_tmpl$2","_v$","_v$2","caption","_el$6","_tmpl$3","_el$7","_v$3","_v$4","_$className","isSupportedVideoUrl","getVideoProvider"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VideoRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAoB,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAGjE,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAsCnD,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAwHvD,CAAA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAE3D"}
1
+ {"version":3,"file":"VideoRenderer.d.ts","sourceRoot":"","sources":["../../src/components/VideoRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAoB,MAAM,UAAU,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAIjE,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAE7B;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAEhC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED;;GAEG;AACH,KAAK,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAsCnD,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAyHvD,CAAA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAE3D"}
@@ -1,7 +1,8 @@
1
1
  import { createComponent, getNextElement, getNextMarker, insert, effect, setAttribute, setProperty, className, template } from "solid-js/web";
2
2
  import { createMemo, Show } from "solid-js";
3
3
  import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
4
- var _tmpl$ = /* @__PURE__ */ template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions>Your browser does not support the video tag.`);
4
+ import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
5
+ var _tmpl$ = /* @__PURE__ */ template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ template(`<iframe class="absolute inset-0 w-full h-full"allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"allowfullscreen loading=lazy>`, true, false, false), _tmpl$3 = /* @__PURE__ */ template(`<div class="px-4 py-3 border-t border-gray-200 dark:border-gray-700 flex-shrink-0"><p class="text-sm text-gray-600 dark:text-gray-400">`), _tmpl$4 = /* @__PURE__ */ template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$5 = /* @__PURE__ */ template(`<video playsinline class="absolute inset-0 w-full h-full object-contain"><track kind=captions><!$><!/>`);
5
6
  function parseVideoUrl(url) {
6
7
  const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
7
8
  if (youtubeMatch) {
@@ -22,6 +23,7 @@ function parseVideoUrl(url) {
22
23
  };
23
24
  }
24
25
  const VideoRenderer = (props) => {
26
+ const strings = useMCPUIStrings();
25
27
  const params = () => {
26
28
  var _a;
27
29
  return props.params || ((_a = props.component) == null ? void 0 : _a.params);
@@ -82,13 +84,15 @@ const VideoRenderer = (props) => {
82
84
  return createComponent(ExpandableWrapper, {
83
85
  get title() {
84
86
  var _a;
85
- return ((_a = params()) == null ? void 0 : _a.title) || "Video";
87
+ return ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle;
86
88
  },
87
89
  get copyData() {
88
90
  var _a;
89
91
  return ((_a = params()) == null ? void 0 : _a.url) || "";
90
92
  },
91
- copyLabel: "Copy video URL",
93
+ get copyLabel() {
94
+ return strings.videoCopy;
95
+ },
92
96
  get toolbarVariant() {
93
97
  return props.toolbarVariant;
94
98
  },
@@ -113,8 +117,9 @@ const VideoRenderer = (props) => {
113
117
  return (
114
118
  // Direct video file
115
119
  (() => {
116
- var _el$10 = getNextElement(_tmpl$5);
120
+ var _el$10 = getNextElement(_tmpl$5), _el$11 = _el$10.firstChild, _el$12 = _el$11.nextSibling, [_el$13, _co$3] = getNextMarker(_el$12.nextSibling);
117
121
  _el$10.addEventListener("error", handleVideoError);
122
+ insert(_el$10, () => strings.videoUnsupported, _el$13, _co$3);
118
123
  effect((_p$) => {
119
124
  var _a, _b, _c, _d, _e, _f;
120
125
  var _v$5 = (_a = params()) == null ? void 0 : _a.url, _v$6 = (_b = params()) == null ? void 0 : _b.poster, _v$7 = (_c = params()) == null ? void 0 : _c.autoplay, _v$8 = ((_d = params()) == null ? void 0 : _d.controls) !== false, _v$9 = (_e = params()) == null ? void 0 : _e.loop, _v$0 = (_f = params()) == null ? void 0 : _f.muted;
@@ -141,7 +146,7 @@ const VideoRenderer = (props) => {
141
146
  var _el$5 = getNextElement(_tmpl$2);
142
147
  effect((_p$) => {
143
148
  var _a;
144
- var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || "Video";
149
+ var _v$ = embedUrl(), _v$2 = ((_a = params()) == null ? void 0 : _a.title) || strings.videoTitle;
145
150
  _v$ !== _p$.e && setAttribute(_el$5, "src", _p$.e = _v$);
146
151
  _v$2 !== _p$.t && setAttribute(_el$5, "title", _p$.t = _v$2);
147
152
  return _p$;