@seed-ship/mcp-ui-solid 6.23.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 +167 -0
- package/README.md +154 -2
- 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 +406 -70
- package/dist/components/ChartJSRenderer.cjs.map +1 -1
- package/dist/components/ChartJSRenderer.d.ts.map +1 -1
- package/dist/components/ChartJSRenderer.js +407 -71
- 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 +35 -3
- package/dist/components/DegradedFallback.cjs.map +1 -1
- package/dist/components/DegradedFallback.d.ts +7 -0
- package/dist/components/DegradedFallback.d.ts.map +1 -1
- package/dist/components/DegradedFallback.js +35 -3
- 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 +43 -0
- package/dist/components/MissingValue.cjs.map +1 -0
- package/dist/components/MissingValue.d.ts +27 -0
- package/dist/components/MissingValue.d.ts.map +1 -0
- package/dist/components/MissingValue.js +43 -0
- package/dist/components/MissingValue.js.map +1 -0
- 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 +255 -217
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +255 -217
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/chart-data-table.cjs +30 -5
- package/dist/components/chart-data-table.cjs.map +1 -1
- package/dist/components/chart-data-table.d.ts +18 -1
- package/dist/components/chart-data-table.d.ts.map +1 -1
- package/dist/components/chart-data-table.js +30 -5
- package/dist/components/chart-data-table.js.map +1 -1
- package/dist/components/chart-missing-plugin.cjs +194 -0
- package/dist/components/chart-missing-plugin.cjs.map +1 -0
- package/dist/components/chart-missing-plugin.d.ts +129 -0
- package/dist/components/chart-missing-plugin.d.ts.map +1 -0
- package/dist/components/chart-missing-plugin.js +194 -0
- package/dist/components/chart-missing-plugin.js.map +1 -0
- package/dist/context/MCPUIStringsContext.cjs +12 -1
- package/dist/context/MCPUIStringsContext.cjs.map +1 -1
- package/dist/context/MCPUIStringsContext.d.ts +62 -0
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -1
- package/dist/context/MCPUIStringsContext.js +12 -1
- package/dist/context/MCPUIStringsContext.js.map +1 -1
- package/dist/services/component-registry.cjs +9 -2
- package/dist/services/component-registry.cjs.map +1 -1
- package/dist/services/component-registry.d.ts.map +1 -1
- package/dist/services/component-registry.js +9 -2
- package/dist/services/component-registry.js.map +1 -1
- package/dist/services/validation.cjs +31 -8
- package/dist/services/validation.cjs.map +1 -1
- package/dist/services/validation.d.ts.map +1 -1
- package/dist/services/validation.js +31 -8
- package/dist/services/validation.js.map +1 -1
- package/dist/types/index.d.ts +14 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/degraded-projections.cjs +19 -3
- package/dist/utils/degraded-projections.cjs.map +1 -1
- package/dist/utils/degraded-projections.d.ts +22 -2
- package/dist/utils/degraded-projections.d.ts.map +1 -1
- package/dist/utils/degraded-projections.js +19 -3
- package/dist/utils/degraded-projections.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/missing-value.cjs +60 -0
- package/dist/utils/missing-value.cjs.map +1 -0
- package/dist/utils/missing-value.d.ts +79 -0
- package/dist/utils/missing-value.d.ts.map +1 -0
- package/dist/utils/missing-value.js +60 -0
- package/dist/utils/missing-value.js.map +1 -0
- 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 +7 -5
- 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 +609 -212
- package/src/components/ChatPrompt.tsx +16 -16
- package/src/components/ComponentToolbar.tsx +5 -5
- package/src/components/DegradedFallback.tsx +33 -11
- 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 +61 -0
- 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 +143 -110
- package/src/components/chart-data-table.ts +50 -6
- package/src/components/chart-missing-plugin.ts +336 -0
- package/src/context/MCPUIStringsContext.tsx +76 -0
- package/src/services/component-registry.ts +10 -2
- package/src/services/validation.ts +44 -9
- package/src/styles/autocomplete.css +53 -53
- package/src/types/index.ts +13 -1
- package/src/utils/degraded-projections.ts +44 -4
- package/src/utils/math-markdown.ts +1 -1
- package/src/utils/missing-value.ts +127 -0
- package/src/utils/theme.ts +101 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphRenderer.js","sources":["../../src/components/GraphRenderer.tsx"],"sourcesContent":["/**\n * GraphRenderer (v6.0.0) — generic node-link visualization powered by\n * `@antv/g6 ^5` (peer-optional). Same lazy-load pattern as\n * `ChartJSRenderer` and `MapRenderer` : the heavy lib is dynamically\n * imported only on first mount, and apps that don't install the peer\n * see an informative fallback instead of a crash.\n *\n * Spec : `@seed-ship/mcp-ui-spec@5.0.4` exports `GraphComponentParamsSchema`,\n * `GraphNode`, `GraphEdge`, `GraphLayoutName`, `GraphLayout`,\n * `GraphComponentParams` — the shape consumed here. Domain semantics\n * (`weight` etc.) are opaque to this renderer ; consumers decide what\n * the values mean.\n *\n * Copy + export : the renderer ships with `<ExpandableWrapper>` (default\n * copy = JSON of `{nodes, edges}`) plus a 3-format export menu — **PNG**\n * (visual snapshot via the underlying canvas/SVG), **Mermaid** (markdown\n * / GitHub-renderable `flowchart` syntax), **JSON** (raw reimportable\n * data). All three are computed lazily on click.\n */\n\nimport { Component, createSignal, onCleanup, onMount, Show, For } from 'solid-js';\nimport type { UIComponent } from '../types';\nimport type {\n GraphComponentParams,\n GraphLayout,\n} from '@seed-ship/mcp-ui-spec';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { PortalDropdownMenu } from './PortalDropdownMenu';\nimport { DegradedFallback } from './DegradedFallback';\nimport { graphToDegradedTable } from '../utils/degraded-projections';\nimport { escapeHtml } from '../utils/escape-html';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';\n\n// Module-scoped lazy import promise — first call triggers the dynamic\n// import, subsequent calls reuse the resolved module.\nlet g6ModulePromise: Promise<typeof import('@antv/g6')> | undefined;\n\n/**\n * Whether the `@antv/g6` peer dependency is installed and importable.\n * Resolves to `true` when the lib is available, `false` otherwise.\n *\n * Mirrors `isChartJSAvailable()` from `ChartJSRenderer`.\n */\nexport async function isG6Available(): Promise<boolean> {\n try {\n if (!g6ModulePromise) {\n g6ModulePromise = import('@antv/g6');\n }\n await g6ModulePromise;\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the spec layout shorthand or object form into the config object\n * G6 v5 expects. When `layout` is omitted, picks `'force'` if edges are\n * present (universal default) or `'circular'` otherwise.\n */\nfunction resolveLayout(params: GraphComponentParams): { type: string; [key: string]: unknown } {\n const layout: GraphLayout | undefined = params.layout;\n if (layout === undefined) {\n const hasEdges = (params.edges?.length ?? 0) > 0;\n return { type: hasEdges ? 'force' : 'circular' };\n }\n if (typeof layout === 'string') {\n return { type: layout };\n }\n // Object form: spread the passthrough options alongside `type`.\n return { type: layout.type, ...(layout.options ?? {}) };\n}\n\n/**\n * Build the G6 v5 `Graph` constructor config from the component params.\n *\n * Pure (no DOM/lib side effects beyond reading `container`), so it can be\n * unit-tested directly without a jsdom render — this is the contract the\n * 2026-05-30 audit (P0.2) wants locked.\n *\n * ⚠️ G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n * (that was the v4 contract). Passing the string `'canvas'` / `'svg'` makes\n * G6 throw `renderer is not a function` — and because the default path used\n * to pass the string `'canvas'`, EVERY graph crashed, not just svg. So we\n * **omit `renderer` entirely**: G6 then uses its built-in canvas renderer\n * (documented default `() => new CanvasRenderer()`).\n *\n * `rendererPref: 'svg'` is reserved but NOT wired yet — a real G6 v5 SVG\n * renderer needs the `@antv/g-svg` factory (a transitive dep of `@antv/g6`,\n * not statically resolvable at build time). Until that's wired behind proper\n * optional-peer resolution, svg degrades to the canvas default with a\n * one-time warning. It must NEVER inject a string `renderer`.\n */\nexport function buildGraphConfig(\n p: GraphComponentParams,\n container?: HTMLElement\n): Record<string, unknown> {\n const config: Record<string, unknown> = {\n container,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n\n if (p.tooltip !== false) {\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`;\n },\n },\n ];\n }\n\n return config;\n}\n\n/**\n * Build the G6 v5 `behaviors` array from the params interactivity flags.\n * Defaults : drag-canvas + zoom-canvas + drag-element + click-select.\n * Any flag set to `false` opts out.\n */\nfunction resolveBehaviors(params: GraphComponentParams): string[] {\n const behaviors: string[] = [];\n if (params.enableDrag !== false) behaviors.push('drag-element');\n if (params.enableZoom !== false) {\n behaviors.push('zoom-canvas', 'drag-canvas');\n }\n if (params.enableSelect !== false) behaviors.push('click-select');\n return behaviors;\n}\n\n/**\n * Pick a sensible Mermaid `flowchart` direction from the resolved layout.\n * `dagre` / `tree` / `mindmap` are top-down hierarchies → TD ; everything\n * else (force, concentric, circular, grid) → LR (default mermaid).\n */\nfunction mermaidDirection(layoutType: string): 'TD' | 'LR' {\n return layoutType === 'dagre' || layoutType === 'tree' || layoutType === 'mindmap' ? 'TD' : 'LR';\n}\n\n/**\n * Sanitize a string for use inside a Mermaid node label. Mermaid breaks\n * on raw quotes / brackets / pipes ; we strip the worst offenders.\n */\nfunction mermaidLabel(s: string): string {\n return s\n .replace(/[\"[\\]|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Convert the graph data to Mermaid `flowchart` syntax. The edge label\n * carries the optional `weight` prefix when present (e.g. `|3| label`).\n */\nfunction toMermaid(params: GraphComponentParams): string {\n const layoutType = resolveLayout(params).type;\n const dir = mermaidDirection(layoutType);\n const lines: string[] = [`flowchart ${dir}`];\n for (const n of params.nodes) {\n const label = mermaidLabel(n.label ?? n.id);\n lines.push(` ${n.id}[\"${label}\"]`);\n }\n for (const e of params.edges ?? []) {\n const labelParts: string[] = [];\n if (e.weight !== undefined) labelParts.push(String(e.weight));\n if (e.label) labelParts.push(mermaidLabel(e.label));\n const labelText = labelParts.join(' · ');\n if (labelText) {\n lines.push(` ${e.source} -->|${labelText}| ${e.target}`);\n } else {\n lines.push(` ${e.source} --> ${e.target}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction toJSON(params: GraphComponentParams): string {\n return JSON.stringify({ nodes: params.nodes, edges: params.edges ?? [] }, null, 2);\n}\n\nfunction downloadBlob(content: string | Blob, filename: string, mimeType?: string): void {\n const blob =\n typeof content === 'string' ? new Blob([content], { type: mimeType ?? 'text/plain' }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nexport interface GraphRendererProps {\n component: UIComponent;\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nexport const GraphRenderer: Component<GraphRendererProps> = (props) => {\n const strings = useMCPUIStrings();\n const params = () => props.component.params as GraphComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\n const [available, setAvailable] = createSignal<boolean | null>(null);\n const [error, setError] = createSignal<string | undefined>();\n const [exportMenuOpen, setExportMenuOpen] = createSignal(false);\n let containerRef: HTMLDivElement | undefined;\n // v6.4.0 — trigger ref consumed by <PortalDropdownMenu> for positioning\n let exportTriggerRef: HTMLButtonElement | undefined;\n // Loosely typed because G6 is a peer-optional — we don't pull its\n // types into the bundle just to type a transient local handle.\n let graphInstance: any | undefined;\n\n onMount(async () => {\n const g6Available = await isG6Available();\n setAvailable(g6Available);\n if (!g6Available || !containerRef) return;\n\n try {\n const { Graph } = await g6ModulePromise!;\n const p = params();\n const config: Record<string, unknown> = {\n container: containerRef,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n\n // G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n // (that was the v4 contract). Passing the string `'canvas'` / `'svg'`\n // makes G6 throw `renderer is not a function` — and because the default\n // path also passed the string `'canvas'`, EVERY graph crashed, not just\n // the svg one.\n //\n // Fix: omit `renderer` entirely. G6 then uses its built-in canvas\n // renderer (documented default `() => new CanvasRenderer()`), which is\n // what we want for every graph.\n //\n // `rendererPref: 'svg'` is NOT wired yet: a real G6 v5 SVG renderer\n // needs the `@antv/g-svg` factory, which is a *transitive* dep of\n // `@antv/g6` (not a declared peer) and isn't statically resolvable at\n // build time. Rather than ship a fragile/unresolvable import, we treat\n // svg as \"not yet supported\" and fall back to the canvas default with a\n // one-time warning. Tracked for a follow-up that wires the factory\n // behind a proper optional-peer resolution. (Never pass a string here.)\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n if (p.tooltip !== false) {\n // Built-in tooltip plugin — shows label + a compact dump of\n // node.data on hover. Consumers can opt out with `tooltip: false`.\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n return `<div style=\"padding:4px 8px\"><strong>${escapeHtml(String(label))}</strong>${\n data\n ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>`\n : ''\n }</div>`;\n },\n },\n ];\n }\n graphInstance = new (Graph as any)(config);\n await graphInstance.render();\n } catch (err) {\n const message = err instanceof Error ? err.message : strings.graphRenderError;\n setError(message);\n // Fallback ladder (P2.5): the native G6 render threw — emit telemetry\n // so the failure is observable, then degrade to the edge/node table\n // below instead of leaving a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: message,\n id: props.component.id ?? '',\n componentType: 'graph',\n ts: Date.now(),\n });\n }\n });\n\n onCleanup(() => {\n try {\n graphInstance?.destroy();\n } catch {\n // G6 destroy can throw on already-destroyed instances or partial\n // init failures — silent because the component is unmounting anyway.\n }\n graphInstance = undefined;\n });\n\n // ─── Export handlers ────────────────────────────────────────────────\n const handleExportJSON = () => {\n downloadBlob(toJSON(params()), `${graphFilenameStem(params())}.json`, 'application/json');\n setExportMenuOpen(false);\n };\n\n const handleExportMermaid = () => {\n downloadBlob(toMermaid(params()), `${graphFilenameStem(params())}.mmd`, 'text/plain');\n setExportMenuOpen(false);\n };\n\n const handleExportPNG = async () => {\n if (!graphInstance) return;\n try {\n // G6 v5 exposes `toDataURL()` on the graph instance.\n const dataUrl: string = await graphInstance.toDataURL?.('image/png');\n if (!dataUrl) {\n // Fallback: try to grab the underlying canvas directly.\n const canvas = containerRef?.querySelector('canvas');\n if (canvas) {\n const url = (canvas as HTMLCanvasElement).toDataURL('image/png');\n await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);\n } else {\n setError(strings.graphPngUnsupported);\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : strings.graphPngExportFailed);\n }\n setExportMenuOpen(false);\n };\n\n return (\n <Show\n when={available() === true}\n fallback={\n <Show\n when={available() === false}\n fallback={\n // Loading skeleton while we determine peer availability\n <div class=\"w-full p-4 bg-gray-50 dark:bg-gray-800/40 border border-gray-200 dark:border-gray-700 rounded-lg animate-pulse\">\n <div class=\"h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2\" />\n <div class=\"h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded\" />\n </div>\n }\n >\n <div class=\"w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg\">\n <p class=\"text-sm font-medium text-yellow-900 dark:text-yellow-100\">\n {strings.graphUnavailable}\n </p>\n <p class=\"text-xs text-yellow-700 dark:text-yellow-300 mt-1\">\n Install <code>@antv/g6</code> peer dependency to render <code>type: \"graph\"</code>{' '}\n components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? strings.graphTitle}\n copyData={toJSON(params())}\n copyLabel={strings.graphCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div\n class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}\n >\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\n ref={exportTriggerRef}\n type=\"button\"\n onClick={() => setExportMenuOpen((v) => !v)}\n 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\"\n title={strings.graphExport}\n aria-label={strings.graphExport}\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n {strings.graphExportMenu}\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For\n each={[\n {\n label: strings.graphDownloadPng,\n onClick: handleExportPNG,\n hint: strings.graphDownloadPngHint,\n },\n {\n label: strings.graphDownloadMermaid,\n onClick: handleExportMermaid,\n hint: strings.graphDownloadMermaidHint,\n },\n {\n label: strings.graphDownloadJson,\n onClick: handleExportJSON,\n hint: strings.graphDownloadJsonHint,\n },\n ]}\n >\n {(item) => (\n <button\n type=\"button\"\n onClick={item.onClick}\n 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\"\n >\n <div class=\"font-medium\">{item.label}</div>\n <div class=\"text-[10px] text-gray-500 dark:text-gray-400\">{item.hint}</div>\n </button>\n )}\n </For>\n </PortalDropdownMenu>\n </div>\n\n {/* Native G6 canvas — hidden once a render error degrades us. */}\n <div\n ref={containerRef}\n class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${\n error() ? 'hidden' : ''\n } ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n {/* Fallback ladder (P2.5): degrade to an edge/node table on error\n rather than showing a bare message. Export menu stays usable. */}\n <Show when={error()}>\n <DegradedFallback\n message={formatMCPUIString(strings.graphRenderFailed, { error: String(error()) })}\n caption={strings.graphDegradedCaption}\n {...graphToDegradedTable(params(), {\n source: strings.degradedColSource,\n target: strings.degradedColTarget,\n label: strings.degradedColLabel,\n node: strings.degradedColNode,\n })}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n </Show>\n );\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\nfunction graphFilenameStem(params: GraphComponentParams): string {\n const base = (params.title ?? 'graph').replace(/[^a-z0-9-_]+/gi, '-').replace(/^-+|-+$/g, '');\n return base || 'graph';\n}\n\nasync function downloadDataUrl(dataUrl: string, filename: string): Promise<void> {\n const res = await fetch(dataUrl);\n const blob = await res.blob();\n downloadBlob(blob, filename);\n}\n\n// Re-export for tests + consumers that want to compose their own export menu\nexport { toMermaid as graphToMermaid, toJSON as graphToJSON };\n"],"names":["g6ModulePromise","isG6Available","resolveLayout","params","layout","undefined","hasEdges","edges","length","type","options","resolveBehaviors","behaviors","enableDrag","push","enableZoom","enableSelect","mermaidDirection","layoutType","mermaidLabel","s","replace","trim","toMermaid","dir","lines","n","nodes","label","id","e","labelParts","weight","String","labelText","join","source","target","toJSON","JSON","stringify","downloadBlob","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","GraphRenderer","props","strings","useMCPUIStrings","component","isExpanded","useExpanded","telemetry","useTelemetry","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","rendererPref","console","warn","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","message","Error","graphRenderError","dispatch","errorMessage","componentType","ts","Date","now","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","graphPngUnsupported","graphPngExportFailed","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$3","children","_el$9","_tmpl$2","_el$0","firstChild","_$insert","graphUnavailable","ExpandableWrapper","title","graphTitle","copyData","copyLabel","graphCopy","toolbarVariant","_el$","_tmpl$","_el$2","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$7","_el$8","_co$2","$$click","v","_ref$","_$use","graphExportMenu","PortalDropdownMenu","open","onClose","trigger","width","For","each","graphDownloadPng","onClick","hint","graphDownloadPngHint","graphDownloadMermaid","graphDownloadMermaidHint","graphDownloadJson","graphDownloadJsonHint","_el$10","_tmpl$4","_el$11","_el$12","_$addEventListener","_$runHydrationEvents","_ref$2","DegradedFallback","_$mergeProps","formatMCPUIString","graphRenderFailed","caption","graphDegradedCaption","graphToDegradedTable","degradedColSource","degradedColTarget","degradedColLabel","node","degradedColNode","_$effect","_p$","_v$","className","_v$2","graphExport","_v$3","_v$4","_v$5","_v$6","height","_$className","t","_$setAttribute","o","i","_$style","base","res","fetch","_$delegateEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GraphRenderer.js","sources":["../../src/components/GraphRenderer.tsx"],"sourcesContent":["/**\n * GraphRenderer (v6.0.0) — generic node-link visualization powered by\n * `@antv/g6 ^5` (peer-optional). Same lazy-load pattern as\n * `ChartJSRenderer` and `MapRenderer` : the heavy lib is dynamically\n * imported only on first mount, and apps that don't install the peer\n * see an informative fallback instead of a crash.\n *\n * Spec : `@seed-ship/mcp-ui-spec@5.0.4` exports `GraphComponentParamsSchema`,\n * `GraphNode`, `GraphEdge`, `GraphLayoutName`, `GraphLayout`,\n * `GraphComponentParams` — the shape consumed here. Domain semantics\n * (`weight` etc.) are opaque to this renderer ; consumers decide what\n * the values mean.\n *\n * Copy + export : the renderer ships with `<ExpandableWrapper>` (default\n * copy = JSON of `{nodes, edges}`) plus a 3-format export menu — **PNG**\n * (visual snapshot via the underlying canvas/SVG), **Mermaid** (markdown\n * / GitHub-renderable `flowchart` syntax), **JSON** (raw reimportable\n * data). All three are computed lazily on click.\n */\n\nimport { Component, createSignal, onCleanup, onMount, Show, For } from 'solid-js';\nimport type { UIComponent } from '../types';\nimport type { GraphComponentParams, GraphLayout } from '@seed-ship/mcp-ui-spec';\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper';\nimport { PortalDropdownMenu } from './PortalDropdownMenu';\nimport { DegradedFallback } from './DegradedFallback';\nimport { graphToDegradedTable } from '../utils/degraded-projections';\nimport { escapeHtml } from '../utils/escape-html';\nimport { useTelemetry } from '../context/MCPUITelemetryContext';\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext';\nimport { observeTheme, readThemeTokens } from '../utils/theme';\n\nconst GRAPH_THEME_TOKENS = [\n ...Array.from({ length: 8 }, (_, i) => `--mcp-ui-chart-${i + 1}`),\n '--mcp-ui-chart-grid',\n '--mcp-ui-chart-axis',\n '--mcp-ui-chart-label',\n] as const;\n\n/** G6's option mappers outrank datum.style, so explicitly retain the latter. */\nfunction graphThemeOptions(p: GraphComponentParams, tokens: Record<string, string | undefined>) {\n const nodeIndices = new Map(p.nodes.map((node, index) => [node.id, index]));\n return {\n node: {\n style: (datum: any) => {\n const index = nodeIndices.get(datum.id) ?? 0;\n const fill = tokens[`--mcp-ui-chart-${(index % 8) + 1}`];\n return {\n ...(fill ? { fill } : {}),\n ...(tokens['--mcp-ui-chart-axis'] ? { stroke: tokens['--mcp-ui-chart-axis'] } : {}),\n ...(tokens['--mcp-ui-chart-label'] ? { labelFill: tokens['--mcp-ui-chart-label'] } : {}),\n ...datum.style,\n };\n },\n },\n edge: {\n style: (datum: any) => ({\n ...(tokens['--mcp-ui-chart-grid'] ? { stroke: tokens['--mcp-ui-chart-grid'] } : {}),\n ...(tokens['--mcp-ui-chart-label'] ? { labelFill: tokens['--mcp-ui-chart-label'] } : {}),\n ...datum.style,\n }),\n },\n };\n}\n\n// Module-scoped lazy import promise — first call triggers the dynamic\n// import, subsequent calls reuse the resolved module.\nlet g6ModulePromise: Promise<typeof import('@antv/g6')> | undefined;\n\n/**\n * Whether the `@antv/g6` peer dependency is installed and importable.\n * Resolves to `true` when the lib is available, `false` otherwise.\n *\n * Mirrors `isChartJSAvailable()` from `ChartJSRenderer`.\n */\nexport async function isG6Available(): Promise<boolean> {\n try {\n if (!g6ModulePromise) {\n g6ModulePromise = import('@antv/g6');\n }\n await g6ModulePromise;\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the spec layout shorthand or object form into the config object\n * G6 v5 expects. When `layout` is omitted, picks `'force'` if edges are\n * present (universal default) or `'circular'` otherwise.\n */\nfunction resolveLayout(params: GraphComponentParams): { type: string; [key: string]: unknown } {\n const layout: GraphLayout | undefined = params.layout;\n if (layout === undefined) {\n const hasEdges = (params.edges?.length ?? 0) > 0;\n return { type: hasEdges ? 'force' : 'circular' };\n }\n if (typeof layout === 'string') {\n return { type: layout };\n }\n // Object form: spread the passthrough options alongside `type`.\n return { type: layout.type, ...(layout.options ?? {}) };\n}\n\n/**\n * Build the G6 v5 `Graph` constructor config from the component params.\n *\n * Pure (no DOM/lib side effects beyond reading `container`), so it can be\n * unit-tested directly without a jsdom render — this is the contract the\n * 2026-05-30 audit (P0.2) wants locked.\n *\n * ⚠️ G6 v5's `renderer` is a factory `(layer) => IRenderer`, NOT a string\n * (that was the v4 contract). Passing the string `'canvas'` / `'svg'` makes\n * G6 throw `renderer is not a function` — and because the default path used\n * to pass the string `'canvas'`, EVERY graph crashed, not just svg. So we\n * **omit `renderer` entirely**: G6 then uses its built-in canvas renderer\n * (documented default `() => new CanvasRenderer()`).\n *\n * `rendererPref: 'svg'` is reserved but NOT wired yet — a real G6 v5 SVG\n * renderer needs the `@antv/g-svg` factory (a transitive dep of `@antv/g6`,\n * not statically resolvable at build time). Until that's wired behind proper\n * optional-peer resolution, svg degrades to the canvas default with a\n * one-time warning. It must NEVER inject a string `renderer`.\n */\nexport function buildGraphConfig(\n p: GraphComponentParams,\n container?: HTMLElement,\n tokens: Record<string, string | undefined> = {}\n): Record<string, unknown> {\n const config: Record<string, unknown> = {\n container,\n data: { nodes: p.nodes, edges: p.edges ?? [] },\n layout: resolveLayout(p),\n behaviors: resolveBehaviors(p),\n };\n if (GRAPH_THEME_TOKENS.some((name) => tokens[name])) {\n Object.assign(config, graphThemeOptions(p, tokens));\n }\n\n if (p.rendererPref === 'svg') {\n console.warn(\n '[MCP-UI] GraphRenderer: rendererPref \"svg\" is not yet supported; using the default canvas renderer.'\n );\n }\n\n if (p.fitView !== false) {\n config.autoFit = 'view';\n }\n\n if (p.tooltip !== false) {\n config.plugins = [\n {\n type: 'tooltip',\n getContent: (_evt: unknown, items: any[]) => {\n const item = items?.[0];\n if (!item) return '';\n const label = item.label ?? item.id ?? '';\n const data = item.data ? JSON.stringify(item.data) : '';\n // G6 may position HTML outside our subtree. Resolve effective host\n // values at hover time, rather than relying on portal inheritance.\n const theme = readThemeTokens(container, ['--mcp-ui-surface', '--mcp-ui-fg', '--mcp-ui-border']);\n const styles = ['padding:4px 8px'];\n if (theme['--mcp-ui-surface']) styles.push(`background-color:${theme['--mcp-ui-surface']}`);\n if (theme['--mcp-ui-fg']) styles.push(`color:${theme['--mcp-ui-fg']}`);\n if (theme['--mcp-ui-border']) styles.push(`border:1px solid ${theme['--mcp-ui-border']}`);\n return `<div style=\"${escapeHtml(styles.join(';'))}\"><strong>${escapeHtml(String(label))}</strong>${\n data ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`;\n },\n },\n ];\n }\n\n return config;\n}\n\n/**\n * Build the G6 v5 `behaviors` array from the params interactivity flags.\n * Defaults : drag-canvas + zoom-canvas + drag-element + click-select.\n * Any flag set to `false` opts out.\n */\nfunction resolveBehaviors(params: GraphComponentParams): string[] {\n const behaviors: string[] = [];\n if (params.enableDrag !== false) behaviors.push('drag-element');\n if (params.enableZoom !== false) {\n behaviors.push('zoom-canvas', 'drag-canvas');\n }\n if (params.enableSelect !== false) behaviors.push('click-select');\n return behaviors;\n}\n\n/**\n * Pick a sensible Mermaid `flowchart` direction from the resolved layout.\n * `dagre` / `tree` / `mindmap` are top-down hierarchies → TD ; everything\n * else (force, concentric, circular, grid) → LR (default mermaid).\n */\nfunction mermaidDirection(layoutType: string): 'TD' | 'LR' {\n return layoutType === 'dagre' || layoutType === 'tree' || layoutType === 'mindmap' ? 'TD' : 'LR';\n}\n\n/**\n * Sanitize a string for use inside a Mermaid node label. Mermaid breaks\n * on raw quotes / brackets / pipes ; we strip the worst offenders.\n */\nfunction mermaidLabel(s: string): string {\n return s\n .replace(/[\"[\\]|]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Convert the graph data to Mermaid `flowchart` syntax. The edge label\n * carries the optional `weight` prefix when present (e.g. `|3| label`).\n */\nfunction toMermaid(params: GraphComponentParams): string {\n const layoutType = resolveLayout(params).type;\n const dir = mermaidDirection(layoutType);\n const lines: string[] = [`flowchart ${dir}`];\n for (const n of params.nodes) {\n const label = mermaidLabel(n.label ?? n.id);\n lines.push(` ${n.id}[\"${label}\"]`);\n }\n for (const e of params.edges ?? []) {\n const labelParts: string[] = [];\n if (e.weight !== undefined) labelParts.push(String(e.weight));\n if (e.label) labelParts.push(mermaidLabel(e.label));\n const labelText = labelParts.join(' · ');\n if (labelText) {\n lines.push(` ${e.source} -->|${labelText}| ${e.target}`);\n } else {\n lines.push(` ${e.source} --> ${e.target}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction toJSON(params: GraphComponentParams): string {\n return JSON.stringify({ nodes: params.nodes, edges: params.edges ?? [] }, null, 2);\n}\n\nfunction downloadBlob(content: string | Blob, filename: string, mimeType?: string): void {\n const blob =\n typeof content === 'string' ? new Blob([content], { type: mimeType ?? 'text/plain' }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n}\n\nexport interface GraphRendererProps {\n component: UIComponent;\n /**\n * Forwarded to the underlying `<ExpandableWrapper>` (v6.3.1).\n * @see ExpandableWrapperProps.toolbarVariant\n */\n toolbarVariant?: 'hover' | 'always-visible';\n}\n\nexport const GraphRenderer: Component<GraphRendererProps> = (props) => {\n const strings = useMCPUIStrings();\n const params = () => props.component.params as GraphComponentParams;\n const isExpanded = useExpanded();\n const telemetry = useTelemetry();\n const [available, setAvailable] = createSignal<boolean | null>(null);\n const [error, setError] = createSignal<string | undefined>();\n const [exportMenuOpen, setExportMenuOpen] = createSignal(false);\n let containerRef: HTMLDivElement | undefined;\n // v6.4.0 — trigger ref consumed by <PortalDropdownMenu> for positioning\n let exportTriggerRef: HTMLButtonElement | undefined;\n // Loosely typed because G6 is a peer-optional — we don't pull its\n // types into the bundle just to type a transient local handle.\n let graphInstance: any | undefined;\n let stopTheme: (() => void) | undefined;\n let disposed = false;\n\n onMount(async () => {\n const g6Available = await isG6Available();\n if (disposed) return;\n setAvailable(g6Available);\n if (!g6Available || !containerRef) return;\n\n try {\n const { Graph } = await g6ModulePromise!;\n if (disposed || !containerRef) return;\n const p = params();\n let tokens = readThemeTokens(containerRef, GRAPH_THEME_TOKENS);\n const config = buildGraphConfig(p, containerRef, tokens);\n\n graphInstance = new (Graph as any)(config);\n await graphInstance.render();\n if (disposed) return;\n const syncTheme = () => {\n if (!graphInstance || disposed) return;\n const next = readThemeTokens(containerRef, GRAPH_THEME_TOKENS);\n if (GRAPH_THEME_TOKENS.every((name) => next[name] === tokens[name])) return;\n tokens = next;\n graphInstance.setOptions(graphThemeOptions(p, tokens));\n // draw() updates styles without rerunning force layout or auto-fit.\n void Promise.resolve(graphInstance.draw()).catch(() => {});\n };\n stopTheme = observeTheme(containerRef, syncTheme);\n // Rendering/layout is async: a host may have switched themes while it\n // was in flight, before the observer could be attached.\n syncTheme();\n } catch (err) {\n if (disposed) return;\n const message = err instanceof Error ? err.message : strings.graphRenderError;\n setError(message);\n // Fallback ladder (P2.5): the native G6 render threw — emit telemetry\n // so the failure is observable, then degrade to the edge/node table\n // below instead of leaving a blank canvas.\n telemetry?.dispatch({\n type: 'render:error',\n errorMessage: message,\n id: props.component.id ?? '',\n componentType: 'graph',\n ts: Date.now(),\n });\n }\n });\n\n onCleanup(() => {\n disposed = true;\n stopTheme?.();\n try {\n graphInstance?.destroy();\n } catch {\n // G6 destroy can throw on already-destroyed instances or partial\n // init failures — silent because the component is unmounting anyway.\n }\n graphInstance = undefined;\n });\n\n // ─── Export handlers ────────────────────────────────────────────────\n const handleExportJSON = () => {\n downloadBlob(toJSON(params()), `${graphFilenameStem(params())}.json`, 'application/json');\n setExportMenuOpen(false);\n };\n\n const handleExportMermaid = () => {\n downloadBlob(toMermaid(params()), `${graphFilenameStem(params())}.mmd`, 'text/plain');\n setExportMenuOpen(false);\n };\n\n const handleExportPNG = async () => {\n if (!graphInstance) return;\n try {\n // G6 v5 exposes `toDataURL()` on the graph instance.\n const dataUrl: string = await graphInstance.toDataURL?.('image/png');\n if (!dataUrl) {\n // Fallback: try to grab the underlying canvas directly.\n const canvas = containerRef?.querySelector('canvas');\n if (canvas) {\n const url = (canvas as HTMLCanvasElement).toDataURL('image/png');\n await downloadDataUrl(url, `${graphFilenameStem(params())}.png`);\n } else {\n setError(strings.graphPngUnsupported);\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : strings.graphPngExportFailed);\n }\n setExportMenuOpen(false);\n };\n\n return (\n <Show\n when={available() === true}\n fallback={\n <Show\n when={available() === false}\n fallback={\n // Loading skeleton while we determine peer availability\n <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\">\n <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\" />\n <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\" />\n </div>\n }\n >\n <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\">\n <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))]\">\n {strings.graphUnavailable}\n </p>\n <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\">\n Install <code>@antv/g6</code> peer dependency to render <code>type: \"graph\"</code>{' '}\n components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? strings.graphTitle}\n copyData={toJSON(params())}\n copyLabel={strings.graphCopy}\n toolbarVariant={props.toolbarVariant}\n >\n <div\n class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}\n >\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\n ref={exportTriggerRef}\n type=\"button\"\n onClick={() => setExportMenuOpen((v) => !v)}\n 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))]\"\n title={strings.graphExport}\n aria-label={strings.graphExport}\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n {strings.graphExportMenu}\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For\n each={[\n {\n label: strings.graphDownloadPng,\n onClick: handleExportPNG,\n hint: strings.graphDownloadPngHint,\n },\n {\n label: strings.graphDownloadMermaid,\n onClick: handleExportMermaid,\n hint: strings.graphDownloadMermaidHint,\n },\n {\n label: strings.graphDownloadJson,\n onClick: handleExportJSON,\n hint: strings.graphDownloadJsonHint,\n },\n ]}\n >\n {(item) => (\n <button\n type=\"button\"\n onClick={item.onClick}\n 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\"\n >\n <div class=\"font-medium\">{item.label}</div>\n <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))]\">\n {item.hint}\n </div>\n </button>\n )}\n </For>\n </PortalDropdownMenu>\n </div>\n\n {/* Native G6 canvas — hidden once a render error degrades us. */}\n <div\n ref={containerRef}\n 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 ${\n error() ? 'hidden' : ''\n } ${isExpanded() ? 'flex-1 min-h-0' : ''}`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n {/* Fallback ladder (P2.5): degrade to an edge/node table on error\n rather than showing a bare message. Export menu stays usable. */}\n <Show when={error()}>\n <DegradedFallback\n message={formatMCPUIString(strings.graphRenderFailed, { error: String(error()) })}\n caption={strings.graphDegradedCaption}\n {...graphToDegradedTable(params(), {\n source: strings.degradedColSource,\n target: strings.degradedColTarget,\n label: strings.degradedColLabel,\n node: strings.degradedColNode,\n })}\n />\n </Show>\n </div>\n </ExpandableWrapper>\n </Show>\n );\n};\n\n// ─── Helpers ──────────────────────────────────────────────────────────\n\nfunction graphFilenameStem(params: GraphComponentParams): string {\n const base = (params.title ?? 'graph').replace(/[^a-z0-9-_]+/gi, '-').replace(/^-+|-+$/g, '');\n return base || 'graph';\n}\n\nasync function downloadDataUrl(dataUrl: string, filename: string): Promise<void> {\n const res = await fetch(dataUrl);\n const blob = await res.blob();\n downloadBlob(blob, filename);\n}\n\n// Re-export for tests + consumers that want to compose their own export menu\nexport { toMermaid as graphToMermaid, toJSON as graphToJSON };\n"],"names":["GRAPH_THEME_TOKENS","Array","from","length","_","i","graphThemeOptions","p","tokens","nodeIndices","Map","nodes","map","node","index","id","style","datum","get","fill","stroke","labelFill","edge","g6ModulePromise","isG6Available","resolveLayout","params","layout","undefined","hasEdges","edges","type","options","buildGraphConfig","container","config","data","behaviors","resolveBehaviors","some","name","Object","assign","rendererPref","console","warn","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","label","JSON","stringify","theme","readThemeTokens","styles","push","escapeHtml","join","String","enableDrag","enableZoom","enableSelect","mermaidDirection","layoutType","mermaidLabel","s","replace","trim","toMermaid","dir","lines","n","e","labelParts","weight","labelText","source","target","toJSON","downloadBlob","content","filename","mimeType","blob","Blob","url","URL","createObjectURL","a","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","GraphRenderer","props","strings","useMCPUIStrings","component","isExpanded","useExpanded","telemetry","useTelemetry","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","stopTheme","disposed","onMount","g6Available","Graph","render","syncTheme","next","every","setOptions","Promise","resolve","draw","catch","observeTheme","err","message","Error","graphRenderError","dispatch","errorMessage","componentType","ts","Date","now","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","graphPngUnsupported","graphPngExportFailed","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$3","children","_el$9","_tmpl$2","_el$0","firstChild","_$insert","graphUnavailable","ExpandableWrapper","title","graphTitle","copyData","copyLabel","graphCopy","toolbarVariant","_el$","_tmpl$","_el$2","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$7","_el$8","_co$2","$$click","v","_ref$","_$use","graphExportMenu","PortalDropdownMenu","open","onClose","trigger","width","For","each","graphDownloadPng","onClick","hint","graphDownloadPngHint","graphDownloadMermaid","graphDownloadMermaidHint","graphDownloadJson","graphDownloadJsonHint","_el$10","_tmpl$4","_el$11","_el$12","_$addEventListener","_$runHydrationEvents","_ref$2","DegradedFallback","_$mergeProps","formatMCPUIString","graphRenderFailed","caption","graphDegradedCaption","graphToDegradedTable","degradedColSource","degradedColTarget","degradedColLabel","degradedColNode","_$effect","_p$","_v$","className","_v$2","graphExport","_v$3","_v$4","_v$5","_v$6","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","_$delegateEvents"],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const web = require("solid-js/web");
|
|
4
|
+
const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
|
|
5
|
+
const missingValue = require("../utils/missing-value.cjs");
|
|
6
|
+
const formatString = require("../utils/format-string.cjs");
|
|
7
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<span data-mcp-missing-value><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))]"></span><span>`), _tmpl$2 = /* @__PURE__ */ web.template(`<p data-mcp-missing-legend role=note>`);
|
|
8
|
+
const VISUALLY_HIDDEN_STYLE = {
|
|
9
|
+
position: "absolute",
|
|
10
|
+
width: "1px",
|
|
11
|
+
height: "1px",
|
|
12
|
+
padding: "0",
|
|
13
|
+
margin: "-1px",
|
|
14
|
+
overflow: "hidden",
|
|
15
|
+
clip: "rect(0, 0, 0, 0)",
|
|
16
|
+
"white-space": "nowrap",
|
|
17
|
+
border: "0"
|
|
18
|
+
};
|
|
19
|
+
const MissingValueMark = () => {
|
|
20
|
+
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
21
|
+
return (() => {
|
|
22
|
+
var _el$ = web.getNextElement(_tmpl$), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
|
|
23
|
+
web.insert(_el$2, missingValue.MISSING_VALUE_MARKER);
|
|
24
|
+
web.insert(_el$3, () => strings.missingValue);
|
|
25
|
+
web.effect((_$p) => web.style(_el$3, VISUALLY_HIDDEN_STYLE, _$p));
|
|
26
|
+
return _el$;
|
|
27
|
+
})();
|
|
28
|
+
};
|
|
29
|
+
const MissingValueLegend = (props) => {
|
|
30
|
+
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
31
|
+
return (() => {
|
|
32
|
+
var _el$4 = web.getNextElement(_tmpl$2);
|
|
33
|
+
web.insert(_el$4, () => formatString.formatMCPUIString(strings.missingValueLegend, {
|
|
34
|
+
marker: missingValue.MISSING_VALUE_MARKER
|
|
35
|
+
}));
|
|
36
|
+
web.effect(() => web.className(_el$4, `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 ?? ""}`));
|
|
37
|
+
return _el$4;
|
|
38
|
+
})();
|
|
39
|
+
};
|
|
40
|
+
exports.MissingValueLegend = MissingValueLegend;
|
|
41
|
+
exports.MissingValueMark = MissingValueMark;
|
|
42
|
+
exports.VISUALLY_HIDDEN_STYLE = VISUALLY_HIDDEN_STYLE;
|
|
43
|
+
//# sourceMappingURL=MissingValue.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MissingValue.cjs","sources":["../../src/components/MissingValue.tsx"],"sourcesContent":["/**\n * The visible and accessible marks of a missing value (v6.24.0).\n *\n * A table cell with no value has always shown `-`. On its own that glyph says\n * nothing to a screen reader (it is read as \"dash\", or skipped) and it is easy\n * to take for a zero or a minus sign. These two components give it a meaning:\n *\n * - {@link MissingValueMark} — the cell content: the `-` hidden from\n * assistive technology, followed by `strings.missingValue` hidden from\n * sight. Visually-hidden through an inline style rather than Tailwind's\n * `sr-only`, so the text cannot leak on a host whose Tailwind build does\n * not scan this package.\n * - {@link MissingValueLegend} — one line under a table or data view that\n * contains a missing value, so the convention never has to be guessed.\n *\n * Both are SSR-safe: no browser API, and the same markup on server and client.\n */\n\nimport type { Component, JSX } from 'solid-js'\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { MISSING_VALUE_MARKER } from '../utils/missing-value'\n\n/** The classic visually-hidden recipe: in the accessibility tree, out of sight. */\nexport const VISUALLY_HIDDEN_STYLE: JSX.CSSProperties = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n 'white-space': 'nowrap',\n border: '0',\n}\n\n/** Content of a cell whose value is missing. */\nexport const MissingValueMark: Component = () => {\n const strings = useMCPUIStrings()\n return (\n <span data-mcp-missing-value=\"\">\n <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))]\">\n {MISSING_VALUE_MARKER}\n </span>\n <span style={VISUALLY_HIDDEN_STYLE}>{strings.missingValue}</span>\n </span>\n )\n}\n\n/** The legend line explaining the missing-value glyph. */\nexport const MissingValueLegend: Component<{ class?: string }> = (props) => {\n const strings = useMCPUIStrings()\n return (\n <p\n data-mcp-missing-legend=\"\"\n role=\"note\"\n 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 ?? ''}`}\n >\n {formatMCPUIString(strings.missingValueLegend, { marker: MISSING_VALUE_MARKER })}\n </p>\n )\n}\n"],"names":["VISUALLY_HIDDEN_STYLE","position","width","height","padding","margin","overflow","clip","border","MissingValueMark","strings","useMCPUIStrings","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","MISSING_VALUE_MARKER","missingValue","_$effect","_$p","_$style","MissingValueLegend","props","_el$4","_tmpl$2","formatMCPUIString","missingValueLegend","marker","_$className","class"],"mappings":";;;;;;;AAuBO,MAAMA,wBAA2C;AAAA,EACtDC,UAAU;AAAA,EACVC,OAAO;AAAA,EACPC,QAAQ;AAAA,EACRC,SAAS;AAAA,EACTC,QAAQ;AAAA,EACRC,UAAU;AAAA,EACVC,MAAM;AAAA,EACN,eAAe;AAAA,EACfC,QAAQ;AACV;AAGO,MAAMC,mBAA8BA,MAAM;AAC/C,QAAMC,UAAUC,oBAAAA,gBAAAA;AAChB,UAAA,MAAA;AAAA,QAAAC,OAAAC,mBAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG;AAAAC,QAAAA,OAAAJ,OAGOK,iCAAoB;AAAAD,QAAAA,OAAAF,OAAA,MAEcP,QAAQW,YAAY;AAAAC,QAAAA,OAAAC,CAAAA,QAAAC,IAAAA,MAAAP,OAA5CjB,uBAAqBuB,GAAA,CAAA;AAAA,WAAAX;AAAAA,EAAA,GAAA;AAGxC;AAGO,MAAMa,qBAAqDC,CAAAA,UAAU;AAC1E,QAAMhB,UAAUC,oBAAAA,gBAAAA;AAChB,UAAA,MAAA;AAAA,QAAAgB,QAAAd,IAAAA,eAAAe,OAAA;AAAAT,QAAAA,OAAAQ,OAAA,MAMKE,+BAAkBnB,QAAQoB,oBAAoB;AAAA,MAAEC,QAAQX,aAAAA;AAAAA,IAAAA,CAAsB,CAAC;AAAAE,eAAA,MAAAU,cAAAL,OAFzE,qJAAqJD,MAAMO,SAAS,EAAE,EAAE,CAAA;AAAA,WAAAN;AAAAA,EAAA,GAAA;AAKrL;;;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The visible and accessible marks of a missing value (v6.24.0).
|
|
3
|
+
*
|
|
4
|
+
* A table cell with no value has always shown `-`. On its own that glyph says
|
|
5
|
+
* nothing to a screen reader (it is read as "dash", or skipped) and it is easy
|
|
6
|
+
* to take for a zero or a minus sign. These two components give it a meaning:
|
|
7
|
+
*
|
|
8
|
+
* - {@link MissingValueMark} — the cell content: the `-` hidden from
|
|
9
|
+
* assistive technology, followed by `strings.missingValue` hidden from
|
|
10
|
+
* sight. Visually-hidden through an inline style rather than Tailwind's
|
|
11
|
+
* `sr-only`, so the text cannot leak on a host whose Tailwind build does
|
|
12
|
+
* not scan this package.
|
|
13
|
+
* - {@link MissingValueLegend} — one line under a table or data view that
|
|
14
|
+
* contains a missing value, so the convention never has to be guessed.
|
|
15
|
+
*
|
|
16
|
+
* Both are SSR-safe: no browser API, and the same markup on server and client.
|
|
17
|
+
*/
|
|
18
|
+
import type { Component, JSX } from 'solid-js';
|
|
19
|
+
/** The classic visually-hidden recipe: in the accessibility tree, out of sight. */
|
|
20
|
+
export declare const VISUALLY_HIDDEN_STYLE: JSX.CSSProperties;
|
|
21
|
+
/** Content of a cell whose value is missing. */
|
|
22
|
+
export declare const MissingValueMark: Component;
|
|
23
|
+
/** The legend line explaining the missing-value glyph. */
|
|
24
|
+
export declare const MissingValueLegend: Component<{
|
|
25
|
+
class?: string;
|
|
26
|
+
}>;
|
|
27
|
+
//# sourceMappingURL=MissingValue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MissingValue.d.ts","sourceRoot":"","sources":["../../src/components/MissingValue.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAI9C,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,EAAE,GAAG,CAAC,aAUvC,CAAA;AAED,gDAAgD;AAChD,eAAO,MAAM,gBAAgB,EAAE,SAU9B,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAW5D,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getNextElement, insert, effect, style, className, template } from "solid-js/web";
|
|
2
|
+
import { useMCPUIStrings } from "../context/MCPUIStringsContext.js";
|
|
3
|
+
import { MISSING_VALUE_MARKER } from "../utils/missing-value.js";
|
|
4
|
+
import { formatMCPUIString } from "../utils/format-string.js";
|
|
5
|
+
var _tmpl$ = /* @__PURE__ */ template(`<span data-mcp-missing-value><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))]"></span><span>`), _tmpl$2 = /* @__PURE__ */ template(`<p data-mcp-missing-legend role=note>`);
|
|
6
|
+
const VISUALLY_HIDDEN_STYLE = {
|
|
7
|
+
position: "absolute",
|
|
8
|
+
width: "1px",
|
|
9
|
+
height: "1px",
|
|
10
|
+
padding: "0",
|
|
11
|
+
margin: "-1px",
|
|
12
|
+
overflow: "hidden",
|
|
13
|
+
clip: "rect(0, 0, 0, 0)",
|
|
14
|
+
"white-space": "nowrap",
|
|
15
|
+
border: "0"
|
|
16
|
+
};
|
|
17
|
+
const MissingValueMark = () => {
|
|
18
|
+
const strings = useMCPUIStrings();
|
|
19
|
+
return (() => {
|
|
20
|
+
var _el$ = getNextElement(_tmpl$), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling;
|
|
21
|
+
insert(_el$2, MISSING_VALUE_MARKER);
|
|
22
|
+
insert(_el$3, () => strings.missingValue);
|
|
23
|
+
effect((_$p) => style(_el$3, VISUALLY_HIDDEN_STYLE, _$p));
|
|
24
|
+
return _el$;
|
|
25
|
+
})();
|
|
26
|
+
};
|
|
27
|
+
const MissingValueLegend = (props) => {
|
|
28
|
+
const strings = useMCPUIStrings();
|
|
29
|
+
return (() => {
|
|
30
|
+
var _el$4 = getNextElement(_tmpl$2);
|
|
31
|
+
insert(_el$4, () => formatMCPUIString(strings.missingValueLegend, {
|
|
32
|
+
marker: MISSING_VALUE_MARKER
|
|
33
|
+
}));
|
|
34
|
+
effect(() => className(_el$4, `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 ?? ""}`));
|
|
35
|
+
return _el$4;
|
|
36
|
+
})();
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
MissingValueLegend,
|
|
40
|
+
MissingValueMark,
|
|
41
|
+
VISUALLY_HIDDEN_STYLE
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=MissingValue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MissingValue.js","sources":["../../src/components/MissingValue.tsx"],"sourcesContent":["/**\n * The visible and accessible marks of a missing value (v6.24.0).\n *\n * A table cell with no value has always shown `-`. On its own that glyph says\n * nothing to a screen reader (it is read as \"dash\", or skipped) and it is easy\n * to take for a zero or a minus sign. These two components give it a meaning:\n *\n * - {@link MissingValueMark} — the cell content: the `-` hidden from\n * assistive technology, followed by `strings.missingValue` hidden from\n * sight. Visually-hidden through an inline style rather than Tailwind's\n * `sr-only`, so the text cannot leak on a host whose Tailwind build does\n * not scan this package.\n * - {@link MissingValueLegend} — one line under a table or data view that\n * contains a missing value, so the convention never has to be guessed.\n *\n * Both are SSR-safe: no browser API, and the same markup on server and client.\n */\n\nimport type { Component, JSX } from 'solid-js'\nimport { formatMCPUIString, useMCPUIStrings } from '../context/MCPUIStringsContext'\nimport { MISSING_VALUE_MARKER } from '../utils/missing-value'\n\n/** The classic visually-hidden recipe: in the accessibility tree, out of sight. */\nexport const VISUALLY_HIDDEN_STYLE: JSX.CSSProperties = {\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n 'white-space': 'nowrap',\n border: '0',\n}\n\n/** Content of a cell whose value is missing. */\nexport const MissingValueMark: Component = () => {\n const strings = useMCPUIStrings()\n return (\n <span data-mcp-missing-value=\"\">\n <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))]\">\n {MISSING_VALUE_MARKER}\n </span>\n <span style={VISUALLY_HIDDEN_STYLE}>{strings.missingValue}</span>\n </span>\n )\n}\n\n/** The legend line explaining the missing-value glyph. */\nexport const MissingValueLegend: Component<{ class?: string }> = (props) => {\n const strings = useMCPUIStrings()\n return (\n <p\n data-mcp-missing-legend=\"\"\n role=\"note\"\n 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 ?? ''}`}\n >\n {formatMCPUIString(strings.missingValueLegend, { marker: MISSING_VALUE_MARKER })}\n </p>\n )\n}\n"],"names":["VISUALLY_HIDDEN_STYLE","position","width","height","padding","margin","overflow","clip","border","MissingValueMark","strings","useMCPUIStrings","_el$","_$getNextElement","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","MISSING_VALUE_MARKER","missingValue","_$effect","_$p","_$style","MissingValueLegend","props","_el$4","_tmpl$2","formatMCPUIString","missingValueLegend","marker","_$className","class"],"mappings":";;;;;AAuBO,MAAMA,wBAA2C;AAAA,EACtDC,UAAU;AAAA,EACVC,OAAO;AAAA,EACPC,QAAQ;AAAA,EACRC,SAAS;AAAA,EACTC,QAAQ;AAAA,EACRC,UAAU;AAAA,EACVC,MAAM;AAAA,EACN,eAAe;AAAA,EACfC,QAAQ;AACV;AAGO,MAAMC,mBAA8BA,MAAM;AAC/C,QAAMC,UAAUC,gBAAAA;AAChB,UAAA,MAAA;AAAA,QAAAC,OAAAC,eAAAC,MAAA,GAAAC,QAAAH,KAAAI,YAAAC,QAAAF,MAAAG;AAAAC,WAAAJ,OAGOK,oBAAoB;AAAAD,WAAAF,OAAA,MAEcP,QAAQW,YAAY;AAAAC,WAAAC,CAAAA,QAAAC,MAAAP,OAA5CjB,uBAAqBuB,GAAA,CAAA;AAAA,WAAAX;AAAAA,EAAA,GAAA;AAGxC;AAGO,MAAMa,qBAAqDC,CAAAA,UAAU;AAC1E,QAAMhB,UAAUC,gBAAAA;AAChB,UAAA,MAAA;AAAA,QAAAgB,QAAAd,eAAAe,OAAA;AAAAT,WAAAQ,OAAA,MAMKE,kBAAkBnB,QAAQoB,oBAAoB;AAAA,MAAEC,QAAQX;AAAAA,IAAAA,CAAsB,CAAC;AAAAE,WAAA,MAAAU,UAAAL,OAFzE,qJAAqJD,MAAMO,SAAS,EAAE,EAAE,CAAA;AAAA,WAAAN;AAAAA,EAAA,GAAA;AAKrL;"}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const web = require("solid-js/web");
|
|
4
4
|
const solidJs = require("solid-js");
|
|
5
|
+
const theme = require("../utils/theme.cjs");
|
|
5
6
|
const MCPUIStringsContext = require("../context/MCPUIStringsContext.cjs");
|
|
6
|
-
var _tmpl$ = /* @__PURE__ */ web.template(`<h2 id=modal-title class="text-lg font-semibold text-gray-900 dark:text-white">`), _tmpl$
|
|
7
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<span hidden aria-hidden=true>`), _tmpl$2 = /* @__PURE__ */ web.template(`<h2 id=modal-title 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))]">`), _tmpl$3 = /* @__PURE__ */ web.template(`<div>`), _tmpl$4 = /* @__PURE__ */ web.template(`<button 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"><svg class="w-5 h-5"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12">`), _tmpl$5 = /* @__PURE__ */ web.template(`<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))]"><!$><!/><!$><!/><!$><!/>`), _tmpl$6 = /* @__PURE__ */ web.template(`<div 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"role=dialog aria-modal=true tabindex=-1 style="animation:modal-fade-in 0.15s ease-out"><div style="animation:modal-scale-in 0.15s ease-out"><!$><!/><div class=p-4>`), _tmpl$7 = /* @__PURE__ */ web.template(`<style>
|
|
7
8
|
@keyframes modal-fade-in {
|
|
8
9
|
from { opacity: 0; }
|
|
9
10
|
to { opacity: 1; }
|
|
@@ -16,6 +17,7 @@ var _tmpl$ = /* @__PURE__ */ web.template(`<h2 id=modal-title class="text-lg fon
|
|
|
16
17
|
const ModalRenderer = (props) => {
|
|
17
18
|
const strings = MCPUIStringsContext.useMCPUIStrings();
|
|
18
19
|
const [isVisible, setIsVisible] = solidJs.createSignal(false);
|
|
20
|
+
let themeAnchor;
|
|
19
21
|
const params = () => {
|
|
20
22
|
var _a;
|
|
21
23
|
return props.params || ((_a = props.component) == null ? void 0 : _a.params) || {};
|
|
@@ -82,7 +84,12 @@ const ModalRenderer = (props) => {
|
|
|
82
84
|
return "max-w-2xl";
|
|
83
85
|
}
|
|
84
86
|
};
|
|
85
|
-
return
|
|
87
|
+
return [(() => {
|
|
88
|
+
var _el$ = web.getNextElement(_tmpl$);
|
|
89
|
+
var _ref$ = themeAnchor;
|
|
90
|
+
typeof _ref$ === "function" ? web.use(_ref$, _el$) : themeAnchor = _el$;
|
|
91
|
+
return _el$;
|
|
92
|
+
})(), web.createComponent(solidJs.Show, {
|
|
86
93
|
get when() {
|
|
87
94
|
return isVisible();
|
|
88
95
|
},
|
|
@@ -90,62 +97,63 @@ const ModalRenderer = (props) => {
|
|
|
90
97
|
return web.createComponent(web.Portal, {
|
|
91
98
|
get children() {
|
|
92
99
|
return [(() => {
|
|
93
|
-
var _el$ = web.getNextElement(_tmpl$
|
|
94
|
-
_el
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
var _el$2 = web.getNextElement(_tmpl$6), _el$3 = _el$2.firstChild, _el$13 = _el$3.firstChild, [_el$14, _co$4] = web.getNextMarker(_el$13.nextSibling), _el$12 = _el$14.nextSibling;
|
|
101
|
+
_el$2.$$click = handleBackdropClick;
|
|
102
|
+
web.use((element) => theme.bindPortalTheme(element, () => themeAnchor), _el$2);
|
|
103
|
+
_el$3.$$click = (e) => e.stopPropagation();
|
|
104
|
+
web.insert(_el$3, web.createComponent(solidJs.Show, {
|
|
97
105
|
get when() {
|
|
98
106
|
var _a, _b;
|
|
99
107
|
return ((_a = params()) == null ? void 0 : _a.title) || ((_b = params()) == null ? void 0 : _b.showClose) !== false;
|
|
100
108
|
},
|
|
101
109
|
get children() {
|
|
102
|
-
var _el$
|
|
103
|
-
web.insert(_el$
|
|
110
|
+
var _el$4 = web.getNextElement(_tmpl$5), _el$8 = _el$4.firstChild, [_el$9, _co$] = web.getNextMarker(_el$8.nextSibling), _el$0 = _el$9.nextSibling, [_el$1, _co$2] = web.getNextMarker(_el$0.nextSibling), _el$10 = _el$1.nextSibling, [_el$11, _co$3] = web.getNextMarker(_el$10.nextSibling);
|
|
111
|
+
web.insert(_el$4, web.createComponent(solidJs.Show, {
|
|
104
112
|
get when() {
|
|
105
113
|
var _a;
|
|
106
114
|
return (_a = params()) == null ? void 0 : _a.title;
|
|
107
115
|
},
|
|
108
116
|
get children() {
|
|
109
|
-
var _el$
|
|
110
|
-
web.insert(_el$
|
|
111
|
-
return _el$
|
|
117
|
+
var _el$5 = web.getNextElement(_tmpl$2);
|
|
118
|
+
web.insert(_el$5, () => params().title);
|
|
119
|
+
return _el$5;
|
|
112
120
|
}
|
|
113
|
-
}), _el$
|
|
114
|
-
web.insert(_el$
|
|
121
|
+
}), _el$9, _co$);
|
|
122
|
+
web.insert(_el$4, web.createComponent(solidJs.Show, {
|
|
115
123
|
get when() {
|
|
116
124
|
var _a;
|
|
117
125
|
return !((_a = params()) == null ? void 0 : _a.title);
|
|
118
126
|
},
|
|
119
127
|
get children() {
|
|
120
|
-
return web.getNextElement(_tmpl$
|
|
128
|
+
return web.getNextElement(_tmpl$3);
|
|
121
129
|
}
|
|
122
|
-
}), _el$
|
|
123
|
-
web.insert(_el$
|
|
130
|
+
}), _el$1, _co$2);
|
|
131
|
+
web.insert(_el$4, web.createComponent(solidJs.Show, {
|
|
124
132
|
get when() {
|
|
125
133
|
var _a;
|
|
126
134
|
return ((_a = params()) == null ? void 0 : _a.showClose) !== false;
|
|
127
135
|
},
|
|
128
136
|
get children() {
|
|
129
|
-
var _el$
|
|
130
|
-
_el$
|
|
131
|
-
web.effect(() => web.setAttribute(_el$
|
|
137
|
+
var _el$7 = web.getNextElement(_tmpl$4);
|
|
138
|
+
_el$7.$$click = handleClose;
|
|
139
|
+
web.effect(() => web.setAttribute(_el$7, "aria-label", strings.modalClose));
|
|
132
140
|
web.runHydrationEvents();
|
|
133
|
-
return _el$
|
|
141
|
+
return _el$7;
|
|
134
142
|
}
|
|
135
|
-
}), _el$
|
|
136
|
-
return _el$
|
|
143
|
+
}), _el$11, _co$3);
|
|
144
|
+
return _el$4;
|
|
137
145
|
}
|
|
138
|
-
}), _el$
|
|
139
|
-
web.insert(_el$
|
|
146
|
+
}), _el$14, _co$4);
|
|
147
|
+
web.insert(_el$12, () => props.children);
|
|
140
148
|
web.effect((_p$) => {
|
|
141
149
|
var _a, _b;
|
|
142
|
-
var _v$ = ((_a = params()) == null ? void 0 : _a.title) ? "modal-title" : void 0, _v$2 = `relative w-full ${sizeClass()} bg-white dark:bg-gray-800 rounded-lg shadow-xl`, _v$3 = ((_b = params()) == null ? void 0 : _b.maxHeight) ? {
|
|
150
|
+
var _v$ = ((_a = params()) == null ? void 0 : _a.title) ? "modal-title" : void 0, _v$2 = `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))]`, _v$3 = ((_b = params()) == null ? void 0 : _b.maxHeight) ? {
|
|
143
151
|
"max-height": params().maxHeight,
|
|
144
152
|
"overflow-y": "auto"
|
|
145
153
|
} : {};
|
|
146
|
-
_v$ !== _p$.e && web.setAttribute(_el
|
|
147
|
-
_v$2 !== _p$.t && web.className(_el$
|
|
148
|
-
_p$.a = web.style(_el$
|
|
154
|
+
_v$ !== _p$.e && web.setAttribute(_el$2, "aria-labelledby", _p$.e = _v$);
|
|
155
|
+
_v$2 !== _p$.t && web.className(_el$3, _p$.t = _v$2);
|
|
156
|
+
_p$.a = web.style(_el$12, _v$3, _p$.a);
|
|
149
157
|
return _p$;
|
|
150
158
|
}, {
|
|
151
159
|
e: void 0,
|
|
@@ -153,12 +161,12 @@ const ModalRenderer = (props) => {
|
|
|
153
161
|
a: void 0
|
|
154
162
|
});
|
|
155
163
|
web.runHydrationEvents();
|
|
156
|
-
return _el
|
|
157
|
-
})(), web.getNextElement(_tmpl$
|
|
164
|
+
return _el$2;
|
|
165
|
+
})(), web.getNextElement(_tmpl$7)];
|
|
158
166
|
}
|
|
159
167
|
});
|
|
160
168
|
}
|
|
161
|
-
});
|
|
169
|
+
})];
|
|
162
170
|
};
|
|
163
171
|
web.delegateEvents(["click"]);
|
|
164
172
|
exports.ModalRenderer = ModalRenderer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalRenderer.cjs","sources":["../../src/components/ModalRenderer.tsx"],"sourcesContent":["/**\n * ModalRenderer - Dialog overlay component\n * Sprint 3: UX Improvements\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport type { UIComponent, ModalComponentParams } from '../types'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface ModalRendererProps {\n /**\n * UIComponent with modal params (for declarative use)\n */\n component?: UIComponent\n\n /**\n * Direct modal params (alternative to component)\n */\n params?: ModalComponentParams\n\n /**\n * External open state control\n */\n isOpen?: boolean\n\n /**\n * Callback when modal is closed\n */\n onClose?: () => void\n\n /**\n * Modal content (preferred for programmatic use)\n */\n children?: JSX.Element\n}\n\n/**\n * Modal/Dialog overlay component with Portal rendering\n *\n * @example Programmatic usage\n * ```tsx\n * function MyComponent() {\n * const { isOpen, open, close } = useModal()\n *\n * return (\n * <>\n * <button onClick={open}>Open Modal</button>\n * <ModalRenderer\n * isOpen={isOpen()}\n * onClose={close}\n * params={{ title: 'My Modal', size: 'md' }}\n * >\n * <p>Modal content here</p>\n * </ModalRenderer>\n * </>\n * )\n * }\n * ```\n *\n * @example Declarative usage\n * ```tsx\n * const modalComponent: UIComponent = {\n * id: 'details-modal',\n * type: 'modal',\n * position: { colStart: 1, colSpan: 12 },\n * params: {\n * title: 'User Details',\n * size: 'lg',\n * },\n * }\n * <ModalRenderer component={modalComponent} isOpen={true} onClose={close} />\n * ```\n */\nexport const ModalRenderer: Component<ModalRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const [isVisible, setIsVisible] = createSignal(false)\n\n const params = () => props.params || (props.component?.params as ModalComponentParams) || {}\n\n // Sync with external isOpen prop\n createEffect(() => {\n if (props.isOpen !== undefined) {\n setIsVisible(props.isOpen)\n }\n })\n\n // Handle escape key\n createEffect(() => {\n if (!isVisible()) return\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && params()?.closeOnEscape !== false) {\n handleClose()\n }\n }\n\n document.addEventListener('keydown', handleEscape)\n onCleanup(() => document.removeEventListener('keydown', handleEscape))\n })\n\n // Prevent body scroll when modal is open\n createEffect(() => {\n if (isVisible()) {\n document.body.style.overflow = 'hidden'\n } else {\n document.body.style.overflow = ''\n }\n onCleanup(() => {\n document.body.style.overflow = ''\n })\n })\n\n // Focus trap - focus the modal when it opens\n createEffect(() => {\n if (isVisible()) {\n // Small delay to ensure the modal is rendered\n setTimeout(() => {\n const modal = document.querySelector('[role=\"dialog\"]')\n if (modal instanceof HTMLElement) {\n modal.focus()\n }\n }, 10)\n }\n })\n\n const handleClose = () => {\n setIsVisible(false)\n props.onClose?.()\n }\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget && params()?.closeOnBackdrop !== false) {\n handleClose()\n }\n }\n\n const sizeClass = () => {\n switch (params()?.size) {\n case 'sm':\n return 'max-w-md'\n case 'lg':\n return 'max-w-4xl'\n case 'xl':\n return 'max-w-6xl'\n case 'full':\n return 'max-w-full mx-4'\n default: // md\n return 'max-w-2xl'\n }\n }\n\n return (\n <Show when={isVisible()}>\n <Portal>\n {/* Backdrop */}\n <div\n class=\"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'modal-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={params()?.title ? 'modal-title' : undefined}\n tabIndex={-1}\n >\n {/* Modal container */}\n <div\n class={`relative w-full ${sizeClass()} bg-white dark:bg-gray-800 rounded-lg shadow-xl`}\n style={{ animation: 'modal-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <Show when={params()?.title || params()?.showClose !== false}>\n <div class=\"flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700\">\n <Show when={params()?.title}>\n <h2\n id=\"modal-title\"\n class=\"text-lg font-semibold text-gray-900 dark:text-white\"\n >\n {params()!.title}\n </h2>\n </Show>\n <Show when={!params()?.title}>\n <div />\n </Show>\n <Show when={params()?.showClose !== false}>\n <button\n onClick={handleClose}\n 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\"\n aria-label={strings.modalClose}\n >\n <svg\n class=\"w-5 h-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </Show>\n </div>\n </Show>\n\n {/* Content */}\n <div\n class=\"p-4\"\n style={params()?.maxHeight ? { 'max-height': params()!.maxHeight, 'overflow-y': 'auto' } : {}}\n >\n {props.children}\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n @keyframes modal-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes modal-scale-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n )\n}\n"],"names":["ModalRenderer","props","strings","useMCPUIStrings","isVisible","setIsVisible","createSignal","params","component","createEffect","isOpen","undefined","handleEscape","e","key","closeOnEscape","handleClose","document","addEventListener","onCleanup","removeEventListener","body","style","overflow","setTimeout","modal","querySelector","HTMLElement","focus","onClose","handleBackdropClick","target","currentTarget","closeOnBackdrop","sizeClass","size","_$createComponent","Show","when","children","Portal","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$12","_el$13","_co$4","_$getNextMarker","nextSibling","_el$11","$$click","stopPropagation","_$insert","title","showClose","_el$3","_tmpl$4","_el$7","_el$8","_co$","_el$9","_el$0","_co$2","_el$1","_el$10","_co$3","_el$4","_tmpl$","_tmpl$2","_el$6","_tmpl$3","_$effect","_$setAttribute","modalClose","_$runHydrationEvents","_p$","_v$","_v$2","_v$3","maxHeight","t","_$className","a","_$style","_tmpl$6","_$delegateEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ModalRenderer.cjs","sources":["../../src/components/ModalRenderer.tsx"],"sourcesContent":["/**\n * ModalRenderer - Dialog overlay component\n * Sprint 3: UX Improvements\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX } from 'solid-js'\nimport { Portal } from 'solid-js/web'\nimport { bindPortalTheme } from '../utils/theme'\nimport type { UIComponent, ModalComponentParams } from '../types'\nimport { useMCPUIStrings } from '../context/MCPUIStringsContext'\n\nexport interface ModalRendererProps {\n /**\n * UIComponent with modal params (for declarative use)\n */\n component?: UIComponent\n\n /**\n * Direct modal params (alternative to component)\n */\n params?: ModalComponentParams\n\n /**\n * External open state control\n */\n isOpen?: boolean\n\n /**\n * Callback when modal is closed\n */\n onClose?: () => void\n\n /**\n * Modal content (preferred for programmatic use)\n */\n children?: JSX.Element\n}\n\n/**\n * Modal/Dialog overlay component with Portal rendering\n *\n * @example Programmatic usage\n * ```tsx\n * function MyComponent() {\n * const { isOpen, open, close } = useModal()\n *\n * return (\n * <>\n * <button onClick={open}>Open Modal</button>\n * <ModalRenderer\n * isOpen={isOpen()}\n * onClose={close}\n * params={{ title: 'My Modal', size: 'md' }}\n * >\n * <p>Modal content here</p>\n * </ModalRenderer>\n * </>\n * )\n * }\n * ```\n *\n * @example Declarative usage\n * ```tsx\n * const modalComponent: UIComponent = {\n * id: 'details-modal',\n * type: 'modal',\n * position: { colStart: 1, colSpan: 12 },\n * params: {\n * title: 'User Details',\n * size: 'lg',\n * },\n * }\n * <ModalRenderer component={modalComponent} isOpen={true} onClose={close} />\n * ```\n */\nexport const ModalRenderer: Component<ModalRendererProps> = (props) => {\n const strings = useMCPUIStrings()\n const [isVisible, setIsVisible] = createSignal(false)\n let themeAnchor: HTMLSpanElement | undefined\n\n const params = () => props.params || (props.component?.params as ModalComponentParams) || {}\n\n // Sync with external isOpen prop\n createEffect(() => {\n if (props.isOpen !== undefined) {\n setIsVisible(props.isOpen)\n }\n })\n\n // Handle escape key\n createEffect(() => {\n if (!isVisible()) return\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && params()?.closeOnEscape !== false) {\n handleClose()\n }\n }\n\n document.addEventListener('keydown', handleEscape)\n onCleanup(() => document.removeEventListener('keydown', handleEscape))\n })\n\n // Prevent body scroll when modal is open\n createEffect(() => {\n if (isVisible()) {\n document.body.style.overflow = 'hidden'\n } else {\n document.body.style.overflow = ''\n }\n onCleanup(() => {\n document.body.style.overflow = ''\n })\n })\n\n // Focus trap - focus the modal when it opens\n createEffect(() => {\n if (isVisible()) {\n // Small delay to ensure the modal is rendered\n setTimeout(() => {\n const modal = document.querySelector('[role=\"dialog\"]')\n if (modal instanceof HTMLElement) {\n modal.focus()\n }\n }, 10)\n }\n })\n\n const handleClose = () => {\n setIsVisible(false)\n props.onClose?.()\n }\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget && params()?.closeOnBackdrop !== false) {\n handleClose()\n }\n }\n\n const sizeClass = () => {\n switch (params()?.size) {\n case 'sm':\n return 'max-w-md'\n case 'lg':\n return 'max-w-4xl'\n case 'xl':\n return 'max-w-6xl'\n case 'full':\n return 'max-w-full mx-4'\n default: // md\n return 'max-w-2xl'\n }\n }\n\n return (\n <>\n <span hidden aria-hidden=\"true\" ref={themeAnchor} />\n <Show when={isVisible()}>\n <Portal>\n {/* Backdrop */}\n <div\n ref={(element) => bindPortalTheme(element, () => themeAnchor)}\n 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\"\n style={{ animation: 'modal-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={params()?.title ? 'modal-title' : undefined}\n tabIndex={-1}\n >\n {/* Modal container */}\n <div\n 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))]`}\n style={{ animation: 'modal-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <Show when={params()?.title || params()?.showClose !== false}>\n <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))]\">\n <Show when={params()?.title}>\n <h2\n id=\"modal-title\"\n 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))]\"\n >\n {params()!.title}\n </h2>\n </Show>\n <Show when={!params()?.title}>\n <div />\n </Show>\n <Show when={params()?.showClose !== false}>\n <button\n onClick={handleClose}\n 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\"\n aria-label={strings.modalClose}\n >\n <svg\n class=\"w-5 h-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </Show>\n </div>\n </Show>\n\n {/* Content */}\n <div\n class=\"p-4\"\n style={params()?.maxHeight ? { 'max-height': params()!.maxHeight, 'overflow-y': 'auto' } : {}}\n >\n {props.children}\n </div>\n </div>\n </div>\n\n {/* CSS Animations */}\n <style>{`\n @keyframes modal-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes modal-scale-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </>\n )\n}\n"],"names":["ModalRenderer","props","strings","useMCPUIStrings","isVisible","setIsVisible","createSignal","themeAnchor","params","component","createEffect","isOpen","undefined","handleEscape","e","key","closeOnEscape","handleClose","document","addEventListener","onCleanup","removeEventListener","body","style","overflow","setTimeout","modal","querySelector","HTMLElement","focus","onClose","handleBackdropClick","target","currentTarget","closeOnBackdrop","sizeClass","size","_el$","_$getNextElement","_tmpl$","_ref$","_$use","_$createComponent","Show","when","children","Portal","_el$2","_tmpl$6","_el$3","firstChild","_el$13","_el$14","_co$4","_$getNextMarker","nextSibling","_el$12","$$click","element","bindPortalTheme","stopPropagation","_$insert","title","showClose","_el$4","_tmpl$5","_el$8","_el$9","_co$","_el$0","_el$1","_co$2","_el$10","_el$11","_co$3","_el$5","_tmpl$2","_tmpl$3","_el$7","_tmpl$4","_$effect","_$setAttribute","modalClose","_$runHydrationEvents","_p$","_v$","_v$2","_v$3","maxHeight","t","_$className","a","_$style","_tmpl$7","_$delegateEvents"],"mappings":";;;;;;;;;;;;;;;;AA2EO,MAAMA,gBAAgDC,CAAAA,UAAU;AACrE,QAAMC,UAAUC,oBAAAA,gBAAAA;AAChB,QAAM,CAACC,WAAWC,YAAY,IAAIC,QAAAA,aAAa,KAAK;AACpD,MAAIC;AAEJ,QAAMC,SAASA,MAAAA;;AAAMP,iBAAMO,YAAWP,WAAMQ,cAANR,mBAAiBO,WAAmC,CAAA;AAAA;AAG1FE,UAAAA,aAAa,MAAM;AACjB,QAAIT,MAAMU,WAAWC,QAAW;AAC9BP,mBAAaJ,MAAMU,MAAM;AAAA,IAC3B;AAAA,EACF,CAAC;AAGDD,UAAAA,aAAa,MAAM;AACjB,QAAI,CAACN,YAAa;AAElB,UAAMS,eAAeA,CAACC,MAAqB;;AACzC,UAAIA,EAAEC,QAAQ,cAAYP,YAAAA,MAAAA,mBAAUQ,mBAAkB,OAAO;AAC3DC,oBAAAA;AAAAA,MACF;AAAA,IACF;AAEAC,aAASC,iBAAiB,WAAWN,YAAY;AACjDO,YAAAA,UAAU,MAAMF,SAASG,oBAAoB,WAAWR,YAAY,CAAC;AAAA,EACvE,CAAC;AAGDH,UAAAA,aAAa,MAAM;AACjB,QAAIN,aAAa;AACfc,eAASI,KAAKC,MAAMC,WAAW;AAAA,IACjC,OAAO;AACLN,eAASI,KAAKC,MAAMC,WAAW;AAAA,IACjC;AACAJ,YAAAA,UAAU,MAAM;AACdF,eAASI,KAAKC,MAAMC,WAAW;AAAA,IACjC,CAAC;AAAA,EACH,CAAC;AAGDd,UAAAA,aAAa,MAAM;AACjB,QAAIN,aAAa;AAEfqB,iBAAW,MAAM;AACf,cAAMC,QAAQR,SAASS,cAAc,iBAAiB;AACtD,YAAID,iBAAiBE,aAAa;AAChCF,gBAAMG,MAAAA;AAAAA,QACR;AAAA,MACF,GAAG,EAAE;AAAA,IACP;AAAA,EACF,CAAC;AAED,QAAMZ,cAAcA,MAAM;;AACxBZ,iBAAa,KAAK;AAClBJ,gBAAM6B,YAAN7B;AAAAA,EACF;AAEA,QAAM8B,sBAAsBA,CAACjB,MAAkB;;AAC7C,QAAIA,EAAEkB,WAAWlB,EAAEmB,mBAAiBzB,YAAAA,MAAAA,mBAAU0B,qBAAoB,OAAO;AACvEjB,kBAAAA;AAAAA,IACF;AAAA,EACF;AAEA,QAAMkB,YAAYA,MAAM;;AACtB,aAAQ3B,YAAAA,MAAAA,mBAAU4B,MAAAA;AAAAA,MAChB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AAEA,SAAA,EAAA,MAAA;AAAA,QAAAC,OAAAC,IAAAA,eAAAC,MAAA;AAAA,QAAAC,QAEuCjC;AAAW,WAAAiC,UAAA,aAAAC,IAAAA,IAAAD,OAAAH,IAAA,IAAX9B,cAAW8B;AAAA,WAAAA;AAAAA,EAAA,GAAA,GAAAK,IAAAA,gBAC/CC,cAAI;AAAA,IAAA,IAACC,OAAI;AAAA,aAAExC,UAAAA;AAAAA,IAAW;AAAA,IAAA,IAAAyC,WAAA;AAAA,aAAAH,IAAAA,gBACpBI,IAAAA,QAAM;AAAA,QAAA,IAAAD,WAAA;AAAA,iBAAA,EAAA,MAAA;AAAA,gBAAAE,QAAAT,IAAAA,eAAAU,OAAA,GAAAC,QAAAF,MAAAG,YAAAC,SAAAF,MAAAC,YAAA,CAAAE,QAAAC,KAAA,IAAAC,IAAAA,cAAAH,OAAAI,WAAA,GAAAC,SAAAJ,OAAAG;AAAAR,kBAAAU,UAMM1B;AAAmBU,gBAAAA,IAHtBiB,aAAYC,MAAAA,gBAAgBD,SAAS,MAAMnD,WAAW,GAACwC,KAAA;AAAAE,kBAAAQ,UAajD3C,CAAAA,MAAMA,EAAE8C,gBAAAA;AAAiBC,uBAAAZ,OAAAP,IAAAA,gBAGlCC,cAAI;AAAA,cAAA,IAACC,OAAI;;AAAA,yBAAEpC,YAAAA,MAAAA,mBAAUsD,YAAStD,YAAAA,MAAAA,mBAAUuD,eAAc;AAAA,cAAK;AAAA,cAAA,IAAAlB,WAAA;AAAA,oBAAAmB,QAAA1B,IAAAA,eAAA2B,OAAA,GAAAC,QAAAF,MAAAd,YAAA,CAAAiB,OAAAC,IAAA,IAAAd,IAAAA,cAAAY,MAAAX,WAAA,GAAAc,QAAAF,MAAAZ,aAAA,CAAAe,OAAAC,KAAA,IAAAjB,IAAAA,cAAAe,MAAAd,WAAA,GAAAiB,SAAAF,MAAAf,aAAA,CAAAkB,QAAAC,KAAA,IAAApB,IAAAA,cAAAkB,OAAAjB,WAAA;AAAAM,2BAAAG,OAAAtB,IAAAA,gBAEvDC,cAAI;AAAA,kBAAA,IAACC,OAAI;;AAAA,4BAAEpC,kBAAAA,mBAAUsD;AAAAA,kBAAK;AAAA,kBAAA,IAAAjB,WAAA;AAAA,wBAAA8B,QAAArC,IAAAA,eAAAsC,OAAA;AAAAf,wBAAAA,OAAAc,OAAA,MAKtBnE,OAAAA,EAAUsD,KAAK;AAAA,2BAAAa;AAAAA,kBAAA;AAAA,gBAAA,CAAA,GAAAR,OAAAC,IAAA;AAAAP,2BAAAG,OAAAtB,IAAAA,gBAGnBC,cAAI;AAAA,kBAAA,IAACC,OAAI;;AAAA,2BAAE,GAACpC,kBAAAA,mBAAUsD;AAAAA,kBAAK;AAAA,kBAAA,IAAAjB,WAAA;AAAA,2BAAAP,IAAAA,eAAAuC,OAAA;AAAA,kBAAA;AAAA,gBAAA,CAAA,GAAAP,OAAAC,KAAA;AAAAV,2BAAAG,OAAAtB,IAAAA,gBAG3BC,cAAI;AAAA,kBAAA,IAACC,OAAI;;AAAA,6BAAEpC,YAAAA,MAAAA,mBAAUuD,eAAc;AAAA,kBAAK;AAAA,kBAAA,IAAAlB,WAAA;AAAA,wBAAAiC,QAAAxC,IAAAA,eAAAyC,OAAA;AAAAD,0BAAArB,UAE5BxC;AAAW+D,wBAAAA,aAAAC,IAAAA,aAAAH,OAAA,cAER5E,QAAQgF,UAAU,CAAA;AAAAC,2CAAAA;AAAA,2BAAAL;AAAAA,kBAAA;AAAA,gBAAA,CAAA,GAAAL,QAAAC,KAAA;AAAA,uBAAAV;AAAAA,cAAA;AAAA,YAAA,CAAA,GAAAZ,QAAAC,KAAA;AAAAQ,gBAAAA,OAAAL,QAAA,MAyBnCvD,MAAM4C,QAAQ;AAAAmC,gBAAAA,OAAAI,CAAAA,QAAA;;AAAA,kBAAAC,QApDF7E,YAAAA,MAAAA,mBAAUsD,SAAQ,gBAAgBlD,QAAS0E,OAKnD,mBAAmBnD,UAAAA,CAAW,2LAAyLoD,SA6CrN/E,YAAAA,MAAAA,mBAAUgF,aAAY;AAAA,gBAAE,cAAchF,SAAUgF;AAAAA,gBAAW,cAAc;AAAA,cAAA,IAAW,CAAA;AAAEH,sBAAAD,IAAAtE,KAAAmE,IAAAA,aAAAlC,OAAA,mBAAAqC,IAAAtE,IAAAuE,GAAA;AAAAC,uBAAAF,IAAAK,KAAAC,IAAAA,UAAAzC,OAAAmC,IAAAK,IAAAH,IAAA;AAAAF,kBAAAO,IAAAC,IAAAA,MAAApC,QAAA+B,MAAAH,IAAAO,CAAA;AAAA,qBAAAP;AAAAA,YAAA,GAAA;AAAA,cAAAtE,GAAAF;AAAAA,cAAA6E,GAAA7E;AAAAA,cAAA+E,GAAA/E;AAAAA,YAAAA,CAAA;AAAAuE,mCAAAA;AAAA,mBAAApC;AAAAA,UAAA,GAAA,GAAAT,mBAAAuD,OAAA,CAAA;AAAA,QAAA;AAAA,MAAA,CAAA;AAAA,IAAA;AAAA,EAAA,CAAA,CAAA;AAsB3G;AAACC,IAAAA,eAAA,CAAA,OAAA,CAAA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ModalRenderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ModalRenderer.d.ts","sourceRoot":"","sources":["../../src/components/ModalRenderer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAAE,MAAM,UAAU,CAAA;AAGtF,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAGjE,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,WAAW,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAE7B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAoKvD,CAAA"}
|