@jmbarzee/force-graph 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +424 -0
- package/dist-lib/components/FilterBar.d.ts +90 -0
- package/dist-lib/components/GraphView.d.ts +213 -0
- package/dist-lib/components/GroupLegend.d.ts +25 -0
- package/dist-lib/components/graph-view/focus.d.ts +16 -0
- package/dist-lib/components/graph-view/useFocusRequest.d.ts +9 -0
- package/dist-lib/components/graph-view/useLinkedViews.d.ts +85 -0
- package/dist-lib/components/graph-view/useRecentlyChanged.d.ts +5 -0
- package/dist-lib/components/graph-view/useTaxonomy.d.ts +11 -0
- package/dist-lib/components/graph-view/useTheme.d.ts +26 -0
- package/dist-lib/filter/linked.d.ts +26 -0
- package/dist-lib/filter/reconcile.d.ts +38 -0
- package/dist-lib/filter/toggle.d.ts +12 -0
- package/dist-lib/filter/types.d.ts +162 -0
- package/dist-lib/graph/axis.d.ts +290 -0
- package/dist-lib/graph/groups.d.ts +31 -0
- package/dist-lib/graph/layout.d.ts +104 -0
- package/dist-lib/graph/model.d.ts +149 -0
- package/dist-lib/graph/scalar.d.ts +88 -0
- package/dist-lib/graph/scheme.d.ts +68 -0
- package/dist-lib/graph/shapes.d.ts +227 -0
- package/dist-lib/graph/taxonomy.d.ts +207 -0
- package/dist-lib/graph/validate.d.ts +47 -0
- package/dist-lib/index.d.ts +35 -0
- package/dist-lib/index.js +4895 -0
- package/dist-lib/index.js.map +1 -0
- package/dist-lib/styles.css +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/graph-view/useTaxonomy.ts","../src/graph/axis.ts","../src/components/graph-view/useControllable.ts","../src/components/graph-view/useIsomorphicLayoutEffect.ts","../src/components/graph-view/useTheme.ts","../src/graph/scheme.ts","../src/json.ts","../src/filter/types.ts","../src/components/graph-view/useRecentlyChanged.ts","../src/filter/toggle.ts","../src/components/LockToggle.tsx","../src/components/icons/SearchIcon.tsx","../src/components/FilterBar.tsx","../src/graph/model.ts","../src/filter/reconcile.ts","../src/graph/scalar.ts","../src/graph/shapes.ts","../src/graph/forces.ts","../src/components/graph-view/present.ts","../src/graph/simulation.ts","../src/graph/node-scale.ts","../src/graph/layout-internal.ts","../src/graph/viewport.ts","../src/components/graph-view/wheelZoom.ts","../src/components/GraphCanvas.tsx","../src/components/CanvasErrorBoundary.tsx","../src/components/controls/bucketColor.ts","../src/components/controls/Plot.tsx","../src/components/controls/PopContext.tsx","../src/components/controls/Slider.tsx","../src/components/ForceMap.tsx","../src/components/ChargeControls.tsx","../src/components/SpringControls.tsx","../src/components/controls/Equation.tsx","../src/components/controls/EquationSection.tsx","../src/components/controls/SubTab.tsx","../src/components/PushPullControls.tsx","../src/components/AxisControls.tsx","../src/components/GravityControls.tsx","../src/components/GraphControlPanel.tsx","../src/graph/groups.ts","../src/components/graph-view/useViewport.ts","../src/graph/highlight.ts","../src/components/graph-view/useHighlight.ts","../src/graph/rng.ts","../src/components/graph-view/useSimulation.ts","../src/components/graph-view/visibleGraph.ts","../src/components/graph-view/useVisibleGraph.ts","../src/components/graph-view/useSimulationLoop.ts","../src/components/graph-view/focus.ts","../src/graph/validate.ts","../src/components/GraphView.tsx","../src/components/GroupLegend.tsx","../src/filter/linked.ts","../src/components/graph-view/useLinkedViews.ts","../src/components/graph-view/useFocusRequest.ts","../src/graph/taxonomy.ts","../src/graph/layout.ts"],"sourcesContent":["// The taxonomy seam for components.\n//\n// Pure functions take an taxonomy as a parameter; React components read it from\n// context, because the alternative is threading one prop through every\n// intermediate component that does not otherwise care about it.\n//\n// **There is no default, deliberately.** It shipped with the domain taxonomy as\n// the context default, which meant every consumer resolved correctly whether or\n// not anyone supplied one — and the seam looked finished while no provider\n// existed anywhere in the tree. A missing provider is a wiring bug that hides\n// perfectly: nothing is undefined, nothing throws, the graph renders, and it\n// renders the shipped domain's colours no matter which taxonomy the host meant\n// to install. So the default is `null` and `useTaxonomy` throws on it.\n\nimport React from 'react'\nimport type { Taxonomy } from '../../graph/taxonomy'\n\n/** The taxonomy for every view and bar below the provider, so a host passes it once. */\nexport const TaxonomyContext = React.createContext<Taxonomy | null>(null)\n\n/**\n * The taxonomy in scope.\n *\n * Throws when no provider is above the caller. That is the point: a wrong\n * taxonomy is invisible, a missing one should not be.\n */\nexport function useTaxonomy(): Taxonomy {\n const taxonomy = React.useContext(TaxonomyContext)\n if (taxonomy === null) {\n throw new Error(\n 'useTaxonomy() was called with no TaxonomyContext.Provider above it. ' +\n 'The graph engine resolves styles, filter keys, spring categories and ' +\n 'physics through the taxonomy, so there is no meaningful default — wrap ' +\n 'the tree in <TaxonomyContext.Provider value={…}>.',\n )\n }\n return taxonomy\n}\n","// Axes — what a node's identity is expressed on.\n//\n// A node is a map of axis → value, and every feature (colour, push/pull,\n// each gravity axis, each filter axis) elects exactly ONE axis to operate on. Features never sum, compose or rank\n// across axes: that is what keeps the model explainable, and it is why nothing\n// here offers a way to combine two axes.\n//\n// **Values are strings, and that is load-bearing rather than lazy.** Selections\n// end up as `Set` and `Map` keys in the force kernels, and a `Set` deduplicates\n// by value for a string and by *identity* for an object. A composite value —\n// an object, a tuple, a freshly-built key — silently degenerates to one entry\n// per node, which shifts the band stack and produces no type error and no\n// crash. Anything that would be composite has to be interned to a stable string\n// before it reaches this type; `internValue` is that step.\n\n/** A named axis of node identity, e.g. the type axis or the source-file axis. */\nexport type AxisId = string\n\n/**\n * One value on an axis. A stable interned string: two nodes on the same value\n * must produce keys that are `===` equal, not merely equivalent.\n */\nexport type AxisValue = string\n\n/** Where a node sits on every axis it has a value for. */\nexport type AxisValues = Readonly<Record<AxisId, AxisValue>>\n\n/**\n * Fold anything into a stable value key.\n *\n * Composite selections are the trap this exists for. Passing an array or an\n * object gives a deterministic string rather than a new object each call, so a\n * `Set` of these deduplicates the way the caller expects.\n */\nexport function internValue(parts: string | readonly string[]): AxisValue {\n return typeof parts === 'string' ? parts : parts.join('\\u0000')\n}\n\n/** One button in a filter bar: a label, an optional glyph, and the values it toggles. */\nexport interface FilterChip {\n id: string\n label: string\n icon?: string\n /** A style value whose colour this chip wears, in the pane's theme; none means neutral. */\n tint?: AxisValue\n /**\n * Hover text, when the label alone is not enough to identify the chip.\n *\n * A chip for a path shows its last segment and needs the whole path here:\n * two paths with the same last segment are otherwise indistinguishable.\n * Omitted means \"Show/Hide <label>\", which is right for a chip whose label is\n * already the whole identity.\n */\n tooltip?: string\n /** Toggled as a unit — \"any on\" turns the whole group off. */\n values: readonly AxisValue[]\n}\n\n/**\n * How an axis names itself and its values — the part of a descriptor a control\n * surface needs, split out so the style axis (which is not a filter and has no\n * descriptor) can supply one too and share the identity-mapping builder.\n */\nexport interface AxisNamer {\n id: AxisId\n /** Human label for the axis itself, e.g. for a dropdown or a chip group. */\n label: string\n labelFor(value: AxisValue): string\n /** One or two characters for a token. */\n abbreviationFor(value: AxisValue): string\n}\n\n/** What a filter change asks of the running simulation. */\nexport interface ReheatPolicy {\n /** Alpha to reheat to. */\n alpha: number\n /** Seed newly-revealed nodes at their nearest visible ancestor first. */\n seedRevealed: boolean\n /** Clear the initial-fit guard, so the camera refits to the new extent. */\n refit: boolean\n /** Resume ticking. */\n resume: boolean\n}\n\n/**\n * One filter axis, as a host declares it: its id, its label, and what an\n * empty selection on it means. Everything else is policy with a default that\n * follows from `emptyMeans`, because the two kinds of axis want opposite\n * answers to each question:\n *\n * - an **allow-list** (`emptyMeans: 'none'`): a node with no value is\n * hidden, a focus always expands the selection, a change reseeds the\n * revealed nodes at their ancestors, refits the camera and reheats to\n * 0.5, and the initial selection is every value;\n * - a **narrowing** axis (`emptyMeans: 'all'`): a node with no value stays\n * visible, a focus expands only an already-active selection (adding the\n * first value would narrow everything to one thing — the opposite of\n * focus), a change reheats to 0.3 in place and keeps the user's pan and\n * zoom, and the initial selection is empty.\n *\n * **It does not carry the values.** Those are derived, not declared: the style\n * axis's values come from the taxonomy, and an origin axis's from scanning the\n * graph. Declaring them here too would be the same fact in two places with\n * nothing enforcing agreement, free to drift apart unnoticed.\n */\nexport interface AxisDescriptor {\n id: AxisId\n /** Human label for the axis itself, e.g. for a dropdown or a chip group. Default: the id. */\n label?: string\n /**\n * What an EMPTY selection on this axis means: `none` hides everything (the\n * chips are an allow-list), `all` shows everything (the chips narrow).\n */\n emptyMeans: 'all' | 'none'\n /** What happens to a node carrying NO value on this axis while a selection is active. */\n absentMeans?: 'visible' | 'hidden'\n /**\n * How a focus widens this axis to reveal its target: `always` adds the\n * target's value; `whenActive` adds it only while a selection is active\n * (on a narrowing axis, adding the first value would narrow everything to\n * that one value — the opposite of a reveal). Default: `always` on an\n * allow-list, `whenActive` on a narrowing axis.\n */\n widens?: 'always' | 'whenActive'\n /**\n * Whether a change on this axis keeps the user's pan and zoom. Default: an\n * allow-list refits the camera to the new extent (and seeds revealed nodes\n * at their ancestors); a narrowing axis keeps the camera and reheats in\n * place.\n */\n keepCamera?: boolean\n /**\n * How this axis's available values group into chips. Not one chip per\n * value: a chip may cover several values. Takes the available values\n * rather than reading a declared list, because an axis's values are\n * derived from the graph. Absent, one chip per value, labelled by\n * `labelFor` and, on the style axis, wearing the value's icon and tint.\n */\n chipsFor?(values: readonly AxisValue[]): readonly FilterChip[]\n /** A value's display name. Absent: the style's label on the style axis, else the value itself. */\n labelFor?(value: AxisValue): string\n /** One or two characters for a token. Absent: the style's abbreviation on the style axis, else the first two characters of `labelFor`'s answer. */\n abbreviationFor?(value: AxisValue): string\n /** A chip's hover text, when the label alone does not identify the value (a file's full path under its basename). Absent: none. */\n tooltipFor?(value: AxisValue): string\n /** A value's glyph, drawn on its chip. Absent: the style's icon on the style axis, none elsewhere. */\n iconFor?(value: AxisValue): string\n /**\n * The values selected when the view first opens, given the values the\n * graph presents on this axis. Absent: every value on an allow-list,\n * none on a narrowing axis. `initialFilter` reads it; a host that passes the\n * `defaultFilter` prop has chosen its own start, and this is not consulted.\n */\n initialSelection?(values: readonly AxisValue[]): readonly AxisValue[]\n /**\n * How the bar draws this axis's chips. `chip` (the default) is a tinted,\n * glyph-led button that reads as a category; `tag` is a quieter, neutral\n * label that reads as an origin — a file, a source, a place — and shows an\n * \"everything included\" state when the selection is empty on an\n * `emptyMeans: 'all'` axis.\n */\n presentation?: 'chip' | 'tag'\n}\n\n/** A filter axis with every policy filled in — what `Taxonomy.filterAxes` carries. */\nexport interface ResolvedAxisDescriptor extends AxisNamer {\n emptyMeans: 'all' | 'none'\n absentMeans: 'visible' | 'hidden'\n /** How a focus widens this axis to reveal its target. */\n widens: 'always' | 'whenActive'\n /** Whether a change on this axis keeps the user's pan and zoom (a narrowing axis does; an allow-list refits). */\n keepCamera: boolean\n tooltipFor?(value: AxisValue): string\n /** A value's glyph, or '' for none. */\n iconFor(value: AxisValue): string\n chipsFor(values: readonly AxisValue[]): readonly FilterChip[]\n initialSelection(values: readonly AxisValue[]): readonly AxisValue[]\n presentation: 'chip' | 'tag'\n}\n\n/** How the style axis names and adorns its values, for a descriptor's defaults. */\nexport interface StyleNamer {\n axis: AxisId\n labelFor(value: AxisValue): string\n abbreviationFor(value: AxisValue): string\n iconFor(value: AxisValue): string\n}\n\n/**\n * A declared axis with every default filled in. The policies follow from\n * `emptyMeans`; the names follow from the style axis when this is it, and\n * from the value itself otherwise.\n */\nexport function resolveDescriptor(d: AxisDescriptor, style: StyleNamer): ResolvedAxisDescriptor {\n const allow = d.emptyMeans === 'none'\n const onStyle = d.id === style.axis\n const labelFor = d.labelFor ?? (onStyle ? style.labelFor : (v: AxisValue) => v)\n const abbreviationFor = d.abbreviationFor ?? (onStyle ? style.abbreviationFor : (v: AxisValue) => labelFor(v).slice(0, 2))\n const keepCamera = d.keepCamera ?? !allow\n const iconFor = d.iconFor ?? (onStyle ? style.iconFor : () => '')\n return {\n id: d.id,\n label: d.label ?? d.id,\n emptyMeans: d.emptyMeans,\n absentMeans: d.absentMeans ?? (allow ? 'hidden' : 'visible'),\n widens: d.widens ?? (allow ? 'always' : 'whenActive'),\n keepCamera,\n labelFor,\n abbreviationFor,\n tooltipFor: d.tooltipFor,\n iconFor,\n chipsFor: d.chipsFor ?? (values => values.map(v => ({\n id: v,\n label: labelFor(v),\n icon: iconFor(v) || undefined,\n tint: onStyle ? v : undefined,\n tooltip: d.tooltipFor?.(v),\n values: [v],\n }))),\n initialSelection: d.initialSelection ?? (values => (allow ? values : [])),\n presentation: d.presentation ?? 'chip',\n }\n}\n\n/** Where a node sits on one axis, or undefined when it carries no value there. */\nexport function valueOf(\n values: AxisValues,\n axis: AxisId,\n): AxisValue | undefined {\n return values[axis]\n}\n\n// ── Axis mappings ───────────────────────────────────────────────────────────\n\n/** One bucket of a mapping: a named group of source values. */\nexport interface MappingBucket {\n id: string\n /** Display name — a tooltip, a column header. */\n label: string\n /**\n * One or two characters for a token. Required rather than derived: a control\n * surface has no room for a name, and truncating one is a guess the host can\n * answer better.\n */\n abbreviation: string\n values: readonly AxisValue[]\n}\n\n/**\n * A function from an axis's values to an equal-or-smaller set.\n *\n * The point is to bound a control surface. Push/pull renders one draggable\n * token per mapped pair, so an axis with fifty values has no usable control\n * surface until something collapses it — and a mapping is that something. The\n * existing example is three separate kinds folded into one filter chip.\n */\nexport interface AxisMapping {\n id: string\n label: string\n axis: AxisId\n buckets: readonly MappingBucket[]\n /** The bucket a value belongs to, or undefined if the mapping omits it. */\n bucketFor(value: AxisValue | undefined): MappingBucket | undefined\n}\n\n/** What `createAxisMapping` is given: the mapping's id, label, axis and buckets, before the buckets are checked for overlap. */\nexport interface AxisMappingSpec {\n id: string\n label: string\n axis: AxisId\n buckets: readonly MappingBucket[]\n}\n\n/**\n * Build a mapping, refusing to build an incoherent one.\n *\n * **Buckets must not intersect.** A value in two buckets has no answer to \"which\n * token tunes this node\", and the failure would otherwise surface as a layout\n * that depends on bucket declaration order — the kind of bug that is found\n * months later by someone reordering a list for readability. So it throws here,\n * loudly, at construction, rather than resolving arbitrarily per lookup.\n */\nexport function createAxisMapping(spec: AxisMappingSpec): AxisMapping {\n const owner = new Map<AxisValue, string>()\n const collisions: string[] = []\n const ids = new Set<string>()\n for (const bucket of spec.buckets) {\n // Two buckets with one id are one override key for two sets of values, and\n // an empty bucket is a token that tunes nothing; both are refused here\n // rather than resolved arbitrarily per lookup.\n if (ids.has(bucket.id)) collisions.push(`bucket id ${JSON.stringify(bucket.id)} is declared twice`)\n ids.add(bucket.id)\n if (bucket.values.length === 0) collisions.push(`bucket ${JSON.stringify(bucket.id)} has no values`)\n for (const value of bucket.values) {\n const existing = owner.get(value)\n if (existing !== undefined && existing !== bucket.id) {\n collisions.push(`${JSON.stringify(value)} is in both ${existing} and ${bucket.id}`)\n } else {\n owner.set(value, bucket.id)\n }\n }\n }\n if (collisions.length > 0) {\n throw new Error(\n `axis mapping ${JSON.stringify(spec.id)} is incoherent: ` +\n collisions.join('; ') +\n '. A value in two buckets has no defined answer for which one tunes it.',\n )\n }\n\n const byValue = new Map<AxisValue, MappingBucket>()\n for (const bucket of spec.buckets) {\n for (const value of bucket.values) byValue.set(value, bucket)\n }\n return {\n id: spec.id,\n label: spec.label,\n axis: spec.axis,\n buckets: spec.buckets,\n bucketFor: value => (value === undefined ? undefined : byValue.get(value)),\n }\n}\n\n/**\n * The identity mapping for an axis — one bucket per value.\n *\n * Useful as the default for an axis that is already small enough to control\n * directly, so a consumer never has to special-case \"no mapping\".\n */\nexport function identityMapping(\n axis: AxisNamer,\n values: readonly AxisValue[],\n): AxisMapping {\n // A namer answers per value and cannot see its neighbours, so two values may\n // abbreviate alike (two files whose names share a prefix). A token label that\n // is not unique within its own mapping is useless, so duplicates are numbered\n // here, where the whole set is visible.\n const seen = new Map<string, number>()\n return createAxisMapping({\n id: `${axis.id}:identity`,\n label: axis.label,\n axis: axis.id,\n buckets: values.map(value => {\n const base = axis.abbreviationFor(value)\n const n = (seen.get(base) ?? 0) + 1\n seen.set(base, n)\n return {\n id: value,\n label: axis.labelFor(value),\n abbreviation: n === 1 ? base : `${base}${n}`,\n values: [value],\n }\n }),\n })\n}\n\n/**\n * True when every bucket is one value named after itself — the shape under\n * which bucket ids ARE the axis's values, so a rule written over values applies\n * to buckets unchanged. Structural, not a flag: a flag would be a second fact\n * about the buckets with nothing holding it to the first.\n */\nexport function isIdentityMapping(mapping: AxisMapping): boolean {\n return mapping.buckets.every(b => b.values.length === 1 && b.values[0] === b.id)\n}\n\n/**\n * The distinct values a set of subjects carries on one axis, in first-encounter\n * order. The domain of an axis is whatever the graph presents on it — the\n * engine declares no value vocabulary of its own — so this is how a control\n * surface or a filter learns what an axis can take.\n */\nexport function valuesOn(\n subjects: Iterable<{ values: AxisValues }>,\n axis: AxisId,\n): AxisValue[] {\n const seen = new Set<AxisValue>()\n for (const s of subjects) {\n const v = s.values[axis]\n if (v !== undefined) seen.add(v)\n }\n return [...seen]\n}\n\n/**\n * How many entities carry each value on each of the given axes — the chip\n * badges' numbers when a host does not supply its own reading. The copies of\n * one entity (`copyKey`) count once; a subject with neither key nor id counts\n * as itself. An axis no subject has a value on gets an empty map, never a\n * missing one.\n */\nexport function countsOn(\n subjects: Iterable<{ values: AxisValues; id?: string; copyKey?: string }>,\n axes: readonly AxisId[],\n): ReadonlyMap<AxisId, ReadonlyMap<AxisValue, number>> {\n const counted = new Map<AxisId, Map<AxisValue, Set<string>>>(axes.map(d => [d, new Map()]))\n const out = new Map<AxisId, Map<AxisValue, number>>(axes.map(d => [d, new Map()]))\n let anonymous = 0\n for (const s of subjects) {\n const key = s.copyKey ?? s.id ?? `\\u0000${anonymous++}`\n for (const d of axes) {\n const v = s.values[d]\n if (v === undefined) continue\n const keys = counted.get(d)!\n let seen = keys.get(v)\n if (!seen) { seen = new Set(); keys.set(v, seen) }\n if (seen.has(key)) continue\n seen.add(key)\n const m = out.get(d)!\n m.set(v, (m.get(v) ?? 0) + 1)\n }\n }\n return out\n}\n\n/**\n * Does this table declare `key` itself, rather than inheriting it?\n *\n * Every keyed lookup on an axis value must go through this. Values are\n * host-supplied strings, so a plain `table[key]` read walks the prototype chain\n * and a value named `constructor`, `toString` or `__proto__` resolves to an\n * inherited member. That member is not `undefined`, which means every\n * `?? default` and `!== undefined` guard downstream silently accepts it.\n *\n * Spelled with `Object.prototype.hasOwnProperty.call` rather than the tidier\n * `Object.hasOwn` so it needs no lib bump, and called on the prototype rather\n * than the table so a table that itself declares a key named `hasOwnProperty`\n * cannot shadow the check.\n */\nexport function hasOwn(table: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(table, key)\n}\n\n/**\n * What a change on one axis asks of the simulation, from its two declared\n * policies: an allow-list reheats harder; an axis that keeps the camera\n * neither seeds revealed nodes at their ancestors nor refits.\n */\nexport function reheatOf(axis: Pick<ResolvedAxisDescriptor, 'emptyMeans' | 'keepCamera'>): ReheatPolicy {\n return { alpha: axis.emptyMeans === 'none' ? 0.5 : 0.3, seedRevealed: !axis.keepCamera, refit: !axis.keepCamera, resume: true }\n}\n\n/** What a filter change asks of the running simulation, folded across axes. */\nexport interface ReheatPlan extends ReheatPolicy {\n /** The axes that actually changed — empty means \"do nothing\". */\n changed: readonly AxisId[]\n}\n\n/**\n * Fold the per-axis reheat policies of every axis that changed. Several axes\n * can move at once (a view switch does exactly that), so the policies combine\n * rather than the last one winning: the strongest reheat, and any axis that\n * wants seeding, refitting or resuming gets it.\n */\nexport function foldReheatPolicy(\n changedAxes: readonly ResolvedAxisDescriptor[],\n): ReheatPlan {\n let alpha = 0, seedRevealed = false, refit = false, resume = false\n for (const axis of changedAxes) {\n const policy = reheatOf(axis)\n alpha = Math.max(alpha, policy.alpha)\n seedRevealed = seedRevealed || policy.seedRevealed\n refit = refit || policy.refit\n resume = resume || policy.resume\n }\n return { alpha, seedRevealed, refit, resume, changed: changedAxes.map(d => d.id) }\n}\n\n/**\n * The structural meaning of an edge — the one fact about an edge the engine\n * reads for itself (graduation, highlighting and spring resolution branch on\n * it). It is a field of the edge, not an axis: every axis, on nodes and on\n * edges, is the host's own.\n */\nexport type Relation = 'containment' | 'dependency'\n","// useControllable — a prop that a host may control or leave to the view.\n//\n// The React convention: a value with its change callback is controlled, the\n// host owning the state; the value absent, the view keeps its own, seeded\n// once from `initial`, and still tells the host about changes through the\n// callback when one is given. A one-view host passes nothing; a host that\n// shares state between views passes both.\n\nimport React from 'react'\n\nexport function useControllable<T>(\n value: T | undefined,\n onChange: ((next: T) => void) | undefined,\n initial: () => T,\n /** The prop's name, for the warning when it flips between controlled and uncontrolled. */\n name: string,\n): [T, (next: T) => void] {\n const [own, setOwn] = React.useState(initial)\n const controlled = value !== undefined\n useControlledWarning(name, controlled)\n const onChangeRef = React.useRef(onChange)\n onChangeRef.current = onChange\n const set = React.useCallback((next: T) => {\n if (!controlled) setOwn(next)\n onChangeRef.current?.(next)\n }, [controlled])\n return [controlled ? value : own, set]\n}\n\n/**\n * Says once, on the console, when a prop goes from defined to undefined or\n * back after the mount: the view then switches between the host's value and\n * its own, which is never what the host meant (`selectedId={sel?.id}` for a\n * selection that may be empty — `sel?.id ?? null` keeps it controlled).\n */\nexport function useControlledWarning(name: string, controlled: boolean): void {\n const wasRef = React.useRef(controlled)\n const saidRef = React.useRef(false)\n React.useEffect(() => {\n if (wasRef.current !== controlled && !saidRef.current) {\n saidRef.current = true\n console.warn(`[force-graph] \\`${name}\\` changed from ${wasRef.current ? 'controlled to uncontrolled' : 'uncontrolled to controlled'}: pass a value on every render (null for none), or on none.`)\n }\n wasRef.current = controlled\n }, [controlled, name])\n}\n\n/**\n * The previous value for as long as the next is equal to it, so a host that\n * rebuilds a value each render (a filter parsed from a URL) does not read as\n * a change downstream: memos and effects key on the object this returns.\n */\nexport function useStable<T>(value: T, equals: (a: T, b: T) => boolean): T {\n const ref = React.useRef(value)\n if (ref.current !== value && !equals(ref.current, value)) ref.current = value\n return ref.current\n}\n\n/** The latest value of a callback prop, read from an effect or a handler without being a dependency. */\nexport function useLatest<T>(value: T): React.MutableRefObject<T> {\n const ref = React.useRef(value)\n ref.current = value\n return ref\n}\n","// A layout effect where there is a layout: on a server render there is none,\n// and React says so on the console for every `useLayoutEffect` it meets.\n\nimport React from 'react'\n\nexport const useIsomorphicLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect\n","// The pane's theme. The user's choice is system, light or dark; the resolved\n// theme is that choice, or — for system — the host's: what it passes as\n// `systemTheme`, else the nearest ancestor that declares one with\n// `data-theme`, else `prefers-color-scheme`, the last two observed so a host\n// that switches mid-session is followed. The pane then carries the resolved\n// theme as its own `data-theme`, so its stylesheet tokens follow the same\n// answer the canvas draws with — one resolver, not two.\n\nimport React from 'react'\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'\nimport type { Theme } from '../../graph/scheme'\n\n/** A theme choice: `system` follows the host, `light` and `dark` are fixed. */\nexport type ThemeChoice = 'system' | Theme\n\n/** The three choices, in the order the pane's button cycles them. */\nexport const THEME_CHOICES: readonly ThemeChoice[] = ['system', 'light', 'dark']\n\n/** A persisted or host-supplied choice, or system for anything outside the three: discard the field, not the state. */\nexport function themeChoiceOf(value: unknown): ThemeChoice {\n return (THEME_CHOICES as readonly unknown[]).includes(value) ? (value as ThemeChoice) : 'system'\n}\n\n/** What an element that marks a theme says: its `data-theme`. */\nexport interface ThemeMarker {\n getAttribute(name: string): string | null\n}\n\n/** The one attribute the library reads a host's theme from. */\nexport const THEME_MARKER_SELECTOR = '[data-theme]'\n\n/** The theme a marker declares; a `data-theme` outside light and dark declares nothing. */\nexport function themeOfMarker(marker: ThemeMarker | null): Theme | undefined {\n if (!marker) return undefined\n const attr = marker.getAttribute('data-theme')\n return attr === 'dark' || attr === 'light' ? attr : undefined\n}\n\n/** What the host has decided ABOVE `el`: the nearest marking ancestor, else the OS. */\nexport function hostTheme(el: Element | null): Theme {\n // No window (a server render): light, the same answer as a host that declares nothing.\n if (typeof window === 'undefined') return 'light'\n let marker: Element | null = el?.parentElement?.closest(THEME_MARKER_SELECTOR) ?? null\n while (marker) {\n const theme = themeOfMarker(marker)\n if (theme) return theme\n marker = marker.parentElement?.closest(THEME_MARKER_SELECTOR) ?? null\n }\n return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\n/**\n * The host's theme for the element at `ref`, re-read when any ancestor's\n * `data-theme` changes and when the OS preference changes. With `enabled`\n * false nothing is read or observed, and the answer is light.\n */\nexport function useHostTheme(ref: React.RefObject<HTMLElement | null>, enabled = true): Theme {\n const [host, setHost] = React.useState<Theme>(() => (enabled ? hostTheme(ref.current) : 'light'))\n // A layout effect: the ref is empty on the first render, so the first\n // answer is read once the element exists and before anything is painted.\n useIsomorphicLayoutEffect(() => {\n if (!enabled) return\n const read = () => setHost(hostTheme(ref.current))\n read()\n if (typeof MutationObserver === 'undefined') return\n const observer = new MutationObserver(read)\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'], subtree: true })\n const media = window.matchMedia?.('(prefers-color-scheme: dark)')\n media?.addEventListener?.('change', read)\n return () => { observer.disconnect(); media?.removeEventListener?.('change', read) }\n }, [ref, enabled])\n return host\n}\n\n/** The resolved theme for the pane rooted at `ref`, given the user's choice and, for `system`, what the host says it means — else what its ancestors and the OS say. */\nexport function useTheme(ref: React.RefObject<HTMLElement | null>, choice: ThemeChoice, systemTheme?: Theme): Theme {\n const host = useHostTheme(ref, systemTheme === undefined)\n return choice === 'system' ? systemTheme ?? host : choice\n}\n","// The colour scheme — what a taxonomy's colours look like, in both themes at\n// once, and the three readers that turn them into pixels: the custom-property\n// name a value's colour is published under, the inline variables a pane\n// carries for the resolved theme, and a colour normaliser for the canvas,\n// which cannot throw and so reads only the forms the taxonomy admits.\n\nimport type { AxisValue } from './axis'\nimport type { Taxonomy } from './taxonomy'\n\n/** The two themes every colour is declared in. */\nexport type Theme = 'light' | 'dark'\n/** Both themes, in order. */\nexport const THEMES: readonly Theme[] = ['light', 'dark']\n\n/** A fill and a border. */\nexport interface ColorPair {\n /** Canvas fill colour. */\n readonly fill: string\n /** Canvas border colour, a few stops darker than the fill. */\n readonly border: string\n}\n\n/** A value's colour in both themes — declared together, so no half can go missing. */\nexport interface ThemedColor {\n readonly light: ColorPair\n readonly dark: ColorPair\n}\n\n/** How an edge type is stroked: its colour per theme, base alpha, dash pattern and width. */\nexport interface EdgeStroke {\n readonly color: { readonly light: string; readonly dark: string }\n readonly alpha: number\n readonly dash: readonly number[]\n readonly width: number\n}\n\n/**\n * The stem of a value's custom-property name: the value id in kebab case\n * (`twoWords` → `two-words`). Derived, never stored.\n */\nexport function cssSuffix(value: AxisValue): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/[^A-Za-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase()\n}\n\n\n/**\n * The inline custom properties a pane carries for one theme: every style\n * value's fill and border. Set on the pane's root, they are what tokens, chip\n * tints and typed rows read, and they follow the resolved theme by being\n * recomputed, not overridden.\n */\nexport function schemeVars(taxonomy: Taxonomy, theme: Theme): Record<string, string> {\n const vars: Record<string, string> = {}\n for (const key of taxonomy.styleValues) {\n const c = taxonomy.styleOf(key).color[theme]\n vars[taxonomy.colorVar(key)] = c.fill\n vars[`${taxonomy.colorVar(key)}-border`] = c.border\n }\n return vars\n}\n\nconst HEX3 = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i\nconst HEX6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i\nconst RGB = /^rgba?\\(\\s*(\\d{1,3})\\s*[, ]\\s*(\\d{1,3})\\s*[, ]\\s*(\\d{1,3})\\s*(?:[,/]\\s*[\\d.]+%?\\s*)?\\)$/i\nconst HSL = /^hsla?\\(\\s*(-?[\\d.]+)(?:deg)?\\s*[, ]\\s*([\\d.]+)%\\s*[, ]\\s*([\\d.]+)%\\s*(?:[,/]\\s*[\\d.]+%?\\s*)?\\)$/i\n\n/** CSS's hsl → rgb, hue in degrees, saturation and lightness in percent, channels rounded to 0–255. */\nfunction hslToRgb(h: number, s: number, l: number): readonly [number, number, number] {\n const sat = Math.min(100, Math.max(0, s)) / 100\n const light = Math.min(100, Math.max(0, l)) / 100\n const hue = ((h % 360) + 360) % 360\n const chroma = (1 - Math.abs(2 * light - 1)) * sat\n const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1))\n const m = light - chroma / 2\n const [r, g, b] =\n hue < 60 ? [chroma, x, 0] : hue < 120 ? [x, chroma, 0] : hue < 180 ? [0, chroma, x]\n : hue < 240 ? [0, x, chroma] : hue < 300 ? [x, 0, chroma] : [chroma, 0, x]\n return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)]\n}\n\n/**\n * The red, green and blue of a scheme colour, or undefined. A scheme colour\n * is hex of either length, `rgb()`/`rgba()` or `hsl()`/`hsla()` — a form\n * every reader can parse without a browser, so the taxonomy can refuse\n * anything else at construction (`isSchemeColor`) and no reader meets a\n * colour it cannot read.\n */\nexport function rgbOf(color: string): readonly [number, number, number] | undefined {\n const c = color.trim()\n let m = HEX6.exec(c)\n if (m) return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)]\n m = HEX3.exec(c)\n if (m) return [parseInt(m[1] + m[1], 16), parseInt(m[2] + m[2], 16), parseInt(m[3] + m[3], 16)]\n m = RGB.exec(c)\n if (m) return [Number(m[1]), Number(m[2]), Number(m[3])]\n m = HSL.exec(c)\n if (m) return hslToRgb(Number(m[1]), Number(m[2]), Number(m[3]))\n return undefined\n}\n\n/** Whether a colour is one every reader parses: hex, `rgb()` or `hsl()`. */\nexport const isSchemeColor = (color: string): boolean => rgbOf(color) !== undefined\n\nconst NEUTRAL: readonly [number, number, number] = [148, 163, 184]\nconst REFUSED = new Set<string>()\n\n/**\n * A colour at an alpha, as `rgba()`, for a canvas gradient or fill. A colour\n * outside the scheme's forms becomes a neutral grey, said once per colour on\n * the console — never a throw, because this runs inside the draw loop.\n * Unreachable for a taxonomy's colours, which are refused at\n * construction; it guards the library's own group palette and any caller\n * that bypasses the taxonomy.\n */\nexport function withAlpha(color: string, alpha: number): string {\n let rgb = rgbOf(color)\n if (!rgb) {\n if (!REFUSED.has(color)) {\n REFUSED.add(color)\n console.error(`[scheme] ${JSON.stringify(color)} is not a colour the canvas can read; drawing it neutral`)\n }\n rgb = NEUTRAL\n }\n return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${alpha})`\n}\n\n/**\n * A stylesheet for a host's own CSS: every style value's fill and border on\n * `:root`, and the dark values that differ under `darkSelector` — by default\n * `[data-theme=\"dark\"]`, the attribute the pane carries on its own root; a\n * host that marks its theme another way names its own selector. The pane\n * does not read this — it carries `schemeVars` inline — so a host that\n * styles nothing of its own by node value never needs it.\n */\nexport function schemeStylesheet(taxonomy: Taxonomy, darkSelector = '[data-theme=\"dark\"]'): string {\n const lightVars: string[] = []\n const darkVars: string[] = []\n for (const key of taxonomy.styleValues) {\n const { light, dark } = taxonomy.styleOf(key).color\n const v = taxonomy.colorVar(key)\n lightVars.push(` ${v}: ${light.fill};`)\n lightVars.push(` ${v}-border: ${light.border};`)\n if (dark.fill !== light.fill) darkVars.push(` ${v}: ${dark.fill};`)\n if (dark.border !== light.border) darkVars.push(` ${v}-border: ${dark.border};`)\n }\n const parts: string[] = [`:root {\\n${lightVars.join('\\n')}\\n}`]\n if (darkVars.length > 0) parts.push(`${darkSelector} {\\n${darkVars.join('\\n')}\\n}`)\n return parts.join('\\n')\n}\n","// What stored data may be trusted to be: the two shape checks every\n// `fromJSON` reader shares, so none guesses.\n\n/** What JSON.parse produces for an object: a plain record, not a Map, a Set, a Date or an instance — whose entries are its own keys. */\nexport function isPlainRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/** A number a layout can use: finite, not NaN. */\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n","// The filter model a host's views share: a selection per axis, over whatever\n// axes the host declares, and the operations on it.\n//\n// Every operation here is copy-on-write: the axis being edited gets a new\n// Set, every other axis keeps its exact instance, and a no-op edit returns the\n// same state. The view compares selections by content, so a host that rebuilds\n// a filter from a URL or a store is not read as a change — identity is the\n// cheap path, not the contract.\n//\n// The search query lives outside a FilterState because the views share it.\n\nimport type { AxisId, AxisValues, ResolvedAxisDescriptor } from '../graph/axis'\nimport { valuesOn } from '../graph/axis'\nimport type { Graph } from '../graph/model'\nimport type { Taxonomy } from '../graph/taxonomy'\nimport { isPlainRecord } from '../json'\n\n/** The chosen values on one axis. Empty means what the axis's descriptor says. */\nexport type AxisSelection = ReadonlySet<string>\n\n/**\n * A selection per axis. A `Map`, not a `Record`: an axis the host did not\n * declare has no key, so a misspelt axis is a miss the type sees rather than\n * an `undefined` every downstream `.has` reads as \"nothing selected\".\n */\nexport type FilterState = ReadonlyMap<AxisId, AxisSelection>\n\n/**\n * Which axes are locked. A locked axis keeps its selection when a `manual`\n * switch adopts the source view's; a focus widens it anyway and reports it.\n */\nexport type AxisLocks = ReadonlyMap<AxisId, boolean>\n\n\nconst EMPTY: AxisSelection = new Set<string>()\n\n/**\n * The selection on one axis, or an empty one.\n *\n * Always returns the *same* empty Set, so an absent axis does not change\n * identity between reads — a fresh `new Set()` per call would defeat exactly the\n * memoisation this module exists to protect.\n */\nexport function selectionFor(state: FilterState, axis: AxisId): AxisSelection {\n return state.get(axis) ?? EMPTY\n}\n\n/** Every axis the state carries a selection for. */\nexport function axesOf(state: FilterState): AxisId[] {\n return [...state.keys()].sort()\n}\n\n/** Whether two selections hold the same values. */\nexport function selectionsEqual(a: AxisSelection, b: AxisSelection): boolean {\n if (a === b) return true\n if (a.size !== b.size) return false\n for (const v of a) if (!b.has(v)) return false\n return true\n}\n\n/**\n * Replace one axis's selection, copy-on-write.\n *\n * Returns `state` **itself** when the new selection is content-equal to the old\n * one, so a no-op edit changes no identity anywhere. Otherwise only the edited\n * axis gets a new Set; every other axis is carried over by reference.\n */\nexport function withSelection(\n state: FilterState,\n axis: AxisId,\n next: AxisSelection,\n): FilterState {\n if (selectionsEqual(selectionFor(state, axis), next)) return state\n const out = new Map(state)\n out.set(axis, next)\n return out\n}\n\n/** Structural equality across every axis either state mentions. */\nexport function filterStatesEqual(a: FilterState, b: FilterState): boolean {\n if (a === b) return true\n const axes = new Set([...a.keys(), ...b.keys()])\n for (const axis of axes) {\n if (!selectionsEqual(selectionFor(a, axis), selectionFor(b, axis))) return false\n }\n return true\n}\n\n/** Structural equality of two lock maps. */\nexport function locksEqual(a: AxisLocks, b: AxisLocks): boolean {\n if (a === b) return true\n if (a.size !== b.size) return false\n for (const [k, v] of a) if (b.get(k) !== v) return false\n return true\n}\n\n/** Element-wise equality of two lists of ids. */\nexport function listEqual(a: readonly string[], b: readonly string[]): boolean {\n return a === b || (a.length === b.length && a.every((v, i) => v === b[i]))\n}\n\n/** Set one axis's lock, copy-on-write. */\nexport function withLock(locks: AxisLocks, axis: AxisId, locked: boolean): AxisLocks {\n const out = new Map(locks)\n out.set(axis, locked)\n return out\n}\n\n/** Whether an axis is locked against adoption. */\nexport function lockedFor(locks: AxisLocks, axis: AxisId): boolean {\n return locks.get(axis) ?? false\n}\n\n/**\n * Where a focus transition wants to land: the target's value per axis. An\n * axis the target says nothing about is absent, which the reconciler reads\n * as \"no expansion needed\". A host that also needs the target's name for a\n * view of its own carries it in a type of its own.\n */\nexport type FocusTarget = {\n values: Readonly<Record<AxisId, string | undefined>>\n}\n\n/** How a view is being switched to: by the user (`manual`: adopt the source's selections) or to show a node (`focus`: widen for its values). */\nexport type ViewTransition =\n | { kind: 'manual' }\n | { kind: 'focus'; target: FocusTarget }\n\n/**\n * A reconciled filter, and which locked axes a focus had to widen to show its\n * target — the ones the destination's bar flashes.\n */\nexport type ReconcileResult = {\n filter: FilterState\n overriddenLocks: ReadonlySet<AxisId>\n}\n\n/**\n * The filter a view starts from: on every filter axis, what its descriptor's\n * `initialSelection` chooses from the values the graph presents there —\n * every value on an allow-list, none on a narrowing axis, or the host's own\n * choice.\n */\nexport function initialFilter(taxonomy: Pick<Taxonomy, 'filterAxes'>, graph: Pick<Graph, 'nodes'>): FilterState {\n const out = new Map<AxisId, AxisSelection>()\n for (const d of taxonomy.filterAxes) {\n const values = valuesOn(graph.nodes, d.id)\n const chosen = new Set(d.initialSelection(values))\n // The bar toggles a chip's values as a unit, so a selection that splits\n // one would show the chip as on with half its nodes hidden, and the\n // first click would turn the whole chip off. Snap to the whole chip —\n // selecting is the safe direction — and say so once per chip.\n const present = new Set(values)\n for (const chip of d.chipsFor(values)) {\n const mine = chip.values.filter(v => present.has(v))\n const on = mine.filter(v => chosen.has(v))\n // Judged as the bar judges it: any value of the chip chosen — a value the\n // graph lacks included — reads as on.\n if (!chip.values.some(v => chosen.has(v)) || on.length === mine.length) continue\n for (const v of mine) chosen.add(v)\n warnSplitChip(taxonomy, d.id, chip.id, mine.filter(v => !on.includes(v)))\n }\n out.set(d.id, chosen)\n }\n return out\n}\n\n/**\n * A filter re-seeded for a graph that changed: every filter axis the user has\n * not touched takes its opening selection on `graph` (`initialFilter`'s), so\n * a host that mounts empty while its data loads is not left blank and a value\n * first seen later is not born hidden; an axis in `touched` keeps its\n * selection. Which axes the user touched is state, not an inference — the\n * bar names the axis of every change it makes (`onFilterChange`'s second\n * argument), the view and `useLinkedViews` keep the set, and a host that\n * controls its filter keeps its own and passes it here from its data effect.\n * Absent, every axis follows the graph. The same object when nothing moves.\n */\nexport function reseedFilter(filter: FilterState, taxonomy: Pick<Taxonomy, 'filterAxes'>, graph: Pick<Graph, 'nodes'>, touched: ReadonlySet<AxisId> = new Set()): FilterState {\n const seed = initialFilter(taxonomy, graph)\n let next = filter\n for (const d of taxonomy.filterAxes) {\n if (touched.has(d.id)) continue\n next = withSelection(next, d.id, selectionFor(seed, d.id))\n }\n return next\n}\n\n/** The axes whose selection differs between two filters — what a write touched. */\nexport function changedAxes(a: FilterState, b: FilterState): AxisId[] {\n const out: AxisId[] = []\n for (const axis of new Set([...a.keys(), ...b.keys()])) {\n if (!selectionsEqual(selectionFor(a, axis), selectionFor(b, axis))) out.push(axis)\n }\n return out\n}\n\n// Once per taxonomy and chip, not per process: a second taxonomy with the\n// same split is a second mistake.\nconst warnedSplits = new WeakMap<object, Set<string>>()\nfunction warnSplitChip(taxonomy: object, axis: AxisId, chip: string, added: readonly string[]): void {\n let warned = warnedSplits.get(taxonomy)\n if (!warned) { warned = new Set(); warnedSplits.set(taxonomy, warned) }\n const key = `${axis}\\u0000${chip}`\n if (warned.has(key)) return\n warned.add(key)\n console.warn(`[filter] the initial selection on axis ${JSON.stringify(axis)} splits the chip ${JSON.stringify(chip)}; the whole chip is selected (${added.map(v => JSON.stringify(v)).join(', ')} added). Choose a selection the chips can show.`)\n}\n\n// The records below are built from entries, not by assignment: an axis id is\n// a host string, and `out['__proto__'] = …` on a literal sets the prototype\n// instead of a key, so that axis would vanish from the stored text.\n\n/** A filter as plain data — each axis's selected values in an array — for a URL, a store or a saved layout. */\nexport function filterToJSON(filter: FilterState): Record<AxisId, string[]> {\n return Object.fromEntries([...filter].map(([axis, selection]) => [axis, [...selection]]))\n}\n\n/**\n * The filter `filterToJSON` wrote, or undefined when `value` is not that\n * shape — stored data is not trusted to be, and a host falls back to its\n * default rather than to a filter that means something else.\n */\nexport function filterFromJSON(value: unknown): FilterState | undefined {\n if (!isPlainRecord(value)) return undefined\n const out = new Map<AxisId, AxisSelection>()\n for (const [axis, values] of Object.entries(value)) {\n if (!Array.isArray(values) || !values.every((v): v is string => typeof v === 'string')) return undefined\n out.set(axis, new Set(values))\n }\n return out\n}\n\n/** Locks as plain data — a boolean per axis — beside `filterToJSON`. */\nexport function locksToJSON(locks: AxisLocks): Record<AxisId, boolean> {\n return Object.fromEntries([...locks])\n}\n\n/** The locks `locksToJSON` wrote, or undefined when `value` is not that shape. */\nexport function locksFromJSON(value: unknown): AxisLocks | undefined {\n if (!isPlainRecord(value)) return undefined\n const out = new Map<AxisId, boolean>()\n for (const [axis, locked] of Object.entries(value)) {\n if (typeof locked !== 'boolean') return undefined\n out.set(axis, locked)\n }\n return out\n}\n\n/** Whether a node answers to a focus target: every value the target names. */\nexport function matchesFocus(node: { values: Readonly<Record<string, string>> }, target: FocusTarget): boolean {\n for (const [axis, value] of Object.entries(target.values)) {\n if (value !== undefined && node.values[axis] !== value) return false\n }\n return true\n}\n\n/** What `passesSelection` reads. */\nexport interface PassesSelectionInput<N> {\n filter: FilterState\n taxonomy: Pick<Taxonomy, 'axisFor'>\n /** The axes the view shows — the only ones that filter. One the taxonomy does not declare filters nothing. */\n axes: readonly AxisId[]\n /** The record judged: a graph node, or any record of the host's. */\n node: N\n /** The record's value on an axis, for a record that carries its values under other names. Default: its `values`, when it has them. */\n valueOn?: (node: N, axis: AxisId) => string | undefined\n}\n\n/** Whether a record carries `values` the way a graph node does. */\nfunction carriesValues(record: unknown): record is { values?: AxisValues } {\n return typeof record === 'object' && record !== null\n}\n\n/**\n * Does a record pass the selection on the axes a view shows? The policy —\n * what an empty selection means, what an absent value means — is read from\n * each axis's descriptor, once, here, so a host's second view and the pane\n * agree on what the filter means.\n */\nexport function passesSelection<N>({ filter, taxonomy, axes, node, valueOn }: PassesSelectionInput<N>): boolean {\n const read = valueOn ?? ((n: N, axis: AxisId) => (carriesValues(n) ? n.values?.[axis] : undefined))\n for (const id of axes) {\n const d = taxonomy.axisFor(id)\n if (d && !passesDescriptors(filter, [d], axis => read(node, axis))) return false\n }\n return true\n}\n\n/** `passesSelection` over descriptors already resolved — the view's hot path, which resolves them once per render. */\nexport function passesDescriptors(\n filter: FilterState,\n axes: readonly ResolvedAxisDescriptor[],\n valueOn: (axis: string) => string | undefined,\n): boolean {\n for (const axis of axes) {\n const selection = selectionFor(filter, axis.id)\n if (selection.size === 0) {\n if (axis.emptyMeans === 'none') return false\n continue\n }\n const value = valueOn(axis.id)\n if (value === undefined) {\n if (axis.absentMeans === 'hidden') return false\n continue\n }\n if (!selection.has(value)) return false\n }\n return true\n}\n\n/** Search matches split by the filter: the visible ones, and the hidden ones tallied on the chip hiding each. */\nexport interface HiddenMatches {\n /** The ids of the matching nodes the filter shows. */\n visible: ReadonlySet<string>\n /** How many matching nodes the filter hides. */\n hiddenCount: number\n /** The hidden matches per axis and value — on the first of the axes shown that each node fails, under its value there. */\n hiddenByAxis: ReadonlyMap<AxisId, ReadonlyMap<string, number>>\n}\n\n/** What `hiddenMatchesOf` reads. */\nexport interface HiddenMatchesInput<N extends { id: string; values?: AxisValues }> {\n /** Every node the search ran over, visible or not. */\n nodes: Iterable<N>\n filter: FilterState\n taxonomy: Pick<Taxonomy, 'axisFor'>\n /** The axes the view shows, in order: the first one a hidden match fails is the chip it is counted on. */\n axes: readonly AxisId[]\n visibleIds: ReadonlySet<string>\n matches: (node: N) => boolean\n /** A node's value on an axis. Default: its `values`. */\n valueOn?: (node: N, axis: AxisId) => string | undefined\n}\n\n/**\n * Split a search's matches by the filter, the way the pane does under its\n * own bar: a match the filter shows lights up, a match it hides is counted\n * on the chip hiding it — the first axis in `axes` the node fails, under the\n * node's value there — so a host's second bar can wear the same badges.\n */\nexport function hiddenMatchesOf<N extends { id: string; values?: AxisValues }>({ nodes, filter, taxonomy, axes, visibleIds, matches, valueOn }: HiddenMatchesInput<N>): HiddenMatches {\n const read = valueOn ?? ((node: N, axis: AxisId) => node.values?.[axis])\n const descriptors = axes.flatMap(id => { const d = taxonomy.axisFor(id); return d ? [d] : [] })\n const visible = new Set<string>()\n let hiddenCount = 0\n const hiddenByAxis = new Map<AxisId, Map<string, number>>()\n for (const node of nodes) {\n if (!matches(node)) continue\n if (visibleIds.has(node.id)) { visible.add(node.id); continue }\n hiddenCount++\n const failing = descriptors.find(d => !passesDescriptors(filter, [d], axis => read(node, axis)))\n const value = failing ? read(node, failing.id) : undefined\n if (!failing || value === undefined) continue\n const tally = hiddenByAxis.get(failing.id) ?? new Map<string, number>()\n tally.set(value, (tally.get(value) ?? 0) + 1)\n hiddenByAxis.set(failing.id, tally)\n }\n return { visible, hiddenCount, hiddenByAxis }\n}\n","// The chip-flash set: per axis, the values that just turned on in the filter,\n// cleared ~450ms later. Derived from the filter alone, so it is the view's and\n// not the host's, and keyed by axis and value rather than a joined string, so\n// a value containing the separator cannot be mistaken for another's.\n\nimport React from 'react'\nimport type { FilterState } from '../../filter/types'\nimport { filterStatesEqual, selectionFor } from '../../filter/types'\nimport { useStable } from './useControllable'\n\n/** The values that just turned on, per axis. */\nexport type RecentlyChanged = ReadonlyMap<string, ReadonlySet<string>>\nconst NONE: RecentlyChanged = new Map()\n\n/** The values that just turned on in a filter, per axis, for about 450 ms — the bar's chip flash — derived from the filter alone. */\nexport function useRecentlyChanged(rawFilter: FilterState): RecentlyChanged {\n // One object per content change, so the effect below re-runs — and its\n // cleanup cancels the timer — only for a real change: a host rebuilding an\n // equal filter each render once cancelled the flash's timer and left the\n // chips lit until the next real change.\n const filter = useStable(rawFilter, filterStatesEqual)\n const prevFilterRef = React.useRef<FilterState>(filter)\n const [recentlyChanged, setRecentlyChanged] = React.useState<RecentlyChanged>(NONE)\n React.useEffect(() => {\n const prev = prevFilterRef.current\n if (filterStatesEqual(prev, filter)) return\n const changed = new Map<string, Set<string>>()\n for (const [axis, values] of filter) {\n const before = selectionFor(prev, axis)\n for (const v of values) {\n if (before.has(v)) continue\n const set = changed.get(axis) ?? new Set<string>()\n set.add(v)\n changed.set(axis, set)\n }\n }\n prevFilterRef.current = filter\n // Set on every change, empty included, so a removal-only change that\n // arrives while an earlier flash is pending does not leave the old set lit.\n setRecentlyChanged(changed)\n if (changed.size === 0) return\n const timer = setTimeout(() => setRecentlyChanged(NONE), 450)\n return () => clearTimeout(timer)\n }, [filter])\n return recentlyChanged\n}\n","// The chip-toggle reducers behind the shared filter bar.\n//\n// Pure, and deliberately outside the component. Each reducer rebuilds only the\n// Set for the axis it edits; every other axis is passed through by reference,\n// which `withSelection` guarantees, so a no-op edit changes no identity and the\n// view's memos have nothing to recompute.\n\nimport type { AxisId } from '../graph/axis'\nimport type { FilterState } from './types'\nimport { selectionFor, withSelection } from './types'\n\n/** Toggle one value on one axis. */\nexport function toggleValue(\n filter: FilterState,\n axis: AxisId,\n value: string,\n): FilterState {\n const current = selectionFor(filter, axis)\n const next = new Set(current)\n if (next.has(value)) next.delete(value)\n else next.add(value)\n return withSelection(filter, axis, next)\n}\n\n/**\n * Toggle a group of values on one axis as a unit.\n *\n * A chip may cover several values — one covers three. \"Any on\" turns the whole\n * group off; otherwise the whole group goes on, so a partially-on group has a\n * single unambiguous next state.\n */\nexport function toggleGroup(\n filter: FilterState,\n axis: AxisId,\n values: readonly string[],\n): FilterState {\n const current = selectionFor(filter, axis)\n const anyOn = values.some(v => current.has(v))\n const next = new Set(current)\n if (anyOn) for (const v of values) next.delete(v)\n else for (const v of values) next.add(v)\n return withSelection(filter, axis, next)\n}\n","// A small lock-icon button on each of the filter bar's axis sections. A\n// locked axis holds its selection: a host that reconciles the filter between\n// its views (`reconcileFilter`'s `manual` transition) leaves a locked axis as\n// it is.\n//\n// The lock can briefly flash to indicate it was overridden by a focus\n// transition — that's signaled by the `flashing` prop, which the parent\n// sets for ~600ms after a focus transition that bypassed the lock.\n\nimport './LockToggle.css'\n\ninterface LockToggleProps {\n locked: boolean\n onClick: () => void\n flashing?: boolean\n /** The axis's label, for the title and aria-label. */\n label: string\n}\n\nexport function LockToggle({ locked, onClick, flashing, label }: LockToggleProps) {\n const title = locked\n ? `${label} filter locked — held as it is; click to unlock`\n : `${label} filter unlocked — click to lock and hold it as it is`\n const className = [\n 'gv-lock-toggle',\n locked ? 'locked' : '',\n flashing ? 'flashing' : '',\n ].filter(Boolean).join(' ')\n\n return (\n <button\n className={className}\n onClick={onClick}\n title={title}\n aria-label={title}\n aria-pressed={locked}\n >\n {locked ? <LockClosedIcon /> : <LockOpenIcon />}\n </button>\n )\n}\n\n// Lock SVGs sized for a 14px control. Stroke-only so they pick up the\n// current text color.\nfunction LockClosedIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"7\" width=\"10\" height=\"7\" rx=\"1.5\" />\n <path d=\"M5.5 7V5a2.5 2.5 0 0 1 5 0v2\" />\n </svg>\n )\n}\n\nfunction LockOpenIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"7\" width=\"10\" height=\"7\" rx=\"1.5\" />\n <path d=\"M5.5 7V5a2.5 2.5 0 0 1 4.9-.6\" />\n </svg>\n )\n}\n","// Magnifying glass icon for search\nexport function SearchIcon({ size = 16 }: { size?: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n style={{ display: 'block' }}\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <line x1=\"16.65\" y1=\"16.65\" x2=\"21\" y2=\"21\" />\n </svg>\n )\n}\n\n// One gear.\n","// The filter bar: one section of chips per axis in the axes, each with its\n// lock and its remove control, the search control, and a slot at the bottom\n// the host fills with its findings. A host with more than one view renders\n// it in each, so the views' toolbars stay in lockstep.\n//\n// How an axis's chips look is the descriptor's `presentation`; the bar names\n// no axis. A view-specific adornment on the search control (a match counter,\n// a hidden-match badge) comes in as `searchExtra`.\n\nimport { TaxonomyContext } from './graph-view/useTaxonomy'\nimport type { AxisId, AxisValue } from '../graph/axis'\nimport { countsOn, valuesOn } from '../graph/axis'\nimport type { Graph } from '../graph/model'\nimport type { Taxonomy } from '../graph/taxonomy'\nimport { useControllable, useStable } from './graph-view/useControllable'\nimport { useTheme } from './graph-view/useTheme'\nimport type { ThemeChoice } from './graph-view/useTheme'\nimport { schemeVars } from '../graph/scheme'\nimport type { Theme } from '../graph/scheme'\nimport { useRecentlyChanged } from './graph-view/useRecentlyChanged'\nimport type { RecentlyChanged } from './graph-view/useRecentlyChanged'\n\n// A chip's tint names a style value; one that names nothing declared is a\n// host bug, said once per chip and per bar on the console rather than thrown\n// in render, and the chip draws neutral.\nfunction tintOf(chipId: string, tint: AxisValue, taxonomy: Taxonomy, said: Set<string>): AxisValue {\n if (taxonomy.styleValues.includes(tint)) return tint\n if (!said.has(chipId)) {\n said.add(chipId)\n console.error(`[filter] chip ${JSON.stringify(chipId)} borrows the colour of ${JSON.stringify(tint)}, which the taxonomy does not declare`)\n }\n return tint\n}\nimport { selectionFor } from '../filter/types'\nimport React from 'react'\nimport './FilterBar.css'\nimport type { ResolvedAxisDescriptor } from '../graph/axis'\nimport { filterStatesEqual, listEqual, lockedFor, locksEqual, withLock, withSelection } from '../filter/types'\nimport type { FilterState, AxisLocks } from '../filter/types'\nimport { toggleGroup } from '../filter/toggle'\nimport { LockToggle } from './LockToggle'\nimport { SearchIcon } from './icons/SearchIcon'\n\nconst NO_LOCKS: ReadonlySet<string> = new Set()\n\n/** The search control's state: the query, and whether the control is open. */\nexport interface SearchState { query: string; active: boolean }\n\n/** Which of the bar's own controls are offered: the search on by default, the lock toggles when the host controls `locks` or asks here. */\nexport interface FilterBarChrome {\n /** The search control. */\n search?: boolean\n /** The lock toggle on each axis — meaningful only to a host with a second view to lock against. */\n locks?: boolean\n}\n\n/**\n * The bar's inputs. The filter and its callback are the one requirement; the\n * values each axis offers come from `values` or from `graph` — one of the\n * two — and everything else the pane supplies for its own bar has a default\n * here, so a host's second view renders the same bar without rebuilding the\n * pane's internals.\n */\nexport type FilterBarProps = FilterBarBaseProps & (\n | {\n /** Values available per axis — the one source every axis's chips are built from; an axis with no entry renders an empty, disabled section rather than vanishing. */\n values: ReadonlyMap<string, readonly string[]>\n graph?: undefined\n }\n | {\n /** The graph the bar filters: the values each axis offers are read off its nodes. */\n graph: Graph\n values?: undefined\n }\n)\n\ninterface FilterBarBaseProps {\n /** The taxonomy, when not supplied through `TaxonomyContext`. */\n taxonomy?: Taxonomy\n /** Which controls are offered. Default: the search; the lock toggles when the host controls `locks` with `onLocksChange`. */\n chrome?: FilterBarChrome\n /**\n * The bar's theme: a choice, or the resolved theme a pane hands it.\n * Default `system` — the nearest ancestor's `data-theme`, else the OS. The\n * bar carries the scheme's variables and its `data-theme` on its own root,\n * so its chips read their tints without a wrapper.\n */\n theme?: ThemeChoice\n /** What `system` resolves to, for a host that knows its own theme. Given, the bar reads no ancestor's `data-theme` and no OS preference for it. */\n systemTheme?: Theme\n /** Chip badge counts per axis and value, read by the host off its own data. Absent: the graph's entity counts (the copies of one `copyKey` once) when `graph` is given, else no badges. */\n counts?: ReadonlyMap<string, ReadonlyMap<string, number>>\n filter: FilterState\n /** Every change the user makes, with the axis they changed — for a host that keeps which axes are the user's (`reseedFilter`). */\n onFilterChange: (next: FilterState, touched: readonly AxisId[]) => void\n /** Which axes are locked. Controlling it with `onLocksChange` shows the lock toggles; a value without its callback is read-only and shows none unless `chrome.locks` asks. */\n locks?: AxisLocks\n onLocksChange?: (next: AxisLocks) => void\n /** Axes a focus just overrode the lock of. The bar flashes them once, for 600 ms, whenever the value changes; the host need not clear it. */\n overriddenLocks?: ReadonlySet<AxisId>\n /** The values that just turned on, per axis — drives the chip flash. Default: tracked here from the filter's changes. */\n recentlyChanged?: RecentlyChanged\n /** The search control. Default: the bar's own, closed and empty. */\n search?: SearchState\n onSearchChange?: (next: SearchState) => void\n /** The search input, for a host that focuses it from its own shortcut. */\n searchInputRef?: React.RefObject<HTMLInputElement>\n searchTitle?: string\n searchPlaceholder?: string\n /** View-specific search adornment rendered inside the search control. */\n searchExtra?: React.ReactNode\n /** Search matches hidden by a chip, per axis and value — the badge the chip hiding them wears. Optional. */\n hiddenMatches?: ReadonlyMap<string, ReadonlyMap<string, number>>\n /** The axes shown, in order; may be empty. Default: every declared axis, editable in place. */\n axes?: readonly string[]\n /** Omitted by a host that controls the axes and does not want it edited in place. */\n onAxesChange?: (next: readonly string[]) => void\n /** The host's findings bars, rendered under the chips. */\n findings?: React.ReactNode\n /** Flash the whole bar once whenever this value changes to something truthy — a counter a host bumps when its data reloads. */\n refreshFlash?: boolean | number\n}\n\n/** The classes a section's chips wear, by the axis's declared presentation. */\nconst LOOKS = {\n chip: { sectionClass: 'gv-chip-section', rowClass: 'gv-chip-row', chipClass: 'gv-chip', modifierPrefix: 'gv-chip', activeClass: 'active', iconClass: 'gv-chip-icon', labelClass: 'gv-chip-label' },\n tag: { sectionClass: 'gv-tag-section', rowClass: 'gv-tag-row', chipClass: 'gv-tag', modifierPrefix: '', activeClass: 'selected', iconClass: 'gv-tag-icon', labelClass: 'gv-tag-label' },\n} as const\n\n/**\n * The shared filter bar: a section of chips per axis shown with its lock and\n * remove controls, the add-axis control, the search control, and the host's\n * findings under them. The pane renders it for itself; a host renders it in\n * a second view with the same props, through `useLinkedViews`.\n */\nexport function FilterBar({\n taxonomy: taxonomyProp,\n chrome,\n theme: themeChoice = 'system',\n systemTheme,\n counts: hostCounts,\n values: hostValues,\n graph,\n filter: filterProp,\n onFilterChange,\n locks: locksProp,\n onLocksChange,\n overriddenLocks = NO_LOCKS,\n recentlyChanged: hostRecentlyChanged,\n search: searchProp,\n onSearchChange,\n searchInputRef: hostInputRef,\n searchTitle = 'Search (/)',\n searchPlaceholder = 'Search…',\n searchExtra,\n hiddenMatches,\n findings,\n refreshFlash,\n axes: hostAxes,\n onAxesChange,\n}: FilterBarProps) {\n const fromContext = React.useContext(TaxonomyContext)\n const taxonomy = taxonomyProp ?? fromContext\n if (!taxonomy) throw new Error('FilterBar has no taxonomy: pass `taxonomy`, or mount it inside <TaxonomyContext.Provider value={…}>.')\n const undeclaredTints = React.useRef(new Set<string>())\n // The filter, locks and axes compared by content, as the pane compares\n // them: a host rebuilding one each render changes nothing here.\n const filter = useStable(filterProp, filterStatesEqual)\n const [rawLocks, setLocks] = useControllable(locksProp, onLocksChange, () => new Map<string, boolean>(), 'locks')\n const locks = useStable(rawLocks, locksEqual)\n const showLocks = chrome?.locks ?? (locksProp !== undefined && onLocksChange !== undefined)\n const showSearch = chrome?.search ?? true\n // The lock flash is the bar's: a new set of overridden locks lights them for\n // 600 ms and goes out by itself, so a host passes the reconciler's answer\n // and nothing more.\n const [flashing, setFlashing] = React.useState<ReadonlySet<string>>(NO_LOCKS)\n React.useEffect(() => {\n // Every change sets the state, the empty one included: the cleanup has\n // already cancelled the previous timer, so an empty set arriving mid-flash\n // must put the light out itself.\n setFlashing(overriddenLocks)\n if (overriddenLocks.size === 0) return\n const timer = setTimeout(() => setFlashing(NO_LOCKS), 600)\n return () => clearTimeout(timer)\n }, [overriddenLocks])\n // The whole-bar flash likewise: once per change of the value, not for as\n // long as a boolean stays set.\n const [refreshing, setRefreshing] = React.useState(false)\n React.useEffect(() => {\n if (!refreshFlash) return\n setRefreshing(true)\n const timer = setTimeout(() => setRefreshing(false), 600)\n return () => clearTimeout(timer)\n }, [refreshFlash])\n const [rawAxes, setAxes] = useControllable(hostAxes, onAxesChange, () => taxonomy.filterAxes.map(d => d.id), 'axes')\n const axes = useStable(rawAxes, listEqual)\n const axesEditable = hostAxes === undefined || onAxesChange !== undefined\n const [search, setSearch] = useControllable(searchProp, onSearchChange, () => ({ query: '', active: false }), 'search')\n const { query: searchQuery, active: searchActive } = search\n const ownInputRef = React.useRef<HTMLInputElement>(null)\n const searchInputRef = hostInputRef ?? ownInputRef\n // The bar's own theme and scheme, on its root: a second view's bar needs\n // no wrapper to read its tints, and a pane's bar is handed the resolved one.\n const rootRef = React.useRef<HTMLDivElement>(null)\n const theme = useTheme(rootRef, themeChoice, systemTheme)\n const schemeStyle = React.useMemo(() => schemeVars(taxonomy, theme) as React.CSSProperties, [taxonomy, theme])\n // The add-axis menu, open while the user picks among several.\n const [addOpen, setAddOpen] = React.useState(false)\n const labelOf = (id: string) => taxonomy.axisFor(id)?.label ?? id\n const tracked = useRecentlyChanged(filter)\n const recentlyChanged = hostRecentlyChanged ?? tracked\n // Values for every declared axis, not only the shown ones: an axis the user\n // removed is offered back by \"+\" only if its values are known.\n const valuesByAxis = React.useMemo(\n () => hostValues ?? new Map([...new Set([...taxonomy.filterAxes.map(d => d.id), ...axes])].map(d => [d, graph ? valuesOn(graph.nodes, d) : []])),\n [hostValues, graph, axes, taxonomy],\n )\n const counts = React.useMemo(\n () => hostCounts ?? (graph ? countsOn(graph.nodes, axes) : undefined),\n [hostCounts, graph, axes],\n )\n\n // What a section is made of: the values the host read off its data, their\n // counts and hidden matches, and the classes its declared presentation\n // wears. Every axis renders, declared or not — an axis in the list must be\n // visible and removable, whatever the taxonomy says of it.\n const lookFor = (axis: string, descriptor?: ResolvedAxisDescriptor) => ({\n values: [...(valuesByAxis.get(axis) ?? [])],\n counts: counts?.get(axis),\n hidden: hiddenMatches?.get(axis),\n ...LOOKS[descriptor?.presentation ?? 'chip'],\n })\n\n // Axes the host declares that the bar is not already showing.\n const available = taxonomy.filterAxes\n .map(d => d.id)\n .filter(id => !axes.includes(id) && (valuesByAxis.get(id)?.length ?? 0) > 0)\n // The menu is for a choice: with fewer than two to choose from it closes,\n // so a stale open flag cannot show it again on a later render.\n React.useEffect(() => { if (available.length < 2) setAddOpen(false) }, [available.length])\n\n const toggleSearch = () => {\n if (searchActive) {\n setSearch({ query: '', active: false })\n } else {\n setSearch({ query: searchQuery, active: true })\n setTimeout(() => searchInputRef.current?.focus(), 50)\n }\n }\n\n return (\n <div className={`gv-canvas-header${refreshing ? ' refresh-flash' : ''}`} ref={rootRef} data-theme={theme} style={schemeStyle}>\n {axes.map(axis => {\n // An axis the taxonomy does not declare filters nothing and offers no\n // chips; it renders as its header, a note and the remove control, so\n // an axis in the list is always visible and always removable.\n const descriptor = taxonomy.axisFor(axis)\n const label = descriptor?.label ?? axis\n const look = lookFor(axis, descriptor)\n // A chip none of whose values the data presents is not offered, whoever built it.\n const present = new Set(look.values)\n const chips = descriptor ? descriptor.chipsFor(look.values).filter(c => c.values.some(v => present.has(v))) : []\n // No values to offer: the header and its remove control render anyway.\n const empty = chips.length === 0\n\n const selection = selectionFor(filter, axis)\n const locked = lockedFor(locks, axis)\n // An `emptyMeans: 'all'` axis with nothing selected is not \"off\" — it is\n // matching everything, and the chips say so rather than reading as a\n // filter that hides the lot.\n const allIncluded = selection.size === 0 && descriptor?.emptyMeans === 'all'\n\n return (\n <React.Fragment key={axis}>\n <div className={`${look.sectionClass}${locked ? ' section-locked' : ''}`}>\n <div className={look.rowClass}>\n <span className=\"gv-header-axis-label\">{label}</span>\n {empty && (\n <span className=\"gv-header-axis-empty\" title={descriptor ? `${label} has no values in this view` : `${label} is not an axis the taxonomy declares`}>\n {descriptor ? `no ${label.toLowerCase()} values` : 'not declared'}\n </span>\n )}\n {chips.map(chip => {\n const isActive = chip.values.some(v => selection.has(v))\n const isChanged = chip.values.some(v => recentlyChanged.get(axis)?.has(v))\n const hiddenCount = chip.values.reduce((sum, v) => sum + (look.hidden?.get(v) ?? 0), 0)\n const count = look.counts === undefined ? undefined : chip.values.reduce((sum, v) => sum + (look.counts!.get(v) ?? 0), 0)\n const cls = [\n look.chipClass,\n allIncluded ? 'all-included' : (isActive ? look.activeClass : ''),\n look.modifierPrefix ? `${look.modifierPrefix}-${chip.id}` : '',\n isChanged ? 'recently-changed' : '',\n ].filter(Boolean).join(' ')\n return (\n <button\n key={chip.id}\n className={cls}\n style={chip.tint ? ({ ['--gv-chip-tint' as string]: `var(${taxonomy.colorVar(tintOf(chip.id, chip.tint, taxonomy, undeclaredTints.current))})` } as React.CSSProperties) : undefined}\n onClick={() => onFilterChange(toggleGroup(filter, axis, chip.values), [axis])}\n title={chip.tooltip ?? (isActive ? `Hide ${chip.label.toLowerCase()}` : `Show ${chip.label.toLowerCase()}`)}\n >\n {chip.icon && <span className={look.iconClass}>{chip.icon}</span>}\n <span className={look.labelClass}>{chip.label}</span>\n {count !== undefined && <span className=\"gv-header-chip-count\">{count}</span>}\n {hiddenCount > 0 && (\n <span className=\"gv-header-hidden-badge\" title={`${hiddenCount} match${hiddenCount !== 1 ? 'es' : ''} hidden by this filter`}>\n {hiddenCount}\n </span>\n )}\n </button>\n )\n })}\n </div>\n {showLocks && descriptor && (\n <LockToggle\n locked={locked}\n onClick={() => setLocks(withLock(locks, axis, !locked))}\n flashing={flashing.has(axis)}\n label={label}\n />\n )}\n {axesEditable && (\n <button\n className=\"gv-header-remove\"\n onClick={() => {\n // Removing an axis also stops it filtering. Keeping the\n // selection would leave the view filtered by something with\n // no on-screen representation and no way to reach it —\n // invisible state that reads as a bug.\n //\n // \"Stops filtering\" is NOT \"empty\", and that difference is\n // this axis's `emptyMeans` again. On the file axis an empty\n // selection matches everything; on the kind axis it matches\n // NOTHING, so clearing it would blank the view instead of\n // unfiltering it. Match-everything is therefore empty for\n // one and the full value set for the other.\n onFilterChange(withSelection(\n filter,\n axis,\n descriptor?.emptyMeans !== 'none'\n ? new Set<string>()\n : new Set(chips.flatMap(c => [...c.values])),\n ), [axis])\n setAxes(axes.filter(d => d !== axis))\n }}\n title={`Remove the ${label} filter`}\n >×</button>\n )}\n </div>\n <div className=\"gv-header-divider\" />\n </React.Fragment>\n )\n })}\n\n {axesEditable && available.length > 0 && (\n <div className=\"gv-header-add\">\n <button\n className=\"gv-header-add-button\"\n onClick={() => (available.length === 1 ? setAxes([...axes, available[0]!]) : setAddOpen(open => !open))}\n title={available.length === 1 ? `Add the ${labelOf(available[0]!)} filter` : 'Add a filter'}\n aria-haspopup={available.length > 1 ? 'menu' : undefined}\n aria-expanded={available.length > 1 ? addOpen : undefined}\n >+</button>\n {addOpen && available.length > 1 && (\n <div className=\"gv-header-add-menu\" role=\"menu\" onKeyDown={e => { if (e.key === 'Escape') setAddOpen(false) }}>\n {available.map(id => (\n <button key={id} role=\"menuitem\" className=\"gv-header-add-menu-item\" onClick={() => { setAxes([...axes, id]); setAddOpen(false) }}>\n {labelOf(id)}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n <div className=\"gv-header-divider\" />\n\n {showSearch && <div className=\"gv-header-controls-section\">\n <div className={`gv-header-search ${searchActive ? 'active' : ''}`}>\n <button className=\"gv-header-search-toggle\" onClick={toggleSearch} title={searchTitle}>\n <SearchIcon size={14} />\n </button>\n {searchActive && (\n <input\n ref={searchInputRef}\n className=\"gv-header-search-input\"\n type=\"text\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={e => setSearch({ query: e.target.value, active: true })}\n onKeyDown={e => { if (e.key === 'Escape') toggleSearch() }}\n />\n )}\n {searchExtra}\n </div>\n </div>}\n\n {findings}\n </div>\n )\n}\n","// Graph data model for the force-directed graph view.\n\nimport type { AxisValues, Relation } from './axis'\n\n/** A node of the host's graph: an opaque id, a name, its values on the host's axes, and an optional parent, copy key, ring, badges and payload. */\nexport interface GraphNode {\n /**\n * Stable identity, owned by whoever produced the graph.\n *\n * **Opaque.** The engine compares ids and looks them up; it never parses one\n * or reads structure out of it. A host is free to make them hierarchical, or\n * not — the two behave identically here, and that is the property worth\n * keeping: the moment anything splits an id on a separator, the id's format\n * becomes part of this model's contract.\n */\n id: string\n /**\n * Where this node sits on every axis the taxonomy declares. Features elect\n * one axis each and read it from here; nothing composes across axes.\n */\n values: AxisValues\n name: string\n /**\n * Metadata the host owns and the library does not interpret.\n *\n * Deliberately opaque: it is per-domain display detail — the things a tooltip\n * shows — and the moment the library reads a key out of it by name, that key\n * is part of the library's vocabulary again.\n */\n payload?: Readonly<Record<string, unknown>>\n /**\n * The container this node sits in, when it has one. One string: containment\n * is a tree, and an entity that sits in two containers is modelled as two\n * nodes sharing a `copyKey`. The view derives the containment edge from\n * this node to its container (`withContainmentEdges`) when the host does\n * not list one, so a host writes the fact once; a host may still list\n * containment edges of its own, between any two nodes.\n */\n parentId?: string\n /**\n * A mark the host puts on a node that is set apart: a dashed ring drawn\n * around it. What \"set apart\" means is the host's — say, a node that\n * has no container. Absent, no ring.\n */\n ring?: 'dashed'\n\n /**\n * The key every copy of one underlying entity shares. One entity placed in\n * three containers produces three nodes with three `id`s and one `copyKey`;\n * the view draws the copies with a halo, lights them together, counts them\n * once on a chip and tells them apart in the hover box (\"copy 2 of 3\").\n * Absent, the node is its own key. Like `id`, opaque: the host decides how\n * to derive it.\n */\n copyKey?: string\n\n /**\n * Short texts drawn as chips above the node, as given — a cardinality, a\n * tag, a count. Display only, never identity. Absent or empty, no chips.\n */\n badges?: readonly string[]\n}\n\n/** The key a node's copies share: its `copyKey`, or its own id. */\nexport const copyKeyOf = (node: Pick<GraphNode, 'id' | 'copyKey'>): string => node.copyKey ?? node.id\n\n/**\n * Every node id by the key it shares with its copies, over a whole graph —\n * the index the view and a host's group overlay read. Derived here from the\n * nodes, once per graph, rather than supplied beside them.\n */\nexport function copyGroups(graph: Pick<Graph, 'nodes'>): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>()\n for (const node of graph.nodes) {\n const key = copyKeyOf(node)\n const group = out.get(key)\n if (group) group.add(node.id)\n else out.set(key, new Set([node.id]))\n }\n return out\n}\n\n/**\n * Read one string out of a node's host-owned payload.\n *\n * The library offers the accessor, never the key: a caller that knows the key\n * is by definition domain-aware, and this keeps that knowledge at the call site\n * instead of smuggling it into the model. Returns undefined for a missing key\n * or a non-string value rather than coercing, so a payload shape change surfaces\n * as an absent field instead of \"[object Object]\" in a tooltip.\n */\nexport function payloadString(\n node: { payload?: Readonly<Record<string, unknown>> },\n key: string,\n): string | undefined {\n const value = node.payload?.[key]\n return typeof value === 'string' ? value : undefined\n}\n\n/** Read one number out of a node's host-owned payload; undefined for a missing key or a non-number, never a coercion. */\nexport function payloadNumber(\n node: { payload?: Readonly<Record<string, unknown>> },\n key: string,\n): number | undefined {\n const value = node.payload?.[key]\n return typeof value === 'number' ? value : undefined\n}\n\n/** An edge of the host's graph: two node ids, a relation, the edge's own axis values and an opaque payload. */\nexport interface GraphEdge {\n id: string\n sourceId: string\n targetId: string\n /**\n * What the edge means to the engine: a `containment` edge runs from a node\n * to its container and is drawn dashed and undirected; a `dependency` edge\n * is directed, walked by the highlight and re-pointed when an end is\n * hidden. Absent, `dependency`. The containment edge from a node to its\n * `parentId` is derived; a host may list containment edges of its own,\n * which are drawn and sprung but never walked as the parent.\n */\n relation?: Relation\n /** Where this edge sits on the host's own axes (a call kind, say) — what an edge type's `match` reads. Absent, none. */\n values?: AxisValues\n /**\n * Metadata the host owns and the library does not interpret — the same\n * contract as `GraphNode.payload`. Per-call-site detail that is not identity\n * and not an axis lives here.\n */\n payload?: Readonly<Record<string, unknown>>\n}\n\n/** The edge's relation: as declared, or `dependency`. A misspelt relation is `validateGraph`'s to name. */\nexport function relationOf(edge: Pick<GraphEdge, 'relation'>): Relation {\n return edge.relation === 'containment' ? 'containment' : 'dependency'\n}\n\n/** Does this edge express containment (as opposed to a dependency)? */\nexport function isContainment(edge: Pick<GraphEdge, 'relation'>): boolean {\n return edge.relation === 'containment'\n}\n\n/**\n * Every node by id — the index a reader builds once per graph when it needs\n * lookups. A duplicated id keeps its first node; `validateGraph` names the\n * duplicate and the view drops the later one.\n */\nexport function nodesById(nodes: Iterable<GraphNode>): Map<string, GraphNode> {\n const out = new Map<string, GraphNode>()\n for (const node of nodes) if (!out.has(node.id)) out.set(node.id, node)\n return out\n}\n\n/**\n * The graph with a containment edge from every node to its `parentId`, for\n * each node whose host listed none: `in:<node id>`, from the child to its\n * container. A node whose parent is not in the graph gets none (validation\n * names that), and a host's own edge to the parent stands. The host's\n * objects are not mutated; a graph needing nothing is returned as is.\n */\nexport function withContainmentEdges(graph: Graph): Graph {\n const byId = nodesById(graph.nodes)\n const listed = new Set<string>()\n const ids = new Set<string>()\n for (const e of graph.edges) {\n ids.add(e.id)\n if (e.relation === 'containment' && e.targetId === byId.get(e.sourceId)?.parentId) listed.add(e.sourceId)\n }\n const added: GraphEdge[] = []\n for (const node of graph.nodes) {\n if (node.parentId === undefined || !byId.has(node.parentId) || listed.has(node.id)) continue\n // `in:<node>`, or the first free numbered variant when a host edge took it.\n let id = `in:${node.id}`\n for (let n = 2; ids.has(id); n++) id = `in:${node.id}#${n}`\n ids.add(id)\n added.push({ id, sourceId: node.id, targetId: node.parentId, relation: 'containment' })\n }\n return added.length === 0 ? graph : { nodes: graph.nodes, edges: [...graph.edges, ...added] }\n}\n\n/**\n * A graph as a reader sees it — its nodes, its edges, and a lookup by id —\n * the shape a computed scalar, the layout frame and the view's visible-graph\n * derivation all read. A `Simulation` satisfies it structurally.\n */\nexport interface ReadableGraph {\n readonly nodes: readonly GraphNode[]\n readonly edges: readonly GraphEdge[]\n getNode(id: string): GraphNode | undefined\n}\n\n/** A host's graph as a reader sees it: its nodes and edges, and a lookup by id built once. */\nexport function readableGraph(graph: Graph): ReadableGraph {\n const byId = nodesById(graph.nodes)\n return { nodes: graph.nodes, edges: graph.edges, getNode: id => byId.get(id) }\n}\n\n/**\n * The host's graph: its nodes, in the order they are laid out, and its edges.\n * Read, never written, by the view, which indexes the nodes itself.\n */\nexport interface Graph {\n readonly nodes: readonly GraphNode[]\n readonly edges: readonly GraphEdge[]\n}\n\nconst sameValues = (a: AxisValues | undefined, b: AxisValues | undefined): boolean => {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n const keys = Object.keys(a)\n if (keys.length !== Object.keys(b).length) return false\n for (const k of keys) if (a[k] !== b[k]) return false\n return true\n}\nconst sameList = (a: readonly string[] | undefined, b: readonly string[] | undefined): boolean =>\n a === b || (a !== undefined && b !== undefined && a.length === b.length && a.every((v, i) => v === b[i]))\n\n/**\n * Whether two graphs say the same thing: the same nodes in the same order\n * with the same fields, and the same edges — values compared, payloads by\n * identity. A new graph object equal to the last is the sign of a host\n * rebuilding what it could have kept; the view says so once.\n */\nexport function graphEquals(a: Graph, b: Graph): boolean {\n if (a === b) return true\n if (a.nodes.length !== b.nodes.length || a.edges.length !== b.edges.length) return false\n for (let i = 0; i < a.nodes.length; i++) {\n const x = a.nodes[i], y = b.nodes[i]\n if (x === y) continue\n if (x.id !== y.id || x.name !== y.name || x.parentId !== y.parentId || x.copyKey !== y.copyKey || x.ring !== y.ring || x.payload !== y.payload) return false\n if (!sameValues(x.values, y.values) || !sameList(x.badges, y.badges)) return false\n }\n for (let i = 0; i < a.edges.length; i++) {\n const x = a.edges[i], y = b.edges[i]\n if (x === y) continue\n if (x.id !== y.id || x.sourceId !== y.sourceId || x.targetId !== y.targetId || x.relation !== y.relation || x.payload !== y.payload) return false\n if (!sameValues(x.values, y.values)) return false\n }\n return true\n}\n","// reconcileFilter — the one decision point for what a destination view's\n// filter is after a switch: what it adopts from the source on a manual\n// switch, and how far it widens to reveal a focus target. Pure: the\n// destination's filter, locks and axes, the source's filter, the transition\n// and the taxonomy in; the new filter and the locks it had to override out.\n// What differs per axis — what an empty selection means, how far a focus\n// widens — is each descriptor's declared policy, read here, never assumed\n// uniform: a shared rule would hide the whole graph on a focus into a\n// narrowing axis.\n\nimport type {\n FilterState,\n AxisLocks,\n ViewTransition,\n ReconcileResult,\n FocusTarget,\n} from './types'\nimport {\n filterStatesEqual,\n lockedFor,\n selectionFor,\n withSelection,\n} from './types'\nimport type { AxisId, AxisValue, ResolvedAxisDescriptor } from '../graph/axis'\nimport { valuesOn } from '../graph/axis'\nimport type { Graph } from '../graph/model'\nimport type { Taxonomy } from '../graph/taxonomy'\n\n/** What a reconciliation reads: the destination view's filter, locks and the axes it shows, the source view's filter, the transition, and the graph when a focus should widen by whole chips. */\nexport interface ReconcileInput {\n /** The view being switched to: its filter as it stands. */\n destination: FilterState\n /** The view being switched from. Read on a manual switch; a focus reads only the destination. */\n source: FilterState\n /** The destination's locks. */\n locks: AxisLocks\n transition: ViewTransition\n /** Resolves the axes' policies. */\n taxonomy: Pick<Taxonomy, 'axisFor'>\n /**\n * The axes the destination view SHOWS, by id — not every declared axis:\n * an axis the user removed from the bar is not filtering there, and\n * adopting a selection on it would revive what they reset. One the\n * taxonomy does not declare is skipped.\n */\n axes: readonly AxisId[]\n /**\n * The graph the destination shows. Given, a focus widens to the WHOLE\n * chip holding the target's value — the bar toggles a chip as a unit, so\n * a selection that split one would read as on with half its nodes hidden,\n * and the first click would turn the chip off. Absent, the value alone.\n */\n graph?: Pick<Graph, 'nodes'>\n}\n\n/**\n * The destination view's filter after a switch: on a manual switch it adopts\n * the source's selections on the axes it shows, except where locked; on a\n * focus it widens just enough to show the target, overriding locks, which\n * come back in `overriddenLocks` for the bar to flash. The same filter object\n * comes back when nothing changed.\n */\nexport function reconcileFilter({ destination, source, locks, transition, taxonomy, axes, graph }: ReconcileInput): ReconcileResult {\n const descriptors = axes.flatMap(id => { const d = taxonomy.axisFor(id); return d ? [d] : [] })\n switch (transition.kind) {\n case 'manual':\n return reconcileManual(destination, source, locks, descriptors)\n case 'focus':\n return reconcileFocus(destination, locks, transition.target, descriptors, graph)\n }\n}\n\n/**\n * manual: per axis, keep the destination's selection where locked, otherwise\n * adopt the source's. Locks are never overridden here, so nothing flashes.\n * Built with `withSelection`, so an axis whose content did not change keeps\n * its Set: the view reads a kept identity as \"unchanged\" and neither reheats\n * nor refits for it.\n */\nfunction reconcileManual(\n destFilter: FilterState,\n sourceFilter: FilterState,\n destLocks: AxisLocks,\n axes: readonly ResolvedAxisDescriptor[],\n): ReconcileResult {\n let next = destFilter\n for (const axis of axes) {\n if (lockedFor(destLocks, axis.id)) continue\n next = withSelection(next, axis.id, selectionFor(sourceFilter, axis.id))\n }\n return { filter: next, overriddenLocks: new Set<AxisId>() }\n}\n\n/**\n * focus: widen the destination just enough to show the target. Per axis, by\n * the descriptor's `widens`: `always` adds the target's value; `whenActive`\n * adds it only while a selection is active — on a narrowing axis an empty\n * selection already shows everything, and adding the first value would\n * narrow to it. With the graph given, the whole chip holding the value is\n * added, as far as the graph presents it. Locks are not changed; each locked\n * axis that had to widen is reported, so the destination's bar can flash it.\n */\nfunction reconcileFocus(\n destFilter: FilterState,\n destLocks: AxisLocks,\n target: FocusTarget,\n axes: readonly ResolvedAxisDescriptor[],\n graph: Pick<Graph, 'nodes'> | undefined,\n): ReconcileResult {\n let next = destFilter\n const overridden = new Set<AxisId>()\n\n for (const axis of axes) {\n const value = target.values[axis.id]\n if (value === undefined) continue\n\n const current = selectionFor(destFilter, axis.id)\n if (current.has(value)) continue\n if (axis.widens === 'whenActive' && current.size === 0) continue\n\n next = withSelection(next, axis.id, new Set([...current, ...chipValues(axis, value, graph)]))\n if (lockedFor(destLocks, axis.id)) overridden.add(axis.id)\n }\n\n return {\n filter: filterStatesEqual(next, destFilter) ? destFilter : next,\n overriddenLocks: overridden,\n }\n}\n\n/** The values a focus on `value` turns on: the whole chip holding it, as far as the graph presents its values; the value alone without a graph or a chip. */\nfunction chipValues(axis: ResolvedAxisDescriptor, value: AxisValue, graph: Pick<Graph, 'nodes'> | undefined): readonly AxisValue[] {\n if (!graph) return [value]\n const present = valuesOn(graph.nodes, axis.id)\n const chip = axis.chipsFor(present).find(c => c.values.includes(value))\n if (!chip) return [value]\n const presentSet = new Set(present)\n const mine = chip.values.filter(v => presentSet.has(v))\n return mine.includes(value) ? mine : [value, ...mine]\n}\n","// Computed scalars — a layout axis backed by a number per node rather than a\n// bucket per value. A scalar reads node metadata and the graph's\n// structure, nothing else, and answers a number per node; the frame maps the\n// answers onto [0, 1] as the scalar's `normalize` says, and the axis maps\n// that interval onto a span. Three are built in and read only structure; a\n// host may declare more through its taxonomy, with access to its own payload.\n\nimport type { ReadableGraph } from './model'\nimport { isContainment } from './model'\n\n/** A computed scalar a layout axis may key on: a number per node, read off the graph and mapped onto [0, 1]. */\nexport interface ScalarAxis {\n /** Stable id — the tuning names the scalar by it. */\n readonly id: string\n /** The control's label. Default: the id. */\n readonly label?: string\n /** The control's hover text. Default: the label. */\n readonly tooltip?: string\n /**\n * One value per node — a finite number, which the frame maps onto [0, 1]\n * as `normalize` says: non-negative under `max`, any under `range`,\n * already in [0, 1] under `none`. A node absent from the map has no value\n * on this axis and rests at the axis's centre, the same answer as a node\n * in no bucket on a mapped axis. A true 0 is a value, and the built-ins\n * answer it as one.\n */\n compute(graph: ReadableGraph): Map<string, number>\n /**\n * How the frame maps the answers onto [0, 1]: `max` (the default) divides\n * by the largest, so 0 stays 0 and a negative answer is refused, loudly;\n * `range` puts the smallest at 0 and the largest at 1, whatever their\n * sign; `none` says the answers already lie in [0, 1], and the frame\n * refuses one outside it.\n */\n readonly normalize?: 'max' | 'range' | 'none'\n}\n\n/** A scalar with its words filled in — what `Taxonomy.scalars` carries. */\nexport interface ResolvedScalarAxis extends ScalarAxis {\n readonly label: string\n readonly tooltip: string\n}\n\n/** A declared scalar with its defaults filled in: the id as label, the label as tooltip. */\nexport function resolveScalarAxis(scalar: ScalarAxis): ResolvedScalarAxis {\n const label = scalar.label ?? scalar.id\n return { id: scalar.id, label, tooltip: scalar.tooltip ?? label, normalize: scalar.normalize, compute: graph => scalar.compute(graph) }\n}\n\n/**\n * Where a scalar's unit interval lands on an axis: the world coordinate a\n * score of 0 rests at, and the one a score of 1 rests at. Either may be the\n * lower — a span reads like a chart's axis and flips by dragging its ends\n * past each other.\n */\nexport interface ScalarSpan {\n readonly zero: number\n readonly one: number\n}\n\n/**\n * A layout axis backed by a computed scalar: which scalar, and — only\n * once the user has dragged an end — its span. Absent, the span is the\n * taxonomy's (`defaultSpan`), so it follows the taxonomy as a band axis's\n * declared bands do; the tuning stores what the user decided, nothing else.\n */\nexport interface ScalarTuning {\n readonly scalar: string\n readonly span?: ScalarSpan\n}\n\n/** The guard on a layout tuning: scalar, or a mapping with its overrides. */\nexport function isScalarTuning<T extends object>(t: ScalarTuning | T): t is ScalarTuning {\n return 'scalar' in t\n}\n\n/**\n * Downstream reach: how many dependency levels deep a node's subtree runs,\n * propagated up the containment hierarchy (one rank per tier, so a container\n * strictly out-ranks everything it hosts), normalized by the top container.\n * Leaves score 0; a graph with no dependency edges scores 0 everywhere.\n * Topological gravity reads this over the visible subgraph; as an axis it is\n * read over the whole graph. Either way the answer covers exactly the graph's\n * nodes: propagation stops at an ancestor the graph does not hold.\n */\nexport function computeDownstreamReach(graph: ReadableGraph): Map<string, number> {\n const adj = new Map<string, string[]>()\n for (const e of graph.edges) {\n if (isContainment(e)) continue\n const list = adj.get(e.sourceId)\n if (list) list.push(e.targetId)\n else adj.set(e.sourceId, [e.targetId])\n }\n\n const depths = new Map<string, number>()\n for (const node of graph.nodes) {\n const seen = new Set<string>([node.id])\n let depth = 0\n let frontier: string[] = [node.id]\n while (frontier.length > 0) {\n const next: string[] = []\n for (const cur of frontier) {\n const out = adj.get(cur)\n if (!out) continue\n for (const t of out) {\n if (seen.has(t)) continue\n seen.add(t)\n next.push(t)\n }\n }\n if (next.length > 0) depth++\n frontier = next\n }\n depths.set(node.id, depth)\n }\n\n // Propagate each node's depth up its containment chain, adding one rank per\n // tier so a container *strictly* out-ranks everything it hosts. Early-out\n // when an ancestor is already ranked higher (a deeper branch already\n // propagated past it, so its own ancestors are too).\n const effective = new Map(depths)\n for (const node of graph.nodes) {\n const d = depths.get(node.id) ?? 0\n if (d <= 0) continue\n let parent = node.parentId === undefined ? undefined : graph.getNode(node.parentId)\n let bump = d\n const guard = new Set<string>()\n while (parent && !guard.has(parent.id)) {\n guard.add(parent.id)\n bump += 1\n if ((effective.get(parent.id) ?? 0) >= bump) break\n effective.set(parent.id, bump)\n parent = parent.parentId === undefined ? undefined : graph.getNode(parent.parentId)\n }\n }\n\n return normalized(effective)\n}\n\n/** Dependency edges in and out, as a fraction of the busiest node's. */\nexport function computeDegree(graph: ReadableGraph): Map<string, number> {\n const count = new Map<string, number>()\n for (const n of graph.nodes) count.set(n.id, 0)\n for (const e of graph.edges) {\n if (isContainment(e)) continue\n if (graph.getNode(e.sourceId)) count.set(e.sourceId, (count.get(e.sourceId) ?? 0) + 1)\n if (graph.getNode(e.targetId)) count.set(e.targetId, (count.get(e.targetId) ?? 0) + 1)\n }\n return normalized(count)\n}\n\n/** Containment depth: 0 at the top of the hierarchy, 1 at the deepest node. */\nexport function computeContainmentDepth(graph: ReadableGraph): Map<string, number> {\n const depth = new Map<string, number>()\n for (const n of graph.nodes) {\n let d = 0\n let parent = n.parentId === undefined ? undefined : graph.getNode(n.parentId)\n const guard = new Set<string>()\n while (parent && !guard.has(parent.id)) {\n guard.add(parent.id)\n d++\n parent = parent.parentId === undefined ? undefined : graph.getNode(parent.parentId)\n }\n depth.set(n.id, d)\n }\n return normalized(depth)\n}\n\n/**\n * Divide by the maximum so the answer spans [0, 1]; a maximum of 0 is a true\n * 0 for every node, not an absence — the span's zero end, not the centre.\n */\nexport function normalized(values: Map<string, number>): Map<string, number> {\n let max = 0\n for (const v of values.values()) if (v > max) max = v\n const scores = new Map<string, number>()\n for (const [id, v] of values) scores.set(id, max === 0 ? 0 : v / max)\n return scores\n}\n\n/**\n * Map the smallest answer to 0 and the largest to 1; answers all alike are a\n * true 0 for every node — the span's zero end, not the centre.\n */\nexport function rangeNormalized(values: Map<string, number>): Map<string, number> {\n let min = Infinity, max = -Infinity\n for (const v of values.values()) { if (v < min) min = v; if (v > max) max = v }\n const scores = new Map<string, number>()\n for (const [id, v] of values) scores.set(id, max > min ? (v - min) / (max - min) : 0)\n return scores\n}\n\n/** The built-in reach scalar. */\nexport const downstreamReach: ScalarAxis = {\n id: 'reach',\n label: 'Reach',\n tooltip: 'Downstream reach: how many dependency levels deep a node’s subtree runs, propagated up containment; leaves at 0',\n compute: computeDownstreamReach,\n normalize: 'none',\n}\n\n/** The built-in degree scalar. */\nexport const degree: ScalarAxis = {\n id: 'degree',\n label: 'Degree',\n tooltip: 'Dependency edges in and out, as a fraction of the busiest node’s',\n compute: computeDegree,\n normalize: 'none',\n}\n\n/** The built-in depth scalar. */\nexport const containmentDepth: ScalarAxis = {\n id: 'depth',\n label: 'Depth',\n tooltip: 'Containment depth: 0 at the top of the hierarchy, 1 at the deepest node',\n compute: computeContainmentDepth,\n normalize: 'none',\n}\n\n/** The scalars every host gets: structural, so they need no domain knowledge. */\nexport const BUILT_IN_SCALARS: readonly ScalarAxis[] = [downstreamReach, degree, containmentDepth]\n","// The definition *shapes* a taxonomy is expressed in.\n//\n// This is the library half of the registries. The shapes live here; the entries\n// — which node styles exist, what they are called, what colour they are — live\n// with the host, and reach the engine through an `Taxonomy`. That split is the\n// whole point: a host declares its own values against these shapes and gets the\n// full graph engine, without the library knowing a single one of its names.\n//\n// Each shape comes twice: the *declared* form a host writes, in which\n// everything that has a sensible default is optional, and the *resolved* form\n// the engine reads, in which every field is present. `createTaxonomy` turns\n// the one into the other once, so the draw loop never asks \"was this given?\".\n\nimport type { AxisId, AxisValue } from './axis'\nimport type { EdgeStroke, ThemedColor } from './scheme'\n\n// ── Node styles ─────────────────────────────────────────────────────────────\n\n/** A rest band on one layout axis: the range inside which a value feels no gravity. */\nexport interface Band { min: number; max: number }\n\n/**\n * How one value on the style axis is drawn, sized, and physically weighted,\n * as a host declares it. Only the id, the label and the colour are required;\n * the rest is derived from them and from the value's position in the\n * declaration order (top of the hierarchy first):\n *\n * - `abbreviation`: the first two characters of the label.\n * - `icon`: none.\n * - `size.r`: 12; `size.iconSize`: nine tenths of the radius, rounded, at least 9.\n * - `physics.charge`: −40 × r; `physics.coreRadius`: 4 × r.\n * - `physics.band.y`: the value's slice of the stack, by position — the\n * first value's band at the top of the canvas, the last at the bottom,\n * neighbours overlapping a fifth; `physics.band.x`: 0 to 380.\n * - `whenHidden`: `ascend`; `summaryKind`: `degree`.\n *\n * A field given overrides its default, field by field — a band's `x` may be\n * declared and its `y` left to the stack.\n */\nexport interface NodeStyle {\n /** The value on the style axis this style is for. */\n id: AxisValue\n /** Display name, e.g. \"Service\". */\n label: string\n /**\n * One or two characters for a control token: a control surface renders a\n * token per value and has no room for a name.\n */\n abbreviation?: string\n /** Single glyph drawn inside the canvas node. */\n icon?: string\n /** Fill and border in both themes; the canvas reads `color[theme]`. */\n color: ThemedColor\n size?: {\n /** Circle radius in world units. Hit-test radius = r + 4. */\n r?: number\n /** Font size for the icon glyph (px). */\n iconSize?: number\n }\n physics?: {\n /**\n * Repulsion charge. Negative = repulsion. The starting point for the\n * value the user tunes at runtime via the control panel.\n */\n charge?: number\n /**\n * Core radius (charge softening, as a length). A pair of nodes softens\n * its charge by the squared average of the two endpoints' effective core\n * radii (`coreRadiusMultiplier × coreRadius`), added to d². Larger =\n * gentler, wider plateau.\n */\n coreRadius?: number\n /**\n * Rest bands, one per layout axis, inside which this value feels no\n * gravity. `y` is the hierarchy (negative = top of canvas); `x` bounds\n * the horizontal spread. The simulation seeds nodes inside their bands on\n * creation, so the structure is visible before the first tick — which\n * also makes this the seeding function, not only the gravity target.\n */\n band?: { x?: Band; y?: Band }\n }\n /**\n * What a dependency edge touching a HIDDEN node of this value does:\n * `ascend` re-points the endpoint to the node's nearest visible ancestor;\n * `splice` follows the node's own dependency edges through to the far\n * side, so caller → hidden → callee draws as caller → callee.\n */\n whenHidden?: 'ascend' | 'splice'\n /**\n * The glanceable summary for a node of this value: the children it contains,\n * counted per style value and named by that value's abbreviation\n * (`2 Sv · 1 Pd`); its dependency degree (`→3 ←2`); or nothing.\n */\n summaryKind?: 'children' | 'degree' | 'none'\n}\n\n/**\n * A node style with every default filled in — what `Taxonomy.styleOf` answers.\n *\n * Reached through `Taxonomy.nodeStyleOf`, never by key lookup from the\n * engine — a miss has to resolve to the fallback rather than throw, because\n * the resolution happens per node per frame inside the draw loop where an\n * exception stops the canvas repainting for good.\n */\nexport interface ResolvedNodeStyle {\n id: AxisValue\n label: string\n abbreviation: string\n /** Empty when the style declares none: nothing is drawn. */\n icon: string\n color: ThemedColor\n size: { r: number; iconSize: number }\n physics: { charge: number; coreRadius: number; band: { x: Band; y: Band } }\n whenHidden: 'ascend' | 'splice'\n summaryKind: 'children' | 'degree' | 'none'\n}\n\n// ── Edge types ──────────────────────────────────────────────────────────────\n\n/**\n * Stable id for one edge type.\n *\n * A string, not a union, and that is the library's whole position on edge\n * vocabulary: it is the override key in the spring tuning and the hover-link\n * key across the spring map, curves, and canvas, so it has to be something a\n * host can extend. A host narrows it to its own union.\n */\nexport type EdgeTypeId = string\n\n/** One spring: stiffness (k) and rest length. */\nexport interface SpringPhysics {\n strength: number\n distance: number\n}\n\n/**\n * One kind of edge, as a host declares it: which values it runs between and\n * which relation it expresses, optionally which values the edge itself must\n * carry (`match`), and — optionally — how it is stroked, its spring and its\n * hover text. An edge is classified as the declared type of its relation\n * that runs between its endpoints' values, in either orientation, and whose\n * `match` (if any) its own values satisfy; a satisfied `match` beats a type\n * without one, the declared orientation beats the reverse, and declaration\n * order settles the rest — unless the taxonomy's `classifyEdge` says\n * otherwise. A label left out is the id; a stroke left out is the neutral\n * stroke (the containment stroke for a containment type), field by field; a\n * spring left out is the middling spring; a tooltip left out is the label.\n */\nexport interface EdgeType {\n /** Stable id — also the override key in the spring tuning. */\n id: EdgeTypeId\n /** Short control-panel label. Default: the id. */\n label?: string\n /**\n * The endpoint values on the style axis this type runs between — one\n * value or several at each end: the type runs between every source value\n * and every target value, in either orientation. Read off the edge's\n * original endpoint nodes when an edge is classified; the first of each\n * is the split-colour token's two halves. Values, not types: the engine\n * resolves an edge's type from where its nodes sit.\n */\n sourceValue: AxisValue | readonly AxisValue[]\n targetValue: AxisValue | readonly AxisValue[]\n /** Which relation an edge of this type expresses; an edge is classified only as a type of its own relation. */\n relation: 'containment' | 'dependency'\n /**\n * Values the edge itself must carry on the host's edge axes to be this\n * type — how two types on one pair are told apart (a call and a signal\n * between the same two values, by the edge's `dispatch`). An edge lacking\n * one of these values is not this type. Absent, the pair alone decides,\n * and this type is the fallback for a pair other types `match` on.\n */\n match?: Readonly<Record<AxisId, AxisValue>>\n /** Declared stiffness / rest length — what the type resolves to until tuned. */\n physics?: SpringPhysics\n /** How the canvas strokes an edge of this type, in both themes; any field left out is its relation's default stroke's (`NEUTRAL_STROKE`, or `CONTAINMENT_STROKE` for a containment type). */\n style?: Partial<EdgeStroke>\n /** The hover text for the type. Default: the label. */\n tooltip?: string\n}\n\n/** An edge type with its defaults filled in — what `Taxonomy.edgeTypes` carries. */\nexport interface ResolvedEdgeType extends EdgeType {\n label: string\n /** The first declared source value — one half of the split-colour token. */\n sourceValue: AxisValue\n /** The first declared target value — the other half. */\n targetValue: AxisValue\n /** Every (source, target) pair the type runs between: each source value with each target value, in declaration order. */\n pairs: readonly (readonly [AxisValue, AxisValue])[]\n physics: SpringPhysics\n style: EdgeStroke\n tooltip: string\n}\n\n/** The stroke a dependency type starts from, and the stroke of a dependency edge no type claims: a quiet, solid grey. */\nexport const NEUTRAL_STROKE: EdgeStroke = { color: { light: '#94A3B8', dark: '#64748B' }, alpha: 0.7, dash: [], width: 1.2 }\n/** The stroke a containment type starts from, and the stroke of a containment edge no type claims: the same grey, faint and dashed. */\nexport const CONTAINMENT_STROKE: EdgeStroke = { color: { light: '#94A3B8', dark: '#64748B' }, alpha: 0.35, dash: [3, 4], width: 1 }\n\n/** The values declared at one end of an edge type, as a list. */\nexport function endpointValues(declared: AxisValue | readonly AxisValue[]): readonly AxisValue[] {\n return typeof declared === 'string' ? [declared] : declared\n}\n\n/** Every (source, target) pair a type runs between: each source value with each target value, in declaration order. */\nexport function edgePairs(type: Pick<EdgeType, 'sourceValue' | 'targetValue'>): readonly (readonly [AxisValue, AxisValue])[] {\n const targets = endpointValues(type.targetValue)\n return endpointValues(type.sourceValue).flatMap(s => targets.map(t => [s, t] as const))\n}\n\n// ── Defaults ────────────────────────────────────────────────────────────────\n\n/**\n * The frame the default rest bands are laid in, in world units: the\n * hierarchy axis runs from `top` (the first declared value) to `bottom` (the\n * last), and every value spreads across `x`. A host that declares a band of\n * its own writes it in these units.\n */\nexport const BAND_FRAME = { top: -300, bottom: 400, x: { min: 0, max: 380 } as Band } as const\nconst DEFAULT_RADIUS = 12\n/** The spring a type starts from when it declares none. */\nexport const MIDDLING_SPRING: SpringPhysics = { strength: 0.5, distance: 120 }\n\n/**\n * The rest band on the hierarchy axis for the value at position `i` of `n`:\n * centres spread evenly down the stack, each band wide enough to overlap its\n * neighbours by a fifth, so the structure reads as tiers without a gap a node\n * could never rest in. One value alone takes the whole stack.\n */\nexport function stackBand(i: number, n: number): Band {\n if (n <= 1) return { min: BAND_FRAME.top, max: BAND_FRAME.bottom }\n const gap = (BAND_FRAME.bottom - BAND_FRAME.top) / (n - 1)\n const centre = BAND_FRAME.top + gap * i\n const half = gap * 0.6\n return { min: Math.round(centre - half), max: Math.round(centre + half) }\n}\n\n/**\n * A declared style with every default filled in, given its position in the\n * declaration order. Pure: the same inputs give the same object, so the\n * taxonomy resolves each value once and hands out that one object.\n */\nexport function resolveNodeStyle(style: NodeStyle, i: number, n: number): ResolvedNodeStyle {\n const r = style.size?.r ?? DEFAULT_RADIUS\n return {\n ...style,\n abbreviation: style.abbreviation ?? style.label.slice(0, 2),\n icon: style.icon ?? '',\n size: { r, iconSize: style.size?.iconSize ?? Math.max(9, Math.round(r * 0.9)) },\n physics: {\n charge: style.physics?.charge ?? -40 * r,\n coreRadius: style.physics?.coreRadius ?? 4 * r,\n band: {\n x: style.physics?.band?.x ?? { ...BAND_FRAME.x },\n y: style.physics?.band?.y ?? stackBand(i, n),\n },\n },\n whenHidden: style.whenHidden ?? 'ascend',\n summaryKind: style.summaryKind ?? 'degree',\n }\n}\n\n/**\n * A declared edge type with its defaults filled in: the id as label, the\n * first declared value at each end beside every pair, the middling spring,\n * its relation's stroke field by field, the label as tooltip. The taxonomy\n * has refused an end that names no value before this runs.\n */\nexport function resolveEdgeType(type: EdgeType): ResolvedEdgeType {\n const start = type.relation === 'containment' ? CONTAINMENT_STROKE : NEUTRAL_STROKE\n const label = type.label ?? type.id\n return {\n ...type,\n label,\n sourceValue: endpointValues(type.sourceValue)[0]!,\n targetValue: endpointValues(type.targetValue)[0]!,\n pairs: edgePairs(type),\n physics: type.physics ?? { ...MIDDLING_SPRING },\n style: { ...start, ...type.style, color: { ...start.color, ...type.style?.color } },\n tooltip: type.tooltip ?? label,\n }\n}\n","// Force computations for the directed-graph layout, split by category.\n//\n// Each `apply*` function mutates the velocities of the active nodes for ONE\n// force, scaled by the simulation's current alpha, and takes only its own\n// category's parameter slice (ChargeParams / LinkParams / GravityParams) — so a\n// force literally cannot read params outside itself. The full ForceParams\n// object structurally satisfies each slice, so the Simulation passes `this.params`\n// unchanged. Behaviour is identical to the previous inline implementation; this\n// is a structural extraction.\n//\n// This module imports only *types* from ./simulation, so there is no runtime\n// import cycle: `simulation -> forces` is the only value dependency.\n\nimport type { AxisId, AxisValue } from './axis'\nimport { isIdentityMapping } from './axis'\nimport type { GraphEdge } from './model'\nimport { relationOf } from './model'\nimport type { ChargeParams, LinkParams, GravityParams, SimNode, PairTuning, PushPhysics, BandRange, LayoutTuning } from './simulation'\nimport type { ResolvedScalarAxis, ScalarAxis, ScalarSpan } from './scalar'\nimport { normalized, rangeNormalized } from './scalar'\nimport { isScalarTuning } from './scalar'\nimport type { ReadableGraph } from './model'\nimport type { Taxonomy } from './taxonomy'\nimport type { AxisMapping } from './axis'\nimport type { EdgeTypeId, ResolvedEdgeType, SpringPhysics } from './shapes'\nimport { MIDDLING_SPRING } from './shapes'\nimport type { Relation } from './axis'\nimport type { Rng } from './rng'\n\n/** The minimum a force needs of a node in order to look up its physics. */\nexport type PhysicsSubject = { values: Readonly<Record<AxisId, AxisValue>> }\n\n// ── Per-value / per-edge accessors ──────────────────────────────────────────\n// Each force reads its per-value/per-edge numbers from the keyed param maps via\n// these thin helpers, taking only its own category slice.\n//\n// **Every one of them defaults.** Today the key space is closed and a miss is\n// impossible; the whole point of the surrounding work is to open it, and the\n// three accessors fail three *different* silent ways when it does: charge\n// returns `undefined`, core radius returns `NaN` (its `Math.max` floor swallows\n// the miss, and the NaN reaches velocity two lines later), and the band\n// dereferences `undefined` and throws outright. Guarding them is cheap; finding\n// a NaN that entered the layout four frames ago is not.\n\n// Minimum effective core radius. A value dragged to 0 would otherwise drop the\n// pair softening toward zero and reintroduce the close-range force singularity\n// the softening exists to prevent, so we floor every read at this value.\nexport const CORE_RADIUS_MIN = 2\n\n/**\n * Physics for a value the param maps do not declare.\n *\n * **Read this before assuming it is inert — it is not.** The charge model\n * couples a pair by the AVERAGE of the two endpoints' charges, so a zero here\n * still repels every neighbour at half that neighbour's charge; and a band\n * contributes its centre to `bandCenters`, so a point band on the origin pulls\n * the median that the whole stack is re-centred on. One node carrying an\n * undeclared value therefore perturbs the layout of every declared one.\n *\n * That is a deliberate trade for now: these numbers keep an undeclared value\n * *finite and bounded* — which is the guard's actual job, and the thing whose\n * absence produces a NaN four frames later or a hard throw — without inventing\n * a \"non-participant\" concept in the physics. Making absent values true\n * non-participants is a change to the force model, and is not made here.\n */\nexport const ABSENT_VALUE_PHYSICS = {\n charge: 0,\n coreRadius: CORE_RADIUS_MIN,\n /** On either axis: a point band at the axis's centre. */\n band: { min: 0, max: 0 },\n} as const\n\n// ── Resolved physics ──────────────────────────────────────────────────────────\n//\n// `AxisTuning` is sparse (only what the user changed) and the kernels are hot\n// (four reads per pair, O(n²)). So the tables are resolved ONCE per\n// (params, taxonomy) into dense per-bucket maps here, and the kernels read\n// those. Resolution is the only place the taxonomy's declared physics is\n// consulted for the forces, and it never runs inside a loop.\n\n/** A per-bucket table, resolved from one axis's tuning and the taxonomy. */\nexport interface ResolvedAxis<T> {\n readonly mapping: AxisMapping\n readonly byBucket: ReadonlyMap<string, T>\n}\n\n/** A layout axis backed by a scalar: the scalar itself, and where its 0 and 1 rest. */\nexport interface ResolvedScalarLayout {\n readonly scalar: ResolvedScalarAxis\n readonly span: ScalarSpan\n}\n\n/** A layout axis as the kernels see it: per-bucket bands, or a scalar over a span. */\nexport type ResolvedLayoutAxis = ResolvedAxis<BandRange> | ResolvedScalarLayout\n\nexport const isScalarAxis = (a: ResolvedLayoutAxis): a is ResolvedScalarLayout => 'scalar' in a\n\n/** The mapped half of a layout axis; loud where a caller expects buckets and meets a scalar. */\nexport function mappedAxis(a: ResolvedLayoutAxis): ResolvedAxis<BandRange> {\n if (isScalarAxis(a)) throw new Error(`expected a mapped axis, got scalar ${JSON.stringify(a.scalar.id)}`)\n return a\n}\n\n/** What the layout frame needs of a node: its values, and its id for a scalar's score. */\nexport type LayoutSubject = PhysicsSubject & { id: string }\n\nexport interface ResolvedPhysics {\n push: ResolvedAxis<PushPhysics>\n /** The two layout axes — the same shape, resolved the same way; either may be a scalar. */\n bandX: ResolvedLayoutAxis\n bandY: ResolvedLayoutAxis\n spring: ResolvedSprings\n}\n\n/** The bucket a node falls in on an axis, or undefined when it carries no value there. */\nexport function bucketOf(on: { readonly mapping: AxisMapping }, node: PhysicsSubject): string | undefined {\n return on.mapping.bucketFor(node.values[on.mapping.axis])?.id\n}\n\n/**\n * A bucket's declared physics is the fold of its values' declared physics.\n *\n * Mean for charge and core radius, union for the band — both order-independent,\n * so a host reordering a bucket's values for readability moves nothing. With the\n * identity mapping every bucket has one value and the fold is the value.\n */\nfunction declaredPush(taxonomy: Taxonomy, values: readonly AxisValue[]): PushPhysics {\n let charge = 0, coreRadius = 0\n for (const v of values) {\n const ph = taxonomy.styleOf(v).physics\n charge += ph.charge; coreRadius += ph.coreRadius\n }\n const n = Math.max(values.length, 1)\n return { charge: charge / n, coreRadius: coreRadius / n }\n}\n\nfunction declaredBand(taxonomy: Taxonomy, values: readonly AxisValue[], axis: 'x' | 'y'): BandRange {\n let min = Infinity, max = -Infinity\n for (const v of values) {\n const b = taxonomy.styleOf(v).physics.band[axis]\n min = Math.min(min, b.min); max = Math.max(max, b.max)\n }\n return values.length === 0 ? { ...ABSENT_VALUE_PHYSICS.band } : { min, max }\n}\n\n/**\n * The span a scalar axis has until the user drags an end: the declared bands'\n * extent on that axis (the same fold as `declaredBand`, over every declared\n * value) centred on the origin, so the layout keeps its scale when an axis\n * switches kind — and read like a chart: 1 at the top (screen y grows\n * downward) and at the right.\n */\nexport function defaultSpan(taxonomy: Taxonomy, axis: 'x' | 'y'): ScalarSpan {\n const extent = declaredBand(taxonomy, taxonomy.styleValues, axis)\n const half = (extent.max - extent.min) / 2\n return axis === 'y' ? { zero: half, one: -half } : { zero: -half, one: half }\n}\n\nfunction resolveAxis<T>(\n mapping: AxisMapping,\n overrides: ReadonlyMap<string, T>,\n declared: (values: readonly AxisValue[]) => T,\n): ResolvedAxis<T> {\n const byBucket = new Map<string, T>()\n for (const bucket of mapping.buckets) {\n byBucket.set(bucket.id, overrides.get(bucket.id) ?? declared(bucket.values))\n }\n return { mapping, byBucket }\n}\n\nexport function resolvePhysics(params: ChargeParams & GravityParams & LinkParams, taxonomy: Taxonomy): ResolvedPhysics {\n // Declared physics belong to the style axis's values. A bucket on any other\n // axis — a file, a kind — is not a style key, so it starts from the host's\n // TYPICAL declared physics (the same fold, over every declared value) rather\n // than from a lookup that would miss to the fallback style, once per bucket,\n // with a warning about placeholder styling that is not even true.\n const style = taxonomy.styleAxis()\n const pushFor = (m: AxisMapping) => m.axis === style\n ? (values: readonly AxisValue[]) => declaredPush(taxonomy, values)\n : () => declaredPush(taxonomy, taxonomy.styleValues)\n const bandOn = (m: AxisMapping, axis: 'x' | 'y') => m.axis === style\n ? (values: readonly AxisValue[]) => declaredBand(taxonomy, values, axis)\n : () => declaredBand(taxonomy, taxonomy.styleValues, axis)\n const layout = (t: LayoutTuning, axis: 'x' | 'y'): ResolvedLayoutAxis => {\n if (!isScalarTuning(t)) return resolveAxis(t.mapping, t.overrides, bandOn(t.mapping, axis))\n const scalar = taxonomy.scalars.find(sc => sc.id === t.scalar)\n // Loud: a tuning naming a scalar the taxonomy does not know is a host bug.\n // The view never holds one — it reads params retuned for the taxonomy\n // (`retunedFor` falls the axis back on a swap), so this is unreachable\n // from the UI and fires only for a host that built the tuning by hand.\n if (!scalar) throw new Error(`layout axis names scalar ${JSON.stringify(t.scalar)}, which the taxonomy does not declare`)\n return { scalar, span: t.span ?? defaultSpan(taxonomy, axis) }\n }\n return {\n push: resolveAxis(params.pair.mapping, params.pair.push, pushFor(params.pair.mapping)),\n bandX: layout(params.bandX, 'x'),\n bandY: layout(params.bandY, 'y'),\n spring: resolveSprings(params.pair, taxonomy),\n }\n}\n\n// ── Per-node accessors over the resolved tables ───────────────────────────────\n\nexport function chargeFor(push: ResolvedAxis<PushPhysics>, node: PhysicsSubject): number {\n const b = bucketOf(push, node)\n return (b === undefined ? undefined : push.byBucket.get(b)?.charge) ?? ABSENT_VALUE_PHYSICS.charge\n}\n\nexport function coreRadiusFor(push: ResolvedAxis<PushPhysics>, node: PhysicsSubject): number {\n const b = bucketOf(push, node)\n return Math.max(\n (b === undefined ? undefined : push.byBucket.get(b)?.coreRadius) ?? ABSENT_VALUE_PHYSICS.coreRadius,\n CORE_RADIUS_MIN,\n )\n}\n\n/** One bucket's push physics, for a control surface that iterates buckets. */\nexport function pushForKey(push: ResolvedAxis<PushPhysics>, bucketId: string): PushPhysics {\n return push.byBucket.get(bucketId) ?? { charge: ABSENT_VALUE_PHYSICS.charge, coreRadius: ABSENT_VALUE_PHYSICS.coreRadius }\n}\n\n/** One bucket's rest band as declared/tuned, for a control surface that edits it. */\nexport function bandRangeFor(band: ResolvedAxis<BandRange>, bucketId: string): BandRange {\n return band.byBucket.get(bucketId) ?? { ...ABSENT_VALUE_PHYSICS.band }\n}\n\n/** The rest band for a bucket, or the absent band for no bucket. Control surfaces iterate buckets; forces take nodes. */\nexport function bandForKey(band: ResolvedAxis<BandRange>, bucketId: string | undefined): BandRange {\n return (bucketId === undefined ? undefined : band.byBucket.get(bucketId)) ?? ABSENT_VALUE_PHYSICS.band\n}\n\nexport function bandFor(band: ResolvedAxis<BandRange>, node: PhysicsSubject): BandRange {\n return bandForKey(band, bucketOf(band, node))\n}\n\n/**\n * The buckets some node of `nodes` falls in, in first-encounter order — the\n * set the band force re-centres on, the radial mapping rings, and the overlay\n * draws (present, never all declared). A node with no value on the axis\n * is in no bucket: it has no band, joins no median, and sits at the axis's\n * centre.\n */\nexport function presentBuckets<T>(nodes: Iterable<PhysicsSubject>, axis: ResolvedAxis<T>): string[] {\n const seen = new Set<string>()\n for (const n of nodes) {\n const key = bucketOf(axis, n)\n if (key !== undefined) seen.add(key)\n }\n return [...seen]\n}\n\nconst centerOf = (b: BandRange) => (b.min + b.max) / 2\n\n/**\n * One band centre per bucket present, in first-encounter order. The collection\n * step — not the median — is where the dedup happens, and a dedup that silently\n * degenerated to one entry per node would be invisible in every downstream\n * number except this array's length. Deliberately NOT deduplicated by centre\n * value: several buckets may share a band.\n */\nexport function bandCenters(nodes: Iterable<PhysicsSubject>, axis: ResolvedAxis<BandRange>): number[] {\n return presentBuckets(nodes, axis).map(key => centerOf(bandForKey(axis, key)))\n}\n\n/**\n * Median of the present band centres. Subtracting it keeps the band stack\n * symmetric about the origin on that axis, so both axes share the origin with\n * center gravity. (Editing one band can move the median and so shift the\n * rest; the stack stays centred, which is the property worth keeping.)\n */\nexport function medianBandCenter(nodes: Iterable<PhysicsSubject>, axis: ResolvedAxis<BandRange>): number {\n const centers = bandCenters(nodes, axis).sort((a, b) => a - b)\n const m = centers.length\n if (m === 0) return 0\n return m % 2 ? centers[(m - 1) / 2] : (centers[m / 2 - 1] + centers[m / 2]) / 2\n}\n\n/**\n * Radial mode: each present bucket's band centre maps to a target radius —\n * smallest centre innermost — over the fixed ring span. Buckets with equal\n * centres share a ring; a node in no bucket has no ring and feels no pull.\n */\nexport function ringRadii(nodes: Iterable<PhysicsSubject>, axis: ResolvedAxis<BandRange>): Map<string, number> {\n const buckets = presentBuckets(nodes, axis)\n const centers = buckets.map(key => centerOf(bandForKey(axis, key)))\n const lo = centers.length ? Math.min(...centers) : 0\n const span = (centers.length ? Math.max(...centers) : 0) - lo || 1\n return new Map(buckets.map((key, i) => [key, RADIAL_R_MIN + ((centers[i] - lo) / span) * (RADIAL_R_MAX - RADIAL_R_MIN)]))\n}\n\n/**\n * The frame the band forces act in, computed over the simulation's WHOLE node\n * set: for a mapped axis the median centre (and, on Y, the ring per bucket);\n * for a scalar axis every node's score. One frame for seeding, the force and\n * the overlay, so they agree by construction — a frame over the visible subset\n * would move with every filter toggle and disagree with the seed. It answers\n * two questions per node: where it rests on an axis, and which ring it belongs\n * on.\n */\nexport interface LayoutFrame {\n readonly centerX: number\n readonly centerY: number\n /** Ring radius per Y bucket (mapped Y only; a scalar Y rings per node). */\n readonly rings: ReadonlyMap<string, number>\n /** A node's rest interval on an axis — a re-centred band, a scalar's point, or the centre itself for a node with no value there. */\n rest(axis: 'x' | 'y', node: LayoutSubject): { lo: number; hi: number }\n /** A node's ring radius in radial mode: its bucket's ring, its scalar's, or the centre (0) for a node with no value on Y. */\n ringOf(node: LayoutSubject): number\n}\n\nconst AT_CENTRE = { lo: 0, hi: 0 }\n\n// A scalar's answer, checked once per frame: every value finite and in [0, 1].\n// Loud, because a value outside the contract would become a rest point or a\n// ring radius and nothing downstream could tell it from a real one.\nfunction checkedScores(scalar: ScalarAxis, graph: ReadableGraph): Map<string, number> {\n const raw = scalar.compute(graph)\n for (const [id, s] of raw) {\n if (!Number.isFinite(s)) throw new Error(`scalar ${JSON.stringify(scalar.id)} answered ${String(s)} for node ${JSON.stringify(id)}; a scalar answers a finite number`)\n }\n const scores = scalar.normalize === 'none' ? raw : scalar.normalize === 'range' ? rangeNormalized(raw) : normalized(raw)\n for (const [id, s] of scores) {\n if (!(Number.isFinite(s) && s >= 0 && s <= 1)) {\n throw new Error(`scalar ${JSON.stringify(scalar.id)} answered ${String(s)} for node ${JSON.stringify(id)}; a scalar answers in [0, 1]`)\n }\n }\n return scores\n}\n\nexport function layoutFrame(graph: ReadableGraph, resolved: ResolvedPhysics): LayoutFrame {\n const all = [...graph.nodes]\n const centerFor = (a: ResolvedLayoutAxis) => (isScalarAxis(a) ? 0 : medianBandCenter(all, a))\n const scoresFor = (a: ResolvedLayoutAxis) => (isScalarAxis(a) ? checkedScores(a.scalar, graph) : undefined)\n const centerX = centerFor(resolved.bandX)\n const centerY = centerFor(resolved.bandY)\n const scoresX = scoresFor(resolved.bandX)\n const scoresY = scoresFor(resolved.bandY)\n const rings = isScalarAxis(resolved.bandY) ? new Map<string, number>() : ringRadii(all, resolved.bandY)\n const restOn = (a: ResolvedLayoutAxis, center: number, scores: Map<string, number> | undefined, node: LayoutSubject) => {\n if (isScalarAxis(a)) {\n const s = scores?.get(node.id)\n if (s === undefined) return AT_CENTRE\n const p = a.span.zero + s * (a.span.one - a.span.zero)\n return { lo: p, hi: p }\n }\n const bucket = bucketOf(a, node)\n const band = bucket === undefined ? undefined : a.byBucket.get(bucket)\n return band === undefined ? AT_CENTRE : { lo: band.min - center, hi: band.max - center }\n }\n return {\n centerX, centerY, rings,\n rest: (axis, node) => axis === 'x' ? restOn(resolved.bandX, centerX, scoresX, node) : restOn(resolved.bandY, centerY, scoresY, node),\n ringOf: node => {\n if (isScalarAxis(resolved.bandY)) {\n // The same rule as `ringRadii`: the low end of Y is the innermost\n // ring — here the span's low end, whichever of 0 and 1 that is.\n if (scoresY?.get(node.id) === undefined) return 0\n const rest = restOn(resolved.bandY, centerY, scoresY, node)\n const { zero, one } = resolved.bandY.span\n const lo = Math.min(zero, one)\n const extent = Math.abs(one - zero) || 1\n return RADIAL_R_MIN + ((rest.lo - lo) / extent) * (RADIAL_R_MAX - RADIAL_R_MIN)\n }\n const bucket = bucketOf(resolved.bandY, node)\n return (bucket === undefined ? undefined : rings.get(bucket)) ?? 0\n },\n }\n}\n\n/** Whether any band axis is pulling, mode-aware: in radial only Y acts. */\nexport function bandAxesActing(params: GravityParams): boolean {\n return params.radial ? params.gravityY > 0 : params.gravityX > 0 || params.gravityY > 0\n}\n\nexport function topologicalActing(params: GravityParams): boolean {\n return params.topologicalEnabled && params.gravityTopological > 0\n}\n\n/**\n * One stripe of the overlay: a band on an axis and every present bucket that\n * shares it — or, for a scalar axis, the whole span as one gradient:\n * no buckets to name, and `gradient` says which end is 0 and which is 1.\n */\nexport interface BandStripe {\n readonly lo: number\n readonly hi: number\n readonly buckets: readonly string[]\n readonly gradient?: ScalarSpan\n}\n\n/** One ring of the radial overlay: a radius, and every present Y bucket that maps to it. */\nexport interface BandRing {\n readonly radius: number\n readonly buckets: readonly string[]\n}\n\n/**\n * The band overlay as data: what the canvas draws, decided here, once.\n * Stripes only while that axis acts in cartesian; rings\n * only while Y acts in radial — the same predicates the tick uses, so the\n * overlay never shows a force that does nothing. `colors` is the view's: one\n * per bucket, keyed by bucket id.\n */\nexport interface BandOverlay {\n readonly acting: boolean\n readonly x: readonly BandStripe[]\n readonly y: readonly BandStripe[]\n readonly rings: readonly BandRing[]\n readonly colors: ReadonlyMap<string, string>\n}\n\nexport function bandOverlay(\n nodes: Iterable<PhysicsSubject>,\n params: GravityParams,\n resolved: ResolvedPhysics,\n frame: LayoutFrame,\n /** A bucket's colour, or undefined for a bucket with none — the canvas then draws its one neutral. */\n colorOf: (axis: ResolvedAxis<BandRange>, bucket: string) => string | undefined,\n): BandOverlay {\n const all = [...nodes]\n const acting = bandAxesActing(params)\n const colors = new Map<string, string>()\n const withColors = (axis: ResolvedAxis<BandRange>, stripes: BandStripe[]) => {\n for (const st of stripes) for (const b of st.buckets) if (!colors.has(b)) { const c = colorOf(axis, b); if (c !== undefined) colors.set(b, c) }\n return stripes\n }\n if (!acting) return { acting, x: [], y: [], rings: [], colors }\n if (params.radial) {\n // A scalar Y rings per node — continuous, so nothing discrete to draw.\n if (isScalarAxis(resolved.bandY)) return { acting, x: [], y: [], rings: [], colors }\n const byRadius = new Map<number, string[]>()\n for (const [bucket, radius] of frame.rings) {\n const list = byRadius.get(radius)\n if (list) list.push(bucket)\n else byRadius.set(radius, [bucket])\n if (!colors.has(bucket)) { const c = colorOf(resolved.bandY, bucket); if (c !== undefined) colors.set(bucket, c) }\n }\n return { acting, x: [], y: [], rings: [...byRadius].map(([radius, buckets]) => ({ radius, buckets })), colors }\n }\n const stripesOn = (a: ResolvedLayoutAxis, center: number): BandStripe[] =>\n isScalarAxis(a)\n ? [{ lo: Math.min(a.span.zero, a.span.one), hi: Math.max(a.span.zero, a.span.one), buckets: [], gradient: a.span }]\n : withColors(a, bandStripes(all, a, center))\n return {\n acting,\n x: params.gravityX > 0 ? stripesOn(resolved.bandX, frame.centerX) : [],\n y: params.gravityY > 0 ? stripesOn(resolved.bandY, frame.centerY) : [],\n rings: [],\n colors,\n }\n}\n\n/**\n * The overlay's stripes on one axis, as data: one per distinct band among the\n * buckets present, re-centred on the frame. Buckets that share a band share a\n * stripe (the default X window, two tiers on one Y band), so the overlay's\n * density reads as bands rather than bucket counts. Pure.\n */\nexport function bandStripes(nodes: Iterable<PhysicsSubject>, axis: ResolvedAxis<BandRange>, center: number): BandStripe[] {\n const byBand = new Map<string, { lo: number; hi: number; buckets: string[] }>()\n for (const bucket of presentBuckets(nodes, axis)) {\n const b = bandForKey(axis, bucket)\n const key = `${b.min}:${b.max}`\n const prev = byBand.get(key)\n if (prev) prev.buckets.push(bucket)\n else byBand.set(key, { lo: b.min - center, hi: b.max - center, buckets: [bucket] })\n }\n return [...byBand.values()]\n}\n\n// ── Resolved springs ──────────────────────────────────────────────────────────\n\n/**\n * The spring for an edge whose endpoint the mapping does not bucket — the edge\n * counterpart of ABSENT_VALUE_PHYSICS. Zero stiffness: the edge exerts no pull,\n * which is the only honest answer when nothing says how strongly it should, and\n * it keeps the kernel finite where `undefined` would write NaN into both\n * endpoints on the first tick.\n */\nexport const ABSENT_EDGE_PHYSICS: SpringPhysics = { strength: 0, distance: 0 }\n\n/**\n * What a control token needs of a spring category: its identity, its two\n * endpoint buckets, its kind and its words. A declared edge type satisfies it;\n * a pair category synthesises it — and neither needs physics or a stroke here\n * (physics is `physicsFor`, and a pair is a spring, never a stroke).\n */\nexport type EdgeCategoryDescription = Pick<ResolvedEdgeType, 'id' | 'label' | 'sourceValue' | 'targetValue' | 'relation' | 'tooltip'>\n\n\n/**\n * The pair rule's category id: the relation and the unordered bucket pair,\n * encoded so a bucket id containing any separator cannot collide with another\n * pair. Ids are keys, never labels; `describe` turns one back into something a\n * token can show.\n */\nfunction pairId(relation: Relation, a: string, b: string): EdgeTypeId {\n return JSON.stringify(a <= b ? [relation, a, b] : [relation, b, a])\n}\n\nfunction parsePairId(id: EdgeTypeId): [Relation, string, string] | undefined {\n try {\n const t: unknown = JSON.parse(id)\n if (Array.isArray(t) && t.length === 3 && (t[0] === 'containment' || t[0] === 'dependency') &&\n typeof t[1] === 'string' && typeof t[2] === 'string') return [t[0], t[1], t[2]]\n } catch { /* not a pair id */ }\n return undefined\n}\n\n/**\n * The spring table, resolved from the pair tuning and the taxonomy.\n *\n * Two classifiers can fill it, and which one is decided here, once, not per\n * edge. On the style axis bucketed by identity, the host's declared table and\n * its prioritized rule apply verbatim: the hand-tuned springs, and a\n * precedence no pair rule reproduces — two of its categories share endpoint\n * values and differ only on the edge's own dispatch. On any other\n * (axis, mapping) the library's pair rule applies: one category per unordered\n * bucket pair per relation, each the middling spring until tuned. The\n * pair rule enumerates nothing: a category exists when an edge falls in it,\n * which is what a control surface shows and what the token ceiling counts.\n */\nexport interface ResolvedSprings {\n readonly mapping: AxisMapping\n /**\n * The category of an edge from its endpoints' bucket ids, or undefined when\n * an endpoint is unbucketed, or — on the style axis — when no declared type\n * of the edge's relation runs between its endpoint values and the\n * taxonomy's `classifyEdge` names none; such an edge pulls nothing and\n * draws in its relation's default stroke.\n */\n classify(edge: GraphEdge, src: string | undefined, tgt: string | undefined): EdgeTypeId | undefined\n /** Effective physics: the override where one exists, else the declared spring — the middling spring, on a pair axis. */\n physicsFor(id: EdgeTypeId): SpringPhysics\n /** What a control surface shows for a category — the declared entry, or one synthesized from a pair id. */\n describe(id: EdgeTypeId): EdgeCategoryDescription | undefined\n}\n\nexport function resolveSprings(pair: PairTuning, taxonomy: Taxonomy): ResolvedSprings {\n const { mapping, spring: overrides } = pair\n if (mapping.axis === taxonomy.styleAxis() && isIdentityMapping(mapping)) {\n const table = new Map(taxonomy.edgeTypes.map(e => [e.id, e]))\n return {\n mapping,\n // An edge no declared type of its relation claims has no category here:\n // it pulls nothing and draws in its relation's stroke, and the\n // diagnostics have named it.\n classify: (edge, src, tgt) => {\n const id = taxonomy.edgeTypeOf(edge, src, tgt)\n const type = id === undefined ? undefined : table.get(id)\n return type !== undefined && type.relation === relationOf(edge) ? id : undefined\n },\n physicsFor: id => overrides.get(id) ?? table.get(id)?.physics ?? ABSENT_EDGE_PHYSICS,\n describe: id => table.get(id),\n }\n }\n // Every pair category starts from the middling spring: the same start a\n // declared type gets when it leaves its spring out, and no type's declared\n // spring moves another's.\n const typical = MIDDLING_SPRING\n const buckets = new Map(mapping.buckets.map(b => [b.id, b]))\n return {\n mapping,\n classify: (edge, src, tgt) =>\n src === undefined || tgt === undefined ? undefined : pairId(relationOf(edge), src, tgt),\n physicsFor: id => overrides.get(id) ?? typical,\n describe: id => {\n const parsed = parsePairId(id)\n if (!parsed) return undefined\n const [relation, a, b] = parsed\n const A = buckets.get(a)\n const B = buckets.get(b)\n if (!A || !B) return undefined\n return {\n id,\n label: `${A.abbreviation}↔${B.abbreviation}`,\n sourceValue: a,\n targetValue: b,\n relation,\n physics: typical,\n tooltip: `${A.label} ↔ ${B.label} ${relation}`,\n }\n },\n }\n}\n\n/** The spring an edge is under: its category, if it has one, and the effective physics. */\nexport interface EdgeCategory {\n strength: number\n distance: number\n /**\n * The category id — what the canvas's active-edge highlight matches to a\n * spring-map token — or undefined for an edge with an unbucketed endpoint.\n */\n key: EdgeTypeId | undefined\n}\n\nexport function springFor(spring: ResolvedSprings, edge: GraphEdge, src: PhysicsSubject, tgt: PhysicsSubject): EdgeCategory {\n const key = spring.classify(edge, bucketOf(spring, src), bucketOf(spring, tgt))\n const ph = key === undefined ? ABSENT_EDGE_PHYSICS : spring.physicsFor(key)\n return { strength: ph.strength, distance: ph.distance, key }\n}\n\n/**\n * The categories some edge of the graph falls in — one token each. Under the\n * pair rule this is the only enumeration there is; under the declared table it\n * is the subset the graph actually uses. O(edges), no table built.\n */\nexport function presentSpringCategories(\n spring: ResolvedSprings,\n edges: Iterable<GraphEdge>,\n nodeOf: (id: string) => PhysicsSubject | undefined,\n): Set<EdgeTypeId> {\n const present = new Set<EdgeTypeId>()\n for (const edge of edges) {\n const src = nodeOf(edge.sourceId)\n const tgt = nodeOf(edge.targetId)\n if (!src || !tgt) continue\n const key = springFor(spring, edge, src, tgt).key\n if (key !== undefined) present.add(key)\n }\n return present\n}\n\n// ── Coordinate adapter ────────────────────────────────────────────────────────\n//\n// Polar conversion about the world origin, with a guard near r = 0 where the\n// angle is undefined and a radial force would otherwise spike. Not used by the\n// forces here, which work in cartesian coordinates; provided so a radial force\n// can express targets in (r, theta) and convert back to (x, y) accelerations.\nexport function toPolar(x: number, y: number): { r: number; theta: number } {\n const r = Math.hypot(x, y)\n if (r < 1e-6) return { r: 0, theta: 0 }\n return { r, theta: Math.atan2(y, x) }\n}\n\nexport function fromPolar(r: number, theta: number): { x: number; y: number } {\n return { x: r * Math.cos(theta), y: r * Math.sin(theta) }\n}\n\n// ── Forces ───────────────────────────────────────────────────────────────────\n\n// Charge force: repulsion between every visible node pair.\n// force = pushMultiplier * strength / (dist² + softening)^chargeExponent\n// The softening prevents the close-range singularity; it is per-pair: the\n// squared average of the two endpoints' effective core radii\n// (rEff = coreRadiusMultiplier × coreRadius[type]). The exponent acts on the\n// squared distance directly (no /2) — chargeExponent 1 = inverse-square.\nexport function applyChargeForce(\n active: SimNode[],\n params: ChargeParams,\n resolved: ResolvedPhysics,\n alpha: number,\n rng: Rng,\n): void {\n const crMul = params.coreRadiusMultiplier\n const pushMul = params.pushMultiplier\n const chargeExp = params.chargeExponent\n // Per-node physics once, O(n), before the O(n²) pair loop reads it: each\n // accessor is an axis read and two map lookups, and the loop below would\n // otherwise make four of those per pair.\n const n = active.length\n const charge = new Float64Array(n)\n const rEff = new Float64Array(n)\n for (let i = 0; i < n; i++) {\n charge[i] = chargeFor(resolved.push, active[i])\n rEff[i] = crMul * coreRadiusFor(resolved.push, active[i])\n }\n for (let i = 0; i < n; i++) {\n for (let j = i + 1; j < n; j++) {\n const a = active[i]\n const b = active[j]\n let dx = b.x - a.x\n let dy = b.y - a.y\n let dist2 = dx * dx + dy * dy\n // Coincident nodes: pick a true random unit vector and reset dist²\n // consistently, so the unit direction stays unit and we don't pump a\n // huge force into a degenerate frame.\n if (dist2 < 0.01) {\n const angle = rng() * Math.PI * 2\n dx = Math.cos(angle)\n dy = Math.sin(angle)\n dist2 = 1\n }\n const rawDist = Math.sqrt(dist2)\n // Per-pair softening from the endpoints' core radii.\n const rAvg = (rEff[i] + rEff[j]) / 2\n const softening = rAvg * rAvg\n const strength = (charge[i] + charge[j]) / 2\n // Negate: strength is negative (convention), but force must be positive\n // for repulsion. The exponent acts on (dist² + softening) directly.\n const force = -(alpha * pushMul * strength / Math.pow(dist2 + softening, chargeExp))\n const fx = force * (dx / rawDist)\n const fy = force * (dy / rawDist)\n if (!a.pinned) { a.vx -= fx; a.vy -= fy }\n if (!b.pinned) { b.vx += fx; b.vy += fy }\n }\n }\n}\n\n// Link force: spring attraction between connected nodes.\n// force = pullMultiplier * strength * sign(disp) * |disp|^linkExponent / dist\n// The per-tick degree map (count of incident active edges per node) biases each\n// edge's pull between its endpoints in inverse proportion to their degree, so a\n// high-degree node anchors and its lighter neighbours orbit — the standard fix\n// for stiff-spring oscillation under explicit Euler integration.\nexport function applyLinkForce(\n activeEdges: readonly GraphEdge[],\n nodeMap: Map<string, SimNode>,\n params: LinkParams,\n categoryOf: (edge: GraphEdge) => EdgeCategory,\n alpha: number,\n rng: Rng,\n): void {\n const degree = new Map<string, number>()\n for (const edge of activeEdges) {\n degree.set(edge.sourceId, (degree.get(edge.sourceId) ?? 0) + 1)\n degree.set(edge.targetId, (degree.get(edge.targetId) ?? 0) + 1)\n }\n\n const pullMul = params.pullMultiplier\n const linkExp = params.linkExponent\n const distMul = params.distanceMultiplier\n for (const edge of activeEdges) {\n const source = nodeMap.get(edge.sourceId)\n const target = nodeMap.get(edge.targetId)\n if (!source || !target) continue\n\n let dx = target.x - source.x\n let dy = target.y - source.y\n let dist = Math.sqrt(dx * dx + dy * dy)\n // Coincident endpoints: use a true random unit vector and reset dist to 1,\n // so the 1/dist factor and the (dx,dy) direction stay sane.\n if (dist < 0.1) {\n const angle = rng() * Math.PI * 2\n dx = Math.cos(angle)\n dy = Math.sin(angle)\n dist = 1\n }\n\n const cat = categoryOf(edge)\n const restDist = cat.distance * distMul\n const disp = dist - restDist\n const absDisp = Math.abs(disp)\n const sign = disp >= 0 ? 1 : -1\n const force = alpha * pullMul * cat.strength * sign * Math.pow(absDisp, linkExp) / dist\n const fx = force * dx\n const fy = force * dy\n\n const sd = degree.get(edge.sourceId) ?? 1\n const td = degree.get(edge.targetId) ?? 1\n const bias = sd / (sd + td)\n\n if (!source.pinned) { source.vx += fx * (1 - bias); source.vy += fy * (1 - bias) }\n if (!target.pinned) { target.vx -= fx * bias; target.vy -= fy * bias }\n }\n}\n\n// Radius range the bands map onto in radial mode (uppermost tier innermost).\n// Exported so the canvas can draw matching ring guides.\nexport const RADIAL_R_MIN = 60\nexport const RADIAL_R_MAX = 540\n\nexport function applyBandGravity(\n active: SimNode[],\n params: GravityParams,\n frame: LayoutFrame,\n alpha: number,\n rng: Rng,\n): void {\n if (params.radial) {\n applyBandGravityRadial(active, params, frame, alpha, rng)\n return\n }\n // The same rule on both axes: a strength of zero is an axis switched off.\n if (params.gravityX > 0) pullToRest(active, 'x', frame, params.gravityX, params.gravityBandExp, alpha)\n if (params.gravityY > 0) pullToRest(active, 'y', frame, params.gravityY, params.gravityBandExp, alpha)\n}\n\n// Pull every node outside its rest interval on one axis back toward the\n// nearest edge of it. The frame answers the interval: a band re-centred on the\n// median, a scalar's point, or the centre itself for a node with no value. The\n// pull never carries a node past its rest in one tick: a point has no band to\n// absorb an overshoot, so an uncapped |d|^exp at exp > 1 bounced nodes across\n// it every tick up to the velocity ceiling.\nfunction pullToRest(\n active: SimNode[],\n coord: 'x' | 'y',\n frame: LayoutFrame,\n strength: number,\n exp: number,\n alpha: number,\n): void {\n const velocity = coord === 'x' ? 'vx' : 'vy'\n for (const node of active) {\n if (node.pinned) continue\n const { lo, hi } = frame.rest(coord, node)\n const p = node[coord]\n let target: number | null = null\n if (p < lo) target = lo\n else if (p > hi) target = hi\n if (target !== null) {\n node[velocity] -= bandForce(p - target, exp, alpha * strength)\n }\n }\n}\n\n// Restoring impulse for the band springs: magnitude |d|^exp × scale, signed\n// toward the rest (exp = 1 is the linear/Hooke spring; exp > 1 is soft just\n// outside the band and stiff far out), and never more than |d| itself — the\n// pull reaches the rest, not beyond it. Shared by both axes and the ring pull.\nfunction bandForce(d: number, exp: number, scale: number): number {\n return Math.sign(d) * Math.min(Math.pow(Math.abs(d), exp) * scale, Math.abs(d))\n}\n\n// Radial band gravity: the Y axis's present band centres map to target radii\n// (smallest centre innermost) and each node's distance from the origin is\n// pulled toward its bucket's ring. Charge distributes nodes around the ring; X\n// has no meaning here and is ignored.\nfunction applyBandGravityRadial(\n active: SimNode[],\n params: GravityParams,\n frame: LayoutFrame,\n alpha: number,\n rng: Rng,\n): void {\n const gy = params.gravityY\n for (const node of active) {\n if (node.pinned) continue\n const targetR = frame.ringOf(node)\n const r = Math.hypot(node.x, node.y)\n if (r < 1e-6) {\n // No defined direction at the origin — give a tiny outward kick so the\n // ring pull has something to act on next tick. A node whose ring IS the\n // origin (no value on Y) is at rest there.\n if (targetR > 0) {\n node.vx += (rng() - 0.5)\n node.vy += (rng() - 0.5)\n }\n continue\n }\n const f = bandForce(r - targetR, params.gravityBandExp, alpha * gy)\n node.vx -= (node.x / r) * f\n node.vy -= (node.y / r) * f\n }\n}\n\n// Topological gravity. A single-sided inward pull whose strength scales with a\n// node's downstream-depth score, drawing the orchestrators atop deep call chains\n// toward the focal point while leaving leaves (score 0) untouched —\n// charge handles spreading those outward. Origin-relative, so it is independent\n// of the band layout. In the cartesian interpretation the focal point is \"up\",\n// toward the top of the world; in radial mode (focal = origin) it is the centre.\nconst TOPOLOGICAL_FOCAL_Y = -350\n\nexport function applyTopologicalGravity(\n active: SimNode[],\n params: GravityParams,\n alpha: number,\n scores?: Map<string, number>,\n): void {\n const gt = params.gravityTopological\n if (gt <= 0 || !scores) return\n const exp = params.gravityTopologicalExp\n const radial = params.radial\n for (const node of active) {\n if (node.pinned) continue\n const score = scores.get(node.id) ?? 0\n if (score <= 0) continue\n // Shape the depth score so the deepest roots dominate (score^exp keeps the\n // deepest root at full pull while mid/low scores fall off fast). Leaves\n // (score 0) feel nothing — charge spreads those outward.\n const weight = Math.pow(score, exp)\n if (radial) {\n // Focal point is the origin: well-connected nodes pulled inward.\n node.vx -= node.x * alpha * gt * weight\n node.vy -= node.y * alpha * gt * weight\n } else {\n // Focal point is \"up\" toward the top of the world.\n node.vy -= (node.y - TOPOLOGICAL_FOCAL_Y) * alpha * gt * weight\n }\n }\n}\n\n// Center gravity. A radial pull toward the world origin — the standard cohesion\n// force for force-directed charts. It is the baseline anchor when neither band\n// nor topological gravity is shaping the layout; off (strength 0) by default.\nexport function applyCenterGravity(active: SimNode[], params: GravityParams, alpha: number): void {\n const gc = params.gravityCenter\n if (gc <= 0) return\n for (const node of active) {\n if (node.pinned) continue\n node.vx -= node.x * alpha * gc\n node.vy -= node.y * alpha * gc\n }\n}\n","// What the controls offer: only what the post-filter visible set holds — a\n// push token per bucket some visible node falls in, a column per such\n// bucket on each layout axis, and a pull token per spring category some edge\n// the simulation SPRINGS falls in. That last set is the simulation's own\n// predicate (`sprungEdges`), not the graduated edges the canvas draws: a\n// graduated edge is re-pointed for drawing and never sprung, so a token\n// classified over it would tune nothing.\n\nimport type { GraphEdge } from '../../graph/model'\nimport type { PhysicsSubject, ResolvedPhysics } from '../../graph/forces'\nimport { isScalarAxis, presentBuckets, presentSpringCategories } from '../../graph/forces'\n\nexport interface PresentSets {\n readonly push: ReadonlySet<string>\n readonly bandX: ReadonlySet<string>\n readonly bandY: ReadonlySet<string>\n readonly springs: ReadonlySet<string>\n}\n\n/** The sets, from the visible nodes and the edges the simulation springs (`sprungEdges`). */\nexport function presentSetsOf(\n visibleNodes: readonly (PhysicsSubject & { id: string })[],\n sprung: readonly GraphEdge[],\n resolved: ResolvedPhysics,\n getNode: (id: string) => PhysicsSubject | undefined,\n): PresentSets {\n return {\n push: new Set(presentBuckets(visibleNodes, resolved.push)),\n bandX: isScalarAxis(resolved.bandX) ? new Set<string>() : new Set(presentBuckets(visibleNodes, resolved.bandX)),\n bandY: isScalarAxis(resolved.bandY) ? new Set<string>() : new Set(presentBuckets(visibleNodes, resolved.bandY)),\n springs: presentSpringCategories(resolved.spring, sprung, getNode),\n }\n}\n","// Force-directed simulation engine.\n\nimport type { GraphNode, GraphEdge, Graph } from './model'\nimport { nodesById, readableGraph } from './model'\nimport type { EdgeTypeId, SpringPhysics } from './shapes'\nimport {\n applyChargeForce,\n applyLinkForce,\n applyBandGravity,\n applyTopologicalGravity,\n applyCenterGravity,\n bandAxesActing,\n layoutFrame,\n presentSpringCategories,\n topologicalActing,\n resolvePhysics,\n resolveSprings,\n springFor,\n} from './forces'\nimport type { Rng } from './rng'\nimport type { EdgeCategory, LayoutFrame, LayoutSubject, ResolvedPhysics } from './forces'\nimport type { ResolvedScalarAxis, ScalarTuning } from './scalar'\nimport { isScalarTuning } from './scalar'\nimport type { AxisNamer, AxisId, AxisValue, AxisMapping } from './axis'\nimport { identityMapping, valuesOn } from './axis'\nimport type { Taxonomy } from './taxonomy'\n\n// The force helpers now live in ./forces; re-export them so existing consumers\n// (GraphCanvas, GraphControlPanel, ChargeControls) keep importing them here.\nexport {\n CORE_RADIUS_MIN,\n chargeFor,\n coreRadiusFor,\n bandFor,\n bandForKey,\n springFor,\n RADIAL_R_MIN,\n RADIAL_R_MAX,\n} from './forces'\n\nexport interface SimNode extends GraphNode {\n x: number\n y: number\n vx: number\n vy: number\n pinned: boolean\n}\n\n// The simulation's tunable parameters, split into the four force categories the\n// control panel exposes (Push / Pull / Gravity / Dynamics) and intersected into\n// `ForceParams` below. At runtime `ForceParams` is still a single flat object —\n// `keyof ForceParams`, the key-addressed control sliders/tokens, the per-type\n// lookup records, and the shallow merges in `Simulation` all keep working. The\n// split is purely at the type level: each force in ./forces takes only its\n// category's slice, so it cannot read params outside its own force.\n\n/** What the push force needs per bucket. */\nexport interface PushPhysics { charge: number; coreRadius: number }\n/** A rest band on one axis, as declared and as tuned. */\nexport interface BandRange { min: number; max: number }\n\n/**\n * Per-bucket tuning of one force along one axis.\n *\n * Three facts that must agree — which axis, how its values bucket, and the\n * per-bucket numbers — as ONE constructed value, so there is no second field\n * for them to disagree with: an axis named apart from its table admits a\n * table keyed by the previous axis, where every lookup misses and the layout\n * collapses in silence.\n *\n * `overrides` holds **only what the user changed**, keyed by bucket id. Every\n * other bucket resolves to the taxonomy's declared physics at read time\n * (`resolvePhysics`). That is what makes the shape a projection rather than a\n * copy: there is no dense table to go stale, and no provenance token to keep in\n * step, because nothing here duplicates the taxonomy. It also makes \"is this\n * value tuned?\" answerable — presence is the answer — and reset granular.\n *\n * The scalars beside this stay flat, so `keyof ForceParams` and the\n * key-addressed sliders are unchanged. Only the table fields changed shape.\n */\nexport interface AxisTuning<T> {\n /** Which axis, and how its values bucket. The mapping names its own axis. */\n readonly mapping: AxisMapping\n readonly overrides: ReadonlyMap<string, T>\n}\n\n/** Set one bucket's override, copy-on-write. */\nexport function withOverride<T>(tuning: AxisTuning<T>, bucketId: string, value: T): AxisTuning<T> {\n const overrides = new Map(tuning.overrides)\n overrides.set(bucketId, value)\n return { ...tuning, overrides }\n}\n\n/**\n * What a layout axis keys on: an axis mapping with its overrides, or a\n * computed scalar with its span. The narrowers are two guards\n * (`isScalarTuning`, `isScalarAxis`) and two loud casts for the callers that\n * hold the kind already (`mappedTuning`, `mappedAxis`).\n */\nexport type LayoutTuning = AxisTuning<BandRange> | ScalarTuning\n\n/** The mapped half of a layout tuning; loud where a caller expects buckets and meets a scalar. */\nexport function mappedTuning(t: LayoutTuning): AxisTuning<BandRange> {\n if (isScalarTuning(t)) throw new Error(`expected a mapped axis, got scalar ${JSON.stringify(t.scalar)}`)\n return t\n}\n\n/**\n * The two pair forces' tuning: ONE axis for both, and only what the user has\n * changed on each. Push and pull keying on different axes is a state the\n * domain has no meaning for (one dropdown drives both), so the type does\n * not admit it — the mapping is stored once, here.\n */\nexport interface PairTuning {\n readonly mapping: AxisMapping\n /** Per-bucket charge and core radius, only where the user changed it. */\n readonly push: ReadonlyMap<string, PushPhysics>\n /** Per-category stiffness and rest length, only where the user changed it. */\n readonly spring: ReadonlyMap<EdgeTypeId, SpringPhysics>\n}\n\n/** Both forces on an axis, nothing overridden. */\nexport function pairTuning(mapping: AxisMapping): PairTuning {\n return { mapping, push: new Map(), spring: new Map() }\n}\n\nexport function withPushOverride(pair: PairTuning, bucketId: string, value: PushPhysics): PairTuning {\n const push = new Map(pair.push)\n push.set(bucketId, value)\n return { ...pair, push }\n}\n\nexport function withSpringOverride(pair: PairTuning, id: EdgeTypeId, value: SpringPhysics): PairTuning {\n const spring = new Map(pair.spring)\n spring.set(id, value)\n return { ...pair, spring }\n}\n\n/**\n * The identity bucketing of the taxonomy's style axis — one bucket per declared\n * value. The style axis has no descriptor (it is not a filter), so its labels\n * come from the taxonomy rather than through `identityMapping`.\n */\nexport function styleIdentityMapping(taxonomy: Taxonomy): AxisMapping {\n return identityMapping({\n id: taxonomy.styleAxis(),\n label: taxonomy.styleLabel(),\n labelFor: value => taxonomy.styleOf(value).label,\n abbreviationFor: value => taxonomy.abbreviationFor(value),\n }, taxonomy.styleValues)\n}\n\n/** A single-axis tuning on the style axis, bucketed by identity, nothing overridden. */\nexport function identityTuning<T>(taxonomy: Taxonomy): AxisTuning<T> {\n return { mapping: styleIdentityMapping(taxonomy), overrides: new Map() }\n}\n\n/**\n * Seeding bias: how far below a band's centre, as a fraction of its height, a\n * node starts. Measured over 24 RNG seeds on three fixtures, 400 ticks each,\n * counting the nodes that settle outside their band: seeded at the centre of\n * two overlapping bands, the stress layout left the bottom tier wedged above\n * the tier over it in a third of the seeds (mean 11.9 outsiders, max 42),\n * because charge alone cannot sort interleaved tiers and the band force only\n * acts on nodes already outside their band. Seeded 0.35 of the band's height\n * below the centre, the tiers start pre-ordered and the force only has to\n * lift (mean 3.5, max 13). Uniform-within-band and a wider jitter were both\n * worse than the centre. The frame is unchanged by the bias, so seed and\n * force still agree; only where within the band a node starts moved.\n */\nexport const SEED_BIAS = 0.35\n\n/**\n * The bounded-token rule. A axis is usable for push/pull only through a\n * mapping small enough that one token per bucket (push) and one per spring\n * category present (pull) is still a control surface a person can use. Above\n * this the surface is unbounded.\n */\nexport const TOKEN_CEILING = 24\n\n/** One axis the push/pull forces could key on, and what it would cost to show. */\nexport interface AxisOption {\n readonly mapping: AxisMapping\n /** Push tokens — one per bucket. */\n readonly pushTokens: number\n /** Pull tokens — one per spring category some edge of the graph falls in. */\n readonly pullTokens: number\n}\n\nexport function fitsCeiling(option: AxisOption): boolean {\n return option.pushTokens <= TOKEN_CEILING && option.pullTokens <= TOKEN_CEILING\n}\n\n/**\n * Every axis push/pull can key on for this graph: the style axis bucketed by\n * identity, then each filter axis bucketed by the values the graph presents on\n * it. The token counts are measured, not estimated, so the ceiling is a number\n * the dropdown can show beside an axis it has to decline.\n */\nexport function axisOptions(taxonomy: Taxonomy, graph: Graph): AxisOption[] {\n const mappings: AxisMapping[] = [styleIdentityMapping(taxonomy)]\n for (const axis of nodeAxes(taxonomy, graph)) {\n const values = valuesOn(graph.nodes, axis)\n if (values.length > 0) mappings.push(identityMapping(namerFor(taxonomy, axis), values))\n }\n const byId = nodesById(graph.nodes)\n const nodeOf = (id: string) => byId.get(id)\n return mappings.map(mapping => ({\n mapping,\n pushTokens: mapping.buckets.length,\n pullTokens: presentSpringCategories(resolveSprings(pairTuning(mapping), taxonomy), graph.edges, nodeOf).size,\n }))\n}\n\n/**\n * Every axis the nodes carry other than the style axis: the declared filter\n * axes first, in declaration order, then any other axis a node has a value\n * on, in first-encounter order. An axis is a fact about the nodes, not a\n * declaration: a host that writes `term` on its nodes may push and pull on\n * it without declaring a filter for it.\n */\nfunction nodeAxes(taxonomy: Taxonomy, graph: Graph): AxisId[] {\n const style = taxonomy.styleAxis()\n const out: AxisId[] = []\n const seen = new Set<AxisId>([style])\n for (const d of taxonomy.filterAxes) {\n if (seen.has(d.id)) continue\n seen.add(d.id); out.push(d.id)\n }\n for (const node of graph.nodes) {\n for (const axis of Object.keys(node.values)) {\n if (seen.has(axis)) continue\n seen.add(axis); out.push(axis)\n }\n }\n return out\n}\n\n/** How an axis names itself and its values: its descriptor's way, or — for an axis no descriptor declares — the id and the value. */\nexport function namerFor(taxonomy: Taxonomy, axis: AxisId): AxisNamer {\n return taxonomy.axisFor(axis) ?? {\n id: axis,\n label: axis,\n labelFor: (v: AxisValue) => v,\n abbreviationFor: (v: AxisValue) => v.slice(0, 2),\n }\n}\n\n/** One choice for a layout axis: a mapping (with its cost) or a computed scalar. */\nexport type LayoutAxisOption =\n | { readonly kind: 'mapping'; readonly option: AxisOption }\n | { readonly kind: 'scalar'; readonly scalar: ResolvedScalarAxis }\n\n/** Every axis X or Y can key on: the mappings push/pull can (already measured), then every scalar the taxonomy knows. */\nexport function layoutAxisOptions(axes: readonly AxisOption[], taxonomy: Taxonomy): LayoutAxisOption[] {\n return [\n ...axes.map(option => ({ kind: 'mapping' as const, option })),\n ...taxonomy.scalars.map(scalar => ({ kind: 'scalar' as const, scalar })),\n ]\n}\n\n/**\n * The one spelling of a layout-axis choice, for the control that offers the\n * options and reads the current one back: what the dropdown's values are and\n * what the tuning answers to.\n */\nexport function layoutAxisKey(choice: LayoutTuning | LayoutAxisOption): string {\n if ('kind' in choice) return choice.kind === 'scalar' ? `scalar:${choice.scalar.id}` : `mapping:${choice.option.mapping.axis}`\n return isScalarTuning(choice) ? `scalar:${choice.scalar}` : `mapping:${choice.mapping.axis}`\n}\n\n/** Same axis, same buckets, same values — order-insensitive, since node order is not a fact about the graph. */\nfunction sameBuckets(a: AxisMapping, b: AxisMapping): boolean {\n const key = (m: AxisMapping) =>\n m.buckets.map(x => `${x.id}=${[...x.values].sort().join(',')}`).sort().join(';')\n return a.axis === b.axis && key(a) === key(b)\n}\n\n/**\n * The same parameters, re-bucketed for the graph and taxonomy they now face.\n * An identity mapping over observed values goes stale when a file is added or\n * removed; the style axis's goes stale when the taxonomy is swapped. Overrides\n * are keyed by value, so they survive where the value does. A tuning whose\n * buckets did not change comes back by reference, so a caller can tell nothing\n * moved. An axis the graph no longer carries, or one that has grown past the\n * token ceiling, falls back to the style axis with nothing overridden. A graph\n * with no nodes judges nothing: the parameters come back as they are, so a\n * layout restored before the data arrives waits for it.\n */\nexport function retunedFor(params: ForceParams, taxonomy: Taxonomy, graph: Graph): ForceParams {\n // A scalar the taxonomy does not know falls back whatever the graph holds:\n // the check reads only the taxonomy, so a stored layout naming a scalar\n // since renamed never reaches the kernels, even while the graph is empty.\n const knownScalar = (t: LayoutTuning): LayoutTuning =>\n isScalarTuning(t) && !taxonomy.scalars.some(sc => sc.id === t.scalar) ? identityTuning<BandRange>(taxonomy) : t\n if (graph.nodes.length === 0) {\n const bandX = knownScalar(params.bandX)\n const bandY = knownScalar(params.bandY)\n return bandX === params.bandX && bandY === params.bandY ? params : { ...params, bandX, bandY }\n }\n const style = taxonomy.styleAxis()\n const byId = nodesById(graph.nodes)\n const nodeOf = (id: string) => byId.get(id)\n const refreshed = (m: AxisMapping): AxisMapping | undefined => {\n if (m.axis === style) return styleIdentityMapping(taxonomy)\n const values = valuesOn(graph.nodes, m.axis)\n return values.length === 0 ? undefined : identityMapping(namerFor(taxonomy, m.axis), values)\n }\n // One rule for every tuning: re-bucket; fall back to the style axis with\n // nothing overridden when the axis vanished or outgrew its ceiling — said\n // on the console, since the control the user set it on now shows another\n // axis; keep the same object when the buckets did not change. What \"fits\"\n // means is the only thing that differs — buckets for a band axis, buckets\n // and present pairs for the pair axis — so it is the one thing passed in.\n const refresh = <T extends { mapping: AxisMapping }>(\n tuning: T, fresh: (mapping: AxisMapping) => T, fits: (mapping: AxisMapping) => boolean,\n ): T => {\n const next = refreshed(tuning.mapping)\n if (next === undefined || (next.axis !== style && !fits(next))) {\n fellBack(tuning.mapping.axis, next === undefined ? 'no node carries it any more' : `it has ${next.buckets.length} buckets, past the ceiling of ${TOKEN_CEILING}`)\n return fresh(styleIdentityMapping(taxonomy))\n }\n return sameBuckets(next, tuning.mapping) ? tuning : { ...tuning, mapping: next }\n }\n const bucketsFit = (mapping: AxisMapping) => mapping.buckets.length <= TOKEN_CEILING\n const pairFits = (mapping: AxisMapping) => fitsCeiling({\n mapping,\n pushTokens: mapping.buckets.length,\n pullTokens: presentSpringCategories(resolveSprings(pairTuning(mapping), taxonomy), graph.edges, nodeOf).size,\n })\n // A scalar axis has no buckets to go stale; it falls back only when the\n // taxonomy no longer knows the scalar (a swap), with the style identity.\n const refreshLayout = (tuning: LayoutTuning): LayoutTuning =>\n isScalarTuning(tuning)\n ? (taxonomy.scalars.some(sc => sc.id === tuning.scalar) ? tuning : identityTuning<BandRange>(taxonomy))\n : refresh(tuning, m => ({ mapping: m, overrides: new Map() }), bucketsFit)\n const pair = refresh(params.pair, pairTuning, pairFits)\n const bandX = refreshLayout(params.bandX)\n const bandY = refreshLayout(params.bandY)\n return pair === params.pair && bandX === params.bandX && bandY === params.bandY ? params : { ...params, pair, bandX, bandY }\n}\n\n// A fallback is said once per axis and reason: it happens on every retune\n// after the first, and the user has one control to look at, not a stream.\nconst FELL_BACK = new Set<string>()\nfunction fellBack(axis: AxisId, reason: string): void {\n const key = `${axis}\\u0000${reason}`\n if (FELL_BACK.has(key)) return\n FELL_BACK.add(key)\n console.warn(`[graph] the ${JSON.stringify(axis)} axis cannot be tuned on: ${reason}; the forces returned to the style axis with nothing overridden.`)\n}\n\n// PUSH — repulsion. Per-type charge strength and core radius, the masters that\n// scale them, and the charge falloff exponent.\nexport interface ChargeParams {\n /** The pair forces' shared axis and per-bucket overrides — see `PairTuning`. */\n pair: PairTuning\n\n pushMultiplier: number // scales all charge (repulsion) forces\n coreRadiusMultiplier: number // scales all per-type core radii (charge softening)\n chargeExponent: number // power of (dist² + softening) in charge falloff (1 = inverse-square)\n}\n\n// PULL — spring attraction. Per-edge-category stiffness (k) and rest length,\n// the masters that scale them, and the displacement exponent.\nexport interface LinkParams {\n /** The same value `ChargeParams` names: one axis for both pair forces. */\n pair: PairTuning\n\n pullMultiplier: number // scales all link (attraction) forces\n distanceMultiplier: number // scales all rest distances\n linkExponent: number // power of displacement in spring force (1 = linear/Hooke)\n}\n\n// GRAVITY — anchoring. Each layout axis carries per-bucket rest bands or a\n// scalar's points (no force\n// inside, pull toward the nearest edge outside), re-centred on the median of\n// the bands present so the stack sits on the origin without canvas-side\n// compensation. Plus the optional topological pull and the center baseline.\nexport interface GravityParams {\n /**\n * The two layout axes' rest bands — the same shape, each on its own mapping.\n * A strength of zero switches an axis off; when both are off and\n * topological gravity is too, center gravity is the baseline.\n */\n bandX: LayoutTuning\n bandY: LayoutTuning\n gravityX: number\n gravityY: number\n // Band falloff exponent applied to the displacement outside the rest band\n // (force = strength × d^exp). 1 = linear (Hooke); higher = softer just outside\n // the band, stiffer far out. Mirrors the Pull tab's linkExponent.\n gravityBandExp: number\n /**\n * Radial layout: the Y axis's bands become rings about the origin (uppermost\n * bucket innermost) and X is ignored; topological gravity's focal point moves\n * from the top to the centre. Entered from the Y axis's switch.\n */\n radial: boolean\n // Topological gravity strength: scales the single-sided inward pull on each\n // node by its (shaped) downstream-reach score. Default 0 (off).\n gravityTopological: number\n // Topological contrast: exponent applied to the reach score (score^exp) so\n // high-reach roots dominate. 1 = linear; higher = sharper tiering (only the\n // biggest hubs pull hard).\n gravityTopologicalExp: number\n // Center gravity strength: radial pull toward the world origin — the baseline\n // cohesion/anchor force used when neither band axis nor Topological is active.\n gravityCenter: number\n topologicalEnabled: boolean\n}\n\n// DYNAMICS — how the simulation cools and damps over time.\nexport interface DynamicsParams {\n alphaDecay: number\n alphaMin: number\n velocityDecay: number\n}\n\n// The full parameter set: a flat object that is the intersection of the four\n// force categories (so `keyof ForceParams` is the union of all category keys).\nexport type ForceParams = ChargeParams & LinkParams & GravityParams & DynamicsParams\n\n// Defaults are a hand-tuned baseline captured from interactive use across\n// the topic library and the example graphs. The model is intentionally\n// charge-dominant: repulsion does most of the layout work, the per-type Y\n// bands hold the hierarchy, and the link springs act as short tethers\n// rather than rigid rods. Concretely:\n//\n// - pushMultiplier 0.4 + chargeExponent 0.7 (acting on dist² + softening)\n// + per-type core radii (read off the charge map) produce strong,\n// softly-decaying repulsion that prevents node overlap and naturally\n// fans siblings apart. push is modest because the per-type charges\n// themselves are now large (container/host tiers ~800).\n// - pullMultiplier 0.6 + distanceMultiplier 0.1 means every spring is\n// ~6% of its old strength × ~10% of its old rest distance — enough to\n// keep connected nodes near each other, not enough to fight the\n// repulsion or the Y bands.\n// - gravityY 0.145 is roughly 2.5× its old strength; the Y bands now\n// carry the vertical structure of the layout. Bands are also wider\n// than before so each tier has room without crowding its neighbours.\n// - linkExponent stays at 1.0 (linear). A sub-linear shape was tested\n// and made the high-fan-out side dominate harder, not less.\n//\n// The per-category sliders all still work; these are starting points the\n// user can drag from. Ranges in GraphControlPanel.tsx are wide enough that\n// every default sits in a comfortable middle of its slider.\n/**\n * The starting parameters for a taxonomy.\n *\n * This was a module-load constant built from the shipped registry, which made\n * the engine's defaults a property of one domain: importing the simulation\n * imported that domain's charges, bands and spring categories, whether or not\n * the caller ever used them. Deriving them from the supplied taxonomy instead\n * is what lets a host bring its own values and get the same tuning surface.\n *\n * Note that this returns a **fresh** object per call, nested maps included. The\n * old constant was shared, and `{ ...DEFAULT_PARAMS }` copied only the top\n * level — so every \"copy\" aliased the same `charge`, `coreRadius`, `band`,\n * `link` and `dist` objects, and an in-place write to any of them would have\n * reached every other holder, including the module default itself. Nothing did\n * that today (the control panel rebuilds each map immutably), which is exactly\n * why it would have been a bad bug to acquire later rather than one to find.\n */\nexport function defaultParamsFor(taxonomy: Taxonomy): ForceParams {\n return {\n // Both default to the axis the taxonomy resolves style on, so the layout\n // keys on the same thing it is coloured by.\n pair: pairTuning(styleIdentityMapping(taxonomy)),\n bandX: identityTuning<BandRange>(taxonomy),\n bandY: identityTuning<BandRange>(taxonomy),\n\n // Gravity. The Y bands carry the vertical structure of the layout — strong\n // (gravityY = 0.145) and overlapping at the edges so adjacent tiers can\n // soften their boundary instead of forming a hard stripe. The X bands bound\n // the horizontal spread, gently (gravityX = 0.05): wide fan-outs may spread.\n gravityX: 0.05,\n gravityY: 0.145,\n gravityBandExp: 1,\n radial: false,\n // Off by default — the experiment lives behind a slider. When non-zero,\n // every node with a positive downstream score gets an additional pull\n // toward the focal point, layered additively on top of the bands.\n gravityTopological: 0,\n gravityTopologicalExp: 3,\n gravityCenter: 0.05,\n topologicalEnabled: false,\n\n // Dynamics. alphaMin is well below the d3 default (0.001) so the\n // simulation continues into its slow-cooling tail — layouts read as\n // \"settled\" rather than freezing while springs are still measurably\n // active. friction and cooling are stock.\n alphaDecay: 0.005,\n alphaMin: 0.0001,\n velocityDecay: 0.4,\n\n pushMultiplier: 0.4,\n pullMultiplier: 0.6,\n distanceMultiplier: 0.1,\n coreRadiusMultiplier: 1.0,\n chargeExponent: 0.7, // acts on (dist² + softening); = old 1.4 with the /2 folded in\n linkExponent: 1.0,\n }\n}\n\n// Stability bounds — defenses against numerical instability cascades.\n//\n// MAX_VELOCITY: per-tick cap on a node's speed (world units / tick). A healthy\n// simulation produces speeds well below this; the cap exists only to break\n// runaway cascades where two nodes briefly overlap, get hit with a singular\n// force, fly apart, collide with a third, and so on. Picked an order of\n// magnitude larger than typical equilibrium speeds and an order of magnitude\n// smaller than what causes visible \"teleport\" frames.\n//\n// MAX_POSITION: hard bound on world coordinates. Final safety net so the canvas\n// 2D context never receives non-finite or astronomically-large coords (which\n// can OOM the GPU command buffer and crash the renderer).\nconst MAX_VELOCITY = 50\nconst MAX_POSITION = 1e6\n\n// REHEAT_TICKS: the fixed settle window (in ticks) that a `reheat()` cools over,\n// regardless of how hard it kicks. Cooling normally subtracts a constant\n// `alphaDecay` each tick, so a bigger kick would linger proportionally longer\n// (alpha 10 ÷ decay 0.005 ≈ 2000 ticks of jitter). Instead, `reheat()` derives a\n// per-burst decay of `alpha / REHEAT_TICKS`, so a stronger kick moves nodes\n// *further* within the same window rather than dragging the tail out *longer*.\nconst REHEAT_TICKS = 200\n\n/**\n * The edges a visible set springs: the graph's own, with both ends visible.\n * A graduated edge (re-pointed for drawing across a hidden node) is never\n * sprung — so the controls read this same predicate to offer pull tokens\n * (`components/graph-view/present.ts`), and nothing else restates it.\n */\nexport function sprungEdges(edges: readonly GraphEdge[], visibleIds: ReadonlySet<string>): GraphEdge[] {\n return edges.filter(e => visibleIds.has(e.sourceId) && visibleIds.has(e.targetId))\n}\n\nexport class Simulation {\n nodes: SimNode[]\n edges: readonly GraphEdge[]\n params: ForceParams\n alpha: number\n\n // Per-burst cooling rate set by `reheat()` so the settle window stays fixed\n // (~REHEAT_TICKS) no matter the kick size. Null means \"cool at the normal\n // alphaDecay\" — used during steady-state and live tuning via `nudge()`.\n private reheatDecay: number | null = null\n\n private nodeMap: Map<string, SimNode>\n\n // Source of randomness for the degenerate-frame breakers and the position\n // jitter. Defaults to the platform generator, so app behaviour is unchanged.\n private rng: Rng\n\n // The taxonomy the link force resolves spring categories through. Held rather\n // than imported so the engine is not bound to one set of edge categories.\n private taxonomy: Taxonomy\n\n // The per-bucket tables, resolved once from (params, taxonomy) and read by the\n // kernels per pair / per node. Recomputed only when either input changes —\n // never per tick, and never inside a loop.\n private resolved: ResolvedPhysics\n // The frame the band forces act in — median centres and ring radii over the\n // WHOLE node set — shared by seeding, the kernels and (through the view) the\n // overlay, so they agree by construction. Recomputed with the tables.\n private frame: LayoutFrame\n // Each edge's spring category, classified once per (edges, resolved) rather\n // than per tick: an edge's category depends on the edge, its endpoints'\n // buckets and the pair mapping, none of which change between ticks. Built on\n // first use and dropped whenever the tables are re-resolved.\n private springByEdge: Map<string, EdgeCategory> | null = null\n\n // `taxonomy` is REQUIRED, deliberately: a default here would let one\n // construction site resolve through a taxonomy every component ignores,\n // a split no gate can see because both halves are individually correct.\n constructor(\n graph: Graph,\n params: Partial<ForceParams> | undefined,\n rng: Rng,\n taxonomy: Taxonomy,\n // Where a node starts when something already knows: a layout the host\n // kept, or the previous simulation's. A node with an entry is not seeded.\n positions?: ReadonlyMap<string, { x: number; y: number }>,\n ) {\n this.params = { ...defaultParamsFor(taxonomy), ...params }\n this.alpha = 1.0\n this.rng = rng\n this.taxonomy = taxonomy\n this.resolved = resolvePhysics(this.params, taxonomy)\n\n // Seed each node in the frame the forces act in. Cartesian: inside its\n // rest band on both axes, re-centred as the force re-centres them, and\n // biased toward the band's bottom edge by SEED_BIAS — centred seeding left a\n // third of the stress layouts with the bottom tier wedged above the tier\n // over it; a seed that pre-orders the tiers lets the band force lift rather\n // than sort (measured over 24 seeds). Radial: on the bucket's\n // ring at a random angle, children near their parent's angle. A node in no\n // bucket sits at the axis's centre, or at the origin.\n this.nodes = []\n this.nodeMap = new Map()\n const seedNodes = graph.nodes\n this.frame = layoutFrame(readableGraph(graph), this.resolved)\n for (const node of seedNodes) {\n const given = positions?.get(node.id)\n const [x, y] = given ? [given.x, given.y] : this.seedPosition(node, rng)\n const sim: SimNode = { ...node, x, y, vx: 0, vy: 0, pinned: false }\n this.nodes.push(sim)\n this.nodeMap.set(sim.id, sim)\n }\n\n // Children sit near their parent — same X (cartesian, then clamped into\n // the child's own X band) or same angle (radial) — so they stack under\n // their container; the other coordinate stays the child's own.\n for (const node of this.nodes) {\n if (!node.parentId || positions?.has(node.id)) continue\n const parent = this.nodeMap.get(node.parentId)\n if (!parent) continue\n if (this.params.radial) {\n const r = Math.hypot(node.x, node.y)\n const theta = Math.atan2(parent.y, parent.x) + (rng() - 0.5) * 0.3\n node.x = r * Math.cos(theta)\n node.y = r * Math.sin(theta)\n } else {\n const bx = this.axisBand(node, 'x')\n node.x = Math.min(Math.max(parent.x + (rng() - 0.5) * 20, bx.lo), bx.hi)\n }\n }\n\n this.edges = graph.edges\n }\n\n getNode(id: string): SimNode | undefined {\n return this.nodeMap.get(id)\n }\n\n // A node's rest interval on one axis in the frame — a re-centred band, a\n // scalar's point, or the centre itself for a node with no value there.\n private axisBand(node: LayoutSubject, axis: 'x' | 'y'): { lo: number; hi: number } {\n return this.frame.rest(axis, node)\n }\n\n private seedPosition(node: LayoutSubject, rng: Rng): [number, number] {\n if (this.params.radial) {\n const r = this.frame.ringOf(node)\n const theta = rng() * Math.PI * 2\n return [r * Math.cos(theta), r * Math.sin(theta)]\n }\n const bx = this.axisBand(node, 'x')\n const by = this.axisBand(node, 'y')\n const xJitter = Math.max(40, (bx.hi - bx.lo) * 0.7)\n const yJitter = Math.max(20, (by.hi - by.lo) / 2)\n return [\n (bx.lo + bx.hi) / 2 + (rng() - 0.5) * xJitter,\n (by.lo + by.hi) / 2 + (by.hi - by.lo) * SEED_BIAS + (rng() - 0.5) * yJitter,\n ]\n }\n\n tick(visibleIds?: Set<string>, downstreamScores?: Map<string, number>): void {\n if (this.alpha < this.params.alphaMin) return\n\n const active = visibleIds\n ? this.nodes.filter(n => visibleIds.has(n.id))\n : this.nodes\n\n const activeEdges = visibleIds ? sprungEdges(this.edges, visibleIds) : this.edges\n\n // Forces — each scaled by the current alpha, mutating node velocities.\n // (Implemented in ./forces, one function per category.) Center gravity is\n // the baseline when nothing else anchors: no band axis acting (mode-aware —\n // in radial only Y acts) and topological off or at zero strength.\n applyChargeForce(active, this.params, this.resolved, this.alpha, this.rng)\n applyLinkForce(activeEdges, this.nodeMap, this.params, this.edgeCategory, this.alpha, this.rng)\n const bands = bandAxesActing(this.params)\n const topo = topologicalActing(this.params)\n if (bands) applyBandGravity(active, this.params, this.frame, this.alpha, this.rng)\n if (topo) applyTopologicalGravity(active, this.params, this.alpha, downstreamScores)\n if (!bands && !topo) {\n applyCenterGravity(active, this.params, this.alpha)\n }\n\n // Apply velocity and damping with stability guards.\n //\n // Three layered defenses against numerical instability cascades:\n // 1. NaN/Infinity guard — if any force computation produced a non-finite\n // value (e.g. division by an unexpected zero), reset that component\n // to zero rather than let it propagate into positions and reach the\n // canvas (where NaN/Infinity coords can crash the renderer).\n // 2. Per-tick velocity clamp — explicit \"node movement limit\" that\n // breaks runaway cascades. A healthy sim never hits this; if it does,\n // we've absorbed a singular force without letting it ricochet.\n // 3. Position hard bound — final safety net so positions can never\n // reach values that crash compositing.\n const decay = this.params.velocityDecay\n const maxV2 = MAX_VELOCITY * MAX_VELOCITY\n for (const node of active) {\n if (node.pinned) continue\n\n node.vx *= decay\n node.vy *= decay\n\n if (!Number.isFinite(node.vx)) node.vx = 0\n if (!Number.isFinite(node.vy)) node.vy = 0\n\n const v2 = node.vx * node.vx + node.vy * node.vy\n if (v2 > maxV2) {\n const s = MAX_VELOCITY / Math.sqrt(v2)\n node.vx *= s\n node.vy *= s\n }\n\n node.x += node.vx\n node.y += node.vy\n\n if (!Number.isFinite(node.x)) node.x = 0\n if (!Number.isFinite(node.y)) node.y = 0\n if (node.x < -MAX_POSITION) node.x = -MAX_POSITION\n else if (node.x > MAX_POSITION) node.x = MAX_POSITION\n if (node.y < -MAX_POSITION) node.y = -MAX_POSITION\n else if (node.y > MAX_POSITION) node.y = MAX_POSITION\n }\n\n // Cool. A `reheat()` burst cools at its own bounded rate so the settle\n // window stays fixed; once it lands back at rest, drop back to the normal\n // alphaDecay for any future steady-state warming.\n const cool = this.reheatDecay ?? this.params.alphaDecay\n this.alpha = Math.max(this.alpha - cool, 0)\n if (this.alpha <= this.params.alphaMin) this.reheatDecay = null\n }\n\n // Strong, bounded-duration kick. A bigger `alpha` rearranges the layout\n // further but settles in the same ~REHEAT_TICKS window (decay scales with the\n // kick) rather than lingering proportionally longer.\n reheat(alpha = 0.5): void {\n this.alpha = alpha\n this.reheatDecay = alpha / REHEAT_TICKS\n }\n\n // Raise alpha to at least `target` without ever cooling a hotter sim.\n // This is the physics primitive behind the control panel's \"keep the\n // layout warm while tuning\" hook: a parameter edit re-energises the\n // simulation just enough to show its effect, but a still-hot sim is\n // left alone so dragging a slider during settling doesn't dampen it.\n nudge(target = 0.3): void {\n if (this.alpha < target) this.alpha = target\n // Live tuning cools at the normal rate — clear any leftover reheat burst so\n // a prior strong kick's fast decay doesn't dampen the warm-while-editing feel.\n this.reheatDecay = null\n }\n\n isStable(): boolean {\n return this.alpha < this.params.alphaMin\n }\n\n pinNode(id: string, x: number, y: number): void {\n const node = this.nodeMap.get(id)\n if (node) {\n node.pinned = true\n node.x = x\n node.y = y\n node.vx = 0\n node.vy = 0\n }\n }\n\n unpinNode(id: string): void {\n const node = this.nodeMap.get(id)\n if (node) {\n node.pinned = false\n }\n }\n\n setParams(params: Partial<ForceParams>): void {\n Object.assign(this.params, params)\n // A patch may replace a whole tuning; the dense tables and the per-edge\n // classification follow it.\n this.resolved = resolvePhysics(this.params, this.taxonomy)\n this.frame = layoutFrame(this, this.resolved)\n this.springByEdge = null\n }\n\n private classifyEdges(): Map<string, EdgeCategory> {\n const byEdge = new Map<string, EdgeCategory>()\n for (const edge of this.edges) {\n const src = this.nodeMap.get(edge.sourceId)\n const tgt = this.nodeMap.get(edge.targetId)\n if (src && tgt) byEdge.set(edge.id, springFor(this.resolved.spring, edge, src, tgt))\n }\n return byEdge\n }\n\n // Loud on a miss: every edge the simulation owns is classified above, so an\n // absent entry means the cache and the edge list have come apart.\n private edgeCategory = (edge: GraphEdge): EdgeCategory => {\n const cats = this.springByEdge ?? (this.springByEdge = this.classifyEdges())\n const cat = cats.get(edge.id)\n if (cat === undefined) throw new Error(`edge ${JSON.stringify(edge.id)} has no spring classification; the cache and the edge list have come apart`)\n return cat\n }\n\n // Seed a node at a given position (used when new nodes appear during a\n // level-toggle transition): the point, snapped into the frame the forces act\n // in — both rest bands in cartesian, the bucket's ring in radial — so the\n // structure is honoured immediately.\n seedAt(id: string, x: number, y: number): void {\n const node = this.nodeMap.get(id)\n if (!node) return\n let sx: number\n let sy: number\n if (this.params.radial) {\n const r = this.frame.ringOf(node)\n const theta = Math.atan2(y, x)\n sx = r * Math.cos(theta)\n sy = r * Math.sin(theta)\n } else {\n const bx = this.axisBand(node, 'x')\n const by = this.axisBand(node, 'y')\n sx = Math.min(Math.max(x, bx.lo), bx.hi)\n sy = Math.min(Math.max(y, by.lo), by.hi)\n }\n node.x = sx + (this.rng() - 0.5) * 10\n node.y = sy + (this.rng() - 0.5) * 10\n node.vx = 0\n node.vy = 0\n }\n}\n","// Render-time node scaling.\n//\n// Nodes are drawn in screen pixels. Without scaling they stay a fixed size at\n// every zoom, so a zoomed-out graph collapses into a chaotic pile of full-size\n// dots. The on-screen size is `baseR × baseMul × clamp(zoom, minZoomMul,\n// maxZoomMul)`:\n// - baseMul overall size knob (1 = the style's declared size).\n// - maxZoomMul ceiling — once the user zooms past this, nodes stop growing.\n// Lower it to make nodes start shrinking sooner as you zoom out.\n// - minZoomMul floor — how small nodes may get when zoomed all the way out.\n// These are user-tunable from the Controls panel (Misc → Node scaling).\n//\n// Nothing here knows what a node *is*; it scales a radius against a zoom level.\n// It lives apart from the taxonomy shapes for that reason — the two were in one\n// file only because the sizes happened to be declared next to the registry.\n\nexport interface NodeScaleParams {\n baseMul: number\n minZoomMul: number\n maxZoomMul: number\n}\n\nexport const DEFAULT_NODE_SCALE: NodeScaleParams = {\n baseMul: 0.6,\n minZoomMul: 0.4,\n maxZoomMul: 1.65,\n}\n\n/** On-screen size multiplier for a node body/label at the given zoom. */\nexport function nodeSizeMul(scale: number, p: NodeScaleParams): number {\n return p.baseMul * Math.max(p.minZoomMul, Math.min(p.maxZoomMul, scale))\n}\n","// The layout state opened: what the view reads and writes, and the JSON\n// readers that build the engine's tunings. Nothing here is on the surface:\n// `ForceParams` and the force kernels are not part of any surface (DESIGN.md,\n// \"The forces\"), and `layout.ts` — the public half — calls in by value only,\n// so its declaration reaches none of this.\n\nimport type { AxisId } from './axis'\nimport { identityMapping } from './axis'\nimport { isFiniteNumber, isPlainRecord } from '../json'\nimport type { LayoutJSON, LayoutState, LayoutTuningJSON } from './layout'\nimport type { NodeScaleParams } from './node-scale'\nimport { DEFAULT_NODE_SCALE } from './node-scale'\nimport { isScalarTuning } from './scalar'\nimport type { SpringPhysics } from './shapes'\nimport type { BandRange, ForceParams, LayoutTuning, PairTuning, PushPhysics } from './simulation'\nimport { defaultParamsFor, namerFor, styleIdentityMapping } from './simulation'\nimport type { Taxonomy } from './taxonomy'\n\n/** The layout opened: the forces and the node scale. */\nexport interface Layout extends LayoutState {\n readonly forces: ForceParams\n readonly nodeScale: NodeScaleParams\n}\n\n/** A layout from its parts — the one place the brand is asserted: every `LayoutState` a host holds was made here. */\nexport function makeLayout(forces: ForceParams, nodeScale: NodeScaleParams): Layout {\n return { forces, nodeScale } as Layout\n}\n\n/** The layout opened for the view. Total: every `LayoutState` is one `makeLayout` made. */\nexport function openLayout(layout: LayoutState): Layout {\n return layout as Layout\n}\n\n/** The layout a view starts from: the taxonomy's default forces and the default node scale. */\nexport function defaultLayoutFor(taxonomy: Taxonomy): Layout {\n return makeLayout(defaultParamsFor(taxonomy), DEFAULT_NODE_SCALE)\n}\n\nconst NUMBER_FIELDS = [\n 'pushMultiplier', 'coreRadiusMultiplier', 'chargeExponent',\n 'pullMultiplier', 'distanceMultiplier', 'linkExponent',\n 'gravityX', 'gravityY', 'gravityBandExp', 'gravityTopological', 'gravityTopologicalExp', 'gravityCenter',\n 'alphaDecay', 'alphaMin', 'velocityDecay',\n] as const\nconst BOOLEAN_FIELDS = ['radial', 'topologicalEnabled'] as const\nconst SCALE_FIELDS = ['baseMul', 'minZoomMul', 'maxZoomMul'] as const\n\n/** The forces as plain data: every number and switch, each tuning by its axis or scalar, each override by its bucket. */\nexport function forcesToJSON(forces: ForceParams): NonNullable<LayoutJSON['forces']> {\n const tuning = (t: LayoutTuning): LayoutTuningJSON =>\n isScalarTuning(t)\n ? { scalar: t.scalar, ...(t.span ? { span: { ...t.span } } : {}) }\n : { axis: t.mapping.axis, ...(t.overrides.size > 0 ? { overrides: Object.fromEntries(t.overrides) } : {}) }\n const out: NonNullable<LayoutJSON['forces']> = {\n pair: {\n axis: forces.pair.mapping.axis,\n ...(forces.pair.push.size > 0 ? { push: Object.fromEntries(forces.pair.push) } : {}),\n ...(forces.pair.spring.size > 0 ? { spring: Object.fromEntries(forces.pair.spring) } : {}),\n },\n bandX: tuning(forces.bandX),\n bandY: tuning(forces.bandY),\n }\n for (const key of NUMBER_FIELDS) out[key] = forces[key]\n for (const key of BOOLEAN_FIELDS) out[key] = forces[key]\n return out\n}\n\n/** The forces a stored record names, over `base`; undefined when a part present is not its shape. */\nexport function forcesFromJSON(value: unknown, base: ForceParams, taxonomy: Taxonomy): ForceParams | undefined {\n if (!isPlainRecord(value)) return undefined\n const forces = { ...base }\n for (const key of NUMBER_FIELDS) {\n const v = value[key]\n if (v === undefined) continue\n if (!isFiniteNumber(v)) return undefined\n forces[key] = v\n }\n for (const key of BOOLEAN_FIELDS) {\n const v = value[key]\n if (v === undefined) continue\n if (typeof v !== 'boolean') return undefined\n forces[key] = v\n }\n if (value.pair !== undefined) {\n const pair = pairFromJSON(value.pair, taxonomy)\n if (!pair) return undefined\n forces.pair = pair\n }\n for (const key of ['bandX', 'bandY'] as const) {\n if (value[key] === undefined) continue\n const tuning = tuningFromJSON(value[key], taxonomy)\n if (!tuning) return undefined\n forces[key] = tuning\n }\n return forces\n}\n\n/** The node scale a stored record names, over `base`; undefined when a field present is not a number. */\nexport function nodeScaleFromJSON(value: unknown, base: NodeScaleParams): NodeScaleParams | undefined {\n if (!isPlainRecord(value)) return undefined\n const nodeScale = { ...base }\n for (const key of SCALE_FIELDS) {\n const v = value[key]\n if (v === undefined) continue\n if (!isFiniteNumber(v)) return undefined\n nodeScale[key] = v\n }\n return nodeScale\n}\n\n/** The mapping a stored axis names: the style axis's from the taxonomy; another axis's without buckets, for the view to complete from the graph. */\nfunction mappingFor(taxonomy: Taxonomy, axis: AxisId) {\n return axis === taxonomy.styleAxis() ? styleIdentityMapping(taxonomy) : identityMapping(namerFor(taxonomy, axis), [])\n}\n\nfunction pairFromJSON(value: unknown, taxonomy: Taxonomy): PairTuning | undefined {\n if (!isPlainRecord(value) || typeof value.axis !== 'string') return undefined\n const push = new Map<string, PushPhysics>()\n if (value.push !== undefined) {\n if (!isPlainRecord(value.push)) return undefined\n for (const [bucket, physics] of Object.entries(value.push)) {\n if (!isPlainRecord(physics) || !isFiniteNumber(physics.charge) || !isFiniteNumber(physics.coreRadius)) return undefined\n push.set(bucket, { charge: physics.charge, coreRadius: physics.coreRadius })\n }\n }\n const spring = new Map<string, SpringPhysics>()\n if (value.spring !== undefined) {\n if (!isPlainRecord(value.spring)) return undefined\n for (const [id, physics] of Object.entries(value.spring)) {\n if (!isPlainRecord(physics) || !isFiniteNumber(physics.strength) || !isFiniteNumber(physics.distance)) return undefined\n spring.set(id, { strength: physics.strength, distance: physics.distance })\n }\n }\n return { mapping: mappingFor(taxonomy, value.axis), push, spring }\n}\n\nfunction tuningFromJSON(value: unknown, taxonomy: Taxonomy): LayoutTuning | undefined {\n if (!isPlainRecord(value)) return undefined\n if (typeof value.scalar === 'string') {\n if (value.span === undefined) return { scalar: value.scalar }\n if (!isPlainRecord(value.span) || !isFiniteNumber(value.span.zero) || !isFiniteNumber(value.span.one)) return undefined\n return { scalar: value.scalar, span: { zero: value.span.zero, one: value.span.one } }\n }\n if (typeof value.axis !== 'string') return undefined\n const overrides = new Map<string, BandRange>()\n if (value.overrides !== undefined) {\n if (!isPlainRecord(value.overrides)) return undefined\n for (const [bucket, band] of Object.entries(value.overrides)) {\n if (!isPlainRecord(band) || !isFiniteNumber(band.min) || !isFiniteNumber(band.max)) return undefined\n overrides.set(bucket, { min: band.min, max: band.max })\n }\n }\n return { mapping: mappingFor(taxonomy, value.axis), overrides }\n}\n\n/** Whether two layouts say the same thing — every tuning, override, number and switch — so a host that re-reads its layout from a store each render is not read as a change. */\nexport function layoutsEqual(a: LayoutState, b: LayoutState): boolean {\n if (a === b) return true\n const key = (l: LayoutState) => { const o = openLayout(l); return JSON.stringify([forcesToJSON(o.forces), o.nodeScale]) }\n return key(a) === key(b)\n}\n","// Viewport transform for the graph canvas.\n// Converts between world coordinates (node positions) and screen coordinates (canvas pixels).\n\nimport type { SimNode } from './simulation'\n\nexport interface Viewport {\n x: number // translation (world offset) in screen pixels\n y: number\n scale: number // zoom level (1 = 100%)\n}\n\nexport const DEFAULT_VIEWPORT: Viewport = { x: 0, y: 0, scale: 1 }\n\n// Zoom bounds. The upper bound is generous so dense clusters can be spread far\n// enough apart on screen to reveal every node's label (labels hide when they'd\n// overlap a neighbour or its node — see GraphCanvas).\nexport const MIN_ZOOM = 0.1\nexport const MAX_ZOOM = 40\n\nexport function worldToScreen(vp: Viewport, wx: number, wy: number): [number, number] {\n return [wx * vp.scale + vp.x, wy * vp.scale + vp.y]\n}\n\nexport function screenToWorld(vp: Viewport, sx: number, sy: number): [number, number] {\n return [(sx - vp.x) / vp.scale, (sy - vp.y) / vp.scale]\n}\n\n// Zoom centered on a screen point\nexport function zoomAt(vp: Viewport, sx: number, sy: number, factor: number): Viewport {\n const newScale = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, vp.scale * factor))\n // Adjust translation so the point under the cursor stays fixed\n const wx = (sx - vp.x) / vp.scale\n const wy = (sy - vp.y) / vp.scale\n return {\n scale: newScale,\n x: sx - wx * newScale,\n y: sy - wy * newScale,\n }\n}\n\nexport interface PaneSize {\n width: number\n height: number\n}\n\n// Keep the world point under the pane's centre fixed across a resize from\n// `prev` to `next`: the centre moves by half the size change, so the\n// translation follows it. The first measurement (no `prev`) and a zero-sized\n// pane on either side (hidden by its host) leave the camera where it is.\nexport function followResize(vp: Viewport, prev: PaneSize | null, next: PaneSize): Viewport {\n if (!prev?.width || !prev.height || !next.width || !next.height) return vp\n const dw = next.width - prev.width, dh = next.height - prev.height\n return dw || dh ? { ...vp, x: vp.x + dw / 2, y: vp.y + dh / 2 } : vp\n}\n\n// Compute viewport that frames all given nodes with padding\nexport function fitToView(\n nodes: SimNode[],\n width: number,\n height: number,\n padding = 60,\n): Viewport {\n if (nodes.length === 0) return { x: width / 2, y: height / 2, scale: 1 }\n\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n for (const n of nodes) {\n if (n.x < minX) minX = n.x\n if (n.x > maxX) maxX = n.x\n if (n.y < minY) minY = n.y\n if (n.y > maxY) maxY = n.y\n }\n\n const graphW = maxX - minX || 1\n const graphH = maxY - minY || 1\n const availW = width - padding * 2\n const availH = height - padding * 2\n\n const scale = Math.min(availW / graphW, availH / graphH, 2) // cap at 2x\n const cx = (minX + maxX) / 2\n const cy = (minY + maxY) / 2\n\n return {\n scale,\n x: width / 2 - cx * scale,\n y: height / 2 - cy * scale,\n }\n}\n","// Wheel zoom about the pointer, for the graph canvas.\n\nimport { zoomAt } from '../../graph/viewport'\nimport type { Viewport } from '../../graph/viewport'\n\n/**\n * Zooms the camera on each wheel event over `canvas`; returns the cleanup.\n * The listener is native and non-passive: React's root wheel listener is\n * passive, so its preventDefault cannot keep the page from scrolling.\n */\nexport function listenWheelZoom(\n canvas: HTMLElement,\n viewport: () => Viewport,\n onViewportChange: (vp: Viewport) => void,\n): () => void {\n const onWheel = (e: WheelEvent) => {\n e.preventDefault()\n const rect = canvas.getBoundingClientRect()\n onViewportChange(zoomAt(viewport(), e.clientX - rect.left, e.clientY - rect.top, e.deltaY < 0 ? 1.15 : 0.85))\n }\n canvas.addEventListener('wheel', onWheel, { passive: false })\n return () => canvas.removeEventListener('wheel', onWheel)\n}\n","// Canvas-based graph renderer with mouse interaction.\n\nimport { copyKeyOf, isContainment } from '../graph/model'\nimport React from 'react'\nimport type { GraphEdge } from '../graph/model'\nimport type { ForceParams, SimNode } from '../graph/simulation'\nimport { bucketOf } from '../graph/forces'\nimport type { BandOverlay } from '../graph/forces'\nimport type { ResolvedPhysics } from '../graph/forces'\nimport { chargeFor, coreRadiusFor, springFor } from '../graph/simulation'\nimport type { Viewport } from '../graph/viewport'\nimport { screenToWorld, worldToScreen } from '../graph/viewport'\nimport { nodeSizeMul, type NodeScaleParams } from '../graph/node-scale'\nimport type { Taxonomy } from '../graph/taxonomy'\nimport { useTaxonomy } from './graph-view/useTaxonomy'\nimport { useLatest } from './graph-view/useControllable'\nimport { listenWheelZoom } from './graph-view/wheelZoom'\nimport type { EdgeStroke, Theme } from '../graph/scheme'\nimport { NEUTRAL_STROKE } from '../graph/shapes'\nimport { withAlpha } from '../graph/scheme'\nimport type { ForceSection } from './GraphControlPanel'\nimport type { GravityHover } from './GravityControls'\n\nconst FOCUS_RING_COLOR = '#4A90D9'\nconst SELECTION_RING_COLOR = '#FFFFFF'\nconst DIM_ALPHA = 0.2\n// A gentler dim floor used while the decomposition group glow is active. The\n// glow already pulls the eye to the grouped nodes, so the full 0.2 dim on the\n// rest makes them illegible — a softer fade keeps the un-grouped context\n// readable without losing the hover/search focus effect when no glow is shown.\nconst DIM_ALPHA_GROUPS = 0.5\n\n// Decomposition group glow (drawn behind everything). A member's halo extends\n// GROUP_GLOW_PAD screen px beyond its node body so adjacent members' halos\n// overlap and fuse.\nconst GROUP_GLOW_PAD = 16\nconst GROUP_GLOW_CENTER_ALPHA = 0.42\nconst GROUP_GLOW_EDGE_ALPHA = 0.16\nconst GROUP_GLOW_EDGE_WIDTH = 9\n// A focused group (strength > 1 — node hover/select or modal hover) doesn't just\n// brighten, it SWELLS: its halo radius and corridor width grow so the group lifts\n// out of a field of faint ones. This is the fraction of the strength boost applied\n// to size (0 = brighten only, 1 = size grows in lockstep with alpha).\nconst GROUP_GLOW_FOCUS_SWELL = 0.6\nfunction glowSwell(strength: number): number {\n return 1 + Math.max(0, strength - 1) * GROUP_GLOW_FOCUS_SWELL\n}\n\n// A marked edge's band, behind its own stroke so the edge keeps its type's\n// colour and dash: dashed in the label ink, the one colour no glow hue takes.\nconst MARK_DASH = [6, 4]\nconst MARK_WIDTH = 6\nconst MARK_ALPHA = 0.55\n\nconst ICON_FONT_FAMILY = '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif'\nconst LABEL_FONT_PX = 11\n// Slight translucency on node labels so they don't read as the only bright\n// text on the dark canvas and edges remain visible through them.\nconst LABEL_ALPHA = 0.78\n\n// Label truncation: names up to LABEL_MAX_CHARS are shown in full; anything\n// longer is shortened to LABEL_TRUNC_CHARS characters plus an ellipsis (so a\n// truncated label always shows at least LABEL_TRUNC_CHARS characters).\nconst LABEL_MAX_CHARS = 15\nconst LABEL_TRUNC_CHARS = 12\nfunction labelText(name: string): string {\n return name.length > LABEL_MAX_CHARS ? name.slice(0, LABEL_TRUNC_CHARS) + '\\u2026' : name\n}\n\n// Build the node-label font string at a given pixel size. Semibold (600) so\n// labels read against the canvas background and over edges without needing a\n// halo/outline.\nfunction labelFont(px: number): string {\n return `600 ${px.toFixed(1)}px ${ICON_FONT_FAMILY}`\n}\n\n// Badges: a host's short texts drawn as a row of chips above the node, as\n// given — a cardinality, a tag, a count. A node with none draws without the\n// row.\nconst BADGE_FONT_PX = 9\nconst BADGE_PAD_X = 4 // horizontal text padding inside a chip\nconst BADGE_PAD_Y = 2 // vertical text padding inside a chip\nconst BADGE_GAP = 3 // gap between adjacent chips\nconst BADGE_NODE_GAP = 4 // gap between the node's top edge and the chip row\n// Violet chip — deliberately outside the node-type palette so a cardinality\n// badge never reads as a node body of some type.\nconst BADGE_FILL = '#7C3AED'\nconst BADGE_TEXT_COLOR = '#FFFFFF'\nfunction badgeFont(px: number): string {\n return `600 ${px.toFixed(1)}px ${ICON_FONT_FAMILY}`\n}\n// Rounded-rect path helper (arcTo keeps it compatible without relying on\n// CanvasRenderingContext2D.roundRect being present).\nfunction roundRectPath(\n ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number,\n): void {\n const rr = Math.min(r, w / 2, h / 2)\n ctx.beginPath()\n ctx.moveTo(x + rr, y)\n ctx.arcTo(x + w, y, x + w, y + h, rr)\n ctx.arcTo(x + w, y + h, x, y + h, rr)\n ctx.arcTo(x, y + h, x, y, rr)\n ctx.arcTo(x, y, x + w, y, rr)\n ctx.closePath()\n}\n// Total extra screen height a node's badge row occupies above the body at the\n// given size multiplier, or 0 when there is none. Used both to place the chip\n// row and to grow the hit radius so the badges are part of the node's\n// clickable target.\nfunction badgeExtent(node: { badges?: readonly string[] }, mul: number): number {\n if (!node.badges || node.badges.length === 0) return 0\n const chipH = BADGE_FONT_PX * mul + BADGE_PAD_Y * 2 * mul\n return BADGE_NODE_GAP * mul + chipH\n}\n\n\n// Read a CSS custom property with a fallback, resolved against a specific\n// element. We resolve against the canvas (not `document.documentElement`)\n// because the theme is declared on the pane's root, below `<html>` — reading\n// from the document root would yield the host's value, not the pane's. The\n// canvas is a descendant of the themed element, so it inherits the right\n// value in either theme.\nfunction cssVar(el: Element | null, name: string, fallback: string): string {\n const target = el ?? (typeof document !== 'undefined' ? document.documentElement : null)\n if (!target) return fallback\n const v = getComputedStyle(target).getPropertyValue(name).trim()\n return v || fallback\n}\n\n// What an edge draws as when the view's stroke map has no entry for it — a\n// state the view does not produce (the map is built from the same edge array\n// this canvas is given), kept quiet because this runs in the draw loop.\n\nconst ARROWHEAD_SIZE = 8\nconst CULL_MARGIN = 100\n\n// Zoom elision thresholds\nconst LABEL_ELIDE_SCALE = 0.25 // hide name labels below this scale (except hovered/selected)\nconst DETAIL_REDUCE_SCALE = 0.1 // drop decorative rings below this scale\n\n// A group glow request: the visible node ids that belong to one decomposition\n// group, the color to glow them, and a strength multiplier (hovered groups glow\n// stronger). ActiveGroup from graph/groups.ts is structurally assignable.\nexport interface GroupGlow {\n nodeIds: ReadonlySet<string>\n color: string\n strength: number\n}\n\ninterface GraphCanvasProps {\n nodes: SimNode[]\n edges: GraphEdge[]\n viewport: Viewport\n onViewportChange: (vp: Viewport) => void\n onNodeDragStart: (id: string, wx: number, wy: number) => void\n onNodeDragMove: (wx: number, wy: number) => void\n onNodeDragEnd: () => void\n onDoubleClickNode: (id: string) => void\n // Frame every visible node (a double-click on the background).\n onFitView: () => void\n onHoverNode: (id: string | null) => void\n onSelectNode: (id: string | null) => void\n highlightedNodes: Set<string> | null\n highlightedEdges: Set<string> | null\n hoveredNodeId: string | null\n selectedNodeId: string | null\n focusedNodeId: string | null\n searchMatchIds: ReadonlySet<string> | null\n running: boolean\n forceParams: ForceParams\n /** Dense per-bucket tables for the overlays — resolved once by the view, not per draw. */\n resolved: ResolvedPhysics\n /** The band overlay as data — stripes per axis, rings — computed by the view in the forces' frame. */\n overlay: BandOverlay\n /** The theme the pane draws in, and every visible edge's stroke, resolved once by the view. */\n theme: Theme\n edgeStrokes: ReadonlyMap<GraphEdge, EdgeStroke>\n /** Lane offsets in screen pixels for edges that share a pair of endpoints, by edge id; absent means on the line. */\n edgeOffsets: ReadonlyMap<string, number>\n activeSection: ForceSection\n activePushBucket: string | null\n activeGravityBucket: GravityHover | null\n activePullEdge: string | null\n nodeScale: NodeScaleParams\n // Decomposition group glow layer (drawn behind everything). Empty = no overlay.\n groupGlows: GroupGlow[]\n /** The visible edges drawn with a marking band, by id. */\n markedEdges: ReadonlySet<string>\n}\n\n/**\n * Shift a segment sideways into its lane: parallel edges between one pair of\n * nodes would otherwise draw on top of each other and read as one. The\n * perpendicular is taken in a fixed orientation of the pair (lower id first),\n * so the two directions of a pair take distinct lanes rather than the same\n * one mirrored.\n */\nfunction laneOffset(\n edge: GraphEdge, offset: number, sx: number, sy: number, tx: number, ty: number,\n): [number, number, number, number] {\n if (!offset) return [sx, sy, tx, ty]\n const forward = edge.sourceId < edge.targetId\n const dx = forward ? tx - sx : sx - tx\n const dy = forward ? ty - sy : sy - ty\n const len = Math.hypot(dx, dy) || 1\n const nx = (-dy / len) * offset\n const ny = (dx / len) * offset\n return [sx + nx, sy + ny, tx + nx, ty + ny]\n}\n\n// All data the draw function needs, stored in a ref to avoid effect teardown\ninterface DrawData {\n // Read by the loop rather than closed over: drawData.current is rewritten\n // every render, a closure is not.\n taxonomy: Taxonomy\n nodes: SimNode[]\n edges: GraphEdge[]\n nodeMap: Map<string, SimNode>\n viewport: Viewport\n groupGlows: GroupGlow[]\n markedEdges: ReadonlySet<string>\n highlightedNodes: Set<string> | null\n highlightedEdges: Set<string> | null\n hoveredNodeId: string | null\n selectedNodeId: string | null\n focusedNodeId: string | null\n searchMatchIds: ReadonlySet<string> | null\n forceParams: ForceParams\n resolved: ResolvedPhysics\n overlay: BandOverlay\n theme: Theme\n edgeStrokes: ReadonlyMap<GraphEdge, EdgeStroke>\n edgeOffsets: ReadonlyMap<string, number>\n activeSection: ForceSection\n activePushBucket: string | null\n activeGravityBucket: GravityHover | null\n activePullEdge: string | null\n nodeScale: NodeScaleParams\n running: boolean\n // Set of node ids where the same underlying definition appears more than\n // once in the visible set (on different hosts). Drawn with a colored halo.\n dupNodeIds: Set<string>\n // The definitionKey of the currently hovered or selected node, when that\n // node belongs to a duplicate group. All visible nodes sharing this key\n // receive the duplicate halo. Null when no dup node is active.\n activeDupDefKey: string | null\n}\n\nexport function GraphCanvas({\n nodes, edges, viewport, onViewportChange,\n onNodeDragStart, onNodeDragMove, onNodeDragEnd,\n onDoubleClickNode, onFitView, onHoverNode, onSelectNode,\n highlightedNodes, highlightedEdges,\n hoveredNodeId, selectedNodeId, focusedNodeId, searchMatchIds,\n running, forceParams, resolved, overlay, theme, edgeStrokes, edgeOffsets, activeSection, activePushBucket,\n activeGravityBucket, activePullEdge, nodeScale, groupGlows, markedEdges,\n}: GraphCanvasProps) {\n const canvasRef = React.useRef<HTMLCanvasElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [size, setSize] = React.useState({ w: 0, h: 0 })\n const dragState = React.useRef<{\n type: 'pan' | 'node'\n startVp?: Viewport\n sx: number\n sy: number\n moved: boolean\n } | null>(null)\n\n // Build node lookup for edge rendering\n const nodeMap = React.useMemo(() => {\n const m = new Map<string, SimNode>()\n for (const n of nodes) m.set(n.id, n)\n return m\n }, [nodes])\n\n // IDs of nodes whose entity appears more than once in the visible set.\n // Computed from the visible `nodes` prop so the halo tracks the current filter.\n const dupNodeIds = React.useMemo(() => {\n const countByKey = new Map<string, number>()\n for (const n of nodes) countByKey.set(copyKeyOf(n), (countByKey.get(copyKeyOf(n)) ?? 0) + 1)\n const result = new Set<string>()\n for (const n of nodes) {\n if ((countByKey.get(copyKeyOf(n)) ?? 0) > 1) result.add(n.id)\n }\n return result\n }, [nodes])\n\n // The copy key of the currently active (hovered or selected) node, when\n // that node has copies. Used by the draw loop to light up every copy with\n // the halo, not just the active node itself.\n const activeDupDefKey = React.useMemo(() => {\n const activeId = hoveredNodeId ?? selectedNodeId\n if (!activeId) return null\n const activeNode = nodeMap.get(activeId)\n if (!activeNode || !dupNodeIds.has(activeId)) return null\n return copyKeyOf(activeNode)\n }, [hoveredNodeId, selectedNodeId, nodeMap, dupNodeIds])\n\n const taxonomy = useTaxonomy()\n\n // --- Ref-based draw data (updated every render, read by draw loop) ---\n const drawData = React.useRef<DrawData>({\n taxonomy,\n nodes, edges, nodeMap, viewport, groupGlows, markedEdges, highlightedNodes, highlightedEdges,\n hoveredNodeId, selectedNodeId, focusedNodeId, searchMatchIds,\n forceParams, resolved, overlay, theme, edgeStrokes, edgeOffsets, activeSection, activePushBucket, activeGravityBucket, activePullEdge, nodeScale, running,\n dupNodeIds, activeDupDefKey,\n })\n drawData.current = {\n taxonomy,\n nodes, edges, nodeMap, viewport, groupGlows, markedEdges, highlightedNodes, highlightedEdges,\n hoveredNodeId, selectedNodeId, focusedNodeId, searchMatchIds,\n forceParams, resolved, overlay, theme, edgeStrokes, edgeOffsets, activeSection, activePushBucket, activeGravityBucket, activePullEdge, nodeScale, running,\n dupNodeIds, activeDupDefKey,\n }\n\n // Resize observer\n React.useEffect(() => {\n const container = containerRef.current\n if (!container) return\n const ro = new ResizeObserver(entries => {\n const { width, height } = entries[0].contentRect\n setSize({ w: Math.floor(width), h: Math.floor(height) })\n })\n ro.observe(container)\n return () => ro.disconnect()\n }, [])\n\n // Hit test: find node under screen coordinates (circular distance check).\n const hitTest = React.useCallback((sx: number, sy: number): SimNode | null => {\n const [wx, wy] = screenToWorld(viewport, sx, sy)\n const mul = nodeSizeMul(viewport.scale, nodeScale)\n for (let i = nodes.length - 1; i >= 0; i--) {\n const n = nodes[i]\n // Match the on-screen size: screen radius is def.r × nodeSizeMul(scale);\n // convert to world units by dividing by scale, plus a small slop. A\n // cardinality badge (parameterized family) sits above the body, so grow\n // the target by its extent to keep the badge part of the clickable node.\n const screenR = taxonomy.nodeStyleOf(n).size.r * mul + badgeExtent(n, mul)\n const r = screenR / viewport.scale + 4\n if ((wx - n.x) ** 2 + (wy - n.y) ** 2 <= r * r) return n\n }\n return null\n }, [nodes, viewport, nodeScale, taxonomy])\n\n // Mouse handlers\n // Wheel zoom, registered once; the camera and its setter are read through refs.\n const viewportRef = useLatest(viewport)\n const onViewportChangeRef = useLatest(onViewportChange)\n React.useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n return listenWheelZoom(canvas, () => viewportRef.current, vp => onViewportChangeRef.current(vp))\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handlePointerDown = React.useCallback((e: React.PointerEvent) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const sx = e.clientX - rect.left\n const sy = e.clientY - rect.top\n const node = hitTest(sx, sy)\n\n if (node) {\n dragState.current = { type: 'node', sx, sy, moved: false }\n const [wx, wy] = screenToWorld(viewport, sx, sy)\n onNodeDragStart(node.id, wx, wy)\n } else {\n dragState.current = { type: 'pan', startVp: { ...viewport }, sx, sy, moved: false }\n }\n canvasRef.current?.setPointerCapture(e.pointerId)\n }, [viewport, hitTest, onNodeDragStart])\n\n const handlePointerMove = React.useCallback((e: React.PointerEvent) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const cx = e.clientX - rect.left\n const cy = e.clientY - rect.top\n\n if (!dragState.current) {\n // Hover detection\n const node = hitTest(cx, cy)\n onHoverNode(node?.id ?? null)\n return\n }\n\n dragState.current.moved = true\n\n if (dragState.current.type === 'pan' && dragState.current.startVp) {\n const dx = cx - dragState.current.sx\n const dy = cy - dragState.current.sy\n onViewportChange({\n ...dragState.current.startVp,\n x: dragState.current.startVp.x + dx,\n y: dragState.current.startVp.y + dy,\n })\n } else if (dragState.current.type === 'node') {\n const [wx, wy] = screenToWorld(viewport, cx, cy)\n onNodeDragMove(wx, wy)\n }\n }, [viewport, hitTest, onViewportChange, onNodeDragMove, onHoverNode])\n\n const handlePointerUp = React.useCallback((e: React.PointerEvent) => {\n const ds = dragState.current\n if (ds?.type === 'node') {\n onNodeDragEnd()\n // Click (no drag) on node → select\n if (!ds.moved) {\n const rect = e.currentTarget.getBoundingClientRect()\n const sx = e.clientX - rect.left\n const sy = e.clientY - rect.top\n const node = hitTest(sx, sy)\n if (node) onSelectNode(node.id)\n }\n } else if (ds?.type === 'pan' && !ds.moved) {\n // Click on background → deselect\n onSelectNode(null)\n }\n dragState.current = null\n canvasRef.current?.releasePointerCapture(e.pointerId)\n }, [onNodeDragEnd, onSelectNode, hitTest])\n\n const handleDoubleClick = React.useCallback((e: React.MouseEvent) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const sx = e.clientX - rect.left\n const sy = e.clientY - rect.top\n const node = hitTest(sx, sy)\n if (node) {\n onDoubleClickNode(node.id)\n } else {\n onFitView()\n }\n }, [hitTest, onFitView, onDoubleClickNode])\n\n // --- Render loop ---\n // Uses a ref for all draw data so the effect only tears down on size change.\n // A dirty flag triggers single-frame redraws when paused.\n const dirtyRef = React.useRef(true)\n\n // Mark dirty on any visual change (runs every render, but just sets a flag)\n React.useEffect(() => { dirtyRef.current = true })\n\n // Main draw effect — only depends on size (canvas allocation)\n React.useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas || size.w === 0) return\n const ctx = canvas.getContext('2d')\n if (!ctx) return\n\n // Allocate canvas bitmap once per resize\n const dpr = window.devicePixelRatio || 1\n canvas.width = size.w * dpr\n canvas.height = size.h * dpr\n\n let loopId = 0\n let active = true\n\n const drawFrame = () => {\n const d = drawData.current\n const { w, h } = size\n const hasHighlight = d.highlightedNodes !== null && d.highlightedNodes.size > 0\n // Soften the dim while the group glow is on (see DIM_ALPHA_GROUPS).\n const dimAlpha = d.groupGlows.length > 0 ? DIM_ALPHA_GROUPS : DIM_ALPHA\n\n // Resolve theme-tracking colours every frame. Cheap (CSS lookup +\n // string trim per draw) and guarantees the canvas text follows a\n // theme switch without needing a full effect teardown.\n // Dedicated, high-contrast label colour (darker than --gv-color-text in\n // light themes, brighter in dark) so node names pop off the background.\n const labelColor = cssVar(canvas, '--gv-color-graph-label', '#0f172a')\n // Dim variant keeps the same hue, just at low alpha. We synthesise it\n // by appending an alpha to the resolved hex when possible; otherwise\n // fall back to the existing rgba grey.\n const labelDimColor = labelColor.startsWith('#') && labelColor.length === 7\n ? labelColor + '33' // ~20% alpha\n : 'rgba(100,116,139,0.2)'\n\n // World coordinates are anchored by hierarchical gravity, so the canvas\n // can project directly through the user's viewport — no per-frame\n // re-centering offset is needed.\n const vp = d.viewport\n\n // Node + label size multiplier for the current zoom (clamped, tunable).\n // Hoisted so the group-glow layer (drawn first) and the node layer below\n // share one value.\n const mul = nodeSizeMul(vp.scale, d.nodeScale)\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.clearRect(0, 0, w, h)\n\n // --- Decomposition group glow (behind everything) ---\n // Drawn before edges/nodes/labels so they all sit on top. Each member\n // node gets a soft radial halo in its group color; adjacent members'\n // halos overlap and fuse into a continuous blob (no hard boundary). An\n // edge whose endpoints share a group glows as a connecting corridor.\n // Sections partition members, so a node belongs to at most one group.\n if (d.groupGlows.length > 0) {\n const glowByNode = new Map<string, { color: string; strength: number }>()\n for (const g of d.groupGlows) {\n for (const id of g.nodeIds) glowByNode.set(id, { color: g.color, strength: g.strength })\n }\n\n ctx.save()\n ctx.lineCap = 'round'\n // Edge corridors first, so node halos sit on top of them.\n for (const edge of d.edges) {\n const gs = glowByNode.get(edge.sourceId)\n const gt = glowByNode.get(edge.targetId)\n if (!gs || !gt || gs.color !== gt.color) continue\n const src = d.nodeMap.get(edge.sourceId)\n const tgt = d.nodeMap.get(edge.targetId)\n if (!src || !tgt) continue\n const [sx, sy] = worldToScreen(vp, src.x, src.y)\n const [tx, ty] = worldToScreen(vp, tgt.x, tgt.y)\n if (Math.max(sx, tx) < -CULL_MARGIN || Math.min(sx, tx) > w + CULL_MARGIN ||\n Math.max(sy, ty) < -CULL_MARGIN || Math.min(sy, ty) > h + CULL_MARGIN) continue\n ctx.globalAlpha = Math.min(0.5, GROUP_GLOW_EDGE_ALPHA * gs.strength)\n ctx.strokeStyle = gs.color\n ctx.lineWidth = GROUP_GLOW_EDGE_WIDTH * glowSwell(gs.strength)\n ctx.beginPath()\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.stroke()\n }\n\n // Node halos: radial gradient from the group color (at strength-scaled\n // alpha) to transparent, so overlapping members merge with no gap.\n ctx.globalAlpha = 1\n for (const [id, g] of glowByNode) {\n const node = d.nodeMap.get(id)\n if (!node) continue\n const [sx, sy] = worldToScreen(vp, node.x, node.y)\n const gr = d.taxonomy.nodeStyleOf(node).size.r * mul + GROUP_GLOW_PAD * glowSwell(g.strength)\n if (sx + gr < 0 || sx - gr > w || sy + gr < 0 || sy - gr > h) continue\n const a = Math.min(0.92, GROUP_GLOW_CENTER_ALPHA * g.strength)\n const grad = ctx.createRadialGradient(sx, sy, 0, sx, sy, gr)\n grad.addColorStop(0, withAlpha(g.color, a))\n grad.addColorStop(0.6, withAlpha(g.color, a * 0.5))\n grad.addColorStop(1, withAlpha(g.color, 0))\n ctx.fillStyle = grad\n ctx.beginPath()\n ctx.arc(sx, sy, gr, 0, Math.PI * 2)\n ctx.fill()\n }\n ctx.restore()\n ctx.globalAlpha = 1\n }\n\n // Draw edges\n for (const edge of d.edges) {\n const src = d.nodeMap.get(edge.sourceId)\n const tgt = d.nodeMap.get(edge.targetId)\n if (!src || !tgt) continue\n\n const [sx0, sy0] = worldToScreen(vp, src.x, src.y)\n const [tx0, ty0] = worldToScreen(vp, tgt.x, tgt.y)\n const [sx, sy, tx, ty] = laneOffset(edge, d.edgeOffsets.get(edge.id) ?? 0, sx0, sy0, tx0, ty0)\n\n if (Math.max(sx, tx) < -CULL_MARGIN || Math.min(sx, tx) > w + CULL_MARGIN ||\n Math.max(sy, ty) < -CULL_MARGIN || Math.min(sy, ty) > h + CULL_MARGIN) continue\n\n const edgeHighlighted = d.highlightedEdges?.has(edge.id) ?? false\n const style = d.edgeStrokes.get(edge) ?? NEUTRAL_STROKE\n const baseAlpha = style.alpha\n // An edge is search-dimmed if a search is active and either\n // endpoint isn't a match — keeps the visible chain readable\n // without showing edges to dimmed nodes.\n const edgeSearchDimmed = d.searchMatchIds !== null &&\n (!d.searchMatchIds.has(edge.sourceId) || !d.searchMatchIds.has(edge.targetId))\n // Highlights override the per-style alpha so the active subgraph\n // pops; dim non-highlighted/non-matching edges down to DIM_ALPHA.\n const edgeDimmed = hasHighlight ? !edgeHighlighted : edgeSearchDimmed\n const edgeAlpha = hasHighlight && edgeHighlighted ? 1 : edgeDimmed ? dimAlpha : baseAlpha\n\n if (d.markedEdges.has(edge.id)) {\n ctx.globalAlpha = edgeDimmed ? dimAlpha : MARK_ALPHA\n ctx.beginPath()\n ctx.setLineDash(MARK_DASH)\n ctx.strokeStyle = labelColor\n ctx.lineWidth = MARK_WIDTH\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.stroke()\n }\n\n ctx.globalAlpha = edgeAlpha\n ctx.beginPath()\n ctx.setLineDash([...style.dash])\n ctx.strokeStyle = style.color[d.theme]\n ctx.lineWidth = style.width\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.stroke()\n ctx.setLineDash([])\n\n // Arrowhead for dependency edges (containment is hierarchical, no\n // direction is interesting to show).\n if (!isContainment(edge)) {\n const angle = Math.atan2(ty - sy, tx - sx)\n // Stop the arrowhead at the target's on-screen edge (screen radius =\n // def.r × nodeSizeMul(scale)) so it tracks the zoom-scaled node body.\n const tgtR = d.taxonomy.nodeStyleOf(tgt).size.r * nodeSizeMul(vp.scale, d.nodeScale)\n const offset = tgtR + 2\n const ax = tx - Math.cos(angle) * offset\n const ay = ty - Math.sin(angle) * offset\n ctx.beginPath()\n ctx.moveTo(ax, ay)\n ctx.lineTo(\n ax - ARROWHEAD_SIZE * Math.cos(angle - Math.PI / 6),\n ay - ARROWHEAD_SIZE * Math.sin(angle - Math.PI / 6),\n )\n ctx.lineTo(\n ax - ARROWHEAD_SIZE * Math.cos(angle + Math.PI / 6),\n ay - ARROWHEAD_SIZE * Math.sin(angle + Math.PI / 6),\n )\n ctx.closePath()\n ctx.fillStyle = style.color[d.theme]\n ctx.fill()\n }\n\n ctx.globalAlpha = 1\n }\n\n // --- Force field visualizations ---\n const pushActive = d.activeSection === 'push'\n const pullActive = d.activeSection === 'pull'\n const gravityActive = d.activeSection === 'gravity'\n\n // PUSH: charge field rings.\n //\n // Rings are placed at distances where the force on a unit test particle\n // crosses absolute thresholds (FORCE_THRESHOLDS, in raw force units).\n // Solving F = pushMul·|q| / (d² + soft)^exp = T for d gives\n // d = sqrt((pushMul·|q| / T)^(1/exp) − soft)\n // which is real only when the threshold is reachable for that node's\n // charge. As a result, stronger charges show larger rings (more reach),\n // and the weakest types may render only the outermost ring — exactly\n // the visual the user intuits when they tune a per-type charge.\n //\n // `soft` is now per-type: a single node's field uses its own effective\n // core radius (rEff = coreRadiusMultiplier × coreRadius[type])², the\n // self-pair case of the simulation's per-pair softening.\n //\n // Rings are hover-only: the persistent toggle was removed, so they show\n // only while the PUSH section (or a charge token) is hovered.\n const FORCE_THRESHOLDS = [0.2, 0.5, 1.5]\n if (pushActive) {\n const exp = d.forceParams.chargeExponent\n const pushMul = d.forceParams.pushMultiplier\n const crMul = d.forceParams.coreRadiusMultiplier\n const bucketFilter = d.activePushBucket\n\n for (const node of d.nodes) {\n const [sx, sy] = worldToScreen(vp, node.x, node.y)\n const maxRing = 2000\n if (sx + maxRing < 0 || sx - maxRing > w ||\n sy + maxRing < 0 || sy - maxRing > h) continue\n\n const highlighted = bucketFilter === null || bucketOf(d.resolved.push, node) === bucketFilter\n const baseAlpha = highlighted ? 0.28 : 0.05\n const stepAlpha = highlighted ? 0.06 : 0.015\n\n const color = d.taxonomy.nodeStyleOf(node).color[d.theme].fill\n ctx.strokeStyle = color\n\n const effectiveCharge = Math.abs(chargeFor(d.resolved.push, node)) * pushMul\n if (effectiveCharge <= 0) continue\n\n const rEff = crMul * coreRadiusFor(d.resolved.push, node)\n const soft = rEff * rEff\n\n // Collect every reachable ring radius. Outer rings (lower threshold\n // T, larger d, weaker force) draw fainter than inner rings — this\n // preserves the \"field fades with distance\" reading.\n const ringRadii: number[] = []\n for (let ti = 0; ti < FORCE_THRESHOLDS.length; ti++) {\n const T = FORCE_THRESHOLDS[ti]\n const ratio = effectiveCharge / T\n const inner = Math.pow(ratio, 1 / Math.max(exp, 0.01)) - soft\n if (inner <= 0) continue\n const dd = Math.sqrt(inner)\n const r = dd * vp.scale\n if (r < 2 || r > 2000) continue\n ringRadii.push(r)\n }\n\n if (ringRadii.length === 0) continue\n\n // Outline rings only — no per-node disc fill. Filling each field\n // washed the screen into a solid colour where many nodes overlap;\n // the ring outlines alone read as \"reach\" without stacking opacity.\n ctx.strokeStyle = color\n for (let ri = 0; ri < ringRadii.length; ri++) {\n const r = ringRadii[ri]\n const ringIdxFromInner = ringRadii.length - 1 - ri\n ctx.beginPath()\n ctx.arc(sx, sy, r, 0, Math.PI * 2)\n ctx.globalAlpha = baseAlpha + ringIdxFromInner * stepAlpha\n ctx.lineWidth = highlighted ? 1.5 : 1\n ctx.stroke()\n }\n }\n ctx.globalAlpha = 1\n }\n\n // PULL: edge tension. When a specific edge category is being tuned in\n // the spring map (activePullEdge), highlight only that category — drawn\n // as a border (a tension-coloured casing with the edge's own colour\n // redrawn on top) so the edge's identity colour stays clear. Otherwise\n // (section hover, no specific token) colour all edges by tension.\n const activePullKey = d.activePullEdge\n if (pullActive || activePullKey) {\n const distMul = d.forceParams.distanceMultiplier\n // Tension palette drawn from the app's own colours (theme-aware) so the\n // highlight reads as part of the view, not a generic stoplight:\n // amber = stretched, cool blue = compressed, calm\n // timer-teal = near rest.\n const tensionStretch = cssVar(canvas, '--gv-tension-stretch', '#d97706')\n const tensionCompress = cssVar(canvas, '--gv-tension-compress', '#7CB9E8')\n const tensionRest = cssVar(canvas, '--gv-tension-rest', '#7EC8B8')\n for (const edge of d.edges) {\n const src = d.nodeMap.get(edge.sourceId)\n const tgt = d.nodeMap.get(edge.targetId)\n if (!src || !tgt) continue\n\n const cat = springFor(d.resolved.spring, edge, src, tgt)\n if (activePullKey && cat.key !== activePullKey) continue\n\n const [sx0, sy0] = worldToScreen(vp, src.x, src.y)\n const [tx0, ty0] = worldToScreen(vp, tgt.x, tgt.y)\n const [sx, sy, tx, ty] = laneOffset(edge, d.edgeOffsets.get(edge.id) ?? 0, sx0, sy0, tx0, ty0)\n if (Math.max(sx, tx) < -CULL_MARGIN || Math.min(sx, tx) > w + CULL_MARGIN ||\n Math.max(sy, ty) < -CULL_MARGIN || Math.min(sy, ty) > h + CULL_MARGIN) continue\n\n const restDist = cat.distance * distMul\n const dx = tgt.x - src.x\n const dy = tgt.y - src.y\n const dist = Math.sqrt(dx * dx + dy * dy)\n const ratio = dist / Math.max(restDist, 0.1)\n\n let tensionColor: string\n if (ratio > 1.15) {\n tensionColor = tensionStretch\n } else if (ratio < 0.85) {\n tensionColor = tensionCompress\n } else {\n tensionColor = tensionRest\n }\n\n if (activePullKey) {\n // Border: a tension-coloured casing wider than the edge, then the\n // edge's own colour redrawn on top at full opacity.\n const style = d.edgeStrokes.get(edge) ?? NEUTRAL_STROKE\n ctx.beginPath()\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.strokeStyle = tensionColor\n ctx.globalAlpha = 0.9\n ctx.lineWidth = style.width + 5\n ctx.setLineDash([])\n ctx.stroke()\n\n ctx.beginPath()\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.strokeStyle = style.color[d.theme]\n ctx.globalAlpha = 1\n ctx.lineWidth = style.width\n ctx.setLineDash([...style.dash])\n ctx.stroke()\n ctx.setLineDash([])\n continue\n }\n\n ctx.beginPath()\n ctx.moveTo(sx, sy)\n ctx.lineTo(tx, ty)\n ctx.strokeStyle = tensionColor\n ctx.globalAlpha = 0.5\n ctx.lineWidth = 3\n ctx.setLineDash([])\n ctx.stroke()\n\n const angle = Math.atan2(dy, dx)\n const perpX = -Math.sin(angle)\n const perpY = Math.cos(angle)\n const tickLen = 5\n const restScreen = restDist * vp.scale\n if (restScreen > 10 && restScreen < Math.sqrt((tx - sx) ** 2 + (ty - sy) ** 2) * 2) {\n const mx1 = sx + Math.cos(angle) * restScreen\n const my1 = sy + Math.sin(angle) * restScreen\n ctx.beginPath()\n ctx.moveTo(mx1 - perpX * tickLen, my1 - perpY * tickLen)\n ctx.lineTo(mx1 + perpX * tickLen, my1 + perpY * tickLen)\n ctx.strokeStyle = tensionRest\n ctx.globalAlpha = 0.7\n ctx.lineWidth = 1.5\n ctx.stroke()\n const mx2 = tx - Math.cos(angle) * restScreen\n const my2 = ty - Math.sin(angle) * restScreen\n ctx.beginPath()\n ctx.moveTo(mx2 - perpX * tickLen, my2 - perpY * tickLen)\n ctx.lineTo(mx2 + perpX * tickLen, my2 + perpY * tickLen)\n ctx.stroke()\n }\n }\n ctx.globalAlpha = 1\n ctx.setLineDash([])\n }\n\n // GRAVITY: the band overlay, drawn from the data the view computed in the\n // forces' frame (`bandOverlay`): stripes per axis in cartesian, rings in\n // radial, only for buckets some node falls in, only while an axis acts.\n // Hovering a column highlights its bucket's stripe or ring on that axis;\n // a hovered bucket that is drawn nowhere dims nothing.\n if (gravityActive && d.overlay.acting) {\n const hover = d.activeGravityBucket\n const fillOf = (bucket: string | undefined): string => {\n const color = bucket !== undefined ? d.overlay.colors.get(bucket) : undefined\n return color ?? cssVar(canvas, '--gv-color-text-muted', '#94A3B8')\n }\n const drawnOn = (axis: 'x' | 'y') => hover !== null && hover.axis === axis &&\n (axis === 'y' && d.overlay.rings.length > 0\n ? d.overlay.rings.some(r => r.buckets.includes(hover.bucket))\n : (axis === 'x' ? d.overlay.x : d.overlay.y).some(st => st.buckets.includes(hover.bucket)))\n\n if (d.overlay.rings.length > 0) {\n const [ox, oy] = worldToScreen(vp, 0, 0)\n const highlight = drawnOn('y')\n for (const ring of d.overlay.rings) {\n const isActive = highlight && hover !== null && ring.buckets.includes(hover.bucket)\n const isDimmed = highlight && !isActive\n ctx.strokeStyle = fillOf(ring.buckets[0])\n ctx.globalAlpha = isActive ? 0.6 : isDimmed ? 0.1 : 0.28\n ctx.lineWidth = isActive ? 1.5 : 1\n ctx.setLineDash([])\n ctx.beginPath()\n ctx.arc(ox, oy, ring.radius * vp.scale, 0, Math.PI * 2)\n ctx.stroke()\n }\n }\n const stripes = (coord: 'x' | 'y') => {\n const list = coord === 'x' ? d.overlay.x : d.overlay.y\n const highlight = drawnOn(coord)\n const toScreen = (v: number) => (coord === 'y' ? worldToScreen(vp, 0, v)[1] : worldToScreen(vp, v, 0)[0])\n for (const { lo, hi, buckets, gradient } of list) {\n const s1 = toScreen(lo)\n const s2 = toScreen(hi)\n if (s2 < 0 || s1 > (coord === 'y' ? h : w)) continue\n const isActive = highlight && hover !== null && buckets.includes(hover.bucket)\n const isDimmed = highlight && !isActive\n const color = fillOf(buckets[0])\n if (gradient) {\n // A scalar axis: one gradient from its 0 end to its 1 end.\n const z = toScreen(gradient.zero), o = toScreen(gradient.one)\n const g = coord === 'y' ? ctx.createLinearGradient(0, z, 0, o) : ctx.createLinearGradient(z, 0, o, 0)\n g.addColorStop(0, cssVar(canvas, '--gv-color-text-muted', '#94A3B8'))\n g.addColorStop(1, cssVar(canvas, '--gv-color-text', '#1E293B'))\n ctx.fillStyle = g\n ctx.globalAlpha = 0.12\n } else {\n ctx.fillStyle = color\n ctx.globalAlpha = isActive ? 0.20 : isDimmed ? 0.04 : 0.10\n }\n if (coord === 'y') ctx.fillRect(0, s1, w, s2 - s1)\n else ctx.fillRect(s1, 0, s2 - s1, h)\n ctx.strokeStyle = color\n ctx.globalAlpha = isActive ? 0.55 : isDimmed ? 0.08 : 0.22\n ctx.lineWidth = isActive ? 1.5 : 1\n ctx.setLineDash([])\n ctx.beginPath()\n if (coord === 'y') { ctx.moveTo(0, s1); ctx.lineTo(w, s1); ctx.moveTo(0, s2); ctx.lineTo(w, s2) }\n else { ctx.moveTo(s1, 0); ctx.lineTo(s1, h); ctx.moveTo(s2, 0); ctx.lineTo(s2, h) }\n ctx.stroke()\n }\n }\n stripes('y')\n stripes('x')\n ctx.globalAlpha = 1\n }\n\n // Draw nodes\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n\n // Bounding boxes of labels already placed this frame, for collision\n // avoidance (see the label block below). Reset every frame.\n const labelBoxes: { x: number; y: number; w: number; h: number }[] = []\n\n // Screen-space circle for every node, so a label can also avoid\n // overlapping any node body (not just other labels). Precomputed because a\n // label may collide with a node drawn later in the same loop.\n const nodeCircles = d.nodes.map(n => {\n const [nx, ny] = worldToScreen(vp, n.x, n.y)\n return { x: nx, y: ny, r: d.taxonomy.nodeStyleOf(n).size.r * mul }\n })\n\n for (const node of d.nodes) {\n const [sx, sy] = worldToScreen(vp, node.x, node.y)\n const def = d.taxonomy.nodeStyleOf(node)\n const r = def.size.r * mul\n const hw = r // nodes are circles: half-width = half-height = r\n\n if (sx + hw < -CULL_MARGIN || sx - hw > w + CULL_MARGIN ||\n sy + hw < -CULL_MARGIN || sy - hw > h + CULL_MARGIN) continue\n\n const nodeHighlighted = d.highlightedNodes?.has(node.id) ?? false\n const searchDimmed = d.searchMatchIds !== null && !d.searchMatchIds.has(node.id)\n const nodeDimmed = (hasHighlight && !nodeHighlighted) || searchDimmed\n ctx.globalAlpha = nodeDimmed ? dimAlpha : 1\n\n const { fill, border } = def.color[d.theme]\n const icon = def.icon\n\n // Filled body + crisp border. The border is ~3 stops darker than the\n // fill; 1.25px at the lowest zoom keeps the outline visible without\n // turning small nodes into bullseyes.\n ctx.beginPath()\n ctx.arc(sx, sy, r, 0, Math.PI * 2)\n ctx.fillStyle = fill\n ctx.fill()\n ctx.lineWidth = Math.max(1, Math.min(2, vp.scale * 1.25))\n ctx.strokeStyle = border\n ctx.stroke()\n\n // Icon glyph at the node centre (single unicode char). Only drawn\n // once we're above the label-elision scale — at very low zoom the\n // icon bleeds into the fill anyway and just adds noise.\n if (vp.scale >= LABEL_ELIDE_SCALE && icon) {\n ctx.save()\n ctx.font = `${def.size.iconSize * mul}px ${ICON_FONT_FAMILY}`\n ctx.fillStyle = '#FFFFFF'\n ctx.globalAlpha = (nodeDimmed ? dimAlpha : 1) * 0.92\n ctx.fillText(icon, sx, sy + 0.5)\n ctx.restore()\n }\n\n // Orphan indicator — dropped at very low zoom (decorative noise at that scale)\n if (node.ring === 'dashed' && vp.scale >= DETAIL_REDUCE_SCALE) {\n ctx.save()\n ctx.setLineDash([3, 3])\n ctx.strokeStyle = fill\n ctx.lineWidth = 1.5\n ctx.beginPath()\n ctx.arc(sx, sy, r + 4, 0, Math.PI * 2)\n ctx.stroke()\n ctx.restore()\n }\n\n // No search-match ring: search is expressed as opacity only (full vs.\n // DIM_ALPHA above) — the ring vocabulary is reserved for other\n // decoration.\n\n // Duplicate halo — colored ring in the node's own fill color drawn on\n // every visible copy of a definition whenever ANY of those copies is\n // hovered or selected. The halo always renders at full color (alpha\n // 0.55) — it deliberately ignores nodeDimmed so it pops even when the\n // rest of the node is faded by the highlight system.\n //\n // Sits at r + 5 (same gap from the node edge as the default\n // selection ring) and uses a slightly thinner stroke so the two rings\n // read as distinct even when they appear together.\n const dupHaloActive = d.activeDupDefKey !== null &&\n copyKeyOf(node) === d.activeDupDefKey &&\n vp.scale >= DETAIL_REDUCE_SCALE\n if (dupHaloActive) {\n ctx.save()\n ctx.strokeStyle = fill\n ctx.lineWidth = 2\n ctx.setLineDash([])\n ctx.globalAlpha = 0.55\n ctx.beginPath()\n ctx.arc(sx, sy, r + 5, 0, Math.PI * 2)\n ctx.stroke()\n ctx.restore()\n }\n\n // Selection ring — white solid ring. Expands to r + 10 when the\n // duplicate halo is also active, keeping the two rings clearly apart.\n if (node.id === d.selectedNodeId) {\n const selR = dupHaloActive ? r + 10 : r + 5\n ctx.save()\n ctx.strokeStyle = SELECTION_RING_COLOR\n ctx.lineWidth = 2.5\n ctx.setLineDash([])\n ctx.beginPath()\n ctx.arc(sx, sy, selR, 0, Math.PI * 2)\n ctx.stroke()\n ctx.restore()\n }\n\n // Focus ring — dropped at very low zoom (dashes too small to read).\n // Sits at r + 9 so it clears the duplicate halo at r + 7.\n if (node.id === d.focusedNodeId && vp.scale >= DETAIL_REDUCE_SCALE) {\n ctx.save()\n ctx.strokeStyle = FOCUS_RING_COLOR\n ctx.lineWidth = 2\n ctx.setLineDash([2, 2])\n ctx.beginPath()\n ctx.arc(sx, sy, r + 9, 0, Math.PI * 2)\n ctx.stroke()\n ctx.restore()\n }\n\n ctx.globalAlpha = 1\n\n // Badges — the host's chip row above the node. Gated on the same\n // elision scale as icons and labels: at very low zoom the chips are\n // noise. A node with none draws without the row.\n if (\n node.badges && node.badges.length > 0 &&\n vp.scale >= LABEL_ELIDE_SCALE\n ) {\n const badgePx = BADGE_FONT_PX * mul\n const chipH = badgePx + BADGE_PAD_Y * 2 * mul\n const padX = BADGE_PAD_X * mul\n const gap = BADGE_GAP * mul\n ctx.save()\n ctx.font = badgeFont(badgePx)\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n // Measure each chip, then centre the whole row horizontally on the node.\n const chips = node.badges.map(p => {\n const text = p\n return { text, w: ctx.measureText(text).width + padX * 2 }\n })\n const totalW = chips.reduce((s, c) => s + c.w, 0) + gap * (chips.length - 1)\n const rowY = sy - r - BADGE_NODE_GAP * mul - chipH\n let cx = sx - totalW / 2\n ctx.globalAlpha = nodeDimmed ? dimAlpha : 1\n for (const chip of chips) {\n roundRectPath(ctx, cx, rowY, chip.w, chipH, chipH / 2)\n ctx.fillStyle = BADGE_FILL\n ctx.fill()\n ctx.fillStyle = BADGE_TEXT_COLOR\n ctx.fillText(chip.text, cx + chip.w / 2, rowY + chipH / 2 + 0.5)\n cx += chip.w + gap\n }\n ctx.restore()\n ctx.globalAlpha = 1\n }\n\n // Label — node name only; all other detail lives in the hover box.\n // Elided at low zoom; always shown for the hovered/selected node.\n // Collision avoidance: a name is drawn only if its box overlaps neither\n // an already-placed label nor any node body this frame (active nodes\n // always win). Greedy in node order — which is stable — so the set of\n // shown labels doesn't flicker once the layout settles. O(n²) worst\n // case, fine at this scale.\n const isActiveNode = node.id === d.hoveredNodeId || node.id === d.selectedNodeId\n if (vp.scale >= LABEL_ELIDE_SCALE || isActiveNode) {\n // Active nodes (hovered/selected) keep a readable floor size even when\n // zoomed out, since they're deliberately surfaced; others scale freely.\n const labelPx = LABEL_FONT_PX * (isActiveNode ? Math.max(mul, 1) : mul)\n const labelY = sy + r + 4 + labelPx * 0.7\n ctx.font = labelFont(labelPx)\n const text = labelText(node.name)\n const tw = ctx.measureText(text).width\n const box = { x: sx - tw / 2 - 2, y: labelY - labelPx / 2 - 1, w: tw + 4, h: labelPx + 2 }\n const hitsLabel = labelBoxes.some(b =>\n box.x < b.x + b.w && box.x + box.w > b.x && box.y < b.y + b.h && box.y + box.h > b.y)\n // Box-vs-circle: closest point on the label box to each node centre.\n const hitsNode = !hitsLabel && nodeCircles.some(c => {\n const cx = Math.max(box.x, Math.min(c.x, box.x + box.w))\n const cy = Math.max(box.y, Math.min(c.y, box.y + box.h))\n return (c.x - cx) ** 2 + (c.y - cy) ** 2 < c.r * c.r\n })\n if (isActiveNode || (!hitsLabel && !hitsNode)) {\n labelBoxes.push(box)\n // Softened + slightly translucent so names aren't the only bright\n // text on the dark canvas and edges stay visible through them.\n // Dimmed labels already encode their fade in the colour itself.\n ctx.globalAlpha = nodeDimmed ? 1 : LABEL_ALPHA\n ctx.fillStyle = nodeDimmed ? labelDimColor : labelColor\n ctx.fillText(text, sx, labelY)\n ctx.globalAlpha = 1\n }\n }\n }\n }\n\n // Animation loop: runs continuously when simulation is running.\n // When paused, a single frame is drawn on demand via the dirty flag.\n const loop = () => {\n if (!active) return\n const isRunning = drawData.current.running\n if (isRunning || dirtyRef.current) {\n dirtyRef.current = false\n drawFrame()\n }\n loopId = requestAnimationFrame(loop)\n }\n loopId = requestAnimationFrame(loop)\n\n return () => { active = false; cancelAnimationFrame(loopId) }\n }, [size]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <div ref={containerRef} className=\"gv-graph-canvas-container\">\n <canvas\n ref={canvasRef}\n style={{ width: size.w, height: size.h }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onDoubleClick={handleDoubleClick}\n />\n </div>\n )\n}\n","// Error boundary around the graph canvas, and around the whole pane.\n//\n// A throw in <GraphCanvas>'s render or in its requestAnimationFrame draw loop\n// would otherwise unmount the whole React tree and blank the app. This small\n// class component (error boundaries must be classes — there is no hook form)\n// catches such errors and renders a static fallback in place of what it\n// wraps, leaving the rest of the app interactive. The view mounts one around\n// the canvas and one around its own root, so no throw in the pane reaches the\n// host's tree.\n\nimport React from 'react'\n\ninterface CanvasErrorBoundaryProps {\n children: React.ReactNode\n /** What the fallback says failed; the canvas by default. */\n what?: string\n}\n\ninterface CanvasErrorBoundaryState {\n error: Error | null\n}\n\nexport class CanvasErrorBoundary extends React.Component<\n CanvasErrorBoundaryProps,\n CanvasErrorBoundaryState\n> {\n constructor(props: CanvasErrorBoundaryProps) {\n super(props)\n this.state = { error: null }\n }\n\n static getDerivedStateFromError(error: Error): CanvasErrorBoundaryState {\n return { error }\n }\n\n componentDidCatch(error: Error, info: React.ErrorInfo): void {\n // Surface the failure to the console for debugging; the fallback UI keeps\n // the rest of the app usable.\n console.error(`${this.props.what ?? 'graph canvas'} render/draw error:`, error, info.componentStack)\n }\n\n render(): React.ReactNode {\n if (this.state.error) {\n return (\n <div className=\"gv-graph-canvas-error\" role=\"alert\">\n <p>The {this.props.what ?? 'graph canvas'} failed to render.</p>\n <p className=\"gv-graph-canvas-error-detail\">{this.state.error.message}</p>\n </div>\n )\n }\n return this.props.children\n }\n}\n","// The colour a control surface paints one bucket of an axis.\n//\n// On the style axis, bucketed by identity, a bucket IS a styled value and takes\n// that value's colour — the `--gv-node-<suffix>` the pane carries inline in its\n// resolved theme, so a token and its nodes match across themes. Any other\n// bucketing has no declared colour (there is no palette for arbitrary axes\n// yet), and says so with one neutral tone rather than warning its way to the\n// fallback style once per bucket.\n\nimport type { AxisMapping } from '../../graph/axis'\nimport { isIdentityMapping } from '../../graph/axis'\nimport type { Taxonomy } from '../../graph/taxonomy'\n\nexport const NEUTRAL_BUCKET_COLOR = 'var(--gv-color-text-muted)'\n\nexport function bucketColor(taxonomy: Taxonomy, mapping: AxisMapping, bucketId: string): string {\n const styled = mapping.axis === taxonomy.styleAxis() && isIdentityMapping(mapping)\n return styled ? `var(${taxonomy.colorVar(bucketId)})` : NEUTRAL_BUCKET_COLOR\n}\n","// Plot — the layout shell shared by the node plot (Push/Pull charge/spring maps)\n// and the band plot (gravity). It owns the grid that frames a square-ish SVG with\n// an axis label + \"scale all\" slider on the left gutter and a stack of controls\n// (x slider, x label, and for the band the X-range bar) beneath it.\n//\n// It is pure layout: the caller passes its own <svg> (keeping its ref, viewBox,\n// and content) as children, plus the gutter slider and labels as slots. This\n// dedups the grid/gutter scaffolding without entangling the plot-specific SVG\n// math or drag handling.\n\nimport React from 'react'\nimport './controls.css'\n\nexport function Plot({\n yLabel, ySlider, bottom, children,\n}: {\n // Vertical y-axis label (rendered rotated in the left gutter).\n yLabel?: React.ReactNode\n // The vertical \"scale all\" slider for the y axis (a <Slider orientation=\"vertical\">).\n ySlider?: React.ReactNode\n // Everything below the plot, top-to-bottom (x slider, x label, band X-range...).\n bottom?: React.ReactNode\n // The plot itself — the caller's <svg>.\n children: React.ReactNode\n}) {\n return (\n <div className=\"gv-ctl-plot\">\n {yLabel !== undefined && <div className=\"gv-ctl-plot-ylabel\">{yLabel}</div>}\n {ySlider !== undefined && <div className=\"gv-ctl-plot-yslider\">{ySlider}</div>}\n <div className=\"gv-ctl-plot-main\">{children}</div>\n {bottom !== undefined && <div className=\"gv-ctl-plot-bottom\">{bottom}</div>}\n </div>\n )\n}\n","// PopContext — links a hovered value in a governing equation to the global\n// slider that controls it. Hovering a FormulaValue sets the \"popped\" id; the\n// Slider whose popId matches emphasizes itself. This is deliberately scoped to\n// the global/major sliders (axis \"scale all\", exp, strength) — not the per-token\n// plot positions — so the formula reads as a legend for the panel's knobs.\n\nimport React from 'react'\nimport './controls.css'\n\ninterface PopCtx {\n popped: string | null\n setPopped: (id: string | null) => void\n}\n\nconst PopContext = React.createContext<PopCtx>({ popped: null, setPopped: () => {} })\n\nexport function PopProvider({ children }: { children: React.ReactNode }) {\n const [popped, setPopped] = React.useState<string | null>(null)\n const value = React.useMemo(() => ({ popped, setPopped }), [popped])\n return <PopContext.Provider value={value}>{children}</PopContext.Provider>\n}\n\nexport function usePop(): PopCtx {\n return React.useContext(PopContext)\n}\n\n// A hoverable token inside a formula. Hovering it pops the matching slider.\nexport function FormulaValue({ id, children }: { id: string; children: React.ReactNode }) {\n const { popped, setPopped } = usePop()\n return (\n <span\n className={`gv-ctl-formula-value${popped === id ? ' active' : ''}`}\n onMouseEnter={() => setPopped(id)}\n onMouseLeave={() => setPopped(null)}\n >\n {children}\n </span>\n )\n}\n","// Slider — the single uniform range-input primitive for the control panel.\n//\n// One component, two orientations: horizontal (rows, exp, x-gutter) and vertical\n// (y-gutter, \"up = stronger\"). Centralizing it means every slider shares the\n// same track height, accent colour, and focus behaviour, and gives one place to\n// hang cross-cutting behaviour (e.g. the formula-hover \"pop\" — see `popId`).\n//\n// Layout/placement is the caller's job: the slider fills its container\n// (width 100% horizontal, height 100% vertical), so a grid cell or flex row\n// positions it.\n\nimport { usePop } from './PopContext'\nimport './controls.css'\n\nexport interface SliderProps {\n value: number\n min: number\n max: number\n step: number\n onChange: (value: number) => void\n orientation?: 'horizontal' | 'vertical'\n title?: string\n ariaLabel?: string\n // Extra class for caller-side tweaks (rarely needed).\n className?: string\n // Identity for the formula-hover \"pop\": hovering a FormulaValue token with the\n // same id (see PopContext) emphasizes this slider. Also emitted as a\n // `data-pop-id` attribute for styling/testing.\n popId?: string\n}\n\nexport function Slider({\n value, min, max, step, onChange,\n orientation = 'horizontal', title, ariaLabel, className, popId,\n}: SliderProps) {\n const { popped } = usePop()\n const isPopped = popId !== undefined && popped === popId\n const cls = `gv-ctl-slider gv-ctl-slider-${orientation === 'vertical' ? 'v' : 'h'}`\n + (isPopped ? ' gv-ctl-slider-popped' : '')\n + (className ? ' ' + className : '')\n return (\n <input\n type=\"range\"\n className={cls}\n min={min}\n max={max}\n step={step}\n value={value}\n onChange={e => onChange(Number(e.target.value))}\n title={title}\n aria-label={ariaLabel}\n data-pop-id={popId}\n />\n )\n}\n","// Shared force-control primitives, reused by both the Pull (spring) and Push\n// (charge) tabs. These are purely presentational/interactive: all physics\n// math (what each axis means, how curve points are computed) lives in the\n// per-tab wrappers (SpringControls.tsx, ChargeControls.tsx) that feed these.\n//\n// ForceMap2D — a 2D map of draggable tokens on shared axes, with a global\n// \"scale all\" slider on each axis. A token with a single\n// colour renders a solid dot (Push); two colours render a\n// split dot (Pull).\n// ForceCurves — a boxed read-out plot that renders pre-computed polylines\n// (per item), an optional baseline, and per-item vertical\n// markers, with one global `exp` slider beneath.\n//\n// Both reuse the `.gv-spring-*` / `.gv-force-curve-*` CSS classes (see ForceMap.css).\n\nimport React from 'react'\nimport './ForceMap.css'\nimport { Plot } from './controls/Plot'\nimport { Slider } from './controls/Slider'\n\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v))\nconst snap = (v: number, step: number) => Math.round(v / step) * step\n\n// ── Map geometry (viewBox units) ─────────────────────────────────────────────\nconst MAP_W = 300\nconst MAP_H = 200\n// Breathing room so a token at an axis extreme sits inside the plot box instead\n// of straddling its edge. The gridlines/box stay at the full viewBox (so the\n// sliders still align to it); only the token positions inset.\nconst MAP_PAD = 12\nconst MAP_PW = MAP_W - 2 * MAP_PAD\nconst MAP_PH = MAP_H - 2 * MAP_PAD\nconst TOKEN_R = 7\n\nexport interface AxisSpec {\n min: number\n max: number\n step: number\n label: string\n}\n\n// A global \"scale all\" slider that sits along one axis of the map.\nexport interface GlobalSlider {\n value: number\n min: number\n max: number\n step: number\n onChange: (v: number) => void\n title?: string\n ariaLabel?: string\n // Links this global slider to a matching formula token (see PopContext).\n popId?: string\n}\n\nexport interface MapToken {\n // Stable id used to link hover across the map, the curves, and the canvas.\n id: string\n // Data-space coordinates (in the axis units).\n x: number\n y: number\n // A single colour → solid token; colorA + colorB → split token.\n colorA: string\n colorB?: string\n // Solid outline (dependency) vs dashed outline (containment), mirroring the\n // canvas edge styling. For Push every token is solid.\n outline: 'solid' | 'dashed'\n label: string\n tooltip?: string\n}\n\nexport function ForceMap2D({\n tokens, xAxis, yAxis, onDrag, hoveredId, onHover, xSlider, ySlider, ariaLabel,\n}: {\n tokens: MapToken[]\n xAxis: AxisSpec\n yAxis: AxisSpec\n onDrag: (id: string, x: number, y: number) => void\n hoveredId: string | null\n onHover: (id: string | null) => void\n xSlider: GlobalSlider\n ySlider: GlobalSlider\n ariaLabel: string\n}) {\n const svgRef = React.useRef<SVGSVGElement>(null)\n const draggingRef = React.useRef<string | null>(null)\n\n // Data ↔ viewBox mappings (token positions are inset by MAP_PAD).\n const xForVal = (v: number) =>\n MAP_PAD + ((v - xAxis.min) / (xAxis.max - xAxis.min)) * MAP_PW\n const yForVal = (v: number) =>\n (MAP_H - MAP_PAD) - ((v - yAxis.min) / (yAxis.max - yAxis.min)) * MAP_PH\n const valForX = (px: number) =>\n xAxis.min + ((px - MAP_PAD) / MAP_PW) * (xAxis.max - xAxis.min)\n const valForY = (py: number) =>\n yAxis.min + (((MAP_H - MAP_PAD) - py) / MAP_PH) * (yAxis.max - yAxis.min)\n\n // Pointer event → SVG user-space coords. getScreenCTM handles any responsive\n // scaling so drag tracking stays accurate.\n const toUser = (e: React.PointerEvent): { x: number; y: number } => {\n const svg = svgRef.current\n if (!svg) return { x: 0, y: 0 }\n const pt = svg.createSVGPoint()\n pt.x = e.clientX\n pt.y = e.clientY\n const ctm = svg.getScreenCTM()\n if (!ctm) return { x: 0, y: 0 }\n const p = pt.matrixTransform(ctm.inverse())\n return { x: p.x, y: p.y }\n }\n\n const handleDown = (e: React.PointerEvent, id: string) => {\n e.preventDefault()\n ;(e.currentTarget as Element).setPointerCapture(e.pointerId)\n draggingRef.current = id\n }\n\n const handleMove = (e: React.PointerEvent) => {\n const id = draggingRef.current\n if (!id) return\n const { x, y } = toUser(e)\n const vx = clamp(snap(valForX(x), xAxis.step), xAxis.min, xAxis.max)\n const vy = clamp(snap(valForY(y), yAxis.step), yAxis.min, yAxis.max)\n onDrag(id, vx, vy)\n }\n\n const handleUp = (e: React.PointerEvent) => {\n draggingRef.current = null\n ;(e.currentTarget as Element).releasePointerCapture?.(e.pointerId)\n }\n\n // Gridlines frame the plot at the viewBox edges + centre (decorative box,\n // independent of the padded token mapping above).\n const vGrid = [0, MAP_W / 2, MAP_W]\n const hGrid = [0, MAP_H / 2, MAP_H]\n\n // Coincident tokens fan out on a small ring around their shared point, so a\n // set of categories that start from one default — every pair on a freshly\n // chosen axis — is still a set of tokens a pointer can tell apart. Only the\n // drawn position moves; a drag still writes the token's own value.\n const placed = spreadCoincident(tokens.map(t => ({ cx: xForVal(t.x), cy: yForVal(t.y) })))\n\n return (\n <Plot\n yLabel={yAxis.label}\n ySlider={\n <Slider\n orientation=\"vertical\"\n min={ySlider.min} max={ySlider.max} step={ySlider.step}\n value={ySlider.value}\n onChange={ySlider.onChange}\n title={ySlider.title}\n ariaLabel={ySlider.ariaLabel}\n popId={ySlider.popId}\n />\n }\n bottom={\n <>\n <Slider\n min={xSlider.min} max={xSlider.max} step={xSlider.step}\n value={xSlider.value}\n onChange={xSlider.onChange}\n title={xSlider.title}\n ariaLabel={xSlider.ariaLabel}\n popId={xSlider.popId}\n />\n <div className=\"gv-ctl-plot-xlabel\">{xAxis.label}</div>\n </>\n }\n >\n <svg\n ref={svgRef}\n className=\"gv-spring-map-svg\"\n viewBox={`0 0 ${MAP_W} ${MAP_H}`}\n role=\"group\"\n aria-label={ariaLabel}\n >\n <rect x={0} y={0} width={MAP_W} height={MAP_H} className=\"gv-spring-plot-bg\" />\n {vGrid.map(x => (\n <line key={`gx${x}`} x1={x} y1={0} x2={x} y2={MAP_H} className=\"gv-spring-grid\" />\n ))}\n {hGrid.map(y => (\n <line key={`gy${y}`} x1={0} y1={y} x2={MAP_W} y2={y} className=\"gv-spring-grid\" />\n ))}\n\n {tokens.map((token, i) => {\n const { cx, cy } = placed[i]\n const dim = hoveredId !== null && hoveredId !== token.id\n const active = hoveredId === token.id\n return (\n <g\n key={token.id}\n transform={`translate(${cx} ${cy})`}\n className={`gv-spring-token${dim ? ' dim' : ''}${active ? ' active' : ''}`}\n onPointerDown={e => handleDown(e, token.id)}\n onPointerMove={handleMove}\n onPointerUp={handleUp}\n onPointerEnter={() => onHover(token.id)}\n onPointerLeave={() => { if (!draggingRef.current) onHover(null) }}\n >\n {token.tooltip && <title>{token.tooltip}</title>}\n {token.colorB === undefined ? (\n <circle r={TOKEN_R} fill={token.colorA} />\n ) : (\n <>\n <path d={`M0,${-TOKEN_R} A${TOKEN_R},${TOKEN_R} 0 0 0 0,${TOKEN_R} Z`} fill={token.colorA} />\n <path d={`M0,${-TOKEN_R} A${TOKEN_R},${TOKEN_R} 0 0 1 0,${TOKEN_R} Z`} fill={token.colorB} />\n </>\n )}\n <circle\n r={TOKEN_R}\n fill=\"none\"\n className=\"gv-spring-token-outline\"\n strokeDasharray={token.outline === 'dashed' ? '2 2' : undefined}\n />\n <text y={TOKEN_R + 11} textAnchor=\"middle\" className=\"gv-spring-token-label\">{token.label}</text>\n </g>\n )\n })}\n </svg>\n </Plot>\n )\n}\n\nfunction spreadCoincident(points: { cx: number; cy: number }[]): { cx: number; cy: number }[] {\n const groups: { cx: number; cy: number; members: number[] }[] = []\n points.forEach((p, i) => {\n const g = groups.find(g => Math.hypot(g.cx - p.cx, g.cy - p.cy) < TOKEN_R)\n if (g) g.members.push(i)\n else groups.push({ cx: p.cx, cy: p.cy, members: [i] })\n })\n const out = points.map(p => ({ cx: p.cx, cy: p.cy }))\n for (const g of groups) {\n if (g.members.length === 1) continue\n // A ring wide enough that neighbours do not overlap: n tokens need a\n // circumference of about n diameters.\n const r = Math.max(TOKEN_R * 1.6, (g.members.length * TOKEN_R) / Math.PI)\n g.members.forEach((i, k) => {\n const a = -Math.PI / 2 + (2 * Math.PI * k) / g.members.length\n out[i] = { cx: g.cx + r * Math.cos(a), cy: g.cy + r * Math.sin(a) }\n })\n }\n return out\n}\n\n// ── Force-curve read-out (viewBox units) ─────────────────────────────────────\n// Exported so wrappers compute their polyline points in this coordinate space.\nexport const CURVE_W = 300\nexport const CURVE_H = 130\nexport const CURVE_SAMPLES = 56\n\nexport interface CurveItem {\n id: string\n // Polyline points string, pre-computed by the wrapper in viewBox coords.\n points: string\n color: string\n // Optional vertical marker (e.g. rest length / core radius) in viewBox x.\n markerX?: number\n // Optional short label drawn at the curve's end point (e.g. \"X\" / \"Y\" for the\n // band curves, where there is no map token to link the identity to).\n label?: string\n}\n\n// Parse the last \"x,y\" pair out of a polyline points string.\nfunction lastPoint(points: string): { x: number; y: number } | null {\n const pts = points.trim().split(/\\s+/)\n const last = pts[pts.length - 1]?.split(',')\n if (!last || last.length < 2) return null\n return { x: Number(last[0]), y: Number(last[1]) }\n}\n\n// A \"nice\" grid step (1/2/5 × 10ⁿ) giving roughly `target` divisions over\n// [0, max]. Bounding the division count is the adaptive behaviour: as the curve's\n// x-domain grows or shrinks while tuning, the step jumps so the gridlines never\n// crowd or thin out — they visibly re-space, signalling the scale changed.\nexport function niceStep(max: number, target: number): number {\n if (!(max > 0)) return 1\n const raw = max / target\n const mag = Math.pow(10, Math.floor(Math.log10(raw)))\n const norm = raw / mag\n const step = norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10\n return step * mag\n}\n\nexport function ForceCurves({\n curves, baselineY, hoveredId, onHover, xLabel, yLabel, exp, ariaLabel, xMax,\n}: {\n curves: CurveItem[]\n // y of an optional horizontal baseline; omit for none.\n baselineY?: number\n hoveredId: string | null\n onHover: (id: string | null) => void\n xLabel: string\n yLabel: string\n exp: GlobalSlider & { label?: string }\n ariaLabel: string\n // The x-axis domain max (in data units). When set, draws value-anchored\n // gridlines at a nice step — they re-space as the domain changes while tuning.\n xMax?: number\n}) {\n const gridXs: number[] = []\n if (xMax !== undefined && xMax > 0) {\n const step = niceStep(xMax, 6)\n for (let v = step; v < xMax * 0.999; v += step) gridXs.push((v / xMax) * CURVE_W)\n }\n return (\n <div className=\"gv-spring-curves\">\n <div className=\"gv-spring-axis-label gv-spring-curves-axis-y\">{yLabel}</div>\n\n <svg\n className=\"gv-spring-curves-svg\"\n viewBox={`0 0 ${CURVE_W} ${CURVE_H}`}\n role=\"img\"\n aria-label={ariaLabel}\n >\n <rect x={0} y={0} width={CURVE_W} height={CURVE_H} className=\"gv-spring-curve-frame\" />\n {gridXs.map((px, i) => (\n <line key={`g${i}`} x1={px.toFixed(1)} y1={0} x2={px.toFixed(1)} y2={CURVE_H} className=\"gv-ctl-grid-line\" />\n ))}\n {baselineY !== undefined && (\n <line x1={0} y1={baselineY} x2={CURVE_W} y2={baselineY} className=\"gv-spring-curve-baseline\" />\n )}\n\n {curves.filter(c => c.id === hoveredId && c.markerX !== undefined).map(c => (\n <line key={`m${c.id}`} x1={c.markerX} y1={0} x2={c.markerX} y2={CURVE_H} className=\"gv-spring-curve-restmark\" />\n ))}\n\n {curves.map(c => {\n const dim = hoveredId !== null && hoveredId !== c.id\n const active = hoveredId === c.id\n return (\n <g key={c.id} className={`gv-spring-curve${dim ? ' dim' : ''}${active ? ' active' : ''}`}>\n <polyline\n points={c.points}\n className=\"gv-spring-curve-hit\"\n onPointerEnter={() => onHover(c.id)}\n onPointerLeave={() => onHover(null)}\n />\n <polyline\n points={c.points}\n className=\"gv-spring-curve-line\"\n style={{ stroke: c.color }}\n />\n {c.label && (() => {\n const p = lastPoint(c.points)\n if (!p) return null\n return (\n <text\n x={Math.min(p.x + 4, CURVE_W - 2)}\n y={clamp(p.y, 9, CURVE_H - 2)}\n className=\"gv-spring-curve-label\"\n style={{ fill: c.color }}\n textAnchor=\"end\"\n >\n {c.label}\n </text>\n )\n })()}\n </g>\n )\n })}\n </svg>\n\n <div className=\"gv-spring-axis-label gv-spring-curves-axis-x\">{xLabel}</div>\n\n <div className=\"gv-spring-curves-exp\" title={exp.title}>\n <span className=\"gv-spring-exp-label\">{exp.label ?? 'exp'}</span>\n <Slider\n min={exp.min} max={exp.max} step={exp.step}\n value={exp.value}\n onChange={exp.onChange}\n popId={exp.popId}\n />\n </div>\n </div>\n )\n}\n","// Push-section controls: a 2D \"charge map\" (the control) and a read-only\n// charge-falloff visualization (the read-out), linked by a shared hovered-type\n// state (the AxisValue). Thin wrappers over the shared ForceMap2D / ForceCurves\n// primitives — this file owns only the charge-specific math (one solid token\n// per node type, inverse-power falloff curves).\n\nimport React from 'react'\nimport type { ForceParams } from '../graph/simulation'\nimport { withPushOverride } from '../graph/simulation'\nimport { pushForKey } from '../graph/forces'\nimport type { ResolvedPhysics } from '../graph/forces'\n\nimport { useTaxonomy } from './graph-view/useTaxonomy'\nimport { bucketColor } from './controls/bucketColor'\nimport { ForceMap2D, ForceCurves, CURVE_W, CURVE_H, CURVE_SAMPLES } from './ForceMap'\nimport type { MapToken, CurveItem } from './ForceMap'\n\n// Shared charge / core-radius ranges. The map plots every node type on these\n// common axes so their relative repulsion strength and reach are comparable.\n// Charge is stored negative (repulsion) but plotted as magnitude.\nexport const CHARGE_MAG_MIN = 0\nexport const CHARGE_MAG_MAX = 1000\nexport const CHARGE_MAG_STEP = 10\nexport const CORE_RADIUS_MIN_AXIS = 0\nexport const CORE_RADIUS_MAX_AXIS = 100\nexport const CORE_RADIUS_STEP = 1\n\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v))\n\ninterface ChargeControlProps {\n params: ForceParams\n /** The dense tables the kernels read, resolved once by the view. */\n resolved: ResolvedPhysics\n /** The push buckets some visible node falls in — the only ones worth a token. */\n present: ReadonlySet<string>\n onParamChange: (patch: Partial<ForceParams>) => void\n // The hovered/active bucket on the push axis — links the map, the curves, and the canvas.\n hoveredBucket: string | null\n onHoverBucket: (b: string | null) => void\n}\n\n// ── 2D Charge Map ─────────────────────────────────────────────────────────────\n//\n// y = charge magnitude, x = core radius. One solid token per bucket of the push\n// axis. Dragging sets both the (negative) charge and the core radius at once.\nexport function ChargeMap({ params, resolved, present, onParamChange, hoveredBucket, onHoverBucket }: ChargeControlProps) {\n const taxonomy = useTaxonomy()\n const { push } = resolved\n // Only the buckets the visible set holds; the tuning keeps the rest.\n const shown = push.mapping.buckets.filter(b => present.has(b.id))\n const tokens: MapToken[] = shown.map(b => ({\n id: b.id,\n x: pushForKey(push, b.id).coreRadius,\n y: Math.abs(pushForKey(push, b.id).charge),\n colorA: bucketColor(taxonomy, push.mapping, b.id),\n outline: 'solid',\n label: b.abbreviation,\n tooltip: `${b.label} repulsion charge & core radius`,\n }))\n\n const handleDrag = (id: string, x: number, y: number) => {\n const t = id\n onParamChange({\n // One override per bucket, carrying both numbers the token encodes;\n // charge is stored negative (repulsion).\n pair: withPushOverride(params.pair, t, { charge: -y, coreRadius: x }),\n })\n }\n\n return (\n <ForceMap2D\n tokens={tokens}\n xAxis={{ min: CORE_RADIUS_MIN_AXIS, max: CORE_RADIUS_MAX_AXIS, step: CORE_RADIUS_STEP, label: 'core radius' }}\n yAxis={{ min: CHARGE_MAG_MIN, max: CHARGE_MAG_MAX, step: CHARGE_MAG_STEP, label: 'charge' }}\n onDrag={handleDrag}\n hoveredId={hoveredBucket}\n onHover={onHoverBucket}\n ariaLabel=\"Charge map: core radius versus charge magnitude\"\n xSlider={{\n value: params.coreRadiusMultiplier, min: 0.1, max: 3, step: 0.05,\n onChange: v => onParamChange({ coreRadiusMultiplier: v }),\n title: 'Scale every type’s core radius', ariaLabel: 'Scale all core radius',\n popId: 'coreRadiusMultiplier',\n }}\n ySlider={{\n value: params.pushMultiplier, min: 0, max: 3, step: 0.1,\n onChange: v => onParamChange({ pushMultiplier: v }),\n title: 'Scale every type’s charge', ariaLabel: 'Scale all charge',\n popId: 'pushMultiplier',\n }}\n />\n )\n}\n\n// ── Charge-falloff visualization (read-only) ──────────────────────────────────\n//\n// Plots each type's repulsion falloff push·|charge| / (d² + rEff²)^exp vs\n// distance (all-positive, baseline at the bottom of the frame), with the core\n// radius marked on the distance axis. The global `exp` (chargeExponent) is the\n// slider beneath.\nexport function ChargeCurves({ params, resolved, present, onParamChange, hoveredBucket, onHoverBucket }: ChargeControlProps) {\n const taxonomy = useTaxonomy()\n const { push } = resolved\n // Only the buckets the visible set holds; the tuning keeps the rest.\n const shown = push.mapping.buckets.filter(b => present.has(b.id))\n const pushMul = params.pushMultiplier\n const crMul = params.coreRadiusMultiplier\n const exp = params.chargeExponent\n\n const { curves, dMax } = React.useMemo<{ curves: CurveItem[]; dMax: number }>(() => {\n const rEffs = shown.map(b => crMul * pushForKey(push, b.id).coreRadius)\n const maxREff = Math.max(0, ...rEffs)\n // Show the falloff out to a few core radii so the plateau-then-drop shape\n // is legible even when the radii are small.\n const dMax = Math.max(150, maxREff * 4)\n\n let maxMag = 0\n const sampled = shown.map(({ id: t }) => {\n const q = Math.abs(pushForKey(push, t).charge) * pushMul\n const rEff = crMul * pushForKey(push, t).coreRadius\n const soft = rEff * rEff\n const pts: { d: number; v: number }[] = []\n for (let i = 0; i <= CURVE_SAMPLES; i++) {\n const d = (i / CURVE_SAMPLES) * dMax\n const v = q / Math.pow(d * d + soft, Math.max(exp, 0.01))\n if (v > maxMag) maxMag = v\n pts.push({ d, v })\n }\n return { type: t, rEff, pts }\n })\n\n const norm = maxMag > 0 ? maxMag : 1\n const ampH = CURVE_H * 0.95 // leave a little headroom at the top\n const xFor = (d: number) => (d / dMax) * CURVE_W\n const yFor = (v: number) => clamp(CURVE_H - (v / norm) * ampH, 0, CURVE_H)\n\n const built = sampled.map(({ type, rEff, pts }) => ({\n id: type,\n color: bucketColor(taxonomy, push.mapping, type),\n markerX: xFor(rEff),\n points: pts.map(p => `${xFor(p.d).toFixed(1)},${yFor(p.v).toFixed(1)}`).join(' '),\n }))\n return { curves: built, dMax }\n }, [params, pushMul, push, crMul, exp, taxonomy, present])\n\n return (\n <ForceCurves\n curves={curves}\n xMax={dMax}\n hoveredId={hoveredBucket}\n onHover={onHoverBucket}\n xLabel=\"distance\"\n yLabel=\"force\"\n ariaLabel=\"Charge falloff curves\"\n exp={{\n value: exp, min: 0.5, max: 1.0, step: 0.05,\n onChange: v => onParamChange({ chargeExponent: v }),\n title: 'Power of (d² + r²) in the charge falloff. 1 = inverse-square; higher = sharper drop-off.',\n popId: 'chargeExponent',\n }}\n />\n )\n}\n","// Pull-section controls: a 2D \"spring map\" (the control) and a read-only\n// force-curve visualization (the read-out), linked by a shared hovered-edge\n// state (keyed by the edge category's k-field). Both are thin wrappers over\n// the shared ForceMap2D / ForceCurves primitives — this file owns only the\n// spring-specific math (split-colour tokens from the resolved spring\n// categories, signed-response curves) and feeds presentational data into the\n// primitives.\n\nimport React from 'react'\nimport type { ForceParams } from '../graph/simulation'\nimport { withSpringOverride } from '../graph/simulation'\nimport type { ResolvedPhysics } from '../graph/forces'\nimport type { EdgeCategoryDescription } from '../graph/forces'\n\nimport { useTaxonomy } from './graph-view/useTaxonomy'\nimport { bucketColor } from './controls/bucketColor'\nimport { ForceMap2D, ForceCurves, CURVE_W, CURVE_H, CURVE_SAMPLES } from './ForceMap'\nimport type { MapToken, CurveItem } from './ForceMap'\n\n// Shared k / rest ranges. The map plots every edge category on these common\n// axes so their relative stiffness / length is directly comparable.\nexport const EDGE_K_MIN = 0\nexport const EDGE_K_MAX = 2\nexport const EDGE_K_STEP = 0.05\nexport const EDGE_REST_MIN = 0\nexport const EDGE_REST_MAX = 1000\nexport const EDGE_REST_STEP = 10\n\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v))\n\n/** One token per category present in the graph, in first-seen order; ids the table cannot describe are skipped. */\nfunction describedCategories(spring: ResolvedPhysics['spring'], present: ReadonlySet<string>): EdgeCategoryDescription[] {\n const out: EdgeCategoryDescription[] = []\n for (const id of present) {\n const c = spring.describe(id)\n if (c) out.push(c)\n }\n return out\n}\n\ninterface SpringControlProps {\n params: ForceParams\n /** The dense tables the kernels read, resolved once by the view. */\n resolved: ResolvedPhysics\n /** The categories some edge of the graph falls in — the only ones worth a token. */\n present: ReadonlySet<string>\n // Routes through the panel's emitParamChange → live reheat.\n onParamChange: (patch: Partial<ForceParams>) => void\n // The hovered/active edge category, identified by its k-field key.\n hoveredEdge: string | null\n onHoverEdge: (key: string | null) => void\n}\n\n// ── 2D Spring Map ───────────────────────────────────────────────────────────\n//\n// x = rest length, y = stiffness (k). Each edge category is a draggable\n// split-colour token; dragging sets both rest and k at once.\nexport function SpringMap({ params, resolved, present, onParamChange, hoveredEdge, onHoverEdge }: SpringControlProps) {\n const taxonomy = useTaxonomy()\n const { spring } = resolved\n const shown = describedCategories(spring, present)\n const tokens: MapToken[] = shown.map(c => {\n const ph = spring.physicsFor(c.id)\n return {\n id: c.id,\n x: ph.distance,\n y: ph.strength,\n colorA: bucketColor(taxonomy, spring.mapping, c.sourceValue),\n colorB: bucketColor(taxonomy, spring.mapping, c.targetValue),\n outline: c.relation === 'containment' ? 'dashed' : 'solid',\n label: c.label,\n tooltip: c.tooltip,\n }\n })\n\n // Drag id is the category id; one override carries both stiffness and length.\n const handleDrag = (id: string, x: number, y: number) => {\n onParamChange({ pair: withSpringOverride(params.pair, id, { strength: y, distance: x }) })\n }\n\n return (\n <ForceMap2D\n tokens={tokens}\n xAxis={{ min: EDGE_REST_MIN, max: EDGE_REST_MAX, step: EDGE_REST_STEP, label: 'length' }}\n yAxis={{ min: EDGE_K_MIN, max: EDGE_K_MAX, step: EDGE_K_STEP, label: 'stiffness' }}\n onDrag={handleDrag}\n hoveredId={hoveredEdge}\n onHover={onHoverEdge}\n ariaLabel=\"Spring map: rest length versus stiffness\"\n xSlider={{\n value: params.distanceMultiplier, min: 0.05, max: 3, step: 0.05,\n onChange: v => onParamChange({ distanceMultiplier: v }),\n title: 'Scale every spring’s rest length', ariaLabel: 'Scale all length',\n popId: 'distanceMultiplier',\n }}\n ySlider={{\n value: params.pullMultiplier, min: 0, max: 3, step: 0.1,\n onChange: v => onParamChange({ pullMultiplier: v }),\n title: 'Scale every spring’s stiffness', ariaLabel: 'Scale all stiffness',\n popId: 'pullMultiplier',\n }}\n />\n )\n}\n\n// ── Force-curve visualization (read-only) ────────────────────────────────────\n//\n// Plots each spring's displacement response stiffness·sign(Δ)|Δ|^exp around its\n// rest length (the /d direction term from the sim is dropped — this shows the\n// spring *shape*, not the per-tick force vector). Zero crossing = length. The\n// baseline sits low so the readable tension side gets most of the height and\n// the compression side is cropped.\n\n// Baseline at 78% down: tension (positive) above gets the bulk of the height.\nconst ZERO_Y = CURVE_H * 0.78\n\nfunction springResponse(d: number, kEff: number, restEff: number, exp: number): number {\n const disp = d - restEff\n return kEff * Math.sign(disp) * Math.pow(Math.abs(disp), exp)\n}\n\nexport function SpringCurves({ params, resolved, present, onParamChange, hoveredEdge, onHoverEdge }: SpringControlProps) {\n const taxonomy = useTaxonomy()\n const { spring } = resolved\n const shown = React.useMemo(() => describedCategories(spring, present), [spring, present])\n const pull = params.pullMultiplier\n const dist = params.distanceMultiplier\n const exp = params.linkExponent\n\n const { curves, dMax } = React.useMemo<{ curves: CurveItem[]; dMax: number }>(() => {\n const restEffs = shown.map(c => spring.physicsFor(c.id).distance * dist)\n const dMax = Math.max(60, Math.max(...restEffs) * 2.2)\n\n let maxMag = 0\n const sampled = shown.map(edge => {\n const ph = spring.physicsFor(edge.id)\n const kEff = ph.strength * pull\n const restEff = ph.distance * dist\n const pts: { d: number; v: number }[] = []\n for (let i = 0; i <= CURVE_SAMPLES; i++) {\n const d = (i / CURVE_SAMPLES) * dMax\n const v = springResponse(d, kEff, restEff, exp)\n if (Math.abs(v) > maxMag) maxMag = Math.abs(v)\n pts.push({ d, v })\n }\n return { edge, restEff, pts }\n })\n\n const norm = maxMag > 0 ? maxMag : 1\n const ampH = ZERO_Y // tension amplitude spans baseline → top edge (y=0)\n const xFor = (d: number) => (d / dMax) * CURVE_W\n // Symmetric scale by the tension amplitude; the compression side simply\n // clips at the bottom (intentional crop).\n const yFor = (v: number) => clamp(ZERO_Y - (v / norm) * ampH, 0, CURVE_H)\n\n const built = sampled.map(({ edge, restEff, pts }) => ({\n id: edge.id,\n color: bucketColor(taxonomy, spring.mapping, edge.sourceValue),\n markerX: xFor(restEff),\n points: pts.map(p => `${xFor(p.d).toFixed(1)},${yFor(p.v).toFixed(1)}`).join(' '),\n }))\n return { curves: built, dMax }\n // `taxonomy` belongs here: the body reads it for the split-colour tokens.\n // Its sibling ChargeCurves already listed it, and the asymmetry was the bug\n // — a taxonomy swap left these curves drawn in the previous taxonomy's\n // colours, on its edge set, with no error.\n }, [spring, shown, pull, dist, exp, taxonomy])\n\n return (\n <ForceCurves\n curves={curves}\n xMax={dMax}\n baselineY={ZERO_Y}\n hoveredId={hoveredEdge}\n onHover={onHoverEdge}\n xLabel=\"distance\"\n yLabel=\"force\"\n ariaLabel=\"Spring force response curves\"\n exp={{\n value: exp, min: 0.5, max: 3, step: 0.1,\n onChange: v => onParamChange({ linkExponent: v }),\n title: 'Power of displacement in the spring force. 1 = linear (Hooke); higher = softer near rest, stiffer far out.',\n popId: 'linkExponent',\n }}\n />\n )\n}\n","// Equation — the governing-formula display block shared by every force section.\n//\n// A string child renders as preformatted text (multi-line dynamics formula); a\n// node child renders inline (the fraction/superscript JSX). Centralizing it gives\n// one home for the formula's typography and visual separation (border/background).\n\nimport React from 'react'\n\nexport function Equation({ children }: { children: React.ReactNode }) {\n if (children === '' || children === null || children === undefined) return null\n return typeof children === 'string'\n ? <pre className=\"gv-graph-control-equation-formula\">{children}</pre>\n : <div className=\"gv-graph-control-equation-formula\">{children}</div>\n}\n","import React from 'react'\nimport { Equation } from './Equation'\n\n// One force section's content: the optional subtitle, the governing equation\n// (transparency), and the controls. The wrapper is the hover target that\n// drives the canvas force-field preview via onHover.\nexport function EquationSection({ subtitle, equation, onHover, children }: {\n subtitle: string\n equation: React.ReactNode\n onHover: (hovered: boolean) => void\n children: React.ReactNode\n}) {\n return (\n <div\n className=\"gv-graph-control-equation-section\"\n onMouseEnter={() => onHover(true)}\n onMouseLeave={() => onHover(false)}\n >\n {subtitle && (\n <div className=\"gv-graph-control-equation-header\">\n <span className=\"gv-graph-control-equation-subtitle\">({subtitle})</span>\n </div>\n )}\n <Equation>{equation}</Equation>\n <div className=\"gv-graph-control-equation-body\">\n {children}\n </div>\n </div>\n )\n}\n","import React from 'react'\n\n// A sub-level of tabs inside one force tab: underline tabs like the main strip,\n// marked as a sub-level by an accent underline. Bodies stack in one grid cell\n// with only the active one visible, so switching never resizes the panel — the\n// same trick the main tabs use.\n\nexport function SubTabs({ label, children }: { label: string; children: React.ReactNode }) {\n return <div className=\"gv-force-subtabs\" role=\"tablist\" aria-label={label}>{children}</div>\n}\n\n/**\n * One sub-tab: a selectable label, optionally led by a control that belongs to\n * the tab itself (Gravity puts each force's on/off switch there). Selecting\n * and the leading control are independent, so a tab's state stays legible\n * while another's body is shown.\n */\nexport function SubTab({ label, active, onSelect, leading }: {\n label: string\n active: boolean\n onSelect: () => void\n leading?: React.ReactNode\n}) {\n return (\n <div\n role=\"tab\"\n aria-selected={active}\n tabIndex={0}\n className={`gv-force-subtab${active ? ' active' : ''}`}\n onClick={onSelect}\n onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelect() } }}\n >\n {leading}\n <span className=\"gv-force-subtab-label\">{label}</span>\n </div>\n )\n}\n\nexport function SubTabBodies({ children }: { children: React.ReactNode }) {\n return <div className=\"gv-force-bodies\">{children}</div>\n}\n\nexport function SubTabBody({ active, children }: { active: boolean; children: React.ReactNode }) {\n return <div className={`gv-force-body${active ? ' active' : ''}`} aria-hidden={!active}>{children}</div>\n}\n","// The Push/Pull tab: one axis dropdown shared by both pair forces, then Push\n// and Pull as sub-tabs — styled like Gravity's Band/Topological, without the\n// switches, since neither force has an off. Each body is its equation, its 2D\n// map, and its force-curve read-out.\n\nimport React from 'react'\nimport type { PresentSets } from './GraphControlPanel'\nimport type { ForceParams, AxisOption } from '../graph/simulation'\nimport { fitsCeiling, TOKEN_CEILING } from '../graph/simulation'\nimport type { ResolvedPhysics } from '../graph/forces'\nimport type { AxisMapping } from '../graph/axis'\nimport { ChargeMap, ChargeCurves } from './ChargeControls'\nimport { SpringMap, SpringCurves } from './SpringControls'\nimport { EquationSection } from './controls/EquationSection'\nimport { SubTabs, SubTab, SubTabBodies, SubTabBody } from './controls/SubTab'\nimport { FormulaValue } from './controls/PopContext'\nimport type { ForceSection } from './GraphControlPanel'\n\ntype PairForce = 'push' | 'pull'\n\ninterface PushPullProps {\n params: ForceParams\n resolved: ResolvedPhysics\n /** Every axis the graph admits, with what each would cost to show. */\n axes: readonly AxisOption[]\n /** What the visible set holds: push buckets and spring categories worth a token. */\n present: Pick<PresentSets, 'push' | 'springs'>\n onParamChange: (patch: Partial<ForceParams>) => void\n onAxisChange: (mapping: AxisMapping) => void\n onActiveSection: (section: ForceSection) => void\n hoveredPushBucket: string | null\n onHoverPushBucket: (b: string | null) => void\n hoveredEdge: string | null\n onHoverEdge: (key: string | null) => void\n}\n\nconst PUSH_EQUATION = (\n <span className=\"gv-eq-line\">\n F =\n <span className=\"gv-eq-frac\">\n <span className=\"gv-eq-num\"><FormulaValue id=\"pushMultiplier\">charge</FormulaValue></span>\n <span className=\"gv-eq-den\">(d² + <FormulaValue id=\"coreRadiusMultiplier\">r</FormulaValue>²)<sup><FormulaValue id=\"chargeExponent\">exp</FormulaValue></sup></span>\n </span>\n </span>\n)\n\nconst PULL_EQUATION = (\n <span className=\"gv-eq-line\">\n F = <FormulaValue id=\"pullMultiplier\">stiffness</FormulaValue> ×\n <span className=\"gv-eq-frac\">\n <span className=\"gv-eq-num\">(d − <FormulaValue id=\"distanceMultiplier\">length</FormulaValue>)<sup><FormulaValue id=\"linkExponent\">exp</FormulaValue></sup></span>\n <span className=\"gv-eq-den\">d</span>\n </span>\n </span>\n)\n\n/** What the dropdown says about an axis: its cost when it fits, why not when it does not. */\nfunction optionLabel(o: AxisOption): string {\n return fitsCeiling(o)\n ? `${o.mapping.label} (${o.pushTokens} · ${o.pullTokens})`\n : `${o.mapping.label} — ${Math.max(o.pushTokens, o.pullTokens)} tokens (max ${TOKEN_CEILING})`\n}\n\nexport function PushPullControls({\n params, resolved, axes, present, onParamChange, onAxisChange, onActiveSection,\n hoveredPushBucket, onHoverPushBucket, hoveredEdge, onHoverEdge,\n}: PushPullProps) {\n const [sub, setSub] = React.useState<PairForce>('push')\n const current = params.pair.mapping.axis\n return (\n <div className=\"gv-pushpull-controls\">\n {/* The axis both forces key on — above the sub-tabs so it stays visible\n whichever force is being edited. */}\n <div className=\"gv-axis-row\">\n <label className=\"gv-axis-label\" htmlFor=\"pushpull-axis\">axis</label>\n <select\n id=\"pushpull-axis\"\n className=\"gv-axis-select\"\n value={current}\n title=\"The axis push and pull key on. Tokens are the visible set's: a push token per bucket some visible node falls in, a pull token per spring category some sprung edge falls in; the counts measure the whole graph\"\n onChange={e => {\n const option = axes.find(o => o.mapping.axis === e.target.value)\n if (option) onAxisChange(option.mapping)\n }}\n >\n {axes.map(o => (\n <option key={o.mapping.axis} value={o.mapping.axis} disabled={!fitsCeiling(o)}>\n {optionLabel(o)}\n </option>\n ))}\n </select>\n </div>\n\n <SubTabs label=\"Pair force\">\n <SubTab label=\"Push\" active={sub === 'push'} onSelect={() => setSub('push')} />\n <SubTab label=\"Pull\" active={sub === 'pull'} onSelect={() => setSub('pull')} />\n </SubTabs>\n\n <SubTabBodies>\n <SubTabBody active={sub === 'push'}>\n <EquationSection subtitle=\"\" equation={PUSH_EQUATION} onHover={h => onActiveSection(h ? 'push' : null)}>\n <ChargeMap params={params} resolved={resolved} present={present.push} onParamChange={onParamChange}\n hoveredBucket={hoveredPushBucket} onHoverBucket={onHoverPushBucket} />\n <ChargeCurves params={params} resolved={resolved} present={present.push} onParamChange={onParamChange}\n hoveredBucket={hoveredPushBucket} onHoverBucket={onHoverPushBucket} />\n </EquationSection>\n </SubTabBody>\n <SubTabBody active={sub === 'pull'}>\n <EquationSection subtitle=\"\" equation={PULL_EQUATION} onHover={h => onActiveSection(h ? 'pull' : null)}>\n <SpringMap params={params} resolved={resolved} present={present.springs} onParamChange={onParamChange}\n hoveredEdge={hoveredEdge} onHoverEdge={onHoverEdge} />\n <SpringCurves params={params} resolved={resolved} present={present.springs} onParamChange={onParamChange}\n hoveredEdge={hoveredEdge} onHoverEdge={onHoverEdge} />\n </EquationSection>\n </SubTabBody>\n </SubTabBodies>\n </div>\n )\n}\n","// One layout axis of the Gravity tab — X or Y — as a sub-tab body: which\n// mapping or scalar the axis keys on, one column per bucket carrying the bucket's rest\n// band as two handles, the axis's own strength in the left gutter,\n// and the band force curve beneath. Both axes are this one component:\n// they are the same kind of thing, so they get the same surface.\n\nimport React from 'react'\nimport type { ForceParams, LayoutAxisOption, AxisTuning, BandRange, LayoutTuning } from '../graph/simulation'\nimport { TOKEN_CEILING, layoutAxisKey, mappedTuning, withOverride } from '../graph/simulation'\nimport type { ScalarSpan } from '../graph/scalar'\nimport type { ResolvedAxis, ResolvedPhysics } from '../graph/forces'\nimport { bandRangeFor, isScalarAxis } from '../graph/forces'\nimport { useTaxonomy } from './graph-view/useTaxonomy'\nimport { bucketColor } from './controls/bucketColor'\nimport { Plot } from './controls/Plot'\nimport { Slider } from './controls/Slider'\nimport { Equation } from './controls/Equation'\nimport { FormulaValue } from './controls/PopContext'\nimport { ForceCurves, CURVE_W, CURVE_H, CURVE_SAMPLES } from './ForceMap'\nimport type { CurveItem } from './ForceMap'\n\nexport type LayoutAxis = 'x' | 'y'\n\n/** A hovered bucket on one layout axis — links the axis plot to the canvas overlay. */\nexport interface GravityHover {\n axis: LayoutAxis\n bucket: string\n}\n\ninterface AxisControlsProps {\n axis: LayoutAxis\n params: ForceParams\n resolved: ResolvedPhysics\n /** Every axis this one may key on: mappings whose bucket count fits the ceiling, and computed scalars. */\n axes: readonly LayoutAxisOption[]\n /** The buckets some visible node falls in on this axis — the only columns worth drawing. */\n present: ReadonlySet<string>\n onParamChange: (patch: Partial<ForceParams>) => void\n onRelayout: (patch: Partial<ForceParams>) => void\n hovered: GravityHover | null\n onHover: (h: GravityHover | null) => void\n}\n\nconst tuningKey = (axis: LayoutAxis): 'bandX' | 'bandY' => (axis === 'x' ? 'bandX' : 'bandY')\nconst strengthKey = (axis: LayoutAxis): 'gravityX' | 'gravityY' => (axis === 'x' ? 'gravityX' : 'gravityY')\n// Patches spelled out per axis, so the compiler checks the value against the\n// field — a computed key under a cast would let a tuning land in a strength.\nconst tuningPatch = (axis: LayoutAxis, tuning: LayoutTuning): Partial<ForceParams> =>\n axis === 'x' ? { bandX: tuning } : { bandY: tuning }\nconst strengthPatch = (axis: LayoutAxis, value: number): Partial<ForceParams> =>\n axis === 'x' ? { gravityX: value } : { gravityY: value }\n\n// ── Axis plot geometry ─────────────────────────────────────────────────────────\n// One column per bucket; the vertical is the axis's world coordinate. Rest\n// bands render as a stripe per column with two draggable handles on top.\nconst AP_W = 300\nconst AP_H = 210\nconst AP_PAD = { top: 16, right: 8, bottom: 2, left: 8 }\nconst PL = AP_PAD.left\nconst PR = AP_W - AP_PAD.right\nconst PT = AP_PAD.top\nconst PB = AP_H - AP_PAD.bottom\nconst W_MIN = -600\nconst W_MAX = 600\nconst W_STEP = 10\nconst DOT_R = 5\n\n// The plot is vertical for both axes. For Y it reads as the screen does —\n// a larger y is lower. For X it reads as a rotated chart: a larger x (further\n// right on the canvas) is higher, so a scalar's \"1 at the right\" sits at the\n// top of the X plot as \"1 at the top\" sits at the top of the Y plot.\nconst yForWorld = (axis: LayoutAxis, v: number) => {\n const t = (v - W_MIN) / (W_MAX - W_MIN)\n return axis === 'y' ? PT + t * (PB - PT) : PB - t * (PB - PT)\n}\nconst valForY = (axis: LayoutAxis, y: number) => {\n const t = axis === 'y' ? (y - PT) / (PB - PT) : (PB - y) / (PB - PT)\n return W_MIN + t * (W_MAX - W_MIN)\n}\nconst clamp = (v: number, lo: number, hi: number) => Math.max(lo, Math.min(hi, v))\nconst snap = (v: number, step: number) => Math.round(v / step) * step\n\nexport function AxisControls({ axis, params, resolved, axes, present, onParamChange, onRelayout, hovered, onHover }: AxisControlsProps) {\n const taxonomy = useTaxonomy()\n const key = tuningKey(axis)\n const tuning: LayoutTuning = params[key]\n const table = axis === 'x' ? resolved.bandX : resolved.bandY\n const strength = params[strengthKey(axis)]\n const label = axis.toUpperCase()\n const ignored = axis === 'x' && params.radial\n const current = layoutAxisKey(tuning)\n\n const onAxisChange = (value: string) => {\n if (value === current) return\n const chosen = axes.find(o => layoutAxisKey(o) === value)\n if (!chosen) return\n // A new axis starts from the taxonomy: the declared bands on a mapping's\n // buckets, or the declared extent as a scalar's span (nothing stored).\n onRelayout(tuningPatch(axis, chosen.kind === 'scalar'\n ? { scalar: chosen.scalar.id }\n : { mapping: chosen.option.mapping, overrides: new Map() }))\n }\n\n return (\n <div className={`gv-gravity-section-tools${strength > 0 && !ignored ? '' : ' inactive'}`}>\n {/* The equation varies by kind: outside a band, or away from the scalar's point. */}\n <Equation>{isScalarAxis(table)\n ? <>F = <FormulaValue id={`bandStrength${label}`}>strength</FormulaValue> × d<sup><FormulaValue id=\"bandExp\">exp</FormulaValue></sup>, d = |p − p<sub>0</sub> − s·(p<sub>1</sub> − p<sub>0</sub>)|</>\n : <>F = <FormulaValue id={`bandStrength${label}`}>strength</FormulaValue> × d<sup><FormulaValue id=\"bandExp\">exp</FormulaValue></sup></>}</Equation>\n\n <div className=\"gv-axis-row\">\n <label className=\"gv-axis-label\" htmlFor={`gravity-axis-${axis}`}>axis</label>\n <select\n id={`gravity-axis-${axis}`}\n className=\"gv-axis-select\"\n value={current}\n title={`What the ${label} axis keys on: one column per bucket of a mapping, or a computed scalar as a gradient`}\n onChange={e => onAxisChange(e.target.value)}\n >\n {axes.map(o => o.kind === 'scalar'\n ? <option key={layoutAxisKey(o)} value={layoutAxisKey(o)} title={o.scalar.tooltip}>{o.scalar.label} — computed</option>\n : <option key={layoutAxisKey(o)} value={layoutAxisKey(o)} disabled={o.option.pushTokens > TOKEN_CEILING}>\n {o.option.pushTokens > TOKEN_CEILING ? `${o.option.mapping.label} — ${o.option.pushTokens} columns (max ${TOKEN_CEILING})` : `${o.option.mapping.label} (${o.option.pushTokens})`}\n </option>)}\n </select>\n {ignored && <span className=\"gv-axis-note\">ignored in radial</span>}\n </div>\n\n {/* The resolved axis decides the kind; the tuning is narrowed to match, loudly. */}\n {isScalarAxis(table) ? (\n <ScalarPlot\n axis={axis}\n radial={params.radial}\n label={table.scalar.label}\n tooltip={table.scalar.tooltip}\n span={table.span}\n strength={strength}\n onStrength={v => onParamChange(strengthPatch(axis, v))}\n onSpan={span => onParamChange(tuningPatch(axis, { scalar: table.scalar.id, span }))}\n />\n ) : (\n <AxisPlot\n axis={axis}\n radial={params.radial}\n tuning={mappedTuning(tuning)}\n table={table}\n present={present}\n strength={strength}\n onStrength={v => onParamChange(strengthPatch(axis, v))}\n onBand={(bucket, next) => onParamChange(tuningPatch(axis, withOverride(mappedTuning(tuning), bucket, next)))}\n hovered={hovered}\n onHover={onHover}\n color={bucket => bucketColor(taxonomy, mappedTuning(tuning).mapping, bucket)}\n />\n )}\n\n <AxisCurve axis={axis} params={params} onParamChange={onParamChange} />\n </div>\n )\n}\n\n// A scalar axis as a control: a gradient from the end where a score of 0\n// rests to the end where 1 rests, in place of the columns. The two ends\n// are handles that move freely — past each other flips the axis. Where each\n// node sits along it is the scalar's answer, not the user's.\nfunction ScalarPlot({ axis, radial, label, tooltip, span, strength, onStrength, onSpan }: {\n axis: LayoutAxis\n radial: boolean\n label: string\n tooltip: string\n span: ScalarSpan\n strength: number\n onStrength: (v: number) => void\n onSpan: (span: ScalarSpan) => void\n}) {\n const svgRef = React.useRef<SVGSVGElement>(null)\n const dragRef = React.useRef<keyof ScalarSpan | null>(null)\n const axisLabel = axis.toUpperCase()\n const x = (PL + PR) / 2\n const userY = (e: React.PointerEvent): number => {\n const svg = svgRef.current\n if (!svg) return 0\n const pt = svg.createSVGPoint()\n pt.x = e.clientX\n pt.y = e.clientY\n const ctm = svg.getScreenCTM()\n if (!ctm) return 0\n return pt.matrixTransform(ctm.inverse()).y\n }\n const handleDown = (e: React.PointerEvent, end: keyof ScalarSpan) => {\n e.preventDefault()\n ;(e.currentTarget as Element).setPointerCapture(e.pointerId)\n dragRef.current = end\n }\n const handleMove = (e: React.PointerEvent) => {\n const end = dragRef.current\n if (!end) return\n onSpan({ ...span, [end]: clamp(snap(valForY(axis, userY(e)), W_STEP), W_MIN, W_MAX) })\n }\n const handleUp = (e: React.PointerEvent) => {\n dragRef.current = null\n ;(e.currentTarget as Element).releasePointerCapture?.(e.pointerId)\n }\n const y0 = yForWorld(axis, span.zero)\n const y1 = yForWorld(axis, span.one)\n const gradientId = React.useId()\n const handle = (end: keyof ScalarSpan, y: number) => (\n <g key={end}>\n <text x={PL + 10} y={y + 4} textAnchor=\"middle\" className=\"gv-gravity-band-label\">{end === 'zero' ? '0' : '1'}</text>\n <circle cx={x} cy={y} r={DOT_R} className=\"gv-gravity-band-dot gv-gravity-scalar-dot\"\n onPointerDown={e => handleDown(e, end)} onPointerMove={handleMove} onPointerUp={handleUp}>\n <title>{`where a score of ${end === 'zero' ? 0 : 1} rests on ${axisLabel}: ${span[end]}`}</title>\n </circle>\n </g>\n )\n return (\n <Plot\n yLabel={`${axisLabel} strength`}\n ySlider={\n <Slider\n orientation=\"vertical\"\n min={0} max={0.25} step={0.005}\n value={strength}\n onChange={onStrength}\n title={`${axisLabel} pull strength (scale all); 0 switches the axis off`}\n ariaLabel={`${axisLabel} strength`}\n popId={`bandStrength${axisLabel}`}\n />\n }\n bottom={<div className=\"gv-ctl-plot-xlabel\">{radial && axis === 'y' ? 'ring: the span’s low end innermost' : `${axisLabel} position: ${label}, 0 → 1`}</div>}\n >\n <svg\n ref={svgRef}\n className=\"gv-gravity-band-svg\"\n viewBox={`0 0 ${AP_W} ${AP_H}`}\n role=\"group\"\n aria-label={`${axisLabel} axis: ${label}, a computed scalar; drag the 0 and 1 ends`}\n >\n <defs>\n <linearGradient id={gradientId} x1=\"0\" y1={y0} x2=\"0\" y2={y1} gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stopColor=\"var(--gv-color-text-muted)\" stopOpacity=\"0.15\" />\n <stop offset=\"1\" stopColor=\"var(--gv-color-text)\" stopOpacity=\"0.55\" />\n </linearGradient>\n </defs>\n <rect x={PL} y={PT} width={PR - PL} height={PB - PT} className=\"gv-gravity-band-bg\" />\n <title>{tooltip}</title>\n <rect x={PL + 24} y={Math.min(y0, y1)} width={PR - PL - 48} height={Math.abs(y1 - y0)} fill={`url(#${gradientId})`} className=\"gv-gravity-scalar-gradient\" />\n <text x={x} y={PT - 6} textAnchor=\"middle\" className=\"gv-gravity-band-label\">{label}</text>\n <line x1={x} y1={PT} x2={x} y2={PB} className=\"gv-gravity-band-track\" />\n {handle('zero', y0)}\n {handle('one', y1)}\n </svg>\n </Plot>\n )\n}\n\nfunction AxisPlot({ axis, radial, tuning, table, present, strength, onStrength, onBand, hovered, onHover, color }: {\n axis: LayoutAxis\n radial: boolean\n tuning: AxisTuning<BandRange>\n table: ResolvedAxis<BandRange>\n present: ReadonlySet<string>\n strength: number\n onStrength: (v: number) => void\n onBand: (bucket: string, next: BandRange) => void\n hovered: GravityHover | null\n onHover: (h: GravityHover | null) => void\n color: (bucket: string) => string\n}) {\n const svgRef = React.useRef<SVGSVGElement>(null)\n const dragRef = React.useRef<{ bucket: string; edge: 'min' | 'max' } | null>(null)\n // Only the buckets the visible set holds; the tuning keeps the rest.\n const buckets = tuning.mapping.buckets.filter(b => present.has(b.id))\n const colX = (i: number) => PL + ((i + 0.5) / Math.max(1, buckets.length)) * (PR - PL)\n const label = axis.toUpperCase()\n\n const userY = (e: React.PointerEvent): number => {\n const svg = svgRef.current\n if (!svg) return 0\n const pt = svg.createSVGPoint()\n pt.x = e.clientX\n pt.y = e.clientY\n const ctm = svg.getScreenCTM()\n if (!ctm) return 0\n return pt.matrixTransform(ctm.inverse()).y\n }\n const handleDown = (e: React.PointerEvent, bucket: string, edge: 'min' | 'max') => {\n e.preventDefault()\n ;(e.currentTarget as Element).setPointerCapture(e.pointerId)\n dragRef.current = { bucket, edge }\n }\n const handleMove = (e: React.PointerEvent) => {\n const drag = dragRef.current\n if (!drag) return\n const cur = bandRangeFor(table, drag.bucket)\n const v = clamp(snap(valForY(axis, userY(e)), W_STEP), W_MIN, W_MAX)\n onBand(drag.bucket, drag.edge === 'min'\n ? { min: Math.min(v, cur.max - W_STEP), max: cur.max }\n : { min: cur.min, max: Math.max(v, cur.min + W_STEP) })\n }\n const handleUp = (e: React.PointerEvent) => {\n dragRef.current = null\n ;(e.currentTarget as Element).releasePointerCapture?.(e.pointerId)\n }\n const isHovered = (bucket: string) => hovered !== null && hovered.axis === axis && hovered.bucket === bucket\n const anyHovered = hovered !== null && hovered.axis === axis\n\n return (\n <Plot\n yLabel={`${label} strength`}\n ySlider={\n <Slider\n orientation=\"vertical\"\n min={0} max={0.25} step={0.005}\n value={strength}\n onChange={onStrength}\n title={`${label} band-pull strength (scale all); 0 switches the axis off`}\n ariaLabel={`${label} band strength`}\n popId={`bandStrength${label}`}\n />\n }\n bottom={\n // In radial, Y reads each bucket's band CENTRE as its ring (width is\n // ignored, equal centres share a ring); the label says so in the same\n // slot, so the body does not change height when the mode flips.\n <div className=\"gv-ctl-plot-xlabel\">{radial && axis === 'y' ? 'ring: the band centre, innermost first' : `${label} position`}</div>\n }\n >\n <svg\n ref={svgRef}\n className=\"gv-gravity-band-svg\"\n viewBox={`0 0 ${AP_W} ${AP_H}`}\n role=\"group\"\n aria-label={`${label} axis: one column per bucket, its rest band as two handles`}\n >\n <rect x={PL} y={PT} width={PR - PL} height={PB - PT} className=\"gv-gravity-band-bg\" />\n <g className=\"gv-gravity-band-stripes\">\n {buckets.map((b, i) => {\n const r = bandRangeFor(table, b.id)\n const yTop = yForWorld(axis, r.min)\n const yBot = yForWorld(axis, r.max)\n const half = (PR - PL) / buckets.length / 2\n return (\n <rect\n key={b.id}\n x={colX(i) - half} y={yTop} width={half * 2} height={Math.max(0, yBot - yTop)}\n fill={color(b.id)}\n className={`gv-gravity-band-stripe${anyHovered && !isHovered(b.id) ? ' dim' : ''}${isHovered(b.id) ? ' active' : ''}`}\n />\n )\n })}\n </g>\n {buckets.map((b, i) => {\n const x = colX(i)\n const r = bandRangeFor(table, b.id)\n const yTop = yForWorld(axis, r.min)\n const yBot = yForWorld(axis, r.max)\n const c = color(b.id)\n return (\n <g\n key={b.id}\n className={`gv-gravity-band-col${anyHovered && !isHovered(b.id) ? ' dim' : ''}${isHovered(b.id) ? ' active' : ''}`}\n onPointerEnter={() => onHover({ axis, bucket: b.id })}\n onPointerLeave={() => { if (!dragRef.current) onHover(null) }}\n >\n <title>{b.label}</title>\n <text x={x} y={PT - 6} textAnchor=\"middle\" className=\"gv-gravity-band-label\" style={{ fill: c }}>{b.abbreviation}</text>\n <line x1={x} y1={PT} x2={x} y2={PB} className=\"gv-gravity-band-track\" />\n <line x1={x} y1={yTop} x2={x} y2={yBot} stroke={c} className=\"gv-gravity-band-stem\" />\n <circle cx={x} cy={yTop} r={DOT_R} fill={c} className=\"gv-gravity-band-dot\"\n onPointerDown={e => handleDown(e, b.id, 'min')} onPointerMove={handleMove} onPointerUp={handleUp} />\n <circle cx={x} cy={yBot} r={DOT_R} fill={c} className=\"gv-gravity-band-dot\"\n onPointerDown={e => handleDown(e, b.id, 'max')} onPointerMove={handleMove} onPointerUp={handleUp} />\n </g>\n )\n })}\n </svg>\n </Plot>\n )\n}\n\n// The band force-response curve for this axis — how the pull grows with\n// distance outside the rest band — with the (shared) falloff exponent beneath.\nconst CURVE_TOP_PAD = 4\nfunction AxisCurve({ axis, params, onParamChange }: {\n axis: LayoutAxis\n params: ForceParams\n onParamChange: (patch: Partial<ForceParams>) => void\n}) {\n const exp = params.gravityBandExp\n const strength = params[strengthKey(axis)]\n const scale = Math.max(params.gravityX, params.gravityY, 1e-6)\n const points = Array.from({ length: CURVE_SAMPLES + 1 }, (_, i) => {\n const d = i / CURVE_SAMPLES\n const f = (strength / scale) * Math.pow(d, exp) // normalized to the stronger axis\n const px = d * CURVE_W\n const py = CURVE_H - CURVE_TOP_PAD - f * (CURVE_H - 2 * CURVE_TOP_PAD)\n return `${px.toFixed(1)},${py.toFixed(1)}`\n }).join(' ')\n const curves: CurveItem[] = [{ id: axis, points, color: 'var(--gv-color-text)', label: axis.toUpperCase() }]\n return (\n <ForceCurves\n curves={curves}\n xMax={1}\n hoveredId={null}\n onHover={() => {}}\n xLabel=\"distance\"\n yLabel=\"force\"\n ariaLabel={`${axis.toUpperCase()} band force-response curve`}\n exp={{\n value: exp, min: 0.5, max: 3, step: 0.1,\n onChange: v => onParamChange({ gravityBandExp: v }),\n title: 'Band falloff exponent, shared by both axes. 1 = linear (Hooke); higher = softer just outside the band, stiffer far out.',\n popId: 'bandExp',\n }}\n />\n )\n}\n","// Gravity-tab controls: three sub-tabs. X Axis and Y Axis are the two layout\n// axes, each the same surface (`AxisControls`): a mapping, one column per\n// bucket with its rest band as two handles (or, on a scalar axis, a gradient\n// with its two ends), the axis's strength, the force\n// curve beneath. Y carries the Radial switch — the way radial mode is entered:\n// Y's bands become rings about the origin and X is ignored. Topological\n// keeps its own switch: a depth→pull curve plus strength/exp sliders. Center\n// gravity is the unexplained baseline (active when both axes are at zero\n// strength and Topological is off), so it has no control here.\n\nimport React from 'react'\nimport type { PresentSets } from './GraphControlPanel'\nimport './GravityControls.css'\nimport type { ForceParams, LayoutAxisOption } from '../graph/simulation'\nimport type { ResolvedPhysics } from '../graph/forces'\nimport { Slider } from './controls/Slider'\nimport { Equation } from './controls/Equation'\nimport { FormulaValue } from './controls/PopContext'\nimport { SubTabs, SubTab, SubTabBodies, SubTabBody } from './controls/SubTab'\nimport { AxisControls } from './AxisControls'\nimport type { GravityHover } from './AxisControls'\n\nexport type { GravityHover, LayoutAxis } from './AxisControls'\n\ninterface GravityControlProps {\n params: ForceParams\n /** The dense tables the kernels read, resolved once by the view. */\n resolved: ResolvedPhysics\n /** Every axis X or Y may key on: mappings with their bucket count, and computed scalars. */\n axes: readonly LayoutAxisOption[]\n /** The band buckets some visible node falls in, per axis — the only columns worth drawing. */\n present: Pick<PresentSets, 'bandX' | 'bandY'>\n onParamChange: (patch: Partial<ForceParams>) => void\n onRelayout: (partial: Partial<ForceParams>) => void\n hovered: GravityHover | null\n onHover: (h: GravityHover | null) => void\n}\n\ntype GravitySub = 'x' | 'y' | 'topological'\n\nexport function GravityControls({ params, resolved, axes, onParamChange, onRelayout, hovered, onHover, present }: GravityControlProps) {\n const [sub, setSub] = React.useState<GravitySub>('y')\n return (\n <div className=\"gv-gravity-controls\">\n <SubTabs label=\"Gravity\">\n <SubTab label=\"X Axis\" active={sub === 'x'} onSelect={() => setSub('x')} />\n <SubTab label=\"Y Axis\" active={sub === 'y'} onSelect={() => setSub('y')}\n leading={<ForceSwitch label=\"Radial layout: Y as distance from the centre\" enabled={params.radial} onToggle={v => onRelayout({ radial: v })} />} />\n <SubTab label=\"Topological\" active={sub === 'topological'} onSelect={() => setSub('topological')}\n leading={<ForceSwitch label=\"Topological gravity enabled\" enabled={params.topologicalEnabled} onToggle={v => onRelayout({ topologicalEnabled: v })} />} />\n </SubTabs>\n\n <SubTabBodies>\n <SubTabBody active={sub === 'x'}>\n <AxisControls axis=\"x\" params={params} resolved={resolved} axes={axes} present={present.bandX}\n onParamChange={onParamChange} onRelayout={onRelayout} hovered={hovered} onHover={onHover} />\n </SubTabBody>\n <SubTabBody active={sub === 'y'}>\n <AxisControls axis=\"y\" params={params} resolved={resolved} axes={axes} present={present.bandY}\n onParamChange={onParamChange} onRelayout={onRelayout} hovered={hovered} onHover={onHover} />\n </SubTabBody>\n <SubTabBody active={sub === 'topological'}>\n <div className={`gv-gravity-section-tools gv-gravity-topo${params.topologicalEnabled ? '' : ' inactive'}`}>\n <Equation>{<>F = <FormulaValue id=\"topoStrength\">strength</FormulaValue> × depth<sup><FormulaValue id=\"topoExp\">exp</FormulaValue></sup> × d</>}</Equation>\n <TopologicalCurve exp={params.gravityTopologicalExp} />\n <div className=\"gv-gravity-slider-row\">\n <label className=\"gv-graph-control-slider-label\">strength</label>\n <Slider\n min={0} max={2} step={0.02}\n value={params.gravityTopological}\n onChange={v => onParamChange({ gravityTopological: v })}\n popId=\"topoStrength\"\n />\n </div>\n <div className=\"gv-gravity-slider-row\">\n <label className=\"gv-graph-control-slider-label\">exp</label>\n <Slider\n min={1} max={6} step={0.1}\n value={params.gravityTopologicalExp}\n onChange={v => onParamChange({ gravityTopologicalExp: v })}\n popId=\"topoExp\"\n />\n </div>\n </div>\n </SubTabBody>\n </SubTabBodies>\n </div>\n )\n}\n\n// A sliding switch led into its sub-tab. Toggling does not select the tab (the\n// click stops here), so the state stays legible while another body is shown.\nfunction ForceSwitch({ label, enabled, onToggle }: { label: string; enabled: boolean; onToggle: (v: boolean) => void }) {\n return (\n <label className={`gv-switch${enabled ? ' on' : ''}`} onClick={e => e.stopPropagation()} title={label}>\n <input type=\"checkbox\" checked={enabled} onChange={e => onToggle(e.target.checked)} aria-label={label} />\n <span className=\"gv-switch-track\"><span className=\"gv-switch-knob\" /></span>\n </label>\n )\n}\n\n// Read-only display of the depth -> pull shaping curve (pull = depth^exp on the\n// normalized [0,1] range), so the exp knob is legible. Axes are labelled:\n// x = depth (root-ness), y = pull (resulting force weight).\nconst TC_W = 220\nconst TC_H = 96\nconst TC_PAD = 8\nconst TC_PAD_L = 16 // extra room on the left for the rotated y-axis label\nfunction TopologicalCurve({ exp }: { exp: number }) {\n const x0 = TC_PAD_L\n const x1 = TC_W - TC_PAD\n const y0 = TC_PAD\n const y1 = TC_H - TC_PAD - 12 // leave room for the x label\n const yMid = (y0 + y1) / 2\n const N = 48\n const points = Array.from({ length: N + 1 }, (_, i) => {\n const s = i / N\n const w = Math.pow(s, exp)\n return `${(x0 + s * (x1 - x0)).toFixed(1)},${(y1 - w * (y1 - y0)).toFixed(1)}`\n }).join(' ')\n return (\n <svg className=\"gv-gravity-topo-curve\" viewBox={`0 0 ${TC_W} ${TC_H}`} role=\"img\" aria-label=\"Depth-to-pull contrast curve\">\n <rect x={x0} y={y0} width={x1 - x0} height={y1 - y0} className=\"gv-spring-curve-frame\" />\n <polyline points={points} className=\"gv-gravity-topo-curve-line\" />\n <text x={(x0 + x1) / 2} y={TC_H - 1} textAnchor=\"middle\" className=\"gv-gravity-topo-curve-axis\">depth</text>\n <text x={5} y={yMid} textAnchor=\"middle\" className=\"gv-gravity-topo-curve-axis\" transform={`rotate(-90 5 ${yMid})`}>pull</text>\n </svg>\n )\n}\n","// Collapsible control panel for graph force parameters and simulation controls.\n// Organized by force equation — each section shows its equation, then relevant sliders.\n\nimport React from 'react'\nimport './GraphControlPanel.css'\nimport type { ForceParams } from '../graph/simulation'\nimport type { ResolvedPhysics } from '../graph/forces'\nimport type { AxisValue } from '../graph/axis'\n\nimport type { NodeScaleParams } from '../graph/node-scale'\nimport { PushPullControls } from './PushPullControls'\nimport { GravityControls } from './GravityControls'\nimport type { GravityHover } from './GravityControls'\nimport { Slider } from './controls/Slider'\nimport { EquationSection } from './controls/EquationSection'\nimport { PopProvider } from './controls/PopContext'\nimport type { AxisOption, LayoutAxisOption } from '../graph/simulation'\nimport { useTaxonomy } from './graph-view/useTaxonomy'\nimport type { AxisMapping } from '../graph/axis'\n\nexport type ForceSection = 'push' | 'pull' | 'gravity' | 'dynamics' | null\n\n// The three control tabs. Unlike ForceSection, a tab is always one of these —\n// there is no \"null\" tab; exactly one section is shown at a time. The \"forces\"\n// tab holds both pair forces under one axis (push and pull are its sub-tabs,\n// and still drive their own ForceSection previews); the \"misc\" tab groups\n// non-force controls (simulation dynamics + render-time node scaling), its\n// dynamics sub-section driving the 'dynamics' preview.\ntype ControlTab = 'forces' | 'gravity' | 'misc'\n\nconst CONTROL_TABS: { id: ControlTab; label: string }[] = [\n { id: 'forces', label: 'Push/Pull' },\n { id: 'gravity', label: 'Gravity' },\n { id: 'misc', label: 'Misc' },\n]\n\nexport type { PresentSets } from './graph-view/present'\nimport type { PresentSets } from './graph-view/present'\n\ninterface GraphControlPanelProps {\n params: ForceParams\n /** The dense tables the kernels read, resolved once by the view. */\n resolved: ResolvedPhysics\n // Patch-based: a control emits the subset of params it changed. (handleGravityChange\n // already used this shape; unifying lets nested map params be written too.)\n onParamChange: (patch: Partial<ForceParams>) => void\n // Shared \"keep the simulation warm while tuning\" signal. Fired alongside\n // every force-control edit so the layout responds live. See GraphView's\n // handleForceAdjust.\n onAdjust: () => void\n // Edits that relocate nodes — gravity's mode and toggles, the push/pull axis.\n // Routed through GraphView so the simulation params update and the layout\n // reheats harder than a numeric nudge.\n onRelayout: (partial: Partial<ForceParams>) => void\n /** Every axis push/pull may key on for this graph, with its measured cost. */\n axes: readonly AxisOption[]\n /** Every axis X or Y may key on: those mappings, and the computed scalars. */\n layoutAxes: readonly LayoutAxisOption[]\n /** What the post-filter visible set holds, per control: the only buckets and categories worth a token or a column. */\n present: PresentSets\n onAxisChange: (mapping: AxisMapping) => void\n onActiveSection: (section: ForceSection) => void\n onActivePushBucket: (bucket: string | null) => void\n onActiveGravityBucket: (hover: GravityHover | null) => void\n // The hovered/active pull edge category (its k-field key), forwarded to the\n // canvas so only that edge category is highlighted while tuning it.\n onActivePullEdge: (key: string | null) => void\n // Render-time node sizing (Misc → Node scaling). Separate from ForceParams\n // because it never touches the simulation — it only affects how big nodes\n // are drawn at a given zoom.\n nodeScale: NodeScaleParams\n onNodeScaleChange: (patch: Partial<NodeScaleParams>) => void\n}\n\n// Keys of ForceParams whose value is a number (ForceParams now also has the\n// boolean toggles and the string mode, which sliders must not target).\ntype NumericForceKey = {\n [K in keyof ForceParams]: ForceParams[K] extends number ? K : never\n}[keyof ForceParams]\n\n// Shared shape for a labelled range row. Key types differ per param family\n// (force params vs. node-scale params), so the key lives on the extending\n// interfaces and SliderRow itself only needs the range/label/tooltip.\ninterface RangeDef {\n label: string\n min: number\n max: number\n step: number\n tooltip: string\n}\n\ninterface SliderDef extends RangeDef {\n key: NumericForceKey\n}\n\ninterface NodeScaleSliderDef extends RangeDef {\n key: keyof NodeScaleParams\n}\n\n// --- PUSH/PULL section ---\n//\n// The pair forces live in PushPullControls: the shared axis dropdown, then Push\n// (ChargeControls: a 2D charge map + a charge-falloff read-out) and Pull\n// (SpringControls: a 2D spring map + a force-curve read-out) as sub-tabs.\n\n// --- GRAVITY section ---\n//\n// The Gravity tab's controls live in GravityControls: X Axis and Y Axis (the\n// same per-axis surface, Y carrying the Radial switch) and Topological. Center\n// gravity is the unexplained baseline.\n\n// --- DYNAMICS section sliders ---\n//\n// Tightened ranges so each default sits well inside its slider:\n// friction default 0.40 in 0.05–0.95\n// cooling default 0.005 in 0.0005–0.02\n// threshold default 0.001 in 0–0.005\n\nconst DYNAMICS_SLIDERS: SliderDef[] = [\n { key: 'velocityDecay', label: 'friction', min: 0.05, max: 0.95, step: 0.05,\n tooltip: 'Damping factor per tick. Higher = more friction' },\n { key: 'alphaDecay', label: 'cooling', min: 0.0005, max: 0.02, step: 0.0005,\n tooltip: 'Energy lost per tick. Higher = settles faster' },\n { key: 'alphaMin', label: 'threshold', min: 0, max: 0.005, step: 0.0001,\n tooltip: 'Energy level below which simulation pauses' },\n]\n\n// --- NODE SCALING section sliders ---\n//\n// Render-time controls for how big nodes are drawn. On-screen size is\n// `baseR × size × clamp(zoom, shrink-floor, grow-cap)`. Lowering \"grow cap\"\n// makes nodes start shrinking sooner as the user zooms out (the common fix for\n// \"nodes are too big when zoomed out\").\nconst NODE_SCALE_SLIDERS: NodeScaleSliderDef[] = [\n { key: 'baseMul', label: 'size', min: 0.3, max: 2.0, step: 0.05,\n tooltip: 'Overall node + label size. 1 = default.' },\n { key: 'maxZoomMul', label: 'grow cap', min: 0.5, max: 3.0, step: 0.05,\n tooltip: 'Zoom past which nodes stop growing. Lower = nodes start shrinking sooner as you zoom out.' },\n { key: 'minZoomMul', label: 'shrink floor', min: 0.1, max: 1.0, step: 0.05,\n tooltip: 'Smallest nodes may get when zoomed all the way out.' },\n]\n\nexport function GraphControlPanel({\n params, resolved, onParamChange, onAdjust, onRelayout, axes, layoutAxes, present, onAxisChange,\n onActiveSection,\n onActivePushBucket, onActiveGravityBucket, onActivePullEdge,\n nodeScale, onNodeScaleChange,\n}: GraphControlPanelProps) {\n const [open, setOpen] = React.useState(false)\n const [activeTab, setActiveTab] = React.useState<ControlTab>('forces')\n // Links the spring map and the force-curve viz: hovering a token in one\n // brightens the matching curve in the other. The key (an edge category's\n // k-field) is also forwarded to the canvas so only that category's edges\n // highlight while tuning.\n const [hoveredPullEdge, setHoveredPullEdge] = React.useState<string | null>(null)\n const handleHoverPullEdge = React.useCallback((key: string | null) => {\n setHoveredPullEdge(key)\n onActivePullEdge(key)\n }, [onActivePullEdge])\n\n // Links the charge map and the charge-falloff viz: hovering a token in one\n // brightens the matching curve in the other. The bucket is also forwarded to\n // the canvas so only that bucket's charge field rings highlight while tuning.\n const [hoveredPushBucket, setHoveredPushBucket] = React.useState<string | null>(null)\n const handleHoverPushBucket = React.useCallback((b: string | null) => {\n setHoveredPushBucket(b)\n onActivePushBucket(b)\n }, [onActivePushBucket])\n\n // Links the band pseudo-plot to the canvas band/ring highlight (and dims the\n // other columns/stripes).\n const [hoveredGravityBucket, setHoveredGravityBucket] = React.useState<GravityHover | null>(null)\n const handleHoverGravityBucket = React.useCallback((h: GravityHover | null) => {\n setHoveredGravityBucket(h)\n onActiveGravityBucket(h)\n }, [onActiveGravityBucket])\n\n // Single funnel for every force control's edits. Routing all controls\n // through one wrapper means the \"nudge the simulation\" behaviour is\n // defined once and inherited by every present and future control\n // (sliders, axis-plot handles, the 2D maps) — no per-control wiring,\n // no chance of a control forgetting to keep the layout warm.\n const emitParamChange = React.useCallback((patch: Partial<ForceParams>) => {\n onParamChange(patch)\n onAdjust()\n }, [onParamChange, onAdjust])\n\n return (\n <div className={`gv-graph-control-panel ${open ? 'open' : ''}`}>\n <button\n className=\"gv-graph-control-panel-toggle\"\n onClick={() => setOpen(!open)}\n title=\"Toggle control panel\"\n >\n {open ? '▼ Controls' : '▶ Controls'}\n </button>\n\n {open && (\n <PopProvider>\n <div className=\"gv-graph-control-panel-body\">\n {/* Control tabs — only one section is shown at a time. */}\n <div className=\"gv-graph-control-tabs\" role=\"tablist\">\n {CONTROL_TABS.map(t => (\n <button\n key={t.id}\n role=\"tab\"\n aria-selected={activeTab === t.id}\n className={`gv-graph-control-tab${activeTab === t.id ? ' active' : ''}`}\n onClick={() => setActiveTab(t.id)}\n >\n {t.label}\n </button>\n ))}\n </div>\n\n {/* All four sections are always mounted and stacked in the same grid\n cell; only the active one is visible. The grid auto-sizes to the\n tallest/widest tab, so the panel never resizes when switching\n tabs — the browser computes the \"max required space\" for us. */}\n <div className=\"gv-graph-control-sections\">\n {/* PUSH/PULL section */}\n <SectionSlot active={activeTab === 'forces'}>\n <PushPullControls\n params={params}\n resolved={resolved}\n axes={axes}\n present={present}\n onParamChange={emitParamChange}\n onAxisChange={onAxisChange}\n onActiveSection={onActiveSection}\n hoveredPushBucket={hoveredPushBucket}\n onHoverPushBucket={handleHoverPushBucket}\n hoveredEdge={hoveredPullEdge}\n onHoverEdge={handleHoverPullEdge}\n />\n </SectionSlot>\n\n {/* GRAVITY section */}\n <SectionSlot active={activeTab === 'gravity'}>\n <EquationSection\n subtitle=\"\"\n equation=\"\"\n onHover={h => { onActiveSection(h ? 'gravity' : null); if (!h) onActiveGravityBucket(null) }}\n >\n <GravityControls\n params={params}\n resolved={resolved}\n axes={layoutAxes}\n present={present}\n onParamChange={emitParamChange}\n onRelayout={onRelayout}\n hovered={hoveredGravityBucket}\n onHover={handleHoverGravityBucket}\n />\n </EquationSection>\n </SectionSlot>\n\n {/* MISC section — non-force controls: simulation dynamics and\n render-time node scaling, each in its own labelled sub-section. */}\n <SectionSlot active={activeTab === 'misc'}>\n <div className=\"gv-graph-control-subsection\">\n <div className=\"gv-graph-control-subsection-title\">Dynamics</div>\n <EquationSection subtitle=\"\" equation={'v \\u00D7= friction\\n\\u03B1 \\u2212= cooling, stop at threshold'} onHover={h => onActiveSection(h ? 'dynamics' : null)}>\n {DYNAMICS_SLIDERS.map(s => (\n <SliderRow key={s.key} def={s} value={params[s.key]} onChange={v => emitParamChange({ [s.key]: v } as Partial<ForceParams>)} />\n ))}\n </EquationSection>\n </div>\n <div className=\"gv-graph-control-subsection\">\n <div className=\"gv-graph-control-subsection-title\">Node scaling</div>\n <EquationSection subtitle=\"\" equation={'size = base \\u00D7 clamp(zoom, floor, cap)'} onHover={() => {}}>\n {NODE_SCALE_SLIDERS.map(s => (\n <SliderRow key={s.key} def={s} value={nodeScale[s.key]} onChange={v => onNodeScaleChange({ [s.key]: v } as Partial<NodeScaleParams>)} />\n ))}\n </EquationSection>\n </div>\n </SectionSlot>\n </div>\n </div>\n </PopProvider>\n )}\n </div>\n )\n}\n\n// A stacking slot for one force section. All slots share a single grid cell\n// (see .gv-graph-control-sections), so the panel sizes to the tallest section and\n// never changes size when switching tabs. Inactive slots stay mounted but\n// hidden (visibility + pointer-events) so they still contribute to the grid's\n// max size and keep their input state.\nfunction SectionSlot({ active, children }: { active: boolean; children: React.ReactNode }) {\n return (\n <div className={`gv-graph-control-section-slot${active ? ' active' : ''}`} aria-hidden={!active}>\n {children}\n </div>\n )\n}\n\nfunction SliderRow({ def, value, onChange, nodeType }: {\n def: RangeDef\n value: number\n onChange: (v: number) => void\n nodeType?: AxisValue\n}) {\n const taxonomy = useTaxonomy()\n const display = def.step < 1 ? String(value) : String(Math.round(value))\n // A typed row reads its colour from the pane's inline variables.\n const typed = nodeType\n ? { className: 'gv-graph-control-slider gv-graph-control-typed-row', style: { ['--gv-type-color' as string]: `var(${taxonomy.colorVar(nodeType)})` } as React.CSSProperties }\n : { className: 'gv-graph-control-slider', style: undefined }\n\n return (\n <div className={typed.className} style={typed.style} title={def.tooltip}>\n <label className=\"gv-graph-control-slider-label\">{def.label}</label>\n <Slider min={def.min} max={def.max} step={def.step} value={value} onChange={onChange} />\n <span className=\"gv-graph-control-slider-value\">{display}</span>\n </div>\n )\n}\n","// Group overlay — the glow groups the Graph view draws. A host hands the view\n// groups over the WHOLE graph (an id, the node ids the group covers, whether it\n// is emphasised); the view restricts each to the visible set, drops the\n// empty, and colours each by its place in the host's list — so a group keeps\n// its colour when a filter hides the groups before it. Membership is\n// derived on every render, never baked into the static graph, exactly like\n// the hover and search highlight sets. What a group IS — the host's chunking,\n// what the user has drilled into — is the host's; the host turns its own\n// structure into `NodeGroup`s.\n\nimport type { Theme } from './scheme'\n\n// A small cycling palette per theme. Identity is carried by the host's legend,\n// so colours only need to separate the handful of sibling groups shown at\n// once — not be globally unique. The light set is the same ten hues a few\n// stops deeper, so a glow reads on a pale canvas.\n/** The cycling glow palette per theme; a group with no `color` of its own takes the slot for its place in the list. */\nexport const GROUP_PALETTE: Readonly<Record<Theme, readonly string[]>> = {\n dark: [\n '#2DD4BF', // teal\n '#F59E0B', // amber\n '#A78BFA', // violet\n '#F472B6', // rose\n '#34D399', // green\n '#60A5FA', // blue\n '#FB923C', // orange\n '#A3E635', // lime\n '#22D3EE', // cyan\n '#E879F9', // fuchsia\n ],\n light: [\n '#0F766E', // teal\n '#B45309', // amber\n '#6D28D9', // violet\n '#BE185D', // rose\n '#047857', // green\n '#1D4ED8', // blue\n '#C2410C', // orange\n '#4D7C0F', // lime\n '#0E7490', // cyan\n '#A21CAF', // fuchsia\n ],\n}\n\n// Glow intensity multiplier for a group the host emphasises (`NodeGroup\n// .emphasized`: hovered in its legend, vs. the default 1). Drives both\n// brightness and — since the canvas swells focused groups — halo/corridor size.\nconst HOVER_STRENGTH = 2.2\n\n// Glow intensity multiplier for the group that contains the node currently\n// hovered/selected on the canvas. This is the antidote to \"tons of groups\": with\n// many faint groups shown at once, moving over the graph makes the group under\n// the pointer surface, so the user can read which group any node belongs to\n// without hunting in the legend. It brightens *and* swells the group's glow\n// (see GROUP_GLOW_FOCUS_SWELL in GraphCanvas) so it clearly lifts out of the field.\n/** The glow strength of the group holding the hovered or selected node. */\nexport const NODE_FOCUS_STRENGTH = 2.2\n\n/** A group as the host asks for it: over the whole graph, visible or not. */\nexport interface NodeGroup {\n id: string\n nodeIds: ReadonlySet<string>\n /** Whether the host emphasises this group; the view glows it stronger. A host's legend inside the pane uses `groupsControl`'s `emphasize` instead and leaves this out. */\n emphasized?: boolean\n /** The glow's colour, hex or rgb(); absent, the palette's slot for this group's place in the list. */\n color?: string\n}\n\n/** A group as drawn: the visible node ids it covers and its palette colour. */\nexport interface ActiveGroup {\n id: string\n color: string\n /** The glow's intensity: 1, or more for a group that is emphasised or holds the hovered or selected node. */\n strength: number\n nodeIds: ReadonlySet<string>\n}\n\n/**\n * The groups as drawn: each restricted to the visible set\n * (filter-as-source-of-truth), the empty dropped, each coloured by its place\n * in the host's list in the theme's palette — so hiding one group recolours\n * none of the others.\n */\nexport function activeGroupsOf(groups: readonly NodeGroup[], visibleIds: ReadonlySet<string>, theme: Theme = 'dark', emphasized: string | null = null): ActiveGroup[] {\n const palette = GROUP_PALETTE[theme]\n const out: ActiveGroup[] = []\n groups.forEach((g, i) => {\n const nodeIds = new Set<string>()\n for (const id of g.nodeIds) if (visibleIds.has(id)) nodeIds.add(id)\n if (nodeIds.size === 0) return\n out.push({ id: g.id, strength: g.emphasized || g.id === emphasized ? HOVER_STRENGTH : 1, nodeIds, color: g.color ?? palette[i % palette.length] })\n })\n return out\n}\n\n// boostGroupsForNodes strengthens any active group that contains one of the\n// focused node ids (the node hovered or selected on the canvas), so that group\n// surfaces out of a field of many faint ones. Returns the same array when no\n// focus node lands in any group, so the common case allocates nothing.\n/** The active groups with any that holds one of the given nodes glowing at `NODE_FOCUS_STRENGTH`; the same array when none does. */\nexport function boostGroupsForNodes(\n groups: ActiveGroup[],\n focusIds: Array<string | null>,\n): ActiveGroup[] {\n const ids = focusIds.filter((id): id is string => !!id)\n if (ids.length === 0) return groups\n let changed = false\n const boosted = groups.map(g => {\n if (ids.some(id => g.nodeIds.has(id))) {\n changed = true\n return { ...g, strength: Math.max(g.strength, NODE_FOCUS_STRENGTH) }\n }\n return g\n })\n return changed ? boosted : groups\n}\n","// useViewport — the camera concern for the Graph View.\n//\n// Owns the viewport (pan/zoom transform, which follows a pane resize) and the\n// DOM container ref used to measure the canvas, plus two coordination refs the\n// physics loop reads: the one-shot `initialFitDone` guard and the cross-view\n// `pendingCenterRef` request. `fit()` frames a set of nodes; every other\n// framing (the loop's initial fit and centring, search) sets the viewport\n// against the size `measure()` returns, and the keyboard handlers zoom about\n// the centre of `containerRef`.\n\nimport React from 'react'\nimport { DEFAULT_VIEWPORT, fitToView, followResize } from '../../graph/viewport'\nimport type { PaneSize, Viewport } from '../../graph/viewport'\nimport type { SimNode } from '../../graph/simulation'\n\nexport interface ViewportController {\n viewport: Viewport\n setViewport: React.Dispatch<React.SetStateAction<Viewport>>\n containerRef: React.RefObject<HTMLDivElement>\n // One-shot initial-fit guard; reset on sim rebuild / visible-type change.\n initialFitDone: React.MutableRefObject<boolean>\n // Cross-view \"center on this node after warmup\" request, consumed by the loop.\n pendingCenterRef: React.MutableRefObject<{ nodeId: string } | null>\n // The container's size, for a viewport computed against it.\n measure: () => PaneSize | null\n // Frame the viewport to the given nodes (measures the container first).\n fit: (nodes: SimNode[], padding?: number) => void\n}\n\nexport function useViewport(): ViewportController {\n const [viewport, setViewport] = React.useState<Viewport>(DEFAULT_VIEWPORT)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const initialFitDone = React.useRef(false)\n const pendingCenterRef = React.useRef<{ nodeId: string } | null>(null)\n\n // A resize keeps the world point at the pane's centre fixed. The shift is\n // the change since the size the viewport was last computed for: `measure()`\n // records that size, so a viewport framed after the host resized the pane,\n // but before the observer ran, is not shifted again. `followResize` leaves\n // the first measurement and a zero-sized (hidden) pane alone.\n const sizeRef = React.useRef<PaneSize | null>(null)\n const measure = React.useCallback(() => {\n const container = containerRef.current\n if (!container) return null\n const { width, height } = container.getBoundingClientRect()\n sizeRef.current = { width, height }\n return { width, height }\n }, [])\n React.useEffect(() => {\n const container = containerRef.current\n if (!container || typeof ResizeObserver === 'undefined') return\n const ro = new ResizeObserver(() => {\n const prev = sizeRef.current\n const size = measure()\n if (size) setViewport(vp => followResize(vp, prev, size))\n })\n ro.observe(container)\n return () => ro.disconnect()\n }, [measure])\n\n const fit = React.useCallback((nodes: SimNode[], padding?: number) => {\n const size = measure()\n if (!size) return\n setViewport(fitToView(nodes, size.width, size.height, padding))\n }, [measure])\n\n return { viewport, setViewport, containerRef, initialFitDone, pendingCenterRef, measure, fit }\n}\n","// Transitive dependency highlighting for graph hover/selection.\n\nimport { isContainment } from './model'\nimport type { GraphEdge, GraphNode } from './model'\nimport type { Highlight, HighlightGraph, Taxonomy } from './taxonomy'\n\nimport type { HighlightDirection } from './taxonomy'\nexport type { HighlightDirection } from './taxonomy'\n\n// BFS traversal of dependency edges (not containment) in the given direction.\n// Returns the set of transitively connected node IDs including the starting node.\nexport function getTransitiveDeps(\n nodeId: string,\n edges: GraphEdge[],\n visibleIds: Set<string>,\n direction: HighlightDirection,\n): Set<string> {\n const result = new Set<string>([nodeId])\n const queue = [nodeId]\n\n while (queue.length > 0) {\n const current = queue.shift()!\n for (const edge of edges) {\n if (isContainment(edge)) continue\n\n let next: string | undefined\n if (direction === 'downstream' && edge.sourceId === current) {\n next = edge.targetId\n } else if (direction === 'upstream' && edge.targetId === current) {\n next = edge.sourceId\n }\n\n if (next && visibleIds.has(next) && !result.has(next)) {\n result.add(next)\n queue.push(next)\n }\n }\n }\n\n return result\n}\n\n// Return edge IDs where both endpoints are in the highlighted node set\n// and the edge is a dependency (not containment).\nexport function getHighlightedEdgeIds(\n highlightedNodes: Set<string>,\n edges: GraphEdge[],\n): Set<string> {\n const result = new Set<string>()\n for (const edge of edges) {\n if (isContainment(edge)) continue\n if (highlightedNodes.has(edge.sourceId) && highlightedNodes.has(edge.targetId)) {\n result.add(edge.id)\n }\n }\n return result\n}\n\n/**\n * The highlight for an active node: the library's directional walk\n * from it, handed to the taxonomy's hook to widen or replace, with the lit\n * edges derived from the lit nodes when the hook names none.\n */\nexport function highlightOf(\n taxonomy: Taxonomy,\n active: GraphNode,\n direction: HighlightDirection,\n graph: HighlightGraph,\n): { nodes: Set<string>; edges: Set<string> } {\n const walked = getTransitiveDeps(active.id, [...graph.visibleEdges], new Set(graph.visibleIds), direction)\n const lit: Highlight = taxonomy.highlight(active, { nodes: walked }, graph, direction)\n const nodes = new Set(lit.nodes)\n const edges = lit.edges ? new Set(lit.edges) : getHighlightedEdgeIds(nodes, [...graph.visibleEdges])\n return { nodes, edges }\n}\n","// useHighlight — hover / keyboard-focus / preview-highlight concern for the\n// Graph View.\n//\n// Owns the hover and keyboard-focus state and the control-panel preview state\n// (active section / charge / gravity / pull edge), tracks the Shift key, and\n// derives the transitive-dependency highlight sets from the active node. The\n// selection is the view's (or the host's) and comes in as an input; so do the\n// visible subgraph and a `getNode` accessor (the explicit data link to the\n// sim). It never touches simRef directly.\n\nimport React from 'react'\nimport type { SimNode } from '../../graph/simulation'\nimport type { GraphEdge, GraphNode } from '../../graph/model'\nimport { highlightOf } from '../../graph/highlight'\nimport type { Taxonomy } from '../../graph/taxonomy'\n\nimport type { ForceSection } from '../GraphControlPanel'\nimport type { GravityHover } from '../GravityControls'\n\nexport interface HighlightController {\n hoveredNodeId: string | null\n setHoveredNodeId: React.Dispatch<React.SetStateAction<string | null>>\n focusedIndex: number\n setFocusedIndex: React.Dispatch<React.SetStateAction<number>>\n shiftHeld: boolean\n focusedNodeId: string | null\n highlightedNodes: Set<string> | null\n highlightedEdges: Set<string> | null\n activeSection: ForceSection\n setActiveSection: React.Dispatch<React.SetStateAction<ForceSection>>\n activePushBucket: string | null\n setActivePushBucket: React.Dispatch<React.SetStateAction<string | null>>\n activeGravityBucket: GravityHover | null\n setActiveGravityBucket: React.Dispatch<React.SetStateAction<GravityHover | null>>\n activePullEdge: string | null\n setActivePullEdge: React.Dispatch<React.SetStateAction<string | null>>\n}\n\nexport function useHighlight(\n visibleNodes: SimNode[],\n visibleEdges: GraphEdge[],\n visibleIds: Set<string>,\n getNode: (id: string) => SimNode | undefined,\n // Changes whenever the underlying graph is rebuilt; clears the hover and\n // the keyboard focus so a stale id never lingers across graphs.\n resetKey: unknown,\n taxonomy: Taxonomy,\n /** The selected node — the view's own or the host's — which the highlight walks from when nothing is hovered. */\n selectedNodeId: string | null,\n /** The host's nodes by id: what the taxonomy's `highlight` hook is handed, never the simulation's copies. */\n hostIndex: ReadonlyMap<string, GraphNode>,\n): HighlightController {\n const [hoveredNodeId, setHoveredNodeId] = React.useState<string | null>(null)\n const [focusedIndex, setFocusedIndex] = React.useState(-1)\n const [shiftHeld, setShiftHeld] = React.useState(false)\n\n // Force-field preview (hover-driven; no persistent toggle).\n const [activeSection, setActiveSection] = React.useState<ForceSection>(null)\n const [activePushBucket, setActivePushBucket] = React.useState<string | null>(null)\n const [activeGravityBucket, setActiveGravityBucket] = React.useState<GravityHover | null>(null)\n const [activePullEdge, setActivePullEdge] = React.useState<string | null>(null)\n\n // Reset hover and keyboard focus when the graph is rebuilt.\n React.useEffect(() => {\n setHoveredNodeId(null)\n setFocusedIndex(-1)\n }, [resetKey])\n\n // Shift key tracking — drives upstream vs downstream highlight direction.\n React.useEffect(() => {\n const down = (e: KeyboardEvent) => { if (e.key === 'Shift') setShiftHeld(true) }\n const up = (e: KeyboardEvent) => { if (e.key === 'Shift') setShiftHeld(false) }\n window.addEventListener('keydown', down)\n window.addEventListener('keyup', up)\n return () => { window.removeEventListener('keydown', down); window.removeEventListener('keyup', up) }\n }, [])\n\n const focusedNodeId = focusedIndex >= 0 && focusedIndex < visibleNodes.length\n ? visibleNodes[focusedIndex].id\n : null\n\n // Transitive dependency highlighting: a directional walk from the active\n // node, which the host may widen or replace through its taxonomy —\n // a value that relates to others by its own metadata says so there, not\n // here. Sister copies are intentionally NOT added (the canvas draws their\n // duplicate halo separately).\n const { highlightedNodes, highlightedEdges } = React.useMemo(() => {\n const activeId = hoveredNodeId ?? selectedNodeId\n if (!activeId || !visibleIds.has(activeId)) {\n return { highlightedNodes: null as Set<string> | null, highlightedEdges: null as Set<string> | null }\n }\n const direction = shiftHeld ? 'upstream' as const : 'downstream' as const\n const activeNode = getNode(activeId)\n if (!activeNode) return { highlightedNodes: null as Set<string> | null, highlightedEdges: null as Set<string> | null }\n // The host's hook sees the host's own nodes: the active one, the visible\n // ones and any it looks up — the simulation's copies stay the view's.\n const hostNode = (id: string) => hostIndex.get(id)\n const { nodes, edges } = highlightOf(taxonomy, hostNode(activeId) ?? activeNode, direction, {\n visibleNodes: visibleNodes.map(n => hostNode(n.id) ?? n),\n visibleEdges, visibleIds,\n getNode: id => hostNode(id) ?? getNode(id),\n })\n return { highlightedNodes: nodes, highlightedEdges: edges }\n }, [hoveredNodeId, selectedNodeId, shiftHeld, visibleEdges, visibleIds, visibleNodes, getNode, taxonomy, hostIndex])\n\n return {\n hoveredNodeId, setHoveredNodeId,\n focusedIndex, setFocusedIndex,\n shiftHeld,\n focusedNodeId,\n highlightedNodes, highlightedEdges,\n activeSection, setActiveSection,\n activePushBucket, setActivePushBucket,\n activeGravityBucket, setActiveGravityBucket,\n activePullEdge, setActivePullEdge,\n }\n}\n","// The simulation's source of randomness, as an injectable seam.\n//\n// The layout uses randomness in exactly two situations — breaking a degenerate\n// coincident-node frame, and jittering initial/seeded positions — and both are\n// unreproducible by construction, which makes the whole engine impossible to\n// characterize. Every such call site now reads a `Rng` passed in rather than\n// calling the global directly.\n//\n// The production default is the global generator, unchanged: nothing about the\n// running app becomes deterministic, and no consumer sees a new knob.\n\n/** A source of uniformly-distributed numbers in [0, 1). */\nexport type Rng = () => number\n\n/**\n * The production default — the platform generator itself. Referenced, not\n * wrapped, so this module is the one place the name appears and the\n * forbidden-pattern check keeps meaning \"no unreproducible draw inside a force\".\n * `Math.random` reads no `this`, so an unbound reference is safe.\n */\nexport const defaultRng: Rng = Math.random\n","// useSimulation — the simulation spine for the Graph View.\n//\n// Owns the long-lived Simulation instance (simRef), its rebuild lifecycle and\n// version signal, the running flag, and every handler that mutates the sim\n// (param/gravity edits, node drag, reheat, play/pause). The force parameters\n// are the view's layout state, read here and written through its writer. It\n// exposes a narrow surface: the ref, a `getNode` accessor, the version (the data\n// link other hooks list in their deps), and the handlers. The RAF loop that\n// *drives* the sim is a separate hook (useSimulationLoop); this owns the instance.\n\nimport React from 'react'\nimport { Simulation, retunedFor } from '../../graph/simulation'\nimport { defaultRng } from '../../graph/rng'\nimport type { ForceParams, SimNode } from '../../graph/simulation'\nimport type { Graph } from '../../graph/model'\nimport { graphEquals } from '../../graph/model'\nimport type { Taxonomy } from '../../graph/taxonomy'\n\nexport interface SimulationController {\n simRef: React.MutableRefObject<Simulation | null>\n // Bumped on each (re)creation so memos reading simRef.current invalidate.\n simVersion: number\n running: boolean\n setRunning: React.Dispatch<React.SetStateAction<boolean>>\n forceParams: ForceParams\n getNode: (id: string) => SimNode | undefined\n onParamChange: (patch: Partial<ForceParams>) => void\n onForceAdjust: () => void\n onRelayout: (partial: Partial<ForceParams>) => void\n onNodeDragStart: (id: string, wx: number, wy: number) => void\n onNodeDragMove: (wx: number, wy: number) => void\n onNodeDragEnd: () => void\n onToggleRunning: () => void\n}\n\nexport function useSimulation(\n graph: Graph,\n taxonomy: Taxonomy,\n // Where nodes start: the host's kept layout, by node id. Read when the\n // simulation is built; the previous simulation's positions take precedence\n // on a rebuild, so a graph update keeps the layout the user has.\n positions: ReadonlyMap<string, { x: number; y: number }> | undefined,\n // Called after each (re)creation with whether positions were carried over,\n // so the camera can keep its place when the layout kept its. Held in a ref\n // so its changing identity never re-triggers the rebuild.\n onRebuild: ((continued: boolean) => void) | undefined,\n // The force parameters as the view's layout state holds them, and the\n // writer every change goes through — the host's, when it controls the layout.\n forces: ForceParams,\n updateForces: (fn: (prev: ForceParams) => ForceParams) => void,\n): SimulationController {\n const simRef = React.useRef<Simulation | null>(null)\n const prevGraphRef = React.useRef<Graph | null>(null)\n const warnedRef = React.useRef(false)\n // A taxonomy rebuilt on every render rebuilds the simulation on every\n // render; three identities inside two seconds is that, said once.\n const ontologyChanges = React.useRef<number[]>([])\n const warnedOntologyRef = React.useRef(false)\n const prevOntologyRef = React.useRef<Taxonomy | null>(null)\n const dragNodeRef = React.useRef<string | null>(null)\n const [running, setRunning] = React.useState(true)\n // What the view and the simulation read: the parameters as retuned for the\n // graph and the taxonomy — `retunedFor` rebuilds any tuning whose buckets no\n // longer match (an axis bucketed by observed values after a file is added,\n // the style axis or a scalar axis after a taxonomy swap), keeps overrides\n // whose values survive, and returns the same object when nothing moved. It\n // runs in render so nothing downstream ever resolves a stale tuning against\n // a new taxonomy; the layout state catches up in the effect below, through\n // the view's writer, so a host that controls the layout hears the retune.\n const forceParams = React.useMemo(() => retunedFor(forces, taxonomy, graph), [forces, taxonomy, graph])\n React.useEffect(() => {\n if (forceParams !== forces) updateForces(prev => (prev === forces ? forceParams : prev))\n }, [forceParams, forces]) // eslint-disable-line react-hooks/exhaustive-deps\n const [simVersion, setSimVersion] = React.useState(0)\n\n const onRebuildRef = React.useRef(onRebuild)\n onRebuildRef.current = onRebuild\n\n // Create the simulation when the graph or the taxonomy changes. A swap used\n // to need a provenance ref here to detect; a tuning that carries its own\n // mapping has nothing left to detect.\n React.useEffect(() => {\n const prev = simRef.current\n // A new graph object equal to the last rebuilds the simulation for\n // nothing; said once, because the fix is on the host's side (memoise).\n if (prev && prevGraphRef.current && prevGraphRef.current !== graph && !warnedRef.current && graphEquals(prevGraphRef.current, graph)) {\n warnedRef.current = true\n console.warn('[graph] the graph\\'s identity changed but nothing in it did; the view rebuilt its simulation for nothing. Keep one graph object until its nodes or edges change.')\n }\n prevGraphRef.current = graph\n if (prevOntologyRef.current !== null && prevOntologyRef.current !== taxonomy && !warnedOntologyRef.current) {\n const now = typeof performance !== 'undefined' ? performance.now() : 0\n const recent = ontologyChanges.current.filter(t => now - t < 2000)\n recent.push(now)\n ontologyChanges.current = recent\n if (recent.length >= 3) {\n warnedOntologyRef.current = true\n console.warn('[graph] the taxonomy\\'s identity changed three times in two seconds; the view rebuilds its simulation for each. Build the taxonomy once, outside render.')\n }\n }\n prevOntologyRef.current = taxonomy\n // Where nodes start: the host's kept layout under the previous\n // simulation's, so a graph update keeps the layout the user has.\n const start = new Map<string, { x: number; y: number }>(positions ?? [])\n if (prev) for (const n of prev.nodes) start.set(n.id, { x: n.x, y: n.y })\n simRef.current = new Simulation(graph, forceParams, defaultRng, taxonomy, start.size > 0 ? start : undefined)\n onRebuildRef.current?.(prev !== null)\n setRunning(true)\n setSimVersion(v => v + 1)\n }, [graph, taxonomy]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Every later change reaches the live simulation here, once, after the\n // retune above — never a raw patch the taxonomy could refuse.\n React.useEffect(() => {\n const sim = simRef.current\n if (sim && sim.params !== forceParams) sim.setParams(forceParams)\n }, [forceParams])\n\n const getNode = React.useCallback((id: string) => simRef.current?.getNode(id), [])\n\n // Param edits update both React state and the live sim. Re-energising is the\n // separate concern of onForceAdjust (so non-committing interactions can warm\n // the sim without writing a param).\n const onParamChange = React.useCallback((patch: Partial<ForceParams>) => {\n updateForces(prev => ({ ...prev, ...patch }))\n }, [updateForces])\n\n const onForceAdjust = React.useCallback(() => {\n simRef.current?.nudge(0.3)\n setRunning(true)\n }, [])\n\n // Edits that relocate nodes — gravity's mode and toggles, the push/pull axis\n // — reheat harder than a numeric nudge.\n const onRelayout = React.useCallback((partial: Partial<ForceParams>) => {\n updateForces(prev => ({ ...prev, ...partial }))\n simRef.current?.reheat(0.6)\n setRunning(true)\n }, [updateForces])\n\n const onNodeDragStart = React.useCallback((id: string, wx: number, wy: number) => {\n dragNodeRef.current = id\n simRef.current?.pinNode(id, wx, wy)\n simRef.current?.reheat(0.3)\n setRunning(true)\n }, [])\n\n const onNodeDragMove = React.useCallback((wx: number, wy: number) => {\n if (dragNodeRef.current) simRef.current?.pinNode(dragNodeRef.current, wx, wy)\n }, [])\n\n const onNodeDragEnd = React.useCallback(() => {\n if (dragNodeRef.current) {\n simRef.current?.unpinNode(dragNodeRef.current)\n dragNodeRef.current = null\n }\n }, [])\n\n const onToggleRunning = React.useCallback(() => {\n if (!running) {\n simRef.current?.reheat(0.5)\n setRunning(true)\n } else {\n setRunning(false)\n }\n }, [running])\n\n return {\n simRef, simVersion, running, setRunning, forceParams, getNode,\n onParamChange, onForceAdjust, onRelayout,\n onNodeDragStart, onNodeDragMove, onNodeDragEnd,\n onToggleRunning,\n }\n}\n","// computeVisibleGraph — the pure derivation behind useVisibleGraph.\n//\n// Filters the graph's nodes on the axes the view shows, graduates edges across\n// hidden nodes (containment walks up to the nearest visible ancestor;\n// dependency edges project or splice, and the graduated ones collapse per\n// pair), classifies every edge once on its original endpoints, and computes\n// per-node summaries and downstream-reach scores.\n//\n// Lifted out of the hook so it is callable without React, and the taxonomy\n// arrives as a parameter, so both the visibility predicate and the summary\n// dispatch resolve through the container.\n//\n// Nothing here names a value: the hidden-node policy is the taxonomy's\n// (`whenHidden`), the summaries read its abbreviations, and which\n// of two graduated edges on one pair survives is the taxonomy's `collapse`.\n// An edge with both ends visible is never collapsed and keeps its id:\n// two edges between the same two visible nodes are two facts, and the\n// payload is not read here or anywhere else in the library.\n\nimport { computeDownstreamReach } from '../../graph/scalar'\nimport { isContainment, relationOf } from '../../graph/model'\nimport type { SimNode } from '../../graph/simulation'\nimport type { GraphEdge, ReadableGraph } from '../../graph/model'\nimport type { Taxonomy } from '../../graph/taxonomy'\nimport type { ResolvedAxisDescriptor } from '../../graph/axis'\nimport type { EdgeTypeId } from '../../graph/shapes'\nimport type { PhysicsSubject } from '../../graph/forces'\nimport type { FilterState } from '../../filter/types'\nimport { passesDescriptors } from '../../filter/types'\n\nexport interface VisibleGraph {\n visibleNodes: SimNode[]\n visibleEdges: GraphEdge[]\n visibleIds: Set<string>\n /**\n * Each visible edge's type, by its id, resolved once from the ORIGINAL\n * edge's endpoints: a graduated edge carries the type of the edge it stands\n * for, so the host's rule never meets an endpoint pair its model does not\n * produce. Undefined for an edge whose type the taxonomy does not\n * declare — `validateGraph` names those; here they draw neutral.\n */\n edgeTypes: Map<string, EdgeTypeId | undefined>\n /** Each visible edge's original: the host's edge it is, or the one a graduated edge stands for. */\n origins: Map<string, string>\n nodeSummaries: Map<string, string>\n downstreamScores: Map<string, number>\n}\n\n/**\n * The read-only slice of a Simulation this derivation needs. A `Simulation`\n * satisfies it structurally, so callers pass one directly.\n */\nexport interface VisibleGraphSource extends ReadableGraph {\n nodes: SimNode[]\n edges: readonly GraphEdge[]\n getNode: (id: string) => SimNode | undefined\n}\n\nexport const EMPTY_VISIBLE_GRAPH: VisibleGraph = {\n visibleNodes: [],\n visibleEdges: [],\n visibleIds: new Set<string>(),\n edgeTypes: new Map<string, EdgeTypeId | undefined>(),\n origins: new Map<string, string>(),\n nodeSummaries: new Map<string, string>(),\n downstreamScores: new Map<string, number>(),\n}\n\n// Walk parentId chain to find nearest ancestor that is in visibleIds.\nfunction findNearestVisibleAncestor(\n nodeId: string,\n visibleIds: Set<string>,\n getNode: (id: string) => SimNode | undefined,\n): string | null {\n const node = getNode(nodeId)\n if (!node) return null\n let id: string | undefined = node.parentId\n while (id) {\n if (visibleIds.has(id)) return id\n const parent = getNode(id)\n id = parent?.parentId\n }\n return null\n}\n\n// Resolve a dependency-edge endpoint to one or more visible node ids. Default:\n// \"if visible keep, else walk to nearest visible ancestor\". A value whose\n// declared policy is `splice` follows the hidden node's own\n// dependency edges through to the far side instead, so caller → hidden →\n// callee draws as caller → callee. A hidden node is passed through at most\n// once per resolution (`visited`): hidden spliced nodes may call each other,\n// and a cycle among them terminates.\nfunction resolveDepEndpoint(\n nodeId: string,\n visible: boolean,\n side: 'src' | 'tgt',\n sim: { edges: readonly GraphEdge[] },\n visibleIds: Set<string>,\n getNode: (id: string) => SimNode | undefined,\n taxonomy: Taxonomy,\n visited: Set<string> = new Set(),\n): string[] {\n if (visible) return [nodeId]\n if (visited.has(nodeId)) return []\n visited.add(nodeId)\n const node = getNode(nodeId)\n if (node && taxonomy.nodeStyleOf(node).whenHidden === 'splice') {\n const out: string[] = []\n for (const e of sim.edges) {\n if (isContainment(e)) continue\n const adjId = side === 'tgt'\n ? (e.sourceId === nodeId ? e.targetId : null) // outgoing — the hidden node is the source\n : (e.targetId === nodeId ? e.sourceId : null) // incoming — the hidden node is the target\n if (!adjId) continue\n for (const r of resolveDepEndpoint(adjId, visibleIds.has(adjId), side, sim, visibleIds, getNode, taxonomy, visited)) {\n out.push(r)\n }\n }\n if (out.length > 0) return out\n }\n const ancestor = findNearestVisibleAncestor(nodeId, visibleIds, getNode)\n return ancestor ? [ancestor] : []\n}\n\n// Glanceable per-node summary string, dispatched on the taxonomy's summaryKind.\nfunction computeGraphNodeSummary(\n node: SimNode,\n visibleEdges: GraphEdge[],\n nodeMap: Map<string, SimNode>,\n taxonomy: Taxonomy,\n): string {\n const { summaryKind } = taxonomy.nodeStyleOf(node)\n\n if (summaryKind === 'none') return ''\n\n if (summaryKind === 'children') {\n // The contained children, counted per style value in the taxonomy's own\n // order and named by its abbreviation — nothing here knows a value.\n const counts = new Map<string, number>()\n for (const e of visibleEdges) {\n if (!isContainment(e) || e.targetId !== node.id) continue\n const child = nodeMap.get(e.sourceId)\n if (!child) continue\n const value = taxonomy.styleValueOf(child)\n if (value === undefined) continue\n counts.set(value, (counts.get(value) ?? 0) + 1)\n }\n return taxonomy.styleValues\n .filter(k => counts.has(k))\n .map(k => `${counts.get(k)} ${taxonomy.abbreviationFor(k)}`)\n .join(' · ')\n }\n\n // summaryKind === 'degree'\n let out = 0, inc = 0\n for (const e of visibleEdges) {\n if (isContainment(e)) continue\n if (e.sourceId === node.id) out++\n if (e.targetId === node.id) inc++\n }\n const parts: string[] = []\n if (out > 0) parts.push(`→${out}`)\n if (inc > 0) parts.push(`←${inc}`)\n return parts.join(' ')\n}\n\n/**\n * `passesDescriptors` for a graph node, projected through the taxonomy, on the\n * axes given — the ones the view shows. Every declared axis is the default\n * for a caller that shows them all.\n */\nexport function passesFilter(\n node: PhysicsSubject,\n filter: FilterState,\n taxonomy: Taxonomy,\n axes: readonly ResolvedAxisDescriptor[] = taxonomy.filterAxes,\n): boolean {\n return passesDescriptors(filter, axes, axis => taxonomy.valueOn(node, axis))\n}\n\n/**\n * Every edge's type from its own endpoints, once. The host's rule may throw\n * or answer with an id the table does not declare; both are host bugs that\n * `validateGraph` reports, and this derivation stays total: such an edge has\n * no type here and draws neutral.\n */\nfunction classifyEdges(sim: VisibleGraphSource, taxonomy: Taxonomy): Map<string, EdgeTypeId | undefined> {\n const declared = new Map(taxonomy.edgeTypes.map(e => [e.id, e]))\n const out = new Map<string, EdgeTypeId | undefined>()\n for (const edge of sim.edges) {\n const src = sim.getNode(edge.sourceId)\n const tgt = sim.getNode(edge.targetId)\n let id: EdgeTypeId | undefined\n try {\n id = taxonomy.edgeTypeOf(edge, src && taxonomy.styleValueOf(src), tgt && taxonomy.styleValueOf(tgt))\n } catch {\n id = undefined\n }\n // A type of another relation than the edge's is no type for it: the edge draws in its relation's stroke, as the diagnostics say.\n out.set(edge.id, id !== undefined && declared.get(id)?.relation === relationOf(edge) ? id : undefined)\n }\n return out\n}\n\nexport function computeVisibleGraph(\n sim: VisibleGraphSource,\n filter: FilterState,\n taxonomy: Taxonomy,\n axes: readonly ResolvedAxisDescriptor[] = taxonomy.filterAxes,\n): VisibleGraph {\n const ids = new Set<string>()\n const vNodes: SimNode[] = []\n\n for (const node of sim.nodes) {\n if (passesFilter(node, filter, taxonomy, axes)) {\n ids.add(node.id)\n vNodes.push(node)\n }\n }\n\n const originalTypes = classifyEdges(sim, taxonomy)\n const edgeTypes = new Map<string, EdgeTypeId | undefined>()\n const origins = new Map<string, string>()\n\n // Graduate edges across hidden nodes. Two passes over the host's edges: the\n // ones drawn as given first, then the projections, because a projection\n // that lands on a pair an original edge already connects is dropped —\n // the original stands for that reach, whichever order the host listed them.\n const getNode = (id: string) => sim.getNode(id)\n const kept: GraphEdge[] = []\n const originalPairs = new Set<string>()\n const projected: GraphEdge[] = []\n\n for (const edge of sim.edges) {\n const srcVisible = ids.has(edge.sourceId)\n const tgtVisible = ids.has(edge.targetId)\n\n if (isContainment(edge)) {\n if (!srcVisible) continue // hidden child — drop\n if (tgtVisible) {\n kept.push(edge)\n edgeTypes.set(edge.id, originalTypes.get(edge.id))\n origins.set(edge.id, edge.id)\n } else {\n const ancestor = findNearestVisibleAncestor(edge.targetId, ids, getNode)\n if (ancestor) {\n const id = `grad:${edge.id}`\n kept.push({ ...edge, targetId: ancestor, id })\n edgeTypes.set(id, originalTypes.get(edge.id))\n origins.set(id, edge.id)\n }\n }\n } else if (srcVisible && tgtVisible) {\n // Both ends visible: the host's edge, as given. Never collapsed with a\n // parallel one — two edges between two visible nodes are two facts.\n kept.push(edge)\n edgeTypes.set(edge.id, originalTypes.get(edge.id))\n origins.set(edge.id, edge.id)\n originalPairs.add(`${edge.sourceId}→${edge.targetId}`)\n } else {\n projected.push(edge)\n }\n }\n\n // Graduated dependency edges, one per visible pair: the original each stands\n // for, and the edge as drawn.\n const graduated = new Map<string, { origin: GraphEdge; edge: GraphEdge }>()\n for (const edge of projected) {\n const sources = resolveDepEndpoint(edge.sourceId, ids.has(edge.sourceId), 'src', sim, ids, getNode, taxonomy)\n const targets = resolveDepEndpoint(edge.targetId, ids.has(edge.targetId), 'tgt', sim, ids, getNode, taxonomy)\n for (const rs of sources) {\n for (const rt of targets) {\n if (rs === rt) continue\n const key = `${rs}→${rt}`\n if (originalPairs.has(key)) continue\n // Two graduated edges on one pair stand for two paths through hidden\n // nodes that now read as one reach; the taxonomy says which survives,\n // and the one met first does when it does not say.\n const existing = graduated.get(key)\n if (existing && taxonomy.collapse(existing.origin, edge) === existing.origin) continue\n graduated.set(key, { origin: edge, edge: { ...edge, sourceId: rs, targetId: rt, id: `grad:${key}` } })\n }\n }\n }\n\n const vEdges = [...kept]\n for (const { origin, edge } of graduated.values()) {\n vEdges.push(edge)\n edgeTypes.set(edge.id, originalTypes.get(origin.id))\n origins.set(edge.id, origin.id)\n }\n\n const vNodeMap = new Map<string, SimNode>()\n for (const n of vNodes) vNodeMap.set(n.id, n)\n const nodeSummaries = new Map<string, string>()\n for (const node of vNodes) {\n const s = computeGraphNodeSummary(node, vEdges, vNodeMap, taxonomy)\n if (s) nodeSummaries.set(node.id, s)\n }\n\n // Reach over the VISIBLE subgraph, for topological gravity; the same scalar\n // over the whole graph is a layout axis (graph/scalar.ts).\n const downstreamScores = computeDownstreamReach({ nodes: vNodes, edges: vEdges, getNode: id => vNodeMap.get(id) })\n\n return { visibleNodes: vNodes, visibleEdges: vEdges, visibleIds: ids, edgeTypes, origins, nodeSummaries, downstreamScores }\n}\n","// useVisibleGraph — derives the visible subgraph from the live simulation.\n//\n// A memo wrapper over `computeVisibleGraph` (./visibleGraph), which owns the\n// derivation itself. Recomputes only on filter, axis or sim-rebuild changes\n// (keyed on simVersion, the explicit data link to useSimulation), never per\n// frame.\n\nimport type { FilterState } from '../../filter/types'\nimport type { ResolvedAxisDescriptor } from '../../graph/axis'\nimport React from 'react'\nimport type { Simulation } from '../../graph/simulation'\nimport { computeVisibleGraph, EMPTY_VISIBLE_GRAPH } from './visibleGraph'\nimport type { Taxonomy } from '../../graph/taxonomy'\nimport type { VisibleGraph } from './visibleGraph'\n\nexport type { VisibleGraph } from './visibleGraph'\n\nexport function useVisibleGraph(\n simRef: React.MutableRefObject<Simulation | null>,\n simVersion: number,\n filter: FilterState,\n /** The axes the view shows — the only ones that filter. */\n axes: readonly ResolvedAxisDescriptor[],\n taxonomy: Taxonomy,\n): VisibleGraph {\n return React.useMemo<VisibleGraph>(() => {\n const sim = simRef.current\n if (!sim) return EMPTY_VISIBLE_GRAPH\n return computeVisibleGraph(sim, filter, taxonomy, axes)\n // `filter` is a strictly better signal than the Sets it replaced: every\n // operation on a FilterState is copy-on-write and returns the SAME object\n // when content is unchanged, so its identity means \"some axis actually\n // changed\" — for any number of axes, without listing them.\n //\n // simVersion is the data link to useSimulation — it bumps on (re)creation so\n // this memo recomputes when the sim instance changes.\n }, [filter, axes, simVersion, taxonomy]) // eslint-disable-line react-hooks/exhaustive-deps\n}\n","// useSimulationLoop — drives the simulation each frame and reacts to filter\n// changes. Owns the requestAnimationFrame physics loop (tick + one-shot initial\n// fit + cross-view center + fps + stability stop), the hover tooltip re-render\n// tick, and the seed-on-type-change / reheat-on-file-change effects. It consumes\n// the sim instance, the visible subgraph, and the camera/selection handles as\n// inputs (the explicit data links) rather than owning any of them.\n\nimport type { FilterState } from '../../filter/types'\nimport { selectionFor, selectionsEqual } from '../../filter/types'\nimport { foldReheatPolicy } from '../../graph/axis'\nimport type { ResolvedAxisDescriptor } from '../../graph/axis'\nimport { passesFilter } from './visibleGraph'\nimport React from 'react'\nimport type { Simulation, SimNode } from '../../graph/simulation'\nimport type { Viewport } from '../../graph/viewport'\nimport { fitToView } from '../../graph/viewport'\nimport type { Taxonomy } from '../../graph/taxonomy'\n\nexport interface SimulationLoopParams {\n simRef: React.MutableRefObject<Simulation | null>\n running: boolean\n setRunning: React.Dispatch<React.SetStateAction<boolean>>\n visibleIds: Set<string>\n visibleNodes: SimNode[]\n downstreamScores: Map<string, number>\n filter: FilterState\n /** The axes the view filters on — its chain; a change elsewhere in the filter moves nothing here. */\n axes: readonly ResolvedAxisDescriptor[]\n hoveredNodeId: string | null\n selectedNodeId: string | null\n /** The container's size, recorded as the one the viewport is computed for. */\n measure: () => { width: number; height: number } | null\n initialFitDone: React.MutableRefObject<boolean>\n pendingCenterRef: React.MutableRefObject<{ nodeId: string } | null>\n setViewport: React.Dispatch<React.SetStateAction<Viewport>>\n /** Select a node the way the user would — the focus recipe's landing. */\n setSelectedNodeId: (id: string | null) => void\n taxonomy: Taxonomy\n}\n\nexport function useSimulationLoop({\n simRef, running, setRunning,\n visibleIds, visibleNodes, downstreamScores,\n filter, axes,\n hoveredNodeId, selectedNodeId,\n measure, initialFitDone, pendingCenterRef, setViewport, setSelectedNodeId, taxonomy,\n}: SimulationLoopParams): { fps: number } {\n // Frame-rate indicator. Updated ~2×/sec from the loop.\n const [fps, setFps] = React.useState(0)\n const fpsTrackRef = React.useRef({ frames: 0, lastStamp: 0 })\n\n // Physics animation loop. Decoupled from React rendering: the canvas has its\n // own RAF draw loop reading live SimNode.x/.y through a ref, so the physics\n // tick does NOT trigger a React re-render each frame. We only touch React state\n // for one-shot events (initial fit, pending cross-view center, stability, FPS).\n React.useEffect(() => {\n if (!running) return\n let frameId = 0\n fpsTrackRef.current = { frames: 0, lastStamp: 0 }\n\n const loop = () => {\n const sim = simRef.current\n if (!sim) return\n\n sim.tick(visibleIds, downstreamScores)\n\n // Initial fit — once per sim instance, after warmup.\n if (!initialFitDone.current && sim.alpha < 0.3) {\n const size = measure()\n if (size && size.width > 0 && size.height > 0) {\n setViewport(fitToView(visibleNodes, size.width, size.height))\n initialFitDone.current = true\n }\n }\n\n // Cross-view navigation: center on target after warmup.\n if (initialFitDone.current && pendingCenterRef.current) {\n const targetNode = sim.getNode(pendingCenterRef.current.nodeId)\n if (targetNode) {\n const size = measure()\n if (size) {\n const { width, height } = size\n setViewport(prev => ({\n scale: Math.max(prev.scale, 1.2),\n x: width / 2 - targetNode.x * Math.max(prev.scale, 1.2),\n y: height / 2 - targetNode.y * Math.max(prev.scale, 1.2),\n }))\n setSelectedNodeId(targetNode.id)\n }\n }\n pendingCenterRef.current = null\n }\n\n // FPS meter — recompute ~2×/sec; setFps only fires on change.\n const now = performance.now()\n const track = fpsTrackRef.current\n track.frames++\n if (track.lastStamp === 0) track.lastStamp = now\n const elapsed = now - track.lastStamp\n if (elapsed >= 500) {\n const measured = Math.round((track.frames * 1000) / elapsed)\n setFps(prev => (prev === measured ? prev : measured))\n track.frames = 0\n track.lastStamp = now\n }\n\n if (sim.isStable()) {\n setRunning(false)\n return\n }\n\n frameId = requestAnimationFrame(loop)\n }\n\n frameId = requestAnimationFrame(loop)\n return () => { if (frameId) cancelAnimationFrame(frameId) }\n }, [running, visibleIds, visibleNodes, selectedNodeId]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // While hovering a node during a running sim, re-render at ~10fps so the DOM\n // tooltip tracks the moving node (canvas nodes update at 60fps via refs).\n const [, setTooltipTick] = React.useState(0)\n React.useEffect(() => {\n if (!running || !hoveredNodeId) return\n const id = window.setInterval(() => setTooltipTick(t => t + 1), 100)\n return () => window.clearInterval(id)\n }, [running, hoveredNodeId])\n\n // React to a filter change, per axis, using that axis's declared reheat policy.\n //\n // This was two hand-written effects — one for types, one for files — and their\n // differences were the trap. They differ on three of four fields, and the\n // one they AGREE on is the one a careless generalisation drops: both call\n // `setRunning(true)`, and omitting it leaves the canvas frozen after every\n // toggle. Writing `resume` out per axis is what keeps that a decision rather\n // than an omission.\n //\n // \"Newly revealed\" is now asked of the whole filter rather than of one axis:\n // a node counts as revealed if it passes now and did not before. The old\n // version tested only the type axis, so a node revealed by a *file* change was\n // never seeded even when the type axis had also moved in the same commit.\n const prevFilter = React.useRef(filter)\n React.useEffect(() => {\n const prev = prevFilter.current\n if (prev === filter) return\n const sim = simRef.current\n if (!sim) { prevFilter.current = filter; return }\n\n const { alpha, seedRevealed, refit, resume } = foldReheatPolicy(\n axes.filter(\n dim => !selectionsEqual(selectionFor(prev, dim.id), selectionFor(filter, dim.id)),\n ),\n )\n\n if (alpha > 0) {\n if (seedRevealed) {\n for (const node of sim.nodes) {\n if (!passesFilter(node, filter, taxonomy, axes)) continue\n if (passesFilter(node, prev, taxonomy, axes)) continue\n let ancestorId = node.parentId\n while (ancestorId) {\n const ancestor = sim.getNode(ancestorId)\n if (!ancestor) break\n if (passesFilter(ancestor, filter, taxonomy, axes)) {\n sim.seedAt(node.id, ancestor.x, ancestor.y)\n break\n }\n ancestorId = ancestor.parentId\n }\n }\n }\n sim.reheat(alpha)\n if (resume) setRunning(true)\n if (refit) initialFitDone.current = false\n }\n prevFilter.current = filter\n }, [filter]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return { fps }\n}\n","// The declarative focus request and how two of them compare.\n\n/**\n * A request to reveal, centre and select a node — the declarative form of the\n * handle's `focusNode`, for a host whose pane mounts lazily or whose data\n * arrives after the request. Compared by value: a new `id` or `key` is a new\n * request, the same pair on a re-render is not, so a host repeats a request\n * for one node by changing `key`. A request waits until its node is in the\n * graph; then the view runs the focus recipe once and `onFocusResult` says\n * whether the filter could show the node. `null`, or a newer request,\n * withdraws a waiting one; a withdrawn request is not reported.\n */\nexport interface FocusRequest {\n id: string\n key?: number | string\n}\n\n/** Whether two requests are the same ask: both absent, or the same id and key. */\nexport function sameFocusRequest(a: FocusRequest | null, b: FocusRequest | null): boolean {\n return a === b || (a !== null && b !== null && a.id === b.id && a.key === b.key)\n}\n","// Graph validation — every defect a host's graph can carry, named before the\n// view builds anything from it.\n//\n// A dangling endpoint, an edge whose relation is neither of the two the engine\n// knows, an edge the host's rule classifies as a type its table does not\n// declare: each is a diagnostic, as data, never a throw from inside a render.\n// The view runs this once per graph, shows the list inside its own pane, hands\n// it to `onDiagnostics`, and draws what remains; a host that wants to refuse a\n// bad graph before mounting calls it itself.\n\nimport type { Graph, GraphEdge, GraphNode } from './model'\nimport { nodesById, relationOf } from './model'\nimport type { Taxonomy } from './taxonomy'\n\n/**\n * The defects `validateGraph` names. Errors: a node or edge id used twice,\n * an edge whose endpoint is not in the graph, an unknown relation, a type\n * whose relation is not the edge's, a rule that threw, a parent not in the\n * graph. Warnings: an edge no type claims, a style value the taxonomy does\n * not declare, containment edges that contradict `parentId`, derived\n * containment edges no type claims (`containment-untyped`, from\n * `derivedContainmentDiagnostics`).\n */\nexport type GraphDiagnosticCode =\n | 'edge-id-duplicate'\n | 'edge-endpoint-missing'\n | 'edge-relation-unknown'\n | 'edge-type-rule-threw'\n | 'edge-type-unclassified'\n | 'edge-type-relation-mismatch'\n | 'parent-missing'\n | 'containment-parent-mismatch'\n | 'node-value-undeclared'\n | 'node-id-duplicate'\n | 'containment-untyped'\n\n/** One defect in a host's graph, as data: its severity, its code, a message, and the node or edge it names. */\nexport interface GraphDiagnostic {\n /** An error names something the view cannot draw as given; a warning, something it draws but the host should know. */\n severity: 'error' | 'warning'\n code: GraphDiagnosticCode\n message: string\n nodeId?: string\n edgeId?: string\n}\n\nconst RELATIONS = new Set(['containment', 'dependency'])\n\n/**\n * Every defect in a host's graph, as data: errors for what the view cannot\n * draw as given (a duplicated node id, a dangling endpoint, an unknown\n * relation, a type whose relation is not the edge's, a parent not in the\n * graph) and warnings for what it draws but the host should know (an\n * unclassified edge, an undeclared style value, containment edges that\n * contradict `parentId`). Pure; the same graph and taxonomy give the same\n * list, in graph order.\n */\nexport function validateGraph(graph: Graph, taxonomy: Taxonomy): GraphDiagnostic[] {\n const out: GraphDiagnostic[] = []\n const declaredTypes = new Map(taxonomy.edgeTypes.map(e => [e.id, e]))\n const declaredValues = new Set(taxonomy.styleValues)\n const byId = nodesById(graph.nodes)\n\n // Nodes: one per id, a parent that exists, a style value the taxonomy\n // declares.\n const seenNodes = new Set<string>()\n for (const node of graph.nodes) {\n if (seenNodes.has(node.id)) {\n out.push({ severity: 'error', code: 'node-id-duplicate', nodeId: node.id, message: `node id ${JSON.stringify(node.id)} is used more than once; the view draws the first` })\n continue\n }\n seenNodes.add(node.id)\n if (node.parentId !== undefined && !byId.has(node.parentId)) {\n out.push({ severity: 'error', code: 'parent-missing', nodeId: node.id, message: `node ${JSON.stringify(node.id)} names parent ${JSON.stringify(node.parentId)}, which is not in the graph` })\n }\n const value = taxonomy.styleValueOf(node)\n if (value !== undefined && !declaredValues.has(value)) {\n out.push({ severity: 'warning', code: 'node-value-undeclared', nodeId: node.id, message: `node ${JSON.stringify(node.id)} sits at ${JSON.stringify(value)} on the style axis, which the taxonomy does not declare; it draws in the fallback style` })\n }\n }\n\n // Edges: unique ids, both ends present, a known relation, a type (when one\n // classifies) whose relation is the edge's.\n const seenIds = new Set<string>()\n const containers = new Map<string, Set<string>>()\n for (const edge of graph.edges) {\n if (seenIds.has(edge.id)) {\n out.push({ severity: 'error', code: 'edge-id-duplicate', edgeId: edge.id, message: `edge id ${JSON.stringify(edge.id)} is used more than once` })\n continue\n }\n seenIds.add(edge.id)\n const src = byId.get(edge.sourceId)\n const tgt = byId.get(edge.targetId)\n if (!src || !tgt) {\n const missing = [!src && edge.sourceId, !tgt && edge.targetId].filter((x): x is string => typeof x === 'string')\n out.push({ severity: 'error', code: 'edge-endpoint-missing', edgeId: edge.id, message: `edge ${JSON.stringify(edge.id)} names ${missing.map(m => JSON.stringify(m)).join(' and ')}, not in the graph` })\n continue\n }\n if (edge.relation !== undefined && !RELATIONS.has(edge.relation)) {\n out.push({ severity: 'error', code: 'edge-relation-unknown', edgeId: edge.id, message: `edge ${JSON.stringify(edge.id)} has relation ${JSON.stringify(edge.relation)}; the engine knows \"containment\" and \"dependency\"` })\n continue\n }\n const relation = relationOf(edge)\n if (relation === 'containment') {\n const set = containers.get(edge.sourceId) ?? new Set<string>()\n set.add(edge.targetId)\n containers.set(edge.sourceId, set)\n }\n let typeId: string | undefined\n try {\n typeId = taxonomy.edgeTypeOf(edge, taxonomy.styleValueOf(src), taxonomy.styleValueOf(tgt))\n } catch (e) {\n out.push({ severity: 'error', code: 'edge-type-rule-threw', edgeId: edge.id, message: `the taxonomy's classifyEdge rule threw on edge ${JSON.stringify(edge.id)}: ${e instanceof Error ? e.message : String(e)}` })\n continue\n }\n const declared = typeId === undefined ? undefined : declaredTypes.get(typeId)\n if (!declared) {\n const said = typeId === undefined ? 'no declared type fits its endpoints and values' : `classifyEdge answered ${JSON.stringify(typeId)}, which edgeTypes does not declare`\n out.push({ severity: 'warning', code: 'edge-type-unclassified', edgeId: edge.id, message: `edge ${JSON.stringify(edge.id)} has no type: ${said}; it draws in its relation's default stroke and pulls nothing while push and pull key on the style axis` })\n continue\n }\n if (declared.relation !== relation) {\n out.push({ severity: 'error', code: 'edge-type-relation-mismatch', edgeId: edge.id, message: `edge ${JSON.stringify(edge.id)} is a ${relation} edge but resolves to type ${JSON.stringify(typeId)}, declared as ${declared.relation}` })\n }\n }\n\n // The two readings of containment agree: a node that lists containment\n // edges of its own lists one to its parent. A node listing none gets that\n // edge derived from `parentId`; a node with no parent may list what it likes.\n for (const node of byId.values()) {\n const viaEdges = containers.get(node.id)\n if (node.parentId !== undefined && viaEdges !== undefined && !viaEdges.has(node.parentId)) {\n out.push({ severity: 'warning', code: 'containment-parent-mismatch', nodeId: node.id, message: `node ${JSON.stringify(node.id)} has containment edges to ${[...viaEdges].map(c => JSON.stringify(c)).join(' and ')} but its parentId is ${JSON.stringify(node.parentId)}` })\n }\n }\n return out\n}\n\n/**\n * The graph with every node and edge an error names removed and every parent\n * an error names cleared — what the view draws when the host's graph has\n * defects. A duplicated id keeps its first node and its first edge. The\n * host's objects are not mutated; a graph with no errors is returned as is.\n */\nexport function dropInvalid(graph: Graph, diagnostics: readonly GraphDiagnostic[]): Graph {\n const badEdges = new Set<string>()\n const badParents = new Set<string>()\n const duplicateNodes = new Set<string>()\n for (const d of diagnostics) {\n if (d.severity !== 'error') continue\n if (d.edgeId !== undefined) badEdges.add(d.edgeId)\n if (d.code === 'parent-missing' && d.nodeId !== undefined) badParents.add(d.nodeId)\n if (d.code === 'node-id-duplicate' && d.nodeId !== undefined) duplicateNodes.add(d.nodeId)\n }\n if (badEdges.size === 0 && badParents.size === 0 && duplicateNodes.size === 0) return graph\n const seenNodes = new Set<string>()\n const nodes: GraphNode[] = []\n for (const node of graph.nodes) {\n if (duplicateNodes.has(node.id) && seenNodes.has(node.id)) continue\n seenNodes.add(node.id)\n nodes.push(badParents.has(node.id) ? { ...node, parentId: undefined } : node)\n }\n // A duplicated id names every edge carrying it; the first keeps its place.\n const kept = new Set<string>()\n const edges: GraphEdge[] = []\n for (const e of graph.edges) {\n if (badEdges.has(e.id) && kept.has(e.id)) continue\n if (badEdges.has(e.id) && !diagnostics.some(d => d.edgeId === e.id && d.code === 'edge-id-duplicate')) continue\n kept.add(e.id)\n edges.push(e)\n }\n return { nodes, edges }\n}\n\n/**\n * The one warning the drawn graph adds to the host's: the containment edges\n * the view derived from `parentId` that no declared containment type claims\n * (a dependency type the host's rule answers does not count). They draw in\n * the containment stroke and pull nothing while push and pull key on the\n * style axis; a host that wants them typed declares a containment type\n * between the child's value and the parent's.\n */\nexport function derivedContainmentDiagnostics(drawn: Graph, host: Graph, taxonomy: Taxonomy): GraphDiagnostic[] {\n const listed = new Set<GraphEdge>(host.edges)\n const byId = nodesById(drawn.nodes)\n const untyped: string[] = []\n for (const edge of drawn.edges) {\n if (listed.has(edge) || relationOf(edge) !== 'containment') continue\n const src = byId.get(edge.sourceId)\n const tgt = byId.get(edge.targetId)\n let typeId: string | undefined\n try {\n typeId = taxonomy.edgeTypeOf(edge, src && taxonomy.styleValueOf(src), tgt && taxonomy.styleValueOf(tgt))\n } catch {\n typeId = undefined\n }\n const declared = typeId === undefined ? undefined : taxonomy.edgeTypes.find(t => t.id === typeId)\n if (declared === undefined || declared.relation !== 'containment') untyped.push(edge.id)\n }\n if (untyped.length === 0) return []\n const shown = untyped.slice(0, 3).map(id => JSON.stringify(id)).join(', ')\n const one = untyped.length === 1\n return [{\n severity: 'warning',\n code: 'containment-untyped',\n edgeId: untyped[0],\n message: `${untyped.length} derived containment edge${one ? '' : 's'} (${shown}${untyped.length > 3 ? ', …' : ''}) ${one ? 'has' : 'have'} no type: no declared containment type runs between the child's value and the parent's; ${one ? 'it draws' : 'they draw'} in the containment stroke and pull${one ? 's' : ''} nothing while push and pull key on the style axis`,\n }]\n}\n","// Graph View: a force-directed graph, filtered, highlighted and tuned.\n// Wires together validation, simulation, viewport, rendering and interaction.\n\nimport React from 'react'\nimport './GraphView.css'\nimport { nodesById, readableGraph, isContainment, withContainmentEdges } from '../graph/model'\nimport type { Graph, GraphEdge, GraphNode } from '../graph/model'\nimport type { FilterState, AxisLocks } from '../filter/types'\nimport { initialFilter, filterStatesEqual, hiddenMatchesOf, listEqual, locksEqual, passesDescriptors, reseedFilter } from '../filter/types'\nimport { reconcileFilter } from '../filter/reconcile'\nimport type { ForceParams, Simulation } from '../graph/simulation'\nimport { bandOverlay, layoutFrame, resolvePhysics } from '../graph/forces'\nimport { countsOn, isIdentityMapping, valuesOn } from '../graph/axis'\nimport type { AxisId, AxisMapping } from '../graph/axis'\nimport type { PresentSets } from './graph-view/present'\nimport { presentSetsOf } from './graph-view/present'\nimport type { EdgeStroke, Theme } from '../graph/scheme'\nimport { schemeVars } from '../graph/scheme'\nimport { useTheme, THEME_CHOICES } from './graph-view/useTheme'\nimport type { ThemeChoice } from './graph-view/useTheme'\nimport { axisOptions, layoutAxisOptions, pairTuning, sprungEdges } from '../graph/simulation'\nimport { copyGroups, copyKeyOf } from '../graph/model'\nimport type { NodeScaleParams } from '../graph/node-scale'\nimport { CONTAINMENT_STROKE, NEUTRAL_STROKE } from '../graph/shapes'\nimport { defaultLayoutFor, layoutsEqual, makeLayout, openLayout } from '../graph/layout-internal'\nimport type { LayoutState } from '../graph/layout'\nimport { TaxonomyContext, useTaxonomy } from './graph-view/useTaxonomy'\nimport type { Taxonomy } from '../graph/taxonomy'\nimport { worldToScreen } from '../graph/viewport'\nimport type { Viewport } from '../graph/viewport'\nimport { zoomAt } from '../graph/viewport'\nimport { GraphCanvas } from './GraphCanvas'\nimport { CanvasErrorBoundary } from './CanvasErrorBoundary'\nimport { GraphControlPanel } from './GraphControlPanel'\nimport { activeGroupsOf, boostGroupsForNodes, type ActiveGroup, type NodeGroup } from '../graph/groups'\nimport { FilterBar } from './FilterBar'\nimport type { SearchState } from './FilterBar'\nexport type { SearchState } from './FilterBar'\nimport { useRecentlyChanged } from './graph-view/useRecentlyChanged'\nimport { useViewport } from './graph-view/useViewport'\nimport { useHighlight } from './graph-view/useHighlight'\nimport { useSimulation } from './graph-view/useSimulation'\nimport { useVisibleGraph } from './graph-view/useVisibleGraph'\nimport { useSimulationLoop } from './graph-view/useSimulationLoop'\nimport { useControllable, useControlledWarning, useLatest, useStable } from './graph-view/useControllable'\nimport { sameFocusRequest } from './graph-view/focus'\nimport type { FocusRequest } from './graph-view/focus'\nexport type { FocusRequest } from './graph-view/focus'\nimport { useIsomorphicLayoutEffect } from './graph-view/useIsomorphicLayoutEffect'\nimport { validateGraph, derivedContainmentDiagnostics, dropInvalid } from '../graph/validate'\nimport type { GraphDiagnostic } from '../graph/validate'\n\n/** A node's position in the layout, in world units. */\nexport interface Position { x: number; y: number }\n\n/** The host's action for a node, offered in the hover box: its label, and what it does with the host's own node. */\nexport interface NodeAction { label: string; onSelect: (node: GraphNode) => void }\n\n/** What `groupsControl` may ask of the view. */\nexport interface GroupsControl {\n /** Emphasise one group (a legend row under the pointer), or none; the view glows it stronger. */\n emphasize(id: string | null): void\n}\n\n/** Which parts of the pane's own chrome are offered; every part is on by default. */\nexport interface GraphViewChrome {\n /** The search control in the bar. */\n search?: boolean\n /** The lock toggle on each axis. Default: only when the host controls `locks` with `onLocksChange` — a one-view host has nothing to lock against, and a value without its callback is read-only. */\n locks?: boolean\n /** The Fit / theme / Play cluster and the node count. */\n transport?: boolean\n /** The pane's own list of the graph's defects under the bar. */\n diagnostics?: boolean\n /** The shortcuts panel and the `?` key that opens it. */\n shortcuts?: boolean\n /** The control panel (the forces, the node scale). */\n controls?: boolean\n /** The Auto / Light / Dark button. It sits in the transport cluster, so `transport: false` hides it too. */\n themeToggle?: boolean\n /** The \"dependencies / dependents\" line in the hover box. */\n directionLine?: boolean\n /** Centre and zoom to a node's neighbours on double-click. */\n activateZoom?: boolean\n}\n\n/**\n * What a host may ask of a mounted view. `focusNode` is the whole focus\n * recipe in one call: widen the filter just enough on the axes shown\n * (overriding a lock, which flashes), centre on the node once the layout has\n * settled, and select it.\n */\nexport interface GraphViewHandle {\n /** Reveal, centre and select a node by id. False when the id is not in the graph or the filter cannot be widened to show it. */\n focusNode(id: string): boolean\n /** Select a node, or clear the selection with null. An id the graph does not hold is ignored. */\n select(id: string | null): void\n /** Frame the visible nodes. */\n fit(): void\n /** Every node's position, by id — a layout the host may keep and hand back as `defaultPositions`. */\n getPositions(): Map<string, Position>\n /** Move nodes to the given positions (pinned ones stay) and let the layout settle around them. */\n setPositions(positions: ReadonlyMap<string, Position>): void\n}\n\n/**\n * The view's inputs. Only `graph` is required, with a taxonomy from either the\n * `taxonomy` prop or an `TaxonomyContext.Provider` above. Every piece of state\n * follows the React convention: pass the value and its callback to control it,\n * or neither and the view keeps it; a callback alone still reports changes.\n */\nexport interface GraphViewProps {\n /** The graph, built by the host from whatever it reads. Keep one object until its nodes or edges change. */\n graph: Graph\n /** The taxonomy, when not supplied through `TaxonomyContext`. */\n taxonomy?: Taxonomy\n /**\n * Where the pane's shortcuts listen. `pane` (the default): the root is\n * focusable and handles the keys that reach it, so a host page keeps Tab\n * and Space for its own controls. `global`: the window — for a host whose\n * whole page is this pane, or a tab shell's active tab. `none`: no\n * shortcuts — a tab shell's hidden tab.\n */\n keyboard?: 'pane' | 'global' | 'none'\n /** Where nodes start, by id — a layout the host kept (`getPositions`). Read whenever the simulation is built; a node the live layout already holds keeps its place. */\n defaultPositions?: ReadonlyMap<string, Position>\n /** Chip badge counts per axis and value — the host's reading of its own data. Absent, the graph's entity counts (the copies of one `copyKey` once). */\n counts?: ReadonlyMap<string, ReadonlyMap<string, number>>\n /** The host's findings bars, rendered under the filter chips. */\n findings?: React.ReactNode\n /** Groups to glow, over the whole graph; the view restricts them to the visible set and colours them. */\n groups?: readonly NodeGroup[]\n /** The host's control for its groups (`GroupLegend`, or its own), rendered inside the pane — which sets `--gv-toolbar-height` on its root so a control can anchor under the bar — and handed the groups as drawn, colours included, and the view's `emphasize`. */\n groupsControl?: (active: readonly ActiveGroup[], control: GroupsControl) => React.ReactNode\n /** Edges to mark, by id, with a dashed band behind their stroke; a graduated edge is marked when the edge it stands for is. */\n markedEdges?: ReadonlySet<string>\n /** The host's action for a node, offered in the hover box: one for every node, or the host's answer per node (null for none). */\n nodeAction?: NodeAction | ((node: GraphNode) => NodeAction | null)\n /** Which parts of the pane's chrome are offered. Default: everything, the lock toggles only when `locks` is controlled with its callback. */\n chrome?: GraphViewChrome\n /** The search control's title and placeholder, for a host with its own words. */\n searchTitle?: string\n searchPlaceholder?: string\n /**\n * The selected node, when the host controls the selection: the view shows\n * this and nothing else while it is defined. A selection the user makes\n * arrives through `onSelectionChange`; the host answers by setting this, or\n * does not. Absent, the view keeps the selection itself.\n */\n selectedId?: string | null\n /** Where the view's own selection starts when the host does not control it. Read once. */\n defaultSelectedId?: string | null\n /**\n * A request to reveal, centre and select a node — the declarative form of\n * the handle's `focusNode`. Compared by value: a new `id` or `key` is a new\n * request. It waits until its node is in the graph, then `onFocusResult`\n * reports whether the filter could show it. Null, or a newer request,\n * withdraws a waiting one, unreported.\n */\n focus?: FocusRequest | null\n /** A focus request's outcome, once its node is in the graph: whether the node ended visible. A withdrawn request is not reported. */\n onFocusResult?: (request: FocusRequest, shown: boolean) => void\n /** Added to the root's class list; the root keeps its own. */\n className?: string\n /** Merged onto the root's style; the scheme's variables stay. */\n style?: React.CSSProperties\n\n /**\n * The selection per axis. Controlled while defined: the view shows it and\n * never rewrites it — a host that controls the filter re-seeds it for a\n * new graph itself (`reseedFilter`). Absent, the view keeps the filter:\n * `defaultFilter`, or `initialFilter(taxonomy, graph)` re-seeded from each\n * new graph on every axis still at the selection it opened with.\n */\n filter?: FilterState\n /**\n * Every change to the filter — the user's and, when the view keeps the\n * filter itself, its own re-seed on a graph change. `touched` names the\n * axes the user changed in the bar; a write of the view's own (its re-seed,\n * a focus's widening) names none — so a host that controls the filter can\n * keep the set `reseedFilter` takes.\n */\n onFilterChange?: (next: FilterState, touched: readonly AxisId[]) => void\n /**\n * Where the view's own filter starts when the host does not control it —\n * a start that depends on the host's state, which a descriptor's\n * `initialSelection` cannot see. Read once; a host that gives it has\n * chosen its start, so a later graph re-seeds nothing.\n */\n defaultFilter?: FilterState\n /** Which axes are locked against a cross-view adoption. Default: `defaultLocks`, else none. Controlling it with `onLocksChange` shows the lock toggles. */\n locks?: AxisLocks\n onLocksChange?: (next: AxisLocks) => void\n /** Where the view's own locks start when the host does not control them. Read once. */\n defaultLocks?: AxisLocks\n /** The locks a host-side focus into this view overrode (`useLinkedViews`'s `switchTo`); flashed once, beside the view's own. */\n overriddenLocks?: ReadonlySet<string>\n /** The axes shown, in order. Default: `defaultAxes`, else every declared filter axis. Editable in the bar unless controlled without a callback. */\n axes?: readonly string[]\n onAxesChange?: (next: readonly string[]) => void\n /** Where the view's own axes start when the host does not control them. Read once. */\n defaultAxes?: readonly string[]\n /** The search control. Default: `defaultSearch`, else closed and empty. */\n search?: SearchState\n onSearchChange?: (next: SearchState) => void\n /** Where the view's own search starts when the host does not control it. Read once. */\n defaultSearch?: SearchState\n /** The pane's theme choice. Default: `defaultTheme`, else `system`. */\n theme?: ThemeChoice\n onThemeChange?: (next: ThemeChoice) => void\n /** Where the view's own theme choice starts when the host does not control it. Read once. */\n defaultTheme?: ThemeChoice\n /** What `system` resolves to, for a host that knows its own theme. Given, the pane reads no ancestor's `data-theme` and no OS preference for it. */\n systemTheme?: Theme\n /**\n * The layout — the force tuning and the node scale, as the user set them.\n * Controlled while defined: the view reads it, retunes it for the graph and\n * taxonomy it faces (a tuning on an axis the graph no longer carries\n * returns to the style axis, reported through `onLayoutChange`), and\n * otherwise never writes it. Absent, the view keeps the layout:\n * `defaultLayout`, else the taxonomy's defaults.\n */\n layout?: LayoutState\n /** Every change to the layout — the user's tuning in the control panel and the view's retune for a new graph or taxonomy. */\n onLayoutChange?: (next: LayoutState) => void\n /** Where the view's own layout starts when the host does not control it — `layoutFromJSON`'s answer, say. Read once. */\n defaultLayout?: LayoutState\n\n /**\n * The selection the user made — a click, Enter, Escape, a search pick, a\n * focus landing, the handle — as the host's own node, or null when they\n * cleared it. Not called for a change the host made through `selectedId`.\n * When the view keeps the selection itself, also null when the selected\n * node leaves the graph.\n */\n onSelectionChange?: (node: GraphNode | null) => void\n /** The graph's diagnostics, whenever they are recomputed — the first graph's included, and an empty list is a call. */\n onDiagnostics?: (diagnostics: readonly GraphDiagnostic[]) => void\n /** A node double-clicked. */\n onNodeActivate?: (node: GraphNode) => void\n /** The node under the pointer, or null. */\n onHover?: (node: GraphNode | null) => void\n /** The theme the pane resolved to draw in — the choice, or the host's for `system`. */\n onThemeResolved?: (theme: Theme) => void\n}\n\nconst NO_FLASH: ReadonlySet<string> = new Set()\n\n/**\n * The graph view: the host's `Graph`, laid out by forces, filtered by the\n * bar, highlighted on hover and selection, tuned by the control panel and\n * drawn on a canvas. Only `graph` and a taxonomy are required; every piece\n * of state is controlled by the host or kept by the view.\n */\nexport const GraphView = React.forwardRef<GraphViewHandle, GraphViewProps>(function GraphView({\n graph: hostGraph,\n taxonomy: taxonomyProp,\n keyboard = 'pane',\n defaultPositions,\n counts: hostCounts,\n findings,\n groups,\n groupsControl,\n markedEdges,\n nodeAction,\n chrome,\n searchTitle = 'Search nodes (/)',\n searchPlaceholder = 'Search nodes...',\n selectedId, defaultSelectedId = null,\n focus,\n onFocusResult,\n className,\n style,\n filter: filterProp, onFilterChange, defaultFilter: defaultFilterProp,\n locks: locksProp, onLocksChange, defaultLocks, overriddenLocks: hostOverridden,\n axes: hostAxes, onAxesChange, defaultAxes,\n search: searchProp, onSearchChange, defaultSearch,\n theme: themeProp, onThemeChange, defaultTheme, systemTheme,\n layout: layoutProp, onLayoutChange, defaultLayout,\n onSelectionChange, onNodeActivate, onHover, onThemeResolved, onDiagnostics,\n}: GraphViewProps, ref) {\n // The taxonomy: the prop, or the context. Neither is a wiring bug a\n // default would hide, so it is said here, once, before any hook.\n const fromContext = React.useContext(TaxonomyContext)\n const taxonomy = taxonomyProp ?? fromContext\n if (!taxonomy) {\n throw new Error('GraphView has no taxonomy: pass `taxonomy`, or mount it inside <TaxonomyContext.Provider value={…}>.')\n }\n // The host's nodes by id, for every output that hands a node back and for\n // the requests that name one.\n const hostIndex = React.useMemo(() => nodesById(hostGraph.nodes), [hostGraph])\n // State the host may control or leave to the view: the filter, the locks,\n // the axes shown, the search control, the theme choice. The first three\n // are compared by content, so a host that rebuilds one each render is not\n // read as a change.\n // The filter's writes go through `writeFilter`, which tells the host which\n // axes the user touched (the view's own writes touch none), and shadows\n // `filterRef` so a write that follows another in one commit — the re-seed\n // below, then a focus landing on the node that just arrived — builds on\n // the earlier result rather than the render's value.\n const [rawFilter, setFilterState] = useControllable(filterProp, undefined, () => defaultFilterProp ?? initialFilter(taxonomy, hostGraph), 'filter')\n const filter = useStable(rawFilter, filterStatesEqual)\n const filterRef = useLatest(filter)\n const onFilterChangeRef = useLatest(onFilterChange)\n // Whether a write reaches anyone: the view's own state, or a host that\n // listens. A controlled filter without a callback is read-only.\n const filterWritable = filterProp === undefined || onFilterChange !== undefined\n const writeFilter = React.useCallback((next: FilterState, touched: readonly AxisId[]) => {\n filterRef.current = next\n setFilterState(next)\n onFilterChangeRef.current?.(next, touched)\n }, [filterRef, setFilterState, onFilterChangeRef])\n // The axes the user has touched in the bar — state, not an inference: a\n // selection that happens to equal a graph's opening one is not thereby\n // untouched. A later graph re-seeds the rest.\n const touchedRef = React.useRef(new Set<AxisId>())\n const userWriteFilter = React.useCallback((next: FilterState, touched: readonly AxisId[]) => {\n for (const axis of touched) touchedRef.current.add(axis)\n writeFilter(next, touched)\n }, [writeFilter])\n // A graph that changes while the view keeps the filter itself re-seeds\n // every axis the user has not touched: a host that mounts empty while its\n // data loads is not left blank, and a value first seen later is not born\n // hidden. The user's selections stand; a host that gave `defaultFilter`\n // chose its start (read once, at mount); a host that controls the filter\n // owns it, and the view never rewrites it.\n const choseStartRef = React.useRef(defaultFilterProp !== undefined)\n const seededFromRef = React.useRef(hostGraph)\n React.useEffect(() => {\n if (seededFromRef.current === hostGraph) return\n seededFromRef.current = hostGraph\n if (filterProp !== undefined || choseStartRef.current) return\n const next = reseedFilter(filter, taxonomy, hostGraph, touchedRef.current)\n if (next !== filter) writeFilter(next, [])\n }, [hostGraph]) // eslint-disable-line react-hooks/exhaustive-deps\n const [rawLocks, setLocks] = useControllable(locksProp, onLocksChange, () => defaultLocks ?? new Map<string, boolean>(), 'locks')\n const locks = useStable(rawLocks, locksEqual)\n const [rawAxes, setAxes] = useControllable(hostAxes, onAxesChange, () => defaultAxes ?? taxonomy.filterAxes.map(d => d.id), 'axes')\n const axes = useStable(rawAxes, listEqual)\n const axesEditable = hostAxes === undefined || onAxesChange !== undefined\n const [search, setSearch] = useControllable(searchProp, onSearchChange, () => defaultSearch ?? { query: '', active: false }, 'search')\n const [themeChoice, setThemeChoice] = useControllable(themeProp, onThemeChange, () => defaultTheme ?? 'system', 'theme')\n const searchQuery = search.query\n const searchActive = search.active\n // The axes this view filters on are those shown, in order: an axis the host\n // declared but this view does not show is not filtering here.\n const filterAxes = React.useMemo(\n () => axes.flatMap(id => { const d = taxonomy.axisFor(id); return d ? [d] : [] }),\n [axes, taxonomy],\n )\n const show = {\n search: chrome?.search ?? true,\n locks: chrome?.locks ?? (locksProp !== undefined && onLocksChange !== undefined),\n transport: chrome?.transport ?? true,\n diagnostics: chrome?.diagnostics ?? true,\n shortcuts: chrome?.shortcuts ?? true,\n directionLine: chrome?.directionLine ?? true,\n activateZoom: chrome?.activateZoom ?? true,\n controls: chrome?.controls ?? true,\n themeToggle: chrome?.themeToggle ?? true,\n }\n // The host's graph, checked once: every defect named, the edges an error\n // names left out of what is drawn, the containment edge each `parentId`\n // implies added where the host listed none, and the one warning the drawn\n // graph adds (a derived edge no type claims). The list is shown under the\n // bar and handed to `onDiagnostics`.\n const { diagnostics, graph } = React.useMemo(() => {\n const own = validateGraph(hostGraph, taxonomy)\n const drawn = withContainmentEdges(dropInvalid(hostGraph, own))\n return { diagnostics: [...own, ...derivedContainmentDiagnostics(drawn, hostGraph, taxonomy)], graph: drawn }\n }, [hostGraph, taxonomy])\n const graphIndex = React.useMemo(() => nodesById(graph.nodes), [graph])\n // The chip badges: the host's reading of its own data, or a count of the\n // graph's entities per axis and value.\n const nodeCounts = React.useMemo(() => countsOn(graph.nodes, axes), [graph, axes])\n const counts = hostCounts ?? nodeCounts\n // Every node's copies, by the key they share — derived here, once per graph.\n const copies = React.useMemo(() => copyGroups(graph), [graph])\n\n // --- Core state ---\n // Camera (viewport transform, container ref, fit/center coordination).\n const { viewport, setViewport, containerRef, initialFitDone, pendingCenterRef, measure, fit } = useViewport()\n // The simulation instance + its handlers are owned by useSimulation, and the\n // RAF loop + fps by useSimulationLoop, both called below once `graph` and the\n // visible subgraph are available.\n\n // Interaction / highlight state (hover, selection, keyboard focus, shift, and\n // the control-panel preview) is owned by useHighlight, called below once the\n // visible subgraph it derives from is available.\n\n // --- Search ---\n const searchInputRef = React.useRef<HTMLInputElement>(null)\n const [shortcutsOpen, setShortcutsOpen] = React.useState(false)\n\n // The overlay (filter chips, search, the host's findings, the pane's\n // diagnostics, the search results) floats over the full-bleed canvas, and\n // its height is dynamic. It is measured whole, so a host control anchored\n // at `--gv-toolbar-height` sits under everything the overlay shows, the open\n // search results included.\n const toolbarRef = React.useRef<HTMLDivElement>(null)\n const [toolbarHeight, setToolbarHeight] = React.useState(0)\n useIsomorphicLayoutEffect(() => {\n const el = toolbarRef.current\n if (!el || typeof ResizeObserver === 'undefined') return\n const update = () => setToolbarHeight(el.getBoundingClientRect().height)\n update()\n const ro = new ResizeObserver(update)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n // A pane that measures no height while displayed draws nothing and says\n // nothing: said once here, with the fix. A pane hidden by its host\n // (`display: none`, a tab shell's other tab) has no offset parent and is\n // not the case.\n const saidZeroRef = React.useRef(false)\n useIsomorphicLayoutEffect(() => {\n const el = containerRef.current\n if (!el || typeof ResizeObserver === 'undefined') return\n const check = () => {\n if (saidZeroRef.current || el.offsetParent === null) return\n const { width, height } = el.getBoundingClientRect()\n if (width > 0 && height === 0) {\n saidZeroRef.current = true\n console.warn('[force-graph] the pane measured 0 px tall: give its parent a height — the pane fills a flex column, or a block with a height.')\n }\n }\n check()\n const ro = new ResizeObserver(check)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n const recentlyChanged = useRecentlyChanged(filter)\n\n // What each filter axis can take, read off the graph: the engine declares no\n // value vocabulary, so the bar learns an axis's domain from the nodes.\n const valuesByAxis = React.useMemo(\n () => new Map(taxonomy.filterAxes.map(d => [d.id, valuesOn(graph.nodes, d.id)])),\n [graph, taxonomy],\n )\n\n // Reset camera/fps coordination on each sim rebuild. Held by useSimulation\n // and invoked from its build effect.\n // The camera refits after the first build only: a rebuild that carried the\n // layout over keeps the user's pan and zoom.\n const onSimRebuild = React.useCallback((continued: boolean) => {\n if (!continued) initialFitDone.current = false\n }, [initialFitDone])\n\n // The layout: the host's while `layout` is defined, the view's own\n // otherwise. Every write — the control panel's, a retune — goes through\n // `updateLayout`, which composes two writes in one tick the way the filter's\n // ref does, tells the host, and applies the write when the view owns it.\n const [rawLayout, setLayout] = useControllable<LayoutState>(layoutProp, onLayoutChange, () => defaultLayout ?? defaultLayoutFor(taxonomy), 'layout')\n // Compared by content, as the filter is: a host that re-reads its layout\n // from JSON each render is not read as a change, and the retune does not\n // write back every render.\n const layout = openLayout(useStable(rawLayout, layoutsEqual))\n const layoutRef = useLatest(layout)\n const updateForces = React.useCallback((fn: (prev: ForceParams) => ForceParams) => {\n const prev = layoutRef.current\n const forces = fn(prev.forces)\n if (forces === prev.forces) return\n const next = makeLayout(forces, prev.nodeScale)\n layoutRef.current = next\n setLayout(next)\n }, [layoutRef, setLayout])\n\n // Simulation spine: the instance, version signal, running flag, the force\n // parameters as retuned for this graph and taxonomy, and every\n // sim-mutating handler.\n const {\n simRef,\n simVersion,\n running, setRunning,\n forceParams,\n getNode: getSimNode,\n onParamChange: handleParamChange,\n onForceAdjust: handleForceAdjust,\n onRelayout: handleRelayout,\n onNodeDragStart: handleNodeDragStart,\n onNodeDragMove: handleNodeDragMove,\n onNodeDragEnd: handleNodeDragEnd,\n onToggleRunning: handleToggleRunning,\n } = useSimulation(graph, taxonomy, defaultPositions, onSimRebuild, layout.forces, updateForces)\n\n\n // The dense per-bucket tables the kernels read, resolved once per\n // (params, taxonomy) here and handed to the canvas and every control.\n const resolved = React.useMemo(() => resolvePhysics(forceParams, taxonomy), [forceParams, taxonomy])\n\n // What the push/pull tab offers: every axis this graph admits with its\n // measured token cost, and the spring categories some edge falls in under\n // the current one (the pull tokens). Both read the whole graph, not the\n // visible subset — tuning covers what a filter may reveal next.\n const axisChoices = React.useMemo(() => axisOptions(taxonomy, graph), [taxonomy, graph])\n const layoutAxes = React.useMemo(() => layoutAxisOptions(axisChoices, taxonomy), [axisChoices, taxonomy])\n // The frame the band forces act in and the overlay the canvas draws, both\n // over the WHOLE graph — the same frame the simulation seeds and ticks in.\n const frame = React.useMemo(() => layoutFrame(readableGraph(graph), resolved), [graph, resolved])\n // The theme the pane draws in: the choice, or the host's for system. The\n // scheme rides inline as custom properties in that theme, and every\n // visible edge's stroke is resolved here once, not per frame.\n const theme = useTheme(containerRef, themeChoice, systemTheme)\n const schemeStyle = React.useMemo(() => schemeVars(taxonomy, theme) as React.CSSProperties, [taxonomy, theme])\n const overlay = React.useMemo(() => bandOverlay(graph.nodes, forceParams, resolved, frame,\n (axis, bucket) => axis.mapping.axis === taxonomy.styleAxis() && isIdentityMapping(axis.mapping)\n ? taxonomy.styleOf(bucket).color[theme].fill\n : undefined), [graph, forceParams, resolved, frame, taxonomy, theme])\n\n // A new axis starts both pair forces from the taxonomy's declared physics on\n // its buckets (a tuning keyed by one axis has no reading on another).\n const handleAxisChange = React.useCallback((mapping: AxisMapping) => {\n if (mapping.axis === forceParams.pair.mapping.axis) return\n handleRelayout({ pair: pairTuning(mapping) })\n }, [forceParams.pair.mapping.axis, handleRelayout])\n\n // Render-time node sizing — the layout's other half, tunable from the\n // Controls panel (Misc → Node scaling); a pure-render concern, so a change\n // reheats nothing.\n const nodeScale = layout.nodeScale\n const handleNodeScaleChange = React.useCallback((patch: Partial<NodeScaleParams>) => {\n const prev = layoutRef.current\n const next = makeLayout(prev.forces, { ...prev.nodeScale, ...patch })\n layoutRef.current = next\n setLayout(next)\n }, [layoutRef, setLayout])\n\n // The locks a focus overrode flash once, for 600 ms, then clear. The view's\n // own state: a host never sees it.\n const [flashLocks, setFlashLocks] = React.useState<ReadonlySet<string>>(NO_FLASH)\n React.useEffect(() => {\n if (flashLocks.size === 0) return\n const timer = setTimeout(() => setFlashLocks(NO_FLASH), 600)\n return () => clearTimeout(timer)\n }, [flashLocks])\n // The host's overridden locks (a focus it reconciled itself) are an event,\n // not a value: folded into the view's own flash when they arrive, so the one\n // timer puts both out and an old set never re-lights.\n React.useEffect(() => {\n if (hostOverridden && hostOverridden.size > 0) setFlashLocks(prev => new Set([...prev, ...hostOverridden]))\n }, [hostOverridden])\n\n // Visible subgraph derived from the live sim (filter + edge graduation +\n // summaries + downstream-depth scores), recomputed on filter / sim-rebuild.\n const { visibleNodes, visibleEdges, visibleIds, edgeTypes, origins, nodeSummaries, downstreamScores } =\n useVisibleGraph(simRef, simVersion, filter, filterAxes, taxonomy)\n\n // The visible edges the host marks, each through its original.\n const visibleMarked = React.useMemo(() => {\n const out = new Set<string>()\n if (markedEdges) for (const edge of visibleEdges) if (markedEdges.has(origins.get(edge.id)!)) out.add(edge.id)\n return out\n }, [markedEdges, visibleEdges, origins])\n\n // Every visible edge's stroke, from the type the derivation resolved on the\n // edge's ORIGINAL endpoints, once, not per frame. An edge no type claims\n // draws in its relation's default stroke; the diagnostics have named it.\n const edgeStrokes = React.useMemo(() => {\n const strokes = new Map<GraphEdge, EdgeStroke>()\n for (const edge of visibleEdges) {\n const type = edgeTypes.get(edge.id)\n strokes.set(edge, type !== undefined ? taxonomy.edgeStrokeFor(type) : isContainment(edge) ? CONTAINMENT_STROKE : NEUTRAL_STROKE)\n }\n return strokes\n }, [visibleEdges, edgeTypes, taxonomy])\n\n // Parallel dependency edges — two or more between one pair of visible nodes,\n // kept apart — take lanes a few pixels either side of the line so\n // both read. One pass per visible set.\n const edgeOffsets = React.useMemo(() => {\n const byPair = new Map<string, string[]>()\n for (const e of visibleEdges) {\n if (isContainment(e)) continue\n const key = e.sourceId < e.targetId ? `${e.sourceId}|${e.targetId}` : `${e.targetId}|${e.sourceId}`\n const list = byPair.get(key)\n if (list) list.push(e.id); else byPair.set(key, [e.id])\n }\n const out = new Map<string, number>()\n for (const ids of byPair.values()) {\n if (ids.length < 2) continue\n ids.forEach((id, i) => out.set(id, (i - (ids.length - 1) / 2) * 7))\n }\n return out\n }, [visibleEdges])\n\n // What the controls offer: only what the post-filter visible set holds —\n // `presentSetsOf`.\n // The pull half is read over the edges the simulation springs (the graph's\n // own, both ends visible), not over the graduated edges it draws.\n const sprung = React.useMemo(() => sprungEdges(graph.edges, visibleIds), [graph, visibleIds])\n const present = React.useMemo<PresentSets>(\n () => presentSetsOf(visibleNodes, sprung, resolved, getSimNode),\n [visibleNodes, sprung, resolved, taxonomy, getSimNode],\n )\n\n // The selection: the host's while `selectedId` is defined, the view's own\n // otherwise. Everything the user does to it — a click, Enter, Escape, a\n // search pick, a focus landing, the handle — goes through `requestSelect`,\n // which tells the host of a change and, when the view owns the state,\n // applies it; a host that controls the selection answers by setting the\n // prop, or does not.\n const controlledSelection = selectedId !== undefined\n useControlledWarning('selectedId', controlledSelection)\n const [ownSelected, setOwnSelected] = React.useState<string | null>(defaultSelectedId)\n const selectedNodeId = controlledSelection ? selectedId : ownSelected\n const selectedRef = useLatest(selectedNodeId)\n const onSelectionChangeRef = useLatest(onSelectionChange)\n // Read through refs so the callback never goes stale: the physics loop\n // holds it across renders, and a focus landing after a graph change must\n // report the new graph's node, not the one the loop was created with.\n const hostIndexRef = useLatest(hostIndex)\n const requestSelect = React.useCallback((next: string | null) => {\n // An id the graph does not hold is not a selection: ignored, so what the\n // view shows and what it reports never disagree.\n if (next !== null && !hostIndexRef.current.has(next)) return\n if (!controlledSelection) setOwnSelected(next)\n if (next !== selectedRef.current) onSelectionChangeRef.current?.(next === null ? null : hostIndexRef.current.get(next) ?? null)\n }, [controlledSelection, hostIndexRef, selectedRef, onSelectionChangeRef])\n // The view's own selection does not outlive its node: a graph that no\n // longer holds it clears the selection, and the host hears the null.\n React.useEffect(() => {\n if (controlledSelection || ownSelected === null || hostIndex.has(ownSelected)) return\n setOwnSelected(null)\n onSelectionChangeRef.current?.(null)\n }, [hostIndex]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Hover / keyboard-focus / shift + control-panel preview state, plus the\n // derived transitive-dependency highlight sets and focusedNodeId.\n const {\n hoveredNodeId, setHoveredNodeId,\n setFocusedIndex,\n shiftHeld,\n focusedNodeId,\n highlightedNodes, highlightedEdges,\n activeSection, setActiveSection,\n activePushBucket, setActivePushBucket,\n activeGravityBucket, setActiveGravityBucket,\n activePullEdge, setActivePullEdge,\n } = useHighlight(visibleNodes, visibleEdges, visibleIds, getSimNode, graph, taxonomy, selectedNodeId, hostIndex)\n\n // The groups as drawn: the host's, restricted to the visible set and\n // coloured in order, the one its control emphasises glowing stronger.\n // Drives the canvas glow and the host's control.\n const [emphasizedGroup, setEmphasizedGroup] = React.useState<string | null>(null)\n const groupsHandle = React.useMemo<GroupsControl>(() => ({ emphasize: setEmphasizedGroup }), [])\n const activeGroups = React.useMemo(() => activeGroupsOf(groups ?? [], visibleIds, theme, emphasizedGroup), [groups, visibleIds, theme, emphasizedGroup])\n\n // Canvas glow set: the active groups with the group under the hovered/selected\n // node strengthened, so one group surfaces from many faint ones as the pointer\n // moves. The modal keeps the un-boosted activeGroups (its legend colors don't\n // depend on strength).\n const glowGroups = React.useMemo(\n () => boostGroupsForNodes(activeGroups, [hoveredNodeId, selectedNodeId]),\n [activeGroups, hoveredNodeId, selectedNodeId],\n )\n\n // Search matches against all nodes (not just visible). When search is\n // active we return a Set even if empty so the canvas dims everything —\n // an empty search with a query is \"no matches yet, all non-matching\",\n // not \"no search active\".\n // Hidden matches are counted on the chip hiding them: the first declared\n // axis the node fails, under its value there — a precedence a host's own\n // filter bar can share, so the two read alike.\n const { visibleMatchIds, hiddenMatchCount, hiddenMatches } = React.useMemo(() => {\n const none = new Map<string, ReadonlyMap<string, number>>()\n if (!searchQuery) return { visibleMatchIds: null as Set<string> | null, hiddenMatchCount: 0, hiddenMatches: none }\n const lq = searchQuery.toLowerCase()\n const sim = simRef.current\n if (!sim) return { visibleMatchIds: new Set<string>(), hiddenMatchCount: 0, hiddenMatches: none }\n // The host's hook is handed the host's own node, never the simulation's copy.\n const split = hiddenMatchesOf({ nodes: sim.nodes, filter, taxonomy, axes, visibleIds, matches: node => taxonomy.searchText(hostIndex.get(node.id) ?? node).toLowerCase().includes(lq) })\n return { visibleMatchIds: split.visible, hiddenMatchCount: split.hiddenCount, hiddenMatches: split.hiddenByAxis }\n }, [searchQuery, visibleIds, filter, axes, taxonomy, hostIndex])\n\n // Physics loop + filter-driven reheats. Owns the RAF tick (with one-shot\n // initial fit, cross-view center, fps, and stability stop), the hover tooltip\n // re-render tick, and the seed/reheat-on-filter-change effects. It consumes the\n // sim, the visible subgraph, and the camera/selection handles as inputs.\n const { fps } = useSimulationLoop({\n simRef, running, setRunning,\n visibleIds, visibleNodes, downstreamScores,\n filter, axes: filterAxes,\n hoveredNodeId, selectedNodeId,\n measure, initialFitDone, pendingCenterRef, setViewport, setSelectedNodeId: requestSelect, taxonomy,\n })\n\n // What the host hears: the hover, a double-click, the resolved theme and\n // the graph's diagnostics — always the host's own node objects, never the\n // view's copies. Read through refs, so a host passing a fresh function\n // each render is not re-notified of an unchanged value. The hover does not\n // fire for the empty state the view mounts in; the diagnostics do, since\n // a host wants its first graph's defects too. The selection reports from\n // `requestSelect`, above.\n const onHoverRef = useLatest(onHover)\n const onNodeActivateRef = useLatest(onNodeActivate)\n const onThemeResolvedRef = useLatest(onThemeResolved)\n const onDiagnosticsRef = useLatest(onDiagnostics)\n const mountedRef = React.useRef(false)\n React.useEffect(() => {\n if (!mountedRef.current) return\n onHoverRef.current?.(hoveredNodeId === null ? null : hostIndex.get(hoveredNodeId) ?? null)\n }, [hoveredNodeId]) // eslint-disable-line react-hooks/exhaustive-deps\n React.useEffect(() => { mountedRef.current = true }, [])\n React.useEffect(() => { onThemeResolvedRef.current?.(theme) }, [theme]) // eslint-disable-line react-hooks/exhaustive-deps\n React.useEffect(() => { onDiagnosticsRef.current?.(diagnostics) }, [diagnostics]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Double-click node: center and zoom to neighbors, and tell the host.\n const handleDoubleClickNode = React.useCallback((id: string) => {\n const sim = simRef.current\n if (!sim) return\n const node = sim.getNode(id)\n if (!node) return\n const hostNode = hostIndex.get(id)\n if (hostNode) onNodeActivateRef.current?.(hostNode)\n const neighborIds = new Set<string>([id])\n for (const edge of sim.edges) {\n if (edge.sourceId === id) neighborIds.add(edge.targetId)\n if (edge.targetId === id) neighborIds.add(edge.sourceId)\n }\n if (!show.activateZoom) return\n const neighbors = sim.nodes.filter(n => neighborIds.has(n.id))\n fit(neighbors, 80)\n }, [fit, hostIndex, show.activateZoom])\n\n // Fit-to-view\n const handleFitToView = React.useCallback(() => {\n fit(visibleNodes)\n }, [fit, visibleNodes])\n\n // Search toggle — used by the keyboard handler (Escape closes search). The\n // filter-bar's own search button has its own copy inside FilterBar. Search\n // query is globally shared so clearing it on close affects both views.\n const toggleSearch = () => {\n if (searchActive) {\n setSearch({ query: '', active: false })\n } else {\n setSearch({ query: searchQuery, active: true })\n setTimeout(() => searchInputRef.current?.focus(), 50)\n }\n }\n\n // Hover with a grace period so the pointer can travel from a node onto its\n // hover tooltip (interactive — it carries the host's cross-view button)\n // without the tooltip vanishing. A null from the canvas schedules a clear;\n // the tooltip's onMouseEnter cancels it, onMouseLeave re-arms it.\n const hoverClearTimer = React.useRef<number | null>(null)\n const cancelHoverClear = React.useCallback(() => {\n if (hoverClearTimer.current !== null) {\n clearTimeout(hoverClearTimer.current)\n hoverClearTimer.current = null\n }\n }, [])\n const handleHoverNode = React.useCallback((id: string | null) => {\n cancelHoverClear()\n if (id !== null) {\n setHoveredNodeId(id)\n } else {\n hoverClearTimer.current = window.setTimeout(() => {\n setHoveredNodeId(null)\n hoverClearTimer.current = null\n }, 150)\n }\n }, [cancelHoverClear, setHoveredNodeId])\n\n // Select node from search result\n const handleSelectSearchResult = (nodeId: string) => {\n requestSelect(nodeId)\n // Center viewport on the node\n const sim = simRef.current\n const node = sim?.getNode(nodeId)\n const size = node && measure()\n if (!node || !size) return\n const { width, height } = size\n setViewport({\n scale: viewport.scale,\n x: width / 2 - node.x * viewport.scale,\n y: height / 2 - node.y * viewport.scale,\n })\n }\n\n // What a host may ask of the mounted view. `focusNode` is the whole focus\n // recipe: widen the filter just enough on the axes shown (a lock it\n // overrides flashes), then centre on and select the node once the layout\n // has settled — the loop consumes the request after its warm-up.\n const visibleNodesRef = useLatest(visibleNodes)\n const focusNodeById = React.useCallback((id: string): boolean => {\n const node = graphIndex.get(id)\n if (!node) return false\n const base = filterRef.current\n // Widened by whole chips: the graph says which values each chip holds.\n const result = reconcileFilter({ destination: base, source: base, locks, transition: { kind: 'focus', target: { values: node.values } }, taxonomy, axes, graph })\n // A read-only filter (controlled, no callback) cannot be widened: the\n // node is judged, centred and reported against the filter the view holds.\n const applied = filterWritable ? result.filter : base\n if (applied !== base) {\n writeFilter(applied, [])\n if (result.overriddenLocks.size > 0) setFlashLocks(result.overriddenLocks)\n }\n // Whether the filter shows the node: a value absent on a hiding axis\n // cannot be revealed by widening, and the host should know.\n const shown = passesDescriptors(applied, filterAxes, axis => taxonomy.valueOn(node, axis))\n if (!shown) return false\n pendingCenterRef.current = { nodeId: id }\n if (!running) {\n simRef.current?.reheat(0.1)\n setRunning(true)\n }\n return true\n }, [graphIndex, graph, filterRef, filterWritable, writeFilter, locks, axes, filterAxes, running, setRunning, simRef, pendingCenterRef, taxonomy])\n // The declarative focus. A request is new when its id or key differs from\n // the last one; it waits until its node is in the graph, then runs the\n // focus recipe once and reports whether the node could be shown. A null\n // withdraws a waiting request.\n const lastFocusRef = React.useRef<FocusRequest | null>(null)\n const pendingFocusRef = React.useRef<FocusRequest | null>(null)\n const onFocusResultRef = useLatest(onFocusResult)\n React.useEffect(() => {\n if (!focus) { lastFocusRef.current = null; pendingFocusRef.current = null; return }\n if (sameFocusRequest(lastFocusRef.current, focus)) return\n lastFocusRef.current = focus\n pendingFocusRef.current = focus\n }, [focus])\n // A request whose node is in the graph lands on the NEXT commit, not this\n // one: a write the host or its hook queued in this same commit — the\n // re-seed for the graph that brought the node — has reached a controlled\n // filter by then, and the recipe builds on it instead of overwriting it.\n const [landing, setLanding] = React.useState<FocusRequest | null>(null)\n React.useEffect(() => {\n const request = pendingFocusRef.current\n if (!request || !graphIndex.has(request.id)) return\n pendingFocusRef.current = null\n setLanding(request)\n }, [focus, graphIndex])\n React.useEffect(() => {\n if (!landing) return\n setLanding(null)\n // Withdrawn meanwhile (null, or a newer request): not landed, not reported.\n if (!sameFocusRequest(lastFocusRef.current, landing)) return\n onFocusResultRef.current?.(landing, focusNodeById(landing.id))\n }, [landing]) // eslint-disable-line react-hooks/exhaustive-deps\n React.useImperativeHandle(ref, () => ({\n focusNode: focusNodeById,\n select: requestSelect,\n fit: () => fit(visibleNodesRef.current),\n getPositions: () => new Map((simRef.current?.nodes ?? []).map(n => [n.id, { x: n.x, y: n.y }])),\n setPositions: (next: ReadonlyMap<string, Position>) => {\n const sim = simRef.current\n if (!sim) return\n for (const [id, p] of next) {\n const n = sim.getNode(id)\n if (!n || n.pinned) continue\n n.x = p.x; n.y = p.y; n.vx = 0; n.vy = 0\n }\n sim.reheat(0.1)\n setRunning(true)\n },\n }), [focusNodeById, fit, setRunning, requestSelect, simRef, visibleNodesRef])\n\n // Keyboard, scoped by `keyboard`: on the pane, the focusable root\n // handles the keys that reach it; globally, the window does — a tab shell\n // passes `global` for its active tab and `none` for the hidden one, kept\n // mounted. A control keeps its own keys either way: Space on a button\n // activates the button, not the simulation, and Tab from a control moves\n // focus.\n const handleKey = React.useCallback((e: KeyboardEvent) => {\n // A chord is the browser's or the host's (find, zoom, the host's own\n // shortcuts): never this pane's.\n if (e.metaKey || e.ctrlKey || e.altKey) return\n const target = e.target instanceof Element ? e.target : null\n if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) return\n const onControl = target !== null && target !== containerRef.current &&\n target.closest('button, select, a, [contenteditable], [role=\"tab\"]') !== null\n if (onControl && e.key !== 'Escape') return\n\n switch (e.key) {\n case 'Tab': {\n // Tab cycles nodes only while the pane itself is focused, in either\n // scope; from the page it moves focus as it always does, so a keyboard\n // user can always reach the host's controls.\n if (target !== containerRef.current) return\n e.preventDefault()\n const count = visibleNodes.length\n if (count === 0) return\n if (e.shiftKey) {\n setFocusedIndex(prev => prev > 0 ? prev - 1 : count - 1)\n } else {\n setFocusedIndex(prev => prev < count - 1 ? prev + 1 : 0)\n }\n break\n }\n case 'Enter': {\n e.preventDefault()\n if (focusedNodeId) requestSelect(focusedNodeId)\n break\n }\n case 'Escape': {\n // Cancelled only when something here closes; otherwise the key is the\n // host's. With nothing left to close, the pane gives focus back.\n if (shortcutsOpen) { e.preventDefault(); setShortcutsOpen(false); break }\n if (searchActive) { e.preventDefault(); toggleSearch(); break }\n if (selectedNodeId) { e.preventDefault(); requestSelect(null); break }\n if (keyboard === 'pane') containerRef.current?.blur()\n break\n }\n case 'ArrowLeft':\n case 'ArrowRight':\n case 'ArrowUp':\n case 'ArrowDown': {\n e.preventDefault()\n const PAN_STEP = 30\n const dx = e.key === 'ArrowLeft' ? PAN_STEP : e.key === 'ArrowRight' ? -PAN_STEP : 0\n const dy = e.key === 'ArrowUp' ? PAN_STEP : e.key === 'ArrowDown' ? -PAN_STEP : 0\n setViewport(prev => ({ ...prev, x: prev.x + dx, y: prev.y + dy }))\n break\n }\n case '+':\n case '=': {\n e.preventDefault()\n setViewport(prev => zoomAt(prev, (containerRef.current?.clientWidth ?? 400) / 2, (containerRef.current?.clientHeight ?? 400) / 2, 1.15))\n break\n }\n case '-':\n case '_': {\n e.preventDefault()\n setViewport(prev => zoomAt(prev, (containerRef.current?.clientWidth ?? 400) / 2, (containerRef.current?.clientHeight ?? 400) / 2, 0.85))\n break\n }\n case 'f':\n case 'F': {\n e.preventDefault()\n handleFitToView()\n break\n }\n case '/': {\n if (!show.search) break\n e.preventDefault()\n if (!searchActive) {\n setSearch({ query: searchQuery, active: true })\n setTimeout(() => searchInputRef.current?.focus(), 50)\n } else {\n searchInputRef.current?.focus()\n }\n break\n }\n case ' ': {\n e.preventDefault()\n handleToggleRunning()\n break\n }\n case '?': {\n if (!show.shortcuts) break\n e.preventDefault()\n setShortcutsOpen(prev => !prev)\n break\n }\n }\n }, [keyboard, visibleNodes, focusedNodeId, selectedNodeId, requestSelect, searchActive, searchQuery, setSearch, shortcutsOpen, show.search, show.shortcuts, handleFitToView, handleToggleRunning]) // eslint-disable-line react-hooks/exhaustive-deps\n React.useEffect(() => {\n if (keyboard !== 'global') return\n window.addEventListener('keydown', handleKey)\n return () => window.removeEventListener('keydown', handleKey)\n }, [keyboard, handleKey])\n const paneKeyDown = React.useCallback((e: React.KeyboardEvent) => handleKey(e.nativeEvent), [handleKey])\n\n // The host's action for the hovered node — one for every node, or the\n // host's answer per node — handed the host's own node object, never the\n // simulation's copy.\n const actionFor = React.useCallback((node: GraphNode): NodeAction | null => {\n const host = hostIndex.get(node.id) ?? node\n const action = typeof nodeAction === 'function' ? nodeAction(host) : nodeAction ?? null\n return action && { label: action.label, onSelect: () => action.onSelect(host) }\n }, [nodeAction, hostIndex])\n const hostNodeOf = React.useCallback((id: string) => hostIndex.get(id), [hostIndex])\n\n const nodeCount = visibleNodes.length\n const edgeCount = visibleEdges.length\n\n // Graph-view search adornment: the hidden-match \"+N\" badge.\n const searchExtra = hiddenMatchCount > 0 ? (\n <span className=\"gv-header-search-badge\" title={`${hiddenMatchCount} match${hiddenMatchCount !== 1 ? 'es' : ''} hidden by filters`}>\n +{hiddenMatchCount}\n </span>\n ) : null\n\n return (\n <TaxonomyContext.Provider value={taxonomy}>\n <CanvasErrorBoundary what=\"graph view\">\n <div\n className={`gv-graph-view${className ? ` ${className}` : ''}`}\n ref={containerRef}\n data-theme={theme}\n tabIndex={keyboard === 'pane' ? 0 : undefined}\n onKeyDown={keyboard === 'pane' ? paneKeyDown : undefined}\n style={{ ...schemeStyle, ...(toolbarHeight ? { ['--gv-toolbar-height' as string]: `${toolbarHeight}px` } : {}), ...style } as React.CSSProperties}\n >\n {/* Canvas fills the full viewport beneath the floating overlay */}\n <div className=\"gv-graph-canvas-area\">\n <CanvasErrorBoundary>\n <GraphCanvas\n nodes={visibleNodes}\n edges={visibleEdges}\n viewport={viewport}\n onViewportChange={setViewport}\n onNodeDragStart={handleNodeDragStart}\n onNodeDragMove={handleNodeDragMove}\n onNodeDragEnd={handleNodeDragEnd}\n onDoubleClickNode={handleDoubleClickNode}\n onFitView={handleFitToView}\n onHoverNode={handleHoverNode}\n onSelectNode={requestSelect}\n highlightedNodes={highlightedNodes}\n highlightedEdges={highlightedEdges}\n hoveredNodeId={hoveredNodeId}\n selectedNodeId={selectedNodeId}\n focusedNodeId={focusedNodeId}\n searchMatchIds={visibleMatchIds}\n running={running}\n forceParams={forceParams}\n resolved={resolved}\n overlay={overlay}\n theme={theme}\n edgeStrokes={edgeStrokes}\n edgeOffsets={edgeOffsets}\n activeSection={activeSection}\n activePushBucket={activePushBucket}\n activeGravityBucket={activeGravityBucket}\n activePullEdge={activePullEdge}\n nodeScale={nodeScale}\n groupGlows={glowGroups}\n markedEdges={visibleMarked}\n />\n </CanvasErrorBoundary>\n <GraphHoverTooltip\n hoveredNodeId={hoveredNodeId}\n simRef={simRef}\n visibleEdges={visibleEdges}\n visibleIds={visibleIds}\n viewport={viewport}\n shiftHeld={shiftHeld}\n directionLine={show.directionLine}\n copies={copies}\n nodeSummaries={nodeSummaries}\n nodeAction={actionFor}\n hostNodeOf={hostNodeOf}\n onMouseEnter={cancelHoverClear}\n onMouseLeave={() => handleHoverNode(null)}\n />\n </div>\n\n {/* Floating overlay: the shared filter bar (with the host's findings), the\n pane's own diagnostics, the search results. Measured whole: its height\n is `--gv-toolbar-height`, where a host control anchors. */}\n <div className=\"gv-graph-overlay\" ref={toolbarRef}>\n <div className=\"gv-graph-toolbar\">\n <FilterBar\n chrome={{ search: show.search, locks: show.locks }}\n theme={theme}\n counts={counts}\n values={valuesByAxis}\n filter={filter}\n onFilterChange={userWriteFilter}\n locks={locks}\n onLocksChange={setLocks}\n axes={axes}\n onAxesChange={axesEditable ? setAxes : undefined}\n overriddenLocks={flashLocks}\n recentlyChanged={recentlyChanged}\n search={search}\n onSearchChange={setSearch}\n searchInputRef={searchInputRef}\n searchTitle={searchTitle}\n searchPlaceholder={searchPlaceholder}\n searchExtra={searchExtra}\n hiddenMatches={hiddenMatches}\n findings={findings}\n />\n </div>\n\n {show.diagnostics && diagnostics.length > 0 && <GraphDiagnostics diagnostics={diagnostics} />}\n\n {/* Search results dropdown */}\n {visibleMatchIds && visibleMatchIds.size > 0 && searchActive && (\n <div className=\"gv-graph-search-results\">\n {visibleNodes.filter(n => visibleMatchIds.has(n.id)).map(n => (\n <button\n key={n.id}\n className=\"gv-graph-search-result\"\n onClick={() => handleSelectSearchResult(n.id)}\n >\n <span className=\"gv-graph-search-result-type\">{taxonomy.nodeStyleOf(n).label}</span>\n <span className=\"gv-graph-search-result-name\">{n.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>{/* /graph-overlay */}\n\n {/* Bottom-left: node/edge counts (shifts right when the shortcuts panel\n is open so they don't overlap). */}\n {show.transport && <div className={`gv-graph-bottom-left${shortcutsOpen ? ' shifted' : ''}`}>\n <span className=\"gv-graph-count\">\n {nodeCount} node{nodeCount !== 1 ? 's' : ''}, {edgeCount} edge{edgeCount !== 1 ? 's' : ''}\n </span>\n </div>}\n\n {/* Bottom-center: simulation controls + live fps. */}\n {show.transport && <div className=\"gv-graph-bottom-center\">\n <button className=\"gv-graph-toolbar-btn\" onClick={handleFitToView} title=\"Fit to view (F)\">Fit</button>\n {show.themeToggle && (\n <button\n className={`gv-graph-toolbar-btn${themeChoice === 'system' ? '' : ' active'}`}\n onClick={() => setThemeChoice(THEME_CHOICES[(THEME_CHOICES.indexOf(themeChoice) + 1) % THEME_CHOICES.length])}\n title={`Colour scheme: ${themeChoice}${themeChoice === 'system' ? ` (${theme})` : ''} — click to change`}\n aria-label=\"Colour scheme\"\n >\n {themeChoice === 'system' ? 'Auto' : themeChoice === 'light' ? 'Light' : 'Dark'}\n </button>\n )}\n <button\n className={`gv-graph-toolbar-btn ${running ? 'active' : ''}`}\n onClick={handleToggleRunning}\n title={running ? 'Pause simulation (Space)' : 'Resume simulation (Space)'}\n >\n {running ? 'Pause' : 'Play'}\n </button>\n {/* Always rendered (reserving its width) so toggling Play/Pause never\n shifts the centred cluster; hidden when the simulation is paused. */}\n <span className={`gv-graph-count-fps${running ? '' : ' hidden'}`} title=\"Simulation frames per second\">{fps} fps</span>\n </div>}\n\n {/* Control panel */}\n {show.controls && <GraphControlPanel\n params={forceParams}\n resolved={resolved}\n onParamChange={handleParamChange}\n onAdjust={handleForceAdjust}\n onRelayout={handleRelayout}\n axes={axisChoices}\n layoutAxes={layoutAxes}\n present={present}\n onAxisChange={handleAxisChange}\n onActiveSection={setActiveSection}\n onActivePushBucket={setActivePushBucket}\n onActiveGravityBucket={setActiveGravityBucket}\n onActivePullEdge={setActivePullEdge}\n nodeScale={nodeScale}\n onNodeScaleChange={handleNodeScaleChange}\n />}\n\n {/* The host's control for its groups (top-left), handed the groups as drawn. */}\n {groupsControl?.(activeGroups, groupsHandle)}\n\n {/* Shortcuts panel */}\n {show.shortcuts && shortcutsOpen && (\n <div className=\"gv-graph-shortcuts-panel\">\n <div className=\"gv-graph-shortcuts-title\">\n Keyboard Shortcuts\n <button className=\"gv-graph-shortcuts-close\" onClick={() => setShortcutsOpen(false)}>×</button>\n </div>\n <div className=\"gv-graph-shortcuts-list\">\n <Shortcut keys=\"Tab / Shift+Tab\" desc=\"Cycle focus\" />\n <Shortcut keys=\"Enter\" desc=\"Select focused node\" />\n <Shortcut keys=\"Escape\" desc=\"Deselect / close\" />\n <Shortcut keys=\"Arrow keys\" desc=\"Pan viewport\" />\n <Shortcut keys=\"+ / -\" desc=\"Zoom in / out\" />\n <Shortcut keys=\"F\" desc=\"Fit to view\" />\n {show.search && <Shortcut keys=\"/\" desc=\"Open search\" />}\n <Shortcut keys=\"Space\" desc=\"Toggle simulation\" />\n <Shortcut keys=\"Shift + hover\" desc=\"Upstream deps\" />\n <Shortcut keys=\"?\" desc=\"This panel\" />\n </div>\n </div>\n )}\n </div>\n </CanvasErrorBoundary>\n </TaxonomyContext.Provider>\n )\n})\n\n/** The pane's own findings about the host's graph: each with its code; errors name edges the view does not draw. */\nfunction GraphDiagnostics({ diagnostics }: { diagnostics: readonly GraphDiagnostic[] }) {\n const errors = diagnostics.filter(d => d.severity === 'error').length\n const warnings = diagnostics.length - errors\n const parts: string[] = []\n if (errors > 0) parts.push(`${errors} graph error${errors === 1 ? '' : 's'}`)\n if (warnings > 0) parts.push(`${warnings} graph warning${warnings === 1 ? '' : 's'}`)\n return (\n <details className={`gv-graph-diagnostics${errors > 0 ? ' has-errors' : ''}`} role={errors > 0 ? 'alert' : undefined}>\n <summary>{parts.join(', ')}{errors > 0 ? ' — what is named is not drawn' : ''}</summary>\n <ul>\n {diagnostics.map((d, i) => (\n <li key={i} className={`gv-graph-diagnostic ${d.severity}`}><code>{d.code}</code>{d.message}</li>\n ))}\n </ul>\n </details>\n )\n}\n\ninterface GraphHoverTooltipProps {\n hoveredNodeId: string | null\n simRef: React.RefObject<Simulation | null>\n visibleEdges: GraphEdge[]\n visibleIds: Set<string>\n viewport: Viewport\n shiftHeld: boolean\n /** Whether the \"dependencies / dependents\" line is shown. */\n directionLine: boolean\n /** Every node's copies by their shared key, over the whole graph. */\n copies: ReadonlyMap<string, ReadonlySet<string>>\n nodeSummaries: Map<string, string>\n // The host's action for a node, when it offers one.\n nodeAction: (node: GraphNode) => NodeAction | null\n /** The host's own node by id — what the taxonomy's `contextLine` is handed, never the simulation's copy. */\n hostNodeOf: (id: string) => GraphNode | undefined\n // The tooltip is interactive: these keep it open while the pointer is over\n // it (so the cross-view button is clickable).\n onMouseEnter: () => void\n onMouseLeave: () => void\n}\n\nfunction GraphHoverTooltip({ hoveredNodeId, simRef, visibleEdges, visibleIds, viewport, shiftHeld, directionLine, copies, nodeSummaries, nodeAction, hostNodeOf, onMouseEnter, onMouseLeave }: GraphHoverTooltipProps) {\n // Before the early returns: this is a hook, and the component bails out four\n // different ways below.\n const taxonomy = useTaxonomy()\n if (!hoveredNodeId) return null\n const sim = simRef.current\n if (!sim) return null\n const node = sim.getNode(hoveredNodeId)\n if (!node) return null\n\n // The context under the name is the taxonomy's answer: a host that keys a\n // value's identity on its own metadata says so there, as one line or\n // several; the library's default is the parent's name. The hook is handed\n // the host's own node and parent. The icon is the value's own.\n const parent = node.parentId === undefined ? undefined : hostNodeOf(node.parentId) ?? sim.getNode(node.parentId)\n const context = taxonomy.contextLine(hostNodeOf(node.id) ?? node, parent)\n const contextLines: React.ReactNode[] = context === undefined || context === null ? []\n : Array.isArray(context) ? [...(context as readonly React.ReactNode[])] : [context as React.ReactNode]\n const action = nodeAction(node)\n const icon = taxonomy.nodeStyleOf(node).icon\n\n // The composition line for a node that summarises its children (counted\n // per style value, named by its abbreviation). Degree nodes are covered by\n // the connection counts row below, so the box does not say it twice.\n const summaryKind = taxonomy.nodeStyleOf(node).summaryKind\n const compositionLine =\n summaryKind === 'children'\n ? nodeSummaries.get(hoveredNodeId) || undefined\n : undefined\n\n // The node's badges, as the canvas draws them above it, in one line.\n const badgesLine = node.badges && node.badges.length > 0 ? node.badges.join(' ') : undefined\n\n let outgoing = 0, incoming = 0\n for (const e of visibleEdges) {\n if (isContainment(e)) continue\n if (e.sourceId === hoveredNodeId) outgoing++\n if (e.targetId === hoveredNodeId) incoming++\n }\n\n // Copies indicator: when the same entity has more than one node in the\n // graph, say \"copy i of N\" so the user knows the halo they're seeing is\n // intentional. Count only copies that survive the current filter — a hidden\n // copy isn't a copy from the user's current point of view.\n const sisters = copies.get(copyKeyOf(node))\n const visibleCopies = sisters\n ? Array.from(sisters).filter(id => visibleIds.has(id))\n : [node.id]\n const copyCount = visibleCopies.length\n const copyIndex = copyCount > 1 ? visibleCopies.indexOf(node.id) + 1 : 0\n\n const [sx, sy] = worldToScreen(viewport, node.x, node.y)\n\n return (\n <div\n className=\"gv-graph-hover-tooltip\"\n style={{ left: sx, top: sy }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n <div className=\"gv-tooltip-identity\">\n {icon && <span className=\"gv-tooltip-type-icon\">{icon}</span>}\n <span className=\"gv-tooltip-name\">{node.name}</span>\n </div>\n {contextLines.map((line, i) => <div key={i} className=\"gv-tooltip-parent\">{line}</div>)}\n {compositionLine && <div className=\"gv-tooltip-summary\">{compositionLine}</div>}\n {badgesLine && <div className=\"gv-tooltip-summary\">{badgesLine}</div>}\n {copyCount > 1 && (\n <div className=\"gv-tooltip-duplicates\" title=\"The same entity appears in more than one place. Hovering any copy marks all copies.\">\n copy {copyIndex} of {copyCount}\n </div>\n )}\n {summaryKind !== 'none' && (outgoing > 0 || incoming > 0) && (\n <div className=\"gv-tooltip-connections\">\n {outgoing > 0 && <span className=\"gv-tooltip-conn-out\">→{outgoing}</span>}\n {incoming > 0 && <span className=\"gv-tooltip-conn-in\">←{incoming}</span>}\n </div>\n )}\n {directionLine && <div className=\"gv-tooltip-direction\">{shiftHeld ? 'dependents' : 'dependencies'}</div>}\n {action && (\n <button\n className=\"gv-tooltip-cross-view\"\n onClick={() => action.onSelect(node)}\n title={action.label}\n >\n {action.label}\n </button>\n )}\n </div>\n )\n}\n\nfunction Shortcut({ keys, desc }: { keys: string; desc: string }) {\n return (\n <div className=\"gv-graph-shortcut-row\">\n <kbd className=\"gv-graph-shortcut-key\">{keys}</kbd>\n <span className=\"gv-graph-shortcut-desc\">{desc}</span>\n </div>\n )\n}\n\n","// A legend for the group overlay: every group the host offers, with the\n// colour the view drew it in, a toggle when the host lets the user turn\n// groups on and off, and hover emphasis through the view's own control.\n// Both hosts had written this one; it sits where `groupsControl` renders it,\n// under the bar.\n\nimport React from 'react'\nimport './GroupLegend.css'\nimport type { ActiveGroup } from '../graph/groups'\n\n/** The legend's inputs: the items, the groups as drawn, and the host's toggle and emphasis. */\nexport interface GroupLegendProps {\n /** Every group the host offers, in order, with its label. */\n items: readonly { id: string; label: string }[]\n /** The groups as drawn, from `groupsControl` — their colours. An item on but not among them has no node the view draws. */\n active: readonly ActiveGroup[]\n /** Which items are on. Absent: every item is on; the toggles follow `onToggle`. */\n enabled?: ReadonlySet<string>\n /** Turn an item on or off; shown as a checkbox when given. */\n onToggle?: (id: string) => void\n /** Hover emphasis — hand it `groupsControl`'s `emphasize`. */\n onEmphasize?: (id: string | null) => void\n title?: string\n className?: string\n}\n\n/**\n * A legend for the group overlay: every group the host offers with the colour\n * the view drew it in, a checkbox when the host lets the user turn groups on\n * and off, and hover emphasis through the view's own `emphasize`.\n */\nexport function GroupLegend({ items, active, enabled, onToggle, onEmphasize, title, className }: GroupLegendProps) {\n const colorById = React.useMemo(() => new Map(active.map(g => [g.id, g.color] as const)), [active])\n // A legend that goes away under the pointer takes its emphasis with it.\n const onEmphasizeRef = React.useRef(onEmphasize)\n onEmphasizeRef.current = onEmphasize\n React.useEffect(() => () => onEmphasizeRef.current?.(null), [])\n return (\n <div className={`gv-group-legend${className ? ` ${className}` : ''}`} onMouseLeave={onEmphasize ? () => onEmphasize(null) : undefined}>\n {title && <div className=\"gv-group-legend-title\">{title}</div>}\n {items.map(item => {\n const on = enabled === undefined || enabled.has(item.id)\n const color = colorById.get(item.id)\n const Row = onToggle ? 'label' : 'div'\n return (\n <Row key={item.id} className={`gv-group-legend-row${on ? '' : ' off'}`} onMouseEnter={onEmphasize ? () => onEmphasize(item.id) : undefined}>\n {onToggle && <input type=\"checkbox\" checked={on} onChange={() => onToggle(item.id)} />}\n <span className=\"gv-group-legend-swatch\" style={{ background: color ?? 'transparent' }} />\n <span className=\"gv-group-legend-label\">{item.label}</span>\n {on && color === undefined && <span className=\"gv-group-legend-note\">not shown</span>}\n </Row>\n )\n })}\n </div>\n )\n}\n","// The linked-views reducer: what a switch between two views does to their\n// filters. Pure, so a host that keeps its own state can call it.\n\nimport type { AxisId } from '../graph/axis'\nimport type { Graph } from '../graph/model'\nimport type { Taxonomy } from '../graph/taxonomy'\nimport { reconcileFilter } from './reconcile'\nimport type { AxisLocks, FilterState, ReconcileResult, ViewTransition } from './types'\n\n/** One view's share of the linked state: what it filters on, what it holds against adoption, and what the last focus into it overrode. */\nexport interface LinkedViewState {\n filter: FilterState\n locks: AxisLocks\n /** The axes this view shows, in order — the ones a switch reconciles. */\n axes: readonly AxisId[]\n /** The locks the last switch into this view overrode: a focus's, a new set per focus so a bar flashes them once; a manual switch overrides none and clears it. */\n overriddenLocks: ReadonlySet<AxisId>\n}\n\n/** The one empty set every view starts from, so an unchanged state keeps its identity. */\nexport const NO_LOCKS: ReadonlySet<AxisId> = new Set()\n\n/**\n * Switch to `to` from `from`: on a manual switch `to` adopts `from`'s\n * selections on the axes it shows, except where locked; on a focus `to`\n * widens just enough to show the target — to the whole chip holding its\n * value when `graph` is given — overriding locks (recorded). The same state\n * object comes back when nothing changed.\n */\nexport function linkedSwitch<V extends string>(\n state: Readonly<Record<V, LinkedViewState>>,\n taxonomy: Pick<Taxonomy, 'axisFor'>,\n to: V,\n from: V,\n transition: ViewTransition,\n graph?: Pick<Graph, 'nodes'>,\n): { state: Readonly<Record<V, LinkedViewState>>; result: ReconcileResult } {\n const dest = state[to]\n const result = reconcileFilter({\n destination: dest.filter,\n source: state[from].filter,\n locks: dest.locks,\n transition,\n taxonomy,\n axes: dest.axes,\n graph,\n })\n const overridden: ReadonlySet<AxisId> = result.overriddenLocks.size > 0 ? result.overriddenLocks : NO_LOCKS\n const sameLocks = overridden === dest.overriddenLocks || (overridden.size === 0 && dest.overriddenLocks.size === 0)\n if (result.filter === dest.filter && sameLocks) return { state, result }\n return { state: { ...state, [to]: { ...dest, filter: result.filter, overriddenLocks: overridden } }, result }\n}\n","// useLinkedViews — the state two or more views share through one bar: each\n// view's filter, locks and axes, the view shown, the search they share, and\n// the focus a switch asks of a pane. One record per view, the props each\n// view's `GraphView` or `FilterBar` takes, one `switchTo`, one `focus`.\n\nimport React from 'react'\nimport type { AxisId } from '../../graph/axis'\nimport type { Graph, GraphNode } from '../../graph/model'\nimport type { Taxonomy } from '../../graph/taxonomy'\nimport { NO_LOCKS, linkedSwitch } from '../../filter/linked'\nimport type { LinkedViewState } from '../../filter/linked'\nimport { changedAxes, initialFilter, reseedFilter } from '../../filter/types'\nimport type { AxisLocks, FilterState, ReconcileResult, ViewTransition } from '../../filter/types'\nimport type { SearchState } from '../FilterBar'\nimport type { FocusRequest } from './focus'\nimport { useLatest } from './useControllable'\n\nexport type { LinkedViewState } from '../../filter/linked'\n\n/** The state props of one linked view — spread onto its `GraphView` or `FilterBar`. */\nexport interface LinkedViewProps {\n filter: FilterState\n /** A change to the filter; `touched` names the axes the user changed (the bar's and the pane's writes name them), which a later graph then leaves alone. */\n onFilterChange: (next: FilterState, touched?: readonly AxisId[]) => void\n locks: AxisLocks\n onLocksChange: (next: AxisLocks) => void\n axes: readonly AxisId[]\n onAxesChange: (next: readonly AxisId[]) => void\n /** The locks the last switch into this view overrode — a focus's, which the bar flashes once per set; a manual switch clears it. */\n overriddenLocks: ReadonlySet<AxisId>\n /** The search the views share: the same value and callback in every view's props. */\n search: SearchState\n onSearchChange: (next: SearchState) => void\n /** The focus `linked.focus` asked of this view, until the pane reports it; a `FilterBar` ignores it. */\n focus: FocusRequest | null\n onFocusResult: (request: FocusRequest, shown: boolean) => void\n}\n\n/** What `useLinkedViews` is given: the taxonomy, the views by name, where each starts, the graph the default filter is seeded from, and the view shown first. */\nexport interface LinkedViewsOptions<V extends string> {\n taxonomy: Taxonomy\n /** The views, by the names the host switches between. Read once. */\n views: readonly V[]\n /** Where each view starts; a part left out defaults — the taxonomy's default filter over `graph`, no locks, every declared axis. Read once. A filter given here counts as the user's on every axis where it differs from that default. */\n initial?: (view: V) => { filter?: FilterState; locks?: AxisLocks; axes?: readonly AxisId[] } | undefined\n /** The graph the views show: the default filter is seeded from it, a later one re-seeds every view on the axes the user has not touched, and a focus widens by its whole chips. */\n graph?: Pick<Graph, 'nodes'>\n /** The view shown first. Default: the first of `views`. Read once. */\n start?: V\n /** Where the shared search starts — a persisted query, say. Default: closed and empty. Read once. */\n initialSearch?: SearchState\n}\n\n/** The state two or more views share through one bar: each view's props, the view shown, the switch and the focus between views, and the whole for persistence. */\nexport interface LinkedViews<V extends string> {\n /** One view's state props. Throws for a view not in `views`. */\n of(view: V): LinkedViewProps\n /** The view shown: `start`, then the last `switchTo` or `focus`. */\n readonly active: V\n /**\n * Switch to `to` from the active view and make it active: a manual switch\n * (the default) adopts the active view's selections on the destination's\n * axes except where locked; a focus widens the destination to show the\n * target and flashes the locks it overrode. Returns the destination's new\n * filter and the overridden locks.\n */\n switchTo(to: V, transition?: ViewTransition): ReconcileResult\n /**\n * Show `node` in `view`: a focus switch to it, and — for a `GraphView` —\n * a request in `of(view).focus` that centres on and selects the node once\n * it is in the graph, cleared when the pane reports it.\n */\n focus(view: V, node: GraphNode): ReconcileResult\n /** Rewrite one view's filter from its current value — a host rule that follows one view's data. */\n updateFilter(view: V, fn: (filter: FilterState) => FilterState): void\n /** Rewrite every view's filter at once — a stale value pruned when the data changes. */\n updateFilters(fn: (filter: FilterState, view: V) => FilterState): void\n /** The search the views share. */\n readonly search: SearchState\n /** Every view's filter, locks and axes, for a host that persists them (with `active` and `search`). */\n readonly state: Readonly<Record<V, LinkedViewState>>\n}\n\n/**\n * A record over the views, filled by a loop over every one of them so every\n * key is present. `Object.create` answers `any`, which is how the loop's\n * result takes the record's type without asserting a key space.\n */\nfunction recordOver<V extends string, T>(views: readonly V[], make: (view: V) => T): Record<V, T> {\n const out: Record<V, T> = Object.create(null)\n for (const view of views) out[view] = make(view)\n return out\n}\n\n/**\n * One record per view — filter, locks, axes, the locks the last focus into it\n * overrode, the focus asked of it — with each view's props (`of`), the view\n * shown (`active`), `switchTo` and `focus`, a per-view or all-views filter\n * rewrite, the shared `search`, and the whole as `state`. `views`, `initial`,\n * `start` and `initialSearch` are read once. A later `graph` re-seeds every\n * view on the axes the user has not touched — the ones no write through\n * `onFilterChange` named, and on which a filter `initial` gave did not differ\n * from the default — so a host that mounts empty is not left blank and the\n * user's own selections stand.\n */\nexport function useLinkedViews<V extends string>({ taxonomy, views, initial, graph, start, initialSearch }: LinkedViewsOptions<V>): LinkedViews<V> {\n // The axes the user has touched, per view: state, not an inference — a\n // selection that happens to equal a graph's opening one is not thereby\n // untouched. A filter `initial` gave starts touched where it differs from\n // the default; every write that names its axes adds them.\n const touchedRef = React.useRef<Record<V, Set<AxisId>>>(recordOver(views, () => new Set<AxisId>()))\n const [state, setState] = React.useState<Readonly<Record<V, LinkedViewState>>>(() => recordOver(views, view => {\n const given = initial?.(view)\n const seed = initialFilter(taxonomy, graph ?? { nodes: [] })\n if (given?.filter !== undefined) for (const axis of changedAxes(seed, given.filter)) touchedRef.current[view].add(axis)\n return {\n filter: given?.filter ?? seed,\n locks: given?.locks ?? new Map(),\n axes: given?.axes ?? taxonomy.filterAxes.map(d => d.id),\n overriddenLocks: NO_LOCKS,\n }\n }))\n const [active, setActive] = React.useState<V>(() => start ?? views[0]!)\n const [search, setSearch] = React.useState<SearchState>(() => initialSearch ?? { query: '', active: false })\n const [focusRequests, setFocusRequests] = React.useState<Readonly<Record<V, FocusRequest | null>>>(() => recordOver(views, () => null))\n const focusSeq = React.useRef(0)\n const stateRef = useLatest(state)\n const activeRef = useLatest(active)\n const taxonomyRef = useLatest(taxonomy)\n const graphRef = useLatest(graph)\n\n // One set of callbacks per view, made once: a view's props keep their\n // identity across renders, so a memoised view does not re-render for them.\n const viewKey = views.join('\\u0000')\n const callbacks = React.useMemo(() => recordOver(views, (view): Pick<LinkedViewProps, 'onFilterChange' | 'onLocksChange' | 'onAxesChange' | 'onSearchChange' | 'onFocusResult'> => ({\n onFilterChange: (filter, touched = []) => {\n for (const axis of touched) touchedRef.current[view].add(axis)\n setState(prev => (prev[view].filter === filter ? prev : { ...prev, [view]: { ...prev[view], filter } }))\n },\n onLocksChange: locks => setState(prev => (prev[view].locks === locks ? prev : { ...prev, [view]: { ...prev[view], locks } })),\n onAxesChange: axes => setState(prev => (prev[view].axes === axes ? prev : { ...prev, [view]: { ...prev[view], axes } })),\n onSearchChange: setSearch,\n // The request is spent once the pane reports it, so a pane that mounts\n // again later does not act on it twice.\n onFocusResult: request => setFocusRequests(prev => (prev[view] === request ? { ...prev, [view]: null } : prev)),\n })), [viewKey]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const props = React.useMemo(() => recordOver(views, (view): LinkedViewProps => ({ ...state[view], ...callbacks[view], search, focus: focusRequests[view] })), [state, callbacks, search, focusRequests]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // A graph that arrives after the mount re-seeds every view on the axes the\n // user has not touched — so a host that mounts empty while its data loads\n // is not left with a list that shows nothing, and a value first seen later\n // is not born hidden. The user's selections stand.\n const seededFromRef = React.useRef(graph)\n React.useEffect(() => {\n if (seededFromRef.current === graph || graph === undefined) return\n seededFromRef.current = graph\n setState(prev => {\n let changed = false\n const out: Record<V, LinkedViewState> = { ...prev }\n for (const view of Object.keys(prev) as V[]) {\n const filter = reseedFilter(prev[view].filter, taxonomyRef.current, graph, touchedRef.current[view])\n if (filter !== prev[view].filter) { changed = true; out[view] = { ...prev[view], filter } }\n }\n return changed ? out : prev\n })\n }, [graph]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const switchTo = React.useCallback((to: V, transition: ViewTransition = { kind: 'manual' }): ReconcileResult => {\n const from = activeRef.current\n // The answer for the caller comes from the last state this closure saw;\n // the write is a functional update, so it composes with a write queued in\n // the same tick — a bar's own, or an `updateFilter` just before — rather\n // than overwriting it.\n const { state: next, result } = linkedSwitch(stateRef.current, taxonomyRef.current, to, from, transition, graphRef.current)\n stateRef.current = next\n setState(prev => linkedSwitch(prev, taxonomyRef.current, to, from, transition, graphRef.current).state)\n activeRef.current = to\n setActive(to)\n return result\n }, [stateRef, activeRef, taxonomyRef, graphRef])\n\n const focus = React.useCallback((view: V, node: GraphNode): ReconcileResult => {\n const result = switchTo(view, { kind: 'focus', target: { values: node.values } })\n setFocusRequests(prev => ({ ...prev, [view]: { id: node.id, key: ++focusSeq.current } }))\n return result\n }, [switchTo])\n\n const updateFilter = React.useCallback((view: V, fn: (filter: FilterState) => FilterState) => {\n setState(prev => {\n const filter = fn(prev[view].filter)\n return filter === prev[view].filter ? prev : { ...prev, [view]: { ...prev[view], filter } }\n })\n }, [])\n\n const updateFilters = React.useCallback((fn: (filter: FilterState, view: V) => FilterState) => {\n setState(prev => {\n let changed = false\n const out: Record<V, LinkedViewState> = { ...prev }\n for (const view of Object.keys(prev) as V[]) {\n const filter = fn(prev[view].filter, view)\n if (filter !== prev[view].filter) { changed = true; out[view] = { ...prev[view], filter } }\n }\n return changed ? out : prev\n })\n }, [])\n\n return React.useMemo(() => ({\n of: (view: V) => {\n // Judged on the state the hook holds, not the props record: a view\n // added to `views` after the mount has props but no state, and a pane\n // spread from them would be silently uncontrolled.\n const found = state[view] === undefined ? undefined : props[view]\n if (found === undefined) throw new Error(`useLinkedViews: no view ${JSON.stringify(view)}; the views (read once, at mount) are ${Object.keys(state).map(v => JSON.stringify(v)).join(', ')}`)\n return found\n },\n active, switchTo, focus, updateFilter, updateFilters, search, state,\n }), [props, active, switchTo, focus, updateFilter, updateFilters, search, state]) // eslint-disable-line react-hooks/exhaustive-deps\n}\n","// useFocusRequest — a focus request a host raises by node id, each ask new.\n\nimport React from 'react'\nimport type { FocusRequest } from './focus'\n\n/**\n * A focus request and the function that raises one: `requestFocus(id)` is a\n * new request every time, for the same node too, so the view acts on each\n * ask; `requestFocus(null)` withdraws a waiting one. Pass the request as the\n * view's `focus`. A host with two views through `useLinkedViews` has this in\n * `linked.focus(view, node)` and needs none of its own.\n */\nexport function useFocusRequest(): [FocusRequest | null, (id: string | null) => void] {\n const [request, setRequest] = React.useState<FocusRequest | null>(null)\n const seq = React.useRef(0)\n const requestFocus = React.useCallback((id: string | null) => setRequest(id === null ? null : { id, key: ++seq.current }), [])\n return [request, requestFocus]\n}\n","// The taxonomy, as an injected container rather than an imported singleton.\n//\n// The library owns the *shape* of a taxonomy, a host supplies the *entries*,\n// and every consumer resolves through the container it is handed, so the\n// graph engine can be pointed at a different taxonomy without editing the\n// engine.\n//\n// Two properties matter more than the shape itself:\n//\n// - **Resolution never throws.** A lookup miss returns a declared fallback.\n// An unguarded miss is a `TypeError` inside a requestAnimationFrame\n// callback, which React error boundaries do not cover: the canvas freezes\n// on its last good frame with no error UI and no console boundary. It\n// looks like a hang, not a lookup failure.\n// - **Resolution allocates nothing.** It sits in the per-frame draw loop and\n// the O(n²) charge loop, so it is a record lookup and a branch, never a\n// constructed object. Every default is filled in here, once, at\n// construction.\n\nimport type { ReactNode } from 'react'\nimport type { AxisDescriptor, AxisId, AxisValues, AxisValue, ResolvedAxisDescriptor } from './axis'\nimport { hasOwn, resolveDescriptor } from './axis'\nimport type { GraphEdge, GraphNode } from './model'\nimport type { EdgeType, EdgeTypeId, NodeStyle, ResolvedEdgeType, ResolvedNodeStyle } from './shapes'\nimport { NEUTRAL_STROKE, edgePairs, endpointValues, resolveEdgeType as resolveEdgeTypeDefaults, resolveNodeStyle as resolveNodeStyleDefaults } from './shapes'\nimport type { ResolvedScalarAxis, ScalarAxis } from './scalar'\nimport type { EdgeStroke } from './scheme'\nimport { cssSuffix, isSchemeColor } from './scheme'\nimport { BUILT_IN_SCALARS, resolveScalarAxis } from './scalar'\n\n/** A highlight: the node ids lit and the edge ids lit. Edges absent means \"every visible edge between lit nodes\". */\nexport interface Highlight {\n readonly nodes: ReadonlySet<string>\n readonly edges?: ReadonlySet<string>\n}\n\n/** What a highlight rule may read: the visible graph, and a lookup for any node. */\nexport interface HighlightGraph {\n readonly visibleNodes: readonly GraphNode[]\n readonly visibleEdges: readonly GraphEdge[]\n readonly visibleIds: ReadonlySet<string>\n getNode(id: string): GraphNode | undefined\n}\n\n/** Which way a highlight walks from its node: what it depends on, or what depends on it. */\nexport type HighlightDirection = 'downstream' | 'upstream'\n\n/** The minimum a consumer needs of a node in order to resolve its style. */\nexport type StyleSubject = { values: AxisValues }\n\n/** What the hover box shows under a node's name: one line, or several — text or a host's own elements. */\nexport type ContextLine = ReactNode | readonly ReactNode[]\n\n/**\n * The built taxonomy: what the view resolves every style, edge type, axis\n * policy and host hook through. Built once by `createTaxonomy`; derive a\n * variant from the spec, never by spreading this object.\n *\n * What a host reads: `filterAxes`, `axisFor` and `valueOn` to filter its own\n * records the way the view does; `styleOf`, `styleValues` and `colorVar` for\n * chrome of its own in the scheme's colours; `edgeTypes` for a legend;\n * `searchText` to match as the view matches. The rest is the engine's\n * interface — resolution for the draw loop — and a host has no call to read\n * it.\n */\nexport interface Taxonomy {\n /**\n * The axis this taxonomy resolves style on.\n *\n * One axis, named once. Every other feature elects its own — the physics reads\n * its axis off the force params, filters read theirs off the filter — and no\n * feature composes two.\n *\n * A method, not a field, and that is a correctness property rather than a\n * style choice: a container derived by spreading (`{ ...taxonomy,\n * styleAxis: 'other' }`) would rewrite a field and leave the closures\n * answering on the old axis — an object that disagrees with itself, the\n * draw loop reading one half and the physics the other. With one source\n * there is nothing to override out of step.\n */\n styleAxis(): AxisId\n /** The style axis's display name, for a control that names axes. */\n styleLabel(): string\n /**\n * The axes a filter may operate on, with their per-axis policies,\n * every default filled in. Host-declared, like the entries: which axes\n * exist is a domain fact. The engine reads only the policies and never\n * enumerates the ids.\n */\n readonly filterAxes: readonly ResolvedAxisDescriptor[]\n /** One axis's policies, or undefined if the host does not declare it. */\n axisFor(axis: AxisId): ResolvedAxisDescriptor | undefined\n /**\n * The value a subject presents on one axis — read off its values, for\n * every axis alike. An axis that is a projection of another is materialized\n * by the host when it builds the node, so nothing here knows which axes are\n * derived.\n */\n valueOn(subject: StyleSubject, axis: AxisId): AxisValue | undefined\n /** Every style value, in declaration order (top of the hierarchy first). */\n readonly styleValues: readonly AxisValue[]\n /** Every edge type, in control-panel order, its defaults filled in. */\n readonly edgeTypes: readonly ResolvedEdgeType[]\n /** Every computed scalar a layout axis may key on, its words filled in: the built-ins, then the host's. */\n readonly scalars: readonly ResolvedScalarAxis[]\n /** How the canvas strokes an edge of this type; the neutral stroke, said once, for an id the taxonomy does not declare. */\n edgeStrokeFor(id: EdgeTypeId): EdgeStroke\n /**\n * The custom property a value's fill is published under (`-border` for its\n * border): `--gv-node-` and the value id in kebab case, or a numbered name\n * when the id kebab-cases to nothing or to another value's name. The one\n * way to learn the name; a host's CSS reads it through `schemeVars` or\n * `schemeStylesheet`.\n */\n colorVar(value: AxisValue): string\n /** The hover box's context line(s) for a node — the host's, or its parent's name. */\n contextLine(node: GraphNode, parent: GraphNode | undefined): ContextLine\n /** What the search control matches against for a node — the host's text, or the name. */\n searchText(node: GraphNode): string\n /** The highlight for an active node — the host's refinement of the library's transitive walk in the given direction. */\n highlight(active: GraphNode, base: Highlight, graph: HighlightGraph, direction: HighlightDirection): Highlight\n\n /**\n * Style, size, physics and summary metadata for a value. Never throws — a\n * value the taxonomy does not declare, and an ABSENT value, both resolve to\n * the fallback.\n */\n styleOf(value: AxisValue | undefined): ResolvedNodeStyle\n /** Where a node sits on the style axis, or undefined if it sits nowhere. */\n styleValueOf(subject: StyleSubject): AxisValue | undefined\n /**\n * Short label for a value — one or two characters. Control surfaces render a\n * token per value and have no room for a full name. A value the taxonomy\n * does not declare abbreviates to its own first two characters.\n */\n abbreviationFor(value: AxisValue): string\n /** The same as `styleOf`, addressed by a node. The hot-path entry point. */\n nodeStyleOf(subject: StyleSubject): ResolvedNodeStyle\n /**\n * Which declared edge type an edge is, given the style values of its two\n * ORIGINAL endpoints: the declared type of the edge's relation that runs\n * between those values in either orientation and whose `match` the edge's\n * own values satisfy (a satisfied `match` over none, the declared\n * orientation over the reverse, then declaration order), handed to the\n * spec's `classifyEdge`, which may keep it, replace it, or supply one where\n * there is none; undefined when neither names one. Classified once, on\n * the original endpoints; the type is carried through graduation, so a\n * re-pointed edge keeps its stroke and its spring.\n */\n edgeTypeOf(edge: GraphEdge, src: AxisValue | undefined, tgt: AxisValue | undefined): EdgeTypeId | undefined\n /** Which of two graduated edges on one visible pair survives: the host's choice, or the one kept so far. */\n collapse(kept: GraphEdge, candidate: GraphEdge): GraphEdge\n}\n\n/**\n * What a host declares in order to build an `Taxonomy`: the style axis and\n * its values, the edge types and the rule that classifies an edge, the filter\n * axes. Everything with a sensible default is optional.\n */\nexport interface TaxonomySpec {\n /** The axis every node's style is read on. */\n styleAxis: AxisId\n /** The style axis's display name. Absent, the axis id. */\n styleLabel?: string\n /**\n * The style values, top of the hierarchy first. The order places each\n * value's rest band down the stack when it declares none, and orders the\n * control surfaces and the hover box's child counts.\n */\n styles: readonly NodeStyle[]\n /**\n * Drawn for a value the taxonomy does not declare, and for a node with no\n * value on the style axis. Absent, a grey disc with a question mark that\n * rests anywhere in the stack.\n */\n fallbackStyle?: Omit<NodeStyle, 'id'>\n /** The edge types, in control-panel order. Absent, none: every edge draws untyped in its relation's stroke, and `validateGraph` says so. */\n edgeTypes?: readonly EdgeType[]\n /**\n * The host's say in classifying an edge, given the style values of its\n * original endpoints and `declared` — the type the declarations answer (by\n * relation, endpoint pair, `match` and orientation), or undefined: an id\n * to use, or undefined to keep the declared answer. For the exceptions the\n * declarations cannot express — a type that also runs between pairs it\n * does not declare, say: `(edge, src, tgt, declared) => declared ?? …`.\n * Required only when two declared types on one pair share an orientation\n * and a `match`.\n */\n classifyEdge?: (edge: GraphEdge, src: AxisValue | undefined, tgt: AxisValue | undefined, declared: EdgeTypeId | undefined) => EdgeTypeId | undefined\n /** The axes the filter bar offers. Absent, the style axis alone, as an allow-list. */\n filterAxes?: readonly AxisDescriptor[]\n /**\n * Computed scalars this host adds to the built-ins: each reads node\n * metadata — its own payload included — and the graph's structure, and\n * answers a number per node, which the frame maps onto [0, 1] the way the\n * scalar's `normalize` says. Absent, the built-ins alone.\n */\n scalars?: readonly ScalarAxis[]\n /**\n * The hover box's context under the name: what a host wants said there —\n * addressing metadata from its own payload, a parent by another name, an\n * origin — as one line or several. Absent, the line is the parent's name.\n */\n contextLine?: (node: GraphNode, parent: GraphNode | undefined) => ContextLine\n /**\n * What the search control matches a node on, case-insensitively: a code\n * from the payload beside the name, say. Absent, the name.\n */\n searchText?: (node: GraphNode) => string\n /**\n * The highlight for an active node: given the library's transitive walk\n * from it in `direction`, the host may widen it (members it relates by\n * metadata) or replace it. Absent, the walk is the highlight.\n */\n highlight?: (active: GraphNode, base: Highlight, graph: HighlightGraph, direction: HighlightDirection) => Highlight\n /**\n * When two dependency edges each lose an end to a hidden node and come to\n * run between the same two visible nodes, which of the two originals the\n * drawn edge stands for. Both arguments are the host's own edges, so\n * a host whose edges carry routing metadata may prefer the one that does.\n * Absent, the one met first in the host's edge order is kept. Two edges\n * whose ends are both visible are never collapsed.\n */\n collapse?: (kept: GraphEdge, candidate: GraphEdge) => GraphEdge\n}\n\n/** The style for a value the taxonomy does not declare, when the host declares none. */\nconst DEFAULT_FALLBACK: Omit<NodeStyle, 'id'> = {\n label: 'Unknown',\n icon: '?',\n color: { light: { fill: '#CBD5E1', border: '#94A3B8' }, dark: { fill: '#64748B', border: '#94A3B8' } },\n size: { r: 10 },\n summaryKind: 'none',\n}\n\n/**\n * Build the container, refusing an incoherent spec: a value declared twice, an\n * edge type declared twice, running between values nobody declared or naming\n * no value at an end, a filter axis declared twice, a colour no reader can\n * parse, a band whose ends cross, a policy outside its vocabulary, a scalar\n * id already taken. Every problem is named at once, by its path in the spec.\n * To derive a variant, build one from a spec (`createTaxonomy({ ...spec, … })`);\n * spreading the built object copies closures that still read the original's\n * fields.\n */\nexport function createTaxonomy(spec: TaxonomySpec): Taxonomy {\n const problems: string[] = []\n const values = spec.styles.map(v => v.id)\n const valueSet = new Set<AxisValue>()\n for (const v of values) {\n if (valueSet.has(v)) problems.push(`style value ${JSON.stringify(v)} is declared twice`)\n valueSet.add(v)\n }\n // The custom-property name is the value id in kebab case where that is\n // unique and non-empty; a value whose id kebab-cases to nothing or to\n // another value's name takes a numbered one, so any string a host's data\n // produces is a value.\n const suffixCount = new Map<string, number>()\n for (const v of values) suffixCount.set(cssSuffix(v), (suffixCount.get(cssSuffix(v)) ?? 0) + 1)\n const suffixOf = new Map<AxisValue, string>()\n values.forEach((v, i) => {\n const kebab = cssSuffix(v)\n suffixOf.set(v, kebab !== '' && suffixCount.get(kebab) === 1 ? kebab : `v${i + 1}`)\n })\n // Every colour must be a form every reader parses without a browser (hex,\n // rgb() or hsl()); the canvas would otherwise ignore it silently.\n const colour = (where: string, c: string) => { if (!isSchemeColor(c)) problems.push(`${where} is ${JSON.stringify(c)}, not a hex, rgb() or hsl() colour`) }\n const band = (where: string, b: { min: number; max: number } | undefined) => {\n if (b && !(b.min <= b.max)) problems.push(`${where} runs from ${b.min} to ${b.max}; a band's min must not exceed its max`)\n }\n const checkStyle = (where: string, style: Omit<NodeStyle, 'id'>) => {\n for (const theme of ['light', 'dark'] as const) {\n colour(`${where}.color.${theme}.fill`, style.color[theme].fill)\n colour(`${where}.color.${theme}.border`, style.color[theme].border)\n }\n if (style.whenHidden !== undefined && style.whenHidden !== 'ascend' && style.whenHidden !== 'splice') {\n problems.push(`${where}.whenHidden is ${JSON.stringify(style.whenHidden)}, not 'ascend' or 'splice'`)\n }\n if (style.summaryKind !== undefined && style.summaryKind !== 'children' && style.summaryKind !== 'degree' && style.summaryKind !== 'none') {\n problems.push(`${where}.summaryKind is ${JSON.stringify(style.summaryKind)}, not 'children', 'degree' or 'none'`)\n }\n band(`${where}.physics.band.x`, style.physics?.band?.x)\n band(`${where}.physics.band.y`, style.physics?.band?.y)\n }\n for (const v of spec.styles) checkStyle(`styles.${v.id}`, v)\n const fallbackSpec = spec.fallbackStyle ?? DEFAULT_FALLBACK\n checkStyle('fallbackStyle', fallbackSpec)\n const edgeIds = new Set<EdgeTypeId>()\n // The pair a type runs between, unordered: what classifies an edge when the\n // host's rule says nothing. Two types on one pair are told apart by their\n // `match`, by their orientation, or by the host's rule — or refused.\n const pairKey = (relation: string, a: AxisValue, b: AxisValue) => JSON.stringify(a <= b ? [relation, a, b] : [relation, b, a])\n const byPair = new Map<string, EdgeType[]>()\n const declaredEdgeTypes = spec.edgeTypes ?? []\n for (const e of declaredEdgeTypes) {\n if (edgeIds.has(e.id)) problems.push(`edge type ${JSON.stringify(e.id)} is declared twice`)\n edgeIds.add(e.id)\n if (e.style?.color?.light !== undefined) colour(`edgeTypes.${e.id}.style.color.light`, e.style.color.light)\n if (e.style?.color?.dark !== undefined) colour(`edgeTypes.${e.id}.style.color.dark`, e.style.color.dark)\n for (const end of ['sourceValue', 'targetValue'] as const) {\n const declared = endpointValues(e[end])\n if (declared.length === 0) problems.push(`edgeTypes.${e.id}.${end} names no value; name at least one`)\n declared.forEach((v, i) => {\n if (!valueSet.has(v)) problems.push(`edgeTypes.${e.id}.${end}${declared.length > 1 ? `[${i}]` : ''} is ${JSON.stringify(v)}, which styles does not declare`)\n })\n }\n if (e.relation !== 'containment' && e.relation !== 'dependency') {\n problems.push(`edgeTypes.${e.id}.relation is ${JSON.stringify(e.relation)}, not 'containment' or 'dependency'`)\n }\n if (e.match !== undefined) {\n // An empty match would be \"specific\" and win every edge on its pair,\n // leaving a type without one unreachable; it is refused, not read as absent.\n if (Object.keys(e.match).length === 0) problems.push(`edgeTypes.${e.id}.match is empty; leave it out`)\n for (const [axis, value] of Object.entries(e.match)) {\n if (typeof value !== 'string') problems.push(`edgeTypes.${e.id}.match.${axis} is ${JSON.stringify(value)}, not a value`)\n }\n }\n // Indexed under every pair it runs between; a type on several pairs is\n // a candidate on each.\n for (const [s, t] of edgePairs(e)) {\n const key = pairKey(e.relation, s, t)\n const sharing = byPair.get(key) ?? []\n if (!sharing.includes(e)) sharing.push(e)\n byPair.set(key, sharing)\n }\n }\n // Two types nothing tells apart: the same orientation and the same `match`\n // (or none). Only the host's rule can settle those.\n const sameMatch = (a: EdgeType, b: EdgeType): boolean => {\n if (a.match === undefined || b.match === undefined) return a.match === b.match\n const keys = Object.keys(a.match)\n return keys.length === Object.keys(b.match).length && keys.every(k => a.match![k] === b.match![k])\n }\n if (!spec.classifyEdge) {\n const sameOrientedPair = (a: EdgeType, b: EdgeType): boolean => edgePairs(a).some(([s, t]) => edgePairs(b).some(([s2, t2]) => s === s2 && t === t2))\n const said = new Set<string>()\n for (const [key, types] of byPair) {\n for (let i = 0; i < types.length; i++) {\n for (let j = i + 1; j < types.length; j++) {\n const a = types[i], b = types[j]\n const once = `${a.id}\\u0000${b.id}`\n if (said.has(once) || !sameOrientedPair(a, b) || !sameMatch(a, b)) continue\n said.add(once)\n problems.push(`edge types ${JSON.stringify(a.id)} and ${JSON.stringify(b.id)} run between the same values with the same relation (${JSON.parse(key)[0]}) and nothing tells them apart; give one a match on an edge axis, declare them in opposite orientations, or give classifyEdge`)\n }\n }\n }\n }\n const filterAxes = spec.filterAxes ?? [{ id: spec.styleAxis, label: spec.styleLabel ?? spec.styleAxis, emptyMeans: 'none' as const }]\n const axisIds = new Set<AxisId>()\n for (const d of filterAxes) {\n if (axisIds.has(d.id)) problems.push(`filter axis ${JSON.stringify(d.id)} is declared twice`)\n axisIds.add(d.id)\n if (d.emptyMeans !== 'all' && d.emptyMeans !== 'none') {\n problems.push(`filterAxes.${d.id}.emptyMeans is ${JSON.stringify(d.emptyMeans)}, not 'all' or 'none'`)\n }\n }\n const scalars = [...BUILT_IN_SCALARS, ...(spec.scalars ?? [])].map(resolveScalarAxis)\n const scalarIds = new Set<string>()\n for (const sc of scalars) {\n if (scalarIds.has(sc.id)) problems.push(`scalar ${JSON.stringify(sc.id)} is declared twice (the built-ins are reach, degree and depth)`)\n scalarIds.add(sc.id)\n }\n if (problems.length > 0) throw new Error(`taxonomy is incoherent: ${problems.join('; ')}`)\n\n // Every default filled in once. The styles are one object per value for\n // the life of the container: the draw loop compares them by reference.\n const n = spec.styles.length\n const styles: Record<AxisValue, ResolvedNodeStyle> = Object.create(null)\n spec.styles.forEach((v, i) => { styles[v.id] = resolveNodeStyleDefaults(v, i, n) })\n // The fallback rests anywhere in the stack: it is nobody's tier.\n const fallback = resolveNodeStyleDefaults({ id: '', ...fallbackSpec }, 0, 1)\n if (n > 0 && fallbackSpec.physics?.band?.y === undefined) {\n fallback.physics.band.y = { min: styles[values[0]].physics.band.y.min, max: styles[values[n - 1]].physics.band.y.max }\n }\n const edgeTypes = declaredEdgeTypes.map(resolveEdgeTypeDefaults)\n const strokeById = new Map(edgeTypes.map(e => [e.id, e.style] as const))\n const unstroked = new Set<string>()\n const edgeStrokeFor = (id: EdgeTypeId): EdgeStroke => {\n const stroke = strokeById.get(id)\n if (stroke) return stroke\n if (!unstroked.has(id)) {\n unstroked.add(id)\n console.warn(`[graph] edge type ${JSON.stringify(id)} has no stroke: the taxonomy does not declare it; drawing it neutral`)\n }\n return NEUTRAL_STROKE\n }\n const satisfies = (edge: GraphEdge, match: Readonly<Record<AxisId, AxisValue>>): boolean => {\n for (const axis of Object.keys(match)) if (edge.values?.[axis] !== match[axis]) return false\n return true\n }\n // What the declarations say an edge is: every type of its relation on its\n // endpoint pair that the edge fits — its `match` absent or satisfied. A\n // satisfied match beats the pair alone; the declared orientation beats the\n // reverse; declaration order settles the rest.\n const declaredTypeOf = (edge: GraphEdge, src: AxisValue | undefined, tgt: AxisValue | undefined): EdgeTypeId | undefined => {\n if (src === undefined || tgt === undefined) return undefined\n const candidates = byPair.get(pairKey(edge.relation === 'containment' ? 'containment' : 'dependency', src, tgt))\n if (candidates === undefined) return undefined\n const fits = candidates.filter(t => t.match === undefined || satisfies(edge, t.match))\n const specific = fits.filter(t => t.match !== undefined)\n const pool = specific.length > 0 ? specific : fits\n const oriented = pool.filter(t => edgePairs(t).some(([s, g]) => s === src && g === tgt))\n return (oriented[0] ?? pool[0])?.id\n }\n // The host's rule sees the declared answer and may keep it, replace it, or\n // supply one where there is none — no \"declared pair?\" guard of its own.\n const edgeTypeOf = (edge: GraphEdge, src: AxisValue | undefined, tgt: AxisValue | undefined): EdgeTypeId | undefined => {\n const declared = declaredTypeOf(edge, src, tgt)\n return spec.classifyEdge?.(edge, src, tgt, declared) ?? declared\n }\n\n // Warn once per unrecognized value, and once PER ONTOLOGY: this resolves per\n // node per frame, so a warning on every miss buries the first under sixty a\n // second — but a process-global set would also silence a second taxonomy that\n // is missing the same value, which is a different bug being hidden by the\n // de-duplication meant for the first.\n const warned = new Set<string>()\n const styleOf = (value: AxisValue | undefined): ResolvedNodeStyle => {\n // `hasOwn`, not a plain index: values are host-supplied strings, and\n // `styles['constructor']` on a table with a prototype would walk the chain\n // and return the `Object` function. The table has no prototype, and the\n // check keeps that from mattering.\n const style = value !== undefined && hasOwn(styles, value) ? styles[value] : undefined\n if (style !== undefined) return style\n const label = value ?? '<absent>'\n if (!warned.has(label)) {\n warned.add(label)\n console.warn(\n `[graph] no style declared for value ${JSON.stringify(label)}; using the fallback. ` +\n 'The graph will render, but this node is drawn with placeholder styling.',\n )\n }\n return fallback\n }\n const { styleAxis } = spec\n const styleValueOf = (subject: StyleSubject): AxisValue | undefined => subject.values[styleAxis]\n const abbreviationFor = (value: AxisValue): string =>\n hasOwn(styles, value) ? styles[value].abbreviation : value.slice(0, 2)\n const namer = {\n axis: styleAxis,\n labelFor: (value: AxisValue) => (hasOwn(styles, value) ? styles[value].label : value),\n abbreviationFor,\n iconFor: (value: AxisValue) => (hasOwn(styles, value) ? styles[value].icon : ''),\n }\n const resolvedAxes = filterAxes.map(d => resolveDescriptor(d, namer))\n const byAxis = new Map(resolvedAxes.map(d => [d.id, d]))\n return {\n styleAxis: () => styleAxis,\n styleLabel: () => spec.styleLabel ?? styleAxis,\n filterAxes: resolvedAxes,\n axisFor: id => byAxis.get(id),\n valueOn: (subject, axis) => subject.values[axis],\n abbreviationFor,\n styleValues: values,\n edgeTypes,\n scalars,\n edgeStrokeFor,\n colorVar: value => `--gv-node-${suffixOf.get(value) ?? (cssSuffix(value) || 'undeclared')}`,\n styleOf,\n styleValueOf,\n // Hot path: one property read, one record lookup, one branch. No allocation\n // — this runs per node per frame in the draw loop.\n nodeStyleOf: subject => styleOf(styleValueOf(subject)),\n edgeTypeOf,\n collapse: spec.collapse ?? (kept => kept),\n contextLine: spec.contextLine ?? ((_node, parent) => parent?.name),\n searchText: spec.searchText ?? (node => node.name),\n highlight: spec.highlight ?? ((_active, base) => base),\n }\n}\n","// The layout as state: the force tuning and the node scale, as the user set\n// them. The view keeps it or the host controls it, like the filter; and it\n// goes to a store and back as plain data, like the filter, so a host restores\n// what the user tuned and not only where the nodes came to rest.\n//\n// The state is opaque to a host, and this module's declaration reaches none\n// of the engine's types: `ForceParams` and the force kernels are not part of\n// any surface (DESIGN.md, \"The forces\"). The opening is `layout-internal.ts`,\n// called by value from here and the view.\n\nimport type { AxisId } from './axis'\nimport { isPlainRecord } from '../json'\nimport { defaultLayoutFor, forcesFromJSON, forcesToJSON, makeLayout, nodeScaleFromJSON, openLayout } from './layout-internal'\nimport type { ScalarSpan } from './scalar'\nimport type { SpringPhysics } from './shapes'\nimport type { Taxonomy } from './taxonomy'\n\ndeclare const LAYOUT_STATE: unique symbol\n\n/**\n * The layout as the user tuned it: the forces (which axis push and pull key\n * on and the per-bucket overrides; the two layout axes with theirs; the\n * multipliers, exponents, gravities and dynamics) and the node scale. Opaque\n * to a host: keep it, hand it back as `layout` or `defaultLayout`, and carry\n * it to a store through `layoutToJSON` and `layoutFromJSON`. The view retunes\n * it for the graph and taxonomy it faces — a tuning on an axis the graph no\n * longer carries returns to the style axis — and reports the result.\n */\nexport interface LayoutState {\n readonly [LAYOUT_STATE]: true\n}\n\n/** One layout axis as plain data: a node axis with the bands the user changed, or a computed scalar with the span they dragged. */\nexport type LayoutTuningJSON =\n | { axis: AxisId; overrides?: Record<string, { min: number; max: number }> }\n | { scalar: string; span?: ScalarSpan }\n\n/**\n * A layout as plain data, for a store, a URL or a host's own hand: what\n * `layoutToJSON` writes and `layoutFromJSON` reads. Every part is optional\n * and defaults to the taxonomy's, so a host that wants the view to open on a\n * scalar writes `{ forces: { bandY: { scalar: 'credits' } } }` and nothing\n * else.\n */\nexport interface LayoutJSON {\n forces?: {\n /** The axis push and pull key on, with the buckets the user changed: each bucket's charge and core radius, each edge type's spring. */\n pair?: { axis: AxisId; push?: Record<string, { charge: number; coreRadius: number }>; spring?: Record<string, SpringPhysics> }\n /** The horizontal layout axis. */\n bandX?: LayoutTuningJSON\n /** The vertical layout axis. */\n bandY?: LayoutTuningJSON\n /** Scales every charge (repulsion). */\n pushMultiplier?: number\n /** Scales every core radius (the softening of a charge at close range). */\n coreRadiusMultiplier?: number\n /** The power of (distance² + softening) a charge falls off by; 1 is inverse-square. */\n chargeExponent?: number\n /** Scales every spring (attraction). */\n pullMultiplier?: number\n /** Scales every rest length. */\n distanceMultiplier?: number\n /** The power of a spring's displacement; 1 is Hooke's law. */\n linkExponent?: number\n /** The horizontal band gravity; 0 switches it off. */\n gravityX?: number\n /** The vertical band gravity; 0 switches it off. */\n gravityY?: number\n /** The power of the displacement outside a rest band; 1 is linear. */\n gravityBandExp?: number\n /** Radial layout: the vertical axis's bands become rings about the origin. */\n radial?: boolean\n /** The inward pull on a node by its downstream reach; 0 is off. */\n gravityTopological?: number\n /** The power of the reach score; higher makes the biggest hubs pull hardest. */\n gravityTopologicalExp?: number\n /** The pull toward the origin — the baseline when no band axis acts. */\n gravityCenter?: number\n /** Whether the topological pull acts. */\n topologicalEnabled?: boolean\n /** How fast the simulation cools per tick. */\n alphaDecay?: number\n /** The temperature the simulation stops at. */\n alphaMin?: number\n /** How much of a node's velocity survives a tick. */\n velocityDecay?: number\n }\n /** The node scale: the size knob, and how small and how large zoom may draw a node. */\n nodeScale?: { baseMul?: number; minZoomMul?: number; maxZoomMul?: number }\n}\n\n/** A layout as plain data — every tuning by its axis or scalar, every override by its bucket, every number — for a store or a URL. */\nexport function layoutToJSON(layout: LayoutState): LayoutJSON {\n const { forces, nodeScale } = openLayout(layout)\n return { forces: forcesToJSON(forces), nodeScale: { ...nodeScale } }\n}\n\n/**\n * The layout `layoutToJSON` wrote — or a host's own partial one — over the\n * taxonomy's defaults, or undefined when a part present is not its shape:\n * stored data is not trusted to be, and a host falls back to the default\n * rather than to a layout that means something else. A tuning on an axis\n * other than the style axis is built without buckets and completed by the\n * view when the graph arrives, its overrides kept where their values survive.\n */\nexport function layoutFromJSON(value: unknown, taxonomy: Taxonomy): LayoutState | undefined {\n if (!isPlainRecord(value)) return undefined\n const base = defaultLayoutFor(taxonomy)\n const forces = value.forces === undefined ? base.forces : forcesFromJSON(value.forces, base.forces, taxonomy)\n if (!forces) return undefined\n const nodeScale = value.nodeScale === undefined ? base.nodeScale : nodeScaleFromJSON(value.nodeScale, base.nodeScale)\n if (!nodeScale) return undefined\n return makeLayout(forces, nodeScale)\n}\n"],"names":["TaxonomyContext","React","useTaxonomy","taxonomy","resolveDescriptor","d","style","allow","onStyle","labelFor","v","abbreviationFor","keepCamera","iconFor","values","_a","createAxisMapping","spec","owner","collisions","ids","bucket","value","existing","byValue","identityMapping","axis","seen","base","n","isIdentityMapping","mapping","b","valuesOn","subjects","countsOn","axes","counted","out","anonymous","s","key","keys","m","hasOwn","table","reheatOf","foldReheatPolicy","changedAxes","alpha","seedRevealed","refit","resume","policy","useControllable","onChange","initial","name","own","setOwn","controlled","useControlledWarning","onChangeRef","set","next","wasRef","saidRef","useStable","equals","ref","useLatest","useIsomorphicLayoutEffect","THEME_CHOICES","themeChoiceOf","THEME_MARKER_SELECTOR","themeOfMarker","marker","attr","hostTheme","el","theme","_b","_c","useHostTheme","enabled","host","setHost","read","observer","media","useTheme","choice","systemTheme","cssSuffix","schemeVars","vars","c","HEX3","HEX6","RGB","HSL","hslToRgb","h","l","sat","light","hue","chroma","x","r","g","rgbOf","color","isSchemeColor","NEUTRAL","REFUSED","withAlpha","rgb","schemeStylesheet","darkSelector","lightVars","darkVars","dark","parts","isPlainRecord","proto","isFiniteNumber","EMPTY","selectionFor","state","selectionsEqual","a","withSelection","filterStatesEqual","locksEqual","k","listEqual","i","withLock","locks","locked","lockedFor","initialFilter","graph","chosen","present","chip","mine","on","warnSplitChip","reseedFilter","filter","touched","seed","warnedSplits","added","warned","filterToJSON","selection","filterFromJSON","locksToJSON","locksFromJSON","matchesFocus","node","target","carriesValues","record","passesSelection","valueOn","id","passesDescriptors","hiddenMatchesOf","nodes","visibleIds","matches","descriptors","visible","hiddenCount","hiddenByAxis","failing","tally","NONE","useRecentlyChanged","rawFilter","prevFilterRef","recentlyChanged","setRecentlyChanged","prev","changed","before","timer","toggleValue","current","toggleGroup","anyOn","LockToggle","onClick","flashing","label","title","className","jsx","LockClosedIcon","LockOpenIcon","SearchIcon","size","jsxs","tintOf","chipId","tint","said","NO_LOCKS","LOOKS","FilterBar","taxonomyProp","chrome","themeChoice","hostCounts","hostValues","filterProp","onFilterChange","locksProp","onLocksChange","overriddenLocks","hostRecentlyChanged","searchProp","onSearchChange","hostInputRef","searchTitle","searchPlaceholder","searchExtra","hiddenMatches","findings","refreshFlash","hostAxes","onAxesChange","fromContext","undeclaredTints","rawLocks","setLocks","showLocks","showSearch","setFlashing","refreshing","setRefreshing","rawAxes","setAxes","axesEditable","search","setSearch","searchQuery","searchActive","ownInputRef","searchInputRef","rootRef","schemeStyle","addOpen","setAddOpen","labelOf","tracked","valuesByAxis","counts","lookFor","descriptor","available","toggleSearch","look","chips","empty","allIncluded","isActive","isChanged","sum","count","cls","open","e","copyKeyOf","copyGroups","group","payloadString","payloadNumber","relationOf","edge","isContainment","nodesById","withContainmentEdges","byId","listed","readableGraph","sameValues","sameList","graphEquals","y","reconcileFilter","destination","source","transition","reconcileManual","reconcileFocus","destFilter","sourceFilter","destLocks","overridden","chipValues","presentSet","resolveScalarAxis","scalar","isScalarTuning","t","computeDownstreamReach","adj","list","depths","depth","frontier","cur","effective","parent","bump","guard","normalized","computeDegree","computeContainmentDepth","max","scores","rangeNormalized","min","downstreamReach","degree","containmentDepth","BUILT_IN_SCALARS","NEUTRAL_STROKE","CONTAINMENT_STROKE","endpointValues","declared","edgePairs","type","targets","BAND_FRAME","DEFAULT_RADIUS","MIDDLING_SPRING","stackBand","gap","centre","half","resolveNodeStyle","_d","_f","_e","_h","_g","resolveEdgeType","start","CORE_RADIUS_MIN","ABSENT_VALUE_PHYSICS","isScalarAxis","bucketOf","declaredPush","charge","coreRadius","ph","declaredBand","defaultSpan","extent","resolveAxis","overrides","byBucket","resolvePhysics","params","pushFor","bandOn","layout","sc","resolveSprings","chargeFor","push","coreRadiusFor","pushForKey","bucketId","bandRangeFor","band","bandForKey","presentBuckets","centerOf","bandCenters","medianBandCenter","centers","ringRadii","buckets","lo","span","RADIAL_R_MIN","RADIAL_R_MAX","AT_CENTRE","checkedScores","raw","layoutFrame","resolved","all","centerFor","scoresFor","centerX","centerY","scoresX","scoresY","rings","restOn","center","p","rest","zero","one","bandAxesActing","topologicalActing","bandOverlay","frame","colorOf","acting","colors","withColors","stripes","st","byRadius","radius","stripesOn","bandStripes","byBand","ABSENT_EDGE_PHYSICS","pairId","relation","parsePairId","pair","src","tgt","typical","parsed","A","B","springFor","spring","presentSpringCategories","edges","nodeOf","applyChargeForce","active","rng","crMul","pushMul","chargeExp","rEff","j","dx","dy","dist2","angle","rawDist","rAvg","softening","strength","force","fx","fy","applyLinkForce","activeEdges","nodeMap","categoryOf","pullMul","linkExp","distMul","dist","cat","restDist","disp","absDisp","sign","sd","td","bias","applyBandGravity","applyBandGravityRadial","pullToRest","coord","exp","velocity","hi","bandForce","scale","gy","targetR","f","TOPOLOGICAL_FOCAL_Y","applyTopologicalGravity","gt","radial","score","weight","applyCenterGravity","gc","presentSetsOf","visibleNodes","sprung","getNode","withOverride","tuning","mappedTuning","pairTuning","withPushOverride","withSpringOverride","styleIdentityMapping","identityTuning","SEED_BIAS","TOKEN_CEILING","fitsCeiling","option","axisOptions","mappings","nodeAxes","namerFor","layoutAxisOptions","layoutAxisKey","sameBuckets","retunedFor","knownScalar","bandX","bandY","refreshed","refresh","fresh","fits","fellBack","bucketsFit","pairFits","refreshLayout","FELL_BACK","reason","defaultParamsFor","MAX_VELOCITY","MAX_POSITION","REHEAT_TICKS","sprungEdges","Simulation","positions","__publicField","seedNodes","given","sim","theta","bx","by","xJitter","yJitter","downstreamScores","bands","topo","decay","maxV2","v2","cool","byEdge","sx","sy","DEFAULT_NODE_SCALE","nodeSizeMul","makeLayout","forces","nodeScale","defaultLayoutFor","NUMBER_FIELDS","BOOLEAN_FIELDS","SCALE_FIELDS","forcesToJSON","forcesFromJSON","pairFromJSON","tuningFromJSON","nodeScaleFromJSON","mappingFor","physics","layoutsEqual","DEFAULT_VIEWPORT","MIN_ZOOM","MAX_ZOOM","worldToScreen","vp","wx","wy","screenToWorld","zoomAt","factor","newScale","followResize","dw","dh","fitToView","width","height","padding","minX","maxX","minY","maxY","graphW","graphH","availW","availH","cx","cy","listenWheelZoom","canvas","viewport","onViewportChange","onWheel","rect","FOCUS_RING_COLOR","SELECTION_RING_COLOR","DIM_ALPHA","DIM_ALPHA_GROUPS","GROUP_GLOW_PAD","GROUP_GLOW_CENTER_ALPHA","GROUP_GLOW_EDGE_ALPHA","GROUP_GLOW_EDGE_WIDTH","GROUP_GLOW_FOCUS_SWELL","glowSwell","MARK_DASH","MARK_WIDTH","MARK_ALPHA","ICON_FONT_FAMILY","LABEL_FONT_PX","LABEL_ALPHA","LABEL_MAX_CHARS","LABEL_TRUNC_CHARS","labelText","labelFont","px","BADGE_FONT_PX","BADGE_PAD_X","BADGE_PAD_Y","BADGE_GAP","BADGE_NODE_GAP","BADGE_FILL","BADGE_TEXT_COLOR","badgeFont","roundRectPath","ctx","w","rr","badgeExtent","mul","chipH","cssVar","fallback","ARROWHEAD_SIZE","CULL_MARGIN","LABEL_ELIDE_SCALE","DETAIL_REDUCE_SCALE","laneOffset","offset","tx","ty","forward","len","nx","ny","GraphCanvas","onNodeDragStart","onNodeDragMove","onNodeDragEnd","onDoubleClickNode","onFitView","onHoverNode","onSelectNode","highlightedNodes","highlightedEdges","hoveredNodeId","selectedNodeId","focusedNodeId","searchMatchIds","running","forceParams","overlay","edgeStrokes","edgeOffsets","activeSection","activePushBucket","activeGravityBucket","activePullEdge","groupGlows","markedEdges","canvasRef","containerRef","setSize","dragState","dupNodeIds","countByKey","result","activeDupDefKey","activeId","activeNode","drawData","container","ro","entries","hitTest","viewportRef","onViewportChangeRef","handlePointerDown","handlePointerMove","handlePointerUp","ds","handleDoubleClick","dirtyRef","dpr","loopId","drawFrame","hasHighlight","dimAlpha","labelColor","labelDimColor","glowByNode","gs","gr","grad","sx0","sy0","tx0","ty0","edgeHighlighted","baseAlpha","edgeSearchDimmed","edgeDimmed","edgeAlpha","ax","ay","pushActive","pullActive","gravityActive","FORCE_THRESHOLDS","bucketFilter","maxRing","highlighted","stepAlpha","effectiveCharge","soft","ti","T","ratio","inner","ri","ringIdxFromInner","activePullKey","tensionStretch","tensionCompress","tensionRest","tensionColor","perpX","perpY","tickLen","restScreen","mx1","my1","mx2","my2","hover","fillOf","drawnOn","ox","oy","highlight","ring","isDimmed","toScreen","gradient","s1","s2","z","o","labelBoxes","nodeCircles","def","hw","nodeHighlighted","searchDimmed","nodeDimmed","fill","border","icon","dupHaloActive","selR","badgePx","padX","text","totalW","rowY","isActiveNode","labelPx","labelY","tw","box","hitsLabel","hitsNode","loop","CanvasErrorBoundary","props","error","info","NEUTRAL_BUCKET_COLOR","bucketColor","Plot","yLabel","ySlider","bottom","children","PopContext","PopProvider","popped","setPopped","usePop","FormulaValue","Slider","step","orientation","ariaLabel","popId","clamp","snap","MAP_W","MAP_H","MAP_PAD","MAP_PW","MAP_PH","TOKEN_R","ForceMap2D","tokens","xAxis","yAxis","onDrag","hoveredId","onHover","xSlider","svgRef","draggingRef","xForVal","yForVal","valForX","valForY","py","toUser","svg","pt","ctm","handleDown","handleMove","vx","vy","handleUp","vGrid","hGrid","placed","spreadCoincident","Fragment","token","dim","points","groups","CURVE_W","CURVE_H","CURVE_SAMPLES","lastPoint","pts","last","niceStep","mag","norm","ForceCurves","curves","baselineY","xLabel","xMax","gridXs","CHARGE_MAG_MIN","CHARGE_MAG_MAX","CHARGE_MAG_STEP","CORE_RADIUS_MIN_AXIS","CORE_RADIUS_MAX_AXIS","CORE_RADIUS_STEP","ChargeMap","onParamChange","hoveredBucket","onHoverBucket","ChargeCurves","shown","dMax","rEffs","maxREff","maxMag","sampled","q","ampH","xFor","yFor","EDGE_K_MIN","EDGE_K_MAX","EDGE_K_STEP","EDGE_REST_MIN","EDGE_REST_MAX","EDGE_REST_STEP","describedCategories","SpringMap","hoveredEdge","onHoverEdge","ZERO_Y","springResponse","kEff","restEff","SpringCurves","pull","restEffs","Equation","EquationSection","subtitle","equation","SubTabs","SubTab","onSelect","leading","SubTabBodies","SubTabBody","PUSH_EQUATION","PULL_EQUATION","optionLabel","PushPullControls","onAxisChange","onActiveSection","hoveredPushBucket","onHoverPushBucket","sub","setSub","tuningKey","strengthKey","tuningPatch","strengthPatch","AP_W","AP_H","AP_PAD","PL","PR","PT","PB","W_MIN","W_MAX","W_STEP","DOT_R","yForWorld","AxisControls","onRelayout","hovered","ignored","ScalarPlot","AxisPlot","AxisCurve","tooltip","onStrength","onSpan","dragRef","axisLabel","userY","end","y0","y1","gradientId","handle","onBand","colX","drag","isHovered","anyHovered","yTop","yBot","CURVE_TOP_PAD","_","GravityControls","ForceSwitch","TopologicalCurve","onToggle","TC_W","TC_H","TC_PAD","TC_PAD_L","x0","x1","yMid","N","CONTROL_TABS","DYNAMICS_SLIDERS","NODE_SCALE_SLIDERS","GraphControlPanel","onAdjust","layoutAxes","onActivePushBucket","onActiveGravityBucket","onActivePullEdge","onNodeScaleChange","setOpen","activeTab","setActiveTab","hoveredPullEdge","setHoveredPullEdge","handleHoverPullEdge","setHoveredPushBucket","handleHoverPushBucket","hoveredGravityBucket","setHoveredGravityBucket","handleHoverGravityBucket","emitParamChange","patch","SectionSlot","SliderRow","nodeType","display","typed","GROUP_PALETTE","HOVER_STRENGTH","NODE_FOCUS_STRENGTH","activeGroupsOf","emphasized","palette","nodeIds","boostGroupsForNodes","focusIds","boosted","useViewport","setViewport","initialFitDone","pendingCenterRef","sizeRef","measure","fit","getTransitiveDeps","nodeId","direction","queue","getHighlightedEdgeIds","highlightOf","walked","lit","useHighlight","visibleEdges","resetKey","hostIndex","setHoveredNodeId","focusedIndex","setFocusedIndex","shiftHeld","setShiftHeld","setActiveSection","setActivePushBucket","setActiveGravityBucket","setActivePullEdge","down","up","hostNode","defaultRng","useSimulation","onRebuild","updateForces","simRef","prevGraphRef","warnedRef","ontologyChanges","warnedOntologyRef","prevOntologyRef","dragNodeRef","setRunning","simVersion","setSimVersion","onRebuildRef","now","recent","onForceAdjust","partial","onToggleRunning","EMPTY_VISIBLE_GRAPH","findNearestVisibleAncestor","resolveDepEndpoint","side","visited","adjId","ancestor","computeGraphNodeSummary","summaryKind","child","inc","passesFilter","classifyEdges","computeVisibleGraph","vNodes","originalTypes","edgeTypes","origins","kept","originalPairs","projected","srcVisible","tgtVisible","graduated","sources","rs","rt","vEdges","origin","vNodeMap","nodeSummaries","useVisibleGraph","useSimulationLoop","setSelectedNodeId","fps","setFps","fpsTrackRef","frameId","targetNode","track","elapsed","measured","setTooltipTick","prevFilter","ancestorId","sameFocusRequest","RELATIONS","validateGraph","declaredTypes","declaredValues","seenNodes","seenIds","containers","missing","typeId","viaEdges","dropInvalid","diagnostics","badEdges","badParents","duplicateNodes","derivedContainmentDiagnostics","drawn","untyped","NO_FLASH","GraphView","hostGraph","keyboard","defaultPositions","groupsControl","nodeAction","selectedId","defaultSelectedId","focus","onFocusResult","defaultFilterProp","defaultLocks","hostOverridden","defaultAxes","defaultSearch","themeProp","onThemeChange","defaultTheme","layoutProp","onLayoutChange","defaultLayout","onSelectionChange","onNodeActivate","onThemeResolved","onDiagnostics","setFilterState","filterRef","onFilterChangeRef","filterWritable","writeFilter","touchedRef","userWriteFilter","choseStartRef","seededFromRef","setThemeChoice","filterAxes","show","graphIndex","nodeCounts","copies","shortcutsOpen","setShortcutsOpen","toolbarRef","toolbarHeight","setToolbarHeight","update","saidZeroRef","check","onSimRebuild","continued","rawLayout","setLayout","layoutRef","fn","getSimNode","handleParamChange","handleForceAdjust","handleRelayout","handleNodeDragStart","handleNodeDragMove","handleNodeDragEnd","handleToggleRunning","axisChoices","handleAxisChange","handleNodeScaleChange","flashLocks","setFlashLocks","visibleMarked","strokes","byPair","controlledSelection","ownSelected","setOwnSelected","selectedRef","onSelectionChangeRef","hostIndexRef","requestSelect","emphasizedGroup","setEmphasizedGroup","groupsHandle","activeGroups","glowGroups","visibleMatchIds","hiddenMatchCount","none","lq","split","onHoverRef","onNodeActivateRef","onThemeResolvedRef","onDiagnosticsRef","mountedRef","handleDoubleClickNode","neighborIds","neighbors","handleFitToView","hoverClearTimer","cancelHoverClear","handleHoverNode","handleSelectSearchResult","visibleNodesRef","focusNodeById","applied","lastFocusRef","pendingFocusRef","onFocusResultRef","landing","setLanding","request","handleKey","PAN_STEP","paneKeyDown","actionFor","action","hostNodeOf","nodeCount","edgeCount","GraphHoverTooltip","GraphDiagnostics","Shortcut","errors","warnings","directionLine","onMouseEnter","onMouseLeave","context","contextLines","compositionLine","badgesLine","outgoing","incoming","sisters","visibleCopies","copyCount","copyIndex","line","desc","GroupLegend","items","onEmphasize","colorById","onEmphasizeRef","item","linkedSwitch","to","from","dest","sameLocks","recordOver","views","make","view","useLinkedViews","initialSearch","setState","setActive","focusRequests","setFocusRequests","focusSeq","stateRef","activeRef","taxonomyRef","graphRef","viewKey","callbacks","switchTo","updateFilter","updateFilters","found","useFocusRequest","setRequest","seq","requestFocus","DEFAULT_FALLBACK","createTaxonomy","problems","valueSet","suffixCount","suffixOf","kebab","colour","where","checkStyle","fallbackSpec","edgeIds","pairKey","declaredEdgeTypes","sharing","sameMatch","sameOrientedPair","t2","types","once","axisIds","scalars","scalarIds","styles","resolveNodeStyleDefaults","resolveEdgeTypeDefaults","strokeById","unstroked","edgeStrokeFor","stroke","satisfies","match","declaredTypeOf","candidates","specific","pool","edgeTypeOf","styleOf","styleAxis","styleValueOf","subject","namer","resolvedAxes","byAxis","_node","_active","layoutToJSON","layoutFromJSON"],"mappings":"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"}
|