@seed-ship/mcp-ui-solid 6.3.1 → 6.5.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/components/GraphRenderer.cjs +30 -24
  3. package/dist/components/GraphRenderer.cjs.map +1 -1
  4. package/dist/components/GraphRenderer.d.ts.map +1 -1
  5. package/dist/components/GraphRenderer.js +30 -24
  6. package/dist/components/GraphRenderer.js.map +1 -1
  7. package/dist/components/PortalDropdownMenu.cjs +82 -0
  8. package/dist/components/PortalDropdownMenu.cjs.map +1 -0
  9. package/dist/components/PortalDropdownMenu.d.ts +56 -0
  10. package/dist/components/PortalDropdownMenu.d.ts.map +1 -0
  11. package/dist/components/PortalDropdownMenu.js +82 -0
  12. package/dist/components/PortalDropdownMenu.js.map +1 -0
  13. package/dist/components/UIResourceRenderer.cjs +297 -263
  14. package/dist/components/UIResourceRenderer.cjs.map +1 -1
  15. package/dist/components/UIResourceRenderer.d.ts +20 -0
  16. package/dist/components/UIResourceRenderer.d.ts.map +1 -1
  17. package/dist/components/UIResourceRenderer.js +299 -265
  18. package/dist/components/UIResourceRenderer.js.map +1 -1
  19. package/dist/components/index.d.ts +2 -0
  20. package/dist/components/index.d.ts.map +1 -1
  21. package/dist/components.cjs +2 -0
  22. package/dist/components.cjs.map +1 -1
  23. package/dist/components.d.cts +2 -0
  24. package/dist/components.d.ts +2 -0
  25. package/dist/components.js +2 -0
  26. package/dist/components.js.map +1 -1
  27. package/dist/index.cjs +6 -0
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +5 -0
  30. package/dist/index.d.ts +5 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +6 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/utils/duplicate-mount-registry.cjs +27 -0
  35. package/dist/utils/duplicate-mount-registry.cjs.map +1 -0
  36. package/dist/utils/duplicate-mount-registry.d.ts +84 -0
  37. package/dist/utils/duplicate-mount-registry.d.ts.map +1 -0
  38. package/dist/utils/duplicate-mount-registry.js +27 -0
  39. package/dist/utils/duplicate-mount-registry.js.map +1 -0
  40. package/dist/utils/stable-key.cjs +41 -0
  41. package/dist/utils/stable-key.cjs.map +1 -0
  42. package/dist/utils/stable-key.d.ts +33 -0
  43. package/dist/utils/stable-key.d.ts.map +1 -0
  44. package/dist/utils/stable-key.js +41 -0
  45. package/dist/utils/stable-key.js.map +1 -0
  46. package/package.json +1 -1
  47. package/src/components/GraphRenderer.tsx +29 -20
  48. package/src/components/PortalDropdownMenu.test.tsx +113 -0
  49. package/src/components/PortalDropdownMenu.tsx +130 -0
  50. package/src/components/UIResourceRenderer.identity.test.tsx +161 -0
  51. package/src/components/UIResourceRenderer.tsx +85 -15
  52. package/src/components/index.ts +4 -0
  53. package/src/index.ts +10 -0
  54. package/src/utils/duplicate-mount-registry.test.ts +82 -0
  55. package/src/utils/duplicate-mount-registry.ts +113 -0
  56. package/src/utils/stable-key.test.ts +96 -0
  57. package/src/utils/stable-key.ts +91 -0
  58. package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,162 @@ 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.5.0] - 2026-05-05
9
+
10
+ Closes Demande 1 + Demande 2 of `deposium_solid`'s
11
+ `BRIEF-MCPUI-2026-05-10.md` (rescue-duplicate root-cause investigation).
12
+
13
+ ### Added — `getUiResourceStableKey(content)` helper
14
+
15
+ New public function exported from the package root :
16
+
17
+ ```ts
18
+ import { getUiResourceStableKey } from '@seed-ship/mcp-ui-solid'
19
+
20
+ getUiResourceStableKey({ id: 'dashboard-q3', components: [...] })
21
+ // → 'dashboard-q3' (passthrough)
22
+
23
+ getUiResourceStableKey({ type: 'chart', params: { ... } })
24
+ // → 'a4f3b91' (FNV-1a 32-bit, 7 chars base36)
25
+ ```
26
+
27
+ If `content.id` is a non-empty string, the helper returns it verbatim.
28
+ Otherwise it derives a deterministic hash from the content with `id` and
29
+ `metadata.generatedAt` stripped — stable across renders for the same
30
+ logical payload, sync, no peer dependency.
31
+
32
+ This is the canonical implementation of the spec's "bare payload"
33
+ fallback policy (cf. `mcp-ui-spec` README → §Runtime Payload Identity).
34
+ Host apps that pre-process payloads before passing them to a renderer
35
+ (e.g. wrapping a bare chart config into a layout) should reuse this
36
+ helper instead of generating `Date.now()` or counter-based ids — those
37
+ break `<For>` reconciliation and double-mount detection.
38
+
39
+ ### Added — opt-in duplicate-mount observability
40
+
41
+ `<UIResourceRenderer>` now exposes two ways for consumers to detect
42
+ when the same content key is mounted concurrently more than once :
43
+
44
+ 1. **Per-instance callback** :
45
+
46
+ ```tsx
47
+ <UIResourceRenderer
48
+ content={layout}
49
+ onMountDuplicate={({ key, count, firstMountedAt }) => {
50
+ console.warn('[app] duplicate mount', { key, count })
51
+ }}
52
+ />
53
+ ```
54
+
55
+ 2. **Module-level reporter** (app-wide telemetry) :
56
+
57
+ ```ts
58
+ import { setDuplicateMountReporter } from '@seed-ship/mcp-ui-solid'
59
+
60
+ setDuplicateMountReporter(({ key, count }) => {
61
+ telemetry.warn('mcp-ui.duplicate-mount', { key, count })
62
+ })
63
+ ```
64
+
65
+ The new `debugDuplicateMounts` prop forces a `console.warn` from a
66
+ single instance even when the global `setDebugMode()` flag is off —
67
+ useful when you want to diagnose one suspect surface without flipping
68
+ the global switch.
69
+
70
+ **The renderer never deduplicates visually on its own.** Hiding a 2nd
71
+ mount would mask parent-framework bugs and could remove legitimate
72
+ co-mounts (drawer + main panel showing the same card). Consumers who
73
+ want dedup implement it on top of the reported events.
74
+
75
+ ### Added — `data-mcp-ui-{layout|component}-id` DOM attributes
76
+
77
+ Every `<UIResourceRenderer>` wrapper now carries a stable identity
78
+ attribute :
79
+
80
+ - The outer wrapper carries `data-mcp-ui-layout-id` when `content` is
81
+ a `UILayout` (composite), or `data-mcp-ui-component-id` when `content`
82
+ is a single `UIComponent`.
83
+ - Each per-component wrapper inside a layout carries
84
+ `data-mcp-ui-component-id`.
85
+
86
+ This enables CSS targeting, debug overlay tooling, and DOM-based
87
+ double-mount detection without a wrapper :
88
+
89
+ ```js
90
+ document.querySelectorAll('[data-mcp-ui-layout-id="dashboard-q3"]').length
91
+ // → 2 (whoops — somewhere in the parent framework, this is mounted twice)
92
+ ```
93
+
94
+ ### Internal — no `Date.now()` in identity-bearing code paths
95
+
96
+ Audit confirmed : every `Date.now()` call inside `mcp-ui-solid` is for
97
+ telemetry timestamps (`ts: Date.now()`), cache TTLs, or download-filename
98
+ fallbacks — none feed into a rendered DOM `id` or a key passed to
99
+ `<For>`. The new `getUiResourceStableKey` helper preserves this
100
+ invariant by hashing content rather than reading the clock.
101
+
102
+ ### Spec companion — `mcp-ui-spec@5.0.6`
103
+
104
+ Documentation-only patch bump : the spec README now formalizes the
105
+ runtime-payload identity contract (§Runtime Payload Identity) — `id`
106
+ obligation, fallback policy, and pointer to `getUiResourceStableKey`.
107
+
108
+ ## [6.4.0] - 2026-05-03
109
+
110
+ Closes axe 3 of `deposium_solid`'s
111
+ `SOLID-MCPUI-IMPROVEMENT-AXES-2026-05-03.md` handoff.
112
+
113
+ ### Changed — Export menus now mount via `<Portal>`
114
+
115
+ Both Export dropdowns shipped by the renderers — the `TableRenderer`
116
+ (`Copy TSV / Download CSV / Download JSON`) and the `GraphRenderer`
117
+ (`Download PNG / Download Mermaid / Download JSON`) — now render via
118
+ `solid-js/web` `<Portal>` on `document.body` instead of an in-tree
119
+ `position: absolute` sibling.
120
+
121
+ This fixes two long-standing pain points :
122
+
123
+ 1. **`overflow: hidden` clipping** — when the table or graph lives
124
+ inside a chat bubble, a card, or any ancestor with `overflow: hidden`,
125
+ the legacy in-tree menu got clipped at the ancestor's boundary.
126
+ Mounting on `document.body` escapes the clip stack entirely.
127
+ 2. **`z-index` wars** — chat surfaces stack composer / message rails
128
+ above the message list, and ancestor `z-index` creates a new stacking
129
+ context that captured the in-tree menu. A portal is a sibling of the
130
+ document, so a single `z-index: 9999` wins.
131
+
132
+ Pre-v6.4.0 deposium had to ship a `overflow: visible !important`
133
+ override + 4 `z-index` overrides on its `ChatUIIsland.tsx` to work
134
+ around this. Both can now be removed.
135
+
136
+ ### Added — `<PortalDropdownMenu>` (factored helper)
137
+
138
+ The portal-mounting + click-outside + Escape + scroll/resize
139
+ re-positioning logic is factored into a generic component for reuse.
140
+ Exported under both `@seed-ship/mcp-ui-solid` root and
141
+ `@seed-ship/mcp-ui-solid/components` :
142
+
143
+ ```tsx
144
+ import { PortalDropdownMenu } from '@seed-ship/mcp-ui-solid'
145
+
146
+ const [open, setOpen] = createSignal(false)
147
+ let triggerRef: HTMLButtonElement | undefined
148
+
149
+ <button ref={triggerRef} onClick={() => setOpen(true)}>Menu</button>
150
+ <PortalDropdownMenu open={open()} onClose={() => setOpen(false)} trigger={triggerRef}>
151
+ <button onClick={...}>Item 1</button>
152
+ <button onClick={...}>Item 2</button>
153
+ </PortalDropdownMenu>
154
+ ```
155
+
156
+ ### Non-breaking
157
+
158
+ The behavior change is transparent to consumers — the menu still opens
159
+ and closes from the same trigger button, with the same items. Tests :
160
+ 7 added in `PortalDropdownMenu.test.tsx` (mount target, position from
161
+ `getBoundingClientRect`, outside click + Escape close, trigger /
162
+ in-menu mousedown ignored).
163
+
8
164
  ## [6.3.1] - 2026-05-03
9
165
 
10
166
  ### 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
- var _tmpl$ = /* @__PURE__ */ web.template(`<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">`), _tmpl$2 = /* @__PURE__ */ web.template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Render error: <!$><!/>`), _tmpl$3 = /* @__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">Export ▾</button><!$><!/></div><div></div><!$><!/>`), _tmpl$4 = /* @__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$5 = /* @__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$6 = /* @__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">`);
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$5)
191
+ web.getNextElement(_tmpl$4)
190
192
  );
191
193
  },
192
194
  get children() {
193
- return web.getNextElement(_tmpl$4);
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$3), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$5 = _el$3.nextSibling, [_el$6, _co$] = web.getNextMarker(_el$5.nextSibling), _el$7 = _el$2.nextSibling, _el$10 = _el$7.nextSibling, [_el$11, _co$3] = web.getNextMarker(_el$10.nextSibling);
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
- web.insert(_el$2, web.createComponent(solidJs.Show, {
213
- get when() {
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
- var _el$4 = web.getNextElement(_tmpl$);
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$14 = web.getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.nextSibling;
234
- web.addEventListener(_el$14, "click", item.onClick, true);
235
- web.insert(_el$15, () => item.label);
236
- web.insert(_el$16, () => item.hint);
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$14;
245
+ return _el$13;
239
246
  })()
240
- }));
241
- return _el$4;
247
+ });
242
248
  }
243
- }), _el$6, _co$);
244
- var _ref$ = containerRef;
245
- typeof _ref$ === "function" ? web.use(_ref$, _el$7) : containerRef = _el$7;
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$8 = web.getNextElement(_tmpl$2), _el$9 = _el$8.firstChild, _el$0 = _el$9.nextSibling, [_el$1, _co$2] = web.getNextMarker(_el$0.nextSibling);
252
- web.insert(_el$8, error, _el$1, _co$2);
253
- return _el$8;
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$11, _co$3);
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$7, _p$.a = _v$3);
261
- _p$.o = web.style(_el$7, _v$4, _p$.o);
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 '&amp;'\n case '<': return '&lt;'\n case '>': return '&gt;'\n case '\"': return '&quot;'\n case \"'\": return '&#39;'\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 '&amp;'\n case '<': return '&lt;'\n case '>': return '&gt;'\n case '\"': return '&quot;'\n case \"'\": return '&#39;'\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;AAOrG;;;;;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,CAwLvD,CAAA;AA6BD,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,IAAI,WAAW,EAAE,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
- var _tmpl$ = /* @__PURE__ */ template(`<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">`), _tmpl$2 = /* @__PURE__ */ template(`<p class="text-xs text-red-600 dark:text-red-400 mt-1">Render error: <!$><!/>`), _tmpl$3 = /* @__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">Export ▾</button><!$><!/></div><div></div><!$><!/>`), _tmpl$4 = /* @__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$5 = /* @__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$6 = /* @__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">`);
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$5)
189
+ getNextElement(_tmpl$4)
188
190
  );
189
191
  },
190
192
  get children() {
191
- return getNextElement(_tmpl$4);
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$3), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$5 = _el$3.nextSibling, [_el$6, _co$] = getNextMarker(_el$5.nextSibling), _el$7 = _el$2.nextSibling, _el$10 = _el$7.nextSibling, [_el$11, _co$3] = getNextMarker(_el$10.nextSibling);
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
- insert(_el$2, createComponent(Show, {
211
- get when() {
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
- var _el$4 = getNextElement(_tmpl$);
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$14 = getNextElement(_tmpl$6), _el$15 = _el$14.firstChild, _el$16 = _el$15.nextSibling;
232
- addEventListener(_el$14, "click", item.onClick, true);
233
- insert(_el$15, () => item.label);
234
- insert(_el$16, () => item.hint);
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$14;
243
+ return _el$13;
237
244
  })()
238
- }));
239
- return _el$4;
245
+ });
240
246
  }
241
- }), _el$6, _co$);
242
- var _ref$ = containerRef;
243
- typeof _ref$ === "function" ? use(_ref$, _el$7) : containerRef = _el$7;
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$8 = getNextElement(_tmpl$2), _el$9 = _el$8.firstChild, _el$0 = _el$9.nextSibling, [_el$1, _co$2] = getNextMarker(_el$0.nextSibling);
250
- insert(_el$8, error, _el$1, _co$2);
251
- return _el$8;
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$11, _co$3);
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$7, _p$.a = _v$3);
259
- _p$.o = style(_el$7, _v$4, _p$.o);
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,