@seed-ship/mcp-ui-solid 6.24.0 → 6.25.1
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 +42 -0
- package/README.md +47 -0
- package/dist/components/ActionGroupRenderer.cjs +6 -6
- package/dist/components/ActionGroupRenderer.cjs.map +1 -1
- package/dist/components/ActionGroupRenderer.js +6 -6
- package/dist/components/ActionGroupRenderer.js.map +1 -1
- package/dist/components/AutocompleteDropdown.cjs +6 -6
- package/dist/components/AutocompleteDropdown.cjs.map +1 -1
- package/dist/components/AutocompleteDropdown.js +6 -6
- package/dist/components/AutocompleteDropdown.js.map +1 -1
- package/dist/components/AutocompleteFormField.cjs +6 -5
- package/dist/components/AutocompleteFormField.cjs.map +1 -1
- package/dist/components/AutocompleteFormField.d.ts.map +1 -1
- package/dist/components/AutocompleteFormField.js +6 -5
- package/dist/components/AutocompleteFormField.js.map +1 -1
- package/dist/components/ChartJSRenderer.cjs +216 -6
- package/dist/components/ChartJSRenderer.cjs.map +1 -1
- package/dist/components/ChartJSRenderer.d.ts.map +1 -1
- package/dist/components/ChartJSRenderer.js +217 -7
- package/dist/components/ChartJSRenderer.js.map +1 -1
- package/dist/components/ChatPrompt.cjs +5 -5
- package/dist/components/ChatPrompt.cjs.map +1 -1
- package/dist/components/ChatPrompt.js +5 -5
- package/dist/components/ChatPrompt.js.map +1 -1
- package/dist/components/ComponentToolbar.cjs +5 -5
- package/dist/components/ComponentToolbar.cjs.map +1 -1
- package/dist/components/ComponentToolbar.js +5 -5
- package/dist/components/ComponentToolbar.js.map +1 -1
- package/dist/components/DegradedFallback.cjs +1 -1
- package/dist/components/DegradedFallback.cjs.map +1 -1
- package/dist/components/DegradedFallback.js +1 -1
- package/dist/components/DegradedFallback.js.map +1 -1
- package/dist/components/ExpandableWrapper.cjs +10 -7
- package/dist/components/ExpandableWrapper.cjs.map +1 -1
- package/dist/components/ExpandableWrapper.d.ts.map +1 -1
- package/dist/components/ExpandableWrapper.js +10 -7
- package/dist/components/ExpandableWrapper.js.map +1 -1
- package/dist/components/FormFieldRenderer.cjs +13 -12
- package/dist/components/FormFieldRenderer.cjs.map +1 -1
- package/dist/components/FormFieldRenderer.d.ts.map +1 -1
- package/dist/components/FormFieldRenderer.js +13 -12
- package/dist/components/FormFieldRenderer.js.map +1 -1
- package/dist/components/FormRenderer.cjs +1 -1
- package/dist/components/FormRenderer.cjs.map +1 -1
- package/dist/components/FormRenderer.js +1 -1
- package/dist/components/FormRenderer.js.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs +1 -1
- package/dist/components/GenerativeUIErrorBoundary.cjs.map +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js +1 -1
- package/dist/components/GenerativeUIErrorBoundary.js.map +1 -1
- package/dist/components/GhostText.cjs +2 -2
- package/dist/components/GhostText.cjs.map +1 -1
- package/dist/components/GhostText.js +2 -2
- package/dist/components/GhostText.js.map +1 -1
- package/dist/components/GraphRenderer.cjs +100 -29
- package/dist/components/GraphRenderer.cjs.map +1 -1
- package/dist/components/GraphRenderer.d.ts +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +100 -29
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/MissingValue.cjs +2 -2
- package/dist/components/MissingValue.cjs.map +1 -1
- package/dist/components/MissingValue.js +2 -2
- package/dist/components/MissingValue.js.map +1 -1
- package/dist/components/ModalRenderer.cjs +39 -31
- package/dist/components/ModalRenderer.cjs.map +1 -1
- package/dist/components/ModalRenderer.d.ts.map +1 -1
- package/dist/components/ModalRenderer.js +40 -32
- package/dist/components/ModalRenderer.js.map +1 -1
- package/dist/components/PortalDropdownMenu.cjs +6 -3
- package/dist/components/PortalDropdownMenu.cjs.map +1 -1
- package/dist/components/PortalDropdownMenu.d.ts.map +1 -1
- package/dist/components/PortalDropdownMenu.js +7 -4
- package/dist/components/PortalDropdownMenu.js.map +1 -1
- package/dist/components/ScratchpadPanel.cjs +38 -44
- package/dist/components/ScratchpadPanel.cjs.map +1 -1
- package/dist/components/ScratchpadPanel.d.ts.map +1 -1
- package/dist/components/ScratchpadPanel.js +38 -44
- package/dist/components/ScratchpadPanel.js.map +1 -1
- package/dist/components/UIResourceRenderer.cjs +20 -20
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.js +20 -20
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/chart-missing-plugin.cjs +4 -4
- package/dist/components/chart-missing-plugin.cjs.map +1 -1
- package/dist/components/chart-missing-plugin.d.ts +3 -1
- package/dist/components/chart-missing-plugin.d.ts.map +1 -1
- package/dist/components/chart-missing-plugin.js +4 -4
- package/dist/components/chart-missing-plugin.js.map +1 -1
- package/dist/utils/math-markdown.cjs +1 -1
- package/dist/utils/math-markdown.cjs.map +1 -1
- package/dist/utils/math-markdown.js +1 -1
- package/dist/utils/math-markdown.js.map +1 -1
- package/dist/utils/theme.cjs +91 -0
- package/dist/utils/theme.cjs.map +1 -0
- package/dist/utils/theme.d.ts +11 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +91 -0
- package/dist/utils/theme.js.map +1 -0
- package/docs/SECURITY-DEPENDENCIES.md +81 -0
- package/docs/THEMING.md +205 -0
- package/package.json +5 -3
- package/src/components/ActionGroupRenderer.tsx +6 -6
- package/src/components/AutocompleteDropdown.tsx +17 -17
- package/src/components/AutocompleteFormField.tsx +10 -9
- package/src/components/ChartJSRenderer.tsx +483 -239
- package/src/components/ChatPrompt.tsx +16 -16
- package/src/components/ComponentToolbar.tsx +5 -5
- package/src/components/DegradedFallback.tsx +9 -9
- package/src/components/ExpandableWrapper.tsx +15 -11
- package/src/components/FormFieldRenderer.tsx +52 -51
- package/src/components/FormRenderer.tsx +12 -12
- package/src/components/GenerativeUIErrorBoundary.tsx +6 -6
- package/src/components/GhostText.tsx +7 -7
- package/src/components/GraphRenderer.tsx +83 -67
- package/src/components/MissingValue.tsx +2 -2
- package/src/components/ModalRenderer.tsx +11 -5
- package/src/components/PortalDropdownMenu.tsx +6 -2
- package/src/components/ScratchpadPanel.tsx +144 -141
- package/src/components/UIResourceRenderer.tsx +101 -101
- package/src/components/chart-missing-plugin.ts +5 -3
- package/src/styles/autocomplete.css +53 -53
- package/src/utils/math-markdown.ts +1 -1
- package/src/utils/theme.ts +101 -0
|
@@ -262,9 +262,9 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
return (
|
|
265
|
-
<div class="w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
|
|
265
|
+
<div class="mcp-ui-root w-full bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))] rounded-lg shadow-[var(--mcp-ui-shadow,var(--shadow-sm))] border border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))] p-4">
|
|
266
266
|
<Show when={params().title}>
|
|
267
|
-
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">
|
|
267
|
+
<h3 class="text-lg font-semibold text-[color:var(--mcp-ui-fg,var(--color-gray-900,#111827))] dark:text-[color:var(--mcp-ui-fg,var(--color-white,#fff))] mb-4">
|
|
268
268
|
{params().title}
|
|
269
269
|
</h3>
|
|
270
270
|
</Show>
|
|
@@ -276,7 +276,7 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
276
276
|
const prefilled = params().fields.filter((f) => f.prefill != null).length
|
|
277
277
|
const total = params().fields.length
|
|
278
278
|
return (
|
|
279
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
|
|
279
|
+
<p class="text-xs text-[color:var(--mcp-ui-fg-muted,var(--color-gray-500,#6b7280))] dark:text-[color:var(--mcp-ui-fg-subtle,var(--color-gray-400,#9ca3af))] mb-3">
|
|
280
280
|
{formatMCPUIString(
|
|
281
281
|
prefilled === 1 ? strings.formPrefilledOne : strings.formPrefilledMany,
|
|
282
282
|
{ count: prefilled, total }
|
|
@@ -301,8 +301,8 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
301
301
|
</div>
|
|
302
302
|
|
|
303
303
|
<Show when={errors()._form}>
|
|
304
|
-
<div class="mt-4 p-3 bg-red-50 dark:bg-red-900
|
|
305
|
-
<p class="text-sm text-red-600 dark:text-red-400" role="alert">
|
|
304
|
+
<div class="mt-4 p-3 bg-[color:var(--mcp-ui-danger-soft,var(--color-red-50))] dark:bg-[color:var(--mcp-ui-danger-soft,color-mix(in_oklab,var(--color-red-900)_20%,transparent))] border border-[color:var(--mcp-ui-danger-border,var(--color-red-200))] dark:border-[color:var(--mcp-ui-danger-border,var(--color-red-800))] rounded-md">
|
|
305
|
+
<p class="text-sm text-[color:var(--mcp-ui-danger,var(--color-red-600))] dark:text-[color:var(--mcp-ui-danger,var(--color-red-400))]" role="alert">
|
|
306
306
|
{errors()._form}
|
|
307
307
|
</p>
|
|
308
308
|
</div>
|
|
@@ -310,8 +310,8 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
310
310
|
|
|
311
311
|
{/* Auto-submit countdown (v4.2.0) */}
|
|
312
312
|
<Show when={countdown() != null}>
|
|
313
|
-
<div class="mt-4 flex items-center gap-3 p-3 bg-blue-50 dark:bg-blue-900
|
|
314
|
-
<span class="text-sm text-blue-700 dark:text-blue-300">
|
|
313
|
+
<div class="mt-4 flex items-center gap-3 p-3 bg-[color:var(--mcp-ui-accent-soft,var(--color-blue-50))] dark:bg-[color:var(--mcp-ui-accent-soft,color-mix(in_oklab,var(--color-blue-900)_20%,transparent))] border border-[color:var(--mcp-ui-accent-border,var(--color-blue-200))] dark:border-[color:var(--mcp-ui-accent-border,var(--color-blue-800))] rounded-md">
|
|
314
|
+
<span class="text-sm text-[color:var(--mcp-ui-accent,var(--color-blue-700))] dark:text-[color:var(--mcp-ui-accent,var(--color-blue-300))]">
|
|
315
315
|
{formatMCPUIString(strings.formSubmitCountdown, {
|
|
316
316
|
label: params().submitLabel || strings.submit,
|
|
317
317
|
seconds: countdown()!,
|
|
@@ -320,22 +320,22 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
320
320
|
<button
|
|
321
321
|
type="button"
|
|
322
322
|
onClick={() => { cancelCountdown(); setUserInteracted(true) }}
|
|
323
|
-
class="text-sm text-blue-600 dark:text-blue-400 underline hover:text-blue-800 dark:hover:text-blue-200"
|
|
323
|
+
class="text-sm text-[color:var(--mcp-ui-accent,var(--color-blue-600))] dark:text-[color:var(--mcp-ui-accent,var(--color-blue-400))] underline hover:text-[color:var(--mcp-ui-accent,var(--color-blue-800))] dark:hover:text-[color:var(--mcp-ui-accent,var(--color-blue-200))]"
|
|
324
324
|
>
|
|
325
325
|
{strings.cancel}
|
|
326
326
|
</button>
|
|
327
327
|
</div>
|
|
328
328
|
</Show>
|
|
329
329
|
|
|
330
|
-
<div class="flex gap-2 pt-4 mt-4 border-t border-gray-200 dark:border-gray-700">
|
|
330
|
+
<div class="flex gap-2 pt-4 mt-4 border-t border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))]">
|
|
331
331
|
<button
|
|
332
332
|
type="submit"
|
|
333
333
|
disabled={isSubmitting()}
|
|
334
|
-
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
334
|
+
class="px-4 py-2 bg-[color:var(--mcp-ui-accent,var(--color-blue-600))] text-[color:var(--mcp-ui-accent-fg,var(--color-white,#fff))] rounded-md hover:bg-[color:var(--mcp-ui-accent-hover,var(--color-blue-700))] focus:outline-none focus:ring-2 focus:ring-[color:var(--mcp-ui-focus,var(--color-blue-500))] focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
335
335
|
>
|
|
336
336
|
{isSubmitting() ? (
|
|
337
337
|
<span class="flex items-center gap-2">
|
|
338
|
-
<span class="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full" />
|
|
338
|
+
<span class="animate-spin h-4 w-4 border-2 border-[color:var(--mcp-ui-border,var(--color-white,#fff))] border-t-transparent rounded-full" />
|
|
339
339
|
{strings.formSubmitting}
|
|
340
340
|
</span>
|
|
341
341
|
) : (
|
|
@@ -347,7 +347,7 @@ export const FormRenderer: Component<FormRendererProps> = (props) => {
|
|
|
347
347
|
type="button"
|
|
348
348
|
onClick={() => initializeForm(true)}
|
|
349
349
|
disabled={isSubmitting()}
|
|
350
|
-
class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
350
|
+
class="px-4 py-2 border border-[color:var(--mcp-ui-border,var(--color-gray-300,#d1d5db))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-600,#4b5563))] text-[color:var(--mcp-ui-fg,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))] rounded-md hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-50,#f9fafb))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] focus:outline-none focus:ring-2 focus:ring-[color:var(--mcp-ui-focus,var(--color-gray-500,#6b7280))] focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
351
351
|
>
|
|
352
352
|
{strings.formReset}
|
|
353
353
|
</button>
|
|
@@ -66,11 +66,11 @@ function DefaultErrorFallback(props: {
|
|
|
66
66
|
}) {
|
|
67
67
|
const strings = useMCPUIStrings()
|
|
68
68
|
return (
|
|
69
|
-
<div class="w-full h-full bg-yellow-50 dark:bg-yellow-900
|
|
69
|
+
<div class="w-full h-full bg-[color:var(--mcp-ui-warning-soft,var(--color-yellow-50))] dark:bg-[color:var(--mcp-ui-warning-soft,color-mix(in_oklab,var(--color-yellow-900)_20%,transparent))] border border-[color:var(--mcp-ui-warning-border,var(--color-yellow-200))] dark:border-[color:var(--mcp-ui-warning-border,var(--color-yellow-800))] rounded-lg p-4">
|
|
70
70
|
<div class="flex items-start gap-3">
|
|
71
71
|
<div class="flex-shrink-0">
|
|
72
72
|
<svg
|
|
73
|
-
class="w-5 h-5 text-yellow-600 dark:text-yellow-400"
|
|
73
|
+
class="w-5 h-5 text-[color:var(--mcp-ui-warning,var(--color-yellow-600))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-400))]"
|
|
74
74
|
fill="none"
|
|
75
75
|
stroke="currentColor"
|
|
76
76
|
viewBox="0 0 24 24"
|
|
@@ -84,24 +84,24 @@ function DefaultErrorFallback(props: {
|
|
|
84
84
|
</svg>
|
|
85
85
|
</div>
|
|
86
86
|
<div class="flex-1 min-w-0">
|
|
87
|
-
<p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">
|
|
87
|
+
<p class="text-sm font-medium text-[color:var(--mcp-ui-warning,var(--color-yellow-900))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-100))]">
|
|
88
88
|
{strings.errorBoundaryTitle}
|
|
89
89
|
</p>
|
|
90
|
-
<p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">
|
|
90
|
+
<p class="text-xs text-[color:var(--mcp-ui-warning,var(--color-yellow-700))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-300))] mt-1">
|
|
91
91
|
{formatMCPUIString(strings.errorBoundaryMeta, {
|
|
92
92
|
type: props.componentType || strings.unknown,
|
|
93
93
|
id: props.componentId?.slice(0, 8) || strings.unknown,
|
|
94
94
|
})}
|
|
95
95
|
</p>
|
|
96
96
|
<Show when={import.meta.env.DEV}>
|
|
97
|
-
<p class="text-xs text-yellow-600 dark:text-yellow-400 mt-2 font-mono">
|
|
97
|
+
<p class="text-xs text-[color:var(--mcp-ui-warning,var(--color-yellow-600))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-400))] mt-2 font-mono">
|
|
98
98
|
{props.error.message}
|
|
99
99
|
</p>
|
|
100
100
|
</Show>
|
|
101
101
|
<Show when={props.allowRetry}>
|
|
102
102
|
<button
|
|
103
103
|
onClick={props.onRetry}
|
|
104
|
-
class="mt-3 text-xs font-medium text-yellow-800 dark:text-yellow-200 hover:text-yellow-900 dark:hover:text-yellow-100 underline"
|
|
104
|
+
class="mt-3 text-xs font-medium text-[color:var(--mcp-ui-warning,var(--color-yellow-800))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-200))] hover:text-[color:var(--mcp-ui-warning,var(--color-yellow-900))] dark:hover:text-[color:var(--mcp-ui-warning,var(--color-yellow-100))] underline"
|
|
105
105
|
>
|
|
106
106
|
{strings.errorBoundaryRetry}
|
|
107
107
|
</button>
|
|
@@ -58,7 +58,7 @@ export const GhostText: Component<GhostTextProps> = (props) => {
|
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<div
|
|
61
|
-
class={`mcp-ghost-text-container ${props.class || ''}`}
|
|
61
|
+
class={`mcp-ui-root mcp-ghost-text-container ${props.class || ''}`}
|
|
62
62
|
style={{
|
|
63
63
|
position: 'relative',
|
|
64
64
|
display: 'inline-block',
|
|
@@ -91,7 +91,7 @@ export const GhostText: Component<GhostTextProps> = (props) => {
|
|
|
91
91
|
<span
|
|
92
92
|
class={`mcp-ghost-text ${props.ghostClass || ''}`}
|
|
93
93
|
style={{
|
|
94
|
-
color: '#9ca3af',
|
|
94
|
+
color: 'var(--mcp-ui-fg-subtle, #9ca3af)',
|
|
95
95
|
opacity: '0.7'
|
|
96
96
|
}}
|
|
97
97
|
>
|
|
@@ -110,8 +110,8 @@ export const GhostText: Component<GhostTextProps> = (props) => {
|
|
|
110
110
|
top: '50%',
|
|
111
111
|
transform: 'translateY(-50%)',
|
|
112
112
|
'font-size': '0.75rem',
|
|
113
|
-
color: '#6b7280',
|
|
114
|
-
'background-color': 'rgba(255, 255, 255, 0.9)',
|
|
113
|
+
color: 'var(--mcp-ui-fg-muted, #6b7280)',
|
|
114
|
+
'background-color': 'var(--mcp-ui-surface, rgba(255, 255, 255, 0.9))',
|
|
115
115
|
padding: '2px 6px',
|
|
116
116
|
'border-radius': '4px',
|
|
117
117
|
'pointer-events': 'none'
|
|
@@ -137,8 +137,8 @@ export const GhostText: Component<GhostTextProps> = (props) => {
|
|
|
137
137
|
display: 'inline-block',
|
|
138
138
|
width: '12px',
|
|
139
139
|
height: '12px',
|
|
140
|
-
border: '2px solid #e5e7eb',
|
|
141
|
-
'border-top-color': '#3b82f6',
|
|
140
|
+
border: '2px solid var(--mcp-ui-border, #e5e7eb)',
|
|
141
|
+
'border-top-color': 'var(--mcp-ui-accent, #3b82f6)',
|
|
142
142
|
'border-radius': '50%',
|
|
143
143
|
animation: 'mcp-spin 0.6s linear infinite'
|
|
144
144
|
}}
|
|
@@ -230,7 +230,7 @@ export interface GhostTextInputProps {
|
|
|
230
230
|
*/
|
|
231
231
|
export const GhostTextInput: Component<GhostTextInputProps> = (props) => {
|
|
232
232
|
return (
|
|
233
|
-
<div class="mcp-ghost-text-input-wrapper" style={{ position: 'relative' }}>
|
|
233
|
+
<div class="mcp-ui-root mcp-ghost-text-input-wrapper" style={{ position: 'relative' }}>
|
|
234
234
|
<GhostText
|
|
235
235
|
inputValue={props.value}
|
|
236
236
|
ghostText={props.ghostText}
|
|
@@ -20,10 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { Component, createSignal, onCleanup, onMount, Show, For } from 'solid-js';
|
|
22
22
|
import type { UIComponent } from '../types';
|
|
23
|
-
import type {
|
|
24
|
-
GraphComponentParams,
|
|
25
|
-
GraphLayout,
|
|
26
|
-
} from '@seed-ship/mcp-ui-spec';
|
|
23
|
+
import type { GraphComponentParams, GraphLayout } from '@seed-ship/mcp-ui-spec';
|
|
27
24
|
import { ExpandableWrapper, useExpanded } from './ExpandableWrapper';
|
|
28
25
|
import { PortalDropdownMenu } from './PortalDropdownMenu';
|
|
29
26
|
import { DegradedFallback } from './DegradedFallback';
|
|
@@ -31,6 +28,40 @@ import { graphToDegradedTable } from '../utils/degraded-projections';
|
|
|
31
28
|
import { escapeHtml } from '../utils/escape-html';
|
|
32
29
|
import { useTelemetry } from '../context/MCPUITelemetryContext';
|
|
33
30
|
import { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';
|
|
31
|
+
import { observeTheme, readThemeTokens } from '../utils/theme';
|
|
32
|
+
|
|
33
|
+
const GRAPH_THEME_TOKENS = [
|
|
34
|
+
...Array.from({ length: 8 }, (_, i) => `--mcp-ui-chart-${i + 1}`),
|
|
35
|
+
'--mcp-ui-chart-grid',
|
|
36
|
+
'--mcp-ui-chart-axis',
|
|
37
|
+
'--mcp-ui-chart-label',
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
/** G6's option mappers outrank datum.style, so explicitly retain the latter. */
|
|
41
|
+
function graphThemeOptions(p: GraphComponentParams, tokens: Record<string, string | undefined>) {
|
|
42
|
+
const nodeIndices = new Map(p.nodes.map((node, index) => [node.id, index]));
|
|
43
|
+
return {
|
|
44
|
+
node: {
|
|
45
|
+
style: (datum: any) => {
|
|
46
|
+
const index = nodeIndices.get(datum.id) ?? 0;
|
|
47
|
+
const fill = tokens[`--mcp-ui-chart-${(index % 8) + 1}`];
|
|
48
|
+
return {
|
|
49
|
+
...(fill ? { fill } : {}),
|
|
50
|
+
...(tokens['--mcp-ui-chart-axis'] ? { stroke: tokens['--mcp-ui-chart-axis'] } : {}),
|
|
51
|
+
...(tokens['--mcp-ui-chart-label'] ? { labelFill: tokens['--mcp-ui-chart-label'] } : {}),
|
|
52
|
+
...datum.style,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
edge: {
|
|
57
|
+
style: (datum: any) => ({
|
|
58
|
+
...(tokens['--mcp-ui-chart-grid'] ? { stroke: tokens['--mcp-ui-chart-grid'] } : {}),
|
|
59
|
+
...(tokens['--mcp-ui-chart-label'] ? { labelFill: tokens['--mcp-ui-chart-label'] } : {}),
|
|
60
|
+
...datum.style,
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
34
65
|
|
|
35
66
|
// Module-scoped lazy import promise — first call triggers the dynamic
|
|
36
67
|
// import, subsequent calls reuse the resolved module.
|
|
@@ -94,7 +125,8 @@ function resolveLayout(params: GraphComponentParams): { type: string; [key: stri
|
|
|
94
125
|
*/
|
|
95
126
|
export function buildGraphConfig(
|
|
96
127
|
p: GraphComponentParams,
|
|
97
|
-
container?: HTMLElement
|
|
128
|
+
container?: HTMLElement,
|
|
129
|
+
tokens: Record<string, string | undefined> = {}
|
|
98
130
|
): Record<string, unknown> {
|
|
99
131
|
const config: Record<string, unknown> = {
|
|
100
132
|
container,
|
|
@@ -102,6 +134,9 @@ export function buildGraphConfig(
|
|
|
102
134
|
layout: resolveLayout(p),
|
|
103
135
|
behaviors: resolveBehaviors(p),
|
|
104
136
|
};
|
|
137
|
+
if (GRAPH_THEME_TOKENS.some((name) => tokens[name])) {
|
|
138
|
+
Object.assign(config, graphThemeOptions(p, tokens));
|
|
139
|
+
}
|
|
105
140
|
|
|
106
141
|
if (p.rendererPref === 'svg') {
|
|
107
142
|
console.warn(
|
|
@@ -122,7 +157,14 @@ export function buildGraphConfig(
|
|
|
122
157
|
if (!item) return '';
|
|
123
158
|
const label = item.label ?? item.id ?? '';
|
|
124
159
|
const data = item.data ? JSON.stringify(item.data) : '';
|
|
125
|
-
|
|
160
|
+
// G6 may position HTML outside our subtree. Resolve effective host
|
|
161
|
+
// values at hover time, rather than relying on portal inheritance.
|
|
162
|
+
const theme = readThemeTokens(container, ['--mcp-ui-surface', '--mcp-ui-fg', '--mcp-ui-border']);
|
|
163
|
+
const styles = ['padding:4px 8px'];
|
|
164
|
+
if (theme['--mcp-ui-surface']) styles.push(`background-color:${theme['--mcp-ui-surface']}`);
|
|
165
|
+
if (theme['--mcp-ui-fg']) styles.push(`color:${theme['--mcp-ui-fg']}`);
|
|
166
|
+
if (theme['--mcp-ui-border']) styles.push(`border:1px solid ${theme['--mcp-ui-border']}`);
|
|
167
|
+
return `<div style="${escapeHtml(styles.join(';'))}"><strong>${escapeHtml(String(label))}</strong>${
|
|
126
168
|
data ? `<br><span style="font-size:11px;opacity:0.7">${escapeHtml(data)}</span>` : ''
|
|
127
169
|
}</div>`;
|
|
128
170
|
},
|
|
@@ -234,70 +276,40 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
234
276
|
// Loosely typed because G6 is a peer-optional — we don't pull its
|
|
235
277
|
// types into the bundle just to type a transient local handle.
|
|
236
278
|
let graphInstance: any | undefined;
|
|
279
|
+
let stopTheme: (() => void) | undefined;
|
|
280
|
+
let disposed = false;
|
|
237
281
|
|
|
238
282
|
onMount(async () => {
|
|
239
283
|
const g6Available = await isG6Available();
|
|
284
|
+
if (disposed) return;
|
|
240
285
|
setAvailable(g6Available);
|
|
241
286
|
if (!g6Available || !containerRef) return;
|
|
242
287
|
|
|
243
288
|
try {
|
|
244
289
|
const { Graph } = await g6ModulePromise!;
|
|
290
|
+
if (disposed || !containerRef) return;
|
|
245
291
|
const p = params();
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
data: { nodes: p.nodes, edges: p.edges ?? [] },
|
|
249
|
-
layout: resolveLayout(p),
|
|
250
|
-
behaviors: resolveBehaviors(p),
|
|
251
|
-
};
|
|
292
|
+
let tokens = readThemeTokens(containerRef, GRAPH_THEME_TOKENS);
|
|
293
|
+
const config = buildGraphConfig(p, containerRef, tokens);
|
|
252
294
|
|
|
253
|
-
// G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string
|
|
254
|
-
// (that was the v4 contract). Passing the string `'canvas'` / `'svg'`
|
|
255
|
-
// makes G6 throw `renderer is not a function` — and because the default
|
|
256
|
-
// path also passed the string `'canvas'`, EVERY graph crashed, not just
|
|
257
|
-
// the svg one.
|
|
258
|
-
//
|
|
259
|
-
// Fix: omit `renderer` entirely. G6 then uses its built-in canvas
|
|
260
|
-
// renderer (documented default `() => new CanvasRenderer()`), which is
|
|
261
|
-
// what we want for every graph.
|
|
262
|
-
//
|
|
263
|
-
// `rendererPref: 'svg'` is NOT wired yet: a real G6 v5 SVG renderer
|
|
264
|
-
// needs the `@antv/g-svg` factory, which is a *transitive* dep of
|
|
265
|
-
// `@antv/g6` (not a declared peer) and isn't statically resolvable at
|
|
266
|
-
// build time. Rather than ship a fragile/unresolvable import, we treat
|
|
267
|
-
// svg as "not yet supported" and fall back to the canvas default with a
|
|
268
|
-
// one-time warning. Tracked for a follow-up that wires the factory
|
|
269
|
-
// behind a proper optional-peer resolution. (Never pass a string here.)
|
|
270
|
-
if (p.rendererPref === 'svg') {
|
|
271
|
-
console.warn(
|
|
272
|
-
'[MCP-UI] GraphRenderer: rendererPref "svg" is not yet supported; using the default canvas renderer.'
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
if (p.fitView !== false) {
|
|
276
|
-
config.autoFit = 'view';
|
|
277
|
-
}
|
|
278
|
-
if (p.tooltip !== false) {
|
|
279
|
-
// Built-in tooltip plugin — shows label + a compact dump of
|
|
280
|
-
// node.data on hover. Consumers can opt out with `tooltip: false`.
|
|
281
|
-
config.plugins = [
|
|
282
|
-
{
|
|
283
|
-
type: 'tooltip',
|
|
284
|
-
getContent: (_evt: unknown, items: any[]) => {
|
|
285
|
-
const item = items?.[0];
|
|
286
|
-
if (!item) return '';
|
|
287
|
-
const label = item.label ?? item.id ?? '';
|
|
288
|
-
const data = item.data ? JSON.stringify(item.data) : '';
|
|
289
|
-
return `<div style="padding:4px 8px"><strong>${escapeHtml(String(label))}</strong>${
|
|
290
|
-
data
|
|
291
|
-
? `<br><span style="font-size:11px;opacity:0.7">${escapeHtml(data)}</span>`
|
|
292
|
-
: ''
|
|
293
|
-
}</div>`;
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
];
|
|
297
|
-
}
|
|
298
295
|
graphInstance = new (Graph as any)(config);
|
|
299
296
|
await graphInstance.render();
|
|
297
|
+
if (disposed) return;
|
|
298
|
+
const syncTheme = () => {
|
|
299
|
+
if (!graphInstance || disposed) return;
|
|
300
|
+
const next = readThemeTokens(containerRef, GRAPH_THEME_TOKENS);
|
|
301
|
+
if (GRAPH_THEME_TOKENS.every((name) => next[name] === tokens[name])) return;
|
|
302
|
+
tokens = next;
|
|
303
|
+
graphInstance.setOptions(graphThemeOptions(p, tokens));
|
|
304
|
+
// draw() updates styles without rerunning force layout or auto-fit.
|
|
305
|
+
void Promise.resolve(graphInstance.draw()).catch(() => {});
|
|
306
|
+
};
|
|
307
|
+
stopTheme = observeTheme(containerRef, syncTheme);
|
|
308
|
+
// Rendering/layout is async: a host may have switched themes while it
|
|
309
|
+
// was in flight, before the observer could be attached.
|
|
310
|
+
syncTheme();
|
|
300
311
|
} catch (err) {
|
|
312
|
+
if (disposed) return;
|
|
301
313
|
const message = err instanceof Error ? err.message : strings.graphRenderError;
|
|
302
314
|
setError(message);
|
|
303
315
|
// Fallback ladder (P2.5): the native G6 render threw — emit telemetry
|
|
@@ -314,6 +326,8 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
314
326
|
});
|
|
315
327
|
|
|
316
328
|
onCleanup(() => {
|
|
329
|
+
disposed = true;
|
|
330
|
+
stopTheme?.();
|
|
317
331
|
try {
|
|
318
332
|
graphInstance?.destroy();
|
|
319
333
|
} catch {
|
|
@@ -365,17 +379,17 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
365
379
|
when={available() === false}
|
|
366
380
|
fallback={
|
|
367
381
|
// Loading skeleton while we determine peer availability
|
|
368
|
-
<div class="w-full p-4 bg-gray-50 dark:bg-gray-800
|
|
369
|
-
<div class="h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2" />
|
|
370
|
-
<div class="h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded" />
|
|
382
|
+
<div class="w-full p-4 bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-50,#f9fafb))] dark:bg-[color:var(--mcp-ui-surface,color-mix(in_oklab,var(--color-gray-800,#1f2937)_40%,transparent))] border border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))] rounded-lg animate-pulse">
|
|
383
|
+
<div class="h-4 w-32 bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-200,#e5e7eb))] dark:bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-700,#374151))] rounded mb-2" />
|
|
384
|
+
<div class="h-3 w-48 bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-200,#e5e7eb))] dark:bg-[color:var(--mcp-ui-surface-muted,var(--color-gray-700,#374151))] rounded" />
|
|
371
385
|
</div>
|
|
372
386
|
}
|
|
373
387
|
>
|
|
374
|
-
<div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900
|
|
375
|
-
<p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">
|
|
388
|
+
<div class="w-full p-4 bg-[color:var(--mcp-ui-warning-soft,var(--color-yellow-50))] dark:bg-[color:var(--mcp-ui-warning-soft,color-mix(in_oklab,var(--color-yellow-900)_20%,transparent))] border border-[color:var(--mcp-ui-warning-border,var(--color-yellow-200))] dark:border-[color:var(--mcp-ui-warning-border,var(--color-yellow-800))] rounded-lg">
|
|
389
|
+
<p class="text-sm font-medium text-[color:var(--mcp-ui-warning,var(--color-yellow-900))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-100))]">
|
|
376
390
|
{strings.graphUnavailable}
|
|
377
391
|
</p>
|
|
378
|
-
<p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">
|
|
392
|
+
<p class="text-xs text-[color:var(--mcp-ui-warning,var(--color-yellow-700))] dark:text-[color:var(--mcp-ui-warning,var(--color-yellow-300))] mt-1">
|
|
379
393
|
Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code>{' '}
|
|
380
394
|
components.
|
|
381
395
|
</p>
|
|
@@ -400,7 +414,7 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
400
414
|
ref={exportTriggerRef}
|
|
401
415
|
type="button"
|
|
402
416
|
onClick={() => setExportMenuOpen((v) => !v)}
|
|
403
|
-
class="px-2 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
|
|
417
|
+
class="px-2 py-1 text-xs bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))] border border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-600,#4b5563))] rounded shadow-[var(--mcp-ui-shadow,var(--shadow-sm))] hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-50,#f9fafb))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] text-[color:var(--mcp-ui-fg,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))]"
|
|
404
418
|
title={strings.graphExport}
|
|
405
419
|
aria-label={strings.graphExport}
|
|
406
420
|
aria-haspopup="menu"
|
|
@@ -438,10 +452,12 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
438
452
|
<button
|
|
439
453
|
type="button"
|
|
440
454
|
onClick={item.onClick}
|
|
441
|
-
class="w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300 border-b border-gray-100 dark:border-gray-700 last:border-b-0"
|
|
455
|
+
class="w-full text-left px-3 py-2 hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-100,#f3f4f6))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] text-[color:var(--mcp-ui-fg,var(--color-gray-700,#374151))] dark:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))] border-b border-[color:var(--mcp-ui-border,var(--color-gray-100,#f3f4f6))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))] last:border-b-0"
|
|
442
456
|
>
|
|
443
457
|
<div class="font-medium">{item.label}</div>
|
|
444
|
-
<div class="text-[10px] text-gray-500 dark:text-gray-400">
|
|
458
|
+
<div class="text-[10px] text-[color:var(--mcp-ui-fg-muted,var(--color-gray-500,#6b7280))] dark:text-[color:var(--mcp-ui-fg-subtle,var(--color-gray-400,#9ca3af))]">
|
|
459
|
+
{item.hint}
|
|
460
|
+
</div>
|
|
445
461
|
</button>
|
|
446
462
|
)}
|
|
447
463
|
</For>
|
|
@@ -451,7 +467,7 @@ export const GraphRenderer: Component<GraphRendererProps> = (props) => {
|
|
|
451
467
|
{/* Native G6 canvas — hidden once a render error degrades us. */}
|
|
452
468
|
<div
|
|
453
469
|
ref={containerRef}
|
|
454
|
-
class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${
|
|
470
|
+
class={`mcp-ui-root bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))] rounded-lg shadow-[var(--mcp-ui-shadow,var(--shadow-sm))] border border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))] overflow-hidden ${
|
|
455
471
|
error() ? 'hidden' : ''
|
|
456
472
|
} ${isExpanded() ? 'flex-1 min-h-0' : ''}`}
|
|
457
473
|
style={
|
|
@@ -38,7 +38,7 @@ export const MissingValueMark: Component = () => {
|
|
|
38
38
|
const strings = useMCPUIStrings()
|
|
39
39
|
return (
|
|
40
40
|
<span data-mcp-missing-value="">
|
|
41
|
-
<span aria-hidden="true" class="text-gray-400 dark:text-gray-500">
|
|
41
|
+
<span aria-hidden="true" class="text-[color:var(--mcp-ui-fg-subtle,var(--color-gray-400,#9ca3af))] dark:text-[color:var(--mcp-ui-fg-muted,var(--color-gray-500,#6b7280))]">
|
|
42
42
|
{MISSING_VALUE_MARKER}
|
|
43
43
|
</span>
|
|
44
44
|
<span style={VISUALLY_HIDDEN_STYLE}>{strings.missingValue}</span>
|
|
@@ -53,7 +53,7 @@ export const MissingValueLegend: Component<{ class?: string }> = (props) => {
|
|
|
53
53
|
<p
|
|
54
54
|
data-mcp-missing-legend=""
|
|
55
55
|
role="note"
|
|
56
|
-
class={`text-xs text-gray-500 dark:text-gray-400 ${props.class ?? ''}`}
|
|
56
|
+
class={`text-xs text-[color:var(--mcp-ui-fg-muted,var(--color-gray-500,#6b7280))] dark:text-[color:var(--mcp-ui-fg-subtle,var(--color-gray-400,#9ca3af))] ${props.class ?? ''}`}
|
|
57
57
|
>
|
|
58
58
|
{formatMCPUIString(strings.missingValueLegend, { marker: MISSING_VALUE_MARKER })}
|
|
59
59
|
</p>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { Component, Show, createSignal, createEffect, onCleanup, JSX } from 'solid-js'
|
|
7
7
|
import { Portal } from 'solid-js/web'
|
|
8
|
+
import { bindPortalTheme } from '../utils/theme'
|
|
8
9
|
import type { UIComponent, ModalComponentParams } from '../types'
|
|
9
10
|
import { useMCPUIStrings } from '../context/MCPUIStringsContext'
|
|
10
11
|
|
|
@@ -75,6 +76,7 @@ export interface ModalRendererProps {
|
|
|
75
76
|
export const ModalRenderer: Component<ModalRendererProps> = (props) => {
|
|
76
77
|
const strings = useMCPUIStrings()
|
|
77
78
|
const [isVisible, setIsVisible] = createSignal(false)
|
|
79
|
+
let themeAnchor: HTMLSpanElement | undefined
|
|
78
80
|
|
|
79
81
|
const params = () => props.params || (props.component?.params as ModalComponentParams) || {}
|
|
80
82
|
|
|
@@ -151,11 +153,14 @@ export const ModalRenderer: Component<ModalRendererProps> = (props) => {
|
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
return (
|
|
156
|
+
<>
|
|
157
|
+
<span hidden aria-hidden="true" ref={themeAnchor} />
|
|
154
158
|
<Show when={isVisible()}>
|
|
155
159
|
<Portal>
|
|
156
160
|
{/* Backdrop */}
|
|
157
161
|
<div
|
|
158
|
-
|
|
162
|
+
ref={(element) => bindPortalTheme(element, () => themeAnchor)}
|
|
163
|
+
class="mcp-ui-root fixed inset-0 z-50 flex items-center justify-center p-4 bg-[color:var(--mcp-ui-overlay,color-mix(in_oklab,var(--color-black,#000)_50%,transparent))] backdrop-blur-sm"
|
|
159
164
|
style={{ animation: 'modal-fade-in 0.15s ease-out' }}
|
|
160
165
|
onClick={handleBackdropClick}
|
|
161
166
|
role="dialog"
|
|
@@ -165,17 +170,17 @@ export const ModalRenderer: Component<ModalRendererProps> = (props) => {
|
|
|
165
170
|
>
|
|
166
171
|
{/* Modal container */}
|
|
167
172
|
<div
|
|
168
|
-
class={`relative w-full ${sizeClass()} bg-white dark:bg-gray-800 rounded-lg shadow-xl`}
|
|
173
|
+
class={`relative w-full ${sizeClass()} bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))] rounded-lg shadow-[var(--mcp-ui-shadow,var(--shadow-xl))]`}
|
|
169
174
|
style={{ animation: 'modal-scale-in 0.15s ease-out' }}
|
|
170
175
|
onClick={(e) => e.stopPropagation()}
|
|
171
176
|
>
|
|
172
177
|
{/* Header */}
|
|
173
178
|
<Show when={params()?.title || params()?.showClose !== false}>
|
|
174
|
-
<div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
|
179
|
+
<div class="flex items-center justify-between p-4 border-b border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-700,#374151))]">
|
|
175
180
|
<Show when={params()?.title}>
|
|
176
181
|
<h2
|
|
177
182
|
id="modal-title"
|
|
178
|
-
class="text-lg font-semibold text-gray-900 dark:text-white"
|
|
183
|
+
class="text-lg font-semibold text-[color:var(--mcp-ui-fg,var(--color-gray-900,#111827))] dark:text-[color:var(--mcp-ui-fg,var(--color-white,#fff))]"
|
|
179
184
|
>
|
|
180
185
|
{params()!.title}
|
|
181
186
|
</h2>
|
|
@@ -186,7 +191,7 @@ export const ModalRenderer: Component<ModalRendererProps> = (props) => {
|
|
|
186
191
|
<Show when={params()?.showClose !== false}>
|
|
187
192
|
<button
|
|
188
193
|
onClick={handleClose}
|
|
189
|
-
class="p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
|
|
194
|
+
class="p-1 text-[color:var(--mcp-ui-fg-subtle,var(--color-gray-400,#9ca3af))] hover:text-[color:var(--mcp-ui-fg-muted,var(--color-gray-600,#4b5563))] dark:hover:text-[color:var(--mcp-ui-fg,var(--color-gray-300,#d1d5db))] rounded-full hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-100,#f3f4f6))] dark:hover:bg-[color:var(--mcp-ui-surface-hover,var(--color-gray-700,#374151))] transition-colors"
|
|
190
195
|
aria-label={strings.modalClose}
|
|
191
196
|
>
|
|
192
197
|
<svg
|
|
@@ -230,5 +235,6 @@ export const ModalRenderer: Component<ModalRendererProps> = (props) => {
|
|
|
230
235
|
`}</style>
|
|
231
236
|
</Portal>
|
|
232
237
|
</Show>
|
|
238
|
+
</>
|
|
233
239
|
)
|
|
234
240
|
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { Component, JSX, Show, createSignal, createEffect, onCleanup } from 'solid-js'
|
|
24
24
|
import { Portal } from 'solid-js/web'
|
|
25
|
+
import { bindPortalTheme } from '../utils/theme'
|
|
25
26
|
|
|
26
27
|
export interface PortalDropdownMenuProps {
|
|
27
28
|
/**
|
|
@@ -111,7 +112,10 @@ export const PortalDropdownMenu: Component<PortalDropdownMenuProps> = (props) =>
|
|
|
111
112
|
<Show when={props.open}>
|
|
112
113
|
<Portal>
|
|
113
114
|
<div
|
|
114
|
-
ref={
|
|
115
|
+
ref={(element) => {
|
|
116
|
+
menuRef = element
|
|
117
|
+
bindPortalTheme(element, () => props.trigger)
|
|
118
|
+
}}
|
|
115
119
|
role="menu"
|
|
116
120
|
data-mcp-ui-portal="menu"
|
|
117
121
|
style={{
|
|
@@ -121,7 +125,7 @@ export const PortalDropdownMenu: Component<PortalDropdownMenuProps> = (props) =>
|
|
|
121
125
|
'z-index': 9999,
|
|
122
126
|
width: `${props.width ?? 144}px`,
|
|
123
127
|
}}
|
|
124
|
-
class={`bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg py-1 text-sm ${props.class ?? ''}`}
|
|
128
|
+
class={`mcp-ui-root bg-[color:var(--mcp-ui-surface,var(--color-white,#fff))] dark:bg-[color:var(--mcp-ui-surface,var(--color-gray-800,#1f2937))] border border-[color:var(--mcp-ui-border,var(--color-gray-200,#e5e7eb))] dark:border-[color:var(--mcp-ui-border,var(--color-gray-600,#4b5563))] rounded-lg shadow-[var(--mcp-ui-shadow,var(--shadow-lg))] py-1 text-sm ${props.class ?? ''}`}
|
|
125
129
|
>
|
|
126
130
|
{props.children}
|
|
127
131
|
</div>
|