@seed-ship/mcp-ui-solid 6.19.1 → 6.21.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 (282) hide show
  1. package/CHANGELOG.md +663 -0
  2. package/README.md +911 -4
  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/IframeFallbackLink.cjs +37 -0
  111. package/dist/components/IframeFallbackLink.cjs.map +1 -0
  112. package/dist/components/IframeFallbackLink.d.ts +23 -0
  113. package/dist/components/IframeFallbackLink.d.ts.map +1 -0
  114. package/dist/components/IframeFallbackLink.js +37 -0
  115. package/dist/components/IframeFallbackLink.js.map +1 -0
  116. package/dist/components/ImageGalleryRenderer.cjs +13 -4
  117. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  118. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  119. package/dist/components/ImageGalleryRenderer.js +13 -4
  120. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  121. package/dist/components/LightboxOverlay.cjs +16 -8
  122. package/dist/components/LightboxOverlay.cjs.map +1 -1
  123. package/dist/components/LightboxOverlay.d.ts.map +1 -1
  124. package/dist/components/LightboxOverlay.js +17 -9
  125. package/dist/components/LightboxOverlay.js.map +1 -1
  126. package/dist/components/MapRenderer.cjs +59 -29
  127. package/dist/components/MapRenderer.cjs.map +1 -1
  128. package/dist/components/MapRenderer.d.ts +10 -2
  129. package/dist/components/MapRenderer.d.ts.map +1 -1
  130. package/dist/components/MapRenderer.js +60 -30
  131. package/dist/components/MapRenderer.js.map +1 -1
  132. package/dist/components/ModalRenderer.cjs +4 -1
  133. package/dist/components/ModalRenderer.cjs.map +1 -1
  134. package/dist/components/ModalRenderer.d.ts.map +1 -1
  135. package/dist/components/ModalRenderer.js +5 -2
  136. package/dist/components/ModalRenderer.js.map +1 -1
  137. package/dist/components/ScratchpadPanel.cjs +578 -535
  138. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  139. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  140. package/dist/components/ScratchpadPanel.js +578 -535
  141. package/dist/components/ScratchpadPanel.js.map +1 -1
  142. package/dist/components/StreamingUIRenderer.cjs +92 -28
  143. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  144. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  145. package/dist/components/StreamingUIRenderer.js +92 -28
  146. package/dist/components/StreamingUIRenderer.js.map +1 -1
  147. package/dist/components/UIResourceRenderer.cjs +481 -365
  148. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  149. package/dist/components/UIResourceRenderer.d.ts +21 -0
  150. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  151. package/dist/components/UIResourceRenderer.js +484 -368
  152. package/dist/components/UIResourceRenderer.js.map +1 -1
  153. package/dist/components/VerifiedText.cjs +39 -20
  154. package/dist/components/VerifiedText.cjs.map +1 -1
  155. package/dist/components/VerifiedText.d.ts +2 -1
  156. package/dist/components/VerifiedText.d.ts.map +1 -1
  157. package/dist/components/VerifiedText.js +39 -20
  158. package/dist/components/VerifiedText.js.map +1 -1
  159. package/dist/components/VideoRenderer.cjs +51 -20
  160. package/dist/components/VideoRenderer.cjs.map +1 -1
  161. package/dist/components/VideoRenderer.d.ts.map +1 -1
  162. package/dist/components/VideoRenderer.js +51 -20
  163. package/dist/components/VideoRenderer.js.map +1 -1
  164. package/dist/components/chart-data-table.cjs +38 -17
  165. package/dist/components/chart-data-table.cjs.map +1 -1
  166. package/dist/components/chart-data-table.d.ts +30 -1
  167. package/dist/components/chart-data-table.d.ts.map +1 -1
  168. package/dist/components/chart-data-table.js +38 -17
  169. package/dist/components/chart-data-table.js.map +1 -1
  170. package/dist/context/MCPActionContext.cjs +3 -1
  171. package/dist/context/MCPActionContext.cjs.map +1 -1
  172. package/dist/context/MCPActionContext.d.ts.map +1 -1
  173. package/dist/context/MCPActionContext.js +3 -1
  174. package/dist/context/MCPActionContext.js.map +1 -1
  175. package/dist/context/MCPUIConfigContext.cjs +29 -0
  176. package/dist/context/MCPUIConfigContext.cjs.map +1 -0
  177. package/dist/context/MCPUIConfigContext.d.ts +124 -0
  178. package/dist/context/MCPUIConfigContext.d.ts.map +1 -0
  179. package/dist/context/MCPUIConfigContext.js +29 -0
  180. package/dist/context/MCPUIConfigContext.js.map +1 -0
  181. package/dist/context/MCPUIStringsContext.cjs +287 -2
  182. package/dist/context/MCPUIStringsContext.cjs.map +1 -1
  183. package/dist/context/MCPUIStringsContext.d.ts +607 -3
  184. package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
  185. package/dist/context/MCPUIStringsContext.js +287 -2
  186. package/dist/context/MCPUIStringsContext.js.map +1 -1
  187. package/dist/hooks/index.d.ts +2 -2
  188. package/dist/hooks/index.d.ts.map +1 -1
  189. package/dist/hooks/useStreamingUI.cjs +28 -10
  190. package/dist/hooks/useStreamingUI.cjs.map +1 -1
  191. package/dist/hooks/useStreamingUI.d.ts +37 -0
  192. package/dist/hooks/useStreamingUI.d.ts.map +1 -1
  193. package/dist/hooks/useStreamingUI.js +28 -10
  194. package/dist/hooks/useStreamingUI.js.map +1 -1
  195. package/dist/hooks.cjs +1 -0
  196. package/dist/hooks.cjs.map +1 -1
  197. package/dist/hooks.js +2 -1
  198. package/dist/index.cjs +29 -8
  199. package/dist/index.cjs.map +1 -1
  200. package/dist/index.d.cts +12 -4
  201. package/dist/index.d.ts +12 -4
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +24 -3
  204. package/dist/index.js.map +1 -1
  205. package/dist/services/index.d.ts +1 -1
  206. package/dist/services/index.d.ts.map +1 -1
  207. package/dist/services/validation.cjs +52 -26
  208. package/dist/services/validation.cjs.map +1 -1
  209. package/dist/services/validation.d.ts +67 -2
  210. package/dist/services/validation.d.ts.map +1 -1
  211. package/dist/services/validation.js +52 -26
  212. package/dist/services/validation.js.map +1 -1
  213. package/dist/utils/degraded-projections.cjs +37 -12
  214. package/dist/utils/degraded-projections.cjs.map +1 -1
  215. package/dist/utils/degraded-projections.d.ts +39 -3
  216. package/dist/utils/degraded-projections.d.ts.map +1 -1
  217. package/dist/utils/degraded-projections.js +37 -12
  218. package/dist/utils/degraded-projections.js.map +1 -1
  219. package/dist/utils/format-string.cjs +10 -0
  220. package/dist/utils/format-string.cjs.map +1 -0
  221. package/dist/utils/format-string.d.ts +26 -0
  222. package/dist/utils/format-string.d.ts.map +1 -0
  223. package/dist/utils/format-string.js +10 -0
  224. package/dist/utils/format-string.js.map +1 -0
  225. package/dist/utils/iframe-coep.cjs +30 -0
  226. package/dist/utils/iframe-coep.cjs.map +1 -0
  227. package/dist/utils/iframe-coep.d.ts +46 -0
  228. package/dist/utils/iframe-coep.d.ts.map +1 -0
  229. package/dist/utils/iframe-coep.js +30 -0
  230. package/dist/utils/iframe-coep.js.map +1 -0
  231. package/dist/validation.cjs +5 -0
  232. package/dist/validation.cjs.map +1 -1
  233. package/dist/validation.d.cts +2 -1
  234. package/dist/validation.d.ts +2 -1
  235. package/dist/validation.d.ts.map +1 -1
  236. package/dist/validation.js +6 -1
  237. package/package.json +1 -1
  238. package/src/adapters/connector.ts +58 -4
  239. package/src/adapters/index.ts +12 -2
  240. package/src/adapters/macro-run.ts +71 -11
  241. package/src/components/ActionGroupRenderer.tsx +3 -1
  242. package/src/components/AgentCard.tsx +27 -9
  243. package/src/components/AgentHandoff.tsx +4 -1
  244. package/src/components/ArtifactRenderer.tsx +17 -5
  245. package/src/components/AutocompleteDropdown.tsx +8 -6
  246. package/src/components/AutocompleteFormField.tsx +5 -1
  247. package/src/components/BriefingDiff.tsx +11 -3
  248. package/src/components/CarouselRenderer.tsx +4 -2
  249. package/src/components/ChartJSRenderer.tsx +18 -9
  250. package/src/components/ChatPrompt.tsx +13 -8
  251. package/src/components/CodeBlockRenderer.tsx +12 -10
  252. package/src/components/DataPreviewSection.tsx +92 -26
  253. package/src/components/DegradedFallback.tsx +4 -2
  254. package/src/components/FooterRenderer.tsx +3 -1
  255. package/src/components/FormFieldRenderer.tsx +34 -19
  256. package/src/components/FormRenderer.tsx +38 -10
  257. package/src/components/GenerativeUIErrorBoundary.tsx +8 -3
  258. package/src/components/GraphRenderer.tsx +31 -16
  259. package/src/components/IframeFallbackLink.tsx +57 -0
  260. package/src/components/ImageGalleryRenderer.tsx +8 -4
  261. package/src/components/LightboxOverlay.tsx +6 -4
  262. package/src/components/MapRenderer.tsx +57 -20
  263. package/src/components/ModalRenderer.tsx +3 -1
  264. package/src/components/ScratchpadPanel.tsx +95 -44
  265. package/src/components/StreamingUIRenderer.tsx +41 -11
  266. package/src/components/UIResourceRenderer.tsx +176 -67
  267. package/src/components/VerifiedText.tsx +19 -9
  268. package/src/components/VideoRenderer.tsx +26 -4
  269. package/src/components/chart-data-table.ts +68 -14
  270. package/src/context/MCPActionContext.tsx +4 -1
  271. package/src/context/MCPUIConfigContext.tsx +151 -0
  272. package/src/context/MCPUIStringsContext.tsx +1014 -1
  273. package/src/hooks/index.ts +2 -1
  274. package/src/hooks/useStreamingUI.ts +64 -10
  275. package/src/index.ts +38 -0
  276. package/src/services/index.ts +1 -0
  277. package/src/services/validation.ts +131 -32
  278. package/src/solid-jsx-attrs.d.ts +24 -0
  279. package/src/utils/degraded-projections.ts +75 -12
  280. package/src/utils/format-string.ts +33 -0
  281. package/src/utils/iframe-coep.ts +74 -0
  282. package/src/validation.ts +9 -0
@@ -6,7 +6,9 @@ import { DegradedFallback } from "./DegradedFallback.js";
6
6
  import { graphToDegradedTable } from "../utils/degraded-projections.js";
7
7
  import { escapeHtml } from "../utils/escape-html.js";
8
8
  import { useTelemetry } from "../context/MCPUITelemetryContext.js";
9
- var _tmpl$ = /* @__PURE__ */ template(`<div><div class="absolute right-2 top-2 z-10"><button type=button class="px-2 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"title="Export graph"aria-label="Export graph"aria-haspopup=menu>Export ▾</button><!$><!/></div><div></div><!$><!/>`), _tmpl$2 = /* @__PURE__ */ template(`<div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Graph rendering unavailable</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code> components.`), _tmpl$3 = /* @__PURE__ */ template(`<div class="w-full p-4 bg-gray-50 dark:bg-gray-800/40 border border-gray-200 dark:border-gray-700 rounded-lg animate-pulse"><div class="h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2"></div><div class="h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded">`), _tmpl$4 = /* @__PURE__ */ template(`<button type=button class="w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-b border-gray-100 dark:border-gray-700 last:border-b-0"><div class=font-medium></div><div class="text-[10px] text-gray-500 dark:text-gray-400">`);
9
+ import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
10
+ import { formatMCPUIString } from "../utils/format-string.js";
11
+ var _tmpl$ = /* @__PURE__ */ template(`<div><div class="absolute right-2 top-2 z-10"><button type=button class="px-2 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"aria-haspopup=menu></button><!$><!/></div><div></div><!$><!/>`), _tmpl$2 = /* @__PURE__ */ template(`<div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100"></p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code> components.`), _tmpl$3 = /* @__PURE__ */ template(`<div class="w-full p-4 bg-gray-50 dark:bg-gray-800/40 border border-gray-200 dark:border-gray-700 rounded-lg animate-pulse"><div class="h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2"></div><div class="h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded">`), _tmpl$4 = /* @__PURE__ */ template(`<button type=button class="w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-b border-gray-100 dark:border-gray-700 last:border-b-0"><div class=font-medium></div><div class="text-[10px] text-gray-500 dark:text-gray-400">`);
10
12
  let g6ModulePromise;
11
13
  async function isG6Available() {
12
14
  try {
@@ -94,6 +96,7 @@ function downloadBlob(content, filename, mimeType) {
94
96
  URL.revokeObjectURL(url);
95
97
  }
96
98
  const GraphRenderer = (props) => {
99
+ const strings = useMCPUIStrings();
97
100
  const params = () => props.component.params;
98
101
  const isExpanded = useExpanded();
99
102
  const telemetry = useTelemetry();
@@ -142,7 +145,7 @@ const GraphRenderer = (props) => {
142
145
  graphInstance = new Graph(config);
143
146
  await graphInstance.render();
144
147
  } catch (err) {
145
- const message = err instanceof Error ? err.message : "Failed to render graph";
148
+ const message = err instanceof Error ? err.message : strings.graphRenderError;
146
149
  setError(message);
147
150
  telemetry == null ? void 0 : telemetry.dispatch({
148
151
  type: "render:error",
@@ -179,13 +182,13 @@ const GraphRenderer = (props) => {
179
182
  const url = canvas.toDataURL("image/png");
180
183
  await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);
181
184
  } else {
182
- setError("PNG export not supported in current renderer mode");
185
+ setError(strings.graphPngUnsupported);
183
186
  }
184
187
  } else {
185
188
  await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);
186
189
  }
187
190
  } catch (err) {
188
- setError(err instanceof Error ? err.message : "PNG export failed");
191
+ setError(err instanceof Error ? err.message : strings.graphPngExportFailed);
189
192
  }
190
193
  setExportMenuOpen(false);
191
194
  };
@@ -205,19 +208,23 @@ const GraphRenderer = (props) => {
205
208
  );
206
209
  },
207
210
  get children() {
208
- return getNextElement(_tmpl$2);
211
+ var _el$9 = getNextElement(_tmpl$2), _el$0 = _el$9.firstChild;
212
+ insert(_el$0, () => strings.graphUnavailable);
213
+ return _el$9;
209
214
  }
210
215
  });
211
216
  },
212
217
  get children() {
213
218
  return createComponent(ExpandableWrapper, {
214
219
  get title() {
215
- return params().title ?? "Graph";
220
+ return params().title ?? strings.graphTitle;
216
221
  },
217
222
  get copyData() {
218
223
  return toJSON(params());
219
224
  },
220
- copyLabel: "Copy graph (JSON)",
225
+ get copyLabel() {
226
+ return strings.graphCopy;
227
+ },
221
228
  get toolbarVariant() {
222
229
  return props.toolbarVariant;
223
230
  },
@@ -226,6 +233,7 @@ const GraphRenderer = (props) => {
226
233
  _el$3.$$click = () => setExportMenuOpen((v) => !v);
227
234
  var _ref$ = exportTriggerRef;
228
235
  typeof _ref$ === "function" ? use(_ref$, _el$3) : exportTriggerRef = _el$3;
236
+ insert(_el$3, () => strings.graphExportMenu);
229
237
  insert(_el$2, createComponent(PortalDropdownMenu, {
230
238
  get open() {
231
239
  return exportMenuOpen();
@@ -236,26 +244,28 @@ const GraphRenderer = (props) => {
236
244
  "class": "text-xs",
237
245
  get children() {
238
246
  return createComponent(For, {
239
- each: [{
240
- label: "Download PNG",
241
- onClick: handleExportPNG,
242
- hint: "visual snapshot"
243
- }, {
244
- label: "Download Mermaid",
245
- onClick: handleExportMermaid,
246
- hint: "markdown / GitHub"
247
- }, {
248
- label: "Download JSON",
249
- onClick: handleExportJSON,
250
- hint: "raw data"
251
- }],
247
+ get each() {
248
+ return [{
249
+ label: strings.graphDownloadPng,
250
+ onClick: handleExportPNG,
251
+ hint: strings.graphDownloadPngHint
252
+ }, {
253
+ label: strings.graphDownloadMermaid,
254
+ onClick: handleExportMermaid,
255
+ hint: strings.graphDownloadMermaidHint
256
+ }, {
257
+ label: strings.graphDownloadJson,
258
+ onClick: handleExportJSON,
259
+ hint: strings.graphDownloadJsonHint
260
+ }];
261
+ },
252
262
  children: (item) => (() => {
253
- var _el$1 = getNextElement(_tmpl$4), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling;
254
- addEventListener(_el$1, "click", item.onClick, true);
255
- insert(_el$10, () => item.label);
256
- insert(_el$11, () => item.hint);
263
+ var _el$10 = getNextElement(_tmpl$4), _el$11 = _el$10.firstChild, _el$12 = _el$11.nextSibling;
264
+ addEventListener(_el$10, "click", item.onClick, true);
265
+ insert(_el$11, () => item.label);
266
+ insert(_el$12, () => item.hint);
257
267
  runHydrationEvents();
258
- return _el$1;
268
+ return _el$10;
259
269
  })()
260
270
  });
261
271
  }
@@ -269,24 +279,37 @@ const GraphRenderer = (props) => {
269
279
  get children() {
270
280
  return createComponent(DegradedFallback, mergeProps({
271
281
  get message() {
272
- return `Graph rendering failed: ${error()}`;
282
+ return formatMCPUIString(strings.graphRenderFailed, {
283
+ error: String(error())
284
+ });
273
285
  },
274
- caption: "Showing the graph data as a table — the interactive view is unavailable."
275
- }, () => graphToDegradedTable(params())));
286
+ get caption() {
287
+ return strings.graphDegradedCaption;
288
+ }
289
+ }, () => graphToDegradedTable(params(), {
290
+ source: strings.degradedColSource,
291
+ target: strings.degradedColTarget,
292
+ label: strings.degradedColLabel,
293
+ node: strings.degradedColNode
294
+ })));
276
295
  }
277
296
  }), _el$8, _co$2);
278
297
  effect((_p$) => {
279
- var _v$ = `relative w-full ${params().className ?? ""} ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = exportMenuOpen(), _v$3 = `bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${error() ? "hidden" : ""} ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$4 = isExpanded() ? `height: 100%; width: ${params().width ?? "100%"};` : `height: ${params().height ?? "400px"}; width: ${params().width ?? "100%"};`;
298
+ var _v$ = `relative w-full ${params().className ?? ""} ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = strings.graphExport, _v$3 = strings.graphExport, _v$4 = exportMenuOpen(), _v$5 = `bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${error() ? "hidden" : ""} ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$6 = isExpanded() ? `height: 100%; width: ${params().width ?? "100%"};` : `height: ${params().height ?? "400px"}; width: ${params().width ?? "100%"};`;
280
299
  _v$ !== _p$.e && className(_el$, _p$.e = _v$);
281
- _v$2 !== _p$.t && setAttribute(_el$3, "aria-expanded", _p$.t = _v$2);
282
- _v$3 !== _p$.a && className(_el$6, _p$.a = _v$3);
283
- _p$.o = style(_el$6, _v$4, _p$.o);
300
+ _v$2 !== _p$.t && setAttribute(_el$3, "title", _p$.t = _v$2);
301
+ _v$3 !== _p$.a && setAttribute(_el$3, "aria-label", _p$.a = _v$3);
302
+ _v$4 !== _p$.o && setAttribute(_el$3, "aria-expanded", _p$.o = _v$4);
303
+ _v$5 !== _p$.i && className(_el$6, _p$.i = _v$5);
304
+ _p$.n = style(_el$6, _v$6, _p$.n);
284
305
  return _p$;
285
306
  }, {
286
307
  e: void 0,
287
308
  t: void 0,
288
309
  a: void 0,
289
- o: void 0
310
+ o: void 0,
311
+ i: void 0,
312
+ n: void 0
290
313
  });
291
314
  runHydrationEvents();
292
315
  return _el$;
@@ -1 +1 @@
1
- {"version":3,"file":"GraphRenderer.js","sources":["../../src/components/GraphRenderer.tsx"],"sourcesContent":["/**\n * GraphRenderer (v6.0.0) — generic node-link visualization powered by\n * `@antv/g6 ^5` (peer-optional). Same lazy-load pattern as\n * `ChartJSRenderer` and `MapRenderer` : the heavy lib is dynamically\n * imported only on first mount, and apps that don't install the peer\n * see an informative fallback instead of a crash.\n *\n * Spec : `@seed-ship/mcp-ui-spec@5.0.4` exports `GraphComponentParamsSchema`,\n * `GraphNode`, `GraphEdge`, `GraphLayoutName`, `GraphLayout`,\n * `GraphComponentParams` — the shape consumed here. Domain semantics\n * (`weight` etc.) are opaque to this renderer ; consumers decide what\n * the values mean.\n *\n * Copy + export : the renderer ships with `<ExpandableWrapper>` (default\n * copy = JSON of `{nodes, edges}`) plus a 3-format export menu — **PNG**\n * (visual snapshot via the underlying canvas/SVG), **Mermaid** (markdown\n * / GitHub-renderable `flowchart` syntax), **JSON** (raw reimportable\n * data). All three are computed lazily on click.\n */\n\nimport { Component, createSignal, onCleanup, onMount, Show, For } from 'solid-js';\nimport type { UIComponent } from '../types';\nimport type {\n GraphComponentParams,\n GraphLayout,\n} from '@seed-ship/mcp-ui-spec';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { PortalDropdownMenu } from './PortalDropdownMenu';\nimport { DegradedFallback } from './DegradedFallback';\nimport { graphToDegradedTable } from '../utils/degraded-projections';\nimport { escapeHtml } from '../utils/escape-html';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\n\n// Module-scoped lazy import promise — first call triggers the dynamic\n// import, subsequent calls reuse the resolved module.\nlet g6ModulePromise: Promise<typeof import('@antv/g6')> | undefined;\n\n/**\n * Whether the `@antv/g6` peer dependency is installed and importable.\n * Resolves to `true` when the lib is available, `false` otherwise.\n *\n * Mirrors `isChartJSAvailable()` from `ChartJSRenderer`.\n */\nexport async function isG6Available(): Promise<boolean> {\n try {\n if (!g6ModulePromise) {\n g6ModulePromise = import('@antv/g6');\n }\n await g6ModulePromise;\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the spec layout shorthand or object form into the config object\n * G6 v5 expects. When `layout` is omitted, picks `'force'` if edges are\n * present (universal default) or `'circular'` otherwise.\n */\nfunction resolveLayout(params: GraphComponentParams): { type: string; [key: string]: unknown } {\n const layout: GraphLayout | undefined = params.layout;\n if (layout === undefined) {\n const hasEdges = (params.edges?.length ?? 0) > 0;\n return { type: hasEdges ? 'force' : 'circular' };\n }\n if (typeof layout === 'string') {\n return { type: layout };\n }\n // Object form: spread the passthrough options alongside `type`.\n return { type: layout.type, ...(layout.options ?? {}) };\n}\n\n/**\n * Build the G6 v5 `Graph` constructor config from the component params.\n *\n * Pure (no DOM/lib side effects beyond reading `container`), so it can be\n * unit-tested directly without a jsdom render — this is the contract the\n * 2026-05-30 audit (P0.2) wants locked.\n *\n * ⚠️ G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n * (that was the v4 contract). Passing the string `'canvas'` / `'svg'` makes\n * G6 throw `renderer is not a function` — and because the default path used\n * to pass the string `'canvas'`, EVERY graph crashed, not just svg. So we\n * **omit `renderer` entirely**: G6 then uses its built-in canvas renderer\n * (documented default `() => new CanvasRenderer()`).\n *\n * `rendererPref: 'svg'` is reserved but NOT wired yet — a real G6 v5 SVG\n * renderer needs the `@antv/g-svg` factory (a transitive dep of `@antv/g6`,\n * not statically resolvable at build time). Until that's wired behind proper\n * optional-peer resolution, svg degrades to the canvas default with a\n * one-time warning. It must NEVER inject a string `renderer`.\n */\nexport function buildGraphConfig(\n p: GraphComponentParams,\n container?: HTMLElement\n): Record<string, unknown> {\n const config: Record<string, unknown> = {\n container,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n\n if (p.tooltip !== false) {\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`;\n },\n },\n ];\n }\n\n return config;\n}\n\n/**\n * Build the G6 v5 `behaviors` array from the params interactivity flags.\n * Defaults : drag-canvas + zoom-canvas + drag-element + click-select.\n * Any flag set to `false` opts out.\n */\nfunction resolveBehaviors(params: GraphComponentParams): string[] {\n const behaviors: string[] = [];\n if (params.enableDrag !== false) behaviors.push('drag-element');\n if (params.enableZoom !== false) {\n behaviors.push('zoom-canvas', 'drag-canvas');\n }\n if (params.enableSelect !== false) behaviors.push('click-select');\n return behaviors;\n}\n\n/**\n * Pick a sensible Mermaid `flowchart` direction from the resolved layout.\n * `dagre` / `tree` / `mindmap` are top-down hierarchies → TD ; everything\n * else (force, concentric, circular, grid) → LR (default mermaid).\n */\nfunction mermaidDirection(layoutType: string): 'TD' | 'LR' {\n return layoutType === 'dagre' || layoutType === 'tree' || layoutType === 'mindmap' ? 'TD' : 'LR';\n}\n\n/**\n * Sanitize a string for use inside a Mermaid node label. Mermaid breaks\n * on raw quotes / brackets / pipes ; we strip the worst offenders.\n */\nfunction mermaidLabel(s: string): string {\n return s\n .replace(/[\"[\\]|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Convert the graph data to Mermaid `flowchart` syntax. The edge label\n * carries the optional `weight` prefix when present (e.g. `|3| label`).\n */\nfunction toMermaid(params: GraphComponentParams): string {\n const layoutType = resolveLayout(params).type;\n const dir = mermaidDirection(layoutType);\n const lines: string[] = [`flowchart ${dir}`];\n for (const n of params.nodes) {\n const label = mermaidLabel(n.label ?? n.id);\n lines.push(` ${n.id}[\"${label}\"]`);\n }\n for (const e of params.edges ?? []) {\n const labelParts: string[] = [];\n if (e.weight !== undefined) labelParts.push(String(e.weight));\n if (e.label) labelParts.push(mermaidLabel(e.label));\n const labelText = labelParts.join(' · ');\n if (labelText) {\n lines.push(` ${e.source} -->|${labelText}| ${e.target}`);\n } else {\n lines.push(` ${e.source} --> ${e.target}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction toJSON(params: GraphComponentParams): string {\n return JSON.stringify({ nodes: params.nodes, edges: params.edges ?? [] }, null, 2);\n}\n\nfunction downloadBlob(content: string | Blob, filename: string, mimeType?: string): void {\n const blob =\n typeof content === 'string' ? new Blob([content], { type: mimeType ?? 'text/plain' }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nexport interface GraphRendererProps {\n component: UIComponent;\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nexport const GraphRenderer: Component<GraphRendererProps> = (props) => {\n const params = () => props.component.params as GraphComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\n const [available, setAvailable] = createSignal<boolean | null>(null);\n const [error, setError] = createSignal<string | undefined>();\n const [exportMenuOpen, setExportMenuOpen] = createSignal(false);\n let containerRef: HTMLDivElement | undefined;\n // v6.4.0 — trigger ref consumed by <PortalDropdownMenu> for positioning\n let exportTriggerRef: HTMLButtonElement | undefined;\n // Loosely typed because G6 is a peer-optional — we don't pull its\n // types into the bundle just to type a transient local handle.\n let graphInstance: any | undefined;\n\n onMount(async () => {\n const g6Available = await isG6Available();\n setAvailable(g6Available);\n if (!g6Available || !containerRef) return;\n\n try {\n const { Graph } = await g6ModulePromise!;\n const p = params();\n const config: Record<string, unknown> = {\n container: containerRef,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n // G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n // (that was the v4 contract). Passing the string `'canvas'` / `'svg'`\n // makes G6 throw `renderer is not a function` — and because the default\n // path also passed the string `'canvas'`, EVERY graph crashed, not just\n // the svg one.\n //\n // Fix: omit `renderer` entirely. G6 then uses its built-in canvas\n // renderer (documented default `() => new CanvasRenderer()`), which is\n // what we want for every graph.\n //\n // `rendererPref: 'svg'` is NOT wired yet: a real G6 v5 SVG renderer\n // needs the `@antv/g-svg` factory, which is a *transitive* dep of\n // `@antv/g6` (not a declared peer) and isn't statically resolvable at\n // build time. Rather than ship a fragile/unresolvable import, we treat\n // svg as \"not yet supported\" and fall back to the canvas default with a\n // one-time warning. Tracked for a follow-up that wires the factory\n // behind a proper optional-peer resolution. (Never pass a string here.)\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n if (p.tooltip !== false) {\n // Built-in tooltip plugin — shows label + a compact dump of\n // node.data on hover. Consumers can opt out with `tooltip: false`.\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data\n ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>`\n : ''\n }</div>`;\n },\n },\n ];\n }\n graphInstance = new (Graph as any)(config);\n await graphInstance.render();\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to render graph';\n setError(message);\n // Fallback ladder (P2.5): the native G6 render threw — emit telemetry\n // so the failure is observable, then degrade to the edge/node table\n // below instead of leaving a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: message,\n id: props.component.id ?? '',\n componentType: 'graph',\n ts: Date.now(),\n });\n }\n });\n\n onCleanup(() => {\n try {\n graphInstance?.destroy();\n } catch {\n // G6 destroy can throw on already-destroyed instances or partial\n // init failures — silent because the component is unmounting anyway.\n }\n graphInstance = undefined;\n });\n\n // ─── Export handlers ────────────────────────────────────────────────\n const handleExportJSON = () => {\n downloadBlob(toJSON(params()), `${graphFilenameStem(params())}.json`, 'application/json');\n setExportMenuOpen(false);\n };\n\n const handleExportMermaid = () => {\n downloadBlob(toMermaid(params()), `${graphFilenameStem(params())}.mmd`, 'text/plain');\n setExportMenuOpen(false);\n };\n\n const handleExportPNG = async () => {\n if (!graphInstance) return;\n try {\n // G6 v5 exposes `toDataURL()` on the graph instance.\n const dataUrl: string = await graphInstance.toDataURL?.('image/png');\n if (!dataUrl) {\n // Fallback: try to grab the underlying canvas directly.\n const canvas = containerRef?.querySelector('canvas');\n if (canvas) {\n const url = (canvas as HTMLCanvasElement).toDataURL('image/png');\n await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);\n } else {\n setError('PNG export not supported in current renderer mode');\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'PNG export failed');\n }\n setExportMenuOpen(false);\n };\n\n return (\n <Show\n when={available() === true}\n fallback={\n <Show\n when={available() === false}\n fallback={\n // Loading skeleton while we determine peer availability\n <div class=\"w-full p-4 bg-gray-50 dark:bg-gray-800/40 border border-gray-200 dark:border-gray-700 rounded-lg animate-pulse\">\n <div class=\"h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2\" />\n <div class=\"h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded\" />\n </div>\n }\n >\n <div class=\"w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n Graph rendering unavailable\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Install <code>@antv/g6</code> peer dependency to render <code>type: \"graph\"</code>{' '}\n components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? 'Graph'}\n copyData={toJSON(params())}\n copyLabel=\"Copy graph (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div\n class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}\n >\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\n ref={exportTriggerRef}\n type=\"button\"\n onClick={() => setExportMenuOpen((v) => !v)}\n class=\"px-2 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300\"\n title=\"Export graph\"\n aria-label=\"Export graph\"\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n Export ▾\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For\n each={[\n { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },\n {\n label: 'Download Mermaid',\n onClick: handleExportMermaid,\n hint: 'markdown / GitHub',\n },\n { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },\n ]}\n >\n {(item) => (\n <button\n type=\"button\"\n onClick={item.onClick}\n class=\"w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-b border-gray-100 dark:border-gray-700 last:border-b-0\"\n >\n <div class=\"font-medium\">{item.label}</div>\n <div class=\"text-[10px] text-gray-500 dark:text-gray-400\">{item.hint}</div>\n </button>\n )}\n </For>\n </PortalDropdownMenu>\n </div>\n\n {/* Native G6 canvas — hidden once a render error degrades us. */}\n <div\n ref={containerRef}\n class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n error() ? 'hidden' : ''\n } ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n {/* Fallback ladder (P2.5): degrade to an edge/node table on error\n rather than showing a bare message. Export menu stays usable. */}\n <Show when={error()}>\n <DegradedFallback\n message={`Graph rendering failed: ${error()}`}\n caption=\"Showing the graph data as a table — the interactive view is unavailable.\"\n {...graphToDegradedTable(params())}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n </Show>\n );\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\nfunction graphFilenameStem(params: GraphComponentParams): string {\n const base = (params.title ?? 'graph').replace(/[^a-z0-9-_]+/gi, '-').replace(/^-+|-+$/g, '');\n return base || 'graph';\n}\n\nasync function downloadDataUrl(dataUrl: string, filename: string): Promise<void> {\n const res = await fetch(dataUrl);\n const blob = await res.blob();\n downloadBlob(blob, filename);\n}\n\n// Re-export for tests + consumers that want to compose their own export menu\nexport { toMermaid as graphToMermaid, toJSON as graphToJSON };\n"],"names":["g6ModulePromise","isG6Available","resolveLayout","params","layout","undefined","hasEdges","edges","length","type","options","resolveBehaviors","behaviors","enableDrag","push","enableZoom","enableSelect","mermaidDirection","layoutType","mermaidLabel","s","replace","trim","toMermaid","dir","lines","n","nodes","label","id","e","labelParts","weight","String","labelText","join","source","target","toJSON","JSON","stringify","downloadBlob","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","GraphRenderer","props","component","isExpanded","useExpanded","telemetry","useTelemetry","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","rendererPref","console","warn","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","message","Error","dispatch","errorMessage","componentType","ts","Date","now","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$3","children","_tmpl$2","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","_el$","_tmpl$","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$7","_el$8","_co$2","$$click","v","_ref$","_$use","_$insert","PortalDropdownMenu","open","onClose","trigger","width","For","each","onClick","hint","_el$1","_tmpl$4","_el$10","_el$11","_$addEventListener","_$runHydrationEvents","_ref$2","DegradedFallback","_$mergeProps","caption","graphToDegradedTable","_$effect","_p$","_v$","className","_v$2","_v$3","_v$4","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"GraphRenderer.js","sources":["../../src/components/GraphRenderer.tsx"],"sourcesContent":["/**\n * GraphRenderer (v6.0.0) — generic node-link visualization powered by\n * `@antv/g6 ^5` (peer-optional). Same lazy-load pattern as\n * `ChartJSRenderer` and `MapRenderer` : the heavy lib is dynamically\n * imported only on first mount, and apps that don't install the peer\n * see an informative fallback instead of a crash.\n *\n * Spec : `@seed-ship/mcp-ui-spec@5.0.4` exports `GraphComponentParamsSchema`,\n * `GraphNode`, `GraphEdge`, `GraphLayoutName`, `GraphLayout`,\n * `GraphComponentParams` — the shape consumed here. Domain semantics\n * (`weight` etc.) are opaque to this renderer ; consumers decide what\n * the values mean.\n *\n * Copy + export : the renderer ships with `<ExpandableWrapper>` (default\n * copy = JSON of `{nodes, edges}`) plus a 3-format export menu — **PNG**\n * (visual snapshot via the underlying canvas/SVG), **Mermaid** (markdown\n * / GitHub-renderable `flowchart` syntax), **JSON** (raw reimportable\n * data). All three are computed lazily on click.\n */\n\nimport { Component, createSignal, onCleanup, onMount, Show, For } from 'solid-js';\nimport type { UIComponent } from '../types';\nimport type {\n GraphComponentParams,\n GraphLayout,\n} from '@seed-ship/mcp-ui-spec';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { PortalDropdownMenu } from './PortalDropdownMenu';\nimport { DegradedFallback } from './DegradedFallback';\nimport { graphToDegradedTable } from '../utils/degraded-projections';\nimport { escapeHtml } from '../utils/escape-html';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';\n\n// Module-scoped lazy import promise — first call triggers the dynamic\n// import, subsequent calls reuse the resolved module.\nlet g6ModulePromise: Promise<typeof import('@antv/g6')> | undefined;\n\n/**\n * Whether the `@antv/g6` peer dependency is installed and importable.\n * Resolves to `true` when the lib is available, `false` otherwise.\n *\n * Mirrors `isChartJSAvailable()` from `ChartJSRenderer`.\n */\nexport async function isG6Available(): Promise<boolean> {\n try {\n if (!g6ModulePromise) {\n g6ModulePromise = import('@antv/g6');\n }\n await g6ModulePromise;\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the spec layout shorthand or object form into the config object\n * G6 v5 expects. When `layout` is omitted, picks `'force'` if edges are\n * present (universal default) or `'circular'` otherwise.\n */\nfunction resolveLayout(params: GraphComponentParams): { type: string; [key: string]: unknown } {\n const layout: GraphLayout | undefined = params.layout;\n if (layout === undefined) {\n const hasEdges = (params.edges?.length ?? 0) > 0;\n return { type: hasEdges ? 'force' : 'circular' };\n }\n if (typeof layout === 'string') {\n return { type: layout };\n }\n // Object form: spread the passthrough options alongside `type`.\n return { type: layout.type, ...(layout.options ?? {}) };\n}\n\n/**\n * Build the G6 v5 `Graph` constructor config from the component params.\n *\n * Pure (no DOM/lib side effects beyond reading `container`), so it can be\n * unit-tested directly without a jsdom render — this is the contract the\n * 2026-05-30 audit (P0.2) wants locked.\n *\n * ⚠️ G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n * (that was the v4 contract). Passing the string `'canvas'` / `'svg'` makes\n * G6 throw `renderer is not a function` — and because the default path used\n * to pass the string `'canvas'`, EVERY graph crashed, not just svg. So we\n * **omit `renderer` entirely**: G6 then uses its built-in canvas renderer\n * (documented default `() => new CanvasRenderer()`).\n *\n * `rendererPref: 'svg'` is reserved but NOT wired yet — a real G6 v5 SVG\n * renderer needs the `@antv/g-svg` factory (a transitive dep of `@antv/g6`,\n * not statically resolvable at build time). Until that's wired behind proper\n * optional-peer resolution, svg degrades to the canvas default with a\n * one-time warning. It must NEVER inject a string `renderer`.\n */\nexport function buildGraphConfig(\n p: GraphComponentParams,\n container?: HTMLElement\n): Record<string, unknown> {\n const config: Record<string, unknown> = {\n container,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n\n if (p.tooltip !== false) {\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`;\n },\n },\n ];\n }\n\n return config;\n}\n\n/**\n * Build the G6 v5 `behaviors` array from the params interactivity flags.\n * Defaults : drag-canvas + zoom-canvas + drag-element + click-select.\n * Any flag set to `false` opts out.\n */\nfunction resolveBehaviors(params: GraphComponentParams): string[] {\n const behaviors: string[] = [];\n if (params.enableDrag !== false) behaviors.push('drag-element');\n if (params.enableZoom !== false) {\n behaviors.push('zoom-canvas', 'drag-canvas');\n }\n if (params.enableSelect !== false) behaviors.push('click-select');\n return behaviors;\n}\n\n/**\n * Pick a sensible Mermaid `flowchart` direction from the resolved layout.\n * `dagre` / `tree` / `mindmap` are top-down hierarchies → TD ; everything\n * else (force, concentric, circular, grid) → LR (default mermaid).\n */\nfunction mermaidDirection(layoutType: string): 'TD' | 'LR' {\n return layoutType === 'dagre' || layoutType === 'tree' || layoutType === 'mindmap' ? 'TD' : 'LR';\n}\n\n/**\n * Sanitize a string for use inside a Mermaid node label. Mermaid breaks\n * on raw quotes / brackets / pipes ; we strip the worst offenders.\n */\nfunction mermaidLabel(s: string): string {\n return s\n .replace(/[\"[\\]|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Convert the graph data to Mermaid `flowchart` syntax. The edge label\n * carries the optional `weight` prefix when present (e.g. `|3| label`).\n */\nfunction toMermaid(params: GraphComponentParams): string {\n const layoutType = resolveLayout(params).type;\n const dir = mermaidDirection(layoutType);\n const lines: string[] = [`flowchart ${dir}`];\n for (const n of params.nodes) {\n const label = mermaidLabel(n.label ?? n.id);\n lines.push(` ${n.id}[\"${label}\"]`);\n }\n for (const e of params.edges ?? []) {\n const labelParts: string[] = [];\n if (e.weight !== undefined) labelParts.push(String(e.weight));\n if (e.label) labelParts.push(mermaidLabel(e.label));\n const labelText = labelParts.join(' · ');\n if (labelText) {\n lines.push(` ${e.source} -->|${labelText}| ${e.target}`);\n } else {\n lines.push(` ${e.source} --> ${e.target}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction toJSON(params: GraphComponentParams): string {\n return JSON.stringify({ nodes: params.nodes, edges: params.edges ?? [] }, null, 2);\n}\n\nfunction downloadBlob(content: string | Blob, filename: string, mimeType?: string): void {\n const blob =\n typeof content === 'string' ? new Blob([content], { type: mimeType ?? 'text/plain' }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nexport interface GraphRendererProps {\n component: UIComponent;\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nexport const GraphRenderer: Component<GraphRendererProps> = (props) => {\n const strings = useMCPUIStrings();\n const params = () => props.component.params as GraphComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\n const [available, setAvailable] = createSignal<boolean | null>(null);\n const [error, setError] = createSignal<string | undefined>();\n const [exportMenuOpen, setExportMenuOpen] = createSignal(false);\n let containerRef: HTMLDivElement | undefined;\n // v6.4.0 — trigger ref consumed by <PortalDropdownMenu> for positioning\n let exportTriggerRef: HTMLButtonElement | undefined;\n // Loosely typed because G6 is a peer-optional — we don't pull its\n // types into the bundle just to type a transient local handle.\n let graphInstance: any | undefined;\n\n onMount(async () => {\n const g6Available = await isG6Available();\n setAvailable(g6Available);\n if (!g6Available || !containerRef) return;\n\n try {\n const { Graph } = await g6ModulePromise!;\n const p = params();\n const config: Record<string, unknown> = {\n container: containerRef,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n // G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n // (that was the v4 contract). Passing the string `'canvas'` / `'svg'`\n // makes G6 throw `renderer is not a function` — and because the default\n // path also passed the string `'canvas'`, EVERY graph crashed, not just\n // the svg one.\n //\n // Fix: omit `renderer` entirely. G6 then uses its built-in canvas\n // renderer (documented default `() => new CanvasRenderer()`), which is\n // what we want for every graph.\n //\n // `rendererPref: 'svg'` is NOT wired yet: a real G6 v5 SVG renderer\n // needs the `@antv/g-svg` factory, which is a *transitive* dep of\n // `@antv/g6` (not a declared peer) and isn't statically resolvable at\n // build time. Rather than ship a fragile/unresolvable import, we treat\n // svg as \"not yet supported\" and fall back to the canvas default with a\n // one-time warning. Tracked for a follow-up that wires the factory\n // behind a proper optional-peer resolution. (Never pass a string here.)\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n if (p.tooltip !== false) {\n // Built-in tooltip plugin — shows label + a compact dump of\n // node.data on hover. Consumers can opt out with `tooltip: false`.\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data\n ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>`\n : ''\n }</div>`;\n },\n },\n ];\n }\n graphInstance = new (Graph as any)(config);\n await graphInstance.render();\n } catch (err) {\n const message = err instanceof Error ? err.message : strings.graphRenderError;\n setError(message);\n // Fallback ladder (P2.5): the native G6 render threw — emit telemetry\n // so the failure is observable, then degrade to the edge/node table\n // below instead of leaving a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: message,\n id: props.component.id ?? '',\n componentType: 'graph',\n ts: Date.now(),\n });\n }\n });\n\n onCleanup(() => {\n try {\n graphInstance?.destroy();\n } catch {\n // G6 destroy can throw on already-destroyed instances or partial\n // init failures — silent because the component is unmounting anyway.\n }\n graphInstance = undefined;\n });\n\n // ─── Export handlers ────────────────────────────────────────────────\n const handleExportJSON = () => {\n downloadBlob(toJSON(params()), `${graphFilenameStem(params())}.json`, 'application/json');\n setExportMenuOpen(false);\n };\n\n const handleExportMermaid = () => {\n downloadBlob(toMermaid(params()), `${graphFilenameStem(params())}.mmd`, 'text/plain');\n setExportMenuOpen(false);\n };\n\n const handleExportPNG = async () => {\n if (!graphInstance) return;\n try {\n // G6 v5 exposes `toDataURL()` on the graph instance.\n const dataUrl: string = await graphInstance.toDataURL?.('image/png');\n if (!dataUrl) {\n // Fallback: try to grab the underlying canvas directly.\n const canvas = containerRef?.querySelector('canvas');\n if (canvas) {\n const url = (canvas as HTMLCanvasElement).toDataURL('image/png');\n await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);\n } else {\n setError(strings.graphPngUnsupported);\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : strings.graphPngExportFailed);\n }\n setExportMenuOpen(false);\n };\n\n return (\n <Show\n when={available() === true}\n fallback={\n <Show\n when={available() === false}\n fallback={\n // Loading skeleton while we determine peer availability\n <div class=\"w-full p-4 bg-gray-50 dark:bg-gray-800/40 border border-gray-200 dark:border-gray-700 rounded-lg animate-pulse\">\n <div class=\"h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2\" />\n <div class=\"h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded\" />\n </div>\n }\n >\n <div class=\"w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n {strings.graphUnavailable}\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Install <code>@antv/g6</code> peer dependency to render <code>type: \"graph\"</code>{' '}\n components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? strings.graphTitle}\n copyData={toJSON(params())}\n copyLabel={strings.graphCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div\n class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}\n >\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\n ref={exportTriggerRef}\n type=\"button\"\n onClick={() => setExportMenuOpen((v) => !v)}\n class=\"px-2 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300\"\n title={strings.graphExport}\n aria-label={strings.graphExport}\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n {strings.graphExportMenu}\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For\n each={[\n {\n label: strings.graphDownloadPng,\n onClick: handleExportPNG,\n hint: strings.graphDownloadPngHint,\n },\n {\n label: strings.graphDownloadMermaid,\n onClick: handleExportMermaid,\n hint: strings.graphDownloadMermaidHint,\n },\n {\n label: strings.graphDownloadJson,\n onClick: handleExportJSON,\n hint: strings.graphDownloadJsonHint,\n },\n ]}\n >\n {(item) => (\n <button\n type=\"button\"\n onClick={item.onClick}\n class=\"w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-b border-gray-100 dark:border-gray-700 last:border-b-0\"\n >\n <div class=\"font-medium\">{item.label}</div>\n <div class=\"text-[10px] text-gray-500 dark:text-gray-400\">{item.hint}</div>\n </button>\n )}\n </For>\n </PortalDropdownMenu>\n </div>\n\n {/* Native G6 canvas — hidden once a render error degrades us. */}\n <div\n ref={containerRef}\n class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n error() ? 'hidden' : ''\n } ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n {/* Fallback ladder (P2.5): degrade to an edge/node table on error\n rather than showing a bare message. Export menu stays usable. */}\n <Show when={error()}>\n <DegradedFallback\n message={formatMCPUIString(strings.graphRenderFailed, { error: String(error()) })}\n caption={strings.graphDegradedCaption}\n {...graphToDegradedTable(params(), {\n source: strings.degradedColSource,\n target: strings.degradedColTarget,\n label: strings.degradedColLabel,\n node: strings.degradedColNode,\n })}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n </Show>\n );\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\nfunction graphFilenameStem(params: GraphComponentParams): string {\n const base = (params.title ?? 'graph').replace(/[^a-z0-9-_]+/gi, '-').replace(/^-+|-+$/g, '');\n return base || 'graph';\n}\n\nasync function downloadDataUrl(dataUrl: string, filename: string): Promise<void> {\n const res = await fetch(dataUrl);\n const blob = await res.blob();\n downloadBlob(blob, filename);\n}\n\n// Re-export for tests + consumers that want to compose their own export menu\nexport { toMermaid as graphToMermaid, toJSON as graphToJSON };\n"],"names":["g6ModulePromise","isG6Available","resolveLayout","params","layout","undefined","hasEdges","edges","length","type","options","resolveBehaviors","behaviors","enableDrag","push","enableZoom","enableSelect","mermaidDirection","layoutType","mermaidLabel","s","replace","trim","toMermaid","dir","lines","n","nodes","label","id","e","labelParts","weight","String","labelText","join","source","target","toJSON","JSON","stringify","downloadBlob","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","GraphRenderer","props","strings","useMCPUIStrings","component","isExpanded","useExpanded","telemetry","useTelemetry","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","rendererPref","console","warn","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","message","Error","graphRenderError","dispatch","errorMessage","componentType","ts","Date","now","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","graphPngUnsupported","graphPngExportFailed","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$3","children","_el$9","_tmpl$2","_el$0","firstChild","_$insert","graphUnavailable","ExpandableWrapper","title","graphTitle","copyData","copyLabel","graphCopy","toolbarVariant","_el$","_tmpl$","_el$2","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$7","_el$8","_co$2","$$click","v","_ref$","_$use","graphExportMenu","PortalDropdownMenu","open","onClose","trigger","width","For","each","graphDownloadPng","onClick","hint","graphDownloadPngHint","graphDownloadMermaid","graphDownloadMermaidHint","graphDownloadJson","graphDownloadJsonHint","_el$10","_tmpl$4","_el$11","_el$12","_$addEventListener","_$runHydrationEvents","_ref$2","DegradedFallback","_$mergeProps","formatMCPUIString","graphRenderFailed","caption","graphDegradedCaption","graphToDegradedTable","degradedColSource","degradedColTarget","degradedColLabel","node","degradedColNode","_$effect","_p$","_v$","className","_v$2","graphExport","_v$3","_v$4","_v$5","_v$6","height","_$className","t","_$setAttribute","o","i","_$style","base","res","fetch","_$delegateEvents"],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const web = require("solid-js/web");
4
+ const solidJs = require("solid-js");
5
+ const safeUrl = require("../utils/safe-url.cjs");
6
+ const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
7
+ const MCPUIConfigContext = require("../context/MCPUIConfigContext.cjs");
8
+ const iframeCoep = require("../utils/iframe-coep.cjs");
9
+ var _tmpl$ = /* @__PURE__ */ web.template(`<div><a target=_blank rel="noopener noreferrer"data-mcp-ui-action=open-external class="text-xs text-blue-600 dark:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded">`);
10
+ function IframeFallbackLink(props) {
11
+ const strings = MCPUIStringsContext.useMCPUIStrings();
12
+ const config = MCPUIConfigContext.useMCPUIConfig();
13
+ const crossOriginIsolated = iframeCoep.createCrossOriginIsolated();
14
+ const href = solidJs.createMemo(() => safeUrl.safeUrl(props.url));
15
+ const visible = solidJs.createMemo(() => !!href() && iframeCoep.shouldShowIframeFallbackLink(config, crossOriginIsolated()));
16
+ return web.createComponent(solidJs.Show, {
17
+ get when() {
18
+ return visible();
19
+ },
20
+ get children() {
21
+ var _el$ = web.getNextElement(_tmpl$), _el$2 = _el$.firstChild;
22
+ web.insert(_el$2, () => strings.iframeOpenInNewTab);
23
+ web.effect((_p$) => {
24
+ var _v$ = `px-4 py-2 text-right ${props.class || ""}`, _v$2 = href();
25
+ _v$ !== _p$.e && web.className(_el$, _p$.e = _v$);
26
+ _v$2 !== _p$.t && web.setAttribute(_el$2, "href", _p$.t = _v$2);
27
+ return _p$;
28
+ }, {
29
+ e: void 0,
30
+ t: void 0
31
+ });
32
+ return _el$;
33
+ }
34
+ });
35
+ }
36
+ exports.IframeFallbackLink = IframeFallbackLink;
37
+ //# sourceMappingURL=IframeFallbackLink.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IframeFallbackLink.cjs","sources":["../../src/components/IframeFallbackLink.tsx"],"sourcesContent":["/**\n * IframeFallbackLink — the way out of a silently blocked embed.\n *\n * @since v6.21.0\n *\n * An iframe refused by `Cross-Origin-Embedder-Policy` fires no event the\n * parent page can observe (`onerror` does not fire for a rejected\n * cross-origin document), so the user is left with a blank frame and no\n * explanation. This link, rendered under the frame, always works.\n *\n * Visibility follows `MCPUIConfig.iframeFallbackLink`; `'auto'` (the default)\n * shows it only on a cross-origin-isolated page. The URL goes through\n * `safeUrl` — an unsafe one renders nothing at all.\n */\n\nimport { createMemo, Show, type JSX } from 'solid-js'\nimport { safeUrl } from '../utils/safe-url'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { useMCPUIConfig } from '../context/MCPUIConfigContext'\nimport {\n createCrossOriginIsolated,\n shouldShowIframeFallbackLink,\n} from '../utils/iframe-coep'\n\nexport interface IframeFallbackLinkProps {\n /** The embedded URL. Bound to `href` only when `safeUrl` accepts it. */\n url: string\n /** Extra classes on the wrapping `<div>`. */\n class?: string\n}\n\nexport function IframeFallbackLink(props: IframeFallbackLinkProps): JSX.Element {\n const strings = useMCPUIStrings()\n const config = useMCPUIConfig()\n const crossOriginIsolated = createCrossOriginIsolated()\n\n const href = createMemo(() => safeUrl(props.url))\n const visible = createMemo(\n () => !!href() && shouldShowIframeFallbackLink(config, crossOriginIsolated())\n )\n\n return (\n <Show when={visible()}>\n <div class={`px-4 py-2 text-right ${props.class || ''}`}>\n <a\n href={href()}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-mcp-ui-action=\"open-external\"\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded\"\n >\n {strings.iframeOpenInNewTab}\n </a>\n </div>\n </Show>\n )\n}\n"],"names":["IframeFallbackLink","props","strings","useMCPUIStrings","config","useMCPUIConfig","crossOriginIsolated","createCrossOriginIsolated","href","createMemo","safeUrl","url","visible","shouldShowIframeFallbackLink","_$createComponent","Show","when","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_$insert","iframeOpenInNewTab","_$effect","_p$","_v$","class","_v$2","e","_$className","t","_$setAttribute","undefined"],"mappings":";;;;;;;;;AA+BO,SAASA,mBAAmBC,OAA6C;AAC9E,QAAMC,UAAUC,oBAAAA,gBAAAA;AAChB,QAAMC,SAASC,mBAAAA,eAAAA;AACf,QAAMC,sBAAsBC,WAAAA,0BAAAA;AAE5B,QAAMC,OAAOC,QAAAA,WAAW,MAAMC,QAAAA,QAAQT,MAAMU,GAAG,CAAC;AAChD,QAAMC,UAAUH,mBACd,MAAM,CAAC,CAACD,KAAAA,KAAUK,wCAA6BT,QAAQE,oBAAAA,CAAqB,CAC9E;AAEA,SAAAQ,IAAAA,gBACGC,QAAAA,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEJ,QAAAA;AAAAA,IAAS;AAAA,IAAA,IAAAK,WAAA;AAAA,UAAAC,OAAAC,IAAAA,eAAAC,MAAA,GAAAC,QAAAH,KAAAI;AAAAC,UAAAA,OAAAF,OAAA,MASdnB,QAAQsB,kBAAkB;AAAAC,UAAAA,OAAAC,CAAAA,QAAA;AAAA,YAAAC,MARnB,wBAAwB1B,MAAM2B,SAAS,EAAE,IAAEC,OAE7CrB,KAAAA;AAAMmB,gBAAAD,IAAAI,KAAAC,IAAAA,UAAAb,MAAAQ,IAAAI,IAAAH,GAAA;AAAAE,iBAAAH,IAAAM,KAAAC,IAAAA,aAAAZ,OAAA,QAAAK,IAAAM,IAAAH,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,MAAAA,CAAA;AAAA,aAAAhB;AAAAA,IAAA;AAAA,EAAA,CAAA;AAWtB;;"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * IframeFallbackLink — the way out of a silently blocked embed.
3
+ *
4
+ * @since v6.21.0
5
+ *
6
+ * An iframe refused by `Cross-Origin-Embedder-Policy` fires no event the
7
+ * parent page can observe (`onerror` does not fire for a rejected
8
+ * cross-origin document), so the user is left with a blank frame and no
9
+ * explanation. This link, rendered under the frame, always works.
10
+ *
11
+ * Visibility follows `MCPUIConfig.iframeFallbackLink`; `'auto'` (the default)
12
+ * shows it only on a cross-origin-isolated page. The URL goes through
13
+ * `safeUrl` — an unsafe one renders nothing at all.
14
+ */
15
+ import { type JSX } from 'solid-js';
16
+ export interface IframeFallbackLinkProps {
17
+ /** The embedded URL. Bound to `href` only when `safeUrl` accepts it. */
18
+ url: string;
19
+ /** Extra classes on the wrapping `<div>`. */
20
+ class?: string;
21
+ }
22
+ export declare function IframeFallbackLink(props: IframeFallbackLinkProps): JSX.Element;
23
+ //# sourceMappingURL=IframeFallbackLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IframeFallbackLink.d.ts","sourceRoot":"","sources":["../../src/components/IframeFallbackLink.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AASrD,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAA;IACX,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,GAAG,CAAC,OAAO,CAyB9E"}
@@ -0,0 +1,37 @@
1
+ import { createComponent, getNextElement, insert, effect, className, setAttribute, template } from "solid-js/web";
2
+ import { createMemo, Show } from "solid-js";
3
+ import { safeUrl } from "../utils/safe-url.js";
4
+ import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
5
+ import { useMCPUIConfig } from "../context/MCPUIConfigContext.js";
6
+ import { createCrossOriginIsolated, shouldShowIframeFallbackLink } from "../utils/iframe-coep.js";
7
+ var _tmpl$ = /* @__PURE__ */ template(`<div><a target=_blank rel="noopener noreferrer"data-mcp-ui-action=open-external class="text-xs text-blue-600 dark:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded">`);
8
+ function IframeFallbackLink(props) {
9
+ const strings = useMCPUIStrings();
10
+ const config = useMCPUIConfig();
11
+ const crossOriginIsolated = createCrossOriginIsolated();
12
+ const href = createMemo(() => safeUrl(props.url));
13
+ const visible = createMemo(() => !!href() && shouldShowIframeFallbackLink(config, crossOriginIsolated()));
14
+ return createComponent(Show, {
15
+ get when() {
16
+ return visible();
17
+ },
18
+ get children() {
19
+ var _el$ = getNextElement(_tmpl$), _el$2 = _el$.firstChild;
20
+ insert(_el$2, () => strings.iframeOpenInNewTab);
21
+ effect((_p$) => {
22
+ var _v$ = `px-4 py-2 text-right ${props.class || ""}`, _v$2 = href();
23
+ _v$ !== _p$.e && className(_el$, _p$.e = _v$);
24
+ _v$2 !== _p$.t && setAttribute(_el$2, "href", _p$.t = _v$2);
25
+ return _p$;
26
+ }, {
27
+ e: void 0,
28
+ t: void 0
29
+ });
30
+ return _el$;
31
+ }
32
+ });
33
+ }
34
+ export {
35
+ IframeFallbackLink
36
+ };
37
+ //# sourceMappingURL=IframeFallbackLink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IframeFallbackLink.js","sources":["../../src/components/IframeFallbackLink.tsx"],"sourcesContent":["/**\n * IframeFallbackLink — the way out of a silently blocked embed.\n *\n * @since v6.21.0\n *\n * An iframe refused by `Cross-Origin-Embedder-Policy` fires no event the\n * parent page can observe (`onerror` does not fire for a rejected\n * cross-origin document), so the user is left with a blank frame and no\n * explanation. This link, rendered under the frame, always works.\n *\n * Visibility follows `MCPUIConfig.iframeFallbackLink`; `'auto'` (the default)\n * shows it only on a cross-origin-isolated page. The URL goes through\n * `safeUrl` — an unsafe one renders nothing at all.\n */\n\nimport { createMemo, Show, type JSX } from 'solid-js'\nimport { safeUrl } from '../utils/safe-url'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { useMCPUIConfig } from '../context/MCPUIConfigContext'\nimport {\n createCrossOriginIsolated,\n shouldShowIframeFallbackLink,\n} from '../utils/iframe-coep'\n\nexport interface IframeFallbackLinkProps {\n /** The embedded URL. Bound to `href` only when `safeUrl` accepts it. */\n url: string\n /** Extra classes on the wrapping `<div>`. */\n class?: string\n}\n\nexport function IframeFallbackLink(props: IframeFallbackLinkProps): JSX.Element {\n const strings = useMCPUIStrings()\n const config = useMCPUIConfig()\n const crossOriginIsolated = createCrossOriginIsolated()\n\n const href = createMemo(() => safeUrl(props.url))\n const visible = createMemo(\n () => !!href() && shouldShowIframeFallbackLink(config, crossOriginIsolated())\n )\n\n return (\n <Show when={visible()}>\n <div class={`px-4 py-2 text-right ${props.class || ''}`}>\n <a\n href={href()}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-mcp-ui-action=\"open-external\"\n class=\"text-xs text-blue-600 dark:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded\"\n >\n {strings.iframeOpenInNewTab}\n </a>\n </div>\n </Show>\n )\n}\n"],"names":["IframeFallbackLink","props","strings","useMCPUIStrings","config","useMCPUIConfig","crossOriginIsolated","createCrossOriginIsolated","href","createMemo","safeUrl","url","visible","shouldShowIframeFallbackLink","_$createComponent","Show","when","children","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_$insert","iframeOpenInNewTab","_$effect","_p$","_v$","class","_v$2","e","_$className","t","_$setAttribute","undefined"],"mappings":";;;;;;;AA+BO,SAASA,mBAAmBC,OAA6C;AAC9E,QAAMC,UAAUC,gBAAAA;AAChB,QAAMC,SAASC,eAAAA;AACf,QAAMC,sBAAsBC,0BAAAA;AAE5B,QAAMC,OAAOC,WAAW,MAAMC,QAAQT,MAAMU,GAAG,CAAC;AAChD,QAAMC,UAAUH,WACd,MAAM,CAAC,CAACD,KAAAA,KAAUK,6BAA6BT,QAAQE,oBAAAA,CAAqB,CAC9E;AAEA,SAAAQ,gBACGC,MAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAEJ,QAAAA;AAAAA,IAAS;AAAA,IAAA,IAAAK,WAAA;AAAA,UAAAC,OAAAC,eAAAC,MAAA,GAAAC,QAAAH,KAAAI;AAAAC,aAAAF,OAAA,MASdnB,QAAQsB,kBAAkB;AAAAC,aAAAC,CAAAA,QAAA;AAAA,YAAAC,MARnB,wBAAwB1B,MAAM2B,SAAS,EAAE,IAAEC,OAE7CrB,KAAAA;AAAMmB,gBAAAD,IAAAI,KAAAC,UAAAb,MAAAQ,IAAAI,IAAAH,GAAA;AAAAE,iBAAAH,IAAAM,KAAAC,aAAAZ,OAAA,QAAAK,IAAAM,IAAAH,IAAA;AAAA,eAAAH;AAAAA,MAAA,GAAA;AAAA,QAAAI,GAAAI;AAAAA,QAAAF,GAAAE;AAAAA,MAAAA,CAAA;AAAA,aAAAhB;AAAAA,IAAA;AAAA,EAAA,CAAA;AAWtB;"}
@@ -5,12 +5,15 @@ const solidJs = require("solid-js");
5
5
  const LightboxOverlay = require("./LightboxOverlay.cjs");
6
6
  const ExpandableWrapper = require("./ExpandableWrapper.cjs");
7
7
  const safeUrl = require("../utils/safe-url.cjs");
8
+ const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
9
+ const formatString = require("../utils/format-string.cjs");
8
10
  var _tmpl$ = /* @__PURE__ */ web.template(`<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0"><h3 class="text-sm font-semibold text-gray-900 dark:text-white">`), _tmpl$2 = /* @__PURE__ */ web.template(`<div><!$><!/><div></div><!$><!/>`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent text-white text-xs p-2 pt-4"><span class="truncate block">`), _tmpl$4 = /* @__PURE__ */ web.template(`<div class="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200"><div class="bg-white/90 dark:bg-gray-800/90 rounded-full p-2"><svg class="w-5 h-5 text-gray-700 dark:text-gray-200"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7">`), _tmpl$5 = /* @__PURE__ */ web.template(`<button type=button><img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-200"loading=lazy><div class="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors duration-200"></div><!$><!/><!$><!/>`, true, false, false);
9
11
  function imagesToTextList(p) {
10
12
  if (!p) return "";
11
13
  return (p.images ?? []).map((img) => img.caption ? `${img.url} ${img.caption}` : img.url).join("\n");
12
14
  }
13
15
  const ImageGalleryRenderer = (props) => {
16
+ const strings = MCPUIStringsContext.useMCPUIStrings();
14
17
  const [selectedIndex, setSelectedIndex] = solidJs.createSignal(null);
15
18
  const isExpanded = ExpandableWrapper.useExpanded();
16
19
  const params = () => {
@@ -67,12 +70,14 @@ const ImageGalleryRenderer = (props) => {
67
70
  return web.createComponent(ExpandableWrapper.ExpandableWrapper, {
68
71
  get title() {
69
72
  var _a;
70
- return ((_a = params()) == null ? void 0 : _a.title) || "Gallery";
73
+ return ((_a = params()) == null ? void 0 : _a.title) || strings.galleryTitle;
71
74
  },
72
75
  get copyData() {
73
76
  return imagesToTextList(params());
74
77
  },
75
- copyLabel: "Copy image URLs",
78
+ get copyLabel() {
79
+ return strings.galleryCopy;
80
+ },
76
81
  get toolbarVariant() {
77
82
  return props.toolbarVariant;
78
83
  },
@@ -118,9 +123,13 @@ const ImageGalleryRenderer = (props) => {
118
123
  }
119
124
  }), _el$16, _co$4);
120
125
  web.effect((_p$) => {
121
- var _v$3 = `relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`, _v$4 = image.alt || `View image ${index() + 1}`, _v$5 = safeUrl.safeUrl(image.thumbnail || image.url, {
126
+ var _v$3 = `relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`, _v$4 = image.alt || formatString.formatMCPUIString(strings.galleryViewImage, {
127
+ index: index() + 1
128
+ }), _v$5 = safeUrl.safeUrl(image.thumbnail || image.url, {
122
129
  allowDataImage: true
123
- }), _v$6 = image.alt || `Image ${index() + 1}`, _v$7 = image.srcset, _v$8 = image.sizes;
130
+ }), _v$6 = image.alt || formatString.formatMCPUIString(strings.galleryImageAlt, {
131
+ index: index() + 1
132
+ }), _v$7 = image.srcset, _v$8 = image.sizes;
124
133
  _v$3 !== _p$.e && web.className(_el$9, _p$.e = _v$3);
125
134
  _v$4 !== _p$.t && web.setAttribute(_el$9, "aria-label", _p$.t = _v$4);
126
135
  _v$5 !== _p$.a && web.setAttribute(_el$0, "src", _p$.a = _v$5);
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGalleryRenderer.cjs","sources":["../../src/components/ImageGalleryRenderer.tsx"],"sourcesContent":["/**\n * ImageGalleryRenderer - Gallery view for multiple images\n * Sprint 5: Media Components\n */\n\nimport { Component, createSignal, For, Show } from 'solid-js'\nimport type { UIComponent, ImageGalleryParams } from '../types'\nimport { LightboxOverlay } from './LightboxOverlay'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { safeUrl } from '../utils/safe-url'\n\nexport interface ImageGalleryRendererProps {\n /**\n * UIComponent containing gallery params\n */\n component?: UIComponent\n\n /**\n * Direct gallery params (alternative to component)\n */\n params?: ImageGalleryParams\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/** Build a newline-separated list of image URLs (with captions when present)\n * for the ExpandableWrapper copy button. v6.2.0. */\nfunction imagesToTextList(p: ImageGalleryParams | undefined): string {\n if (!p) return ''\n return (p.images ?? [])\n .map((img) => (img.caption ? `${img.url}\\t${img.caption}` : img.url))\n .join('\\n')\n}\n\nexport const ImageGalleryRenderer: Component<ImageGalleryRendererProps> = (props) => {\n const [selectedIndex, setSelectedIndex] = createSignal<number | null>(null)\n const isExpanded = useExpanded()\n\n const params = () => props.params || (props.component?.params as ImageGalleryParams)\n\n const columnsClass = () => {\n switch (params()?.columns) {\n case 2:\n return 'grid-cols-2'\n case 3:\n return 'grid-cols-3'\n case 4:\n return 'grid-cols-4'\n case 5:\n return 'grid-cols-5'\n default:\n return 'grid-cols-3'\n }\n }\n\n const gapClass = () => {\n switch (params()?.gap) {\n case 'none':\n return 'gap-0'\n case 'sm':\n return 'gap-1'\n case 'lg':\n return 'gap-4'\n default:\n return 'gap-2'\n }\n }\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '16:9':\n return 'aspect-video'\n case '4:3':\n return 'aspect-[4/3]'\n default:\n return ''\n }\n }\n\n const handleImageClick = (index: number) => {\n if (params()?.lightbox !== false) {\n setSelectedIndex(index)\n }\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || 'Gallery'}\n copyData={imagesToTextList(params())}\n copyLabel=\"Copy image URLs\"\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 flex-shrink-0\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Gallery Grid */}\n <div class={`grid ${columnsClass()} ${gapClass()} p-4 ${isExpanded() ? 'flex-1 min-h-0 overflow-auto' : ''}`}>\n <For each={params()?.images}>\n {(image, index) => (\n <button\n class={`relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`}\n onClick={() => handleImageClick(index())}\n type=\"button\"\n aria-label={image.alt || `View image ${index() + 1}`}\n >\n <img\n src={safeUrl(image.thumbnail || image.url, { allowDataImage: true })}\n alt={image.alt || `Image ${index() + 1}`}\n srcset={image.srcset}\n sizes={image.sizes}\n class=\"w-full h-full object-cover group-hover:scale-105 transition-transform duration-200\"\n loading=\"lazy\"\n />\n\n {/* Hover overlay */}\n <div class=\"absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors duration-200\" />\n\n {/* Caption overlay */}\n <Show when={image.caption && params()?.showCaptions}>\n <div class=\"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent text-white text-xs p-2 pt-4\">\n <span class=\"truncate block\">{image.caption}</span>\n </div>\n </Show>\n\n {/* Zoom icon on hover */}\n <Show when={params()?.lightbox !== false}>\n <div class=\"absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200\">\n <div class=\"bg-white/90 dark:bg-gray-800/90 rounded-full p-2\">\n <svg\n class=\"w-5 h-5 text-gray-700 dark:text-gray-200\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\n />\n </svg>\n </div>\n </div>\n </Show>\n </button>\n )}\n </For>\n </div>\n\n {/* Lightbox */}\n <LightboxOverlay\n images={params()?.images || []}\n selectedIndex={selectedIndex()}\n onClose={() => setSelectedIndex(null)}\n onNavigate={setSelectedIndex}\n />\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["imagesToTextList","p","images","map","img","caption","url","join","ImageGalleryRenderer","props","selectedIndex","setSelectedIndex","createSignal","isExpanded","useExpanded","params","component","columnsClass","columns","gapClass","gap","aspectClass","aspectRatio","handleImageClick","index","lightbox","_$createComponent","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","children","_el$","_$getNextElement","_tmpl$2","_el$5","firstChild","_el$6","_co$","_$getNextMarker","nextSibling","_el$4","_el$7","_el$8","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","For","each","image","_el$9","_tmpl$5","_el$0","_el$1","_el$13","_el$14","_co$3","_el$15","_el$16","_co$4","$$click","_$memo","showCaptions","_el$10","_tmpl$3","_el$11","_tmpl$4","_$effect","_p$","_v$3","_v$4","alt","_v$5","safeUrl","thumbnail","allowDataImage","_v$6","_v$7","srcset","_v$8","sizes","e","_$className","t","_$setAttribute","a","o","i","n","undefined","_$runHydrationEvents","LightboxOverlay","onClose","onNavigate","_v$","_v$2","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ImageGalleryRenderer.cjs","sources":["../../src/components/ImageGalleryRenderer.tsx"],"sourcesContent":["/**\n * ImageGalleryRenderer - Gallery view for multiple images\n * Sprint 5: Media Components\n */\n\nimport { Component, createSignal, For, Show } from 'solid-js'\nimport type { UIComponent, ImageGalleryParams } from '../types'\nimport { LightboxOverlay } from './LightboxOverlay'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { safeUrl } from '../utils/safe-url'\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface ImageGalleryRendererProps {\n /**\n * UIComponent containing gallery params\n */\n component?: UIComponent\n\n /**\n * Direct gallery params (alternative to component)\n */\n params?: ImageGalleryParams\n\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/** Build a newline-separated list of image URLs (with captions when present)\n * for the ExpandableWrapper copy button. v6.2.0. */\nfunction imagesToTextList(p: ImageGalleryParams | undefined): string {\n if (!p) return ''\n return (p.images ?? [])\n .map((img) => (img.caption ? `${img.url}\\t${img.caption}` : img.url))\n .join('\\n')\n}\n\nexport const ImageGalleryRenderer: Component<ImageGalleryRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const [selectedIndex, setSelectedIndex] = createSignal<number | null>(null)\n const isExpanded = useExpanded()\n\n const params = () => props.params || (props.component?.params as ImageGalleryParams)\n\n const columnsClass = () => {\n switch (params()?.columns) {\n case 2:\n return 'grid-cols-2'\n case 3:\n return 'grid-cols-3'\n case 4:\n return 'grid-cols-4'\n case 5:\n return 'grid-cols-5'\n default:\n return 'grid-cols-3'\n }\n }\n\n const gapClass = () => {\n switch (params()?.gap) {\n case 'none':\n return 'gap-0'\n case 'sm':\n return 'gap-1'\n case 'lg':\n return 'gap-4'\n default:\n return 'gap-2'\n }\n }\n\n const aspectClass = () => {\n switch (params()?.aspectRatio) {\n case '1:1':\n return 'aspect-square'\n case '16:9':\n return 'aspect-video'\n case '4:3':\n return 'aspect-[4/3]'\n default:\n return ''\n }\n }\n\n const handleImageClick = (index: number) => {\n if (params()?.lightbox !== false) {\n setSelectedIndex(index)\n }\n }\n\n return (\n <ExpandableWrapper\n title={params()?.title || strings.galleryTitle}\n copyData={imagesToTextList(params())}\n copyLabel={strings.galleryCopy}\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 flex-shrink-0\">\n <h3 class=\"text-sm font-semibold text-gray-900 dark:text-white\">{params()!.title}</h3>\n </div>\n </Show>\n\n {/* Gallery Grid */}\n <div class={`grid ${columnsClass()} ${gapClass()} p-4 ${isExpanded() ? 'flex-1 min-h-0 overflow-auto' : ''}`}>\n <For each={params()?.images}>\n {(image, index) => (\n <button\n class={`relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`}\n onClick={() => handleImageClick(index())}\n type=\"button\"\n aria-label={\n image.alt || formatMCPUIString(strings.galleryViewImage, { index: index() + 1 })\n }\n >\n <img\n src={safeUrl(image.thumbnail || image.url, { allowDataImage: true })}\n alt={image.alt || formatMCPUIString(strings.galleryImageAlt, { index: index() + 1 })}\n srcset={image.srcset}\n sizes={image.sizes}\n class=\"w-full h-full object-cover group-hover:scale-105 transition-transform duration-200\"\n loading=\"lazy\"\n />\n\n {/* Hover overlay */}\n <div class=\"absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors duration-200\" />\n\n {/* Caption overlay */}\n <Show when={image.caption && params()?.showCaptions}>\n <div class=\"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent text-white text-xs p-2 pt-4\">\n <span class=\"truncate block\">{image.caption}</span>\n </div>\n </Show>\n\n {/* Zoom icon on hover */}\n <Show when={params()?.lightbox !== false}>\n <div class=\"absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200\">\n <div class=\"bg-white/90 dark:bg-gray-800/90 rounded-full p-2\">\n <svg\n class=\"w-5 h-5 text-gray-700 dark:text-gray-200\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7\"\n />\n </svg>\n </div>\n </div>\n </Show>\n </button>\n )}\n </For>\n </div>\n\n {/* Lightbox */}\n <LightboxOverlay\n images={params()?.images || []}\n selectedIndex={selectedIndex()}\n onClose={() => setSelectedIndex(null)}\n onNavigate={setSelectedIndex}\n />\n </div>\n </ExpandableWrapper>\n )\n}\n"],"names":["imagesToTextList","p","images","map","img","caption","url","join","ImageGalleryRenderer","props","strings","useMCPUIStrings","selectedIndex","setSelectedIndex","createSignal","isExpanded","useExpanded","params","component","columnsClass","columns","gapClass","gap","aspectClass","aspectRatio","handleImageClick","index","lightbox","_$createComponent","ExpandableWrapper","title","galleryTitle","copyData","copyLabel","galleryCopy","toolbarVariant","children","_el$","_$getNextElement","_tmpl$2","_el$5","firstChild","_el$6","_co$","_$getNextMarker","nextSibling","_el$4","_el$7","_el$8","_co$2","_$insert","Show","when","_el$2","_tmpl$","_el$3","For","each","image","_el$9","_tmpl$5","_el$0","_el$1","_el$13","_el$14","_co$3","_el$15","_el$16","_co$4","$$click","_$memo","showCaptions","_el$10","_tmpl$3","_el$11","_tmpl$4","_$effect","_p$","_v$3","_v$4","alt","formatMCPUIString","galleryViewImage","_v$5","safeUrl","thumbnail","allowDataImage","_v$6","galleryImageAlt","_v$7","srcset","_v$8","sizes","e","_$className","t","_$setAttribute","a","o","i","n","undefined","_$runHydrationEvents","LightboxOverlay","onClose","onNavigate","_v$","_v$2","_$delegateEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGalleryRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ImageGalleryRenderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAA2B,MAAM,UAAU,CAAA;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAK/D,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAE3B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAWD,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,yBAAyB,CAsIrE,CAAA"}
1
+ {"version":3,"file":"ImageGalleryRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ImageGalleryRenderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAA2B,MAAM,UAAU,CAAA;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAM/D,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAE3B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAWD,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,yBAAyB,CAyIrE,CAAA"}