@graphty/graphty-element 2.2.3 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/ai.js +3 -3
  2. package/dist/catalog.js +5 -5
  3. package/dist/chunks/{AiManager-DC3JWPeM.js → AiManager-Bd_r1Hei.js} +7 -2
  4. package/dist/chunks/{DataSource-DEg3igzS.js → DataSource-Dag8H5US.js} +3 -3
  5. package/dist/chunks/{GraphSession-DDrQWABb.js → GraphSession-mfo75G0q.js} +3308 -3284
  6. package/dist/chunks/{VoiceInputAdapter-BueUG885.js → VoiceInputAdapter-DszYl6Ha.js} +442 -424
  7. package/dist/chunks/{XRPivotCameraController-DENGN2uV.js → XRPivotCameraController-BdoMFcON.js} +1 -1
  8. package/dist/chunks/{capability-check-BC3Qn6fS.js → capability-check-CdoqmrF0.js} +1 -1
  9. package/dist/chunks/{detect-CqN0mC6u.js → detect-LAOOaxY_.js} +1 -1
  10. package/dist/chunks/{format-detection-C8vbCSlS.js → format-detection-a0bD5PIB.js} +1 -1
  11. package/dist/chunks/{index-COss7yTG.js → index-BY1gnMiZ.js} +1001 -848
  12. package/dist/chunks/{paletteRegistry-NrWOKT-A.js → paletteRegistry-YSpPryiT.js} +13 -13
  13. package/dist/chunks/{scales-DKsbJE2L.js → scales-DOpwlWuz.js} +3 -3
  14. package/dist/extend.js +3 -3
  15. package/dist/graphty-catalog.json +1 -1
  16. package/dist/graphty.bundle.js +23040 -22842
  17. package/dist/graphty.js +3 -3
  18. package/dist/session.js +37 -52
  19. package/dist/src/Graph.d.ts +3 -0
  20. package/dist/src/acceleration/AccelerationController.d.ts +11 -0
  21. package/dist/src/ai/input/VoiceInputAdapter.d.ts +18 -0
  22. package/dist/src/data/ingest.d.ts +13 -0
  23. package/dist/src/managers/DataManager.d.ts +51 -0
  24. package/dist/src/managers/RenderManager.d.ts +21 -0
  25. package/dist/src/managers/UpdateManager.d.ts +38 -23
  26. package/dist/src/session/limits.d.ts +24 -5
  27. package/package.json +5 -5
package/dist/graphty.js CHANGED
@@ -1,6 +1,6 @@
1
- import { A as e, a as E, b as r, D as O, E as o, d as S, G as R, e as _, I as t, L, N as C, O as A, R as N, S as n, f as I, g as T, h as g, U as i, i as l } from "./chunks/index-COss7yTG.js";
2
- import { a as p, S as G, b as f } from "./chunks/paletteRegistry-NrWOKT-A.js";
3
- import { D as y, E as D } from "./chunks/DataSource-DEg3igzS.js";
1
+ import { A as e, a as E, b as r, D as O, E as o, d as S, G as R, e as _, I as t, L, N as C, O as A, R as N, S as n, f as I, g as T, h as g, U as i, i as l } from "./chunks/index-BY1gnMiZ.js";
2
+ import { a as p, S as G, b as f } from "./chunks/paletteRegistry-YSpPryiT.js";
3
+ import { D as y, E as D } from "./chunks/DataSource-Dag8H5US.js";
4
4
  import { k as P, l as U, g as c, h as m, A as u, C as x, i as B } from "./chunks/Algorithm-RQ629NLb.js";
5
5
  import { e as H, E as b, N as Y, a as F, P as W, R as k, S as K, d as v, b as w, c as Q } from "./chunks/NodeStyle-DKj7HjMJ.js";
6
6
  import { A as j, a as q, G as z, i as J, b as Z } from "./chunks/GraphtyError-BwcnblTH.js";
package/dist/session.js CHANGED
@@ -1,26 +1,11 @@
1
- import { R as A, e as R } from "./chunks/types-B7bX5c0K.js";
2
- import { A as _, a as C, G as g, i as m, b as L } from "./chunks/GraphtyError-BwcnblTH.js";
3
- import { g as U, h as I, i as N } from "./chunks/Algorithm-RQ629NLb.js";
4
- import { D as h } from "./chunks/GraphSession-DDrQWABb.js";
5
- import { a as y, b as D, c as w, Q as b, R as v, d as G, e as x, S as z, T as F, f as M, i as j, g as H, h as Y, j as k } from "./chunks/GraphSession-DDrQWABb.js";
6
- import { R as Q, w as X, x as B, y as J, z as K, A as V } from "./chunks/paletteRegistry-NrWOKT-A.js";
7
- import { a as n } from "./chunks/scales-DKsbJE2L.js";
8
- const u = Object.freeze({
9
- /** Above this node count the element draws less visual detail. A shipped default, not measured. */
10
- largeGraphThreshold: 1e4,
11
- /** The most nodes this machine is expected to draw at an interactive frame rate. A shipped default, not measured. */
12
- renderCeiling: 2e5,
13
- /** The most elements one selection will hold before it refuses to grow. A shipped default, not measured. */
14
- selectionCap: h,
15
- /** The most edges drawn at once; beyond it edges are hidden until the view narrows. A shipped default, not measured. */
16
- edgesDrawn: 5e5,
17
- /**
18
- * Above this NODE COUNT an approximable algorithm is approximated rather than computed
19
- * exactly. A shipped default, not measured. Not to be confused with the cost gate's
20
- * `exactComputationSeconds`, which is a duration and answers a different question.
21
- */
22
- approximateAboveNodes: 2e3
23
- }), l = [
1
+ import { R, e as _ } from "./chunks/types-B7bX5c0K.js";
2
+ import { A as c, a as C, G as p, i as m, b as g } from "./chunks/GraphtyError-BwcnblTH.js";
3
+ import { g as O, h as U, i as I } from "./chunks/Algorithm-RQ629NLb.js";
4
+ import { D as h } from "./chunks/GraphSession-mfo75G0q.js";
5
+ import { a as N, b as y, c as D, d as v, Q as w, R as G, e as b, f as x, S as F, T as M, g as z, i as k, h as H, j as Y, k as j } from "./chunks/GraphSession-mfo75G0q.js";
6
+ import { R as Q, t as X, u as B, v as J, w as K, x as V } from "./chunks/paletteRegistry-YSpPryiT.js";
7
+ import { L as s } from "./chunks/scales-DOpwlWuz.js";
8
+ const u = [
24
9
  {
25
10
  id: "fixed",
26
11
  fires: ({ statistics: e, placedNodes: a }) => e.nodeCount > 0 && a >= e.nodeCount,
@@ -42,60 +27,60 @@ const u = Object.freeze({
42
27
  reason: "Connected nodes are pulled together and unconnected ones pushed apart, which is the arrangement that shows this graph's structure without being told anything about it."
43
28
  }
44
29
  ];
45
- function s(e, a) {
30
+ function n(e, a) {
46
31
  return e.structuralInputs.length > 0 ? !1 : e.sizeRating === "any" || a <= e.sizeRating;
47
32
  }
48
- function f(e, a = {}) {
33
+ function T(e, a = {}) {
49
34
  const i = {
50
35
  statistics: e,
51
36
  placedNodes: a.placedNodes ?? 0,
52
- largeGraphThreshold: a.largeGraphThreshold ?? u.largeGraphThreshold
37
+ largeGraphThreshold: a.largeGraphThreshold ?? h.largeGraphThreshold
53
38
  };
54
- for (const r of l) {
39
+ for (const r of u) {
55
40
  if (!r.fires(i))
56
41
  continue;
57
- const o = n.find((d) => d.id === r.id);
58
- if (o !== void 0 && s(o, e.nodeCount))
42
+ const o = s.find((d) => d.id === r.id);
43
+ if (o !== void 0 && n(o, e.nodeCount))
59
44
  return Object.freeze({ layout: o, reason: r.reason });
60
45
  }
61
- const t = n.find((r) => s(r, e.nodeCount));
46
+ const t = s.find((r) => n(r, e.nodeCount));
62
47
  return t === void 0 ? void 0 : Object.freeze({
63
48
  layout: t,
64
49
  reason: "The first arrangement this element can place a graph of this size with."
65
50
  });
66
51
  }
67
52
  export {
68
- _ as ACCELERATION_ERROR_CODES,
69
- U as ACCELERATION_POLICIES,
70
- I as ACCELERATION_POLICY_DEFAULT,
71
- y as DEFAULT_COST_GATE_LIMITS,
72
- D as DEFAULT_EXACT_COMPUTATION_CAP_SECONDS,
73
- u as DEFAULT_LIMITS,
74
- w as DEFAULT_SCOPE_SAMPLE,
75
- h as DEFAULT_SELECTION_CAP,
53
+ c as ACCELERATION_ERROR_CODES,
54
+ O as ACCELERATION_POLICIES,
55
+ U as ACCELERATION_POLICY_DEFAULT,
56
+ N as DEFAULT_COST_GATE_LIMITS,
57
+ y as DEFAULT_EXACT_COMPUTATION_CAP_SECONDS,
58
+ h as DEFAULT_LIMITS,
59
+ D as DEFAULT_SCOPE_SAMPLE,
60
+ v as DEFAULT_SELECTION_CAP,
76
61
  C as GRAPHTY_ERROR_CODES,
77
- g as GraphtyError,
78
- b as QUEUE_POLICIES,
62
+ p as GraphtyError,
63
+ w as QUEUE_POLICIES,
79
64
  Q as RESULT_FIELD_NAMES,
80
65
  X as RESULT_ROOT,
81
- A as RESULT_SHAPES,
66
+ R as RESULT_SHAPES,
82
67
  B as RESULT_SHAPE_CONTRACTS,
83
- v as RUN_ID_PATTERN,
84
- G as RUN_PHASES,
68
+ G as RUN_ID_PATTERN,
69
+ b as RUN_PHASES,
85
70
  x as RUN_STATUSES,
86
- z as SET_OPS,
87
- F as TERMINAL_RUN_STATUSES,
88
- M as createGraphSession,
71
+ F as SET_OPS,
72
+ M as TERMINAL_RUN_STATUSES,
73
+ z as createGraphSession,
89
74
  J as defaultReading,
90
- N as isAccelerationPolicy,
91
- j as isAlgorithmRunCommand,
75
+ I as isAccelerationPolicy,
76
+ k as isAlgorithmRunCommand,
92
77
  m as isGraphtyError,
93
- L as isGraphtyErrorCode,
94
- R as isResultShape,
78
+ g as isGraphtyErrorCode,
79
+ _ as isResultShape,
95
80
  H as isRunId,
96
81
  Y as isRunStatus,
97
- k as isTerminalRunStatus,
82
+ j as isTerminalRunStatus,
98
83
  K as quotePath,
99
- f as recommendLayout,
84
+ T as recommendLayout,
100
85
  V as resultPath
101
86
  };
@@ -350,6 +350,9 @@ export declare class Graph implements GraphContext {
350
350
  * present each time a host re-assigned the property -- and a host that re-renders on state
351
351
  * change re-assigns it constantly. The old drop guard was silently doing this job; deleting the
352
352
  * guard without this would have turned "assign the same edges twice" into "hold them twice".
353
+ *
354
+ * A set past the render ceiling is refused with `E_TOO_LARGE` before an edge is removed, so
355
+ * the graph keeps the edges it had.
353
356
  * @param edges - the edges the graph should hold afterwards
354
357
  * @param options - The endpoint expressions, the repeat policy, and queue ordering
355
358
  * @returns Promise that resolves once the graph holds exactly these edges
@@ -92,6 +92,17 @@ interface AccelerationControllerOptions {
92
92
  readonly recoverOnDeviceLoss?: boolean;
93
93
  /** How many consecutive recovery attempts to make before giving up. Defaults to 3. */
94
94
  readonly maxRecoveryAttempts?: number;
95
+ /**
96
+ * Opens a span around every call-shaped accelerated run, returning what closes it.
97
+ *
98
+ * The element hands in its render manager's `holdFrames`: a GPU readback is delivered as a
99
+ * task and waits behind whatever frame the host is drawing, so a run that is a few
100
+ * milliseconds on the device came back a frame or two later through the element (issue
101
+ * #390). The span covers exactly the accelerated call -- not the decision before it, and not
102
+ * the CPU path -- and is closed however the call ends. A simulation, which steps every frame
103
+ * through {@link AccelerationController.beginWork}, never opens one: it needs the frames.
104
+ */
105
+ readonly whileRunning?: () => () => void;
95
106
  }
96
107
  /**
97
108
  * Owns hardware acceleration for one session: whether there is any, what it is, and whether a
@@ -47,6 +47,10 @@ declare global {
47
47
  }
48
48
  /** Callback for voice input start/error events */
49
49
  export type VoiceStartCallback = (started: boolean, error?: string) => void;
50
+ /** Why a voice session ended: stopped by the caller, ended by the recogniser, or failed. */
51
+ type VoiceEndReason = "user" | "timeout" | "error";
52
+ /** Callback for a voice session starting (`active` true) or ending (`active` false, with a reason). */
53
+ type VoiceActiveCallback = (active: boolean, reason?: VoiceEndReason) => void;
50
54
  /**
51
55
  * Voice input adapter using the Web Speech API.
52
56
  * Provides voice-to-text functionality with support for interim and final results.
@@ -57,6 +61,8 @@ export declare class VoiceInputAdapter implements InputAdapter {
57
61
  private _isActive;
58
62
  private callbacks;
59
63
  private startCallbacks;
64
+ private activeCallbacks;
65
+ private failed;
60
66
  private SpeechRecognitionCtor;
61
67
  /**
62
68
  * Creates a new VoiceInputAdapter instance.
@@ -92,10 +98,22 @@ export declare class VoiceInputAdapter implements InputAdapter {
92
98
  * @param callback - Function called with (started: boolean, error?: string)
93
99
  */
94
100
  onStart(callback: VoiceStartCallback): void;
101
+ /**
102
+ * Register a callback for every voice session starting and ending.
103
+ * Unlike {@link VoiceInputAdapter.onStart} it stays registered across sessions.
104
+ * @param callback - Called with (true) on start and (false, reason) on end
105
+ */
106
+ onActiveChange(callback: VoiceActiveCallback): void;
95
107
  /**
96
108
  * Clean up resources and remove all callbacks.
97
109
  */
98
110
  dispose(): void;
111
+ /**
112
+ * Notify active-change callbacks.
113
+ * @param active - Whether a session is now running
114
+ * @param reason - Why it ended, when it did
115
+ */
116
+ private notifyActive;
99
117
  /**
100
118
  * Notify start callbacks and clear them (one-shot).
101
119
  * @param started - Whether voice input started successfully
@@ -1,4 +1,17 @@
1
1
  import type { GraphStore } from "./GraphStore";
2
+ /**
3
+ * Whether a value may be used as a graph-format node id.
4
+ *
5
+ * graph-format accepts a string or a FINITE number and throws `E_INVALID_ID` for anything else
6
+ * (`graph-format/src/ids/node-id-map.ts`). The element is looser: a node id is whatever the
7
+ * configured JMESPath expression returns, which is `null` for a record that does not carry the
8
+ * key at all, and the element has always let such a record through and rendered it. So the id is
9
+ * CHECKED here rather than thrown on -- an unusable id leaves the render object exactly as it is
10
+ * today and keeps it out of the store, which is the one place the id has to be real.
11
+ * @param id - the extracted id
12
+ * @returns true when graph-format will accept it
13
+ */
14
+ export declare function isStorableId(id: unknown): id is string | number;
2
15
  /**
3
16
  * Push one node record into the element's builder and seed its import position.
4
17
  *
@@ -423,6 +423,36 @@ export declare class DataManager implements Manager {
423
423
  * @returns the edges, oldest first; empty when there are none
424
424
  */
425
425
  getEdgesBetween(srcNodeId: NodeIdType, dstNodeId: NodeIdType): readonly Edge[];
426
+ /**
427
+ * Replace every built edge with a new set, or leave the graph exactly as it was.
428
+ *
429
+ * The ceiling is decided BEFORE anything is removed. Removing first and letting `addEdges`
430
+ * refuse would leave a host that assigned too many edges with its old edges gone and none of
431
+ * the new ones held, which is neither the graph it had nor the one it asked for. The new
432
+ * batch is counted against an emptied graph, since the old edges are what it replaces; a
433
+ * pending edge, whose endpoints have not arrived, survives the replace as it always has.
434
+ * @param edges - the edges the graph should hold afterwards
435
+ * @param options - the endpoint expressions and the repeat policy for this call
436
+ * @throws A `GraphtyError` with `E_TOO_LARGE` when the new set is past the ceiling, and
437
+ * whatever `addEdges` throws.
438
+ */
439
+ setEdges(edges: Record<string | number, unknown>[], options?: AddEdgesOptions): void;
440
+ /**
441
+ * How many edges a batch would add, by the same tests the ingest loop applies.
442
+ *
443
+ * A record whose endpoint ids graph-format will not store adds nothing (the loop rejects it).
444
+ * Under the `keep` policy every other record is an edge. Under a folding policy a record that
445
+ * repeats an edge the graph holds, or a record earlier in the same batch, folds into it and
446
+ * adds nothing; a repeat is named the way `knownEdgeFor` names it, by record id when one is
447
+ * configured and stored, else by the ordered endpoint pair.
448
+ * @param edges - the batch
449
+ * @param endpoints - the batch's endpoint expressions
450
+ * @param policy - the repeat policy the batch is under
451
+ * @param replacing - true when every held edge is about to be removed, so none of them can be
452
+ * repeated
453
+ * @returns the number of edges the batch would add
454
+ */
455
+ private edgesAdded;
426
456
  /**
427
457
  * Removes an edge from the graph
428
458
  * @param edgeId - Edge identifier to remove
@@ -467,6 +497,27 @@ export declare class DataManager implements Manager {
467
497
  * @returns the node and edge counts the graph holds
468
498
  */
469
499
  private heldCounts;
500
+ /**
501
+ * Refuse to grow past what the renderer can draw, instead of freezing the tab.
502
+ *
503
+ * WHY A REFUSAL AND NOT A DEGRADED DRAW. The design says that above the render ceiling the
504
+ * element draws a smaller render set, and above `edgesDrawn` it hides edges until the view
505
+ * narrows. Neither exists yet. What exists is a renderer that, past these counts, exhausts
506
+ * the renderer process and produces no further frame -- measured for issue #405 at 18,000
507
+ * nodes / 180,000 edges on an RTX 4070 SUPER, where the renderer process reached 4.7 GB and
508
+ * died while 17,000 / 170,000 loaded in 17 s. Until the degraded draw lands, the honest
509
+ * behaviour at the ceiling is a coded error the consumer can show, so `DEFAULT_LIMITS` is
510
+ * the number the element enforces rather than a number it merely publishes.
511
+ *
512
+ * `E_TOO_LARGE` is the code because the ceiling is a hard limit of this renderer, and the
513
+ * caller's remedy is the one that code names: load a subset.
514
+ * @param of - what is being counted
515
+ * @param held - how many the graph holds already
516
+ * @param adding - how many this call would add
517
+ * @param limit - the most the renderer can draw
518
+ * @throws A `GraphtyError` with `E_TOO_LARGE` when `held + adding` is past the limit
519
+ */
520
+ private refuseAboveCeiling;
470
521
  /**
471
522
  * Clear all data
472
523
  */
@@ -22,6 +22,8 @@ export declare class RenderManager implements Manager {
22
22
  graphRoot: TransformNode;
23
23
  private renderLoopActive;
24
24
  private updateCallback?;
25
+ /** How many callers currently hold the frames back; see {@link holdFrames}. */
26
+ private frameHolds;
25
27
  private resizeHandler;
26
28
  /**
27
29
  * Stands in for Babylon's own pointer handling, which calls preventDefault and then
@@ -54,6 +56,25 @@ export declare class RenderManager implements Manager {
54
56
  * Stop the render loop
55
57
  */
56
58
  stopRenderLoop(): void;
59
+ /**
60
+ * Keeps the render loop from drawing until the returned function is called.
61
+ *
62
+ * A FRAME IS WHAT A GPU READBACK WAITS BEHIND. Drawing a scene of thousands of meshes keeps
63
+ * the main thread for tens to hundreds of milliseconds, and a promise the GPU resolves --
64
+ * the mapped buffer at the end of a traversal, the frontier count between its levels -- is
65
+ * delivered as a task, which cannot run until the frame that was drawing has finished. A
66
+ * breadth-first search that costs 7 ms on the device came back after 225 ms through the
67
+ * element at 1,000 nodes and after 8.6 s at 10,000, two frames per readback (issue #390).
68
+ * Measured apart, the CPU update of a frame is 2.5 ms and is not what the readback waits on;
69
+ * the draw is 48 ms at 1,000 nodes and is.
70
+ *
71
+ * So a call-shaped accelerated run holds the frames for as long as it is on the device, and
72
+ * the picture stands still for those milliseconds instead of the run stretching to seconds.
73
+ * Holds nest: the frames resume when the last holder releases, and releasing twice is a
74
+ * no-op, so a `finally` cannot over-release.
75
+ * @returns Releases this hold.
76
+ */
77
+ holdFrames(): () => void;
57
78
  /**
58
79
  * Update the background color
59
80
  * @param color - Hex color string (e.g., "#FFFFFF")
@@ -1,5 +1,8 @@
1
+ import type { Vector3 } from "@babylonjs/core";
1
2
  import type { CameraManager } from "../cameras/CameraManager";
2
3
  import type { EdgeId, NodeId } from "../catalog/types";
4
+ import { Edge } from "../Edge";
5
+ import type { Node } from "../Node";
3
6
  import type { ElementMask } from "../session/scope/index";
4
7
  import type { DataManager } from "./DataManager";
5
8
  import type { EventManager } from "./EventManager";
@@ -7,6 +10,34 @@ import type { GraphContext } from "./GraphContext";
7
10
  import type { Manager } from "./interfaces";
8
11
  import type { LayoutManager } from "./LayoutManager";
9
12
  import type { StatsManager } from "./StatsManager";
13
+ /** The corners of a box in world space. */
14
+ interface FramingBox {
15
+ min: Vector3;
16
+ max: Vector3;
17
+ }
18
+ /**
19
+ * The box the nodes alone occupy: every visible node, where it is in world space, out to its size.
20
+ * This is what the 2D/3D view-mode switch frames. Zoom-to-fit frames {@link framingBox}, which
21
+ * grows this one by the labels.
22
+ *
23
+ * NO MARGIN on top. A fixed one is paid by every graph, labelled or not, and on a small graph it is
24
+ * most of the picture: one world unit on each side moved a two-node graph's camera from 6.0 to 8.6
25
+ * units out. The cameras pad the fit themselves.
26
+ * @param nodes - The nodes to frame; hidden ones are skipped.
27
+ * @returns The corners, or undefined when no node is visible.
28
+ */
29
+ export declare function nodeFramingBox(nodes: Iterable<Node>): FramingBox | undefined;
30
+ /**
31
+ * The box zoom-to-fit frames: {@link nodeFramingBox} grown by every label on a visible node and
32
+ * every label and arrow caption on a visible edge, so no label is cut off by the edge of the
33
+ * viewport. A label's size depends on its text and font, which is why framing runs on data loads
34
+ * and layout changes and NOT on label edits: see `Graph`, which asks for a framing only from
35
+ * those.
36
+ * @param nodes - The nodes to frame; hidden ones and their labels are skipped.
37
+ * @param edges - The edges whose labels to frame; hidden ones are skipped.
38
+ * @returns The corners, or undefined when no node is visible.
39
+ */
40
+ export declare function framingBox(nodes: Iterable<Node>, edges: Iterable<Edge>): FramingBox | undefined;
10
41
  /**
11
42
  * One set of elements the renderer honours, as the session holds it.
12
43
  *
@@ -366,32 +397,17 @@ export declare class UpdateManager implements Manager {
366
397
  */
367
398
  private largestMove;
368
399
  /**
369
- * Update all nodes, and measure the graph when the camera is about to be framed on it.
370
- * @param measure - Whether this frame's bounding box will be used. False skips the
371
- * measurement entirely, which is most frames.
372
- * @returns Object containing minimum and maximum bounding box vectors
400
+ * Update all nodes.
373
401
  */
374
402
  private updateNodes;
375
403
  /**
376
- * Update bounding box for a single axis
377
- * @param pos - Position vector
378
- * @param min - Minimum bounds vector
379
- * @param max - Maximum bounds vector
380
- * @param size - Node size
381
- * @param axis - Axis to update (x, y, or z)
382
- */
383
- private updateBoundingBoxAxis;
384
- /**
385
- * Expand bounding box to include a label mesh
386
- * @param labelMesh - The label mesh to include
387
- * @param min - Minimum bounds vector
388
- * @param max - Maximum bounds vector
404
+ * Measure the graph the camera is about to be framed on. Taken AFTER the nodes and edges have
405
+ * updated, so the labels are where this frame's positions put them.
406
+ * @returns The box, or undefined when nothing is visible.
389
407
  */
390
- private expandBoundingBoxForLabel;
408
+ private measure;
391
409
  /**
392
- * Update all edges and expand bounding box for edge labels
393
- * @param boundingBoxMin - Minimum bounds (optional)
394
- * @param boundingBoxMax - Maximum bounds (optional)
410
+ * Update all edges.
395
411
  */
396
412
  private updateEdges;
397
413
  /**
@@ -405,8 +421,7 @@ export declare class UpdateManager implements Manager {
405
421
  private willZoomToFit;
406
422
  /**
407
423
  * Frame the camera on a box {@link UpdateManager.willZoomToFit} has already approved.
408
- * @param boundingBoxMin - Minimum bounds (optional)
409
- * @param boundingBoxMax - Maximum bounds (optional)
424
+ * @param box - The box to frame, or undefined when there is nothing to frame.
410
425
  */
411
426
  private applyZoomToFit;
412
427
  /**
@@ -7,11 +7,30 @@
7
7
  * large-graph threshold of its own, ten times the element's, with nothing between the two to
8
8
  * notice they disagreed.
9
9
  *
10
- * So these are published as what they are: SHIPPED DEFAULTS, not measurements. Every one of them
11
- * is the fallback the element API design's Performance defaults table names for the case where the
12
- * probe cannot run, and none of them describes the machine this code is running on. When
13
- * `calibrate()` lands it replaces them and `Capabilities.calibration.basis` turns from
14
- * `"defaults"` into `"probe"`, which is how a consumer can tell which kind of number it is holding.
10
+ * So these are published as what they are: SHIPPED DEFAULTS, not measurements of the machine this
11
+ * code is running on. When `calibrate()` lands it replaces them and `Capabilities.calibration.basis`
12
+ * turns from `"defaults"` into `"probe"`, which is how a consumer can tell which kind of number it
13
+ * is holding.
14
+ *
15
+ * TWO OF THEM ARE ENFORCED, AND WERE MEASURED ONCE. `renderCeiling` and `edgesDrawn` used to be
16
+ * the design table's figures, 200,000 nodes and 500,000 edges, and nothing checked them. The
17
+ * renderer could not reach either: at 18,000 nodes / 180,000 edges the page stopped producing
18
+ * frames (issue #405). The measurement behind the numbers below, taken 2026-09-26 in headless
19
+ * Chromium on an RTX 4070 SUPER with `layout="random"`, ten edges per node and the default
20
+ * style, found that the wall is not the GPU. It is V8's heap: `performance.memory.jsHeapSizeLimit`
21
+ * is 3.5 GB in that Chromium and the renderer draws every edge as two Babylon meshes plus, on
22
+ * the default arrow-headed style, a ShaderMaterial of its own, which costs about 20 KB of heap
23
+ * per edge and 10 KB per node. The heap was at its limit from 15,000 / 150,000 up (every load
24
+ * past that point is garbage-collection bound: 12.6 s, then 19.4 s at 17,000) and the renderer
25
+ * process died at 18,000 / 180,000. Nodes alone are cheap: 200,000 with no edges used 2.0 GB.
26
+ *
27
+ * The ceilings below keep the worst case they allow together, 50,000 nodes AND 100,000 edges,
28
+ * at 2.7 GB of heap (74 % of the limit, loaded in 8.0 s), which leaves room for a layout and a
29
+ * run to allocate. 100,000 nodes with the same edges reached 84 % and 11.4 s, which is why the
30
+ * node ceiling is the lower of the two measured figures. `DataManager` refuses a load past
31
+ * either with `E_TOO_LARGE`; see `refuseAboveCeiling` there for why a refusal and not a
32
+ * degraded draw. When the arrowheads share one material (pull request #394 in flight) the
33
+ * per-edge cost falls and the same measurement should be repeated to raise these.
15
34
  *
16
35
  * ONE OF THE SIX FIELDS IS ABSENT, and it is worth saying why the other five are not.
17
36
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphty/graphty-element",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "A Web Component library for 3D/2D graph visualization built with Lit and Babylon.js",
5
5
  "type": "module",
6
6
  "customElements": "./dist/custom-elements.json",
@@ -134,7 +134,7 @@
134
134
  "vitepress": "^1.6.3",
135
135
  "vitest": "^3.2.4",
136
136
  "@graphty/remote-logger": "^1.3.7",
137
- "@graphty/webgpu-graph-algorithms": "^0.6.4"
137
+ "@graphty/webgpu-graph-algorithms": "^0.6.5"
138
138
  },
139
139
  "peerDependencies": {
140
140
  "@ai-sdk/anthropic": "^2.0.50",
@@ -146,7 +146,7 @@
146
146
  "ai": "^5.0.104",
147
147
  "encrypt-storage": "^2.14.7",
148
148
  "lit": "^3.0.0",
149
- "@graphty/webgpu-graph-algorithms": "^0.6.4"
149
+ "@graphty/webgpu-graph-algorithms": "^0.6.5"
150
150
  },
151
151
  "peerDependenciesMeta": {
152
152
  "@ai-sdk/anthropic": {
@@ -184,9 +184,9 @@
184
184
  "papaparse": "^5.5.3",
185
185
  "toposort": "^2.0.2",
186
186
  "zod": "^3.25.28",
187
+ "@graphty/algorithms": "^2.0.3",
187
188
  "@graphty/graph-format": "^1.0.5",
188
- "@graphty/layout": "^1.9.1",
189
- "@graphty/algorithms": "^2.0.3"
189
+ "@graphty/layout": "^1.9.1"
190
190
  },
191
191
  "overrides": {
192
192
  "storybook": "$storybook"