@kanzo-tech/graph 0.19.0 → 0.21.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.
Files changed (56) hide show
  1. package/README.md +25 -7
  2. package/dist/core/detail.d.ts +2 -0
  3. package/dist/core/detail.d.ts.map +1 -1
  4. package/dist/core/detail.js +51 -30
  5. package/dist/core/detail.js.map +1 -1
  6. package/dist/core/state.d.ts +7 -0
  7. package/dist/core/state.d.ts.map +1 -1
  8. package/dist/core/store.d.ts.map +1 -1
  9. package/dist/core/store.js +112 -108
  10. package/dist/core/store.js.map +1 -1
  11. package/dist/core/types.d.ts +4 -4
  12. package/dist/core/types.d.ts.map +1 -1
  13. package/dist/index.d.ts +7 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +24 -18
  16. package/dist/index.js.map +1 -1
  17. package/dist/parts/graph-counts.d.ts +15 -0
  18. package/dist/parts/graph-counts.d.ts.map +1 -0
  19. package/dist/parts/graph-counts.js +41 -0
  20. package/dist/parts/graph-counts.js.map +1 -0
  21. package/dist/parts/graph-legend.d.ts +3 -2
  22. package/dist/parts/graph-legend.d.ts.map +1 -1
  23. package/dist/parts/graph-legend.js +30 -48
  24. package/dist/parts/graph-legend.js.map +1 -1
  25. package/dist/parts/graph-looks.d.ts +16 -0
  26. package/dist/parts/graph-looks.d.ts.map +1 -0
  27. package/dist/parts/graph-looks.js +138 -0
  28. package/dist/parts/graph-looks.js.map +1 -0
  29. package/dist/parts/graph-search.d.ts +17 -0
  30. package/dist/parts/graph-search.d.ts.map +1 -0
  31. package/dist/parts/graph-search.js +89 -0
  32. package/dist/parts/graph-search.js.map +1 -0
  33. package/dist/react/use-graph-prefs.d.ts +6 -2
  34. package/dist/react/use-graph-prefs.d.ts.map +1 -1
  35. package/dist/react/use-graph-prefs.js +7 -7
  36. package/dist/react/use-graph-prefs.js.map +1 -1
  37. package/dist/render/camera.d.ts +26 -0
  38. package/dist/render/camera.d.ts.map +1 -0
  39. package/dist/render/camera.js +40 -0
  40. package/dist/render/camera.js.map +1 -0
  41. package/dist/render/graph-looks.d.ts +10 -0
  42. package/dist/render/graph-looks.d.ts.map +1 -1
  43. package/dist/render/graph-looks.js +37 -20
  44. package/dist/render/graph-looks.js.map +1 -1
  45. package/dist/render/placement.d.ts +23 -0
  46. package/dist/render/placement.d.ts.map +1 -0
  47. package/dist/render/placement.js +23 -0
  48. package/dist/render/placement.js.map +1 -0
  49. package/dist/render/renderer.d.ts.map +1 -1
  50. package/dist/render/renderer.js +109 -100
  51. package/dist/render/renderer.js.map +1 -1
  52. package/dist/render/webgl.d.ts +5 -4
  53. package/dist/render/webgl.d.ts.map +1 -1
  54. package/dist/render/webgl.js +14 -11
  55. package/dist/render/webgl.js.map +1 -1
  56. package/package.json +7 -6
@@ -1,45 +1,40 @@
1
- import { Graph as Q } from "@cosmos.gl/graph";
2
- import { GraphError as w } from "../core/error.js";
1
+ import { Graph as nn } from "@cosmos.gl/graph";
2
+ import { GraphError as E } from "../core/error.js";
3
3
  import { resolveLook as M } from "./graph-looks.js";
4
- import { forces as C, appearance as V, paint as X } from "./graph-model.js";
5
- import { resolveSim as q } from "./graph-sim.js";
6
- import { hasWebGL as $, releaseContext as v } from "./webgl.js";
7
- const E = 420, I = 0.18, k = 0.35, U = 20, W = 1e4, Z = (a) => [a.x, a.y, a.x + a.w, a.y + a.h];
8
- function nn(a, i) {
9
- if (!i) return a.positions;
10
- const e = a.positions.slice();
11
- for (let s = 0; s < a.size; s++)
12
- i[s] || (e[s * 2] = e[s * 2 + 1] = Number.NaN);
13
- return e;
14
- }
15
- function sn(a, i, e = {}) {
16
- let s = !1;
17
- const h = (n) => {
18
- s || i.unrenderable(n);
19
- };
20
- if (!$())
21
- return h(new w("graph/no-webgl", "This browser offers no WebGL context to draw with.")), null;
22
- let P = q(i.getOptions().sim), f = i.getOptions().simulate ?? !1, b = null, y = null, L = -1;
23
- const z = (n) => {
24
- const t = Math.round(n * U);
25
- t !== L && (L = t, i.reportProgress(t / U));
4
+ import { forces as z, appearance as W, paint as tn } from "./graph-model.js";
5
+ import { resolveSim as j } from "./graph-sim.js";
6
+ import { createCamera as on, FIT_DURATION as en } from "./camera.js";
7
+ import { placed as rn, UNPLACED as V, placementOf as an, cornersOf as ln } from "./placement.js";
8
+ import { webglBox as sn, releaseContext as un } from "./webgl.js";
9
+ const T = 0.35, cn = 1200, Y = 20, J = 1e4;
10
+ function kn(l, e, i = {}) {
11
+ let O = !1;
12
+ const S = (n) => {
13
+ O || e.unrenderable(n);
14
+ }, I = sn();
15
+ if (I === null)
16
+ return S(new E("graph/no-webgl", "This browser offers no WebGL context to draw with.")), null;
17
+ let k = j(e.getOptions().sim), c = e.getOptions().simulate ?? !1, x = null, C = null, b = 0, A = -1;
18
+ const F = (n) => {
19
+ const t = Math.round(n * Y);
20
+ t !== A && (A = t, e.reportProgress(t / Y));
26
21
  };
27
- let S = M(i.getOptions().look), F = i.getOptions().look;
22
+ let w = M(e.getOptions().look), B = e.getOptions().look;
28
23
  const r = { positions: !0, paint: !0, state: !0, pinned: !0 };
29
- let g = 0, R = null, D = null, u = i.getSnapshot(), p = i.getOptions();
24
+ let h = 0, G = null, P = null, D = null, s = e.getSnapshot(), f = e.getOptions();
30
25
  const N = () => {
31
26
  var n;
32
- return ((n = i.getSnapshot().geometry) == null ? void 0 : n.size) ?? 0;
27
+ return ((n = e.getSnapshot().geometry) == null ? void 0 : n.size) ?? 0;
33
28
  };
34
29
  let o;
35
30
  try {
36
- o = new Q(a, {
31
+ o = new nn(l, {
37
32
  // No `spaceSize` here: the box is the corpus's extent, set when the graph has loaded.
38
- // `rescalePositions: false` because the corpus's coordinates are the camera's.
33
+ // `rescalePositions: false` because the placement below is ours, and the corpus's own scale.
39
34
  rescalePositions: !1,
40
35
  transitionDuration: 0,
41
- enableSimulation: f,
42
- ...C(P),
36
+ enableSimulation: c,
37
+ ...z(k),
43
38
  // Frames to convergence: alpha reaches its floor after exactly this many rendered frames.
44
39
  simulationDecay: 400,
45
40
  randomSeed: "kanzo-discovery",
@@ -47,103 +42,111 @@ function sn(a, i, e = {}) {
47
42
  enableDrag: !0,
48
43
  hoveredPointCursor: "pointer",
49
44
  attribution: "",
50
- onSimulationStart: () => i.report("running"),
45
+ onSimulationStart: () => e.report("running"),
51
46
  onSimulationEnd: () => {
52
47
  var n;
53
- i.report("settled"), z(1), (n = e.onFrame) == null || n.call(e);
48
+ e.report("settled"), F(1), p.settle(), (n = i.onFrame) == null || n.call(i);
54
49
  },
55
- onSimulationPause: () => i.report("paused"),
56
- onSimulationUnpause: () => i.report("running"),
57
- onSimulationTick: (n, t, l) => {
58
- var c, d;
59
- z(o.progress), t !== void 0 && l && ((c = e.onHover) == null || c.call(e, l)), (d = e.onFrame) == null || d.call(e);
50
+ onSimulationPause: () => e.report("paused"),
51
+ onSimulationUnpause: () => e.report("running"),
52
+ onSimulationTick: (n, t, a) => {
53
+ var u, m;
54
+ F(o.progress), p.tick(), t !== void 0 && a && ((u = i.onHover) == null || u.call(i, a)), (m = i.onFrame) == null || m.call(i);
60
55
  },
56
+ onZoomStart: (n, t) => K(t),
61
57
  onZoom: () => {
62
58
  var n;
63
- return (n = e.onFrame) == null ? void 0 : n.call(e);
59
+ return (n = i.onFrame) == null ? void 0 : n.call(i);
64
60
  },
65
61
  onPointMouseOver: (n, t) => {
66
- var l;
67
- b = n, (l = e.onHover) == null || l.call(e, t), i.hover(n);
62
+ var a;
63
+ x = n, (a = i.onHover) == null || a.call(i, t), e.hover(n);
68
64
  },
69
65
  onPointMouseOut: () => {
70
66
  var n;
71
- b = null, (n = e.onHover) == null || n.call(e, null), i.hover(null);
67
+ x = null, (n = i.onHover) == null || n.call(i, null), e.hover(null);
72
68
  },
73
69
  onDragStart: () => {
74
- y = b;
70
+ C = x;
75
71
  },
76
72
  onDrag: (n) => {
77
- var t, l;
78
- (t = e.onHover) == null || t.call(e, o.screenToSpacePosition([n.x, n.y])), (l = e.onFrame) == null || l.call(e);
73
+ var t, a;
74
+ (t = i.onHover) == null || t.call(i, o.screenToSpacePosition([n.x, n.y])), (a = i.onFrame) == null || a.call(i);
79
75
  },
76
+ // d3-force's drag-to-fix, and only while a layout can move the point back: cosmos.gl moves a
77
+ // dragged point with the simulation off too, and a pin nothing pulls against means nothing.
80
78
  onDragEnd: () => {
81
- const n = y;
82
- y = null, n !== null && i.pin([...i.getSnapshot().pinned, n]);
79
+ const n = C;
80
+ C = null;
81
+ const { motion: t, pinned: a } = e.getSnapshot();
82
+ n !== null && t !== "settled" && !a.includes(n) && e.pin([...a, n]);
83
83
  },
84
- onPointClick: (n) => A(n),
85
- onBackgroundClick: () => G()
84
+ onPointClick: (n) => _(n),
85
+ onBackgroundClick: () => H()
86
86
  });
87
87
  } catch (n) {
88
- return h(new w("graph/no-webgl", "The renderer failed to start.", {}, { cause: n })), null;
88
+ return S(new E("graph/no-webgl", "The renderer failed to start.", {}, { cause: n })), null;
89
89
  }
90
- const T = () => {
91
- g || (g = requestAnimationFrame(() => {
92
- g = 0, j();
90
+ const { camera: p, taken: K } = on(o, l), L = () => {
91
+ h || (h = requestAnimationFrame(() => {
92
+ h = 0, Q();
93
93
  }));
94
94
  };
95
- function j() {
96
- var _;
97
- const n = i.getSnapshot(), { geometry: t, encoding: l } = n, c = i.getOptions();
98
- let d = !1;
99
- if (r.positions && t && (r.positions = !1, Y(t), o.setPointPositions(nn(t, n.mask), !0), t.links !== D && (D = t.links, o.setLinks(D), r.paint = r.state = r.pinned = !0), d = !0), r.paint && t && l) {
95
+ function Q() {
96
+ var q;
97
+ const n = e.getSnapshot(), { geometry: t, encoding: a } = n, u = e.getOptions();
98
+ let m = !1;
99
+ if (r.positions && t && (r.positions = !1, o.setPointPositions(rn(t, X(t), n.mask), !0), t.links !== D && (D = t.links, o.setLinks(D), r.paint = r.state = r.pinned = !0), m = !0), r.paint && t && a) {
100
100
  r.paint = !1;
101
- const m = X(t, l, S, a, c);
102
- o.setPointColors(m.colors), o.setPointSizes(m.sizes), o.setPointShapes(m.shapes), o.setLinkColors(m.linkColors), o.setConfigPartial(V(S, a)), d = !0;
101
+ const g = tn(t, a, w, l, u);
102
+ o.setPointColors(g.colors), o.setPointSizes(g.sizes), o.setPointShapes(g.shapes), o.setLinkColors(g.linkColors), o.setConfigPartial(W(w, l)), m = !0;
103
103
  }
104
104
  r.state && t && (r.state = !1, o.setConfigPartial({
105
- highlightedPointIndices: n.selection ? n.selection.vertices.filter((m) => m < t.size) : void 0,
105
+ highlightedPointIndices: n.selection ? n.selection.vertices.filter((g) => g < t.size) : void 0,
106
106
  focusedPointIndex: n.focus === null || n.focus >= t.size ? void 0 : n.focus
107
- })), r.pinned && t && (r.pinned = !1, o.setPinnedPoints(n.pinned.length > 0 ? [...n.pinned] : null)), d && o.render(), t && l && !r.paint && i.reportDrawn(n), (_ = e.onFrame) == null || _.call(e);
107
+ })), r.pinned && t && (r.pinned = !1, o.setPinnedPoints(n.pinned.length > 0 ? [...n.pinned] : null)), m && o.render(), t && a && !r.paint && e.reportDrawn(n), (q = i.onFrame) == null || q.call(i);
108
108
  }
109
- function Y(n) {
110
- n === R || !n.extent || (R = n, o.setConfigPartial({ spaceSize: Math.max(n.extent.w, n.extent.h) }), o.fitViewByPointPositions(Z(n.extent), 0, I));
109
+ function X(n) {
110
+ return n === G ? P ?? V : n.extent ? (G = n, P = an(n.extent, I), o.setConfigPartial({ spaceSize: P.side }), p.frame(ln(n.extent, P)), P) : V;
111
111
  }
112
- function J(n) {
113
- const t = q(n), l = Object.keys(t).some((c) => t[c] !== P[c]);
114
- P = t, f && (o.setConfigPartial(C(t)), l && i.getSnapshot().motion !== "paused" && o.start(k));
112
+ function $(n) {
113
+ const t = j(n), a = Object.keys(t).some((u) => t[u] !== k[u]);
114
+ k = t, c && (o.setConfigPartial(z(t)), a && e.getSnapshot().motion !== "paused" && o.start(T));
115
115
  }
116
- function x(n) {
117
- f || (f = !0, o.setConfigPartial({ enableSimulation: !0, ...C(P) })), o.start(n);
116
+ function y(n) {
117
+ c || (c = !0, o.setConfigPartial({ enableSimulation: !0, ...z(k) })), p.run(), o.start(n);
118
118
  }
119
- function A(n) {
119
+ function d() {
120
+ clearTimeout(b), b = 0;
121
+ }
122
+ function _(n) {
120
123
  const t = o.getNeighboringPointIndices(n) ?? [];
121
- i.select([n, ...t], "node", "Node"), i.focus(n);
124
+ e.select([n, ...t], "node", "Node"), e.focus(n);
122
125
  }
123
- function G() {
124
- i.select(null), i.focus(null);
126
+ function H() {
127
+ e.select(null), e.focus(null);
125
128
  }
126
- o.setConfigPartial(V(S, a)), o.render();
127
- const B = (n) => {
128
- n.preventDefault(), h(new w("graph/context-lost", "The browser took back the WebGL context; reload to get one."));
129
- }, H = (n, t) => h(new w("graph/no-webgl", "No GPU device came up to draw with.", n, { cause: t })), O = setTimeout(() => H({ after: W }), W);
129
+ o.setConfigPartial(W(w, l)), o.render();
130
+ const U = (n) => {
131
+ n.preventDefault(), S(new E("graph/context-lost", "The browser took back the WebGL context; reload to get one."));
132
+ }, Z = (n, t) => S(new E("graph/no-webgl", "No GPU device came up to draw with.", n, { cause: t })), R = setTimeout(() => Z({ after: J }), J);
130
133
  o.ready.then(
131
134
  () => {
132
135
  var n;
133
- clearTimeout(O), s || (n = a.querySelector("canvas")) == null || n.addEventListener("webglcontextlost", B);
136
+ clearTimeout(R), O || (n = l.querySelector("canvas")) == null || n.addEventListener("webglcontextlost", U);
134
137
  },
135
138
  (n) => {
136
- clearTimeout(O), H({}, n);
139
+ clearTimeout(R), Z({}, n);
137
140
  }
138
- ), i.report(f && o.isSimulationRunning ? "running" : "settled"), T();
139
- const K = i.subscribe(() => {
140
- const n = i.getSnapshot(), t = i.getOptions();
141
- (n.geometry !== u.geometry || n.mask !== u.mask) && (r.positions = !0), n.encoding !== u.encoding && (r.paint = !0), t.look !== F && (F = t.look, S = M(t.look), r.paint = !0), (t.fill !== p.fill || t.symbol !== p.symbol || t.stroke !== p.stroke) && (r.paint = !0), (n.selection !== u.selection || n.focus !== u.focus) && (r.state = !0), n.pinned !== u.pinned && (r.pinned = !0), t.sim !== p.sim && J(t.sim), (t.simulate ?? !1) !== (p.simulate ?? !1) && (t.simulate ? x(k) : o.pause()), u = n, p = t, (r.positions || r.paint || r.state || r.pinned) && T();
141
+ ), e.report(c && o.isSimulationRunning ? "running" : "settled"), L();
142
+ const v = e.subscribe(() => {
143
+ const n = e.getSnapshot(), t = e.getOptions();
144
+ (n.geometry !== s.geometry || n.mask !== s.mask) && (r.positions = !0), n.encoding !== s.encoding && (r.paint = !0), t.look !== B && (B = t.look, w = M(t.look), r.paint = !0), (t.fill !== f.fill || t.symbol !== f.symbol || t.stroke !== f.stroke) && (r.paint = !0), (n.selection !== s.selection || n.focus !== s.focus) && (r.state = !0), n.pinned !== s.pinned && (r.pinned = !0), t.sim !== f.sim && $(t.sim), (t.simulate ?? !1) !== (f.simulate ?? !1) && (d(), t.simulate ? y(T) : o.pause()), s = n, f = t, (r.positions || r.paint || r.state || r.pinned) && L();
142
145
  });
143
146
  return {
144
147
  graph: o,
145
148
  repaint() {
146
- r.paint = !0, T();
149
+ r.paint = !0, L();
147
150
  },
148
151
  hit(n) {
149
152
  const t = "rect" in n ? o.findPointsInRect(n.rect) : o.findPointsInPolygon(n.polygon);
@@ -152,38 +155,44 @@ function sn(a, i, e = {}) {
152
155
  zoomBy(n) {
153
156
  o.setZoomLevel(o.getZoomLevel() * n, 220);
154
157
  },
155
- fit() {
156
- var t;
157
- const n = (t = i.getSnapshot().geometry) == null ? void 0 : t.extent;
158
- n ? o.fitViewByPointPositions(Z(n), E, I) : o.fitView(E, I);
158
+ fit: () => p.fit(),
159
+ pause() {
160
+ d(), o.pause();
159
161
  },
160
- pause: () => o.pause(),
161
162
  resume() {
162
- i.getSnapshot().motion === "settled" || !f ? x(k) : o.unpause();
163
+ d(), e.getSnapshot().motion === "settled" || !c ? y(T) : o.unpause();
163
164
  },
164
165
  restart() {
165
- i.pin([]), x(1);
166
+ d(), e.pin([]), y(1);
166
167
  },
168
+ /**
169
+ * d3's release: unfix, then reheat, so the released points visibly flow back. A running layout
170
+ * takes the heat and goes on; a settled one cools to settled on its own; a paused one gets a
171
+ * bounded burst and is paused again, because the reader paused it.
172
+ */
167
173
  unpin() {
168
- i.getSnapshot().pinned.length !== 0 && (i.pin([]), i.getSnapshot().motion === "running" && o.start(k));
174
+ const { motion: n, pinned: t } = e.getSnapshot();
175
+ t.length !== 0 && (d(), e.pin([]), y(T), n === "paused" && (b = window.setTimeout(() => {
176
+ b = 0, o.pause();
177
+ }, cn)));
169
178
  },
170
179
  reveal(n) {
171
- n >= N() || (A(n), o.zoomToPointByIndex(n, 500, 5, !0));
180
+ n >= N() || (_(n), o.zoomToPointByIndex(n, 500, 5, !0));
172
181
  },
173
182
  frameSelection() {
174
183
  var t;
175
- const n = (((t = i.getSnapshot().selection) == null ? void 0 : t.vertices) ?? []).filter((l) => l < N());
176
- n.length > 0 && o.fitViewByPointIndices(n, E, 0.25);
184
+ const n = (((t = e.getSnapshot().selection) == null ? void 0 : t.vertices) ?? []).filter((a) => a < N());
185
+ n.length > 0 && o.fitViewByPointIndices(n, en, 0.25);
177
186
  },
178
- clear: G,
187
+ clear: H,
179
188
  destroy() {
180
- K(), g && cancelAnimationFrame(g), clearTimeout(O), s = !0;
181
- const n = a.querySelector("canvas");
182
- n == null || n.removeEventListener("webglcontextlost", B), o.destroy(), v(n);
189
+ v(), d(), h && cancelAnimationFrame(h), clearTimeout(R), p.destroy(), O = !0;
190
+ const n = l.querySelector("canvas");
191
+ n == null || n.removeEventListener("webglcontextlost", U), o.destroy(), un(n);
183
192
  }
184
193
  };
185
194
  }
186
195
  export {
187
- sn as createRenderer
196
+ kn as createRenderer
188
197
  };
189
198
  //# sourceMappingURL=renderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.js","sources":["../../src/render/renderer.ts"],"sourcesContent":["import { Graph } from \"@cosmos.gl/graph\";\nimport { GraphError } from \"../core/error\";\nimport type { Geometry } from \"../core/load\";\nimport type { GraphStore } from \"../core/store\";\nimport type { GraphCommands, VertexId } from \"../core/types\";\nimport { resolveLook, type Look } from \"./graph-looks\";\nimport { appearance, forces, paint } from \"./graph-model\";\nimport { resolveSim, type Sim } from \"./graph-sim\";\nimport { hasWebGL, releaseContext } from \"./webgl\";\n\nexport interface RendererEvents {\n /** After every frame and every camera move — where the overlays repaint. */\n onFrame?: () => void;\n /** Where the hovered point is in space, as cosmos.gl reports it: no GPU readback per hover. */\n onHover?: (position: [number, number] | null) => void;\n}\n\nexport interface Renderer extends GraphCommands {\n readonly graph: Graph;\n /** The theme moved under the canvas: colours are resolved again, nothing else. */\n repaint(): void;\n /** The drawn points inside a screen rectangle or polygon, once the device is ready. */\n hit(shape: { rect: [[number, number], [number, number]] } | { polygon: [number, number][] }): number[];\n destroy(): void;\n}\n\nconst FIT_DURATION = 420;\nconst FIT_PADDING = 0.18;\nconst REHEAT = 0.35;\n/** Twentieths: a settle costs twenty reports rather than one per frame. */\nconst PROGRESS_STEPS = 20;\n/**\n * The slowest honest answer for a GPU device: one comes up in milliseconds when it can be made at\n * all, so ten seconds is a device that will not come, not one that is slow.\n */\nconst READY_DEADLINE = 10_000;\n\ntype Extent = NonNullable<Geometry[\"extent\"]>;\nconst corners = (box: Extent) => [box.x, box.y, box.x + box.w, box.y + box.h];\n\n/** The loaded positions, with `NaN` where the page's filter hides a vertex — cosmos.gl draws no such point. */\nfunction shown(geometry: Geometry, mask: Uint8Array | null): Float32Array {\n if (!mask) return geometry.positions;\n const positions = geometry.positions.slice();\n for (let id = 0; id < geometry.size; id++) {\n if (!mask[id]) positions[id * 2] = positions[id * 2 + 1] = Number.NaN;\n }\n return positions;\n}\n\n/**\n * **cosmos.gl's lifetime, and the frame that renders once.** Built once per element; it subscribes to\n * the store and discharges what changed in one `requestAnimationFrame`:\n *\n * - a new graph uploads positions and links, once per corpus; a filter uploads positions alone;\n * - a binding, a look or a theme change uploads colours, sizes and shapes and nothing else;\n * - a selection, a focus or a pin sets config and uploads nothing, and never calls `render()`:\n * `render()` walks every point and link in JS, and `setConfigPartial` asks for its own frame;\n * - the camera reaches nothing: cosmos.gl moves it, and the overlays follow.\n *\n * A vertex's id is its index here, so nothing is resolved between the store and the buffers.\n * `transitionDuration` is 0: the default animates every upload for 800 ms and keeps the loop awake.\n * The live layout is off unless `simulate` or the toolbar asks, and runs from the current positions.\n * Every callback reads the store's latest options, so a host's inline `onFailure` never rebuilds it.\n * A failure to draw at all marks the store `failed` through `unrenderable`, never `onFailure` alone.\n */\nexport function createRenderer(host: HTMLDivElement, store: GraphStore, events: RendererEvents = {}): Renderer | null {\n let destroyed = false;\n const fail = (error: unknown) => {\n if (!destroyed) store.unrenderable(error);\n };\n if (!hasWebGL()) {\n fail(new GraphError(\"graph/no-webgl\", \"This browser offers no WebGL context to draw with.\"));\n return null;\n }\n\n let sim: Sim = resolveSim(store.getOptions().sim);\n let live = store.getOptions().simulate ?? false;\n let hovering: number | null = null;\n let dragging: number | null = null;\n let reported = -1;\n const progress = (value: number) => {\n const bucket = Math.round(value * PROGRESS_STEPS);\n if (bucket === reported) return;\n reported = bucket;\n store.reportProgress(bucket / PROGRESS_STEPS);\n };\n\n let look: Look = resolveLook(store.getOptions().look);\n let lookPatch = store.getOptions().look;\n const dirty = { positions: true, paint: true, state: true, pinned: true };\n let frame = 0;\n let framed: Geometry | null = null;\n let links: Float32Array | null = null;\n let last = store.getSnapshot();\n let lastOptions = store.getOptions();\n\n const size = () => store.getSnapshot().geometry?.size ?? 0;\n\n let graph: Graph;\n try {\n graph = new Graph(host, {\n // No `spaceSize` here: the box is the corpus's extent, set when the graph has loaded.\n // `rescalePositions: false` because the corpus's coordinates are the camera's.\n rescalePositions: false,\n transitionDuration: 0,\n enableSimulation: live,\n ...forces(sim),\n // Frames to convergence: alpha reaches its floor after exactly this many rendered frames.\n simulationDecay: 400,\n randomSeed: \"kanzo-discovery\",\n pixelRatio: window.devicePixelRatio || 1,\n enableDrag: true,\n hoveredPointCursor: \"pointer\",\n attribution: \"\",\n onSimulationStart: () => store.report(\"running\"),\n onSimulationEnd: () => {\n store.report(\"settled\");\n progress(1);\n events.onFrame?.();\n },\n onSimulationPause: () => store.report(\"paused\"),\n onSimulationUnpause: () => store.report(\"running\"),\n onSimulationTick: (_alpha, index, position) => {\n progress(graph.progress);\n if (index !== undefined && position) events.onHover?.(position);\n events.onFrame?.();\n },\n onZoom: () => events.onFrame?.(),\n onPointMouseOver: (index, position) => {\n hovering = index;\n events.onHover?.(position);\n store.hover(index);\n },\n onPointMouseOut: () => {\n hovering = null;\n events.onHover?.(null);\n store.hover(null);\n },\n onDragStart: () => {\n dragging = hovering;\n },\n onDrag: (event) => {\n events.onHover?.(graph.screenToSpacePosition([event.x, event.y]));\n events.onFrame?.();\n },\n onDragEnd: () => {\n const vertex = dragging;\n dragging = null;\n if (vertex !== null) store.pin([...store.getSnapshot().pinned, vertex]);\n },\n onPointClick: (index) => focusOn(index),\n onBackgroundClick: () => clear(),\n });\n } catch (error) {\n fail(new GraphError(\"graph/no-webgl\", \"The renderer failed to start.\", {}, { cause: error }));\n return null;\n }\n\n const schedule = () => {\n if (frame) return;\n frame = requestAnimationFrame(() => {\n frame = 0;\n draw();\n });\n };\n\n function draw(): void {\n const snapshot = store.getSnapshot();\n const { geometry, encoding } = snapshot;\n const options = store.getOptions();\n let changed = false;\n if (dirty.positions && geometry) {\n dirty.positions = false;\n frameExtent(geometry);\n graph.setPointPositions(shown(geometry, snapshot.mask), true);\n if (geometry.links !== links) {\n links = geometry.links;\n graph.setLinks(links);\n dirty.paint = dirty.state = dirty.pinned = true;\n }\n changed = true;\n }\n if (dirty.paint && geometry && encoding) {\n dirty.paint = false;\n const buffers = paint(geometry, encoding, look, host, options);\n graph.setPointColors(buffers.colors);\n graph.setPointSizes(buffers.sizes);\n graph.setPointShapes(buffers.shapes);\n graph.setLinkColors(buffers.linkColors);\n graph.setConfigPartial(appearance(look, host));\n changed = true;\n }\n if (dirty.state && geometry) {\n dirty.state = false;\n graph.setConfigPartial({\n highlightedPointIndices: snapshot.selection ? snapshot.selection.vertices.filter((id) => id < geometry.size) : undefined,\n focusedPointIndex: snapshot.focus === null || snapshot.focus >= geometry.size ? undefined : snapshot.focus,\n });\n }\n if (dirty.pinned && geometry) {\n dirty.pinned = false;\n graph.setPinnedPoints(snapshot.pinned.length > 0 ? [...snapshot.pinned] : null);\n }\n if (changed) graph.render();\n if (geometry && encoding && !dirty.paint) store.reportDrawn(snapshot);\n events.onFrame?.();\n }\n\n function frameExtent(geometry: Geometry): void {\n if (geometry === framed || !geometry.extent) return;\n framed = geometry;\n graph.setConfigPartial({ spaceSize: Math.max(geometry.extent.w, geometry.extent.h) });\n graph.fitViewByPointPositions(corners(geometry.extent), 0, FIT_PADDING);\n }\n\n function applyForces(patch: Partial<Sim> | undefined): void {\n const next = resolveSim(patch);\n const moved = Object.keys(next).some((key) => next[key as keyof Sim] !== sim[key as keyof Sim]);\n sim = next;\n if (!live) return;\n graph.setConfigPartial(forces(next));\n if (moved && store.getSnapshot().motion !== \"paused\") graph.start(REHEAT);\n }\n\n /** A layout runs from where the points are: enabling cosmos.gl's simulation keeps its positions. */\n function run(alpha: number): void {\n if (!live) {\n live = true;\n graph.setConfigPartial({ enableSimulation: true, ...forces(sim) });\n }\n graph.start(alpha);\n }\n\n function focusOn(vertex: VertexId): void {\n const around = graph.getNeighboringPointIndices(vertex) ?? [];\n store.select([vertex, ...around], \"node\", \"Node\");\n store.focus(vertex);\n }\n\n function clear(): void {\n store.select(null);\n store.focus(null);\n }\n\n // cosmos.gl 3.4 queues every setter and command until its device exists (`ensureDevice`), and its\n // two synchronous hit tests answer `[]` until then; only the canvas element has to wait for `ready`.\n graph.setConfigPartial(appearance(look, host));\n graph.render();\n const onLost = (event: Event) => {\n event.preventDefault();\n fail(new GraphError(\"graph/context-lost\", \"The browser took back the WebGL context; reload to get one.\"));\n };\n const noDevice = (data: GraphError[\"data\"], cause?: unknown) =>\n fail(new GraphError(\"graph/no-webgl\", \"No GPU device came up to draw with.\", data, { cause }));\n // `ready` never settles when cosmos.gl cannot make a device, so the deadline is what ends the wait.\n const deadline = setTimeout(() => noDevice({ after: READY_DEADLINE }), READY_DEADLINE);\n void graph.ready.then(\n () => {\n clearTimeout(deadline);\n if (!destroyed) host.querySelector(\"canvas\")?.addEventListener(\"webglcontextlost\", onLost);\n },\n (error: unknown) => {\n clearTimeout(deadline);\n noDevice({}, error);\n },\n );\n store.report(live && graph.isSimulationRunning ? \"running\" : \"settled\");\n schedule();\n\n const unsubscribe = store.subscribe(() => {\n const snapshot = store.getSnapshot();\n const options = store.getOptions();\n if (snapshot.geometry !== last.geometry || snapshot.mask !== last.mask) dirty.positions = true;\n if (snapshot.encoding !== last.encoding) dirty.paint = true;\n if (options.look !== lookPatch) {\n lookPatch = options.look;\n look = resolveLook(options.look);\n dirty.paint = true;\n }\n if (options.fill !== lastOptions.fill || options.symbol !== lastOptions.symbol || options.stroke !== lastOptions.stroke) {\n dirty.paint = true;\n }\n if (snapshot.selection !== last.selection || snapshot.focus !== last.focus) dirty.state = true;\n if (snapshot.pinned !== last.pinned) dirty.pinned = true;\n if (options.sim !== lastOptions.sim) applyForces(options.sim);\n if ((options.simulate ?? false) !== (lastOptions.simulate ?? false)) {\n if (options.simulate) run(REHEAT);\n else graph.pause();\n }\n last = snapshot;\n lastOptions = options;\n if (dirty.positions || dirty.paint || dirty.state || dirty.pinned) schedule();\n });\n\n return {\n graph,\n repaint() {\n dirty.paint = true;\n schedule();\n },\n hit(shape) {\n const found = \"rect\" in shape ? graph.findPointsInRect(shape.rect) : graph.findPointsInPolygon(shape.polygon);\n return Array.from(found);\n },\n zoomBy(factor) {\n graph.setZoomLevel(graph.getZoomLevel() * factor, 220);\n },\n fit() {\n const extent = store.getSnapshot().geometry?.extent;\n if (extent) graph.fitViewByPointPositions(corners(extent), FIT_DURATION, FIT_PADDING);\n else graph.fitView(FIT_DURATION, FIT_PADDING);\n },\n pause: () => graph.pause(),\n resume() {\n const settled = store.getSnapshot().motion === \"settled\";\n if (settled || !live) run(REHEAT);\n else graph.unpause();\n },\n restart() {\n store.pin([]);\n run(1);\n },\n unpin() {\n if (store.getSnapshot().pinned.length === 0) return;\n store.pin([]);\n if (store.getSnapshot().motion === \"running\") graph.start(REHEAT);\n },\n reveal(vertex) {\n if (vertex >= size()) return;\n focusOn(vertex);\n graph.zoomToPointByIndex(vertex, 500, 5, true);\n },\n frameSelection() {\n const vertices = (store.getSnapshot().selection?.vertices ?? []).filter((id) => id < size());\n if (vertices.length > 0) graph.fitViewByPointIndices(vertices, FIT_DURATION, 0.25);\n },\n clear,\n destroy() {\n unsubscribe();\n if (frame) cancelAnimationFrame(frame);\n clearTimeout(deadline);\n destroyed = true;\n // Read here rather than remembered from construction: the element exists only with the device.\n const canvas = host.querySelector(\"canvas\");\n canvas?.removeEventListener(\"webglcontextlost\", onLost);\n graph.destroy();\n releaseContext(canvas);\n },\n };\n}\n"],"names":["FIT_DURATION","FIT_PADDING","REHEAT","PROGRESS_STEPS","READY_DEADLINE","corners","box","shown","geometry","mask","positions","id","createRenderer","host","store","events","destroyed","fail","error","hasWebGL","GraphError","sim","resolveSim","live","hovering","dragging","reported","progress","value","bucket","look","resolveLook","lookPatch","dirty","frame","framed","links","last","lastOptions","size","_a","graph","Graph","forces","_alpha","index","position","_b","event","vertex","focusOn","clear","schedule","draw","snapshot","encoding","options","changed","frameExtent","buffers","paint","appearance","applyForces","patch","next","moved","key","run","alpha","around","onLost","noDevice","data","cause","deadline","unsubscribe","shape","found","factor","extent","vertices","canvas","releaseContext"],"mappings":"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"}
1
+ {"version":3,"file":"renderer.js","sources":["../../src/render/renderer.ts"],"sourcesContent":["import { Graph } from \"@cosmos.gl/graph\";\nimport { GraphError } from \"../core/error\";\nimport type { Geometry } from \"../core/load\";\nimport type { GraphStore } from \"../core/store\";\nimport type { GraphCommands, VertexId } from \"../core/types\";\nimport { resolveLook, type Look } from \"./graph-looks\";\nimport { appearance, forces, paint } from \"./graph-model\";\nimport { resolveSim, type Sim } from \"./graph-sim\";\nimport { createCamera, FIT_DURATION } from \"./camera\";\nimport { cornersOf, placed, placementOf, UNPLACED, type Placement } from \"./placement\";\nimport { releaseContext, webglBox } from \"./webgl\";\n\nexport interface RendererEvents {\n /** After every frame and every camera move — where the overlays repaint. */\n onFrame?: () => void;\n /** Where the hovered point is in space, as cosmos.gl reports it: no GPU readback per hover. */\n onHover?: (position: [number, number] | null) => void;\n}\n\nexport interface Renderer extends GraphCommands {\n readonly graph: Graph;\n /** The theme moved under the canvas: colours are resolved again, nothing else. */\n repaint(): void;\n /** The drawn points inside a screen rectangle or polygon, once the device is ready. */\n hit(shape: { rect: [[number, number], [number, number]] } | { polygon: [number, number][] }): number[];\n destroy(): void;\n}\n\nconst REHEAT = 0.35;\n/** How long a release wakes a paused layout before pausing it again. */\nconst RELEASE_BURST = 1200;\n/** Twentieths: a settle costs twenty reports rather than one per frame. */\nconst PROGRESS_STEPS = 20;\n/**\n * The slowest honest answer for a GPU device: one comes up in milliseconds when it can be made at\n * all, so ten seconds is a device that will not come, not one that is slow.\n */\nconst READY_DEADLINE = 10_000;\n\n/**\n * **cosmos.gl's lifetime, and the frame that renders once.** Built once per element; it subscribes to\n * the store and discharges what changed in one `requestAnimationFrame`:\n *\n * - a new graph uploads positions and links, once per corpus; a filter uploads positions alone;\n * - a binding, a look or a theme change uploads colours, sizes and shapes and nothing else;\n * - a selection, a focus or a pin sets config and uploads nothing, and never calls `render()`:\n * `render()` walks every point and link in JS, and `setConfigPartial` asks for its own frame;\n * - the camera reaches nothing: cosmos.gl moves it, and the overlays follow.\n *\n * A vertex's id is its index here, so nothing is resolved between the store and the buffers.\n * `transitionDuration` is 0: the default animates every upload for 800 ms and keeps the loop awake.\n * The live layout is off unless `simulate` or the toolbar asks, and runs from the current positions.\n * Every callback reads the store's latest options, so a host's inline `onFailure` never rebuilds it.\n * A failure to draw at all marks the store `failed` through `unrenderable`, never `onFailure` alone.\n */\nexport function createRenderer(host: HTMLDivElement, store: GraphStore, events: RendererEvents = {}): Renderer | null {\n let destroyed = false;\n const fail = (error: unknown) => {\n if (!destroyed) store.unrenderable(error);\n };\n const box = webglBox();\n if (box === null) {\n fail(new GraphError(\"graph/no-webgl\", \"This browser offers no WebGL context to draw with.\"));\n return null;\n }\n\n let sim: Sim = resolveSim(store.getOptions().sim);\n let live = store.getOptions().simulate ?? false;\n let hovering: number | null = null;\n let dragging: number | null = null;\n let burst = 0;\n let reported = -1;\n const progress = (value: number) => {\n const bucket = Math.round(value * PROGRESS_STEPS);\n if (bucket === reported) return;\n reported = bucket;\n store.reportProgress(bucket / PROGRESS_STEPS);\n };\n\n let look: Look = resolveLook(store.getOptions().look);\n let lookPatch = store.getOptions().look;\n const dirty = { positions: true, paint: true, state: true, pinned: true };\n let frame = 0;\n let framed: Geometry | null = null;\n let placement: Placement | null = null;\n let links: Float32Array | null = null;\n let last = store.getSnapshot();\n let lastOptions = store.getOptions();\n\n const size = () => store.getSnapshot().geometry?.size ?? 0;\n\n let graph: Graph;\n try {\n graph = new Graph(host, {\n // No `spaceSize` here: the box is the corpus's extent, set when the graph has loaded.\n // `rescalePositions: false` because the placement below is ours, and the corpus's own scale.\n rescalePositions: false,\n transitionDuration: 0,\n enableSimulation: live,\n ...forces(sim),\n // Frames to convergence: alpha reaches its floor after exactly this many rendered frames.\n simulationDecay: 400,\n randomSeed: \"kanzo-discovery\",\n pixelRatio: window.devicePixelRatio || 1,\n enableDrag: true,\n hoveredPointCursor: \"pointer\",\n attribution: \"\",\n onSimulationStart: () => store.report(\"running\"),\n onSimulationEnd: () => {\n store.report(\"settled\");\n progress(1);\n camera.settle();\n events.onFrame?.();\n },\n onSimulationPause: () => store.report(\"paused\"),\n onSimulationUnpause: () => store.report(\"running\"),\n onSimulationTick: (_alpha, index, position) => {\n progress(graph.progress);\n camera.tick();\n if (index !== undefined && position) events.onHover?.(position);\n events.onFrame?.();\n },\n onZoomStart: (_event, userDriven) => taken(userDriven),\n onZoom: () => events.onFrame?.(),\n onPointMouseOver: (index, position) => {\n hovering = index;\n events.onHover?.(position);\n store.hover(index);\n },\n onPointMouseOut: () => {\n hovering = null;\n events.onHover?.(null);\n store.hover(null);\n },\n onDragStart: () => {\n dragging = hovering;\n },\n onDrag: (event) => {\n events.onHover?.(graph.screenToSpacePosition([event.x, event.y]));\n events.onFrame?.();\n },\n // d3-force's drag-to-fix, and only while a layout can move the point back: cosmos.gl moves a\n // dragged point with the simulation off too, and a pin nothing pulls against means nothing.\n onDragEnd: () => {\n const vertex = dragging;\n dragging = null;\n const { motion, pinned } = store.getSnapshot();\n if (vertex !== null && motion !== \"settled\" && !pinned.includes(vertex)) store.pin([...pinned, vertex]);\n },\n onPointClick: (index) => focusOn(index),\n onBackgroundClick: () => clear(),\n });\n } catch (error) {\n fail(new GraphError(\"graph/no-webgl\", \"The renderer failed to start.\", {}, { cause: error }));\n return null;\n }\n const { camera, taken } = createCamera(graph, host);\n\n const schedule = () => {\n if (frame) return;\n frame = requestAnimationFrame(() => {\n frame = 0;\n draw();\n });\n };\n\n function draw(): void {\n const snapshot = store.getSnapshot();\n const { geometry, encoding } = snapshot;\n const options = store.getOptions();\n let changed = false;\n if (dirty.positions && geometry) {\n dirty.positions = false;\n graph.setPointPositions(placed(geometry, frameExtent(geometry), snapshot.mask), true);\n if (geometry.links !== links) {\n links = geometry.links;\n graph.setLinks(links);\n dirty.paint = dirty.state = dirty.pinned = true;\n }\n changed = true;\n }\n if (dirty.paint && geometry && encoding) {\n dirty.paint = false;\n const buffers = paint(geometry, encoding, look, host, options);\n graph.setPointColors(buffers.colors);\n graph.setPointSizes(buffers.sizes);\n graph.setPointShapes(buffers.shapes);\n graph.setLinkColors(buffers.linkColors);\n graph.setConfigPartial(appearance(look, host));\n changed = true;\n }\n if (dirty.state && geometry) {\n dirty.state = false;\n graph.setConfigPartial({\n highlightedPointIndices: snapshot.selection ? snapshot.selection.vertices.filter((id) => id < geometry.size) : undefined,\n focusedPointIndex: snapshot.focus === null || snapshot.focus >= geometry.size ? undefined : snapshot.focus,\n });\n }\n if (dirty.pinned && geometry) {\n dirty.pinned = false;\n graph.setPinnedPoints(snapshot.pinned.length > 0 ? [...snapshot.pinned] : null);\n }\n if (changed) graph.render();\n if (geometry && encoding && !dirty.paint) store.reportDrawn(snapshot);\n events.onFrame?.();\n }\n\n function frameExtent(geometry: Geometry): Placement {\n if (geometry === framed) return placement ?? UNPLACED;\n if (!geometry.extent) return UNPLACED;\n framed = geometry;\n placement = placementOf(geometry.extent, box as number);\n graph.setConfigPartial({ spaceSize: placement.side });\n camera.frame(cornersOf(geometry.extent, placement));\n return placement;\n }\n\n function applyForces(patch: Partial<Sim> | undefined): void {\n const next = resolveSim(patch);\n const moved = Object.keys(next).some((key) => next[key as keyof Sim] !== sim[key as keyof Sim]);\n sim = next;\n if (!live) return;\n graph.setConfigPartial(forces(next));\n if (moved && store.getSnapshot().motion !== \"paused\") graph.start(REHEAT);\n }\n\n /** A layout runs from where the points are: enabling cosmos.gl's simulation keeps its positions. */\n function run(alpha: number): void {\n if (!live) {\n live = true;\n graph.setConfigPartial({ enableSimulation: true, ...forces(sim) });\n }\n camera.run();\n graph.start(alpha);\n }\n\n function endBurst(): void {\n clearTimeout(burst);\n burst = 0;\n }\n\n function focusOn(vertex: VertexId): void {\n const around = graph.getNeighboringPointIndices(vertex) ?? [];\n store.select([vertex, ...around], \"node\", \"Node\");\n store.focus(vertex);\n }\n\n function clear(): void {\n store.select(null);\n store.focus(null);\n }\n\n // cosmos.gl 3.4 queues every setter and command until its device exists (`ensureDevice`), and its\n // two synchronous hit tests answer `[]` until then; only the canvas element has to wait for `ready`.\n graph.setConfigPartial(appearance(look, host));\n graph.render();\n const onLost = (event: Event) => {\n event.preventDefault();\n fail(new GraphError(\"graph/context-lost\", \"The browser took back the WebGL context; reload to get one.\"));\n };\n const noDevice = (data: GraphError[\"data\"], cause?: unknown) =>\n fail(new GraphError(\"graph/no-webgl\", \"No GPU device came up to draw with.\", data, { cause }));\n // `ready` never settles when cosmos.gl cannot make a device, so the deadline is what ends the wait.\n const deadline = setTimeout(() => noDevice({ after: READY_DEADLINE }), READY_DEADLINE);\n void graph.ready.then(\n () => {\n clearTimeout(deadline);\n if (!destroyed) host.querySelector(\"canvas\")?.addEventListener(\"webglcontextlost\", onLost);\n },\n (error: unknown) => {\n clearTimeout(deadline);\n noDevice({}, error);\n },\n );\n store.report(live && graph.isSimulationRunning ? \"running\" : \"settled\");\n schedule();\n\n const unsubscribe = store.subscribe(() => {\n const snapshot = store.getSnapshot();\n const options = store.getOptions();\n if (snapshot.geometry !== last.geometry || snapshot.mask !== last.mask) dirty.positions = true;\n if (snapshot.encoding !== last.encoding) dirty.paint = true;\n if (options.look !== lookPatch) {\n lookPatch = options.look;\n look = resolveLook(options.look);\n dirty.paint = true;\n }\n if (options.fill !== lastOptions.fill || options.symbol !== lastOptions.symbol || options.stroke !== lastOptions.stroke) {\n dirty.paint = true;\n }\n if (snapshot.selection !== last.selection || snapshot.focus !== last.focus) dirty.state = true;\n if (snapshot.pinned !== last.pinned) dirty.pinned = true;\n if (options.sim !== lastOptions.sim) applyForces(options.sim);\n if ((options.simulate ?? false) !== (lastOptions.simulate ?? false)) {\n endBurst();\n if (options.simulate) run(REHEAT);\n else graph.pause();\n }\n last = snapshot;\n lastOptions = options;\n if (dirty.positions || dirty.paint || dirty.state || dirty.pinned) schedule();\n });\n\n return {\n graph,\n repaint() {\n dirty.paint = true;\n schedule();\n },\n hit(shape) {\n const found = \"rect\" in shape ? graph.findPointsInRect(shape.rect) : graph.findPointsInPolygon(shape.polygon);\n return Array.from(found);\n },\n zoomBy(factor) {\n graph.setZoomLevel(graph.getZoomLevel() * factor, 220);\n },\n fit: () => camera.fit(),\n pause() {\n endBurst();\n graph.pause();\n },\n resume() {\n endBurst();\n const settled = store.getSnapshot().motion === \"settled\";\n if (settled || !live) run(REHEAT);\n else graph.unpause();\n },\n restart() {\n endBurst();\n store.pin([]);\n run(1);\n },\n /**\n * d3's release: unfix, then reheat, so the released points visibly flow back. A running layout\n * takes the heat and goes on; a settled one cools to settled on its own; a paused one gets a\n * bounded burst and is paused again, because the reader paused it.\n */\n unpin() {\n const { motion, pinned } = store.getSnapshot();\n if (pinned.length === 0) return;\n endBurst();\n store.pin([]);\n run(REHEAT);\n if (motion === \"paused\") {\n burst = window.setTimeout(() => {\n burst = 0;\n graph.pause();\n }, RELEASE_BURST);\n }\n },\n reveal(vertex) {\n if (vertex >= size()) return;\n focusOn(vertex);\n graph.zoomToPointByIndex(vertex, 500, 5, true);\n },\n frameSelection() {\n const vertices = (store.getSnapshot().selection?.vertices ?? []).filter((id) => id < size());\n if (vertices.length > 0) graph.fitViewByPointIndices(vertices, FIT_DURATION, 0.25);\n },\n clear,\n destroy() {\n unsubscribe();\n endBurst();\n if (frame) cancelAnimationFrame(frame);\n clearTimeout(deadline);\n camera.destroy();\n destroyed = true;\n // Read here rather than remembered from construction: the element exists only with the device.\n const canvas = host.querySelector(\"canvas\");\n canvas?.removeEventListener(\"webglcontextlost\", onLost);\n graph.destroy();\n releaseContext(canvas);\n },\n };\n}\n"],"names":["REHEAT","RELEASE_BURST","PROGRESS_STEPS","READY_DEADLINE","createRenderer","host","store","events","destroyed","fail","error","box","webglBox","GraphError","sim","resolveSim","live","hovering","dragging","burst","reported","progress","value","bucket","look","resolveLook","lookPatch","dirty","frame","framed","placement","links","last","lastOptions","size","_a","graph","Graph","forces","camera","_alpha","index","position","_b","_event","userDriven","taken","event","vertex","motion","pinned","focusOn","clear","createCamera","schedule","draw","snapshot","geometry","encoding","options","changed","placed","frameExtent","buffers","paint","appearance","id","UNPLACED","placementOf","cornersOf","applyForces","patch","next","moved","key","run","alpha","endBurst","around","onLost","noDevice","data","cause","deadline","unsubscribe","shape","found","factor","vertices","FIT_DURATION","canvas","releaseContext"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Whether this browser can run the renderer at all. cosmos.gl draws its own message rather than
3
- * throwing, and under 3.x `graph.ready` never settles when no device can be made — so the probe
4
- * answers the common case before either can happen.
2
+ * **The largest square cosmos.gl will simulate in, or `null` with no WebGL at all.** cosmos.gl draws its
3
+ * own message rather than throwing, and under 3.x `graph.ready` never settles when no device can be
4
+ * made — so the probe answers the common case before either can happen. The box is half the device's
5
+ * `MAX_TEXTURE_SIZE`, the bound cosmos.gl reduces `spaceSize` to; `Infinity` where the probe cannot say.
5
6
  */
6
- export declare function hasWebGL(): boolean;
7
+ export declare function webglBox(): number | null;
7
8
  /**
8
9
  * **Give the WebGL context back, because cosmos.gl does not.** `WEBGL_lose_context` appears zero times
9
10
  * in `@cosmos.gl/graph@3.4.0`, and Chrome keeps sixteen contexts per renderer process and evicts the
@@ -1 +1 @@
1
- {"version":3,"file":"webgl.d.ts","sourceRoot":"","sources":["../../src/render/webgl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,OAAO,CAQlC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAGrE"}
1
+ {"version":3,"file":"webgl.d.ts","sourceRoot":"","sources":["../../src/render/webgl.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAYxC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAGrE"}
@@ -1,19 +1,22 @@
1
- function n() {
2
- if (typeof document > "u") return !1;
1
+ function x() {
2
+ var t, o, n;
3
+ if (typeof document > "u") return null;
3
4
  try {
4
- const e = document.createElement("canvas");
5
- return e.getContext("webgl2") !== null || e.getContext("webgl") !== null;
5
+ const r = document.createElement("canvas"), e = r.getContext("webgl2") ?? r.getContext("webgl");
6
+ if (!e) return null;
7
+ const l = Number((t = e.getParameter) == null ? void 0 : t.call(e, e.MAX_TEXTURE_SIZE)) / 2;
8
+ return (n = (o = e.getExtension) == null ? void 0 : o.call(e, "WEBGL_lose_context")) == null || n.loseContext(), l > 0 ? l : 1 / 0;
6
9
  } catch {
7
- return !1;
10
+ return null;
8
11
  }
9
12
  }
10
- function l(e) {
11
- var o;
12
- const t = (e == null ? void 0 : e.getContext("webgl2")) ?? (e == null ? void 0 : e.getContext("webgl"));
13
- t && !t.isContextLost() && ((o = t.getExtension("WEBGL_lose_context")) == null || o.loseContext());
13
+ function u(t) {
14
+ var n;
15
+ const o = (t == null ? void 0 : t.getContext("webgl2")) ?? (t == null ? void 0 : t.getContext("webgl"));
16
+ o && !o.isContextLost() && ((n = o.getExtension("WEBGL_lose_context")) == null || n.loseContext());
14
17
  }
15
18
  export {
16
- n as hasWebGL,
17
- l as releaseContext
19
+ u as releaseContext,
20
+ x as webglBox
18
21
  };
19
22
  //# sourceMappingURL=webgl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webgl.js","sources":["../../src/render/webgl.ts"],"sourcesContent":["/**\n * Whether this browser can run the renderer at all. cosmos.gl draws its own message rather than\n * throwing, and under 3.x `graph.ready` never settles when no device can be made — so the probe\n * answers the common case before either can happen.\n */\nexport function hasWebGL(): boolean {\n if (typeof document === \"undefined\") return false;\n try {\n const probe = document.createElement(\"canvas\");\n return probe.getContext(\"webgl2\") !== null || probe.getContext(\"webgl\") !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * **Give the WebGL context back, because cosmos.gl does not.** `WEBGL_lose_context` appears zero times\n * in `@cosmos.gl/graph@3.4.0`, and Chrome keeps sixteen contexts per renderer process and evicts the\n * oldest without an error: `/docs/graph` mounts four graphs under StrictMode and three of them\n * measured `isContextLost === true` before this existed.\n */\nexport function releaseContext(canvas: HTMLCanvasElement | null): void {\n const gl = canvas?.getContext(\"webgl2\") ?? canvas?.getContext(\"webgl\");\n if (gl && !gl.isContextLost()) gl.getExtension(\"WEBGL_lose_context\")?.loseContext();\n}\n"],"names":["hasWebGL","probe","releaseContext","canvas","_a","gl"],"mappings":"AAKO,SAASA,IAAoB;AAClC,MAAI,OAAO,WAAa,IAAa,QAAO;AAC5C,MAAI;AACF,UAAMC,IAAQ,SAAS,cAAc,QAAQ;AAC7C,WAAOA,EAAM,WAAW,QAAQ,MAAM,QAAQA,EAAM,WAAW,OAAO,MAAM;AAAA,EAC9E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAASC,EAAeC,GAAwC;AAhBhE,MAAAC;AAiBL,QAAMC,KAAKF,KAAA,gBAAAA,EAAQ,WAAW,eAAaA,KAAA,gBAAAA,EAAQ,WAAW;AAC9D,EAAIE,KAAM,CAACA,EAAG,cAAA,OAAiBD,IAAAC,EAAG,aAAa,oBAAoB,MAApC,QAAAD,EAAuC;AACxE;"}
1
+ {"version":3,"file":"webgl.js","sources":["../../src/render/webgl.ts"],"sourcesContent":["/**\n * **The largest square cosmos.gl will simulate in, or `null` with no WebGL at all.** cosmos.gl draws its\n * own message rather than throwing, and under 3.x `graph.ready` never settles when no device can be\n * made — so the probe answers the common case before either can happen. The box is half the device's\n * `MAX_TEXTURE_SIZE`, the bound cosmos.gl reduces `spaceSize` to; `Infinity` where the probe cannot say.\n */\nexport function webglBox(): number | null {\n if (typeof document === \"undefined\") return null;\n try {\n const probe = document.createElement(\"canvas\");\n const gl: Partial<WebGLRenderingContext> | null = probe.getContext(\"webgl2\") ?? probe.getContext(\"webgl\");\n if (!gl) return null;\n const max = Number(gl.getParameter?.(gl.MAX_TEXTURE_SIZE as number)) / 2;\n gl.getExtension?.(\"WEBGL_lose_context\")?.loseContext();\n return max > 0 ? max : Infinity;\n } catch {\n return null;\n }\n}\n\n/**\n * **Give the WebGL context back, because cosmos.gl does not.** `WEBGL_lose_context` appears zero times\n * in `@cosmos.gl/graph@3.4.0`, and Chrome keeps sixteen contexts per renderer process and evicts the\n * oldest without an error: `/docs/graph` mounts four graphs under StrictMode and three of them\n * measured `isContextLost === true` before this existed.\n */\nexport function releaseContext(canvas: HTMLCanvasElement | null): void {\n const gl = canvas?.getContext(\"webgl2\") ?? canvas?.getContext(\"webgl\");\n if (gl && !gl.isContextLost()) gl.getExtension(\"WEBGL_lose_context\")?.loseContext();\n}\n"],"names":["webglBox","_a","_b","_c","probe","gl","max","releaseContext","canvas"],"mappings":"AAMO,SAASA,IAA0B;AAAnC,MAAAC,GAAAC,GAAAC;AACL,MAAI,OAAO,WAAa,IAAa,QAAO;AAC5C,MAAI;AACF,UAAMC,IAAQ,SAAS,cAAc,QAAQ,GACvCC,IAA4CD,EAAM,WAAW,QAAQ,KAAKA,EAAM,WAAW,OAAO;AACxG,QAAI,CAACC,EAAI,QAAO;AAChB,UAAMC,IAAM,QAAOL,IAAAI,EAAG,iBAAH,gBAAAJ,EAAA,KAAAI,GAAkBA,EAAG,iBAA2B,IAAI;AACvE,YAAAF,KAAAD,IAAAG,EAAG,iBAAH,gBAAAH,EAAA,KAAAG,GAAkB,0BAAlB,QAAAF,EAAyC,eAClCG,IAAM,IAAIA,IAAM;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAASC,EAAeC,GAAwC;AApBhE,MAAAP;AAqBL,QAAMI,KAAKG,KAAA,gBAAAA,EAAQ,WAAW,eAAaA,KAAA,gBAAAA,EAAQ,WAAW;AAC9D,EAAIH,KAAM,CAACA,EAAG,cAAA,OAAiBJ,IAAAI,EAAG,aAAa,oBAAoB,MAApC,QAAAJ,EAAuC;AACxE;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanzo-tech/graph",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Kanzo graph view — a whole fossil corpus, read once through its scan and drawn with cosmos.gl, inside the page's Mosaic crossfilter. Sibling of @kanzo-tech/ui, not part of it: the admission rules exclude graphs from the generic vocabulary by name.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,14 +29,15 @@
29
29
  "@fossil-lang/corpus": "^0.3.0-alpha.19",
30
30
  "lucide-react": ">=1",
31
31
  "react": "^19.0.0",
32
- "@kanzo-tech/ui": "0.19.0",
33
- "@kanzo-tech/mosaic": "0.19.0"
32
+ "@kanzo-tech/mosaic": "0.21.0",
33
+ "@kanzo-tech/ui": "0.21.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@cosmos.gl/graph": "^3.4.0",
37
37
  "@fossil-lang/corpus": "^0.3.0-alpha.19",
38
38
  "@testing-library/dom": "^10.4.1",
39
39
  "@testing-library/react": "^16.3.2",
40
+ "@testing-library/user-event": "^14.6.1",
40
41
  "@types/react": "^19.0.0",
41
42
  "@types/react-dom": "^19.0.0",
42
43
  "@uwdata/mosaic-core": "^0.29.2",
@@ -46,14 +47,14 @@
46
47
  "react": "^19.0.0",
47
48
  "react-dom": "^19.0.0",
48
49
  "rollup-plugin-preserve-directives": "^0.4.0",
49
- "@kanzo-tech/mosaic": "0.19.0"
50
+ "@kanzo-tech/mosaic": "0.21.0"
50
51
  },
51
- "//size-limit": "Budgets our code only: `ignore` mirrors the peers the build externalises in vite.config.ts, cosmos.gl included (about 190 kB gzipped on its own, and the consumer pays it once whatever this package does). First measured 2026-09-30 at 19.07 kB, before the lag work of `perf/phase1-lag`; 19.13 kB before the whole-corpus store deleted the tileset, 15.29 kB after it, and the budget follows it down so real growth trips the gate.",
52
+ "//size-limit": "Budgets our code only: `ignore` mirrors the peers the build externalises in vite.config.ts, cosmos.gl included (about 190 kB gzipped on its own, and the consumer pays it once whatever this package does). First measured 2026-09-30 at 19.07 kB, before the lag work of `perf/phase1-lag`; 19.13 kB before the whole-corpus store deleted the tileset, 15.29 kB after it, and the budget follows it down so real growth trips the gate. Raised to 18 kB on 2026-10-01 as a decision: 17.71 kB after GraphCounts, GraphSearch and GraphLooks moved in from the workspace showcase (their code left the host, it did not appear from nowhere) and the drag-to-fix release. Raised to 18.5 kB on 2026-10-02 as a decision (Ángel): 18.01 kB once the layout's square placement and the camera that follows a running layout landed.",
52
53
  "size-limit": [
53
54
  {
54
55
  "name": "root barrel (JS)",
55
56
  "path": "dist/index.js",
56
- "limit": "15.5 kB",
57
+ "limit": "18.5 kB",
57
58
  "ignore": [
58
59
  "react",
59
60
  "react-dom",