@memnest/ui-core 0.0.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2398 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ CLUSTER_THRESHOLD: () => CLUSTER_THRESHOLD,
24
+ DEFAULT_GRAPH_FILTER: () => DEFAULT_GRAPH_FILTER,
25
+ DEFAULT_TRACE_BUDGET: () => DEFAULT_TRACE_BUDGET,
26
+ GRAPH_LOAD_LIMIT: () => GRAPH_LOAD_LIMIT,
27
+ IDENTITY_VIEWPORT: () => IDENTITY_VIEWPORT,
28
+ WORKER_LAYOUT_THRESHOLD: () => WORKER_LAYOUT_THRESHOLD,
29
+ ZOOM_LIMITS: () => ZOOM_LIMITS,
30
+ boundsOf: () => boundsOf,
31
+ buildGraphScene: () => buildGraphScene,
32
+ buildTimeline: () => buildTimeline,
33
+ buildTraceRows: () => buildTraceRows,
34
+ clamp: () => clamp,
35
+ clusterNodes: () => clusterNodes,
36
+ clusterRadius: () => clusterRadius,
37
+ computeLayout: () => computeLayout,
38
+ createDetailController: () => createDetailController,
39
+ createFinderController: () => createFinderController,
40
+ createGraphController: () => createGraphController,
41
+ createHitIndex: () => createHitIndex,
42
+ createLineageController: () => createLineageController,
43
+ createSequencer: () => createSequencer,
44
+ createStore: () => createStore,
45
+ createTimelineController: () => createTimelineController,
46
+ createTraceController: () => createTraceController,
47
+ createWorkerLayoutRunner: () => createWorkerLayoutRunner,
48
+ createWorkspace: () => createWorkspace,
49
+ documentRadius: () => documentRadius,
50
+ drawScene: () => drawScene,
51
+ fitViewport: () => fitViewport,
52
+ forceLayout: () => forceLayout,
53
+ inlineLayoutRunner: () => inlineLayoutRunner,
54
+ layeredLayout: () => layeredLayout,
55
+ layoutLineage: () => layoutLineage,
56
+ matchesFilter: () => matchesFilter,
57
+ nodeRadius: () => nodeRadius,
58
+ panBy: () => panBy,
59
+ runLayoutRequest: () => runLayoutRequest,
60
+ seededRandom: () => seededRandom,
61
+ serveLayoutRequests: () => serveLayoutRequests,
62
+ shorten: () => shorten,
63
+ timeTicks: () => timeTicks,
64
+ toScreen: () => toScreen,
65
+ toWorld: () => toWorld,
66
+ versionChain: () => versionChain,
67
+ zoomAt: () => zoomAt
68
+ });
69
+ module.exports = __toCommonJS(src_exports);
70
+
71
+ // src/store.ts
72
+ function createStore(initial) {
73
+ let state = initial;
74
+ const listeners = /* @__PURE__ */ new Set();
75
+ return {
76
+ getState: () => state,
77
+ subscribe(listener) {
78
+ listeners.add(listener);
79
+ return () => listeners.delete(listener);
80
+ },
81
+ set(patch) {
82
+ const next = typeof patch === "function" ? patch(state) : patch;
83
+ state = { ...state, ...next };
84
+ for (const listener of [...listeners]) listener();
85
+ }
86
+ };
87
+ }
88
+ function createSequencer() {
89
+ let current = 0;
90
+ return {
91
+ next: () => ++current,
92
+ isCurrent: (token) => token === current,
93
+ /** Invalidates everything in flight (dispose). */
94
+ cancel: () => {
95
+ current++;
96
+ }
97
+ };
98
+ }
99
+ var errorText = (error) => error instanceof Error ? error.message : String(error);
100
+
101
+ // src/geometry.ts
102
+ var IDENTITY_VIEWPORT = { x: 0, y: 0, k: 1 };
103
+ var ZOOM_LIMITS = { min: 0.02, max: 8 };
104
+ function boundsOf(circles) {
105
+ let bounds = null;
106
+ for (const { x: x3, y: y3, r = 0 } of circles) {
107
+ if (!bounds) bounds = { minX: x3 - r, minY: y3 - r, maxX: x3 + r, maxY: y3 + r };
108
+ else {
109
+ bounds.minX = Math.min(bounds.minX, x3 - r);
110
+ bounds.minY = Math.min(bounds.minY, y3 - r);
111
+ bounds.maxX = Math.max(bounds.maxX, x3 + r);
112
+ bounds.maxY = Math.max(bounds.maxY, y3 + r);
113
+ }
114
+ }
115
+ return bounds;
116
+ }
117
+ function fitViewport(bounds, size, padding = 60, maxK = 2.5) {
118
+ if (!bounds || size.width <= 0 || size.height <= 0) return { x: size.width / 2, y: size.height / 2, k: 1 };
119
+ const width = Math.max(bounds.maxX - bounds.minX, 1);
120
+ const height = Math.max(bounds.maxY - bounds.minY, 1);
121
+ const k = clamp(
122
+ Math.min((size.width - padding * 2) / width, (size.height - padding * 2) / height),
123
+ ZOOM_LIMITS.min,
124
+ maxK
125
+ );
126
+ const cx = (bounds.minX + bounds.maxX) / 2;
127
+ const cy = (bounds.minY + bounds.maxY) / 2;
128
+ return { x: size.width / 2 - cx * k, y: size.height / 2 - cy * k, k };
129
+ }
130
+ function zoomAt(viewport, screen, factor) {
131
+ const k = clamp(viewport.k * factor, ZOOM_LIMITS.min, ZOOM_LIMITS.max);
132
+ const world = toWorld(viewport, screen);
133
+ return { x: screen.x - world.x * k, y: screen.y - world.y * k, k };
134
+ }
135
+ var panBy = (viewport, dx, dy) => ({ ...viewport, x: viewport.x + dx, y: viewport.y + dy });
136
+ var toWorld = (viewport, screen) => ({ x: (screen.x - viewport.x) / viewport.k, y: (screen.y - viewport.y) / viewport.k });
137
+ var toScreen = (viewport, world) => ({ x: world.x * viewport.k + viewport.x, y: world.y * viewport.k + viewport.y });
138
+ var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
139
+
140
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/add.js
141
+ function add_default(d) {
142
+ const x3 = +this._x.call(null, d), y3 = +this._y.call(null, d);
143
+ return add(this.cover(x3, y3), x3, y3, d);
144
+ }
145
+ function add(tree, x3, y3, d) {
146
+ if (isNaN(x3) || isNaN(y3)) return tree;
147
+ var parent, node = tree._root, leaf = { data: d }, x0 = tree._x0, y0 = tree._y0, x1 = tree._x1, y1 = tree._y1, xm, ym, xp, yp, right, bottom, i, j;
148
+ if (!node) return tree._root = leaf, tree;
149
+ while (node.length) {
150
+ if (right = x3 >= (xm = (x0 + x1) / 2)) x0 = xm;
151
+ else x1 = xm;
152
+ if (bottom = y3 >= (ym = (y0 + y1) / 2)) y0 = ym;
153
+ else y1 = ym;
154
+ if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
155
+ }
156
+ xp = +tree._x.call(null, node.data);
157
+ yp = +tree._y.call(null, node.data);
158
+ if (x3 === xp && y3 === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
159
+ do {
160
+ parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
161
+ if (right = x3 >= (xm = (x0 + x1) / 2)) x0 = xm;
162
+ else x1 = xm;
163
+ if (bottom = y3 >= (ym = (y0 + y1) / 2)) y0 = ym;
164
+ else y1 = ym;
165
+ } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | xp >= xm));
166
+ return parent[j] = node, parent[i] = leaf, tree;
167
+ }
168
+ function addAll(data) {
169
+ var d, i, n = data.length, x3, y3, xz = new Array(n), yz = new Array(n), x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
170
+ for (i = 0; i < n; ++i) {
171
+ if (isNaN(x3 = +this._x.call(null, d = data[i])) || isNaN(y3 = +this._y.call(null, d))) continue;
172
+ xz[i] = x3;
173
+ yz[i] = y3;
174
+ if (x3 < x0) x0 = x3;
175
+ if (x3 > x1) x1 = x3;
176
+ if (y3 < y0) y0 = y3;
177
+ if (y3 > y1) y1 = y3;
178
+ }
179
+ if (x0 > x1 || y0 > y1) return this;
180
+ this.cover(x0, y0).cover(x1, y1);
181
+ for (i = 0; i < n; ++i) {
182
+ add(this, xz[i], yz[i], data[i]);
183
+ }
184
+ return this;
185
+ }
186
+
187
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/cover.js
188
+ function cover_default(x3, y3) {
189
+ if (isNaN(x3 = +x3) || isNaN(y3 = +y3)) return this;
190
+ var x0 = this._x0, y0 = this._y0, x1 = this._x1, y1 = this._y1;
191
+ if (isNaN(x0)) {
192
+ x1 = (x0 = Math.floor(x3)) + 1;
193
+ y1 = (y0 = Math.floor(y3)) + 1;
194
+ } else {
195
+ var z = x1 - x0 || 1, node = this._root, parent, i;
196
+ while (x0 > x3 || x3 >= x1 || y0 > y3 || y3 >= y1) {
197
+ i = (y3 < y0) << 1 | x3 < x0;
198
+ parent = new Array(4), parent[i] = node, node = parent, z *= 2;
199
+ switch (i) {
200
+ case 0:
201
+ x1 = x0 + z, y1 = y0 + z;
202
+ break;
203
+ case 1:
204
+ x0 = x1 - z, y1 = y0 + z;
205
+ break;
206
+ case 2:
207
+ x1 = x0 + z, y0 = y1 - z;
208
+ break;
209
+ case 3:
210
+ x0 = x1 - z, y0 = y1 - z;
211
+ break;
212
+ }
213
+ }
214
+ if (this._root && this._root.length) this._root = node;
215
+ }
216
+ this._x0 = x0;
217
+ this._y0 = y0;
218
+ this._x1 = x1;
219
+ this._y1 = y1;
220
+ return this;
221
+ }
222
+
223
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/data.js
224
+ function data_default() {
225
+ var data = [];
226
+ this.visit(function(node) {
227
+ if (!node.length) do
228
+ data.push(node.data);
229
+ while (node = node.next);
230
+ });
231
+ return data;
232
+ }
233
+
234
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/extent.js
235
+ function extent_default(_) {
236
+ return arguments.length ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) : isNaN(this._x0) ? void 0 : [[this._x0, this._y0], [this._x1, this._y1]];
237
+ }
238
+
239
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/quad.js
240
+ function quad_default(node, x0, y0, x1, y1) {
241
+ this.node = node;
242
+ this.x0 = x0;
243
+ this.y0 = y0;
244
+ this.x1 = x1;
245
+ this.y1 = y1;
246
+ }
247
+
248
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/find.js
249
+ function find_default(x3, y3, radius) {
250
+ var data, x0 = this._x0, y0 = this._y0, x1, y1, x22, y22, x32 = this._x1, y32 = this._y1, quads = [], node = this._root, q, i;
251
+ if (node) quads.push(new quad_default(node, x0, y0, x32, y32));
252
+ if (radius == null) radius = Infinity;
253
+ else {
254
+ x0 = x3 - radius, y0 = y3 - radius;
255
+ x32 = x3 + radius, y32 = y3 + radius;
256
+ radius *= radius;
257
+ }
258
+ while (q = quads.pop()) {
259
+ if (!(node = q.node) || (x1 = q.x0) > x32 || (y1 = q.y0) > y32 || (x22 = q.x1) < x0 || (y22 = q.y1) < y0) continue;
260
+ if (node.length) {
261
+ var xm = (x1 + x22) / 2, ym = (y1 + y22) / 2;
262
+ quads.push(
263
+ new quad_default(node[3], xm, ym, x22, y22),
264
+ new quad_default(node[2], x1, ym, xm, y22),
265
+ new quad_default(node[1], xm, y1, x22, ym),
266
+ new quad_default(node[0], x1, y1, xm, ym)
267
+ );
268
+ if (i = (y3 >= ym) << 1 | x3 >= xm) {
269
+ q = quads[quads.length - 1];
270
+ quads[quads.length - 1] = quads[quads.length - 1 - i];
271
+ quads[quads.length - 1 - i] = q;
272
+ }
273
+ } else {
274
+ var dx = x3 - +this._x.call(null, node.data), dy = y3 - +this._y.call(null, node.data), d2 = dx * dx + dy * dy;
275
+ if (d2 < radius) {
276
+ var d = Math.sqrt(radius = d2);
277
+ x0 = x3 - d, y0 = y3 - d;
278
+ x32 = x3 + d, y32 = y3 + d;
279
+ data = node.data;
280
+ }
281
+ }
282
+ }
283
+ return data;
284
+ }
285
+
286
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/remove.js
287
+ function remove_default(d) {
288
+ if (isNaN(x3 = +this._x.call(null, d)) || isNaN(y3 = +this._y.call(null, d))) return this;
289
+ var parent, node = this._root, retainer, previous, next, x0 = this._x0, y0 = this._y0, x1 = this._x1, y1 = this._y1, x3, y3, xm, ym, right, bottom, i, j;
290
+ if (!node) return this;
291
+ if (node.length) while (true) {
292
+ if (right = x3 >= (xm = (x0 + x1) / 2)) x0 = xm;
293
+ else x1 = xm;
294
+ if (bottom = y3 >= (ym = (y0 + y1) / 2)) y0 = ym;
295
+ else y1 = ym;
296
+ if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
297
+ if (!node.length) break;
298
+ if (parent[i + 1 & 3] || parent[i + 2 & 3] || parent[i + 3 & 3]) retainer = parent, j = i;
299
+ }
300
+ while (node.data !== d) if (!(previous = node, node = node.next)) return this;
301
+ if (next = node.next) delete node.next;
302
+ if (previous) return next ? previous.next = next : delete previous.next, this;
303
+ if (!parent) return this._root = next, this;
304
+ next ? parent[i] = next : delete parent[i];
305
+ if ((node = parent[0] || parent[1] || parent[2] || parent[3]) && node === (parent[3] || parent[2] || parent[1] || parent[0]) && !node.length) {
306
+ if (retainer) retainer[j] = node;
307
+ else this._root = node;
308
+ }
309
+ return this;
310
+ }
311
+ function removeAll(data) {
312
+ for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
313
+ return this;
314
+ }
315
+
316
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/root.js
317
+ function root_default() {
318
+ return this._root;
319
+ }
320
+
321
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/size.js
322
+ function size_default() {
323
+ var size = 0;
324
+ this.visit(function(node) {
325
+ if (!node.length) do
326
+ ++size;
327
+ while (node = node.next);
328
+ });
329
+ return size;
330
+ }
331
+
332
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/visit.js
333
+ function visit_default(callback) {
334
+ var quads = [], q, node = this._root, child, x0, y0, x1, y1;
335
+ if (node) quads.push(new quad_default(node, this._x0, this._y0, this._x1, this._y1));
336
+ while (q = quads.pop()) {
337
+ if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
338
+ var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
339
+ if (child = node[3]) quads.push(new quad_default(child, xm, ym, x1, y1));
340
+ if (child = node[2]) quads.push(new quad_default(child, x0, ym, xm, y1));
341
+ if (child = node[1]) quads.push(new quad_default(child, xm, y0, x1, ym));
342
+ if (child = node[0]) quads.push(new quad_default(child, x0, y0, xm, ym));
343
+ }
344
+ }
345
+ return this;
346
+ }
347
+
348
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/visitAfter.js
349
+ function visitAfter_default(callback) {
350
+ var quads = [], next = [], q;
351
+ if (this._root) quads.push(new quad_default(this._root, this._x0, this._y0, this._x1, this._y1));
352
+ while (q = quads.pop()) {
353
+ var node = q.node;
354
+ if (node.length) {
355
+ var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
356
+ if (child = node[0]) quads.push(new quad_default(child, x0, y0, xm, ym));
357
+ if (child = node[1]) quads.push(new quad_default(child, xm, y0, x1, ym));
358
+ if (child = node[2]) quads.push(new quad_default(child, x0, ym, xm, y1));
359
+ if (child = node[3]) quads.push(new quad_default(child, xm, ym, x1, y1));
360
+ }
361
+ next.push(q);
362
+ }
363
+ while (q = next.pop()) {
364
+ callback(q.node, q.x0, q.y0, q.x1, q.y1);
365
+ }
366
+ return this;
367
+ }
368
+
369
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/x.js
370
+ function defaultX(d) {
371
+ return d[0];
372
+ }
373
+ function x_default(_) {
374
+ return arguments.length ? (this._x = _, this) : this._x;
375
+ }
376
+
377
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/y.js
378
+ function defaultY(d) {
379
+ return d[1];
380
+ }
381
+ function y_default(_) {
382
+ return arguments.length ? (this._y = _, this) : this._y;
383
+ }
384
+
385
+ // ../../node_modules/.pnpm/d3-quadtree@3.0.1/node_modules/d3-quadtree/src/quadtree.js
386
+ function quadtree(nodes, x3, y3) {
387
+ var tree = new Quadtree(x3 == null ? defaultX : x3, y3 == null ? defaultY : y3, NaN, NaN, NaN, NaN);
388
+ return nodes == null ? tree : tree.addAll(nodes);
389
+ }
390
+ function Quadtree(x3, y3, x0, y0, x1, y1) {
391
+ this._x = x3;
392
+ this._y = y3;
393
+ this._x0 = x0;
394
+ this._y0 = y0;
395
+ this._x1 = x1;
396
+ this._y1 = y1;
397
+ this._root = void 0;
398
+ }
399
+ function leaf_copy(leaf) {
400
+ var copy = { data: leaf.data }, next = copy;
401
+ while (leaf = leaf.next) next = next.next = { data: leaf.data };
402
+ return copy;
403
+ }
404
+ var treeProto = quadtree.prototype = Quadtree.prototype;
405
+ treeProto.copy = function() {
406
+ var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), node = this._root, nodes, child;
407
+ if (!node) return copy;
408
+ if (!node.length) return copy._root = leaf_copy(node), copy;
409
+ nodes = [{ source: node, target: copy._root = new Array(4) }];
410
+ while (node = nodes.pop()) {
411
+ for (var i = 0; i < 4; ++i) {
412
+ if (child = node.source[i]) {
413
+ if (child.length) nodes.push({ source: child, target: node.target[i] = new Array(4) });
414
+ else node.target[i] = leaf_copy(child);
415
+ }
416
+ }
417
+ }
418
+ return copy;
419
+ };
420
+ treeProto.add = add_default;
421
+ treeProto.addAll = addAll;
422
+ treeProto.cover = cover_default;
423
+ treeProto.data = data_default;
424
+ treeProto.extent = extent_default;
425
+ treeProto.find = find_default;
426
+ treeProto.remove = remove_default;
427
+ treeProto.removeAll = removeAll;
428
+ treeProto.root = root_default;
429
+ treeProto.size = size_default;
430
+ treeProto.visit = visit_default;
431
+ treeProto.visitAfter = visitAfter_default;
432
+ treeProto.x = x_default;
433
+ treeProto.y = y_default;
434
+
435
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/constant.js
436
+ function constant_default(x3) {
437
+ return function() {
438
+ return x3;
439
+ };
440
+ }
441
+
442
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/jiggle.js
443
+ function jiggle_default(random) {
444
+ return (random() - 0.5) * 1e-6;
445
+ }
446
+
447
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/collide.js
448
+ function x(d) {
449
+ return d.x + d.vx;
450
+ }
451
+ function y(d) {
452
+ return d.y + d.vy;
453
+ }
454
+ function collide_default(radius) {
455
+ var nodes, radii, random, strength = 1, iterations = 1;
456
+ if (typeof radius !== "function") radius = constant_default(radius == null ? 1 : +radius);
457
+ function force() {
458
+ var i, n = nodes.length, tree, node, xi, yi, ri, ri2;
459
+ for (var k = 0; k < iterations; ++k) {
460
+ tree = quadtree(nodes, x, y).visitAfter(prepare);
461
+ for (i = 0; i < n; ++i) {
462
+ node = nodes[i];
463
+ ri = radii[node.index], ri2 = ri * ri;
464
+ xi = node.x + node.vx;
465
+ yi = node.y + node.vy;
466
+ tree.visit(apply);
467
+ }
468
+ }
469
+ function apply(quad, x0, y0, x1, y1) {
470
+ var data = quad.data, rj = quad.r, r = ri + rj;
471
+ if (data) {
472
+ if (data.index > node.index) {
473
+ var x3 = xi - data.x - data.vx, y3 = yi - data.y - data.vy, l = x3 * x3 + y3 * y3;
474
+ if (l < r * r) {
475
+ if (x3 === 0) x3 = jiggle_default(random), l += x3 * x3;
476
+ if (y3 === 0) y3 = jiggle_default(random), l += y3 * y3;
477
+ l = (r - (l = Math.sqrt(l))) / l * strength;
478
+ node.vx += (x3 *= l) * (r = (rj *= rj) / (ri2 + rj));
479
+ node.vy += (y3 *= l) * r;
480
+ data.vx -= x3 * (r = 1 - r);
481
+ data.vy -= y3 * r;
482
+ }
483
+ }
484
+ return;
485
+ }
486
+ return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
487
+ }
488
+ }
489
+ function prepare(quad) {
490
+ if (quad.data) return quad.r = radii[quad.data.index];
491
+ for (var i = quad.r = 0; i < 4; ++i) {
492
+ if (quad[i] && quad[i].r > quad.r) {
493
+ quad.r = quad[i].r;
494
+ }
495
+ }
496
+ }
497
+ function initialize() {
498
+ if (!nodes) return;
499
+ var i, n = nodes.length, node;
500
+ radii = new Array(n);
501
+ for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
502
+ }
503
+ force.initialize = function(_nodes, _random) {
504
+ nodes = _nodes;
505
+ random = _random;
506
+ initialize();
507
+ };
508
+ force.iterations = function(_) {
509
+ return arguments.length ? (iterations = +_, force) : iterations;
510
+ };
511
+ force.strength = function(_) {
512
+ return arguments.length ? (strength = +_, force) : strength;
513
+ };
514
+ force.radius = function(_) {
515
+ return arguments.length ? (radius = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : radius;
516
+ };
517
+ return force;
518
+ }
519
+
520
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/link.js
521
+ function index(d) {
522
+ return d.index;
523
+ }
524
+ function find(nodeById, nodeId) {
525
+ var node = nodeById.get(nodeId);
526
+ if (!node) throw new Error("node not found: " + nodeId);
527
+ return node;
528
+ }
529
+ function link_default(links) {
530
+ var id = index, strength = defaultStrength, strengths, distance = constant_default(30), distances, nodes, count, bias, random, iterations = 1;
531
+ if (links == null) links = [];
532
+ function defaultStrength(link) {
533
+ return 1 / Math.min(count[link.source.index], count[link.target.index]);
534
+ }
535
+ function force(alpha) {
536
+ for (var k = 0, n = links.length; k < iterations; ++k) {
537
+ for (var i = 0, link, source, target, x3, y3, l, b; i < n; ++i) {
538
+ link = links[i], source = link.source, target = link.target;
539
+ x3 = target.x + target.vx - source.x - source.vx || jiggle_default(random);
540
+ y3 = target.y + target.vy - source.y - source.vy || jiggle_default(random);
541
+ l = Math.sqrt(x3 * x3 + y3 * y3);
542
+ l = (l - distances[i]) / l * alpha * strengths[i];
543
+ x3 *= l, y3 *= l;
544
+ target.vx -= x3 * (b = bias[i]);
545
+ target.vy -= y3 * b;
546
+ source.vx += x3 * (b = 1 - b);
547
+ source.vy += y3 * b;
548
+ }
549
+ }
550
+ }
551
+ function initialize() {
552
+ if (!nodes) return;
553
+ var i, n = nodes.length, m2 = links.length, nodeById = new Map(nodes.map((d, i2) => [id(d, i2, nodes), d])), link;
554
+ for (i = 0, count = new Array(n); i < m2; ++i) {
555
+ link = links[i], link.index = i;
556
+ if (typeof link.source !== "object") link.source = find(nodeById, link.source);
557
+ if (typeof link.target !== "object") link.target = find(nodeById, link.target);
558
+ count[link.source.index] = (count[link.source.index] || 0) + 1;
559
+ count[link.target.index] = (count[link.target.index] || 0) + 1;
560
+ }
561
+ for (i = 0, bias = new Array(m2); i < m2; ++i) {
562
+ link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
563
+ }
564
+ strengths = new Array(m2), initializeStrength();
565
+ distances = new Array(m2), initializeDistance();
566
+ }
567
+ function initializeStrength() {
568
+ if (!nodes) return;
569
+ for (var i = 0, n = links.length; i < n; ++i) {
570
+ strengths[i] = +strength(links[i], i, links);
571
+ }
572
+ }
573
+ function initializeDistance() {
574
+ if (!nodes) return;
575
+ for (var i = 0, n = links.length; i < n; ++i) {
576
+ distances[i] = +distance(links[i], i, links);
577
+ }
578
+ }
579
+ force.initialize = function(_nodes, _random) {
580
+ nodes = _nodes;
581
+ random = _random;
582
+ initialize();
583
+ };
584
+ force.links = function(_) {
585
+ return arguments.length ? (links = _, initialize(), force) : links;
586
+ };
587
+ force.id = function(_) {
588
+ return arguments.length ? (id = _, force) : id;
589
+ };
590
+ force.iterations = function(_) {
591
+ return arguments.length ? (iterations = +_, force) : iterations;
592
+ };
593
+ force.strength = function(_) {
594
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default(+_), initializeStrength(), force) : strength;
595
+ };
596
+ force.distance = function(_) {
597
+ return arguments.length ? (distance = typeof _ === "function" ? _ : constant_default(+_), initializeDistance(), force) : distance;
598
+ };
599
+ return force;
600
+ }
601
+
602
+ // ../../node_modules/.pnpm/d3-dispatch@3.0.1/node_modules/d3-dispatch/src/dispatch.js
603
+ var noop = { value: () => {
604
+ } };
605
+ function dispatch() {
606
+ for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
607
+ if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
608
+ _[t] = [];
609
+ }
610
+ return new Dispatch(_);
611
+ }
612
+ function Dispatch(_) {
613
+ this._ = _;
614
+ }
615
+ function parseTypenames(typenames, types) {
616
+ return typenames.trim().split(/^|\s+/).map(function(t) {
617
+ var name = "", i = t.indexOf(".");
618
+ if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
619
+ if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
620
+ return { type: t, name };
621
+ });
622
+ }
623
+ Dispatch.prototype = dispatch.prototype = {
624
+ constructor: Dispatch,
625
+ on: function(typename, callback) {
626
+ var _ = this._, T = parseTypenames(typename + "", _), t, i = -1, n = T.length;
627
+ if (arguments.length < 2) {
628
+ while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
629
+ return;
630
+ }
631
+ if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
632
+ while (++i < n) {
633
+ if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);
634
+ else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
635
+ }
636
+ return this;
637
+ },
638
+ copy: function() {
639
+ var copy = {}, _ = this._;
640
+ for (var t in _) copy[t] = _[t].slice();
641
+ return new Dispatch(copy);
642
+ },
643
+ call: function(type, that) {
644
+ if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
645
+ if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
646
+ for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
647
+ },
648
+ apply: function(type, that, args) {
649
+ if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
650
+ for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
651
+ }
652
+ };
653
+ function get(type, name) {
654
+ for (var i = 0, n = type.length, c2; i < n; ++i) {
655
+ if ((c2 = type[i]).name === name) {
656
+ return c2.value;
657
+ }
658
+ }
659
+ }
660
+ function set(type, name, callback) {
661
+ for (var i = 0, n = type.length; i < n; ++i) {
662
+ if (type[i].name === name) {
663
+ type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
664
+ break;
665
+ }
666
+ }
667
+ if (callback != null) type.push({ name, value: callback });
668
+ return type;
669
+ }
670
+ var dispatch_default = dispatch;
671
+
672
+ // ../../node_modules/.pnpm/d3-timer@3.0.1/node_modules/d3-timer/src/timer.js
673
+ var frame = 0;
674
+ var timeout = 0;
675
+ var interval = 0;
676
+ var pokeDelay = 1e3;
677
+ var taskHead;
678
+ var taskTail;
679
+ var clockLast = 0;
680
+ var clockNow = 0;
681
+ var clockSkew = 0;
682
+ var clock = typeof performance === "object" && performance.now ? performance : Date;
683
+ var setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) {
684
+ setTimeout(f, 17);
685
+ };
686
+ function now() {
687
+ return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
688
+ }
689
+ function clearNow() {
690
+ clockNow = 0;
691
+ }
692
+ function Timer() {
693
+ this._call = this._time = this._next = null;
694
+ }
695
+ Timer.prototype = timer.prototype = {
696
+ constructor: Timer,
697
+ restart: function(callback, delay, time) {
698
+ if (typeof callback !== "function") throw new TypeError("callback is not a function");
699
+ time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
700
+ if (!this._next && taskTail !== this) {
701
+ if (taskTail) taskTail._next = this;
702
+ else taskHead = this;
703
+ taskTail = this;
704
+ }
705
+ this._call = callback;
706
+ this._time = time;
707
+ sleep();
708
+ },
709
+ stop: function() {
710
+ if (this._call) {
711
+ this._call = null;
712
+ this._time = Infinity;
713
+ sleep();
714
+ }
715
+ }
716
+ };
717
+ function timer(callback, delay, time) {
718
+ var t = new Timer();
719
+ t.restart(callback, delay, time);
720
+ return t;
721
+ }
722
+ function timerFlush() {
723
+ now();
724
+ ++frame;
725
+ var t = taskHead, e;
726
+ while (t) {
727
+ if ((e = clockNow - t._time) >= 0) t._call.call(void 0, e);
728
+ t = t._next;
729
+ }
730
+ --frame;
731
+ }
732
+ function wake() {
733
+ clockNow = (clockLast = clock.now()) + clockSkew;
734
+ frame = timeout = 0;
735
+ try {
736
+ timerFlush();
737
+ } finally {
738
+ frame = 0;
739
+ nap();
740
+ clockNow = 0;
741
+ }
742
+ }
743
+ function poke() {
744
+ var now2 = clock.now(), delay = now2 - clockLast;
745
+ if (delay > pokeDelay) clockSkew -= delay, clockLast = now2;
746
+ }
747
+ function nap() {
748
+ var t0, t1 = taskHead, t2, time = Infinity;
749
+ while (t1) {
750
+ if (t1._call) {
751
+ if (time > t1._time) time = t1._time;
752
+ t0 = t1, t1 = t1._next;
753
+ } else {
754
+ t2 = t1._next, t1._next = null;
755
+ t1 = t0 ? t0._next = t2 : taskHead = t2;
756
+ }
757
+ }
758
+ taskTail = t0;
759
+ sleep(time);
760
+ }
761
+ function sleep(time) {
762
+ if (frame) return;
763
+ if (timeout) timeout = clearTimeout(timeout);
764
+ var delay = time - clockNow;
765
+ if (delay > 24) {
766
+ if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
767
+ if (interval) interval = clearInterval(interval);
768
+ } else {
769
+ if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
770
+ frame = 1, setFrame(wake);
771
+ }
772
+ }
773
+
774
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/lcg.js
775
+ var a = 1664525;
776
+ var c = 1013904223;
777
+ var m = 4294967296;
778
+ function lcg_default() {
779
+ let s = 1;
780
+ return () => (s = (a * s + c) % m) / m;
781
+ }
782
+
783
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/simulation.js
784
+ function x2(d) {
785
+ return d.x;
786
+ }
787
+ function y2(d) {
788
+ return d.y;
789
+ }
790
+ var initialRadius = 10;
791
+ var initialAngle = Math.PI * (3 - Math.sqrt(5));
792
+ function simulation_default(nodes) {
793
+ var simulation, alpha = 1, alphaMin = 1e-3, alphaDecay = 1 - Math.pow(alphaMin, 1 / 300), alphaTarget = 0, velocityDecay = 0.6, forces = /* @__PURE__ */ new Map(), stepper = timer(step), event = dispatch_default("tick", "end"), random = lcg_default();
794
+ if (nodes == null) nodes = [];
795
+ function step() {
796
+ tick();
797
+ event.call("tick", simulation);
798
+ if (alpha < alphaMin) {
799
+ stepper.stop();
800
+ event.call("end", simulation);
801
+ }
802
+ }
803
+ function tick(iterations) {
804
+ var i, n = nodes.length, node;
805
+ if (iterations === void 0) iterations = 1;
806
+ for (var k = 0; k < iterations; ++k) {
807
+ alpha += (alphaTarget - alpha) * alphaDecay;
808
+ forces.forEach(function(force) {
809
+ force(alpha);
810
+ });
811
+ for (i = 0; i < n; ++i) {
812
+ node = nodes[i];
813
+ if (node.fx == null) node.x += node.vx *= velocityDecay;
814
+ else node.x = node.fx, node.vx = 0;
815
+ if (node.fy == null) node.y += node.vy *= velocityDecay;
816
+ else node.y = node.fy, node.vy = 0;
817
+ }
818
+ }
819
+ return simulation;
820
+ }
821
+ function initializeNodes() {
822
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
823
+ node = nodes[i], node.index = i;
824
+ if (node.fx != null) node.x = node.fx;
825
+ if (node.fy != null) node.y = node.fy;
826
+ if (isNaN(node.x) || isNaN(node.y)) {
827
+ var radius = initialRadius * Math.sqrt(0.5 + i), angle = i * initialAngle;
828
+ node.x = radius * Math.cos(angle);
829
+ node.y = radius * Math.sin(angle);
830
+ }
831
+ if (isNaN(node.vx) || isNaN(node.vy)) {
832
+ node.vx = node.vy = 0;
833
+ }
834
+ }
835
+ }
836
+ function initializeForce(force) {
837
+ if (force.initialize) force.initialize(nodes, random);
838
+ return force;
839
+ }
840
+ initializeNodes();
841
+ return simulation = {
842
+ tick,
843
+ restart: function() {
844
+ return stepper.restart(step), simulation;
845
+ },
846
+ stop: function() {
847
+ return stepper.stop(), simulation;
848
+ },
849
+ nodes: function(_) {
850
+ return arguments.length ? (nodes = _, initializeNodes(), forces.forEach(initializeForce), simulation) : nodes;
851
+ },
852
+ alpha: function(_) {
853
+ return arguments.length ? (alpha = +_, simulation) : alpha;
854
+ },
855
+ alphaMin: function(_) {
856
+ return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
857
+ },
858
+ alphaDecay: function(_) {
859
+ return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
860
+ },
861
+ alphaTarget: function(_) {
862
+ return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
863
+ },
864
+ velocityDecay: function(_) {
865
+ return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
866
+ },
867
+ randomSource: function(_) {
868
+ return arguments.length ? (random = _, forces.forEach(initializeForce), simulation) : random;
869
+ },
870
+ force: function(name, _) {
871
+ return arguments.length > 1 ? (_ == null ? forces.delete(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name);
872
+ },
873
+ find: function(x3, y3, radius) {
874
+ var i = 0, n = nodes.length, dx, dy, d2, node, closest;
875
+ if (radius == null) radius = Infinity;
876
+ else radius *= radius;
877
+ for (i = 0; i < n; ++i) {
878
+ node = nodes[i];
879
+ dx = x3 - node.x;
880
+ dy = y3 - node.y;
881
+ d2 = dx * dx + dy * dy;
882
+ if (d2 < radius) closest = node, radius = d2;
883
+ }
884
+ return closest;
885
+ },
886
+ on: function(name, _) {
887
+ return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
888
+ }
889
+ };
890
+ }
891
+
892
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/manyBody.js
893
+ function manyBody_default() {
894
+ var nodes, node, random, alpha, strength = constant_default(-30), strengths, distanceMin2 = 1, distanceMax2 = Infinity, theta2 = 0.81;
895
+ function force(_) {
896
+ var i, n = nodes.length, tree = quadtree(nodes, x2, y2).visitAfter(accumulate);
897
+ for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
898
+ }
899
+ function initialize() {
900
+ if (!nodes) return;
901
+ var i, n = nodes.length, node2;
902
+ strengths = new Array(n);
903
+ for (i = 0; i < n; ++i) node2 = nodes[i], strengths[node2.index] = +strength(node2, i, nodes);
904
+ }
905
+ function accumulate(quad) {
906
+ var strength2 = 0, q, c2, weight = 0, x3, y3, i;
907
+ if (quad.length) {
908
+ for (x3 = y3 = i = 0; i < 4; ++i) {
909
+ if ((q = quad[i]) && (c2 = Math.abs(q.value))) {
910
+ strength2 += q.value, weight += c2, x3 += c2 * q.x, y3 += c2 * q.y;
911
+ }
912
+ }
913
+ quad.x = x3 / weight;
914
+ quad.y = y3 / weight;
915
+ } else {
916
+ q = quad;
917
+ q.x = q.data.x;
918
+ q.y = q.data.y;
919
+ do
920
+ strength2 += strengths[q.data.index];
921
+ while (q = q.next);
922
+ }
923
+ quad.value = strength2;
924
+ }
925
+ function apply(quad, x1, _, x22) {
926
+ if (!quad.value) return true;
927
+ var x3 = quad.x - node.x, y3 = quad.y - node.y, w = x22 - x1, l = x3 * x3 + y3 * y3;
928
+ if (w * w / theta2 < l) {
929
+ if (l < distanceMax2) {
930
+ if (x3 === 0) x3 = jiggle_default(random), l += x3 * x3;
931
+ if (y3 === 0) y3 = jiggle_default(random), l += y3 * y3;
932
+ if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
933
+ node.vx += x3 * quad.value * alpha / l;
934
+ node.vy += y3 * quad.value * alpha / l;
935
+ }
936
+ return true;
937
+ } else if (quad.length || l >= distanceMax2) return;
938
+ if (quad.data !== node || quad.next) {
939
+ if (x3 === 0) x3 = jiggle_default(random), l += x3 * x3;
940
+ if (y3 === 0) y3 = jiggle_default(random), l += y3 * y3;
941
+ if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
942
+ }
943
+ do
944
+ if (quad.data !== node) {
945
+ w = strengths[quad.data.index] * alpha / l;
946
+ node.vx += x3 * w;
947
+ node.vy += y3 * w;
948
+ }
949
+ while (quad = quad.next);
950
+ }
951
+ force.initialize = function(_nodes, _random) {
952
+ nodes = _nodes;
953
+ random = _random;
954
+ initialize();
955
+ };
956
+ force.strength = function(_) {
957
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : strength;
958
+ };
959
+ force.distanceMin = function(_) {
960
+ return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
961
+ };
962
+ force.distanceMax = function(_) {
963
+ return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
964
+ };
965
+ force.theta = function(_) {
966
+ return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
967
+ };
968
+ return force;
969
+ }
970
+
971
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/x.js
972
+ function x_default2(x3) {
973
+ var strength = constant_default(0.1), nodes, strengths, xz;
974
+ if (typeof x3 !== "function") x3 = constant_default(x3 == null ? 0 : +x3);
975
+ function force(alpha) {
976
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
977
+ node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
978
+ }
979
+ }
980
+ function initialize() {
981
+ if (!nodes) return;
982
+ var i, n = nodes.length;
983
+ strengths = new Array(n);
984
+ xz = new Array(n);
985
+ for (i = 0; i < n; ++i) {
986
+ strengths[i] = isNaN(xz[i] = +x3(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
987
+ }
988
+ }
989
+ force.initialize = function(_) {
990
+ nodes = _;
991
+ initialize();
992
+ };
993
+ force.strength = function(_) {
994
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : strength;
995
+ };
996
+ force.x = function(_) {
997
+ return arguments.length ? (x3 = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : x3;
998
+ };
999
+ return force;
1000
+ }
1001
+
1002
+ // ../../node_modules/.pnpm/d3-force@3.0.0/node_modules/d3-force/src/y.js
1003
+ function y_default2(y3) {
1004
+ var strength = constant_default(0.1), nodes, strengths, yz;
1005
+ if (typeof y3 !== "function") y3 = constant_default(y3 == null ? 0 : +y3);
1006
+ function force(alpha) {
1007
+ for (var i = 0, n = nodes.length, node; i < n; ++i) {
1008
+ node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
1009
+ }
1010
+ }
1011
+ function initialize() {
1012
+ if (!nodes) return;
1013
+ var i, n = nodes.length;
1014
+ strengths = new Array(n);
1015
+ yz = new Array(n);
1016
+ for (i = 0; i < n; ++i) {
1017
+ strengths[i] = isNaN(yz[i] = +y3(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
1018
+ }
1019
+ }
1020
+ force.initialize = function(_) {
1021
+ nodes = _;
1022
+ initialize();
1023
+ };
1024
+ force.strength = function(_) {
1025
+ return arguments.length ? (strength = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : strength;
1026
+ };
1027
+ force.y = function(_) {
1028
+ return arguments.length ? (y3 = typeof _ === "function" ? _ : constant_default(+_), initialize(), force) : y3;
1029
+ };
1030
+ return force;
1031
+ }
1032
+
1033
+ // src/layout/force.ts
1034
+ function seededRandom(seed) {
1035
+ let state = seed >>> 0;
1036
+ return () => {
1037
+ state = state + 1831565813 >>> 0;
1038
+ let t = state;
1039
+ t = Math.imul(t ^ t >>> 15, t | 1);
1040
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1041
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1042
+ };
1043
+ }
1044
+ function forceLayout(nodes, edges, options = {}) {
1045
+ const initial = options.initial instanceof Map ? options.initial : new Map(options.initial ?? []);
1046
+ const scatter = seededRandom((options.seed ?? 1) * 7919);
1047
+ const disk = Math.sqrt(nodes.length) * ((options.linkDistance ?? 36) * 0.9);
1048
+ const simNodes = nodes.map((n2) => {
1049
+ const start = initial.get(n2.id);
1050
+ if (start) return { id: n2.id, r: n2.r ?? 6, x: start.x, y: start.y };
1051
+ const angle = scatter() * Math.PI * 2;
1052
+ const radius = Math.sqrt(scatter()) * disk;
1053
+ return { id: n2.id, r: n2.r ?? 6, x: Math.cos(angle) * radius, y: Math.sin(angle) * radius };
1054
+ });
1055
+ const ids = new Set(simNodes.map((n2) => n2.id));
1056
+ const links = edges.filter((e) => ids.has(e.from) && ids.has(e.to) && e.from !== e.to).map((e) => ({ source: e.from, target: e.to }));
1057
+ const n = simNodes.length;
1058
+ const iterations = options.iterations ?? (n > 1e3 ? 240 : 300);
1059
+ const simulation = simulation_default(simNodes).randomSource(seededRandom(options.seed ?? 1)).alphaDecay(1 - Math.pow(1e-3, 1 / iterations)).force(
1060
+ "link",
1061
+ link_default(links).id((d) => d.id).distance(options.linkDistance ?? 36).strength(0.6)
1062
+ ).force("charge", manyBody_default().strength(options.charge ?? -40).theta(0.9)).force("collide", collide_default((d) => d.r + (options.collidePadding ?? 2)).iterations(options.collideIterations ?? 1)).force("x", x_default2(0).strength(0.04)).force("y", y_default2(0).strength(0.04)).stop();
1063
+ for (let i = 0; i < iterations; i++) simulation.tick();
1064
+ const positions = /* @__PURE__ */ new Map();
1065
+ for (const node of simNodes) positions.set(node.id, { x: Math.round(node.x * 10) / 10, y: Math.round(node.y * 10) / 10 });
1066
+ return positions;
1067
+ }
1068
+
1069
+ // src/layout/layered.ts
1070
+ function layeredLayout(nodes, edges, options = {}) {
1071
+ const layerGap = options.layerGap ?? 220;
1072
+ const nodeGap = options.nodeGap ?? 84;
1073
+ const sweeps = options.sweeps ?? 6;
1074
+ const ids = nodes.map((n) => n.id);
1075
+ const known = new Set(ids);
1076
+ const out = new Map(ids.map((id) => [id, []]));
1077
+ const incoming = new Map(ids.map((id) => [id, []]));
1078
+ for (const { from, to } of edges) {
1079
+ if (!known.has(from) || !known.has(to) || from === to) continue;
1080
+ out.get(from).push(to);
1081
+ incoming.get(to).push(from);
1082
+ }
1083
+ const layer = new Map(ids.map((id) => [id, 0]));
1084
+ const remaining = new Map(ids.map((id) => [id, incoming.get(id).length]));
1085
+ const queue = ids.filter((id) => remaining.get(id) === 0);
1086
+ const acyclicOut = new Map(ids.map((id) => [id, []]));
1087
+ for (let i = 0; i < queue.length; i++) {
1088
+ const id = queue[i];
1089
+ for (const to of out.get(id)) {
1090
+ layer.set(to, Math.max(layer.get(to), layer.get(id) + 1));
1091
+ acyclicOut.get(id).push(to);
1092
+ remaining.set(to, remaining.get(to) - 1);
1093
+ if (remaining.get(to) === 0) queue.push(to);
1094
+ }
1095
+ }
1096
+ const layers = [];
1097
+ for (const id of ids) {
1098
+ const l = layer.get(id);
1099
+ (layers[l] ??= []).push(id);
1100
+ }
1101
+ const predecessors = new Map(ids.map((id) => [id, []]));
1102
+ for (const [from, tos] of acyclicOut) for (const to of tos) predecessors.get(to).push(from);
1103
+ const position = /* @__PURE__ */ new Map();
1104
+ const index2 = () => layers.forEach((members) => members.forEach((id, i) => position.set(id, i)));
1105
+ index2();
1106
+ const barycentre = (neighbours, fallback) => neighbours.length === 0 ? fallback : neighbours.reduce((sum, n) => sum + position.get(n), 0) / neighbours.length;
1107
+ for (let sweep = 0; sweep < sweeps; sweep++) {
1108
+ const down = sweep % 2 === 0;
1109
+ const order = down ? layers.slice(1) : layers.slice(0, -1).reverse();
1110
+ for (const members of order) {
1111
+ const keyed = members.map((id, i) => ({
1112
+ id,
1113
+ i,
1114
+ key: barycentre(down ? predecessors.get(id) : acyclicOut.get(id), position.get(id))
1115
+ }));
1116
+ keyed.sort((a2, z) => a2.key - z.key || a2.i - z.i);
1117
+ members.splice(0, members.length, ...keyed.map((k) => k.id));
1118
+ members.forEach((id, i) => position.set(id, i));
1119
+ }
1120
+ }
1121
+ const positions = /* @__PURE__ */ new Map();
1122
+ layers.forEach((members, l) => {
1123
+ members.forEach((id, i) => positions.set(id, { x: l * layerGap, y: (i - (members.length - 1) / 2) * nodeGap }));
1124
+ });
1125
+ return positions;
1126
+ }
1127
+
1128
+ // src/layout/index.ts
1129
+ function computeLayout(nodes, edges, opts) {
1130
+ return opts.algorithm === "layered" ? layeredLayout(nodes, edges, opts) : forceLayout(nodes, edges, opts);
1131
+ }
1132
+ function runLayoutRequest(request) {
1133
+ return request.algorithm === "layered" ? layeredLayout(request.nodes, request.edges, request.options) : forceLayout(request.nodes, request.edges, request.options);
1134
+ }
1135
+
1136
+ // src/layout/runner.ts
1137
+ var WORKER_LAYOUT_THRESHOLD = 500;
1138
+ var inlineLayoutRunner = {
1139
+ run: async (request) => {
1140
+ await new Promise((resolve) => setTimeout(resolve, 0));
1141
+ return runLayoutRequest(request);
1142
+ }
1143
+ };
1144
+ function encodePositions(positions) {
1145
+ const ids = [...positions.keys()];
1146
+ const coords = new Float64Array(ids.length * 2);
1147
+ ids.forEach((id, i) => {
1148
+ const p = positions.get(id);
1149
+ coords[i * 2] = p.x;
1150
+ coords[i * 2 + 1] = p.y;
1151
+ });
1152
+ return { ids, coords };
1153
+ }
1154
+ function decodePositions(ids, coords) {
1155
+ const positions = /* @__PURE__ */ new Map();
1156
+ ids.forEach((id, i) => positions.set(id, { x: coords[i * 2], y: coords[i * 2 + 1] }));
1157
+ return positions;
1158
+ }
1159
+ function serveLayoutRequests(port) {
1160
+ const listener = (event) => {
1161
+ const { id, request } = event.data;
1162
+ try {
1163
+ const { ids, coords } = encodePositions(runLayoutRequest(request));
1164
+ port.postMessage({ id, ids, coords });
1165
+ } catch (error) {
1166
+ port.postMessage({ id, error: error instanceof Error ? error.message : String(error) });
1167
+ }
1168
+ };
1169
+ port.addEventListener("message", listener);
1170
+ return () => port.removeEventListener("message", listener);
1171
+ }
1172
+ function createWorkerLayoutRunner(port, options = {}) {
1173
+ const threshold = options.threshold ?? WORKER_LAYOUT_THRESHOLD;
1174
+ const pending = /* @__PURE__ */ new Map();
1175
+ let nextId = 0;
1176
+ const listener = (event) => {
1177
+ const reply = event.data;
1178
+ const waiter = pending.get(reply.id);
1179
+ if (!waiter) return;
1180
+ pending.delete(reply.id);
1181
+ if ("error" in reply) waiter.reject(new Error(reply.error));
1182
+ else waiter.resolve(decodePositions(reply.ids, reply.coords));
1183
+ };
1184
+ port.addEventListener("message", listener);
1185
+ return {
1186
+ run(request) {
1187
+ if (request.nodes.length < threshold) return inlineLayoutRunner.run(request);
1188
+ const id = ++nextId;
1189
+ return new Promise((resolve, reject) => {
1190
+ pending.set(id, { resolve, reject });
1191
+ port.postMessage({ id, request });
1192
+ });
1193
+ },
1194
+ dispose() {
1195
+ port.removeEventListener("message", listener);
1196
+ for (const waiter of pending.values()) waiter.reject(new Error("layout runner disposed"));
1197
+ pending.clear();
1198
+ port.terminate?.();
1199
+ }
1200
+ };
1201
+ }
1202
+
1203
+ // src/cluster.ts
1204
+ var import_core = require("@memnest/core");
1205
+ var CATCH_ALL = "cluster:*";
1206
+ var usable = (term) => term.length >= 3 && !/^\d+$/.test(term);
1207
+ function clusterNodes(nodes, edges, options = {}) {
1208
+ const maxClusters = Math.max(2, options.maxClusters ?? 48);
1209
+ const exclude = new Set(options.exclude?.flatMap((t) => (0, import_core.queryTerms)(t)) ?? []);
1210
+ const termsOf = nodes.map((node) => (0, import_core.queryTerms)(node.content).filter((t) => usable(t) && !exclude.has(t)));
1211
+ const df = /* @__PURE__ */ new Map();
1212
+ for (const terms of termsOf) for (const t of terms) df.set(t, (df.get(t) ?? 0) + 1);
1213
+ const ceiling = Math.max(2, Math.floor(nodes.length * 0.6));
1214
+ const keyOf = termsOf.map((terms) => {
1215
+ let best;
1216
+ for (const t of terms) {
1217
+ const count = df.get(t);
1218
+ if (count < 2 || count > ceiling) continue;
1219
+ const bestCount = best === void 0 ? -1 : df.get(best);
1220
+ if (count > bestCount || count === bestCount && t < best) best = t;
1221
+ }
1222
+ return best ?? "";
1223
+ });
1224
+ const sizes = /* @__PURE__ */ new Map();
1225
+ for (const key of keyOf) if (key) sizes.set(key, (sizes.get(key) ?? 0) + 1);
1226
+ const kept = new Set(
1227
+ [...sizes.entries()].sort(([a2, x3], [b, y3]) => y3 - x3 || (a2 < b ? -1 : 1)).slice(0, maxClusters - 1).map(([key]) => key)
1228
+ );
1229
+ const byId = /* @__PURE__ */ new Map();
1230
+ const clusterOf = /* @__PURE__ */ new Map();
1231
+ nodes.forEach((node, i) => {
1232
+ const key = kept.has(keyOf[i]) ? keyOf[i] : "";
1233
+ const id = key ? `cluster:${key}` : CATCH_ALL;
1234
+ let cluster = byId.get(id);
1235
+ if (!cluster) {
1236
+ cluster = {
1237
+ id,
1238
+ key,
1239
+ label: key || "other",
1240
+ count: 0,
1241
+ kinds: Object.fromEntries(import_core.MEMORY_KINDS.map((k) => [k, 0])),
1242
+ inactive: 0,
1243
+ memberIds: []
1244
+ };
1245
+ byId.set(id, cluster);
1246
+ }
1247
+ cluster.count++;
1248
+ cluster.kinds[node.kind]++;
1249
+ if (!node.isLatest || node.forgotten) cluster.inactive++;
1250
+ cluster.memberIds.push(node.id);
1251
+ clusterOf.set(node.id, id);
1252
+ });
1253
+ const weights = /* @__PURE__ */ new Map();
1254
+ for (const edge of edges) {
1255
+ const from = clusterOf.get(edge.from);
1256
+ const to = clusterOf.get(edge.to);
1257
+ if (!from || !to || from === to) continue;
1258
+ const pair = from < to ? `${from}\0${to}` : `${to}\0${from}`;
1259
+ weights.set(pair, (weights.get(pair) ?? 0) + 1);
1260
+ }
1261
+ return {
1262
+ clusters: [...byId.values()].sort((a2, z) => z.count - a2.count || (a2.key < z.key ? -1 : 1)),
1263
+ edges: [...weights.entries()].map(([pair, weight]) => {
1264
+ const [from, to] = pair.split("\0");
1265
+ return { from, to, weight };
1266
+ }),
1267
+ clusterOf
1268
+ };
1269
+ }
1270
+
1271
+ // src/hit.ts
1272
+ function createHitIndex(circles) {
1273
+ const tree = quadtree(
1274
+ [...circles],
1275
+ (c2) => c2.x,
1276
+ (c2) => c2.y
1277
+ );
1278
+ const maxR = circles.reduce((max, c2) => Math.max(max, c2.r), 0);
1279
+ return {
1280
+ size: circles.length,
1281
+ pick(world, slop = 0) {
1282
+ const candidate = tree.find(world.x, world.y, maxR + slop);
1283
+ if (candidate && Math.hypot(candidate.x - world.x, candidate.y - world.y) <= candidate.r + slop) return candidate;
1284
+ let best = null;
1285
+ let bestDistance = Infinity;
1286
+ tree.visit((node, x0, y0, x1, y1) => {
1287
+ if (!node.length) {
1288
+ for (let leaf = node; leaf; leaf = leaf.next) {
1289
+ const c2 = leaf.data;
1290
+ const d = Math.hypot(c2.x - world.x, c2.y - world.y);
1291
+ if (d <= c2.r + slop && d < bestDistance) {
1292
+ best = c2;
1293
+ bestDistance = d;
1294
+ }
1295
+ }
1296
+ }
1297
+ const reach = maxR + slop;
1298
+ return x0 > world.x + reach || x1 < world.x - reach || y0 > world.y + reach || y1 < world.y - reach;
1299
+ });
1300
+ return best;
1301
+ }
1302
+ };
1303
+ }
1304
+
1305
+ // src/encoding.ts
1306
+ var nodeRadius = (node) => 5 + Math.min(11, 2.5 * Math.sqrt(Math.max(0, node.reinforcementCount - 1)));
1307
+ var clusterRadius = (count) => 14 + Math.min(70, 3.2 * Math.sqrt(count));
1308
+ var documentRadius = 7;
1309
+ function shorten(text, max = 48) {
1310
+ if (text.length <= max) return text;
1311
+ const cut = text.slice(0, max - 1);
1312
+ const space = cut.lastIndexOf(" ");
1313
+ return `${space > max * 0.6 ? cut.slice(0, space) : cut}\u2026`;
1314
+ }
1315
+
1316
+ // src/draw.ts
1317
+ function seedOf(text) {
1318
+ let hash = 2166136261;
1319
+ for (let i = 0; i < text.length; i++) hash = Math.imul(hash ^ text.charCodeAt(i), 16777619);
1320
+ hash = Math.imul(hash ^ hash >>> 16, 2246822507);
1321
+ hash = Math.imul(hash ^ hash >>> 13, 3266489909);
1322
+ return ((hash ^ hash >>> 16) >>> 0) / 4294967296;
1323
+ }
1324
+ function buildGraphScene(state, theme) {
1325
+ const { positions, selectedId, lineage } = state;
1326
+ const circles = [];
1327
+ const lines = [];
1328
+ if (state.mode === "clusters") {
1329
+ for (const edge of state.clusterEdges) {
1330
+ const a2 = positions.get(edge.from);
1331
+ const b = positions.get(edge.to);
1332
+ if (!a2 || !b) continue;
1333
+ lines.push({
1334
+ x1: a2.x,
1335
+ y1: a2.y,
1336
+ x2: b.x,
1337
+ y2: b.y,
1338
+ to: edge.to,
1339
+ color: theme.edges.aggregate,
1340
+ width: Math.min(6, 1 + Math.log2(edge.weight)),
1341
+ alpha: 0.5,
1342
+ dash: null,
1343
+ arrow: null,
1344
+ seed: seedOf(`${edge.from} ${edge.to}`)
1345
+ });
1346
+ }
1347
+ for (const cluster of state.clusters) {
1348
+ const p = positions.get(cluster.id);
1349
+ if (!p) continue;
1350
+ circles.push({
1351
+ id: cluster.id,
1352
+ x: p.x,
1353
+ y: p.y,
1354
+ r: clusterRadius(cluster.count),
1355
+ fill: theme.cluster,
1356
+ stroke: theme.clusterStroke,
1357
+ strokeWidth: 1,
1358
+ // Translucent, so the glow behind shows through.
1359
+ alpha: 0.62,
1360
+ glow: 0.45 + Math.min(0.4, Math.log10(cluster.count) / 8),
1361
+ selected: false,
1362
+ seed: seedOf(cluster.id),
1363
+ label: `${cluster.label} \xB7 ${cluster.count.toLocaleString("en")}`,
1364
+ priority: cluster.count
1365
+ });
1366
+ }
1367
+ return { circles, lines };
1368
+ }
1369
+ const radius = new Map(state.nodes.map((n) => [n.id, nodeRadius(n)]));
1370
+ const lineageEdges = new Set(lineage?.edges.map((e) => `${e.from} ${e.to}`) ?? []);
1371
+ for (const edge of state.edges) {
1372
+ const a2 = positions.get(edge.from);
1373
+ const b = positions.get(edge.to);
1374
+ if (!a2 || !b) continue;
1375
+ const inLineage = lineageEdges.has(`${edge.from} ${edge.to}`);
1376
+ lines.push({
1377
+ x1: a2.x,
1378
+ y1: a2.y,
1379
+ x2: b.x,
1380
+ y2: b.y,
1381
+ to: edge.to,
1382
+ color: inLineage ? theme.lineage : edge.relation === "updates" ? theme.edges.updates : theme.edges.extends,
1383
+ width: inLineage ? 2.5 : 1.25,
1384
+ alpha: lineage && !inLineage ? 0.25 : 0.9,
1385
+ // updates: solid with an arrow to the replaced fact; extends: dotted, no arrow.
1386
+ dash: edge.relation === "extends" ? [0.5, 4] : null,
1387
+ arrow: edge.relation === "updates" ? radius.get(edge.to) ?? 6 : null,
1388
+ seed: seedOf(`${edge.from} ${edge.to}`)
1389
+ });
1390
+ }
1391
+ for (const node of state.nodes) {
1392
+ const p = positions.get(node.id);
1393
+ if (!p) continue;
1394
+ const selected = node.id === selectedId;
1395
+ const inLineage = lineage?.memoryIds.has(node.id) ?? false;
1396
+ const dimmed = Boolean(lineage) && !inLineage && !selected;
1397
+ const base = node.forgotten ? theme.forgottenAlpha : node.isLatest ? 1 : theme.supersededAlpha;
1398
+ const glow = node.forgotten ? 0 : node.isLatest ? 0.55 + Math.min(0.45, 0.12 * (node.reinforcementCount - 1)) : 0.22;
1399
+ circles.push({
1400
+ id: node.id,
1401
+ x: p.x,
1402
+ y: p.y,
1403
+ r: radius.get(node.id),
1404
+ fill: node.forgotten ? null : theme.kinds[node.kind],
1405
+ stroke: selected ? theme.selection : inLineage ? theme.lineage : node.forgotten ? theme.kinds[node.kind] : null,
1406
+ strokeWidth: selected ? 3 : inLineage || node.forgotten ? 2 : 0,
1407
+ alpha: dimmed ? base * 0.35 : base,
1408
+ glow: dimmed ? glow * 0.3 : selected ? 1 : glow,
1409
+ selected,
1410
+ seed: seedOf(node.id),
1411
+ label: shorten(node.content, 42),
1412
+ priority: (selected ? 1e9 : 0) + (inLineage ? 1e6 : 0) + node.reinforcementCount * 10 + (node.isLatest ? 5 : 0)
1413
+ });
1414
+ }
1415
+ return { circles, lines };
1416
+ }
1417
+ var TAU = Math.PI * 2;
1418
+ var TOPIC_RADIUS = 17;
1419
+ function parseHex(color) {
1420
+ let hex = color.trim();
1421
+ if (!hex.startsWith("#")) return null;
1422
+ hex = hex.slice(1);
1423
+ if (hex.length === 3) hex = hex.replace(/./g, (c2) => c2 + c2);
1424
+ if (!/^[0-9a-f]{6}$/i.test(hex)) return null;
1425
+ const n = Number.parseInt(hex, 16);
1426
+ return [n >> 16, n >> 8 & 255, n & 255];
1427
+ }
1428
+ function withAlpha(color, alpha) {
1429
+ const rgb = parseHex(color);
1430
+ if (!rgb) return alpha <= 0 ? "rgba(0,0,0,0)" : color;
1431
+ return `rgba(${rgb[0]},${rgb[1]},${rgb[2]},${alpha})`;
1432
+ }
1433
+ function mix(color, toward, t) {
1434
+ const a2 = parseHex(color);
1435
+ const b = parseHex(toward);
1436
+ if (!a2 || !b) return color;
1437
+ const channel = (i) => Math.round(a2[i] + (b[i] - a2[i]) * t);
1438
+ return `rgb(${channel(0)},${channel(1)},${channel(2)})`;
1439
+ }
1440
+ var spriteCache = /* @__PURE__ */ new WeakMap();
1441
+ function unitGradient(ctx, key, stops) {
1442
+ let cache = spriteCache.get(ctx);
1443
+ if (!cache) spriteCache.set(ctx, cache = /* @__PURE__ */ new Map());
1444
+ let gradient = cache.get(key);
1445
+ if (!gradient) {
1446
+ gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 1);
1447
+ for (const [offset, color] of stops()) gradient.addColorStop(offset, color);
1448
+ cache.set(key, gradient);
1449
+ }
1450
+ return gradient;
1451
+ }
1452
+ var haloGradient = (ctx, color, ground) => unitGradient(
1453
+ ctx,
1454
+ `halo ${ground} ${color}`,
1455
+ () => ground === "dark" ? [
1456
+ [0, withAlpha(color, 0.6)],
1457
+ [0.22, withAlpha(color, 0.26)],
1458
+ [0.55, withAlpha(color, 0.07)],
1459
+ [1, withAlpha(color, 0)]
1460
+ ] : (
1461
+ // A soft tint: on a light ground a strong halo reads as a stain, not a glow.
1462
+ [
1463
+ [0, withAlpha(color, 0.34)],
1464
+ [0.25, withAlpha(color, 0.15)],
1465
+ [0.6, withAlpha(color, 0.04)],
1466
+ [1, withAlpha(color, 0)]
1467
+ ]
1468
+ )
1469
+ );
1470
+ var coreGradient = (ctx, color, ground) => unitGradient(
1471
+ ctx,
1472
+ `core ${ground} ${color}`,
1473
+ () => ground === "dark" ? [
1474
+ [0, mix(color, "#ffffff", 0.9)],
1475
+ [0.3, mix(color, "#ffffff", 0.45)],
1476
+ [0.75, color],
1477
+ [1, mix(color, "#000000", 0.3)]
1478
+ ] : (
1479
+ // A softer highlight and a firmer edge, so the neuron keeps its shape against white.
1480
+ [
1481
+ [0, mix(color, "#ffffff", 0.6)],
1482
+ [0.45, mix(color, "#ffffff", 0.12)],
1483
+ [0.85, color],
1484
+ [1, mix(color, "#000000", 0.2)]
1485
+ ]
1486
+ )
1487
+ );
1488
+ var sparkGradient = (ctx, color, core) => unitGradient(ctx, `spark ${color} ${core}`, () => [
1489
+ [0, withAlpha(core, 1)],
1490
+ [0.15, withAlpha(mix(color, core, 0.5), 0.9)],
1491
+ [0.4, withAlpha(color, 0.3)],
1492
+ [1, withAlpha(color, 0)]
1493
+ ]);
1494
+ function drawScene(ctx, scene, viewport, size, theme, options = {}) {
1495
+ const ratio = options.pixelRatio ?? 1;
1496
+ const live = options.time !== void 0;
1497
+ const time = options.time ?? 0;
1498
+ const { k } = viewport;
1499
+ const glow = theme.ground === "dark" ? "lighter" : "source-over";
1500
+ ctx.save();
1501
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
1502
+ ctx.globalCompositeOperation = "source-over";
1503
+ ctx.globalAlpha = 1;
1504
+ ctx.fillStyle = theme.background;
1505
+ ctx.fillRect(0, 0, size.width, size.height);
1506
+ const world = () => ctx.setTransform(ratio * k, 0, 0, ratio * k, ratio * viewport.x, ratio * viewport.y);
1507
+ const place = (x3, y3, r) => {
1508
+ const scale = ratio * k * r;
1509
+ ctx.setTransform(scale, 0, 0, scale, ratio * (viewport.x + x3 * k), ratio * (viewport.y + y3 * k));
1510
+ };
1511
+ const margin = 80 / k;
1512
+ const minX = -viewport.x / k - margin;
1513
+ const minY = -viewport.y / k - margin;
1514
+ const maxX = (size.width - viewport.x) / k + margin;
1515
+ const maxY = (size.height - viewport.y) / k + margin;
1516
+ const visible = (x3, y3) => x3 >= minX && x3 <= maxX && y3 >= minY && y3 <= maxY;
1517
+ world();
1518
+ ctx.globalCompositeOperation = glow;
1519
+ ctx.lineCap = "round";
1520
+ ctx.lineJoin = "round";
1521
+ const flashes = /* @__PURE__ */ new Map();
1522
+ const sparks = [];
1523
+ const lines = scene.lines.filter((line) => visible(line.x1, line.y1) || visible(line.x2, line.y2));
1524
+ const density = Math.min(1, 12 / Math.sqrt(lines.length || 1));
1525
+ for (const line of lines) {
1526
+ const dx = line.x2 - line.x1;
1527
+ const dy = line.y2 - line.y1;
1528
+ const length = Math.hypot(dx, dy) || 1;
1529
+ const bend = (line.seed - 0.5) * 0.4 * length;
1530
+ const cx = (line.x1 + line.x2) / 2 - dy / length * bend;
1531
+ const cy = (line.y1 + line.y2) / 2 + dx / length * bend;
1532
+ ctx.strokeStyle = line.color;
1533
+ ctx.beginPath();
1534
+ ctx.moveTo(line.x1, line.y1);
1535
+ ctx.quadraticCurveTo(cx, cy, line.x2, line.y2);
1536
+ ctx.setLineDash([]);
1537
+ ctx.globalAlpha = line.alpha * 0.1 * density;
1538
+ ctx.lineWidth = line.width * 4 / k;
1539
+ ctx.stroke();
1540
+ ctx.setLineDash(line.dash ? line.dash.map((d) => d / k) : []);
1541
+ ctx.globalAlpha = line.alpha * (0.25 + 0.35 * density);
1542
+ ctx.lineWidth = line.width / k;
1543
+ ctx.stroke();
1544
+ if (line.arrow !== null) {
1545
+ const angle = Math.atan2(line.y2 - cy, line.x2 - cx);
1546
+ const tipX = line.x2 - Math.cos(angle) * (line.arrow + 1.5 / k);
1547
+ const tipY = line.y2 - Math.sin(angle) * (line.arrow + 1.5 / k);
1548
+ const head = Math.min(6, Math.max(2, line.arrow * k)) / k;
1549
+ ctx.setLineDash([]);
1550
+ ctx.fillStyle = line.color;
1551
+ ctx.beginPath();
1552
+ ctx.moveTo(tipX, tipY);
1553
+ ctx.lineTo(tipX - Math.cos(angle - 0.45) * head, tipY - Math.sin(angle - 0.45) * head);
1554
+ ctx.lineTo(tipX - Math.cos(angle + 0.45) * head, tipY - Math.sin(angle + 0.45) * head);
1555
+ ctx.fill();
1556
+ }
1557
+ if (live) {
1558
+ const period = 2800 + line.seed * 6e3;
1559
+ const travel = Math.min(2400, Math.max(700, length * 9));
1560
+ const u = (time + line.seed * 9973) % period / travel;
1561
+ if (u <= 1) {
1562
+ const e = u < 0.5 ? 2 * u * u : 1 - (2 - 2 * u) ** 2 / 2;
1563
+ const inv = 1 - e;
1564
+ sparks.push({
1565
+ x: inv * inv * line.x1 + 2 * inv * e * cx + e * e * line.x2,
1566
+ y: inv * inv * line.y1 + 2 * inv * e * cy + e * e * line.y2,
1567
+ color: line.color,
1568
+ strength: Math.min(1, line.alpha * 1.1) * Math.sin(Math.PI * Math.min(1, u * 1.15 + 0.08))
1569
+ });
1570
+ } else if (line.to && u < 1.5) {
1571
+ flashes.set(line.to, Math.max(flashes.get(line.to) ?? 0, 1 - (u - 1) / 0.5));
1572
+ }
1573
+ }
1574
+ }
1575
+ ctx.setLineDash([]);
1576
+ for (const spark of sparks) {
1577
+ place(spark.x, spark.y, Math.min(9, Math.max(4, 3 * k)) / k);
1578
+ ctx.globalAlpha = Math.max(0, spark.strength);
1579
+ ctx.fillStyle = sparkGradient(ctx, spark.color, theme.spark);
1580
+ ctx.fillRect(-1, -1, 2, 2);
1581
+ }
1582
+ const shown = scene.circles.filter((c2) => visible(c2.x, c2.y));
1583
+ const crowd = Math.min(1, 22 / Math.sqrt(shown.length || 1));
1584
+ for (const circle of shown) {
1585
+ const color = circle.fill ?? circle.stroke;
1586
+ const flash = flashes.get(circle.id) ?? 0;
1587
+ const pulse = live ? 0.75 + 0.25 * Math.sin(time / (1100 + circle.seed * 1400) + circle.seed * TAU) : 1;
1588
+ const strength = Math.min(1, circle.glow * (pulse * crowd + flash * 0.55));
1589
+ if (!color || strength <= 0.01) continue;
1590
+ place(circle.x, circle.y, Math.max(circle.r * (3 + flash * 0.8), 7 / k));
1591
+ ctx.globalAlpha = strength;
1592
+ ctx.fillStyle = haloGradient(ctx, color, theme.ground);
1593
+ ctx.fillRect(-1, -1, 2, 2);
1594
+ }
1595
+ ctx.globalCompositeOperation = "source-over";
1596
+ const labelled = [];
1597
+ for (const circle of shown) {
1598
+ place(circle.x, circle.y, circle.r);
1599
+ const ghost = circle.fill !== null && circle.alpha < 1 && circle.r < TOPIC_RADIUS;
1600
+ ctx.globalCompositeOperation = ghost ? glow : "source-over";
1601
+ ctx.globalAlpha = ghost ? circle.alpha * 0.45 : circle.alpha;
1602
+ ctx.beginPath();
1603
+ ctx.arc(0, 0, 1, 0, TAU);
1604
+ if (circle.fill) {
1605
+ ctx.fillStyle = coreGradient(ctx, circle.fill, theme.ground);
1606
+ ctx.fill();
1607
+ }
1608
+ if (circle.stroke && circle.strokeWidth > 0) {
1609
+ ctx.globalAlpha = circle.alpha;
1610
+ ctx.strokeStyle = circle.stroke;
1611
+ ctx.lineWidth = circle.strokeWidth / (k * circle.r);
1612
+ ctx.stroke();
1613
+ } else if (ghost) {
1614
+ ctx.globalAlpha = Math.min(1, circle.alpha * 2);
1615
+ ctx.strokeStyle = circle.fill;
1616
+ ctx.lineWidth = 1 / (k * circle.r);
1617
+ ctx.stroke();
1618
+ }
1619
+ if (circle.label && (circle.r * k >= 9 || circle.priority >= 1e6)) labelled.push(circle);
1620
+ }
1621
+ ctx.globalCompositeOperation = glow;
1622
+ world();
1623
+ if (live) {
1624
+ for (const circle of shown) {
1625
+ if (!circle.selected) continue;
1626
+ const phase = time % 2400 / 2400;
1627
+ ctx.globalAlpha = 0.6 * (1 - phase);
1628
+ ctx.strokeStyle = circle.stroke ?? theme.selection;
1629
+ ctx.lineWidth = 1.5 / k;
1630
+ ctx.beginPath();
1631
+ ctx.arc(circle.x, circle.y, circle.r * (1.3 + phase * 2.2), 0, TAU);
1632
+ ctx.stroke();
1633
+ }
1634
+ }
1635
+ ctx.globalCompositeOperation = "source-over";
1636
+ labelled.sort((a2, z) => z.priority - a2.priority);
1637
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
1638
+ ctx.font = theme.font;
1639
+ ctx.textAlign = "center";
1640
+ ctx.textBaseline = "top";
1641
+ ctx.lineJoin = "round";
1642
+ const taken = [];
1643
+ for (const circle of labelled.slice(0, options.maxLabels ?? 160)) {
1644
+ const x3 = circle.x * k + viewport.x;
1645
+ const y3 = (circle.y + circle.r) * k + viewport.y + 6;
1646
+ const width = circle.label.length * 6.2;
1647
+ const box = [x3 - width / 2, y3, x3 + width / 2, y3 + 14];
1648
+ if (taken.some(([x0, y0, x1, y1]) => box[0] < x1 && box[2] > x0 && box[1] < y1 && box[3] > y0)) continue;
1649
+ taken.push(box);
1650
+ ctx.globalAlpha = Math.max(circle.alpha, 0.7);
1651
+ ctx.strokeStyle = theme.labelHalo;
1652
+ ctx.lineWidth = 4;
1653
+ ctx.strokeText(circle.label, x3, y3);
1654
+ ctx.fillStyle = theme.label;
1655
+ ctx.fillText(circle.label, x3, y3);
1656
+ }
1657
+ ctx.restore();
1658
+ }
1659
+
1660
+ // src/controllers/graph.ts
1661
+ var import_core2 = require("@memnest/core");
1662
+ var CLUSTER_THRESHOLD = 2e3;
1663
+ var GRAPH_LOAD_LIMIT = 1e4;
1664
+ var DEFAULT_GRAPH_FILTER = { kinds: [], includeSuperseded: true, includeForgotten: false, search: "" };
1665
+ function matchesFilter(node, filter) {
1666
+ if (filter.kinds.length > 0 && !filter.kinds.includes(node.kind)) return false;
1667
+ if (!node.isLatest && !filter.includeSuperseded) return false;
1668
+ if (node.forgotten && !filter.includeForgotten) return false;
1669
+ const search = filter.search.trim().toLowerCase();
1670
+ if (!search) return true;
1671
+ if (node.id === filter.search.trim()) return true;
1672
+ const content = node.content.toLowerCase();
1673
+ return search.split(/\s+/).every((word) => content.includes(word));
1674
+ }
1675
+ function createGraphController(options) {
1676
+ const { client } = options;
1677
+ const scope = (0, import_core2.scopeOf)(options.containerTag);
1678
+ const runner = options.layout ?? inlineLayoutRunner;
1679
+ const threshold = options.clusterThreshold ?? CLUSTER_THRESHOLD;
1680
+ const limit = options.limit ?? GRAPH_LOAD_LIMIT;
1681
+ const store = createStore({
1682
+ containerTag: scope.containerTag,
1683
+ status: "idle",
1684
+ error: null,
1685
+ filter: { ...DEFAULT_GRAPH_FILTER, ...options.filter },
1686
+ mode: "nodes",
1687
+ totalMemories: 0,
1688
+ loaded: 0,
1689
+ truncated: false,
1690
+ matching: 0,
1691
+ nodes: [],
1692
+ edges: [],
1693
+ clusters: [],
1694
+ clusterEdges: [],
1695
+ positions: /* @__PURE__ */ new Map(),
1696
+ selectedId: null,
1697
+ lineage: null,
1698
+ viewport: IDENTITY_VIEWPORT,
1699
+ size: { width: 0, height: 0 },
1700
+ layoutMs: null,
1701
+ hover: null
1702
+ });
1703
+ const loads = createSequencer();
1704
+ const layouts = createSequencer();
1705
+ let snapshot = null;
1706
+ let hits = createHitIndex([]);
1707
+ let userMoved = false;
1708
+ let disposed = false;
1709
+ async function recompute() {
1710
+ if (!snapshot) return;
1711
+ const token = layouts.next();
1712
+ const { filter, lineage, positions: previous, mode: previousMode } = store.getState();
1713
+ let nodes = snapshot.nodes.filter((n) => matchesFilter(n, filter));
1714
+ const matching = nodes.length;
1715
+ if (lineage) {
1716
+ const present = new Set(nodes.map((n) => n.id));
1717
+ nodes = nodes.concat(snapshot.nodes.filter((n) => lineage.memoryIds.has(n.id) && !present.has(n.id)));
1718
+ }
1719
+ const ids = new Set(nodes.map((n) => n.id));
1720
+ const edges = snapshot.edges.filter((e) => ids.has(e.from) && ids.has(e.to));
1721
+ const mode = nodes.length > threshold ? "clusters" : "nodes";
1722
+ let clusters = [];
1723
+ let clusterEdges = [];
1724
+ let layoutNodes;
1725
+ let layoutEdges;
1726
+ if (mode === "clusters") {
1727
+ ({ clusters, edges: clusterEdges } = clusterNodes(nodes, edges, { exclude: filter.search ? [filter.search] : [] }));
1728
+ layoutNodes = clusters.map((c2) => ({ id: c2.id, r: clusterRadius(c2.count) }));
1729
+ layoutEdges = clusterEdges;
1730
+ } else {
1731
+ layoutNodes = nodes.map((n) => ({ id: n.id, r: nodeRadius(n) }));
1732
+ layoutEdges = edges;
1733
+ }
1734
+ store.set({ status: "layout", hover: null, matching, mode, nodes: mode === "nodes" ? nodes : [], edges: mode === "nodes" ? edges : [], clusters, clusterEdges });
1735
+ const started = performance.now();
1736
+ let positions;
1737
+ const laidOut = new Set(layoutNodes.map((n) => n.id));
1738
+ try {
1739
+ positions = await runner.run({
1740
+ algorithm: "force",
1741
+ nodes: layoutNodes,
1742
+ edges: layoutEdges,
1743
+ options: {
1744
+ initial: [...previous].filter(([id]) => laidOut.has(id)),
1745
+ // Clusters are big circles that must not overlap; a handful of nodes needs room for labels.
1746
+ ...mode === "clusters" ? { linkDistance: 180, charge: -900, collidePadding: 28, collideIterations: 4 } : layoutNodes.length <= 40 ? { linkDistance: 90, charge: -320, collidePadding: 24 } : {}
1747
+ }
1748
+ });
1749
+ } catch (error) {
1750
+ if (layouts.isCurrent(token) && !disposed) store.set({ status: "error", error: errorText(error) });
1751
+ return;
1752
+ }
1753
+ if (!layouts.isCurrent(token) || disposed) return;
1754
+ const radius = new Map(layoutNodes.map((n) => [n.id, n.r]));
1755
+ const circles = [...positions].map(([id, p]) => ({ id, x: p.x, y: p.y, r: radius.get(id) ?? 6 }));
1756
+ hits = createHitIndex(circles);
1757
+ const state = store.getState();
1758
+ const refit = !userMoved || previousMode !== mode;
1759
+ store.set({
1760
+ status: "ready",
1761
+ error: null,
1762
+ positions,
1763
+ layoutMs: Math.round(performance.now() - started),
1764
+ ...refit ? { viewport: fitViewport(boundsOf(circles), state.size) } : {}
1765
+ });
1766
+ if (refit) userMoved = false;
1767
+ }
1768
+ const controller = {
1769
+ getState: store.getState,
1770
+ subscribe: store.subscribe,
1771
+ async load() {
1772
+ const token = loads.next();
1773
+ store.set({ status: "loading", error: null });
1774
+ try {
1775
+ const loaded = await client.graph(scope, { limit, includeSuperseded: true, includeForgotten: true });
1776
+ if (!loads.isCurrent(token) || disposed) return;
1777
+ snapshot = loaded;
1778
+ const { selectedId, lineage } = store.getState();
1779
+ const present = new Set(loaded.nodes.map((n) => n.id));
1780
+ store.set({
1781
+ totalMemories: loaded.totalMemories,
1782
+ loaded: loaded.nodes.length,
1783
+ truncated: loaded.truncated,
1784
+ selectedId: selectedId && present.has(selectedId) ? selectedId : null,
1785
+ lineage: lineage && present.has(lineage.rootId) ? lineage : null
1786
+ });
1787
+ await recompute();
1788
+ } catch (error) {
1789
+ if (loads.isCurrent(token) && !disposed) store.set({ status: "error", error: errorText(error) });
1790
+ }
1791
+ },
1792
+ setFilter(patch) {
1793
+ store.set((s) => ({ filter: { ...s.filter, ...patch } }));
1794
+ void recompute();
1795
+ },
1796
+ select(memoryId) {
1797
+ if (store.getState().selectedId === memoryId) return;
1798
+ store.set({ selectedId: memoryId });
1799
+ options.onSelect?.(memoryId);
1800
+ },
1801
+ async expandLineage(memoryId) {
1802
+ const token = loads.next();
1803
+ try {
1804
+ const graph = await client.getLineage(scope, memoryId);
1805
+ if (!loads.isCurrent(token) || disposed) return;
1806
+ if (!graph) {
1807
+ store.set({ lineage: null });
1808
+ return;
1809
+ }
1810
+ store.set({
1811
+ lineage: {
1812
+ rootId: memoryId,
1813
+ memoryIds: new Set(graph.memories.map((m2) => m2.id)),
1814
+ edges: graph.edges.filter((e) => e.relation !== "source")
1815
+ }
1816
+ });
1817
+ const { mode } = store.getState();
1818
+ if (mode === "clusters") store.set((s) => ({ filter: { ...s.filter, search: memoryId } }));
1819
+ await recompute();
1820
+ } catch (error) {
1821
+ if (loads.isCurrent(token) && !disposed) store.set({ status: "error", error: errorText(error) });
1822
+ }
1823
+ },
1824
+ collapseLineage() {
1825
+ if (!store.getState().lineage) return;
1826
+ store.set({ lineage: null });
1827
+ void recompute();
1828
+ },
1829
+ expandCluster(clusterId) {
1830
+ const cluster = store.getState().clusters.find((c2) => c2.id === clusterId);
1831
+ if (!cluster || !cluster.key) return;
1832
+ const current = store.getState().filter.search.trim();
1833
+ controller.setFilter({ search: current ? `${current} ${cluster.key}` : cluster.key });
1834
+ },
1835
+ setSize(width, height) {
1836
+ const { size, viewport } = store.getState();
1837
+ if (size.width === width && size.height === height) return;
1838
+ const first = size.width === 0 || size.height === 0;
1839
+ const next = first ? viewport : panBy(viewport, (width - size.width) / 2, (height - size.height) / 2);
1840
+ store.set({ size: { width, height }, viewport: next });
1841
+ if (first && !userMoved) controller.fit();
1842
+ },
1843
+ panBy(dx, dy) {
1844
+ userMoved = true;
1845
+ store.set((s) => ({ viewport: panBy(s.viewport, dx, dy), hover: null }));
1846
+ },
1847
+ zoomAt(screen, factor) {
1848
+ userMoved = true;
1849
+ store.set((s) => ({ viewport: zoomAt(s.viewport, screen, factor), hover: null }));
1850
+ },
1851
+ fit() {
1852
+ const { positions, size, mode, clusters, nodes } = store.getState();
1853
+ const radius = new Map(
1854
+ mode === "clusters" ? clusters.map((c2) => [c2.id, clusterRadius(c2.count)]) : nodes.map((n) => [n.id, nodeRadius(n)])
1855
+ );
1856
+ userMoved = false;
1857
+ store.set({ viewport: fitViewport(boundsOf([...positions].map(([id, p]) => ({ ...p, r: radius.get(id) ?? 6 }))), size) });
1858
+ },
1859
+ pick(screen) {
1860
+ const { viewport, mode, clusters } = store.getState();
1861
+ const hit = hits.pick(toWorld(viewport, screen), 4 / viewport.k);
1862
+ if (!hit) return null;
1863
+ if (mode === "clusters") {
1864
+ const cluster = clusters.find((c2) => c2.id === hit.id);
1865
+ return cluster ? { type: "cluster", id: cluster.id, key: cluster.key } : null;
1866
+ }
1867
+ return { type: "node", id: hit.id };
1868
+ },
1869
+ hover(screen) {
1870
+ const previous = store.getState().hover;
1871
+ const pick = screen ? controller.pick(screen) : null;
1872
+ if (!pick) {
1873
+ if (previous) store.set({ hover: null });
1874
+ return;
1875
+ }
1876
+ if (previous && previous.pick.id === pick.id) return;
1877
+ const { viewport, positions } = store.getState();
1878
+ const p = positions.get(pick.id);
1879
+ store.set({ hover: { pick, x: p.x * viewport.k + viewport.x, y: p.y * viewport.k + viewport.y } });
1880
+ },
1881
+ dispose() {
1882
+ disposed = true;
1883
+ loads.cancel();
1884
+ layouts.cancel();
1885
+ }
1886
+ };
1887
+ if (options.autoload !== false) void controller.load();
1888
+ return controller;
1889
+ }
1890
+
1891
+ // src/controllers/lineage.ts
1892
+ var import_core3 = require("@memnest/core");
1893
+ function layoutLineage(graph) {
1894
+ const nodes = [
1895
+ ...graph.memories.map((m2) => ({ id: m2.id, r: nodeRadius(m2) })),
1896
+ ...graph.documents.map((d) => ({ id: d.id, r: documentRadius }))
1897
+ ];
1898
+ const positions = layeredLayout(nodes, graph.edges, { layerGap: 190, nodeGap: 96 });
1899
+ const radius = new Map(nodes.map((n) => [n.id, n.r]));
1900
+ return { positions, bounds: boundsOf([...positions].map(([id, p]) => ({ ...p, r: radius.get(id) + 90 }))) };
1901
+ }
1902
+ function createLineageController(options) {
1903
+ const scope = (0, import_core3.scopeOf)(options.containerTag);
1904
+ const store = createStore({ status: "idle", error: null, rootId: null, graph: null, positions: /* @__PURE__ */ new Map(), bounds: null });
1905
+ const seq = createSequencer();
1906
+ const controller = {
1907
+ getState: store.getState,
1908
+ subscribe: store.subscribe,
1909
+ async load(memoryId) {
1910
+ const token = seq.next();
1911
+ store.set({ status: "loading", error: null, rootId: memoryId });
1912
+ try {
1913
+ const graph = await options.client.getLineage(scope, memoryId);
1914
+ if (!seq.isCurrent(token)) return;
1915
+ if (!graph) {
1916
+ store.set({ status: "not-found", graph: null, positions: /* @__PURE__ */ new Map(), bounds: null });
1917
+ return;
1918
+ }
1919
+ store.set({ status: "ready", graph, ...layoutLineage(graph) });
1920
+ } catch (error) {
1921
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
1922
+ }
1923
+ },
1924
+ async reload() {
1925
+ const { rootId } = store.getState();
1926
+ if (rootId) await controller.load(rootId);
1927
+ },
1928
+ clear() {
1929
+ seq.cancel();
1930
+ store.set({ status: "idle", error: null, rootId: null, graph: null, positions: /* @__PURE__ */ new Map(), bounds: null });
1931
+ },
1932
+ dispose: () => seq.cancel()
1933
+ };
1934
+ return controller;
1935
+ }
1936
+
1937
+ // src/controllers/detail.ts
1938
+ var import_core4 = require("@memnest/core");
1939
+ function versionChain(graph, rootId) {
1940
+ const byId = new Map(graph.memories.map((m2) => [m2.id, m2]));
1941
+ const newerOf = /* @__PURE__ */ new Map();
1942
+ for (const m2 of graph.memories) if (m2.supersedes) newerOf.set(m2.supersedes, m2);
1943
+ const root = byId.get(rootId);
1944
+ if (!root) return [];
1945
+ const chain = [root];
1946
+ const seen = /* @__PURE__ */ new Set([root.id]);
1947
+ for (let older = root.supersedes ? byId.get(root.supersedes) : void 0; older && !seen.has(older.id); older = older.supersedes ? byId.get(older.supersedes) : void 0) {
1948
+ chain.unshift(older);
1949
+ seen.add(older.id);
1950
+ }
1951
+ for (let newer = newerOf.get(root.id); newer && !seen.has(newer.id); newer = newerOf.get(newer.id)) {
1952
+ chain.push(newer);
1953
+ seen.add(newer.id);
1954
+ }
1955
+ return chain;
1956
+ }
1957
+ var EMPTY = {
1958
+ status: "idle",
1959
+ error: null,
1960
+ memoryId: null,
1961
+ memory: null,
1962
+ versions: [],
1963
+ extends: [],
1964
+ extendedBy: [],
1965
+ sources: [],
1966
+ forget: "idle",
1967
+ forgetError: null
1968
+ };
1969
+ function createDetailController(options) {
1970
+ const { client } = options;
1971
+ const scope = (0, import_core4.scopeOf)(options.containerTag);
1972
+ const store = createStore(EMPTY);
1973
+ const seq = createSequencer();
1974
+ const updateSource = (documentId, patch) => store.set((s) => ({ sources: s.sources.map((source) => source.document.id === documentId ? { ...source, ...patch } : source) }));
1975
+ const controller = {
1976
+ getState: store.getState,
1977
+ subscribe: store.subscribe,
1978
+ async load(memoryId) {
1979
+ const token = seq.next();
1980
+ store.set({ ...EMPTY, status: "loading", memoryId });
1981
+ try {
1982
+ const [memory, graph] = await Promise.all([client.getMemory(scope, memoryId), client.getLineage(scope, memoryId)]);
1983
+ if (!seq.isCurrent(token)) return;
1984
+ if (!memory || !graph) {
1985
+ store.set({ status: "not-found" });
1986
+ return;
1987
+ }
1988
+ const byId = new Map(graph.memories.map((m2) => [m2.id, m2]));
1989
+ const sourceIds = new Set(graph.edges.filter((e) => e.relation === "source" && e.from === memoryId).map((e) => e.to));
1990
+ store.set({
1991
+ status: "ready",
1992
+ memory,
1993
+ versions: versionChain(graph, memoryId),
1994
+ extends: memory.extendsIds.flatMap((id) => byId.has(id) ? [byId.get(id)] : []),
1995
+ extendedBy: graph.memories.filter((m2) => m2.extendsIds.includes(memoryId)),
1996
+ sources: graph.documents.filter((d) => sourceIds.has(d.id)).map((document) => ({ document, content: null, chunks: null, loading: false, error: null }))
1997
+ });
1998
+ } catch (error) {
1999
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
2000
+ }
2001
+ },
2002
+ async reload() {
2003
+ const { memoryId } = store.getState();
2004
+ if (memoryId) await controller.load(memoryId);
2005
+ },
2006
+ clear() {
2007
+ seq.cancel();
2008
+ store.set(EMPTY);
2009
+ },
2010
+ async loadSource(documentId) {
2011
+ const memoryId = store.getState().memoryId;
2012
+ updateSource(documentId, { loading: true, error: null });
2013
+ try {
2014
+ const found = await client.getDocument(scope, documentId);
2015
+ if (store.getState().memoryId !== memoryId) return;
2016
+ updateSource(documentId, {
2017
+ loading: false,
2018
+ content: found?.document.content ?? null,
2019
+ chunks: found?.chunks ?? [],
2020
+ ...found ? {} : { error: "document not found" }
2021
+ });
2022
+ } catch (error) {
2023
+ if (store.getState().memoryId === memoryId) updateSource(documentId, { loading: false, error: errorText(error) });
2024
+ }
2025
+ },
2026
+ requestForget() {
2027
+ const { memory, forget } = store.getState();
2028
+ if (!memory || memory.forgottenAt || forget !== "idle") return;
2029
+ store.set({ forget: "confirming", forgetError: null });
2030
+ },
2031
+ cancelForget() {
2032
+ if (store.getState().forget === "confirming") store.set({ forget: "idle" });
2033
+ },
2034
+ async confirmForget() {
2035
+ const { memory, forget } = store.getState();
2036
+ if (!memory || forget !== "confirming") return null;
2037
+ store.set({ forget: "forgetting", forgetError: null });
2038
+ try {
2039
+ const forgotten = await client.forget(scope, memory.id);
2040
+ if (store.getState().memoryId !== memory.id) return forgotten;
2041
+ store.set((s) => ({
2042
+ forget: "idle",
2043
+ memory: forgotten,
2044
+ versions: s.versions.map((v) => v.id === forgotten.id ? forgotten : v)
2045
+ }));
2046
+ options.onForgotten?.(forgotten);
2047
+ return forgotten;
2048
+ } catch (error) {
2049
+ store.set({ forget: "confirming", forgetError: errorText(error) });
2050
+ return null;
2051
+ }
2052
+ },
2053
+ dispose: () => seq.cancel()
2054
+ };
2055
+ return controller;
2056
+ }
2057
+
2058
+ // src/controllers/trace.ts
2059
+ var import_core5 = require("@memnest/core");
2060
+ var DEFAULT_TRACE_BUDGET = 2e3;
2061
+ function buildTraceRows(response, memories) {
2062
+ let used = 0;
2063
+ let budgetLine = null;
2064
+ const rows = response.trace.candidates.map((candidate, index2) => {
2065
+ if (candidate.included) used += candidate.tokens;
2066
+ if (budgetLine === null && candidate.excludedReason === "budget") budgetLine = index2;
2067
+ const memory = memories.get(candidate.memoryId);
2068
+ return { ...candidate, content: memory?.content ?? null, kind: memory?.kind ?? null, cumulativeTokens: used };
2069
+ });
2070
+ return { rows, budgetLine };
2071
+ }
2072
+ function createTraceController(options) {
2073
+ const { client } = options;
2074
+ const scope = (0, import_core5.scopeOf)(options.containerTag);
2075
+ const store = createStore({
2076
+ query: "",
2077
+ tokenBudget: options.tokenBudget ?? DEFAULT_TRACE_BUDGET,
2078
+ status: "idle",
2079
+ error: null,
2080
+ response: null,
2081
+ rows: [],
2082
+ budgetLine: null,
2083
+ chunkTokens: 0
2084
+ });
2085
+ const seq = createSequencer();
2086
+ let lastRun = null;
2087
+ async function execute(query, tokenBudget) {
2088
+ const token = seq.next();
2089
+ lastRun = { query, tokenBudget };
2090
+ store.set({ status: "loading", error: null });
2091
+ try {
2092
+ const response = await client.search(query, scope, { tokenBudget });
2093
+ const memories = new Map(response.memories.map((r) => [r.memory.id, r.memory]));
2094
+ const missing = response.trace.candidates.map((c2) => c2.memoryId).filter((id) => !memories.has(id));
2095
+ const fetched = await Promise.all(missing.map((id) => client.getMemory(scope, id)));
2096
+ if (!seq.isCurrent(token)) return;
2097
+ for (const memory of fetched) if (memory) memories.set(memory.id, memory);
2098
+ store.set({
2099
+ status: "ready",
2100
+ response,
2101
+ ...buildTraceRows(response, memories),
2102
+ chunkTokens: response.chunks.reduce((sum, c2) => sum + c2.tokens, 0)
2103
+ });
2104
+ } catch (error) {
2105
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
2106
+ }
2107
+ }
2108
+ return {
2109
+ getState: store.getState,
2110
+ subscribe: store.subscribe,
2111
+ setQuery: (query) => store.set({ query }),
2112
+ setTokenBudget: (tokenBudget) => store.set({ tokenBudget }),
2113
+ async run() {
2114
+ const { query, tokenBudget } = store.getState();
2115
+ if (!query.trim()) return;
2116
+ await execute(query, tokenBudget);
2117
+ },
2118
+ async rerun() {
2119
+ if (lastRun) await execute(lastRun.query, lastRun.tokenBudget);
2120
+ },
2121
+ dispose: () => seq.cancel()
2122
+ };
2123
+ }
2124
+
2125
+ // src/controllers/timeline.ts
2126
+ var import_core6 = require("@memnest/core");
2127
+ var DAY = 864e5;
2128
+ var iso = (ms) => new Date(ms).toISOString();
2129
+ var earliest = (...values) => values.filter((v) => typeof v === "string").sort()[0] ?? null;
2130
+ function buildTimeline(memories, now2) {
2131
+ const byId = new Map(memories.map((m2) => [m2.id, m2]));
2132
+ const newer = /* @__PURE__ */ new Map();
2133
+ for (const m2 of memories) if (m2.supersedes && byId.has(m2.supersedes)) newer.set(m2.supersedes, m2);
2134
+ const laneOf = /* @__PURE__ */ new Map();
2135
+ const rootOf = (m2) => {
2136
+ const seen = /* @__PURE__ */ new Set();
2137
+ let current = m2;
2138
+ while (current.supersedes && byId.has(current.supersedes) && !seen.has(current.id)) {
2139
+ seen.add(current.id);
2140
+ current = byId.get(current.supersedes);
2141
+ }
2142
+ return current.id;
2143
+ };
2144
+ for (const m2 of memories) laneOf.set(m2.id, rootOf(m2));
2145
+ const items = memories.map((memory) => {
2146
+ const replacement = newer.get(memory.id);
2147
+ const supersededAt = replacement ? earliest(replacement.validFrom, replacement.createdAt) : null;
2148
+ const expiredAt = memory.validUntil && memory.validUntil <= now2 ? memory.validUntil : null;
2149
+ const endedAt = earliest(supersededAt, expiredAt, memory.forgottenAt);
2150
+ const status = endedAt === null ? memory.isLatest ? "current" : "superseded" : endedAt === memory.forgottenAt ? "forgotten" : endedAt === supersededAt ? "superseded" : "expired";
2151
+ return {
2152
+ memory,
2153
+ laneId: laneOf.get(memory.id),
2154
+ start: memory.validFrom,
2155
+ // A current fact with a future expiry shows where it will end.
2156
+ end: endedAt ?? memory.validUntil ?? null,
2157
+ status,
2158
+ supersededBy: replacement?.id ?? null
2159
+ };
2160
+ });
2161
+ const lanes = /* @__PURE__ */ new Map();
2162
+ for (const item of items) (lanes.get(item.laneId) ?? lanes.set(item.laneId, []).get(item.laneId)).push(item);
2163
+ const laneList = [...lanes.entries()].map(([id, laneItems]) => {
2164
+ laneItems.sort((a2, z) => a2.start < z.start ? -1 : a2.start > z.start ? 1 : a2.memory.version - z.memory.version);
2165
+ return { id, label: shorten(laneItems.at(-1).memory.content, 60), items: laneItems };
2166
+ }).sort((a2, z) => a2.items[0].start < z.items[0].start ? -1 : 1);
2167
+ if (items.length === 0) return { lanes: [], range: null, ticks: [] };
2168
+ const start = Date.parse(items.map((i) => i.start).sort()[0]);
2169
+ const lastEnd = Math.max(Date.parse(now2), ...items.map((i) => Date.parse(i.end ?? now2)));
2170
+ const span = Math.max(lastEnd - start, DAY);
2171
+ const range = { start: iso(start - span * 0.03), end: iso(start + span * 1.03) };
2172
+ return { lanes: laneList, range, ticks: timeTicks(range.start, range.end) };
2173
+ }
2174
+ function timeTicks(start, end, count = 6) {
2175
+ const from = Date.parse(start);
2176
+ const to = Date.parse(end);
2177
+ const span = Math.max(to - from, 1);
2178
+ const withTime = span < 3 * DAY;
2179
+ const crossesYear = new Date(from).getUTCFullYear() !== new Date(to).getUTCFullYear();
2180
+ const format = new Intl.DateTimeFormat("en", {
2181
+ timeZone: "UTC",
2182
+ month: "short",
2183
+ day: "numeric",
2184
+ ...withTime ? { hour: "2-digit", minute: "2-digit", hourCycle: "h23" } : crossesYear ? { year: "numeric" } : {}
2185
+ });
2186
+ return Array.from({ length: count }, (_, i) => {
2187
+ const at = from + span * i / (count - 1);
2188
+ return { at: iso(at), label: format.format(at) };
2189
+ });
2190
+ }
2191
+ function createTimelineController(options) {
2192
+ const { client } = options;
2193
+ const scope = (0, import_core6.scopeOf)(options.containerTag);
2194
+ const now2 = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
2195
+ const store = createStore({ topic: "", status: "idle", error: null, lanes: [], range: null, ticks: [] });
2196
+ const seq = createSequencer();
2197
+ let lastTopic = null;
2198
+ async function execute(topic) {
2199
+ const token = seq.next();
2200
+ lastTopic = topic;
2201
+ store.set({ status: "loading", error: null });
2202
+ try {
2203
+ const response = await client.search(topic, scope, { tokenBudget: 1, candidates: 30 });
2204
+ const found = new Map(response.memories.map((r) => [r.memory.id, r.memory]));
2205
+ const ids = response.trace.candidates.map((c2) => c2.memoryId);
2206
+ for (const memory of await Promise.all(ids.filter((id) => !found.has(id)).map((id) => client.getMemory(scope, id)))) {
2207
+ if (memory) found.set(memory.id, memory);
2208
+ }
2209
+ const chainRoots = [...found.values()].filter((m2) => m2.supersedes || !m2.isLatest).slice(0, 20);
2210
+ for (const graph of await Promise.all(chainRoots.map((m2) => client.getLineage(scope, m2.id)))) {
2211
+ for (const m2 of graph?.memories ?? []) {
2212
+ if (graph.edges.some((e) => e.relation === "updates" && (e.from === m2.id || e.to === m2.id))) found.set(m2.id, m2);
2213
+ }
2214
+ }
2215
+ if (!seq.isCurrent(token)) return;
2216
+ store.set({ status: "ready", ...buildTimeline([...found.values()], now2()) });
2217
+ } catch (error) {
2218
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
2219
+ }
2220
+ }
2221
+ return {
2222
+ getState: store.getState,
2223
+ subscribe: store.subscribe,
2224
+ setTopic: (topic) => store.set({ topic }),
2225
+ async run() {
2226
+ const { topic } = store.getState();
2227
+ if (topic.trim()) await execute(topic);
2228
+ },
2229
+ async rerun() {
2230
+ if (lastTopic !== null) await execute(lastTopic);
2231
+ },
2232
+ dispose: () => seq.cancel()
2233
+ };
2234
+ }
2235
+
2236
+ // src/controllers/finder.ts
2237
+ var import_core7 = require("@memnest/core");
2238
+ var PAGE = 50;
2239
+ function createFinderController(options) {
2240
+ const { client } = options;
2241
+ const scope = (0, import_core7.scopeOf)(options.containerTag);
2242
+ const store = createStore({ query: "", includeHistory: false, status: "idle", error: null, items: [], hasMore: false });
2243
+ const seq = createSequencer();
2244
+ const visible = (m2, includeHistory) => includeHistory || m2.isLatest && !m2.forgottenAt;
2245
+ async function search(query, includeHistory, token) {
2246
+ const response = await client.search(query, scope, { tokenBudget: 1e6, candidates: PAGE });
2247
+ const found = new Map(response.memories.map((r) => [r.memory.id, r.memory]));
2248
+ const ids = response.trace.candidates.map((c2) => c2.memoryId);
2249
+ const missing = ids.filter((id) => !found.has(id));
2250
+ for (const memory of await Promise.all(missing.map((id) => client.getMemory(scope, id)))) if (memory) found.set(memory.id, memory);
2251
+ if (!seq.isCurrent(token)) return;
2252
+ store.set({ status: "ready", hasMore: false, items: ids.flatMap((id) => found.has(id) && visible(found.get(id), includeHistory) ? [found.get(id)] : []) });
2253
+ }
2254
+ async function browse(includeHistory, token, after) {
2255
+ const page = await client.listMemories(scope, { limit: PAGE, ...after ? { after } : {} }, { latestOnly: !includeHistory, includeForgotten: includeHistory });
2256
+ if (!seq.isCurrent(token)) return;
2257
+ store.set((s) => ({ status: "ready", items: after ? [...s.items, ...page] : page, hasMore: page.length === PAGE }));
2258
+ }
2259
+ return {
2260
+ getState: store.getState,
2261
+ subscribe: store.subscribe,
2262
+ setQuery: (query) => store.set({ query }),
2263
+ setIncludeHistory: (includeHistory) => store.set({ includeHistory }),
2264
+ async run() {
2265
+ const token = seq.next();
2266
+ const { query, includeHistory } = store.getState();
2267
+ store.set({ status: "loading", error: null });
2268
+ try {
2269
+ if (query.trim()) await search(query, includeHistory, token);
2270
+ else await browse(includeHistory, token);
2271
+ } catch (error) {
2272
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
2273
+ }
2274
+ },
2275
+ async loadMore() {
2276
+ const { items, hasMore, includeHistory, query, status } = store.getState();
2277
+ if (!hasMore || query.trim() || status === "loading" || items.length === 0) return;
2278
+ const token = seq.next();
2279
+ store.set({ status: "loading" });
2280
+ try {
2281
+ await browse(includeHistory, token, items.at(-1).id);
2282
+ } catch (error) {
2283
+ if (seq.isCurrent(token)) store.set({ status: "error", error: errorText(error) });
2284
+ }
2285
+ },
2286
+ dispose: () => seq.cancel()
2287
+ };
2288
+ }
2289
+
2290
+ // src/workspace.ts
2291
+ function createWorkspace(options) {
2292
+ const { client, containerTag } = options;
2293
+ const selection = createStore({ memoryId: null });
2294
+ let workspace;
2295
+ const onForgotten = (_memory) => {
2296
+ void Promise.all([
2297
+ workspace.finder.run(),
2298
+ workspace.trace.rerun(),
2299
+ workspace.timeline.rerun(),
2300
+ workspace.graph.load(),
2301
+ workspace.lineage.reload()
2302
+ ]);
2303
+ };
2304
+ const select = (memoryId) => {
2305
+ if (selection.getState().memoryId === memoryId) return;
2306
+ selection.set({ memoryId });
2307
+ workspace.graph.select(memoryId);
2308
+ if (memoryId) {
2309
+ void workspace.detail.load(memoryId);
2310
+ void workspace.lineage.load(memoryId);
2311
+ } else {
2312
+ workspace.detail.clear();
2313
+ workspace.lineage.clear();
2314
+ }
2315
+ };
2316
+ workspace = {
2317
+ containerTag,
2318
+ selection,
2319
+ select,
2320
+ finder: createFinderController({ client, containerTag }),
2321
+ detail: createDetailController({ client, containerTag, onForgotten }),
2322
+ lineage: createLineageController({ client, containerTag }),
2323
+ trace: createTraceController({ client, containerTag }),
2324
+ timeline: createTimelineController({ client, containerTag, ...options.now ? { now: options.now } : {} }),
2325
+ graph: createGraphController({
2326
+ client,
2327
+ containerTag,
2328
+ ...options.graph,
2329
+ ...options.layout ? { layout: options.layout } : {},
2330
+ onSelect: (memoryId) => select(memoryId)
2331
+ }),
2332
+ async refresh() {
2333
+ await Promise.all([
2334
+ workspace.finder.run(),
2335
+ workspace.trace.rerun(),
2336
+ workspace.timeline.rerun(),
2337
+ workspace.graph.load(),
2338
+ workspace.detail.reload(),
2339
+ workspace.lineage.reload()
2340
+ ]);
2341
+ },
2342
+ dispose() {
2343
+ for (const controller of [workspace.finder, workspace.detail, workspace.lineage, workspace.trace, workspace.timeline, workspace.graph]) {
2344
+ controller.dispose();
2345
+ }
2346
+ }
2347
+ };
2348
+ return workspace;
2349
+ }
2350
+ // Annotate the CommonJS export names for ESM import in node:
2351
+ 0 && (module.exports = {
2352
+ CLUSTER_THRESHOLD,
2353
+ DEFAULT_GRAPH_FILTER,
2354
+ DEFAULT_TRACE_BUDGET,
2355
+ GRAPH_LOAD_LIMIT,
2356
+ IDENTITY_VIEWPORT,
2357
+ WORKER_LAYOUT_THRESHOLD,
2358
+ ZOOM_LIMITS,
2359
+ boundsOf,
2360
+ buildGraphScene,
2361
+ buildTimeline,
2362
+ buildTraceRows,
2363
+ clamp,
2364
+ clusterNodes,
2365
+ clusterRadius,
2366
+ computeLayout,
2367
+ createDetailController,
2368
+ createFinderController,
2369
+ createGraphController,
2370
+ createHitIndex,
2371
+ createLineageController,
2372
+ createSequencer,
2373
+ createStore,
2374
+ createTimelineController,
2375
+ createTraceController,
2376
+ createWorkerLayoutRunner,
2377
+ createWorkspace,
2378
+ documentRadius,
2379
+ drawScene,
2380
+ fitViewport,
2381
+ forceLayout,
2382
+ inlineLayoutRunner,
2383
+ layeredLayout,
2384
+ layoutLineage,
2385
+ matchesFilter,
2386
+ nodeRadius,
2387
+ panBy,
2388
+ runLayoutRequest,
2389
+ seededRandom,
2390
+ serveLayoutRequests,
2391
+ shorten,
2392
+ timeTicks,
2393
+ toScreen,
2394
+ toWorld,
2395
+ versionChain,
2396
+ zoomAt
2397
+ });
2398
+ //# sourceMappingURL=index.cjs.map