@seed-ship/mcp-ui-solid 6.19.1 → 6.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +663 -0
- package/README.md +911 -4
- 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/IframeFallbackLink.cjs +37 -0
- package/dist/components/IframeFallbackLink.cjs.map +1 -0
- package/dist/components/IframeFallbackLink.d.ts +23 -0
- package/dist/components/IframeFallbackLink.d.ts.map +1 -0
- package/dist/components/IframeFallbackLink.js +37 -0
- package/dist/components/IframeFallbackLink.js.map +1 -0
- 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 +481 -365
- 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 +484 -368
- 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 +51 -20
- package/dist/components/VideoRenderer.cjs.map +1 -1
- package/dist/components/VideoRenderer.d.ts.map +1 -1
- package/dist/components/VideoRenderer.js +51 -20
- 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/MCPUIConfigContext.cjs +29 -0
- package/dist/context/MCPUIConfigContext.cjs.map +1 -0
- package/dist/context/MCPUIConfigContext.d.ts +124 -0
- package/dist/context/MCPUIConfigContext.d.ts.map +1 -0
- package/dist/context/MCPUIConfigContext.js +29 -0
- package/dist/context/MCPUIConfigContext.js.map +1 -0
- package/dist/context/MCPUIStringsContext.cjs +287 -2
- package/dist/context/MCPUIStringsContext.cjs.map +1 -1
- package/dist/context/MCPUIStringsContext.d.ts +607 -3
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
- package/dist/context/MCPUIStringsContext.js +287 -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 +29 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/validation.cjs +52 -26
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts +67 -2
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +52 -26
- 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/utils/iframe-coep.cjs +30 -0
- package/dist/utils/iframe-coep.cjs.map +1 -0
- package/dist/utils/iframe-coep.d.ts +46 -0
- package/dist/utils/iframe-coep.d.ts.map +1 -0
- package/dist/utils/iframe-coep.js +30 -0
- package/dist/utils/iframe-coep.js.map +1 -0
- package/dist/validation.cjs +5 -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 +6 -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/IframeFallbackLink.tsx +57 -0
- 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 +176 -67
- package/src/components/VerifiedText.tsx +19 -9
- package/src/components/VideoRenderer.tsx +26 -4
- package/src/components/chart-data-table.ts +68 -14
- package/src/context/MCPActionContext.tsx +4 -1
- package/src/context/MCPUIConfigContext.tsx +151 -0
- package/src/context/MCPUIStringsContext.tsx +1014 -1
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useStreamingUI.ts +64 -10
- package/src/index.ts +38 -0
- package/src/services/index.ts +1 -0
- package/src/services/validation.ts +131 -32
- package/src/solid-jsx-attrs.d.ts +24 -0
- package/src/utils/degraded-projections.ts +75 -12
- package/src/utils/format-string.ts +33 -0
- package/src/utils/iframe-coep.ts +74 -0
- package/src/validation.ts +9 -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,12 +134,26 @@ 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,
|
|
140
142
|
MCPUIStringsProviderProps,
|
|
141
143
|
} from './context/MCPUIStringsContext'
|
|
142
144
|
|
|
145
|
+
// Host rendering policy — iframe `credentialless` / COEP (v6.21.0)
|
|
146
|
+
export {
|
|
147
|
+
MCPUIConfigProvider,
|
|
148
|
+
MCPUIConfigContext,
|
|
149
|
+
useMCPUIConfig,
|
|
150
|
+
DEFAULT_MCPUI_CONFIG,
|
|
151
|
+
} from './context/MCPUIConfigContext'
|
|
152
|
+
export type {
|
|
153
|
+
MCPUIConfig,
|
|
154
|
+
MCPUIConfigProviderProps,
|
|
155
|
+
} from './context/MCPUIConfigContext'
|
|
156
|
+
|
|
143
157
|
// Telemetry sink (B.5 — v5.6.0)
|
|
144
158
|
export {
|
|
145
159
|
MCPUITelemetryProvider,
|
|
@@ -177,6 +191,7 @@ export type { AutocompleteFormFieldProps, AutocompleteFormFieldParams } from './
|
|
|
177
191
|
// Hooks
|
|
178
192
|
export {
|
|
179
193
|
useStreamingUI,
|
|
194
|
+
DEFAULT_STREAMING_UI_MESSAGES,
|
|
180
195
|
useAction,
|
|
181
196
|
useToolAction,
|
|
182
197
|
useConditionalField,
|
|
@@ -195,6 +210,7 @@ export {
|
|
|
195
210
|
|
|
196
211
|
export type {
|
|
197
212
|
UseStreamingUIOptions,
|
|
213
|
+
StreamingUIMessages,
|
|
198
214
|
StreamingUIState,
|
|
199
215
|
StreamProgress,
|
|
200
216
|
StreamError,
|
|
@@ -327,6 +343,9 @@ export {
|
|
|
327
343
|
validateLayout,
|
|
328
344
|
validateIframeDomain,
|
|
329
345
|
getIframeSandbox,
|
|
346
|
+
// v6.21.0 — the trusted-host test behind `getIframeSandbox` and the
|
|
347
|
+
// `credentialless` iframe attribute.
|
|
348
|
+
isTrustedIframeDomain,
|
|
330
349
|
DEFAULT_RESOURCE_LIMITS,
|
|
331
350
|
DEFAULT_IFRAME_DOMAINS,
|
|
332
351
|
TRUSTED_IFRAME_DOMAINS,
|
|
@@ -338,6 +357,25 @@ export {
|
|
|
338
357
|
validateAgainstSource,
|
|
339
358
|
} from './services'
|
|
340
359
|
|
|
360
|
+
// i18n of the pure helpers (v6.20.0) — English baselines a host spreads and
|
|
361
|
+
// overrides when it renders in another language. The helpers themselves take
|
|
362
|
+
// the table as a trailing optional parameter, so nothing else changes.
|
|
363
|
+
export {
|
|
364
|
+
validateFieldValue,
|
|
365
|
+
validateFormData,
|
|
366
|
+
DEFAULT_VALIDATION_MESSAGES,
|
|
367
|
+
} from './services/validation'
|
|
368
|
+
export type { FormValidationMessages } from './services/validation'
|
|
369
|
+
export {
|
|
370
|
+
graphToDegradedTable,
|
|
371
|
+
mapToDegradedTable,
|
|
372
|
+
chartToDegradedTable,
|
|
373
|
+
DEGRADED_PROJECTION_LABELS,
|
|
374
|
+
} from './utils/degraded-projections'
|
|
375
|
+
export type { DegradedProjectionLabels } from './utils/degraded-projections'
|
|
376
|
+
export { chartToDataTable, CHART_DATA_TABLE_LABELS } from './components/chart-data-table'
|
|
377
|
+
export type { ChartDataTableLabels } from './components/chart-data-table'
|
|
378
|
+
|
|
341
379
|
// Clarification → Prompt helper (v4.3.9)
|
|
342
380
|
export { clarificationToPromptConfig } from './services/chat-bus'
|
|
343
381
|
|
package/src/services/index.ts
CHANGED
|
@@ -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
|
|
@@ -686,6 +689,42 @@ export function validateIframeDomain(
|
|
|
686
689
|
}
|
|
687
690
|
}
|
|
688
691
|
|
|
692
|
+
/**
|
|
693
|
+
* Is this URL's host a trusted iframe host?
|
|
694
|
+
*
|
|
695
|
+
* A host matches when it equals an entry of `TRUSTED_IFRAME_DOMAINS` (plus
|
|
696
|
+
* `options.customTrustedDomains`) or is a subdomain of one. Trusted hosts
|
|
697
|
+
* need their own cookies/storage to authenticate, which is why they receive
|
|
698
|
+
* `allow-same-origin` in `getIframeSandbox` and deliberately do NOT receive
|
|
699
|
+
* the `credentialless` iframe attribute (an authenticated embed loaded
|
|
700
|
+
* without its cookies only shows a login screen).
|
|
701
|
+
*
|
|
702
|
+
* An unparsable URL is never trusted.
|
|
703
|
+
*
|
|
704
|
+
* @param url - The iframe URL
|
|
705
|
+
* @param options - Optional custom trusted domains, merged over the defaults
|
|
706
|
+
* @returns `true` when the URL's host is trusted
|
|
707
|
+
* @since v6.21.0 — extracted from `getIframeSandbox`, identical matching.
|
|
708
|
+
*/
|
|
709
|
+
export function isTrustedIframeDomain(
|
|
710
|
+
url: string,
|
|
711
|
+
options?: { customTrustedDomains?: string[] }
|
|
712
|
+
): boolean {
|
|
713
|
+
try {
|
|
714
|
+
const domain = new URL(url).hostname;
|
|
715
|
+
const trustedList = options?.customTrustedDomains
|
|
716
|
+
? [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains]
|
|
717
|
+
: TRUSTED_IFRAME_DOMAINS;
|
|
718
|
+
|
|
719
|
+
return trustedList.some(
|
|
720
|
+
(trusted) => domain === trusted || domain.endsWith(`.${trusted}`)
|
|
721
|
+
);
|
|
722
|
+
} catch {
|
|
723
|
+
// Invalid URL — never trusted.
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
689
728
|
/**
|
|
690
729
|
* Get the appropriate sandbox attribute for an iframe URL.
|
|
691
730
|
*
|
|
@@ -704,22 +743,8 @@ export function getIframeSandbox(
|
|
|
704
743
|
): string {
|
|
705
744
|
const baseSandbox = 'allow-scripts allow-popups';
|
|
706
745
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
let trustedList = TRUSTED_IFRAME_DOMAINS;
|
|
710
|
-
if (options?.customTrustedDomains) {
|
|
711
|
-
trustedList = [...TRUSTED_IFRAME_DOMAINS, ...options.customTrustedDomains];
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
const isTrusted = trustedList.some(
|
|
715
|
-
(trusted) => domain === trusted || domain.endsWith(`.${trusted}`)
|
|
716
|
-
);
|
|
717
|
-
|
|
718
|
-
if (isTrusted) {
|
|
719
|
-
return `${baseSandbox} allow-same-origin allow-forms`;
|
|
720
|
-
}
|
|
721
|
-
} catch {
|
|
722
|
-
// Invalid URL — use restrictive sandbox
|
|
746
|
+
if (isTrustedIframeDomain(url, options)) {
|
|
747
|
+
return `${baseSandbox} allow-same-origin allow-forms`;
|
|
723
748
|
}
|
|
724
749
|
|
|
725
750
|
return baseSandbox;
|
|
@@ -919,20 +944,85 @@ export function validateLayout(layout: UILayout, options?: ValidationOptions): V
|
|
|
919
944
|
};
|
|
920
945
|
}
|
|
921
946
|
|
|
947
|
+
/**
|
|
948
|
+
* The end-user form-validation wording.
|
|
949
|
+
*
|
|
950
|
+
* `validateFieldValue` / `validateFormData` are pure and SSR-safe (they are
|
|
951
|
+
* re-exported from the `@seed-ship/mcp-ui-solid/validation` subpath), so
|
|
952
|
+
* they cannot read `MCPUIStrings`. The wording is injected instead: both
|
|
953
|
+
* take an optional trailing `messages` partial, and `FormRenderer` feeds it
|
|
954
|
+
* from `useMCPUIStrings()` (keys `fieldRequired`, `fieldMinLength`, …).
|
|
955
|
+
*
|
|
956
|
+
* The STRUCTURAL validators (`validateComponent`, `validateLayout`, …) keep
|
|
957
|
+
* their English `ValidationError.message`: those are payload diagnostics
|
|
958
|
+
* addressed to whoever produced the payload, not end-user chrome.
|
|
959
|
+
*
|
|
960
|
+
* @since v6.20.0
|
|
961
|
+
*/
|
|
962
|
+
export interface FormValidationMessages {
|
|
963
|
+
/** Template \u2014 `{field}`. */
|
|
964
|
+
required: string;
|
|
965
|
+
/** Template \u2014 `{field}`. */
|
|
966
|
+
mustBeChecked: string;
|
|
967
|
+
/** Template \u2014 `{min}`. */
|
|
968
|
+
minLength: string;
|
|
969
|
+
/** Template \u2014 `{max}`. */
|
|
970
|
+
maxLength: string;
|
|
971
|
+
/** Value does not match `field.pattern`. */
|
|
972
|
+
invalidPattern: string;
|
|
973
|
+
/** Value is not a valid email address. */
|
|
974
|
+
invalidEmail: string;
|
|
975
|
+
/** Value is not a number. */
|
|
976
|
+
invalidNumber: string;
|
|
977
|
+
/** Template \u2014 `{min}`. */
|
|
978
|
+
minValue: string;
|
|
979
|
+
/** Template \u2014 `{max}`. */
|
|
980
|
+
maxValue: string;
|
|
981
|
+
/** Template \u2014 `{min}`. */
|
|
982
|
+
minDate: string;
|
|
983
|
+
/** Template \u2014 `{max}`. */
|
|
984
|
+
maxDate: string;
|
|
985
|
+
/** Value is not one of `field.options`. */
|
|
986
|
+
invalidOption: string;
|
|
987
|
+
/** Template \u2014 `{format}`. `field.valueFormatHint` still wins. */
|
|
988
|
+
invalidFormat: string;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/** English defaults for {@link FormValidationMessages}. */
|
|
992
|
+
export const DEFAULT_VALIDATION_MESSAGES: FormValidationMessages = {
|
|
993
|
+
required: '{field} is required',
|
|
994
|
+
mustBeChecked: '{field} must be checked',
|
|
995
|
+
minLength: 'Minimum {min} characters required',
|
|
996
|
+
maxLength: 'Maximum {max} characters allowed',
|
|
997
|
+
invalidPattern: 'Invalid format',
|
|
998
|
+
invalidEmail: 'Invalid email address',
|
|
999
|
+
invalidNumber: 'Must be a valid number',
|
|
1000
|
+
minValue: 'Minimum value is {min}',
|
|
1001
|
+
maxValue: 'Maximum value is {max}',
|
|
1002
|
+
minDate: 'Date must be after {min}',
|
|
1003
|
+
maxDate: 'Date must be before {max}',
|
|
1004
|
+
invalidOption: 'Please select a valid option',
|
|
1005
|
+
invalidFormat: 'Invalid format (expected: {format})',
|
|
1006
|
+
};
|
|
1007
|
+
|
|
922
1008
|
/**
|
|
923
1009
|
* Validate a single form field value against field rules
|
|
924
1010
|
*/
|
|
925
1011
|
export function validateFieldValue(
|
|
926
1012
|
value: any,
|
|
927
|
-
field: FormFieldParams
|
|
1013
|
+
field: FormFieldParams,
|
|
1014
|
+
messages?: Partial<FormValidationMessages>
|
|
928
1015
|
): { valid: boolean; error?: string } {
|
|
1016
|
+
const m: FormValidationMessages = { ...DEFAULT_VALIDATION_MESSAGES, ...messages };
|
|
1017
|
+
const fieldName = field.label || field.name;
|
|
1018
|
+
|
|
929
1019
|
// Required check
|
|
930
1020
|
if (field.required) {
|
|
931
1021
|
if (value === undefined || value === null || value === '') {
|
|
932
|
-
return { valid: false, error:
|
|
1022
|
+
return { valid: false, error: formatMCPUIString(m.required, { field: fieldName }) };
|
|
933
1023
|
}
|
|
934
1024
|
if (field.type === 'checkbox' && value !== true) {
|
|
935
|
-
return { valid: false, error:
|
|
1025
|
+
return { valid: false, error: formatMCPUIString(m.mustBeChecked, { field: fieldName }) };
|
|
936
1026
|
}
|
|
937
1027
|
}
|
|
938
1028
|
|
|
@@ -947,42 +1037,48 @@ export function validateFieldValue(
|
|
|
947
1037
|
case 'textarea':
|
|
948
1038
|
case 'password':
|
|
949
1039
|
if (field.minLength && String(value).length < field.minLength) {
|
|
950
|
-
return {
|
|
1040
|
+
return {
|
|
1041
|
+
valid: false,
|
|
1042
|
+
error: formatMCPUIString(m.minLength, { min: field.minLength }),
|
|
1043
|
+
};
|
|
951
1044
|
}
|
|
952
1045
|
if (field.maxLength && String(value).length > field.maxLength) {
|
|
953
|
-
return {
|
|
1046
|
+
return {
|
|
1047
|
+
valid: false,
|
|
1048
|
+
error: formatMCPUIString(m.maxLength, { max: field.maxLength }),
|
|
1049
|
+
};
|
|
954
1050
|
}
|
|
955
1051
|
if (field.pattern && !new RegExp(field.pattern).test(String(value))) {
|
|
956
|
-
return { valid: false, error:
|
|
1052
|
+
return { valid: false, error: m.invalidPattern };
|
|
957
1053
|
}
|
|
958
1054
|
break;
|
|
959
1055
|
|
|
960
1056
|
case 'email':
|
|
961
1057
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))) {
|
|
962
|
-
return { valid: false, error:
|
|
1058
|
+
return { valid: false, error: m.invalidEmail };
|
|
963
1059
|
}
|
|
964
1060
|
break;
|
|
965
1061
|
|
|
966
1062
|
case 'number': {
|
|
967
1063
|
const numValue = Number(value);
|
|
968
1064
|
if (isNaN(numValue)) {
|
|
969
|
-
return { valid: false, error:
|
|
1065
|
+
return { valid: false, error: m.invalidNumber };
|
|
970
1066
|
}
|
|
971
1067
|
if (field.min !== undefined && numValue < field.min) {
|
|
972
|
-
return { valid: false, error:
|
|
1068
|
+
return { valid: false, error: formatMCPUIString(m.minValue, { min: field.min }) };
|
|
973
1069
|
}
|
|
974
1070
|
if (field.max !== undefined && numValue > field.max) {
|
|
975
|
-
return { valid: false, error:
|
|
1071
|
+
return { valid: false, error: formatMCPUIString(m.maxValue, { max: field.max }) };
|
|
976
1072
|
}
|
|
977
1073
|
break;
|
|
978
1074
|
}
|
|
979
1075
|
|
|
980
1076
|
case 'date':
|
|
981
1077
|
if (field.minDate && value < field.minDate) {
|
|
982
|
-
return { valid: false, error:
|
|
1078
|
+
return { valid: false, error: formatMCPUIString(m.minDate, { min: field.minDate }) };
|
|
983
1079
|
}
|
|
984
1080
|
if (field.maxDate && value > field.maxDate) {
|
|
985
|
-
return { valid: false, error:
|
|
1081
|
+
return { valid: false, error: formatMCPUIString(m.maxDate, { max: field.maxDate }) };
|
|
986
1082
|
}
|
|
987
1083
|
break;
|
|
988
1084
|
|
|
@@ -992,7 +1088,7 @@ export function validateFieldValue(
|
|
|
992
1088
|
if (field.options && field.options.length > 0) {
|
|
993
1089
|
const validValues = field.options.map((opt) => opt.value);
|
|
994
1090
|
if (!validValues.includes(String(value))) {
|
|
995
|
-
return { valid: false, error:
|
|
1091
|
+
return { valid: false, error: m.invalidOption };
|
|
996
1092
|
}
|
|
997
1093
|
}
|
|
998
1094
|
break;
|
|
@@ -1005,7 +1101,9 @@ export function validateFieldValue(
|
|
|
1005
1101
|
if (!new RegExp(field.valueFormat).test(v)) {
|
|
1006
1102
|
return {
|
|
1007
1103
|
valid: false,
|
|
1008
|
-
error:
|
|
1104
|
+
error:
|
|
1105
|
+
field.valueFormatHint ||
|
|
1106
|
+
formatMCPUIString(m.invalidFormat, { format: field.valueFormat }),
|
|
1009
1107
|
};
|
|
1010
1108
|
}
|
|
1011
1109
|
}
|
|
@@ -1019,12 +1117,13 @@ export function validateFieldValue(
|
|
|
1019
1117
|
*/
|
|
1020
1118
|
export function validateFormData(
|
|
1021
1119
|
data: Record<string, any>,
|
|
1022
|
-
fields: FormFieldParams[]
|
|
1120
|
+
fields: FormFieldParams[],
|
|
1121
|
+
messages?: Partial<FormValidationMessages>
|
|
1023
1122
|
): { valid: boolean; errors: Record<string, string> } {
|
|
1024
1123
|
const errors: Record<string, string> = {};
|
|
1025
1124
|
|
|
1026
1125
|
for (const field of fields) {
|
|
1027
|
-
const result = validateFieldValue(data[field.name], field);
|
|
1126
|
+
const result = validateFieldValue(data[field.name], field, messages);
|
|
1028
1127
|
if (!result.valid && result.error) {
|
|
1029
1128
|
errors[field.name] = result.error;
|
|
1030
1129
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solid JSX augmentation — `attr:credentialless` (v6.21.0).
|
|
3
|
+
*
|
|
4
|
+
* `credentialless` is listed in dom-expressions' `Properties` set, so the
|
|
5
|
+
* plain `credentialless={…}` form compiles to a JS property assignment
|
|
6
|
+
* (`el.credentialless = v`) and never reaches the DOM as an attribute in an
|
|
7
|
+
* engine that does not reflect it (jsdom, and any pre-110 browser). The
|
|
8
|
+
* `attr:` namespace forces `setAttribute` / `ssrAttribute` instead.
|
|
9
|
+
*
|
|
10
|
+
* Solid types `attr:*` only for keys declared in `JSX.ExplicitAttributes`,
|
|
11
|
+
* which ships empty — hence this augmentation. The value type is
|
|
12
|
+
* `true | undefined` on purpose: `undefined` removes the attribute, so the
|
|
13
|
+
* boolean attribute is never emitted as `credentialless="false"`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import 'solid-js'
|
|
17
|
+
|
|
18
|
+
declare module 'solid-js' {
|
|
19
|
+
namespace JSX {
|
|
20
|
+
interface ExplicitAttributes {
|
|
21
|
+
credentialless: true | undefined
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -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
|
}
|