@graphty/graphty-element 2.3.1 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/AGENTS.md +4 -3
  2. package/dist/ai.js +3 -3
  3. package/dist/catalog.js +28 -27
  4. package/dist/chunks/{AiManager-BBmGJbH4.js → AiManager-DNJCoeTO.js} +5 -5
  5. package/dist/chunks/{DataSource-OeN3NeyD.js → DataSource-xL3Yn0Pa.js} +72 -61
  6. package/dist/chunks/{GraphSession-Bef1AYw9.js → GraphSession-PFm2tJ_j.js} +2942 -2822
  7. package/dist/chunks/{GraphtyLogger-5KEttFUo.js → GraphtyLogger-kbOx2zq3.js} +170 -222
  8. package/dist/chunks/{NodeStyle-DKj7HjMJ.js → NodeStyle-CtmA7dXi.js} +16 -14
  9. package/dist/chunks/{VoiceInputAdapter-Dr9Gcmds.js → VoiceInputAdapter-CDNKQgUK.js} +1 -1
  10. package/dist/chunks/{XRPivotCameraController-BbfgZWpS.js → XRPivotCameraController-JdlLBcD7.js} +146 -134
  11. package/dist/chunks/{algorithms-CpX56sUB.js → algorithms-B87OPYw4.js} +780 -635
  12. package/dist/chunks/{capability-check-Blhb2aBB.js → capability-check-B2oYf_30.js} +1 -1
  13. package/dist/chunks/{detect-Cqwshr9a.js → detect-tuYQLJbT.js} +2 -2
  14. package/dist/chunks/{format-detection-BXGO1lSn.js → format-detection-BG6CMwPO.js} +1 -1
  15. package/dist/chunks/{index-C0mIoumR.js → index-6GDfNfwJ.js} +3185 -2731
  16. package/dist/chunks/{optionsFromZod-17lkrAJs.js → optionsFromZod-DHLLiX_8.js} +275 -267
  17. package/dist/chunks/{paletteRegistry-x7WOEKZY.js → paletteRegistry-jg9uS7wo.js} +72 -70
  18. package/dist/chunks/pluginRegistry-MaTIDh6l.js +238 -0
  19. package/dist/chunks/{registry-CSba5QGJ.js → registry-DQeq4B2K.js} +8 -8
  20. package/dist/chunks/{scales-BRwl51k8.js → scales-BXHmwPXC.js} +572 -399
  21. package/dist/chunks/{types-B7bX5c0K.js → types-C_c53VgR.js} +31 -26
  22. package/dist/commands.d.ts +4 -0
  23. package/dist/custom-elements.json +1 -1
  24. package/dist/extend.js +8 -8
  25. package/dist/graphty-catalog.json +245 -12
  26. package/dist/graphty.bundle.js +37843 -36551
  27. package/dist/graphty.js +69 -65
  28. package/dist/index.d.ts +3 -0
  29. package/dist/logging.js +2 -2
  30. package/dist/schema.d.ts +1 -1
  31. package/dist/schema.js +42 -40
  32. package/dist/session.js +6 -6
  33. package/dist/src/Edge.d.ts +4 -4
  34. package/dist/src/Graph.d.ts +79 -7
  35. package/dist/src/acceleration/AccelerationController.d.ts +20 -3
  36. package/dist/src/acceleration/registry.d.ts +3 -0
  37. package/dist/src/acceleration/types.d.ts +85 -0
  38. package/dist/src/algorithms/Algorithm.d.ts +13 -0
  39. package/dist/src/algorithms/KCoreAlgorithm.d.ts +26 -0
  40. package/dist/src/algorithms/LinkPredictionAlgorithm.d.ts +41 -0
  41. package/dist/src/cameras/InputUtils.d.ts +67 -0
  42. package/dist/src/cameras/XRInputHandler.d.ts +0 -8
  43. package/dist/src/catalog/algorithms.d.ts +5 -5
  44. package/dist/src/catalog/index.d.ts +2 -2
  45. package/dist/src/catalog/layouts.d.ts +7 -6
  46. package/dist/src/catalog/pluginRegistry.d.ts +62 -0
  47. package/dist/src/catalog/registry.d.ts +7 -0
  48. package/dist/src/catalog/types.d.ts +77 -6
  49. package/dist/src/config/EdgeStyle.d.ts +35 -0
  50. package/dist/src/config/index.d.ts +1 -1
  51. package/dist/src/data/DataSource.d.ts +1 -1
  52. package/dist/src/data/GEXFDataSource.d.ts +23 -0
  53. package/dist/src/errors/codes.d.ts +16 -1
  54. package/dist/src/events.d.ts +30 -0
  55. package/dist/src/graphty-element.d.ts +66 -26
  56. package/dist/src/layout/GridLayoutEngine.d.ts +37 -0
  57. package/dist/src/layout/LayoutEngine.d.ts +7 -7
  58. package/dist/src/layout/NGraphLayoutEngine.d.ts +2 -0
  59. package/dist/src/layout/RadialLayoutEngine.d.ts +37 -0
  60. package/dist/src/layout/SimulationLayoutEngine.d.ts +12 -0
  61. package/dist/src/managers/DataManager.d.ts +69 -2
  62. package/dist/src/managers/EventManager.d.ts +10 -4
  63. package/dist/src/managers/GraphContext.d.ts +2 -1
  64. package/dist/src/managers/LayoutManager.d.ts +30 -3
  65. package/dist/src/meshes/CustomLineRenderer.d.ts +11 -9
  66. package/dist/src/meshes/EdgeMesh.d.ts +18 -9
  67. package/dist/src/meshes/FilledArrowRenderer.d.ts +61 -23
  68. package/dist/src/meshes/MeshCache.d.ts +18 -0
  69. package/dist/src/meshes/NodeEffects.d.ts +16 -11
  70. package/dist/src/meshes/NodeMesh.d.ts +2 -2
  71. package/dist/src/meshes/PatternedLineRenderer.d.ts +5 -37
  72. package/dist/src/meshes/PerSceneMaterials.d.ts +49 -0
  73. package/dist/src/meshes/RichTextParser.d.ts +26 -0
  74. package/dist/src/meshes/RichTextRenderer.d.ts +0 -1
  75. package/dist/src/session/cost/estimate.d.ts +1 -1
  76. package/dist/src/session/layout.d.ts +3 -3
  77. package/dist/src/session/runs/RunsApi.d.ts +1 -1
  78. package/dist/src/session/styles/StylesApi.d.ts +6 -0
  79. package/dist/src/session/styles/intern.d.ts +26 -5
  80. package/dist/src/session/styles/repaint.d.ts +2 -1
  81. package/dist/src/session/types.d.ts +6 -6
  82. package/dist/src/xr/XRSessionManager.d.ts +1 -1
  83. package/dist/webgpu.d.ts +8 -0
  84. package/dist/webgpu.js +2 -2
  85. package/package.json +6 -6
  86. package/dist/chunks/GraphtyError-BwcnblTH.js +0 -132
@@ -16,6 +16,7 @@
16
16
  * The file is free of Babylon.js, Lit and DOM references so it can be re-exported from the
17
17
  * Node-safe entry points.
18
18
  */
19
+ import type { AlgorithmAccelerator } from "@graphty/algorithms";
19
20
  import type { AccelerationErrorCode } from "../errors";
20
21
  /**
21
22
  * Where hardware acceleration stands, in one word.
@@ -220,6 +221,13 @@ export type AcceleratorFactory = (options?: AcceleratorFactoryOptions) => Promis
220
221
  * renders. It is plain, frozen, serialisable data: no GPU objects, no promises, no classes.
221
222
  */
222
223
  export interface AccelerationStatus {
224
+ /**
225
+ * The policy in force: what the consumer asked for, however they asked -- the `acceleration`
226
+ * attribute, the property, or `session.acceleration`. A change of policy publishes a new
227
+ * status even when the state does not move.
228
+ * @since 2.3.0
229
+ */
230
+ readonly policy: AccelerationPolicy;
223
231
  /** The one-word state. */
224
232
  readonly state: AccelerationState;
225
233
  /** The attached accelerator's backend, when one is attached. */
@@ -362,3 +370,80 @@ export declare const ACCELERATION_MIN_NODES_KEY = "acceleration.minNodes";
362
370
  * which is not part of the published documentation site.
363
371
  */
364
372
  export declare const ACCELERATION_MIN_NODES_DEFAULT = 0;
373
+ /**
374
+ * Where and when the per-capability floors below were measured, as the plan's reason quotes it.
375
+ */
376
+ export declare const ACCELERATION_MIN_NODES_MEASUREMENT = "RTX 4070 SUPER, headless Chromium, 2026-09-25";
377
+ /**
378
+ * The node count below which the element declines the accelerator for one capability, when the
379
+ * consumer has not set {@link ACCELERATION_MIN_NODES_KEY} themselves.
380
+ *
381
+ * {@link ACCELERATION_MIN_NODES_DEFAULT} is 0 because it was measured for the forceatlas2
382
+ * LAYOUT, whose accelerated frame was never slower than the CPU's at any size. A traversal is a
383
+ * different shape of work: the whole walk is one call, and on the device that call has a floor
384
+ * of several submit round trips whatever the size, so below some node count the CPU walk is done
385
+ * before the device has started. One number cannot serve both, which is why the layout default
386
+ * stays 0 and each algorithm capability the adapters route carries its own floor here.
387
+ *
388
+ * MEASURED, not guessed. `tmp/traversal-threshold/kernel-cpu-vs-gpu.ts` (the harness, kept
389
+ * out of the tree) mounted `<graphty-element acceleration="required">` in headless Chromium on
390
+ * the dev box (RTX 4070 SUPER through ANGLE's Vulkan backend, 2026-09-25, load average
391
+ * 18 to 23), took the accelerator the element built, built seeded undirected graphs with ten
392
+ * edges per node and integer weights 1..10 in the page, and timed the two dispatchers
393
+ * `Algorithm.accelerated()` uses -- `accelerated(null)` for the CPU port and
394
+ * `accelerated(narrowAlgorithms(accelerator))` for the device -- directly on the snapshot, so no
395
+ * renderer frame is in the loop. Medians of nine warm calls after one cold call, in ms:
396
+ *
397
+ * | nodes / edges | BFS cpu | BFS gpu | sssp cpu | sssp gpu | pageRank cpu | pageRank gpu | components cpu | components gpu |
398
+ * | ------------- | ------: | ------: | -------: | -------: | -----------: | -----------: | -------------: | -------------: |
399
+ * | 1k / 10k | 0.1 | 7.8 | 0.3 | 7.3 | 0.7 | 3.0 | 0.2 | 3.9 |
400
+ * | 5k / 50k | 0.6 | 8.8 | 1.6 | 7.9 | 5.3 | 2.9 | 1.2 | 5.3 |
401
+ * | 10k / 100k | 1.0 | 7.5 | 2.8 | 7.2 | 4.6 | 2.9 | 2.3 | 7.4 |
402
+ * | 20k / 200k | 1.7 | 9.1 | 3.8 | 13.5 | 16.2 | 3.0 | 4.8 | 7.5 |
403
+ * | 50k / 500k | 5.7 | 13.0 | 25.8 | 27.6 | 50.3 | 4.3 | 11.6 | 7.8 |
404
+ * | 100k / 1M | 9.8 | 17.3 | 30.4 | 49.2 | 67.4 | 5.6 | 16.2 | 8.6 |
405
+ * | 200k / 2M | 17.6 | 23.2 | 73.0 | 79.6 | 148.3 | 10.5 | 29.9 | 11.5 |
406
+ * | 500k / 5M | 88.3 | 33.6 | 343.8 | 150.3 | 523.0 | 36.1 | 99.7 | 20.9 |
407
+ * | 1M / 10M | 250.9 | 24.2 | 1031.1 | 274.9 | 1762.4 | 17.5 | 215.7 | 43.2 |
408
+ *
409
+ * The two traversals were then measured twice more around their crossover, because the device's
410
+ * time at one size moves with what else the box is doing (the first sweep's 200k row was taken
411
+ * at a load average of 22.6, the repeats at about 18):
412
+ *
413
+ * | nodes / edges | BFS cpu | BFS gpu | sssp cpu | sssp gpu |
414
+ * | ------------- | ------: | ------: | -------: | -------: |
415
+ * | 100k / 1M | 7.4 | 9.5 | 26.9 | 20.9 |
416
+ * | 200k / 2M | 15.6 | 10.1 | 66.3 | 37.8 |
417
+ * | 200k / 2M | 17.8 | 11.0 | 70.6 | 42.5 |
418
+ * | 300k / 3M | 35.2 | 13.3 | 129.2 | 77.1 |
419
+ * | 300k / 3M | 34.4 | 18.9 | 129.8 | 65.2 |
420
+ * | 400k / 4M | 63.3 | 30.7 | 226.6 | 128.2 |
421
+ * | 500k / 5M | 93.1 | 32.5 | 361.9 | 200.0 |
422
+ *
423
+ * So: BFS loses at 100k in both runs and splits at 200k (one loss, two wins); Dijkstra splits at
424
+ * both 100k and 200k; from 300k up the device wins every run of both by two to ten times.
425
+ * PageRank wins from 5k and connected components from 50k, in the one run each was measured.
426
+ *
427
+ * A floor is the smallest measured size at which the device's median was at or below the CPU
428
+ * port's IN EVERY RUN, so a size that won under one load and lost under another is below it. A
429
+ * capability that is not listed has no floor and follows {@link ACCELERATION_MIN_NODES_DEFAULT}.
430
+ *
431
+ * Two things the table does not cover. It is one card and one browser: a slower CPU or a
432
+ * slower device moves the crossover, and a consumer who has measured their own machine sets
433
+ * `acceleration.minNodes`, which replaces every floor here with their number. And it is the
434
+ * kernel's time, not the run's: through the element a run also publishes its result rows,
435
+ * which costs the same on either path and is why the element-level numbers in the issue read
436
+ * higher on both sides.
437
+ *
438
+ * Under `acceleration="required"` the floors do not apply: `"required"` is what a benchmark
439
+ * runs under, and a benchmark of the small end of the curve has to reach the device.
440
+ */
441
+ /**
442
+ * The capabilities a floor can name: the seam's algorithm members, by their exact names.
443
+ *
444
+ * Typed against the seam rather than as a string so that a member renamed on one side and not
445
+ * the other is a compile error here, not a floor that silently stops applying and sends that
446
+ * capability back to the GPU at every size.
447
+ */
448
+ export type FlooredCapability = Exclude<keyof AlgorithmAccelerator, "kind" | "release">;
449
+ export declare const ACCELERATION_MIN_NODES_BY_CAPABILITY: Readonly<Partial<Record<FlooredCapability, number>>>;
@@ -50,8 +50,21 @@ export interface AlgorithmStatics {
50
50
  *
51
51
  * Absent, the element estimates from the `costClass` the descriptor declares, which is what
52
52
  * every algorithm this package ships does.
53
+ *
54
+ * Prefer {@link costUnits}: seconds written on one machine are wrong on every other, and no
55
+ * calibration can scale them, so an estimate from this hook always reports "modelled".
53
56
  */
54
57
  cost?: (n: number, m: number) => number;
58
+ /**
59
+ * A cost model in WORK UNITS over a graph of n nodes and m edges, for the whole run.
60
+ *
61
+ * The units are those of the descriptor's `costClass`: elements for `instant` and
62
+ * `iterative` (count every iteration), source-edge pairs for `heavy`, operations for
63
+ * `cubic`. The element divides them by the rate it measured for that class on this device,
64
+ * so the estimate follows the machine and reports "calibrated" once the device is probed,
65
+ * exactly as a built-in's does. Wins over {@link cost} when both are declared.
66
+ */
67
+ costUnits?: (n: number, m: number) => number;
55
68
  /**
56
69
  * The plugin's own version, recorded on every run this algorithm produces.
57
70
  *
@@ -0,0 +1,26 @@
1
+ import type { FieldDescriptor, NodeId } from "../catalog/types";
2
+ import { MetricAlgorithm } from "./metrics/MetricAlgorithm";
3
+ import type { MetricMeasurement, MetricRunContext } from "./metrics/types";
4
+ /**
5
+ * K-core decomposition: how deep in the graph's densely connected core each node sits.
6
+ *
7
+ * A node's core number is the largest k for which it belongs to a k-core -- the largest part of
8
+ * the graph in which every node has at least k neighbours. Every node has one, so every node is
9
+ * measured.
10
+ */
11
+ export declare class KCoreAlgorithm extends MetricAlgorithm {
12
+ static namespace: string;
13
+ static type: string;
14
+ /**
15
+ * The fields a k-core result publishes.
16
+ * @returns The uniform node-metric fields.
17
+ */
18
+ protected resultFields(): readonly FieldDescriptor[];
19
+ /**
20
+ * Give every node its core number.
21
+ * @param context - Where progress goes and where cancellation arrives.
22
+ * @param nodeIds - The nodes to measure.
23
+ * @returns One core number per node.
24
+ */
25
+ protected measure(context: MetricRunContext, nodeIds: readonly NodeId[]): Promise<MetricMeasurement>;
26
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file Link prediction: the pairs of unconnected nodes most likely to be joined next.
3
+ *
4
+ * The answer is a list of scored pairs, not a value on each node or edge -- a predicted link is
5
+ * an edge that does not exist yet, so there is no element to hang it on. The result is shaped as
6
+ * a pair list, which is why a run of it suggests no style: painting it would mean inventing the
7
+ * thing it painted.
8
+ */
9
+ import { adamicAdarPrediction, commonNeighborsPrediction } from "@graphty/algorithms";
10
+ import { type OptionsSchema as ZodOptionsSchema } from "../config";
11
+ import { type AlgorithmOutput, type AlgorithmRunContext, DeclaredAlgorithm } from "./results";
12
+ import type { OptionsSchema } from "./types/OptionSchema";
13
+ /** The two scoring methods, by the name the `method` option takes. */
14
+ declare const METHODS: {
15
+ readonly "adamic-adar": typeof adamicAdarPrediction;
16
+ readonly "common-neighbors": typeof commonNeighborsPrediction;
17
+ };
18
+ /** Options for link prediction. */
19
+ interface LinkPredictionOptions extends Record<string, unknown> {
20
+ /** Which score ranks the pairs. */
21
+ method: keyof typeof METHODS;
22
+ /** How many of the best-scoring pairs to keep. */
23
+ topK: number;
24
+ }
25
+ /**
26
+ * Link prediction: scores every pair of nodes that is not already joined by how many neighbours
27
+ * the two share, and keeps the best.
28
+ */
29
+ export declare class LinkPredictionAlgorithm extends DeclaredAlgorithm<LinkPredictionOptions> {
30
+ static namespace: string;
31
+ static type: string;
32
+ static zodOptionsSchema: ZodOptionsSchema;
33
+ static optionsSchema: OptionsSchema;
34
+ /**
35
+ * Score every unconnected pair of nodes and keep the best.
36
+ * @param context - What the element gave the run.
37
+ * @returns The scored pairs, best first, or null when the graph has no nodes.
38
+ */
39
+ compute(context: AlgorithmRunContext): Promise<AlgorithmOutput | null>;
40
+ }
41
+ export {};
@@ -1,3 +1,4 @@
1
+ import { Vector3 } from "@babylonjs/core";
1
2
  /**
2
3
  * Shared input utility functions for camera controllers.
3
4
  * Used by both 3D (OrbitInputController) and XR (XRInputHandler).
@@ -11,3 +12,69 @@
11
12
  * @returns Adjusted value with deadzone and curve applied
12
13
  */
13
14
  export declare function applyDeadzone(value: number, threshold?: number): number;
15
+ /** Thumbstick deflection, per axis, that XR input ignores. */
16
+ export declare const XR_THUMBSTICK_DEADZONE = 0.15;
17
+ /**
18
+ * Whether a tracked hand is pinching, with hysteresis: a hand that is already pinching lets go at
19
+ * a looser distance than an open hand needs to start.
20
+ * @param distance - Thumb tip to index tip, in metres
21
+ * @param wasPinching - Whether the hand was pinching last frame
22
+ * @returns Whether the hand is pinching now
23
+ */
24
+ export declare function isPinching(distance: number, wasPinching: boolean): boolean;
25
+ /**
26
+ * How firmly a tracked hand is pinching: 1 with the finger tips touching, falling to 0 at the
27
+ * pinch start distance.
28
+ * @param distance - Thumb tip to index tip, in metres
29
+ * @returns Pinch strength in [0, 1]
30
+ */
31
+ export declare function pinchStrength(distance: number): number;
32
+ /** What one frame of thumbstick input does to the XR pivot. Zero (or a zoom of 1) means leave it alone. */
33
+ interface ThumbstickDeltas {
34
+ /** Radians, for `PivotController.rotate`. */
35
+ yaw: number;
36
+ /** Radians, for `PivotController.rotate`. */
37
+ pitch: number;
38
+ /** Factor, for `PivotController.zoom`. */
39
+ zoom: number;
40
+ /** Distance, for `PivotController.panViewRelative`. */
41
+ pan: number;
42
+ }
43
+ /**
44
+ * Map one frame of thumbstick input to pivot movement. Left stick X yaws and Y pitches (forward
45
+ * moves the graph up, as mouse and touch do); right stick Y zooms (forward zooms in) and X pans.
46
+ * @param left - Left stick axes, each in [-1, 1]
47
+ * @param left.x - Left stick X
48
+ * @param left.y - Left stick Y
49
+ * @param right - Right stick axes, each in [-1, 1]
50
+ * @param right.x - Right stick X
51
+ * @param right.y - Right stick Y
52
+ * @returns The movement to apply this frame
53
+ */
54
+ export declare function thumbstickDeltas(left: {
55
+ x: number;
56
+ y: number;
57
+ }, right: {
58
+ x: number;
59
+ y: number;
60
+ }): ThumbstickDeltas;
61
+ /** What one frame of a two-hand gesture does to the XR pivot. */
62
+ interface TwoHandGestureDelta {
63
+ /** Factor, for `PivotController.zoom`, clamped to [0.9, 1.1]. Hands moving apart zoom out. */
64
+ zoom: number;
65
+ /** Unit axis for `PivotController.rotateAroundAxis`, or null when the hands did not turn. */
66
+ axis: Vector3 | null;
67
+ /** Radians, for `PivotController.rotateAroundAxis`. */
68
+ angle: number;
69
+ }
70
+ /**
71
+ * Map the change in the line between two pinching hands, from one frame to the next, to pivot
72
+ * movement: the change in its length zooms, and the change in its direction rotates.
73
+ * @param previousDistance - Distance between the hands last frame
74
+ * @param previousDirection - Unit vector from left hand to right hand last frame
75
+ * @param distance - Distance between the hands this frame
76
+ * @param direction - Unit vector from left hand to right hand this frame
77
+ * @returns The movement to apply this frame
78
+ */
79
+ export declare function twoHandGestureDelta(previousDistance: number, previousDirection: Vector3, distance: number, direction: Vector3): TwoHandGestureDelta;
80
+ export {};
@@ -32,14 +32,6 @@ export declare class XRInputHandler {
32
32
  private previousDirection;
33
33
  private wasPinching;
34
34
  private handTrackingFeature;
35
- private readonly DEADZONE;
36
- private readonly YAW_SPEED;
37
- private readonly PITCH_SPEED;
38
- private readonly PAN_SPEED;
39
- private readonly ZOOM_SPEED;
40
- private readonly GESTURE_ZOOM_SENSITIVITY;
41
- private readonly _PINCH_START;
42
- private readonly _PINCH_END;
43
35
  private lastControllerRemovedTime;
44
36
  private readonly INPUT_SWITCH_DELAY_MS;
45
37
  private isDraggingNode;
@@ -20,10 +20,10 @@
20
20
  * `JSON.stringify`, a `postMessage` to a worker and a write to a saved document, and a
21
21
  * closure survives none of those. A cost estimate that needs code belongs to
22
22
  * `session.estimate()`, which runs where the code is.
23
- * - **The table only lists what ships.** Four members of `KNOWN_ALGORITHMS` -- `all-paths`,
24
- * `clustering-coefficient`, `k-core` and `link-prediction` -- are not registered by this
25
- * package and therefore have no descriptor here. A catalogue that advertised them would be
26
- * lying about what the element can run.
23
+ * - **The table only lists what ships.** Two members of `KNOWN_ALGORITHMS` -- `all-paths` and
24
+ * `clustering-coefficient`, listed in `DEPRECATED_ALGORITHMS` -- are not implemented and
25
+ * therefore have no descriptor here. A catalogue that advertised them would be lying about
26
+ * what the element can run.
27
27
  *
28
28
  * Two conventions worth stating once:
29
29
  *
@@ -70,7 +70,7 @@ export interface BuiltInAlgorithmDescriptor extends AlgorithmDescriptor {
70
70
  /**
71
71
  * Every algorithm this package registers, as a plain-JSON descriptor.
72
72
  *
73
- * Twenty-one descriptors for twenty-three registered algorithms: the two single-source
73
+ * Twenty-three descriptors for twenty-five registered algorithms: the two single-source
74
74
  * shortest-path engines are one key with a `method` parameter, and the two component algorithms
75
75
  * are one key with a `strength` parameter. Each descriptor's `legacyKeys` names the 1.10 keys it
76
76
  * replaces and the parameters that reproduce them.
@@ -9,8 +9,8 @@ export type { LabelAnimation, LabelBadge, LabelGradientDirection, LabelGradientT
9
9
  export { LABEL_STYLE_FIELDS } from "./label-style";
10
10
  export type { OptionsFromZodOptions, OptionsSource, OptionUiMeta } from "./optionsFromZod";
11
11
  export { optionsFromZod } from "./optionsFromZod";
12
- export type { AlgorithmDescriptor, AlgorithmKey, AttributeDescriptor, AttributeType, Binding, CameraDescriptor, CameraId, CatalogApi, Channel, ChannelValue, CostClass, DrawingMode, EdgeId, EdgeLinePattern, Encoding, FieldDescriptor, FormatDescriptor, FormatId, FunctionDescriptor, GraphtyErrorCode, KnownAlgorithm, LabelStyle, LayerId, LayerKind, LayerSource, LayerSpec, LayoutDescriptor, LayoutId, LogSinkDescriptor, LogSinkId, MetricAvailability, NodeId, OptionBound, OptionChoice, OptionDescriptor, OptionType, PaletteDescriptor, PaletteId, Path, Query, QueryValidation, ResultShape, Rgba, RunId, ScaleDescriptor, Scope, ScopeId, Selector, StaticStyle, StyleDocument, ThemeDescriptor, } from "./types";
13
- export { ATTRIBUTE_TYPES, COST_CLASSES, isAttributeType, isCostClass, isOptionBound, isOptionType, isResultShape, KNOWN_ALGORITHMS, KNOWN_CAMERA_IDS, KNOWN_FORMAT_IDS, KNOWN_LAYOUT_IDS, KNOWN_LOG_SINK_IDS, KNOWN_PALETTE_IDS, OPTION_BOUND_SOURCES, OPTION_TYPES, RESULT_SHAPES, } from "./types";
12
+ export type { AlgorithmDescriptor, AlgorithmKey, AttributeDescriptor, AttributeType, Binding, CameraDescriptor, CameraId, CatalogApi, Channel, ChannelValue, CostClass, DeprecatedAlgorithm, DeprecatedCatalogMethod, DrawingMode, EdgeId, EdgeLinePattern, Encoding, FieldDescriptor, FormatDescriptor, FormatId, FunctionDescriptor, GraphtyErrorCode, KnownAlgorithm, LabelStyle, LayerId, LayerKind, LayerSource, LayerSpec, LayoutDescriptor, LayoutId, LogSinkDescriptor, LogSinkId, MetricAvailability, NodeId, OptionBound, OptionChoice, OptionDescriptor, OptionType, PaletteDescriptor, PaletteId, Path, Query, QueryValidation, ResultShape, Rgba, RunId, ScaleDescriptor, Scope, ScopeId, Selector, StaticStyle, StyleDocument, ThemeDescriptor, } from "./types";
13
+ export { ATTRIBUTE_TYPES, COST_CLASSES, DEPRECATED_ALGORITHMS, isAttributeType, isCostClass, isOptionBound, isOptionType, isResultShape, KNOWN_ALGORITHMS, KNOWN_CAMERA_IDS, KNOWN_FORMAT_IDS, KNOWN_LAYOUT_IDS, KNOWN_LOG_SINK_IDS, KNOWN_PALETTE_IDS, OPTION_BOUND_SOURCES, OPTION_TYPES, RESULT_SHAPES, } from "./types";
14
14
  /**
15
15
  * What the element does NOT serve on the styling surface, said out loud.
16
16
  *
@@ -2,7 +2,7 @@
2
2
  * @file The layout catalogue: the arrangements the element offers, and the engines behind them.
3
3
  *
4
4
  * A public layout name says what the arrangement IS -- "force", "hierarchical", "circular" --
5
- * and never which library draws it. The element registers seventeen engines whose registered
5
+ * and never which library draws it. The element registers nineteen engines whose registered
6
6
  * names ARE their implementations ("ngraph", "d3", "forceatlas2"), and freezing those into the
7
7
  * public API makes swapping an implementation a rename every consumer can see. So the engine is
8
8
  * data on the descriptor instead: `LayoutDescriptor.engine` names the implementation the element
@@ -14,10 +14,11 @@
14
14
  * descriptor a consumer reads carries the default engine and that engine's options; the rest of
15
15
  * the list is there for a consumer that wants to choose.
16
16
  *
17
- * Two names in the built-in layout list have no engine behind them yet, and two engines describe
18
- * an arrangement that list has no name for. Both are recorded here -- {@link UNSERVED_LAYOUT_IDS}
19
- * and the `spiral` and `planar` entries -- rather than left for a consumer to discover by asking
20
- * for a layout that never answers, or by never learning a capability exists.
17
+ * A name in the built-in layout list with no engine behind it would be recorded in
18
+ * {@link UNSERVED_LAYOUT_IDS} (none is, today), and two engines describe an arrangement that list
19
+ * has no name for, which the `spiral` and `planar` entries record -- rather than left for a
20
+ * consumer to discover by asking for a layout that never answers, or by never learning a
21
+ * capability exists.
21
22
  *
22
23
  * `sizeRating` is the largest graph the default engine is recommended for, read from its cost: a
23
24
  * placement that visits each node once rates "any", an iterative all-pairs force rates 2000.
@@ -78,7 +79,7 @@ export declare const LAYOUT_DESCRIPTORS: readonly LayoutDescriptor[];
78
79
  /**
79
80
  * The built-in layout names no registered engine draws yet. Listed rather than omitted, because
80
81
  * a name that is in the type and missing from the catalogue is otherwise discovered by asking
81
- * for it and getting an error.
82
+ * for it and getting an error. Empty today: every built-in name has an engine.
82
83
  */
83
84
  export declare const UNSERVED_LAYOUT_IDS: readonly UnservedLayout[];
84
85
  /**
@@ -33,6 +33,12 @@
33
33
  * {@link PluginRegistry.clearForTesting} exists so a suite can leave the registry as it found
34
34
  * it, and is named so nobody can mistake it for part of the contract.
35
35
  *
36
+ * ONE STORE PER PAGE, NOT PER COPY. A page can evaluate graphty-element twice -- the self-contained
37
+ * bundle beside an `./extend` import, two installs of the package, a dev server loading one module
38
+ * under two URLs -- and the element deliberately survives that. Every registry therefore keeps its
39
+ * state on `globalThis` under a `Symbol.for` key ({@link sharedStore}), so a registration made
40
+ * through any copy reaches the element any other copy defined.
41
+ *
36
42
  * NOTHING HERE IMPORTS A CLASS, a reader, an engine or a renderer. A registry holds whatever it
37
43
  * was handed at run time, which is what keeps `./catalog` and `./extend` resolvable in Node.
38
44
  */
@@ -110,6 +116,62 @@ export interface PluginRegistrySpec<TEntry, TDescriptor> {
110
116
  */
111
117
  builtInIds(): readonly string[];
112
118
  }
119
+ /**
120
+ * The one instance of a piece of registry state on this page, whichever copy of graphty-element
121
+ * asks for it.
122
+ *
123
+ * Keyed on `Symbol.for`, which is shared by every realm-local module graph, so two copies of this
124
+ * module find the same value. The `v1` names the shape of what is stored, and that includes the
125
+ * shape of every entry and descriptor inside it, not only the container: any change to an entry
126
+ * or a descriptor that an older copy on the same page cannot read must bump the version, so the
127
+ * two copies keep separate stores rather than misreading each other's.
128
+ * @param kind - Which piece of state.
129
+ * @param create - Builds it the first time any copy asks.
130
+ * @returns The page's instance.
131
+ */
132
+ export declare function sharedStore<T>(kind: string, create: () => T): T;
133
+ /**
134
+ * A map from name to implementation (a class, a factory) that this copy of graphty-element reads
135
+ * first and every other copy on the page reads after its own.
136
+ *
137
+ * THIS COPY'S OWN ENTRIES WIN, because they include its built-ins: a second copy's box shape is
138
+ * built with the second copy's Babylon.js, and its degree algorithm extends the second copy's base
139
+ * class, so handing those to this copy's element would mix two renderers in one scene. What a copy
140
+ * does not have itself -- a plugin registered through another copy -- comes from the page's
141
+ * shared map.
142
+ */
143
+ export declare class SharedImplementationMap<V> {
144
+ #private;
145
+ /**
146
+ * Join the page's shared map for this extension point, creating it if this copy is first.
147
+ * @param kind - Which extension point, which names the page's shared map.
148
+ */
149
+ constructor(kind: string);
150
+ /**
151
+ * File an implementation for this copy and for every other copy on the page.
152
+ * @param name - The name it is filed under.
153
+ * @param value - The implementation.
154
+ */
155
+ set(name: string, value: V): void;
156
+ /**
157
+ * This copy's implementation, or else the one another copy filed.
158
+ * @param name - The name.
159
+ * @returns The implementation, or undefined when no copy filed one.
160
+ */
161
+ get(name: string): V | undefined;
162
+ /**
163
+ * Whether THIS copy filed the name, which is what a built-in duplicate check asks: another
164
+ * copy registering its own built-ins is not a collision.
165
+ * @param name - The name.
166
+ * @returns True when this copy filed it.
167
+ */
168
+ hasOwn(name: string): boolean;
169
+ /**
170
+ * Every name any copy filed.
171
+ * @returns The names, this copy's first.
172
+ */
173
+ keys(): IterableIterator<string>;
174
+ }
113
175
  /**
114
176
  * Build a registry for one extension point.
115
177
  * @param spec - How this kind of extension is read.
@@ -61,6 +61,13 @@ export interface RegisteredAlgorithm {
61
61
  * plugin still supplies real arithmetic for the estimator to use.
62
62
  */
63
63
  readonly cost?: (n: number, m: number) => number;
64
+ /**
65
+ * A cost model in work units of the descriptor's cost class, read from `static costUnits`.
66
+ *
67
+ * The estimator divides it by this device's rate for that class, so calibration scales it.
68
+ * Wins over {@link cost} when both are present.
69
+ */
70
+ readonly costUnits?: (n: number, m: number) => number;
64
71
  /**
65
72
  * The plugin's own version, read from `static version`.
66
73
  *
@@ -53,10 +53,29 @@ export type Query = string;
53
53
  /**
54
54
  * The built-in algorithms. The list is also available at runtime so a consumer can enumerate
55
55
  * the built-in set without a catalogue instance.
56
+ *
57
+ * Two of these names are deprecated: `all-paths` and `clustering-coefficient` are reserved but
58
+ * not implemented, and starting either fails with `E_UNSUPPORTED`. See
59
+ * {@link DEPRECATED_ALGORITHMS}.
56
60
  */
57
61
  export declare const KNOWN_ALGORITHMS: readonly ["degree", "betweenness", "closeness", "pagerank", "eigenvector", "katz", "hits", "louvain", "leiden", "label-propagation", "components", "shortest-path", "all-pairs-distance", "all-paths", "max-flow", "min-cut", "k-core", "clustering-coefficient", "girvan-newman", "bfs", "dfs", "kruskal", "prim", "bipartite-matching", "link-prediction"];
58
- /** One of the built-in algorithms. */
62
+ /**
63
+ * One of the built-in algorithms. `all-paths` and `clustering-coefficient` are deprecated and do
64
+ * not run; see {@link DEPRECATED_ALGORITHMS}.
65
+ */
59
66
  export type KnownAlgorithm = (typeof KNOWN_ALGORITHMS)[number];
67
+ /**
68
+ * The built-in algorithm names the element reserves but does not run.
69
+ *
70
+ * Nothing implements these two yet. The names stay in {@link KNOWN_ALGORITHMS}, so no plugin can
71
+ * claim them and no code that names them stops compiling, but starting one fails with
72
+ * `E_UNSUPPORTED` rather than `E_UNKNOWN_ALGORITHM`. Each is removed at the next major release
73
+ * unless it is implemented first: `all-paths` is tracked by issue #329 and
74
+ * `clustering-coefficient` by issue #330.
75
+ */
76
+ export declare const DEPRECATED_ALGORITHMS: readonly ["all-paths", "clustering-coefficient"];
77
+ /** A built-in algorithm name the element reserves but does not run, and will remove. */
78
+ export type DeprecatedAlgorithm = (typeof DEPRECATED_ALGORITHMS)[number];
60
79
  /**
61
80
  * An algorithm key. The built-in names keep autocomplete alive; the string arm accepts a
62
81
  * plugin's name.
@@ -446,13 +465,23 @@ export interface ScaleDescriptor {
446
465
  domainKind: "numeric" | "categorical" | "boolean";
447
466
  options: readonly OptionDescriptor[];
448
467
  }
449
- /** One named style document, offered as a whole look. */
468
+ /**
469
+ * One named style document, offered as a whole look.
470
+ *
471
+ * Nothing produces one yet: it is returned only by the deprecated `CatalogApi.themes()`, and goes
472
+ * with it at the next major release unless that is implemented first (issue #331).
473
+ */
450
474
  export interface ThemeDescriptor {
451
475
  name: string;
452
476
  plainName: string;
453
477
  document: StyleDocument;
454
478
  }
455
- /** One function the expression grammar accepts. */
479
+ /**
480
+ * One function the expression grammar accepts.
481
+ *
482
+ * Nothing produces one yet: it is returned only by the deprecated `CatalogApi.functions()`, and
483
+ * goes with it at the next major release unless that is implemented first (issue #332).
484
+ */
456
485
  export interface FunctionDescriptor {
457
486
  name: string;
458
487
  /** The smallest and largest argument count accepted. */
@@ -524,7 +553,12 @@ export type Scope = "visible" | "graph" | "selection" | "largest-component" | {
524
553
  } | {
525
554
  nodes: readonly NodeId[];
526
555
  };
527
- /** The catalogue: everything the element can offer, as data. */
556
+ /**
557
+ * The catalogue: everything the element can offer, as data.
558
+ *
559
+ * `session.catalog` implements every method here except the six named in
560
+ * {@link DeprecatedCatalogMethod}, which nothing implements yet.
561
+ */
528
562
  export interface CatalogApi {
529
563
  algorithms(): readonly AlgorithmDescriptor[];
530
564
  layouts(): readonly LayoutDescriptor[];
@@ -533,24 +567,61 @@ export interface CatalogApi {
533
567
  cameras(): readonly CameraDescriptor[];
534
568
  logSinks(): readonly LogSinkDescriptor[];
535
569
  scales(): readonly ScaleDescriptor[];
570
+ /**
571
+ * @deprecated Not implemented. Removed at the next major release unless it is implemented
572
+ * first (issue #331).
573
+ */
536
574
  themes(): readonly ThemeDescriptor[];
575
+ /**
576
+ * @deprecated Not implemented. Removed at the next major release unless it is implemented
577
+ * first (issue #332).
578
+ */
537
579
  functions(): readonly FunctionDescriptor[];
580
+ /**
581
+ * @deprecated Not implemented. Removed at the next major release unless it is implemented
582
+ * first (issue #333).
583
+ */
538
584
  timeAttributes(): readonly AttributeDescriptor[];
539
585
  metrics(): readonly MetricAvailability[];
540
- /** The metrics that can run on this graph. A runtime query, not a static list. */
586
+ /**
587
+ * The metrics that can run on this graph. A runtime query, not a static list.
588
+ * @deprecated Not implemented; `metrics()` carries `available` and `reason` for the same
589
+ * question. Removed at the next major release unless it is implemented first (issue #334).
590
+ */
541
591
  applicable(): readonly MetricAvailability[];
592
+ /**
593
+ * @deprecated Not implemented. Removed at the next major release unless it is implemented
594
+ * first (issue #335).
595
+ */
542
596
  validate(query: Query, o?: {
543
597
  kind?: "selector" | "filter" | "formula";
544
598
  }): QueryValidation;
545
- /** The options for one algorithm or layout, with data-dependent bounds resolved. */
599
+ /**
600
+ * The options for one algorithm or layout, with data-dependent bounds resolved.
601
+ * @deprecated Not implemented; `algorithms()` and `layouts()` carry the static option
602
+ * descriptors. Removed at the next major release unless it is implemented first (issue #336).
603
+ */
546
604
  optionsFor(key: AlgorithmKey | LayoutId, scope?: Scope): Promise<readonly OptionDescriptor[]>;
547
605
  }
606
+ /**
607
+ * The {@link CatalogApi} methods nothing implements yet, which `session.catalog` leaves out.
608
+ *
609
+ * Implementing one means deleting its name here: `SessionCatalogApi` is derived from this list,
610
+ * so the two cannot drift apart.
611
+ */
612
+ export type DeprecatedCatalogMethod = "themes" | "functions" | "timeAttributes" | "applicable" | "validate" | "optionsFor";
548
613
  /**
549
614
  * Tell whether a value is one of the option types.
550
615
  * @param value - The value to test.
551
616
  * @returns True when the value is a member of OPTION_TYPES.
552
617
  */
553
618
  export declare function isOptionType(value: unknown): value is OptionType;
619
+ /**
620
+ * Tell whether an algorithm key names a built-in the element reserves but does not run.
621
+ * @param key - The algorithm key.
622
+ * @returns True when the key is a member of DEPRECATED_ALGORITHMS.
623
+ */
624
+ export declare function isDeprecatedAlgorithm(key: string): key is DeprecatedAlgorithm;
554
625
  /**
555
626
  * Tell whether a value is one of the cost classes.
556
627
  * @param value - The value to test.
@@ -1,4 +1,39 @@
1
1
  import { z } from "zod/v4";
2
+ /**
3
+ * Every arrow the edge renderer can draw at the head or tail of an edge, in schema order.
4
+ * Read `EdgeArrowTypes.options` for the list, e.g. to build a picker.
5
+ */
6
+ export declare const EdgeArrowTypes: z.ZodEnum<{
7
+ none: "none";
8
+ dot: "dot";
9
+ normal: "normal";
10
+ inverted: "inverted";
11
+ "sphere-dot": "sphere-dot";
12
+ "open-dot": "open-dot";
13
+ tee: "tee";
14
+ "open-normal": "open-normal";
15
+ diamond: "diamond";
16
+ "open-diamond": "open-diamond";
17
+ crow: "crow";
18
+ box: "box";
19
+ "half-open": "half-open";
20
+ vee: "vee";
21
+ }>;
22
+ /**
23
+ * Every line pattern the edge renderer can draw, in schema order.
24
+ * Read `EdgeLineTypes.options` for the list, e.g. to build a picker.
25
+ */
26
+ export declare const EdgeLineTypes: z.ZodEnum<{
27
+ solid: "solid";
28
+ dot: "dot";
29
+ diamond: "diamond";
30
+ box: "box";
31
+ star: "star";
32
+ dash: "dash";
33
+ "dash-dot": "dash-dot";
34
+ sinewave: "sinewave";
35
+ zigzag: "zigzag";
36
+ }>;
2
37
  /**
3
38
  * Everything a style layer can say about how one edge is drawn.
4
39
  *
@@ -1,7 +1,7 @@
1
1
  export type { AdHocData, ImageData } from "./common";
2
2
  export { colorToHex } from "./common";
3
3
  export type { EdgeStyleConfig } from "./EdgeStyle";
4
- export { defaultEdgeStyle, EdgeStyle } from "./EdgeStyle";
4
+ export { defaultEdgeStyle, EdgeArrowTypes, EdgeLineTypes, EdgeStyle } from "./EdgeStyle";
5
5
  export type { FetchEdgesFn, FetchNodesFn, GraphBehaviorConfig } from "./GraphBehavior";
6
6
  export type { NodeIdType } from "./GraphBehavior";
7
7
  export { GraphBehaviorOpts } from "./GraphBehavior";
@@ -1,5 +1,5 @@
1
1
  import * as z4 from "zod/v4/core";
2
- import type { RegisterOptions } from "../catalog/pluginRegistry";
2
+ import { type RegisterOptions } from "../catalog/pluginRegistry";
3
3
  import type { FormatDescriptor } from "../catalog/types";
4
4
  import { AdHocData } from "../config";
5
5
  import { ErrorAggregator } from "./ErrorAggregator.js";