@seed-ship/mcp-ui-solid 6.19.0 → 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 (262) hide show
  1. package/CHANGELOG.md +532 -0
  2. package/README.md +798 -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/FeedbackInline.cjs +19 -3
  86. package/dist/components/FeedbackInline.cjs.map +1 -1
  87. package/dist/components/FeedbackInline.js +19 -3
  88. package/dist/components/FeedbackInline.js.map +1 -1
  89. package/dist/components/FooterRenderer.cjs +21 -17
  90. package/dist/components/FooterRenderer.cjs.map +1 -1
  91. package/dist/components/FooterRenderer.d.ts.map +1 -1
  92. package/dist/components/FooterRenderer.js +21 -17
  93. package/dist/components/FooterRenderer.js.map +1 -1
  94. package/dist/components/FormFieldRenderer.cjs +116 -93
  95. package/dist/components/FormFieldRenderer.cjs.map +1 -1
  96. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  97. package/dist/components/FormFieldRenderer.js +116 -93
  98. package/dist/components/FormFieldRenderer.js.map +1 -1
  99. package/dist/components/FormRenderer.cjs +55 -31
  100. package/dist/components/FormRenderer.cjs.map +1 -1
  101. package/dist/components/FormRenderer.d.ts.map +1 -1
  102. package/dist/components/FormRenderer.js +55 -31
  103. package/dist/components/FormRenderer.js.map +1 -1
  104. package/dist/components/GenerativeUIErrorBoundary.cjs +20 -15
  105. package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
  106. package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
  107. package/dist/components/GenerativeUIErrorBoundary.js +20 -15
  108. package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
  109. package/dist/components/GraphRenderer.cjs +56 -33
  110. package/dist/components/GraphRenderer.cjs.map +1 -1
  111. package/dist/components/GraphRenderer.d.ts.map +1 -1
  112. package/dist/components/GraphRenderer.js +56 -33
  113. package/dist/components/GraphRenderer.js.map +1 -1
  114. package/dist/components/ImageGalleryRenderer.cjs +13 -4
  115. package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
  116. package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
  117. package/dist/components/ImageGalleryRenderer.js +13 -4
  118. package/dist/components/ImageGalleryRenderer.js.map +1 -1
  119. package/dist/components/LightboxOverlay.cjs +16 -8
  120. package/dist/components/LightboxOverlay.cjs.map +1 -1
  121. package/dist/components/LightboxOverlay.d.ts.map +1 -1
  122. package/dist/components/LightboxOverlay.js +17 -9
  123. package/dist/components/LightboxOverlay.js.map +1 -1
  124. package/dist/components/MapRenderer.cjs +59 -29
  125. package/dist/components/MapRenderer.cjs.map +1 -1
  126. package/dist/components/MapRenderer.d.ts +10 -2
  127. package/dist/components/MapRenderer.d.ts.map +1 -1
  128. package/dist/components/MapRenderer.js +60 -30
  129. package/dist/components/MapRenderer.js.map +1 -1
  130. package/dist/components/ModalRenderer.cjs +4 -1
  131. package/dist/components/ModalRenderer.cjs.map +1 -1
  132. package/dist/components/ModalRenderer.d.ts.map +1 -1
  133. package/dist/components/ModalRenderer.js +5 -2
  134. package/dist/components/ModalRenderer.js.map +1 -1
  135. package/dist/components/ScratchpadPanel.cjs +578 -535
  136. package/dist/components/ScratchpadPanel.cjs.map +1 -1
  137. package/dist/components/ScratchpadPanel.d.ts.map +1 -1
  138. package/dist/components/ScratchpadPanel.js +578 -535
  139. package/dist/components/ScratchpadPanel.js.map +1 -1
  140. package/dist/components/StreamingUIRenderer.cjs +92 -28
  141. package/dist/components/StreamingUIRenderer.cjs.map +1 -1
  142. package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
  143. package/dist/components/StreamingUIRenderer.js +92 -28
  144. package/dist/components/StreamingUIRenderer.js.map +1 -1
  145. package/dist/components/UIResourceRenderer.cjs +456 -363
  146. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  147. package/dist/components/UIResourceRenderer.d.ts +21 -0
  148. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  149. package/dist/components/UIResourceRenderer.js +458 -365
  150. package/dist/components/UIResourceRenderer.js.map +1 -1
  151. package/dist/components/VerifiedText.cjs +39 -20
  152. package/dist/components/VerifiedText.cjs.map +1 -1
  153. package/dist/components/VerifiedText.d.ts +2 -1
  154. package/dist/components/VerifiedText.d.ts.map +1 -1
  155. package/dist/components/VerifiedText.js +39 -20
  156. package/dist/components/VerifiedText.js.map +1 -1
  157. package/dist/components/VideoRenderer.cjs +10 -5
  158. package/dist/components/VideoRenderer.cjs.map +1 -1
  159. package/dist/components/VideoRenderer.d.ts.map +1 -1
  160. package/dist/components/VideoRenderer.js +10 -5
  161. package/dist/components/VideoRenderer.js.map +1 -1
  162. package/dist/components/chart-data-table.cjs +38 -17
  163. package/dist/components/chart-data-table.cjs.map +1 -1
  164. package/dist/components/chart-data-table.d.ts +30 -1
  165. package/dist/components/chart-data-table.d.ts.map +1 -1
  166. package/dist/components/chart-data-table.js +38 -17
  167. package/dist/components/chart-data-table.js.map +1 -1
  168. package/dist/context/MCPActionContext.cjs +3 -1
  169. package/dist/context/MCPActionContext.cjs.map +1 -1
  170. package/dist/context/MCPActionContext.d.ts.map +1 -1
  171. package/dist/context/MCPActionContext.js +3 -1
  172. package/dist/context/MCPActionContext.js.map +1 -1
  173. package/dist/context/MCPUIStringsContext.cjs +285 -2
  174. package/dist/context/MCPUIStringsContext.cjs.map +1 -1
  175. package/dist/context/MCPUIStringsContext.d.ts +600 -3
  176. package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
  177. package/dist/context/MCPUIStringsContext.js +285 -2
  178. package/dist/context/MCPUIStringsContext.js.map +1 -1
  179. package/dist/hooks/index.d.ts +2 -2
  180. package/dist/hooks/index.d.ts.map +1 -1
  181. package/dist/hooks/useStreamingUI.cjs +28 -10
  182. package/dist/hooks/useStreamingUI.cjs.map +1 -1
  183. package/dist/hooks/useStreamingUI.d.ts +37 -0
  184. package/dist/hooks/useStreamingUI.d.ts.map +1 -1
  185. package/dist/hooks/useStreamingUI.js +28 -10
  186. package/dist/hooks/useStreamingUI.js.map +1 -1
  187. package/dist/hooks.cjs +1 -0
  188. package/dist/hooks.cjs.map +1 -1
  189. package/dist/hooks.js +2 -1
  190. package/dist/index.cjs +23 -8
  191. package/dist/index.cjs.map +1 -1
  192. package/dist/index.d.cts +9 -3
  193. package/dist/index.d.ts +9 -3
  194. package/dist/index.d.ts.map +1 -1
  195. package/dist/index.js +18 -3
  196. package/dist/index.js.map +1 -1
  197. package/dist/services/validation.cjs +41 -16
  198. package/dist/services/validation.cjs.map +1 -1
  199. package/dist/services/validation.d.ts +47 -2
  200. package/dist/services/validation.d.ts.map +1 -1
  201. package/dist/services/validation.js +41 -16
  202. package/dist/services/validation.js.map +1 -1
  203. package/dist/utils/degraded-projections.cjs +37 -12
  204. package/dist/utils/degraded-projections.cjs.map +1 -1
  205. package/dist/utils/degraded-projections.d.ts +39 -3
  206. package/dist/utils/degraded-projections.d.ts.map +1 -1
  207. package/dist/utils/degraded-projections.js +37 -12
  208. package/dist/utils/degraded-projections.js.map +1 -1
  209. package/dist/utils/format-string.cjs +10 -0
  210. package/dist/utils/format-string.cjs.map +1 -0
  211. package/dist/utils/format-string.d.ts +26 -0
  212. package/dist/utils/format-string.d.ts.map +1 -0
  213. package/dist/utils/format-string.js +10 -0
  214. package/dist/utils/format-string.js.map +1 -0
  215. package/dist/validation.cjs +3 -0
  216. package/dist/validation.cjs.map +1 -1
  217. package/dist/validation.d.cts +2 -1
  218. package/dist/validation.d.ts +2 -1
  219. package/dist/validation.d.ts.map +1 -1
  220. package/dist/validation.js +4 -1
  221. package/package.json +1 -1
  222. package/src/adapters/connector.ts +58 -4
  223. package/src/adapters/index.ts +12 -2
  224. package/src/adapters/macro-run.ts +71 -11
  225. package/src/components/ActionGroupRenderer.tsx +3 -1
  226. package/src/components/AgentCard.tsx +27 -9
  227. package/src/components/AgentHandoff.tsx +4 -1
  228. package/src/components/ArtifactRenderer.tsx +17 -5
  229. package/src/components/AutocompleteDropdown.tsx +8 -6
  230. package/src/components/AutocompleteFormField.tsx +5 -1
  231. package/src/components/BriefingDiff.tsx +11 -3
  232. package/src/components/CarouselRenderer.tsx +4 -2
  233. package/src/components/ChartJSRenderer.tsx +18 -9
  234. package/src/components/ChatPrompt.tsx +13 -8
  235. package/src/components/CodeBlockRenderer.tsx +12 -10
  236. package/src/components/DataPreviewSection.tsx +92 -26
  237. package/src/components/DegradedFallback.tsx +4 -2
  238. package/src/components/FeedbackInline.tsx +2 -2
  239. package/src/components/FooterRenderer.tsx +3 -1
  240. package/src/components/FormFieldRenderer.tsx +34 -19
  241. package/src/components/FormRenderer.tsx +38 -10
  242. package/src/components/GenerativeUIErrorBoundary.tsx +8 -3
  243. package/src/components/GraphRenderer.tsx +31 -16
  244. package/src/components/ImageGalleryRenderer.tsx +8 -4
  245. package/src/components/LightboxOverlay.tsx +6 -4
  246. package/src/components/MapRenderer.tsx +57 -20
  247. package/src/components/ModalRenderer.tsx +3 -1
  248. package/src/components/ScratchpadPanel.tsx +95 -44
  249. package/src/components/StreamingUIRenderer.tsx +41 -11
  250. package/src/components/UIResourceRenderer.tsx +145 -63
  251. package/src/components/VerifiedText.tsx +19 -9
  252. package/src/components/VideoRenderer.tsx +6 -4
  253. package/src/components/chart-data-table.ts +68 -14
  254. package/src/context/MCPActionContext.tsx +4 -1
  255. package/src/context/MCPUIStringsContext.tsx +1004 -1
  256. package/src/hooks/index.ts +2 -1
  257. package/src/hooks/useStreamingUI.ts +64 -10
  258. package/src/index.ts +23 -0
  259. package/src/services/validation.ts +93 -16
  260. package/src/utils/degraded-projections.ts +75 -12
  261. package/src/utils/format-string.ts +33 -0
  262. package/src/validation.ts +5 -0
@@ -4,9 +4,10 @@
4
4
  * SolidJS hooks for managing UI resource state and streaming
5
5
  */
6
6
 
7
- export { useStreamingUI } from './useStreamingUI'
7
+ export { useStreamingUI, DEFAULT_STREAMING_UI_MESSAGES } from './useStreamingUI'
8
8
  export type {
9
9
  UseStreamingUIOptions,
10
+ StreamingUIMessages,
10
11
  StreamingUIState,
11
12
  StreamProgress,
12
13
  StreamError,
@@ -24,6 +24,7 @@
24
24
  import { createSignal, onCleanup } from 'solid-js'
25
25
  import type { UIComponent } from '../types'
26
26
  import { createLogger } from '../utils/logger'
27
+ import { formatMCPUIString } from '../utils/format-string'
27
28
 
28
29
  const logger = createLogger('useStreamingUI')
29
30
 
@@ -34,6 +35,50 @@ const isServer = typeof window === 'undefined'
34
35
  // Types
35
36
  // ============================================================================
36
37
 
38
+ /**
39
+ * Every string `useStreamingUI` puts into `progress().message` and
40
+ * `error()` — both are rendered by `<StreamingUIRenderer>`.
41
+ *
42
+ * @since v6.20.0 — `StreamingUIRenderer` feeds these from `MCPUIStrings`
43
+ * (`stream*` keys); a host calling the hook directly passes `messages`.
44
+ */
45
+ export interface StreamingUIMessages {
46
+ /** Initial progress message. */
47
+ initializing: string
48
+ /** Progress message while the request is sent. */
49
+ connecting: string
50
+ /** Progress message on `component-start`. Template — `{type}`. */
51
+ loadingComponent: string
52
+ /** Progress message on `complete`. */
53
+ dashboardLoaded: string
54
+ /** Progress message after an error. Template — `{message}`. */
55
+ errorProgress: string
56
+ /** `StreamError.error` when the fetch / read fails. */
57
+ connectionFailed: string
58
+ /** `StreamError.message` when the server answers non-OK without a message. */
59
+ requestFailed: string
60
+ /** `StreamError.message` when the response has no body. */
61
+ emptyResponse: string
62
+ /** `StreamError.message` when streaming starts during SSR. */
63
+ serverSide: string
64
+ /** `StreamError.message` when the failure carries no message. */
65
+ unknownError: string
66
+ }
67
+
68
+ /** English defaults of {@link StreamingUIMessages}. */
69
+ export const DEFAULT_STREAMING_UI_MESSAGES: StreamingUIMessages = {
70
+ initializing: 'Initializing...',
71
+ connecting: 'Connecting to server...',
72
+ loadingComponent: 'Loading {type} component...',
73
+ dashboardLoaded: 'Dashboard loaded',
74
+ errorProgress: 'Error: {message}',
75
+ connectionFailed: 'Stream connection failed',
76
+ requestFailed: 'Stream request failed',
77
+ emptyResponse: 'Response body is null',
78
+ serverSide: 'Streaming UI cannot start on server-side',
79
+ unknownError: 'Unknown error',
80
+ }
81
+
37
82
  export interface UseStreamingUIOptions {
38
83
  query: string
39
84
  spaceIds?: string[]
@@ -47,6 +92,12 @@ export interface UseStreamingUIOptions {
47
92
  onComplete?: (metadata: CompleteMetadata) => void
48
93
  onError?: (error: StreamError) => void
49
94
  onComponentReceived?: (component: UIComponent) => void
95
+ /**
96
+ * Localized progress / error messages (v6.20.0). Each key is read when
97
+ * the message is produced, so an object with getters stays reactive.
98
+ * Omitted keys fall back to {@link DEFAULT_STREAMING_UI_MESSAGES}.
99
+ */
100
+ messages?: Partial<StreamingUIMessages>
50
101
  }
51
102
 
52
103
  export interface StreamingUIState {
@@ -124,6 +175,9 @@ interface ComponentEvent {
124
175
  // ============================================================================
125
176
 
126
177
  export function useStreamingUI(options: UseStreamingUIOptions) {
178
+ const msg = (key: keyof StreamingUIMessages): string =>
179
+ options.messages?.[key] ?? DEFAULT_STREAMING_UI_MESSAGES[key]
180
+
127
181
  // State
128
182
  const [components, setComponents] = createSignal<UIComponent[]>([])
129
183
  const [isLoading, setIsLoading] = createSignal(false)
@@ -132,7 +186,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
132
186
  const [progress, setProgress] = createSignal<StreamProgress>({
133
187
  receivedCount: 0,
134
188
  totalCount: null,
135
- message: 'Initializing...',
189
+ message: msg('initializing'),
136
190
  timestamp: new Date().toISOString(),
137
191
  })
138
192
  const [metadata, setMetadata] = createSignal<CompleteMetadata | null>(null)
@@ -194,7 +248,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
194
248
 
195
249
  setProgress((prev) => ({
196
250
  ...prev,
197
- message: `Loading ${data.type} component...`,
251
+ message: formatMCPUIString(msg('loadingComponent'), { type: data.type }),
198
252
  timestamp: new Date().toISOString(),
199
253
  }))
200
254
  }
@@ -242,7 +296,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
242
296
 
243
297
  setProgress((prev) => ({
244
298
  ...prev,
245
- message: 'Dashboard loaded',
299
+ message: msg('dashboardLoaded'),
246
300
  timestamp: new Date().toISOString(),
247
301
  }))
248
302
 
@@ -264,7 +318,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
264
318
 
265
319
  setProgress((prev) => ({
266
320
  ...prev,
267
- message: `Error: ${data.message}`,
321
+ message: formatMCPUIString(msg('errorProgress'), { message: data.message }),
268
322
  timestamp: new Date().toISOString(),
269
323
  }))
270
324
 
@@ -325,7 +379,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
325
379
  logger.warn('startStreaming called on server-side - skipping')
326
380
  setError({
327
381
  error: 'ssr',
328
- message: 'Streaming UI cannot start on server-side',
382
+ message: msg('serverSide'),
329
383
  recoverable: false,
330
384
  })
331
385
  setIsLoading(false)
@@ -343,7 +397,7 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
343
397
  setProgress({
344
398
  receivedCount: 0,
345
399
  totalCount: null,
346
- message: 'Connecting to server...',
400
+ message: msg('connecting'),
347
401
  timestamp: new Date().toISOString(),
348
402
  })
349
403
 
@@ -372,11 +426,11 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
372
426
  .then(async (response) => {
373
427
  if (!response.ok) {
374
428
  const errorData = await response.json()
375
- throw new Error(errorData.message || 'Stream request failed')
429
+ throw new Error(errorData.message || msg('requestFailed'))
376
430
  }
377
431
 
378
432
  if (!response.body) {
379
- throw new Error('Response body is null')
433
+ throw new Error(msg('emptyResponse'))
380
434
  }
381
435
 
382
436
  const reader = response.body.getReader()
@@ -423,8 +477,8 @@ export function useStreamingUI(options: UseStreamingUIOptions) {
423
477
  })
424
478
 
425
479
  handleErrorEvent({
426
- error: 'Stream connection failed',
427
- message: err instanceof Error ? err.message : 'Unknown error',
480
+ error: msg('connectionFailed'),
481
+ message: err instanceof Error ? err.message : msg('unknownError'),
428
482
  recoverable: true,
429
483
  })
430
484
  })
package/src/index.ts CHANGED
@@ -134,6 +134,8 @@ export {
134
134
  MCPUIStringsContext,
135
135
  useMCPUIStrings,
136
136
  DEFAULT_MCPUI_STRINGS,
137
+ formatMCPUIString,
138
+ resolveMCPUILocale,
137
139
  } from './context/MCPUIStringsContext'
138
140
  export type {
139
141
  MCPUIStrings,
@@ -177,6 +179,7 @@ export type { AutocompleteFormFieldProps, AutocompleteFormFieldParams } from './
177
179
  // Hooks
178
180
  export {
179
181
  useStreamingUI,
182
+ DEFAULT_STREAMING_UI_MESSAGES,
180
183
  useAction,
181
184
  useToolAction,
182
185
  useConditionalField,
@@ -195,6 +198,7 @@ export {
195
198
 
196
199
  export type {
197
200
  UseStreamingUIOptions,
201
+ StreamingUIMessages,
198
202
  StreamingUIState,
199
203
  StreamProgress,
200
204
  StreamError,
@@ -338,6 +342,25 @@ export {
338
342
  validateAgainstSource,
339
343
  } from './services'
340
344
 
345
+ // i18n of the pure helpers (v6.20.0) — English baselines a host spreads and
346
+ // overrides when it renders in another language. The helpers themselves take
347
+ // the table as a trailing optional parameter, so nothing else changes.
348
+ export {
349
+ validateFieldValue,
350
+ validateFormData,
351
+ DEFAULT_VALIDATION_MESSAGES,
352
+ } from './services/validation'
353
+ export type { FormValidationMessages } from './services/validation'
354
+ export {
355
+ graphToDegradedTable,
356
+ mapToDegradedTable,
357
+ chartToDegradedTable,
358
+ DEGRADED_PROJECTION_LABELS,
359
+ } from './utils/degraded-projections'
360
+ export type { DegradedProjectionLabels } from './utils/degraded-projections'
361
+ export { chartToDataTable, CHART_DATA_TABLE_LABELS } from './components/chart-data-table'
362
+ export type { ChartDataTableLabels } from './components/chart-data-table'
363
+
341
364
  // Clarification → Prompt helper (v4.3.9)
342
365
  export { clarificationToPromptConfig } from './services/chat-bus'
343
366
 
@@ -42,6 +42,9 @@ import type {
42
42
  ValidationOptions,
43
43
  ComponentType,
44
44
  } from '../types';
45
+ // Runtime-free template helper: `src/validation.ts` (the SSR-safe subpath)
46
+ // re-exports this module, so it must stay free of any solid-js import.
47
+ import { formatMCPUIString } from '../utils/format-string';
45
48
 
46
49
  /**
47
50
  * All known ComponentType values — used to distinguish known-but-unvalidated
@@ -919,20 +922,85 @@ export function validateLayout(layout: UILayout, options?: ValidationOptions): V
919
922
  };
920
923
  }
921
924
 
925
+ /**
926
+ * The end-user form-validation wording.
927
+ *
928
+ * `validateFieldValue` / `validateFormData` are pure and SSR-safe (they are
929
+ * re-exported from the `@seed-ship/mcp-ui-solid/validation` subpath), so
930
+ * they cannot read `MCPUIStrings`. The wording is injected instead: both
931
+ * take an optional trailing `messages` partial, and `FormRenderer` feeds it
932
+ * from `useMCPUIStrings()` (keys `fieldRequired`, `fieldMinLength`, …).
933
+ *
934
+ * The STRUCTURAL validators (`validateComponent`, `validateLayout`, …) keep
935
+ * their English `ValidationError.message`: those are payload diagnostics
936
+ * addressed to whoever produced the payload, not end-user chrome.
937
+ *
938
+ * @since v6.20.0
939
+ */
940
+ export interface FormValidationMessages {
941
+ /** Template \u2014 `{field}`. */
942
+ required: string;
943
+ /** Template \u2014 `{field}`. */
944
+ mustBeChecked: string;
945
+ /** Template \u2014 `{min}`. */
946
+ minLength: string;
947
+ /** Template \u2014 `{max}`. */
948
+ maxLength: string;
949
+ /** Value does not match `field.pattern`. */
950
+ invalidPattern: string;
951
+ /** Value is not a valid email address. */
952
+ invalidEmail: string;
953
+ /** Value is not a number. */
954
+ invalidNumber: string;
955
+ /** Template \u2014 `{min}`. */
956
+ minValue: string;
957
+ /** Template \u2014 `{max}`. */
958
+ maxValue: string;
959
+ /** Template \u2014 `{min}`. */
960
+ minDate: string;
961
+ /** Template \u2014 `{max}`. */
962
+ maxDate: string;
963
+ /** Value is not one of `field.options`. */
964
+ invalidOption: string;
965
+ /** Template \u2014 `{format}`. `field.valueFormatHint` still wins. */
966
+ invalidFormat: string;
967
+ }
968
+
969
+ /** English defaults for {@link FormValidationMessages}. */
970
+ export const DEFAULT_VALIDATION_MESSAGES: FormValidationMessages = {
971
+ required: '{field} is required',
972
+ mustBeChecked: '{field} must be checked',
973
+ minLength: 'Minimum {min} characters required',
974
+ maxLength: 'Maximum {max} characters allowed',
975
+ invalidPattern: 'Invalid format',
976
+ invalidEmail: 'Invalid email address',
977
+ invalidNumber: 'Must be a valid number',
978
+ minValue: 'Minimum value is {min}',
979
+ maxValue: 'Maximum value is {max}',
980
+ minDate: 'Date must be after {min}',
981
+ maxDate: 'Date must be before {max}',
982
+ invalidOption: 'Please select a valid option',
983
+ invalidFormat: 'Invalid format (expected: {format})',
984
+ };
985
+
922
986
  /**
923
987
  * Validate a single form field value against field rules
924
988
  */
925
989
  export function validateFieldValue(
926
990
  value: any,
927
- field: FormFieldParams
991
+ field: FormFieldParams,
992
+ messages?: Partial<FormValidationMessages>
928
993
  ): { valid: boolean; error?: string } {
994
+ const m: FormValidationMessages = { ...DEFAULT_VALIDATION_MESSAGES, ...messages };
995
+ const fieldName = field.label || field.name;
996
+
929
997
  // Required check
930
998
  if (field.required) {
931
999
  if (value === undefined || value === null || value === '') {
932
- return { valid: false, error: `${field.label || field.name} is required` };
1000
+ return { valid: false, error: formatMCPUIString(m.required, { field: fieldName }) };
933
1001
  }
934
1002
  if (field.type === 'checkbox' && value !== true) {
935
- return { valid: false, error: `${field.label || field.name} must be checked` };
1003
+ return { valid: false, error: formatMCPUIString(m.mustBeChecked, { field: fieldName }) };
936
1004
  }
937
1005
  }
938
1006
 
@@ -947,42 +1015,48 @@ export function validateFieldValue(
947
1015
  case 'textarea':
948
1016
  case 'password':
949
1017
  if (field.minLength && String(value).length < field.minLength) {
950
- return { valid: false, error: `Minimum ${field.minLength} characters required` };
1018
+ return {
1019
+ valid: false,
1020
+ error: formatMCPUIString(m.minLength, { min: field.minLength }),
1021
+ };
951
1022
  }
952
1023
  if (field.maxLength && String(value).length > field.maxLength) {
953
- return { valid: false, error: `Maximum ${field.maxLength} characters allowed` };
1024
+ return {
1025
+ valid: false,
1026
+ error: formatMCPUIString(m.maxLength, { max: field.maxLength }),
1027
+ };
954
1028
  }
955
1029
  if (field.pattern && !new RegExp(field.pattern).test(String(value))) {
956
- return { valid: false, error: 'Invalid format' };
1030
+ return { valid: false, error: m.invalidPattern };
957
1031
  }
958
1032
  break;
959
1033
 
960
1034
  case 'email':
961
1035
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))) {
962
- return { valid: false, error: 'Invalid email address' };
1036
+ return { valid: false, error: m.invalidEmail };
963
1037
  }
964
1038
  break;
965
1039
 
966
1040
  case 'number': {
967
1041
  const numValue = Number(value);
968
1042
  if (isNaN(numValue)) {
969
- return { valid: false, error: 'Must be a valid number' };
1043
+ return { valid: false, error: m.invalidNumber };
970
1044
  }
971
1045
  if (field.min !== undefined && numValue < field.min) {
972
- return { valid: false, error: `Minimum value is ${field.min}` };
1046
+ return { valid: false, error: formatMCPUIString(m.minValue, { min: field.min }) };
973
1047
  }
974
1048
  if (field.max !== undefined && numValue > field.max) {
975
- return { valid: false, error: `Maximum value is ${field.max}` };
1049
+ return { valid: false, error: formatMCPUIString(m.maxValue, { max: field.max }) };
976
1050
  }
977
1051
  break;
978
1052
  }
979
1053
 
980
1054
  case 'date':
981
1055
  if (field.minDate && value < field.minDate) {
982
- return { valid: false, error: `Date must be after ${field.minDate}` };
1056
+ return { valid: false, error: formatMCPUIString(m.minDate, { min: field.minDate }) };
983
1057
  }
984
1058
  if (field.maxDate && value > field.maxDate) {
985
- return { valid: false, error: `Date must be before ${field.maxDate}` };
1059
+ return { valid: false, error: formatMCPUIString(m.maxDate, { max: field.maxDate }) };
986
1060
  }
987
1061
  break;
988
1062
 
@@ -992,7 +1066,7 @@ export function validateFieldValue(
992
1066
  if (field.options && field.options.length > 0) {
993
1067
  const validValues = field.options.map((opt) => opt.value);
994
1068
  if (!validValues.includes(String(value))) {
995
- return { valid: false, error: 'Please select a valid option' };
1069
+ return { valid: false, error: m.invalidOption };
996
1070
  }
997
1071
  }
998
1072
  break;
@@ -1005,7 +1079,9 @@ export function validateFieldValue(
1005
1079
  if (!new RegExp(field.valueFormat).test(v)) {
1006
1080
  return {
1007
1081
  valid: false,
1008
- error: field.valueFormatHint || `Invalid format (expected: ${field.valueFormat})`,
1082
+ error:
1083
+ field.valueFormatHint ||
1084
+ formatMCPUIString(m.invalidFormat, { format: field.valueFormat }),
1009
1085
  };
1010
1086
  }
1011
1087
  }
@@ -1019,12 +1095,13 @@ export function validateFieldValue(
1019
1095
  */
1020
1096
  export function validateFormData(
1021
1097
  data: Record<string, any>,
1022
- fields: FormFieldParams[]
1098
+ fields: FormFieldParams[],
1099
+ messages?: Partial<FormValidationMessages>
1023
1100
  ): { valid: boolean; errors: Record<string, string> } {
1024
1101
  const errors: Record<string, string> = {};
1025
1102
 
1026
1103
  for (const field of fields) {
1027
- const result = validateFieldValue(data[field.name], field);
1104
+ const result = validateFieldValue(data[field.name], field, messages);
1028
1105
  if (!result.valid && result.error) {
1029
1106
  errors[field.name] = result.error;
1030
1107
  }
@@ -11,6 +11,63 @@
11
11
  * map/graph.
12
12
  */
13
13
 
14
+ import { formatMCPUIString } from './format-string';
15
+
16
+ /**
17
+ * Column headers and series names of the degraded tables.
18
+ *
19
+ * The projections are pure helpers with no access to `MCPUIStrings`, so the
20
+ * wording is injected: each function takes an optional trailing `labels`
21
+ * partial, and the renderers feed it from `useMCPUIStrings()`. Left out, the
22
+ * English defaults below apply — the pre-v6.20.0 behaviour.
23
+ *
24
+ * @since v6.20.0
25
+ */
26
+ export interface DegradedProjectionLabels {
27
+ /** Graph edge table — source column. */
28
+ source: string;
29
+ /** Graph edge table — target column. */
30
+ target: string;
31
+ /** Graph edge / node table — label column. */
32
+ label: string;
33
+ /** Graph node table — node id column. */
34
+ node: string;
35
+ /** Map table — feature type column. */
36
+ type: string;
37
+ /** Map table — latitude column. */
38
+ lat: string;
39
+ /** Map table — longitude column. */
40
+ lng: string;
41
+ /** Map table — property summary column. */
42
+ info: string;
43
+ /** Map table — `Type` cell of a marker row (v6.20.0, 6th pass). */
44
+ marker: string;
45
+ /** Map table — `Type` cell of a GeoJSON feature without a geometry type (v6.20.0). */
46
+ feature: string;
47
+ /** Chart table — fallback dataset name. Template — `{n}`. */
48
+ series: string;
49
+ }
50
+
51
+ /** English defaults for {@link DegradedProjectionLabels}. */
52
+ export const DEGRADED_PROJECTION_LABELS: DegradedProjectionLabels = {
53
+ source: 'Source',
54
+ target: 'Target',
55
+ label: 'Label',
56
+ node: 'Node',
57
+ type: 'Type',
58
+ lat: 'Lat',
59
+ lng: 'Lng',
60
+ info: 'Info',
61
+ marker: 'marker',
62
+ feature: 'feature',
63
+ series: 'Series {n}',
64
+ };
65
+
66
+ const withLabels = (labels?: Partial<DegradedProjectionLabels>): DegradedProjectionLabels => ({
67
+ ...DEGRADED_PROJECTION_LABELS,
68
+ ...labels,
69
+ });
70
+
14
71
  export interface DegradedTable {
15
72
  columns: string[];
16
73
  rows: Array<Array<string | number>>;
@@ -41,11 +98,12 @@ function cell(value: unknown): string {
41
98
  export function graphToDegradedTable(params: {
42
99
  nodes?: Array<{ id: string; label?: string }>;
43
100
  edges?: Array<{ source: string; target: string; label?: string; weight?: number }>;
44
- }): DegradedTable {
101
+ }, labels?: Partial<DegradedProjectionLabels>): DegradedTable {
102
+ const l = withLabels(labels);
45
103
  const edges = params.edges ?? [];
46
104
  if (edges.length > 0) {
47
105
  return {
48
- columns: ['Source', 'Target', 'Label'],
106
+ columns: [l.source, l.target, l.label],
49
107
  rows: edges.slice(0, MAX_PROJECTED_ROWS).map((e) => {
50
108
  const label = [e.weight != null ? String(e.weight) : '', e.label ?? '']
51
109
  .filter(Boolean)
@@ -56,7 +114,7 @@ export function graphToDegradedTable(params: {
56
114
  }
57
115
  const nodes = params.nodes ?? [];
58
116
  return {
59
- columns: ['Node', 'Label'],
117
+ columns: [l.node, l.label],
60
118
  rows: nodes.slice(0, MAX_PROJECTED_ROWS).map((n) => [cell(n.id), cell(n.label ?? n.id)]),
61
119
  };
62
120
  }
@@ -91,13 +149,14 @@ export function mapToDegradedTable(params: {
91
149
  popup?: string;
92
150
  }>;
93
151
  geojson?: unknown;
94
- }): DegradedTable {
152
+ }, labels?: Partial<DegradedProjectionLabels>): DegradedTable {
153
+ const l = withLabels(labels);
95
154
  const rows: Array<Array<string | number>> = [];
96
155
 
97
156
  for (const m of params.markers ?? []) {
98
157
  const lat = Array.isArray(m.position) ? m.position[0] : m.position?.lat;
99
158
  const lng = Array.isArray(m.position) ? m.position[1] : m.position?.lng;
100
- rows.push(['marker', cell(lat), cell(lng), cell(m.tooltip ?? m.popup ?? '')]);
159
+ rows.push([l.marker, cell(lat), cell(lng), cell(m.tooltip ?? m.popup ?? '')]);
101
160
  }
102
161
 
103
162
  const fc = params.geojson as { features?: GeoJSONLikeFeature[] } | undefined;
@@ -110,7 +169,7 @@ export function mapToDegradedTable(params: {
110
169
  .map((k) => `${k}=${cell(props[k])}`)
111
170
  .join(', ');
112
171
  rows.push([
113
- cell(f.geometry?.type ?? 'feature'),
172
+ cell(f.geometry?.type ?? l.feature),
114
173
  ll ? cell(ll[1]) : '',
115
174
  ll ? cell(ll[0]) : '',
116
175
  propSummary,
@@ -118,7 +177,7 @@ export function mapToDegradedTable(params: {
118
177
  }
119
178
 
120
179
  return {
121
- columns: ['Type', 'Lat', 'Lng', 'Info'],
180
+ columns: [l.type, l.lat, l.lng, l.info],
122
181
  rows: rows.slice(0, MAX_PROJECTED_ROWS),
123
182
  };
124
183
  }
@@ -135,15 +194,19 @@ export function chartToDegradedTable(params: {
135
194
  labels?: Array<string | number>;
136
195
  datasets?: Array<{ label?: string; data?: unknown[] }>;
137
196
  };
138
- }): DegradedTable {
197
+ }, labels?: Partial<DegradedProjectionLabels>): DegradedTable {
198
+ const l = withLabels(labels);
139
199
  const datasets = params.data?.datasets ?? [];
140
- const labels = params.data?.labels ?? [];
141
- const rowCount = Math.max(labels.length, ...datasets.map((d) => d.data?.length ?? 0), 0);
200
+ const dataLabels = params.data?.labels ?? [];
201
+ const rowCount = Math.max(dataLabels.length, ...datasets.map((d) => d.data?.length ?? 0), 0);
142
202
 
143
- const columns = ['', ...datasets.map((d, i) => d.label ?? `Series ${i + 1}`)];
203
+ const columns = [
204
+ '',
205
+ ...datasets.map((d, i) => d.label ?? formatMCPUIString(l.series, { n: i + 1 })),
206
+ ];
144
207
  const rows: Array<Array<string | number>> = [];
145
208
  for (let r = 0; r < Math.min(rowCount, MAX_PROJECTED_ROWS); r++) {
146
- rows.push([cell(labels[r] ?? r + 1), ...datasets.map((d) => cell(d.data?.[r]))]);
209
+ rows.push([cell(dataLabels[r] ?? r + 1), ...datasets.map((d) => cell(d.data?.[r]))]);
147
210
  }
148
211
  return { columns, rows };
149
212
  }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `formatMCPUIString` — the chrome-string template helper, runtime-free.
3
+ *
4
+ * @since v6.20.0 (extracted from `MCPUIStringsContext` so the pure adapters
5
+ * under `src/adapters` can interpolate their own message templates without
6
+ * importing `solid-js`). `MCPUIStringsContext` re-exports it, so every
7
+ * existing import path — including the root barrel — keeps working.
8
+ *
9
+ * This module imports nothing. It must stay that way: `src/adapters/*` is
10
+ * published as a dedicated subpath that never pulls in the renderer.
11
+ */
12
+
13
+ /**
14
+ * Substitutes `{name}` placeholders in a chrome string template.
15
+ *
16
+ * Only the keys present in `vars` are replaced; an unknown placeholder is
17
+ * left verbatim so a mistyped or locale-specific template degrades to
18
+ * visible text rather than to `undefined`.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * formatMCPUIString('Sort by {column}', { column: 'Revenue' }) // 'Sort by Revenue'
23
+ * formatMCPUIString('Export CSV ({count} rows)', { count: 42 }) // 'Export CSV (42 rows)'
24
+ * ```
25
+ */
26
+ export function formatMCPUIString(
27
+ template: string,
28
+ vars: Record<string, string | number>
29
+ ): string {
30
+ return template.replace(/\{(\w+)\}/g, (match, name: string) =>
31
+ Object.prototype.hasOwnProperty.call(vars, name) ? String(vars[name]) : match
32
+ )
33
+ }
package/src/validation.ts CHANGED
@@ -21,7 +21,12 @@ export {
21
21
  validateIframeDomain,
22
22
  sanitizeString,
23
23
  DEFAULT_RESOURCE_LIMITS,
24
+ // v6.20.0 — end-user form validation with injectable wording.
25
+ validateFieldValue,
26
+ validateFormData,
27
+ DEFAULT_VALIDATION_MESSAGES,
24
28
  } from './services/validation'
29
+ export type { FormValidationMessages } from './services/validation'
25
30
 
26
31
  // Re-export types only (no runtime code)
27
32
  export type {