@modernrelay/orbit-react 0.14.0 → 0.15.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/dist/components/Minimap.js +10 -5
- package/dist/components/Minimap.js.map +1 -1
- package/dist/components/Navigator.js +8 -1
- package/dist/components/Navigator.js.map +1 -1
- package/dist/components/Tooltip.js +2 -1
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +36 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -34,7 +34,7 @@ function GraphMinimap(props) {
|
|
|
34
34
|
const rootRef = useRef(null);
|
|
35
35
|
const canvasRef = useRef(null);
|
|
36
36
|
const rectRef = useRef(null);
|
|
37
|
-
const
|
|
37
|
+
const draggingPointerRef = useRef(null);
|
|
38
38
|
const declaredSceneRef = useRef({ ids: [], scene: null });
|
|
39
39
|
const visibleRef = useRef({
|
|
40
40
|
ids: [],
|
|
@@ -160,8 +160,10 @@ function GraphMinimap(props) {
|
|
|
160
160
|
instance.setViewport({ x: world[0], y: world[1] });
|
|
161
161
|
};
|
|
162
162
|
const onPointerDown = (e) => {
|
|
163
|
+
const owner = draggingPointerRef.current;
|
|
164
|
+
if (owner !== null && owner !== e.pointerId) return;
|
|
163
165
|
e.preventDefault();
|
|
164
|
-
|
|
166
|
+
draggingPointerRef.current = e.pointerId;
|
|
165
167
|
try {
|
|
166
168
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
167
169
|
} catch {
|
|
@@ -169,11 +171,12 @@ function GraphMinimap(props) {
|
|
|
169
171
|
panTo(e);
|
|
170
172
|
};
|
|
171
173
|
const onPointerMove = (e) => {
|
|
172
|
-
if (
|
|
174
|
+
if (draggingPointerRef.current !== e.pointerId) return;
|
|
173
175
|
panTo(e);
|
|
174
176
|
};
|
|
175
|
-
const endDrag = () => {
|
|
176
|
-
|
|
177
|
+
const endDrag = (e) => {
|
|
178
|
+
if (draggingPointerRef.current !== e.pointerId) return;
|
|
179
|
+
draggingPointerRef.current = null;
|
|
177
180
|
};
|
|
178
181
|
return /* @__PURE__ */ jsxs(
|
|
179
182
|
"div",
|
|
@@ -208,6 +211,8 @@ function GraphMinimap(props) {
|
|
|
208
211
|
onPointerDown,
|
|
209
212
|
onPointerMove,
|
|
210
213
|
onPointerUp: endDrag,
|
|
214
|
+
onPointerCancel: endDrag,
|
|
215
|
+
onLostPointerCapture: endDrag,
|
|
211
216
|
onPointerLeave: endDrag
|
|
212
217
|
}
|
|
213
218
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Minimap/index.tsx"],"names":[],"mappings":"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","file":"Minimap.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the\n * whole-graph thumbnail + viewport rectangle.\n *\n * v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass`\n * cosmos matrix): the component owns an {@link OverviewController} and\n * blits its `rasterize` output into a small canvas via `putImageData`. The\n * three lanes stay strictly decoupled:\n *\n * 1. **Thumbnail refresh** — a 500 ms `setInterval` probes the controller's\n * `shouldRefresh(now, simulationRunning, epoch)` gate, so the effective\n * cadence is the controller's spec budget (≤ 2 Hz hot, ≤ 1 Hz idle, zero\n * work idle-with-unchanged-epoch) and each `true` is followed by exactly\n * one `rasterize` + blit. The interval — not a per-frame engine lane —\n * is the documented v0.9 cadence: the `onFrame` fan-out only reaches\n * React through the label lane, which is gated on labels being enabled,\n * so a plain timer is the correct always-on ≤ 2 Hz driver. The epoch is\n * `revisions.render` (a positions-bearing superset — over-approximate but\n * bounded by the same throttle).\n * 2. **Viewport rectangle** — an imperative store subscription writes the\n * rect's transform/size O(1) per `store.viewport` publication (CSS\n * transform on a ref; no React re-render, no re-rasterize).\n * 3. **Pan** — pointer down/drag maps minimap px → world via the\n * controller's `minimapToWorld` (exact inverse over the last rasterized\n * frame) and calls `instance.setViewport({x, y})` — real camera pans.\n *\n * Scene source (documented v0.9 trim): the instance does not yet expose the\n * posBuf mirror (reserved for the columnar lane), so the\n * component probes a pinned structural seam `getOverviewScene` first\n * (optional access — picked up automatically once core lands\n * it) and falls back to the DECLARED node positions (`GraphNode.x/y`) of the\n * current scene roster — exact for prepared/fixed-position datasets from\n * orbit-data, and empty for force layouts without a position mirror.\n * Position-less nodes rasterize as NaN tombstones (skipped); mask-hidden nodes draw dimmed\n * via `getVisible`.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { OverviewController, OVERVIEW_HOT_INTERVAL_MS } from '@modernrelay/orbit-core';\nimport type { NodeId, OverviewScene, ViewportState } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\n/** Default thumbnail edge (px) — the component default; the CPU target\n * default (256²) applies when a caller constructs a bare controller. */\nconst SIZE_DEFAULT = 200;\n\n/** Minimum rendered viewport-rect edge so a tiny/degenerate viewport stays\n * visible as a marker. */\nconst MIN_RECT_PX = 6;\n\n/** Pinned structural seam: the instance-side\n * posBuf mirror read. Optional access until core lands it — this view\n * stays signature-compatible when the member becomes required. */\ninterface OverviewSceneSurface {\n getOverviewScene?(): OverviewScene | null;\n}\n\nexport interface GraphMinimapProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Square thumbnail edge in CSS px (also the raster resolution). Default 200. */\n size?: number;\n /** Accessible name. Default 'Graph minimap'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-right'. */\n position?: GraphCorner;\n /** Distance from the two anchored edges in CSS px. Default 12. */\n offset?: number;\n /** Class hook for the root; providing it drops the default styles\n * INCLUDING placement — position it yourself in CSS. */\n className?: string;\n /** Style overrides merged over the defaults; an inset you set here\n * wins over the one `position` implies (resolved per axis). */\n style?: CSSProperties;\n}\n\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n overflow: 'hidden',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n};\n\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n cursor: 'crosshair',\n touchAction: 'none',\n};\n\nconst RECT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n display: 'none',\n border: '1px solid rgba(255, 255, 255, 0.85)',\n background: 'rgba(255, 255, 255, 0.12)',\n pointerEvents: 'none',\n willChange: 'transform',\n boxSizing: 'border-box',\n};\n\n/** Declared-position scene over the current roster, cached by the roster's\n * identity (`scene.idByIndex` is a stable reference per reconciled scene). */\ninterface DeclaredSceneCache {\n ids: readonly NodeId[];\n scene: OverviewScene | null;\n}\n\nexport function GraphMinimap(props: GraphMinimapProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphMinimap>');\n const size = props.size ?? SIZE_DEFAULT;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rectRef = useRef<HTMLDivElement | null>(null);\n const draggingRef = useRef(false);\n const declaredSceneRef = useRef<DeclaredSceneCache>({ ids: [], scene: null });\n /** mask lane for `getVisible`, recomputed once per refresh tick. */\n const visibleRef = useRef<{ ids: readonly NodeId[]; set: ReadonlySet<NodeId> | null }>({\n ids: [],\n set: null,\n });\n\n const controller = useMemo(() => {\n const getScene = (): OverviewScene | null => {\n // 1. Pinned core seam: the posBuf mirror, once it lands.\n const mirrored = (instance as unknown as OverviewSceneSurface).getOverviewScene?.();\n if (mirrored !== undefined) return mirrored;\n // 2. v0.9 fallback: declared node positions over the scene roster.\n const ids = instance.getSceneNodeIds();\n if (ids.length === 0) return null;\n const cache = declaredSceneRef.current;\n if (cache.ids === ids) return cache.scene;\n const positions = new Float32Array(2 * ids.length);\n let known = 0;\n for (let i = 0; i < ids.length; i++) {\n const node = instance.getNode(ids[i]!);\n const x = node?.x;\n const y = node?.y;\n if (typeof x === 'number' && typeof y === 'number') {\n positions[2 * i] = x;\n positions[2 * i + 1] = y;\n known++;\n } else {\n positions[2 * i] = NaN;\n positions[2 * i + 1] = NaN;\n }\n }\n const scene: OverviewScene | null = known > 0 ? { positions, count: ids.length } : null;\n declaredSceneRef.current = { ids, scene };\n return scene;\n };\n const getVisible = (index: number): boolean => {\n const v = visibleRef.current;\n if (v.set === null) return true;\n const id = v.ids[index];\n return id === undefined ? true : v.set.has(id);\n };\n return new OverviewController({ getScene, getVisible, size });\n }, [instance, size]);\n\n /** O(1) rect update from the store viewport over the LAST rasterized frame.\n * `zoom` is screen px per space unit; the world extent in view is the\n * host container's CSS size / zoom — measured from the positioned ancestor\n * (the <Graph> container). Zero-size hosts (jsdom/unmounted) degrade to a\n * MIN_RECT_PX center marker. */\n const updateRect = (v: ViewportState | null): void => {\n const rect = rectRef.current;\n if (rect === null) return;\n if (v === null || v.zoom <= 0) {\n rect.style.display = 'none';\n return;\n }\n const host = rootRef.current?.offsetParent;\n const hostW = host instanceof HTMLElement ? host.clientWidth : 0;\n const hostH = host instanceof HTMLElement ? host.clientHeight : 0;\n const halfW = hostW > 0 ? hostW / v.zoom / 2 : 0;\n const halfH = hostH > 0 ? hostH / v.zoom / 2 : 0;\n // Two opposite world corners (world y grows up, minimap y grows down).\n const a = controller.worldToMinimap(v.x - halfW, v.y + halfH);\n const b = controller.worldToMinimap(v.x + halfW, v.y - halfH);\n if (a === null || b === null) {\n rect.style.display = 'none'; // nothing rasterized yet\n return;\n }\n const w = Math.max(Math.abs(b[0] - a[0]), MIN_RECT_PX);\n const h = Math.max(Math.abs(b[1] - a[1]), MIN_RECT_PX);\n const x = (a[0] + b[0]) / 2 - w / 2;\n const y = (a[1] + b[1]) / 2 - h / 2;\n rect.style.display = 'block';\n rect.style.width = `${w}px`;\n rect.style.height = `${h}px`;\n rect.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n };\n\n // Lane 1: thumbnail refresh — interval-probed controller throttle.\n useEffect(() => {\n const blit = (): void => {\n const state = instance.store.getState();\n // mask lane for the dimmed-alpha rasterization.\n if (state.visible.nodes !== state.nodeCount || state.hiddenNodeIds.size > 0) {\n const ids = instance.getSceneNodeIds();\n visibleRef.current = { ids, set: new Set(instance.getVisibleNodeIds()) };\n } else {\n visibleRef.current = { ids: [], set: null };\n }\n const raster = controller.rasterize();\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const ctx = canvas.getContext('2d');\n if (ctx === null) return;\n if (raster === null) {\n ctx.clearRect(0, 0, size, size);\n updateRect(null);\n return;\n }\n if (typeof ImageData === 'undefined') return; // non-DOM environment\n // Copy: ImageData requires a plain-ArrayBuffer-backed clamped array.\n const pixels = new Uint8ClampedArray(raster.bitmap);\n ctx.putImageData(new ImageData(pixels, controller.size, controller.size), 0, 0);\n // A fresh frame can move the world→minimap mapping: re-derive the rect.\n updateRect(instance.store.getState().viewport);\n };\n const tick = (): void => {\n const state = instance.store.getState();\n // `revisions.render` is the v0.9 positions epoch (see module JSDoc):\n // idle with an unchanged render revision is provably position-stable.\n if (!controller.shouldRefresh(Date.now(), state.simulationRunning, state.revisions.render)) {\n return;\n }\n blit();\n };\n tick(); // first paint without waiting a full interval\n const timer = setInterval(tick, OVERVIEW_HOT_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n // updateRect is stable for each (instance, controller) pair — refs + controller\n // only — so it is deliberately not a dependency (this repo does not\n // configure the react-hooks lint plugin).\n }, [instance, controller, size]);\n\n // Lane 2: viewport rectangle — imperative store subscription, O(1) per\n // viewport publication, no re-rasterize and no React re-render.\n useEffect(() => {\n let prev = instance.store.getState().viewport;\n updateRect(prev);\n return instance.store.subscribe(() => {\n const next = instance.store.getState().viewport;\n if (next === prev) return;\n prev = next;\n updateRect(next);\n });\n // updateRect deliberately not a dependency (see the refresh effect above).\n }, [instance, controller]);\n\n // Lane 3: pan. Down pans immediately; move keeps panning while dragging.\n const panTo = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const b = canvas.getBoundingClientRect();\n // Canvas CSS size vs raster size (zero-size rects — jsdom — map 1:1).\n const sx = b.width > 0 ? controller.size / b.width : 1;\n const sy = b.height > 0 ? controller.size / b.height : 1;\n const world = controller.minimapToWorld((e.clientX - b.left) * sx, (e.clientY - b.top) * sy);\n if (world === null) return; // nothing rasterized yet — nothing to map\n instance.setViewport({ x: world[0], y: world[1] });\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n e.preventDefault();\n draggingRef.current = true;\n try {\n e.currentTarget.setPointerCapture(e.pointerId);\n } catch {\n /* pointer capture unavailable (jsdom/legacy) */\n }\n panTo(e);\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (!draggingRef.current) return;\n panTo(e);\n };\n\n const endDrag = (): void => {\n draggingRef.current = false;\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-minimap=\"\"\n role=\"group\"\n aria-label={props.label ?? 'Graph minimap'}\n className={props.className}\n style={\n props.className !== undefined\n ? // The thumbnail is MECHANICAL, not decorative: dropping its box\n // would collapse the canvas, so size survives the class hook.\n { width: size, height: size, ...props.style }\n : mergeStyle(\n {\n ...cornerStyle(props.position ?? 'bottom-right', props.offset),\n ...ROOT_STYLE,\n width: size,\n height: size,\n },\n props.style,\n )\n }\n >\n <canvas\n ref={canvasRef}\n data-orbit-minimap-canvas=\"\"\n width={size}\n height={size}\n style={CANVAS_STYLE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerLeave={endDrag}\n />\n <div ref={rectRef} data-orbit-minimap-viewport=\"\" style={RECT_STYLE} />\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Minimap/index.tsx"],"names":[],"mappings":"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","file":"Minimap.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Minimap` — `<GraphMinimap>`, the\n * whole-graph thumbnail + viewport rectangle.\n *\n * v0.9 is CPU-fallback-only (no engine exposes `capabilities.overviewPass`\n * cosmos matrix): the component owns an {@link OverviewController} and\n * blits its `rasterize` output into a small canvas via `putImageData`. The\n * three lanes stay strictly decoupled:\n *\n * 1. **Thumbnail refresh** — a 500 ms `setInterval` probes the controller's\n * `shouldRefresh(now, simulationRunning, epoch)` gate, so the effective\n * cadence is the controller's spec budget (≤ 2 Hz hot, ≤ 1 Hz idle, zero\n * work idle-with-unchanged-epoch) and each `true` is followed by exactly\n * one `rasterize` + blit. The interval — not a per-frame engine lane —\n * is the documented v0.9 cadence: the `onFrame` fan-out only reaches\n * React through the label lane, which is gated on labels being enabled,\n * so a plain timer is the correct always-on ≤ 2 Hz driver. The epoch is\n * `revisions.render` (a positions-bearing superset — over-approximate but\n * bounded by the same throttle).\n * 2. **Viewport rectangle** — an imperative store subscription writes the\n * rect's transform/size O(1) per `store.viewport` publication (CSS\n * transform on a ref; no React re-render, no re-rasterize).\n * 3. **Pan** — pointer down/drag maps minimap px → world via the\n * controller's `minimapToWorld` (exact inverse over the last rasterized\n * frame) and calls `instance.setViewport({x, y})` — real camera pans.\n *\n * Scene source (documented v0.9 trim): the instance does not yet expose the\n * posBuf mirror (reserved for the columnar lane), so the\n * component probes a pinned structural seam `getOverviewScene` first\n * (optional access — picked up automatically once core lands\n * it) and falls back to the DECLARED node positions (`GraphNode.x/y`) of the\n * current scene roster — exact for prepared/fixed-position datasets from\n * orbit-data, and empty for force layouts without a position mirror.\n * Position-less nodes rasterize as NaN tombstones (skipped); mask-hidden nodes draw dimmed\n * via `getVisible`.\n */\n\nimport { useEffect, useMemo, useRef } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { OverviewController, OVERVIEW_HOT_INTERVAL_MS } from '@modernrelay/orbit-core';\nimport type { NodeId, OverviewScene, ViewportState } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\n/** Default thumbnail edge (px) — the component default; the CPU target\n * default (256²) applies when a caller constructs a bare controller. */\nconst SIZE_DEFAULT = 200;\n\n/** Minimum rendered viewport-rect edge so a tiny/degenerate viewport stays\n * visible as a marker. */\nconst MIN_RECT_PX = 6;\n\n/** Pinned structural seam: the instance-side\n * posBuf mirror read. Optional access until core lands it — this view\n * stays signature-compatible when the member becomes required. */\ninterface OverviewSceneSurface {\n getOverviewScene?(): OverviewScene | null;\n}\n\nexport interface GraphMinimapProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Square thumbnail edge in CSS px (also the raster resolution). Default 200. */\n size?: number;\n /** Accessible name. Default 'Graph minimap'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-right'. */\n position?: GraphCorner;\n /** Distance from the two anchored edges in CSS px. Default 12. */\n offset?: number;\n /** Class hook for the root; providing it drops the default styles\n * INCLUDING placement — position it yourself in CSS. */\n className?: string;\n /** Style overrides merged over the defaults; an inset you set here\n * wins over the one `position` implies (resolved per axis). */\n style?: CSSProperties;\n}\n\nconst ROOT_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n overflow: 'hidden',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.85)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n};\n\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n cursor: 'crosshair',\n touchAction: 'none',\n};\n\nconst RECT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n display: 'none',\n border: '1px solid rgba(255, 255, 255, 0.85)',\n background: 'rgba(255, 255, 255, 0.12)',\n pointerEvents: 'none',\n willChange: 'transform',\n boxSizing: 'border-box',\n};\n\n/** Declared-position scene over the current roster, cached by the roster's\n * identity (`scene.idByIndex` is a stable reference per reconciled scene). */\ninterface DeclaredSceneCache {\n ids: readonly NodeId[];\n scene: OverviewScene | null;\n}\n\nexport function GraphMinimap(props: GraphMinimapProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphMinimap>');\n const size = props.size ?? SIZE_DEFAULT;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const rectRef = useRef<HTMLDivElement | null>(null);\n /** Pointer that owns the current pan gesture. Other simultaneous pointers\n * cannot move or terminate it. */\n const draggingPointerRef = useRef<number | null>(null);\n const declaredSceneRef = useRef<DeclaredSceneCache>({ ids: [], scene: null });\n /** mask lane for `getVisible`, recomputed once per refresh tick. */\n const visibleRef = useRef<{ ids: readonly NodeId[]; set: ReadonlySet<NodeId> | null }>({\n ids: [],\n set: null,\n });\n\n const controller = useMemo(() => {\n const getScene = (): OverviewScene | null => {\n // 1. Pinned core seam: the posBuf mirror, once it lands.\n const mirrored = (instance as unknown as OverviewSceneSurface).getOverviewScene?.();\n if (mirrored !== undefined) return mirrored;\n // 2. v0.9 fallback: declared node positions over the scene roster.\n const ids = instance.getSceneNodeIds();\n if (ids.length === 0) return null;\n const cache = declaredSceneRef.current;\n if (cache.ids === ids) return cache.scene;\n const positions = new Float32Array(2 * ids.length);\n let known = 0;\n for (let i = 0; i < ids.length; i++) {\n const node = instance.getNode(ids[i]!);\n const x = node?.x;\n const y = node?.y;\n if (typeof x === 'number' && typeof y === 'number') {\n positions[2 * i] = x;\n positions[2 * i + 1] = y;\n known++;\n } else {\n positions[2 * i] = NaN;\n positions[2 * i + 1] = NaN;\n }\n }\n const scene: OverviewScene | null = known > 0 ? { positions, count: ids.length } : null;\n declaredSceneRef.current = { ids, scene };\n return scene;\n };\n const getVisible = (index: number): boolean => {\n const v = visibleRef.current;\n if (v.set === null) return true;\n const id = v.ids[index];\n return id === undefined ? true : v.set.has(id);\n };\n return new OverviewController({ getScene, getVisible, size });\n }, [instance, size]);\n\n /** O(1) rect update from the store viewport over the LAST rasterized frame.\n * `zoom` is screen px per space unit; the world extent in view is the\n * host container's CSS size / zoom — measured from the positioned ancestor\n * (the <Graph> container). Zero-size hosts (jsdom/unmounted) degrade to a\n * MIN_RECT_PX center marker. */\n const updateRect = (v: ViewportState | null): void => {\n const rect = rectRef.current;\n if (rect === null) return;\n if (v === null || v.zoom <= 0) {\n rect.style.display = 'none';\n return;\n }\n const host = rootRef.current?.offsetParent;\n const hostW = host instanceof HTMLElement ? host.clientWidth : 0;\n const hostH = host instanceof HTMLElement ? host.clientHeight : 0;\n const halfW = hostW > 0 ? hostW / v.zoom / 2 : 0;\n const halfH = hostH > 0 ? hostH / v.zoom / 2 : 0;\n // Two opposite world corners (world y grows up, minimap y grows down).\n const a = controller.worldToMinimap(v.x - halfW, v.y + halfH);\n const b = controller.worldToMinimap(v.x + halfW, v.y - halfH);\n if (a === null || b === null) {\n rect.style.display = 'none'; // nothing rasterized yet\n return;\n }\n const w = Math.max(Math.abs(b[0] - a[0]), MIN_RECT_PX);\n const h = Math.max(Math.abs(b[1] - a[1]), MIN_RECT_PX);\n const x = (a[0] + b[0]) / 2 - w / 2;\n const y = (a[1] + b[1]) / 2 - h / 2;\n rect.style.display = 'block';\n rect.style.width = `${w}px`;\n rect.style.height = `${h}px`;\n rect.style.transform = `translate3d(${x}px, ${y}px, 0)`;\n };\n\n // Lane 1: thumbnail refresh — interval-probed controller throttle.\n useEffect(() => {\n const blit = (): void => {\n const state = instance.store.getState();\n // mask lane for the dimmed-alpha rasterization.\n if (state.visible.nodes !== state.nodeCount || state.hiddenNodeIds.size > 0) {\n const ids = instance.getSceneNodeIds();\n visibleRef.current = { ids, set: new Set(instance.getVisibleNodeIds()) };\n } else {\n visibleRef.current = { ids: [], set: null };\n }\n const raster = controller.rasterize();\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const ctx = canvas.getContext('2d');\n if (ctx === null) return;\n if (raster === null) {\n ctx.clearRect(0, 0, size, size);\n updateRect(null);\n return;\n }\n if (typeof ImageData === 'undefined') return; // non-DOM environment\n // Copy: ImageData requires a plain-ArrayBuffer-backed clamped array.\n const pixels = new Uint8ClampedArray(raster.bitmap);\n ctx.putImageData(new ImageData(pixels, controller.size, controller.size), 0, 0);\n // A fresh frame can move the world→minimap mapping: re-derive the rect.\n updateRect(instance.store.getState().viewport);\n };\n const tick = (): void => {\n const state = instance.store.getState();\n // `revisions.render` is the v0.9 positions epoch (see module JSDoc):\n // idle with an unchanged render revision is provably position-stable.\n if (!controller.shouldRefresh(Date.now(), state.simulationRunning, state.revisions.render)) {\n return;\n }\n blit();\n };\n tick(); // first paint without waiting a full interval\n const timer = setInterval(tick, OVERVIEW_HOT_INTERVAL_MS);\n return () => {\n clearInterval(timer);\n };\n // updateRect is stable for each (instance, controller) pair — refs + controller\n // only — so it is deliberately not a dependency (this repo does not\n // configure the react-hooks lint plugin).\n }, [instance, controller, size]);\n\n // Lane 2: viewport rectangle — imperative store subscription, O(1) per\n // viewport publication, no re-rasterize and no React re-render.\n useEffect(() => {\n let prev = instance.store.getState().viewport;\n updateRect(prev);\n return instance.store.subscribe(() => {\n const next = instance.store.getState().viewport;\n if (next === prev) return;\n prev = next;\n updateRect(next);\n });\n // updateRect deliberately not a dependency (see the refresh effect above).\n }, [instance, controller]);\n\n // Lane 3: pan. Down pans immediately; move keeps panning while dragging.\n const panTo = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n const canvas = canvasRef.current;\n if (canvas === null) return;\n const b = canvas.getBoundingClientRect();\n // Canvas CSS size vs raster size (zero-size rects — jsdom — map 1:1).\n const sx = b.width > 0 ? controller.size / b.width : 1;\n const sy = b.height > 0 ? controller.size / b.height : 1;\n const world = controller.minimapToWorld((e.clientX - b.left) * sx, (e.clientY - b.top) * sy);\n if (world === null) return; // nothing rasterized yet — nothing to map\n instance.setViewport({ x: world[0], y: world[1] });\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n const owner = draggingPointerRef.current;\n if (owner !== null && owner !== e.pointerId) return;\n e.preventDefault();\n draggingPointerRef.current = e.pointerId;\n try {\n e.currentTarget.setPointerCapture(e.pointerId);\n } catch {\n /* pointer capture unavailable (jsdom/legacy) */\n }\n panTo(e);\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (draggingPointerRef.current !== e.pointerId) return;\n panTo(e);\n };\n\n const endDrag = (e: ReactPointerEvent<HTMLCanvasElement>): void => {\n if (draggingPointerRef.current !== e.pointerId) return;\n draggingPointerRef.current = null;\n };\n\n return (\n <div\n ref={rootRef}\n data-orbit-minimap=\"\"\n role=\"group\"\n aria-label={props.label ?? 'Graph minimap'}\n className={props.className}\n style={\n props.className !== undefined\n ? // The thumbnail is MECHANICAL, not decorative: dropping its box\n // would collapse the canvas, so size survives the class hook.\n { width: size, height: size, ...props.style }\n : mergeStyle(\n {\n ...cornerStyle(props.position ?? 'bottom-right', props.offset),\n ...ROOT_STYLE,\n width: size,\n height: size,\n },\n props.style,\n )\n }\n >\n <canvas\n ref={canvasRef}\n data-orbit-minimap-canvas=\"\"\n width={size}\n height={size}\n style={CANVAS_STYLE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onLostPointerCapture={endDrag}\n onPointerLeave={endDrag}\n />\n <div ref={rectRef} data-orbit-minimap-viewport=\"\" style={RECT_STYLE} />\n </div>\n );\n}\n"]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getSearchResultUnavailableCallback } from '../chunk-ODOPSUI7.js';
|
|
2
|
+
import '../chunk-S36LHSNX.js';
|
|
1
3
|
import { useResolvedInstance } from '../chunk-NPOV5GA3.js';
|
|
2
4
|
import { useId, useState, useRef, useEffect, useCallback, useSyncExternalStore } from 'react';
|
|
3
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
@@ -252,7 +254,12 @@ function GraphNavigator(props) {
|
|
|
252
254
|
const activate = (row) => {
|
|
253
255
|
if (row.listKey === "search") {
|
|
254
256
|
const result = searchResults.find((r) => r.id === row.nodeId);
|
|
255
|
-
if (result !== void 0)
|
|
257
|
+
if (result !== void 0) {
|
|
258
|
+
const activation = instance.activateSearchResult(result);
|
|
259
|
+
if (activation.status === "unavailable") {
|
|
260
|
+
getSearchResultUnavailableCallback(instance)?.(result, activation.reason);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
256
263
|
return;
|
|
257
264
|
}
|
|
258
265
|
const neighbors = instance.focusNode(row.nodeId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Navigator/index.tsx"],"names":[],"mappings":";;;;AAkFA,IAAM,wBAAA,GAA2B,EAAA;AAgEjC,SAAS,iBAAiB,QAAA,EAA2C;AACnE,EAAA,MAAM,QAAA,GAAW,OAA6B,IAAI,CAAA;AAClD,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,EAAA,KAAmB,QAAA,CAAS,KAAA,CAAM,UAAU,EAAE,CAAA;AAAA,IAC/C,CAAC,QAAQ;AAAA,GACX;AACA,EAAA,MAAM,WAAA,GAAc,YAAY,MAAqB;AACnD,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,KAAA,CAAM,QAAA,EAAS;AAClC,IAAA,MAAM,OAAO,QAAA,CAAS,OAAA;AACtB,IAAA,IACE,IAAA,KAAS,IAAA,IACT,IAAA,CAAK,SAAA,KAAc,CAAA,CAAE,SAAA,IACrB,IAAA,CAAK,IAAA,KAAS,CAAA,CAAE,IAAA,IAChB,IAAA,CAAK,MAAA,KAAW,CAAA,CAAE,iBAClB,IAAA,CAAK,KAAA,KAAU,CAAA,CAAE,SAAA,CAAU,KAAA,IAC3B,IAAA,CAAK,SAAA,KAAc,CAAA,CAAE,SAAA,IACrB,IAAA,CAAK,MAAA,KAAW,CAAA,CAAE,MAAA,EAClB;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,MAAM,IAAA,GAAsB;AAAA,MAC1B,WAAW,CAAA,CAAE,SAAA;AAAA,MACb,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,QAAQ,CAAA,CAAE,aAAA;AAAA,MACV,KAAA,EAAO,EAAE,SAAA,CAAU,KAAA;AAAA,MACnB,WAAW,CAAA,CAAE,SAAA;AAAA,MACb,QAAQ,CAAA,CAAE;AAAA,KACZ;AACA,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AACb,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,WAAW,CAAA;AACjE;AASA,SAAS,UAAA,CAAW,GAAA,EAAwB,OAAA,EAAiB,IAAA,EAA0B;AACrF,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,GAAA,CAAI,MAAA,GAAS,IAAI,CAAC,CAAA;AAC1D,EAAA,MAAM,IAAA,GAAO,KAAK,GAAA,CAAI,IAAA,CAAK,IAAI,OAAA,EAAS,CAAC,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA;AACzD,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,GAAA,CAAI,KAAA,CAAM,IAAA,GAAO,IAAA,EAAM,IAAA,GAAO,IAAA,GAAO,IAAI,CAAA,EAAE;AAC9E;AAGA,IAAM,WAAA,GAA6B;AAAA,EACjC,aAAA,EAAe,MAAA;AAAA,EACf,OAAA,EAAS,MAAA;AAAA,EACT,aAAA,EAAe,QAAA;AAAA,EACf,GAAA,EAAK,CAAA;AAAA,EACL,QAAA,EAAU,GAAA;AAAA,EACV,QAAA,EAAU;AACZ,CAAA;AACA,IAAM,UAAA,GAA4B,EAAE,SAAA,EAAW,MAAA,EAAQ,WAAW,GAAA,EAAI;AACtE,IAAM,YAAA,GAA8B,EAAE,UAAA,EAAY,GAAA,EAAK,SAAS,SAAA,EAAU;AAC1E,IAAM,UAAA,GAA4B;AAAA,EAChC,OAAA,EAAS,SAAA;AAAA,EACT,MAAA,EAAQ,SAAA;AAAA,EACR,UAAA,EAAY,QAAA;AAAA,EACZ,QAAA,EAAU,QAAA;AAAA,EACV,YAAA,EAAc;AAChB,CAAA;AAEA,IAAM,iBAAA,GAAmC;AAAA,EACvC,GAAG,UAAA;AAAA,EACH,OAAA,EAAS,wBAAA;AAAA,EACT,aAAA,EAAe;AACjB,CAAA;AACA,IAAM,eAA8B,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAE;AACpF,IAAM,WAAA,GAA6B,EAAE,OAAA,EAAS,SAAA,EAAW,WAAW,QAAA,EAAS;AAEtE,SAAS,eAAe,KAAA,EAA0C;AACvE,EAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,KAAA,CAAM,QAAA,EAAU,kBAAkB,CAAA;AACvE,EAAA,MAAM,KAAA,GAAQ,iBAAiB,QAAQ,CAAA;AAIvC,EAAA,KAAK,KAAA,CAAM,YAAA;AAEX,EAAA,MAAM,SAAS,KAAA,EAAM;AACrB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAyB,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAAuC;AAAA,IAC/D,MAAA,EAAQ,CAAA;AAAA,IACR,SAAA,EAAW,CAAA;AAAA,IACX,SAAA,EAAW,CAAA;AAAA,IACX,KAAA,EAAO;AAAA,GACR,CAAA;AAED,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAwB,IAAI,CAAA;AAG9D,EAAA,MAAM,SAAA,GAAY,MAAA,iBAA4B,IAAI,GAAA,EAAK,CAAA;AAEvD,EAAA,MAAM,aAAa,MAAA,CAAuD;AAAA,IACxE,KAAA,EAAO,EAAA;AAAA,IACP,MAAA,sBAAY,GAAA;AAAI,GACjB,CAAA;AACD,EAAA,MAAM,QAAA,GAAW,MAAA,iBAAiC,IAAI,GAAA,EAAK,CAAA;AAE3D,EAAA,MAAM,eAAA,GAAkB,OAAO,KAAK,CAAA;AAIpC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAS,IAAA,IAAQ,IAAA,CAAK,UAAU,KAAA,CAAM,KAAA,UAAe,IAAI,CAAA;AAAA,EAC/D,CAAA,EAAG,CAAC,IAAA,EAAM,KAAA,CAAM,KAAK,CAAC,CAAA;AAMtB,EAAA,MAAM,eAAA,GAAkB,YAAY,MAAY;AAC9C,IAAA,IAAI,QAAA,CAAS,MAAM,QAAA,EAAS,CAAE,MAAM,MAAA,KAAW,IAAA,EAAM,QAAA,CAAS,aAAA,CAAc,IAAI,CAAA;AAAA,EAClF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAGb,EAAA,SAAA,CAAU,MAAM,MAAM,eAAA,EAAgB,EAAG,CAAC,eAAe,CAAC,CAAA;AAG1D,EAAA,IAAI,UAAA,CAAW,OAAA,CAAQ,KAAA,KAAU,KAAA,CAAM,KAAA,EAAO;AAC5C,IAAA,UAAA,CAAW,OAAA,GAAU,EAAE,KAAA,EAAO,KAAA,CAAM,OAAO,MAAA,kBAAQ,IAAI,KAAI,EAAE;AAAA,EAC/D;AAEA,EAAA,MAAM,aAAA,GAAgB,SAAS,gBAAA,EAAiB;AAGhD,EAAA,MAAM,YAAY,aAAA,EAAe,eAAA;AACjC,EAAA,MAAM,UAAA,GACJ,OAAO,SAAA,KAAc,QAAA,IAAY,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,IAAK,SAAA,IAAa,CAAA,GACxE,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA,GACpB,wBAAA;AAEN,EAAA,MAAM,qBAAqB,aAAA,EAAe,kBAAA;AAC1C,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAAuB;AACrC,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,CAAQ,EAAE,CAAA;AAChC,IAAA,IAAI,IAAA,KAAS,QAAW,OAAO,EAAA;AAC/B,IAAA,IAAI,uBAAuB,MAAA,EAAW;AACpC,MAAA,IAAI;AACF,QAAA,OAAO,mBAAmB,IAAI,CAAA;AAAA,MAChC,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,GAAQ,OAAO,CAAA;AAClC,IAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,IAAA,CAAK,EAAA;AAAA,EAClD,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAY,KAAA,CAAM,UAAU,OAAO,CAAA;AAG3D,EAAA,MAAM,OAAA,GAAU,CAAC,EAAA,KAAuB;AACtC,IAAA,MAAM,KAAA,GAAQ,CAAC,MAAA,CAAO,EAAE,CAAC,CAAA;AACzB,IAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,CAAQ,MAAA,CAAO,IAAI,EAAE,CAAA;AAC9C,IAAA,IAAI,KAAA,KAAU,QAAW,KAAA,CAAM,IAAA,CAAK,UAAU,CAAA,GAAI,YAAA,GAAe,CAAA,EAAG,KAAK,CAAA,UAAA,CAAY,CAAA;AACrF,IAAA,IAAI,MAAM,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,EAAG,KAAA,CAAM,KAAK,QAAQ,CAAA;AAC3C,IAAA,IAAI,MAAM,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA,EAAG,KAAA,CAAM,KAAK,QAAQ,CAAA;AAC7C,IAAA,OAAO,KAAA,CAAM,KAAK,QAAK,CAAA;AAAA,EACzB,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,OAAA,EAAiB,EAAA,KAAuB;AACxD,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA;AACzB,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,KAAA,GAAQ,MAAA,CAAO,IAAA;AACf,MAAA,MAAA,CAAO,GAAA,CAAI,IAAI,KAAK,CAAA;AAAA,IACtB;AACA,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,IAAA,EAAO,OAAO,IAAI,KAAK,CAAA,CAAA;AAAA,EACzC,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,OAAA,EAAuB,EAAA,MAAwB;AAAA,IAC9D,GAAA,EAAK,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAAA,IACrB,KAAA,EAAO,QAAA,CAAS,OAAA,EAAS,EAAE,CAAA;AAAA,IAC3B,MAAA,EAAQ,EAAA;AAAA,IACR,OAAA;AAAA,IACA,IAAA,EAAM,QAAQ,EAAE,CAAA;AAAA,IAChB,QAAA,EAAU,WAAA,CAAY,GAAA,CAAI,EAAE;AAAA,GAC9B,CAAA;AAGA,EAAA,MAAM,gBAAyC,KAAA,CAAM,MAAA,KAAW,OAAO,EAAC,GAAI,MAAM,MAAA,CAAO,OAAA;AACzF,EAAA,MAAM,SAAA,GAAqD;AAAA,IACzD,QAAQ,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,EAAE,CAAA;AAAA,IACrC,SAAA,EAAW,IAAA,KAAS,IAAA,GAAO,IAAA,CAAK,YAAY,EAAC;AAAA,IAC7C,SAAA,EAAW,MAAM,SAAA,CAAU,OAAA;AAAA;AAAA;AAAA,IAG3B,OAAO,IAAA,KAAS,IAAA,GAAO,QAAA,CAAS,eAAA,KAAoB;AAAC,GACvD;AAEA,EAAA,MAAM,WAAyB,EAAC;AAChC,EAAA,IAAI,KAAA,CAAM,WAAW,IAAA,EAAM;AACzB,IAAA,MAAM,OAAO,UAAA,CAAW,SAAA,CAAU,MAAA,EAAQ,KAAA,CAAM,QAAQ,UAAU,CAAA;AAClE,IAAA,MAAM,QAAQ,aAAA,CAAc,MAAA;AAC5B,IAAA,MAAM,KAAA,GAAQ,KAAK,IAAA,GAAO,UAAA;AAC1B,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,GAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAS,QAAA;AAAA,MACT,KAAA,EAAO,gBAAA;AAAA,MACP,YAAY,CAAA,mBAAA,EAAsB,KAAA,CAAM,OAAO,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,CAAA,EAC5D,KAAA,KAAU,CAAA,GAAI,QAAA,GAAW,SAC3B,CAAA,OAAA,EAAU,IAAA,CAAK,OAAO,CAAC,CAAA,IAAA,EAAO,KAAK,SAAS,CAAA,CAAA;AAAA;AAAA;AAAA;AAAA,MAI5C,MAAM,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,IAAI,CAAA,KAAM;AAC9B,QAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAChC,QAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,KAAA,GAAQ,CAAC,CAAA,EAAG,KAAA;AACxC,QAAA,OAAO,UAAU,MAAA,GAAY,GAAA,GAAM,EAAE,GAAG,GAAA,EAAK,MAAM,KAAA,EAAM;AAAA,MAC3D,CAAC,CAAA;AAAA,MACD,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,MAAM,OAAO,UAAA,CAAW,SAAA,CAAU,SAAA,EAAW,KAAA,CAAM,WAAW,UAAU,CAAA;AACxE,IAAA,MAAM,OAAA,GAAkB;AAAA,MACtB,GAAA,EAAK,CAAA,KAAA,EAAQ,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,MACpB,KAAA,EAAO,QAAA,CAAS,MAAA,EAAQ,IAAA,CAAK,EAAE,CAAA;AAAA,MAC/B,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,OAAA,EAAS,WAAA;AAAA,MACT,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA;AAAA,MACrB,QAAA,EAAU,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE;AAAA,KACnC;AACA,IAAA,MAAM,KAAA,GAAQ,KAAK,SAAA,CAAU,MAAA;AAC7B,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,GAAA,EAAK,SAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,KAAA,EAAO,cAAA;AAAA,MACP,YAAY,CAAA,aAAA,EAAgB,MAAA,CAAO,KAAK,EAAE,CAAC,KAAK,KAAK,CAAA,CAAA,EACnD,UAAU,CAAA,GAAI,UAAA,GAAa,WAC7B,CAAA,OAAA,EAAU,IAAA,CAAK,OAAO,CAAC,CAAA,IAAA,EAAO,KAAK,SAAS,CAAA,CAAA;AAAA,MAC5C,IAAA,EAAM,CAAC,OAAA,EAAS,GAAG,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAC,CAAC,CAAA;AAAA,MACnE,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,IAAI,KAAA,CAAM,SAAA,CAAU,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AACtC,IAAA,MAAM,OAAO,UAAA,CAAW,SAAA,CAAU,SAAA,EAAW,KAAA,CAAM,WAAW,UAAU,CAAA;AACxE,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,SAAA,CAAU,OAAA,CAAQ,MAAA;AACtC,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,GAAA,EAAK,WAAA;AAAA,MACL,OAAA,EAAS,WAAA;AAAA,MACT,KAAA,EAAO,WAAA;AAAA,MACP,UAAA,EAAY,CAAA,WAAA,EAAc,KAAK,CAAA,CAAA,EAAI,UAAU,CAAA,GAAI,MAAA,GAAS,OAAO,CAAA,OAAA,EAC/D,IAAA,CAAK,IAAA,GAAO,CACd,CAAA,IAAA,EAAO,KAAK,SAAS,CAAA,CAAA;AAAA,MACrB,IAAA,EAAM,KAAK,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAC,CAAA;AAAA,MACrD,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AACA,EAAA,IAAI,SAAS,IAAA,EAAM;AACjB,IAAA,MAAM,OAAO,UAAA,CAAW,SAAA,CAAU,KAAA,EAAO,KAAA,CAAM,OAAO,UAAU,CAAA;AAChE,IAAA,MAAM,KAAA,GAAQ,UAAU,KAAA,CAAM,MAAA;AAC9B,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,GAAA,EAAK,OAAA;AAAA,MACL,OAAA,EAAS,OAAA;AAAA,MACT,KAAA,EAAO,WAAA;AAAA,MACP,UAAA,EAAY,CAAA,WAAA,EAAc,KAAK,CAAA,CAAA,EAAI,UAAU,CAAA,GAAI,MAAA,GAAS,OAAO,CAAA,OAAA,EAC/D,IAAA,CAAK,IAAA,GAAO,CACd,CAAA,IAAA,EAAO,KAAK,SAAS,CAAA,CAAA;AAAA,MACrB,IAAA,EAAM,KAAK,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAC,CAAA;AAAA,MACjD,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,OAAiB,QAAA,CAAS,OAAA,CAAQ,CAAC,CAAA,KAAM,EAAE,IAAI,CAAA;AACrD,EAAA,IAAI,WAAA,GAAc,SAAA,KAAc,IAAA,GAAO,EAAA,GAAK,IAAA,CAAK,UAAU,CAAC,CAAA,KAAM,CAAA,CAAE,GAAA,KAAQ,SAAS,CAAA;AACrF,EAAA,IAAI,WAAA,KAAgB,EAAA,IAAM,IAAA,CAAK,MAAA,GAAS,GAAG,WAAA,GAAc,CAAA;AACzD,EAAA,MAAM,SAAA,GAAY,WAAA,IAAe,CAAA,GAAI,IAAA,CAAK,WAAW,CAAA,GAAI,MAAA;AACzD,EAAA,MAAM,aAAA,GACJ,SAAA,KAAc,MAAA,GAAY,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,SAAA,CAAU,OAAO,CAAA,GAAI,MAAA;AAIpF,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,gBAAgB,OAAA,EAAS;AAC9B,IAAA,eAAA,CAAgB,OAAA,GAAU,KAAA;AAC1B,IAAA,IAAI,cAAc,MAAA,EAAW;AAC7B,IAAA,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,SAAA,CAAU,KAAK,GAAG,KAAA,EAAM;AAAA,EAC/C,CAAC,CAAA;AAGD,EAAA,MAAM,gBAAgB,MAAY;AAChC,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAAA,EAC5B,CAAA;AAEA,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KAAwB;AACtC,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACvB,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,KAAA,EAAO,CAAC,CAAA,EAAG,IAAA,CAAK,MAAA,GAAS,CAAC,CAAC,CAAA;AAC9D,IAAA,IAAI,QAAQ,MAAA,EAAW;AACvB,IAAA,YAAA,CAAa,IAAI,GAAG,CAAA;AAGpB,IAAA,QAAA,CAAS,aAAA,CAAc,IAAI,MAAM,CAAA;AACjC,IAAA,aAAA,EAAc;AAAA,EAChB,CAAA;AAEA,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KAAwB;AACtC,IAAA,IAAI,cAAc,MAAA,EAAW;AAC7B,IAAA,MAAM,UAAU,SAAA,CAAU,OAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,UAAU,OAAO,CAAA;AAC/B,IAAA,MAAM,SAAA,GAAY,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,UAAU,CAAC,CAAA;AAClE,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA,EAAG,CAAC,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA;AACnE,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,UAAU,KAAA,EAAO,CAAC,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA;AACjE,IAAA,IAAI,SAAS,OAAA,EAAS;AACtB,IAAA,QAAA,CAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,GAAG,IAAA,EAAK,CAAE,CAAA;AAC3C,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,GAAO,UAAU,CAAA;AACrC,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,YAAA,CAAa,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE,CAAA;AAClC,MAAA,QAAA,CAAS,cAAc,KAAK,CAAA;AAAA,IAC9B;AACA,IAAA,aAAA,EAAc;AAAA,EAChB,CAAA;AAQA,EAAA,MAAM,QAAA,GAAW,CAAC,GAAA,KAAsB;AACtC,IAAA,IAAI,GAAA,CAAI,YAAY,QAAA,EAAU;AAC5B,MAAA,MAAM,MAAA,GAAS,cAAc,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,IAAI,MAAM,CAAA;AAC5D,MAAA,IAAI,MAAA,KAAW,MAAA,EAAW,QAAA,CAAS,oBAAA,CAAqB,MAAM,CAAA;AAC9D,MAAA;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAY,QAAA,CAAS,SAAA,CAAU,GAAA,CAAI,MAAM,CAAA;AAC/C,IAAA,UAAA,CAAW,QAAQ,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,MAAA,EAAQ,UAAU,MAAM,CAAA;AAC1D,IAAA,OAAA,CAAQ,EAAE,IAAI,GAAA,CAAI,MAAA,EAAQ,WAAW,KAAA,EAAO,KAAA,CAAM,OAAO,CAAA;AACzD,IAAA,QAAA,CAAS,CAAC,CAAA,MAAO,EAAE,GAAG,CAAA,EAAG,SAAA,EAAW,GAAE,CAAE,CAAA;AACxC,IAAA,YAAA,CAAa,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AACjC,IAAA,aAAA,EAAc;AAAA,EAChB,CAAA;AAIA,EAAA,MAAM,eAAA,GAAkB,CAAC,GAAA,KAAsB;AAC7C,IAAA,IAAI,GAAA,CAAI,YAAY,QAAA,IAAY,QAAA,CAAS,QAAQ,GAAA,CAAI,MAAM,MAAM,MAAA,EAAW;AAC5E,IAAA,MAAM,OAAA,GAAU,MAAM,SAAA,CAAU,OAAA;AAChC,IAAA,MAAM,OAAO,OAAA,CAAQ,QAAA,CAAS,IAAI,MAAM,CAAA,GACpC,QAAQ,MAAA,CAAO,CAAC,EAAA,KAAO,EAAA,KAAO,IAAI,MAAM,CAAA,GACxC,CAAC,GAAG,OAAA,EAAS,IAAI,MAAM,CAAA;AAC3B,IAAA,QAAA,CAAS,YAAY,IAAI,CAAA;AAAA,EAC3B,CAAA;AAEA,EAAA,MAAM,sBAAsB,MAAY;AACtC,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,YAAA,CAAa,IAAI,CAAA;AACjB,IAAA,eAAA,EAAgB;AAChB,IAAA,aAAA,EAAc;AAAA,EAChB,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAA2C;AAC5D,IAAA,IAAI,cAAc,MAAA,EAAW;AAC7B,IAAA,QAAQ,EAAE,GAAA;AAAK,MACb,KAAK,WAAA;AACH,QAAA,MAAA,CAAO,cAAc,CAAC,CAAA;AACtB,QAAA;AAAA,MACF,KAAK,SAAA;AACH,QAAA,MAAA,CAAO,cAAc,CAAC,CAAA;AACtB,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,MAAA,CAAO,CAAC,CAAA;AACR,QAAA;AAAA,MACF,KAAK,KAAA;AACH,QAAA,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AACtB,QAAA;AAAA,MACF,KAAK,UAAA;AACH,QAAA,MAAA,CAAO,CAAC,CAAA;AACR,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,MAAA,CAAO,EAAE,CAAA;AACT,QAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,QAAA,CAAS,SAAS,CAAA;AAClB,QAAA;AAAA,MACF,KAAK,GAAA;AACH,QAAA,eAAA,CAAgB,SAAS,CAAA;AACzB,QAAA;AAAA,MACF,KAAK,QAAA;AACH,QAAA,IAAI,SAAS,IAAA,EAAM;AACnB,QAAA,mBAAA,EAAoB;AACpB,QAAA;AAAA,MACF;AACE,QAAA;AAAA;AAEJ,IAAA,CAAA,CAAE,cAAA,EAAe;AAAA,EACnB,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,MAAM,KAAA,IAAS,iBAAA;AAElC,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,sBAAA,EAAqB,EAAA;AAAA,MACrB,IAAA,EAAK,OAAA;AAAA,MACL,YAAA,EAAY,UAAA;AAAA,MACZ,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,OAAO,EAAE,GAAG,WAAA,EAAa,GAAG,MAAM,KAAA,EAAM;AAAA,MACxC,MAAA,EAAQ,CAAC,CAAA,KAAM;AAGb,QAAA,IAAI,CAAC,CAAA,CAAE,aAAA,CAAc,QAAA,CAAS,CAAA,CAAE,aAA4B,CAAA,EAAG;AAC7D,UAAA,eAAA,EAAgB;AAAA,QAClB;AAAA,MACF,CAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,SAAA;AAAA,YACL,YAAA,EAAY,GAAG,UAAU,CAAA,MAAA,CAAA;AAAA,YACzB,yBAAuB,SAAA,EAAW,KAAA;AAAA,YAClC,mBAAA,EAAkB,+DAAA;AAAA,YAClB,SAAA;AAAA,YACA,KAAA,EAAO,UAAA;AAAA,YAEN,QAAA,EAAA,IAAA,CAAK,WAAW,CAAA,mBACf,GAAA,CAAC,SAAI,IAAA,EAAK,cAAA,EAAe,OAAO,WAAA,EAAa,QAAA,EAAA,UAAA,EAE7C,IAEA,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,qBACZ,IAAA,CAAC,SAAsB,IAAA,EAAK,OAAA,EAAQ,YAAA,EAAY,OAAA,CAAQ,UAAA,EACtD,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,SAAI,IAAA,EAAK,cAAA,EAAe,KAAA,EAAO,YAAA,EAC7B,kBAAQ,KAAA,EACX,CAAA;AAAA,cACC,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACzB,gBAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,KAAQ,SAAA,EAAW,GAAA;AACxC,gBAAA,uBACE,GAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBAEC,IAAI,GAAA,CAAI,KAAA;AAAA,oBACR,IAAA,EAAK,QAAA;AAAA,oBACL,iBAAe,GAAA,CAAI,QAAA;AAAA,oBACnB,QAAA,EAAU,WAAW,CAAA,GAAI,EAAA;AAAA,oBACzB,GAAA,EAAK,CAAC,EAAA,KAAO;AACX,sBAAA,IAAI,OAAO,IAAA,EAAM,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,IAAI,KAAK,CAAA;AAAA,2BAC7C,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,GAAA,CAAI,OAAO,EAAE,CAAA;AAAA,oBACzC,CAAA;AAAA,oBACA,SAAS,MAAM;AACb,sBAAA,YAAA,CAAa,IAAI,GAAG,CAAA;AACpB,sBAAA,QAAA,CAAS,aAAA,CAAc,IAAI,MAAM,CAAA;AACjC,sBAAA,aAAA,EAAc;AAAA,oBAChB,CAAA;AAAA,oBACA,KAAA,EAAO,WAAW,iBAAA,GAAoB,UAAA;AAAA,oBAErC,QAAA,EAAA,GAAA,CAAI;AAAA,mBAAA;AAAA,kBAhBA,GAAA,CAAI;AAAA,iBAiBX;AAAA,cAEJ,CAAC;AAAA,aAAA,EAAA,EA3BO,OAAA,CAAQ,GA4BlB,CACD;AAAA;AAAA,SAEL;AAAA,QACC,kBAAkB,MAAA,mBACjB,IAAA,CAAC,SAAI,6BAAA,EAA4B,EAAA,EAAG,OAAO,YAAA,EACzC,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAS,MAAM,MAAA,CAAO,EAAE,CAAA;AAAA,cACxB,QAAA,EAAU,cAAc,IAAA,KAAS,CAAA;AAAA,cACjC,mBAAA,EAAkB,QAAA;AAAA,cACnB,QAAA,EAAA;AAAA;AAAA,WAED;AAAA,0BACA,GAAA,CAAC,MAAA,EAAA,EAAK,IAAA,EAAK,QAAA,EAAS,kCAAA,EAAiC,IAClD,QAAA,EAAA,CAAA,EAAG,aAAA,CAAc,KAAK,CAAA,OAAA,EAAU,aAAA,CAAc,IAAA,GAAO,CAAC,CAAA,IAAA,EACrD,aAAA,CAAc,SAChB,CAAA,EAAA,EAAK,aAAA,CAAc,KAAK,CAAA,CAAA,EAAI,aAAA,CAAc,KAAA,KAAU,CAAA,GAAI,MAAA,GAAS,OAAO,CAAA,CAAA,CAAA,EAC1E,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,OAAA,EAAS,MAAM,MAAA,CAAO,CAAC,CAAA;AAAA,cACvB,QAAA,EAAU,aAAA,CAAc,IAAA,IAAQ,aAAA,CAAc,SAAA,GAAY,CAAA;AAAA,cAC1D,mBAAA,EAAkB,UAAA;AAAA,cACnB,QAAA,EAAA;AAAA;AAAA;AAED,SAAA,EACF,CAAA,GACE;AAAA;AAAA;AAAA,GACN;AAEJ","file":"Navigator.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Navigator` — `<GraphNavigator>`, the\n * bounded semantic navigator.\n *\n * The navigator is the keyboard/screen-reader equivalent surface for the\n * canvas: a visually compact panel with `role=\"listbox\"` semantics driven by\n * a roving tabindex — exactly one item is tabbable at any time, and\n * `aria-activedescendant` on the listbox container mirrors the active item's\n * stable DOM id. It is bounded by design: every list is paged by\n * `accessibility.navigatorWindow` (default 50), so the DOM never grows one\n * row per entity.\n *\n * Sections:\n * - **Search results** — the last completed search\n * (`store.search`), shown whenever the slice is non-null (it clears on\n * `clearSearch` / dataset swap). Paged like every other list; Enter\n * routes through `instance.activateSearchResult` (result contract:\n * an in-scene, mask-visible id focuses; anything else is classification\n * only — the navigator never mutates scope/filters). Row text prefers the\n * service-supplied `label`.\n * - **Focused node** — the transiently focused root plus its paged 1-hop\n * neighborhood.\n * - **Selection** — the current node selection, paged.\n * - **All nodes** — the entry list (first `navigatorWindow` nodes in\n * accepted-base order, paged over the full set), shown when nothing is\n * focused.\n *\n * ## Keyboard model — `keyboardMode: 'topology'` (the v0.4 default and only mode)\n *\n * | Key | Action |\n * |---------------------|-------------------------------------------------------------------|\n * | ArrowDown / ArrowUp | move the active item within the current list |\n * | Home / End | move to the bounds of the current list |\n * | PageDown / PageUp | next / previous page of the active item's list |\n * | Enter | focus the node (`instance.focusNode` — camera fly) and re-root the neighborhood at it |\n * | Space | toggle the node's selection membership |\n * | Escape | clear the transient focus (returns to the entry list) |\n *\n * `keyboardMode: 'spatial'` (arrows move by screen direction) is a\n * documented post-v0.4 mode and intentionally NOT part of the accepted prop\n * union yet — only `'topology'` type-checks in v0.4.\n *\n * Text names resolve via `accessibility.getAccessibleLabel`, falling back to\n * `attrs.label` (when a string) and then the node id — always rendered as\n * TEXT NODES.\n * Each item exposes selection state (`aria-selected`), its neighbor count\n * when known, and pinned/hidden state in text (e.g. `hub · 5 neighbors ·\n * pinned`).\n *\n * Neighborhood semantics (v0.4): direction is UNDIRECTED — `focusNode`\n * resolves the 1-hop neighbor ids through the engine's adjacency when the\n * adapter provides one, else through the core CSR adjacency\n * (`buildAdjacency`/`neighborsOf`) built lazily per accepted model revision\n * and invalidated on model change; self-loops are excluded and parallel\n * edges deduplicate. The navigator caches the resolved neighborhood and the\n * per-node neighbor counts it has learned in refs keyed by\n * `revisions.model`; a model change drops the (now stale) transient root and\n * returns to the entry list. Neighbor counts therefore appear on items whose\n * neighborhood has been resolved (the current and previously visited roots);\n * a public O(1) degree read is a post-v0.4 core surface.\n *\n * Focus never lives only in WebGL pixels: the active item carries a\n * visible DOM focus outline and receives real DOM focus on keyboard moves.\n * Sync is navigator → graph (Enter → `focusNode` camera fly); graph →\n * navigator focus sync is deferred past v0.4. Space routes through\n * `instance.selectNodes`, so a controlled selection surfaces intent only\n * — `aria-selected` always reflects the store.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent, ReactElement } from 'react';\nimport type {\n AccessibilityConfig,\n GraphNode,\n NodeId,\n SearchResult,\n SelectionState,\n} from '@modernrelay/orbit-core';\nimport { useResolvedInstance } from '../shared';\nimport type { AnyGraphInstance } from '../../GraphProvider';\n\n/** default page size when `accessibility.navigatorWindow` is unset. */\nconst DEFAULT_NAVIGATOR_WINDOW = 50;\n\nexport interface GraphNavigatorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /**\n * Keyboard model. v0.4 ships `'topology'` (the default; documented above);\n * `'spatial'` (arrows-by-screen-direction) is post-v0.4 and not accepted\n * by this union yet.\n */\n keyboardMode?: 'topology';\n /** Accessible name of the navigator panel. Default 'Graph navigator'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n/** Which paged id list a row belongs to (drives PageUp/PageDown targeting). */\ntype PagedListKey = 'search' | 'neighbors' | 'selection' | 'entry';\n\ninterface NavRow {\n /** Stable identity across renders: `${list}:${nodeId}` (`root:` for the root row). */\n key: string;\n /** Stable DOM id (aria-activedescendant target). */\n domId: string;\n nodeId: NodeId;\n listKey: PagedListKey;\n text: string;\n selected: boolean;\n}\n\ninterface NavSection {\n key: 'search' | 'focused' | 'selection' | 'entry';\n listKey: PagedListKey;\n label: string;\n /** Group accessible name; carries the 'page x of y' state. */\n groupLabel: string;\n rows: NavRow[];\n page: number;\n pageCount: number;\n /** Total pageable items (excludes the focused section's root chrome row). */\n total: number;\n}\n\n/** Transient navigator focus: the neighborhood root. */\ninterface NavRoot {\n id: NodeId;\n neighbors: readonly NodeId[];\n /** Model revision the neighborhood was resolved against. */\n model: number;\n}\n\ninterface NavStoreSlice {\n selection: SelectionState;\n pins: ReadonlyMap<NodeId, readonly [number, number]>;\n hidden: ReadonlySet<NodeId>;\n model: number;\n nodeCount: number;\n /** Last completed search; null hides the section. */\n search: { query: string; results: readonly SearchResult[] } | null;\n}\n\n/** Narrow store subscription: re-renders only when a navigator-relevant slice\n * changes (never on per-frame viewport writes). */\nfunction useNavStoreSlice(instance: AnyGraphInstance): NavStoreSlice {\n const cacheRef = useRef<NavStoreSlice | null>(null);\n const subscribe = useCallback(\n (cb: () => void) => instance.store.subscribe(cb),\n [instance],\n );\n const getSnapshot = useCallback((): NavStoreSlice => {\n const s = instance.store.getState();\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === s.selection &&\n prev.pins === s.pins &&\n prev.hidden === s.hiddenNodeIds &&\n prev.model === s.revisions.model &&\n prev.nodeCount === s.nodeCount &&\n prev.search === s.search\n ) {\n return prev;\n }\n const next: NavStoreSlice = {\n selection: s.selection,\n pins: s.pins,\n hidden: s.hiddenNodeIds,\n model: s.revisions.model,\n nodeCount: s.nodeCount,\n search: s.search,\n };\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\ninterface PageWindow {\n page: number;\n pageCount: number;\n items: readonly NodeId[];\n}\n\n/** Clamp a raw page into `[0, pageCount)` and slice that page's ids. */\nfunction pageWindow(all: readonly NodeId[], rawPage: number, size: number): PageWindow {\n const pageCount = Math.max(1, Math.ceil(all.length / size));\n const page = Math.min(Math.max(rawPage, 0), pageCount - 1);\n return { page, pageCount, items: all.slice(page * size, page * size + size) };\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n fontSize: 12,\n};\nconst LIST_STYLE: CSSProperties = { overflowY: 'auto', maxHeight: 320 };\nconst HEADER_STYLE: CSSProperties = { fontWeight: 600, padding: '4px 6px' };\nconst ITEM_STYLE: CSSProperties = {\n padding: '2px 6px',\n cursor: 'default',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n/** visible DOM focus ring on the active item. */\nconst ACTIVE_ITEM_STYLE: CSSProperties = {\n ...ITEM_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst PAGING_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 8 };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 6px', fontStyle: 'italic' };\n\nexport function GraphNavigator(props: GraphNavigatorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphNavigator>');\n const slice = useNavStoreSlice(instance);\n\n // 'topology' is the only v0.4 mode; the prop exists so the shape is stable\n // when 'spatial' lands post-v0.4 (no behavioral switch yet).\n void props.keyboardMode;\n\n const baseId = useId();\n const [root, setRoot] = useState<NavRoot | null>(null);\n const [pages, setPages] = useState<Record<PagedListKey, number>>({\n search: 0,\n neighbors: 0,\n selection: 0,\n entry: 0,\n });\n /** Active row identity (roving target); null → first row. */\n const [activeKey, setActiveKey] = useState<string | null>(null);\n\n /** Stable per-node DOM-id tokens (node ids may contain arbitrary text). */\n const tokensRef = useRef<Map<NodeId, number>>(new Map());\n /** Learned neighbor counts, cached per model revision (see module JSDoc). */\n const degreesRef = useRef<{ model: number; counts: Map<NodeId, number> }>({\n model: -1,\n counts: new Map(),\n });\n const itemsRef = useRef<Map<string, HTMLElement>>(new Map());\n /** Set by keyboard handlers so the focus effect moves DOM focus once. */\n const pendingFocusRef = useRef(false);\n\n // A model change invalidates the resolved neighborhood: drop the transient\n // root (back to the entry list) rather than rendering stale rows.\n useEffect(() => {\n if (root !== null && root.model !== slice.model) setRoot(null);\n }, [root, slice.model]);\n\n // release the keyboard ring — but ONLY when no pointer hover owns the\n // shared channel (blur while the pointer rests on a canvas node must not\n // erase that node's hover ring; hover already superseded the sticky\n // keyboard target inside core, so there is nothing else to clear).\n const releaseEmphasis = useCallback((): void => {\n if (instance.store.getState().hover.nodeId === null) instance.emphasizeNode(null);\n }, [instance]);\n\n // A ring left by keyboard navigation must not outlive the navigator.\n useEffect(() => () => releaseEmphasis(), [releaseEmphasis]);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (degreesRef.current.model !== slice.model) {\n degreesRef.current = { model: slice.model, counts: new Map() };\n }\n\n const accessibility = instance.getAccessibility() as\n | AccessibilityConfig<Record<string, unknown>>\n | undefined;\n const rawWindow = accessibility?.navigatorWindow;\n const windowSize =\n typeof rawWindow === 'number' && Number.isFinite(rawWindow) && rawWindow >= 1\n ? Math.floor(rawWindow)\n : DEFAULT_NAVIGATOR_WINDOW;\n\n const getAccessibleLabel = accessibility?.getAccessibleLabel;\n const nameOf = (id: NodeId): string => {\n const node = instance.getNode(id) as GraphNode<Record<string, unknown>> | undefined;\n if (node === undefined) return id;\n if (getAccessibleLabel !== undefined) {\n try {\n return getAccessibleLabel(node);\n } catch {\n // fall through to the built-in resolution\n }\n }\n const label = node.attrs?.['label'];\n return typeof label === 'string' ? label : node.id;\n };\n\n const selectedSet = new Set<NodeId>(slice.selection.nodeIds);\n\n /** '{name} · {n} neighbors · pinned · hidden' — always a single text node. */\n const rowText = (id: NodeId): string => {\n const parts = [nameOf(id)];\n const count = degreesRef.current.counts.get(id);\n if (count !== undefined) parts.push(count === 1 ? '1 neighbor' : `${count} neighbors`);\n if (slice.pins.has(id)) parts.push('pinned');\n if (slice.hidden.has(id)) parts.push('hidden');\n return parts.join(' · ');\n };\n\n const domIdFor = (listKey: string, id: NodeId): string => {\n const tokens = tokensRef.current;\n let token = tokens.get(id);\n if (token === undefined) {\n token = tokens.size;\n tokens.set(id, token);\n }\n return `${baseId}nav-${listKey}-${token}`;\n };\n\n const makeRow = (listKey: PagedListKey, id: NodeId): NavRow => ({\n key: `${listKey}:${id}`,\n domId: domIdFor(listKey, id),\n nodeId: id,\n listKey,\n text: rowText(id),\n selected: selectedSet.has(id),\n });\n\n // --- section/row model (all O(navigatorWindow) work) ---\n const searchResults: readonly SearchResult[] = slice.search === null ? [] : slice.search.results;\n const listItems: Record<PagedListKey, readonly NodeId[]> = {\n search: searchResults.map((r) => r.id),\n neighbors: root !== null ? root.neighbors : [],\n selection: slice.selection.nodeIds,\n // the entry list is the SCENE roster (scope applied, mask not)\n // hidden/filtered nodes stay listed with their state exposed in text.\n entry: root === null ? instance.getSceneNodeIds() : [],\n };\n\n const sections: NavSection[] = [];\n if (slice.search !== null) {\n const view = pageWindow(listItems.search, pages.search, windowSize);\n const count = searchResults.length;\n const start = view.page * windowSize;\n sections.push({\n key: 'search',\n listKey: 'search',\n label: 'Search results',\n groupLabel: `Search results for ${slice.search.query}: ${count} ${\n count === 1 ? 'result' : 'results'\n }, page ${view.page + 1} of ${view.pageCount}`,\n // row text prefers the service-supplied label (still a TEXT\n // NODE); results may name ids outside the model — nameOf falls back to\n // the id for those.\n rows: view.items.map((id, i) => {\n const row = makeRow('search', id);\n const label = searchResults[start + i]?.label;\n return label === undefined ? row : { ...row, text: label };\n }),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root !== null) {\n const view = pageWindow(listItems.neighbors, pages.neighbors, windowSize);\n const rootRow: NavRow = {\n key: `root:${root.id}`,\n domId: domIdFor('root', root.id),\n nodeId: root.id,\n listKey: 'neighbors',\n text: rowText(root.id),\n selected: selectedSet.has(root.id),\n };\n const count = root.neighbors.length;\n sections.push({\n key: 'focused',\n listKey: 'neighbors',\n label: 'Focused node',\n groupLabel: `Focused node ${nameOf(root.id)}: ${count} ${\n count === 1 ? 'neighbor' : 'neighbors'\n }, page ${view.page + 1} of ${view.pageCount}`,\n rows: [rootRow, ...view.items.map((id) => makeRow('neighbors', id))],\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (slice.selection.nodeIds.length > 0) {\n const view = pageWindow(listItems.selection, pages.selection, windowSize);\n const count = slice.selection.nodeIds.length;\n sections.push({\n key: 'selection',\n listKey: 'selection',\n label: 'Selection',\n groupLabel: `Selection: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('selection', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root === null) {\n const view = pageWindow(listItems.entry, pages.entry, windowSize);\n const count = listItems.entry.length;\n sections.push({\n key: 'entry',\n listKey: 'entry',\n label: 'All nodes',\n groupLabel: `All nodes: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('entry', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n\n const rows: NavRow[] = sections.flatMap((s) => s.rows);\n let activeIndex = activeKey === null ? -1 : rows.findIndex((r) => r.key === activeKey);\n if (activeIndex === -1 && rows.length > 0) activeIndex = 0;\n const activeRow = activeIndex >= 0 ? rows[activeIndex] : undefined;\n const activeSection =\n activeRow !== undefined ? sections.find((s) => s.listKey === activeRow.listKey) : undefined;\n\n // Move real DOM focus to the active item after keyboard-driven changes so\n // the focus ring is a genuine DOM focus, never only a WebGL highlight.\n useEffect(() => {\n if (!pendingFocusRef.current) return;\n pendingFocusRef.current = false;\n if (activeRow === undefined) return;\n itemsRef.current.get(activeRow.domId)?.focus();\n });\n\n // --- keyboard model ('topology'; see module JSDoc) ---\n const focusItemSoon = (): void => {\n pendingFocusRef.current = true;\n };\n\n const moveTo = (index: number): void => {\n if (rows.length === 0) return;\n const row = rows[Math.min(Math.max(index, 0), rows.length - 1)];\n if (row === undefined) return;\n setActiveKey(row.key);\n // Keyboard parity with pointer hover: ring the active row\n // WITHOUT flying the camera (that stays on Enter via focusNode).\n instance.emphasizeNode(row.nodeId);\n focusItemSoon();\n };\n\n const pageBy = (delta: number): void => {\n if (activeRow === undefined) return;\n const listKey = activeRow.listKey;\n const items = listItems[listKey];\n const pageCount = Math.max(1, Math.ceil(items.length / windowSize));\n const current = Math.min(Math.max(pages[listKey], 0), pageCount - 1);\n const next = Math.min(Math.max(current + delta, 0), pageCount - 1);\n if (next === current) return;\n setPages((p) => ({ ...p, [listKey]: next }));\n const first = items[next * windowSize];\n if (first !== undefined) {\n setActiveKey(`${listKey}:${first}`);\n instance.emphasizeNode(first);\n }\n focusItemSoon();\n };\n\n /** Enter: focus the node in the graph (camera fly) and re-root the\n * neighborhood. The returned ids come from the core adjacency resolution\n * (undirected; see module JSDoc). Search rows route through the\n * result contract instead: `activateSearchResult` focuses an in-scene,\n * mask-visible id and otherwise only CLASSIFIES — the navigator never\n * mutates scope/filters (the host reacts through its own channels). */\n const activate = (row: NavRow): void => {\n if (row.listKey === 'search') {\n const result = searchResults.find((r) => r.id === row.nodeId);\n if (result !== undefined) instance.activateSearchResult(result);\n return;\n }\n const neighbors = instance.focusNode(row.nodeId);\n degreesRef.current.counts.set(row.nodeId, neighbors.length);\n setRoot({ id: row.nodeId, neighbors, model: slice.model });\n setPages((p) => ({ ...p, neighbors: 0 }));\n setActiveKey(`root:${row.nodeId}`);\n focusItemSoon();\n };\n\n /** Space: toggle membership through the selection ownership path.\n * Search rows may name ids outside the accepted model — those no-op. */\n const toggleSelection = (row: NavRow): void => {\n if (row.listKey === 'search' && instance.getNode(row.nodeId) === undefined) return;\n const current = slice.selection.nodeIds;\n const next = current.includes(row.nodeId)\n ? current.filter((id) => id !== row.nodeId)\n : [...current, row.nodeId];\n instance.selectNodes(next);\n };\n\n const clearTransientFocus = (): void => {\n setRoot(null);\n setActiveKey(null);\n releaseEmphasis();\n focusItemSoon();\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (activeRow === undefined) return;\n switch (e.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rows.length - 1);\n break;\n case 'PageDown':\n pageBy(1);\n break;\n case 'PageUp':\n pageBy(-1);\n break;\n case 'Enter':\n activate(activeRow);\n break;\n case ' ':\n toggleSelection(activeRow);\n break;\n case 'Escape':\n if (root === null) return; // nothing transient — let it bubble\n clearTransientFocus();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const panelLabel = props.label ?? 'Graph navigator';\n\n return (\n <div\n data-orbit-navigator=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...props.style }}\n onBlur={(e) => {\n // Focus left the panel entirely (paging buttons stay inside): the\n // keyboard ring clears, mirroring pointer hover leaving the canvas.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n releaseEmphasis();\n }\n }}\n >\n <div\n role=\"listbox\"\n aria-label={`${panelLabel} items`}\n aria-activedescendant={activeRow?.domId}\n aria-keyshortcuts=\"ArrowUp ArrowDown Home End PageUp PageDown Enter Space Escape\"\n onKeyDown={onKeyDown}\n style={LIST_STYLE}\n >\n {rows.length === 0 ? (\n <div role=\"presentation\" style={EMPTY_STYLE}>\n No nodes\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.key} role=\"group\" aria-label={section.groupLabel}>\n <div role=\"presentation\" style={HEADER_STYLE}>\n {section.label}\n </div>\n {section.rows.map((row) => {\n const isActive = row.key === activeRow?.key;\n return (\n <div\n key={row.key}\n id={row.domId}\n role=\"option\"\n aria-selected={row.selected}\n tabIndex={isActive ? 0 : -1}\n ref={(el) => {\n if (el === null) itemsRef.current.delete(row.domId);\n else itemsRef.current.set(row.domId, el);\n }}\n onClick={() => {\n setActiveKey(row.key);\n instance.emphasizeNode(row.nodeId); // active row = ringed row\n focusItemSoon();\n }}\n style={isActive ? ACTIVE_ITEM_STYLE : ITEM_STYLE}\n >\n {row.text}\n </div>\n );\n })}\n </div>\n ))\n )}\n </div>\n {activeSection !== undefined ? (\n <div data-orbit-navigator-paging=\"\" style={PAGING_STYLE}>\n <button\n type=\"button\"\n onClick={() => pageBy(-1)}\n disabled={activeSection.page === 0}\n aria-keyshortcuts=\"PageUp\"\n >\n Previous page\n </button>\n <span role=\"status\" data-orbit-navigator-page-status=\"\">\n {`${activeSection.label}: page ${activeSection.page + 1} of ${\n activeSection.pageCount\n } (${activeSection.total} ${activeSection.total === 1 ? 'item' : 'items'})`}\n </span>\n <button\n type=\"button\"\n onClick={() => pageBy(1)}\n disabled={activeSection.page >= activeSection.pageCount - 1}\n aria-keyshortcuts=\"PageDown\"\n >\n Next page\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Navigator/index.tsx"],"names":[],"mappings":"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","file":"Navigator.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Navigator` — `<GraphNavigator>`, the\n * bounded semantic navigator.\n *\n * The navigator is the keyboard/screen-reader equivalent surface for the\n * canvas: a visually compact panel with `role=\"listbox\"` semantics driven by\n * a roving tabindex — exactly one item is tabbable at any time, and\n * `aria-activedescendant` on the listbox container mirrors the active item's\n * stable DOM id. It is bounded by design: every list is paged by\n * `accessibility.navigatorWindow` (default 50), so the DOM never grows one\n * row per entity.\n *\n * Sections:\n * - **Search results** — the last completed search\n * (`store.search`), shown whenever the slice is non-null (it clears on\n * `clearSearch` / dataset swap). Paged like every other list; Enter\n * routes through `instance.activateSearchResult` (result contract:\n * an in-scene, mask-visible id focuses; anything else is classification\n * only — the navigator never mutates scope/filters). Row text prefers the\n * service-supplied `label`.\n * - **Focused node** — the transiently focused root plus its paged 1-hop\n * neighborhood.\n * - **Selection** — the current node selection, paged.\n * - **All nodes** — the entry list (first `navigatorWindow` nodes in\n * accepted-base order, paged over the full set), shown when nothing is\n * focused.\n *\n * ## Keyboard model — `keyboardMode: 'topology'` (the v0.4 default and only mode)\n *\n * | Key | Action |\n * |---------------------|-------------------------------------------------------------------|\n * | ArrowDown / ArrowUp | move the active item within the current list |\n * | Home / End | move to the bounds of the current list |\n * | PageDown / PageUp | next / previous page of the active item's list |\n * | Enter | focus the node (`instance.focusNode` — camera fly) and re-root the neighborhood at it |\n * | Space | toggle the node's selection membership |\n * | Escape | clear the transient focus (returns to the entry list) |\n *\n * `keyboardMode: 'spatial'` (arrows move by screen direction) is a\n * documented post-v0.4 mode and intentionally NOT part of the accepted prop\n * union yet — only `'topology'` type-checks in v0.4.\n *\n * Text names resolve via `accessibility.getAccessibleLabel`, falling back to\n * `attrs.label` (when a string) and then the node id — always rendered as\n * TEXT NODES.\n * Each item exposes selection state (`aria-selected`), its neighbor count\n * when known, and pinned/hidden state in text (e.g. `hub · 5 neighbors ·\n * pinned`).\n *\n * Neighborhood semantics (v0.4): direction is UNDIRECTED — `focusNode`\n * resolves the 1-hop neighbor ids through the engine's adjacency when the\n * adapter provides one, else through the core CSR adjacency\n * (`buildAdjacency`/`neighborsOf`) built lazily per accepted model revision\n * and invalidated on model change; self-loops are excluded and parallel\n * edges deduplicate. The navigator caches the resolved neighborhood and the\n * per-node neighbor counts it has learned in refs keyed by\n * `revisions.model`; a model change drops the (now stale) transient root and\n * returns to the entry list. Neighbor counts therefore appear on items whose\n * neighborhood has been resolved (the current and previously visited roots);\n * a public O(1) degree read is a post-v0.4 core surface.\n *\n * Focus never lives only in WebGL pixels: the active item carries a\n * visible DOM focus outline and receives real DOM focus on keyboard moves.\n * Sync is navigator → graph (Enter → `focusNode` camera fly); graph →\n * navigator focus sync is deferred past v0.4. Space routes through\n * `instance.selectNodes`, so a controlled selection surfaces intent only\n * — `aria-selected` always reflects the store.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, KeyboardEvent, ReactElement } from 'react';\nimport type {\n AccessibilityConfig,\n GraphNode,\n NodeId,\n SearchResult,\n SelectionState,\n} from '@modernrelay/orbit-core';\nimport { getSearchResultUnavailableCallback } from '../../hooks';\nimport { useResolvedInstance } from '../shared';\nimport type { AnyGraphInstance } from '../../GraphProvider';\n\n/** default page size when `accessibility.navigatorWindow` is unset. */\nconst DEFAULT_NAVIGATOR_WINDOW = 50;\n\nexport interface GraphNavigatorProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /**\n * Keyboard model. v0.4 ships `'topology'` (the default; documented above);\n * `'spatial'` (arrows-by-screen-direction) is post-v0.4 and not accepted\n * by this union yet.\n */\n keyboardMode?: 'topology';\n /** Accessible name of the navigator panel. Default 'Graph navigator'. */\n label?: string;\n className?: string;\n style?: CSSProperties;\n}\n\n/** Which paged id list a row belongs to (drives PageUp/PageDown targeting). */\ntype PagedListKey = 'search' | 'neighbors' | 'selection' | 'entry';\n\ninterface NavRow {\n /** Stable identity across renders: `${list}:${nodeId}` (`root:` for the root row). */\n key: string;\n /** Stable DOM id (aria-activedescendant target). */\n domId: string;\n nodeId: NodeId;\n listKey: PagedListKey;\n text: string;\n selected: boolean;\n}\n\ninterface NavSection {\n key: 'search' | 'focused' | 'selection' | 'entry';\n listKey: PagedListKey;\n label: string;\n /** Group accessible name; carries the 'page x of y' state. */\n groupLabel: string;\n rows: NavRow[];\n page: number;\n pageCount: number;\n /** Total pageable items (excludes the focused section's root chrome row). */\n total: number;\n}\n\n/** Transient navigator focus: the neighborhood root. */\ninterface NavRoot {\n id: NodeId;\n neighbors: readonly NodeId[];\n /** Model revision the neighborhood was resolved against. */\n model: number;\n}\n\ninterface NavStoreSlice {\n selection: SelectionState;\n pins: ReadonlyMap<NodeId, readonly [number, number]>;\n hidden: ReadonlySet<NodeId>;\n model: number;\n nodeCount: number;\n /** Last completed search; null hides the section. */\n search: { query: string; results: readonly SearchResult[] } | null;\n}\n\n/** Narrow store subscription: re-renders only when a navigator-relevant slice\n * changes (never on per-frame viewport writes). */\nfunction useNavStoreSlice(instance: AnyGraphInstance): NavStoreSlice {\n const cacheRef = useRef<NavStoreSlice | null>(null);\n const subscribe = useCallback(\n (cb: () => void) => instance.store.subscribe(cb),\n [instance],\n );\n const getSnapshot = useCallback((): NavStoreSlice => {\n const s = instance.store.getState();\n const prev = cacheRef.current;\n if (\n prev !== null &&\n prev.selection === s.selection &&\n prev.pins === s.pins &&\n prev.hidden === s.hiddenNodeIds &&\n prev.model === s.revisions.model &&\n prev.nodeCount === s.nodeCount &&\n prev.search === s.search\n ) {\n return prev;\n }\n const next: NavStoreSlice = {\n selection: s.selection,\n pins: s.pins,\n hidden: s.hiddenNodeIds,\n model: s.revisions.model,\n nodeCount: s.nodeCount,\n search: s.search,\n };\n cacheRef.current = next;\n return next;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\ninterface PageWindow {\n page: number;\n pageCount: number;\n items: readonly NodeId[];\n}\n\n/** Clamp a raw page into `[0, pageCount)` and slice that page's ids. */\nfunction pageWindow(all: readonly NodeId[], rawPage: number, size: number): PageWindow {\n const pageCount = Math.max(1, Math.ceil(all.length / size));\n const page = Math.min(Math.max(rawPage, 0), pageCount - 1);\n return { page, pageCount, items: all.slice(page * size, page * size + size) };\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n maxWidth: 320,\n fontSize: 12,\n};\nconst LIST_STYLE: CSSProperties = { overflowY: 'auto', maxHeight: 320 };\nconst HEADER_STYLE: CSSProperties = { fontWeight: 600, padding: '4px 6px' };\nconst ITEM_STYLE: CSSProperties = {\n padding: '2px 6px',\n cursor: 'default',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n/** visible DOM focus ring on the active item. */\nconst ACTIVE_ITEM_STYLE: CSSProperties = {\n ...ITEM_STYLE,\n outline: '2px solid currentColor',\n outlineOffset: -2,\n};\nconst PAGING_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 8 };\nconst EMPTY_STYLE: CSSProperties = { padding: '4px 6px', fontStyle: 'italic' };\n\nexport function GraphNavigator(props: GraphNavigatorProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphNavigator>');\n const slice = useNavStoreSlice(instance);\n\n // 'topology' is the only v0.4 mode; the prop exists so the shape is stable\n // when 'spatial' lands post-v0.4 (no behavioral switch yet).\n void props.keyboardMode;\n\n const baseId = useId();\n const [root, setRoot] = useState<NavRoot | null>(null);\n const [pages, setPages] = useState<Record<PagedListKey, number>>({\n search: 0,\n neighbors: 0,\n selection: 0,\n entry: 0,\n });\n /** Active row identity (roving target); null → first row. */\n const [activeKey, setActiveKey] = useState<string | null>(null);\n\n /** Stable per-node DOM-id tokens (node ids may contain arbitrary text). */\n const tokensRef = useRef<Map<NodeId, number>>(new Map());\n /** Learned neighbor counts, cached per model revision (see module JSDoc). */\n const degreesRef = useRef<{ model: number; counts: Map<NodeId, number> }>({\n model: -1,\n counts: new Map(),\n });\n const itemsRef = useRef<Map<string, HTMLElement>>(new Map());\n /** Set by keyboard handlers so the focus effect moves DOM focus once. */\n const pendingFocusRef = useRef(false);\n\n // A model change invalidates the resolved neighborhood: drop the transient\n // root (back to the entry list) rather than rendering stale rows.\n useEffect(() => {\n if (root !== null && root.model !== slice.model) setRoot(null);\n }, [root, slice.model]);\n\n // release the keyboard ring — but ONLY when no pointer hover owns the\n // shared channel (blur while the pointer rests on a canvas node must not\n // erase that node's hover ring; hover already superseded the sticky\n // keyboard target inside core, so there is nothing else to clear).\n const releaseEmphasis = useCallback((): void => {\n if (instance.store.getState().hover.nodeId === null) instance.emphasizeNode(null);\n }, [instance]);\n\n // A ring left by keyboard navigation must not outlive the navigator.\n useEffect(() => () => releaseEmphasis(), [releaseEmphasis]);\n\n // Idempotent render-time cache reset (ref only — safe under StrictMode).\n if (degreesRef.current.model !== slice.model) {\n degreesRef.current = { model: slice.model, counts: new Map() };\n }\n\n const accessibility = instance.getAccessibility() as\n | AccessibilityConfig<Record<string, unknown>>\n | undefined;\n const rawWindow = accessibility?.navigatorWindow;\n const windowSize =\n typeof rawWindow === 'number' && Number.isFinite(rawWindow) && rawWindow >= 1\n ? Math.floor(rawWindow)\n : DEFAULT_NAVIGATOR_WINDOW;\n\n const getAccessibleLabel = accessibility?.getAccessibleLabel;\n const nameOf = (id: NodeId): string => {\n const node = instance.getNode(id) as GraphNode<Record<string, unknown>> | undefined;\n if (node === undefined) return id;\n if (getAccessibleLabel !== undefined) {\n try {\n return getAccessibleLabel(node);\n } catch {\n // fall through to the built-in resolution\n }\n }\n const label = node.attrs?.['label'];\n return typeof label === 'string' ? label : node.id;\n };\n\n const selectedSet = new Set<NodeId>(slice.selection.nodeIds);\n\n /** '{name} · {n} neighbors · pinned · hidden' — always a single text node. */\n const rowText = (id: NodeId): string => {\n const parts = [nameOf(id)];\n const count = degreesRef.current.counts.get(id);\n if (count !== undefined) parts.push(count === 1 ? '1 neighbor' : `${count} neighbors`);\n if (slice.pins.has(id)) parts.push('pinned');\n if (slice.hidden.has(id)) parts.push('hidden');\n return parts.join(' · ');\n };\n\n const domIdFor = (listKey: string, id: NodeId): string => {\n const tokens = tokensRef.current;\n let token = tokens.get(id);\n if (token === undefined) {\n token = tokens.size;\n tokens.set(id, token);\n }\n return `${baseId}nav-${listKey}-${token}`;\n };\n\n const makeRow = (listKey: PagedListKey, id: NodeId): NavRow => ({\n key: `${listKey}:${id}`,\n domId: domIdFor(listKey, id),\n nodeId: id,\n listKey,\n text: rowText(id),\n selected: selectedSet.has(id),\n });\n\n // --- section/row model (all O(navigatorWindow) work) ---\n const searchResults: readonly SearchResult[] = slice.search === null ? [] : slice.search.results;\n const listItems: Record<PagedListKey, readonly NodeId[]> = {\n search: searchResults.map((r) => r.id),\n neighbors: root !== null ? root.neighbors : [],\n selection: slice.selection.nodeIds,\n // the entry list is the SCENE roster (scope applied, mask not)\n // hidden/filtered nodes stay listed with their state exposed in text.\n entry: root === null ? instance.getSceneNodeIds() : [],\n };\n\n const sections: NavSection[] = [];\n if (slice.search !== null) {\n const view = pageWindow(listItems.search, pages.search, windowSize);\n const count = searchResults.length;\n const start = view.page * windowSize;\n sections.push({\n key: 'search',\n listKey: 'search',\n label: 'Search results',\n groupLabel: `Search results for ${slice.search.query}: ${count} ${\n count === 1 ? 'result' : 'results'\n }, page ${view.page + 1} of ${view.pageCount}`,\n // row text prefers the service-supplied label (still a TEXT\n // NODE); results may name ids outside the model — nameOf falls back to\n // the id for those.\n rows: view.items.map((id, i) => {\n const row = makeRow('search', id);\n const label = searchResults[start + i]?.label;\n return label === undefined ? row : { ...row, text: label };\n }),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root !== null) {\n const view = pageWindow(listItems.neighbors, pages.neighbors, windowSize);\n const rootRow: NavRow = {\n key: `root:${root.id}`,\n domId: domIdFor('root', root.id),\n nodeId: root.id,\n listKey: 'neighbors',\n text: rowText(root.id),\n selected: selectedSet.has(root.id),\n };\n const count = root.neighbors.length;\n sections.push({\n key: 'focused',\n listKey: 'neighbors',\n label: 'Focused node',\n groupLabel: `Focused node ${nameOf(root.id)}: ${count} ${\n count === 1 ? 'neighbor' : 'neighbors'\n }, page ${view.page + 1} of ${view.pageCount}`,\n rows: [rootRow, ...view.items.map((id) => makeRow('neighbors', id))],\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (slice.selection.nodeIds.length > 0) {\n const view = pageWindow(listItems.selection, pages.selection, windowSize);\n const count = slice.selection.nodeIds.length;\n sections.push({\n key: 'selection',\n listKey: 'selection',\n label: 'Selection',\n groupLabel: `Selection: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('selection', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n if (root === null) {\n const view = pageWindow(listItems.entry, pages.entry, windowSize);\n const count = listItems.entry.length;\n sections.push({\n key: 'entry',\n listKey: 'entry',\n label: 'All nodes',\n groupLabel: `All nodes: ${count} ${count === 1 ? 'node' : 'nodes'}, page ${\n view.page + 1\n } of ${view.pageCount}`,\n rows: view.items.map((id) => makeRow('entry', id)),\n page: view.page,\n pageCount: view.pageCount,\n total: count,\n });\n }\n\n const rows: NavRow[] = sections.flatMap((s) => s.rows);\n let activeIndex = activeKey === null ? -1 : rows.findIndex((r) => r.key === activeKey);\n if (activeIndex === -1 && rows.length > 0) activeIndex = 0;\n const activeRow = activeIndex >= 0 ? rows[activeIndex] : undefined;\n const activeSection =\n activeRow !== undefined ? sections.find((s) => s.listKey === activeRow.listKey) : undefined;\n\n // Move real DOM focus to the active item after keyboard-driven changes so\n // the focus ring is a genuine DOM focus, never only a WebGL highlight.\n useEffect(() => {\n if (!pendingFocusRef.current) return;\n pendingFocusRef.current = false;\n if (activeRow === undefined) return;\n itemsRef.current.get(activeRow.domId)?.focus();\n });\n\n // --- keyboard model ('topology'; see module JSDoc) ---\n const focusItemSoon = (): void => {\n pendingFocusRef.current = true;\n };\n\n const moveTo = (index: number): void => {\n if (rows.length === 0) return;\n const row = rows[Math.min(Math.max(index, 0), rows.length - 1)];\n if (row === undefined) return;\n setActiveKey(row.key);\n // Keyboard parity with pointer hover: ring the active row\n // WITHOUT flying the camera (that stays on Enter via focusNode).\n instance.emphasizeNode(row.nodeId);\n focusItemSoon();\n };\n\n const pageBy = (delta: number): void => {\n if (activeRow === undefined) return;\n const listKey = activeRow.listKey;\n const items = listItems[listKey];\n const pageCount = Math.max(1, Math.ceil(items.length / windowSize));\n const current = Math.min(Math.max(pages[listKey], 0), pageCount - 1);\n const next = Math.min(Math.max(current + delta, 0), pageCount - 1);\n if (next === current) return;\n setPages((p) => ({ ...p, [listKey]: next }));\n const first = items[next * windowSize];\n if (first !== undefined) {\n setActiveKey(`${listKey}:${first}`);\n instance.emphasizeNode(first);\n }\n focusItemSoon();\n };\n\n /** Enter: focus the node in the graph (camera fly) and re-root the\n * neighborhood. The returned ids come from the core adjacency resolution\n * (undirected; see module JSDoc). Search rows route through the\n * result contract instead: `activateSearchResult` focuses an in-scene,\n * mask-visible id and otherwise only CLASSIFIES — the navigator never\n * mutates scope/filters (the host reacts through its own channels). */\n const activate = (row: NavRow): void => {\n if (row.listKey === 'search') {\n const result = searchResults.find((r) => r.id === row.nodeId);\n if (result !== undefined) {\n const activation = instance.activateSearchResult(result);\n if (activation.status === 'unavailable') {\n getSearchResultUnavailableCallback(instance)?.(result, activation.reason);\n }\n }\n return;\n }\n const neighbors = instance.focusNode(row.nodeId);\n degreesRef.current.counts.set(row.nodeId, neighbors.length);\n setRoot({ id: row.nodeId, neighbors, model: slice.model });\n setPages((p) => ({ ...p, neighbors: 0 }));\n setActiveKey(`root:${row.nodeId}`);\n focusItemSoon();\n };\n\n /** Space: toggle membership through the selection ownership path.\n * Search rows may name ids outside the accepted model — those no-op. */\n const toggleSelection = (row: NavRow): void => {\n if (row.listKey === 'search' && instance.getNode(row.nodeId) === undefined) return;\n const current = slice.selection.nodeIds;\n const next = current.includes(row.nodeId)\n ? current.filter((id) => id !== row.nodeId)\n : [...current, row.nodeId];\n instance.selectNodes(next);\n };\n\n const clearTransientFocus = (): void => {\n setRoot(null);\n setActiveKey(null);\n releaseEmphasis();\n focusItemSoon();\n };\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (activeRow === undefined) return;\n switch (e.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rows.length - 1);\n break;\n case 'PageDown':\n pageBy(1);\n break;\n case 'PageUp':\n pageBy(-1);\n break;\n case 'Enter':\n activate(activeRow);\n break;\n case ' ':\n toggleSelection(activeRow);\n break;\n case 'Escape':\n if (root === null) return; // nothing transient — let it bubble\n clearTransientFocus();\n break;\n default:\n return;\n }\n e.preventDefault();\n };\n\n const panelLabel = props.label ?? 'Graph navigator';\n\n return (\n <div\n data-orbit-navigator=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={{ ...PANEL_STYLE, ...props.style }}\n onBlur={(e) => {\n // Focus left the panel entirely (paging buttons stay inside): the\n // keyboard ring clears, mirroring pointer hover leaving the canvas.\n if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {\n releaseEmphasis();\n }\n }}\n >\n <div\n role=\"listbox\"\n aria-label={`${panelLabel} items`}\n aria-activedescendant={activeRow?.domId}\n aria-keyshortcuts=\"ArrowUp ArrowDown Home End PageUp PageDown Enter Space Escape\"\n onKeyDown={onKeyDown}\n style={LIST_STYLE}\n >\n {rows.length === 0 ? (\n <div role=\"presentation\" style={EMPTY_STYLE}>\n No nodes\n </div>\n ) : (\n sections.map((section) => (\n <div key={section.key} role=\"group\" aria-label={section.groupLabel}>\n <div role=\"presentation\" style={HEADER_STYLE}>\n {section.label}\n </div>\n {section.rows.map((row) => {\n const isActive = row.key === activeRow?.key;\n return (\n <div\n key={row.key}\n id={row.domId}\n role=\"option\"\n aria-selected={row.selected}\n tabIndex={isActive ? 0 : -1}\n ref={(el) => {\n if (el === null) itemsRef.current.delete(row.domId);\n else itemsRef.current.set(row.domId, el);\n }}\n onClick={() => {\n setActiveKey(row.key);\n instance.emphasizeNode(row.nodeId); // active row = ringed row\n focusItemSoon();\n }}\n style={isActive ? ACTIVE_ITEM_STYLE : ITEM_STYLE}\n >\n {row.text}\n </div>\n );\n })}\n </div>\n ))\n )}\n </div>\n {activeSection !== undefined ? (\n <div data-orbit-navigator-paging=\"\" style={PAGING_STYLE}>\n <button\n type=\"button\"\n onClick={() => pageBy(-1)}\n disabled={activeSection.page === 0}\n aria-keyshortcuts=\"PageUp\"\n >\n Previous page\n </button>\n <span role=\"status\" data-orbit-navigator-page-status=\"\">\n {`${activeSection.label}: page ${activeSection.page + 1} of ${\n activeSection.pageCount\n } (${activeSection.total} ${activeSection.total === 1 ? 'item' : 'items'})`}\n </span>\n <button\n type=\"button\"\n onClick={() => pageBy(1)}\n disabled={activeSection.page >= activeSection.pageCount - 1}\n aria-keyshortcuts=\"PageDown\"\n >\n Next page\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
|
@@ -106,6 +106,7 @@ function GraphTooltip(props) {
|
|
|
106
106
|
setVisibleKey(null);
|
|
107
107
|
return void 0;
|
|
108
108
|
}
|
|
109
|
+
setVisibleKey((current) => current === hoverKey ? current : null);
|
|
109
110
|
const timer = setTimeout(() => {
|
|
110
111
|
setVisibleKey(hoverKey);
|
|
111
112
|
}, delayMs);
|
|
@@ -146,7 +147,7 @@ function GraphTooltip(props) {
|
|
|
146
147
|
useLayoutEffect(() => {
|
|
147
148
|
if (visibleKey !== null) position(decodeHover(visibleKey).id);
|
|
148
149
|
}, [visibleKey, position]);
|
|
149
|
-
const target = visibleKey === null ? null : decodeHover(visibleKey);
|
|
150
|
+
const target = visibleKey === null || visibleKey !== hoverKey ? null : decodeHover(visibleKey);
|
|
150
151
|
const node = target?.kind === "node" ? instance.getNode(target.id) : void 0;
|
|
151
152
|
const edge = target?.kind === "edge" ? instance.getEdge(target.id) : void 0;
|
|
152
153
|
const item = node ?? edge;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Tooltip/index.tsx"],"names":[],"mappings":"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","file":"Tooltip.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Tooltip` — `<GraphTooltip>`, the\n * hover card.\n *\n * Visibility rides the store hover slice: a\n * non-null hover arms a `delayMs` timer (default 150); the card mounts when\n * it fires and unmounts IMMEDIATELY when hover clears — no flicker-out delay.\n *\n * BOTH hover namespaces are served: `hover.nodeId` and `hover.edgeId`. A node\n * WINS when both are set — it is the more specific target and sits on top of\n * its own links. Edges have no entry in the overlay position lane (that lane\n * tracks labeled NODE slots), so an edge card always rides the cursor\n * fallback, which is exactly right: the hovered edge is under the pointer.\n *\n * Positioning is imperative (transform writes on a ref — never a React\n * re-render per movement), fed by two lanes:\n * 1. the overlay position lane (`instance.labels.subscribePositions`,\n * the same public seam <LabelLayer> renders from): when the hovered id is\n * in the tracked candidate set, each scheduler tick writes its fresh\n * container-relative screen x/y, so the card follows the node through\n * simulation drift and camera moves;\n * 2. the pointer fallback: when the hovered id is NOT label-tracked (the\n * common case — labels cover k ranked nodes), a window `pointermove`\n * listener (attached only while hovering) pins the card to the cursor\n * + a fixed offset. The engine's `spaceToScreen` is not reachable through\n * the public instance surface in v0.9, so cursor-anchoring is the\n * documented fallback — the hovered node is by definition under the\n * cursor.\n *\n * Default content: the target's label (`attrs.label` string, else id) plus up\n * to {@link TOOLTIP_ATTR_ROWS} attr rows (label excluded — it is the title),\n * every key/value rendered as TEXT NODES only (untrusted-content rule;\n * hostile attr strings appear literally). `render` / `renderEdge` replace the\n * card content entirely, and `getEdgeText` retitles an edge card without\n * taking over its body — useful where the meaningful name lives in an\n * adapter-injected attr rather than `label` (an Omnigraph edge's relationship\n * type is at `attrs['orbit:type']`, for instance). The card is pointer-inert\n * (it must never steal canvas hover).\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { AcceptedEdge, GraphNode, NodeId } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { overlaySurface, useResolvedInstance } from '../shared';\n\nconst DELAY_MS_DEFAULT = 150;\n/** Max attr rows in the default card content. */\nexport const TOOLTIP_ATTR_ROWS = 6;\n/** Cursor-anchored offset (px) so the card never sits under the pointer. */\nconst POINTER_OFFSET_PX = 12;\n\nexport interface GraphTooltipProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Replaces the default card content for a NODE (label + attr rows). */\n render?: (node: GraphNode<any>) => ReactNode;\n /** Replaces the default card content for an EDGE. */\n renderEdge?: (edge: AcceptedEdge<any>) => ReactNode;\n /** Title text for an edge card. Default `attrs.label ?? id`. */\n getEdgeText?: (edge: AcceptedEdge<any>) => string;\n /** Set false to ignore edge hovers entirely (nodes only). Default true. */\n edges?: boolean;\n /** Hover → show delay in ms. Default 150. */\n delayMs?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst LAYER_STYLE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n};\n\nconst CARD_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n maxWidth: 280,\n padding: '6px 10px',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.5 system-ui, sans-serif',\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n willChange: 'transform',\n};\n\nconst TITLE_STYLE: CSSProperties = {\n fontWeight: 600,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nconst ROW_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 12,\n overflow: 'hidden',\n};\n\nconst ROW_KEY_STYLE: CSSProperties = { opacity: 0.65 };\nconst ROW_VALUE_STYLE: CSSProperties = { overflow: 'hidden', textOverflow: 'ellipsis' };\n\n/** Same derivation as the core label default: `attrs.label ?? id`. */\nfunction nodeLabelText(node: GraphNode<any>): string {\n const attrs = node.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(node.id) : String(label);\n}\n\n/** Attr value → display TEXT (never markup): primitives verbatim, structured\n * values JSON (guarded — cycles degrade to the String form). */\nfunction attrValueText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/** The hovered target, node-first. Encoded as a STRING (`'node\\u0000id'`)\n * rather than an object so `useSyncExternalStore` compares by value — a fresh\n * object every publication would loop. */\ntype HoverKey = string;\n\nfunction encodeHover(kind: 'node' | 'edge', id: string): HoverKey {\n return `${kind}\\u0000${id}`;\n}\n\nfunction decodeHover(key: HoverKey): { kind: 'node' | 'edge'; id: string } {\n const at = key.indexOf('\\u0000');\n return { kind: key.slice(0, at) as 'node' | 'edge', id: key.slice(at + 1) };\n}\n\nfunction useHoveredTarget(instance: AnyGraphInstance, edges: boolean): HoverKey | null {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback((): HoverKey | null => {\n const hover = instance.store.getState().hover;\n // Node wins: it is the more specific target and sits above its links.\n if (hover.nodeId !== null) return encodeHover('node', hover.nodeId);\n if (edges && hover.edgeId !== null) return encodeHover('edge', hover.edgeId);\n return null;\n }, [instance, edges]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Same derivation as the node default: `attrs.label ?? id`. */\nfunction edgeLabelText(edge: AcceptedEdge<any>): string {\n const attrs = edge.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(edge.id) : String(label);\n}\n\nexport function GraphTooltip(props: GraphTooltipProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphTooltip>');\n const labels = overlaySurface(instance).labels;\n const delayMs = props.delayMs ?? DELAY_MS_DEFAULT;\n\n const edgesEnabled = props.edges ?? true;\n const hoverKey = useHoveredTarget(instance, edgesEnabled);\n /** The target the mounted card shows — hoverKey promoted after delayMs. */\n const [visibleKey, setVisibleKey] = useState<HoverKey | null>(null);\n\n const layerRef = useRef<HTMLDivElement | null>(null);\n const cardRef = useRef<HTMLDivElement | null>(null);\n /** Freshest label-lane screen position per tracked id (container-relative). */\n const trackedRef = useRef(new Map<NodeId, readonly [number, number]>());\n /** Freshest cursor position (container-relative, offset applied). */\n const pointerRef = useRef<readonly [number, number] | null>(null);\n\n /** One place decides the transform: label lane wins when it tracks the id. */\n const position = useCallback(\n (id: NodeId): void => {\n const el = cardRef.current;\n if (el === null) return;\n const at = trackedRef.current.get(id) ?? pointerRef.current;\n if (at === null || at === undefined) return;\n el.style.transform = `translate3d(${at[0]}px, ${at[1]}px, 0)`;\n },\n [],\n );\n\n // Delay lane: arm on hover, cancel/hide immediately on unhover.\n useEffect(() => {\n if (hoverKey === null) {\n setVisibleKey(null);\n return undefined;\n }\n const timer = setTimeout(() => {\n setVisibleKey(hoverKey);\n }, delayMs);\n return () => {\n clearTimeout(timer);\n };\n }, [hoverKey, delayMs]);\n\n // Position lane 1: the overlay position lane (same seam as LabelLayer).\n useLayoutEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribePositions((list) => {\n const tracked = trackedRef.current;\n tracked.clear();\n for (const p of list) tracked.set(p.id, [p.x, p.y]);\n const id = instance.store.getState().hover.nodeId;\n if (id !== null && tracked.has(id)) position(id);\n });\n }, [labels, instance, position]);\n\n // Position lane 2: cursor fallback. The listener lives for the component's\n // lifetime (storing two numbers per move) so a hover that lands WITHOUT a\n // subsequent move still knows where the cursor already is; it only writes\n // the card transform while a node is hovered and the id is untracked.\n useEffect(() => {\n const onMove = (e: PointerEvent): void => {\n // Container-relative like the label lane: subtract the positioned\n // ancestor's origin (zero-rect hosts — jsdom — degrade to client px).\n const host = layerRef.current?.offsetParent;\n const origin = host instanceof Element ? host.getBoundingClientRect() : null;\n pointerRef.current = [\n e.clientX - (origin?.left ?? 0) + POINTER_OFFSET_PX,\n e.clientY - (origin?.top ?? 0) + POINTER_OFFSET_PX,\n ];\n const hover = instance.store.getState().hover;\n if (hover.nodeId !== null) {\n if (!trackedRef.current.has(hover.nodeId)) position(hover.nodeId);\n } else if (hover.edgeId !== null) {\n // Edges never appear in the label lane — always cursor-anchored.\n position(hover.edgeId);\n }\n };\n window.addEventListener('pointermove', onMove);\n return () => {\n window.removeEventListener('pointermove', onMove);\n };\n }, [instance, position]);\n\n // Place the card the moment it mounts (before paint — no corner flash).\n useLayoutEffect(() => {\n if (visibleKey !== null) position(decodeHover(visibleKey).id);\n }, [visibleKey, position]);\n\n const target = visibleKey === null ? null : decodeHover(visibleKey);\n const node =\n target?.kind === 'node'\n ? (instance.getNode(target.id) as GraphNode<any> | undefined)\n : undefined;\n const edge =\n target?.kind === 'edge'\n ? (instance.getEdge(target.id) as AcceptedEdge<any> | undefined)\n : undefined;\n const item = node ?? edge;\n const isEdge = edge !== undefined;\n\n /** Title + custom body, shared by both kinds — only the text derivation and\n * the `data-orbit-tooltip-edge` marker differ. Computed ONLY when a target\n * actually resolved: `visibleKey` can name an id the model no longer holds\n * (a hover surviving a dataset swap), and `target` is null whenever nothing\n * is hovered at all. */\n const title =\n item === undefined\n ? ''\n : isEdge\n ? (props.getEdgeText ?? edgeLabelText)(edge)\n : nodeLabelText(node as GraphNode<any>);\n const custom =\n item === undefined\n ? undefined\n : isEdge\n ? props.renderEdge?.(edge)\n : props.render?.(node as GraphNode<any>);\n\n return (\n <div ref={layerRef} data-orbit-tooltip-layer=\"\" style={LAYER_STYLE}>\n {item !== undefined ? (\n <div\n ref={cardRef}\n data-orbit-tooltip=\"\"\n {...(isEdge ? { 'data-orbit-tooltip-edge': '' } : {})}\n role=\"tooltip\"\n className={props.className}\n style={{ ...CARD_STYLE, ...props.style }}\n >\n {custom !== undefined ? (\n custom\n ) : (\n <>\n {/* Untrusted text renders as TEXT NODES only. */}\n <div data-orbit-tooltip-label=\"\" style={TITLE_STYLE}>\n {title}\n </div>\n {Object.entries((item.attrs ?? {}) as Record<string, unknown>)\n .filter(([key]) => key !== 'label')\n .slice(0, TOOLTIP_ATTR_ROWS)\n .map(([key, value]) => (\n <div key={key} data-orbit-tooltip-attr={key} style={ROW_STYLE}>\n <span style={ROW_KEY_STYLE}>{key}</span>\n <span style={ROW_VALUE_STYLE}>{attrValueText(value)}</span>\n </div>\n ))}\n </>\n )}\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/Tooltip/index.tsx"],"names":[],"mappings":"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","file":"Tooltip.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/Tooltip` — `<GraphTooltip>`, the\n * hover card.\n *\n * Visibility rides the store hover slice: a\n * non-null hover arms a `delayMs` timer (default 150); the card mounts when\n * it fires and unmounts IMMEDIATELY when hover clears — no flicker-out delay.\n *\n * BOTH hover namespaces are served: `hover.nodeId` and `hover.edgeId`. A node\n * WINS when both are set — it is the more specific target and sits on top of\n * its own links. Edges have no entry in the overlay position lane (that lane\n * tracks labeled NODE slots), so an edge card always rides the cursor\n * fallback, which is exactly right: the hovered edge is under the pointer.\n *\n * Positioning is imperative (transform writes on a ref — never a React\n * re-render per movement), fed by two lanes:\n * 1. the overlay position lane (`instance.labels.subscribePositions`,\n * the same public seam <LabelLayer> renders from): when the hovered id is\n * in the tracked candidate set, each scheduler tick writes its fresh\n * container-relative screen x/y, so the card follows the node through\n * simulation drift and camera moves;\n * 2. the pointer fallback: when the hovered id is NOT label-tracked (the\n * common case — labels cover k ranked nodes), a window `pointermove`\n * listener (attached only while hovering) pins the card to the cursor\n * + a fixed offset. The engine's `spaceToScreen` is not reachable through\n * the public instance surface in v0.9, so cursor-anchoring is the\n * documented fallback — the hovered node is by definition under the\n * cursor.\n *\n * Default content: the target's label (`attrs.label` string, else id) plus up\n * to {@link TOOLTIP_ATTR_ROWS} attr rows (label excluded — it is the title),\n * every key/value rendered as TEXT NODES only (untrusted-content rule;\n * hostile attr strings appear literally). `render` / `renderEdge` replace the\n * card content entirely, and `getEdgeText` retitles an edge card without\n * taking over its body — useful where the meaningful name lives in an\n * adapter-injected attr rather than `label` (an Omnigraph edge's relationship\n * type is at `attrs['orbit:type']`, for instance). The card is pointer-inert\n * (it must never steal canvas hover).\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { AcceptedEdge, GraphNode, NodeId } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { overlaySurface, useResolvedInstance } from '../shared';\n\nconst DELAY_MS_DEFAULT = 150;\n/** Max attr rows in the default card content. */\nexport const TOOLTIP_ATTR_ROWS = 6;\n/** Cursor-anchored offset (px) so the card never sits under the pointer. */\nconst POINTER_OFFSET_PX = 12;\n\nexport interface GraphTooltipProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Replaces the default card content for a NODE (label + attr rows). */\n render?: (node: GraphNode<any>) => ReactNode;\n /** Replaces the default card content for an EDGE. */\n renderEdge?: (edge: AcceptedEdge<any>) => ReactNode;\n /** Title text for an edge card. Default `attrs.label ?? id`. */\n getEdgeText?: (edge: AcceptedEdge<any>) => string;\n /** Set false to ignore edge hovers entirely (nodes only). Default true. */\n edges?: boolean;\n /** Hover → show delay in ms. Default 150. */\n delayMs?: number;\n className?: string;\n style?: CSSProperties;\n}\n\nconst LAYER_STYLE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n};\n\nconst CARD_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n maxWidth: 280,\n padding: '6px 10px',\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.5 system-ui, sans-serif',\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n willChange: 'transform',\n};\n\nconst TITLE_STYLE: CSSProperties = {\n fontWeight: 600,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nconst ROW_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'space-between',\n gap: 12,\n overflow: 'hidden',\n};\n\nconst ROW_KEY_STYLE: CSSProperties = { opacity: 0.65 };\nconst ROW_VALUE_STYLE: CSSProperties = { overflow: 'hidden', textOverflow: 'ellipsis' };\n\n/** Same derivation as the core label default: `attrs.label ?? id`. */\nfunction nodeLabelText(node: GraphNode<any>): string {\n const attrs = node.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(node.id) : String(label);\n}\n\n/** Attr value → display TEXT (never markup): primitives verbatim, structured\n * values JSON (guarded — cycles degrade to the String form). */\nfunction attrValueText(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'object' && value !== null) {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/** The hovered target, node-first. Encoded as a STRING (`'node\\u0000id'`)\n * rather than an object so `useSyncExternalStore` compares by value — a fresh\n * object every publication would loop. */\ntype HoverKey = string;\n\nfunction encodeHover(kind: 'node' | 'edge', id: string): HoverKey {\n return `${kind}\\u0000${id}`;\n}\n\nfunction decodeHover(key: HoverKey): { kind: 'node' | 'edge'; id: string } {\n const at = key.indexOf('\\u0000');\n return { kind: key.slice(0, at) as 'node' | 'edge', id: key.slice(at + 1) };\n}\n\nfunction useHoveredTarget(instance: AnyGraphInstance, edges: boolean): HoverKey | null {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback((): HoverKey | null => {\n const hover = instance.store.getState().hover;\n // Node wins: it is the more specific target and sits above its links.\n if (hover.nodeId !== null) return encodeHover('node', hover.nodeId);\n if (edges && hover.edgeId !== null) return encodeHover('edge', hover.edgeId);\n return null;\n }, [instance, edges]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** Same derivation as the node default: `attrs.label ?? id`. */\nfunction edgeLabelText(edge: AcceptedEdge<any>): string {\n const attrs = edge.attrs as Record<string, unknown> | undefined;\n const label = attrs?.['label'];\n return label === undefined || label === null ? String(edge.id) : String(label);\n}\n\nexport function GraphTooltip(props: GraphTooltipProps): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphTooltip>');\n const labels = overlaySurface(instance).labels;\n const delayMs = props.delayMs ?? DELAY_MS_DEFAULT;\n\n const edgesEnabled = props.edges ?? true;\n const hoverKey = useHoveredTarget(instance, edgesEnabled);\n /** The target the mounted card shows — hoverKey promoted after delayMs. */\n const [visibleKey, setVisibleKey] = useState<HoverKey | null>(null);\n\n const layerRef = useRef<HTMLDivElement | null>(null);\n const cardRef = useRef<HTMLDivElement | null>(null);\n /** Freshest label-lane screen position per tracked id (container-relative). */\n const trackedRef = useRef(new Map<NodeId, readonly [number, number]>());\n /** Freshest cursor position (container-relative, offset applied). */\n const pointerRef = useRef<readonly [number, number] | null>(null);\n\n /** One place decides the transform: label lane wins when it tracks the id. */\n const position = useCallback(\n (id: NodeId): void => {\n const el = cardRef.current;\n if (el === null) return;\n const at = trackedRef.current.get(id) ?? pointerRef.current;\n if (at === null || at === undefined) return;\n el.style.transform = `translate3d(${at[0]}px, ${at[1]}px, 0)`;\n },\n [],\n );\n\n // Delay lane: arm on hover, cancel/hide immediately on unhover OR target\n // replacement. Keeping the previous card mounted while a new target's\n // timer runs presents stale content for an item that is no longer hovered.\n useEffect(() => {\n if (hoverKey === null) {\n setVisibleKey(null);\n return undefined;\n }\n setVisibleKey((current) => (current === hoverKey ? current : null));\n const timer = setTimeout(() => {\n setVisibleKey(hoverKey);\n }, delayMs);\n return () => {\n clearTimeout(timer);\n };\n }, [hoverKey, delayMs]);\n\n // Position lane 1: the overlay position lane (same seam as LabelLayer).\n useLayoutEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribePositions((list) => {\n const tracked = trackedRef.current;\n tracked.clear();\n for (const p of list) tracked.set(p.id, [p.x, p.y]);\n const id = instance.store.getState().hover.nodeId;\n if (id !== null && tracked.has(id)) position(id);\n });\n }, [labels, instance, position]);\n\n // Position lane 2: cursor fallback. The listener lives for the component's\n // lifetime (storing two numbers per move) so a hover that lands WITHOUT a\n // subsequent move still knows where the cursor already is; it only writes\n // the card transform while a node is hovered and the id is untracked.\n useEffect(() => {\n const onMove = (e: PointerEvent): void => {\n // Container-relative like the label lane: subtract the positioned\n // ancestor's origin (zero-rect hosts — jsdom — degrade to client px).\n const host = layerRef.current?.offsetParent;\n const origin = host instanceof Element ? host.getBoundingClientRect() : null;\n pointerRef.current = [\n e.clientX - (origin?.left ?? 0) + POINTER_OFFSET_PX,\n e.clientY - (origin?.top ?? 0) + POINTER_OFFSET_PX,\n ];\n const hover = instance.store.getState().hover;\n if (hover.nodeId !== null) {\n if (!trackedRef.current.has(hover.nodeId)) position(hover.nodeId);\n } else if (hover.edgeId !== null) {\n // Edges never appear in the label lane — always cursor-anchored.\n position(hover.edgeId);\n }\n };\n window.addEventListener('pointermove', onMove);\n return () => {\n window.removeEventListener('pointermove', onMove);\n };\n }, [instance, position]);\n\n // Place the card the moment it mounts (before paint — no corner flash).\n useLayoutEffect(() => {\n if (visibleKey !== null) position(decodeHover(visibleKey).id);\n }, [visibleKey, position]);\n\n // Render-gate synchronously as well as clearing state in the effect: on the\n // first commit for a replacement hover, effects have not run yet and\n // `visibleKey` still names the previous target.\n const target = visibleKey === null || visibleKey !== hoverKey ? null : decodeHover(visibleKey);\n const node =\n target?.kind === 'node'\n ? (instance.getNode(target.id) as GraphNode<any> | undefined)\n : undefined;\n const edge =\n target?.kind === 'edge'\n ? (instance.getEdge(target.id) as AcceptedEdge<any> | undefined)\n : undefined;\n const item = node ?? edge;\n const isEdge = edge !== undefined;\n\n /** Title + custom body, shared by both kinds — only the text derivation and\n * the `data-orbit-tooltip-edge` marker differ. Computed ONLY when a target\n * actually resolved: `visibleKey` can name an id the model no longer holds\n * (a hover surviving a dataset swap), and `target` is null whenever nothing\n * is hovered at all. */\n const title =\n item === undefined\n ? ''\n : isEdge\n ? (props.getEdgeText ?? edgeLabelText)(edge)\n : nodeLabelText(node as GraphNode<any>);\n const custom =\n item === undefined\n ? undefined\n : isEdge\n ? props.renderEdge?.(edge)\n : props.render?.(node as GraphNode<any>);\n\n return (\n <div ref={layerRef} data-orbit-tooltip-layer=\"\" style={LAYER_STYLE}>\n {item !== undefined ? (\n <div\n ref={cardRef}\n data-orbit-tooltip=\"\"\n {...(isEdge ? { 'data-orbit-tooltip-edge': '' } : {})}\n role=\"tooltip\"\n className={props.className}\n style={{ ...CARD_STYLE, ...props.style }}\n >\n {custom !== undefined ? (\n custom\n ) : (\n <>\n {/* Untrusted text renders as TEXT NODES only. */}\n <div data-orbit-tooltip-label=\"\" style={TITLE_STYLE}>\n {title}\n </div>\n {Object.entries((item.attrs ?? {}) as Record<string, unknown>)\n .filter(([key]) => key !== 'label')\n .slice(0, TOOLTIP_ATTR_ROWS)\n .map(([key, value]) => (\n <div key={key} data-orbit-tooltip-attr={key} style={ROW_STYLE}>\n <span style={ROW_KEY_STYLE}>{key}</span>\n <span style={ROW_VALUE_STYLE}>{attrValueText(value)}</span>\n </div>\n ))}\n </>\n )}\n </div>\n ) : null}\n </div>\n );\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -380,9 +380,12 @@ declare const Graph: GraphComponent;
|
|
|
380
380
|
* the SAME set → imperative `style.transform` writes through refs keyed by
|
|
381
381
|
* id. No React re-render per tick (M0: the label lane is pure CPU O(k)).
|
|
382
382
|
*
|
|
383
|
-
* The layer is pointer-inert; label divs opt back in
|
|
384
|
-
*
|
|
385
|
-
*
|
|
383
|
+
* The layer is pointer-inert; built-in label divs opt back in as focusable
|
|
384
|
+
* buttons and drive the same public selection mutators by click or Enter/Space
|
|
385
|
+
* (replace normally, toggle with meta/shift). Custom renderers own their
|
|
386
|
+
* content's focus semantics, avoiding a button-role wrapper around a native
|
|
387
|
+
* interactive control. Node labels expose their context action through
|
|
388
|
+
* right-click plus ContextMenu/Shift+F10.
|
|
386
389
|
*/
|
|
387
390
|
|
|
388
391
|
interface LabelLayerProps {
|
package/dist/index.js
CHANGED
|
@@ -190,6 +190,7 @@ function LabelLayer(props) {
|
|
|
190
190
|
const [candidates, setCandidates] = useState([]);
|
|
191
191
|
const elementsRef = useRef(/* @__PURE__ */ new Map());
|
|
192
192
|
const positionsRef = useRef(/* @__PURE__ */ new Map());
|
|
193
|
+
const layerRef = useRef(null);
|
|
193
194
|
useEffect(() => {
|
|
194
195
|
if (labels === void 0) return void 0;
|
|
195
196
|
return labels.subscribeCandidates((list) => {
|
|
@@ -219,20 +220,43 @@ function LabelLayer(props) {
|
|
|
219
220
|
}
|
|
220
221
|
});
|
|
221
222
|
}, [labels]);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
if (e.metaKey || e.shiftKey) {
|
|
223
|
+
const selectLabel = (id, additive) => {
|
|
224
|
+
if (additive) {
|
|
225
225
|
const cur = instance.store.getState().selection.nodeIds;
|
|
226
226
|
instance.setSelection(cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]);
|
|
227
227
|
return;
|
|
228
228
|
}
|
|
229
229
|
instance.setSelection([id]);
|
|
230
230
|
};
|
|
231
|
+
const onLabelClick = (id, e) => {
|
|
232
|
+
e.stopPropagation();
|
|
233
|
+
selectLabel(id, e.metaKey || e.shiftKey);
|
|
234
|
+
};
|
|
231
235
|
const onClusterLabelClick = (key, e) => {
|
|
232
236
|
e.stopPropagation();
|
|
233
237
|
instance.selectCluster(key, { additive: e.metaKey || e.shiftKey });
|
|
234
238
|
};
|
|
235
|
-
const
|
|
239
|
+
const onLabelKeyDown = (p, e) => {
|
|
240
|
+
if (p.kind !== "cluster" && (e.key === "ContextMenu" || e.key === "F10" && e.shiftKey)) {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
e.stopPropagation();
|
|
243
|
+
const layerRect = layerRef.current?.getBoundingClientRect();
|
|
244
|
+
if (layerRect === void 0) return;
|
|
245
|
+
const labelRect = e.currentTarget.getBoundingClientRect();
|
|
246
|
+
instance.requestNodeContextMenu(p.id, [
|
|
247
|
+
labelRect.left + labelRect.width / 2 - layerRect.left,
|
|
248
|
+
labelRect.top + labelRect.height / 2 - layerRect.top
|
|
249
|
+
]);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (e.target !== e.currentTarget) return;
|
|
253
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
e.stopPropagation();
|
|
256
|
+
const additive = e.metaKey || e.shiftKey;
|
|
257
|
+
if (p.kind === "cluster") instance.selectCluster(p.id, { additive });
|
|
258
|
+
else selectLabel(p.id, additive);
|
|
259
|
+
};
|
|
236
260
|
const onLabelContextMenu = (id, e) => {
|
|
237
261
|
e.preventDefault();
|
|
238
262
|
e.stopPropagation();
|
|
@@ -246,17 +270,25 @@ function LabelLayer(props) {
|
|
|
246
270
|
const [x, y] = positionsRef.current.get(key) ?? [p.x, p.y];
|
|
247
271
|
const isCluster = p.kind === "cluster";
|
|
248
272
|
const className = isCluster ? [props.labelClassName, props.clusterLabelClassName].filter(Boolean).join(" ") || void 0 : props.labelClassName;
|
|
273
|
+
const hasCustomRenderer = isCluster ? props.renderClusterLabel !== void 0 : props.renderNodeLabel !== void 0;
|
|
249
274
|
return /* @__PURE__ */ jsx(
|
|
250
275
|
"div",
|
|
251
276
|
{
|
|
252
277
|
...isCluster ? { "data-orbit-cluster-label": p.id } : { "data-orbit-label": p.id },
|
|
253
278
|
className,
|
|
279
|
+
...!hasCustomRenderer ? {
|
|
280
|
+
role: "button",
|
|
281
|
+
tabIndex: 0,
|
|
282
|
+
"aria-label": p.text,
|
|
283
|
+
...!isCluster ? { "aria-haspopup": "menu" } : {}
|
|
284
|
+
} : {},
|
|
254
285
|
style: { ...LABEL_BASE_STYLE, transform: transformOf(x, y) },
|
|
255
286
|
ref: (el) => {
|
|
256
287
|
if (el === null) elementsRef.current.delete(key);
|
|
257
288
|
else elementsRef.current.set(key, el);
|
|
258
289
|
},
|
|
259
290
|
onClick: (e) => isCluster ? onClusterLabelClick(p.id, e) : onLabelClick(p.id, e),
|
|
291
|
+
onKeyDown: (e) => onLabelKeyDown(p, e),
|
|
260
292
|
onContextMenu: isCluster ? void 0 : (e) => onLabelContextMenu(p.id, e),
|
|
261
293
|
children: isCluster ? props.renderClusterLabel !== void 0 ? props.renderClusterLabel({
|
|
262
294
|
clusterKey: p.id,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Lasso.tsx","../src/LabelLayer.tsx","../src/LiveRegion.tsx","../src/Graph.tsx"],"names":["useState","useRef","useEffect","jsx","useLayoutEffect","prevRef"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Lasso — the Shift+drag freeform selection overlay.\n *\n * A transparent div rendered above the engine canvas that captures pointer\n * events ONLY while Shift is held (or a lasso drag is already in progress);\n * otherwise `pointer-events: none` so it never intercepts canvas interaction.\n *\n * Gesture: pointerdown starts capture; pointermove appends polygon points\n * throttled to at most one per animation frame (leading edge + trailing\n * commit); an SVG path previews the polygon; pointerup with ≥3 points calls\n * `instance.selectWithinPolygon(polygon, { additive: metaKey })`. Abort paths\n * (pointercancel, Escape, unmount) always release capture and leave no state.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { useAmbientGraphInstance } from './GraphProvider';\n\ninterface DragState {\n pointerId: number;\n /** Committed polygon vertices (overlay-local screen coordinates). */\n points: [number, number][];\n /** Latest uncommitted pointer position (throttle window open). */\n pending: [number, number] | null;\n /** Scheduled animation frame id; null when no frame is pending. */\n frame: number | null;\n}\n\nconst OVERLAY_BASE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n touchAction: 'none',\n userSelect: 'none',\n};\n\nconst OVERLAY_INACTIVE: CSSProperties = { ...OVERLAY_BASE, pointerEvents: 'none' };\nconst OVERLAY_ACTIVE: CSSProperties = {\n ...OVERLAY_BASE,\n pointerEvents: 'auto',\n cursor: 'crosshair',\n};\n\nconst SVG_STYLE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n overflow: 'visible',\n};\n\nfunction toPathData(points: readonly [number, number][]): string {\n let d = `M ${points[0]![0]} ${points[0]![1]}`;\n for (let i = 1; i < points.length; i++) d += ` L ${points[i]![0]} ${points[i]![1]}`;\n return points.length >= 3 ? `${d} Z` : d;\n}\n\n/** rAF when available; otherwise run synchronously (headless environments\n * throttling degrades to per-event appends, which is still correct). */\nfunction scheduleFrame(cb: () => void): number | null {\n if (typeof requestAnimationFrame === 'function') return requestAnimationFrame(cb);\n cb();\n return null;\n}\n\nfunction cancelFrame(id: number | null): void {\n if (id !== null && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(id);\n}\n\n/** Guarded pointer capture: jsdom (and older engines) may not implement it. */\nfunction capturePointer(el: HTMLElement, pointerId: number): void {\n if (typeof el.setPointerCapture === 'function') {\n try {\n el.setPointerCapture(pointerId);\n } catch {\n /* capture unavailable for this pointer — the gesture still works */\n }\n }\n}\n\nfunction releasePointer(el: HTMLElement, pointerId: number): void {\n if (typeof el.releasePointerCapture === 'function') {\n try {\n el.releasePointerCapture(pointerId);\n } catch {\n /* no active capture — already released */\n }\n }\n}\n\n/** Internal to <Graph> (gated by its `enableLasso` prop); reads the ambient\n * instance so advanced hosts composing GraphProvider directly can reuse it. */\nexport function Lasso(): ReactElement {\n const instance = useAmbientGraphInstance('Lasso');\n const overlayRef = useRef<HTMLDivElement | null>(null);\n const dragRef = useRef<DragState | null>(null);\n const [shiftHeld, setShiftHeld] = useState(false);\n /** Render mirror of the committed polygon; null = no drag in progress. */\n const [preview, setPreview] = useState<readonly [number, number][] | null>(null);\n\n /** Tear down the in-progress drag: cancel the pending frame, release\n * capture, clear all drag state. Safe to call when idle. */\n const releaseDrag = useCallback((): void => {\n const drag = dragRef.current;\n if (drag === null) return;\n dragRef.current = null;\n cancelFrame(drag.frame);\n const el = overlayRef.current;\n if (el !== null) releasePointer(el, drag.pointerId);\n setPreview(null);\n }, []);\n\n // Shift tracking + Escape abort. Window-level so the overlay activates\n // before it can receive any pointer event; blur releases a stuck Shift.\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === 'Shift') setShiftHeld(true);\n else if (e.key === 'Escape') releaseDrag();\n };\n const onKeyUp = (e: KeyboardEvent): void => {\n if (e.key === 'Shift') setShiftHeld(false);\n };\n const onBlur = (): void => setShiftHeld(false);\n window.addEventListener('keydown', onKeyDown);\n window.addEventListener('keyup', onKeyUp);\n window.addEventListener('blur', onBlur);\n return () => {\n window.removeEventListener('keydown', onKeyDown);\n window.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('blur', onBlur);\n releaseDrag(); // unmount mid-drag leaves no capture and no frame\n };\n }, [releaseDrag]);\n\n const localPoint = (e: ReactPointerEvent<HTMLDivElement>): [number, number] => {\n const el = overlayRef.current;\n if (el === null) return [e.clientX, e.clientY];\n const rect = el.getBoundingClientRect();\n return [e.clientX - rect.left, e.clientY - rect.top];\n };\n\n const commitPending = (drag: DragState): void => {\n if (drag.pending === null) return;\n drag.points.push(drag.pending);\n drag.pending = null;\n setPreview([...drag.points]);\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (dragRef.current !== null || e.button !== 0) return;\n // Lasso is a Shift gesture: the overlay only receives events while Shift\n // is held, but re-check for robustness (tests, synthetic dispatch).\n if (!e.shiftKey && !shiftHeld) return;\n const pointerId = e.pointerId ?? -1;\n const el = overlayRef.current;\n if (el !== null) capturePointer(el, pointerId);\n const p = localPoint(e);\n dragRef.current = { pointerId, points: [p], pending: null, frame: null };\n setPreview([p]);\n e.preventDefault();\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n drag.pending = localPoint(e);\n if (drag.frame !== null) return; // throttle window open — coalesce\n // Leading edge: append immediately, then hold the window one frame at a\n // time while moves keep streaming in (trailing commit per frame).\n commitPending(drag);\n const tick = (): void => {\n drag.frame = null;\n if (dragRef.current !== drag) return; // drag ended while scheduled\n if (drag.pending !== null) {\n commitPending(drag);\n drag.frame = scheduleFrame(tick);\n }\n };\n drag.frame = scheduleFrame(tick);\n };\n\n const onPointerUp = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n commitPending(drag); // the trailing point must land in the polygon\n const polygon = drag.points;\n const additive = e.metaKey;\n releaseDrag();\n if (polygon.length >= 3) instance.selectWithinPolygon(polygon, { additive });\n };\n\n const onPointerCancel = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n releaseDrag();\n };\n\n const active = shiftHeld || preview !== null;\n return (\n <div\n ref={overlayRef}\n data-orbit-lasso=\"\"\n style={active ? OVERLAY_ACTIVE : OVERLAY_INACTIVE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerCancel}\n >\n {preview !== null && preview.length > 0 ? (\n <svg data-orbit-lasso-preview=\"\" style={SVG_STYLE}>\n <path\n d={toPathData(preview)}\n fill=\"rgba(88, 134, 255, 0.12)\"\n stroke=\"rgba(88, 134, 255, 0.9)\"\n strokeWidth={1.5}\n strokeDasharray=\"4 3\"\n />\n </svg>\n ) : null}\n </div>\n );\n}\n","/**\n * LabelLayer — the DOM label lane.\n *\n * Two-channel rendering against the pinned overlay interface:\n * - `labels.subscribeCandidates` fires only when the candidate SET changes →\n * React state → one absolutely-positioned <div> per candidate. Label text\n * is rendered as a TEXT NODE only (never markup), so hostile strings in\n * node attrs appear literally.\n * - `labels.subscribePositions` fires on scheduler ticks with fresh x/y for\n * the SAME set → imperative `style.transform` writes through refs keyed by\n * id. No React re-render per tick (M0: the label lane is pure CPU O(k)).\n *\n * The layer is pointer-inert; label divs opt back in and drive the\n * click-selection path through the same public mutators (replace on plain\n * click and toggle on meta/shift).\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type { CSSProperties, MouseEvent as ReactMouseEvent, ReactElement, ReactNode } from 'react';\nimport type { GraphNode, LabelPlacement, NodeId } from '@modernrelay/orbit-core';\nimport { useAmbientGraphInstance } from './GraphProvider';\nimport { overlaySurface, overlayLayerStyle } from './components/shared';\n\nexport interface LabelLayerProps {\n /** Class hook applied to every label div. */\n labelClassName?: string | undefined;\n /** Escape hatch rendered INSIDE the positioned div instead of the text node. */\n renderNodeLabel?: ((ctx: { node: GraphNode<any>; text: string }) => ReactNode) | undefined;\n /** Class hook applied to CLUSTER label divs (in addition to\n * `labelClassName`), so the coarse layer can be styled apart. */\n clusterLabelClassName?: string | undefined;\n /** Escape hatch for cluster labels; `memberIds` are the ids a click\n * selects. */\n renderClusterLabel?:\n | ((ctx: { clusterKey: string; text: string; memberIds: readonly NodeId[] }) => ReactNode)\n | undefined;\n}\n\n/** Placements come from two id namespaces (node ids and cluster keys), so\n * element/position maps and React keys are keyed on `(kind, id)`. */\nfunction placementKey(p: LabelPlacement): string {\n return p.kind === 'cluster' ? `cluster\\u0000${p.id}` : `node\\u0000${p.id}`;\n}\n\nconst LAYER_STYLE: CSSProperties = { ...overlayLayerStyle, overflow: 'hidden' };\n\nconst LABEL_BASE_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'auto',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n willChange: 'transform',\n};\n\nfunction transformOf(x: number, y: number): string {\n return `translate3d(${x}px, ${y}px, 0)`;\n}\n\n/** Internal to <Graph> (gated by `labels.enabled`); reads the ambient\n * instance so hosts composing GraphProvider directly can reuse it. */\nexport function LabelLayer(props: LabelLayerProps): ReactElement {\n const instance = useAmbientGraphInstance('LabelLayer');\n const labels = overlaySurface(instance).labels;\n\n const [candidates, setCandidates] = useState<readonly LabelPlacement[]>([]);\n /** Live label elements, keyed by `(kind, id)` (populated via ref callbacks). */\n const elementsRef = useRef(new Map<string, HTMLDivElement>());\n /** Freshest known screen position per candidate — written by BOTH channels\n * so a candidate re-render never snaps back to a stale x/y. */\n const positionsRef = useRef(new Map<string, readonly [number, number]>());\n\n // Candidate SET changes: throttled re-rank → React re-render of the content.\n useEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribeCandidates((list) => {\n const positions = positionsRef.current;\n const keep = new Set<string>();\n for (const p of list) {\n const key = placementKey(p);\n keep.add(key);\n positions.set(key, [p.x, p.y]);\n }\n for (const key of [...positions.keys()]) {\n if (!keep.has(key)) positions.delete(key);\n }\n // The core REUSES the emitted array/placement objects across ticks\n // copy so React state gets a fresh identity (no Object.is bail-out).\n setCandidates(list.map((p) => ({ ...p })));\n });\n }, [labels]);\n\n // Scheduler position ticks: imperative transform writes, no re-render.\n useLayoutEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribePositions((list) => {\n const elements = elementsRef.current;\n const positions = positionsRef.current;\n for (const p of list) {\n const key = placementKey(p);\n positions.set(key, [p.x, p.y]);\n const el = elements.get(key);\n if (el !== undefined) el.style.transform = transformOf(p.x, p.y);\n }\n });\n }, [labels]);\n\n // Click-selection semantics through the public mutators:\n // plain click replaces the node selection; meta/shift click toggles the id.\n const onLabelClick = (id: NodeId, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.stopPropagation();\n if (e.metaKey || e.shiftKey) {\n const cur = instance.store.getState().selection.nodeIds;\n instance.setSelection(cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]);\n return;\n }\n instance.setSelection([id]);\n };\n\n // a cluster label resolves to its MEMBER node ids through the\n // core mutator — clusters never enter the group selection namespace.\n const onClusterLabelClick = (key: string, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.stopPropagation();\n instance.selectCluster(key, { additive: e.metaKey || e.shiftKey });\n };\n\n // Right-click routes into the SAME typed 'contextMenu' channel the canvas\n // gesture feeds. Label divs are pointerEvents:'auto' (click-to-focus), so\n // without this the gesture dies here and the browser's native menu opens\n // on exactly the nodes prominent enough to carry labels. Coordinates are\n // container-relative CSS px (the layer fills the container), matching the\n // payload contract the canvas path uses.\n const layerRef = useRef<HTMLDivElement | null>(null);\n const onLabelContextMenu = (id: NodeId, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.preventDefault();\n e.stopPropagation();\n const rect = layerRef.current?.getBoundingClientRect();\n if (rect === undefined) return;\n instance.requestNodeContextMenu(id, [e.clientX - rect.left, e.clientY - rect.top]);\n };\n\n const clusterMembers = (key: string): readonly NodeId[] =>\n instance.getClusters().find((c) => c.key === key)?.memberIds ?? [];\n\n return (\n <div data-orbit-label-layer=\"\" ref={layerRef} style={LAYER_STYLE}>\n {candidates.map((p) => {\n const key = placementKey(p);\n const [x, y] = positionsRef.current.get(key) ?? [p.x, p.y];\n const isCluster = p.kind === 'cluster';\n const className = isCluster\n ? [props.labelClassName, props.clusterLabelClassName].filter(Boolean).join(' ') ||\n undefined\n : props.labelClassName;\n return (\n <div\n key={key}\n {...(isCluster ? { 'data-orbit-cluster-label': p.id } : { 'data-orbit-label': p.id })}\n className={className}\n style={{ ...LABEL_BASE_STYLE, transform: transformOf(x, y) }}\n ref={(el) => {\n if (el === null) elementsRef.current.delete(key);\n else elementsRef.current.set(key, el);\n }}\n onClick={(e) => (isCluster ? onClusterLabelClick(p.id, e) : onLabelClick(p.id, e))}\n // Cluster labels keep the browser menu for now: their click\n // already selects members, and no cluster menu target exists.\n onContextMenu={isCluster ? undefined : (e) => onLabelContextMenu(p.id, e)}\n >\n {/* text-node-only rendering holds for both kinds: a hostile\n key or label appears literally, never as markup. */}\n {isCluster\n ? props.renderClusterLabel !== undefined\n ? props.renderClusterLabel({\n clusterKey: p.id,\n text: p.text,\n memberIds: clusterMembers(p.id),\n })\n : p.text\n : props.renderNodeLabel !== undefined\n ? props.renderNodeLabel({\n node: instance.getNode(p.id) ?? { id: p.id },\n text: p.text,\n })\n : p.text}\n </div>\n );\n })}\n </div>\n );\n}\n","/**\n * LiveRegion — screen-reader announcements.\n *\n * A visually-hidden `aria-live=\"polite\"` region adjacent to the canvas. It\n * subscribes to the STORE (never the frame clock, so simulation frames can\n * never announce) and speaks a coalesced summary — node/edge counts,\n * selection count, status — at most once every 800ms and only when the\n * summary STRING changes. Trailing-edge coalescing: a burst of store changes\n * lands as ONE announcement carrying the freshest state.\n *\n * Gated off entirely when `accessibility.announcements === false`.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport type { GraphStoreState } from '@modernrelay/orbit-core';\nimport { useAmbientGraphInstance } from './GraphProvider';\nimport { visuallyHiddenStyle } from './components/shared';\n\nexport const ANNOUNCE_INTERVAL_MS = 800;\n\nexport interface LiveRegionProps {\n /** `accessibility.announcements`; false gates all announcements. Default true. */\n announcements?: boolean | undefined;\n}\n\nfunction summarize(s: GraphStoreState): string {\n const selected = s.selection.nodeIds.length + s.selection.edgeIds.length;\n return `Graph ${s.status}. ${s.nodeCount} nodes, ${s.edgeCount} edges. ${selected} selected.`;\n}\n\n/** Internal to <Graph>; reads the ambient instance so hosts composing\n * GraphProvider directly can reuse it. */\nexport function LiveRegion(props: LiveRegionProps): ReactElement {\n const instance = useAmbientGraphInstance('LiveRegion');\n const enabled = props.announcements !== false;\n const [message, setMessage] = useState('');\n /** Last ANNOUNCED summary; seeded at subscribe time so the initial state\n * (already conveyed by the container's aria-label) is never announced. */\n const lastAnnouncedRef = useRef<string | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!enabled) return undefined;\n const store = instance.store;\n lastAnnouncedRef.current = summarize(store.getState());\n\n const fire = (): void => {\n timerRef.current = null;\n const next = summarize(store.getState());\n if (next === lastAnnouncedRef.current) return;\n lastAnnouncedRef.current = next;\n setMessage(next);\n };\n\n const unsubscribe = store.subscribe(() => {\n if (timerRef.current !== null) return; // window open — coalesce\n if (summarize(store.getState()) === lastAnnouncedRef.current) return;\n timerRef.current = setTimeout(fire, ANNOUNCE_INTERVAL_MS);\n });\n\n return () => {\n unsubscribe();\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, [instance, enabled]);\n\n return (\n <div\n data-orbit-live-region=\"\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={visuallyHiddenStyle}\n >\n {message}\n </div>\n );\n}\n","/**\n * <Graph> — the declarative React binding over a headless GraphInstance.\n *\n * Binding parity: one committed render that changes multiple declarative\n * props issues AT MOST ONE applyHostUpdate carrying exactly the changed keys,\n * so a multi-prop commit produces exactly one store revision and at most one\n * engine commit.\n */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { CSSProperties, ForwardedRef, ReactElement, ReactNode, RefAttributes } from 'react';\nimport {\n canonicalJson,\n canonicalScaleKey,\n createGraphInstance,\n sameGroupSpecArrays,\n} from '@modernrelay/orbit-core';\nimport type {\n Accessor,\n AcceptedEdge,\n AccessibilityConfig,\n BeginIngestOptions,\n ClusterSpec,\n CreateGraphInstanceOptions,\n CrossfilterSession,\n DimensionSpec,\n ExpandNodeResult,\n FilterExpr,\n FilterSpec,\n GraphDiagnostic,\n GraphHostUpdate,\n GraphInstance,\n GraphNode,\n GraphSnapshotInput,\n GraphTheme,\n GraphViewState,\n GroupBySpec,\n GroupSpec,\n IngestSession,\n JsonValue,\n LabelConfig,\n LayoutKind,\n MetaEdge,\n MetricColumn,\n PathOptions,\n PathResult,\n MetricName,\n NodeId,\n ResolvedCluster,\n ResolvedGroup,\n Revisions,\n Scale,\n ScaleChannelInfo,\n SearchResult,\n SearchUnavailableReason,\n SelectionState,\n ScaleLimits,\n WorkerFactoryOption,\n DegradeEvent,\n GraphPerfSnapshot,\n SetViewStateResult,\n SimulationConfig,\n SubgraphSpec,\n ThemeInput,\n TimelinePlayback,\n ViewportState,\n} from '@modernrelay/orbit-core';\nimport type { EngineFactory, FitViewOptions } from '@modernrelay/orbit-core/engine';\nimport { GraphProvider } from './GraphProvider';\nimport { setSearchResultUnavailableCallback, useScaleInfoSubscription } from './hooks';\nimport { Lasso } from './Lasso';\nimport { LabelLayer } from './LabelLayer';\nimport { LiveRegion } from './LiveRegion';\nimport { overlaySurface, visuallyHiddenStyle } from './components/shared';\n\n/** Snapshot handed to `renderLegend`: the active scale info per styling\n * channel (null when the channel is not scale-valued) plus the resolved\n * theme tokens. Recomputed on model/scope/render/theme store changes. */\nexport interface GraphLegendRenderInfo<N = Record<string, unknown>> {\n nodeColor: ScaleChannelInfo<N> | null;\n nodeSize: ScaleChannelInfo<N> | null;\n theme: GraphTheme;\n}\n\nexport interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {\n /** Engine factory; captured at first render — one factory per instance. */\n engine: EngineFactory;\n data?: GraphSnapshotInput<N, E>;\n /** Constant, accessor, or Scale descriptor. Scale-valued props compare\n * by canonical structural value — equal inline literals never reproject. */\n nodeColor?: Accessor<GraphNode<N>, string> | Scale<string, N>;\n nodeSize?: Accessor<GraphNode<N>, number> | Scale<number, N>;\n linkColor?: Accessor<AcceptedEdge<E>, string>;\n linkWidth?: Accessor<AcceptedEdge<E>, number>;\n layout?: LayoutKind;\n simulation?: SimulationConfig;\n /** theme tokens: full GraphTheme, Partial over a named base, or the\n * v0.1 `{background}` shorthand. Diffed structurally (JSON). */\n theme?: ThemeInput;\n /** async metric columns, joined once with revision-gated admission.\n * Diffed by array reference. */\n metrics?: readonly MetricColumn[];\n /** image sprites: synchronous string-ref accessor (URL/blob ref/cache\n * key — opaque to orbit). Diffed by reference. */\n nodeImage?: (node: GraphNode<N>) => string | null;\n /** instanced arrowheads (capability-gated; inert when unsupported). */\n edgeArrows?: boolean;\n /** runtime link-render toggle — a config-only commit. */\n showLinks?: boolean;\n /** emphasis-ring toggle (default true). False clears the ring once and\n * suppresses every driver (hover, focusNode, emphasizeNode). */\n emphasisRing?: boolean;\n /** legend escape hatch: rendered inside the provider in a positioned\n * wrapper (bottom-left) whenever provided; re-invoked when scale info or\n * theme changes. Return custom JSX, or compose <GraphLegend> from\n * '@modernrelay/orbit-react/components/Legend' for the built-in rendering. */\n renderLegend?: (info: GraphLegendRenderInfo<N>) => ReactNode;\n /** DOM label lane; forwarded through applyHostUpdate. `enabled: false`\n * also unmounts the <LabelLayer> overlay entirely. */\n labels?: LabelConfig<N>;\n /** accessibility runtime options; forwarded through applyHostUpdate\n * and mirrored onto the container ARIA surface / live region. */\n accessibility?: AccessibilityConfig<N>;\n /** Class hook applied to every DOM label div. */\n labelClassName?: string;\n /** Escape hatch: custom label content rendered INSIDE the positioned label\n * div, replacing the default text node. */\n renderNodeLabel?: (ctx: { node: GraphNode<N>; text: string }) => ReactNode;\n /** Class hook applied to cluster label divs, in addition to\n * `labelClassName`. */\n clusterLabelClassName?: string;\n /** Escape hatch: custom cluster-label content (replaces the default text\n * node; text-node-only rendering otherwise). `memberIds` are the ids a\n * click selects. */\n renderClusterLabel?: (ctx: {\n clusterKey: string;\n text: string;\n memberIds: readonly NodeId[];\n }) => ReactNode;\n /** Controlled selection of the NODE namespace; providing it once flips\n * ownership permanently. Edge/group namespaces stay instance-owned. */\n selection?: readonly NodeId[];\n /** hard scope: feed ONLY the resolved subset through the reconciler;\n * `null` restores the full accepted model. Diffed STRUCTURALLY (specs are\n * small), so a new-but-equal object is a no-op. UNCONTROLLED-ONLY in v0.5:\n * this prop and isolateSelection/resetIsolation write the same\n * instance-owned state — last writer wins, and omitting the prop leaves\n * the last written scope in place. */\n subgraph?: SubgraphSpec | null;\n /** soft filter: mask (hide/dim) with ZERO relayout; `null` clears.\n * Diffed by canonical structural compare — serializable exprs by JSON,\n * function predicates by reference — so a new-but-equal spec never\n * re-publishes (and the core no-ops canonical-equal specs regardless). */\n filter?: FilterSpec<N, E> | null;\n /** crossfilter dimensions (declarative; brushes live on the session\n * — reach it via useGraphCrossfilter or handle.getCrossfilterSession).\n * Forwarded on array-reference change; the core no-ops when every\n * DimensionSpec element is reference-equal. */\n crossfilter?: readonly DimensionSpec<N>[];\n /** manual groups; `null` clears. Diffed STRUCTURALLY (equal inline\n * literals never re-forward). Providing it once flips the groups slice to\n * CONTROLLED: handle ops then fire onGroupsChange with the next\n * array instead of writing — reflect it back here. Config error together\n * with `groupBy`. */\n groups?: readonly GroupSpec[] | null;\n /** derived grouping; `null` clears. Reference-diffed (the spec\n * carries a function accessor); the core additionally no-ops structurally\n * equal respecs. Membership under groupBy is derived and READ-ONLY\n * groupNodes/ungroup become config errors; setGroupCollapsed toggles the\n * instance-owned per-key collapsed residue. */\n groupBy?: GroupBySpec<N> | null;\n /** stage-4 non-collapsing clusters; `null` clears (D2).\n * Reference-diffed (the spec carries a function accessor). Clusters PRESERVE\n * every node and edge — they never collapse anything — so they coexist with\n * `groups`/`groupBy`. Their labels ride the overlay lane under\n * `labels.maxZoom` and select their member nodes on click. */\n clusters?: ClusterSpec<N> | null;\n /** PERSISTENT pins — independent of transient drag\n * pinning: the engine receives the UNION, so releasing a drag on a\n * persistently-pinned node leaves it pinned. Providing it once latches the\n * slice controlled: handle ops then fire onPinnedChange with the\n * next array instead of writing. `null` clears (D2). */\n pinnedNodeIds?: readonly NodeId[] | null;\n /** node attr fields the DEFAULT search service indexes (ids always).\n * Absent = id-only search — the service never guesses attr names.\n * CONSTRUCTION-ONLY (D7, spec host construction options): read once at\n * mount; changing it requires a keyed remount (a changed prop warns once\n * and is ignored). */\n searchIndex?: readonly string[];\n /** Degradation-ladder thresholds. CONSTRUCTION-ONLY: read\n * once at mount; a runtime change warns once and is ignored. */\n limits?: Partial<ScaleLimits>;\n /** Execution mode. CONSTRUCTION-ONLY: read once\n * at mount; a runtime change warns once and is ignored. Current worker\n * cargo: columnar acceptance off-thread. */\n execution?: 'auto' | 'main' | 'worker';\n /** Worker construction tri-option. CONSTRUCTION-ONLY. */\n workerFactory?: WorkerFactoryOption;\n /** result contract: the instance-wide DEFAULT for an activated\n * search result that cannot be focused ('not-loaded' | 'out-of-scope' |\n * 'filtered'). `<GraphSearch onResultUnavailable>` overrides it locally;\n * the host reacts explicitly (fetch/publish an overlay, reset isolation,\n * alter filters) — search never mutates scope/filters itself. */\n onSearchResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Captured at first render (instance construction option). Default true. */\n fitViewOnFirstData?: boolean;\n /** service seam (instance construction option, D7): custom\n * revision-aware services — most usefully an async `expansion` service\n * backed by the host's own data source, so `expandNode`/the context menu's\n * Expand run against the network instead of the built-in local-adjacency\n * walk. CONSTRUCTION-ONLY: read once at mount; changing it requires a keyed\n * remount. */\n services?: CreateGraphInstanceOptions<N, E>['services'];\n /** Shift+drag freeform lasso selection overlay. Default true. */\n enableLasso?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Rendered inside the provider, above (after) the canvas container. */\n children?: ReactNode;\n onNodeClick?: (payload: { node: GraphNode<N>; metaKey?: boolean }) => void;\n onBackgroundClick?: () => void;\n onNodeHover?: (payload: { node: GraphNode<N> | null }) => void;\n onEdgeClick?: (payload: { edge: AcceptedEdge<E> }) => void;\n onEdgeHover?: (payload: { edge: AcceptedEdge<E> | null }) => void;\n onNodeDragStart?: (payload: { node: GraphNode<N> }) => void;\n onNodeDragEnd?: (payload: { node: GraphNode<N>; x: number; y: number }) => void;\n /** Fires with the full namespaced SelectionState. */\n onSelectionChange?: (payload: SelectionState) => void;\n /** ladder step engagement/disengagement (notification pattern). */\n onDegrade?: (event: DegradeEvent) => void;\n /** throttled telemetry sample — never per frame. */\n onPerfSample?: (snapshot: GraphPerfSnapshot) => void;\n /** Fires on a super-node hit with the resolved group payload, never a GraphNode. */\n onGroupClick?: (payload: { group: ResolvedGroup; metaKey?: boolean }) => void;\n /** Fires on a meta-edge hit with the MetaEdge record used by the count badge. */\n onMetaEdgeClick?: (payload: { metaEdge: MetaEdge }) => void;\n /** groups slice change callback: op results (uncontrolled), op\n * INTENTS (controlled — reflect the array back into `groups`), and groupBy\n * re-derivations (notification). Receives the resolved array. */\n onGroupsChange?: (groups: readonly ResolvedGroup[]) => void;\n /** durable source coordinate — forwarded verbatim; serialized into\n * view states and canonically compared on restore. Diffed by canonical\n * JSON, so a new-but-equal object never re-forwards. */\n dataRef?: JsonValue;\n /** aggregate restore intent (fires once per restore/history\n * transaction touching a controlled slice or serialized styling): reflect\n * every participating prop in ONE commit; matching values acknowledge. */\n onViewStateRestore?: (intent: {\n transactionId: string;\n source: 'setViewState' | 'undo' | 'redo';\n next: unknown;\n }) => void;\n /** fired INSTEAD of applying a state whose dataRef mismatches. */\n onViewStateMismatch?: (stored: JsonValue | undefined, current: JsonValue | undefined) => void;\n /** persistent-pin slice change: the applied set when\n * uncontrolled, the INTENT when controlled. */\n onPinnedChange?: (pinnedNodeIds: readonly NodeId[]) => void;\n onViewportChange?: (v: ViewportState) => void;\n onReady?: () => void;\n onError?: (payload: { error: Error }) => void;\n}\n\nexport interface GraphHandle<N = Record<string, unknown>, E = Record<string, unknown>> {\n /** `opts` is accepted for forward compatibility; core v0.1 ignores it. */\n fitView(opts?: FitViewOptions): void;\n zoomIn(): void;\n zoomOut(): void;\n /** `opts` is accepted for forward compatibility; core v0.1 ignores it. */\n setViewport(v: Partial<ViewportState>, opts?: { durationMs?: number }): void;\n focusNode(id: NodeId): void;\n /** ring-only emphasis, no camera move (null clears; unknown ids\n * no-op). The keyboard-navigation analog of hover. */\n emphasizeNode(id: NodeId | null): void;\n /** telemetry snapshot (never raw attrs or ids). */\n getPerfSnapshot(): GraphPerfSnapshot;\n /** Open the typed context menu for a node from host chrome (a list row, a\n * custom label) — the same 'contextMenu' event the canvas gesture emits.\n * `screen` is container-relative CSS px. */\n requestNodeContextMenu(id: NodeId, screen: readonly [number, number]): void;\n /** serialize the exploration state (see core docs). */\n getViewState(opts?: { includePositions?: false }): GraphViewState;\n /** atomic restore; typed results, never partial. */\n setViewState(\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult>;\n /** picture export: 'png' via the engine screenshot; 'svg' via the\n * engine-free exporter (typed export-too-large past the bound;\n * raster-hybrid fallback available). */\n exportImage(format: 'png'): Promise<Blob>;\n exportImage(\n format: 'svg',\n opts?: { maxSvgElements?: number; fallback?: 'raster-hybrid' },\n ): Promise<string>;\n /** bounded object export; typed rejection past the limit. */\n exportData(\n scope?: 'visible' | 'accepted',\n opts?: { limit?: number },\n ): Promise<{ nodes: readonly GraphNode<N>[]; edges: readonly AcceptedEdge<E>[] }>;\n /** memory-bounded JSONL stream over one pinned revision. */\n exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;\n /** bounded id → [x, y] map (one position readback). */\n exportLayout(opts?: { limit?: number }): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;\n /** layout JSONL stream over one pinned readback. */\n exportLayoutStream(): AsyncGenerator<string, void, undefined>;\n setSelection(ids: readonly NodeId[]): void;\n clearSelection(): void;\n // --- persistent pins ---\n /** Hold nodes at their CURRENT position (independent of drag pinning). */\n pinNodes(ids: readonly NodeId[]): void;\n unpinNodes(ids: readonly NodeId[]): void;\n // --- paths ---\n /** Resolve + atomically emphasize a path; null = unreachable (a result).\n * Emphasis is session-local — released by clearPath, any selection\n * mutation, undo/redo, or a scene rebuild; never in history/view state. */\n findPath(sourceId: NodeId, targetId: NodeId, options?: PathOptions): Promise<PathResult | null>;\n clearPath(): void;\n // --- selection algebra ---\n /** Expand to the 1-hop neighborhood of `id` (or of the current selection). */\n selectNeighbors(id?: NodeId): void;\n selectAll(): void;\n invertSelection(): void;\n /** Resolve a SCREEN-coordinate polygon to node ids and replace (default) or\n * union (`additive`) the node selection; returns the resolved ids. */\n selectWithinPolygon(\n polygon: readonly [number, number][],\n opts?: { additive?: boolean },\n ): readonly NodeId[];\n // --- pin / hide slices ---\n /** Pin at `xy` (space coords) or at the node's current position. */\n pinNode(id: NodeId, xy?: readonly [number, number]): void;\n unpinNode(id: NodeId): void;\n clearPins(): void;\n hideNodes(ids: readonly NodeId[]): void;\n showAll(): void;\n // --- hard scope + expansion ---\n /** Hard-scope to the current node selection; no-op when empty. */\n isolateSelection(): void;\n /** Clear the hard scope — the full accepted model returns. */\n resetIsolation(): void;\n /** Ego-expand `id` through the configured ExpansionService. */\n expandNode(id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult>;\n /** Abort `id`'s pending expansion and remove its committed expansion\n * overlays. */\n retractExpansion(id: NodeId): void;\n // --- group operations ---\n /** Add one group (same acyclic/singly-parented validation as the `groups` prop).\n * Controlled mode fires onGroupsChange with the next array instead. */\n groupNodes(spec: GroupSpec): void;\n /** Remove one group definition; its id prunes from selection.groupIds. */\n ungroup(groupId: string): void;\n /** Collapse/expand one group as a structural diff — the ONE op allowed on\n * groupBy-derived groups (toggles the per-key collapsed residue). */\n setGroupCollapsed(groupId: string, collapsed: boolean): void;\n // --- node folds: an EXISTING node stands for its own neighbourhood.\n // Distinct from `expandNode`/`retractExpansion`, which navigate the data\n // source; a fold is pure containment over what is already loaded. ---\n /** Hide `id`'s neighbourhood behind `id`, rerouting the members' outside\n * edges onto it. Members default to unclaimed neighbours (first fold wins). */\n foldNode(id: NodeId, opts?: { memberIds?: readonly NodeId[] }): void;\n /** Return `id`'s folded members to the scene. No-op when not folded. */\n unfoldNode(id: NodeId): void;\n /** The members `id` stands for, or null when it is not folded. */\n getFold(id: NodeId): { memberIds: readonly NodeId[] } | null;\n // --- stage-4 clusters ---\n /** Current clusters over the physical scene (keys, members, force center,\n * settled centroid). Empty when no `clusters` prop is active. */\n getClusters(): readonly ResolvedCluster[];\n /** Select a cluster's MEMBER node ids; `additive` unions. */\n selectCluster(key: string, opts?: { additive?: boolean }): void;\n // --- revisioned ingestion ---\n /** Begin a bounded, cancellable ingest session. */\n beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;\n /** Atomically remove exactly one committed overlay; unknown ids are an\n * idempotent `{ removed: false }`. */\n removeOverlay(overlayId: string): { removed: boolean };\n // --- history ---\n /** Undo the most recent uncontrolled mutation entry (selection / hidden /\n * pins / scope / brushes). Returns false when there is nothing to undo. */\n undo(): boolean;\n /** Re-apply the most recently undone entry. False when nothing to redo. */\n redo(): boolean;\n // --- timeline playback ---\n /** Play a brush window across a numeric/temporal dimension's domain\n * (crossfilter mask fast path; one playing dimension at a time). */\n playTimeline(key: string, playback?: Partial<TimelinePlayback>): void;\n pauseTimeline(): void;\n // --- crossfilter ---\n /** The crossfilter session facade, or null until the `crossfilter` prop\n * has configured dimensions over an accepted base. */\n getCrossfilterSession(): CrossfilterSession | null;\n // --- styling reads ---\n /** legend surface: the active Scale on a styling channel plus its\n * resolved domain or categorical rows; null when not scale-valued. */\n getScaleInfo(channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null;\n /** metric read for one node id (built-in degree family + admitted\n * columns); null for unknown ids or metrics and for null/non-finite values. */\n getMetricValue(metric: MetricName, id: NodeId): number | null;\n getRevisions(): Revisions;\n getDiagnostics(): readonly GraphDiagnostic[];\n instance: GraphInstance<N, E>;\n}\n\n/** Last-committed declarative props. Fields are `| undefined` (not optional)\n * so the diff record is assignable under exactOptionalPropertyTypes. */\ninterface CommittedProps<N, E> {\n data: GraphSnapshotInput<N, E> | undefined;\n nodeColor: Accessor<GraphNode<N>, string> | Scale<string, N> | undefined;\n /** Canonical structural key when the committed nodeColor is a Scale;\n * undefined for accessor/constant forms (those diff by reference). */\n nodeColorKey: string | undefined;\n nodeSize: Accessor<GraphNode<N>, number> | Scale<number, N> | undefined;\n nodeSizeKey: string | undefined;\n linkColor: Accessor<AcceptedEdge<E>, string> | undefined;\n linkWidth: Accessor<AcceptedEdge<E>, number> | undefined;\n layout: LayoutKind | undefined;\n simulation: SimulationConfig | undefined;\n /** JSON form of the last committed `theme` prop (small token set). */\n themeJson: string | undefined;\n metrics: readonly MetricColumn[] | undefined;\n nodeImage: ((node: GraphNode<N>) => string | null) | undefined;\n edgeArrows: boolean | undefined;\n showLinks: boolean | undefined;\n emphasisRing: boolean | undefined;\n selection: readonly NodeId[] | undefined;\n labels: LabelConfig<N> | undefined;\n accessibility: AccessibilityConfig<N> | undefined;\n /** JSON form of the last committed `subgraph` prop — specs are small, so\n * the diff is a structural string compare ('null' when scope cleared). */\n subgraphJson: string | undefined;\n /** Last committed `filter` prop + its canonical key: exprs compare\n * by JSON, function predicates by reference. */\n filter: FilterSpec<N, E> | null | undefined;\n filterKey: string | undefined;\n /** Last committed `crossfilter` prop, compared by array reference. */\n crossfilter: readonly DimensionSpec<N>[] | undefined;\n /** Last committed `groups` prop (structural diff via sameGroupSpecArrays;\n * — equal inline literals never re-forward). */\n groups: readonly GroupSpec[] | null | undefined;\n dataRef: JsonValue | undefined;\n /** Last committed `groupBy` prop (reference diff — function-bearing). */\n groupBy: GroupBySpec<N> | null | undefined;\n /** Last committed `clusters` prop (reference diff — function-bearing). */\n clusters: ClusterSpec<N> | null | undefined;\n /** Last committed `pinnedNodeIds` prop, compared by shallow element diff. */\n pinnedNodeIds: readonly NodeId[] | null | undefined;\n /** Last committed `searchIndex` prop (shallow element diff). */\n searchIndex: readonly string[] | undefined;\n}\n\n/**\n * Canonical key of a scale-valued styling channel: `canonicalScaleKey`\n * when the value is a Scale descriptor, undefined for constant/accessor forms\n * (which diff by reference). Equal inline scale literals therefore never\n * re-forward; a function `by` keys by reference identity inside the core.\n */\nfunction scaleKeyOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const kind = (value as { kind?: unknown }).kind;\n if (kind !== 'sequential' && kind !== 'categorical' && kind !== 'diverging') return undefined;\n return canonicalScaleKey(value as Scale<unknown>);\n}\n\n/** Diff one nodeColor/nodeSize channel: scale forms compare by canonical key,\n * everything else by reference (a scale↔accessor flip always differs). */\nfunction styleChannelChanged(\n prevValue: unknown,\n prevKey: string | undefined,\n nextValue: unknown,\n nextKey: string | undefined,\n): boolean {\n if (nextKey !== undefined || prevKey !== undefined) return nextKey !== prevKey;\n return prevValue !== nextValue;\n}\n\n/** Canonical key of one filter side: 'u' absent, 'fn' a function predicate\n * (paired with a reference compare), else the expr JSON. */\nfunction filterSideKey(side: FilterExpr | ((item: never) => boolean) | undefined): string {\n if (side === undefined) return 'u';\n if (typeof side === 'function') return 'fn';\n return JSON.stringify(side);\n}\n\nfunction filterKeyOf<N, E>(spec: FilterSpec<N, E> | null): string {\n if (spec === null) return 'null';\n return `${spec.mode ?? 'hide'}|${filterSideKey(spec.nodes)}|${filterSideKey(spec.edges)}`;\n}\n\n/** Canonical-equal check: keys match AND any function sides are the SAME\n * references (a changed predicate function must re-evaluate). */\nfunction sameFilterSpec<N, E>(\n prev: FilterSpec<N, E> | null | undefined,\n prevKey: string | undefined,\n next: FilterSpec<N, E> | null,\n nextKey: string,\n): boolean {\n if (prevKey === undefined || prevKey !== nextKey) return false;\n if (next === null) return prev === null;\n if (prev === null || prev === undefined) return false;\n if (typeof next.nodes === 'function' && next.nodes !== prev.nodes) return false;\n if (typeof next.edges === 'function' && next.edges !== prev.edges) return false;\n return true;\n}\n\ninterface CallbackHolder<N, E> {\n onNodeClick: ((payload: { node: GraphNode<N>; metaKey?: boolean }) => void) | undefined;\n onBackgroundClick: (() => void) | undefined;\n onNodeHover: ((payload: { node: GraphNode<N> | null }) => void) | undefined;\n onEdgeClick: ((payload: { edge: AcceptedEdge<E> }) => void) | undefined;\n onEdgeHover: ((payload: { edge: AcceptedEdge<E> | null }) => void) | undefined;\n onNodeDragStart: ((payload: { node: GraphNode<N> }) => void) | undefined;\n onNodeDragEnd: ((payload: { node: GraphNode<N>; x: number; y: number }) => void) | undefined;\n onSelectionChange: ((payload: SelectionState) => void) | undefined;\n onDegrade: ((event: DegradeEvent) => void) | undefined;\n onPerfSample: ((snapshot: GraphPerfSnapshot) => void) | undefined;\n onGroupClick: ((payload: { group: ResolvedGroup; metaKey?: boolean }) => void) | undefined;\n onMetaEdgeClick: ((payload: { metaEdge: MetaEdge }) => void) | undefined;\n onGroupsChange: ((groups: readonly ResolvedGroup[]) => void) | undefined;\n onViewStateRestore:\n | ((intent: {\n transactionId: string;\n source: 'setViewState' | 'undo' | 'redo';\n next: unknown;\n }) => void)\n | undefined;\n onViewStateMismatch:\n | ((stored: JsonValue | undefined, current: JsonValue | undefined) => void)\n | undefined;\n onPinnedChange: ((pinnedNodeIds: readonly NodeId[]) => void) | undefined;\n onViewportChange: ((v: ViewportState) => void) | undefined;\n onReady: (() => void) | undefined;\n onError: ((payload: { error: Error }) => void) | undefined;\n}\n\nfunction sameIdArrays(a: readonly NodeId[], b: readonly NodeId[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nconst CONTAINER_BASE_STYLE: CSSProperties = {\n position: 'relative',\n width: '100%',\n height: '100%',\n};\n\n// The canvas layer fills the outer div; children render after it in DOM order\n// so they naturally stack above the canvas.\nconst CANVAS_STYLE: CSSProperties = { position: 'absolute', inset: 0 };\n\n/** Positioned wrapper for the `renderLegend` slot: bottom-left, above\n * the canvas, interactive (category rows are clickable). */\nconst LEGEND_SLOT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 12,\n bottom: 12,\n};\n\n/** `renderLegend` slot host: re-reads getScaleInfo on every store publication\n * that can move scale info (model/scope/render revisions) or theme change,\n * and re-invokes the host's render function with the fresh snapshot. */\nfunction LegendSlot<N, E>(props: {\n instance: GraphInstance<N, E>;\n renderLegend: (info: GraphLegendRenderInfo<N>) => ReactNode;\n}): ReactElement {\n const { instance } = props;\n const { version, theme } = useScaleInfoSubscription(instance);\n const info = useMemo<GraphLegendRenderInfo<N>>(\n () => ({\n nodeColor: instance.getScaleInfo('nodeColor'),\n nodeSize: instance.getScaleInfo('nodeSize'),\n theme,\n }),\n // `version` is the invalidation token: a new store revision can change\n // what getScaleInfo returns without changing any other dependency.\n [instance, theme, version],\n );\n return (\n <div data-orbit-legend-slot=\"\" style={LEGEND_SLOT_STYLE}>\n {props.renderLegend(info)}\n </div>\n );\n}\n\nfunction GraphInner<N, E>(\n props: GraphProps<N, E>,\n ref: ForwardedRef<GraphHandle<N, E>>,\n): ReactElement {\n // Lazy, once per mounted component. `engine`/`fitViewOnFirstData`/\n // `searchIndex` are construction options (D7); later changes to them are\n // intentionally ignored (searchIndex additionally warns once below).\n const [instance] = useState<GraphInstance<N, E>>(() => {\n const options: CreateGraphInstanceOptions<N, E> = { engine: props.engine };\n if (props.fitViewOnFirstData !== undefined) {\n options.fitViewOnFirstData = props.fitViewOnFirstData;\n }\n if (props.searchIndex !== undefined) options.searchIndex = props.searchIndex;\n if (props.limits !== undefined) options.limits = props.limits;\n if (props.execution !== undefined) options.execution = props.execution;\n if (props.workerFactory !== undefined) options.workerFactory = props.workerFactory;\n if (props.services !== undefined) options.services = props.services;\n return createGraphInstance<N, E>(options);\n });\n const searchIndexWarnedRef = useRef(false);\n const limitsWarnedRef = useRef(false);\n const initialLimitsRef = useRef(props.limits);\n if (props.limits !== initialLimitsRef.current && !limitsWarnedRef.current) {\n limitsWarnedRef.current = true;\n console.warn(\n 'orbit: `limits` is a construction-only prop — the runtime change is ignored; remount with a key to apply new thresholds.',\n );\n }\n const executionWarnedRef = useRef(false);\n const initialExecutionRef = useRef(props.execution);\n if (props.execution !== initialExecutionRef.current && !executionWarnedRef.current) {\n executionWarnedRef.current = true;\n console.warn(\n 'orbit: `execution` is a construction-only prop — the runtime change is ignored; remount with a key to switch lanes.',\n );\n }\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // --- latest-callback holder: event subscriptions read through this ref so\n // per-render callback identity churn never resubscribes ---\n const callbacksRef = useRef<CallbackHolder<N, E>>({\n onNodeClick: undefined,\n onBackgroundClick: undefined,\n onNodeHover: undefined,\n onEdgeClick: undefined,\n onEdgeHover: undefined,\n onNodeDragStart: undefined,\n onNodeDragEnd: undefined,\n onSelectionChange: undefined,\n onDegrade: undefined,\n onPerfSample: undefined,\n onGroupClick: undefined,\n onMetaEdgeClick: undefined,\n onGroupsChange: undefined,\n onViewStateRestore: undefined,\n onViewStateMismatch: undefined,\n onPinnedChange: undefined,\n onViewportChange: undefined,\n onReady: undefined,\n onError: undefined,\n });\n useLayoutEffect(() => {\n callbacksRef.current = {\n onNodeClick: props.onNodeClick,\n onBackgroundClick: props.onBackgroundClick,\n onNodeHover: props.onNodeHover,\n onEdgeClick: props.onEdgeClick,\n onEdgeHover: props.onEdgeHover,\n onNodeDragStart: props.onNodeDragStart,\n onNodeDragEnd: props.onNodeDragEnd,\n onSelectionChange: props.onSelectionChange,\n onDegrade: props.onDegrade,\n onPerfSample: props.onPerfSample,\n onGroupClick: props.onGroupClick,\n onMetaEdgeClick: props.onMetaEdgeClick,\n onGroupsChange: props.onGroupsChange,\n onViewStateRestore: props.onViewStateRestore,\n onViewStateMismatch: props.onViewStateMismatch,\n onPinnedChange: props.onPinnedChange,\n onViewportChange: props.onViewportChange,\n onReady: props.onReady,\n onError: props.onError,\n };\n });\n\n // --- event subscriptions: keyed on the instance only; identity churn of\n // the props is absorbed by callbacksRef. A LAYOUT effect so it commits\n // BEFORE the prop-diff layout effect of the NEXT render pass — and on the\n // first pass the subscriptions land in the same commit as the initial\n // host update's effects; events fired during that first applyHostUpdate\n // (e.g. a mount-time 'degrade' engagement) are re-emittable states\n // that the ladder re-evaluates, but ordering here still matters for any\n // strictly edge-triggered channel, so subscriptions must not lag a frame\n // behind the data. ---\n useLayoutEffect(() => {\n const offs = [\n instance.on('nodeClick', (p) => callbacksRef.current.onNodeClick?.(p)),\n instance.on('backgroundClick', () => callbacksRef.current.onBackgroundClick?.()),\n instance.on('nodeHover', (p) => callbacksRef.current.onNodeHover?.(p)),\n instance.on('edgeClick', (p) => callbacksRef.current.onEdgeClick?.(p)),\n instance.on('edgeHover', (p) => callbacksRef.current.onEdgeHover?.(p)),\n instance.on('nodeDragStart', (p) => callbacksRef.current.onNodeDragStart?.(p)),\n instance.on('nodeDragEnd', (p) => callbacksRef.current.onNodeDragEnd?.(p)),\n instance.on('selectionChange', (p) => callbacksRef.current.onSelectionChange?.(p)),\n instance.on('degrade', (p) => callbacksRef.current.onDegrade?.(p)),\n instance.on('perfSample', (p) => callbacksRef.current.onPerfSample?.(p)),\n instance.on('groupClick', (p) => callbacksRef.current.onGroupClick?.(p)),\n instance.on('metaEdgeClick', (p) => callbacksRef.current.onMetaEdgeClick?.(p)),\n instance.on('groupsChange', (p) => callbacksRef.current.onGroupsChange?.(p.groups)),\n instance.on('viewStateRestore', (p) => callbacksRef.current.onViewStateRestore?.(p)),\n instance.on('viewStateMismatch', (p) =>\n callbacksRef.current.onViewStateMismatch?.(p.stored, p.current),\n ),\n instance.on('pinnedChange', (p) => callbacksRef.current.onPinnedChange?.(p.pinnedNodeIds)),\n instance.on('viewportChange', (v) => callbacksRef.current.onViewportChange?.(v)),\n instance.on('ready', () => callbacksRef.current.onReady?.()),\n instance.on('error', (p) => callbacksRef.current.onError?.(p)),\n ];\n return () => {\n for (const off of offs) off();\n };\n }, [instance]);\n\n // --- prop → host update: diff against the previous committed render and\n // issue at most one applyHostUpdate with exactly the changed keys ---\n const prevRef = useRef<CommittedProps<N, E> | null>(null);\n useLayoutEffect(() => {\n const prev = prevRef.current;\n const update: GraphHostUpdate<N, E> = {};\n let dirty = false;\n\n if (props.data !== undefined && (prev === null || prev.data !== props.data)) {\n update.data = props.data;\n dirty = true;\n }\n // scale-aware channels: Scale descriptors diff by canonical\n // structural key (equal inline literals never re-forward); accessor and\n // constant forms keep the reference diff.\n const nodeColorKey = scaleKeyOf(props.nodeColor);\n if (\n props.nodeColor !== undefined &&\n (prev === null ||\n styleChannelChanged(prev.nodeColor, prev.nodeColorKey, props.nodeColor, nodeColorKey))\n ) {\n update.nodeColor = props.nodeColor;\n dirty = true;\n }\n const nodeSizeKey = scaleKeyOf(props.nodeSize);\n if (\n props.nodeSize !== undefined &&\n (prev === null ||\n styleChannelChanged(prev.nodeSize, prev.nodeSizeKey, props.nodeSize, nodeSizeKey))\n ) {\n update.nodeSize = props.nodeSize;\n dirty = true;\n }\n if (props.linkColor !== undefined && (prev === null || prev.linkColor !== props.linkColor)) {\n update.linkColor = props.linkColor;\n dirty = true;\n }\n if (props.linkWidth !== undefined && (prev === null || prev.linkWidth !== props.linkWidth)) {\n update.linkWidth = props.linkWidth;\n dirty = true;\n }\n if (props.layout !== undefined && (prev === null || prev.layout !== props.layout)) {\n update.layout = props.layout;\n dirty = true;\n }\n if (\n props.simulation !== undefined &&\n (prev === null || prev.simulation !== props.simulation)\n ) {\n update.simulation = props.simulation;\n dirty = true;\n }\n // theme: a small token set — diffed structurally (JSON), so a\n // new-but-equal inline object never re-publishes.\n let themeJson: string | undefined;\n if (props.theme !== undefined) {\n themeJson = JSON.stringify(props.theme);\n if (prev === null || prev.themeJson !== themeJson) {\n update.theme = props.theme;\n dirty = true;\n }\n }\n // metric columns: array-reference diff (columns are one-shot joins).\n if (props.metrics !== undefined && (prev === null || prev.metrics !== props.metrics)) {\n update.metrics = props.metrics;\n dirty = true;\n }\n // image refs: function-prop contract — reference diff.\n if (props.nodeImage !== undefined && (prev === null || prev.nodeImage !== props.nodeImage)) {\n update.nodeImage = props.nodeImage;\n dirty = true;\n } else if (props.nodeImage === undefined && prev !== null && prev.nodeImage !== undefined) {\n // Removing the prop forwards the explicit clear; the natural\n // React idiom must not silently freeze the last accessor's images.\n update.nodeImage = null;\n dirty = true;\n }\n // Display toggles use plain value diffing for config-only commits.\n if (props.edgeArrows !== undefined && (prev === null || prev.edgeArrows !== props.edgeArrows)) {\n update.edgeArrows = props.edgeArrows;\n dirty = true;\n }\n if (props.showLinks !== undefined && (prev === null || prev.showLinks !== props.showLinks)) {\n update.showLinks = props.showLinks;\n dirty = true;\n }\n if (\n props.emphasisRing !== undefined &&\n (prev === null || prev.emphasisRing !== props.emphasisRing)\n ) {\n update.emphasisRing = props.emphasisRing;\n dirty = true;\n } else if (\n props.emphasisRing === undefined &&\n prev !== null &&\n prev.emphasisRing !== undefined\n ) {\n // Removing the prop restores the documented default (true); the\n // natural React idiom must not leave the ring silently disabled.\n update.emphasisRing = true;\n dirty = true;\n }\n if (\n props.selection !== undefined &&\n (prev === null ||\n prev.selection === undefined ||\n !sameIdArrays(prev.selection, props.selection))\n ) {\n update.selection = props.selection;\n dirty = true;\n }\n if (props.labels !== undefined && (prev === null || prev.labels !== props.labels)) {\n update.labels = props.labels;\n dirty = true;\n }\n if (\n props.accessibility !== undefined &&\n (prev === null || prev.accessibility !== props.accessibility)\n ) {\n update.accessibility = props.accessibility;\n dirty = true;\n }\n // hard scope: structural (JSON) diff — a new-but-equal spec object\n // never re-publishes. An omitted prop is never forwarded (uncontrolled).\n let subgraphJson: string | undefined;\n if (props.subgraph !== undefined) {\n subgraphJson = JSON.stringify(props.subgraph);\n if (prev === null || prev.subgraphJson !== subgraphJson) {\n update.subgraph = props.subgraph;\n dirty = true;\n }\n }\n // soft filter: canonical structural diff (exprs by JSON, function\n // predicates by reference). An omitted prop is never forwarded.\n let filterKey: string | undefined;\n if (props.filter !== undefined) {\n filterKey = filterKeyOf(props.filter);\n if (\n prev === null ||\n !sameFilterSpec(prev.filter, prev.filterKey, props.filter, filterKey)\n ) {\n update.filter = props.filter;\n dirty = true;\n }\n }\n // crossfilter dimensions: forwarded on array-reference change; the\n // core no-ops when every DimensionSpec element is reference-equal.\n if (\n props.crossfilter !== undefined &&\n (prev === null || prev.crossfilter !== props.crossfilter)\n ) {\n update.crossfilter = props.crossfilter;\n dirty = true;\n }\n // groups: structural diff (equal inline literals never\n // re-forward). An omitted prop is never forwarded — but ONCE provided\n // the core latches the slice controlled.\n if (props.dataRef !== undefined) {\n const prevRef = prev === null ? undefined : prev.dataRef;\n if (prevRef === undefined || canonicalJson(props.dataRef) !== canonicalJson(prevRef)) {\n update.dataRef = props.dataRef;\n dirty = true;\n }\n }\n if (props.groups !== undefined) {\n const prevGroups = prev === null ? undefined : prev.groups;\n if (prevGroups === undefined || !sameGroupSpecArrays(prevGroups, props.groups)) {\n update.groups = props.groups;\n dirty = true;\n }\n }\n // groupBy: reference diff (the spec carries a function accessor);\n // the core additionally no-ops structurally equal respecs.\n if (props.groupBy !== undefined && (prev === null || prev.groupBy !== props.groupBy)) {\n update.groupBy = props.groupBy;\n dirty = true;\n }\n // clusters: reference diff (function-bearing spec); the core\n // re-derives membership only on a `by` identity change.\n if (props.clusters !== undefined && (prev === null || prev.clusters !== props.clusters)) {\n update.clusters = props.clusters;\n dirty = true;\n }\n // pinnedNodeIds: shallow element diff (a small id list); ONCE\n // provided the core latches the slice controlled.\n if (props.pinnedNodeIds !== undefined) {\n const prevPinned = prev === null ? undefined : prev.pinnedNodeIds;\n const changed =\n prevPinned === undefined ||\n prevPinned === null ||\n props.pinnedNodeIds === null ||\n !sameIdArrays(prevPinned, props.pinnedNodeIds);\n if (changed) {\n update.pinnedNodeIds = props.pinnedNodeIds;\n dirty = true;\n }\n }\n // searchIndex is construction-only and is read once above. A\n // changed prop cannot take effect without a keyed remount — warn once\n // (shallow element diff, so a new-but-equal inline literal is fine).\n if (\n prev !== null &&\n !sameIdArrays(prev.searchIndex ?? [], props.searchIndex ?? []) &&\n !searchIndexWarnedRef.current\n ) {\n searchIndexWarnedRef.current = true;\n console.warn(\n '[orbit] searchIndex is construction-only: the changed value was ignored — key-remount <Graph> to change it',\n );\n }\n\n prevRef.current = {\n data: props.data,\n nodeColor: props.nodeColor,\n nodeColorKey,\n nodeSize: props.nodeSize,\n nodeSizeKey,\n linkColor: props.linkColor,\n linkWidth: props.linkWidth,\n layout: props.layout,\n simulation: props.simulation,\n themeJson,\n metrics: props.metrics,\n nodeImage: props.nodeImage,\n edgeArrows: props.edgeArrows,\n showLinks: props.showLinks,\n emphasisRing: props.emphasisRing,\n selection: props.selection,\n labels: props.labels,\n accessibility: props.accessibility,\n subgraphJson,\n filter: props.filter,\n filterKey,\n crossfilter: props.crossfilter,\n groups: props.groups,\n dataRef: props.dataRef,\n groupBy: props.groupBy,\n clusters: props.clusters,\n pinnedNodeIds: props.pinnedNodeIds,\n searchIndex: props.searchIndex,\n };\n\n if (dirty) instance.applyHostUpdate(update);\n });\n\n // Register the instance-wide default unavailable-result\n // callback so <GraphSearch> (and bespoke activation UIs) resolve it without\n // <Graph> in their import graph. Identity churn is fine — registration is a\n // WeakMap write, never a re-render.\n useLayoutEffect(() => {\n setSearchResultUnavailableCallback(instance, props.onSearchResultUnavailable);\n });\n\n\n // --- attach/detach: StrictMode's simulated remount detaches (destroying\n // the first engine) and re-attaches (fresh engine + full state replay) ---\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return undefined;\n // Each StrictMode setup (and each real mount) owns exactly one attach\n // result. Its promise may settle after cleanup because engine mounting is\n // asynchronous; a discarded generation must never inspect the newer\n // instance state or call the latest component callbacks.\n let active = true;\n void instance.attach(container).catch((err: unknown) => {\n if (!active) return;\n // A fatal mount fault already reached onError via the 'error' event.\n if (instance.store.getState().status === 'error') return;\n const error = err instanceof Error ? err : new Error(String(err));\n callbacksRef.current.onError?.({ error });\n });\n return () => {\n active = false;\n instance.detach();\n };\n }, [instance]);\n\n // --- destroy on true unmount only. destroy is irreversible, so the\n // cleanup defers it one microtask; a StrictMode remount re-runs the setup\n // synchronously first and cancels it, leaving detach/attach replay to\n // restore the engine. A real unmount has no re-setup, so destroy fires. ---\n const destroyGateRef = useRef({ pendingDestroy: false });\n useEffect(() => {\n const gate = destroyGateRef.current;\n gate.pendingDestroy = false;\n return () => {\n gate.pendingDestroy = true;\n queueMicrotask(() => {\n if (gate.pendingDestroy) instance.destroy();\n });\n };\n }, [instance]);\n\n // --- reduced motion: the binding detects the media preference and\n // reports it via instance.setReducedMotion; accessibility.reducedMotion\n // overrides inside the core. Subscribed for live preference changes. ---\n useEffect(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return undefined;\n }\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n const surface = overlaySurface(instance);\n surface.setReducedMotion?.(mq.matches);\n const onChange = (e: MediaQueryListEvent): void => {\n surface.setReducedMotion?.(e.matches);\n };\n if (typeof mq.addEventListener === 'function') {\n mq.addEventListener('change', onChange);\n return () => {\n mq.removeEventListener('change', onChange);\n };\n }\n // Legacy MediaQueryList (Safari < 14).\n if (typeof mq.addListener === 'function') {\n mq.addListener(onChange);\n return () => {\n mq.removeListener(onChange);\n };\n }\n return undefined;\n }, [instance]);\n\n useImperativeHandle(\n ref,\n (): GraphHandle<N, E> => ({\n fitView: (_opts?: FitViewOptions) => {\n instance.fitView();\n },\n zoomIn: () => {\n instance.zoomIn();\n },\n zoomOut: () => {\n instance.zoomOut();\n },\n setViewport: (v: Partial<ViewportState>, _opts?: { durationMs?: number }) => {\n instance.setViewport(v);\n },\n requestNodeContextMenu: (id: NodeId, screen: readonly [number, number]) => {\n instance.requestNodeContextMenu(id, screen);\n },\n getViewState: (opts?: { includePositions?: false }): GraphViewState =>\n instance.getViewState(opts),\n setViewState: (\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult> => instance.setViewState(raw, opts),\n exportImage: ((format: 'png' | 'svg', opts?: { maxSvgElements?: number; fallback?: 'raster-hybrid' }) =>\n format === 'png' ? instance.exportImage('png') : instance.exportImage('svg', opts)) as GraphHandle<N, E>['exportImage'],\n exportData: (scope?: 'visible' | 'accepted', opts?: { limit?: number }) =>\n instance.exportData(scope, opts),\n exportDataStream: (scope?: 'visible' | 'accepted') => instance.exportDataStream(scope),\n exportLayout: (opts?: { limit?: number }) => instance.exportLayout(opts),\n exportLayoutStream: () => instance.exportLayoutStream(),\n focusNode: (id: NodeId) => {\n instance.focusNode(id);\n },\n emphasizeNode: (id: NodeId | null) => {\n instance.emphasizeNode(id);\n },\n getPerfSnapshot: () => instance.getPerfSnapshot(),\n setSelection: (ids: readonly NodeId[]) => {\n instance.setSelection(ids);\n },\n findPath: (sourceId: NodeId, targetId: NodeId, options?: PathOptions) =>\n instance.findPath(sourceId, targetId, options),\n clearPath: () => {\n instance.clearPath();\n },\n clearSelection: () => {\n instance.clearSelection();\n },\n selectNeighbors: (id?: NodeId) => {\n instance.selectNeighbors(id);\n },\n selectAll: () => {\n instance.selectAll();\n },\n invertSelection: () => {\n instance.invertSelection();\n },\n selectWithinPolygon: (\n polygon: readonly [number, number][],\n opts?: { additive?: boolean },\n ): readonly NodeId[] => instance.selectWithinPolygon(polygon, opts),\n pinNode: (id: NodeId, xy?: readonly [number, number]) => {\n instance.pinNode(id, xy);\n },\n unpinNode: (id: NodeId) => {\n instance.unpinNode(id);\n },\n clearPins: () => {\n instance.clearPins();\n },\n hideNodes: (ids: readonly NodeId[]) => {\n instance.hideNodes(ids);\n },\n showAll: () => {\n instance.showAll();\n },\n isolateSelection: () => {\n instance.isolateSelection();\n },\n resetIsolation: () => {\n instance.resetIsolation();\n },\n expandNode: (id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult> =>\n instance.expandNode(id, opts),\n retractExpansion: (id: NodeId) => {\n instance.retractExpansion(id);\n },\n pinNodes: (ids: readonly NodeId[]) => {\n instance.pinNodes(ids);\n },\n unpinNodes: (ids: readonly NodeId[]) => {\n instance.unpinNodes(ids);\n },\n groupNodes: (spec: GroupSpec) => {\n instance.groupNodes(spec);\n },\n ungroup: (groupId: string) => {\n instance.ungroup(groupId);\n },\n setGroupCollapsed: (groupId: string, collapsed: boolean) => {\n instance.setGroupCollapsed(groupId, collapsed);\n },\n foldNode: (id: NodeId, opts?: { memberIds?: readonly NodeId[] }) => {\n instance.foldNode(id, opts);\n },\n unfoldNode: (id: NodeId) => {\n instance.unfoldNode(id);\n },\n getFold: (id: NodeId): { memberIds: readonly NodeId[] } | null => instance.getFold(id),\n getClusters: (): readonly ResolvedCluster[] => instance.getClusters(),\n selectCluster: (key: string, opts?: { additive?: boolean }) => {\n instance.selectCluster(key, opts);\n },\n beginIngest: (opts: BeginIngestOptions): IngestSession<N, E> => instance.beginIngest(opts),\n removeOverlay: (overlayId: string): { removed: boolean } =>\n instance.removeOverlay(overlayId),\n undo: (): boolean => instance.undo(),\n redo: (): boolean => instance.redo(),\n playTimeline: (key: string, playback?: Partial<TimelinePlayback>) => {\n instance.playTimeline(key, playback);\n },\n pauseTimeline: () => {\n instance.pauseTimeline();\n },\n getCrossfilterSession: (): CrossfilterSession | null => instance.getCrossfilterSession(),\n getScaleInfo: (channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null =>\n instance.getScaleInfo(channel),\n getMetricValue: (metric: MetricName, id: NodeId): number | null =>\n instance.getMetricValue(metric, id),\n getRevisions: () => instance.getRevisions(),\n getDiagnostics: () => instance.getDiagnostics(),\n instance,\n }),\n [instance],\n );\n\n // --- container ARIA surface ---\n const descriptionId = useId();\n const description = props.accessibility?.description;\n\n return (\n <div className={props.className} style={{ ...CONTAINER_BASE_STYLE, ...props.style }}>\n <GraphProvider instance={instance}>\n {/* Engine-owned canvas layer: must never contain React children. */}\n <div\n ref={containerRef}\n data-orbit-canvas=\"\"\n style={CANVAS_STYLE}\n role=\"application\"\n aria-label={props.accessibility?.label ?? 'Graph visualization'}\n aria-describedby={description !== undefined ? descriptionId : undefined}\n />\n {description !== undefined ? (\n <div id={descriptionId} style={visuallyHiddenStyle}>\n {description}\n </div>\n ) : null}\n {/* DOM label lane: candidates via React, positions imperative. */}\n {props.labels?.enabled !== false ? (\n <LabelLayer\n labelClassName={props.labelClassName}\n renderNodeLabel={props.renderNodeLabel}\n clusterLabelClassName={props.clusterLabelClassName}\n renderClusterLabel={props.renderClusterLabel}\n />\n ) : null}\n {/* Lasso overlay: above the canvas, pointer-inert unless Shift-drag. */}\n {props.enableLasso !== false ? <Lasso /> : null}\n {/* live region: store-driven, coalesced, visually hidden. */}\n <LiveRegion announcements={props.accessibility?.announcements} />\n {/* legend slot: positioned wrapper re-rendered on scale/theme\n changes; the host returns custom JSX (or composes <GraphLegend>). */}\n {props.renderLegend !== undefined ? (\n <LegendSlot instance={instance} renderLegend={props.renderLegend} />\n ) : null}\n {props.children}\n </GraphProvider>\n </div>\n );\n}\n\n/** forwardRef erases generics; restore them with a typed call signature. */\ntype GraphComponent = <N = Record<string, unknown>, E = Record<string, unknown>>(\n props: GraphProps<N, E> & RefAttributes<GraphHandle<N, E>>,\n) => ReactElement;\n\nexport const Graph = forwardRef(GraphInner) as GraphComponent;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/Lasso.tsx","../src/LabelLayer.tsx","../src/LiveRegion.tsx","../src/Graph.tsx"],"names":["useState","useRef","useEffect","jsx","useLayoutEffect","prevRef"],"mappings":";;;;;;;;;AA4BA,IAAM,YAAA,GAA8B;AAAA,EAClC,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,WAAA,EAAa,MAAA;AAAA,EACb,UAAA,EAAY;AACd,CAAA;AAEA,IAAM,gBAAA,GAAkC,EAAE,GAAG,YAAA,EAAc,eAAe,MAAA,EAAO;AACjF,IAAM,cAAA,GAAgC;AAAA,EACpC,GAAG,YAAA;AAAA,EACH,aAAA,EAAe,MAAA;AAAA,EACf,MAAA,EAAQ;AACV,CAAA;AAEA,IAAM,SAAA,GAA2B;AAAA,EAC/B,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ,MAAA;AAAA,EACR,aAAA,EAAe,MAAA;AAAA,EACf,QAAA,EAAU;AACZ,CAAA;AAEA,SAAS,WAAW,MAAA,EAA6C;AAC/D,EAAA,IAAI,CAAA,GAAI,CAAA,EAAA,EAAK,MAAA,CAAO,CAAC,CAAA,CAAG,CAAC,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,CAAC,CAAA,CAAG,CAAC,CAAC,CAAA,CAAA;AAC3C,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,OAAO,MAAA,EAAQ,CAAA,EAAA,OAAU,CAAA,GAAA,EAAM,MAAA,CAAO,CAAC,CAAA,CAAG,CAAC,CAAC,CAAA,CAAA,EAAI,OAAO,CAAC,CAAA,CAAG,CAAC,CAAC,CAAA,CAAA;AACjF,EAAA,OAAO,MAAA,CAAO,MAAA,IAAU,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,EAAA,CAAA,GAAO,CAAA;AACzC;AAIA,SAAS,cAAc,EAAA,EAA+B;AACpD,EAAA,IAAI,OAAO,qBAAA,KAA0B,UAAA,EAAY,OAAO,sBAAsB,EAAE,CAAA;AAChF,EAAA,EAAA,EAAG;AACH,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,YAAY,EAAA,EAAyB;AAC5C,EAAA,IAAI,OAAO,IAAA,IAAQ,OAAO,oBAAA,KAAyB,UAAA,uBAAiC,EAAE,CAAA;AACxF;AAGA,SAAS,cAAA,CAAe,IAAiB,SAAA,EAAyB;AAChE,EAAA,IAAI,OAAO,EAAA,CAAG,iBAAA,KAAsB,UAAA,EAAY;AAC9C,IAAA,IAAI;AACF,MAAA,EAAA,CAAG,kBAAkB,SAAS,CAAA;AAAA,IAChC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,SAAS,cAAA,CAAe,IAAiB,SAAA,EAAyB;AAChE,EAAA,IAAI,OAAO,EAAA,CAAG,qBAAA,KAA0B,UAAA,EAAY;AAClD,IAAA,IAAI;AACF,MAAA,EAAA,CAAG,sBAAsB,SAAS,CAAA;AAAA,IACpC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF;AAIO,SAAS,KAAA,GAAsB;AACpC,EAAA,MAAM,QAAA,GAAW,wBAAwB,OAAO,CAAA;AAChD,EAAA,MAAM,UAAA,GAAa,OAA8B,IAAI,CAAA;AACrD,EAAA,MAAM,OAAA,GAAU,OAAyB,IAAI,CAAA;AAC7C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA6C,IAAI,CAAA;AAI/E,EAAA,MAAM,WAAA,GAAc,YAAY,MAAY;AAC1C,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,SAAS,IAAA,EAAM;AACnB,IAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAClB,IAAA,WAAA,CAAY,KAAK,KAAK,CAAA;AACtB,IAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,IAAA,IAAI,EAAA,KAAO,IAAA,EAAM,cAAA,CAAe,EAAA,EAAI,KAAK,SAAS,CAAA;AAClD,IAAA,UAAA,CAAW,IAAI,CAAA;AAAA,EACjB,CAAA,EAAG,EAAE,CAAA;AAIL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,SAAA,GAAY,CAAC,CAAA,KAA2B;AAC5C,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,EAAS,YAAA,CAAa,IAAI,CAAA;AAAA,WAAA,IAC/B,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,WAAA,EAAY;AAAA,IAC3C,CAAA;AACA,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAA2B;AAC1C,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,EAAS,YAAA,CAAa,KAAK,CAAA;AAAA,IAC3C,CAAA;AACA,IAAA,MAAM,MAAA,GAAS,MAAY,YAAA,CAAa,KAAK,CAAA;AAC7C,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC5C,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACtC,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAC/C,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC3C,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAQ,MAAM,CAAA;AACzC,MAAA,WAAA,EAAY;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAA2D;AAC7E,IAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,IAAA,IAAI,OAAO,IAAA,EAAM,OAAO,CAAC,CAAA,CAAE,OAAA,EAAS,EAAE,OAAO,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAO,GAAG,qBAAA,EAAsB;AACtC,IAAA,OAAO,CAAC,EAAE,OAAA,GAAU,IAAA,CAAK,MAAM,CAAA,CAAE,OAAA,GAAU,KAAK,GAAG,CAAA;AAAA,EACrD,CAAA;AAEA,EAAA,MAAM,aAAA,GAAgB,CAAC,IAAA,KAA0B;AAC/C,IAAA,IAAI,IAAA,CAAK,YAAY,IAAA,EAAM;AAC3B,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA;AAC7B,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,IAAA,UAAA,CAAW,CAAC,GAAG,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAA+C;AACpE,IAAA,IAAI,OAAA,CAAQ,OAAA,KAAY,IAAA,IAAQ,CAAA,CAAE,WAAW,CAAA,EAAG;AAGhD,IAAA,IAAI,CAAC,CAAA,CAAE,QAAA,IAAY,CAAC,SAAA,EAAW;AAC/B,IAAA,MAAM,SAAA,GAAY,EAAE,SAAA,IAAa,EAAA;AACjC,IAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,IAAA,IAAI,EAAA,KAAO,IAAA,EAAM,cAAA,CAAe,EAAA,EAAI,SAAS,CAAA;AAC7C,IAAA,MAAM,CAAA,GAAI,WAAW,CAAC,CAAA;AACtB,IAAA,OAAA,CAAQ,OAAA,GAAU,EAAE,SAAA,EAAW,MAAA,EAAQ,CAAC,CAAC,CAAA,EAAG,OAAA,EAAS,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AACvE,IAAA,UAAA,CAAW,CAAC,CAAC,CAAC,CAAA;AACd,IAAA,CAAA,CAAE,cAAA,EAAe;AAAA,EACnB,CAAA;AAEA,EAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAA+C;AACpE,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,SAAS,IAAA,IAAA,CAAS,CAAA,CAAE,SAAA,IAAa,EAAA,MAAQ,KAAK,SAAA,EAAW;AAC7D,IAAA,IAAA,CAAK,OAAA,GAAU,WAAW,CAAC,CAAA;AAC3B,IAAA,IAAI,IAAA,CAAK,UAAU,IAAA,EAAM;AAGzB,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,MAAM,OAAO,MAAY;AACvB,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,MAAA,IAAI,OAAA,CAAQ,YAAY,IAAA,EAAM;AAC9B,MAAA,IAAI,IAAA,CAAK,YAAY,IAAA,EAAM;AACzB,QAAA,aAAA,CAAc,IAAI,CAAA;AAClB,QAAA,IAAA,CAAK,KAAA,GAAQ,cAAc,IAAI,CAAA;AAAA,MACjC;AAAA,IACF,CAAA;AACA,IAAA,IAAA,CAAK,KAAA,GAAQ,cAAc,IAAI,CAAA;AAAA,EACjC,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAA+C;AAClE,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,SAAS,IAAA,IAAA,CAAS,CAAA,CAAE,SAAA,IAAa,EAAA,MAAQ,KAAK,SAAA,EAAW;AAC7D,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,MAAM,UAAU,IAAA,CAAK,MAAA;AACrB,IAAA,MAAM,WAAW,CAAA,CAAE,OAAA;AACnB,IAAA,WAAA,EAAY;AACZ,IAAA,IAAI,OAAA,CAAQ,UAAU,CAAA,EAAG,QAAA,CAAS,oBAAoB,OAAA,EAAS,EAAE,UAAU,CAAA;AAAA,EAC7E,CAAA;AAEA,EAAA,MAAM,eAAA,GAAkB,CAAC,CAAA,KAA+C;AACtE,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,SAAS,IAAA,IAAA,CAAS,CAAA,CAAE,SAAA,IAAa,EAAA,MAAQ,KAAK,SAAA,EAAW;AAC7D,IAAA,WAAA,EAAY;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,MAAA,GAAS,aAAa,OAAA,KAAY,IAAA;AACxC,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,kBAAA,EAAiB,EAAA;AAAA,MACjB,KAAA,EAAO,SAAS,cAAA,GAAiB,gBAAA;AAAA,MACjC,aAAA;AAAA,MACA,aAAA;AAAA,MACA,WAAA;AAAA,MACA,eAAA;AAAA,MAEC,QAAA,EAAA,OAAA,KAAY,IAAA,IAAQ,OAAA,CAAQ,MAAA,GAAS,CAAA,uBACnC,KAAA,EAAA,EAAI,0BAAA,EAAyB,EAAA,EAAG,KAAA,EAAO,SAAA,EACtC,QAAA,kBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,CAAA,EAAG,WAAW,OAAO,CAAA;AAAA,UACrB,IAAA,EAAK,0BAAA;AAAA,UACL,MAAA,EAAO,yBAAA;AAAA,UACP,WAAA,EAAa,GAAA;AAAA,UACb,eAAA,EAAgB;AAAA;AAAA,SAEpB,CAAA,GACE;AAAA;AAAA,GACN;AAEJ;AC5KA,SAAS,aAAa,CAAA,EAA2B;AAC/C,EAAA,OAAO,CAAA,CAAE,SAAS,SAAA,GAAY,CAAA,SAAA,EAAgB,EAAE,EAAE,CAAA,CAAA,GAAK,CAAA,MAAA,EAAa,CAAA,CAAE,EAAE,CAAA,CAAA;AAC1E;AAEA,IAAM,WAAA,GAA6B,EAAE,GAAG,iBAAA,EAAmB,UAAU,QAAA,EAAS;AAE9E,IAAM,gBAAA,GAAkC;AAAA,EACtC,QAAA,EAAU,UAAA;AAAA,EACV,GAAA,EAAK,CAAA;AAAA,EACL,IAAA,EAAM,CAAA;AAAA,EACN,aAAA,EAAe,MAAA;AAAA,EACf,MAAA,EAAQ,SAAA;AAAA,EACR,UAAA,EAAY,QAAA;AAAA,EACZ,UAAA,EAAY,MAAA;AAAA,EACZ,UAAA,EAAY;AACd,CAAA;AAEA,SAAS,WAAA,CAAY,GAAW,CAAA,EAAmB;AACjD,EAAA,OAAO,CAAA,YAAA,EAAe,CAAC,CAAA,IAAA,EAAO,CAAC,CAAA,MAAA,CAAA;AACjC;AAIO,SAAS,WAAW,KAAA,EAAsC;AAC/D,EAAA,MAAM,QAAA,GAAW,wBAAwB,YAAY,CAAA;AACrD,EAAA,MAAM,MAAA,GAAS,cAAA,CAAe,QAAQ,CAAA,CAAE,MAAA;AAExC,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,QAAAA,CAAoC,EAAE,CAAA;AAE1E,EAAA,MAAM,WAAA,GAAcC,MAAAA,iBAAO,IAAI,GAAA,EAA6B,CAAA;AAG5D,EAAA,MAAM,YAAA,GAAeA,MAAAA,iBAAO,IAAI,GAAA,EAAwC,CAAA;AACxE,EAAA,MAAM,QAAA,GAAWA,OAA8B,IAAI,CAAA;AAGnD,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AACjC,IAAA,OAAO,MAAA,CAAO,mBAAA,CAAoB,CAAC,IAAA,KAAS;AAC1C,MAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,MAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,MAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,QAAA,MAAM,GAAA,GAAM,aAAa,CAAC,CAAA;AAC1B,QAAA,IAAA,CAAK,IAAI,GAAG,CAAA;AACZ,QAAA,SAAA,CAAU,IAAI,GAAA,EAAK,CAAC,EAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,MAC/B;AACA,MAAA,KAAA,MAAW,OAAO,CAAC,GAAG,SAAA,CAAU,IAAA,EAAM,CAAA,EAAG;AACvC,QAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG,SAAA,CAAU,OAAO,GAAG,CAAA;AAAA,MAC1C;AAGA,MAAA,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA,MAAO,EAAE,GAAG,CAAA,GAAI,CAAC,CAAA;AAAA,IAC3C,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AACjC,IAAA,OAAO,MAAA,CAAO,kBAAA,CAAmB,CAAC,IAAA,KAAS;AACzC,MAAA,MAAM,WAAW,WAAA,CAAY,OAAA;AAC7B,MAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,MAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AACpB,QAAA,MAAM,GAAA,GAAM,aAAa,CAAC,CAAA;AAC1B,QAAA,SAAA,CAAU,IAAI,GAAA,EAAK,CAAC,EAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAC7B,QAAA,MAAM,EAAA,GAAK,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AAC3B,QAAA,IAAI,EAAA,KAAO,QAAW,EAAA,CAAG,KAAA,CAAM,YAAY,WAAA,CAAY,CAAA,CAAE,CAAA,EAAG,CAAA,CAAE,CAAC,CAAA;AAAA,MACjE;AAAA,IACF,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAIX,EAAA,MAAM,WAAA,GAAc,CAAC,EAAA,EAAY,QAAA,KAA4B;AAC3D,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,GAAA,GAAM,QAAA,CAAS,KAAA,CAAM,QAAA,GAAW,SAAA,CAAU,OAAA;AAChD,MAAA,QAAA,CAAS,aAAa,GAAA,CAAI,QAAA,CAAS,EAAE,CAAA,GAAI,IAAI,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,EAAE,CAAA,GAAI,CAAC,GAAG,GAAA,EAAK,EAAE,CAAC,CAAA;AACnF,MAAA;AAAA,IACF;AACA,IAAA,QAAA,CAAS,YAAA,CAAa,CAAC,EAAE,CAAC,CAAA;AAAA,EAC5B,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,EAAA,EAAY,CAAA,KAA6C;AAC7E,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,WAAA,CAAY,EAAA,EAAI,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,QAAQ,CAAA;AAAA,EACzC,CAAA;AAIA,EAAA,MAAM,mBAAA,GAAsB,CAAC,GAAA,EAAa,CAAA,KAA6C;AACrF,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,QAAA,CAAS,aAAA,CAAc,KAAK,EAAE,QAAA,EAAU,EAAE,OAAA,IAAW,CAAA,CAAE,UAAU,CAAA;AAAA,EACnE,CAAA;AAEA,EAAA,MAAM,cAAA,GAAiB,CACrB,CAAA,EACA,CAAA,KACS;AAOT,IAAA,IACE,CAAA,CAAE,IAAA,KAAS,SAAA,KACV,CAAA,CAAE,GAAA,KAAQ,iBAAkB,CAAA,CAAE,GAAA,KAAQ,KAAA,IAAS,CAAA,CAAE,QAAA,CAAA,EAClD;AACA,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,MAAM,SAAA,GAAY,QAAA,CAAS,OAAA,EAAS,qBAAA,EAAsB;AAC1D,MAAA,IAAI,cAAc,MAAA,EAAW;AAC7B,MAAA,MAAM,SAAA,GAAY,CAAA,CAAE,aAAA,CAAc,qBAAA,EAAsB;AACxD,MAAA,QAAA,CAAS,sBAAA,CAAuB,EAAE,EAAA,EAAI;AAAA,QACpC,SAAA,CAAU,IAAA,GAAO,SAAA,CAAU,KAAA,GAAQ,IAAI,SAAA,CAAU,IAAA;AAAA,QACjD,SAAA,CAAU,GAAA,GAAM,SAAA,CAAU,MAAA,GAAS,IAAI,SAAA,CAAU;AAAA,OAClD,CAAA;AACD,MAAA;AAAA,IACF;AAKA,IAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,aAAA,EAAe;AAClC,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,EAAK;AACxC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,MAAM,QAAA,GAAW,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,QAAA;AAChC,IAAA,IAAI,CAAA,CAAE,SAAS,SAAA,EAAW,QAAA,CAAS,cAAc,CAAA,CAAE,EAAA,EAAI,EAAE,QAAA,EAAU,CAAA;AAAA,SAC9D,WAAA,CAAY,CAAA,CAAE,EAAA,EAAI,QAAQ,CAAA;AAAA,EACjC,CAAA;AAQA,EAAA,MAAM,kBAAA,GAAqB,CAAC,EAAA,EAAY,CAAA,KAA6C;AACnF,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,OAAA,EAAS,qBAAA,EAAsB;AACrD,IAAA,IAAI,SAAS,MAAA,EAAW;AACxB,IAAA,QAAA,CAAS,sBAAA,CAAuB,EAAA,EAAI,CAAC,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,IAAA,EAAM,CAAA,CAAE,OAAA,GAAU,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,EACnF,CAAA;AAEA,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,KACtB,QAAA,CAAS,aAAY,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,GAAA,KAAQ,GAAG,CAAA,EAAG,aAAa,EAAC;AAEnE,EAAA,uBACEC,GAAAA,CAAC,KAAA,EAAA,EAAI,wBAAA,EAAuB,EAAA,EAAG,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,WAAA,EAClD,QAAA,EAAA,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,KAAM;AACrB,IAAA,MAAM,GAAA,GAAM,aAAa,CAAC,CAAA;AAC1B,IAAA,MAAM,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,IAAK,CAAC,CAAA,CAAE,CAAA,EAAG,EAAE,CAAC,CAAA;AACzD,IAAA,MAAM,SAAA,GAAY,EAAE,IAAA,KAAS,SAAA;AAC7B,IAAA,MAAM,SAAA,GAAY,SAAA,GACd,CAAC,KAAA,CAAM,gBAAgB,KAAA,CAAM,qBAAqB,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,IAC5E,SACA,KAAA,CAAM,cAAA;AACV,IAAA,MAAM,oBAAoB,SAAA,GACtB,KAAA,CAAM,kBAAA,KAAuB,MAAA,GAC7B,MAAM,eAAA,KAAoB,MAAA;AAC9B,IAAA,uBACEA,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QAEE,GAAI,SAAA,GAAY,EAAE,0BAAA,EAA4B,CAAA,CAAE,IAAG,GAAI,EAAE,kBAAA,EAAoB,CAAA,CAAE,EAAA,EAAG;AAAA,QACnF,SAAA;AAAA,QACC,GAAI,CAAC,iBAAA,GACF;AAAA,UACE,IAAA,EAAM,QAAA;AAAA,UACN,QAAA,EAAU,CAAA;AAAA,UACV,cAAc,CAAA,CAAE,IAAA;AAAA,UAChB,GAAI,CAAC,SAAA,GAAY,EAAE,eAAA,EAAiB,MAAA,KAAoB;AAAC,YAE3D,EAAC;AAAA,QACL,KAAA,EAAO,EAAE,GAAG,gBAAA,EAAkB,WAAW,WAAA,CAAY,CAAA,EAAG,CAAC,CAAA,EAAE;AAAA,QAC3D,GAAA,EAAK,CAAC,EAAA,KAAO;AACX,UAAA,IAAI,EAAA,KAAO,IAAA,EAAM,WAAA,CAAY,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,eAC1C,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,EAAE,CAAA;AAAA,QACtC,CAAA;AAAA,QACA,OAAA,EAAS,CAAC,CAAA,KAAO,SAAA,GAAY,mBAAA,CAAoB,CAAA,CAAE,EAAA,EAAI,CAAC,CAAA,GAAI,YAAA,CAAa,CAAA,CAAE,EAAA,EAAI,CAAC,CAAA;AAAA,QAChF,SAAA,EAAW,CAAC,CAAA,KAAM,cAAA,CAAe,GAAG,CAAC,CAAA;AAAA,QAGrC,aAAA,EAAe,YAAY,MAAA,GAAY,CAAC,MAAM,kBAAA,CAAmB,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,QAIvE,QAAA,EAAA,SAAA,GACG,KAAA,CAAM,kBAAA,KAAuB,MAAA,GAC3B,MAAM,kBAAA,CAAmB;AAAA,UACvB,YAAY,CAAA,CAAE,EAAA;AAAA,UACd,MAAM,CAAA,CAAE,IAAA;AAAA,UACR,SAAA,EAAW,cAAA,CAAe,CAAA,CAAE,EAAE;AAAA,SAC/B,IACD,CAAA,CAAE,IAAA,GACJ,MAAM,eAAA,KAAoB,MAAA,GACxB,MAAM,eAAA,CAAgB;AAAA,UACpB,IAAA,EAAM,SAAS,OAAA,CAAQ,CAAA,CAAE,EAAE,CAAA,IAAK,EAAE,EAAA,EAAI,CAAA,CAAE,EAAA,EAAG;AAAA,UAC3C,MAAM,CAAA,CAAE;AAAA,SACT,IACD,CAAA,CAAE;AAAA,OAAA;AAAA,MArCH;AAAA,KAsCP;AAAA,EAEJ,CAAC,CAAA,EACH,CAAA;AAEJ;AC5OO,IAAM,oBAAA,GAAuB,GAAA;AAOpC,SAAS,UAAU,CAAA,EAA4B;AAC7C,EAAA,MAAM,WAAW,CAAA,CAAE,SAAA,CAAU,QAAQ,MAAA,GAAS,CAAA,CAAE,UAAU,OAAA,CAAQ,MAAA;AAClE,EAAA,OAAO,CAAA,MAAA,EAAS,CAAA,CAAE,MAAM,CAAA,EAAA,EAAK,CAAA,CAAE,SAAS,CAAA,QAAA,EAAW,CAAA,CAAE,SAAS,CAAA,QAAA,EAAW,QAAQ,CAAA,UAAA,CAAA;AACnF;AAIO,SAAS,WAAW,KAAA,EAAsC;AAC/D,EAAA,MAAM,QAAA,GAAW,wBAAwB,YAAY,CAAA;AACrD,EAAA,MAAM,OAAA,GAAU,MAAM,aAAA,KAAkB,KAAA;AACxC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIH,SAAS,EAAE,CAAA;AAGzC,EAAA,MAAM,gBAAA,GAAmBC,OAAsB,IAAI,CAAA;AACnD,EAAA,MAAM,QAAA,GAAWA,OAA6C,IAAI,CAAA;AAElE,EAAAC,UAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAS,OAAO,MAAA;AACrB,IAAA,MAAM,QAAQ,QAAA,CAAS,KAAA;AACvB,IAAA,gBAAA,CAAiB,OAAA,GAAU,SAAA,CAAU,KAAA,CAAM,QAAA,EAAU,CAAA;AAErD,IAAA,MAAM,OAAO,MAAY;AACvB,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,KAAA,CAAM,QAAA,EAAU,CAAA;AACvC,MAAA,IAAI,IAAA,KAAS,iBAAiB,OAAA,EAAS;AACvC,MAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAC3B,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,SAAA,CAAU,MAAM;AACxC,MAAA,IAAI,QAAA,CAAS,YAAY,IAAA,EAAM;AAC/B,MAAA,IAAI,UAAU,KAAA,CAAM,QAAA,EAAU,CAAA,KAAM,iBAAiB,OAAA,EAAS;AAC9D,MAAA,QAAA,CAAS,OAAA,GAAU,UAAA,CAAW,IAAA,EAAM,oBAAoB,CAAA;AAAA,IAC1D,CAAC,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,WAAA,EAAY;AACZ,MAAA,IAAI,QAAA,CAAS,YAAY,IAAA,EAAM;AAC7B,QAAA,YAAA,CAAa,SAAS,OAAO,CAAA;AAC7B,QAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,MACrB;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,OAAO,CAAC,CAAA;AAEtB,EAAA,uBACEC,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,wBAAA,EAAuB,EAAA;AAAA,MACvB,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,QAAA;AAAA,MACV,aAAA,EAAY,MAAA;AAAA,MACZ,KAAA,EAAO,mBAAA;AAAA,MAEN,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;ACgYA,SAAS,WAAW,KAAA,EAAoC;AACtD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,MAAM,OAAO,MAAA;AACxD,EAAA,MAAM,OAAQ,KAAA,CAA6B,IAAA;AAC3C,EAAA,IAAI,SAAS,YAAA,IAAgB,IAAA,KAAS,aAAA,IAAiB,IAAA,KAAS,aAAa,OAAO,MAAA;AACpF,EAAA,OAAO,kBAAkB,KAAuB,CAAA;AAClD;AAIA,SAAS,mBAAA,CACP,SAAA,EACA,OAAA,EACA,SAAA,EACA,OAAA,EACS;AACT,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,OAAA,KAAY,MAAA,SAAkB,OAAA,KAAY,OAAA;AACvE,EAAA,OAAO,SAAA,KAAc,SAAA;AACvB;AAIA,SAAS,cAAc,IAAA,EAAmE;AACxF,EAAA,IAAI,IAAA,KAAS,QAAW,OAAO,GAAA;AAC/B,EAAA,IAAI,OAAO,IAAA,KAAS,UAAA,EAAY,OAAO,IAAA;AACvC,EAAA,OAAO,IAAA,CAAK,UAAU,IAAI,CAAA;AAC5B;AAEA,SAAS,YAAkB,IAAA,EAAuC;AAChE,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,MAAA;AAC1B,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,IAAA,IAAQ,MAAM,CAAA,CAAA,EAAI,aAAA,CAAc,IAAA,CAAK,KAAK,CAAC,CAAA,CAAA,EAAI,aAAA,CAAc,IAAA,CAAK,KAAK,CAAC,CAAA,CAAA;AACzF;AAIA,SAAS,cAAA,CACP,IAAA,EACA,OAAA,EACA,IAAA,EACA,OAAA,EACS;AACT,EAAA,IAAI,OAAA,KAAY,MAAA,IAAa,OAAA,KAAY,OAAA,EAAS,OAAO,KAAA;AACzD,EAAA,IAAI,IAAA,KAAS,IAAA,EAAM,OAAO,IAAA,KAAS,IAAA;AACnC,EAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,MAAA,EAAW,OAAO,KAAA;AAChD,EAAA,IAAI,OAAO,KAAK,KAAA,KAAU,UAAA,IAAc,KAAK,KAAA,KAAU,IAAA,CAAK,OAAO,OAAO,KAAA;AAC1E,EAAA,IAAI,OAAO,KAAK,KAAA,KAAU,UAAA,IAAc,KAAK,KAAA,KAAU,IAAA,CAAK,OAAO,OAAO,KAAA;AAC1E,EAAA,OAAO,IAAA;AACT;AAgCA,SAAS,YAAA,CAAa,GAAsB,CAAA,EAA+B;AACzE,EAAA,IAAI,CAAA,KAAM,GAAG,OAAO,IAAA;AACpB,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ,OAAO,KAAA;AAClC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACjC,IAAA,IAAI,EAAE,CAAC,CAAA,KAAM,CAAA,CAAE,CAAC,GAAG,OAAO,KAAA;AAAA,EAC5B;AACA,EAAA,OAAO,IAAA;AACT;AAEA,IAAM,oBAAA,GAAsC;AAAA,EAC1C,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ;AACV,CAAA;AAIA,IAAM,YAAA,GAA8B,EAAE,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA,EAAE;AAIrE,IAAM,iBAAA,GAAmC;AAAA,EACvC,QAAA,EAAU,UAAA;AAAA,EACV,IAAA,EAAM,EAAA;AAAA,EACN,MAAA,EAAQ;AACV,CAAA;AAKA,SAAS,WAAiB,KAAA,EAGT;AACf,EAAA,MAAM,EAAE,UAAS,GAAI,KAAA;AACrB,EAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,yBAAyB,QAAQ,CAAA;AAC5D,EAAA,MAAM,IAAA,GAAO,OAAA;AAAA,IACX,OAAO;AAAA,MACL,SAAA,EAAW,QAAA,CAAS,YAAA,CAAa,WAAW,CAAA;AAAA,MAC5C,QAAA,EAAU,QAAA,CAAS,YAAA,CAAa,UAAU,CAAA;AAAA,MAC1C;AAAA,KACF,CAAA;AAAA;AAAA;AAAA,IAGA,CAAC,QAAA,EAAU,KAAA,EAAO,OAAO;AAAA,GAC3B;AACA,EAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAI,wBAAA,EAAuB,EAAA,EAAG,OAAO,iBAAA,EACnC,QAAA,EAAA,KAAA,CAAM,YAAA,CAAa,IAAI,CAAA,EAC1B,CAAA;AAEJ;AAEA,SAAS,UAAA,CACP,OACA,GAAA,EACc;AAId,EAAA,MAAM,CAAC,QAAQ,CAAA,GAAIH,QAAAA,CAA8B,MAAM;AACrD,IAAA,MAAM,OAAA,GAA4C,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO;AACzE,IAAA,IAAI,KAAA,CAAM,uBAAuB,MAAA,EAAW;AAC1C,MAAA,OAAA,CAAQ,qBAAqB,KAAA,CAAM,kBAAA;AAAA,IACrC;AACA,IAAA,IAAI,KAAA,CAAM,WAAA,KAAgB,MAAA,EAAW,OAAA,CAAQ,cAAc,KAAA,CAAM,WAAA;AACjE,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,EAAW,OAAA,CAAQ,SAAS,KAAA,CAAM,MAAA;AACvD,IAAA,IAAI,KAAA,CAAM,SAAA,KAAc,MAAA,EAAW,OAAA,CAAQ,YAAY,KAAA,CAAM,SAAA;AAC7D,IAAA,IAAI,KAAA,CAAM,aAAA,KAAkB,MAAA,EAAW,OAAA,CAAQ,gBAAgB,KAAA,CAAM,aAAA;AACrE,IAAA,IAAI,KAAA,CAAM,QAAA,KAAa,MAAA,EAAW,OAAA,CAAQ,WAAW,KAAA,CAAM,QAAA;AAC3D,IAAA,OAAO,oBAA0B,OAAO,CAAA;AAAA,EAC1C,CAAC,CAAA;AACD,EAAA,MAAM,oBAAA,GAAuBC,OAAO,KAAK,CAAA;AACzC,EAAA,MAAM,eAAA,GAAkBA,OAAO,KAAK,CAAA;AACpC,EAAA,MAAM,gBAAA,GAAmBA,MAAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AAC5C,EAAA,IAAI,MAAM,MAAA,KAAW,gBAAA,CAAiB,OAAA,IAAW,CAAC,gBAAgB,OAAA,EAAS;AACzE,IAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAC1B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,kBAAA,GAAqBA,OAAO,KAAK,CAAA;AACvC,EAAA,MAAM,mBAAA,GAAsBA,MAAAA,CAAO,KAAA,CAAM,SAAS,CAAA;AAClD,EAAA,IAAI,MAAM,SAAA,KAAc,mBAAA,CAAoB,OAAA,IAAW,CAAC,mBAAmB,OAAA,EAAS;AAClF,IAAA,kBAAA,CAAmB,OAAA,GAAU,IAAA;AAC7B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,YAAA,GAAeA,OAA8B,IAAI,CAAA;AAIvD,EAAA,MAAM,eAAeA,MAAAA,CAA6B;AAAA,IAChD,WAAA,EAAa,MAAA;AAAA,IACb,iBAAA,EAAmB,MAAA;AAAA,IACnB,WAAA,EAAa,MAAA;AAAA,IACb,WAAA,EAAa,MAAA;AAAA,IACb,WAAA,EAAa,MAAA;AAAA,IACb,eAAA,EAAiB,MAAA;AAAA,IACjB,aAAA,EAAe,MAAA;AAAA,IACf,iBAAA,EAAmB,MAAA;AAAA,IACnB,SAAA,EAAW,MAAA;AAAA,IACX,YAAA,EAAc,MAAA;AAAA,IACd,YAAA,EAAc,MAAA;AAAA,IACd,eAAA,EAAiB,MAAA;AAAA,IACjB,cAAA,EAAgB,MAAA;AAAA,IAChB,kBAAA,EAAoB,MAAA;AAAA,IACpB,mBAAA,EAAqB,MAAA;AAAA,IACrB,cAAA,EAAgB,MAAA;AAAA,IAChB,gBAAA,EAAkB,MAAA;AAAA,IAClB,OAAA,EAAS,MAAA;AAAA,IACT,OAAA,EAAS;AAAA,GACV,CAAA;AACD,EAAAG,gBAAgB,MAAM;AACpB,IAAA,YAAA,CAAa,OAAA,GAAU;AAAA,MACrB,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,mBAAmB,KAAA,CAAM,iBAAA;AAAA,MACzB,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,iBAAiB,KAAA,CAAM,eAAA;AAAA,MACvB,eAAe,KAAA,CAAM,aAAA;AAAA,MACrB,mBAAmB,KAAA,CAAM,iBAAA;AAAA,MACzB,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,cAAc,KAAA,CAAM,YAAA;AAAA,MACpB,cAAc,KAAA,CAAM,YAAA;AAAA,MACpB,iBAAiB,KAAA,CAAM,eAAA;AAAA,MACvB,gBAAgB,KAAA,CAAM,cAAA;AAAA,MACtB,oBAAoB,KAAA,CAAM,kBAAA;AAAA,MAC1B,qBAAqB,KAAA,CAAM,mBAAA;AAAA,MAC3B,gBAAgB,KAAA,CAAM,cAAA;AAAA,MACtB,kBAAkB,KAAA,CAAM,gBAAA;AAAA,MACxB,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,SAAS,KAAA,CAAM;AAAA,KACjB;AAAA,EACF,CAAC,CAAA;AAWD,EAAAA,gBAAgB,MAAM;AACpB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,QAAA,CAAS,GAAG,WAAA,EAAa,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,WAAA,GAAc,CAAC,CAAC,CAAA;AAAA,MACrE,SAAS,EAAA,CAAG,iBAAA,EAAmB,MAAM,YAAA,CAAa,OAAA,CAAQ,qBAAqB,CAAA;AAAA,MAC/E,QAAA,CAAS,GAAG,WAAA,EAAa,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,WAAA,GAAc,CAAC,CAAC,CAAA;AAAA,MACrE,QAAA,CAAS,GAAG,WAAA,EAAa,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,WAAA,GAAc,CAAC,CAAC,CAAA;AAAA,MACrE,QAAA,CAAS,GAAG,WAAA,EAAa,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,WAAA,GAAc,CAAC,CAAC,CAAA;AAAA,MACrE,QAAA,CAAS,GAAG,eAAA,EAAiB,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,eAAA,GAAkB,CAAC,CAAC,CAAA;AAAA,MAC7E,QAAA,CAAS,GAAG,aAAA,EAAe,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,aAAA,GAAgB,CAAC,CAAC,CAAA;AAAA,MACzE,QAAA,CAAS,GAAG,iBAAA,EAAmB,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,iBAAA,GAAoB,CAAC,CAAC,CAAA;AAAA,MACjF,QAAA,CAAS,GAAG,SAAA,EAAW,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,SAAA,GAAY,CAAC,CAAC,CAAA;AAAA,MACjE,QAAA,CAAS,GAAG,YAAA,EAAc,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,YAAA,GAAe,CAAC,CAAC,CAAA;AAAA,MACvE,QAAA,CAAS,GAAG,YAAA,EAAc,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,YAAA,GAAe,CAAC,CAAC,CAAA;AAAA,MACvE,QAAA,CAAS,GAAG,eAAA,EAAiB,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,eAAA,GAAkB,CAAC,CAAC,CAAA;AAAA,MAC7E,QAAA,CAAS,EAAA,CAAG,cAAA,EAAgB,CAAC,CAAA,KAAM,aAAa,OAAA,CAAQ,cAAA,GAAiB,CAAA,CAAE,MAAM,CAAC,CAAA;AAAA,MAClF,QAAA,CAAS,GAAG,kBAAA,EAAoB,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,kBAAA,GAAqB,CAAC,CAAC,CAAA;AAAA,MACnF,QAAA,CAAS,EAAA;AAAA,QAAG,mBAAA;AAAA,QAAqB,CAAC,MAChC,YAAA,CAAa,OAAA,CAAQ,sBAAsB,CAAA,CAAE,MAAA,EAAQ,EAAE,OAAO;AAAA,OAChE;AAAA,MACA,QAAA,CAAS,EAAA,CAAG,cAAA,EAAgB,CAAC,CAAA,KAAM,aAAa,OAAA,CAAQ,cAAA,GAAiB,CAAA,CAAE,aAAa,CAAC,CAAA;AAAA,MACzF,QAAA,CAAS,GAAG,gBAAA,EAAkB,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,gBAAA,GAAmB,CAAC,CAAC,CAAA;AAAA,MAC/E,SAAS,EAAA,CAAG,OAAA,EAAS,MAAM,YAAA,CAAa,OAAA,CAAQ,WAAW,CAAA;AAAA,MAC3D,QAAA,CAAS,GAAG,OAAA,EAAS,CAAC,MAAM,YAAA,CAAa,OAAA,CAAQ,OAAA,GAAU,CAAC,CAAC;AAAA,KAC/D;AACA,IAAA,OAAO,MAAM;AACX,MAAA,KAAA,MAAW,GAAA,IAAO,MAAM,GAAA,EAAI;AAAA,IAC9B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAIb,EAAA,MAAM,OAAA,GAAUH,OAAoC,IAAI,CAAA;AACxD,EAAAG,gBAAgB,MAAM;AACpB,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,MAAM,SAAgC,EAAC;AACvC,IAAA,IAAI,KAAA,GAAQ,KAAA;AAEZ,IAAA,IAAI,KAAA,CAAM,SAAS,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,IAAA,KAAS,MAAM,IAAA,CAAA,EAAO;AAC3E,MAAA,MAAA,CAAO,OAAO,KAAA,CAAM,IAAA;AACpB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAIA,IAAA,MAAM,YAAA,GAAe,UAAA,CAAW,KAAA,CAAM,SAAS,CAAA;AAC/C,IAAA,IACE,KAAA,CAAM,SAAA,KAAc,MAAA,KACnB,IAAA,KAAS,IAAA,IACR,mBAAA,CAAoB,IAAA,CAAK,SAAA,EAAW,IAAA,CAAK,YAAA,EAAc,KAAA,CAAM,SAAA,EAAW,YAAY,CAAA,CAAA,EACtF;AACA,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,MAAM,WAAA,GAAc,UAAA,CAAW,KAAA,CAAM,QAAQ,CAAA;AAC7C,IAAA,IACE,KAAA,CAAM,QAAA,KAAa,MAAA,KAClB,IAAA,KAAS,IAAA,IACR,mBAAA,CAAoB,IAAA,CAAK,QAAA,EAAU,IAAA,CAAK,WAAA,EAAa,KAAA,CAAM,QAAA,EAAU,WAAW,CAAA,CAAA,EAClF;AACA,MAAA,MAAA,CAAO,WAAW,KAAA,CAAM,QAAA;AACxB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,KAAA,CAAM,cAAc,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,CAAA,EAAY;AAC1F,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,KAAA,CAAM,cAAc,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,CAAA,EAAY;AAC1F,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,MAAA,KAAW,MAAM,MAAA,CAAA,EAAS;AACjF,MAAA,MAAA,CAAO,SAAS,KAAA,CAAM,MAAA;AACtB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IACE,KAAA,CAAM,eAAe,MAAA,KACpB,IAAA,KAAS,QAAQ,IAAA,CAAK,UAAA,KAAe,MAAM,UAAA,CAAA,EAC5C;AACA,MAAA,MAAA,CAAO,aAAa,KAAA,CAAM,UAAA;AAC1B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAGA,IAAA,IAAI,SAAA;AACJ,IAAA,IAAI,KAAA,CAAM,UAAU,MAAA,EAAW;AAC7B,MAAA,SAAA,GAAY,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,KAAK,CAAA;AACtC,MAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,CAAK,SAAA,KAAc,SAAA,EAAW;AACjD,QAAA,MAAA,CAAO,QAAQ,KAAA,CAAM,KAAA;AACrB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AAEA,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,OAAA,KAAY,MAAM,OAAA,CAAA,EAAU;AACpF,MAAA,MAAA,CAAO,UAAU,KAAA,CAAM,OAAA;AACvB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAEA,IAAA,IAAI,KAAA,CAAM,cAAc,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,CAAA,EAAY;AAC1F,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV,CAAA,MAAA,IAAW,MAAM,SAAA,KAAc,MAAA,IAAa,SAAS,IAAA,IAAQ,IAAA,CAAK,cAAc,MAAA,EAAW;AAGzF,MAAA,MAAA,CAAO,SAAA,GAAY,IAAA;AACnB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAEA,IAAA,IAAI,KAAA,CAAM,eAAe,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,UAAA,KAAe,MAAM,UAAA,CAAA,EAAa;AAC7F,MAAA,MAAA,CAAO,aAAa,KAAA,CAAM,UAAA;AAC1B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,KAAA,CAAM,cAAc,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,SAAA,KAAc,MAAM,SAAA,CAAA,EAAY;AAC1F,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IACE,KAAA,CAAM,iBAAiB,MAAA,KACtB,IAAA,KAAS,QAAQ,IAAA,CAAK,YAAA,KAAiB,MAAM,YAAA,CAAA,EAC9C;AACA,MAAA,MAAA,CAAO,eAAe,KAAA,CAAM,YAAA;AAC5B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV,CAAA,MAAA,IACE,MAAM,YAAA,KAAiB,MAAA,IACvB,SAAS,IAAA,IACT,IAAA,CAAK,iBAAiB,MAAA,EACtB;AAGA,MAAA,MAAA,CAAO,YAAA,GAAe,IAAA;AACtB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IACE,KAAA,CAAM,SAAA,KAAc,MAAA,KACnB,IAAA,KAAS,QACR,IAAA,CAAK,SAAA,KAAc,MAAA,IACnB,CAAC,YAAA,CAAa,IAAA,CAAK,SAAA,EAAW,KAAA,CAAM,SAAS,CAAA,CAAA,EAC/C;AACA,MAAA,MAAA,CAAO,YAAY,KAAA,CAAM,SAAA;AACzB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,MAAA,KAAW,MAAM,MAAA,CAAA,EAAS;AACjF,MAAA,MAAA,CAAO,SAAS,KAAA,CAAM,MAAA;AACtB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IACE,KAAA,CAAM,kBAAkB,MAAA,KACvB,IAAA,KAAS,QAAQ,IAAA,CAAK,aAAA,KAAkB,MAAM,aAAA,CAAA,EAC/C;AACA,MAAA,MAAA,CAAO,gBAAgB,KAAA,CAAM,aAAA;AAC7B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAGA,IAAA,IAAI,YAAA;AACJ,IAAA,IAAI,KAAA,CAAM,aAAa,MAAA,EAAW;AAChC,MAAA,YAAA,GAAe,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,QAAQ,CAAA;AAC5C,MAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,IAAA,CAAK,YAAA,KAAiB,YAAA,EAAc;AACvD,QAAA,MAAA,CAAO,WAAW,KAAA,CAAM,QAAA;AACxB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AAGA,IAAA,IAAI,SAAA;AACJ,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,EAAW;AAC9B,MAAA,SAAA,GAAY,WAAA,CAAY,MAAM,MAAM,CAAA;AACpC,MAAA,IACE,IAAA,KAAS,IAAA,IACT,CAAC,cAAA,CAAe,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,SAAA,EAAW,KAAA,CAAM,MAAA,EAAQ,SAAS,CAAA,EACpE;AACA,QAAA,MAAA,CAAO,SAAS,KAAA,CAAM,MAAA;AACtB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AAGA,IAAA,IACE,KAAA,CAAM,gBAAgB,MAAA,KACrB,IAAA,KAAS,QAAQ,IAAA,CAAK,WAAA,KAAgB,MAAM,WAAA,CAAA,EAC7C;AACA,MAAA,MAAA,CAAO,cAAc,KAAA,CAAM,WAAA;AAC3B,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAIA,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,EAAW;AAC/B,MAAA,MAAMC,QAAAA,GAAU,IAAA,KAAS,IAAA,GAAO,MAAA,GAAY,IAAA,CAAK,OAAA;AACjD,MAAA,IAAIA,QAAAA,KAAY,UAAa,aAAA,CAAc,KAAA,CAAM,OAAO,CAAA,KAAM,aAAA,CAAcA,QAAO,CAAA,EAAG;AACpF,QAAA,MAAA,CAAO,UAAU,KAAA,CAAM,OAAA;AACvB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,WAAW,MAAA,EAAW;AAC9B,MAAA,MAAM,UAAA,GAAa,IAAA,KAAS,IAAA,GAAO,MAAA,GAAY,IAAA,CAAK,MAAA;AACpD,MAAA,IAAI,eAAe,MAAA,IAAa,CAAC,oBAAoB,UAAA,EAAY,KAAA,CAAM,MAAM,CAAA,EAAG;AAC9E,QAAA,MAAA,CAAO,SAAS,KAAA,CAAM,MAAA;AACtB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AAGA,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,OAAA,KAAY,MAAM,OAAA,CAAA,EAAU;AACpF,MAAA,MAAA,CAAO,UAAU,KAAA,CAAM,OAAA;AACvB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAGA,IAAA,IAAI,KAAA,CAAM,aAAa,MAAA,KAAc,IAAA,KAAS,QAAQ,IAAA,CAAK,QAAA,KAAa,MAAM,QAAA,CAAA,EAAW;AACvF,MAAA,MAAA,CAAO,WAAW,KAAA,CAAM,QAAA;AACxB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAGA,IAAA,IAAI,KAAA,CAAM,kBAAkB,MAAA,EAAW;AACrC,MAAA,MAAM,UAAA,GAAa,IAAA,KAAS,IAAA,GAAO,MAAA,GAAY,IAAA,CAAK,aAAA;AACpD,MAAA,MAAM,OAAA,GACJ,UAAA,KAAe,MAAA,IACf,UAAA,KAAe,IAAA,IACf,KAAA,CAAM,aAAA,KAAkB,IAAA,IACxB,CAAC,YAAA,CAAa,UAAA,EAAY,KAAA,CAAM,aAAa,CAAA;AAC/C,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,MAAA,CAAO,gBAAgB,KAAA,CAAM,aAAA;AAC7B,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF;AAIA,IAAA,IACE,IAAA,KAAS,IAAA,IACT,CAAC,YAAA,CAAa,KAAK,WAAA,IAAe,EAAC,EAAG,KAAA,CAAM,eAAe,EAAE,CAAA,IAC7D,CAAC,qBAAqB,OAAA,EACtB;AACA,MAAA,oBAAA,CAAqB,OAAA,GAAU,IAAA;AAC/B,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OACF;AAAA,IACF;AAEA,IAAA,OAAA,CAAQ,OAAA,GAAU;AAAA,MAChB,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,YAAA;AAAA,MACA,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,WAAA;AAAA,MACA,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,YAAY,KAAA,CAAM,UAAA;AAAA,MAClB,SAAA;AAAA,MACA,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,YAAY,KAAA,CAAM,UAAA;AAAA,MAClB,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,cAAc,KAAA,CAAM,YAAA;AAAA,MACpB,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,eAAe,KAAA,CAAM,aAAA;AAAA,MACrB,YAAA;AAAA,MACA,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,SAAA;AAAA,MACA,aAAa,KAAA,CAAM,WAAA;AAAA,MACnB,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,eAAe,KAAA,CAAM,aAAA;AAAA,MACrB,aAAa,KAAA,CAAM;AAAA,KACrB;AAEA,IAAA,IAAI,KAAA,EAAO,QAAA,CAAS,eAAA,CAAgB,MAAM,CAAA;AAAA,EAC5C,CAAC,CAAA;AAMD,EAAAD,gBAAgB,MAAM;AACpB,IAAA,kCAAA,CAAmC,QAAA,EAAU,MAAM,yBAAyB,CAAA;AAAA,EAC9E,CAAC,CAAA;AAKD,EAAAF,UAAU,MAAM;AACd,IAAA,MAAM,YAAY,YAAA,CAAa,OAAA;AAC/B,IAAA,IAAI,SAAA,KAAc,MAAM,OAAO,MAAA;AAK/B,IAAA,IAAI,MAAA,GAAS,IAAA;AACb,IAAA,KAAK,SAAS,MAAA,CAAO,SAAS,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAiB;AACtD,MAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,MAAA,IAAI,QAAA,CAAS,KAAA,CAAM,QAAA,EAAS,CAAE,WAAW,OAAA,EAAS;AAClD,MAAA,MAAM,KAAA,GAAQ,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAChE,MAAA,YAAA,CAAa,OAAA,CAAQ,OAAA,GAAU,EAAE,KAAA,EAAO,CAAA;AAAA,IAC1C,CAAC,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,GAAS,KAAA;AACT,MAAA,QAAA,CAAS,MAAA,EAAO;AAAA,IAClB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAMb,EAAA,MAAM,cAAA,GAAiBD,MAAAA,CAAO,EAAE,cAAA,EAAgB,OAAO,CAAA;AACvD,EAAAC,UAAU,MAAM;AACd,IAAA,MAAM,OAAO,cAAA,CAAe,OAAA;AAC5B,IAAA,IAAA,CAAK,cAAA,GAAiB,KAAA;AACtB,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,MAAA,cAAA,CAAe,MAAM;AACnB,QAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,QAAA,CAAS,OAAA,EAAQ;AAAA,MAC5C,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAKb,EAAAA,UAAU,MAAM;AACd,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,eAAe,UAAA,EAAY;AAC5E,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,UAAA,CAAW,kCAAkC,CAAA;AAC/D,IAAA,MAAM,OAAA,GAAU,eAAe,QAAQ,CAAA;AACvC,IAAA,OAAA,CAAQ,gBAAA,GAAmB,GAAG,OAAO,CAAA;AACrC,IAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAAiC;AACjD,MAAA,OAAA,CAAQ,gBAAA,GAAmB,EAAE,OAAO,CAAA;AAAA,IACtC,CAAA;AACA,IAAA,IAAI,OAAO,EAAA,CAAG,gBAAA,KAAqB,UAAA,EAAY;AAC7C,MAAA,EAAA,CAAG,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACtC,MAAA,OAAO,MAAM;AACX,QAAA,EAAA,CAAG,mBAAA,CAAoB,UAAU,QAAQ,CAAA;AAAA,MAC3C,CAAA;AAAA,IACF;AAEA,IAAA,IAAI,OAAO,EAAA,CAAG,WAAA,KAAgB,UAAA,EAAY;AACxC,MAAA,EAAA,CAAG,YAAY,QAAQ,CAAA;AACvB,MAAA,OAAO,MAAM;AACX,QAAA,EAAA,CAAG,eAAe,QAAQ,CAAA;AAAA,MAC5B,CAAA;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,mBAAA;AAAA,IACE,GAAA;AAAA,IACA,OAA0B;AAAA,MACxB,OAAA,EAAS,CAAC,KAAA,KAA2B;AACnC,QAAA,QAAA,CAAS,OAAA,EAAQ;AAAA,MACnB,CAAA;AAAA,MACA,QAAQ,MAAM;AACZ,QAAA,QAAA,CAAS,MAAA,EAAO;AAAA,MAClB,CAAA;AAAA,MACA,SAAS,MAAM;AACb,QAAA,QAAA,CAAS,OAAA,EAAQ;AAAA,MACnB,CAAA;AAAA,MACA,WAAA,EAAa,CAAC,CAAA,EAA2B,KAAA,KAAoC;AAC3E,QAAA,QAAA,CAAS,YAAY,CAAC,CAAA;AAAA,MACxB,CAAA;AAAA,MACA,sBAAA,EAAwB,CAAC,EAAA,EAAY,MAAA,KAAsC;AACzE,QAAA,QAAA,CAAS,sBAAA,CAAuB,IAAI,MAAM,CAAA;AAAA,MAC5C,CAAA;AAAA,MACA,YAAA,EAAc,CAAC,IAAA,KACb,QAAA,CAAS,aAAa,IAAI,CAAA;AAAA,MAC5B,cAAc,CACZ,GAAA,EACA,SACgC,QAAA,CAAS,YAAA,CAAa,KAAK,IAAI,CAAA;AAAA,MACjE,WAAA,GAAc,CAAC,MAAA,EAAuB,IAAA,KACpC,MAAA,KAAW,KAAA,GAAQ,QAAA,CAAS,WAAA,CAAY,KAAK,CAAA,GAAI,QAAA,CAAS,WAAA,CAAY,OAAO,IAAI,CAAA,CAAA;AAAA,MACnF,YAAY,CAAC,KAAA,EAAgC,SAC3C,QAAA,CAAS,UAAA,CAAW,OAAO,IAAI,CAAA;AAAA,MACjC,gBAAA,EAAkB,CAAC,KAAA,KAAmC,QAAA,CAAS,iBAAiB,KAAK,CAAA;AAAA,MACrF,YAAA,EAAc,CAAC,IAAA,KAA8B,QAAA,CAAS,aAAa,IAAI,CAAA;AAAA,MACvE,kBAAA,EAAoB,MAAM,QAAA,CAAS,kBAAA,EAAmB;AAAA,MACtD,SAAA,EAAW,CAAC,EAAA,KAAe;AACzB,QAAA,QAAA,CAAS,UAAU,EAAE,CAAA;AAAA,MACvB,CAAA;AAAA,MACA,aAAA,EAAe,CAAC,EAAA,KAAsB;AACpC,QAAA,QAAA,CAAS,cAAc,EAAE,CAAA;AAAA,MAC3B,CAAA;AAAA,MACA,eAAA,EAAiB,MAAM,QAAA,CAAS,eAAA,EAAgB;AAAA,MAChD,YAAA,EAAc,CAAC,GAAA,KAA2B;AACxC,QAAA,QAAA,CAAS,aAAa,GAAG,CAAA;AAAA,MAC3B,CAAA;AAAA,MACA,QAAA,EAAU,CAAC,QAAA,EAAkB,QAAA,EAAkB,YAC7C,QAAA,CAAS,QAAA,CAAS,QAAA,EAAU,QAAA,EAAU,OAAO,CAAA;AAAA,MAC/C,WAAW,MAAM;AACf,QAAA,QAAA,CAAS,SAAA,EAAU;AAAA,MACrB,CAAA;AAAA,MACA,gBAAgB,MAAM;AACpB,QAAA,QAAA,CAAS,cAAA,EAAe;AAAA,MAC1B,CAAA;AAAA,MACA,eAAA,EAAiB,CAAC,EAAA,KAAgB;AAChC,QAAA,QAAA,CAAS,gBAAgB,EAAE,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA,WAAW,MAAM;AACf,QAAA,QAAA,CAAS,SAAA,EAAU;AAAA,MACrB,CAAA;AAAA,MACA,iBAAiB,MAAM;AACrB,QAAA,QAAA,CAAS,eAAA,EAAgB;AAAA,MAC3B,CAAA;AAAA,MACA,qBAAqB,CACnB,OAAA,EACA,SACsB,QAAA,CAAS,mBAAA,CAAoB,SAAS,IAAI,CAAA;AAAA,MAClE,OAAA,EAAS,CAAC,EAAA,EAAY,EAAA,KAAmC;AACvD,QAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,EAAE,CAAA;AAAA,MACzB,CAAA;AAAA,MACA,SAAA,EAAW,CAAC,EAAA,KAAe;AACzB,QAAA,QAAA,CAAS,UAAU,EAAE,CAAA;AAAA,MACvB,CAAA;AAAA,MACA,WAAW,MAAM;AACf,QAAA,QAAA,CAAS,SAAA,EAAU;AAAA,MACrB,CAAA;AAAA,MACA,SAAA,EAAW,CAAC,GAAA,KAA2B;AACrC,QAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AAAA,MACxB,CAAA;AAAA,MACA,SAAS,MAAM;AACb,QAAA,QAAA,CAAS,OAAA,EAAQ;AAAA,MACnB,CAAA;AAAA,MACA,kBAAkB,MAAM;AACtB,QAAA,QAAA,CAAS,gBAAA,EAAiB;AAAA,MAC5B,CAAA;AAAA,MACA,gBAAgB,MAAM;AACpB,QAAA,QAAA,CAAS,cAAA,EAAe;AAAA,MAC1B,CAAA;AAAA,MACA,YAAY,CAAC,EAAA,EAAY,SACvB,QAAA,CAAS,UAAA,CAAW,IAAI,IAAI,CAAA;AAAA,MAC9B,gBAAA,EAAkB,CAAC,EAAA,KAAe;AAChC,QAAA,QAAA,CAAS,iBAAiB,EAAE,CAAA;AAAA,MAC9B,CAAA;AAAA,MACA,QAAA,EAAU,CAAC,GAAA,KAA2B;AACpC,QAAA,QAAA,CAAS,SAAS,GAAG,CAAA;AAAA,MACvB,CAAA;AAAA,MACA,UAAA,EAAY,CAAC,GAAA,KAA2B;AACtC,QAAA,QAAA,CAAS,WAAW,GAAG,CAAA;AAAA,MACzB,CAAA;AAAA,MACA,UAAA,EAAY,CAAC,IAAA,KAAoB;AAC/B,QAAA,QAAA,CAAS,WAAW,IAAI,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,OAAA,KAAoB;AAC5B,QAAA,QAAA,CAAS,QAAQ,OAAO,CAAA;AAAA,MAC1B,CAAA;AAAA,MACA,iBAAA,EAAmB,CAAC,OAAA,EAAiB,SAAA,KAAuB;AAC1D,QAAA,QAAA,CAAS,iBAAA,CAAkB,SAAS,SAAS,CAAA;AAAA,MAC/C,CAAA;AAAA,MACA,QAAA,EAAU,CAAC,EAAA,EAAY,IAAA,KAA6C;AAClE,QAAA,QAAA,CAAS,QAAA,CAAS,IAAI,IAAI,CAAA;AAAA,MAC5B,CAAA;AAAA,MACA,UAAA,EAAY,CAAC,EAAA,KAAe;AAC1B,QAAA,QAAA,CAAS,WAAW,EAAE,CAAA;AAAA,MACxB,CAAA;AAAA,MACA,OAAA,EAAS,CAAC,EAAA,KAAwD,QAAA,CAAS,QAAQ,EAAE,CAAA;AAAA,MACrF,WAAA,EAAa,MAAkC,QAAA,CAAS,WAAA,EAAY;AAAA,MACpE,aAAA,EAAe,CAAC,GAAA,EAAa,IAAA,KAAkC;AAC7D,QAAA,QAAA,CAAS,aAAA,CAAc,KAAK,IAAI,CAAA;AAAA,MAClC,CAAA;AAAA,MACA,WAAA,EAAa,CAAC,IAAA,KAAkD,QAAA,CAAS,YAAY,IAAI,CAAA;AAAA,MACzF,aAAA,EAAe,CAAC,SAAA,KACd,QAAA,CAAS,cAAc,SAAS,CAAA;AAAA,MAClC,IAAA,EAAM,MAAe,QAAA,CAAS,IAAA,EAAK;AAAA,MACnC,IAAA,EAAM,MAAe,QAAA,CAAS,IAAA,EAAK;AAAA,MACnC,YAAA,EAAc,CAAC,GAAA,EAAa,QAAA,KAAyC;AACnE,QAAA,QAAA,CAAS,YAAA,CAAa,KAAK,QAAQ,CAAA;AAAA,MACrC,CAAA;AAAA,MACA,eAAe,MAAM;AACnB,QAAA,QAAA,CAAS,aAAA,EAAc;AAAA,MACzB,CAAA;AAAA,MACA,qBAAA,EAAuB,MAAiC,QAAA,CAAS,qBAAA,EAAsB;AAAA,MACvF,YAAA,EAAc,CAAC,OAAA,KACb,QAAA,CAAS,aAAa,OAAO,CAAA;AAAA,MAC/B,gBAAgB,CAAC,MAAA,EAAoB,OACnC,QAAA,CAAS,cAAA,CAAe,QAAQ,EAAE,CAAA;AAAA,MACpC,YAAA,EAAc,MAAM,QAAA,CAAS,YAAA,EAAa;AAAA,MAC1C,cAAA,EAAgB,MAAM,QAAA,CAAS,cAAA,EAAe;AAAA,MAC9C;AAAA,KACF,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAGA,EAAA,MAAM,gBAAgB,KAAA,EAAM;AAC5B,EAAA,MAAM,WAAA,GAAc,MAAM,aAAA,EAAe,WAAA;AAEzC,EAAA,uBACEC,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,KAAA,CAAM,WAAW,KAAA,EAAO,EAAE,GAAG,oBAAA,EAAsB,GAAG,KAAA,CAAM,KAAA,EAAM,EAChF,QAAA,kBAAA,IAAA,CAAC,iBAAc,QAAA,EAEb,QAAA,EAAA;AAAA,oBAAAA,GAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,YAAA;AAAA,QACL,mBAAA,EAAkB,EAAA;AAAA,QAClB,KAAA,EAAO,YAAA;AAAA,QACP,IAAA,EAAK,aAAA;AAAA,QACL,YAAA,EAAY,KAAA,CAAM,aAAA,EAAe,KAAA,IAAS,qBAAA;AAAA,QAC1C,kBAAA,EAAkB,WAAA,KAAgB,MAAA,GAAY,aAAA,GAAgB;AAAA;AAAA,KAChE;AAAA,IACC,WAAA,KAAgB,MAAA,mBACfA,GAAAA,CAAC,KAAA,EAAA,EAAI,IAAI,aAAA,EAAe,KAAA,EAAO,mBAAA,EAC5B,QAAA,EAAA,WAAA,EACH,CAAA,GACE,IAAA;AAAA,IAEH,KAAA,CAAM,MAAA,EAAQ,OAAA,KAAY,KAAA,mBACzBA,GAAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,gBAAgB,KAAA,CAAM,cAAA;AAAA,QACtB,iBAAiB,KAAA,CAAM,eAAA;AAAA,QACvB,uBAAuB,KAAA,CAAM,qBAAA;AAAA,QAC7B,oBAAoB,KAAA,CAAM;AAAA;AAAA,KAC5B,GACE,IAAA;AAAA,IAEH,MAAM,WAAA,KAAgB,KAAA,mBAAQA,GAAAA,CAAC,SAAM,CAAA,GAAK,IAAA;AAAA,oBAE3CA,GAAAA,CAAC,UAAA,EAAA,EAAW,aAAA,EAAe,KAAA,CAAM,eAAe,aAAA,EAAe,CAAA;AAAA,IAG9D,KAAA,CAAM,YAAA,KAAiB,MAAA,mBACtBA,GAAAA,CAAC,cAAW,QAAA,EAAoB,YAAA,EAAc,KAAA,CAAM,YAAA,EAAc,CAAA,GAChE,IAAA;AAAA,IACH,KAAA,CAAM;AAAA,GAAA,EACT,CAAA,EACF,CAAA;AAEJ;AAOO,IAAM,KAAA,GAAQ,WAAW,UAAU","file":"index.js","sourcesContent":["/**\n * Lasso — the Shift+drag freeform selection overlay.\n *\n * A transparent div rendered above the engine canvas that captures pointer\n * events ONLY while Shift is held (or a lasso drag is already in progress);\n * otherwise `pointer-events: none` so it never intercepts canvas interaction.\n *\n * Gesture: pointerdown starts capture; pointermove appends polygon points\n * throttled to at most one per animation frame (leading edge + trailing\n * commit); an SVG path previews the polygon; pointerup with ≥3 points calls\n * `instance.selectWithinPolygon(polygon, { additive: metaKey })`. Abort paths\n * (pointercancel, Escape, unmount) always release capture and leave no state.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement } from 'react';\nimport { useAmbientGraphInstance } from './GraphProvider';\n\ninterface DragState {\n pointerId: number;\n /** Committed polygon vertices (overlay-local screen coordinates). */\n points: [number, number][];\n /** Latest uncommitted pointer position (throttle window open). */\n pending: [number, number] | null;\n /** Scheduled animation frame id; null when no frame is pending. */\n frame: number | null;\n}\n\nconst OVERLAY_BASE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n touchAction: 'none',\n userSelect: 'none',\n};\n\nconst OVERLAY_INACTIVE: CSSProperties = { ...OVERLAY_BASE, pointerEvents: 'none' };\nconst OVERLAY_ACTIVE: CSSProperties = {\n ...OVERLAY_BASE,\n pointerEvents: 'auto',\n cursor: 'crosshair',\n};\n\nconst SVG_STYLE: CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none',\n overflow: 'visible',\n};\n\nfunction toPathData(points: readonly [number, number][]): string {\n let d = `M ${points[0]![0]} ${points[0]![1]}`;\n for (let i = 1; i < points.length; i++) d += ` L ${points[i]![0]} ${points[i]![1]}`;\n return points.length >= 3 ? `${d} Z` : d;\n}\n\n/** rAF when available; otherwise run synchronously (headless environments\n * throttling degrades to per-event appends, which is still correct). */\nfunction scheduleFrame(cb: () => void): number | null {\n if (typeof requestAnimationFrame === 'function') return requestAnimationFrame(cb);\n cb();\n return null;\n}\n\nfunction cancelFrame(id: number | null): void {\n if (id !== null && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(id);\n}\n\n/** Guarded pointer capture: jsdom (and older engines) may not implement it. */\nfunction capturePointer(el: HTMLElement, pointerId: number): void {\n if (typeof el.setPointerCapture === 'function') {\n try {\n el.setPointerCapture(pointerId);\n } catch {\n /* capture unavailable for this pointer — the gesture still works */\n }\n }\n}\n\nfunction releasePointer(el: HTMLElement, pointerId: number): void {\n if (typeof el.releasePointerCapture === 'function') {\n try {\n el.releasePointerCapture(pointerId);\n } catch {\n /* no active capture — already released */\n }\n }\n}\n\n/** Internal to <Graph> (gated by its `enableLasso` prop); reads the ambient\n * instance so advanced hosts composing GraphProvider directly can reuse it. */\nexport function Lasso(): ReactElement {\n const instance = useAmbientGraphInstance('Lasso');\n const overlayRef = useRef<HTMLDivElement | null>(null);\n const dragRef = useRef<DragState | null>(null);\n const [shiftHeld, setShiftHeld] = useState(false);\n /** Render mirror of the committed polygon; null = no drag in progress. */\n const [preview, setPreview] = useState<readonly [number, number][] | null>(null);\n\n /** Tear down the in-progress drag: cancel the pending frame, release\n * capture, clear all drag state. Safe to call when idle. */\n const releaseDrag = useCallback((): void => {\n const drag = dragRef.current;\n if (drag === null) return;\n dragRef.current = null;\n cancelFrame(drag.frame);\n const el = overlayRef.current;\n if (el !== null) releasePointer(el, drag.pointerId);\n setPreview(null);\n }, []);\n\n // Shift tracking + Escape abort. Window-level so the overlay activates\n // before it can receive any pointer event; blur releases a stuck Shift.\n useEffect(() => {\n const onKeyDown = (e: KeyboardEvent): void => {\n if (e.key === 'Shift') setShiftHeld(true);\n else if (e.key === 'Escape') releaseDrag();\n };\n const onKeyUp = (e: KeyboardEvent): void => {\n if (e.key === 'Shift') setShiftHeld(false);\n };\n const onBlur = (): void => setShiftHeld(false);\n window.addEventListener('keydown', onKeyDown);\n window.addEventListener('keyup', onKeyUp);\n window.addEventListener('blur', onBlur);\n return () => {\n window.removeEventListener('keydown', onKeyDown);\n window.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('blur', onBlur);\n releaseDrag(); // unmount mid-drag leaves no capture and no frame\n };\n }, [releaseDrag]);\n\n const localPoint = (e: ReactPointerEvent<HTMLDivElement>): [number, number] => {\n const el = overlayRef.current;\n if (el === null) return [e.clientX, e.clientY];\n const rect = el.getBoundingClientRect();\n return [e.clientX - rect.left, e.clientY - rect.top];\n };\n\n const commitPending = (drag: DragState): void => {\n if (drag.pending === null) return;\n drag.points.push(drag.pending);\n drag.pending = null;\n setPreview([...drag.points]);\n };\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (dragRef.current !== null || e.button !== 0) return;\n // Lasso is a Shift gesture: the overlay only receives events while Shift\n // is held, but re-check for robustness (tests, synthetic dispatch).\n if (!e.shiftKey && !shiftHeld) return;\n const pointerId = e.pointerId ?? -1;\n const el = overlayRef.current;\n if (el !== null) capturePointer(el, pointerId);\n const p = localPoint(e);\n dragRef.current = { pointerId, points: [p], pending: null, frame: null };\n setPreview([p]);\n e.preventDefault();\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n drag.pending = localPoint(e);\n if (drag.frame !== null) return; // throttle window open — coalesce\n // Leading edge: append immediately, then hold the window one frame at a\n // time while moves keep streaming in (trailing commit per frame).\n commitPending(drag);\n const tick = (): void => {\n drag.frame = null;\n if (dragRef.current !== drag) return; // drag ended while scheduled\n if (drag.pending !== null) {\n commitPending(drag);\n drag.frame = scheduleFrame(tick);\n }\n };\n drag.frame = scheduleFrame(tick);\n };\n\n const onPointerUp = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n commitPending(drag); // the trailing point must land in the polygon\n const polygon = drag.points;\n const additive = e.metaKey;\n releaseDrag();\n if (polygon.length >= 3) instance.selectWithinPolygon(polygon, { additive });\n };\n\n const onPointerCancel = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current;\n if (drag === null || (e.pointerId ?? -1) !== drag.pointerId) return;\n releaseDrag();\n };\n\n const active = shiftHeld || preview !== null;\n return (\n <div\n ref={overlayRef}\n data-orbit-lasso=\"\"\n style={active ? OVERLAY_ACTIVE : OVERLAY_INACTIVE}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerCancel}\n >\n {preview !== null && preview.length > 0 ? (\n <svg data-orbit-lasso-preview=\"\" style={SVG_STYLE}>\n <path\n d={toPathData(preview)}\n fill=\"rgba(88, 134, 255, 0.12)\"\n stroke=\"rgba(88, 134, 255, 0.9)\"\n strokeWidth={1.5}\n strokeDasharray=\"4 3\"\n />\n </svg>\n ) : null}\n </div>\n );\n}\n","/**\n * LabelLayer — the DOM label lane.\n *\n * Two-channel rendering against the pinned overlay interface:\n * - `labels.subscribeCandidates` fires only when the candidate SET changes →\n * React state → one absolutely-positioned <div> per candidate. Label text\n * is rendered as a TEXT NODE only (never markup), so hostile strings in\n * node attrs appear literally.\n * - `labels.subscribePositions` fires on scheduler ticks with fresh x/y for\n * the SAME set → imperative `style.transform` writes through refs keyed by\n * id. No React re-render per tick (M0: the label lane is pure CPU O(k)).\n *\n * The layer is pointer-inert; built-in label divs opt back in as focusable\n * buttons and drive the same public selection mutators by click or Enter/Space\n * (replace normally, toggle with meta/shift). Custom renderers own their\n * content's focus semantics, avoiding a button-role wrapper around a native\n * interactive control. Node labels expose their context action through\n * right-click plus ContextMenu/Shift+F10.\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type {\n CSSProperties,\n KeyboardEvent as ReactKeyboardEvent,\n MouseEvent as ReactMouseEvent,\n ReactElement,\n ReactNode,\n} from 'react';\nimport type { GraphNode, LabelPlacement, NodeId } from '@modernrelay/orbit-core';\nimport { useAmbientGraphInstance } from './GraphProvider';\nimport { overlaySurface, overlayLayerStyle } from './components/shared';\n\nexport interface LabelLayerProps {\n /** Class hook applied to every label div. */\n labelClassName?: string | undefined;\n /** Escape hatch rendered INSIDE the positioned div instead of the text node. */\n renderNodeLabel?: ((ctx: { node: GraphNode<any>; text: string }) => ReactNode) | undefined;\n /** Class hook applied to CLUSTER label divs (in addition to\n * `labelClassName`), so the coarse layer can be styled apart. */\n clusterLabelClassName?: string | undefined;\n /** Escape hatch for cluster labels; `memberIds` are the ids a click\n * selects. */\n renderClusterLabel?:\n | ((ctx: { clusterKey: string; text: string; memberIds: readonly NodeId[] }) => ReactNode)\n | undefined;\n}\n\n/** Placements come from two id namespaces (node ids and cluster keys), so\n * element/position maps and React keys are keyed on `(kind, id)`. */\nfunction placementKey(p: LabelPlacement): string {\n return p.kind === 'cluster' ? `cluster\\u0000${p.id}` : `node\\u0000${p.id}`;\n}\n\nconst LAYER_STYLE: CSSProperties = { ...overlayLayerStyle, overflow: 'hidden' };\n\nconst LABEL_BASE_STYLE: CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'auto',\n cursor: 'pointer',\n whiteSpace: 'nowrap',\n userSelect: 'none',\n willChange: 'transform',\n};\n\nfunction transformOf(x: number, y: number): string {\n return `translate3d(${x}px, ${y}px, 0)`;\n}\n\n/** Internal to <Graph> (gated by `labels.enabled`); reads the ambient\n * instance so hosts composing GraphProvider directly can reuse it. */\nexport function LabelLayer(props: LabelLayerProps): ReactElement {\n const instance = useAmbientGraphInstance('LabelLayer');\n const labels = overlaySurface(instance).labels;\n\n const [candidates, setCandidates] = useState<readonly LabelPlacement[]>([]);\n /** Live label elements, keyed by `(kind, id)` (populated via ref callbacks). */\n const elementsRef = useRef(new Map<string, HTMLDivElement>());\n /** Freshest known screen position per candidate — written by BOTH channels\n * so a candidate re-render never snaps back to a stale x/y. */\n const positionsRef = useRef(new Map<string, readonly [number, number]>());\n const layerRef = useRef<HTMLDivElement | null>(null);\n\n // Candidate SET changes: throttled re-rank → React re-render of the content.\n useEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribeCandidates((list) => {\n const positions = positionsRef.current;\n const keep = new Set<string>();\n for (const p of list) {\n const key = placementKey(p);\n keep.add(key);\n positions.set(key, [p.x, p.y]);\n }\n for (const key of [...positions.keys()]) {\n if (!keep.has(key)) positions.delete(key);\n }\n // The core REUSES the emitted array/placement objects across ticks\n // copy so React state gets a fresh identity (no Object.is bail-out).\n setCandidates(list.map((p) => ({ ...p })));\n });\n }, [labels]);\n\n // Scheduler position ticks: imperative transform writes, no re-render.\n useLayoutEffect(() => {\n if (labels === undefined) return undefined;\n return labels.subscribePositions((list) => {\n const elements = elementsRef.current;\n const positions = positionsRef.current;\n for (const p of list) {\n const key = placementKey(p);\n positions.set(key, [p.x, p.y]);\n const el = elements.get(key);\n if (el !== undefined) el.style.transform = transformOf(p.x, p.y);\n }\n });\n }, [labels]);\n\n // Click-selection semantics through the public mutators:\n // plain click replaces the node selection; meta/shift click toggles the id.\n const selectLabel = (id: NodeId, additive: boolean): void => {\n if (additive) {\n const cur = instance.store.getState().selection.nodeIds;\n instance.setSelection(cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]);\n return;\n }\n instance.setSelection([id]);\n };\n\n const onLabelClick = (id: NodeId, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.stopPropagation();\n selectLabel(id, e.metaKey || e.shiftKey);\n };\n\n // a cluster label resolves to its MEMBER node ids through the\n // core mutator — clusters never enter the group selection namespace.\n const onClusterLabelClick = (key: string, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.stopPropagation();\n instance.selectCluster(key, { additive: e.metaKey || e.shiftKey });\n };\n\n const onLabelKeyDown = (\n p: LabelPlacement,\n e: ReactKeyboardEvent<HTMLDivElement>,\n ): void => {\n // Native button activation does not exist for a div with role=\"button\",\n // and keyboard-triggered `contextmenu` coordinates vary by browser. Give\n // node labels (including native controls supplied by a custom renderer)\n // an explicit ContextMenu / Shift+F10 path, anchored at the label's center\n // in the same container-relative coordinate system as a pointer-triggered\n // menu.\n if (\n p.kind !== 'cluster' &&\n (e.key === 'ContextMenu' || (e.key === 'F10' && e.shiftKey))\n ) {\n e.preventDefault();\n e.stopPropagation();\n const layerRect = layerRef.current?.getBoundingClientRect();\n if (layerRect === undefined) return;\n const labelRect = e.currentTarget.getBoundingClientRect();\n instance.requestNodeContextMenu(p.id, [\n labelRect.left + labelRect.width / 2 - layerRect.left,\n labelRect.top + labelRect.height / 2 - layerRect.top,\n ]);\n return;\n }\n // An escape-hatch renderer may supply its own focusable control. Its\n // Enter keydown can synthesize a click that already bubbles through the\n // label's click path, so handling the bubbled keydown here as well would\n // activate the label twice.\n if (e.target !== e.currentTarget) return;\n if (e.key !== 'Enter' && e.key !== ' ') return;\n e.preventDefault();\n e.stopPropagation();\n const additive = e.metaKey || e.shiftKey;\n if (p.kind === 'cluster') instance.selectCluster(p.id, { additive });\n else selectLabel(p.id, additive);\n };\n\n // Right-click routes into the SAME typed 'contextMenu' channel the canvas\n // gesture feeds. Label divs are pointerEvents:'auto' (click-to-focus), so\n // without this the gesture dies here and the browser's native menu opens\n // on exactly the nodes prominent enough to carry labels. Coordinates are\n // container-relative CSS px (the layer fills the container), matching the\n // payload contract the canvas path uses.\n const onLabelContextMenu = (id: NodeId, e: ReactMouseEvent<HTMLDivElement>): void => {\n e.preventDefault();\n e.stopPropagation();\n const rect = layerRef.current?.getBoundingClientRect();\n if (rect === undefined) return;\n instance.requestNodeContextMenu(id, [e.clientX - rect.left, e.clientY - rect.top]);\n };\n\n const clusterMembers = (key: string): readonly NodeId[] =>\n instance.getClusters().find((c) => c.key === key)?.memberIds ?? [];\n\n return (\n <div data-orbit-label-layer=\"\" ref={layerRef} style={LAYER_STYLE}>\n {candidates.map((p) => {\n const key = placementKey(p);\n const [x, y] = positionsRef.current.get(key) ?? [p.x, p.y];\n const isCluster = p.kind === 'cluster';\n const className = isCluster\n ? [props.labelClassName, props.clusterLabelClassName].filter(Boolean).join(' ') ||\n undefined\n : props.labelClassName;\n const hasCustomRenderer = isCluster\n ? props.renderClusterLabel !== undefined\n : props.renderNodeLabel !== undefined;\n return (\n <div\n key={key}\n {...(isCluster ? { 'data-orbit-cluster-label': p.id } : { 'data-orbit-label': p.id })}\n className={className}\n {...(!hasCustomRenderer\n ? {\n role: 'button',\n tabIndex: 0,\n 'aria-label': p.text,\n ...(!isCluster ? { 'aria-haspopup': 'menu' as const } : {}),\n }\n : {})}\n style={{ ...LABEL_BASE_STYLE, transform: transformOf(x, y) }}\n ref={(el) => {\n if (el === null) elementsRef.current.delete(key);\n else elementsRef.current.set(key, el);\n }}\n onClick={(e) => (isCluster ? onClusterLabelClick(p.id, e) : onLabelClick(p.id, e))}\n onKeyDown={(e) => onLabelKeyDown(p, e)}\n // Cluster labels keep the browser menu for now: their click\n // already selects members, and no cluster menu target exists.\n onContextMenu={isCluster ? undefined : (e) => onLabelContextMenu(p.id, e)}\n >\n {/* text-node-only rendering holds for both kinds: a hostile\n key or label appears literally, never as markup. */}\n {isCluster\n ? props.renderClusterLabel !== undefined\n ? props.renderClusterLabel({\n clusterKey: p.id,\n text: p.text,\n memberIds: clusterMembers(p.id),\n })\n : p.text\n : props.renderNodeLabel !== undefined\n ? props.renderNodeLabel({\n node: instance.getNode(p.id) ?? { id: p.id },\n text: p.text,\n })\n : p.text}\n </div>\n );\n })}\n </div>\n );\n}\n","/**\n * LiveRegion — screen-reader announcements.\n *\n * A visually-hidden `aria-live=\"polite\"` region adjacent to the canvas. It\n * subscribes to the STORE (never the frame clock, so simulation frames can\n * never announce) and speaks a coalesced summary — node/edge counts,\n * selection count, status — at most once every 800ms and only when the\n * summary STRING changes. Trailing-edge coalescing: a burst of store changes\n * lands as ONE announcement carrying the freshest state.\n *\n * Gated off entirely when `accessibility.announcements === false`.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport type { GraphStoreState } from '@modernrelay/orbit-core';\nimport { useAmbientGraphInstance } from './GraphProvider';\nimport { visuallyHiddenStyle } from './components/shared';\n\nexport const ANNOUNCE_INTERVAL_MS = 800;\n\nexport interface LiveRegionProps {\n /** `accessibility.announcements`; false gates all announcements. Default true. */\n announcements?: boolean | undefined;\n}\n\nfunction summarize(s: GraphStoreState): string {\n const selected = s.selection.nodeIds.length + s.selection.edgeIds.length;\n return `Graph ${s.status}. ${s.nodeCount} nodes, ${s.edgeCount} edges. ${selected} selected.`;\n}\n\n/** Internal to <Graph>; reads the ambient instance so hosts composing\n * GraphProvider directly can reuse it. */\nexport function LiveRegion(props: LiveRegionProps): ReactElement {\n const instance = useAmbientGraphInstance('LiveRegion');\n const enabled = props.announcements !== false;\n const [message, setMessage] = useState('');\n /** Last ANNOUNCED summary; seeded at subscribe time so the initial state\n * (already conveyed by the container's aria-label) is never announced. */\n const lastAnnouncedRef = useRef<string | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!enabled) return undefined;\n const store = instance.store;\n lastAnnouncedRef.current = summarize(store.getState());\n\n const fire = (): void => {\n timerRef.current = null;\n const next = summarize(store.getState());\n if (next === lastAnnouncedRef.current) return;\n lastAnnouncedRef.current = next;\n setMessage(next);\n };\n\n const unsubscribe = store.subscribe(() => {\n if (timerRef.current !== null) return; // window open — coalesce\n if (summarize(store.getState()) === lastAnnouncedRef.current) return;\n timerRef.current = setTimeout(fire, ANNOUNCE_INTERVAL_MS);\n });\n\n return () => {\n unsubscribe();\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, [instance, enabled]);\n\n return (\n <div\n data-orbit-live-region=\"\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n style={visuallyHiddenStyle}\n >\n {message}\n </div>\n );\n}\n","/**\n * <Graph> — the declarative React binding over a headless GraphInstance.\n *\n * Binding parity: one committed render that changes multiple declarative\n * props issues AT MOST ONE applyHostUpdate carrying exactly the changed keys,\n * so a multi-prop commit produces exactly one store revision and at most one\n * engine commit.\n */\n\nimport {\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { CSSProperties, ForwardedRef, ReactElement, ReactNode, RefAttributes } from 'react';\nimport {\n canonicalJson,\n canonicalScaleKey,\n createGraphInstance,\n sameGroupSpecArrays,\n} from '@modernrelay/orbit-core';\nimport type {\n Accessor,\n AcceptedEdge,\n AccessibilityConfig,\n BeginIngestOptions,\n ClusterSpec,\n CreateGraphInstanceOptions,\n CrossfilterSession,\n DimensionSpec,\n ExpandNodeResult,\n FilterExpr,\n FilterSpec,\n GraphDiagnostic,\n GraphHostUpdate,\n GraphInstance,\n GraphNode,\n GraphSnapshotInput,\n GraphTheme,\n GraphViewState,\n GroupBySpec,\n GroupSpec,\n IngestSession,\n JsonValue,\n LabelConfig,\n LayoutKind,\n MetaEdge,\n MetricColumn,\n PathOptions,\n PathResult,\n MetricName,\n NodeId,\n ResolvedCluster,\n ResolvedGroup,\n Revisions,\n Scale,\n ScaleChannelInfo,\n SearchResult,\n SearchUnavailableReason,\n SelectionState,\n ScaleLimits,\n WorkerFactoryOption,\n DegradeEvent,\n GraphPerfSnapshot,\n SetViewStateResult,\n SimulationConfig,\n SubgraphSpec,\n ThemeInput,\n TimelinePlayback,\n ViewportState,\n} from '@modernrelay/orbit-core';\nimport type { EngineFactory, FitViewOptions } from '@modernrelay/orbit-core/engine';\nimport { GraphProvider } from './GraphProvider';\nimport { setSearchResultUnavailableCallback, useScaleInfoSubscription } from './hooks';\nimport { Lasso } from './Lasso';\nimport { LabelLayer } from './LabelLayer';\nimport { LiveRegion } from './LiveRegion';\nimport { overlaySurface, visuallyHiddenStyle } from './components/shared';\n\n/** Snapshot handed to `renderLegend`: the active scale info per styling\n * channel (null when the channel is not scale-valued) plus the resolved\n * theme tokens. Recomputed on model/scope/render/theme store changes. */\nexport interface GraphLegendRenderInfo<N = Record<string, unknown>> {\n nodeColor: ScaleChannelInfo<N> | null;\n nodeSize: ScaleChannelInfo<N> | null;\n theme: GraphTheme;\n}\n\nexport interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {\n /** Engine factory; captured at first render — one factory per instance. */\n engine: EngineFactory;\n data?: GraphSnapshotInput<N, E>;\n /** Constant, accessor, or Scale descriptor. Scale-valued props compare\n * by canonical structural value — equal inline literals never reproject. */\n nodeColor?: Accessor<GraphNode<N>, string> | Scale<string, N>;\n nodeSize?: Accessor<GraphNode<N>, number> | Scale<number, N>;\n linkColor?: Accessor<AcceptedEdge<E>, string>;\n linkWidth?: Accessor<AcceptedEdge<E>, number>;\n layout?: LayoutKind;\n simulation?: SimulationConfig;\n /** theme tokens: full GraphTheme, Partial over a named base, or the\n * v0.1 `{background}` shorthand. Diffed structurally (JSON). */\n theme?: ThemeInput;\n /** async metric columns, joined once with revision-gated admission.\n * Diffed by array reference. */\n metrics?: readonly MetricColumn[];\n /** image sprites: synchronous string-ref accessor (URL/blob ref/cache\n * key — opaque to orbit). Diffed by reference. */\n nodeImage?: (node: GraphNode<N>) => string | null;\n /** instanced arrowheads (capability-gated; inert when unsupported). */\n edgeArrows?: boolean;\n /** runtime link-render toggle — a config-only commit. */\n showLinks?: boolean;\n /** emphasis-ring toggle (default true). False clears the ring once and\n * suppresses every driver (hover, focusNode, emphasizeNode). */\n emphasisRing?: boolean;\n /** legend escape hatch: rendered inside the provider in a positioned\n * wrapper (bottom-left) whenever provided; re-invoked when scale info or\n * theme changes. Return custom JSX, or compose <GraphLegend> from\n * '@modernrelay/orbit-react/components/Legend' for the built-in rendering. */\n renderLegend?: (info: GraphLegendRenderInfo<N>) => ReactNode;\n /** DOM label lane; forwarded through applyHostUpdate. `enabled: false`\n * also unmounts the <LabelLayer> overlay entirely. */\n labels?: LabelConfig<N>;\n /** accessibility runtime options; forwarded through applyHostUpdate\n * and mirrored onto the container ARIA surface / live region. */\n accessibility?: AccessibilityConfig<N>;\n /** Class hook applied to every DOM label div. */\n labelClassName?: string;\n /** Escape hatch: custom label content rendered INSIDE the positioned label\n * div, replacing the default text node. */\n renderNodeLabel?: (ctx: { node: GraphNode<N>; text: string }) => ReactNode;\n /** Class hook applied to cluster label divs, in addition to\n * `labelClassName`. */\n clusterLabelClassName?: string;\n /** Escape hatch: custom cluster-label content (replaces the default text\n * node; text-node-only rendering otherwise). `memberIds` are the ids a\n * click selects. */\n renderClusterLabel?: (ctx: {\n clusterKey: string;\n text: string;\n memberIds: readonly NodeId[];\n }) => ReactNode;\n /** Controlled selection of the NODE namespace; providing it once flips\n * ownership permanently. Edge/group namespaces stay instance-owned. */\n selection?: readonly NodeId[];\n /** hard scope: feed ONLY the resolved subset through the reconciler;\n * `null` restores the full accepted model. Diffed STRUCTURALLY (specs are\n * small), so a new-but-equal object is a no-op. UNCONTROLLED-ONLY in v0.5:\n * this prop and isolateSelection/resetIsolation write the same\n * instance-owned state — last writer wins, and omitting the prop leaves\n * the last written scope in place. */\n subgraph?: SubgraphSpec | null;\n /** soft filter: mask (hide/dim) with ZERO relayout; `null` clears.\n * Diffed by canonical structural compare — serializable exprs by JSON,\n * function predicates by reference — so a new-but-equal spec never\n * re-publishes (and the core no-ops canonical-equal specs regardless). */\n filter?: FilterSpec<N, E> | null;\n /** crossfilter dimensions (declarative; brushes live on the session\n * — reach it via useGraphCrossfilter or handle.getCrossfilterSession).\n * Forwarded on array-reference change; the core no-ops when every\n * DimensionSpec element is reference-equal. */\n crossfilter?: readonly DimensionSpec<N>[];\n /** manual groups; `null` clears. Diffed STRUCTURALLY (equal inline\n * literals never re-forward). Providing it once flips the groups slice to\n * CONTROLLED: handle ops then fire onGroupsChange with the next\n * array instead of writing — reflect it back here. Config error together\n * with `groupBy`. */\n groups?: readonly GroupSpec[] | null;\n /** derived grouping; `null` clears. Reference-diffed (the spec\n * carries a function accessor); the core additionally no-ops structurally\n * equal respecs. Membership under groupBy is derived and READ-ONLY\n * groupNodes/ungroup become config errors; setGroupCollapsed toggles the\n * instance-owned per-key collapsed residue. */\n groupBy?: GroupBySpec<N> | null;\n /** stage-4 non-collapsing clusters; `null` clears (D2).\n * Reference-diffed (the spec carries a function accessor). Clusters PRESERVE\n * every node and edge — they never collapse anything — so they coexist with\n * `groups`/`groupBy`. Their labels ride the overlay lane under\n * `labels.maxZoom` and select their member nodes on click. */\n clusters?: ClusterSpec<N> | null;\n /** PERSISTENT pins — independent of transient drag\n * pinning: the engine receives the UNION, so releasing a drag on a\n * persistently-pinned node leaves it pinned. Providing it once latches the\n * slice controlled: handle ops then fire onPinnedChange with the\n * next array instead of writing. `null` clears (D2). */\n pinnedNodeIds?: readonly NodeId[] | null;\n /** node attr fields the DEFAULT search service indexes (ids always).\n * Absent = id-only search — the service never guesses attr names.\n * CONSTRUCTION-ONLY (D7, spec host construction options): read once at\n * mount; changing it requires a keyed remount (a changed prop warns once\n * and is ignored). */\n searchIndex?: readonly string[];\n /** Degradation-ladder thresholds. CONSTRUCTION-ONLY: read\n * once at mount; a runtime change warns once and is ignored. */\n limits?: Partial<ScaleLimits>;\n /** Execution mode. CONSTRUCTION-ONLY: read once\n * at mount; a runtime change warns once and is ignored. Current worker\n * cargo: columnar acceptance off-thread. */\n execution?: 'auto' | 'main' | 'worker';\n /** Worker construction tri-option. CONSTRUCTION-ONLY. */\n workerFactory?: WorkerFactoryOption;\n /** result contract: the instance-wide DEFAULT for an activated\n * search result that cannot be focused ('not-loaded' | 'out-of-scope' |\n * 'filtered'). `<GraphSearch onResultUnavailable>` overrides it locally;\n * the host reacts explicitly (fetch/publish an overlay, reset isolation,\n * alter filters) — search never mutates scope/filters itself. */\n onSearchResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Captured at first render (instance construction option). Default true. */\n fitViewOnFirstData?: boolean;\n /** service seam (instance construction option, D7): custom\n * revision-aware services — most usefully an async `expansion` service\n * backed by the host's own data source, so `expandNode`/the context menu's\n * Expand run against the network instead of the built-in local-adjacency\n * walk. CONSTRUCTION-ONLY: read once at mount; changing it requires a keyed\n * remount. */\n services?: CreateGraphInstanceOptions<N, E>['services'];\n /** Shift+drag freeform lasso selection overlay. Default true. */\n enableLasso?: boolean;\n className?: string;\n style?: CSSProperties;\n /** Rendered inside the provider, above (after) the canvas container. */\n children?: ReactNode;\n onNodeClick?: (payload: { node: GraphNode<N>; metaKey?: boolean }) => void;\n onBackgroundClick?: () => void;\n onNodeHover?: (payload: { node: GraphNode<N> | null }) => void;\n onEdgeClick?: (payload: { edge: AcceptedEdge<E> }) => void;\n onEdgeHover?: (payload: { edge: AcceptedEdge<E> | null }) => void;\n onNodeDragStart?: (payload: { node: GraphNode<N> }) => void;\n onNodeDragEnd?: (payload: { node: GraphNode<N>; x: number; y: number }) => void;\n /** Fires with the full namespaced SelectionState. */\n onSelectionChange?: (payload: SelectionState) => void;\n /** ladder step engagement/disengagement (notification pattern). */\n onDegrade?: (event: DegradeEvent) => void;\n /** throttled telemetry sample — never per frame. */\n onPerfSample?: (snapshot: GraphPerfSnapshot) => void;\n /** Fires on a super-node hit with the resolved group payload, never a GraphNode. */\n onGroupClick?: (payload: { group: ResolvedGroup; metaKey?: boolean }) => void;\n /** Fires on a meta-edge hit with the MetaEdge record used by the count badge. */\n onMetaEdgeClick?: (payload: { metaEdge: MetaEdge }) => void;\n /** groups slice change callback: op results (uncontrolled), op\n * INTENTS (controlled — reflect the array back into `groups`), and groupBy\n * re-derivations (notification). Receives the resolved array. */\n onGroupsChange?: (groups: readonly ResolvedGroup[]) => void;\n /** durable source coordinate — forwarded verbatim; serialized into\n * view states and canonically compared on restore. Diffed by canonical\n * JSON, so a new-but-equal object never re-forwards. */\n dataRef?: JsonValue;\n /** aggregate restore intent (fires once per restore/history\n * transaction touching a controlled slice or serialized styling): reflect\n * every participating prop in ONE commit; matching values acknowledge. */\n onViewStateRestore?: (intent: {\n transactionId: string;\n source: 'setViewState' | 'undo' | 'redo';\n next: unknown;\n }) => void;\n /** fired INSTEAD of applying a state whose dataRef mismatches. */\n onViewStateMismatch?: (stored: JsonValue | undefined, current: JsonValue | undefined) => void;\n /** persistent-pin slice change: the applied set when\n * uncontrolled, the INTENT when controlled. */\n onPinnedChange?: (pinnedNodeIds: readonly NodeId[]) => void;\n onViewportChange?: (v: ViewportState) => void;\n onReady?: () => void;\n onError?: (payload: { error: Error }) => void;\n}\n\nexport interface GraphHandle<N = Record<string, unknown>, E = Record<string, unknown>> {\n /** `opts` is accepted for forward compatibility; core v0.1 ignores it. */\n fitView(opts?: FitViewOptions): void;\n zoomIn(): void;\n zoomOut(): void;\n /** `opts` is accepted for forward compatibility; core v0.1 ignores it. */\n setViewport(v: Partial<ViewportState>, opts?: { durationMs?: number }): void;\n focusNode(id: NodeId): void;\n /** ring-only emphasis, no camera move (null clears; unknown ids\n * no-op). The keyboard-navigation analog of hover. */\n emphasizeNode(id: NodeId | null): void;\n /** telemetry snapshot (never raw attrs or ids). */\n getPerfSnapshot(): GraphPerfSnapshot;\n /** Open the typed context menu for a node from host chrome (a list row, a\n * custom label) — the same 'contextMenu' event the canvas gesture emits.\n * `screen` is container-relative CSS px. */\n requestNodeContextMenu(id: NodeId, screen: readonly [number, number]): void;\n /** serialize the exploration state (see core docs). */\n getViewState(opts?: { includePositions?: false }): GraphViewState;\n /** atomic restore; typed results, never partial. */\n setViewState(\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult>;\n /** picture export: 'png' via the engine screenshot; 'svg' via the\n * engine-free exporter (typed export-too-large past the bound;\n * raster-hybrid fallback available). */\n exportImage(format: 'png'): Promise<Blob>;\n exportImage(\n format: 'svg',\n opts?: { maxSvgElements?: number; fallback?: 'raster-hybrid' },\n ): Promise<string>;\n /** bounded object export; typed rejection past the limit. */\n exportData(\n scope?: 'visible' | 'accepted',\n opts?: { limit?: number },\n ): Promise<{ nodes: readonly GraphNode<N>[]; edges: readonly AcceptedEdge<E>[] }>;\n /** memory-bounded JSONL stream over one pinned revision. */\n exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;\n /** bounded id → [x, y] map (one position readback). */\n exportLayout(opts?: { limit?: number }): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;\n /** layout JSONL stream over one pinned readback. */\n exportLayoutStream(): AsyncGenerator<string, void, undefined>;\n setSelection(ids: readonly NodeId[]): void;\n clearSelection(): void;\n // --- persistent pins ---\n /** Hold nodes at their CURRENT position (independent of drag pinning). */\n pinNodes(ids: readonly NodeId[]): void;\n unpinNodes(ids: readonly NodeId[]): void;\n // --- paths ---\n /** Resolve + atomically emphasize a path; null = unreachable (a result).\n * Emphasis is session-local — released by clearPath, any selection\n * mutation, undo/redo, or a scene rebuild; never in history/view state. */\n findPath(sourceId: NodeId, targetId: NodeId, options?: PathOptions): Promise<PathResult | null>;\n clearPath(): void;\n // --- selection algebra ---\n /** Expand to the 1-hop neighborhood of `id` (or of the current selection). */\n selectNeighbors(id?: NodeId): void;\n selectAll(): void;\n invertSelection(): void;\n /** Resolve a SCREEN-coordinate polygon to node ids and replace (default) or\n * union (`additive`) the node selection; returns the resolved ids. */\n selectWithinPolygon(\n polygon: readonly [number, number][],\n opts?: { additive?: boolean },\n ): readonly NodeId[];\n // --- pin / hide slices ---\n /** Pin at `xy` (space coords) or at the node's current position. */\n pinNode(id: NodeId, xy?: readonly [number, number]): void;\n unpinNode(id: NodeId): void;\n clearPins(): void;\n hideNodes(ids: readonly NodeId[]): void;\n showAll(): void;\n // --- hard scope + expansion ---\n /** Hard-scope to the current node selection; no-op when empty. */\n isolateSelection(): void;\n /** Clear the hard scope — the full accepted model returns. */\n resetIsolation(): void;\n /** Ego-expand `id` through the configured ExpansionService. */\n expandNode(id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult>;\n /** Abort `id`'s pending expansion and remove its committed expansion\n * overlays. */\n retractExpansion(id: NodeId): void;\n // --- group operations ---\n /** Add one group (same acyclic/singly-parented validation as the `groups` prop).\n * Controlled mode fires onGroupsChange with the next array instead. */\n groupNodes(spec: GroupSpec): void;\n /** Remove one group definition; its id prunes from selection.groupIds. */\n ungroup(groupId: string): void;\n /** Collapse/expand one group as a structural diff — the ONE op allowed on\n * groupBy-derived groups (toggles the per-key collapsed residue). */\n setGroupCollapsed(groupId: string, collapsed: boolean): void;\n // --- node folds: an EXISTING node stands for its own neighbourhood.\n // Distinct from `expandNode`/`retractExpansion`, which navigate the data\n // source; a fold is pure containment over what is already loaded. ---\n /** Hide `id`'s neighbourhood behind `id`, rerouting the members' outside\n * edges onto it. Members default to unclaimed neighbours (first fold wins). */\n foldNode(id: NodeId, opts?: { memberIds?: readonly NodeId[] }): void;\n /** Return `id`'s folded members to the scene. No-op when not folded. */\n unfoldNode(id: NodeId): void;\n /** The members `id` stands for, or null when it is not folded. */\n getFold(id: NodeId): { memberIds: readonly NodeId[] } | null;\n // --- stage-4 clusters ---\n /** Current clusters over the physical scene (keys, members, force center,\n * settled centroid). Empty when no `clusters` prop is active. */\n getClusters(): readonly ResolvedCluster[];\n /** Select a cluster's MEMBER node ids; `additive` unions. */\n selectCluster(key: string, opts?: { additive?: boolean }): void;\n // --- revisioned ingestion ---\n /** Begin a bounded, cancellable ingest session. */\n beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;\n /** Atomically remove exactly one committed overlay; unknown ids are an\n * idempotent `{ removed: false }`. */\n removeOverlay(overlayId: string): { removed: boolean };\n // --- history ---\n /** Undo the most recent uncontrolled mutation entry (selection / hidden /\n * pins / scope / brushes). Returns false when there is nothing to undo. */\n undo(): boolean;\n /** Re-apply the most recently undone entry. False when nothing to redo. */\n redo(): boolean;\n // --- timeline playback ---\n /** Play a brush window across a numeric/temporal dimension's domain\n * (crossfilter mask fast path; one playing dimension at a time). */\n playTimeline(key: string, playback?: Partial<TimelinePlayback>): void;\n pauseTimeline(): void;\n // --- crossfilter ---\n /** The crossfilter session facade, or null until the `crossfilter` prop\n * has configured dimensions over an accepted base. */\n getCrossfilterSession(): CrossfilterSession | null;\n // --- styling reads ---\n /** legend surface: the active Scale on a styling channel plus its\n * resolved domain or categorical rows; null when not scale-valued. */\n getScaleInfo(channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null;\n /** metric read for one node id (built-in degree family + admitted\n * columns); null for unknown ids or metrics and for null/non-finite values. */\n getMetricValue(metric: MetricName, id: NodeId): number | null;\n getRevisions(): Revisions;\n getDiagnostics(): readonly GraphDiagnostic[];\n instance: GraphInstance<N, E>;\n}\n\n/** Last-committed declarative props. Fields are `| undefined` (not optional)\n * so the diff record is assignable under exactOptionalPropertyTypes. */\ninterface CommittedProps<N, E> {\n data: GraphSnapshotInput<N, E> | undefined;\n nodeColor: Accessor<GraphNode<N>, string> | Scale<string, N> | undefined;\n /** Canonical structural key when the committed nodeColor is a Scale;\n * undefined for accessor/constant forms (those diff by reference). */\n nodeColorKey: string | undefined;\n nodeSize: Accessor<GraphNode<N>, number> | Scale<number, N> | undefined;\n nodeSizeKey: string | undefined;\n linkColor: Accessor<AcceptedEdge<E>, string> | undefined;\n linkWidth: Accessor<AcceptedEdge<E>, number> | undefined;\n layout: LayoutKind | undefined;\n simulation: SimulationConfig | undefined;\n /** JSON form of the last committed `theme` prop (small token set). */\n themeJson: string | undefined;\n metrics: readonly MetricColumn[] | undefined;\n nodeImage: ((node: GraphNode<N>) => string | null) | undefined;\n edgeArrows: boolean | undefined;\n showLinks: boolean | undefined;\n emphasisRing: boolean | undefined;\n selection: readonly NodeId[] | undefined;\n labels: LabelConfig<N> | undefined;\n accessibility: AccessibilityConfig<N> | undefined;\n /** JSON form of the last committed `subgraph` prop — specs are small, so\n * the diff is a structural string compare ('null' when scope cleared). */\n subgraphJson: string | undefined;\n /** Last committed `filter` prop + its canonical key: exprs compare\n * by JSON, function predicates by reference. */\n filter: FilterSpec<N, E> | null | undefined;\n filterKey: string | undefined;\n /** Last committed `crossfilter` prop, compared by array reference. */\n crossfilter: readonly DimensionSpec<N>[] | undefined;\n /** Last committed `groups` prop (structural diff via sameGroupSpecArrays;\n * — equal inline literals never re-forward). */\n groups: readonly GroupSpec[] | null | undefined;\n dataRef: JsonValue | undefined;\n /** Last committed `groupBy` prop (reference diff — function-bearing). */\n groupBy: GroupBySpec<N> | null | undefined;\n /** Last committed `clusters` prop (reference diff — function-bearing). */\n clusters: ClusterSpec<N> | null | undefined;\n /** Last committed `pinnedNodeIds` prop, compared by shallow element diff. */\n pinnedNodeIds: readonly NodeId[] | null | undefined;\n /** Last committed `searchIndex` prop (shallow element diff). */\n searchIndex: readonly string[] | undefined;\n}\n\n/**\n * Canonical key of a scale-valued styling channel: `canonicalScaleKey`\n * when the value is a Scale descriptor, undefined for constant/accessor forms\n * (which diff by reference). Equal inline scale literals therefore never\n * re-forward; a function `by` keys by reference identity inside the core.\n */\nfunction scaleKeyOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const kind = (value as { kind?: unknown }).kind;\n if (kind !== 'sequential' && kind !== 'categorical' && kind !== 'diverging') return undefined;\n return canonicalScaleKey(value as Scale<unknown>);\n}\n\n/** Diff one nodeColor/nodeSize channel: scale forms compare by canonical key,\n * everything else by reference (a scale↔accessor flip always differs). */\nfunction styleChannelChanged(\n prevValue: unknown,\n prevKey: string | undefined,\n nextValue: unknown,\n nextKey: string | undefined,\n): boolean {\n if (nextKey !== undefined || prevKey !== undefined) return nextKey !== prevKey;\n return prevValue !== nextValue;\n}\n\n/** Canonical key of one filter side: 'u' absent, 'fn' a function predicate\n * (paired with a reference compare), else the expr JSON. */\nfunction filterSideKey(side: FilterExpr | ((item: never) => boolean) | undefined): string {\n if (side === undefined) return 'u';\n if (typeof side === 'function') return 'fn';\n return JSON.stringify(side);\n}\n\nfunction filterKeyOf<N, E>(spec: FilterSpec<N, E> | null): string {\n if (spec === null) return 'null';\n return `${spec.mode ?? 'hide'}|${filterSideKey(spec.nodes)}|${filterSideKey(spec.edges)}`;\n}\n\n/** Canonical-equal check: keys match AND any function sides are the SAME\n * references (a changed predicate function must re-evaluate). */\nfunction sameFilterSpec<N, E>(\n prev: FilterSpec<N, E> | null | undefined,\n prevKey: string | undefined,\n next: FilterSpec<N, E> | null,\n nextKey: string,\n): boolean {\n if (prevKey === undefined || prevKey !== nextKey) return false;\n if (next === null) return prev === null;\n if (prev === null || prev === undefined) return false;\n if (typeof next.nodes === 'function' && next.nodes !== prev.nodes) return false;\n if (typeof next.edges === 'function' && next.edges !== prev.edges) return false;\n return true;\n}\n\ninterface CallbackHolder<N, E> {\n onNodeClick: ((payload: { node: GraphNode<N>; metaKey?: boolean }) => void) | undefined;\n onBackgroundClick: (() => void) | undefined;\n onNodeHover: ((payload: { node: GraphNode<N> | null }) => void) | undefined;\n onEdgeClick: ((payload: { edge: AcceptedEdge<E> }) => void) | undefined;\n onEdgeHover: ((payload: { edge: AcceptedEdge<E> | null }) => void) | undefined;\n onNodeDragStart: ((payload: { node: GraphNode<N> }) => void) | undefined;\n onNodeDragEnd: ((payload: { node: GraphNode<N>; x: number; y: number }) => void) | undefined;\n onSelectionChange: ((payload: SelectionState) => void) | undefined;\n onDegrade: ((event: DegradeEvent) => void) | undefined;\n onPerfSample: ((snapshot: GraphPerfSnapshot) => void) | undefined;\n onGroupClick: ((payload: { group: ResolvedGroup; metaKey?: boolean }) => void) | undefined;\n onMetaEdgeClick: ((payload: { metaEdge: MetaEdge }) => void) | undefined;\n onGroupsChange: ((groups: readonly ResolvedGroup[]) => void) | undefined;\n onViewStateRestore:\n | ((intent: {\n transactionId: string;\n source: 'setViewState' | 'undo' | 'redo';\n next: unknown;\n }) => void)\n | undefined;\n onViewStateMismatch:\n | ((stored: JsonValue | undefined, current: JsonValue | undefined) => void)\n | undefined;\n onPinnedChange: ((pinnedNodeIds: readonly NodeId[]) => void) | undefined;\n onViewportChange: ((v: ViewportState) => void) | undefined;\n onReady: (() => void) | undefined;\n onError: ((payload: { error: Error }) => void) | undefined;\n}\n\nfunction sameIdArrays(a: readonly NodeId[], b: readonly NodeId[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nconst CONTAINER_BASE_STYLE: CSSProperties = {\n position: 'relative',\n width: '100%',\n height: '100%',\n};\n\n// The canvas layer fills the outer div; children render after it in DOM order\n// so they naturally stack above the canvas.\nconst CANVAS_STYLE: CSSProperties = { position: 'absolute', inset: 0 };\n\n/** Positioned wrapper for the `renderLegend` slot: bottom-left, above\n * the canvas, interactive (category rows are clickable). */\nconst LEGEND_SLOT_STYLE: CSSProperties = {\n position: 'absolute',\n left: 12,\n bottom: 12,\n};\n\n/** `renderLegend` slot host: re-reads getScaleInfo on every store publication\n * that can move scale info (model/scope/render revisions) or theme change,\n * and re-invokes the host's render function with the fresh snapshot. */\nfunction LegendSlot<N, E>(props: {\n instance: GraphInstance<N, E>;\n renderLegend: (info: GraphLegendRenderInfo<N>) => ReactNode;\n}): ReactElement {\n const { instance } = props;\n const { version, theme } = useScaleInfoSubscription(instance);\n const info = useMemo<GraphLegendRenderInfo<N>>(\n () => ({\n nodeColor: instance.getScaleInfo('nodeColor'),\n nodeSize: instance.getScaleInfo('nodeSize'),\n theme,\n }),\n // `version` is the invalidation token: a new store revision can change\n // what getScaleInfo returns without changing any other dependency.\n [instance, theme, version],\n );\n return (\n <div data-orbit-legend-slot=\"\" style={LEGEND_SLOT_STYLE}>\n {props.renderLegend(info)}\n </div>\n );\n}\n\nfunction GraphInner<N, E>(\n props: GraphProps<N, E>,\n ref: ForwardedRef<GraphHandle<N, E>>,\n): ReactElement {\n // Lazy, once per mounted component. `engine`/`fitViewOnFirstData`/\n // `searchIndex` are construction options (D7); later changes to them are\n // intentionally ignored (searchIndex additionally warns once below).\n const [instance] = useState<GraphInstance<N, E>>(() => {\n const options: CreateGraphInstanceOptions<N, E> = { engine: props.engine };\n if (props.fitViewOnFirstData !== undefined) {\n options.fitViewOnFirstData = props.fitViewOnFirstData;\n }\n if (props.searchIndex !== undefined) options.searchIndex = props.searchIndex;\n if (props.limits !== undefined) options.limits = props.limits;\n if (props.execution !== undefined) options.execution = props.execution;\n if (props.workerFactory !== undefined) options.workerFactory = props.workerFactory;\n if (props.services !== undefined) options.services = props.services;\n return createGraphInstance<N, E>(options);\n });\n const searchIndexWarnedRef = useRef(false);\n const limitsWarnedRef = useRef(false);\n const initialLimitsRef = useRef(props.limits);\n if (props.limits !== initialLimitsRef.current && !limitsWarnedRef.current) {\n limitsWarnedRef.current = true;\n console.warn(\n 'orbit: `limits` is a construction-only prop — the runtime change is ignored; remount with a key to apply new thresholds.',\n );\n }\n const executionWarnedRef = useRef(false);\n const initialExecutionRef = useRef(props.execution);\n if (props.execution !== initialExecutionRef.current && !executionWarnedRef.current) {\n executionWarnedRef.current = true;\n console.warn(\n 'orbit: `execution` is a construction-only prop — the runtime change is ignored; remount with a key to switch lanes.',\n );\n }\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n\n // --- latest-callback holder: event subscriptions read through this ref so\n // per-render callback identity churn never resubscribes ---\n const callbacksRef = useRef<CallbackHolder<N, E>>({\n onNodeClick: undefined,\n onBackgroundClick: undefined,\n onNodeHover: undefined,\n onEdgeClick: undefined,\n onEdgeHover: undefined,\n onNodeDragStart: undefined,\n onNodeDragEnd: undefined,\n onSelectionChange: undefined,\n onDegrade: undefined,\n onPerfSample: undefined,\n onGroupClick: undefined,\n onMetaEdgeClick: undefined,\n onGroupsChange: undefined,\n onViewStateRestore: undefined,\n onViewStateMismatch: undefined,\n onPinnedChange: undefined,\n onViewportChange: undefined,\n onReady: undefined,\n onError: undefined,\n });\n useLayoutEffect(() => {\n callbacksRef.current = {\n onNodeClick: props.onNodeClick,\n onBackgroundClick: props.onBackgroundClick,\n onNodeHover: props.onNodeHover,\n onEdgeClick: props.onEdgeClick,\n onEdgeHover: props.onEdgeHover,\n onNodeDragStart: props.onNodeDragStart,\n onNodeDragEnd: props.onNodeDragEnd,\n onSelectionChange: props.onSelectionChange,\n onDegrade: props.onDegrade,\n onPerfSample: props.onPerfSample,\n onGroupClick: props.onGroupClick,\n onMetaEdgeClick: props.onMetaEdgeClick,\n onGroupsChange: props.onGroupsChange,\n onViewStateRestore: props.onViewStateRestore,\n onViewStateMismatch: props.onViewStateMismatch,\n onPinnedChange: props.onPinnedChange,\n onViewportChange: props.onViewportChange,\n onReady: props.onReady,\n onError: props.onError,\n };\n });\n\n // --- event subscriptions: keyed on the instance only; identity churn of\n // the props is absorbed by callbacksRef. A LAYOUT effect so it commits\n // BEFORE the prop-diff layout effect of the NEXT render pass — and on the\n // first pass the subscriptions land in the same commit as the initial\n // host update's effects; events fired during that first applyHostUpdate\n // (e.g. a mount-time 'degrade' engagement) are re-emittable states\n // that the ladder re-evaluates, but ordering here still matters for any\n // strictly edge-triggered channel, so subscriptions must not lag a frame\n // behind the data. ---\n useLayoutEffect(() => {\n const offs = [\n instance.on('nodeClick', (p) => callbacksRef.current.onNodeClick?.(p)),\n instance.on('backgroundClick', () => callbacksRef.current.onBackgroundClick?.()),\n instance.on('nodeHover', (p) => callbacksRef.current.onNodeHover?.(p)),\n instance.on('edgeClick', (p) => callbacksRef.current.onEdgeClick?.(p)),\n instance.on('edgeHover', (p) => callbacksRef.current.onEdgeHover?.(p)),\n instance.on('nodeDragStart', (p) => callbacksRef.current.onNodeDragStart?.(p)),\n instance.on('nodeDragEnd', (p) => callbacksRef.current.onNodeDragEnd?.(p)),\n instance.on('selectionChange', (p) => callbacksRef.current.onSelectionChange?.(p)),\n instance.on('degrade', (p) => callbacksRef.current.onDegrade?.(p)),\n instance.on('perfSample', (p) => callbacksRef.current.onPerfSample?.(p)),\n instance.on('groupClick', (p) => callbacksRef.current.onGroupClick?.(p)),\n instance.on('metaEdgeClick', (p) => callbacksRef.current.onMetaEdgeClick?.(p)),\n instance.on('groupsChange', (p) => callbacksRef.current.onGroupsChange?.(p.groups)),\n instance.on('viewStateRestore', (p) => callbacksRef.current.onViewStateRestore?.(p)),\n instance.on('viewStateMismatch', (p) =>\n callbacksRef.current.onViewStateMismatch?.(p.stored, p.current),\n ),\n instance.on('pinnedChange', (p) => callbacksRef.current.onPinnedChange?.(p.pinnedNodeIds)),\n instance.on('viewportChange', (v) => callbacksRef.current.onViewportChange?.(v)),\n instance.on('ready', () => callbacksRef.current.onReady?.()),\n instance.on('error', (p) => callbacksRef.current.onError?.(p)),\n ];\n return () => {\n for (const off of offs) off();\n };\n }, [instance]);\n\n // --- prop → host update: diff against the previous committed render and\n // issue at most one applyHostUpdate with exactly the changed keys ---\n const prevRef = useRef<CommittedProps<N, E> | null>(null);\n useLayoutEffect(() => {\n const prev = prevRef.current;\n const update: GraphHostUpdate<N, E> = {};\n let dirty = false;\n\n if (props.data !== undefined && (prev === null || prev.data !== props.data)) {\n update.data = props.data;\n dirty = true;\n }\n // scale-aware channels: Scale descriptors diff by canonical\n // structural key (equal inline literals never re-forward); accessor and\n // constant forms keep the reference diff.\n const nodeColorKey = scaleKeyOf(props.nodeColor);\n if (\n props.nodeColor !== undefined &&\n (prev === null ||\n styleChannelChanged(prev.nodeColor, prev.nodeColorKey, props.nodeColor, nodeColorKey))\n ) {\n update.nodeColor = props.nodeColor;\n dirty = true;\n }\n const nodeSizeKey = scaleKeyOf(props.nodeSize);\n if (\n props.nodeSize !== undefined &&\n (prev === null ||\n styleChannelChanged(prev.nodeSize, prev.nodeSizeKey, props.nodeSize, nodeSizeKey))\n ) {\n update.nodeSize = props.nodeSize;\n dirty = true;\n }\n if (props.linkColor !== undefined && (prev === null || prev.linkColor !== props.linkColor)) {\n update.linkColor = props.linkColor;\n dirty = true;\n }\n if (props.linkWidth !== undefined && (prev === null || prev.linkWidth !== props.linkWidth)) {\n update.linkWidth = props.linkWidth;\n dirty = true;\n }\n if (props.layout !== undefined && (prev === null || prev.layout !== props.layout)) {\n update.layout = props.layout;\n dirty = true;\n }\n if (\n props.simulation !== undefined &&\n (prev === null || prev.simulation !== props.simulation)\n ) {\n update.simulation = props.simulation;\n dirty = true;\n }\n // theme: a small token set — diffed structurally (JSON), so a\n // new-but-equal inline object never re-publishes.\n let themeJson: string | undefined;\n if (props.theme !== undefined) {\n themeJson = JSON.stringify(props.theme);\n if (prev === null || prev.themeJson !== themeJson) {\n update.theme = props.theme;\n dirty = true;\n }\n }\n // metric columns: array-reference diff (columns are one-shot joins).\n if (props.metrics !== undefined && (prev === null || prev.metrics !== props.metrics)) {\n update.metrics = props.metrics;\n dirty = true;\n }\n // image refs: function-prop contract — reference diff.\n if (props.nodeImage !== undefined && (prev === null || prev.nodeImage !== props.nodeImage)) {\n update.nodeImage = props.nodeImage;\n dirty = true;\n } else if (props.nodeImage === undefined && prev !== null && prev.nodeImage !== undefined) {\n // Removing the prop forwards the explicit clear; the natural\n // React idiom must not silently freeze the last accessor's images.\n update.nodeImage = null;\n dirty = true;\n }\n // Display toggles use plain value diffing for config-only commits.\n if (props.edgeArrows !== undefined && (prev === null || prev.edgeArrows !== props.edgeArrows)) {\n update.edgeArrows = props.edgeArrows;\n dirty = true;\n }\n if (props.showLinks !== undefined && (prev === null || prev.showLinks !== props.showLinks)) {\n update.showLinks = props.showLinks;\n dirty = true;\n }\n if (\n props.emphasisRing !== undefined &&\n (prev === null || prev.emphasisRing !== props.emphasisRing)\n ) {\n update.emphasisRing = props.emphasisRing;\n dirty = true;\n } else if (\n props.emphasisRing === undefined &&\n prev !== null &&\n prev.emphasisRing !== undefined\n ) {\n // Removing the prop restores the documented default (true); the\n // natural React idiom must not leave the ring silently disabled.\n update.emphasisRing = true;\n dirty = true;\n }\n if (\n props.selection !== undefined &&\n (prev === null ||\n prev.selection === undefined ||\n !sameIdArrays(prev.selection, props.selection))\n ) {\n update.selection = props.selection;\n dirty = true;\n }\n if (props.labels !== undefined && (prev === null || prev.labels !== props.labels)) {\n update.labels = props.labels;\n dirty = true;\n }\n if (\n props.accessibility !== undefined &&\n (prev === null || prev.accessibility !== props.accessibility)\n ) {\n update.accessibility = props.accessibility;\n dirty = true;\n }\n // hard scope: structural (JSON) diff — a new-but-equal spec object\n // never re-publishes. An omitted prop is never forwarded (uncontrolled).\n let subgraphJson: string | undefined;\n if (props.subgraph !== undefined) {\n subgraphJson = JSON.stringify(props.subgraph);\n if (prev === null || prev.subgraphJson !== subgraphJson) {\n update.subgraph = props.subgraph;\n dirty = true;\n }\n }\n // soft filter: canonical structural diff (exprs by JSON, function\n // predicates by reference). An omitted prop is never forwarded.\n let filterKey: string | undefined;\n if (props.filter !== undefined) {\n filterKey = filterKeyOf(props.filter);\n if (\n prev === null ||\n !sameFilterSpec(prev.filter, prev.filterKey, props.filter, filterKey)\n ) {\n update.filter = props.filter;\n dirty = true;\n }\n }\n // crossfilter dimensions: forwarded on array-reference change; the\n // core no-ops when every DimensionSpec element is reference-equal.\n if (\n props.crossfilter !== undefined &&\n (prev === null || prev.crossfilter !== props.crossfilter)\n ) {\n update.crossfilter = props.crossfilter;\n dirty = true;\n }\n // groups: structural diff (equal inline literals never\n // re-forward). An omitted prop is never forwarded — but ONCE provided\n // the core latches the slice controlled.\n if (props.dataRef !== undefined) {\n const prevRef = prev === null ? undefined : prev.dataRef;\n if (prevRef === undefined || canonicalJson(props.dataRef) !== canonicalJson(prevRef)) {\n update.dataRef = props.dataRef;\n dirty = true;\n }\n }\n if (props.groups !== undefined) {\n const prevGroups = prev === null ? undefined : prev.groups;\n if (prevGroups === undefined || !sameGroupSpecArrays(prevGroups, props.groups)) {\n update.groups = props.groups;\n dirty = true;\n }\n }\n // groupBy: reference diff (the spec carries a function accessor);\n // the core additionally no-ops structurally equal respecs.\n if (props.groupBy !== undefined && (prev === null || prev.groupBy !== props.groupBy)) {\n update.groupBy = props.groupBy;\n dirty = true;\n }\n // clusters: reference diff (function-bearing spec); the core\n // re-derives membership only on a `by` identity change.\n if (props.clusters !== undefined && (prev === null || prev.clusters !== props.clusters)) {\n update.clusters = props.clusters;\n dirty = true;\n }\n // pinnedNodeIds: shallow element diff (a small id list); ONCE\n // provided the core latches the slice controlled.\n if (props.pinnedNodeIds !== undefined) {\n const prevPinned = prev === null ? undefined : prev.pinnedNodeIds;\n const changed =\n prevPinned === undefined ||\n prevPinned === null ||\n props.pinnedNodeIds === null ||\n !sameIdArrays(prevPinned, props.pinnedNodeIds);\n if (changed) {\n update.pinnedNodeIds = props.pinnedNodeIds;\n dirty = true;\n }\n }\n // searchIndex is construction-only and is read once above. A\n // changed prop cannot take effect without a keyed remount — warn once\n // (shallow element diff, so a new-but-equal inline literal is fine).\n if (\n prev !== null &&\n !sameIdArrays(prev.searchIndex ?? [], props.searchIndex ?? []) &&\n !searchIndexWarnedRef.current\n ) {\n searchIndexWarnedRef.current = true;\n console.warn(\n '[orbit] searchIndex is construction-only: the changed value was ignored — key-remount <Graph> to change it',\n );\n }\n\n prevRef.current = {\n data: props.data,\n nodeColor: props.nodeColor,\n nodeColorKey,\n nodeSize: props.nodeSize,\n nodeSizeKey,\n linkColor: props.linkColor,\n linkWidth: props.linkWidth,\n layout: props.layout,\n simulation: props.simulation,\n themeJson,\n metrics: props.metrics,\n nodeImage: props.nodeImage,\n edgeArrows: props.edgeArrows,\n showLinks: props.showLinks,\n emphasisRing: props.emphasisRing,\n selection: props.selection,\n labels: props.labels,\n accessibility: props.accessibility,\n subgraphJson,\n filter: props.filter,\n filterKey,\n crossfilter: props.crossfilter,\n groups: props.groups,\n dataRef: props.dataRef,\n groupBy: props.groupBy,\n clusters: props.clusters,\n pinnedNodeIds: props.pinnedNodeIds,\n searchIndex: props.searchIndex,\n };\n\n if (dirty) instance.applyHostUpdate(update);\n });\n\n // Register the instance-wide default unavailable-result\n // callback so <GraphSearch> (and bespoke activation UIs) resolve it without\n // <Graph> in their import graph. Identity churn is fine — registration is a\n // WeakMap write, never a re-render.\n useLayoutEffect(() => {\n setSearchResultUnavailableCallback(instance, props.onSearchResultUnavailable);\n });\n\n\n // --- attach/detach: StrictMode's simulated remount detaches (destroying\n // the first engine) and re-attaches (fresh engine + full state replay) ---\n useEffect(() => {\n const container = containerRef.current;\n if (container === null) return undefined;\n // Each StrictMode setup (and each real mount) owns exactly one attach\n // result. Its promise may settle after cleanup because engine mounting is\n // asynchronous; a discarded generation must never inspect the newer\n // instance state or call the latest component callbacks.\n let active = true;\n void instance.attach(container).catch((err: unknown) => {\n if (!active) return;\n // A fatal mount fault already reached onError via the 'error' event.\n if (instance.store.getState().status === 'error') return;\n const error = err instanceof Error ? err : new Error(String(err));\n callbacksRef.current.onError?.({ error });\n });\n return () => {\n active = false;\n instance.detach();\n };\n }, [instance]);\n\n // --- destroy on true unmount only. destroy is irreversible, so the\n // cleanup defers it one microtask; a StrictMode remount re-runs the setup\n // synchronously first and cancels it, leaving detach/attach replay to\n // restore the engine. A real unmount has no re-setup, so destroy fires. ---\n const destroyGateRef = useRef({ pendingDestroy: false });\n useEffect(() => {\n const gate = destroyGateRef.current;\n gate.pendingDestroy = false;\n return () => {\n gate.pendingDestroy = true;\n queueMicrotask(() => {\n if (gate.pendingDestroy) instance.destroy();\n });\n };\n }, [instance]);\n\n // --- reduced motion: the binding detects the media preference and\n // reports it via instance.setReducedMotion; accessibility.reducedMotion\n // overrides inside the core. Subscribed for live preference changes. ---\n useEffect(() => {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return undefined;\n }\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n const surface = overlaySurface(instance);\n surface.setReducedMotion?.(mq.matches);\n const onChange = (e: MediaQueryListEvent): void => {\n surface.setReducedMotion?.(e.matches);\n };\n if (typeof mq.addEventListener === 'function') {\n mq.addEventListener('change', onChange);\n return () => {\n mq.removeEventListener('change', onChange);\n };\n }\n // Legacy MediaQueryList (Safari < 14).\n if (typeof mq.addListener === 'function') {\n mq.addListener(onChange);\n return () => {\n mq.removeListener(onChange);\n };\n }\n return undefined;\n }, [instance]);\n\n useImperativeHandle(\n ref,\n (): GraphHandle<N, E> => ({\n fitView: (_opts?: FitViewOptions) => {\n instance.fitView();\n },\n zoomIn: () => {\n instance.zoomIn();\n },\n zoomOut: () => {\n instance.zoomOut();\n },\n setViewport: (v: Partial<ViewportState>, _opts?: { durationMs?: number }) => {\n instance.setViewport(v);\n },\n requestNodeContextMenu: (id: NodeId, screen: readonly [number, number]) => {\n instance.requestNodeContextMenu(id, screen);\n },\n getViewState: (opts?: { includePositions?: false }): GraphViewState =>\n instance.getViewState(opts),\n setViewState: (\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult> => instance.setViewState(raw, opts),\n exportImage: ((format: 'png' | 'svg', opts?: { maxSvgElements?: number; fallback?: 'raster-hybrid' }) =>\n format === 'png' ? instance.exportImage('png') : instance.exportImage('svg', opts)) as GraphHandle<N, E>['exportImage'],\n exportData: (scope?: 'visible' | 'accepted', opts?: { limit?: number }) =>\n instance.exportData(scope, opts),\n exportDataStream: (scope?: 'visible' | 'accepted') => instance.exportDataStream(scope),\n exportLayout: (opts?: { limit?: number }) => instance.exportLayout(opts),\n exportLayoutStream: () => instance.exportLayoutStream(),\n focusNode: (id: NodeId) => {\n instance.focusNode(id);\n },\n emphasizeNode: (id: NodeId | null) => {\n instance.emphasizeNode(id);\n },\n getPerfSnapshot: () => instance.getPerfSnapshot(),\n setSelection: (ids: readonly NodeId[]) => {\n instance.setSelection(ids);\n },\n findPath: (sourceId: NodeId, targetId: NodeId, options?: PathOptions) =>\n instance.findPath(sourceId, targetId, options),\n clearPath: () => {\n instance.clearPath();\n },\n clearSelection: () => {\n instance.clearSelection();\n },\n selectNeighbors: (id?: NodeId) => {\n instance.selectNeighbors(id);\n },\n selectAll: () => {\n instance.selectAll();\n },\n invertSelection: () => {\n instance.invertSelection();\n },\n selectWithinPolygon: (\n polygon: readonly [number, number][],\n opts?: { additive?: boolean },\n ): readonly NodeId[] => instance.selectWithinPolygon(polygon, opts),\n pinNode: (id: NodeId, xy?: readonly [number, number]) => {\n instance.pinNode(id, xy);\n },\n unpinNode: (id: NodeId) => {\n instance.unpinNode(id);\n },\n clearPins: () => {\n instance.clearPins();\n },\n hideNodes: (ids: readonly NodeId[]) => {\n instance.hideNodes(ids);\n },\n showAll: () => {\n instance.showAll();\n },\n isolateSelection: () => {\n instance.isolateSelection();\n },\n resetIsolation: () => {\n instance.resetIsolation();\n },\n expandNode: (id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult> =>\n instance.expandNode(id, opts),\n retractExpansion: (id: NodeId) => {\n instance.retractExpansion(id);\n },\n pinNodes: (ids: readonly NodeId[]) => {\n instance.pinNodes(ids);\n },\n unpinNodes: (ids: readonly NodeId[]) => {\n instance.unpinNodes(ids);\n },\n groupNodes: (spec: GroupSpec) => {\n instance.groupNodes(spec);\n },\n ungroup: (groupId: string) => {\n instance.ungroup(groupId);\n },\n setGroupCollapsed: (groupId: string, collapsed: boolean) => {\n instance.setGroupCollapsed(groupId, collapsed);\n },\n foldNode: (id: NodeId, opts?: { memberIds?: readonly NodeId[] }) => {\n instance.foldNode(id, opts);\n },\n unfoldNode: (id: NodeId) => {\n instance.unfoldNode(id);\n },\n getFold: (id: NodeId): { memberIds: readonly NodeId[] } | null => instance.getFold(id),\n getClusters: (): readonly ResolvedCluster[] => instance.getClusters(),\n selectCluster: (key: string, opts?: { additive?: boolean }) => {\n instance.selectCluster(key, opts);\n },\n beginIngest: (opts: BeginIngestOptions): IngestSession<N, E> => instance.beginIngest(opts),\n removeOverlay: (overlayId: string): { removed: boolean } =>\n instance.removeOverlay(overlayId),\n undo: (): boolean => instance.undo(),\n redo: (): boolean => instance.redo(),\n playTimeline: (key: string, playback?: Partial<TimelinePlayback>) => {\n instance.playTimeline(key, playback);\n },\n pauseTimeline: () => {\n instance.pauseTimeline();\n },\n getCrossfilterSession: (): CrossfilterSession | null => instance.getCrossfilterSession(),\n getScaleInfo: (channel: 'nodeColor' | 'nodeSize'): ScaleChannelInfo<N> | null =>\n instance.getScaleInfo(channel),\n getMetricValue: (metric: MetricName, id: NodeId): number | null =>\n instance.getMetricValue(metric, id),\n getRevisions: () => instance.getRevisions(),\n getDiagnostics: () => instance.getDiagnostics(),\n instance,\n }),\n [instance],\n );\n\n // --- container ARIA surface ---\n const descriptionId = useId();\n const description = props.accessibility?.description;\n\n return (\n <div className={props.className} style={{ ...CONTAINER_BASE_STYLE, ...props.style }}>\n <GraphProvider instance={instance}>\n {/* Engine-owned canvas layer: must never contain React children. */}\n <div\n ref={containerRef}\n data-orbit-canvas=\"\"\n style={CANVAS_STYLE}\n role=\"application\"\n aria-label={props.accessibility?.label ?? 'Graph visualization'}\n aria-describedby={description !== undefined ? descriptionId : undefined}\n />\n {description !== undefined ? (\n <div id={descriptionId} style={visuallyHiddenStyle}>\n {description}\n </div>\n ) : null}\n {/* DOM label lane: candidates via React, positions imperative. */}\n {props.labels?.enabled !== false ? (\n <LabelLayer\n labelClassName={props.labelClassName}\n renderNodeLabel={props.renderNodeLabel}\n clusterLabelClassName={props.clusterLabelClassName}\n renderClusterLabel={props.renderClusterLabel}\n />\n ) : null}\n {/* Lasso overlay: above the canvas, pointer-inert unless Shift-drag. */}\n {props.enableLasso !== false ? <Lasso /> : null}\n {/* live region: store-driven, coalesced, visually hidden. */}\n <LiveRegion announcements={props.accessibility?.announcements} />\n {/* legend slot: positioned wrapper re-rendered on scale/theme\n changes; the host returns custom JSX (or composes <GraphLegend>). */}\n {props.renderLegend !== undefined ? (\n <LegendSlot instance={instance} renderLegend={props.renderLegend} />\n ) : null}\n {props.children}\n </GraphProvider>\n </div>\n );\n}\n\n/** forwardRef erases generics; restore them with a typed call signature. */\ntype GraphComponent = <N = Record<string, unknown>, E = Record<string, unknown>>(\n props: GraphProps<N, E> & RefAttributes<GraphHandle<N, E>>,\n) => ReactElement;\n\nexport const Graph = forwardRef(GraphInner) as GraphComponent;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modernrelay/orbit-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "React binding for Orbit: <Graph> plus tree-shakeable components (toolbar, search, minimap, tooltip, inspector, table, navigator, and more).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@modernrelay/orbit-core": "0.
|
|
78
|
+
"@modernrelay/orbit-core": "0.15.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
81
|
"react": ">=18",
|