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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/CHANGELOG.md +519 -0
  2. package/README.md +791 -1
  3. package/dist/adapters/connector.cjs +15 -4
  4. package/dist/adapters/connector.cjs.map +1 -1
  5. package/dist/adapters/connector.d.ts +38 -0
  6. package/dist/adapters/connector.d.ts.map +1 -1
  7. package/dist/adapters/connector.js +15 -4
  8. package/dist/adapters/connector.js.map +1 -1
  9. package/dist/adapters/index.d.ts +4 -3
  10. package/dist/adapters/index.d.ts.map +1 -1
  11. package/dist/adapters/macro-run.cjs +25 -13
  12. package/dist/adapters/macro-run.cjs.map +1 -1
  13. package/dist/adapters/macro-run.d.ts +37 -2
  14. package/dist/adapters/macro-run.d.ts.map +1 -1
  15. package/dist/adapters/macro-run.js +25 -13
  16. package/dist/adapters/macro-run.js.map +1 -1
  17. package/dist/adapters.cjs +2 -0
  18. package/dist/adapters.cjs.map +1 -1
  19. package/dist/adapters.js +4 -2
  20. package/dist/components/ActionGroupRenderer.cjs +3 -1
  21. package/dist/components/ActionGroupRenderer.cjs.map +1 -1
  22. package/dist/components/ActionGroupRenderer.d.ts.map +1 -1
  23. package/dist/components/ActionGroupRenderer.js +3 -1
  24. package/dist/components/ActionGroupRenderer.js.map +1 -1
  25. package/dist/components/AgentCard.cjs +11 -8
  26. package/dist/components/AgentCard.cjs.map +1 -1
  27. package/dist/components/AgentCard.d.ts.map +1 -1
  28. package/dist/components/AgentCard.js +11 -8
  29. package/dist/components/AgentCard.js.map +1 -1
  30. package/dist/components/AgentHandoff.cjs +6 -1
  31. package/dist/components/AgentHandoff.cjs.map +1 -1
  32. package/dist/components/AgentHandoff.d.ts.map +1 -1
  33. package/dist/components/AgentHandoff.js +6 -1
  34. package/dist/components/AgentHandoff.js.map +1 -1
  35. package/dist/components/ArtifactRenderer.cjs +25 -6
  36. package/dist/components/ArtifactRenderer.cjs.map +1 -1
  37. package/dist/components/ArtifactRenderer.d.ts.map +1 -1
  38. package/dist/components/ArtifactRenderer.js +25 -6
  39. package/dist/components/ArtifactRenderer.js.map +1 -1
  40. package/dist/components/AutocompleteDropdown.cjs +30 -24
  41. package/dist/components/AutocompleteDropdown.cjs.map +1 -1
  42. package/dist/components/AutocompleteDropdown.d.ts.map +1 -1
  43. package/dist/components/AutocompleteDropdown.js +30 -24
  44. package/dist/components/AutocompleteDropdown.js.map +1 -1
  45. package/dist/components/AutocompleteFormField.cjs +3 -1
  46. package/dist/components/AutocompleteFormField.cjs.map +1 -1
  47. package/dist/components/AutocompleteFormField.d.ts.map +1 -1
  48. package/dist/components/AutocompleteFormField.js +4 -2
  49. package/dist/components/AutocompleteFormField.js.map +1 -1
  50. package/dist/components/BriefingDiff.cjs +45 -39
  51. package/dist/components/BriefingDiff.cjs.map +1 -1
  52. package/dist/components/BriefingDiff.d.ts.map +1 -1
  53. package/dist/components/BriefingDiff.js +45 -39
  54. package/dist/components/BriefingDiff.js.map +1 -1
  55. package/dist/components/CarouselRenderer.cjs +8 -2
  56. package/dist/components/CarouselRenderer.cjs.map +1 -1
  57. package/dist/components/CarouselRenderer.d.ts.map +1 -1
  58. package/dist/components/CarouselRenderer.js +8 -2
  59. package/dist/components/CarouselRenderer.js.map +1 -1
  60. package/dist/components/ChartJSRenderer.cjs +60 -36
  61. package/dist/components/ChartJSRenderer.cjs.map +1 -1
  62. package/dist/components/ChartJSRenderer.d.ts.map +1 -1
  63. package/dist/components/ChartJSRenderer.js +61 -37
  64. package/dist/components/ChartJSRenderer.js.map +1 -1
  65. package/dist/components/ChatPrompt.cjs +15 -9
  66. package/dist/components/ChatPrompt.cjs.map +1 -1
  67. package/dist/components/ChatPrompt.d.ts.map +1 -1
  68. package/dist/components/ChatPrompt.js +15 -9
  69. package/dist/components/ChatPrompt.js.map +1 -1
  70. package/dist/components/CodeBlockRenderer.cjs +34 -16
  71. package/dist/components/CodeBlockRenderer.cjs.map +1 -1
  72. package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
  73. package/dist/components/CodeBlockRenderer.js +34 -16
  74. package/dist/components/CodeBlockRenderer.js.map +1 -1
  75. package/dist/components/DataPreviewSection.cjs +91 -44
  76. package/dist/components/DataPreviewSection.cjs.map +1 -1
  77. package/dist/components/DataPreviewSection.d.ts.map +1 -1
  78. package/dist/components/DataPreviewSection.js +92 -45
  79. package/dist/components/DataPreviewSection.js.map +1 -1
  80. package/dist/components/DegradedFallback.cjs +20 -16
  81. package/dist/components/DegradedFallback.cjs.map +1 -1
  82. package/dist/components/DegradedFallback.d.ts.map +1 -1
  83. package/dist/components/DegradedFallback.js +20 -16
  84. package/dist/components/DegradedFallback.js.map +1 -1
  85. package/dist/components/FooterRenderer.cjs +21 -17
  86. package/dist/components/FooterRenderer.cjs.map +1 -1
  87. package/dist/components/FooterRenderer.d.ts.map +1 -1
  88. package/dist/components/FooterRenderer.js +21 -17
  89. package/dist/components/FooterRenderer.js.map +1 -1
  90. package/dist/components/FormFieldRenderer.cjs +116 -93
  91. package/dist/components/FormFieldRenderer.cjs.map +1 -1
  92. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  93. package/dist/components/FormFieldRenderer.js +116 -93
  94. package/dist/components/FormFieldRenderer.js.map +1 -1
  95. package/dist/components/FormRenderer.cjs +55 -31
  96. package/dist/components/FormRenderer.cjs.map +1 -1
  97. package/dist/components/FormRenderer.d.ts.map +1 -1
  98. package/dist/components/FormRenderer.js +55 -31
  99. package/dist/components/FormRenderer.js.map +1 -1
  100. package/dist/components/GenerativeUIErrorBoundary.cjs +20 -15
  101. package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
  102. package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
  103. package/dist/components/GenerativeUIErrorBoundary.js +20 -15
  104. package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
  105. package/dist/components/GraphRenderer.cjs +56 -33
  106. package/dist/components/GraphRenderer.cjs.map +1 -1
  107. package/dist/components/GraphRenderer.d.ts.map +1 -1
  108. package/dist/components/GraphRenderer.js +56 -33
  109. package/dist/components/GraphRenderer.js.map +1 -1
  110. package/dist/components/ImageGalleryRenderer.cjs +13 -4
  111. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  112. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  113. package/dist/components/ImageGalleryRenderer.js +13 -4
  114. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  115. package/dist/components/LightboxOverlay.cjs +16 -8
  116. package/dist/components/LightboxOverlay.cjs.map +1 -1
  117. package/dist/components/LightboxOverlay.d.ts.map +1 -1
  118. package/dist/components/LightboxOverlay.js +17 -9
  119. package/dist/components/LightboxOverlay.js.map +1 -1
  120. package/dist/components/MapRenderer.cjs +59 -29
  121. package/dist/components/MapRenderer.cjs.map +1 -1
  122. package/dist/components/MapRenderer.d.ts +10 -2
  123. package/dist/components/MapRenderer.d.ts.map +1 -1
  124. package/dist/components/MapRenderer.js +60 -30
  125. package/dist/components/MapRenderer.js.map +1 -1
  126. package/dist/components/ModalRenderer.cjs +4 -1
  127. package/dist/components/ModalRenderer.cjs.map +1 -1
  128. package/dist/components/ModalRenderer.d.ts.map +1 -1
  129. package/dist/components/ModalRenderer.js +5 -2
  130. package/dist/components/ModalRenderer.js.map +1 -1
  131. package/dist/components/ScratchpadPanel.cjs +578 -535
  132. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  133. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  134. package/dist/components/ScratchpadPanel.js +578 -535
  135. package/dist/components/ScratchpadPanel.js.map +1 -1
  136. package/dist/components/StreamingUIRenderer.cjs +92 -28
  137. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  138. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  139. package/dist/components/StreamingUIRenderer.js +92 -28
  140. package/dist/components/StreamingUIRenderer.js.map +1 -1
  141. package/dist/components/UIResourceRenderer.cjs +456 -363
  142. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  143. package/dist/components/UIResourceRenderer.d.ts +21 -0
  144. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  145. package/dist/components/UIResourceRenderer.js +458 -365
  146. package/dist/components/UIResourceRenderer.js.map +1 -1
  147. package/dist/components/VerifiedText.cjs +39 -20
  148. package/dist/components/VerifiedText.cjs.map +1 -1
  149. package/dist/components/VerifiedText.d.ts +2 -1
  150. package/dist/components/VerifiedText.d.ts.map +1 -1
  151. package/dist/components/VerifiedText.js +39 -20
  152. package/dist/components/VerifiedText.js.map +1 -1
  153. package/dist/components/VideoRenderer.cjs +10 -5
  154. package/dist/components/VideoRenderer.cjs.map +1 -1
  155. package/dist/components/VideoRenderer.d.ts.map +1 -1
  156. package/dist/components/VideoRenderer.js +10 -5
  157. package/dist/components/VideoRenderer.js.map +1 -1
  158. package/dist/components/chart-data-table.cjs +38 -17
  159. package/dist/components/chart-data-table.cjs.map +1 -1
  160. package/dist/components/chart-data-table.d.ts +30 -1
  161. package/dist/components/chart-data-table.d.ts.map +1 -1
  162. package/dist/components/chart-data-table.js +38 -17
  163. package/dist/components/chart-data-table.js.map +1 -1
  164. package/dist/context/MCPActionContext.cjs +3 -1
  165. package/dist/context/MCPActionContext.cjs.map +1 -1
  166. package/dist/context/MCPActionContext.d.ts.map +1 -1
  167. package/dist/context/MCPActionContext.js +3 -1
  168. package/dist/context/MCPActionContext.js.map +1 -1
  169. package/dist/context/MCPUIStringsContext.cjs +285 -2
  170. package/dist/context/MCPUIStringsContext.cjs.map +1 -1
  171. package/dist/context/MCPUIStringsContext.d.ts +600 -3
  172. package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
  173. package/dist/context/MCPUIStringsContext.js +285 -2
  174. package/dist/context/MCPUIStringsContext.js.map +1 -1
  175. package/dist/hooks/index.d.ts +2 -2
  176. package/dist/hooks/index.d.ts.map +1 -1
  177. package/dist/hooks/useStreamingUI.cjs +28 -10
  178. package/dist/hooks/useStreamingUI.cjs.map +1 -1
  179. package/dist/hooks/useStreamingUI.d.ts +37 -0
  180. package/dist/hooks/useStreamingUI.d.ts.map +1 -1
  181. package/dist/hooks/useStreamingUI.js +28 -10
  182. package/dist/hooks/useStreamingUI.js.map +1 -1
  183. package/dist/hooks.cjs +1 -0
  184. package/dist/hooks.cjs.map +1 -1
  185. package/dist/hooks.js +2 -1
  186. package/dist/index.cjs +23 -8
  187. package/dist/index.cjs.map +1 -1
  188. package/dist/index.d.cts +9 -3
  189. package/dist/index.d.ts +9 -3
  190. package/dist/index.d.ts.map +1 -1
  191. package/dist/index.js +18 -3
  192. package/dist/index.js.map +1 -1
  193. package/dist/services/validation.cjs +41 -16
  194. package/dist/services/validation.cjs.map +1 -1
  195. package/dist/services/validation.d.ts +47 -2
  196. package/dist/services/validation.d.ts.map +1 -1
  197. package/dist/services/validation.js +41 -16
  198. package/dist/services/validation.js.map +1 -1
  199. package/dist/utils/degraded-projections.cjs +37 -12
  200. package/dist/utils/degraded-projections.cjs.map +1 -1
  201. package/dist/utils/degraded-projections.d.ts +39 -3
  202. package/dist/utils/degraded-projections.d.ts.map +1 -1
  203. package/dist/utils/degraded-projections.js +37 -12
  204. package/dist/utils/degraded-projections.js.map +1 -1
  205. package/dist/utils/format-string.cjs +10 -0
  206. package/dist/utils/format-string.cjs.map +1 -0
  207. package/dist/utils/format-string.d.ts +26 -0
  208. package/dist/utils/format-string.d.ts.map +1 -0
  209. package/dist/utils/format-string.js +10 -0
  210. package/dist/utils/format-string.js.map +1 -0
  211. package/dist/validation.cjs +3 -0
  212. package/dist/validation.cjs.map +1 -1
  213. package/dist/validation.d.cts +2 -1
  214. package/dist/validation.d.ts +2 -1
  215. package/dist/validation.d.ts.map +1 -1
  216. package/dist/validation.js +4 -1
  217. package/package.json +1 -1
  218. package/src/adapters/connector.ts +58 -4
  219. package/src/adapters/index.ts +12 -2
  220. package/src/adapters/macro-run.ts +71 -11
  221. package/src/components/ActionGroupRenderer.tsx +3 -1
  222. package/src/components/AgentCard.tsx +27 -9
  223. package/src/components/AgentHandoff.tsx +4 -1
  224. package/src/components/ArtifactRenderer.tsx +17 -5
  225. package/src/components/AutocompleteDropdown.tsx +8 -6
  226. package/src/components/AutocompleteFormField.tsx +5 -1
  227. package/src/components/BriefingDiff.tsx +11 -3
  228. package/src/components/CarouselRenderer.tsx +4 -2
  229. package/src/components/ChartJSRenderer.tsx +18 -9
  230. package/src/components/ChatPrompt.tsx +13 -8
  231. package/src/components/CodeBlockRenderer.tsx +12 -10
  232. package/src/components/DataPreviewSection.tsx +92 -26
  233. package/src/components/DegradedFallback.tsx +4 -2
  234. package/src/components/FooterRenderer.tsx +3 -1
  235. package/src/components/FormFieldRenderer.tsx +34 -19
  236. package/src/components/FormRenderer.tsx +38 -10
  237. package/src/components/GenerativeUIErrorBoundary.tsx +8 -3
  238. package/src/components/GraphRenderer.tsx +31 -16
  239. package/src/components/ImageGalleryRenderer.tsx +8 -4
  240. package/src/components/LightboxOverlay.tsx +6 -4
  241. package/src/components/MapRenderer.tsx +57 -20
  242. package/src/components/ModalRenderer.tsx +3 -1
  243. package/src/components/ScratchpadPanel.tsx +95 -44
  244. package/src/components/StreamingUIRenderer.tsx +41 -11
  245. package/src/components/UIResourceRenderer.tsx +145 -63
  246. package/src/components/VerifiedText.tsx +19 -9
  247. package/src/components/VideoRenderer.tsx +6 -4
  248. package/src/components/chart-data-table.ts +68 -14
  249. package/src/context/MCPActionContext.tsx +4 -1
  250. package/src/context/MCPUIStringsContext.tsx +1004 -1
  251. package/src/hooks/index.ts +2 -1
  252. package/src/hooks/useStreamingUI.ts +64 -10
  253. package/src/index.ts +23 -0
  254. package/src/services/validation.ts +93 -16
  255. package/src/utils/degraded-projections.ts +75 -12
  256. package/src/utils/format-string.ts +33 -0
  257. package/src/validation.ts +5 -0
@@ -30,6 +30,7 @@ import { DegradedFallback } from './DegradedFallback';
30
30
  import { graphToDegradedTable } from '../utils/degraded-projections';
31
31
  import { escapeHtml } from '../utils/escape-html';
32
32
  import { useTelemetry } from '../context/MCPUITelemetryContext';
33
+ import { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';
33
34
 
34
35
  // Module-scoped lazy import promise — first call triggers the dynamic
35
36
  // import, subsequent calls reuse the resolved module.
@@ -220,6 +221,7 @@ export interface GraphRendererProps {
220
221
  }
221
222
 
222
223
  export const GraphRenderer: Component<GraphRendererProps> = (props) => {
224
+ const strings = useMCPUIStrings();
223
225
  const params = () => props.component.params as GraphComponentParams;
224
226
  const isExpanded = useExpanded();
225
227
  const telemetry = useTelemetry();
@@ -296,7 +298,7 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
296
298
  graphInstance = new (Graph as any)(config);
297
299
  await graphInstance.render();
298
300
  } catch (err) {
299
- const message = err instanceof Error ? err.message : 'Failed to render graph';
301
+ const message = err instanceof Error ? err.message : strings.graphRenderError;
300
302
  setError(message);
301
303
  // Fallback ladder (P2.5): the native G6 render threw — emit telemetry
302
304
  // so the failure is observable, then degrade to the edge/node table
@@ -344,13 +346,13 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
344
346
  const url = (canvas as HTMLCanvasElement).toDataURL('image/png');
345
347
  await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);
346
348
  } else {
347
- setError('PNG export not supported in current renderer mode');
349
+ setError(strings.graphPngUnsupported);
348
350
  }
349
351
  } else {
350
352
  await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);
351
353
  }
352
354
  } catch (err) {
353
- setError(err instanceof Error ? err.message : 'PNG export failed');
355
+ setError(err instanceof Error ? err.message : strings.graphPngExportFailed);
354
356
  }
355
357
  setExportMenuOpen(false);
356
358
  };
@@ -371,7 +373,7 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
371
373
  >
372
374
  <div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg">
373
375
  <p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">
374
- Graph rendering unavailable
376
+ {strings.graphUnavailable}
375
377
  </p>
376
378
  <p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">
377
379
  Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code>{' '}
@@ -382,9 +384,9 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
382
384
  }
383
385
  >
384
386
  <ExpandableWrapper
385
- title={params().title ?? 'Graph'}
387
+ title={params().title ?? strings.graphTitle}
386
388
  copyData={toJSON(params())}
387
- copyLabel="Copy graph (JSON)"
389
+ copyLabel={strings.graphCopy}
388
390
  toolbarVariant={props.toolbarVariant}
389
391
  >
390
392
  <div
@@ -399,12 +401,12 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
399
401
  type="button"
400
402
  onClick={() => setExportMenuOpen((v) => !v)}
401
403
  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"
402
- title="Export graph"
403
- aria-label="Export graph"
404
+ title={strings.graphExport}
405
+ aria-label={strings.graphExport}
404
406
  aria-haspopup="menu"
405
407
  aria-expanded={exportMenuOpen()}
406
408
  >
407
- Export ▾
409
+ {strings.graphExportMenu}
408
410
  </button>
409
411
  <PortalDropdownMenu
410
412
  open={exportMenuOpen()}
@@ -415,13 +417,21 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
415
417
  >
416
418
  <For
417
419
  each={[
418
- { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },
419
420
  {
420
- label: 'Download Mermaid',
421
+ label: strings.graphDownloadPng,
422
+ onClick: handleExportPNG,
423
+ hint: strings.graphDownloadPngHint,
424
+ },
425
+ {
426
+ label: strings.graphDownloadMermaid,
421
427
  onClick: handleExportMermaid,
422
- hint: 'markdown / GitHub',
428
+ hint: strings.graphDownloadMermaidHint,
429
+ },
430
+ {
431
+ label: strings.graphDownloadJson,
432
+ onClick: handleExportJSON,
433
+ hint: strings.graphDownloadJsonHint,
423
434
  },
424
- { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },
425
435
  ]}
426
436
  >
427
437
  {(item) => (
@@ -454,9 +464,14 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
454
464
  rather than showing a bare message. Export menu stays usable. */}
455
465
  <Show when={error()}>
456
466
  <DegradedFallback
457
- message={`Graph rendering failed: ${error()}`}
458
- caption="Showing the graph data as a table — the interactive view is unavailable."
459
- {...graphToDegradedTable(params())}
467
+ message={formatMCPUIString(strings.graphRenderFailed, { error: String(error()) })}
468
+ caption={strings.graphDegradedCaption}
469
+ {...graphToDegradedTable(params(), {
470
+ source: strings.degradedColSource,
471
+ target: strings.degradedColTarget,
472
+ label: strings.degradedColLabel,
473
+ node: strings.degradedColNode,
474
+ })}
460
475
  />
461
476
  </Show>
462
477
  </div>
@@ -8,6 +8,7 @@ import type { UIComponent, ImageGalleryParams } from '../types'
8
8
  import { LightboxOverlay } from './LightboxOverlay'
9
9
  import { ExpandableWrapper, useExpanded } from './ExpandableWrapper'
10
10
  import { safeUrl } from '../utils/safe-url'
11
+ import { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'
11
12
 
12
13
  export interface ImageGalleryRendererProps {
13
14
  /**
@@ -37,6 +38,7 @@ function imagesToTextList(p: ImageGalleryParams | undefined): string {
37
38
  }
38
39
 
39
40
  export const ImageGalleryRenderer: Component<ImageGalleryRendererProps> = (props) => {
41
+ const strings = useMCPUIStrings()
40
42
  const [selectedIndex, setSelectedIndex] = createSignal<number | null>(null)
41
43
  const isExpanded = useExpanded()
42
44
 
@@ -91,9 +93,9 @@ export const ImageGalleryRenderer: Component<ImageGalleryRendererProps> = (props
91
93
 
92
94
  return (
93
95
  <ExpandableWrapper
94
- title={params()?.title || 'Gallery'}
96
+ title={params()?.title || strings.galleryTitle}
95
97
  copyData={imagesToTextList(params())}
96
- copyLabel="Copy image URLs"
98
+ copyLabel={strings.galleryCopy}
97
99
  toolbarVariant={props.toolbarVariant}
98
100
  >
99
101
  <div class={`w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${
@@ -114,11 +116,13 @@ export const ImageGalleryRenderer: Component<ImageGalleryRendererProps> = (props
114
116
  class={`relative overflow-hidden rounded-md focus:ring-2 focus:ring-blue-500 focus:outline-none group ${aspectClass()}`}
115
117
  onClick={() => handleImageClick(index())}
116
118
  type="button"
117
- aria-label={image.alt || `View image ${index() + 1}`}
119
+ aria-label={
120
+ image.alt || formatMCPUIString(strings.galleryViewImage, { index: index() + 1 })
121
+ }
118
122
  >
119
123
  <img
120
124
  src={safeUrl(image.thumbnail || image.url, { allowDataImage: true })}
121
- alt={image.alt || `Image ${index() + 1}`}
125
+ alt={image.alt || formatMCPUIString(strings.galleryImageAlt, { index: index() + 1 })}
122
126
  srcset={image.srcset}
123
127
  sizes={image.sizes}
124
128
  class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-200"
@@ -7,6 +7,7 @@ import { Component, Show, createEffect, onCleanup } from 'solid-js'
7
7
  import { Portal } from 'solid-js/web'
8
8
  import type { GalleryImage } from '../types'
9
9
  import { safeUrl } from '../utils/safe-url'
10
+ import { useMCPUIStrings } from '../context/MCPUIStringsContext'
10
11
 
11
12
  export interface LightboxOverlayProps {
12
13
  /**
@@ -31,6 +32,7 @@ export interface LightboxOverlayProps {
31
32
  }
32
33
 
33
34
  export const LightboxOverlay: Component<LightboxOverlayProps> = (props) => {
35
+ const strings = useMCPUIStrings()
34
36
  const isOpen = () => props.selectedIndex !== null
35
37
  const currentImage = () =>
36
38
  props.selectedIndex !== null ? props.images[props.selectedIndex] : null
@@ -85,13 +87,13 @@ export const LightboxOverlay: Component<LightboxOverlayProps> = (props) => {
85
87
  onClick={props.onClose}
86
88
  role="dialog"
87
89
  aria-modal="true"
88
- aria-label="Image lightbox"
90
+ aria-label={strings.lightboxLabel}
89
91
  >
90
92
  {/* Close button */}
91
93
  <button
92
94
  class="absolute top-4 right-4 text-white/80 hover:text-white p-2 rounded-full hover:bg-white/10 transition-colors"
93
95
  onClick={props.onClose}
94
- aria-label="Close lightbox"
96
+ aria-label={strings.lightboxClose}
95
97
  >
96
98
  <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
97
99
  <path
@@ -111,7 +113,7 @@ export const LightboxOverlay: Component<LightboxOverlayProps> = (props) => {
111
113
  e.stopPropagation()
112
114
  handlePrev()
113
115
  }}
114
- aria-label="Previous image"
116
+ aria-label={strings.lightboxPrevious}
115
117
  >
116
118
  <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
117
119
  <path
@@ -132,7 +134,7 @@ export const LightboxOverlay: Component<LightboxOverlayProps> = (props) => {
132
134
  e.stopPropagation()
133
135
  handleNext()
134
136
  }}
135
- aria-label="Next image"
137
+ aria-label={strings.lightboxNext}
136
138
  >
137
139
  <svg class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
138
140
  <path
@@ -4,7 +4,7 @@
4
4
  * v3.1.0: GeoJSON, choropleth, popups, multi-layer, PMTiles
5
5
  */
6
6
 
7
- import { Component, createEffect, onCleanup, createSignal, Show } from 'solid-js';
7
+ import { Component, createEffect, onCleanup, createSignal, Show, untrack } from 'solid-js';
8
8
  import { isServer } from 'solid-js/web';
9
9
  import type {
10
10
  UIComponent,
@@ -18,6 +18,11 @@ import { DegradedFallback } from './DegradedFallback';
18
18
  import { mapToDegradedTable } from '../utils/degraded-projections';
19
19
  import { escapeHtml } from '../utils/escape-html';
20
20
  import { useTelemetry } from '../context/MCPUITelemetryContext';
21
+ import {
22
+ DEFAULT_MCPUI_STRINGS,
23
+ formatMCPUIString,
24
+ useMCPUIStrings,
25
+ } from '../context/MCPUIStringsContext';
21
26
 
22
27
  // Lazy load leaflet (it doesn't support SSR well)
23
28
  let L: any = null;
@@ -137,7 +142,9 @@ function buildStyleFn(
137
142
  export function buildPopupContent(
138
143
  feature: any,
139
144
  popup: MapPopupConfig | undefined,
140
- allowHtml = false
145
+ allowHtml = false,
146
+ /** BCP-47 tag for numeric values (`MCPUIStrings.locale`). Was `'fr-FR'` before 6.20.0. */
147
+ locale: string = DEFAULT_MCPUI_STRINGS.locale
141
148
  ): string | null {
142
149
  if (!popup || !feature?.properties) return null;
143
150
  const props = feature.properties;
@@ -162,7 +169,7 @@ export function buildPopupContent(
162
169
  if (key === popup.titleField) continue;
163
170
  const val = props[key];
164
171
  if (val == null) continue;
165
- const formatted = typeof val === 'number' ? val.toLocaleString('fr-FR') : String(val);
172
+ const formatted = typeof val === 'number' ? val.toLocaleString(locale) : String(val);
166
173
  parts.push(
167
174
  `<span style="color:#666;font-size:11px">${escapeHtml(key)}</span>: ${escapeHtml(formatted)}`
168
175
  );
@@ -204,7 +211,13 @@ export function addGeoJSONLayer(
204
211
  geojson: unknown,
205
212
  style?: MapGeoJSONStyle,
206
213
  popup?: MapPopupConfig,
207
- allowHtml = false
214
+ allowHtml = false,
215
+ /**
216
+ * BCP-47 tag forwarded to {@link buildPopupContent}, or a getter. With a
217
+ * getter, each popup is bound to a function Leaflet evaluates when the popup
218
+ * opens, so it follows locale changes without rebuilding the layer.
219
+ */
220
+ locale: string | (() => string) = DEFAULT_MCPUI_STRINGS.locale
208
221
  ): any {
209
222
  const styleFn = buildStyleFn(style);
210
223
 
@@ -223,9 +236,18 @@ export function addGeoJSONLayer(
223
236
  });
224
237
  },
225
238
  onEachFeature: (feature: any, featureLayer: any) => {
226
- const html = buildPopupContent(feature, popup, allowHtml);
239
+ // `untrack`: onEachFeature runs synchronously inside the map setup effect
240
+ // when Leaflet is already loaded. A tracked read there would subscribe
241
+ // that non-idempotent effect to the locale.
242
+ const currentLocale = () => (typeof locale === 'function' ? untrack(locale) : locale);
243
+ const html = buildPopupContent(feature, popup, allowHtml, currentLocale());
227
244
  if (html) {
228
- featureLayer.bindPopup(html, { maxWidth: 300 });
245
+ featureLayer.bindPopup(
246
+ typeof locale === 'function'
247
+ ? () => buildPopupContent(feature, popup, allowHtml, currentLocale()) ?? ''
248
+ : html,
249
+ { maxWidth: 300 }
250
+ );
229
251
  }
230
252
  },
231
253
  });
@@ -264,6 +286,7 @@ function mapToGeoJSON(p: MapComponentParams | undefined): string {
264
286
  }
265
287
 
266
288
  export const MapRenderer: Component<MapRendererProps> = (props) => {
289
+ const strings = useMCPUIStrings();
267
290
  let mapContainer: HTMLDivElement | undefined;
268
291
  let mapInstance: any = null;
269
292
  const [isLeafletLoaded, setIsLeafletLoaded] = createSignal(false);
@@ -305,7 +328,7 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
305
328
  setIsLeafletLoaded(true);
306
329
  } catch (e) {
307
330
  console.warn('Failed to load leaflet', e);
308
- setError('Map library could not be loaded.');
331
+ setError(strings.mapLibraryUnavailable);
309
332
  return;
310
333
  }
311
334
  } else {
@@ -418,7 +441,11 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
418
441
  p.geojson,
419
442
  p.geojsonStyle,
420
443
  p.popup,
421
- allowHtml()
444
+ allowHtml(),
445
+ // A getter, not a value: popups read the current locale when they
446
+ // open. Tracking the locale here would re-run this effect, which is
447
+ // not idempotent (layer control, PMTiles layer, view reset).
448
+ () => strings.locale
422
449
  );
423
450
  allBoundsLayers.push(geoLayer);
424
451
  }
@@ -434,7 +461,8 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
434
461
  layerDef.geojson,
435
462
  layerDef.style || p?.geojsonStyle,
436
463
  layerDef.popup || p?.popup,
437
- allowHtml()
464
+ allowHtml(),
465
+ () => strings.locale
438
466
  );
439
467
 
440
468
  overlays[layerDef.name] = geoLayer;
@@ -501,14 +529,16 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
501
529
  // most common cause is the optional `protomaps-leaflet` peer not
502
530
  // being installed.
503
531
  const detail = e instanceof Error ? e.message : String(e);
504
- const message =
532
+ // English diagnostic for logs and telemetry (P7); the visible
533
+ // banner reads the localized `mapPmtilesUnavailable` key.
534
+ const diagnostic =
505
535
  'PMTiles layer unavailable — the optional "protomaps-leaflet" ' +
506
536
  'peer dependency failed to load or render.';
507
- console.warn('[MCP-UI] ' + message, e);
508
- setPmtilesError(message);
537
+ console.warn('[MCP-UI] ' + diagnostic, e);
538
+ setPmtilesError(strings.mapPmtilesUnavailable);
509
539
  telemetry?.dispatch({
510
540
  type: 'render:error',
511
- errorMessage: `${message} (${detail})`,
541
+ errorMessage: `${diagnostic} (${detail})`,
512
542
  id: props.component?.id ?? '',
513
543
  componentType: 'map',
514
544
  ts: Date.now(),
@@ -529,7 +559,7 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
529
559
  } catch (err) {
530
560
  // Fallback ladder (P2.5): a Leaflet drawing failure degrades to
531
561
  // the coordinate table below instead of a blank/partial map.
532
- const message = err instanceof Error ? err.message : 'Failed to render map';
562
+ const message = err instanceof Error ? err.message : strings.mapRenderError;
533
563
  setError(message);
534
564
  telemetry?.dispatch({
535
565
  type: 'render:error',
@@ -552,9 +582,9 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
552
582
 
553
583
  return (
554
584
  <ExpandableWrapper
555
- title={'Map'}
585
+ title={strings.mapTitle}
556
586
  copyData={mapToGeoJSON(params())}
557
- copyLabel="Copy markers as GeoJSON"
587
+ copyLabel={strings.mapCopy}
558
588
  toolbarVariant={props.toolbarVariant}
559
589
  >
560
590
  <div
@@ -567,9 +597,16 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
567
597
  rather than a bare error string. */}
568
598
  <div class="p-3">
569
599
  <DegradedFallback
570
- message={`Map rendering failed: ${error()}`}
571
- caption="Showing the map data as a coordinate table — the interactive map is unavailable."
572
- {...mapToDegradedTable(params() ?? {})}
600
+ message={formatMCPUIString(strings.mapRenderFailed, { error: String(error()) })}
601
+ caption={strings.mapDegradedCaption}
602
+ {...mapToDegradedTable(params() ?? {}, {
603
+ type: strings.degradedColType,
604
+ lat: strings.degradedColLat,
605
+ lng: strings.degradedColLng,
606
+ info: strings.degradedColInfo,
607
+ marker: strings.degradedMarker,
608
+ feature: strings.degradedFeature,
609
+ })}
573
610
  />
574
611
  </div>
575
612
  </Show>
@@ -581,7 +618,7 @@ export const MapRenderer: Component<MapRendererProps> = (props) => {
581
618
  role="alert"
582
619
  class="m-2 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/20 dark:text-amber-200"
583
620
  >
584
- {pmtilesError()} The base map is still shown.
621
+ {pmtilesError()} {strings.mapBaseMapStillShown}
585
622
  </div>
586
623
  </Show>
587
624
  <div
@@ -6,6 +6,7 @@
6
6
  import { Component, Show, createSignal, createEffect, onCleanup, JSX } from 'solid-js'
7
7
  import { Portal } from 'solid-js/web'
8
8
  import type { UIComponent, ModalComponentParams } from '../types'
9
+ import { useMCPUIStrings } from '../context/MCPUIStringsContext'
9
10
 
10
11
  export interface ModalRendererProps {
11
12
  /**
@@ -72,6 +73,7 @@ export interface ModalRendererProps {
72
73
  * ```
73
74
  */
74
75
  export const ModalRenderer: Component<ModalRendererProps> = (props) => {
76
+ const strings = useMCPUIStrings()
75
77
  const [isVisible, setIsVisible] = createSignal(false)
76
78
 
77
79
  const params = () => props.params || (props.component?.params as ModalComponentParams) || {}
@@ -185,7 +187,7 @@ export const ModalRenderer: Component<ModalRendererProps> = (props) => {
185
187
  <button
186
188
  onClick={handleClose}
187
189
  class="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
188
- aria-label="Close modal"
190
+ aria-label={strings.modalClose}
189
191
  >
190
192
  <svg
191
193
  class="w-5 h-5"