@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.
- package/CHANGELOG.md +519 -0
- package/README.md +791 -1
- package/dist/adapters/connector.cjs +15 -4
- package/dist/adapters/connector.cjs.map +1 -1
- package/dist/adapters/connector.d.ts +38 -0
- package/dist/adapters/connector.d.ts.map +1 -1
- package/dist/adapters/connector.js +15 -4
- package/dist/adapters/connector.js.map +1 -1
- package/dist/adapters/index.d.ts +4 -3
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/macro-run.cjs +25 -13
- package/dist/adapters/macro-run.cjs.map +1 -1
- package/dist/adapters/macro-run.d.ts +37 -2
- package/dist/adapters/macro-run.d.ts.map +1 -1
- package/dist/adapters/macro-run.js +25 -13
- package/dist/adapters/macro-run.js.map +1 -1
- package/dist/adapters.cjs +2 -0
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.js +4 -2
- package/dist/components/ActionGroupRenderer.cjs +3 -1
- package/dist/components/ActionGroupRenderer.cjs.map +1 -1
- package/dist/components/ActionGroupRenderer.d.ts.map +1 -1
- package/dist/components/ActionGroupRenderer.js +3 -1
- package/dist/components/ActionGroupRenderer.js.map +1 -1
- package/dist/components/AgentCard.cjs +11 -8
- package/dist/components/AgentCard.cjs.map +1 -1
- package/dist/components/AgentCard.d.ts.map +1 -1
- package/dist/components/AgentCard.js +11 -8
- package/dist/components/AgentCard.js.map +1 -1
- package/dist/components/AgentHandoff.cjs +6 -1
- package/dist/components/AgentHandoff.cjs.map +1 -1
- package/dist/components/AgentHandoff.d.ts.map +1 -1
- package/dist/components/AgentHandoff.js +6 -1
- package/dist/components/AgentHandoff.js.map +1 -1
- package/dist/components/ArtifactRenderer.cjs +25 -6
- package/dist/components/ArtifactRenderer.cjs.map +1 -1
- package/dist/components/ArtifactRenderer.d.ts.map +1 -1
- package/dist/components/ArtifactRenderer.js +25 -6
- package/dist/components/ArtifactRenderer.js.map +1 -1
- package/dist/components/AutocompleteDropdown.cjs +30 -24
- package/dist/components/AutocompleteDropdown.cjs.map +1 -1
- package/dist/components/AutocompleteDropdown.d.ts.map +1 -1
- package/dist/components/AutocompleteDropdown.js +30 -24
- package/dist/components/AutocompleteDropdown.js.map +1 -1
- package/dist/components/AutocompleteFormField.cjs +3 -1
- package/dist/components/AutocompleteFormField.cjs.map +1 -1
- package/dist/components/AutocompleteFormField.d.ts.map +1 -1
- package/dist/components/AutocompleteFormField.js +4 -2
- package/dist/components/AutocompleteFormField.js.map +1 -1
- package/dist/components/BriefingDiff.cjs +45 -39
- package/dist/components/BriefingDiff.cjs.map +1 -1
- package/dist/components/BriefingDiff.d.ts.map +1 -1
- package/dist/components/BriefingDiff.js +45 -39
- package/dist/components/BriefingDiff.js.map +1 -1
- package/dist/components/CarouselRenderer.cjs +8 -2
- package/dist/components/CarouselRenderer.cjs.map +1 -1
- package/dist/components/CarouselRenderer.d.ts.map +1 -1
- package/dist/components/CarouselRenderer.js +8 -2
- package/dist/components/CarouselRenderer.js.map +1 -1
- package/dist/components/ChartJSRenderer.cjs +60 -36
- package/dist/components/ChartJSRenderer.cjs.map +1 -1
- package/dist/components/ChartJSRenderer.d.ts.map +1 -1
- package/dist/components/ChartJSRenderer.js +61 -37
- package/dist/components/ChartJSRenderer.js.map +1 -1
- package/dist/components/ChatPrompt.cjs +15 -9
- package/dist/components/ChatPrompt.cjs.map +1 -1
- package/dist/components/ChatPrompt.d.ts.map +1 -1
- package/dist/components/ChatPrompt.js +15 -9
- package/dist/components/ChatPrompt.js.map +1 -1
- package/dist/components/CodeBlockRenderer.cjs +34 -16
- package/dist/components/CodeBlockRenderer.cjs.map +1 -1
- package/dist/components/CodeBlockRenderer.d.ts.map +1 -1
- package/dist/components/CodeBlockRenderer.js +34 -16
- package/dist/components/CodeBlockRenderer.js.map +1 -1
- package/dist/components/DataPreviewSection.cjs +91 -44
- package/dist/components/DataPreviewSection.cjs.map +1 -1
- package/dist/components/DataPreviewSection.d.ts.map +1 -1
- package/dist/components/DataPreviewSection.js +92 -45
- package/dist/components/DataPreviewSection.js.map +1 -1
- package/dist/components/DegradedFallback.cjs +20 -16
- package/dist/components/DegradedFallback.cjs.map +1 -1
- package/dist/components/DegradedFallback.d.ts.map +1 -1
- package/dist/components/DegradedFallback.js +20 -16
- package/dist/components/DegradedFallback.js.map +1 -1
- package/dist/components/FooterRenderer.cjs +21 -17
- package/dist/components/FooterRenderer.cjs.map +1 -1
- package/dist/components/FooterRenderer.d.ts.map +1 -1
- package/dist/components/FooterRenderer.js +21 -17
- package/dist/components/FooterRenderer.js.map +1 -1
- package/dist/components/FormFieldRenderer.cjs +116 -93
- package/dist/components/FormFieldRenderer.cjs.map +1 -1
- package/dist/components/FormFieldRenderer.d.ts.map +1 -1
- package/dist/components/FormFieldRenderer.js +116 -93
- package/dist/components/FormFieldRenderer.js.map +1 -1
- package/dist/components/FormRenderer.cjs +55 -31
- package/dist/components/FormRenderer.cjs.map +1 -1
- package/dist/components/FormRenderer.d.ts.map +1 -1
- package/dist/components/FormRenderer.js +55 -31
- package/dist/components/FormRenderer.js.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs +20 -15
- package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.d.ts.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js +20 -15
- package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
- package/dist/components/GraphRenderer.cjs +56 -33
- package/dist/components/GraphRenderer.cjs.map +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +56 -33
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/ImageGalleryRenderer.cjs +13 -4
- package/dist/components/ImageGalleryRenderer.cjs.map +1 -1
- package/dist/components/ImageGalleryRenderer.d.ts.map +1 -1
- package/dist/components/ImageGalleryRenderer.js +13 -4
- package/dist/components/ImageGalleryRenderer.js.map +1 -1
- package/dist/components/LightboxOverlay.cjs +16 -8
- package/dist/components/LightboxOverlay.cjs.map +1 -1
- package/dist/components/LightboxOverlay.d.ts.map +1 -1
- package/dist/components/LightboxOverlay.js +17 -9
- package/dist/components/LightboxOverlay.js.map +1 -1
- package/dist/components/MapRenderer.cjs +59 -29
- package/dist/components/MapRenderer.cjs.map +1 -1
- package/dist/components/MapRenderer.d.ts +10 -2
- package/dist/components/MapRenderer.d.ts.map +1 -1
- package/dist/components/MapRenderer.js +60 -30
- package/dist/components/MapRenderer.js.map +1 -1
- package/dist/components/ModalRenderer.cjs +4 -1
- package/dist/components/ModalRenderer.cjs.map +1 -1
- package/dist/components/ModalRenderer.d.ts.map +1 -1
- package/dist/components/ModalRenderer.js +5 -2
- package/dist/components/ModalRenderer.js.map +1 -1
- package/dist/components/ScratchpadPanel.cjs +578 -535
- package/dist/components/ScratchpadPanel.cjs.map +1 -1
- package/dist/components/ScratchpadPanel.d.ts.map +1 -1
- package/dist/components/ScratchpadPanel.js +578 -535
- package/dist/components/ScratchpadPanel.js.map +1 -1
- package/dist/components/StreamingUIRenderer.cjs +92 -28
- package/dist/components/StreamingUIRenderer.cjs.map +1 -1
- package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
- package/dist/components/StreamingUIRenderer.js +92 -28
- package/dist/components/StreamingUIRenderer.js.map +1 -1
- package/dist/components/UIResourceRenderer.cjs +456 -363
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts +21 -0
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +458 -365
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/VerifiedText.cjs +39 -20
- package/dist/components/VerifiedText.cjs.map +1 -1
- package/dist/components/VerifiedText.d.ts +2 -1
- package/dist/components/VerifiedText.d.ts.map +1 -1
- package/dist/components/VerifiedText.js +39 -20
- package/dist/components/VerifiedText.js.map +1 -1
- package/dist/components/VideoRenderer.cjs +10 -5
- package/dist/components/VideoRenderer.cjs.map +1 -1
- package/dist/components/VideoRenderer.d.ts.map +1 -1
- package/dist/components/VideoRenderer.js +10 -5
- package/dist/components/VideoRenderer.js.map +1 -1
- package/dist/components/chart-data-table.cjs +38 -17
- package/dist/components/chart-data-table.cjs.map +1 -1
- package/dist/components/chart-data-table.d.ts +30 -1
- package/dist/components/chart-data-table.d.ts.map +1 -1
- package/dist/components/chart-data-table.js +38 -17
- package/dist/components/chart-data-table.js.map +1 -1
- package/dist/context/MCPActionContext.cjs +3 -1
- package/dist/context/MCPActionContext.cjs.map +1 -1
- package/dist/context/MCPActionContext.d.ts.map +1 -1
- package/dist/context/MCPActionContext.js +3 -1
- package/dist/context/MCPActionContext.js.map +1 -1
- package/dist/context/MCPUIStringsContext.cjs +285 -2
- package/dist/context/MCPUIStringsContext.cjs.map +1 -1
- package/dist/context/MCPUIStringsContext.d.ts +600 -3
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
- package/dist/context/MCPUIStringsContext.js +285 -2
- package/dist/context/MCPUIStringsContext.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useStreamingUI.cjs +28 -10
- package/dist/hooks/useStreamingUI.cjs.map +1 -1
- package/dist/hooks/useStreamingUI.d.ts +37 -0
- package/dist/hooks/useStreamingUI.d.ts.map +1 -1
- package/dist/hooks/useStreamingUI.js +28 -10
- package/dist/hooks/useStreamingUI.js.map +1 -1
- package/dist/hooks.cjs +1 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.js +2 -1
- package/dist/index.cjs +23 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -3
- package/dist/index.js.map +1 -1
- package/dist/services/validation.cjs +41 -16
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts +47 -2
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +41 -16
- package/dist/services/validation.js.map +1 -1
- package/dist/utils/degraded-projections.cjs +37 -12
- package/dist/utils/degraded-projections.cjs.map +1 -1
- package/dist/utils/degraded-projections.d.ts +39 -3
- package/dist/utils/degraded-projections.d.ts.map +1 -1
- package/dist/utils/degraded-projections.js +37 -12
- package/dist/utils/degraded-projections.js.map +1 -1
- package/dist/utils/format-string.cjs +10 -0
- package/dist/utils/format-string.cjs.map +1 -0
- package/dist/utils/format-string.d.ts +26 -0
- package/dist/utils/format-string.d.ts.map +1 -0
- package/dist/utils/format-string.js +10 -0
- package/dist/utils/format-string.js.map +1 -0
- package/dist/validation.cjs +3 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +2 -1
- package/dist/validation.d.ts +2 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +4 -1
- package/package.json +1 -1
- package/src/adapters/connector.ts +58 -4
- package/src/adapters/index.ts +12 -2
- package/src/adapters/macro-run.ts +71 -11
- package/src/components/ActionGroupRenderer.tsx +3 -1
- package/src/components/AgentCard.tsx +27 -9
- package/src/components/AgentHandoff.tsx +4 -1
- package/src/components/ArtifactRenderer.tsx +17 -5
- package/src/components/AutocompleteDropdown.tsx +8 -6
- package/src/components/AutocompleteFormField.tsx +5 -1
- package/src/components/BriefingDiff.tsx +11 -3
- package/src/components/CarouselRenderer.tsx +4 -2
- package/src/components/ChartJSRenderer.tsx +18 -9
- package/src/components/ChatPrompt.tsx +13 -8
- package/src/components/CodeBlockRenderer.tsx +12 -10
- package/src/components/DataPreviewSection.tsx +92 -26
- package/src/components/DegradedFallback.tsx +4 -2
- package/src/components/FooterRenderer.tsx +3 -1
- package/src/components/FormFieldRenderer.tsx +34 -19
- package/src/components/FormRenderer.tsx +38 -10
- package/src/components/GenerativeUIErrorBoundary.tsx +8 -3
- package/src/components/GraphRenderer.tsx +31 -16
- package/src/components/ImageGalleryRenderer.tsx +8 -4
- package/src/components/LightboxOverlay.tsx +6 -4
- package/src/components/MapRenderer.tsx +57 -20
- package/src/components/ModalRenderer.tsx +3 -1
- package/src/components/ScratchpadPanel.tsx +95 -44
- package/src/components/StreamingUIRenderer.tsx +41 -11
- package/src/components/UIResourceRenderer.tsx +145 -63
- package/src/components/VerifiedText.tsx +19 -9
- package/src/components/VideoRenderer.tsx +6 -4
- package/src/components/chart-data-table.ts +68 -14
- package/src/context/MCPActionContext.tsx +4 -1
- package/src/context/MCPUIStringsContext.tsx +1004 -1
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useStreamingUI.ts +64 -10
- package/src/index.ts +23 -0
- package/src/services/validation.ts +93 -16
- package/src/utils/degraded-projections.ts +75 -12
- package/src/utils/format-string.ts +33 -0
- package/src/validation.ts +5 -0
package/src/hooks/index.ts
CHANGED
|
@@ -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: '
|
|
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:
|
|
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: '
|
|
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:
|
|
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: '
|
|
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: '
|
|
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 || '
|
|
429
|
+
throw new Error(errorData.message || msg('requestFailed'))
|
|
376
430
|
}
|
|
377
431
|
|
|
378
432
|
if (!response.body) {
|
|
379
|
-
throw new Error('
|
|
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: '
|
|
427
|
-
message: err instanceof Error ? err.message : '
|
|
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:
|
|
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:
|
|
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 {
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
1043
|
+
return { valid: false, error: m.invalidNumber };
|
|
970
1044
|
}
|
|
971
1045
|
if (field.min !== undefined && numValue < field.min) {
|
|
972
|
-
return { valid: false, error:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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: [
|
|
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: [
|
|
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([
|
|
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 ??
|
|
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: [
|
|
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
|
|
141
|
-
const rowCount = Math.max(
|
|
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 = [
|
|
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(
|
|
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 {
|