import type{LyraEventDetailSnapshot}from'../../../internal/lyra-element.js';import{type TemplateResult,type PropertyValues}from'lit';import{LyraElement}from'../../../internal/lyra-element.js';import type{LyraNodeTypeStyle}from'../../../internal/node-type-style.js';export type{LyraNodeTypeStyle}from'../../../internal/node-type-style.js';import{type LyraGraphCommunity,type LyraGraphLink,type LyraGraphNode}from'./graph-model.js';export type{LyraGraphCommunity,LyraGraphLink,LyraGraphNode,}from'./graph-model.js';import'../../overlays/skeleton/skeleton.class.js';export type LyraGraphLayout='force'|'layered';export type LyraGraphRenderer='svg'|'canvas';export type LyraGraphSelectionMode='none'|'single'|'multiple';export type LyraGraphPickKind='node'|'link'; /** See `nodeLabels`'s own doc for the per-renderer default when unset. */ export type LyraGraphNodeLabelsMode='always'|'zoom'|'none'; /** See `fitTo`'s own doc. `'none'` keeps the numeric `width`/`height` as the drawing space; * `'container'` measures the host's own content box instead. */ export type LyraGraphFit='none'|'container'; /** Shared score-tier thresholds for retrieval relevance and grounding confidence. */ export interface LyraScoreThresholds{readonly high:number;readonly medium:number;}export interface LyraGraphEventMap{'lr-node-click':CustomEvent<{nodeId:string;x:number;y:number;}>;'lr-link-click':CustomEvent<{sourceNodeId:string;targetNodeId:string;linkId?:string;}>;'lr-node-enter':CustomEvent<{nodeId:string;}>;'lr-node-leave':CustomEvent<{nodeId:string;}>;'lr-link-enter':CustomEvent<{sourceNodeId:string;targetNodeId:string;linkId?:string;}>;'lr-link-leave':CustomEvent<{sourceNodeId:string;targetNodeId:string;linkId?:string;}>;'lr-node-expand':CustomEvent<{nodeId:string;}>;'lr-selection-change':CustomEvent>;'lr-community-click':CustomEvent<{communityId:string;}>; /** Frame-coalesced pan/zoom/layout signal — see the class doc's `lr-viewport-change` event entry. */ 'lr-viewport-change':CustomEvent<{k:number;x:number;y:number;}>;} /** * `` — a force-directed node-link diagram with pan/zoom/drag. * Requires the optional peer deps `d3-force`/`d3-drag`/`d3-zoom`/`d3-selection` * (lazy-loaded; a consumer who never uses this component pays zero d3 cost). * * Set `seed` for a deterministic layout: node initial positions become * reproducible (keyed by node id) and the settle happens synchronously * instead of animating, like `prefers-reduced-motion`. `seed` only takes * effect on the update that first populates `nodes`/`links` (or a later * update that adds genuinely new node ids) — willUpdate() only reads it from * inside rebuildSimulation(), which itself only ever assigns x/y to nodes * that don't already have a settled position, so changing `seed` on an * already-rendered graph is a no-op; nothing re-derives already-positioned * nodes' x/y from the new value. * * `hiddenTypes` filters nodes/links by `LyraGraphNode.type` without discarding position state -- * `lastPositionById` remembers every node's last settled x/y across a hide/show round-trip, so * toggling a type off and back on restores each node where it was instead of re-randomizing it. * * `communities` draws one translucent convex-hull blob per entry, behind links/nodes -- a hull's * membership is the union of its own `memberIds` and every node whose `communityId` matches its * `id`. A community with no currently-visible members (all its nodes hidden by `hiddenTypes`, or * simply empty) renders no hull. * * `layout="layered"` swaps the d3-force simulation for a deterministic layered layout (see * `src/internal/layered-layout.ts`) -- node drag is disabled in that mode, and `chargeStrength` is * a documented no-op. * * `renderer="canvas"` swaps the per-node/per-link SVG DOM for a single DPR-aware `` -- * every event/method/property behaves identically to `renderer="svg"` (the default), with hit- * testing resolved via an offscreen color-picking canvas instead of DOM event targets. The * documented trade-offs: no `::part(node)`/`::part(link)` styling (pixels, not elements -- theme * via cssprops instead), no native SVG `` tooltip (replaced by `part="tooltip"`), and a * drawn focus ring instead of a CSS one. Keyboard roving/announcements are preserved through an * offscreen `part="cursor-item"` button per node/link/hull, driving the identical roving-tabindex * logic as `renderer="svg"`. Both renderers skip nonoperable links when moving real keyboard * focus. Zero-width links retain topology but paint neither a stroke nor an arrowhead. * * Public collection properties take bounded, clone-owned readonly snapshots. Create a new * collection and reassign it after changes; mutating the assigned array does not update the view. * * @customElement lr-graph * Empty/blank node, link, node-type, and community identities are omitted, and later duplicate * effective identities are first-wins before layout, keyed DOM, selection, focus, or events. * Retained identity spelling is not rewritten. * * @event lr-node-click - `detail: { nodeId, x, y }`, where `x` and `y` are the * node's current coordinates in the graph's local drawing space. * @event lr-link-click - `detail: { sourceNodeId, targetNodeId, linkId? }`. * @event lr-node-enter - A node was hovered. `detail: { nodeId }`. Suppressed while dragging or * panning. Canvas enter/leave events fire once per hit-identity transition or exit. In SVG, * also toggles a `data-hovered` attribute on that node's `[part="node"]` element for * pure-CSS theming (not a substitute for this event — a consumer computing its own * adjacency-based highlight needs the id, which only the event carries). * @event lr-node-leave - The hover from `lr-node-enter` ended. `detail: { nodeId }`. * @event lr-link-enter - A link was hovered. `detail: { sourceNodeId, targetNodeId, linkId? }`. Same * suppression/`data-hovered` behavior as `lr-node-enter`. * @event lr-link-leave - The hover from `lr-link-enter` ended. `detail: { sourceNodeId, * targetNodeId, linkId? }`. * @event lr-node-expand - A node was double-activated (native `dblclick`, or two Enter/Space * activations of the same focused node within 500ms). `detail: { nodeId }`. Fires for any node * regardless of `LyraGraphNode.expandable` -- that flag only controls the visual "+" affordance and * spoken "expandable" suffix. * @event lr-selection-change - `detail: { nodeIds, linkIds }`. Fires when `selectionMode` is not * `'none'` and the user activates/clears a node or link. The component never assigns * `selectedNodeIds`/`selectedLinkIds` itself -- controlled, mirroring `lr-heatmap.selectedCell`. * @event lr-community-click - A hull was activated. `detail: { communityId }`. * @event lr-viewport-change - `detail: { k, x, y }`, the live d3-zoom camera transform. Fires at * most once per animation frame regardless of how many pan/zoom/simulation-tick updates land * within it, coalescing every source that can move a rendered node's screen position -- a user * pan/zoom gesture, `focusNode()`/`fit()`'s camera tween, and every d3-force simulation tick * (dragging a node, or the initial settle). A consumer anchoring its own UI (e.g. a details * popover) to a node's `getBoundingClientRect()` can re-read it from this event instead of * polling on a `requestAnimationFrame` loop of its own. * @csspart base - The graph wrapper. * @csspart svg - The graph SVG. * @csspart node - A graph node. * @csspart link - A graph link. * @csspart arrowhead - The marker used by directed graph links. * @csspart label - A node label (`renderer="svg"` only; not rendered at all when `nodeLabels` is * `'none'`). * @csspart link-label - A drawn edge label (only rendered when `showEdgeLabels` is set). * @csspart expand-indicator - The "+" badge rendered on a node with `expandable: true`. * @csspart focus-halo - The persistent ring tracking `focusNodeId`'s node. * @csspart hull - A community hull (behind links/nodes; role="button"). * @csspart community-label - A hull's label text. * @csspart live-region - The aria-hidden shadow mirror of the current graph item announcement; * assistive-technology announcements use a shared light-DOM sink. * @csspart data-list - A visually hidden list alternative for graph data. * @csspart empty - The empty-state message, shown when `nodes` is empty. * @csspart error - Static visible error shown instead of the graph when the optional `d3` peer * dependency is not installed; its transition is announced through a shared light-DOM alert. * @csspart canvas - The single canvas surface (`renderer="canvas"` only). * @csspart tooltip - The hover tooltip (`renderer="canvas"` only; the SVG `<title>` replacement). * @csspart cursor-items - The container of offscreen keyboard-roving items (`renderer="canvas"` only). * @csspart cursor-item - An offscreen keyboard-roving item (`renderer="canvas"`'s a11y virtual cursor). * @cssprop [--lr-canvas-reserved-height=var(--lr-size-24rem)] - Default host block size, shared * with the pre-upgrade reservation stylesheet. Below this in the fallback chain, the normalized * `height` property sizes the host too (a private custom property, not itself settable) -- * setting this always overrides `height`, and an explicit outer `block-size` still wins over * both. * @cssprop [--lr-node-fill=var(--lr-color-brand)] - Default node fill, overridden per-node by `LyraGraphNode.color`. * @cssprop [--lr-link-color=var(--lr-color-border)] - Default link stroke, overridden per-link by a link's own `color`. * @cssprop [--lr-graph-cat-1=var(--lr-theme-graph-cat-1,#8250df)] - First categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-2=var(--lr-theme-graph-cat-2,#bf3989)] - Second categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-3=var(--lr-theme-graph-cat-3,#0a7d91)] - Third categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-4=var(--lr-theme-graph-cat-4,#57606a)] - Fourth categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-5=var(--lr-theme-graph-cat-5,#b083f5)] - Fifth categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-6=var(--lr-theme-graph-cat-6,#f470b8)] - Sixth categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-7=var(--lr-theme-graph-cat-7,#52d6e8)] - Seventh categorical fallback color for typed nodes. * @cssprop [--lr-graph-cat-8=var(--lr-theme-graph-cat-8,#c9d1d9)] - Eighth categorical fallback color for typed nodes; the palette wraps * for later `nodeTypes` entries. * @cssprop [--lr-graph-edge-label-halo=var(--lr-color-surface)] - Legibility halo (`stroke`) * behind a drawn edge label, painted under the fill via `paint-order: stroke`. * @cssprop [--lr-graph-focus-halo-color=var(--lr-color-brand)] - `focus-halo` stroke color. * @cssprop [--lr-graph-selected-color=var(--lr-color-success)] - Selected node/link stroke. * @cssprop [--lr-graph-dimmed-opacity=0.35] - Opacity applied to a node/link when * `dimmedNodeIds`/`dimmedLinkIds` includes its id (both SVG and canvas renderers). Visible by * default -- a consumer controlling `dimmedNodeIds`/`dimmedLinkIds` (e.g. * `lr-knowledge-graph-explorer`) sees the dimming take effect with no extra host styling. * @cssprop [--lr-graph-hull-fill=var(--lr-color-brand)] - Hull fill/stroke color. * @cssprop [--lr-graph-hull-opacity=0.12] - Hull element opacity (composites fill+stroke as one * group, avoiding a double-opacity seam at the fill/stroke boundary). Applies to both SVG and * canvas renderers. * @status stable * @since 4.0.0 */ export declare class LyraGraph extends LyraElement<LyraGraphEventMap>{protected static readonly ownedCollectionProperties:readonly string[];static get observedAttributes():string[];static styles:import("lit").CSSResultGroup[];protected static readonly immutableEventDetails:readonly string[]; /** Readonly nodes in the controlled graph model. Node ids provide stable render and interaction identity. */ nodes:readonly LyraGraphNode[]; /** Directed or undirected connections between node ids in `nodes`. */ links:readonly LyraGraphLink[]; /** Declares each `LyraGraphNode.type` value's legend label, fill color, and shape. A typed node with * no matching entry here renders as untyped (default circle, token fill) but still participates * in `hiddenTypes` filtering by its raw `type` string. */ nodeTypes:readonly LyraNodeTypeStyle[]; /** Hides every node whose raw `type` value is listed here (no matching `nodeTypes` entry * required), plus every link incident to a hidden node -- removed from the render, the * simulation input, the keyboard roving ring, the sr-only data list, and the accessible * diagram counts, as if absent. Positions round-trip via `lastPositionById`: toggling a type * off and back on restores each node where it was. */ hiddenTypes:readonly string[]; /** Renders one translucent hull per entry, behind links/nodes. Membership is the union of * `memberIds` and every node whose `communityId` matches this entry's `id`. */ communities:readonly LyraGraphCommunity[];private normalizedGraphModel?;private normalizedGraphSources?; /** A single cached projection keeps every graph consumer on the same deterministic identity * policy without rescanning the bounded public snapshots on every lookup or render branch. */ private get graphModel();private _layout; /** `'force'` (default) runs the existing d3-force simulation, untouched. `'layered'` computes a * deterministic Sugiyama-lite layout (`src/internal/layered-layout.ts`, a shared, * dependency-free util suitable for any future layered-diagram consumer) instead -- no settle * animation, node drag disabled (dragging would fight a computed layout), `chargeStrength` a * documented no-op, `linkDistance` retunes the layer gap. Switching at runtime repositions * without a tween. */ get layout():LyraGraphLayout;set layout(next:LyraGraphLayout); /** `'svg'` (default, unchanged) renders the existing per-node/per-link DOM. `'canvas'` swaps to * a single `<canvas part="canvas">` -- the scale path (an honest ceiling for `'svg'`: dozens to * low hundreds of nodes; `'canvas'` targets roughly 5,000 nodes / 10,000 links). Feature-reduced * by design: no `::part(node)`/`::part(link)` styling (pixels, not elements -- theme via * cssprops), no SVG `<title>`, a drawn focus ring instead of a CSS one. All events/methods/ * props otherwise behave identically across renderers. Runtime changes tear down and rebuild * the surface; positions survive via `prevById`/`lastPositionById`. */ renderer:LyraGraphRenderer; /** Where the drawing space comes from. `'none'` (the default) uses the numeric `width`/`height` * below, unchanged. `'container'` measures the host's own content box -- through the same * `ResizeObserver` the canvas renderer already owns -- and feeds that measurement to the SVG * `viewBox`, the layout's centering force, `focusNode()`/`fit()`'s camera math and the loading * skeleton, so the drawing always matches the box it is rendered into and no host-side observer * is needed. A resize re-centers the running layout in place (`forceCenter` plus a low-alpha * restart); it never rebuilds the simulation, so settled positions survive. While * `'container'` is in effect the measured box wins over `width`/`height`, which stay the * explicit override path under the `'none'` default. This does not change how the host itself * is sized -- an outer `block-size`, `--lr-canvas-reserved-height` and `height` still do that, * and `'container'` simply follows whichever of them won. Falls back to `width`/`height` when * the box is unmeasurable (detached, `display: none`, or a realm with no `ResizeObserver`). */ fitTo:LyraGraphFit; /** Requested graph viewport width in CSS pixels. Ignored while `fitTo === 'container'`. */ width:number; /** Requested graph viewport height in CSS pixels. Also sizes the rendered host itself (see * `--lr-canvas-reserved-height`'s doc) whenever neither that nor an explicit outer `block-size` * overrides it. Only the drawing space is ignored while `fitTo === 'container'`; the host * sizing above still applies. */ height:number; /** Many-body force strength used by the force layout. Negative values repel nodes. */ chargeStrength:number; /** Preferred link length for force layout and layer separation for layered layout. */ linkDistance:number; /** Minimum camera scale accepted by zoom interactions; updates live in both renderers. */ minZoom:number; /** Maximum camera scale accepted by zoom interactions; updates live in both renderers. */ maxZoom:number; /** Accessible name for the graph. A present host `aria-label`, including an explicitly empty * one, makes this host the sole graph owner; otherwise the SVG/canvas owns the localized name. */ accessibleLabel:string|null; /** When set, seeds each node's initial x/y deterministically (keyed by * node id, not array index) instead of forceSimulation()'s own random * start, and settles the simulation synchronously — see rebuildSimulation(). * Only takes effect on the update that first assigns a given node id an * x/y (i.e. supplied at/before `nodes`/`links` first populate, or when a * later update introduces new node ids) — changing `seed` afterwards does * not retroactively reposition already-settled nodes; there is currently * no way to make an already-rendered graph reproducible after the fact. */ seed?:number; /** Draws each resolved (non-dangling) link's `label` as visible SVG text at the segment * midpoint. Off by default — `LyraGraphLink.label` stays spoken/tooltip-only, matching today's * behavior, unless this is set. */ showEdgeLabels:boolean; /** Below this zoom scale, every drawn edge label is hidden (a `data-edge-labels-hidden` * attribute toggled on the zoomed `<g>`, no Lit re-render). Ignored when `showEdgeLabels` is * false. */ edgeLabelMinZoom:number; /** Node-label visibility. `'always'` draws every node's label unconditionally; `'zoom'` hides * them below `NODE_LABEL_MIN_ZOOM` (a `data-node-labels-hidden` attribute toggled on the zoomed * `<g>`, mirroring `showEdgeLabels`/`edgeLabelMinZoom`'s own zoom-gate mechanism, no Lit * re-render); `'none'` never renders them. Unset (the default) preserves each renderer's * pre-existing behavior exactly -- `'always'` for `renderer="svg"`, `'zoom'` for * `renderer="canvas"` -- so this stays a purely additive opt-in. */ nodeLabels?:LyraGraphNodeLabelsMode; /** Declaratively centers the camera on this node id once, the first time it resolves (on mount * or when the id first appears in `nodes`) -- does not re-center on later mutations, so it * can't fight a user's panning on a streaming graph. Renders a persistent halo * (`part="focus-halo"`) around the node while set. See `focusNode()` for the imperative twin. */ focusNodeId:string|null; /** `'none'` (default) preserves today's behavior exactly -- no `aria-pressed`/`data-selected`, * no `lr-selection-change`. Controlled, mirroring `lr-heatmap.selectedCell`: the component * never mutates `selectedNodeIds`/`selectedLinkIds` itself, only emits intent; the host assigns * them back. */ selectionMode:LyraGraphSelectionMode; /** Controlled ids of selected nodes. Selection gestures emit intent without mutating this array. */ selectedNodeIds:readonly string[]; /** Controlled ids of selected links, using each link's stable effective key. */ selectedLinkIds:readonly string[]; /** Node ids to render dimmed (`data-dimmed` on the matching `[part="node"]`, themeable via * `--lr-graph-dimmed-opacity`). Controlled, mirroring `selectedNodeIds`/`selectedLinkIds`: the * component never assigns this itself, only renders it -- a host typically computes it from a * `lr-node-enter`/`lr-link-enter` hover (the complement of the hovered id's neighbor set, * computed from the host's own `links` array) and assigns the result back. Empty (the default) * renders every node at full opacity, unchanged from today. */ dimmedNodeIds:readonly string[]; /** Same contract as `dimmedNodeIds`, for links. A link's dimming key is the same `linkKey()` * value (`LyraGraphLink.id`, else `` `${source}->${target}` ``) `selectedLinkIds` already uses. */ dimmedLinkIds:readonly string[];private readonly arrowMarkerId; /** True until the lazy-loaded d3 peer dependencies have settled (success or failure). */ private loading; /** * True once the optional `d3` peer failed to load (not installed) -- `render()` fails closed * into a visible `part="error"` and announces the transition through the document-level sink. */ private loadFailed;private loadLibrary;private loadGeneration; /** The host's own content box, in whole CSS pixels, as last measured under * `fitTo === 'container'`; `null` before any usable measurement (and whenever `fitTo` leaves * `'container'`, so a later opt-in re-measures instead of reusing a stale box). Reactive * because the SVG `viewBox` renders straight from it. */ private containerSize;private simNodes;private simLinks;private danglingLinks; /** Every node's last-known settled position, keyed by id, independent of current visibility -- * consulted by `rebuildSimulation()` (after the existing carried-over-position map) so a * `hiddenTypes` toggle restores a node where it was instead of re-randomizing it. Pruned to ids * present in `this.nodes` (not just currently-visible ones) on every rebuild. */ private lastPositionById; /** The `hiddenNodeCount` computed by the most recent `rebuildSimulation()` -- lets that method * tell "nothing has ever been hidden" (never touch `graphLiveText`, so a consumer that never * sets `hiddenTypes` keeps today's exact live-region output) apart from "a hide was just * cleared" (still announce the resulting "0 of N" count). */ private lastHiddenNodeCount; /** One roving tab stop across all nodes and links; nodes are the initial entry order. */ private activeGraphItem;private graphLiveText; /** Shared document-level regions that carry announcements. The visually hidden shadow copy is * an inspection mirror only because shadow-root live regions are not consistently spoken. */ private politeAnnouncementSink?;private assertiveAnnouncementSink?; /** Becomes true only after the first successful, non-loading graph render. This suppresses both * the default first item and an initially configured hidden-node count. */ private graphAnnouncementsReady; /** Focus repair scheduled by `willUpdate()` after a structural graph change. A numeric value * targets the surviving flat graph-item index; `'base'` targets the now-empty renderer. */ private pendingGraphItemFocus; /** Gates the mount-time selection announcement in `willUpdate()` so a freshly-mounted graph * never announces its own initial (default-`[]`) selection as though it were a live change -- * mirrors `<lr-branch-picker>`'s identical `isMounting` gate. */ private isMounting; /** Host `aria-label` makes the host the one named graph owner. Remember an independently * authored role so the default `group` role can be added/removed without overwriting it. */ private authorRole;private syncingGraphHostRole;private simulation?; /** The live charge/link force objects, kept so chargeStrength/linkDistance * changes can retune them in place (see updated()) instead of requiring a * full rebuildSimulation(). */ private chargeForce?;private linkForce?;private d3?; /** The `<svg>` (or, in `renderer="canvas"` mode, the `<canvas>`) currently wired up with d3-zoom * (guards a one-time bind per element). */ private zoomedEl?; /** The pan/zoom `<g>`, cached alongside `zoomedEl` so the zoom handler can * write the transform straight to the DOM (see applyInteractions()) instead * of round-tripping through a Lit reactive property on every pan/zoom event. */ private gEl?; /** The live zoom behavior, kept so minZoom/maxZoom changes can retune its * scaleExtent in place (see applyInteractions()) instead of requiring the * `<svg>` to be rebound. */ private zoomBehavior?; /** Node `<circle>`s already wired up with d3-drag; cleared on every simulation rebuild * so DOM elements Lit reuses across a rebuild get rebound to their fresh datum. */ private boundNodeEls; /** Node/link/label DOM elements, index-aligned with simNodes/simLinks, cached * once per structural rebuild and written to directly by onTick() — this is * what lets ticks update positions without going through Lit's reactive * simNodes/simLinks properties (see rebuildSimulation()'s doc comment). */ private nodeEls;private nodeHitEls;private nodeLabelEls;private expandIndicatorEls; /** Tracks the index/time of the last Enter/Space activation, for double-Enter expand detection * (mirroring native dblclick semantics for keyboard users). */ private lastKeyActivateIndex;private lastKeyActivateTime; /** The last `focusNodeId` value `focusNode()` was auto-invoked for by `updated()`'s declarative * centering branch -- guards against re-centering on every update while `focusNodeId` stays set * (see the `focusNodeId` property doc for why it only ever centers once per value). Reset to `null` * whenever `focusNodeId` itself is cleared, so the same id can center again later. */ private lastAppliedFocusNodeId;private focusHaloEl?;private communityHullEls;private communityHullHitEls;private communityLabelEls;private canvasEl?;private canvasCtx?; /** Offscreen, same-size, same-camera-transform canvas used only for hit-testing (see * `redrawPickCanvas()`/`hitTest()`) -- never attached to the DOM or painted to the screen. */ private pickCanvas?;private pickCtx?; /** One `ResizeObserver` on the host, shared by its two consumers: `renderer="canvas"`'s * backing-store invalidation and `fitTo="container"`'s box measurement. Named for the host it * watches rather than either consumer, since it outlives a renderer switch whenever * `fitTo === 'container'` still needs it. */ private hostResizeObserver?; /** The document the live `hostResizeObserver` was created in -- lets `watchHostResize()` keep an * already-correct instance instead of replacing it (and losing its pending measurement) on * every call, while still rebuilding after an adoption into another realm. */ private hostResizeObserverDocument?;private canvasDprQuery?;private canvasDrawRafId?;private canvasDrawRafOwner?; /** Gates `scheduleCanvasDraw()` -- an off-screen (scrolled away, hidden tab panel) canvas-mode * instance would otherwise still pay the full redraw cost throughout its simulation settle and * any drag, same problem `<lr-chart>`'s identical `visible`/`IntersectionObserver` pair * addresses. Not `@state()`: unlike `loading`, this never drives `render()`'s template, only * gates the imperative canvas-raster path, so making it reactive would just schedule a wasted * Lit update on every visibility crossing. */ private visible;private intersectionObserver?; /** Set when `scheduleCanvasDraw()` was asked to draw while off-screen -- consulted by the * IntersectionObserver callback to catch up with exactly one draw once visible again, instead * of either silently dropping the request or drawing every missed frame. */ private canvasDrawPending;private pickDirty;private canvasCamera;private canvasTooltipEl?; /** Flat, index-aligned list matching `drawPickingScene()`'s own hulls-then-links-then-nodes pick * order -- rebuilt by `redrawPickCanvas()` alongside the pick canvas itself, so a pick color's * decoded index always maps back to the exact item it was drawn for. */ private pickItems;private canvasDragNode?;private canvasPointerId?;private canvasPointerDownAt?;private canvasPointerDownId?; /** The latest hover pointer position awaiting a hit test -- `pointermove` can fire far more * often than the display refreshes, and each hit test costs a bounding-rect read plus a * pick-pixel readback, so hover resolution is coalesced to at most one per animation frame. */ private pendingHover?;private canvasHover?;private hoverRafId?;private hoverRafOwner?; /** Cached world-space draw scene, reused for camera-only repaints (pan/zoom moves the camera, * not the scene) -- building it costs a `getComputedStyle()` pass plus full per-node/per-link * array rebuilds, so it's only invalidated (`markCanvasDirty()`) when data/selection/style * state or node positions actually change. */ private canvasScene?; /** Whether `canvasScene` was built with edge labels included -- the zoom gate makes the scene * camera-dependent at exactly two thresholds (`edgeLabelMinZoom`, `NODE_LABEL_MIN_ZOOM`), * so a camera-only draw that crosses either must rebuild instead of reusing the cache. */ private canvasSceneHasEdgeLabels; /** The in-flight `requestAnimationFrame` id for a camera tween (`focusNode()`/`fit()`), if any -- * canceled by a new tween request or a user pan/zoom gesture (see `applyInteractions()`'s zoom * `'start'` handler). */ private cameraTweenId?;private cameraTweenFrameOwner?; /** The current tween's own `resolve`, so cancellation (a superseding tween, or a real user * pan/zoom gesture) settles it with `false` instead of leaving the caller's `Promise` hanging * forever -- `cancelAnimationFrame()` alone stops the rAF loop but never touches the Promise. */ private cameraTweenResolve?; /** True for a camera tween's whole duration (set before its first frame, cleared on * resolution) -- `isPanning` alone doesn't cover this: `applyZoomTransform()`'s per-frame * `zoomBehavior.transform()` call on a non-transition selection fires d3-zoom's * start/zoom/end synchronously within that single call, so `isPanning` flips true-then-false * within one frame rather than staying true for the tween's real duration the way an actual * user gesture does. */ private isCameraTweening; /** Pending rAF id for the coalesced `lr-viewport-change` emission — see * `scheduleViewportChange()`. Only one is ever outstanding at a time regardless of how many * zoom/tick callbacks request one within the same frame. */ private viewportChangeRafId?;private viewportChangeRafOwner?;private linkEls;private linkHitEls;private linkLabelEls; /** Per-simLink-index flip cache for the length declutter gate -- `onTick()` only writes * `visibility` when the boolean actually changes, not every tick. */ private linkLabelHiddenByLength;private edgeLabelWidthCache; /** Dangling-stub `<line>`s, index-aligned with `danglingLinks` -- cached separately from * `linkEls` (real, simulated links only) so onTick() can write their positions too; see * onTick()'s own comment for why a stub needs this at all. */ private danglingLinkEls;private edgeLabelMeasureCanvas?;private edgeLabelMeasureCtx?;private linkPaintProbe?;private readonly linkPaintVisibilityCache;private readonly resolvedCssColorCache;constructor();attributeChangedCallback(name:string,oldValue:string|null,value:string|null):void;private hostOwnsGraphSemantics;private syncGraphHostRole;private get ownerWindow();private computedStyle;connectedCallback():void;disconnectedCallback():void;adoptedCallback():void; /** Re-target the ref-counted regions after reconnect/adoption without replaying existing text. */ private syncAnnouncementSinks;private releaseAnnouncementSinks; /** Coalesces every pan/zoom/tick-driven `lr-viewport-change` emission into at most one per * animation frame -- called from both the svg/canvas zoom handlers and `onTick()`, all of which * can fire far more often than once per frame (a wheel-zoom gesture, a settling simulation). */ private scheduleViewportChange; /** * A caller-supplied `radius` is clamped to [MIN_RADIUS, MAX_RADIUS] (and a * non-finite/NaN value falls back to the same default average as an unset * one) — an unclamped 0/negative radius would render an invisibly small * `<circle>` that's still `role="button" tabindex="0"`, an invisible, * focusable/clickable control with no visible focus indicator. */ private nodeRadius; /** Link stroke width reaches SVG paint, canvas stroke/arrowhead math, and the picking surface. * Keep those three representations synchronized on one finite, non-negative value. */ private safeLinkWidth; /** Invisible edge paint never creates a pointer/keyboard control. The edge remains in * `simLinks` and the offscreen topology summary, but is excluded from navigation and picking. */ private isInteractiveLink; /** Cleared twice, from two different points in `willUpdate()` -- neither alone is enough: * (1) unconditionally at the top, because `isInteractiveLink()` (what this filters on) reads * live computed style (`--lr-link-color`, `--lr-color-border`) and `link.color`/`link.width`, * none of which are reactive properties `changed` would ever report, so a style-only update * (a CSS custom-property edit plus `requestUpdate()`, `nodes`/`links` untouched) must still see * a fresh result; (2) again right after `rebuildSimulation()` reassigns `simLinks`, because * `willUpdate()`'s own `previousIndex` computation (right after clear (1), before * `rebuildSimulation()` runs) can itself call `navigableLinks()` and repopulate the cache from * the OLD `simLinks` -- without this second clear a structural change would render against * stale link membership. Caching within one render pass is still enough to fix the actual * cost: without any caching, `navigableLinks()`'s `simLinks.filter()` re-runs on every call, and * `graphItemCount()`/`normalizedGraphItem()` call it once per rendered item (roving-tabindex * math in both the SVG and canvas-mode offscreen cursor-item templates) -- an O(links) refilter * inside an O(nodes + links) render loop is an O((nodes + links) * links) render, confirmed by * local profiling to take 60+ seconds alone for a 5,000-node/10,000-link canvas-mode graph. */ private navigableLinksCache?;private navigableLinks; /** `width`/`height` normalized to a finite, positive viewport size — an invalid attribute value * would otherwise flow straight into `forceCenter`, the SVG `viewBox`, and the canvas backing * store's `width`/`height`, producing `NaN` geometry/transforms that silently render nothing * instead of erroring. Under `fitTo === 'container'` the measured host content box replaces * them, with the normalized numeric value still the fallback for every frame before a usable * measurement exists (see `measureHostBox()`). */ private get safeWidth();private get safeHeight(); /** Records a freshly measured host content box, returning whether it actually changed. * * Rounding to whole CSS pixels is the jitter damper: a fractional layout, a browser zoom level * or a scrollbar appearing for one frame otherwise re-renders every node and re-centers the * layout for a difference nobody can see. No extra `requestAnimationFrame` coalescing is * layered on top -- `ResizeObserver` already delivers at most once per frame, so a second * frame of delay would only add latency plus another cancellation path to leak. A zero or * negative box (detached, `display: none`) is rejected rather than collapsing the drawing space * to nothing; `safeWidth`/`safeHeight`'s numeric fallback keeps applying. */ private recordContainerSize; /** Measures the host synchronously, so the very first painted frame already uses the real box * instead of the 800x600 numeric fallback -- waiting for the observer's first callback is * exactly the "hard-coded fallback size for the frames before the first measurement" a consumer * otherwise hand-rolls. `clientWidth`/`clientHeight` are the content box (already integral, and * scrollbars excluded), matching what `ResizeObserver`'s `contentRect` reports afterwards for * this component's own unpadded, unbordered host; a consumer who adds padding to the host gets * that first frame padding-inclusive and the observer's own content-box reading from then on. */ private measureHostBox; /** Writes the private `--_lr-graph-requested-height` fallback that `:host`'s block-size resolves * through (see graph.styles.ts), from the normalized numeric `height`. Kept private and beneath * the author-facing `--lr-canvas-reserved-height` so an ancestor's reservation always still * wins. Always finite (the fallback is 600), so there is no unset branch to handle. * Deliberately normalizes `height` directly rather than reading `safeHeight`: under * `fitTo === 'container'` safeHeight IS the measured box, and writing that back into the * property that sizes the host would close a measure -> resize -> measure loop. */ private syncRequestedHeightVar; /** `minZoom`/`maxZoom` normalized to finite, positive scale bounds before ever reaching * d3-zoom's `scaleExtent()` or a camera-clamp `Math.min`/`Math.max` — a non-finite bound would * otherwise poison every subsequent zoom/pan computation with `NaN`. */ private get safeMinZoom();private get safeMaxZoom(); /** Stable ascending zoom domain used by d3 and every imperative camera clamp. */ private get effectiveZoomBounds(); /** `edgeLabelMinZoom` is compared directly against the live camera scale (same domain as * `minZoom`/`maxZoom`), so it's normalized with the same bounds -- a non-finite value would * otherwise make every `>=`/`<` comparison against it silently `false`/`true` forever. */ private get safeEdgeLabelMinZoom(); /** An explicit `nodeLabels` always wins; unset preserves each renderer's own pre-existing * default instead of picking a single shared literal default that would change one of them -- * see `nodeLabels`'s own doc. */ private get resolvedNodeLabelsMode(); /** `renderer="canvas"`'s own node-label visibility, folding `resolvedNodeLabelsMode` in with the * live camera scale -- `'zoom'`'s threshold check only applies at this one call site, so it's * centralized here rather than repeated at both `buildCanvasScene()`'s and `drawCanvas()`'s own * call sites. */ private canvasNodeLabelsVisible; /** `seed`, normalized to a finite integer when set -- `undefined` (unseeded/random) is left * untouched, since it's a meaningful third state, not a missing number. Without this, * `hashNodeSeed`/`mulberry32`'s `>>> 0` coercion would silently fold `NaN`/`Infinity` to `0` * instead of normalizing an out-of-range attribute value the way every other numeric prop here * does. */ private get safeSeed(); /** `chargeStrength` is a signed d3-force strength (negative = repulsion, positive = attraction) * — only guarded for finiteness, not clamped to a range, since either sign is a legitimate * value. */ private get safeChargeStrength(); /** `linkDistance` normalized to a finite, non-negative pixel distance — feeds `forceLink()`'s * `distance()`, the layered layout's `gapY`, and the neighbor-jitter spawn radius, none of * which have a sane meaning for a negative/non-finite value. */ private get safeLinkDistance();private resolveNodeType;private nodeShape; /** `nodeTypes` filtered to a non-blank `label`, in the same order `lr-graph-legend` renders its * rows in (its `render()` applies an identical filter before assigning a row its palette * index). Keeping this filter in sync here is what keeps a node's categorical fallback color * matching the swatch color a paired legend shows for the same type. */ private labeledNodeTypes; /** Resolution precedence: `node.color` (existing, most specific) > matched `LyraNodeTypeStyle.color` * > the ordered categorical fallback palette by the type's index among label-bearing `nodeTypes` * entries, matching `lr-graph-legend`'s own row order > (returns `undefined`, letting the * untyped `--lr-node-fill` token default apply). Both data-driven color sources pass the * existing `sanitizeNodeColor()`. A blank-label type itself (never shown as its own legend row) * falls back to its raw `nodeTypes` position, since there is no legend row to stay in sync with. */ private nodeFill; /** `this.nodes` filtered down to the ids `hiddenTypes` doesn't hide -- an untyped node (`type == * null`) is never hidden, regardless of `hiddenTypes`' contents. */ private visibleNodes; /** Resolves `this.links` against an already-built `byId` node map: a link whose source isn't in * `byId` is dropped (hidden source, or a genuinely missing one); a link whose target isn't in * `byId` either stubs as a dangling link (target id doesn't exist in `this.nodes` at all) or is * dropped (target exists but is hidden by `hiddenTypes`). Shared by both the force and layered * layout paths in `rebuildSimulation()`. */ private resolveLinksAgainst; /** A community's currently-visible members -- the union of `memberIds` and every currently * simulated node (already filtered by `hiddenTypes`) whose `communityId` matches. */ private communityMembers; /** Memoized `visibleCommunities()` result -- `undefined` means "stale, recompute on next call". * Cleared from `willUpdate()` whenever `simNodes`/`communities` actually change, the same * structural-change gate `applyInteractions()` re-caches its own DOM lookups on, so every other * call site (roving-ring math, `render()`'s template, keyboard navigation) shares one * `O(communities × simNodes)` computation per structural update instead of repeating it. */ private visibleCommunitiesCache?; /** `communities` narrowed to entries with at least one currently-visible member -- a community * whose members are all hidden by `hiddenTypes` (or that starts out empty) draws no hull and * doesn't occupy a roving-ring slot. */ private visibleCommunities;private communityHull;private onCommunityClick;private cameraTransitionMs;private cancelCameraTween; /** Writing a transform on a plain (non-transition) selection makes d3-zoom fire its own * start/zoom/end sequence synchronously, in this same call -- `isApplyingZoomTransform` lets * the zoom `'start'` handler tell that self-triggered echo apart from a genuine external * gesture, so a camera tween's own per-frame write doesn't cancel itself. */ private isApplyingZoomTransform;private applyZoomTransform; /** Animates from the zoom behavior's current transform toward `computeTarget()`'s result via a * rAF tween that calls `zoomBehavior.transform()` every frame -- keeps d3-zoom's own internal * state consistent (so the next user pan doesn't jump), unlike writing the `<g>` transform * attribute directly. `computeTarget` is re-invoked on every single frame (not read once * up-front) so the tween keeps tracking a still-settling force simulation's live node positions * instead of tweening toward a stale snapshot from the moment the call was made -- * `focusNode()`/`fit()` are just as likely to run while the graph is still animating its initial * layout as after it's settled. `prefers-reduced-motion` jumps straight to one write of the * then-current target. A concurrent call cancels the previous tween -- resolves `true` on * genuine arrival, `false` if superseded or interrupted by a user gesture before completing. */ private tweenCamera; /** Animates the camera so `id` centers in the viewport (the `width` x `height` viewBox), at * `options.zoom` (clamped to `[minZoom, maxZoom]`) or the current scale when omitted. Resolves * `true` on arrival; `false` for an id with no matching entry in `simNodes` -- there's nothing * to center on. Announces `graphNodeFocused` through the shared light-DOM sink. Does not move DOM * focus -- this is a camera operation, not a roving-focus one. */ focusNode(id:string,options?:{zoom?:number;}):Promise<boolean>; /** Animates the camera to frame the bounding box of every currently visible node position (plus * each node's own radius) at the largest scale that fits within `width` x `height` minus * `padding` viewport-px on each side (clamped to `[minZoom, maxZoom]`). Silent -- no data * changed, so no announcement. A no-op with no visible nodes. */ fit(options?:{padding?:number;}):void;private updateFocusHalo;private setUpCanvasSurface;private ensureCanvasOwnerRealm; /** Arms the host observer when either consumer needs it, and disconnects it when neither does -- * the single place that decides, so `fitTo="container"` keeps its measurement across a renderer * switch that used to unconditionally tear the observer down. */ private syncHostResizeObserver; /** The raw arm. Only `syncHostResizeObserver()` may call this -- every other caller must go * through that gate so an already-correct instance is kept instead of rebuilt. */ private watchHostResize;private watchCanvasDpr;private onCanvasDprChange;private markCanvasDirty; /** The camera-only sibling of `markCanvasDirty()`: a pan/zoom moves the camera but leaves every * world-space scene value (positions, colors, labels) untouched, so the cached `canvasScene` * stays valid and only needs redrawing under the new transform. The pick canvas bakes the * camera transform into its pixels, though, so it still needs a redraw before the next hit * test. */ private markCanvasCameraDirty;private scheduleCanvasDraw; /** Resolves every accepted CSS color through the live cascade before it reaches Canvas 2D. * Canvas does not consistently accept CSS-wide keywords, custom properties, or newer color * functions even when the style engine does. A shadow child inherits the same host tokens and * `color` as the rendered graph, so its computed `color` is the concrete, canvas-safe value. */ private resolveCssColorWithProbe;private createCanvasColorProbe;private withCanvasColorResolver;private buildCanvasScene; /** Sizes the backing store to the canvas's own rendered CSS box (`clientWidth`/`clientHeight`, * themselves stretched to fill the host via `[part="base"]`/`[part="canvas"]`'s `100%` sizing) * times `devicePixelRatio`, only touching `width`/`height` when the target actually changed -- * reassigning either unconditionally would implicitly clear the canvas and reset its transform * on every single draw, even a pure pan/zoom repaint. Mirrors `<lr-heatmap>`'s own DPR-scaled * backing-store convention (`watchDpr()`/`onDprChange()`), adapted to `setTransform()` (an * absolute reset) rather than a relative `scale()`, since this canvas -- unlike heatmap's, which * always resizes its backing store on every draw -- only resizes conditionally. */ private drawCanvas;private rebuildPickItems;private redrawPickCanvas;private hitTest;private bindCanvasZoom;private bindCanvasPointer;private onCanvasPointerDown;private onCanvasPointerMove; /** `alpha` decays toward `alphaMin` and d3-force stops its internal timer once it crosses below * it, so `alpha > alphaMin` mirrors the simulation's own running condition -- including a drag's * `alphaTarget(0.3)` reheat, and correctly excluding a seeded/reduced-motion graph whose settle * loop already converged synchronously. */ private simulationIsTicking;private onCanvasPointerUp; /** Releases the force pin and capture belonging to one active canvas node drag. Pointer state is * cleared before `releasePointerCapture()` because that call may synchronously dispatch * `lostpointercapture`; the resulting handler then observes an already-finished gesture. */ private finishCanvasNodeDrag;private takeCanvasPointerDown;private onCanvasPointerCancel;private onCanvasLostPointerCapture;private onCanvasPointerLeave;private canvasHoverSuppressed;private updateCanvasHover;private onCanvasDblClick; /** Geometric fallback for dblclick: browsers can deliver the event before the offscreen pick * canvas has painted the latest frame, while the simulation coordinates are already current. */ private nodeAtCanvasPoint;private updateCanvasTooltip;private isSelected;private isDimmed;private linkKey; /** Computes and emits the selection intent for activating `id`; never assigns * `selectedNodeIds`/`selectedLinkIds` itself -- see the class doc's controlled-selection note. */ private emitSelectionIntent;private clearSelection;protected willUpdate(changed:PropertyValues):void;protected updated(changed:PropertyValues):void; /** Suppresses hover events/`data-hovered` while a node drag is in progress (tracked from the * existing d3-drag `.on('start')`/`.on('end')` handlers in `applyInteractions()`) — a drag * crossing over other nodes/links would otherwise spam enter/leave pairs unrelated to genuine * pointer hovering. */ private isDragging; /** Same purpose as `isDragging`, for d3-zoom pan/zoom gestures (tracked from `applyInteractions()`'s * zoom `.on('start')`/`.on('end')` handlers, added by this same change). */ private isPanning; /** * Imperatively wires up d3-zoom (pan/zoom on the `<svg>`) and d3-drag * (per-node drag) against the just-rendered DOM. The zoom bind itself is a * one-time guard (`zoomedEl`) — but the bound `zoomBehavior`'s * `scaleExtent` is re-read from `minZoom`/`maxZoom` on every call so a * post-mount change to either still takes effect. The node-drag bind + * node/link/label element caching for `onTick()` only run when * `changed` indicates a fresh structural render just happened, not on * every call — otherwise this would re-scan the DOM via * `querySelectorAll` on every Lit update, which used to include every * single simulation tick. `<circle>`s already bound are skipped * (`boundNodeEls`) — a WeakSet reset on every `rebuildSimulation()` so DOM * nodes Lit reuses across a rebuild get rebound against their new datum * instead of a stale one. The zoom handler writes the resulting transform * straight to the cached `gEl` (bound once alongside `zoomedEl`, since the * outer `<g>` is a static part of the template and never recreated by Lit) * instead of assigning a Lit reactive property — panning/zooming fires * continuously while dragging, and reassigning a `@state()` there would * force a full re-render (recomputing every node/link template) on every * single event, the same class of cost `onTick()` already avoids for ticks. */ private applyInteractions; /** * Keeps SVG pointer strokes at their tokenized screen-space size. WebKit computes * `vector-effect: non-scaling-stroke` correctly but still hit-tests the pre-vector-effect, * transformed width, so explicit inverse zoom is required for the interactive geometry. */ private updateHitAreaZoomScale; /** The `renderer="canvas"` twin of `applyInteractions()`'s svg zoom-bind branch -- binds d3-zoom * and the pointer/hit-testing handlers to the just-rendered `<canvas>` once (guarded by the same * `zoomedEl` field `applyInteractions()` uses, so `focusNode()`/`fit()`/`tweenCamera()` keep * working unmodified against whichever element -- svg or canvas -- is currently bound), then * marks the canvas dirty on every call so any structural/style-affecting change (new nodes/ * links, a selection change, a hiddenTypes toggle, ...) schedules a fresh draw the same way a * Lit re-render already does for svg mode. */ private applyCanvasInteractions; /** * Runs on every d3-force simulation tick (up to ~300 while a graph settles * on load, continuously while a node is being dragged via * `alphaTarget(0.3)`). Writes positions straight to the already-rendered * DOM via `setAttribute()` instead of reassigning the reactive * `simNodes`/`simLinks` properties, which would force a full Lit re-render * (and, before the structural-render gate in `applyInteractions()`, an * unconditional `querySelectorAll` scan) on every single frame. Writing attributes * directly (rather than wrapping each element in a d3 selection just to * call `.attr()`) avoids allocating a throwaway Selection per element on * this component's highest-frequency code path. */ private onTick;private rebuildSimulation; /** Announces the current hidden-node count via the live region -- shared by both the force and * layered rebuild paths (each calls this with its own final visible-node count) so `hiddenTypes` * filtering announces identically regardless of `layout`. Called from right here (not from * willUpdate()/updated() gated on a 'hiddenTypes'/'nodes' PropertyValues diff) because * rebuildSimulation() itself is also invoked directly from connectedCallback() once the lazy d3 * peer deps resolve -- a call that never goes through Lit's changed-property diffing at all. * Computing it there instead would miss that path entirely: a graph mounted with hiddenTypes * already set would compute this from a still-empty simNodes (0 settled nodes yet) on the * property-driven pass, then never get a chance to correct it once the real simNodes became * available. Only ever touches graphLiveText when there's something to say -- a node is * currently hidden, or one just stopped being hidden -- so a consumer that never sets * hiddenTypes keeps today's exact live-region output. */ private announceHiddenNodeCount; /** The `layout="layered"` path: computes final positions synchronously via the shared * `layeredLayout()` util (2r x 2r boxes, `gapY = linkDistance`, `gapX = 12`), centers the * drawing in `width` x `height`, and skips forceSimulation() entirely -- no `this.simulation`, * no ticking, no `prevById` carry-over (deterministic input -> output makes it unnecessary; a * structural change simply recomputes wholesale). `lr-graph` never passes `fixedPositions`. */ private rebuildLayeredLayout;private onNodeClick; /** Returns a node's current position in the graph's local drawing space. */ getNodePosition(id:string):{x:number;y:number;}|undefined;private onLinkClick;private onNodeEnter;private onNodeLeave;private onNodeDblClick;private onLinkEnter;private linkHoverDetail;private onLinkLeave;private nodeAccessibleText; /** One bounded tooltip/content-summary model shared by SVG, canvas and live announcements. */ private boundedGraphText;private nodeTooltipText;private linkAccessibleText;private linkTooltipText;private linkCoordinates; /** World-space midpoint of a link, offset EDGE_LABEL_OFFSET_PX perpendicular to the segment * (horizontal, unrotated text — rotated edge-label text is a readability and RTL hazard). */ private edgeLabelPosition; /** The edge-label font size in used pixels: `--lr-font-size-2xs` resolved against the live root * (rem) or own (em) font size through the shared `resolveCssLength()`, so canvas text matches * what the same token paints in CSS on a page that isn't at the default 16px root size. A token * in a unit that has no used pixel length here (`ch`, `pt`, `calc()`) falls back to * DEFAULT_EDGE_LABEL_FONT_PX rather than being measured as raw pixels. */ private edgeLabelFontPx;private edgeLabelContext;private edgeLabelWidth; /** Toggles `data-edge-labels-hidden` on the cached zoomed `<g>` when crossing `edgeLabelMinZoom` * -- called from the d3-zoom `'zoom'` handler (render-free, CSS hides `[part="link-label"]` * beneath the attribute) so this scales with every pan/zoom event without a Lit re-render. */ private updateEdgeLabelZoomGate; /** The `renderer="svg"` sibling of `updateEdgeLabelZoomGate()`: toggles * `data-node-labels-hidden` on the cached zoomed `<g>` when `resolvedNodeLabelsMode === 'zoom'` * crosses `NODE_LABEL_MIN_ZOOM`, and always clears it otherwise (`'always'` never hides, * `'none'` never renders `[part="label"]` in the first place, so the attribute is moot either * way) -- render-free, CSS hides `[part="label"]` beneath the attribute, so this scales with * every pan/zoom event without a Lit re-render. */ private updateNodeLabelZoomGate; /** The roving-tabindex/keyboard-cursor index space concatenates three item kinds in one order: * nodes, then links, then community hulls. These two helpers are the single definition of where * each later segment starts. Every consumer -- the total count, the identity/text * decompositions, the reverse lookup, and both render branches' offscreen cursor items -- * derives from them, so adding a fourth kind or reordering the three is one edit instead of six * that can silently fall out of step and point keyboard focus at the wrong item. */ private linkIndexBase;private communityIndexBase;private graphItemCount;private normalizedGraphItem;private graphItemIdentity;private graphItemIndex;private communityText;private graphItemText;private graphItemAnnouncement;private onGraphItemFocus;private focusGraphItem;private focusGraphItemElement; /** * The forward physical arrow key (`ArrowRight` in LTR, `ArrowLeft` under * `dir="rtl"` — see `isRtl()`) moves to the next roving-tabindex item, the * backward one to the previous, in flat array order (`simNodes` then * `simLinks`) — the same `forwardKey`/`backwardKey` swap this library's * other "physical arrow key drives sequential previous/next" components * (`<lr-tab-group>`, `<lr-slider>`, `<lr-segmented>`) apply under RTL. * `ArrowDown`/`ArrowUp` always mean next/previous regardless of direction. */ private onGraphKeyDown;render():TemplateResult;}declare global{interface HTMLElementTagNameMap{'lr-graph':LyraGraph;}}