@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
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
type DuplicateMountInfo,
|
|
23
23
|
} from '../utils/duplicate-mount-registry'
|
|
24
24
|
import { useTelemetry } from '../context/MCPUITelemetryContext'
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
DEFAULT_MCPUI_STRINGS,
|
|
27
|
+
formatMCPUIString,
|
|
28
|
+
useMCPUIStrings,
|
|
29
|
+
} from '../context/MCPUIStringsContext'
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
32
|
* How `<UIResourceRenderer>` reacts when `validateComponent()` rejects a
|
|
@@ -65,6 +69,7 @@ import { marked } from 'marked'
|
|
|
65
69
|
* Copy button component with visual feedback
|
|
66
70
|
*/
|
|
67
71
|
function CopyButton(props: { getText: () => string; title?: string; position?: 'top-right' | 'bottom-right' }) {
|
|
72
|
+
const strings = useMCPUIStrings()
|
|
68
73
|
const [copied, setCopied] = createSignal(false)
|
|
69
74
|
|
|
70
75
|
const handleCopy = async () => {
|
|
@@ -87,8 +92,8 @@ function CopyButton(props: { getText: () => string; title?: string; position?: '
|
|
|
87
92
|
<button
|
|
88
93
|
onClick={handleCopy}
|
|
89
94
|
class={`${positionClasses()} opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm z-10`}
|
|
90
|
-
title={props.title ||
|
|
91
|
-
aria-label={props.title ||
|
|
95
|
+
title={props.title || strings.copy}
|
|
96
|
+
aria-label={props.title || strings.copy}
|
|
92
97
|
data-mcp-ui-action="copy"
|
|
93
98
|
type="button"
|
|
94
99
|
>
|
|
@@ -210,6 +215,7 @@ function ChartRenderer(props: {
|
|
|
210
215
|
/** Host opt-in for the external quickchart.io fallback (audit P1.7). */
|
|
211
216
|
allowQuickchartFallback?: boolean
|
|
212
217
|
}) {
|
|
218
|
+
const strings = useMCPUIStrings()
|
|
213
219
|
const [useNative, setUseNative] = createSignal(false)
|
|
214
220
|
const [iframeUrl, setIframeUrl] = createSignal<string>()
|
|
215
221
|
const [isLoading, setIsLoading] = createSignal(true)
|
|
@@ -223,6 +229,11 @@ function ChartRenderer(props: {
|
|
|
223
229
|
const params = () => props.component.params as any
|
|
224
230
|
const rendererPref = () => params()?.renderer || 'auto'
|
|
225
231
|
const allowQuickchart = () => props.allowQuickchartFallback === true
|
|
232
|
+
/** `alt` / `aria-label` of the quickchart image — localized chrome. */
|
|
233
|
+
const chartImageAlt = () =>
|
|
234
|
+
params()?.title
|
|
235
|
+
? formatMCPUIString(strings.chartWithTitleAlt, { title: String(params().title) })
|
|
236
|
+
: strings.chartVisualizationAlt
|
|
226
237
|
|
|
227
238
|
// Emit a clear, observable signal whenever we decline the external fallback.
|
|
228
239
|
const signalBlockedFallback = (reason: string) => {
|
|
@@ -242,7 +253,7 @@ function ChartRenderer(props: {
|
|
|
242
253
|
if (!params()?.data?.datasets) {
|
|
243
254
|
return (
|
|
244
255
|
<div class="p-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
|
|
245
|
-
<p class="text-red-500 dark:text-red-400 text-sm">
|
|
256
|
+
<p class="text-red-500 dark:text-red-400 text-sm">{strings.chartInvalidData}</p>
|
|
246
257
|
</div>
|
|
247
258
|
)
|
|
248
259
|
}
|
|
@@ -270,7 +281,7 @@ function ChartRenderer(props: {
|
|
|
270
281
|
setUseNative(true)
|
|
271
282
|
setIsLoading(false)
|
|
272
283
|
} else {
|
|
273
|
-
setError(
|
|
284
|
+
setError(strings.chartJsUnavailable)
|
|
274
285
|
setIsLoading(false)
|
|
275
286
|
}
|
|
276
287
|
} else {
|
|
@@ -327,9 +338,11 @@ function ChartRenderer(props: {
|
|
|
327
338
|
<Show when={degraded()}>
|
|
328
339
|
<div class="p-3">
|
|
329
340
|
<DegradedFallback
|
|
330
|
-
message=
|
|
331
|
-
caption=
|
|
332
|
-
{...chartToDegradedTable(params() ?? {}
|
|
341
|
+
message={strings.chartIframeUnavailable}
|
|
342
|
+
caption={strings.chartQuickchartCaption}
|
|
343
|
+
{...chartToDegradedTable(params() ?? {}, {
|
|
344
|
+
series: strings.degradedSeries,
|
|
345
|
+
})}
|
|
333
346
|
/>
|
|
334
347
|
</div>
|
|
335
348
|
</Show>
|
|
@@ -343,7 +356,7 @@ function ChartRenderer(props: {
|
|
|
343
356
|
<Show when={error()}>
|
|
344
357
|
<div class="absolute inset-0 flex items-center justify-center p-4">
|
|
345
358
|
<div class="text-center">
|
|
346
|
-
<p class="text-red-600 dark:text-red-400 text-sm font-medium">
|
|
359
|
+
<p class="text-red-600 dark:text-red-400 text-sm font-medium">{strings.chartError}</p>
|
|
347
360
|
<p class="text-gray-600 dark:text-gray-400 text-xs mt-1">{error()}</p>
|
|
348
361
|
</div>
|
|
349
362
|
</div>
|
|
@@ -356,13 +369,13 @@ function ChartRenderer(props: {
|
|
|
356
369
|
{params()?.title}
|
|
357
370
|
</h3>
|
|
358
371
|
</Show>
|
|
359
|
-
<div class="w-full h-full" role="img" aria-label={
|
|
372
|
+
<div class="w-full h-full" role="img" aria-label={chartImageAlt()}>
|
|
360
373
|
<img
|
|
361
374
|
src={iframeUrl()}
|
|
362
|
-
alt={
|
|
375
|
+
alt={chartImageAlt()}
|
|
363
376
|
class="w-full h-auto max-h-[300px] object-contain"
|
|
364
377
|
onError={() => {
|
|
365
|
-
setError(
|
|
378
|
+
setError(strings.chartLoadFailed)
|
|
366
379
|
props.onError?.({
|
|
367
380
|
type: 'render',
|
|
368
381
|
message: 'Chart rendering failed',
|
|
@@ -491,6 +504,27 @@ export interface CitationCtx {
|
|
|
491
504
|
id: number,
|
|
492
505
|
mapping: { page: number | string; file?: string; file_id?: number | string } | undefined
|
|
493
506
|
) => string
|
|
507
|
+
/**
|
|
508
|
+
* Template for the visible placeholder kept when `map` is EMPTY and a
|
|
509
|
+
* marker therefore resolves to nothing — `{id}` is the marker number.
|
|
510
|
+
*
|
|
511
|
+
* Optional: `transformCellCitations` falls back to
|
|
512
|
+
* `DEFAULT_MCPUI_STRINGS.citationUnresolved` (`'[ref. {id}]'`). The
|
|
513
|
+
* renderers populate it from `strings.citationUnresolved`, so a host with a
|
|
514
|
+
* `<MCPUIStringsProvider>` gets its own wording; a direct
|
|
515
|
+
* `renderCellValue(value, ctx)` call may pass it explicitly.
|
|
516
|
+
*
|
|
517
|
+
* @since v6.20.0 — the hardcoded placeholder used to be French.
|
|
518
|
+
*/
|
|
519
|
+
unresolvedLabel?: string
|
|
520
|
+
/**
|
|
521
|
+
* Tooltip template of the default citation chip button — `{label}` is the
|
|
522
|
+
* file name and page. Falls back to `DEFAULT_MCPUI_STRINGS.citationViewSource`;
|
|
523
|
+
* the renderers populate it from `strings.citationViewSource`.
|
|
524
|
+
*
|
|
525
|
+
* @since v6.20.0
|
|
526
|
+
*/
|
|
527
|
+
viewSourceLabel?: string
|
|
494
528
|
}
|
|
495
529
|
|
|
496
530
|
/**
|
|
@@ -502,7 +536,8 @@ export interface CitationCtx {
|
|
|
502
536
|
function defaultCitationChip(
|
|
503
537
|
pageNum: number | string,
|
|
504
538
|
fileName: string,
|
|
505
|
-
verified = true
|
|
539
|
+
verified = true,
|
|
540
|
+
viewSourceLabel: string = DEFAULT_MCPUI_STRINGS.citationViewSource
|
|
506
541
|
): string {
|
|
507
542
|
const safeDocName = encodeURIComponent(fileName || '')
|
|
508
543
|
const label = fileName ? `${fileName} - ${pageNum}` : `${pageNum}`
|
|
@@ -516,7 +551,7 @@ function defaultCitationChip(
|
|
|
516
551
|
` data-citation-page="${pageNum}"`,
|
|
517
552
|
` data-citation-doc="${safeDocName}"`,
|
|
518
553
|
' data-citation-verified="true"',
|
|
519
|
-
` title="
|
|
554
|
+
` title="${escapeHtml(formatMCPUIString(viewSourceLabel, { label }))}">`,
|
|
520
555
|
'<svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>',
|
|
521
556
|
'</button>',
|
|
522
557
|
'</span>',
|
|
@@ -543,12 +578,16 @@ function transformCellCitations(text: string, ctx: CitationCtx): string {
|
|
|
543
578
|
const id = parseInt(idStr, 10)
|
|
544
579
|
const mapping = ctx.map[id] ?? ctx.map[String(id)]
|
|
545
580
|
if (ctx.render) return ctx.render(id, mapping)
|
|
546
|
-
if (mapping) return defaultCitationChip(mapping.page, mapping.file ?? '', true)
|
|
581
|
+
if (mapping) return defaultCitationChip(mapping.page, mapping.file ?? '', true, ctx.viewSourceLabel)
|
|
547
582
|
// Unresolved id: when the map is non-empty (consumer claims to know
|
|
548
583
|
// the citations), drop silently — it's likely an LLM hallucination.
|
|
549
584
|
// When the map is empty (consumer didn't supply one), preserve a
|
|
550
585
|
// human-visible placeholder so the marker isn't lost.
|
|
551
|
-
return Object.keys(ctx.map).length > 0
|
|
586
|
+
return Object.keys(ctx.map).length > 0
|
|
587
|
+
? ''
|
|
588
|
+
: formatMCPUIString(ctx.unresolvedLabel ?? DEFAULT_MCPUI_STRINGS.citationUnresolved, {
|
|
589
|
+
id,
|
|
590
|
+
})
|
|
552
591
|
}
|
|
553
592
|
)
|
|
554
593
|
}
|
|
@@ -670,7 +709,17 @@ function TableRenderer(props: {
|
|
|
670
709
|
// every `renderCellValue` call below. Absent → undefined → cells render
|
|
671
710
|
// as before (regression-safe).
|
|
672
711
|
const citationCtx: CitationCtx | undefined = tableParams.citationMap
|
|
673
|
-
? {
|
|
712
|
+
? {
|
|
713
|
+
map: tableParams.citationMap,
|
|
714
|
+
render: tableParams.citationRender,
|
|
715
|
+
// Getters, not snapshots: cells read the provider's current wording.
|
|
716
|
+
get unresolvedLabel() {
|
|
717
|
+
return strings.citationUnresolved
|
|
718
|
+
},
|
|
719
|
+
get viewSourceLabel() {
|
|
720
|
+
return strings.citationViewSource
|
|
721
|
+
},
|
|
722
|
+
}
|
|
674
723
|
: undefined
|
|
675
724
|
|
|
676
725
|
// ─── Client-side sorting (v4.0.5) ────────────────────────
|
|
@@ -706,7 +755,7 @@ function TableRenderer(props: {
|
|
|
706
755
|
if (isNum) {
|
|
707
756
|
cmp = (Number(va) || 0) - (Number(vb) || 0)
|
|
708
757
|
} else {
|
|
709
|
-
cmp = String(va).localeCompare(String(vb),
|
|
758
|
+
cmp = String(va).localeCompare(String(vb), strings.locale, { sensitivity: 'base' })
|
|
710
759
|
}
|
|
711
760
|
return dir === 'desc' ? -cmp : cmp
|
|
712
761
|
})
|
|
@@ -729,7 +778,7 @@ function TableRenderer(props: {
|
|
|
729
778
|
// context where users scan many tables across messages. Backward-compat
|
|
730
779
|
// for anyone who explicitly disabled.
|
|
731
780
|
const isSearchable = () => tableParams.searchable !== false
|
|
732
|
-
const searchPlaceholder = () => tableParams.searchPlaceholder ||
|
|
781
|
+
const searchPlaceholder = () => tableParams.searchPlaceholder || strings.tableSearchPlaceholder
|
|
733
782
|
|
|
734
783
|
const handleSearch = (value: string) => {
|
|
735
784
|
setSearchQuery(value)
|
|
@@ -800,7 +849,7 @@ function TableRenderer(props: {
|
|
|
800
849
|
for (const n of [10, 30, 60, 100]) {
|
|
801
850
|
if (n < total) opts.push({ value: n, label: String(n) })
|
|
802
851
|
}
|
|
803
|
-
opts.push({ value: 0, label:
|
|
852
|
+
opts.push({ value: 0, label: strings.paginationAllRows })
|
|
804
853
|
return opts
|
|
805
854
|
}
|
|
806
855
|
|
|
@@ -1006,18 +1055,18 @@ function TableRenderer(props: {
|
|
|
1006
1055
|
}
|
|
1007
1056
|
|
|
1008
1057
|
return (
|
|
1009
|
-
<ExpandableWrapper title={tableParams.title ||
|
|
1058
|
+
<ExpandableWrapper title={tableParams.title || strings.tableTitle} copyData={getTableCSV()} copyLabel={strings.tableCopyCsv} toolbarVariant={props.toolbarVariant} onExpandedChange={setSelfExpanded}>
|
|
1010
1059
|
<div class={`relative w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden group ${
|
|
1011
1060
|
isExpanded() ? 'flex-1 min-h-0 flex flex-col' : 'h-full'
|
|
1012
1061
|
} ${tableParams.className || ''}`}>
|
|
1013
|
-
<Show when={exportable} fallback={<CopyButton getText={getTableCSV} title=
|
|
1062
|
+
<Show when={exportable} fallback={<CopyButton getText={getTableCSV} title={strings.tableCopyCsv} position="top-right" />}>
|
|
1014
1063
|
<div class="absolute right-10 top-2 z-10">
|
|
1015
1064
|
<button
|
|
1016
1065
|
ref={exportTriggerRef}
|
|
1017
1066
|
onClick={() => setShowExportMenu(!showExportMenu())}
|
|
1018
1067
|
class="opacity-60 hover:opacity-100 px-2 py-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"
|
|
1019
|
-
title=
|
|
1020
|
-
aria-label=
|
|
1068
|
+
title={strings.tableExport}
|
|
1069
|
+
aria-label={strings.tableExport}
|
|
1021
1070
|
aria-haspopup="menu"
|
|
1022
1071
|
aria-expanded={showExportMenu()}
|
|
1023
1072
|
>
|
|
@@ -1032,13 +1081,13 @@ function TableRenderer(props: {
|
|
|
1032
1081
|
width={144}
|
|
1033
1082
|
>
|
|
1034
1083
|
<Show when={(exportFormats as string[]).includes('tsv')}>
|
|
1035
|
-
<button onClick={() => handleExport('tsv')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">
|
|
1084
|
+
<button onClick={() => handleExport('tsv')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">{strings.tableCopyTsv}</button>
|
|
1036
1085
|
</Show>
|
|
1037
1086
|
<Show when={(exportFormats as string[]).includes('csv')}>
|
|
1038
|
-
<button onClick={() => handleExport('csv')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">
|
|
1087
|
+
<button onClick={() => handleExport('csv')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">{strings.tableDownloadCsv}</button>
|
|
1039
1088
|
</Show>
|
|
1040
1089
|
<Show when={(exportFormats as string[]).includes('json')}>
|
|
1041
|
-
<button onClick={() => handleExport('json')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">
|
|
1090
|
+
<button onClick={() => handleExport('json')} class="w-full text-left px-3 py-1.5 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300">{strings.tableDownloadJson}</button>
|
|
1042
1091
|
</Show>
|
|
1043
1092
|
</PortalDropdownMenu>
|
|
1044
1093
|
</div>
|
|
@@ -1048,7 +1097,9 @@ function TableRenderer(props: {
|
|
|
1048
1097
|
<h3 id={`${tableId}-title`} class="text-sm font-semibold text-gray-900 dark:text-white mb-3 flex-shrink-0">
|
|
1049
1098
|
{tableParams.title}
|
|
1050
1099
|
<Show when={isVirtualizing()}>
|
|
1051
|
-
<span class="ml-2 text-xs font-normal text-gray-400">
|
|
1100
|
+
<span class="ml-2 text-xs font-normal text-gray-400">
|
|
1101
|
+
{formatMCPUIString(strings.tableVirtualizedRows, { count: tableParams.rows?.length ?? 0 })}
|
|
1102
|
+
</span>
|
|
1052
1103
|
</Show>
|
|
1053
1104
|
</h3>
|
|
1054
1105
|
</Show>
|
|
@@ -1069,13 +1120,16 @@ function TableRenderer(props: {
|
|
|
1069
1120
|
type="button"
|
|
1070
1121
|
onClick={() => { handleSearch(''); setSearchQuery(''); setDebouncedQuery('') }}
|
|
1071
1122
|
class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"
|
|
1072
|
-
aria-label=
|
|
1123
|
+
aria-label={strings.tableClearSearch}
|
|
1073
1124
|
>×</button>
|
|
1074
1125
|
</Show>
|
|
1075
1126
|
</div>
|
|
1076
1127
|
<Show when={debouncedQuery() && filteredRows().length !== sortedRows().length}>
|
|
1077
1128
|
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">
|
|
1078
|
-
{
|
|
1129
|
+
{formatMCPUIString(
|
|
1130
|
+
filteredRows().length === 1 ? strings.tableSearchResultsOne : strings.tableSearchResultsMany,
|
|
1131
|
+
{ count: filteredRows().length, total: sortedRows().length }
|
|
1132
|
+
)}
|
|
1079
1133
|
</p>
|
|
1080
1134
|
</Show>
|
|
1081
1135
|
</Show>
|
|
@@ -1106,7 +1160,7 @@ function TableRenderer(props: {
|
|
|
1106
1160
|
})()
|
|
1107
1161
|
}
|
|
1108
1162
|
role="region"
|
|
1109
|
-
aria-label={tableParams.title ||
|
|
1163
|
+
aria-label={tableParams.title || strings.tableAriaLabel}
|
|
1110
1164
|
tabindex="0"
|
|
1111
1165
|
>
|
|
1112
1166
|
<table
|
|
@@ -1122,7 +1176,7 @@ function TableRenderer(props: {
|
|
|
1122
1176
|
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider border-b border-gray-200 dark:border-gray-700 first:pl-6 last:pr-6 bg-gray-100 dark:bg-gray-900 cursor-pointer select-none hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors"
|
|
1123
1177
|
style={column.width ? { width: column.width } : {}}
|
|
1124
1178
|
on:click={() => handleSort(column.key)}
|
|
1125
|
-
title={
|
|
1179
|
+
title={formatMCPUIString(strings.sortBy, { column: column.label })}
|
|
1126
1180
|
>
|
|
1127
1181
|
<span class="inline-flex items-center gap-1">
|
|
1128
1182
|
{column.label}
|
|
@@ -1151,12 +1205,14 @@ function TableRenderer(props: {
|
|
|
1151
1205
|
<Show when={tableParams.pagination}>
|
|
1152
1206
|
<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
1153
1207
|
<span>
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1208
|
+
{formatMCPUIString(strings.tableServerPageRange, {
|
|
1209
|
+
start: tableParams.pagination.currentPage * tableParams.pagination.pageSize + 1,
|
|
1210
|
+
end: Math.min(
|
|
1211
|
+
(tableParams.pagination.currentPage + 1) * tableParams.pagination.pageSize,
|
|
1212
|
+
tableParams.pagination.totalRows
|
|
1213
|
+
),
|
|
1214
|
+
total: tableParams.pagination.totalRows,
|
|
1215
|
+
})}
|
|
1160
1216
|
</span>
|
|
1161
1217
|
</div>
|
|
1162
1218
|
</Show>
|
|
@@ -1165,7 +1221,7 @@ function TableRenderer(props: {
|
|
|
1165
1221
|
<Show when={needsClientPagination()}>
|
|
1166
1222
|
<div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
|
1167
1223
|
<span>
|
|
1168
|
-
{clientRangeStart()}–{clientRangeEnd()} / {filteredRows().length.toLocaleString(
|
|
1224
|
+
{clientRangeStart()}–{clientRangeEnd()} / {filteredRows().length.toLocaleString(strings.locale)}
|
|
1169
1225
|
</span>
|
|
1170
1226
|
<div class="flex items-center gap-2">
|
|
1171
1227
|
<button
|
|
@@ -1202,7 +1258,7 @@ function TableRenderer(props: {
|
|
|
1202
1258
|
{(opt) => <option value={opt.value}>{opt.label}</option>}
|
|
1203
1259
|
</For>
|
|
1204
1260
|
</select>
|
|
1205
|
-
<span class="text-gray-400"
|
|
1261
|
+
<span class="text-gray-400">{strings.perPageSuffix}</span>
|
|
1206
1262
|
</Show>
|
|
1207
1263
|
</div>
|
|
1208
1264
|
</div>
|
|
@@ -1217,6 +1273,7 @@ function TableRenderer(props: {
|
|
|
1217
1273
|
* Render a metric card component
|
|
1218
1274
|
*/
|
|
1219
1275
|
function MetricRenderer(props: { component: UIComponent }) {
|
|
1276
|
+
const strings = useMCPUIStrings()
|
|
1220
1277
|
const metricParams = props.component.params as any
|
|
1221
1278
|
|
|
1222
1279
|
// Generate copyable text for metric
|
|
@@ -1229,7 +1286,7 @@ function MetricRenderer(props: { component: UIComponent }) {
|
|
|
1229
1286
|
|
|
1230
1287
|
return (
|
|
1231
1288
|
<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group">
|
|
1232
|
-
<CopyButton getText={getMetricText} title=
|
|
1289
|
+
<CopyButton getText={getMetricText} title={strings.copyMetric} position="top-right" />
|
|
1233
1290
|
<div class="flex flex-col h-full justify-between">
|
|
1234
1291
|
<div>
|
|
1235
1292
|
<p class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
|
@@ -1277,14 +1334,17 @@ function MetricRenderer(props: { component: UIComponent }) {
|
|
|
1277
1334
|
* Extract image data from markdown image link format
|
|
1278
1335
|
* Pattern: [](link-url)\n*Photo by Author*
|
|
1279
1336
|
*/
|
|
1280
|
-
function extractImageFromMarkdown(
|
|
1337
|
+
function extractImageFromMarkdown(
|
|
1338
|
+
content: string,
|
|
1339
|
+
fallbackAlt: string
|
|
1340
|
+
): { alt: string; imageUrl: string; linkUrl: string; credit: string } | null {
|
|
1281
1341
|
// Pattern: [](link-url) followed by optional credit line
|
|
1282
1342
|
const imagePattern = /\[!\[([^\]]*)\]\(([^)]+)\)\]\(([^)]+)\)\s*\*([^*]+)\*/
|
|
1283
1343
|
const match = content.match(imagePattern)
|
|
1284
1344
|
|
|
1285
1345
|
if (match) {
|
|
1286
1346
|
return {
|
|
1287
|
-
alt: match[1] ||
|
|
1347
|
+
alt: match[1] || fallbackAlt,
|
|
1288
1348
|
imageUrl: match[2],
|
|
1289
1349
|
linkUrl: match[3],
|
|
1290
1350
|
credit: match[4].trim()
|
|
@@ -1298,6 +1358,7 @@ function extractImageFromMarkdown(content: string): { alt: string; imageUrl: str
|
|
|
1298
1358
|
* Render a text component (with optional markdown)
|
|
1299
1359
|
*/
|
|
1300
1360
|
function TextRenderer(props: { component: UIComponent }) {
|
|
1361
|
+
const strings = useMCPUIStrings()
|
|
1301
1362
|
const textParams = props.component.params as any
|
|
1302
1363
|
|
|
1303
1364
|
// Check if this is an image markdown that should be rendered as image
|
|
@@ -1308,7 +1369,7 @@ function TextRenderer(props: { component: UIComponent }) {
|
|
|
1308
1369
|
// markdown path; an unusable link URL only drops the anchor.
|
|
1309
1370
|
const imageData = createMemo(() => {
|
|
1310
1371
|
if (textParams.markdown && textParams.content) {
|
|
1311
|
-
const extracted = extractImageFromMarkdown(textParams.content)
|
|
1372
|
+
const extracted = extractImageFromMarkdown(textParams.content, strings.imageAlt)
|
|
1312
1373
|
if (!extracted) return null
|
|
1313
1374
|
const imageUrl = safeUrl(extracted.imageUrl, { allowDataImage: true })
|
|
1314
1375
|
if (!imageUrl) return null
|
|
@@ -1351,7 +1412,7 @@ function TextRenderer(props: { component: UIComponent }) {
|
|
|
1351
1412
|
when={imageData()}
|
|
1352
1413
|
fallback={
|
|
1353
1414
|
<div class="relative w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 group">
|
|
1354
|
-
<CopyButton getText={getTextContent} title=
|
|
1415
|
+
<CopyButton getText={getTextContent} title={strings.copyText} position="top-right" />
|
|
1355
1416
|
<SafeHtml
|
|
1356
1417
|
class={`prose prose-sm dark:prose-invert max-w-none ${textParams.className || ''}`}
|
|
1357
1418
|
html={htmlContent}
|
|
@@ -1396,6 +1457,7 @@ function TextRenderer(props: { component: UIComponent }) {
|
|
|
1396
1457
|
* Render an iframe component
|
|
1397
1458
|
*/
|
|
1398
1459
|
function IframeRenderer(props: { component: UIComponent }) {
|
|
1460
|
+
const strings = useMCPUIStrings()
|
|
1399
1461
|
const params = props.component.params as any
|
|
1400
1462
|
return (
|
|
1401
1463
|
<div class="w-full h-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col">
|
|
@@ -1406,7 +1468,7 @@ function IframeRenderer(props: { component: UIComponent }) {
|
|
|
1406
1468
|
</Show>
|
|
1407
1469
|
<iframe
|
|
1408
1470
|
src={params.url}
|
|
1409
|
-
title={params.title ||
|
|
1471
|
+
title={params.title || strings.iframeTitle}
|
|
1410
1472
|
class="w-full border-0 flex-1"
|
|
1411
1473
|
style={`height: ${params.height || '400px'}; min-height: 300px;`}
|
|
1412
1474
|
sandbox={getIframeSandbox(params.url)}
|
|
@@ -1420,6 +1482,7 @@ function IframeRenderer(props: { component: UIComponent }) {
|
|
|
1420
1482
|
* Render an image component
|
|
1421
1483
|
*/
|
|
1422
1484
|
function ImageRenderer(props: { component: UIComponent }) {
|
|
1485
|
+
const strings = useMCPUIStrings()
|
|
1423
1486
|
const params = props.component.params as any
|
|
1424
1487
|
|
|
1425
1488
|
return (
|
|
@@ -1430,11 +1493,13 @@ function ImageRenderer(props: { component: UIComponent }) {
|
|
|
1430
1493
|
target="_blank"
|
|
1431
1494
|
rel="noopener noreferrer"
|
|
1432
1495
|
class="cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded"
|
|
1433
|
-
aria-label={
|
|
1496
|
+
aria-label={formatMCPUIString(strings.imageViewFullSize, {
|
|
1497
|
+
alt: params.alt || strings.imageAltFallback,
|
|
1498
|
+
})}
|
|
1434
1499
|
>
|
|
1435
1500
|
<img
|
|
1436
1501
|
src={safeUrl(params.url, { allowDataImage: true })}
|
|
1437
|
-
alt={params.alt ||
|
|
1502
|
+
alt={params.alt || strings.imageAlt}
|
|
1438
1503
|
class="max-w-full max-h-[500px] object-contain rounded shadow-sm hover:opacity-95 transition-opacity"
|
|
1439
1504
|
loading="lazy"
|
|
1440
1505
|
/>
|
|
@@ -1453,6 +1518,7 @@ function ImageRenderer(props: { component: UIComponent }) {
|
|
|
1453
1518
|
* Render a link component
|
|
1454
1519
|
*/
|
|
1455
1520
|
function LinkRenderer(props: { component: UIComponent }) {
|
|
1521
|
+
const strings = useMCPUIStrings()
|
|
1456
1522
|
const params = props.component.params as any
|
|
1457
1523
|
|
|
1458
1524
|
return (
|
|
@@ -1460,7 +1526,10 @@ function LinkRenderer(props: { component: UIComponent }) {
|
|
|
1460
1526
|
href={safeUrl(params.url)}
|
|
1461
1527
|
target="_blank"
|
|
1462
1528
|
rel="noopener noreferrer"
|
|
1463
|
-
aria-label={
|
|
1529
|
+
aria-label={formatMCPUIString(strings.linkOpensInNewTab, {
|
|
1530
|
+
label: params.label || strings.linkLabel,
|
|
1531
|
+
description: params.description || params.url,
|
|
1532
|
+
})}
|
|
1464
1533
|
class={`flex items-center gap-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors group h-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${params.className || ''}`}
|
|
1465
1534
|
onClick={(e) => e.stopPropagation()}
|
|
1466
1535
|
>
|
|
@@ -1525,6 +1594,7 @@ function ComponentRenderer(props: {
|
|
|
1525
1594
|
// mounted above; null-checked at every dispatch site so apps that don't
|
|
1526
1595
|
// opt in see zero behavior change.
|
|
1527
1596
|
const telemetry = useTelemetry()
|
|
1597
|
+
const strings = useMCPUIStrings()
|
|
1528
1598
|
|
|
1529
1599
|
onMount(() => {
|
|
1530
1600
|
markRenderEnd(props.component.id)
|
|
@@ -1590,7 +1660,7 @@ function ComponentRenderer(props: {
|
|
|
1590
1660
|
}
|
|
1591
1661
|
|
|
1592
1662
|
const mode: ValidationErrorMode = props.errorMode ?? 'block'
|
|
1593
|
-
const firstError = validation.errors?.[0]?.message ||
|
|
1663
|
+
const firstError = validation.errors?.[0]?.message || strings.validationUnknownError
|
|
1594
1664
|
|
|
1595
1665
|
// P1.6 — an UNKNOWN component type must never produce a silent blank,
|
|
1596
1666
|
// whatever the errorMode. The renderer has no branch for it, so even
|
|
@@ -1609,7 +1679,7 @@ function ComponentRenderer(props: {
|
|
|
1609
1679
|
<div
|
|
1610
1680
|
class="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 text-xs text-yellow-800 dark:text-yellow-200"
|
|
1611
1681
|
role="alert"
|
|
1612
|
-
aria-label=
|
|
1682
|
+
aria-label={strings.validationWarning}
|
|
1613
1683
|
title={firstError}
|
|
1614
1684
|
>
|
|
1615
1685
|
<svg
|
|
@@ -1627,7 +1697,7 @@ function ComponentRenderer(props: {
|
|
|
1627
1697
|
<line x1="12" y1="9" x2="12" y2="13" />
|
|
1628
1698
|
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
1629
1699
|
</svg>
|
|
1630
|
-
<span>
|
|
1700
|
+
<span>{formatMCPUIString(strings.invalidComponent, { type: props.component.type })}</span>
|
|
1631
1701
|
</div>
|
|
1632
1702
|
)
|
|
1633
1703
|
}
|
|
@@ -1635,7 +1705,7 @@ function ComponentRenderer(props: {
|
|
|
1635
1705
|
// mode === 'block' (default, pre-v5.4.0 behavior)
|
|
1636
1706
|
return (
|
|
1637
1707
|
<div class="w-full h-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
|
|
1638
|
-
<p class="text-sm font-medium text-red-900 dark:text-red-100">
|
|
1708
|
+
<p class="text-sm font-medium text-red-900 dark:text-red-100">{strings.validationError}</p>
|
|
1639
1709
|
<p class="text-xs text-red-700 dark:text-red-300 mt-1">
|
|
1640
1710
|
{firstError}
|
|
1641
1711
|
</p>
|
|
@@ -1737,12 +1807,13 @@ function ComponentRenderer(props: {
|
|
|
1737
1807
|
* 'UNKNOWN_COMPONENT_TYPE'`), so this component stays purely presentational.
|
|
1738
1808
|
*/
|
|
1739
1809
|
function UnsupportedComponentFallback(props: { component: UIComponent }) {
|
|
1810
|
+
const strings = useMCPUIStrings()
|
|
1740
1811
|
return (
|
|
1741
1812
|
<div
|
|
1742
1813
|
role="alert"
|
|
1743
1814
|
class="w-full rounded-lg border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:border-amber-700/50 dark:bg-amber-900/20 dark:text-amber-200"
|
|
1744
1815
|
>
|
|
1745
|
-
|
|
1816
|
+
{strings.unsupportedComponentType} <code class="font-mono">{props.component.type}</code>
|
|
1746
1817
|
</div>
|
|
1747
1818
|
)
|
|
1748
1819
|
}
|
|
@@ -1852,13 +1923,21 @@ function ActionRenderer(props: { component: UIComponent }) {
|
|
|
1852
1923
|
* Handles {error: true, message: "...", tool: "...", suggestions: [...]} format
|
|
1853
1924
|
*/
|
|
1854
1925
|
function ErrorCardRenderer(props: { error: any }) {
|
|
1926
|
+
const strings = useMCPUIStrings()
|
|
1927
|
+
// The timestamp depends on the viewer's time zone, which the server cannot
|
|
1928
|
+
// know. Render it only after mount so SSR and hydration produce the same markup.
|
|
1929
|
+
const [mounted, setMounted] = createSignal(false)
|
|
1930
|
+
onMount(() => setMounted(true))
|
|
1855
1931
|
const getErrorText = () => {
|
|
1856
|
-
return
|
|
1932
|
+
return formatMCPUIString(strings.errorCopyText, {
|
|
1933
|
+
tool: props.error.tool || strings.errorUnknownTool,
|
|
1934
|
+
message: props.error.message || strings.errorUnknown,
|
|
1935
|
+
})
|
|
1857
1936
|
}
|
|
1858
1937
|
|
|
1859
1938
|
return (
|
|
1860
1939
|
<div class="relative w-full bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 group">
|
|
1861
|
-
<CopyButton getText={getErrorText} title=
|
|
1940
|
+
<CopyButton getText={getErrorText} title={strings.copyErrorDetails} position="top-right" />
|
|
1862
1941
|
<div class="flex items-start gap-3">
|
|
1863
1942
|
<div class="p-2 bg-red-100 dark:bg-red-900/40 rounded-full shrink-0">
|
|
1864
1943
|
<svg class="w-5 h-5 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
@@ -1867,19 +1946,21 @@ function ErrorCardRenderer(props: { error: any }) {
|
|
|
1867
1946
|
</div>
|
|
1868
1947
|
<div class="flex-1 min-w-0">
|
|
1869
1948
|
<h4 class="text-sm font-semibold text-red-800 dark:text-red-200">
|
|
1870
|
-
|
|
1949
|
+
{formatMCPUIString(strings.toolErrorTitle, {
|
|
1950
|
+
tool: props.error.tool || strings.errorUnknownToolName,
|
|
1951
|
+
})}
|
|
1871
1952
|
</h4>
|
|
1872
1953
|
<p class="text-sm text-red-700 dark:text-red-300 mt-1">
|
|
1873
|
-
{props.error.message ||
|
|
1954
|
+
{props.error.message || strings.errorToolExecution}
|
|
1874
1955
|
</p>
|
|
1875
1956
|
<Show when={props.error.type}>
|
|
1876
1957
|
<p class="text-xs text-red-600 dark:text-red-400 mt-1">
|
|
1877
|
-
|
|
1958
|
+
{formatMCPUIString(strings.errorTypeLabel, { type: props.error.type })}
|
|
1878
1959
|
</p>
|
|
1879
1960
|
</Show>
|
|
1880
1961
|
<Show when={props.error.suggestions?.length}>
|
|
1881
1962
|
<div class="mt-3">
|
|
1882
|
-
<p class="text-xs font-medium text-red-700 dark:text-red-300">
|
|
1963
|
+
<p class="text-xs font-medium text-red-700 dark:text-red-300">{strings.errorSuggestions}</p>
|
|
1883
1964
|
<ul class="mt-1 text-xs text-red-600 dark:text-red-400 list-disc list-inside">
|
|
1884
1965
|
<For each={props.error.suggestions}>
|
|
1885
1966
|
{(suggestion: string) => <li>{suggestion}</li>}
|
|
@@ -1887,9 +1968,9 @@ function ErrorCardRenderer(props: { error: any }) {
|
|
|
1887
1968
|
</ul>
|
|
1888
1969
|
</div>
|
|
1889
1970
|
</Show>
|
|
1890
|
-
<Show when={props.error.timestamp}>
|
|
1971
|
+
<Show when={mounted() && props.error.timestamp}>
|
|
1891
1972
|
<p class="text-xs text-red-500 dark:text-red-500 mt-2">
|
|
1892
|
-
{new Date(props.error.timestamp).toLocaleString()}
|
|
1973
|
+
{new Date(props.error.timestamp).toLocaleString(strings.locale)}
|
|
1893
1974
|
</p>
|
|
1894
1975
|
</Show>
|
|
1895
1976
|
</div>
|
|
@@ -1922,6 +2003,7 @@ function isUIResource(content: any): boolean {
|
|
|
1922
2003
|
* Handles HTML resources returned by tools like ui_show_dashboard, ui_show_health
|
|
1923
2004
|
*/
|
|
1924
2005
|
function UIResourceHtmlRenderer(props: { resource: any }) {
|
|
2006
|
+
const strings = useMCPUIStrings()
|
|
1925
2007
|
const htmlContent = () => {
|
|
1926
2008
|
if (props.resource.content?.htmlString) {
|
|
1927
2009
|
return sanitizeHtml(String(props.resource.content.htmlString), SANITIZE_PROFILES.resource)
|
|
@@ -1930,7 +2012,7 @@ function UIResourceHtmlRenderer(props: { resource: any }) {
|
|
|
1930
2012
|
}
|
|
1931
2013
|
|
|
1932
2014
|
const resourceTitle = () => {
|
|
1933
|
-
return props.resource.metadata?.title || props.resource.uri?.replace('ui://deposium/', '') ||
|
|
2015
|
+
return props.resource.metadata?.title || props.resource.uri?.replace('ui://deposium/', '') || strings.resourceTitle
|
|
1934
2016
|
}
|
|
1935
2017
|
|
|
1936
2018
|
return (
|