@seed-ship/mcp-ui-solid 6.3.1 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/components/GraphRenderer.cjs +30 -24
- package/dist/components/GraphRenderer.cjs.map +1 -1
- package/dist/components/GraphRenderer.d.ts.map +1 -1
- package/dist/components/GraphRenderer.js +30 -24
- package/dist/components/GraphRenderer.js.map +1 -1
- package/dist/components/PortalDropdownMenu.cjs +82 -0
- package/dist/components/PortalDropdownMenu.cjs.map +1 -0
- package/dist/components/PortalDropdownMenu.d.ts +56 -0
- package/dist/components/PortalDropdownMenu.d.ts.map +1 -0
- package/dist/components/PortalDropdownMenu.js +82 -0
- package/dist/components/PortalDropdownMenu.js.map +1 -0
- package/dist/components/UIResourceRenderer.cjs +260 -256
- package/dist/components/UIResourceRenderer.cjs.map +1 -1
- package/dist/components/UIResourceRenderer.d.ts.map +1 -1
- package/dist/components/UIResourceRenderer.js +260 -256
- package/dist/components/UIResourceRenderer.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +2 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +2 -0
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/GraphRenderer.tsx +29 -20
- package/src/components/PortalDropdownMenu.test.tsx +113 -0
- package/src/components/PortalDropdownMenu.tsx +130 -0
- package/src/components/UIResourceRenderer.tsx +22 -13
- package/src/components/index.ts +4 -0
- package/src/index.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [6.4.0] - 2026-05-03
|
|
9
|
+
|
|
10
|
+
Closes axe 3 of `deposium_solid`'s
|
|
11
|
+
`SOLID-MCPUI-IMPROVEMENT-AXES-2026-05-03.md` handoff.
|
|
12
|
+
|
|
13
|
+
### Changed — Export menus now mount via `<Portal>`
|
|
14
|
+
|
|
15
|
+
Both Export dropdowns shipped by the renderers — the `TableRenderer`
|
|
16
|
+
(`Copy TSV / Download CSV / Download JSON`) and the `GraphRenderer`
|
|
17
|
+
(`Download PNG / Download Mermaid / Download JSON`) — now render via
|
|
18
|
+
`solid-js/web` `<Portal>` on `document.body` instead of an in-tree
|
|
19
|
+
`position: absolute` sibling.
|
|
20
|
+
|
|
21
|
+
This fixes two long-standing pain points :
|
|
22
|
+
|
|
23
|
+
1. **`overflow: hidden` clipping** — when the table or graph lives
|
|
24
|
+
inside a chat bubble, a card, or any ancestor with `overflow: hidden`,
|
|
25
|
+
the legacy in-tree menu got clipped at the ancestor's boundary.
|
|
26
|
+
Mounting on `document.body` escapes the clip stack entirely.
|
|
27
|
+
2. **`z-index` wars** — chat surfaces stack composer / message rails
|
|
28
|
+
above the message list, and ancestor `z-index` creates a new stacking
|
|
29
|
+
context that captured the in-tree menu. A portal is a sibling of the
|
|
30
|
+
document, so a single `z-index: 9999` wins.
|
|
31
|
+
|
|
32
|
+
Pre-v6.4.0 deposium had to ship a `overflow: visible !important`
|
|
33
|
+
override + 4 `z-index` overrides on its `ChatUIIsland.tsx` to work
|
|
34
|
+
around this. Both can now be removed.
|
|
35
|
+
|
|
36
|
+
### Added — `<PortalDropdownMenu>` (factored helper)
|
|
37
|
+
|
|
38
|
+
The portal-mounting + click-outside + Escape + scroll/resize
|
|
39
|
+
re-positioning logic is factored into a generic component for reuse.
|
|
40
|
+
Exported under both `@seed-ship/mcp-ui-solid` root and
|
|
41
|
+
`@seed-ship/mcp-ui-solid/components` :
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { PortalDropdownMenu } from '@seed-ship/mcp-ui-solid'
|
|
45
|
+
|
|
46
|
+
const [open, setOpen] = createSignal(false)
|
|
47
|
+
let triggerRef: HTMLButtonElement | undefined
|
|
48
|
+
|
|
49
|
+
<button ref={triggerRef} onClick={() => setOpen(true)}>Menu</button>
|
|
50
|
+
<PortalDropdownMenu open={open()} onClose={() => setOpen(false)} trigger={triggerRef}>
|
|
51
|
+
<button onClick={...}>Item 1</button>
|
|
52
|
+
<button onClick={...}>Item 2</button>
|
|
53
|
+
</PortalDropdownMenu>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Non-breaking
|
|
57
|
+
|
|
58
|
+
The behavior change is transparent to consumers — the menu still opens
|
|
59
|
+
and closes from the same trigger button, with the same items. Tests :
|
|
60
|
+
7 added in `PortalDropdownMenu.test.tsx` (mount target, position from
|
|
61
|
+
`getBoundingClientRect`, outside click + Escape close, trigger /
|
|
62
|
+
in-menu mousedown ignored).
|
|
63
|
+
|
|
8
64
|
## [6.3.1] - 2026-05-03
|
|
9
65
|
|
|
10
66
|
### Added — `<UIResourceRenderer toolbarVariant>` forwarding
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const web = require("solid-js/web");
|
|
4
4
|
const solidJs = require("solid-js");
|
|
5
5
|
const ExpandableWrapper = require("./ExpandableWrapper.cjs");
|
|
6
|
-
|
|
6
|
+
const PortalDropdownMenu = require("./PortalDropdownMenu.cjs");
|
|
7
|
+
var _tmpl$ = /* @__PURE__ */ web.template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Render error: <!$><!/>`), _tmpl$2 = /* @__PURE__ */ web.template(`<div><div class="absolute right-2 top-2 z-10"><button type=button 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"title="Export graph"aria-label="Export graph"aria-haspopup=menu>Export ▾</button><!$><!/></div><div></div><!$><!/>`), _tmpl$3 = /* @__PURE__ */ web.template(`<div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Graph rendering unavailable</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code> components.`), _tmpl$4 = /* @__PURE__ */ web.template(`<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"><div class="h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2"></div><div class="h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded">`), _tmpl$5 = /* @__PURE__ */ web.template(`<button type=button 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"><div class=font-medium></div><div class="text-[10px] text-gray-500 dark:text-gray-400">`);
|
|
7
8
|
let g6ModulePromise;
|
|
8
9
|
async function isG6Available() {
|
|
9
10
|
try {
|
|
@@ -97,6 +98,7 @@ const GraphRenderer = (props) => {
|
|
|
97
98
|
const [error, setError] = solidJs.createSignal();
|
|
98
99
|
const [exportMenuOpen, setExportMenuOpen] = solidJs.createSignal(false);
|
|
99
100
|
let containerRef;
|
|
101
|
+
let exportTriggerRef;
|
|
100
102
|
let graphInstance;
|
|
101
103
|
solidJs.onMount(async () => {
|
|
102
104
|
const g6Available = await isG6Available();
|
|
@@ -186,11 +188,11 @@ const GraphRenderer = (props) => {
|
|
|
186
188
|
get fallback() {
|
|
187
189
|
return (
|
|
188
190
|
// Loading skeleton while we determine peer availability
|
|
189
|
-
web.getNextElement(_tmpl$
|
|
191
|
+
web.getNextElement(_tmpl$4)
|
|
190
192
|
);
|
|
191
193
|
},
|
|
192
194
|
get children() {
|
|
193
|
-
return web.getNextElement(_tmpl$
|
|
195
|
+
return web.getNextElement(_tmpl$3);
|
|
194
196
|
}
|
|
195
197
|
});
|
|
196
198
|
},
|
|
@@ -207,15 +209,20 @@ const GraphRenderer = (props) => {
|
|
|
207
209
|
return props.toolbarVariant;
|
|
208
210
|
},
|
|
209
211
|
get children() {
|
|
210
|
-
var _el$ = web.getNextElement(_tmpl$
|
|
212
|
+
var _el$ = web.getNextElement(_tmpl$2), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, [_el$5, _co$] = web.getNextMarker(_el$4.nextSibling), _el$6 = _el$2.nextSibling, _el$1 = _el$6.nextSibling, [_el$10, _co$3] = web.getNextMarker(_el$1.nextSibling);
|
|
211
213
|
_el$3.$$click = () => setExportMenuOpen((v) => !v);
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
var _ref$ = exportTriggerRef;
|
|
215
|
+
typeof _ref$ === "function" ? web.use(_ref$, _el$3) : exportTriggerRef = _el$3;
|
|
216
|
+
web.insert(_el$2, web.createComponent(PortalDropdownMenu.PortalDropdownMenu, {
|
|
217
|
+
get open() {
|
|
214
218
|
return exportMenuOpen();
|
|
215
219
|
},
|
|
220
|
+
onClose: () => setExportMenuOpen(false),
|
|
221
|
+
trigger: exportTriggerRef,
|
|
222
|
+
width: 176,
|
|
223
|
+
"class": "text-xs",
|
|
216
224
|
get children() {
|
|
217
|
-
|
|
218
|
-
web.insert(_el$4, web.createComponent(solidJs.For, {
|
|
225
|
+
return web.createComponent(solidJs.For, {
|
|
219
226
|
each: [{
|
|
220
227
|
label: "Download PNG",
|
|
221
228
|
onClick: handleExportPNG,
|
|
@@ -230,35 +237,34 @@ const GraphRenderer = (props) => {
|
|
|
230
237
|
hint: "raw data"
|
|
231
238
|
}],
|
|
232
239
|
children: (item) => (() => {
|
|
233
|
-
var _el$
|
|
234
|
-
web.addEventListener(_el$
|
|
235
|
-
web.insert(_el$
|
|
236
|
-
web.insert(_el$
|
|
240
|
+
var _el$13 = web.getNextElement(_tmpl$5), _el$14 = _el$13.firstChild, _el$15 = _el$14.nextSibling;
|
|
241
|
+
web.addEventListener(_el$13, "click", item.onClick, true);
|
|
242
|
+
web.insert(_el$14, () => item.label);
|
|
243
|
+
web.insert(_el$15, () => item.hint);
|
|
237
244
|
web.runHydrationEvents();
|
|
238
|
-
return _el$
|
|
245
|
+
return _el$13;
|
|
239
246
|
})()
|
|
240
|
-
})
|
|
241
|
-
return _el$4;
|
|
247
|
+
});
|
|
242
248
|
}
|
|
243
|
-
}), _el$
|
|
244
|
-
var _ref$ = containerRef;
|
|
245
|
-
typeof _ref$ === "function" ? web.use(_ref
|
|
249
|
+
}), _el$5, _co$);
|
|
250
|
+
var _ref$2 = containerRef;
|
|
251
|
+
typeof _ref$2 === "function" ? web.use(_ref$2, _el$6) : containerRef = _el$6;
|
|
246
252
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
247
253
|
get when() {
|
|
248
254
|
return error();
|
|
249
255
|
},
|
|
250
256
|
get children() {
|
|
251
|
-
var _el$
|
|
252
|
-
web.insert(_el$
|
|
253
|
-
return _el$
|
|
257
|
+
var _el$7 = web.getNextElement(_tmpl$), _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, [_el$0, _co$2] = web.getNextMarker(_el$9.nextSibling);
|
|
258
|
+
web.insert(_el$7, error, _el$0, _co$2);
|
|
259
|
+
return _el$7;
|
|
254
260
|
}
|
|
255
|
-
}), _el$
|
|
261
|
+
}), _el$10, _co$3);
|
|
256
262
|
web.effect((_p$) => {
|
|
257
263
|
var _v$ = `relative w-full ${params().className ?? ""} ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = exportMenuOpen(), _v$3 = `bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$4 = isExpanded() ? `height: 100%; width: ${params().width ?? "100%"};` : `height: ${params().height ?? "400px"}; width: ${params().width ?? "100%"};`;
|
|
258
264
|
_v$ !== _p$.e && web.className(_el$, _p$.e = _v$);
|
|
259
265
|
_v$2 !== _p$.t && web.setAttribute(_el$3, "aria-expanded", _p$.t = _v$2);
|
|
260
|
-
_v$3 !== _p$.a && web.className(_el$
|
|
261
|
-
_p$.o = web.style(_el$
|
|
266
|
+
_v$3 !== _p$.a && web.className(_el$6, _p$.a = _v$3);
|
|
267
|
+
_p$.o = web.style(_el$6, _v$4, _p$.o);
|
|
262
268
|
return _p$;
|
|
263
269
|
}, {
|
|
264
270
|
e: void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphRenderer.cjs","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, GraphNode, GraphEdge } from '@seed-ship/mcp-ui-spec'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\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 `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.replace(/[\"[\\]|]/g, '').replace(/\\s+/g, ' ').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 = 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 params = () => props.component.params as GraphComponentParams\n const isExpanded = useExpanded()\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 // 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 renderer: p.rendererPref === 'svg' ? 'svg' : 'canvas',\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 ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`\n },\n },\n ]\n }\n graphInstance = new (Graph as any)(config)\n await graphInstance.render()\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to render graph')\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('PNG export not supported in current renderer mode')\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`)\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'PNG export failed')\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 Graph rendering unavailable\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> components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? 'Graph'}\n copyData={toJSON(params())}\n copyLabel=\"Copy graph (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\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=\"Export graph\"\n aria-label=\"Export graph\"\n aria-expanded={exportMenuOpen()}\n >\n Export ▾\n </button>\n <Show when={exportMenuOpen()}>\n <div class=\"absolute right-0 mt-1 w-44 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-lg text-xs\">\n <For each={[\n { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },\n { label: 'Download Mermaid', onClick: handleExportMermaid, hint: 'markdown / GitHub' },\n { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },\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 </div>\n </Show>\n </div>\n\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 isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n <Show when={error()}>\n <p class=\"text-xs text-red-600 dark:text-red-400 mt-1\">Render error: {error()}</p>\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\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&': return '&'\n case '<': return '<'\n case '>': return '>'\n case '\"': return '"'\n case \"'\": return '''\n default: return c\n }\n })\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","component","isExpanded","useExpanded","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","renderer","rendererPref","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","Error","message","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$5","children","_tmpl$4","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$5","nextSibling","_el$6","_co$","_$getNextMarker","_el$7","_el$10","_el$11","_co$3","$$click","v","_$insert","_el$4","_tmpl$","For","each","onClick","hint","_el$14","_tmpl$6","_el$15","_el$16","_$addEventListener","_$runHydrationEvents","_ref$","_$use","_el$8","_tmpl$2","_el$9","_el$0","_el$1","_co$2","_$effect","_p$","_v$","className","_v$2","_v$3","_v$4","width","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","c","_$delegateEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GraphRenderer.cjs","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, GraphNode, GraphEdge } from '@seed-ship/mcp-ui-spec'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { PortalDropdownMenu } from './PortalDropdownMenu'\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 `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.replace(/[\"[\\]|]/g, '').replace(/\\s+/g, ' ').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 = 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 params = () => props.component.params as GraphComponentParams\n const isExpanded = useExpanded()\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 renderer: p.rendererPref === 'svg' ? 'svg' : 'canvas',\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 ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`\n },\n },\n ]\n }\n graphInstance = new (Graph as any)(config)\n await graphInstance.render()\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to render graph')\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('PNG export not supported in current renderer mode')\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`)\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'PNG export failed')\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 Graph rendering unavailable\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> components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? 'Graph'}\n copyData={toJSON(params())}\n copyLabel=\"Copy graph (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\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=\"Export graph\"\n aria-label=\"Export graph\"\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n Export ▾\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For each={[\n { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },\n { label: 'Download Mermaid', onClick: handleExportMermaid, hint: 'markdown / GitHub' },\n { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },\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 <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 isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n <Show when={error()}>\n <p class=\"text-xs text-red-600 dark:text-red-400 mt-1\">Render error: {error()}</p>\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\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&': return '&'\n case '<': return '<'\n case '>': return '>'\n case '\"': return '"'\n case \"'\": return '''\n default: return c\n }\n })\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","component","isExpanded","useExpanded","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","renderer","rendererPref","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","Error","message","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$4","children","_tmpl$3","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","_el$","_tmpl$2","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$1","_el$10","_co$3","$$click","v","_ref$","_$use","_$insert","PortalDropdownMenu","open","onClose","trigger","width","For","each","onClick","hint","_el$13","_tmpl$5","_el$14","_el$15","_$addEventListener","_$runHydrationEvents","_ref$2","_el$7","_tmpl$","_el$8","_el$9","_el$0","_co$2","_$effect","_p$","_v$","className","_v$2","_v$3","_v$4","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","c","_$delegateEvents"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GraphRenderer.d.ts","sourceRoot":"","sources":["../../src/components/GraphRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAA;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAqC,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"GraphRenderer.d.ts","sourceRoot":"","sources":["../../src/components/GraphRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,SAAS,EAA+C,MAAM,UAAU,CAAA;AACjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAqC,MAAM,wBAAwB,CAAA;AAQrG;;;;;GAKG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAUtD;AAoDD;;;GAGG;AACH,iBAAS,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAoBvD;AAED,iBAAS,MAAM,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAEpD;AAcD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,WAAW,CAAA;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,kBAAkB,CAgMvD,CAAA;AA6BD,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,IAAI,WAAW,EAAE,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { delegateEvents, createComponent, getNextElement, template, getNextMarker, insert, addEventListener, runHydrationEvents, effect, className, setAttribute, style, use } from "solid-js/web";
|
|
2
2
|
import { createSignal, onMount, onCleanup, Show, For } from "solid-js";
|
|
3
3
|
import { useExpanded, ExpandableWrapper } from "./ExpandableWrapper.js";
|
|
4
|
-
|
|
4
|
+
import { PortalDropdownMenu } from "./PortalDropdownMenu.js";
|
|
5
|
+
var _tmpl$ = /* @__PURE__ */ template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Render error: <!$><!/>`), _tmpl$2 = /* @__PURE__ */ template(`<div><div class="absolute right-2 top-2 z-10"><button type=button 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"title="Export graph"aria-label="Export graph"aria-haspopup=menu>Export ▾</button><!$><!/></div><div></div><!$><!/>`), _tmpl$3 = /* @__PURE__ */ template(`<div class="w-full p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg"><p class="text-sm font-medium text-yellow-900 dark:text-yellow-100">Graph rendering unavailable</p><p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">Install <code>@antv/g6</code> peer dependency to render <code>type: "graph"</code> components.`), _tmpl$4 = /* @__PURE__ */ template(`<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"><div class="h-4 w-32 bg-gray-200 dark:bg-gray-700 rounded mb-2"></div><div class="h-3 w-48 bg-gray-200 dark:bg-gray-700 rounded">`), _tmpl$5 = /* @__PURE__ */ template(`<button type=button 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"><div class=font-medium></div><div class="text-[10px] text-gray-500 dark:text-gray-400">`);
|
|
5
6
|
let g6ModulePromise;
|
|
6
7
|
async function isG6Available() {
|
|
7
8
|
try {
|
|
@@ -95,6 +96,7 @@ const GraphRenderer = (props) => {
|
|
|
95
96
|
const [error, setError] = createSignal();
|
|
96
97
|
const [exportMenuOpen, setExportMenuOpen] = createSignal(false);
|
|
97
98
|
let containerRef;
|
|
99
|
+
let exportTriggerRef;
|
|
98
100
|
let graphInstance;
|
|
99
101
|
onMount(async () => {
|
|
100
102
|
const g6Available = await isG6Available();
|
|
@@ -184,11 +186,11 @@ const GraphRenderer = (props) => {
|
|
|
184
186
|
get fallback() {
|
|
185
187
|
return (
|
|
186
188
|
// Loading skeleton while we determine peer availability
|
|
187
|
-
getNextElement(_tmpl$
|
|
189
|
+
getNextElement(_tmpl$4)
|
|
188
190
|
);
|
|
189
191
|
},
|
|
190
192
|
get children() {
|
|
191
|
-
return getNextElement(_tmpl$
|
|
193
|
+
return getNextElement(_tmpl$3);
|
|
192
194
|
}
|
|
193
195
|
});
|
|
194
196
|
},
|
|
@@ -205,15 +207,20 @@ const GraphRenderer = (props) => {
|
|
|
205
207
|
return props.toolbarVariant;
|
|
206
208
|
},
|
|
207
209
|
get children() {
|
|
208
|
-
var _el$ = getNextElement(_tmpl$
|
|
210
|
+
var _el$ = getNextElement(_tmpl$2), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling, [_el$5, _co$] = getNextMarker(_el$4.nextSibling), _el$6 = _el$2.nextSibling, _el$1 = _el$6.nextSibling, [_el$10, _co$3] = getNextMarker(_el$1.nextSibling);
|
|
209
211
|
_el$3.$$click = () => setExportMenuOpen((v) => !v);
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
var _ref$ = exportTriggerRef;
|
|
213
|
+
typeof _ref$ === "function" ? use(_ref$, _el$3) : exportTriggerRef = _el$3;
|
|
214
|
+
insert(_el$2, createComponent(PortalDropdownMenu, {
|
|
215
|
+
get open() {
|
|
212
216
|
return exportMenuOpen();
|
|
213
217
|
},
|
|
218
|
+
onClose: () => setExportMenuOpen(false),
|
|
219
|
+
trigger: exportTriggerRef,
|
|
220
|
+
width: 176,
|
|
221
|
+
"class": "text-xs",
|
|
214
222
|
get children() {
|
|
215
|
-
|
|
216
|
-
insert(_el$4, createComponent(For, {
|
|
223
|
+
return createComponent(For, {
|
|
217
224
|
each: [{
|
|
218
225
|
label: "Download PNG",
|
|
219
226
|
onClick: handleExportPNG,
|
|
@@ -228,35 +235,34 @@ const GraphRenderer = (props) => {
|
|
|
228
235
|
hint: "raw data"
|
|
229
236
|
}],
|
|
230
237
|
children: (item) => (() => {
|
|
231
|
-
var _el$
|
|
232
|
-
addEventListener(_el$
|
|
233
|
-
insert(_el$
|
|
234
|
-
insert(_el$
|
|
238
|
+
var _el$13 = getNextElement(_tmpl$5), _el$14 = _el$13.firstChild, _el$15 = _el$14.nextSibling;
|
|
239
|
+
addEventListener(_el$13, "click", item.onClick, true);
|
|
240
|
+
insert(_el$14, () => item.label);
|
|
241
|
+
insert(_el$15, () => item.hint);
|
|
235
242
|
runHydrationEvents();
|
|
236
|
-
return _el$
|
|
243
|
+
return _el$13;
|
|
237
244
|
})()
|
|
238
|
-
})
|
|
239
|
-
return _el$4;
|
|
245
|
+
});
|
|
240
246
|
}
|
|
241
|
-
}), _el$
|
|
242
|
-
var _ref$ = containerRef;
|
|
243
|
-
typeof _ref$ === "function" ? use(_ref
|
|
247
|
+
}), _el$5, _co$);
|
|
248
|
+
var _ref$2 = containerRef;
|
|
249
|
+
typeof _ref$2 === "function" ? use(_ref$2, _el$6) : containerRef = _el$6;
|
|
244
250
|
insert(_el$, createComponent(Show, {
|
|
245
251
|
get when() {
|
|
246
252
|
return error();
|
|
247
253
|
},
|
|
248
254
|
get children() {
|
|
249
|
-
var _el$
|
|
250
|
-
insert(_el$
|
|
251
|
-
return _el$
|
|
255
|
+
var _el$7 = getNextElement(_tmpl$), _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, [_el$0, _co$2] = getNextMarker(_el$9.nextSibling);
|
|
256
|
+
insert(_el$7, error, _el$0, _co$2);
|
|
257
|
+
return _el$7;
|
|
252
258
|
}
|
|
253
|
-
}), _el$
|
|
259
|
+
}), _el$10, _co$3);
|
|
254
260
|
effect((_p$) => {
|
|
255
261
|
var _v$ = `relative w-full ${params().className ?? ""} ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$2 = exportMenuOpen(), _v$3 = `bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$4 = isExpanded() ? `height: 100%; width: ${params().width ?? "100%"};` : `height: ${params().height ?? "400px"}; width: ${params().width ?? "100%"};`;
|
|
256
262
|
_v$ !== _p$.e && className(_el$, _p$.e = _v$);
|
|
257
263
|
_v$2 !== _p$.t && setAttribute(_el$3, "aria-expanded", _p$.t = _v$2);
|
|
258
|
-
_v$3 !== _p$.a && className(_el$
|
|
259
|
-
_p$.o = style(_el$
|
|
264
|
+
_v$3 !== _p$.a && className(_el$6, _p$.a = _v$3);
|
|
265
|
+
_p$.o = style(_el$6, _v$4, _p$.o);
|
|
260
266
|
return _p$;
|
|
261
267
|
}, {
|
|
262
268
|
e: void 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 { GraphComponentParams, GraphLayout, GraphNode, GraphEdge } from '@seed-ship/mcp-ui-spec'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\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 `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.replace(/[\"[\\]|]/g, '').replace(/\\s+/g, ' ').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 = 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 params = () => props.component.params as GraphComponentParams\n const isExpanded = useExpanded()\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 // 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 renderer: p.rendererPref === 'svg' ? 'svg' : 'canvas',\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 ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`\n },\n },\n ]\n }\n graphInstance = new (Graph as any)(config)\n await graphInstance.render()\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to render graph')\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('PNG export not supported in current renderer mode')\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`)\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'PNG export failed')\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 Graph rendering unavailable\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> components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? 'Graph'}\n copyData={toJSON(params())}\n copyLabel=\"Copy graph (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\n }`}>\n {/* Export menu — top-right, mirrors TableRenderer's pattern */}\n <div class=\"absolute right-2 top-2 z-10\">\n <button\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=\"Export graph\"\n aria-label=\"Export graph\"\n aria-expanded={exportMenuOpen()}\n >\n Export ▾\n </button>\n <Show when={exportMenuOpen()}>\n <div class=\"absolute right-0 mt-1 w-44 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded shadow-lg text-xs\">\n <For each={[\n { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },\n { label: 'Download Mermaid', onClick: handleExportMermaid, hint: 'markdown / GitHub' },\n { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },\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 </div>\n </Show>\n </div>\n\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 isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n <Show when={error()}>\n <p class=\"text-xs text-red-600 dark:text-red-400 mt-1\">Render error: {error()}</p>\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\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&': return '&'\n case '<': return '<'\n case '>': return '>'\n case '\"': return '"'\n case \"'\": return '''\n default: return c\n }\n })\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","component","isExpanded","useExpanded","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","renderer","rendererPref","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","Error","message","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$5","children","_tmpl$4","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","_el$","_tmpl$3","_el$2","firstChild","_el$3","_el$5","nextSibling","_el$6","_co$","_$getNextMarker","_el$7","_el$10","_el$11","_co$3","$$click","v","_$insert","_el$4","_tmpl$","For","each","onClick","hint","_el$14","_tmpl$6","_el$15","_el$16","_$addEventListener","_$runHydrationEvents","_ref$","_$use","_el$8","_tmpl$2","_el$9","_el$0","_el$1","_co$2","_$effect","_p$","_v$","className","_v$2","_v$3","_v$4","width","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","c","_$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, GraphNode, GraphEdge } from '@seed-ship/mcp-ui-spec'\nimport { ExpandableWrapper, useExpanded } from './ExpandableWrapper'\nimport { PortalDropdownMenu } from './PortalDropdownMenu'\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 `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.replace(/[\"[\\]|]/g, '').replace(/\\s+/g, ' ').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 = 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 params = () => props.component.params as GraphComponentParams\n const isExpanded = useExpanded()\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 renderer: p.rendererPref === 'svg' ? 'svg' : 'canvas',\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 ? `<br><span style=\"font-size:11px;opacity:0.7\">${escapeHtml(data)}</span>` : ''\n }</div>`\n },\n },\n ]\n }\n graphInstance = new (Graph as any)(config)\n await graphInstance.render()\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to render graph')\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('PNG export not supported in current renderer mode')\n }\n } else {\n await downloadDataUrl(dataUrl, `${graphFilenameStem(params())}.png`)\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'PNG export failed')\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 Graph rendering unavailable\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> components.\n </p>\n </div>\n </Show>\n }\n >\n <ExpandableWrapper\n title={params().title ?? 'Graph'}\n copyData={toJSON(params())}\n copyLabel=\"Copy graph (JSON)\"\n toolbarVariant={props.toolbarVariant}\n >\n <div class={`relative w-full ${params().className ?? ''} ${\n isExpanded() ? 'flex-1 min-h-0 flex flex-col' : ''\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=\"Export graph\"\n aria-label=\"Export graph\"\n aria-haspopup=\"menu\"\n aria-expanded={exportMenuOpen()}\n >\n Export ▾\n </button>\n <PortalDropdownMenu\n open={exportMenuOpen()}\n onClose={() => setExportMenuOpen(false)}\n trigger={exportTriggerRef}\n width={176}\n class=\"text-xs\"\n >\n <For each={[\n { label: 'Download PNG', onClick: handleExportPNG, hint: 'visual snapshot' },\n { label: 'Download Mermaid', onClick: handleExportMermaid, hint: 'markdown / GitHub' },\n { label: 'Download JSON', onClick: handleExportJSON, hint: 'raw data' },\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 <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 isExpanded() ? 'flex-1 min-h-0' : ''\n }`}\n style={\n isExpanded()\n ? `height: 100%; width: ${params().width ?? '100%'};`\n : `height: ${params().height ?? '400px'}; width: ${params().width ?? '100%'};`\n }\n />\n <Show when={error()}>\n <p class=\"text-xs text-red-600 dark:text-red-400 mt-1\">Render error: {error()}</p>\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\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&': return '&'\n case '<': return '<'\n case '>': return '>'\n case '\"': return '"'\n case \"'\": return '''\n default: return c\n }\n })\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","component","isExpanded","useExpanded","available","setAvailable","createSignal","error","setError","exportMenuOpen","setExportMenuOpen","containerRef","exportTriggerRef","graphInstance","onMount","g6Available","Graph","p","config","container","data","renderer","rendererPref","fitView","autoFit","tooltip","plugins","getContent","_evt","items","item","escapeHtml","render","err","Error","message","onCleanup","destroy","handleExportJSON","graphFilenameStem","handleExportMermaid","handleExportPNG","dataUrl","toDataURL","canvas","querySelector","downloadDataUrl","_$createComponent","Show","when","fallback","_$getNextElement","_tmpl$4","children","_tmpl$3","ExpandableWrapper","title","copyData","copyLabel","toolbarVariant","_el$","_tmpl$2","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$5","_co$","_$getNextMarker","_el$6","_el$1","_el$10","_co$3","$$click","v","_ref$","_$use","_$insert","PortalDropdownMenu","open","onClose","trigger","width","For","each","onClick","hint","_el$13","_tmpl$5","_el$14","_el$15","_$addEventListener","_$runHydrationEvents","_ref$2","_el$7","_tmpl$","_el$8","_el$9","_el$0","_co$2","_$effect","_p$","_v$","className","_v$2","_v$3","_v$4","height","_$className","t","_$setAttribute","o","_$style","base","res","fetch","c","_$delegateEvents"],"mappings":"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"}
|