@yggy-tech/graphraum 0.0.0-stage → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,854 @@
1
+ import Graph from "graphology";
2
+ //#region src/types.d.ts
3
+ type GraphraumColor = number | string;
4
+ type GraphraumMode = "2d" | "3d";
5
+ type GraphraumNodeState = "dimmed" | "focused" | "hovered" | "selected";
6
+ /** Application-owned edge state applied with `setEdgeState`. Edge selection uses `setEdgeSelection`. */
7
+ type GraphraumEdgeState = "dimmed";
8
+ type GraphraumNodeShape = "circle" | "diamond" | "hexagon" | "pill" | "rounded" | "square" | "triangle";
9
+ interface GraphraumPosition {
10
+ x: number;
11
+ y: number;
12
+ z?: number;
13
+ }
14
+ type GraphraumAttributes<Attributes> = [Attributes] extends [undefined] ? {
15
+ attributes?: never;
16
+ } : {
17
+ attributes: Attributes;
18
+ };
19
+ interface GraphraumNodeGeometry {
20
+ id: string;
21
+ position: GraphraumPosition;
22
+ color?: GraphraumColor;
23
+ /**
24
+ * Opt-in soft halo in 0..1 (default 0, none). Glowing nodes add one batched draw call;
25
+ * see the visual-language docs for the radius and opacity mapping.
26
+ */
27
+ glow?: number;
28
+ height?: number;
29
+ shape?: GraphraumNodeShape;
30
+ size?: number;
31
+ strokeColor?: GraphraumColor;
32
+ strokeWidth?: number;
33
+ width?: number;
34
+ }
35
+ type GraphraumNode<NodeAttributes = undefined> = GraphraumNodeGeometry & GraphraumAttributes<NodeAttributes>;
36
+ interface GraphraumNodeUpdate {
37
+ color?: GraphraumColor | undefined;
38
+ glow?: number | undefined;
39
+ height?: number | undefined;
40
+ id: string;
41
+ position?: GraphraumPosition;
42
+ shape?: GraphraumNodeShape | undefined;
43
+ size?: number | undefined;
44
+ strokeColor?: GraphraumColor | undefined;
45
+ strokeWidth?: number | undefined;
46
+ width?: number | undefined;
47
+ }
48
+ /** A transferable XYZ position batch in the same order as `nodeIds`. */
49
+ interface GraphraumLayoutPositions {
50
+ nodeIds: readonly string[];
51
+ positions: Float32Array;
52
+ }
53
+ type GraphraumEdgeStyle = "solid" | "dashed" | "dotted";
54
+ type GraphraumEdgeMarker = "none" | "triangle";
55
+ type GraphraumEdgeMarkerEnd = "target" | "source" | "both";
56
+ type GraphraumEdgePath = "straight" | "quadratic" | "cubic";
57
+ type GraphraumEdge<EdgeAttributes = undefined> = {
58
+ color?: GraphraumColor;
59
+ width?: number;
60
+ opacity?: number;
61
+ style?: GraphraumEdgeStyle;
62
+ marker?: GraphraumEdgeMarker;
63
+ markerSize?: number;
64
+ markerEnd?: GraphraumEdgeMarkerEnd;
65
+ path?: GraphraumEdgePath;
66
+ controlPoints?: readonly GraphraumPosition[];
67
+ id: string;
68
+ source: string;
69
+ target: string;
70
+ } & GraphraumAttributes<EdgeAttributes>;
71
+ interface GraphraumData<NodeAttributes = undefined, EdgeAttributes = undefined> {
72
+ nodes: readonly GraphraumNode<NodeAttributes>[];
73
+ edges: readonly GraphraumEdge<EdgeAttributes>[];
74
+ }
75
+ interface GraphraumDataPatch<NodeAttributes = undefined, EdgeAttributes = undefined> {
76
+ addedNodes?: readonly GraphraumNode<NodeAttributes>[];
77
+ addedEdges?: readonly GraphraumEdge<EdgeAttributes>[];
78
+ removedNodeIds?: readonly string[];
79
+ removedEdgeIds?: readonly string[];
80
+ }
81
+ interface GraphraumNodeVisual {
82
+ color?: GraphraumColor;
83
+ /**
84
+ * Opt-in soft halo in 0..1 (default 0, none). Glowing nodes add one batched draw call;
85
+ * see the visual-language docs for the radius and opacity mapping.
86
+ */
87
+ glow?: number;
88
+ height?: number;
89
+ shape?: GraphraumNodeShape;
90
+ size?: number;
91
+ strokeColor?: GraphraumColor;
92
+ strokeWidth?: number;
93
+ width?: number;
94
+ }
95
+ interface GraphraumEdgeVisual {
96
+ color?: GraphraumColor;
97
+ width?: number;
98
+ opacity?: number;
99
+ style?: GraphraumEdgeStyle;
100
+ marker?: GraphraumEdgeMarker;
101
+ markerSize?: number;
102
+ markerEnd?: GraphraumEdgeMarkerEnd;
103
+ path?: GraphraumEdgePath;
104
+ controlPoints?: readonly GraphraumPosition[];
105
+ }
106
+ type GraphraumPropertyValue = boolean | null | number | string;
107
+ interface GraphraumPresentationProperty {
108
+ id: string;
109
+ label: string;
110
+ value: GraphraumPropertyValue;
111
+ }
112
+ interface GraphraumPresentationAction {
113
+ disabled?: boolean;
114
+ id: string;
115
+ label: string;
116
+ }
117
+ interface GraphraumPresentation {
118
+ actions?: readonly GraphraumPresentationAction[];
119
+ properties?: readonly GraphraumPresentationProperty[];
120
+ subtitle?: string;
121
+ title: string;
122
+ }
123
+ interface CompiledGraphraumPresentation extends GraphraumPresentation {
124
+ actions: readonly Readonly<GraphraumPresentationAction>[];
125
+ properties: readonly Readonly<GraphraumPresentationProperty>[];
126
+ }
127
+ interface GraphraumScreenPosition {
128
+ visible: boolean;
129
+ x: number;
130
+ y: number;
131
+ }
132
+ interface GraphraumOverlayNode {
133
+ id: string;
134
+ presentation: CompiledGraphraumPresentation | null;
135
+ }
136
+ interface GraphraumLabelCandidate {
137
+ id: string;
138
+ importance: number;
139
+ visible: boolean;
140
+ }
141
+ interface GraphraumOverlayOptions {
142
+ /**
143
+ * Label refresh policy. `manual` only shows ids from `setLabels()`. `auto` budgets by
144
+ * importance on view change. `focus` budgets selected ∪ hovered ∪ 1-hop neighbors first.
145
+ * When omitted, `autoLabels: true` maps to `auto` for compatibility; otherwise `manual`.
146
+ */
147
+ labelPolicy?: "manual" | "auto" | "focus";
148
+ /**
149
+ * When `labelPolicy` is `focus`, controls whether leftover budget is filled by importance.
150
+ * Default `importance`. Use `none` for selected/hover/neighbor labels only.
151
+ */
152
+ focusLabelFill?: "importance" | "none";
153
+ /**
154
+ * @deprecated Prefer `labelPolicy: "auto"`. When `labelPolicy` is omitted, `true` enables `auto`.
155
+ */
156
+ autoLabels?: boolean;
157
+ /**
158
+ * When set with `renderToolbar`, keeps the toolbar pinned to the selected or hovered node.
159
+ * Default `false` — host calls `setToolbar` manually.
160
+ */
161
+ autoToolbar?: false | "selected" | "hovered";
162
+ overlayClassName?: string;
163
+ maxLabels?: number;
164
+ /**
165
+ * Hard cap for host HTML focus cards. Default 24. Manual `setRichNodes` throws when exceeded;
166
+ * `autoRichNodes` slices to the cap.
167
+ */
168
+ maxRichNodes?: number;
169
+ labelClassName?: string;
170
+ toolbarClassName?: string;
171
+ richNodeClassName?: string;
172
+ /**
173
+ * When set with `renderRichNode`, keeps focus cards on selected and/or hovered nodes.
174
+ * Default `false` — host calls `setRichNodes`.
175
+ */
176
+ autoRichNodes?: false | "selected" | "hovered" | "focus";
177
+ renderLabel?: (node: GraphraumOverlayNode) => HTMLElement | null;
178
+ renderToolbar?: (node: GraphraumOverlayNode) => HTMLElement | null;
179
+ /** Host HTML for a strictly bounded focused subset. Not the dense node path. */
180
+ renderRichNode?: (node: GraphraumOverlayNode) => HTMLElement | null;
181
+ }
182
+ interface GraphraumNodeEncoding {
183
+ presentation?: GraphraumPresentation;
184
+ visual?: GraphraumNodeVisual;
185
+ }
186
+ interface GraphraumEdgeEncoding {
187
+ presentation?: GraphraumPresentation;
188
+ visual?: GraphraumEdgeVisual;
189
+ }
190
+ interface GraphraumVisualMapper<NodeAttributes = undefined, EdgeAttributes = undefined> {
191
+ edge?: (edge: Readonly<GraphraumEdge<EdgeAttributes>>) => GraphraumEdgeEncoding | undefined;
192
+ node?: (node: Readonly<GraphraumNode<NodeAttributes>>) => GraphraumNodeEncoding | undefined;
193
+ }
194
+ /**
195
+ * Canvas backdrop.
196
+ * - CSS color string paints a solid WebGL clear
197
+ * - `null` / `"transparent"` leaves the canvas clear so a host CSS color, gradient, or pattern shows through
198
+ * - `{ type: "pattern", source }` uses an image/canvas/bitmap as `scene.background`
199
+ */
200
+ type GraphraumPatternBackground = {
201
+ type: "pattern";
202
+ source: CanvasImageSource;
203
+ /** Horizontal and vertical tile count. Default `[1, 1]` stretches to fill. */
204
+ repeat?: readonly [number, number];
205
+ };
206
+ type GraphraumBackground = GraphraumColor | GraphraumPatternBackground | "transparent" | null;
207
+ type GraphraumThemeName = "dark" | "light";
208
+ interface GraphraumTheme {
209
+ background: GraphraumBackground;
210
+ /** Color of edges dimmed with `setEdgeState("dimmed", ids)`. Selected edges stay selected. */
211
+ dimmedEdge: GraphraumColor;
212
+ /** Opacity cap (0..1) for dimmed edges; an edge that is already fainter keeps its own opacity. */
213
+ dimmedEdgeOpacity: number;
214
+ dimmedNode: GraphraumColor;
215
+ edge: GraphraumColor;
216
+ edgeOpacity: number;
217
+ edgeWidth: number;
218
+ /** How edge ends meet nodes. Default `boundary`. */
219
+ endpointAttach: "boundary" | "center";
220
+ focusedNode: GraphraumColor;
221
+ hoveredNode: GraphraumColor;
222
+ node: GraphraumColor;
223
+ nodeStroke: GraphraumColor;
224
+ selectedEdge: GraphraumColor;
225
+ selectedNode: GraphraumColor;
226
+ }
227
+ /** Discriminated pick result. Nodes win over edges when both intersect the pointer. */
228
+ type GraphraumPickHit = {
229
+ kind: "edge";
230
+ id: string;
231
+ } | {
232
+ kind: "node";
233
+ id: string;
234
+ };
235
+ interface GraphraumOptions<NodeAttributes = undefined, EdgeAttributes = undefined> {
236
+ antialias?: boolean;
237
+ maxVisibleEdges?: number;
238
+ maxVisibleNodes?: number;
239
+ maxPixelRatio?: number;
240
+ mode?: GraphraumMode;
241
+ /** Named preset (`"dark"` / `"light"`) or a partial override merged onto dark. */
242
+ theme?: Partial<GraphraumTheme> | GraphraumThemeName;
243
+ viewportCulling?: boolean;
244
+ viewportOverscan?: number;
245
+ visuals?: GraphraumVisualMapper<NodeAttributes, EdgeAttributes>;
246
+ }
247
+ /** Options for `Graphraum.setAutoOrbit`. */
248
+ interface GraphraumAutoOrbitOptions {
249
+ /** Orbit speed in radians per second. Must be above 0 and at most 2. Defaults to 0.07 (about 90 seconds per turn). */
250
+ speed?: number;
251
+ /** Idle time in milliseconds after a pointer, wheel or key interaction before the orbit resumes. Defaults to 3000. */
252
+ resumeAfterMs?: number;
253
+ }
254
+ /** Options for `Graphraum.flyTo`. */
255
+ interface GraphraumFlyToOptions {
256
+ /** Nodes to frame, as `fitView({ nodeIds })` frames them. Unknown IDs are ignored. */
257
+ nodeIds: readonly string[];
258
+ /** Flight time in milliseconds. 0 jumps. Defaults to 1200. */
259
+ durationMs?: number;
260
+ /** Multiplies the fitted 3D camera distance or 2D visible height; above 1 leaves air around the nodes. Defaults to 1. */
261
+ distanceScale?: number;
262
+ }
263
+ /** `off`: not enabled. `active`: orbiting with a running frame loop. `paused`: enabled but stopped by interaction, a hidden tab or an off-screen canvas. */
264
+ type GraphraumAutoOrbitStatus = "active" | "off" | "paused";
265
+ interface GraphraumDiagnostics {
266
+ aggregatedNodeClusters: number;
267
+ autoOrbit: GraphraumAutoOrbitStatus;
268
+ cpuFrameMilliseconds: number;
269
+ gpuFrameMilliseconds: number | null;
270
+ gpuDrawCalls: number;
271
+ gpuGeometries: number;
272
+ gpuTextures: number;
273
+ lodLevel: "density" | "detail" | "overview";
274
+ pickingStrategy: "raycaster-3d" | "spatial-grid-2d";
275
+ dimmedEdges: number;
276
+ selectedEdges: number;
277
+ selectedNodes: number;
278
+ totalEdges: number;
279
+ totalNodes: number;
280
+ visibleEdgeCandidates: number;
281
+ visibleEdgeMarkers: number;
282
+ visibleEdgeSegments: number;
283
+ visibleEdges: number;
284
+ /** Glow halo instances in the final frame (at most 256; 0 in the density LOD tier). */
285
+ visibleGlowNodes: number;
286
+ visibleNodes: number;
287
+ visibleNodeCandidates: number;
288
+ }
289
+ //#endregion
290
+ //#region src/edge-endpoint-attach.d.ts
291
+ type EndpointAttach = "boundary" | "center";
292
+ declare const DEFAULT_ENDPOINT_ATTACH: EndpointAttach;
293
+ declare const DEFAULT_ENDPOINT_CLEARANCE = 0.75;
294
+ interface EndpointOutline {
295
+ height?: number;
296
+ shape?: GraphraumNodeShape;
297
+ size?: number;
298
+ strokeWidth?: number;
299
+ width?: number;
300
+ }
301
+ interface TrimEdgeEndpointsInput {
302
+ attach?: EndpointAttach;
303
+ clearance?: number;
304
+ /**
305
+ * `3d` trims along the 3D segment so ends meet camera-facing outlines; exact for circles,
306
+ * approximate for other shapes. Default `2d`.
307
+ */
308
+ mode?: GraphraumMode;
309
+ source: GraphraumPosition;
310
+ sourceOutline: EndpointOutline;
311
+ target: GraphraumPosition;
312
+ targetOutline: EndpointOutline;
313
+ }
314
+ /**
315
+ * Walk from the node center toward the far point and stop on the outline,
316
+ * then pull back by `clearance` so the stroke is not stabbed.
317
+ *
318
+ * In `2d` the outline lies in the xy plane, so only the xy part of the chord counts. In `3d` the
319
+ * billboard faces the camera, so the edge stops one outline radius from the center along the 3D
320
+ * segment. The radius is measured along the chord's world xy heading (or +x when the chord is
321
+ * parallel to z), not along its on-screen heading. For circles the radius is the same in every
322
+ * direction, so the projected end lies on the outline or inside it (where the node covers the
323
+ * edge) from every view. Other shapes are approximate in 3D: an elongated or angular node can end
324
+ * inside its outline or short of it, depending on the camera.
325
+ */
326
+ declare function attachPointOnOutline(center: GraphraumPosition, far: GraphraumPosition, outline: EndpointOutline, clearance?: number, mode?: GraphraumMode): GraphraumPosition;
327
+ /** Trim both ends of a center-to-center chord (or curve endpoints) to node boundaries. */
328
+ declare function trimEdgeEndpoints(input: TrimEdgeEndpointsInput): {
329
+ source: GraphraumPosition;
330
+ target: GraphraumPosition;
331
+ };
332
+ //#endregion
333
+ //#region src/edge-styles.d.ts
334
+ declare const graphraumEdgePaths: readonly ["straight", "quadratic", "cubic"];
335
+ //#endregion
336
+ //#region src/force-layout.d.ts
337
+ /** Input for graphraum's dimension-aware force layout helpers. */
338
+ interface ForceLayoutRequest {
339
+ dimensions: 2 | 3;
340
+ edges: Uint32Array;
341
+ /** When omitted, `forceIterationCount(nodeCount)` is used. */
342
+ iterations?: number;
343
+ /**
344
+ * Optional rest length per edge (`edges.length / 2` values).
345
+ * Omitted → `settings.linkDistance` for every edge.
346
+ */
347
+ linkDistances?: Float32Array;
348
+ nodeCount: number;
349
+ /**
350
+ * Optional cluster index per node (`nodeCount` values). `createForceSimulation()` seeds nodes
351
+ * around cluster centres and pulls each node towards its cluster centroid
352
+ * (`settings.clusterStrength`). A copy is stored; the array is never mutated.
353
+ */
354
+ clusters?: Uint32Array;
355
+ settings?: ForceSettings;
356
+ /**
357
+ * Optional spring strength per edge (`edges.length / 2` values).
358
+ * Omitted → `settings.springStrength` for every edge.
359
+ */
360
+ springStrengths?: Float32Array;
361
+ }
362
+ interface ForceSettings {
363
+ centerAttraction: number;
364
+ /**
365
+ * Pull of every node towards its cluster centroid, scaled by the step alpha.
366
+ * `0` disables the pull. Ignored without `clusters`.
367
+ * Omitted → `DEFAULT_FORCE_SETTINGS.clusterStrength`.
368
+ */
369
+ clusterStrength?: number;
370
+ damping: number;
371
+ linkDistance: number;
372
+ repulsion: number;
373
+ springStrength: number;
374
+ }
375
+ declare const DEFAULT_FORCE_SETTINGS: Required<ForceSettings>;
376
+ declare function forceIterationCount(nodeCount: number): number;
377
+ declare function createForceSimulation(request: ForceLayoutRequest): {
378
+ readonly nodeCount: number;
379
+ readonly edges: Uint32Array<ArrayBuffer>;
380
+ readonly positions: Float32Array<ArrayBuffer>;
381
+ /**
382
+ * Add nodes while preserving every existing position and velocity. A simulation created with
383
+ * `clusters` requires one cluster index per added node (a new id starts a new cluster); a
384
+ * simulation created without clusters rejects them.
385
+ */
386
+ addNodes(count: number, initial?: Float32Array, addedClusters?: Uint32Array): number;
387
+ /** Append endpoint indices for new edges; existing edge indices remain stable. */
388
+ addEdges(nextEdges: Uint32Array): void;
389
+ step(alpha: number): void;
390
+ };
391
+ declare function computeForcePositions(request: ForceLayoutRequest): Float32Array<ArrayBuffer>;
392
+ declare function computeClusteredForcePositions(request: ForceLayoutRequest & {
393
+ clusters: Uint32Array;
394
+ }): Float32Array<ArrayBuffer>;
395
+ //#endregion
396
+ //#region src/graphology.d.ts
397
+ type GraphologyNodeAttributes = Record<string, unknown> & {
398
+ color?: GraphraumColor;
399
+ shape?: GraphraumNodeShape;
400
+ size?: number;
401
+ x?: number;
402
+ y?: number;
403
+ z?: number;
404
+ };
405
+ type GraphologyEdgeAttributes = Record<string, unknown> & {
406
+ color?: GraphraumColor;
407
+ };
408
+ interface GraphologyRenderer<NodeAttributes, EdgeAttributes> {
409
+ setData(data: GraphraumData<NodeAttributes, EdgeAttributes>): void;
410
+ }
411
+ /** Projects a mutable Graphology graph into Graphraum and keeps the binding alive until disposed. */
412
+ declare function bindGraphology<NodeAttributes extends GraphologyNodeAttributes, EdgeAttributes extends GraphologyEdgeAttributes>(renderer: GraphologyRenderer<NodeAttributes, EdgeAttributes>, graph: Graph<NodeAttributes, EdgeAttributes>): () => void;
413
+ //#endregion
414
+ //#region src/overlay.d.ts
415
+ /** A bounded DOM layer for labels, a focused-node toolbar, and optional rich HTML cards. */
416
+ declare class GraphraumOverlay<NodeAttributes = undefined, EdgeAttributes = undefined> {
417
+ private readonly graph;
418
+ private readonly container;
419
+ private readonly options;
420
+ private readonly root;
421
+ private readonly labels;
422
+ private readonly richNodes;
423
+ private toolbar;
424
+ private toolbarNodeId;
425
+ private readonly stopObservingView;
426
+ private readonly stopObservingFocus;
427
+ private readonly previousContainerPosition;
428
+ private readonly labelPolicy;
429
+ private readonly focusLabelFill;
430
+ private readonly autoToolbar;
431
+ private readonly autoRichNodes;
432
+ private readonly maxLabels;
433
+ private readonly maxRichNodes;
434
+ constructor(graph: Graphraum<NodeAttributes, EdgeAttributes>, container: HTMLElement, options: GraphraumOverlayOptions);
435
+ /**
436
+ * Replaces the labeled node set. When `labelPolicy` is `auto` or `focus`, the next view/focus
437
+ * change overwrites this list from the policy.
438
+ */
439
+ setLabels(nodeIds: Iterable<string>): void;
440
+ /**
441
+ * Replaces the rich HTML node set. When `autoRichNodes` is set, the next focus change
442
+ * overwrites this list from the policy.
443
+ */
444
+ setRichNodes(nodeIds: Iterable<string>): void;
445
+ setToolbar(nodeId: string | null): void;
446
+ destroy(): void;
447
+ private update;
448
+ private syncRichNodesFromFocus;
449
+ private syncToolbar;
450
+ private syncLabels;
451
+ private syncRichNodes;
452
+ private updatePositions;
453
+ private createEntry;
454
+ private updateEntry;
455
+ private applyClassName;
456
+ }
457
+ //#endregion
458
+ //#region src/graphraum.d.ts
459
+ /** A small, explicit WebGL graph renderer: one instanced node mesh and one batched edge geometry. */
460
+ declare class Graphraum<NodeAttributes = undefined, EdgeAttributes = undefined> {
461
+ private readonly container;
462
+ private readonly renderer;
463
+ private readonly scene;
464
+ private readonly raycaster;
465
+ private readonly pointer;
466
+ private readonly pickCenter;
467
+ private readonly pickRight;
468
+ private readonly pickUp;
469
+ private theme;
470
+ private backgroundTexture;
471
+ private readonly resizeObserver;
472
+ private readonly maxVisibleEdges;
473
+ private readonly maxVisibleNodes;
474
+ private readonly viewportCulling;
475
+ private readonly viewportOverscan;
476
+ private readonly visuals;
477
+ private camera;
478
+ private controls;
479
+ private data;
480
+ private nodeIds;
481
+ private nodeIndices;
482
+ private edgeNodeIndices;
483
+ private canonicalEdgePositions;
484
+ private incidentEdgeIndices;
485
+ private nodePresentations;
486
+ private edgePresentations;
487
+ private spatialGrid2d;
488
+ private nodeMesh;
489
+ private nodePickMesh;
490
+ private edgeMesh;
491
+ private readonly glowLayer;
492
+ private hasGlowNodes;
493
+ private nodeCapacity;
494
+ private edgeCapacity;
495
+ private markerCapacity;
496
+ private edgeInstanceCapacity;
497
+ private selectedNodeIds;
498
+ private selectedEdgeIds;
499
+ private dimmedEdgeIds;
500
+ private edgeIndexCache;
501
+ private hoveredNodeIds;
502
+ private focusedNodeIds;
503
+ private dimmedNodeIds;
504
+ private visibleNodeSlots;
505
+ private visibleEdgeSlots;
506
+ private visibleEdgeLayouts;
507
+ private lastEdgeLodTier;
508
+ private visibleNodeIndices;
509
+ private pickableEdgeSegments;
510
+ private visibleEdgeSegmentEdgeIndices;
511
+ private visibleNodeCount;
512
+ private visibleNodeCandidateCount;
513
+ private densityLodActive;
514
+ private visibleEdgeSegmentCount;
515
+ private visibleEdgeCount;
516
+ private visibleEdgeMarkerCount;
517
+ private visibleEdgeCandidateCount;
518
+ private edgeSegmentCapacity;
519
+ private mode;
520
+ private frameRequest;
521
+ private autoOrbit;
522
+ private flight;
523
+ private readonly flightDirection;
524
+ /** True only during the orbit's own `controls.update(dt)`; see `handleViewChange`. */
525
+ private orbitUpdating;
526
+ private destroyed;
527
+ private cpuFrameMilliseconds;
528
+ private gpuFrameMilliseconds;
529
+ private gpuTimer;
530
+ private readonly viewListeners;
531
+ private readonly focusListeners;
532
+ private lastHoveredNodeId;
533
+ constructor(container: HTMLElement, options?: GraphraumOptions<NodeAttributes, EdgeAttributes>);
534
+ setData(data: GraphraumData<NodeAttributes, EdgeAttributes>): void;
535
+ /** Merge a topology patch without fitting or resetting the current camera. */
536
+ applyDataPatch(patch: GraphraumDataPatch<NodeAttributes, EdgeAttributes>): void;
537
+ private canAppendDataPatch;
538
+ private canMutateDataInPlace;
539
+ private mutateDataInPlace;
540
+ private appendDataPatch;
541
+ private replaceData;
542
+ updateNodes(updates: readonly GraphraumNodeUpdate[]): void;
543
+ /** Applies a transferable XYZ layout batch. Call repeatedly as a worker produces progressive positions. */
544
+ applyLayout(layout: GraphraumLayoutPositions): void;
545
+ private getGraphObjects;
546
+ private applyPreparedNodeUpdates;
547
+ /** Patches GPU edge instances for moved endpoints when the visible edge set is unchanged. */
548
+ private patchVisibleEdgeEndpoints;
549
+ setSelection(nodeIds: Iterable<string>): void;
550
+ /**
551
+ * Highlights edges without changing node selection or source graph data. Unknown IDs are ignored.
552
+ * Only the instance colors of edges whose selection changed are rewritten and uploaded.
553
+ */
554
+ setEdgeSelection(edgeIds: Iterable<string>): void;
555
+ /**
556
+ * Applies an application-owned edge state without changing source graph data. `dimmed` uses
557
+ * `theme.dimmedEdge` / `theme.dimmedEdgeOpacity`; selected edges stay selected. Unknown IDs are ignored.
558
+ */
559
+ setEdgeState(state: GraphraumEdgeState, edgeIds: Iterable<string>): void;
560
+ private knownEdgeIds;
561
+ /**
562
+ * Visible slots, LOD tier, and geometry only change through materialize or endpoint patches,
563
+ * which keep `visibleEdgeLayouts` current, so a state change repaints in place. If a layout no
564
+ * longer fits the drawn instances, that invariant broke: fall back to a full materialize.
565
+ */
566
+ private repaintEdgeStates;
567
+ private edgeStateStyling;
568
+ /** Edge ID to compiled index, rebuilt only when the edge list is replaced. */
569
+ private edgeIndicesById;
570
+ /** Drops edge states whose edges left the graph. */
571
+ private pruneEdgeStates;
572
+ /** Applies an application-owned visual state without changing source graph data. */
573
+ setNodeState(state: GraphraumNodeState, nodeIds: Iterable<string>): void;
574
+ /** One-hop neighbor node ids via incident edges (undirected adjacency). */
575
+ getNeighborIds(nodeId: string): string[];
576
+ getSelectedNodeIds(): readonly string[];
577
+ getHoveredNodeIds(): readonly string[];
578
+ /** Schedules one render on the next animation frame. Repeated calls in one frame are coalesced. */
579
+ render(): void;
580
+ setMode(mode: GraphraumMode): void;
581
+ getMode(): GraphraumMode;
582
+ /**
583
+ * Slowly orbits the 3D camera around its target until called with `false`. The orbit pauses while
584
+ * the camera controls handle a drag, pinch or wheel step and resumes after `resumeAfterMs` of idle;
585
+ * it also stops while the document is hidden or the canvas is off screen. While it is active a
586
+ * frame loop renders every frame (no viewport rematerialization), so check `prefers-reduced-motion`
587
+ * before enabling it. Throws in 2D mode; `setMode("2d")` turns it off. Does nothing after `destroy()`.
588
+ */
589
+ setAutoOrbit(options: GraphraumAutoOrbitOptions | false): void;
590
+ getNodePresentation(nodeId: string): CompiledGraphraumPresentation | null;
591
+ /** Returns canvas-relative screen coordinates for a node, or null when the node does not exist. */
592
+ getNodeScreenPosition(nodeId: string): GraphraumScreenPosition | null;
593
+ /**
594
+ * Candidates for a budgeted DOM label layer. Importance is incident edge degree, with a boost
595
+ * for selected nodes so selection stays labeled. Visibility comes from screen projection.
596
+ */
597
+ getLabelCandidates(): GraphraumLabelCandidate[];
598
+ /** Converts client coordinates to graph world coordinates on the z=0 plane. */
599
+ screenToWorld(clientX: number, clientY: number): {
600
+ x: number;
601
+ y: number;
602
+ z: number;
603
+ } | null;
604
+ /** Creates a bounded DOM layer for labels, a toolbar, and optional rich HTML focus cards. */
605
+ createOverlay(options?: GraphraumOverlayOptions): GraphraumOverlay<NodeAttributes, EdgeAttributes>;
606
+ /** Subscribes to camera and viewport changes. Returns an unsubscribe function. */
607
+ onViewChange(listener: () => void): () => boolean;
608
+ /** Subscribes to selection and hover changes that affect overlay focus chrome. */
609
+ onFocusChange(listener: () => void): () => boolean;
610
+ private notifyFocusChange;
611
+ private handlePointerMove;
612
+ private handlePointerLeave;
613
+ getEdgePresentation(edgeId: string): CompiledGraphraumPresentation | null;
614
+ getDiagnostics(): GraphraumDiagnostics;
615
+ /** Picks a node under the pointer. Prefer `pickHit` when edges must be selectable too. */
616
+ pick(clientX: number, clientY: number): string | null;
617
+ /**
618
+ * Picks the top-most graph element under the pointer. Nodes win over edges when both hit.
619
+ * 2D uses the spatial grid for nodes and world-space segment distance for edges; 3D uses
620
+ * the node pick mesh then the edge instance mesh.
621
+ */
622
+ pickHit(clientX: number, clientY: number): GraphraumPickHit | null;
623
+ /** Frames every node, or only `nodeIds`; unknown ids are ignored and no match frames all nodes. Cancels a running `flyTo`. */
624
+ fitView(options?: {
625
+ nodeIds?: readonly string[];
626
+ }): void;
627
+ /**
628
+ * Animates the camera to frame `nodeIds` as `fitView({ nodeIds })` would, with an ease-in-out
629
+ * over `durationMs`. In 3D the camera and the orbit target move together and keep the current
630
+ * view direction, so a running auto-orbit carries on around the new target, at the fitted
631
+ * distance times `distanceScale`. In 2D it pans and zooms. Resolves `true` on arrival and
632
+ * `false` when cancelled by canvas pointer, wheel or key input, another `flyTo`, `fitView`,
633
+ * `setData`, `setMode`, a 2D resize or `destroy()`. Unknown IDs are ignored; when none match,
634
+ * or after `destroy()`, it resolves `false` without moving. With `prefers-reduced-motion:
635
+ * reduce` or a zero duration it jumps. Throws for an invalid duration or distance scale.
636
+ */
637
+ flyTo(options: GraphraumFlyToOptions): Promise<boolean>;
638
+ /** Nodes whose id is in `nodeIds`, in graph order; every node when `nodeIds` is omitted. */
639
+ private nodesWithIds;
640
+ /** The closest front-view camera that frames `nodes` (issue #124); applies the container aspect. */
641
+ private perspectiveFit;
642
+ /**
643
+ * A 3D flight keeps the view direction it reads from the camera every frame, so an orbit step
644
+ * between frames turns the flight with it. The destination distance is the front-view fit
645
+ * distance; near and far span both ends while flying, and the far plane at arrival still holds
646
+ * the whole graph so the rest of it stays visible around the framed nodes.
647
+ */
648
+ private planPerspectiveFlight;
649
+ /**
650
+ * A 2D flight switches to the destination frustum up front with a zoom that keeps the current
651
+ * view, then pans and eases the zoom back to 1, so it lands exactly on the fitted frustum.
652
+ */
653
+ private planOrthographicFlight;
654
+ /** Largest distance from `center` to any node's far edge. */
655
+ private graphRadiusAround;
656
+ /** Steps a running flight to the frame time; settles and resolves it at the end. */
657
+ private advanceFlight;
658
+ private readonly cancelFlight;
659
+ setTheme(theme: Partial<GraphraumTheme> | GraphraumThemeName): void;
660
+ setBackground(background: GraphraumBackground): void;
661
+ resize(): void;
662
+ destroy(): void;
663
+ private readonly requestRender;
664
+ /** Renders a pending frame now instead of on the next animation frame. */
665
+ private flushRender;
666
+ private renderFrame;
667
+ private stopAutoOrbit;
668
+ /**
669
+ * One orbit frame. `autoRotate` is on only for this `update(dt)` call: OrbitControls also calls
670
+ * `update()` without a delta from wheel and key handlers and `fitView`, which would otherwise add
671
+ * a fixed per-call rotation. A moved camera emits `change` (a render request only, see
672
+ * `handleViewChange`); flushing that request renders this frame instead of the next one. During
673
+ * a `flyTo` the orbit still turns the camera but leaves the pending frame to the flight, which
674
+ * steps from the turned direction before it renders. A
675
+ * throwing update, render or view listener turns the orbit off rather than leaving it enabled
676
+ * without a loop.
677
+ */
678
+ private readonly stepAutoOrbit;
679
+ private readGpuTimers;
680
+ /**
681
+ * Orbit frames skip materialization. In 3D the packed instances do not depend on camera
682
+ * orientation: there is no viewport culling, density LOD and edge tiers follow counts only,
683
+ * and the one orientation-dependent value (`worldUnitsPerPixel`, for edge hit slop) is used
684
+ * only by 2D picking. 3D picking raycasts the instances and screen positions are projected per
685
+ * call, so an orbit frame needs only a render, whose view listeners still run every frame.
686
+ */
687
+ private readonly handleViewChange;
688
+ private createCamera;
689
+ private createControls;
690
+ private pickNode;
691
+ private pickEdge;
692
+ private worldUnitsPerPixel;
693
+ private applySelectionColors;
694
+ /** Rebuilds the opt-in halo pass; graphs without glow release it and skip the visible scan. */
695
+ private syncGlow;
696
+ private readonly glowAt;
697
+ private readonly describeGlowNode;
698
+ private getNodeStateIds;
699
+ private setNodeStateIds;
700
+ private getNodeColor;
701
+ private getViewportBounds2d;
702
+ private isNodeVisible2d;
703
+ private disposeBackgroundTexture;
704
+ private createBackgroundTexture;
705
+ private applyBackground;
706
+ private materializeViewport;
707
+ private materializeNodes;
708
+ private disposeGraphObjects;
709
+ }
710
+ //#endregion
711
+ //#region src/label-budget.d.ts
712
+ interface LabelBudgetCandidate {
713
+ id: string;
714
+ importance: number;
715
+ visible: boolean;
716
+ }
717
+ interface SelectBudgetedLabelIdsInput {
718
+ candidates: readonly LabelBudgetCandidate[];
719
+ maxLabels: number;
720
+ }
721
+ /** Picks visible label ids by importance within a hard DOM budget. */
722
+ declare function selectBudgetedLabelIds({ candidates, maxLabels }: SelectBudgetedLabelIdsInput): string[];
723
+ interface SelectFocusLabelIdsInput {
724
+ /** Prefer these ids first (selected, then hovered, then neighbors) — order within each tier is free. */
725
+ focusIds: readonly string[];
726
+ candidates: readonly LabelBudgetCandidate[];
727
+ maxLabels: number;
728
+ /**
729
+ * When `"importance"` (default), leftover budget is filled by visible importance.
730
+ * When `"none"`, only focus ids are labeled.
731
+ */
732
+ fill?: "importance" | "none";
733
+ }
734
+ /**
735
+ * Labels for a sparse focus set: keep focus ids that are visible, then optionally fill
736
+ * remaining budget from other visible candidates by importance. Focus order is preserved.
737
+ */
738
+ declare function selectFocusLabelIds({ focusIds, candidates, maxLabels, fill }: SelectFocusLabelIdsInput): string[];
739
+ /** Builds an ordered focus id list: selected, then hovered, then unique neighbors. */
740
+ declare function orderFocusNodeIds(input: {
741
+ selectedIds: readonly string[];
742
+ hoveredIds: readonly string[];
743
+ neighborIds: readonly string[];
744
+ }): string[];
745
+ //#endregion
746
+ //#region src/node-shapes.d.ts
747
+ declare const graphraumNodeShapes: readonly ["circle", "square", "diamond", "hexagon", "triangle", "pill", "rounded"];
748
+ //#endregion
749
+ //#region src/overlay-budget.d.ts
750
+ type RichNodePolicy = "selected" | "hovered" | "focus";
751
+ declare function boundOverlayIds(ids: Iterable<string>, max: number, kind: string): string[];
752
+ declare function selectRichNodeIds(input: {
753
+ hoveredIds: readonly string[];
754
+ maxRichNodes: number;
755
+ policy: RichNodePolicy;
756
+ selectedIds: readonly string[];
757
+ }): string[];
758
+ //#endregion
759
+ //#region src/theme.d.ts
760
+ /** Dark canvas: Ink Black, Turf Green, Porcelain. */
761
+ declare const graphraumThemeDark: Readonly<{
762
+ background: string;
763
+ dimmedEdge: string;
764
+ dimmedEdgeOpacity: number;
765
+ dimmedNode: string;
766
+ edge: string;
767
+ edgeOpacity: number;
768
+ edgeWidth: number;
769
+ endpointAttach: "boundary";
770
+ focusedNode: string;
771
+ hoveredNode: string;
772
+ node: string;
773
+ nodeStroke: string;
774
+ selectedEdge: string;
775
+ selectedNode: string;
776
+ }>;
777
+ /** Light canvas: Porcelain field with Turf marks and Ink selection. */
778
+ declare const graphraumThemeLight: Readonly<{
779
+ background: string;
780
+ dimmedEdge: string;
781
+ dimmedEdgeOpacity: number;
782
+ dimmedNode: string;
783
+ edge: string;
784
+ edgeOpacity: number;
785
+ edgeWidth: number;
786
+ endpointAttach: "boundary";
787
+ focusedNode: string;
788
+ hoveredNode: string;
789
+ node: string;
790
+ nodeStroke: string;
791
+ selectedEdge: string;
792
+ selectedNode: string;
793
+ }>;
794
+ declare const graphraumThemes: Readonly<{
795
+ dark: Readonly<{
796
+ background: string;
797
+ dimmedEdge: string;
798
+ dimmedEdgeOpacity: number;
799
+ dimmedNode: string;
800
+ edge: string;
801
+ edgeOpacity: number;
802
+ edgeWidth: number;
803
+ endpointAttach: "boundary";
804
+ focusedNode: string;
805
+ hoveredNode: string;
806
+ node: string;
807
+ nodeStroke: string;
808
+ selectedEdge: string;
809
+ selectedNode: string;
810
+ }>;
811
+ light: Readonly<{
812
+ background: string;
813
+ dimmedEdge: string;
814
+ dimmedEdgeOpacity: number;
815
+ dimmedNode: string;
816
+ edge: string;
817
+ edgeOpacity: number;
818
+ edgeWidth: number;
819
+ endpointAttach: "boundary";
820
+ focusedNode: string;
821
+ hoveredNode: string;
822
+ node: string;
823
+ nodeStroke: string;
824
+ selectedEdge: string;
825
+ selectedNode: string;
826
+ }>;
827
+ }>;
828
+ /** Alias of {@link graphraumThemeDark} for backward compatibility. */
829
+ declare const graphraumTheme: Readonly<{
830
+ background: string;
831
+ dimmedEdge: string;
832
+ dimmedEdgeOpacity: number;
833
+ dimmedNode: string;
834
+ edge: string;
835
+ edgeOpacity: number;
836
+ edgeWidth: number;
837
+ endpointAttach: "boundary";
838
+ focusedNode: string;
839
+ hoveredNode: string;
840
+ node: string;
841
+ nodeStroke: string;
842
+ selectedEdge: string;
843
+ selectedNode: string;
844
+ }>;
845
+ declare function resolveGraphraumTheme(input?: Partial<GraphraumTheme> | GraphraumThemeName, base?: GraphraumTheme): GraphraumTheme;
846
+ /** Collapse `"transparent"` onto `null` so callers have one transparent sentinel. */
847
+ declare function normalizeGraphraumBackground(background: GraphraumBackground): Exclude<GraphraumBackground, "transparent">;
848
+ declare function isTransparentGraphraumBackground(background: GraphraumBackground): boolean;
849
+ //#endregion
850
+ //#region src/visuals.d.ts
851
+ /** Defines typed, serializable graph visuals without coupling application data to the render loop. */
852
+ declare function defineVisuals<NodeAttributes = undefined, EdgeAttributes = undefined>(visuals: GraphraumVisualMapper<NodeAttributes, EdgeAttributes>): GraphraumVisualMapper<NodeAttributes, EdgeAttributes>;
853
+ //#endregion
854
+ export { type CompiledGraphraumPresentation, DEFAULT_ENDPOINT_ATTACH, DEFAULT_ENDPOINT_CLEARANCE, DEFAULT_FORCE_SETTINGS, type EndpointAttach, type EndpointOutline, type ForceLayoutRequest, type ForceSettings, type GraphologyEdgeAttributes, type GraphologyNodeAttributes, type GraphologyRenderer, Graphraum, type GraphraumAutoOrbitOptions, type GraphraumAutoOrbitStatus, type GraphraumBackground, type GraphraumColor, type GraphraumData, type GraphraumDataPatch, type GraphraumDiagnostics, type GraphraumEdge, type GraphraumEdgeEncoding, type GraphraumEdgeMarker, type GraphraumEdgeMarkerEnd, type GraphraumEdgePath, type GraphraumEdgeState, type GraphraumEdgeStyle, type GraphraumEdgeVisual, type GraphraumFlyToOptions, type GraphraumLabelCandidate, type GraphraumLayoutPositions, type GraphraumMode, type GraphraumNode, type GraphraumNodeEncoding, type GraphraumNodeGeometry, type GraphraumNodeShape, type GraphraumNodeState, type GraphraumNodeUpdate, type GraphraumNodeVisual, type GraphraumOptions, GraphraumOverlay, type GraphraumOverlayNode, type GraphraumOverlayOptions, type GraphraumPatternBackground, type GraphraumPickHit, type GraphraumPosition, type GraphraumPresentation, type GraphraumPresentationAction, type GraphraumPresentationProperty, type GraphraumPropertyValue, type GraphraumScreenPosition, type GraphraumTheme, type GraphraumThemeName, type GraphraumVisualMapper, type LabelBudgetCandidate, type RichNodePolicy, type SelectBudgetedLabelIdsInput, type SelectFocusLabelIdsInput, attachPointOnOutline, bindGraphology, boundOverlayIds, computeClusteredForcePositions, computeForcePositions, createForceSimulation, defineVisuals, forceIterationCount, graphraumEdgePaths, graphraumNodeShapes, graphraumTheme, graphraumThemeDark, graphraumThemeLight, graphraumThemes, isTransparentGraphraumBackground, normalizeGraphraumBackground, orderFocusNodeIds, resolveGraphraumTheme, selectBudgetedLabelIds, selectFocusLabelIds, selectRichNodeIds, trimEdgeEndpoints };