@modernrelay/orbit-react 0.2.0 → 0.13.5

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.
@@ -80,6 +80,10 @@ function GraphNavigator(props) {
80
80
  useEffect(() => {
81
81
  if (root !== null && root.model !== slice.model) setRoot(null);
82
82
  }, [root, slice.model]);
83
+ const releaseEmphasis = useCallback(() => {
84
+ if (instance.store.getState().hover.nodeId === null) instance.emphasizeNode(null);
85
+ }, [instance]);
86
+ useEffect(() => () => releaseEmphasis(), [releaseEmphasis]);
83
87
  if (degreesRef.current.model !== slice.model) {
84
88
  degreesRef.current = { model: slice.model, counts: /* @__PURE__ */ new Map() };
85
89
  }
@@ -226,6 +230,7 @@ function GraphNavigator(props) {
226
230
  const row = rows[Math.min(Math.max(index, 0), rows.length - 1)];
227
231
  if (row === void 0) return;
228
232
  setActiveKey(row.key);
233
+ instance.emphasizeNode(row.nodeId);
229
234
  focusItemSoon();
230
235
  };
231
236
  const pageBy = (delta) => {
@@ -238,7 +243,10 @@ function GraphNavigator(props) {
238
243
  if (next === current) return;
239
244
  setPages((p) => ({ ...p, [listKey]: next }));
240
245
  const first = items[next * windowSize];
241
- if (first !== void 0) setActiveKey(`${listKey}:${first}`);
246
+ if (first !== void 0) {
247
+ setActiveKey(`${listKey}:${first}`);
248
+ instance.emphasizeNode(first);
249
+ }
242
250
  focusItemSoon();
243
251
  };
244
252
  const activate = (row) => {
@@ -263,6 +271,7 @@ function GraphNavigator(props) {
263
271
  const clearTransientFocus = () => {
264
272
  setRoot(null);
265
273
  setActiveKey(null);
274
+ releaseEmphasis();
266
275
  focusItemSoon();
267
276
  };
268
277
  const onKeyDown = (e) => {
@@ -310,6 +319,11 @@ function GraphNavigator(props) {
310
319
  "aria-label": panelLabel,
311
320
  className: props.className,
312
321
  style: { ...PANEL_STYLE, ...props.style },
322
+ onBlur: (e) => {
323
+ if (!e.currentTarget.contains(e.relatedTarget)) {
324
+ releaseEmphasis();
325
+ }
326
+ },
313
327
  children: [
314
328
  /* @__PURE__ */ jsx(
315
329
  "div",
@@ -337,6 +351,7 @@ function GraphNavigator(props) {
337
351
  },
338
352
  onClick: () => {
339
353
  setActiveKey(row.key);
354
+ instance.emphasizeNode(row.nodeId);
340
355
  focusItemSoon();
341
356
  },
342
357
  style: isActive ? ACTIVE_ITEM_STYLE : ITEM_STYLE,
@@ -1 +1 @@
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 (spec §15.1; S7-T11/T19).\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** (S11-T15) — the last completed §16.5 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` (§16.5 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 (§14 untrusted-content rule; markup in labels stays literal).\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 (§15.1): 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 * (§6.4) — `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/** §15.1 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 (§15.1). */\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 §16.5 search (S11-T15); 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/** §15.1 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 // 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 // §15.1: 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 // §16.5: 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 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) setActiveKey(`${listKey}:${first}`);\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 §16.5\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 §6.4 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 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 >\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 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":";;;;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 (spec §15.1; S7-T11/T19).\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** (S11-T15) — the last completed §16.5 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` (§16.5 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 (§14 untrusted-content rule; markup in labels stays literal).\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 (§15.1): 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 * (§6.4) — `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/** §15.1 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 (§15.1). */\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 §16.5 search (S11-T15); 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/** §15.1 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 // §13: 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 // §15.1: 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 // §16.5: 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 // §13/§15.1 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 §16.5\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 §6.4 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"]}
@@ -7,9 +7,9 @@ import { G as GraphCorner } from '../shared-Bj3Gk219.js';
7
7
  * `@modernrelay/orbit-react/components/SimControls` — `<GraphSimControls>`,
8
8
  * the §16.11 simulation control panel (S12-T12).
9
9
  *
10
- * Live sliders over every `SimulationConfig` field (gravity, repulsion,
11
- * friction, linkDistance, linkSpring, collision, decay, repulsionTheta,
12
- * center the full §10 tunable set), a speed
10
+ * Live sliders over the effective `SimulationConfig` fields (gravity,
11
+ * repulsion, friction, linkDistance, linkSpring, collision, decay, center —
12
+ * repulsionTheta is omitted: inert on cosmos >= 3.4), a speed
13
13
  * preset toggle, and a reheat button. Every control write is ONE atomic
14
14
  * host update — `applyHostUpdate({ simulation: merged })`, the same lane the
15
15
  * `<Graph>` `simulation` prop uses — which sinks as a single config-only
@@ -13,7 +13,9 @@ var SIM_FIELDS = [
13
13
  // what turns the force on.
14
14
  { key: "collision", label: "Collision", min: 0, max: 2, step: 0.01, fallback: 0 },
15
15
  { key: "decay", label: "Cool-down", min: 100, max: 2e4, step: 100, fallback: 5e3 },
16
- { key: "repulsionTheta", label: "Repulsion \u03B8", min: 0.3, max: 2, step: 0.05, fallback: 1.15 },
16
+ // repulsionTheta is deliberately absent: cosmos >= 3.4 ignores it
17
+ // (grid-based repulsion) — a slider that visibly does nothing is worse
18
+ // than no slider. The SimulationConfig field survives for other engines.
17
19
  { key: "center", label: "Centering", min: 0, max: 1, step: 0.01, fallback: 0 }
18
20
  ];
19
21
  var SPEED_NORMAL_DECAY = 5e3;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/SimControls/index.tsx"],"names":[],"mappings":"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","file":"SimControls.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/SimControls` — `<GraphSimControls>`,\n * the §16.11 simulation control panel (S12-T12).\n *\n * Live sliders over every `SimulationConfig` field (gravity, repulsion,\n * friction, linkDistance, linkSpring, collision, decay, repulsionTheta,\n * center — the full §10 tunable set), a speed\n * preset toggle, and a reheat button. Every control write is ONE atomic\n * host update — `applyHostUpdate({ simulation: merged })`, the same lane the\n * `<Graph>` `simulation` prop uses — which sinks as a single config-only\n * `engine.commit`: no structure, no buffers, no restart, positions untouched\n * (R-16.11-02, E1).\n *\n * ## Current-value resolution (v0.10, investigated)\n * The store publishes NO simulation/layout lane and the instance exposes no\n * config getter — `layout`/`simulation` are instance-local in\n * `createGraphInstance`; `GraphStoreState` carries only `simulationRunning`.\n * The panel therefore mirrors the caller's `simulation` prop (the same\n * object handed to `<Graph>`) and merges control writes over that mirror; a\n * prop identity change re-seeds the mirror (the external writer wins). Like\n * `subgraph` (§9.2), the lane is last-writer-wins: the panel and the\n * `<Graph>` prop write the same state. `onSimulationChange` fires with the\n * full merged config after every write so controlled callers can loop it\n * back through their `simulation` prop (the spec's `onLayoutChange` seam\n * traffics in the LayoutSpec object form and is post-v0.10).\n *\n * ## Applicability (R-16.11-04)\n * Rendered ONLY under layout `'force'` — the `layout` prop mirrors the\n * caller's `<Graph>` prop for the same no-observable-lane reason, defaulting\n * to `'force'` (the instance construction default). Under `'fixed'` the\n * panel renders nothing. The spec's third case — `static` while its\n * convergence run is active — is N/A in v0.10: `LayoutKind` is\n * `'force' | 'fixed'` (core types.ts) and the store exposes no\n * convergence-run state; wire it when the `static` variant lands.\n *\n * ## Reheat\n * The spec names `reheat(alpha)`; the shipped instance surface is\n * `resumeSimulation()` (§16.1 simulation controls — sinks to\n * `engine.start()` and publishes `simulationRunning`). The button calls\n * that; the alpha parameter follows when core grows the reheat API.\n *\n * ## Speed toggle\n * A preset over the `decay` lane — the §10 cool-down tunable (cosmos\n * `simulationDecay`), which core exposes as of v0.10.2. ON writes a smaller\n * coefficient so the run cools SLOWER and stays lively; OFF restores the\n * pre-toggle value, or the engine default when the field was unset. The\n * toggle and the Cool-down slider are two views over one lane, so moving\n * either is reflected by the other. Same config-only commit discipline as\n * every slider. (Before v0.10.2 this proxied through `friction` because\n * `decay` had no core lane — that workaround is now retired.)\n *\n * ## Keyboard & ARIA (§15.1)\n * Native `<input type=\"range\">`/checkbox controls with `<label htmlFor>`\n * wiring. Arrow/Home/End/Page keys are handled in `onKeyDown` with\n * `preventDefault()` so stepping is deterministic both in browsers (never\n * double-steps against the native range keyboard model) and under jsdom\n * (which has no native range keyboard model at all).\n */\n\nimport { Fragment, useCallback, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { LayoutKind, SimulationConfig } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\nexport type SimFieldKey = keyof SimulationConfig;\n\nexport interface SimFieldDescriptor {\n key: SimFieldKey;\n /** Visible label text (rendered as a text node). */\n label: string;\n min: number;\n max: number;\n step: number;\n /** Displayed when the field is unset — the engine default the simulation\n * is actually running with (cosmos 3.3.0 `defaultConfigValues`). Never\n * WRITTEN unless the user moves the control. */\n fallback: number;\n}\n\n/** §16.11 slider roster = the full v0.10 `SimulationConfig` field list. */\nexport const SIM_FIELDS: readonly SimFieldDescriptor[] = [\n { key: 'gravity', label: 'Gravity', min: 0, max: 1, step: 0.01, fallback: 0.25 },\n { key: 'repulsion', label: 'Repulsion', min: 0, max: 2, step: 0.01, fallback: 1 },\n { key: 'friction', label: 'Friction', min: 0, max: 1, step: 0.01, fallback: 0.85 },\n { key: 'linkDistance', label: 'Link distance', min: 1, max: 100, step: 1, fallback: 10 },\n { key: 'linkSpring', label: 'Link spring', min: 0, max: 2, step: 0.01, fallback: 1 },\n // v0.10.2: the rest of the §10 tunable set. `collision` and `center` ship\n // OFF at the engine default, so their sliders start at 0 — moving one is\n // what turns the force on.\n { key: 'collision', label: 'Collision', min: 0, max: 2, step: 0.01, fallback: 0 },\n { key: 'decay', label: 'Cool-down', min: 100, max: 20000, step: 100, fallback: 5000 },\n { key: 'repulsionTheta', label: 'Repulsion θ', min: 0.3, max: 2, step: 0.05, fallback: 1.15 },\n { key: 'center', label: 'Centering', min: 0, max: 1, step: 0.01, fallback: 0 },\n];\n\n/** Speed-preset friction values (module JSDoc: the toggle's documented\n * simulation field is `friction` until core ships `decay`). */\nexport const SPEED_NORMAL_DECAY = 5000;\n/** A smaller decay coefficient cools SLOWER, so the run keeps moving —\n * \"fast\" here means \"stays lively\", matching the §10 semantics. */\nexport const SPEED_FAST_DECAY = 1000;\n\nexport interface GraphSimControlsProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Active layout kind — mirror of the caller's <Graph> `layout` prop\n * (no observable lane exists; module JSDoc). Default 'force'. */\n layout?: LayoutKind;\n /** Current simulation config seed — the same object passed to <Graph>.\n * An identity change re-seeds the panel's mirror. */\n simulation?: SimulationConfig;\n /** Fires with the FULL merged SimulationConfig after every write. */\n onSimulationChange?: (next: SimulationConfig) => void;\n /** Render-prop REPLACEMENT for a field row; the default renders\n * label + native range input + value readout. `set` clamps/quantizes to\n * the descriptor's bounds and step before writing. */\n renderField?: (ctx: {\n field: SimFieldDescriptor;\n value: number;\n /** Stable id for `htmlFor` wiring in custom rows. */\n inputId: string;\n set: (value: number) => void;\n }) => ReactNode;\n /** Accessible name of the panel. Default 'Simulation controls'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-left'. */\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\n/** Decimal places of `step` — the quantization grid for keyboard stepping. */\nfunction stepDecimals(step: number): number {\n const s = String(step);\n const dot = s.indexOf('.');\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/** Clamp to [min, max] and quantize to the step's decimal grid so repeated\n * keyboard steps never accumulate float noise (0.25 + 0.01 stays 0.26). */\nfunction quantize(field: SimFieldDescriptor, raw: number): number {\n const clamped = Math.min(field.max, Math.max(field.min, raw));\n return Number(clamped.toFixed(stepDecimals(field.step)));\n}\n\n/** §15.1 keyboard model for a slider; null = key not handled (let it pass). */\nfunction keyboardTarget(field: SimFieldDescriptor, value: number, key: string): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowUp':\n return quantize(field, value + field.step);\n case 'ArrowLeft':\n case 'ArrowDown':\n return quantize(field, value - field.step);\n case 'PageUp':\n return quantize(field, value + field.step * 10);\n case 'PageDown':\n return quantize(field, value - field.step * 10);\n case 'Home':\n return field.min;\n case 'End':\n return field.max;\n default:\n return null;\n }\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n width: 250,\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 10,\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.4 system-ui, sans-serif',\n};\nconst TITLE_STYLE: CSSProperties = { fontWeight: 600, fontSize: 13 };\nconst ROW_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 6 };\nconst LABEL_STYLE: CSSProperties = { flex: 'none', width: 86, opacity: 0.8 };\nconst SLIDER_STYLE: CSSProperties = { flex: 1, minWidth: 0 };\nconst VALUE_STYLE: CSSProperties = {\n flex: 'none',\n width: 38,\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n};\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n background: 'transparent',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '4px 10px',\n};\nconst STATUS_STYLE: CSSProperties = { opacity: 0.65 };\n\nexport function GraphSimControls(props: GraphSimControlsProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphSimControls>');\n const baseId = useId();\n\n // Mirror of the last-known SimulationConfig (module JSDoc: no store lane\n // exists to subscribe to). Prop identity change re-seeds — external\n // writer wins (the React-documented derive-during-render pattern).\n const [mirror, setMirror] = useState<SimulationConfig>(() => props.simulation ?? {});\n const [seededFrom, setSeededFrom] = useState(props.simulation);\n if (seededFrom !== props.simulation) {\n setSeededFrom(props.simulation);\n setMirror(props.simulation ?? {});\n }\n\n /** Decay the speed preset restores on release; undefined = the field was\n * unset pre-toggle (restore the engine-default constant). */\n const preSpeedDecayRef = useRef<number | undefined>(undefined);\n\n // The one simulation lane the store DOES publish (§16.1).\n const subscribe = useCallback(\n (onStoreChange: () => void) => instance.store.subscribe(onStoreChange),\n [instance],\n );\n const getRunning = useCallback(\n () => instance.store.getState().simulationRunning,\n [instance],\n );\n const simulationRunning = useSyncExternalStore(subscribe, getRunning, getRunning);\n\n // R-16.11-04 applicability gate — after hooks, per the rules of hooks.\n if ((props.layout ?? 'force') !== 'force') return null;\n\n /** ONE atomic config-only host update per control write (R-16.11-02). */\n const write = (next: SimulationConfig): void => {\n setMirror(next);\n instance.applyHostUpdate({ simulation: next });\n props.onSimulationChange?.(next);\n };\n\n const setField = (key: SimFieldKey, value: number): void => {\n const next: SimulationConfig = { ...mirror };\n next[key] = value;\n write(next);\n };\n\n // The preset holds exactly while decay sits at the fast value — the toggle\n // and the cool-down slider are two views over the same lane.\n const speedOn = mirror.decay === SPEED_FAST_DECAY;\n const toggleSpeed = (): void => {\n if (speedOn) {\n const restored = preSpeedDecayRef.current ?? SPEED_NORMAL_DECAY;\n preSpeedDecayRef.current = undefined;\n setField('decay', restored);\n } else {\n preSpeedDecayRef.current = mirror.decay;\n setField('decay', SPEED_FAST_DECAY);\n }\n };\n\n const panelLabel = props.label ?? 'Simulation controls';\n const speedId = `${baseId}-speed`;\n\n return (\n <div\n data-orbit-simcontrols=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={\n props.className !== undefined\n ? props.style\n : mergeStyle(\n { ...cornerStyle(props.position ?? 'bottom-left', props.offset), ...PANEL_STYLE },\n props.style,\n )\n }\n >\n <div data-orbit-simcontrols-title=\"\" style={TITLE_STYLE}>\n {panelLabel}\n </div>\n\n {SIM_FIELDS.map((field) => {\n const value = mirror[field.key] ?? field.fallback;\n const inputId = `${baseId}-${field.key}`;\n const set = (v: number): void => {\n setField(field.key, quantize(field, v));\n };\n if (props.renderField !== undefined) {\n return (\n <Fragment key={field.key}>\n {props.renderField({ field, value, inputId, set })}\n </Fragment>\n );\n }\n return (\n <div key={field.key} data-orbit-simcontrols-field={field.key} style={ROW_STYLE}>\n <label htmlFor={inputId} style={LABEL_STYLE}>\n {field.label}\n </label>\n <input\n id={inputId}\n type=\"range\"\n data-orbit-simcontrols-input={field.key}\n min={field.min}\n max={field.max}\n step={field.step}\n value={value}\n onChange={(e) => {\n const v = Number(e.target.value);\n if (Number.isFinite(v)) set(v);\n }}\n onKeyDown={(e) => {\n const target = keyboardTarget(field, value, e.key);\n if (target === null) return;\n // Suppress the browser's native range step so the write goes\n // through this handler exactly once (module JSDoc).\n e.preventDefault();\n if (target !== value) setField(field.key, target);\n }}\n style={SLIDER_STYLE}\n />\n <output\n htmlFor={inputId}\n data-orbit-simcontrols-value={field.key}\n style={VALUE_STYLE}\n >\n {String(value)}\n </output>\n </div>\n );\n })}\n\n <div data-orbit-simcontrols-speed-row=\"\" style={ROW_STYLE}>\n <input\n id={speedId}\n type=\"checkbox\"\n data-orbit-simcontrols-speed=\"\"\n checked={speedOn}\n onChange={toggleSpeed}\n />\n <label htmlFor={speedId}>Speed boost</label>\n </div>\n\n <div style={ROW_STYLE}>\n <button\n type=\"button\"\n data-orbit-simcontrols-reheat=\"\"\n style={BUTTON_STYLE}\n onClick={() => {\n instance.resumeSimulation();\n }}\n >\n Reheat\n </button>\n <span data-orbit-simcontrols-status=\"\" style={STATUS_STYLE}>\n {simulationRunning ? 'simulation running' : 'settled'}\n </span>\n </div>\n </div>\n );\n}\n\n/** S0-T02 tree-shake probe — must never reach a root-only consumer bundle. */\nexport const __ORBIT_SIMCONTROLS_SENTINEL__ = 'orbit-react/components/SimControls';\n"]}
1
+ {"version":3,"sources":["../../src/components/SimControls/index.tsx"],"names":[],"mappings":"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","file":"SimControls.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/SimControls` — `<GraphSimControls>`,\n * the §16.11 simulation control panel (S12-T12).\n *\n * Live sliders over the effective `SimulationConfig` fields (gravity,\n * repulsion, friction, linkDistance, linkSpring, collision, decay, center —\n * repulsionTheta is omitted: inert on cosmos >= 3.4), a speed\n * preset toggle, and a reheat button. Every control write is ONE atomic\n * host update — `applyHostUpdate({ simulation: merged })`, the same lane the\n * `<Graph>` `simulation` prop uses — which sinks as a single config-only\n * `engine.commit`: no structure, no buffers, no restart, positions untouched\n * (R-16.11-02, E1).\n *\n * ## Current-value resolution (v0.10, investigated)\n * The store publishes NO simulation/layout lane and the instance exposes no\n * config getter — `layout`/`simulation` are instance-local in\n * `createGraphInstance`; `GraphStoreState` carries only `simulationRunning`.\n * The panel therefore mirrors the caller's `simulation` prop (the same\n * object handed to `<Graph>`) and merges control writes over that mirror; a\n * prop identity change re-seeds the mirror (the external writer wins). Like\n * `subgraph` (§9.2), the lane is last-writer-wins: the panel and the\n * `<Graph>` prop write the same state. `onSimulationChange` fires with the\n * full merged config after every write so controlled callers can loop it\n * back through their `simulation` prop (the spec's `onLayoutChange` seam\n * traffics in the LayoutSpec object form and is post-v0.10).\n *\n * ## Applicability (R-16.11-04)\n * Rendered ONLY under layout `'force'` — the `layout` prop mirrors the\n * caller's `<Graph>` prop for the same no-observable-lane reason, defaulting\n * to `'force'` (the instance construction default). Under `'fixed'` the\n * panel renders nothing. The spec's third case — `static` while its\n * convergence run is active — is N/A in v0.10: `LayoutKind` is\n * `'force' | 'fixed'` (core types.ts) and the store exposes no\n * convergence-run state; wire it when the `static` variant lands.\n *\n * ## Reheat\n * The spec names `reheat(alpha)`; the shipped instance surface is\n * `resumeSimulation()` (§16.1 simulation controls — sinks to\n * `engine.start()` and publishes `simulationRunning`). The button calls\n * that; the alpha parameter follows when core grows the reheat API.\n *\n * ## Speed toggle\n * A preset over the `decay` lane — the §10 cool-down tunable (cosmos\n * `simulationDecay`), which core exposes as of v0.10.2. ON writes a smaller\n * coefficient so the run cools SLOWER and stays lively; OFF restores the\n * pre-toggle value, or the engine default when the field was unset. The\n * toggle and the Cool-down slider are two views over one lane, so moving\n * either is reflected by the other. Same config-only commit discipline as\n * every slider. (Before v0.10.2 this proxied through `friction` because\n * `decay` had no core lane — that workaround is now retired.)\n *\n * ## Keyboard & ARIA (§15.1)\n * Native `<input type=\"range\">`/checkbox controls with `<label htmlFor>`\n * wiring. Arrow/Home/End/Page keys are handled in `onKeyDown` with\n * `preventDefault()` so stepping is deterministic both in browsers (never\n * double-steps against the native range keyboard model) and under jsdom\n * (which has no native range keyboard model at all).\n */\n\nimport { Fragment, useCallback, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { LayoutKind, SimulationConfig } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\nexport type SimFieldKey = keyof SimulationConfig;\n\nexport interface SimFieldDescriptor {\n key: SimFieldKey;\n /** Visible label text (rendered as a text node). */\n label: string;\n min: number;\n max: number;\n step: number;\n /** Displayed when the field is unset — the engine default the simulation\n * is actually running with (cosmos 3.3.0 `defaultConfigValues`). Never\n * WRITTEN unless the user moves the control. */\n fallback: number;\n}\n\n/** §16.11 slider roster = the full v0.10 `SimulationConfig` field list. */\nexport const SIM_FIELDS: readonly SimFieldDescriptor[] = [\n { key: 'gravity', label: 'Gravity', min: 0, max: 1, step: 0.01, fallback: 0.25 },\n { key: 'repulsion', label: 'Repulsion', min: 0, max: 2, step: 0.01, fallback: 1 },\n { key: 'friction', label: 'Friction', min: 0, max: 1, step: 0.01, fallback: 0.85 },\n { key: 'linkDistance', label: 'Link distance', min: 1, max: 100, step: 1, fallback: 10 },\n { key: 'linkSpring', label: 'Link spring', min: 0, max: 2, step: 0.01, fallback: 1 },\n // v0.10.2: the rest of the §10 tunable set. `collision` and `center` ship\n // OFF at the engine default, so their sliders start at 0 — moving one is\n // what turns the force on.\n { key: 'collision', label: 'Collision', min: 0, max: 2, step: 0.01, fallback: 0 },\n { key: 'decay', label: 'Cool-down', min: 100, max: 20000, step: 100, fallback: 5000 },\n // repulsionTheta is deliberately absent: cosmos >= 3.4 ignores it\n // (grid-based repulsion) — a slider that visibly does nothing is worse\n // than no slider. The SimulationConfig field survives for other engines.\n { key: 'center', label: 'Centering', min: 0, max: 1, step: 0.01, fallback: 0 },\n];\n\n/** Speed-preset friction values (module JSDoc: the toggle's documented\n * simulation field is `friction` until core ships `decay`). */\nexport const SPEED_NORMAL_DECAY = 5000;\n/** A smaller decay coefficient cools SLOWER, so the run keeps moving —\n * \"fast\" here means \"stays lively\", matching the §10 semantics. */\nexport const SPEED_FAST_DECAY = 1000;\n\nexport interface GraphSimControlsProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Active layout kind — mirror of the caller's <Graph> `layout` prop\n * (no observable lane exists; module JSDoc). Default 'force'. */\n layout?: LayoutKind;\n /** Current simulation config seed — the same object passed to <Graph>.\n * An identity change re-seeds the panel's mirror. */\n simulation?: SimulationConfig;\n /** Fires with the FULL merged SimulationConfig after every write. */\n onSimulationChange?: (next: SimulationConfig) => void;\n /** Render-prop REPLACEMENT for a field row; the default renders\n * label + native range input + value readout. `set` clamps/quantizes to\n * the descriptor's bounds and step before writing. */\n renderField?: (ctx: {\n field: SimFieldDescriptor;\n value: number;\n /** Stable id for `htmlFor` wiring in custom rows. */\n inputId: string;\n set: (value: number) => void;\n }) => ReactNode;\n /** Accessible name of the panel. Default 'Simulation controls'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-left'. */\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\n/** Decimal places of `step` — the quantization grid for keyboard stepping. */\nfunction stepDecimals(step: number): number {\n const s = String(step);\n const dot = s.indexOf('.');\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/** Clamp to [min, max] and quantize to the step's decimal grid so repeated\n * keyboard steps never accumulate float noise (0.25 + 0.01 stays 0.26). */\nfunction quantize(field: SimFieldDescriptor, raw: number): number {\n const clamped = Math.min(field.max, Math.max(field.min, raw));\n return Number(clamped.toFixed(stepDecimals(field.step)));\n}\n\n/** §15.1 keyboard model for a slider; null = key not handled (let it pass). */\nfunction keyboardTarget(field: SimFieldDescriptor, value: number, key: string): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowUp':\n return quantize(field, value + field.step);\n case 'ArrowLeft':\n case 'ArrowDown':\n return quantize(field, value - field.step);\n case 'PageUp':\n return quantize(field, value + field.step * 10);\n case 'PageDown':\n return quantize(field, value - field.step * 10);\n case 'Home':\n return field.min;\n case 'End':\n return field.max;\n default:\n return null;\n }\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n width: 250,\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 10,\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.4 system-ui, sans-serif',\n};\nconst TITLE_STYLE: CSSProperties = { fontWeight: 600, fontSize: 13 };\nconst ROW_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 6 };\nconst LABEL_STYLE: CSSProperties = { flex: 'none', width: 86, opacity: 0.8 };\nconst SLIDER_STYLE: CSSProperties = { flex: 1, minWidth: 0 };\nconst VALUE_STYLE: CSSProperties = {\n flex: 'none',\n width: 38,\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n};\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n background: 'transparent',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '4px 10px',\n};\nconst STATUS_STYLE: CSSProperties = { opacity: 0.65 };\n\nexport function GraphSimControls(props: GraphSimControlsProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphSimControls>');\n const baseId = useId();\n\n // Mirror of the last-known SimulationConfig (module JSDoc: no store lane\n // exists to subscribe to). Prop identity change re-seeds — external\n // writer wins (the React-documented derive-during-render pattern).\n const [mirror, setMirror] = useState<SimulationConfig>(() => props.simulation ?? {});\n const [seededFrom, setSeededFrom] = useState(props.simulation);\n if (seededFrom !== props.simulation) {\n setSeededFrom(props.simulation);\n setMirror(props.simulation ?? {});\n }\n\n /** Decay the speed preset restores on release; undefined = the field was\n * unset pre-toggle (restore the engine-default constant). */\n const preSpeedDecayRef = useRef<number | undefined>(undefined);\n\n // The one simulation lane the store DOES publish (§16.1).\n const subscribe = useCallback(\n (onStoreChange: () => void) => instance.store.subscribe(onStoreChange),\n [instance],\n );\n const getRunning = useCallback(\n () => instance.store.getState().simulationRunning,\n [instance],\n );\n const simulationRunning = useSyncExternalStore(subscribe, getRunning, getRunning);\n\n // R-16.11-04 applicability gate — after hooks, per the rules of hooks.\n if ((props.layout ?? 'force') !== 'force') return null;\n\n /** ONE atomic config-only host update per control write (R-16.11-02). */\n const write = (next: SimulationConfig): void => {\n setMirror(next);\n instance.applyHostUpdate({ simulation: next });\n props.onSimulationChange?.(next);\n };\n\n const setField = (key: SimFieldKey, value: number): void => {\n const next: SimulationConfig = { ...mirror };\n next[key] = value;\n write(next);\n };\n\n // The preset holds exactly while decay sits at the fast value — the toggle\n // and the cool-down slider are two views over the same lane.\n const speedOn = mirror.decay === SPEED_FAST_DECAY;\n const toggleSpeed = (): void => {\n if (speedOn) {\n const restored = preSpeedDecayRef.current ?? SPEED_NORMAL_DECAY;\n preSpeedDecayRef.current = undefined;\n setField('decay', restored);\n } else {\n preSpeedDecayRef.current = mirror.decay;\n setField('decay', SPEED_FAST_DECAY);\n }\n };\n\n const panelLabel = props.label ?? 'Simulation controls';\n const speedId = `${baseId}-speed`;\n\n return (\n <div\n data-orbit-simcontrols=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={\n props.className !== undefined\n ? props.style\n : mergeStyle(\n { ...cornerStyle(props.position ?? 'bottom-left', props.offset), ...PANEL_STYLE },\n props.style,\n )\n }\n >\n <div data-orbit-simcontrols-title=\"\" style={TITLE_STYLE}>\n {panelLabel}\n </div>\n\n {SIM_FIELDS.map((field) => {\n const value = mirror[field.key] ?? field.fallback;\n const inputId = `${baseId}-${field.key}`;\n const set = (v: number): void => {\n setField(field.key, quantize(field, v));\n };\n if (props.renderField !== undefined) {\n return (\n <Fragment key={field.key}>\n {props.renderField({ field, value, inputId, set })}\n </Fragment>\n );\n }\n return (\n <div key={field.key} data-orbit-simcontrols-field={field.key} style={ROW_STYLE}>\n <label htmlFor={inputId} style={LABEL_STYLE}>\n {field.label}\n </label>\n <input\n id={inputId}\n type=\"range\"\n data-orbit-simcontrols-input={field.key}\n min={field.min}\n max={field.max}\n step={field.step}\n value={value}\n onChange={(e) => {\n const v = Number(e.target.value);\n if (Number.isFinite(v)) set(v);\n }}\n onKeyDown={(e) => {\n const target = keyboardTarget(field, value, e.key);\n if (target === null) return;\n // Suppress the browser's native range step so the write goes\n // through this handler exactly once (module JSDoc).\n e.preventDefault();\n if (target !== value) setField(field.key, target);\n }}\n style={SLIDER_STYLE}\n />\n <output\n htmlFor={inputId}\n data-orbit-simcontrols-value={field.key}\n style={VALUE_STYLE}\n >\n {String(value)}\n </output>\n </div>\n );\n })}\n\n <div data-orbit-simcontrols-speed-row=\"\" style={ROW_STYLE}>\n <input\n id={speedId}\n type=\"checkbox\"\n data-orbit-simcontrols-speed=\"\"\n checked={speedOn}\n onChange={toggleSpeed}\n />\n <label htmlFor={speedId}>Speed boost</label>\n </div>\n\n <div style={ROW_STYLE}>\n <button\n type=\"button\"\n data-orbit-simcontrols-reheat=\"\"\n style={BUTTON_STYLE}\n onClick={() => {\n instance.resumeSimulation();\n }}\n >\n Reheat\n </button>\n <span data-orbit-simcontrols-status=\"\" style={STATUS_STYLE}>\n {simulationRunning ? 'simulation running' : 'settled'}\n </span>\n </div>\n </div>\n );\n}\n\n/** S0-T02 tree-shake probe — must never reach a root-only consumer bundle. */\nexport const __ORBIT_SIMCONTROLS_SENTINEL__ = 'orbit-react/components/SimControls';\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, CSSProperties, RefAttributes, ReactElement } from 'react';
2
- import { GraphSnapshot, Accessor, GraphNode, Scale, AcceptedEdge, LayoutKind, SimulationConfig, ThemeInput, MetricColumn, ScaleChannelInfo, GraphTheme, LabelConfig, AccessibilityConfig, NodeId, SubgraphSpec, FilterSpec, DimensionSpec, GroupSpec, GroupBySpec, ClusterSpec, SearchResult, SearchUnavailableReason, CreateGraphInstanceOptions, SelectionState, ResolvedGroup, MetaEdge, JsonValue, ViewportState, GraphViewState, SetViewStateResult, PathOptions, PathResult, ExpandNodeResult, ResolvedCluster, BeginIngestOptions, IngestSession, TimelinePlayback, CrossfilterSession, MetricName, Revisions, GraphDiagnostic, GraphInstance, DimensionSummary, BrushState, EdgeId, InstanceStatus } from '@modernrelay/orbit-core';
2
+ import { GraphSnapshotInput, Accessor, GraphNode, Scale, AcceptedEdge, LayoutKind, SimulationConfig, ThemeInput, MetricColumn, ScaleChannelInfo, GraphTheme, LabelConfig, AccessibilityConfig, NodeId, SubgraphSpec, FilterSpec, DimensionSpec, GroupSpec, GroupBySpec, ClusterSpec, ScaleLimits, WorkerFactoryOption, SearchResult, SearchUnavailableReason, CreateGraphInstanceOptions, SelectionState, DegradeEvent, GraphPerfSnapshot, ResolvedGroup, MetaEdge, JsonValue, ViewportState, GraphViewState, SetViewStateResult, PathOptions, PathResult, ExpandNodeResult, ResolvedCluster, BeginIngestOptions, IngestSession, TimelinePlayback, CrossfilterSession, MetricName, Revisions, GraphDiagnostic, GraphInstance, DimensionSummary, BrushState, EdgeId, InstanceStatus } from '@modernrelay/orbit-core';
3
3
  import { EngineFactory, FitViewOptions } from '@modernrelay/orbit-core/engine';
4
4
  export { G as GraphProvider, a as GraphProviderProps } from './GraphProvider-B8ITDXS0.js';
5
5
  export { D as DEFAULT_CORNER_OFFSET, G as GraphCorner, a as GraphLabelsSurface, b as GraphOverlaySurface, u as useResolvedInstance } from './shared-Bj3Gk219.js';
@@ -24,7 +24,7 @@ interface GraphLegendRenderInfo<N = Record<string, unknown>> {
24
24
  interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {
25
25
  /** Engine factory; captured at first render — one factory per instance. */
26
26
  engine: EngineFactory;
27
- data?: GraphSnapshot<N, E>;
27
+ data?: GraphSnapshotInput<N, E>;
28
28
  /** Constant, accessor, or §11 Scale descriptor. Scale-valued props compare
29
29
  * by canonical structural value — equal inline literals never reproject. */
30
30
  nodeColor?: Accessor<GraphNode<N>, string> | Scale<string, N>;
@@ -46,6 +46,9 @@ interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {
46
46
  edgeArrows?: boolean;
47
47
  /** §16.13 runtime link-render toggle — a config-only commit. */
48
48
  showLinks?: boolean;
49
+ /** §13 emphasis-ring toggle (default true). False clears the ring once and
50
+ * suppresses every driver (hover, focusNode, emphasizeNode). */
51
+ emphasisRing?: boolean;
49
52
  /** §11 legend escape hatch: rendered inside the provider in a positioned
50
53
  * wrapper (bottom-left) whenever provided; re-invoked when scale info or
51
54
  * theme changes. Return custom JSX, or compose <GraphLegend> from
@@ -126,6 +129,15 @@ interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {
126
129
  * mount; changing it requires a keyed remount (a changed prop warns once
127
130
  * and is ignored). */
128
131
  searchIndex?: readonly string[];
132
+ /** §17/§6.1 degradation-ladder thresholds. CONSTRUCTION-ONLY (D7): read
133
+ * once at mount; a runtime change warns once and is ignored. */
134
+ limits?: Partial<ScaleLimits>;
135
+ /** §6.1/§17 execution mode (ADR-006). CONSTRUCTION-ONLY (D7): read once
136
+ * at mount; a runtime change warns once and is ignored. Current worker
137
+ * cargo: columnar acceptance off-thread. */
138
+ execution?: 'auto' | 'main' | 'worker';
139
+ /** ADR-006 D5 worker construction tri-option. CONSTRUCTION-ONLY (D7). */
140
+ workerFactory?: WorkerFactoryOption;
129
141
  /** §16.5 result contract: the instance-wide DEFAULT for an activated
130
142
  * search result that cannot be focused ('not-loaded' | 'out-of-scope' |
131
143
  * 'filtered'). `<GraphSearch onResultUnavailable>` overrides it locally;
@@ -171,6 +183,10 @@ interface GraphProps<N = Record<string, unknown>, E = Record<string, unknown>> {
171
183
  }) => void;
172
184
  /** Fires with the full namespaced SelectionState (§16.2). */
173
185
  onSelectionChange?: (payload: SelectionState) => void;
186
+ /** §17 ladder step engagement/disengagement (notification pattern). */
187
+ onDegrade?: (event: DegradeEvent) => void;
188
+ /** §17 throttled telemetry sample — never per frame. */
189
+ onPerfSample?: (snapshot: GraphPerfSnapshot) => void;
174
190
  /** §16.3/§7.4: a super-node hit — the resolved GROUP payload, never a
175
191
  * GraphNode (R-16.3-12). */
176
192
  onGroupClick?: (payload: {
@@ -218,6 +234,11 @@ interface GraphHandle<N = Record<string, unknown>, E = Record<string, unknown>>
218
234
  durationMs?: number;
219
235
  }): void;
220
236
  focusNode(id: NodeId): void;
237
+ /** §13 ring-only emphasis, no camera move (null clears; unknown ids
238
+ * no-op). The keyboard-navigation analog of hover. */
239
+ emphasizeNode(id: NodeId | null): void;
240
+ /** §17 telemetry snapshot (never raw attrs or ids). */
241
+ getPerfSnapshot(): GraphPerfSnapshot;
221
242
  /** Open the typed context menu for a node from host chrome (a list row, a
222
243
  * custom label) — the same 'contextMenu' event the canvas gesture emits.
223
244
  * `screen` is container-relative CSS px. */
package/dist/index.js CHANGED
@@ -386,10 +386,29 @@ function GraphInner(props, ref) {
386
386
  options.fitViewOnFirstData = props.fitViewOnFirstData;
387
387
  }
388
388
  if (props.searchIndex !== void 0) options.searchIndex = props.searchIndex;
389
+ if (props.limits !== void 0) options.limits = props.limits;
390
+ if (props.execution !== void 0) options.execution = props.execution;
391
+ if (props.workerFactory !== void 0) options.workerFactory = props.workerFactory;
389
392
  if (props.services !== void 0) options.services = props.services;
390
393
  return createGraphInstance(options);
391
394
  });
392
395
  const searchIndexWarnedRef = useRef(false);
396
+ const limitsWarnedRef = useRef(false);
397
+ const initialLimitsRef = useRef(props.limits);
398
+ if (props.limits !== initialLimitsRef.current && !limitsWarnedRef.current) {
399
+ limitsWarnedRef.current = true;
400
+ console.warn(
401
+ "orbit: `limits` is a construction-only prop (D7/\xA717) \u2014 the runtime change is ignored; remount with a key to apply new thresholds."
402
+ );
403
+ }
404
+ const executionWarnedRef = useRef(false);
405
+ const initialExecutionRef = useRef(props.execution);
406
+ if (props.execution !== initialExecutionRef.current && !executionWarnedRef.current) {
407
+ executionWarnedRef.current = true;
408
+ console.warn(
409
+ "orbit: `execution` is a construction-only prop (D7/ADR-006) \u2014 the runtime change is ignored; remount with a key to switch lanes."
410
+ );
411
+ }
393
412
  const containerRef = useRef(null);
394
413
  const callbacksRef = useRef({
395
414
  onNodeClick: void 0,
@@ -400,6 +419,8 @@ function GraphInner(props, ref) {
400
419
  onNodeDragStart: void 0,
401
420
  onNodeDragEnd: void 0,
402
421
  onSelectionChange: void 0,
422
+ onDegrade: void 0,
423
+ onPerfSample: void 0,
403
424
  onGroupClick: void 0,
404
425
  onMetaEdgeClick: void 0,
405
426
  onGroupsChange: void 0,
@@ -420,6 +441,8 @@ function GraphInner(props, ref) {
420
441
  onNodeDragStart: props.onNodeDragStart,
421
442
  onNodeDragEnd: props.onNodeDragEnd,
422
443
  onSelectionChange: props.onSelectionChange,
444
+ onDegrade: props.onDegrade,
445
+ onPerfSample: props.onPerfSample,
423
446
  onGroupClick: props.onGroupClick,
424
447
  onMetaEdgeClick: props.onMetaEdgeClick,
425
448
  onGroupsChange: props.onGroupsChange,
@@ -431,6 +454,35 @@ function GraphInner(props, ref) {
431
454
  onError: props.onError
432
455
  };
433
456
  });
457
+ useLayoutEffect(() => {
458
+ const offs = [
459
+ instance.on("nodeClick", (p) => callbacksRef.current.onNodeClick?.(p)),
460
+ instance.on("backgroundClick", () => callbacksRef.current.onBackgroundClick?.()),
461
+ instance.on("nodeHover", (p) => callbacksRef.current.onNodeHover?.(p)),
462
+ instance.on("edgeClick", (p) => callbacksRef.current.onEdgeClick?.(p)),
463
+ instance.on("edgeHover", (p) => callbacksRef.current.onEdgeHover?.(p)),
464
+ instance.on("nodeDragStart", (p) => callbacksRef.current.onNodeDragStart?.(p)),
465
+ instance.on("nodeDragEnd", (p) => callbacksRef.current.onNodeDragEnd?.(p)),
466
+ instance.on("selectionChange", (p) => callbacksRef.current.onSelectionChange?.(p)),
467
+ instance.on("degrade", (p) => callbacksRef.current.onDegrade?.(p)),
468
+ instance.on("perfSample", (p) => callbacksRef.current.onPerfSample?.(p)),
469
+ instance.on("groupClick", (p) => callbacksRef.current.onGroupClick?.(p)),
470
+ instance.on("metaEdgeClick", (p) => callbacksRef.current.onMetaEdgeClick?.(p)),
471
+ instance.on("groupsChange", (p) => callbacksRef.current.onGroupsChange?.(p.groups)),
472
+ instance.on("viewStateRestore", (p) => callbacksRef.current.onViewStateRestore?.(p)),
473
+ instance.on(
474
+ "viewStateMismatch",
475
+ (p) => callbacksRef.current.onViewStateMismatch?.(p.stored, p.current)
476
+ ),
477
+ instance.on("pinnedChange", (p) => callbacksRef.current.onPinnedChange?.(p.pinnedNodeIds)),
478
+ instance.on("viewportChange", (v) => callbacksRef.current.onViewportChange?.(v)),
479
+ instance.on("ready", () => callbacksRef.current.onReady?.()),
480
+ instance.on("error", (p) => callbacksRef.current.onError?.(p))
481
+ ];
482
+ return () => {
483
+ for (const off of offs) off();
484
+ };
485
+ }, [instance]);
434
486
  const prevRef = useRef(null);
435
487
  useLayoutEffect(() => {
436
488
  const prev = prevRef.current;
@@ -493,6 +545,13 @@ function GraphInner(props, ref) {
493
545
  update.showLinks = props.showLinks;
494
546
  dirty = true;
495
547
  }
548
+ if (props.emphasisRing !== void 0 && (prev === null || prev.emphasisRing !== props.emphasisRing)) {
549
+ update.emphasisRing = props.emphasisRing;
550
+ dirty = true;
551
+ } else if (props.emphasisRing === void 0 && prev !== null && prev.emphasisRing !== void 0) {
552
+ update.emphasisRing = true;
553
+ dirty = true;
554
+ }
496
555
  if (props.selection !== void 0 && (prev === null || prev.selection === void 0 || !sameIdArrays(prev.selection, props.selection))) {
497
556
  update.selection = props.selection;
498
557
  dirty = true;
@@ -576,6 +635,7 @@ function GraphInner(props, ref) {
576
635
  nodeImage: props.nodeImage,
577
636
  edgeArrows: props.edgeArrows,
578
637
  showLinks: props.showLinks,
638
+ emphasisRing: props.emphasisRing,
579
639
  selection: props.selection,
580
640
  labels: props.labels,
581
641
  accessibility: props.accessibility,
@@ -595,33 +655,6 @@ function GraphInner(props, ref) {
595
655
  useLayoutEffect(() => {
596
656
  setSearchResultUnavailableCallback(instance, props.onSearchResultUnavailable);
597
657
  });
598
- useEffect(() => {
599
- const offs = [
600
- instance.on("nodeClick", (p) => callbacksRef.current.onNodeClick?.(p)),
601
- instance.on("backgroundClick", () => callbacksRef.current.onBackgroundClick?.()),
602
- instance.on("nodeHover", (p) => callbacksRef.current.onNodeHover?.(p)),
603
- instance.on("edgeClick", (p) => callbacksRef.current.onEdgeClick?.(p)),
604
- instance.on("edgeHover", (p) => callbacksRef.current.onEdgeHover?.(p)),
605
- instance.on("nodeDragStart", (p) => callbacksRef.current.onNodeDragStart?.(p)),
606
- instance.on("nodeDragEnd", (p) => callbacksRef.current.onNodeDragEnd?.(p)),
607
- instance.on("selectionChange", (p) => callbacksRef.current.onSelectionChange?.(p)),
608
- instance.on("groupClick", (p) => callbacksRef.current.onGroupClick?.(p)),
609
- instance.on("metaEdgeClick", (p) => callbacksRef.current.onMetaEdgeClick?.(p)),
610
- instance.on("groupsChange", (p) => callbacksRef.current.onGroupsChange?.(p.groups)),
611
- instance.on("viewStateRestore", (p) => callbacksRef.current.onViewStateRestore?.(p)),
612
- instance.on(
613
- "viewStateMismatch",
614
- (p) => callbacksRef.current.onViewStateMismatch?.(p.stored, p.current)
615
- ),
616
- instance.on("pinnedChange", (p) => callbacksRef.current.onPinnedChange?.(p.pinnedNodeIds)),
617
- instance.on("viewportChange", (v) => callbacksRef.current.onViewportChange?.(v)),
618
- instance.on("ready", () => callbacksRef.current.onReady?.()),
619
- instance.on("error", (p) => callbacksRef.current.onError?.(p))
620
- ];
621
- return () => {
622
- for (const off of offs) off();
623
- };
624
- }, [instance]);
625
658
  useEffect(() => {
626
659
  const container = containerRef.current;
627
660
  if (container === null) return void 0;
@@ -700,6 +733,10 @@ function GraphInner(props, ref) {
700
733
  focusNode: (id) => {
701
734
  instance.focusNode(id);
702
735
  },
736
+ emphasizeNode: (id) => {
737
+ instance.emphasizeNode(id);
738
+ },
739
+ getPerfSnapshot: () => instance.getPerfSnapshot(),
703
740
  setSelection: (ids) => {
704
741
  instance.setSelection(ids);
705
742
  },
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 (§16.2, S6-T05 React half).\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 §14 DOM label lane (S7-T05).\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 S6\n * click-selection path through the same public mutators (replace on plain\n * click, toggle on meta/shift — S7-T17).\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 §16.3 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 (R-16.3-18). */\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 // S6 click-selection semantics through the public mutators (S7-T17):\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 // R-16.3-18: 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 // §15 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 {/* §14 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 — §15.1 screen-reader announcements (S7-T10).\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 (§6): 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 GraphSnapshot,\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 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` (§11): 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?: GraphSnapshot<N, E>;\n /** Constant, accessor, or §11 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 /** §8 theme tokens: full GraphTheme, Partial over a named base, or the\n * v0.1 `{background}` shorthand. Diffed structurally (JSON). */\n theme?: ThemeInput;\n /** §12 async metric columns, joined once with revision-gated admission.\n * Diffed by array reference. */\n metrics?: readonly MetricColumn[];\n /** §8 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 /** §16.12 instanced arrowheads (capability-gated; inert when unsupported). */\n edgeArrows?: boolean;\n /** §16.13 runtime link-render toggle — a config-only commit. */\n showLinks?: boolean;\n /** §11 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 /** §14 DOM label lane; forwarded through applyHostUpdate. `enabled: false`\n * also unmounts the <LabelLayer> overlay entirely. */\n labels?: LabelConfig<N>;\n /** §15.1 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 (§14). */\n labelClassName?: string;\n /** Escape hatch: custom label content rendered INSIDE the positioned label\n * div (replaces the default text node; §14). */\n renderNodeLabel?: (ctx: { node: GraphNode<N>; text: string }) => ReactNode;\n /** Class hook applied to §16.3 cluster label divs, in addition to\n * `labelClassName` (S12-T07). */\n clusterLabelClassName?: string;\n /** Escape hatch: custom cluster-label content (replaces the default text\n * node; §14 text-node-only rendering otherwise). `memberIds` are the ids a\n * click selects (R-16.3-18). */\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 (§6.4). Edge/group namespaces stay instance-owned. */\n selection?: readonly NodeId[];\n /** §9.2 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 /** §9.1 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 /** §16.6 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 /** §16.3 manual groups; `null` clears. Diffed STRUCTURALLY (equal inline\n * literals never re-forward). Providing it once flips the groups slice to\n * CONTROLLED (§6.4): handle ops then fire onGroupsChange with the next\n * array instead of writing — reflect it back here. Config error together\n * with `groupBy` (R-16.3-15: neither applies until one is removed). */\n groups?: readonly GroupSpec[] | null;\n /** §16.3 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 (R-16.3-16). */\n groupBy?: GroupBySpec<N> | null;\n /** §16.3 stage-4 non-collapsing clusters (S12-T06); `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 §14 overlay lane under\n * `labels.maxZoom` and select their member nodes on click. */\n clusters?: ClusterSpec<N> | null;\n /** §16.3 PERSISTENT pins (S12-T09) — 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 (§6.4): handle ops then fire onPinnedChange with the\n * next array instead of writing. `null` clears (D2). */\n pinnedNodeIds?: readonly NodeId[] | null;\n /** §16.5 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 /** §16.5 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 (S11). */\n onSearchResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Captured at first render (instance construction option). Default true. */\n fitViewOnFirstData?: boolean;\n /** §9.2 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 (§16.2). 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 (§16.2). */\n onSelectionChange?: (payload: SelectionState) => void;\n /** §16.3/§7.4: a super-node hit — the resolved GROUP payload, never a\n * GraphNode (R-16.3-12). */\n onGroupClick?: (payload: { group: ResolvedGroup; metaKey?: boolean }) => void;\n /** §16.3/§7.4: a meta-edge hit — the MetaEdge record (count badge datum). */\n onMetaEdgeClick?: (payload: { metaEdge: MetaEdge }) => void;\n /** §6.4 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 /** §16.14 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 /** §16.14 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 /** §16.14: fired INSTEAD of applying a state whose dataRef mismatches. */\n onViewStateMismatch?: (stored: JsonValue | undefined, current: JsonValue | undefined) => void;\n /** §6.4 persistent-pin slice change (S12-T09): 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 /** 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 /** §16.14: serialize the exploration state (see core docs). */\n getViewState(opts?: { includePositions?: false }): GraphViewState;\n /** §16.14: atomic restore; typed results, never partial. */\n setViewState(\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult>;\n /** §16.14 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 /** §16.14 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 /** §16.14 memory-bounded JSONL stream over one pinned revision. */\n exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;\n /** §16.14 bounded id → [x, y] map (one position readback). */\n exportLayout(opts?: { limit?: number }): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;\n /** §16.14 layout JSONL stream over one pinned readback. */\n exportLayoutStream(): AsyncGenerator<string, void, undefined>;\n setSelection(ids: readonly NodeId[]): void;\n clearSelection(): void;\n // --- §16.3 persistent pins (S12-T09) ---\n /** Hold nodes at their CURRENT position (independent of drag pinning). */\n pinNodes(ids: readonly NodeId[]): void;\n unpinNodes(ids: readonly NodeId[]): void;\n // --- §16.2 paths (S12-T08) ---\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 // --- §16.2 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 // --- §16.3 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 // --- §9.2 hard scope + expansion ---\n /** Hard-scope to the current node selection (no-op when empty; §9.2). */\n isolateSelection(): void;\n /** Clear the hard scope — the full accepted model returns (§9.2). */\n resetIsolation(): void;\n /** Ego-expand `id` through the configured ExpansionService (§9.2/§16.3). */\n expandNode(id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult>;\n /** Abort `id`'s pending expansion and remove its committed expansion\n * overlays (§16.3). */\n retractExpansion(id: NodeId): void;\n // --- §16.3 group operations (§6.4 groups slice; S12-T02/T04) ---\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 // --- §16.3 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 // --- §16.3 stage-4 clusters (S12-T06/T07) ---\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 (R-16.3-18); `additive` unions. */\n selectCluster(key: string, opts?: { additive?: boolean }): void;\n // --- §7.5 revisioned ingestion ---\n /** Begin a bounded, cancellable ingest session (§7.5). */\n beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;\n /** Atomically remove exactly one committed overlay; unknown ids are an\n * idempotent `{ removed: false }` (§7.5). */\n removeOverlay(overlayId: string): { removed: boolean };\n // --- §16.14 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 // --- §16.6 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 // --- §16.6 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 // --- §11/§12 styling reads (S10) ---\n /** §11 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 /** §12 metric read for one node id (built-in degree family + admitted\n * columns); null for unknown ids/metrics and §8-null 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: GraphSnapshot<N, E> | undefined;\n nodeColor: Accessor<GraphNode<N>, string> | Scale<string, N> | undefined;\n /** Canonical structural key when the committed nodeColor is a Scale (§11);\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 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 (§9.1): 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 (array reference; §16.6). */\n crossfilter: readonly DimensionSpec<N>[] | undefined;\n /** Last committed `groups` prop (structural diff via sameGroupSpecArrays;\n * §16.3 — 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 (shallow element diff; S12-T09). */\n pinnedNodeIds: readonly NodeId[] | null | undefined;\n /** Last committed `searchIndex` prop (shallow element diff; §16.5/S11). */\n searchIndex: readonly string[] | undefined;\n}\n\n/**\n * Canonical key of a scale-valued styling channel (§11): `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 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 (§11): 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.services !== undefined) options.services = props.services;\n return createGraphInstance<N, E>(options);\n });\n const searchIndexWarnedRef = useRef(false);\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 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 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 // --- 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 // §11 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 // §8 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 // §12 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 // §8 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 // D2/F11-04: prop REMOVAL 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 // §16.12/§16.13 toggles: plain value diff (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.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 // §9.2 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 // §9.1 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 // §16.6 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 // §16.3 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 (§6.4).\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 // §16.3 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 // §16.3 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 // §16.3 pinnedNodeIds: shallow element diff (a small id list); ONCE\n // provided the core latches the slice controlled (§6.4).\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 // §16.5/D7: searchIndex is CONSTRUCTION-ONLY (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 (§16.5): 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 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 // §16.5 (S11): 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 // --- event subscriptions: keyed on the instance only; identity churn of\n // the props is absorbed by callbacksRef ---\n useEffect(() => {\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('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 // --- 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 (§15.1): 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 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 // --- §15.1 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 (§14): 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 {/* §15.1 live region: store-driven, coalesced, visually hidden. */}\n <LiveRegion announcements={props.accessibility?.announcements} />\n {/* §11 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":"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","file":"index.js","sourcesContent":["/**\n * Lasso — the Shift+drag freeform selection overlay (§16.2, S6-T05 React half).\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 §14 DOM label lane (S7-T05).\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 S6\n * click-selection path through the same public mutators (replace on plain\n * click, toggle on meta/shift — S7-T17).\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 §16.3 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 (R-16.3-18). */\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 // S6 click-selection semantics through the public mutators (S7-T17):\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 // R-16.3-18: 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 // §15 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 {/* §14 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 — §15.1 screen-reader announcements (S7-T10).\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 (§6): 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` (§11): 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 §11 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 /** §8 theme tokens: full GraphTheme, Partial over a named base, or the\n * v0.1 `{background}` shorthand. Diffed structurally (JSON). */\n theme?: ThemeInput;\n /** §12 async metric columns, joined once with revision-gated admission.\n * Diffed by array reference. */\n metrics?: readonly MetricColumn[];\n /** §8 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 /** §16.12 instanced arrowheads (capability-gated; inert when unsupported). */\n edgeArrows?: boolean;\n /** §16.13 runtime link-render toggle — a config-only commit. */\n showLinks?: boolean;\n /** §13 emphasis-ring toggle (default true). False clears the ring once and\n * suppresses every driver (hover, focusNode, emphasizeNode). */\n emphasisRing?: boolean;\n /** §11 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 /** §14 DOM label lane; forwarded through applyHostUpdate. `enabled: false`\n * also unmounts the <LabelLayer> overlay entirely. */\n labels?: LabelConfig<N>;\n /** §15.1 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 (§14). */\n labelClassName?: string;\n /** Escape hatch: custom label content rendered INSIDE the positioned label\n * div (replaces the default text node; §14). */\n renderNodeLabel?: (ctx: { node: GraphNode<N>; text: string }) => ReactNode;\n /** Class hook applied to §16.3 cluster label divs, in addition to\n * `labelClassName` (S12-T07). */\n clusterLabelClassName?: string;\n /** Escape hatch: custom cluster-label content (replaces the default text\n * node; §14 text-node-only rendering otherwise). `memberIds` are the ids a\n * click selects (R-16.3-18). */\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 (§6.4). Edge/group namespaces stay instance-owned. */\n selection?: readonly NodeId[];\n /** §9.2 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 /** §9.1 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 /** §16.6 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 /** §16.3 manual groups; `null` clears. Diffed STRUCTURALLY (equal inline\n * literals never re-forward). Providing it once flips the groups slice to\n * CONTROLLED (§6.4): handle ops then fire onGroupsChange with the next\n * array instead of writing — reflect it back here. Config error together\n * with `groupBy` (R-16.3-15: neither applies until one is removed). */\n groups?: readonly GroupSpec[] | null;\n /** §16.3 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 (R-16.3-16). */\n groupBy?: GroupBySpec<N> | null;\n /** §16.3 stage-4 non-collapsing clusters (S12-T06); `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 §14 overlay lane under\n * `labels.maxZoom` and select their member nodes on click. */\n clusters?: ClusterSpec<N> | null;\n /** §16.3 PERSISTENT pins (S12-T09) — 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 (§6.4): handle ops then fire onPinnedChange with the\n * next array instead of writing. `null` clears (D2). */\n pinnedNodeIds?: readonly NodeId[] | null;\n /** §16.5 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 /** §17/§6.1 degradation-ladder thresholds. CONSTRUCTION-ONLY (D7): read\n * once at mount; a runtime change warns once and is ignored. */\n limits?: Partial<ScaleLimits>;\n /** §6.1/§17 execution mode (ADR-006). CONSTRUCTION-ONLY (D7): 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 /** ADR-006 D5 worker construction tri-option. CONSTRUCTION-ONLY (D7). */\n workerFactory?: WorkerFactoryOption;\n /** §16.5 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 (S11). */\n onSearchResultUnavailable?: (result: SearchResult, reason: SearchUnavailableReason) => void;\n /** Captured at first render (instance construction option). Default true. */\n fitViewOnFirstData?: boolean;\n /** §9.2 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 (§16.2). 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 (§16.2). */\n onSelectionChange?: (payload: SelectionState) => void;\n /** §17 ladder step engagement/disengagement (notification pattern). */\n onDegrade?: (event: DegradeEvent) => void;\n /** §17 throttled telemetry sample — never per frame. */\n onPerfSample?: (snapshot: GraphPerfSnapshot) => void;\n /** §16.3/§7.4: a super-node hit — the resolved GROUP payload, never a\n * GraphNode (R-16.3-12). */\n onGroupClick?: (payload: { group: ResolvedGroup; metaKey?: boolean }) => void;\n /** §16.3/§7.4: a meta-edge hit — the MetaEdge record (count badge datum). */\n onMetaEdgeClick?: (payload: { metaEdge: MetaEdge }) => void;\n /** §6.4 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 /** §16.14 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 /** §16.14 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 /** §16.14: fired INSTEAD of applying a state whose dataRef mismatches. */\n onViewStateMismatch?: (stored: JsonValue | undefined, current: JsonValue | undefined) => void;\n /** §6.4 persistent-pin slice change (S12-T09): 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 /** §13 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 /** §17 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 /** §16.14: serialize the exploration state (see core docs). */\n getViewState(opts?: { includePositions?: false }): GraphViewState;\n /** §16.14: atomic restore; typed results, never partial. */\n setViewState(\n raw: unknown,\n opts?: { ignoreMismatch?: boolean },\n ): Promise<SetViewStateResult>;\n /** §16.14 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 /** §16.14 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 /** §16.14 memory-bounded JSONL stream over one pinned revision. */\n exportDataStream(scope?: 'visible' | 'accepted'): AsyncGenerator<string, void, undefined>;\n /** §16.14 bounded id → [x, y] map (one position readback). */\n exportLayout(opts?: { limit?: number }): Promise<ReadonlyMap<NodeId, readonly [number, number]>>;\n /** §16.14 layout JSONL stream over one pinned readback. */\n exportLayoutStream(): AsyncGenerator<string, void, undefined>;\n setSelection(ids: readonly NodeId[]): void;\n clearSelection(): void;\n // --- §16.3 persistent pins (S12-T09) ---\n /** Hold nodes at their CURRENT position (independent of drag pinning). */\n pinNodes(ids: readonly NodeId[]): void;\n unpinNodes(ids: readonly NodeId[]): void;\n // --- §16.2 paths (S12-T08) ---\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 // --- §16.2 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 // --- §16.3 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 // --- §9.2 hard scope + expansion ---\n /** Hard-scope to the current node selection (no-op when empty; §9.2). */\n isolateSelection(): void;\n /** Clear the hard scope — the full accepted model returns (§9.2). */\n resetIsolation(): void;\n /** Ego-expand `id` through the configured ExpansionService (§9.2/§16.3). */\n expandNode(id: NodeId, opts?: { hops?: number }): Promise<ExpandNodeResult>;\n /** Abort `id`'s pending expansion and remove its committed expansion\n * overlays (§16.3). */\n retractExpansion(id: NodeId): void;\n // --- §16.3 group operations (§6.4 groups slice; S12-T02/T04) ---\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 // --- §16.3 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 // --- §16.3 stage-4 clusters (S12-T06/T07) ---\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 (R-16.3-18); `additive` unions. */\n selectCluster(key: string, opts?: { additive?: boolean }): void;\n // --- §7.5 revisioned ingestion ---\n /** Begin a bounded, cancellable ingest session (§7.5). */\n beginIngest(opts: BeginIngestOptions): IngestSession<N, E>;\n /** Atomically remove exactly one committed overlay; unknown ids are an\n * idempotent `{ removed: false }` (§7.5). */\n removeOverlay(overlayId: string): { removed: boolean };\n // --- §16.14 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 // --- §16.6 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 // --- §16.6 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 // --- §11/§12 styling reads (S10) ---\n /** §11 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 /** §12 metric read for one node id (built-in degree family + admitted\n * columns); null for unknown ids/metrics and §8-null 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 (§11);\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 (§9.1): 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 (array reference; §16.6). */\n crossfilter: readonly DimensionSpec<N>[] | undefined;\n /** Last committed `groups` prop (structural diff via sameGroupSpecArrays;\n * §16.3 — 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 (shallow element diff; S12-T09). */\n pinnedNodeIds: readonly NodeId[] | null | undefined;\n /** Last committed `searchIndex` prop (shallow element diff; §16.5/S11). */\n searchIndex: readonly string[] | undefined;\n}\n\n/**\n * Canonical key of a scale-valued styling channel (§11): `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 (§11): 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 (D7/§17) — 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 (D7/ADR-006) — 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 §17 '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 // §11 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 // §8 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 // §12 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 // §8 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 // D2/F11-04: prop REMOVAL 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 // §16.12/§16.13 toggles: plain value diff (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 // D2/F11-04: prop REMOVAL 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 // §9.2 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 // §9.1 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 // §16.6 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 // §16.3 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 (§6.4).\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 // §16.3 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 // §16.3 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 // §16.3 pinnedNodeIds: shallow element diff (a small id list); ONCE\n // provided the core latches the slice controlled (§6.4).\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 // §16.5/D7: searchIndex is CONSTRUCTION-ONLY (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 (§16.5): 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 // §16.5 (S11): 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 (§15.1): 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 // --- §15.1 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 (§14): 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 {/* §15.1 live region: store-driven, coalesced, visually hidden. */}\n <LiveRegion announcements={props.accessibility?.announcements} />\n {/* §11 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.2.0",
3
+ "version": "0.13.5",
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.2.0"
78
+ "@modernrelay/orbit-core": "0.13.5"
79
79
  },
80
80
  "peerDependencies": {
81
81
  "react": ">=18",