@visualli/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +263 -0
  2. package/dist/index.cjs +2325 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +1181 -0
  5. package/dist/index.d.ts +1181 -0
  6. package/dist/index.js +2119 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +80 -0
  9. package/src/animations/constants.ts +72 -0
  10. package/src/animations/easing.ts +70 -0
  11. package/src/animations/index.ts +2 -0
  12. package/src/config/blobShapes.ts +146 -0
  13. package/src/config/textScaling.ts +54 -0
  14. package/src/constants/design.ts +281 -0
  15. package/src/constants/index.ts +3 -0
  16. package/src/constants/performanceConstants.ts +157 -0
  17. package/src/constants/renderConfig.ts +134 -0
  18. package/src/index.ts +18 -0
  19. package/src/layout/circularLayout.ts +95 -0
  20. package/src/layout/index.ts +3 -0
  21. package/src/layout/layoutUtils.ts +132 -0
  22. package/src/layout/linearLayout.ts +84 -0
  23. package/src/parser/configUtils.ts +129 -0
  24. package/src/parser/index.ts +4 -0
  25. package/src/parser/middleware.ts +24 -0
  26. package/src/parser/mindmapUtils.ts +76 -0
  27. package/src/parser/visualliConverter.ts +328 -0
  28. package/src/parser/visualliParser.ts +191 -0
  29. package/src/performance/index.ts +1 -0
  30. package/src/performance/performanceMonitor.ts +145 -0
  31. package/src/rendering/culling.ts +71 -0
  32. package/src/spatial/index.ts +1 -0
  33. package/src/spatial/spatialIndex.ts +83 -0
  34. package/src/stores/index.ts +61 -0
  35. package/src/types/document.ts +15 -0
  36. package/src/types/index.ts +17 -0
  37. package/src/types/layer.ts +67 -0
  38. package/src/types/meta.ts +9 -0
  39. package/src/types/mindmap.ts +136 -0
  40. package/src/types/schema.ts +36 -0
  41. package/src/utils/navigation.ts +161 -0
  42. package/src/viewport/index.ts +1 -0
  43. package/src/viewport/viewportUtils.ts +195 -0
package/dist/index.js ADDED
@@ -0,0 +1,2119 @@
1
+ // src/layout/circularLayout.ts
2
+ function applyCircularLayout(nodes, options = {}) {
3
+ const {
4
+ radiusPercentage = 0.135,
5
+ containerWidth = 2e3,
6
+ containerHeight = 2e3,
7
+ centerX = 0,
8
+ centerY = 0
9
+ } = options;
10
+ if (nodes.length === 0) return nodes;
11
+ if (nodes.length === 1) {
12
+ nodes[0].x = centerX;
13
+ nodes[0].y = centerY;
14
+ return nodes;
15
+ }
16
+ const radius = Math.min(containerWidth, containerHeight) * radiusPercentage;
17
+ const startAngle = nodes.length === 2 ? 0 : -Math.PI / 2;
18
+ const angleStep = 2 * Math.PI / nodes.length;
19
+ for (let i = 0; i < nodes.length; i++) {
20
+ const angle = startAngle + i * angleStep;
21
+ nodes[i].x = centerX + radius * Math.cos(angle);
22
+ nodes[i].y = centerY + radius * Math.sin(angle);
23
+ }
24
+ return nodes;
25
+ }
26
+ function calculateMinimumRadius(nodeCount, nodeWidth = 200, nodePadding = 150) {
27
+ if (nodeCount <= 1) return 0;
28
+ const angleStep = 2 * Math.PI / nodeCount;
29
+ const minChordLength = nodeWidth + nodePadding;
30
+ return minChordLength / (2 * Math.sin(angleStep / 2));
31
+ }
32
+ function calculateOptimalRadiusPercentage(nodeCount, containerWidth = 2e3, containerHeight = 2e3, nodeWidth = 200, nodePadding = 120) {
33
+ const minRadius = calculateMinimumRadius(nodeCount, nodeWidth, nodePadding);
34
+ const minDimension = Math.min(containerWidth, containerHeight);
35
+ return Math.min(0.45, Math.max(0.135, minRadius / minDimension));
36
+ }
37
+
38
+ // src/layout/linearLayout.ts
39
+ function applyLinearHorizontalLayout(nodes, options = {}) {
40
+ const { nodeWidth = 200, hGap = 180, centerX = 0, centerY = 0 } = options;
41
+ if (nodes.length === 0) return nodes;
42
+ if (nodes.length === 1) {
43
+ nodes[0].x = centerX;
44
+ nodes[0].y = centerY;
45
+ return nodes;
46
+ }
47
+ const spacing = nodeWidth + hGap;
48
+ const totalWidth = (nodes.length - 1) * spacing;
49
+ const startX = centerX - totalWidth / 2;
50
+ for (let i = 0; i < nodes.length; i++) {
51
+ nodes[i].x = startX + i * spacing;
52
+ nodes[i].y = centerY;
53
+ }
54
+ return nodes;
55
+ }
56
+ function applyLinearVerticalLayout(nodes, options = {}) {
57
+ const { nodeHeight = 80, vGap = 220, centerX = 0, centerY = 0 } = options;
58
+ if (nodes.length === 0) return nodes;
59
+ if (nodes.length === 1) {
60
+ nodes[0].x = centerX;
61
+ nodes[0].y = centerY;
62
+ return nodes;
63
+ }
64
+ const spacing = nodeHeight + vGap;
65
+ const totalHeight = (nodes.length - 1) * spacing;
66
+ const startY = centerY - totalHeight / 2;
67
+ for (let i = 0; i < nodes.length; i++) {
68
+ nodes[i].x = centerX;
69
+ nodes[i].y = startY + i * spacing;
70
+ }
71
+ return nodes;
72
+ }
73
+
74
+ // src/layout/layoutUtils.ts
75
+ function getNodeBounds(node) {
76
+ const baseWidth = Math.max(120, node.title.length * 8 + 40);
77
+ const baseHeight = Math.max(40, 32 + 16);
78
+ const padding = 20;
79
+ return {
80
+ x: node.x - baseWidth / 2 - padding,
81
+ y: node.y - baseHeight / 2 - padding,
82
+ width: baseWidth + padding * 2,
83
+ height: baseHeight + padding * 2
84
+ };
85
+ }
86
+ function checkCollision(box1, box2) {
87
+ return !(box1.x + box1.width < box2.x || box2.x + box2.width < box1.x || box1.y + box1.height < box2.y || box2.y + box2.height < box1.y);
88
+ }
89
+ function resolveCollisions(nodes, maxIterations = 10) {
90
+ const result = [...nodes];
91
+ for (let iter = 0; iter < maxIterations; iter++) {
92
+ let hasCollisions = false;
93
+ const byLevel = result.reduce((acc, n) => {
94
+ var _a;
95
+ (acc[_a = n.level] ?? (acc[_a] = [])).push(n);
96
+ return acc;
97
+ }, {});
98
+ for (const levelNodes of Object.values(byLevel)) {
99
+ for (let i = 0; i < levelNodes.length; i++) {
100
+ for (let j = i + 1; j < levelNodes.length; j++) {
101
+ const n1 = levelNodes[i];
102
+ const n2 = levelNodes[j];
103
+ if (!checkCollision(getNodeBounds(n1), getNodeBounds(n2))) continue;
104
+ hasCollisions = true;
105
+ const dx = n2.x - n1.x;
106
+ const dy = n2.y - n1.y;
107
+ const dist = Math.sqrt(dx * dx + dy * dy);
108
+ if (dist < 1) {
109
+ n2.x += 100;
110
+ } else {
111
+ const b1 = getNodeBounds(n1);
112
+ const b2 = getNodeBounds(n2);
113
+ const minSep = (b1.width + b2.width) / 2;
114
+ const overlap = minSep - dist + 20;
115
+ if (overlap > 0) {
116
+ const move = overlap / 2;
117
+ n1.x -= dx / dist * move;
118
+ n1.y -= dy / dist * move;
119
+ n2.x += dx / dist * move;
120
+ n2.y += dy / dist * move;
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ if (!hasCollisions) break;
127
+ }
128
+ return result;
129
+ }
130
+ function optimizeLayout(nodes) {
131
+ const result = [...nodes];
132
+ const iterations = 5;
133
+ const repulsion = 1e3;
134
+ const damp = 0.8;
135
+ for (let iter = 0; iter < iterations; iter++) {
136
+ const forces = /* @__PURE__ */ new Map();
137
+ result.forEach((n) => forces.set(n.id, { x: 0, y: 0 }));
138
+ for (let i = 0; i < result.length; i++) {
139
+ for (let j = i + 1; j < result.length; j++) {
140
+ const n1 = result[i];
141
+ const n2 = result[j];
142
+ if (n1.level !== n2.level) continue;
143
+ const dx = n2.x - n1.x;
144
+ const dy = n2.y - n1.y;
145
+ const dist = Math.sqrt(dx * dx + dy * dy);
146
+ if (dist <= 0 || dist >= 300) continue;
147
+ const force = repulsion / (dist * dist);
148
+ const f1 = forces.get(n1.id);
149
+ const f2 = forces.get(n2.id);
150
+ f1.x -= dx / dist * force;
151
+ f1.y -= dy / dist * force;
152
+ f2.x += dx / dist * force;
153
+ f2.y += dy / dist * force;
154
+ }
155
+ }
156
+ result.forEach((n) => {
157
+ const f = forces.get(n.id);
158
+ n.x += f.x * damp;
159
+ n.y += f.y * damp;
160
+ });
161
+ }
162
+ return resolveCollisions(result);
163
+ }
164
+
165
+ // src/parser/visualliParser.ts
166
+ function toVisualliLayer(schemaLayer) {
167
+ return {
168
+ type: "layer",
169
+ id: schemaLayer.id,
170
+ level: schemaLayer.level,
171
+ parentLayerId: schemaLayer.parentLayerId,
172
+ parentNodeId: schemaLayer.parentNodeId,
173
+ layout: schemaLayer.layout,
174
+ nodes: schemaLayer.nodes ?? [],
175
+ connections: schemaLayer.connections ?? [],
176
+ containers: schemaLayer.containers ?? []
177
+ };
178
+ }
179
+ var VisualliParseError = class extends Error {
180
+ constructor(message, lineNumber) {
181
+ super(message);
182
+ this.lineNumber = lineNumber;
183
+ this.name = "VisualliParseError";
184
+ }
185
+ };
186
+ function parseVisualliFile(content) {
187
+ const lines = content.trim().split("\n");
188
+ const doc = {
189
+ meta: null,
190
+ layers: /* @__PURE__ */ new Map(),
191
+ layersByLevel: /* @__PURE__ */ new Map(),
192
+ rootLayer: null
193
+ };
194
+ let lineNumber = 0;
195
+ for (const line of lines) {
196
+ lineNumber++;
197
+ if (!line.trim()) continue;
198
+ let obj;
199
+ try {
200
+ obj = JSON.parse(line);
201
+ } catch (err) {
202
+ throw new VisualliParseError(
203
+ `Failed to parse JSON at line ${lineNumber}: ${err}`,
204
+ lineNumber
205
+ );
206
+ }
207
+ if (!obj["type"]) {
208
+ throw new VisualliParseError(
209
+ `Missing 'type' field at line ${lineNumber}`,
210
+ lineNumber
211
+ );
212
+ }
213
+ switch (obj["type"]) {
214
+ case "meta":
215
+ doc.meta = obj;
216
+ break;
217
+ case "layer": {
218
+ const schemaLayer = obj;
219
+ const layer = toVisualliLayer(schemaLayer);
220
+ doc.layers.set(layer.id, layer);
221
+ if (!doc.layersByLevel.has(layer.level)) {
222
+ doc.layersByLevel.set(layer.level, []);
223
+ }
224
+ doc.layersByLevel.get(layer.level).push(layer);
225
+ if (layer.level === 0) {
226
+ doc.rootLayer = layer;
227
+ }
228
+ break;
229
+ }
230
+ default:
231
+ break;
232
+ }
233
+ }
234
+ if (!doc.meta) throw new VisualliParseError("Missing required meta section");
235
+ if (!doc.rootLayer) throw new VisualliParseError("Missing root layer (level 0)");
236
+ return doc;
237
+ }
238
+ async function loadVisualliFile(url) {
239
+ const response = await fetch(url);
240
+ if (!response.ok) {
241
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
242
+ }
243
+ return parseVisualliFile(await response.text());
244
+ }
245
+ function parseVisualliFromMongoDB(visualliJsonl) {
246
+ if (!visualliJsonl || typeof visualliJsonl !== "string") {
247
+ throw new VisualliParseError("Invalid visualli data: expected a JSONL string");
248
+ }
249
+ const trimmed = visualliJsonl.trim();
250
+ if (!trimmed) throw new VisualliParseError("Empty visualli data from MongoDB");
251
+ return parseVisualliFile(trimmed);
252
+ }
253
+ function loadVisualliFileFromFile(file) {
254
+ return new Promise((resolve, reject) => {
255
+ const reader = new FileReader();
256
+ reader.onload = (e) => {
257
+ try {
258
+ resolve(parseVisualliFile(e.target?.result));
259
+ } catch (err) {
260
+ reject(err);
261
+ }
262
+ };
263
+ reader.onerror = () => reject(new Error("Failed to read file"));
264
+ reader.readAsText(file);
265
+ });
266
+ }
267
+ function getChildLayers(doc, nodeId) {
268
+ const result = [];
269
+ for (const layer of doc.layers.values()) {
270
+ if (layer.parentNodeId === nodeId) result.push(layer);
271
+ }
272
+ return result;
273
+ }
274
+ function countLayersBeneath(doc, nodeId, visited = /* @__PURE__ */ new Set()) {
275
+ if (visited.has(nodeId)) return 0;
276
+ visited.add(nodeId);
277
+ const children = getChildLayers(doc, nodeId);
278
+ if (children.length === 0) return 0;
279
+ let maxDepth = 0;
280
+ for (const layer of children) {
281
+ let depth = 1;
282
+ for (const node of layer.nodes) {
283
+ depth = Math.max(depth, 1 + countLayersBeneath(doc, node.id, visited));
284
+ }
285
+ maxDepth = Math.max(maxDepth, depth);
286
+ }
287
+ return maxDepth;
288
+ }
289
+
290
+ // src/parser/visualliConverter.ts
291
+ function doNodesOverlap(n1, n2, padding = 20) {
292
+ return !(n1.x + n1.width / 2 + padding < n2.x - n2.width / 2 - padding || n1.x - n1.width / 2 - padding > n2.x + n2.width / 2 + padding || n1.y + n1.height / 2 + padding < n2.y - n2.height / 2 - padding || n1.y - n1.height / 2 - padding > n2.y + n2.height / 2 + padding);
293
+ }
294
+ function resolveNodeOverlaps(nodes, maxIterations = 10) {
295
+ const padding = 150;
296
+ for (let iter = 0; iter < maxIterations; iter++) {
297
+ let any = false;
298
+ for (let i = 0; i < nodes.length; i++) {
299
+ for (let j = i + 1; j < nodes.length; j++) {
300
+ const n1 = nodes[i];
301
+ const n2 = nodes[j];
302
+ if (!doNodesOverlap(n1, n2, padding)) continue;
303
+ any = true;
304
+ const dx = n2.x - n1.x;
305
+ const dy = n2.y - n1.y;
306
+ const dist = Math.sqrt(dx * dx + dy * dy);
307
+ if (dist < 1) {
308
+ n2.x += n2.width / 2 + padding;
309
+ continue;
310
+ }
311
+ const minDist = (n1.width + n2.width) / 2 + padding;
312
+ if (dist < minDist) {
313
+ const push = (minDist - dist) / 2;
314
+ n1.x -= dx / dist * push;
315
+ n1.y -= dy / dist * push;
316
+ n2.x += dx / dist * push;
317
+ n2.y += dy / dist * push;
318
+ }
319
+ }
320
+ }
321
+ if (!any) break;
322
+ }
323
+ }
324
+ var COLOR_PALETTE = [
325
+ "#12C7D3",
326
+ "#325E8C",
327
+ "#8A70A6",
328
+ "#F54A57",
329
+ "#FF6C4D",
330
+ "#F28C16",
331
+ "#FFD347",
332
+ "#12C7D3",
333
+ "#7F7F7F",
334
+ "#8D8D8D",
335
+ "#12C7D3"
336
+ ];
337
+ function getRandomColorForNode(nodeId) {
338
+ let hash = 0;
339
+ for (let i = 0; i < nodeId.length; i++) {
340
+ hash = nodeId.charCodeAt(i) + ((hash << 5) - hash);
341
+ }
342
+ const index = Math.abs(hash) % COLOR_PALETTE.length;
343
+ return COLOR_PALETTE[index];
344
+ }
345
+ function applyFormation(nodes, formation) {
346
+ if (nodes.length === 0) return;
347
+ if (nodes.length === 1) {
348
+ nodes[0].x = 0;
349
+ nodes[0].y = 0;
350
+ return;
351
+ }
352
+ if (formation === "linear-horizontal" || formation === "linear") {
353
+ applyLinearHorizontalLayout(nodes, { centerX: 0, centerY: 0 });
354
+ } else if (formation === "linear-vertical") {
355
+ applyLinearVerticalLayout(nodes, { centerX: 0, centerY: 0 });
356
+ } else {
357
+ const r = Math.max(250, nodes.length * 70);
358
+ const step = 2 * Math.PI / nodes.length;
359
+ for (let i = 0; i < nodes.length; i++) {
360
+ nodes[i].x = r * Math.cos(i * step);
361
+ nodes[i].y = r * Math.sin(i * step);
362
+ }
363
+ }
364
+ }
365
+ function containerBBox(nodes, padding) {
366
+ let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
367
+ for (const n of nodes) {
368
+ minX = Math.min(minX, n.x - n.width / 2);
369
+ maxX = Math.max(maxX, n.x + n.width / 2);
370
+ minY = Math.min(minY, n.y - n.height / 2);
371
+ maxY = Math.max(maxY, n.y + n.height / 2);
372
+ }
373
+ return { width: maxX - minX + padding * 2, height: maxY - minY + padding * 2 };
374
+ }
375
+ function proxyLinearH(proxies, gap = 180) {
376
+ const total = proxies.reduce((s, p) => s + p.width, 0) + gap * (proxies.length - 1);
377
+ let x = -total / 2;
378
+ for (const p of proxies) {
379
+ p.x = x + p.width / 2;
380
+ p.y = 0;
381
+ x += p.width + gap;
382
+ }
383
+ }
384
+ function proxyLinearV(proxies, gap = 180) {
385
+ const total = proxies.reduce((s, p) => s + p.height, 0) + gap * (proxies.length - 1);
386
+ let y = -total / 2;
387
+ for (const p of proxies) {
388
+ p.x = 0;
389
+ p.y = y + p.height / 2;
390
+ y += p.height + gap;
391
+ }
392
+ }
393
+ function proxyRadial(proxies, radius = 500) {
394
+ if (proxies.length === 1) {
395
+ proxies[0].x = 0;
396
+ proxies[0].y = 0;
397
+ return;
398
+ }
399
+ const startAngle = proxies.length === 2 ? 0 : -Math.PI / 2;
400
+ const step = 2 * Math.PI / proxies.length;
401
+ for (let i = 0; i < proxies.length; i++) {
402
+ const angle = startAngle + i * step;
403
+ proxies[i].x = radius * Math.cos(angle);
404
+ proxies[i].y = radius * Math.sin(angle);
405
+ }
406
+ }
407
+ function calculateNodeWidth(title) {
408
+ return Math.max(200, title.length * 8 + 40);
409
+ }
410
+ function makeFlatNode(nodeId, label, summary, layer, color, branchCount, x = 0, y = 0) {
411
+ return {
412
+ id: nodeId,
413
+ parentId: layer.parentNodeId ?? null,
414
+ x,
415
+ y,
416
+ level: layer.level,
417
+ title: label,
418
+ description: summary,
419
+ color,
420
+ width: calculateNodeWidth(label),
421
+ height: 80,
422
+ isExpanded: branchCount > 0,
423
+ branchCount,
424
+ createdAt: /* @__PURE__ */ new Date(),
425
+ updatedAt: /* @__PURE__ */ new Date()
426
+ };
427
+ }
428
+ function convertLayerWithContainers(layer, doc) {
429
+ const PADDING = 80;
430
+ const flatNodes = [];
431
+ for (const node of layer.nodes) {
432
+ const label = Array.isArray(node.data.label) ? node.data.label.join(" ") : node.data.label || "Untitled";
433
+ const branchCount = countLayersBeneath(doc, node.id);
434
+ flatNodes.push(
435
+ makeFlatNode(
436
+ node.id,
437
+ label,
438
+ node.data.summary || "",
439
+ layer,
440
+ node.data.color || getRandomColorForNode(node.id),
441
+ branchCount
442
+ )
443
+ );
444
+ }
445
+ const byId = new Map(flatNodes.map((n) => [n.id, n]));
446
+ const containerNodeIds = /* @__PURE__ */ new Set();
447
+ const containerGroups = /* @__PURE__ */ new Map();
448
+ for (const container of layer.containers) {
449
+ const group = container.nodes.map((id) => byId.get(id)).filter((n) => !!n);
450
+ if (!group.length) continue;
451
+ container.nodes.forEach((id) => containerNodeIds.add(id));
452
+ const formation = container.data?.["formation"] ?? "radial";
453
+ applyFormation(group, formation);
454
+ containerGroups.set(container.id, group);
455
+ }
456
+ const proxies = [];
457
+ for (const node of layer.nodes) {
458
+ if (!containerNodeIds.has(node.id)) {
459
+ const nodeWidth = calculateNodeWidth(node.data.label);
460
+ proxies.push({ id: node.id, x: 0, y: 0, width: nodeWidth, height: 80 });
461
+ }
462
+ }
463
+ for (const container of layer.containers) {
464
+ const group = containerGroups.get(container.id);
465
+ if (!group?.length) continue;
466
+ const bbox = containerBBox(group, PADDING);
467
+ proxies.push({ id: `__c__${container.id}`, x: 0, y: 0, width: bbox.width, height: bbox.height });
468
+ }
469
+ const layout = layer.layout || "radial";
470
+ if (layout === "linear-horizontal") proxyLinearH(proxies, 180);
471
+ else if (layout === "linear-vertical") proxyLinearV(proxies, 180);
472
+ else proxyRadial(proxies, 500);
473
+ for (const proxy of proxies) {
474
+ if (proxy.id.startsWith("__c__")) {
475
+ const group = containerGroups.get(proxy.id.slice(5));
476
+ group?.forEach((n) => {
477
+ n.x += proxy.x;
478
+ n.y += proxy.y;
479
+ });
480
+ } else {
481
+ const fn = byId.get(proxy.id);
482
+ if (fn) {
483
+ fn.x = proxy.x;
484
+ fn.y = proxy.y;
485
+ }
486
+ }
487
+ }
488
+ resolveNodeOverlaps(flatNodes);
489
+ return flatNodes;
490
+ }
491
+ function convertLayerToFlatNodes(layer, doc) {
492
+ if ((layer.containers ?? []).length > 0) {
493
+ return convertLayerWithContainers(layer, doc);
494
+ }
495
+ const flatNodes = [];
496
+ for (const node of layer.nodes) {
497
+ const label = Array.isArray(node.data.label) ? node.data.label.join(" ") : node.data.label || "Untitled";
498
+ const branchCount = countLayersBeneath(doc, node.id);
499
+ flatNodes.push(
500
+ makeFlatNode(
501
+ node.id,
502
+ label,
503
+ node.data.summary || "",
504
+ layer,
505
+ node.data.color || getRandomColorForNode(node.id),
506
+ branchCount,
507
+ 0,
508
+ 0
509
+ )
510
+ );
511
+ }
512
+ if (flatNodes.length > 0) {
513
+ const layout = layer.layout || "radial";
514
+ if (layout === "linear-horizontal") {
515
+ applyLinearHorizontalLayout(flatNodes, { centerX: 0, centerY: 0 });
516
+ } else if (layout === "linear-vertical") {
517
+ applyLinearVerticalLayout(flatNodes, { centerX: 0, centerY: 0 });
518
+ } else {
519
+ const pct = calculateOptimalRadiusPercentage(flatNodes.length, 2e3, 2e3, 200, 30);
520
+ applyCircularLayout(flatNodes, {
521
+ radiusPercentage: pct,
522
+ containerWidth: 2e3,
523
+ containerHeight: 2e3,
524
+ centerX: 0,
525
+ centerY: 0
526
+ });
527
+ }
528
+ }
529
+ resolveNodeOverlaps(flatNodes);
530
+ return flatNodes;
531
+ }
532
+ function convertVisualliToFlatNodes(doc) {
533
+ const map = /* @__PURE__ */ new Map();
534
+ for (const layer of doc.layers.values()) {
535
+ for (const node of convertLayerToFlatNodes(layer, doc)) {
536
+ map.set(node.id, node);
537
+ }
538
+ }
539
+ for (const node of map.values()) {
540
+ node.childrenIds = [];
541
+ for (const candidate of map.values()) {
542
+ if (candidate.parentId === node.id) node.childrenIds.push(candidate.id);
543
+ }
544
+ }
545
+ return map;
546
+ }
547
+ function getNodesForLayer(doc, layerId) {
548
+ const layer = doc.layers.get(layerId);
549
+ return layer ? convertLayerToFlatNodes(layer, doc) : [];
550
+ }
551
+
552
+ // src/parser/mindmapUtils.ts
553
+ function flattenNodes(nodes) {
554
+ const result = [];
555
+ const traverse = (list) => {
556
+ for (const n of list) {
557
+ result.push(n);
558
+ if (n.children) traverse(n.children);
559
+ }
560
+ };
561
+ traverse(nodes);
562
+ return result;
563
+ }
564
+ function getVisibleNodes(nodes, zoom, focusedNode) {
565
+ const flat = flattenNodes(nodes);
566
+ if (focusedNode) {
567
+ const focused = flat.find((n) => n.id === focusedNode);
568
+ if (focused) {
569
+ return focused.children ? [focused, ...focused.children] : [focused];
570
+ }
571
+ }
572
+ if (zoom <= 0.7) return flat.filter((n) => n.level === 0);
573
+ const maxLevel = Math.floor((zoom - 0.7) / 0.05);
574
+ return flat.filter((n) => n.level <= maxLevel);
575
+ }
576
+ function getNodeConnections(nodes) {
577
+ const flat = flattenNodes(nodes);
578
+ const result = [];
579
+ for (const n of flat) {
580
+ if (!n.parent) continue;
581
+ const parent = flat.find((p) => p.id === n.parent);
582
+ if (parent) result.push({ from: parent, to: n, label: n.relationshipLabel });
583
+ }
584
+ return result;
585
+ }
586
+ function getTopLevelConnections(nodes, topLevelConnections) {
587
+ const flat = flattenNodes(nodes);
588
+ const result = [];
589
+ for (const conn of topLevelConnections) {
590
+ const from = flat.find((n) => n.id === conn.from);
591
+ const to = flat.find((n) => n.id === conn.to);
592
+ if (from && to) result.push({ from, to, label: conn.label ?? "" });
593
+ }
594
+ return result;
595
+ }
596
+ function calculateDistance(x1, y1, x2, y2) {
597
+ return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
598
+ }
599
+
600
+ // src/constants/design.ts
601
+ var DS_COLORS = {
602
+ light: {
603
+ bgApp: "#F0EDE6",
604
+ bgSurface: "#FAF8F4",
605
+ bgInput: "#FFFFFF",
606
+ border: "#DDD9D0",
607
+ borderDivider: "#E8E4DC",
608
+ primary: "#1B2D4F",
609
+ primaryHover: "#243B68",
610
+ textPrimary: "#1A1A18",
611
+ textSecondary: "#6B6860",
612
+ textMuted: "#747270",
613
+ textOnPrimary: "#FAF8F4",
614
+ hoverItem: "rgba(27,45,79,0.07)",
615
+ focusRing: "rgba(27,45,79,0.40)",
616
+ btnMuted: "#8E8C88"
617
+ },
618
+ dark: {
619
+ bgApp: "#141412",
620
+ bgSurface: "#1C1C1A",
621
+ bgInput: "#262622",
622
+ border: "#333330",
623
+ borderDivider: "#2A2A28",
624
+ primary: "#BB532D",
625
+ primaryHover: "#CC6440",
626
+ textPrimary: "#F0EDE6",
627
+ textSecondary: "#B0ABA3",
628
+ textMuted: "#8E8C88",
629
+ textOnPrimary: "#FAF8F4",
630
+ hoverItem: "rgba(198,94,56,0.12)",
631
+ focusRing: "rgba(198,94,56,0.50)",
632
+ btnMuted: "#4A4845"
633
+ }
634
+ };
635
+ var DS_TYPOGRAPHY = {
636
+ fontDisplay: "'Nunito', sans-serif",
637
+ fontBody: "'Nunito Sans', sans-serif",
638
+ logo: { size: "20px", weight: 800, letterSpacing: "0.04em" },
639
+ heading: { size: "16px", weight: 700, letterSpacing: "-0.2px" },
640
+ cta: { size: "11px", weight: 800, letterSpacing: "0.12em", transform: "uppercase" },
641
+ body: { size: "13px", weight: 400 },
642
+ input: { size: "13px", weight: 300, style: "italic" },
643
+ search: { size: "12px", weight: 400 },
644
+ username: { size: "13px", weight: 600 },
645
+ avatar: { size: "11px", weight: 700 },
646
+ footer: { size: "12px", weight: 400 },
647
+ empty: { size: "11px", weight: 300, style: "italic" }
648
+ };
649
+ var DS_RADII = {
650
+ sidebar: 16,
651
+ input: 8,
652
+ cta: 7,
653
+ item: 6,
654
+ swatch: 10
655
+ };
656
+ var BRAND_COLORS = {
657
+ primary: {
658
+ 50: "#EFF6FF",
659
+ 100: "#DBEAFE",
660
+ 200: "#BFDBFE",
661
+ 300: "#93C5FD",
662
+ 400: "#60A5FA",
663
+ 500: "#3B82F6",
664
+ 600: "#2563EB",
665
+ 700: "#1D4ED8",
666
+ 800: "#1E40AF",
667
+ 900: "#1E3A8A"
668
+ },
669
+ secondary: {
670
+ 50: "#F0FDF4",
671
+ 100: "#DCFCE7",
672
+ 200: "#BBF7D0",
673
+ 300: "#86EFAC",
674
+ 400: "#4ADE80",
675
+ 500: "#10B981",
676
+ 600: "#059669",
677
+ 700: "#047857",
678
+ 800: "#065F46",
679
+ 900: "#064E3B"
680
+ },
681
+ accent: {
682
+ purple: "#8B5CF6",
683
+ cyan: "#06B6D4",
684
+ sky: "#0EA5E9",
685
+ lime: "#84CC16",
686
+ amber: "#F59E0B",
687
+ red: "#EF4444"
688
+ }
689
+ };
690
+ var LEVEL_COLORS = {
691
+ level0: BRAND_COLORS.primary[500],
692
+ level1: BRAND_COLORS.primary[400],
693
+ level2: BRAND_COLORS.primary[300],
694
+ level3: BRAND_COLORS.primary[200],
695
+ level4: BRAND_COLORS.primary[300],
696
+ level5: BRAND_COLORS.primary[400],
697
+ level6: BRAND_COLORS.primary[500],
698
+ level7: BRAND_COLORS.primary[600],
699
+ level8: BRAND_COLORS.primary[700]
700
+ };
701
+ var LEVEL_COLOR_ARRAY = [
702
+ LEVEL_COLORS.level0,
703
+ LEVEL_COLORS.level1,
704
+ LEVEL_COLORS.level2,
705
+ LEVEL_COLORS.level3,
706
+ LEVEL_COLORS.level4,
707
+ LEVEL_COLORS.level5,
708
+ LEVEL_COLORS.level6,
709
+ LEVEL_COLORS.level7,
710
+ LEVEL_COLORS.level8
711
+ ];
712
+ var PARENT_FALLBACK_COLORS = [
713
+ "#12C7D3",
714
+ "#325E8C",
715
+ "#8A70A6",
716
+ "#F54A57",
717
+ "#FF6C4D",
718
+ "#F28C16",
719
+ "#FFD347",
720
+ "#7F7F7F",
721
+ "#8D8D8D"
722
+ ];
723
+ var SEMANTIC_COLORS = {
724
+ success: BRAND_COLORS.secondary[500],
725
+ error: BRAND_COLORS.accent.red,
726
+ warning: BRAND_COLORS.accent.amber,
727
+ info: BRAND_COLORS.primary[500]
728
+ };
729
+ var THEME_COLORS = {
730
+ light: {
731
+ background: { primary: "#FAF8F4", secondary: "#F0EDE6", tertiary: "#FFFFFF" },
732
+ text: { primary: "#1A1A18", secondary: "#6B6860", tertiary: "#747270" },
733
+ border: { primary: "#DDD9D0", secondary: "#E8E4DC" }
734
+ },
735
+ dark: {
736
+ background: { primary: "#1C1C1A", secondary: "#141412", tertiary: "#262622" },
737
+ text: { primary: "#F0EDE6", secondary: "#B0ABA3", tertiary: "#8E8C88" },
738
+ border: { primary: "#333330", secondary: "#2A2A28" }
739
+ }
740
+ };
741
+ var CANVAS_COLORS = {
742
+ node: {
743
+ border: "#FFFFFF",
744
+ titleColor: "#1e1b4b",
745
+ badgeBackground: "#FFC107",
746
+ badgeStroke: "#fcd34d"
747
+ }
748
+ };
749
+ var SPACING = {
750
+ xs: 4,
751
+ sm: 8,
752
+ md: 16,
753
+ lg: 24,
754
+ xl: 32,
755
+ "2xl": 48,
756
+ "3xl": 64,
757
+ "4xl": 96
758
+ };
759
+ var LAYOUT_SPACING = {
760
+ nodeSpacing: 200,
761
+ levelSpacing: 150,
762
+ containerPadding: 20,
763
+ gridSize: 50
764
+ };
765
+ var FONTS = {
766
+ primary: "'Inter', sans-serif",
767
+ secondary: "'Poppins', sans-serif",
768
+ mono: "'Roboto Mono', monospace"
769
+ };
770
+ var FONT_SIZES = {
771
+ xs: "0.75rem",
772
+ sm: "0.875rem",
773
+ base: "1rem",
774
+ lg: "1.125rem",
775
+ xl: "1.25rem",
776
+ "2xl": "1.5rem",
777
+ "3xl": "1.875rem",
778
+ "4xl": "2.25rem"
779
+ };
780
+ function getColorForLevel(level) {
781
+ return LEVEL_COLOR_ARRAY[level % LEVEL_COLOR_ARRAY.length];
782
+ }
783
+ function darkenHexColor(hex, amount = 0.22) {
784
+ if (!hex || !hex.startsWith("#")) return hex;
785
+ let r, g, b;
786
+ if (hex.length === 4) {
787
+ r = parseInt(hex[1] + hex[1], 16);
788
+ g = parseInt(hex[2] + hex[2], 16);
789
+ b = parseInt(hex[3] + hex[3], 16);
790
+ } else if (hex.length === 7) {
791
+ r = parseInt(hex.slice(1, 3), 16);
792
+ g = parseInt(hex.slice(3, 5), 16);
793
+ b = parseInt(hex.slice(5, 7), 16);
794
+ } else {
795
+ return hex;
796
+ }
797
+ if (isNaN(r) || isNaN(g) || isNaN(b)) return hex;
798
+ const rn = r / 255, gn = g / 255, bn = b / 255;
799
+ const cmax = Math.max(rn, gn, bn), cmin = Math.min(rn, gn, bn);
800
+ const d = cmax - cmin;
801
+ const l = (cmax + cmin) / 2;
802
+ const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
803
+ let h = 0;
804
+ if (d !== 0) {
805
+ const seg = cmax === rn ? (gn - bn) / d : cmax === gn ? (bn - rn) / d + 2 : (rn - gn) / d + 4;
806
+ h = (seg * 60 + 360) % 360;
807
+ }
808
+ const newL = Math.max(0, l - amount);
809
+ const c2 = (1 - Math.abs(2 * newL - 1)) * s;
810
+ const x2 = c2 * (1 - Math.abs(h / 60 % 2 - 1));
811
+ const m2 = newL - c2 / 2;
812
+ let r1 = 0, g1 = 0, b1 = 0;
813
+ if (h < 60) {
814
+ r1 = c2;
815
+ g1 = x2;
816
+ b1 = 0;
817
+ } else if (h < 120) {
818
+ r1 = x2;
819
+ g1 = c2;
820
+ b1 = 0;
821
+ } else if (h < 180) {
822
+ r1 = 0;
823
+ g1 = c2;
824
+ b1 = x2;
825
+ } else if (h < 240) {
826
+ r1 = 0;
827
+ g1 = x2;
828
+ b1 = c2;
829
+ } else if (h < 300) {
830
+ r1 = x2;
831
+ g1 = 0;
832
+ b1 = c2;
833
+ } else {
834
+ r1 = c2;
835
+ g1 = 0;
836
+ b1 = x2;
837
+ }
838
+ const toHex = (v) => Math.round((v + m2) * 255).toString(16).padStart(2, "0");
839
+ return `#${toHex(r1)}${toHex(g1)}${toHex(b1)}`;
840
+ }
841
+ function getThemeBackground(isDark, variant = "primary") {
842
+ return isDark ? THEME_COLORS.dark.background[variant] : THEME_COLORS.light.background[variant];
843
+ }
844
+ function getThemeText(isDark, variant = "primary") {
845
+ return isDark ? THEME_COLORS.dark.text[variant] : THEME_COLORS.light.text[variant];
846
+ }
847
+ function getThemeBorder(isDark, variant = "primary") {
848
+ return isDark ? THEME_COLORS.dark.border[variant] : THEME_COLORS.light.border[variant];
849
+ }
850
+
851
+ // src/parser/configUtils.ts
852
+ function createMindMapConfig(nodes, topLevelConnections, title = "Untitled Mindmap") {
853
+ const now = (/* @__PURE__ */ new Date()).toISOString();
854
+ return {
855
+ metadata: {
856
+ title,
857
+ description: `Interactive mindmap with ${nodes.length} nodes`,
858
+ version: "1.0.0",
859
+ created: now,
860
+ lastModified: now
861
+ },
862
+ nodes,
863
+ topLevelConnections,
864
+ settings: {
865
+ defaultColors: {
866
+ level0: LEVEL_COLORS.level0,
867
+ level1: LEVEL_COLORS.level1,
868
+ level2: LEVEL_COLORS.level2,
869
+ level3: LEVEL_COLORS.level3
870
+ },
871
+ layout: {
872
+ nodeSpacing: LAYOUT_SPACING.nodeSpacing,
873
+ levelSpacing: LAYOUT_SPACING.levelSpacing
874
+ }
875
+ }
876
+ };
877
+ }
878
+ function generateId(prefix, level, index) {
879
+ return `${prefix}-l${level}-${index}`;
880
+ }
881
+ function generateLevelNodes(parentId, level, maxLevels, xOffset = 0, yOffset = 0) {
882
+ if (level >= maxLevels) return [];
883
+ const count = 5;
884
+ const levelSpacing = 200;
885
+ const nodeSpacing = 300;
886
+ const nodes = [];
887
+ for (let i = 0; i < count; i++) {
888
+ const nodeId = parentId ? generateId(`${parentId}-${i}`, level, i) : generateId("root", level, i);
889
+ const x = xOffset + (i - (count - 1) / 2) * nodeSpacing;
890
+ const y = yOffset + level * levelSpacing;
891
+ const node = {
892
+ id: nodeId,
893
+ title: `Level ${level} Node ${i + 1}`,
894
+ level,
895
+ x,
896
+ y,
897
+ color: getColorForLevel(level),
898
+ relationshipLabel: level > 0 ? "child" : void 0,
899
+ parent: parentId ?? void 0
900
+ };
901
+ const children = generateLevelNodes(nodeId, level + 1, maxLevels, x, y + levelSpacing);
902
+ if (children.length > 0) node.children = children;
903
+ nodes.push(node);
904
+ }
905
+ return nodes;
906
+ }
907
+ function generateSampleConfig(maxLevels = 9) {
908
+ const nodes = generateLevelNodes(null, 0, maxLevels, 0, 0);
909
+ const topLevelConnections = nodes.slice(0, -1).map((n, i) => ({
910
+ from: n.id,
911
+ to: nodes[i + 1].id,
912
+ label: "related to"
913
+ }));
914
+ return {
915
+ metadata: {
916
+ id: `mindmap-${Date.now()}`,
917
+ title: "Sample Mind Map",
918
+ description: "A sample mind map",
919
+ version: "1.0.0",
920
+ created: (/* @__PURE__ */ new Date()).toISOString(),
921
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
922
+ },
923
+ nodes,
924
+ topLevelConnections,
925
+ settings: {
926
+ defaultColors: {
927
+ level0: LEVEL_COLORS.level0,
928
+ level1: LEVEL_COLORS.level1,
929
+ level2: LEVEL_COLORS.level2,
930
+ level3: LEVEL_COLORS.level3,
931
+ level4: LEVEL_COLORS.level4,
932
+ level5: LEVEL_COLORS.level5,
933
+ level6: LEVEL_COLORS.level6,
934
+ level7: LEVEL_COLORS.level7,
935
+ level8: LEVEL_COLORS.level8
936
+ },
937
+ layout: {
938
+ nodeSpacing: LAYOUT_SPACING.nodeSpacing,
939
+ levelSpacing: LAYOUT_SPACING.levelSpacing
940
+ }
941
+ }
942
+ };
943
+ }
944
+
945
+ // src/constants/performanceConstants.ts
946
+ var FPS_TARGET = 60;
947
+ var FPS_WARNING_THRESHOLD = 45;
948
+ var FPS_CRITICAL_THRESHOLD = 30;
949
+ var TARGET_FRAME_TIME = 1e3 / FPS_TARGET;
950
+ var MAX_FRAME_TIME = 18;
951
+ var FPS_TARGETS = {
952
+ PAN_MIN: 30,
953
+ ZOOM_TARGET: 60,
954
+ WARNING_THRESHOLD: 20
955
+ };
956
+ var TIMING_THRESHOLDS = {
957
+ VIEWPORT_QUERY_MAX: 5,
958
+ SPATIAL_UPDATE_MAX: 10,
959
+ DRAG_RESPONSE_MAX: 16,
960
+ LOAD_5K_TARGET: 2e3,
961
+ LOAD_10K_TARGET: 3e3,
962
+ VIEWPORT_DEBOUNCE: 100,
963
+ SPATIAL_INDEX_BUILD: 50
964
+ };
965
+ var MEMORY_TARGET_MB = 500;
966
+ var MEMORY_WARNING_MB = 600;
967
+ var MEMORY_CRITICAL_MB = 800;
968
+ var MEMORY_LIMITS = {
969
+ MAX_10K_NODES: 500,
970
+ MAX_GROWTH_PERCENT: 20,
971
+ WARNING_THRESHOLD: 400
972
+ };
973
+ var VIEWPORT_MARGIN = 0.1;
974
+ var MAX_VISIBLE_NODES = 800;
975
+ var CULLING_QUERY_TIME_BUDGET = 5;
976
+ var RBUSH_MAX_ENTRIES = 16;
977
+ var SPATIAL_INDEX_BUILD_BUDGET = 50;
978
+ var NODE_MIN_WIDTH = 200;
979
+ var NODE_HEIGHT = 80;
980
+ var NODE_PADDING = 40;
981
+ var CHAR_WIDTH = 8;
982
+ var TITLE_MAX_LENGTH = 150;
983
+ var CORNER_RADIUS = 8;
984
+ var BORDER_WIDTH_NORMAL = 2;
985
+ var BORDER_WIDTH_SELECTED = 3;
986
+ var COLOR_SELECTED = "#1B2D4F";
987
+ var COLOR_BORDER_LIGHT = "#4A3728";
988
+ var COLOR_BORDER_DARK = "#FAF6F1";
989
+ var COLOR_DEFAULT_NODE = "#FAF6F1";
990
+ var ZOOM_MIN = 0.05;
991
+ var ZOOM_MAX = 5;
992
+ var ZOOM_DEFAULT = 1;
993
+ var ZOOM_STEP = 0.1;
994
+ var ZOOM_WHEEL_SENSITIVITY = 1e-3;
995
+ var ZOOM_NAV_IN_THRESHOLD = 2.7;
996
+ var ZOOM_NAV_OUT_THRESHOLD = 0.4;
997
+ var TEXT_LABEL_HIDE_BELOW_ZOOM = 0.3;
998
+ var QUALITY_HIGH = {
999
+ shadows: true,
1000
+ bezierCurves: true,
1001
+ animations: true,
1002
+ textQuality: "high",
1003
+ antialiasing: true
1004
+ };
1005
+ var QUALITY_MEDIUM = {
1006
+ shadows: false,
1007
+ bezierCurves: true,
1008
+ animations: true,
1009
+ textQuality: "medium",
1010
+ antialiasing: true
1011
+ };
1012
+ var QUALITY_LOW = {
1013
+ shadows: false,
1014
+ bezierCurves: false,
1015
+ animations: false,
1016
+ textQuality: "low",
1017
+ antialiasing: false
1018
+ };
1019
+ var FPS_SAMPLE_WINDOW = 60;
1020
+ var FPS_UPDATE_INTERVAL = 1e3;
1021
+ var MEMORY_CHECK_INTERVAL = 5e3;
1022
+ var PERFORMANCE_TOAST_COOLDOWN = 1e4;
1023
+ var FPS_DOWNGRADE_THRESHOLD = 45;
1024
+ var FPS_UPGRADE_THRESHOLD = 58;
1025
+ var FPS_STABILIZATION_FRAMES = 180;
1026
+ var DEFAULT_CANVAS_WIDTH = 1920;
1027
+ var DEFAULT_CANVAS_HEIGHT = 1080;
1028
+ var CLICK_THRESHOLD = 5;
1029
+ var DOUBLE_CLICK_DELAY = 300;
1030
+ var DRAG_START_DISTANCE = 10;
1031
+ var MAX_NODES = 2e4;
1032
+ var CIRCULAR_REFERENCE_MAX_DEPTH = 1e3;
1033
+ var RESIZE_DEBOUNCE = 300;
1034
+ var SCROLL_THROTTLE = 16;
1035
+ var SEARCH_DEBOUNCE = 500;
1036
+ var NODE_COUNT_THRESHOLDS = {
1037
+ SPATIAL_INDEX_MIN: 100,
1038
+ VIEWPORT_CULLING_MIN: 500,
1039
+ CANVAS_RENDERING_MIN: 2e3,
1040
+ DOM_RENDERING_MAX: 5e3
1041
+ };
1042
+ var VIEWPORT_CONFIG = {
1043
+ BUFFER_PERCENT: 0.2,
1044
+ MIN_VISIBLE_NODES: 10
1045
+ };
1046
+
1047
+ // src/constants/renderConfig.ts
1048
+ var DEFAULT_RENDER_CONFIG = {
1049
+ renderMode: "canvas",
1050
+ qualityLevel: "high",
1051
+ cullingEnabled: false,
1052
+ fpsTarget: FPS_TARGET,
1053
+ memoryLimit: MEMORY_TARGET_MB
1054
+ };
1055
+ var SIMPLIFIED_RENDER_CONFIG = {
1056
+ renderMode: "simplified",
1057
+ qualityLevel: "low",
1058
+ cullingEnabled: true,
1059
+ fpsTarget: 30,
1060
+ memoryLimit: 400
1061
+ };
1062
+ var QUALITY_SETTINGS = {
1063
+ high: QUALITY_HIGH,
1064
+ medium: QUALITY_MEDIUM,
1065
+ low: QUALITY_LOW
1066
+ };
1067
+ var FEATURE_FLAGS = {
1068
+ viewportCulling: true,
1069
+ spatialIndexing: true,
1070
+ shadows: true,
1071
+ bezierCurves: true,
1072
+ animations: true,
1073
+ branchBadges: true,
1074
+ autoQualityAdjustment: true,
1075
+ performanceMonitoring: true,
1076
+ performanceToasts: true,
1077
+ debugOverlay: false,
1078
+ showFPS: false,
1079
+ showMemory: false,
1080
+ showVisibleCount: false
1081
+ };
1082
+ var RENDER_MODES = {
1083
+ canvas: {
1084
+ name: "Canvas",
1085
+ description: "Full Konva canvas rendering with all features",
1086
+ features: { culling: true, shadows: true, bezierCurves: true, animations: true }
1087
+ },
1088
+ simplified: {
1089
+ name: "Simplified",
1090
+ description: "Reduced feature set for low-end devices",
1091
+ features: { culling: true, shadows: false, bezierCurves: false, animations: false }
1092
+ }
1093
+ };
1094
+ var QUALITY_LEVELS = {
1095
+ high: { name: "High", description: "All visual features enabled", targetFPS: 60, features: QUALITY_HIGH },
1096
+ medium: { name: "Medium", description: "Shadows disabled, other features enabled", targetFPS: 60, features: QUALITY_MEDIUM },
1097
+ low: { name: "Low", description: "Minimal visual features, max performance", targetFPS: 30, features: QUALITY_LOW }
1098
+ };
1099
+ function getOptimalQualityLevel(currentFPS, currentQuality) {
1100
+ if (currentFPS < 45 && currentQuality === "high") return "medium";
1101
+ if (currentFPS < 30 && currentQuality === "medium") return "low";
1102
+ if (currentFPS > 58 && currentQuality === "low") return "medium";
1103
+ if (currentFPS > 58 && currentQuality === "medium") return "high";
1104
+ return currentQuality;
1105
+ }
1106
+ function getRenderConfigForQuality(quality) {
1107
+ return { qualityLevel: quality, fpsTarget: QUALITY_LEVELS[quality].targetFPS };
1108
+ }
1109
+ function isFeatureEnabled(featureName, config) {
1110
+ if (!FEATURE_FLAGS[featureName]) return false;
1111
+ const qs = QUALITY_SETTINGS[config.qualityLevel];
1112
+ switch (featureName) {
1113
+ case "shadows":
1114
+ return qs.shadows;
1115
+ case "bezierCurves":
1116
+ return qs.bezierCurves;
1117
+ case "animations":
1118
+ return qs.animations;
1119
+ default:
1120
+ return FEATURE_FLAGS[featureName];
1121
+ }
1122
+ }
1123
+ function getRecommendedRenderConfig() {
1124
+ if (typeof performance === "undefined" || !performance["memory"]) {
1125
+ return SIMPLIFIED_RENDER_CONFIG;
1126
+ }
1127
+ const memory = performance["memory"];
1128
+ if (memory?.["jsHeapSizeLimit"] && memory["jsHeapSizeLimit"] < 512 * 1024 * 1024) {
1129
+ return SIMPLIFIED_RENDER_CONFIG;
1130
+ }
1131
+ return DEFAULT_RENDER_CONFIG;
1132
+ }
1133
+
1134
+ // src/viewport/viewportUtils.ts
1135
+ function calculateViewportBounds(viewport, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1136
+ const worldW = canvasWidth / viewport.zoomLevel;
1137
+ const worldH = canvasHeight / viewport.zoomLevel;
1138
+ const mx = worldW * VIEWPORT_MARGIN;
1139
+ const my = worldH * VIEWPORT_MARGIN;
1140
+ return {
1141
+ minX: viewport.centerX - worldW / 2 - mx,
1142
+ minY: viewport.centerY - worldH / 2 - my,
1143
+ maxX: viewport.centerX + worldW / 2 + mx,
1144
+ maxY: viewport.centerY + worldH / 2 + my
1145
+ };
1146
+ }
1147
+ function recalculateViewportBounds(viewport, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1148
+ return {
1149
+ ...viewport,
1150
+ visibleBounds: calculateViewportBounds(viewport, canvasWidth, canvasHeight)
1151
+ };
1152
+ }
1153
+ function worldToScreen(worldX, worldY, viewport, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1154
+ return {
1155
+ x: (worldX - viewport.centerX) * viewport.zoomLevel + canvasWidth / 2,
1156
+ y: (worldY - viewport.centerY) * viewport.zoomLevel + canvasHeight / 2
1157
+ };
1158
+ }
1159
+ function screenToWorld(screenX, screenY, viewport, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1160
+ return {
1161
+ x: (screenX - canvasWidth / 2) / viewport.zoomLevel + viewport.centerX,
1162
+ y: (screenY - canvasHeight / 2) / viewport.zoomLevel + viewport.centerY
1163
+ };
1164
+ }
1165
+ function clampZoom(zoom) {
1166
+ return Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoom));
1167
+ }
1168
+ function panViewport(deltaX, deltaY, viewport) {
1169
+ return {
1170
+ ...viewport,
1171
+ centerX: viewport.centerX - deltaX / viewport.zoomLevel,
1172
+ centerY: viewport.centerY - deltaY / viewport.zoomLevel
1173
+ };
1174
+ }
1175
+ function setViewportCenter(x, y, viewport) {
1176
+ return { ...viewport, centerX: x, centerY: y };
1177
+ }
1178
+ function zoomViewport(delta, viewport, pivotX, pivotY, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1179
+ const newZoom = clampZoom(viewport.zoomLevel * (1 + delta));
1180
+ let cx = viewport.centerX;
1181
+ let cy = viewport.centerY;
1182
+ if (pivotX !== void 0 && pivotY !== void 0) {
1183
+ const worldPivot = screenToWorld(pivotX, pivotY, viewport, canvasWidth, canvasHeight);
1184
+ cx = worldPivot.x - (pivotX - canvasWidth / 2) / newZoom;
1185
+ cy = worldPivot.y - (pivotY - canvasHeight / 2) / newZoom;
1186
+ }
1187
+ return { ...viewport, centerX: cx, centerY: cy, zoomLevel: newZoom };
1188
+ }
1189
+ function focusOnNode(nodeX, nodeY, zoom) {
1190
+ const clamped = clampZoom(zoom);
1191
+ return {
1192
+ centerX: nodeX,
1193
+ centerY: nodeY,
1194
+ zoomLevel: clamped,
1195
+ rotation: 0,
1196
+ visibleBounds: calculateViewportBounds(
1197
+ { centerX: nodeX, centerY: nodeY, zoomLevel: clamped, rotation: 0, visibleBounds: { minX: 0, minY: 0, maxX: 0, maxY: 0 } }
1198
+ )
1199
+ };
1200
+ }
1201
+ function isNodeInViewport(nodeX, nodeY, nodeWidth, nodeHeight, viewport, canvasWidth = DEFAULT_CANVAS_WIDTH, canvasHeight = DEFAULT_CANVAS_HEIGHT) {
1202
+ const bounds = calculateViewportBounds(viewport, canvasWidth, canvasHeight);
1203
+ const hw = nodeWidth / 2;
1204
+ const hh = nodeHeight / 2;
1205
+ return !(nodeX + hw < bounds.minX || nodeX - hw > bounds.maxX || nodeY + hh < bounds.minY || nodeY - hh > bounds.maxY);
1206
+ }
1207
+
1208
+ // src/performance/performanceMonitor.ts
1209
+ var FPSMonitor = class {
1210
+ constructor() {
1211
+ this.frameTimes = [];
1212
+ this.lastTime = 0;
1213
+ this.currentFPS = FPS_TARGET;
1214
+ this.rafId = null;
1215
+ }
1216
+ start() {
1217
+ if (this.rafId !== null) return;
1218
+ const measure = (ts) => {
1219
+ if (this.lastTime > 0) {
1220
+ this.frameTimes.push(ts - this.lastTime);
1221
+ if (this.frameTimes.length > FPS_SAMPLE_WINDOW) this.frameTimes.shift();
1222
+ this.currentFPS = this.calc();
1223
+ }
1224
+ this.lastTime = ts;
1225
+ this.rafId = requestAnimationFrame(measure);
1226
+ };
1227
+ this.rafId = requestAnimationFrame(measure);
1228
+ }
1229
+ stop() {
1230
+ if (this.rafId !== null) {
1231
+ cancelAnimationFrame(this.rafId);
1232
+ this.rafId = null;
1233
+ }
1234
+ }
1235
+ calc() {
1236
+ if (this.frameTimes.length === 0) return FPS_TARGET;
1237
+ const avg = this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;
1238
+ return Math.round(1e3 / avg);
1239
+ }
1240
+ getFPS() {
1241
+ return this.currentFPS;
1242
+ }
1243
+ getAverageFPS() {
1244
+ return this.currentFPS;
1245
+ }
1246
+ getP95FrameTime() {
1247
+ if (this.frameTimes.length === 0) return 0;
1248
+ const sorted = [...this.frameTimes].sort((a, b) => a - b);
1249
+ return sorted[Math.floor(sorted.length * 0.95)];
1250
+ }
1251
+ reset() {
1252
+ this.frameTimes = [];
1253
+ this.currentFPS = FPS_TARGET;
1254
+ }
1255
+ };
1256
+ var MemoryMonitor = class {
1257
+ constructor() {
1258
+ this.timer = null;
1259
+ this.current = 0;
1260
+ }
1261
+ start(onUpdate) {
1262
+ if (this.timer !== null) return;
1263
+ this.timer = setInterval(() => {
1264
+ this.current = this.measure();
1265
+ onUpdate?.(this.current);
1266
+ }, MEMORY_CHECK_INTERVAL);
1267
+ }
1268
+ stop() {
1269
+ if (this.timer !== null) {
1270
+ clearInterval(this.timer);
1271
+ this.timer = null;
1272
+ }
1273
+ }
1274
+ measure() {
1275
+ const mem = performance["memory"];
1276
+ return mem ? mem["usedJSHeapSize"] / (1024 * 1024) : 0;
1277
+ }
1278
+ getMemoryMB() {
1279
+ return this.current;
1280
+ }
1281
+ getStats() {
1282
+ const mem = performance["memory"];
1283
+ if (!mem) return null;
1284
+ return {
1285
+ usedJSHeapSize: mem["usedJSHeapSize"],
1286
+ totalJSHeapSize: mem["totalJSHeapSize"],
1287
+ jsHeapSizeLimit: mem["jsHeapSizeLimit"],
1288
+ heapUsagePercent: mem["usedJSHeapSize"] / mem["jsHeapSizeLimit"] * 100
1289
+ };
1290
+ }
1291
+ static isSupported() {
1292
+ return !!performance["memory"];
1293
+ }
1294
+ };
1295
+ var PerformanceProfiler = class {
1296
+ constructor() {
1297
+ this.marks = /* @__PURE__ */ new Map();
1298
+ this.measures = /* @__PURE__ */ new Map();
1299
+ }
1300
+ mark(label) {
1301
+ this.marks.set(label, performance.now());
1302
+ }
1303
+ measure(label) {
1304
+ const start = this.marks.get(label);
1305
+ if (start === void 0) return 0;
1306
+ const duration = performance.now() - start;
1307
+ const list = this.measures.get(label) ?? [];
1308
+ list.push(duration);
1309
+ this.measures.set(label, list);
1310
+ this.marks.delete(label);
1311
+ return duration;
1312
+ }
1313
+ getAverage(label) {
1314
+ const list = this.measures.get(label);
1315
+ if (!list?.length) return 0;
1316
+ return list.reduce((a, b) => a + b, 0) / list.length;
1317
+ }
1318
+ getP95(label) {
1319
+ const list = this.measures.get(label);
1320
+ if (!list?.length) return 0;
1321
+ const sorted = [...list].sort((a, b) => a - b);
1322
+ return sorted[Math.floor(sorted.length * 0.95)];
1323
+ }
1324
+ clear(label) {
1325
+ if (label) {
1326
+ this.marks.delete(label);
1327
+ this.measures.delete(label);
1328
+ } else {
1329
+ this.marks.clear();
1330
+ this.measures.clear();
1331
+ }
1332
+ }
1333
+ };
1334
+
1335
+ // src/animations/easing.ts
1336
+ var easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
1337
+ var easeInOutQuart = (t) => t < 0.5 ? 8 * t * t * t * t : 1 - Math.pow(-2 * t + 2, 4) / 2;
1338
+ var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1339
+ var easeInCubic = (t) => t * t * t;
1340
+ var easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
1341
+ var easeInOutSine = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
1342
+ function createCubicBezier(x1, y1, x2, y2) {
1343
+ const N = 256;
1344
+ const lut = new Float64Array(N + 1);
1345
+ for (let i = 0; i <= N; i++) {
1346
+ const target = i / N;
1347
+ let lo = 0, hi = 1;
1348
+ for (let j = 0; j < 20; j++) {
1349
+ const mid = (lo + hi) / 2;
1350
+ const om = 1 - mid;
1351
+ const x = 3 * om * om * mid * x1 + 3 * om * mid * mid * x2 + mid * mid * mid;
1352
+ if (x < target) lo = mid;
1353
+ else hi = mid;
1354
+ }
1355
+ const u = (lo + hi) / 2;
1356
+ const ou = 1 - u;
1357
+ lut[i] = 3 * ou * ou * u * y1 + 3 * ou * u * u * y2 + u * u * u;
1358
+ }
1359
+ return (t) => {
1360
+ if (t <= 0) return 0;
1361
+ if (t >= 1) return 1;
1362
+ const s = t * N;
1363
+ const i = s | 0;
1364
+ return lut[i] + (lut[i + 1] - lut[i]) * (s - i);
1365
+ };
1366
+ }
1367
+ var easeZoomTransition = createCubicBezier(0.2, 0.8, 0.2, 1);
1368
+
1369
+ // src/animations/constants.ts
1370
+ var ANIMATION_DURATION = {
1371
+ ZOOM_IN_LAYER: 500,
1372
+ ZOOM_OUT_LAYER: 500,
1373
+ ZOOM_GESTURE: 500,
1374
+ FADE_OUT: 200,
1375
+ FADE_IN: 250,
1376
+ COLOR_TRANSITION: 500,
1377
+ CURSOR_CHANGE: 150,
1378
+ LOADING_FADE: 200
1379
+ };
1380
+ var ANIMATION_PHASES = {
1381
+ ZOOM_IN: {
1382
+ ZOOM_TO_TARGET: 500,
1383
+ FADE_OUT_START: 120,
1384
+ FADE_OUT: 280,
1385
+ CONTENT_SWAP: 310,
1386
+ FADE_IN: 200,
1387
+ TOTAL: 510
1388
+ },
1389
+ ZOOM_OUT: {
1390
+ FADE_OUT: 200,
1391
+ ZOOM_BACK: 50,
1392
+ FADE_IN: 250,
1393
+ TOTAL: 500
1394
+ }
1395
+ };
1396
+ var ZOOM_THRESHOLDS = {
1397
+ ENTER_LAYER: 5,
1398
+ EXIT_LAYER: 0.5,
1399
+ GESTURE_TRIGGER: 0.8
1400
+ };
1401
+ var COOLDOWN = {
1402
+ NAVIGATION: 1e3,
1403
+ ZOOM_GESTURE: 800,
1404
+ DOUBLE_CLICK: 200
1405
+ };
1406
+ var OPACITY = {
1407
+ VISIBLE: 1,
1408
+ HIDDEN: 0,
1409
+ TRANSITIONING: 0.3
1410
+ };
1411
+ var TARGET_ZOOM = {
1412
+ NORMAL: 1,
1413
+ FOCUSED: 0.8,
1414
+ FILL_NODE: 3,
1415
+ DETAIL: 2
1416
+ };
1417
+ var VIEWPORT_FRAMING = {
1418
+ NODE_FILL_RATIO: 0.7,
1419
+ EXIT_NODE_SIZE: 0.4,
1420
+ PADDING_RATIO: 0.1
1421
+ };
1422
+
1423
+ // src/spatial/spatialIndex.ts
1424
+ import RBush from "rbush";
1425
+ var RBushSpatialIndex = class {
1426
+ constructor() {
1427
+ this.tree = new RBush();
1428
+ this.cache = /* @__PURE__ */ new Map();
1429
+ }
1430
+ insert(nodeId, bounds) {
1431
+ this.tree.insert({ ...bounds, nodeId });
1432
+ this.cache.set(nodeId, bounds);
1433
+ }
1434
+ remove(nodeId, bounds) {
1435
+ this.tree.remove({ ...bounds, nodeId }, (a, b) => a.nodeId === b.nodeId);
1436
+ this.cache.delete(nodeId);
1437
+ }
1438
+ update(nodeId, oldBounds, newBounds) {
1439
+ this.remove(nodeId, oldBounds);
1440
+ this.insert(nodeId, newBounds);
1441
+ }
1442
+ query(bounds) {
1443
+ return this.tree.search(bounds).map((item) => item.nodeId);
1444
+ }
1445
+ clear() {
1446
+ this.tree.clear();
1447
+ this.cache.clear();
1448
+ }
1449
+ size() {
1450
+ return this.cache.size;
1451
+ }
1452
+ bulkLoad(items) {
1453
+ const rbushItems = items.map(({ nodeId, bounds }) => ({ ...bounds, nodeId }));
1454
+ this.tree.load(rbushItems);
1455
+ for (const { nodeId, bounds } of items) this.cache.set(nodeId, bounds);
1456
+ }
1457
+ /** Return the cached bounding box for a node (undefined if not indexed). */
1458
+ getBounds(nodeId) {
1459
+ return this.cache.get(nodeId);
1460
+ }
1461
+ };
1462
+
1463
+ // ../../node_modules/zustand/esm/vanilla.mjs
1464
+ var createStoreImpl = (createState) => {
1465
+ let state;
1466
+ const listeners = /* @__PURE__ */ new Set();
1467
+ const setState = (partial, replace) => {
1468
+ const nextState = typeof partial === "function" ? partial(state) : partial;
1469
+ if (!Object.is(nextState, state)) {
1470
+ const previousState = state;
1471
+ state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
1472
+ listeners.forEach((listener) => listener(state, previousState));
1473
+ }
1474
+ };
1475
+ const getState = () => state;
1476
+ const getInitialState = () => initialState;
1477
+ const subscribe = (listener) => {
1478
+ listeners.add(listener);
1479
+ return () => listeners.delete(listener);
1480
+ };
1481
+ const destroy = () => {
1482
+ if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") {
1483
+ console.warn(
1484
+ "[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."
1485
+ );
1486
+ }
1487
+ listeners.clear();
1488
+ };
1489
+ const api = { setState, getState, getInitialState, subscribe, destroy };
1490
+ const initialState = state = createState(setState, getState, api);
1491
+ return api;
1492
+ };
1493
+ var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
1494
+
1495
+ // src/stores/index.ts
1496
+ var selectionStore = createStore((set, get) => ({
1497
+ selectedId: null,
1498
+ select: (nodeId) => set({ selectedId: nodeId }),
1499
+ clear: () => set({ selectedId: null }),
1500
+ toggle: (nodeId) => set((s) => ({ selectedId: s.selectedId === nodeId ? null : nodeId })),
1501
+ isSelected: (nodeId) => get().selectedId === nodeId
1502
+ }));
1503
+ var renderConfigStore = createStore((set, get) => ({
1504
+ ...DEFAULT_RENDER_CONFIG,
1505
+ setQualityLevel: (level) => set(getRenderConfigForQuality(level)),
1506
+ setRenderMode: (mode) => set({ renderMode: mode }),
1507
+ setCullingEnabled: (enabled) => set({ cullingEnabled: enabled }),
1508
+ autoAdjustQuality: (fps) => set((s) => {
1509
+ const q = getOptimalQualityLevel(fps, s.qualityLevel);
1510
+ return q !== s.qualityLevel ? getRenderConfigForQuality(q) : s;
1511
+ }),
1512
+ autoAdjustQualityByMemory: (mb) => set((s) => {
1513
+ const q = mb > 500 ? "low" : mb > 400 && s.qualityLevel === "high" ? "medium" : s.qualityLevel;
1514
+ return q !== s.qualityLevel ? getRenderConfigForQuality(q) : s;
1515
+ }),
1516
+ resetToDefaults: () => set({ ...DEFAULT_RENDER_CONFIG }),
1517
+ isFeatureEnabled: (feature) => isFeatureEnabled(feature, get())
1518
+ }));
1519
+
1520
+ // src/utils/navigation.ts
1521
+ function getConnectionsForLayer(doc, layerId) {
1522
+ const layer = doc.layers.get(layerId);
1523
+ if (!layer) return [];
1524
+ return layer.connections.map((c) => ({
1525
+ from: c.from,
1526
+ to: c.to,
1527
+ level: layer.level,
1528
+ label: c.data?.label
1529
+ }));
1530
+ }
1531
+ function getChildLayerForNode(doc, nodeId, currentLayerId) {
1532
+ for (const [, layer] of doc.layers) {
1533
+ if (layer.parentLayerId === currentLayerId && layer.parentNodeId === nodeId) {
1534
+ return layer;
1535
+ }
1536
+ }
1537
+ return null;
1538
+ }
1539
+ function getLayerForNavigation(doc, nodeId, currentLayerId) {
1540
+ return getChildLayerForNode(doc, nodeId, currentLayerId);
1541
+ }
1542
+ function calculateFitZoom(nodes, canvasWidth, canvasHeight, padding = 0.15) {
1543
+ if (nodes.length === 0) return 1;
1544
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1545
+ for (const n of nodes) {
1546
+ minX = Math.min(minX, n.x - n.width / 2);
1547
+ minY = Math.min(minY, n.y - n.height / 2);
1548
+ maxX = Math.max(maxX, n.x + n.width / 2);
1549
+ maxY = Math.max(maxY, n.y + n.height / 2);
1550
+ }
1551
+ const rawW = maxX - minX;
1552
+ const rawH = maxY - minY;
1553
+ const spreadW = nodes.length === 1 ? Math.max(rawW, 600) : rawW;
1554
+ const spreadH = nodes.length === 1 ? Math.max(rawH, 400) : rawH;
1555
+ const zoomX = canvasWidth * (1 - padding) / spreadW;
1556
+ const zoomY = canvasHeight * (1 - padding) / spreadH;
1557
+ const z = Math.min(zoomX, zoomY);
1558
+ return Math.max(z * 0.8, ZOOM_NAV_OUT_THRESHOLD);
1559
+ }
1560
+ function calculateFitCenter(nodes) {
1561
+ if (nodes.length === 0) return { x: 0, y: 0 };
1562
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1563
+ for (const n of nodes) {
1564
+ minX = Math.min(minX, n.x - n.width / 2);
1565
+ minY = Math.min(minY, n.y - n.height / 2);
1566
+ maxX = Math.max(maxX, n.x + n.width / 2);
1567
+ maxY = Math.max(maxY, n.y + n.height / 2);
1568
+ }
1569
+ return { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
1570
+ }
1571
+ function getContainersForLayer(doc, layerId) {
1572
+ const layer = doc.layers.get(layerId);
1573
+ if (!layer) return [];
1574
+ return layer.containers.map((c) => {
1575
+ const label = c.data?.label ?? c.id;
1576
+ return { id: c.id, label, nodeIds: c.nodes, level: layer.level };
1577
+ });
1578
+ }
1579
+ function calculateFitView(nodes, canvasWidth, canvasHeight) {
1580
+ const center = calculateFitCenter(nodes);
1581
+ const zoom = calculateFitZoom(nodes, canvasWidth, canvasHeight);
1582
+ return { centerX: center.x, centerY: center.y, zoomLevel: zoom };
1583
+ }
1584
+
1585
+ // src/rendering/culling.ts
1586
+ var DEFAULT_SPATIAL_INDEX_THRESHOLD = 200;
1587
+ function getViewportVisibleNodes(options) {
1588
+ const {
1589
+ nodes,
1590
+ viewport,
1591
+ canvasWidth,
1592
+ canvasHeight,
1593
+ levelFilter,
1594
+ skipCulling = false,
1595
+ isDragging = false,
1596
+ spatialIndex,
1597
+ spatialIndexThreshold = DEFAULT_SPATIAL_INDEX_THRESHOLD
1598
+ } = options;
1599
+ const filtered = levelFilter !== void 0 ? nodes.filter((n) => n.level === levelFilter) : nodes;
1600
+ if (filtered.length === 0) return [];
1601
+ if (skipCulling || isDragging) return filtered;
1602
+ const bounds = calculateViewportBounds(viewport, canvasWidth, canvasHeight);
1603
+ if (filtered.length < spatialIndexThreshold) {
1604
+ return filtered.filter(
1605
+ (n) => n.x + n.width >= bounds.minX && n.x <= bounds.maxX && n.y + n.height >= bounds.minY && n.y <= bounds.maxY
1606
+ );
1607
+ }
1608
+ if (spatialIndex) {
1609
+ const nodeIds = spatialIndex.query({
1610
+ minX: bounds.minX,
1611
+ minY: bounds.minY,
1612
+ maxX: bounds.maxX,
1613
+ maxY: bounds.maxY
1614
+ });
1615
+ const idSet = new Set(nodeIds);
1616
+ return filtered.filter((n) => idSet.has(n.id));
1617
+ }
1618
+ return filtered.filter(
1619
+ (n) => n.x + n.width >= bounds.minX && n.x <= bounds.maxX && n.y + n.height >= bounds.minY && n.y <= bounds.maxY
1620
+ );
1621
+ }
1622
+
1623
+ // src/config/textScaling.ts
1624
+ var NODE_TEXT_BASE_FONT_PX = 28;
1625
+ var DESCRIPTION_TEXT_BASE_FONT_PX = 12;
1626
+ var CONTAINER_LABEL_BASE_FONT_PX = 24;
1627
+ var EDGE_LABEL_BASE_FONT_PX = 18;
1628
+ var OVERLAY_FONT_UPPER_BOOST_PX = 8;
1629
+ var NODE_TEXT_SAFE_BLOB_FILL_RATIO = 0.88;
1630
+ function computeNodeTextWorldScale(nodeWidth, zoomLevel) {
1631
+ const safeZoom = Math.max(zoomLevel, 1e-4);
1632
+ const baseRadiusX = nodeWidth / 2;
1633
+ const baseRadiusY = Math.max(NODE_HEIGHT * 1.6 / 2, baseRadiusX * 0.74);
1634
+ const textNaturalWidth = nodeWidth - 40;
1635
+ const textNaturalHeight = NODE_HEIGHT - 20;
1636
+ const maxScaleX = baseRadiusX * 2 * NODE_TEXT_SAFE_BLOB_FILL_RATIO / Math.max(textNaturalWidth, 1);
1637
+ const maxScaleY = baseRadiusY * 2 * NODE_TEXT_SAFE_BLOB_FILL_RATIO / Math.max(textNaturalHeight, 1);
1638
+ const fitScale = Math.max(0.01, Math.min(maxScaleX, maxScaleY));
1639
+ return Math.min(1 / safeZoom, fitScale);
1640
+ }
1641
+ function computeNodeTextScreenScale(nodeWidth, zoomLevel) {
1642
+ return computeNodeTextWorldScale(nodeWidth, zoomLevel) * zoomLevel;
1643
+ }
1644
+ function computeOverlayScale(zoomLevel) {
1645
+ const safeZoom = Math.max(zoomLevel, 1e-4);
1646
+ const inverseScale = 1 / safeZoom;
1647
+ const maxScale = (NODE_TEXT_BASE_FONT_PX + OVERLAY_FONT_UPPER_BOOST_PX) / NODE_TEXT_BASE_FONT_PX;
1648
+ return Math.min(Math.max(inverseScale, 1), maxScale);
1649
+ }
1650
+ function computeEdgeLabelScale(zoomLevel) {
1651
+ const safeZoom = Math.max(zoomLevel, 1e-4);
1652
+ return Math.min(Math.max(EDGE_LABEL_BASE_FONT_PX / safeZoom, 10), 32);
1653
+ }
1654
+
1655
+ // src/config/blobShapes.ts
1656
+ var BLOB_LAYER_CONFIG = [
1657
+ { opacity: 0.3, rotation: -15, scaleUp: 1.3, strokeWidth: 3, dash: [6, 4] },
1658
+ // Layer #3 (outermost)
1659
+ { opacity: 0.4, rotation: -10, scaleUp: 1.2, strokeWidth: 1, dash: void 0 },
1660
+ // Layer #2
1661
+ { opacity: 0.5, rotation: -5, scaleUp: 1.1, strokeWidth: 2, dash: void 0 }
1662
+ // Layer #1
1663
+ ];
1664
+ var NODE_LAYER_CONFIG = { opacity: 1, rotation: 0, strokeWidth: 3 };
1665
+ var ALL_BLOB_SHAPES = [
1666
+ // Shape 0 · Blob 1
1667
+ [
1668
+ { x: 1.12, y: 0 },
1669
+ { x: 0.86, y: 0.56 },
1670
+ { x: 0.46, y: 0.94 },
1671
+ { x: -0.08, y: 1.06 },
1672
+ { x: -0.68, y: 0.82 },
1673
+ { x: -0.96, y: 0.08 },
1674
+ { x: -0.55, y: -0.82 },
1675
+ { x: 0.04, y: -1.02 },
1676
+ { x: 0.72, y: -0.8 },
1677
+ { x: 1.08, y: -0.38 }
1678
+ ],
1679
+ // Shape 1 · Blob 2
1680
+ [
1681
+ { x: -0.489, y: -0.625 },
1682
+ { x: -0.384, y: -0.716 },
1683
+ { x: -0.285, y: -0.792 },
1684
+ { x: -0.189, y: -0.855 },
1685
+ { x: -0.09, y: -0.903 },
1686
+ { x: 0.014, y: -0.932 },
1687
+ { x: 0.129, y: -0.94 },
1688
+ { x: 0.255, y: -0.924 },
1689
+ { x: 0.412, y: -0.876 },
1690
+ { x: 0.516, y: -0.832 },
1691
+ { x: 0.612, y: -0.78 },
1692
+ { x: 0.695, y: -0.725 },
1693
+ { x: 0.767, y: -0.666 },
1694
+ { x: 0.83, y: -0.606 },
1695
+ { x: 0.885, y: -0.546 },
1696
+ { x: 0.933, y: -0.486 },
1697
+ { x: 0.979, y: -0.425 },
1698
+ { x: 1.006, y: -0.385 },
1699
+ { x: 1.034, y: -0.334 },
1700
+ { x: 1.058, y: -0.276 },
1701
+ { x: 1.078, y: -0.209 },
1702
+ { x: 1.092, y: -0.133 },
1703
+ { x: 1.101, y: -0.046 },
1704
+ { x: 1.102, y: 0.052 },
1705
+ { x: 1.094, y: 0.173 },
1706
+ { x: 1.083, y: 0.244 },
1707
+ { x: 1.063, y: 0.324 },
1708
+ { x: 1.034, y: 0.401 },
1709
+ { x: 0.996, y: 0.477 },
1710
+ { x: 0.948, y: 0.549 },
1711
+ { x: 0.889, y: 0.617 },
1712
+ { x: 0.82, y: 0.681 },
1713
+ { x: 0.735, y: 0.741 },
1714
+ { x: 0.658, y: 0.783 },
1715
+ { x: 0.569, y: 0.822 },
1716
+ { x: 0.474, y: 0.855 },
1717
+ { x: 0.372, y: 0.882 },
1718
+ { x: 0.265, y: 0.904 },
1719
+ { x: 0.156, y: 0.92 },
1720
+ { x: 0.046, y: 0.931 },
1721
+ { x: -0.069, y: 0.938 },
1722
+ { x: -0.155, y: 0.935 },
1723
+ { x: -0.25, y: 0.919 },
1724
+ { x: -0.346, y: 0.89 },
1725
+ { x: -0.443, y: 0.851 },
1726
+ { x: -0.539, y: 0.8 },
1727
+ { x: -0.633, y: 0.738 },
1728
+ { x: -0.723, y: 0.667 },
1729
+ { x: -0.808, y: 0.585 },
1730
+ { x: -0.882, y: 0.501 },
1731
+ { x: -0.944, y: 0.421 },
1732
+ { x: -0.994, y: 0.343 },
1733
+ { x: -1.034, y: 0.267 },
1734
+ { x: -1.064, y: 0.192 },
1735
+ { x: -1.085, y: 0.12 },
1736
+ { x: -1.097, y: 0.042 },
1737
+ { x: -1.102, y: 1e-3 },
1738
+ { x: -1.09, y: -0.243 },
1739
+ { x: -1.05, y: -0.365 },
1740
+ { x: -0.99, y: -0.441 },
1741
+ { x: -0.911, y: -0.479 },
1742
+ { x: -0.819, y: -0.499 },
1743
+ { x: -0.715, y: -0.517 },
1744
+ { x: -0.605, y: -0.551 }
1745
+ ],
1746
+ // Shape 2 · Blob 3
1747
+ [
1748
+ { x: 3e-3, y: -0.97 },
1749
+ { x: 0.163, y: -0.984 },
1750
+ { x: 0.286, y: -1.028 },
1751
+ { x: 0.389, y: -1.096 },
1752
+ { x: 0.488, y: -1.174 },
1753
+ { x: 0.597, y: -1.21 },
1754
+ { x: 0.728, y: -1.165 },
1755
+ { x: 0.867, y: -1.036 },
1756
+ { x: 0.996, y: -0.839 },
1757
+ { x: 1.098, y: -0.59 },
1758
+ { x: 1.158, y: -0.304 },
1759
+ { x: 1.153, y: 0.012 },
1760
+ { x: 1.08, y: 0.336 },
1761
+ { x: 0.944, y: 0.639 },
1762
+ { x: 0.756, y: 0.889 },
1763
+ { x: 0.484, y: 1.072 },
1764
+ { x: 0.339, y: 1.079 },
1765
+ { x: 0.219, y: 1.051 },
1766
+ { x: 0.131, y: 0.994 },
1767
+ { x: 0.059, y: 0.939 },
1768
+ { x: 8e-3, y: 0.915 },
1769
+ { x: -0.105, y: 0.931 },
1770
+ { x: -0.176, y: 0.965 },
1771
+ { x: -0.249, y: 1.005 },
1772
+ { x: -0.345, y: 1.039 },
1773
+ { x: -0.497, y: 1.055 },
1774
+ { x: -0.672, y: 1.032 },
1775
+ { x: -0.817, y: 0.952 },
1776
+ { x: -0.945, y: 0.81 },
1777
+ { x: -1.054, y: 0.6 },
1778
+ { x: -1.16, y: 0.302 },
1779
+ { x: -1.187, y: -0.011 },
1780
+ { x: -1.165, y: -0.326 },
1781
+ { x: -1.097, y: -0.599 },
1782
+ { x: -0.991, y: -0.838 },
1783
+ { x: -0.852, y: -0.912 },
1784
+ { x: -0.695, y: -0.958 },
1785
+ { x: -0.53, y: -0.988 },
1786
+ { x: -0.357, y: -0.992 },
1787
+ { x: -0.18, y: -0.983 }
1788
+ ],
1789
+ // Shape 3 · Blob 4
1790
+ [
1791
+ { x: -0.734, y: -1.127 },
1792
+ { x: -0.656, y: -1.146 },
1793
+ { x: -0.567, y: -1.142 },
1794
+ { x: -0.468, y: -1.115 },
1795
+ { x: -0.358, y: -1.079 },
1796
+ { x: -0.238, y: -1.034 },
1797
+ { x: -0.107, y: -0.989 },
1798
+ { x: 0.035, y: -0.953 },
1799
+ { x: 0.188, y: -0.929 },
1800
+ { x: 0.325, y: -0.916 },
1801
+ { x: 0.484, y: -0.896 },
1802
+ { x: 0.65, y: -0.854 },
1803
+ { x: 0.811, y: -0.77 },
1804
+ { x: 0.955, y: -0.631 },
1805
+ { x: 1.068, y: -0.422 },
1806
+ { x: 1.138, y: -0.126 },
1807
+ { x: 1.152, y: 0.272 },
1808
+ { x: 1.121, y: 0.451 },
1809
+ { x: 1.046, y: 0.602 },
1810
+ { x: 0.928, y: 0.727 },
1811
+ { x: 0.767, y: 0.833 },
1812
+ { x: 0.565, y: 0.925 },
1813
+ { x: 0.323, y: 1.001 },
1814
+ { x: 0.042, y: 1.075 },
1815
+ { x: -0.277, y: 1.142 },
1816
+ { x: -0.368, y: 1.147 },
1817
+ { x: -0.475, y: 1.136 },
1818
+ { x: -0.589, y: 1.104 },
1819
+ { x: -0.704, y: 1.05 },
1820
+ { x: -0.815, y: 0.973 },
1821
+ { x: -0.913, y: 0.871 },
1822
+ { x: -0.993, y: 0.746 },
1823
+ { x: -1.048, y: 0.593 },
1824
+ { x: -1.109, y: 0.298 },
1825
+ { x: -1.145, y: 0.018 },
1826
+ { x: -1.154, y: -0.243 },
1827
+ { x: -1.135, y: -0.482 },
1828
+ { x: -1.085, y: -0.695 },
1829
+ { x: -1.003, y: -0.874 },
1830
+ { x: -0.886, y: -1.02 }
1831
+ ],
1832
+ // Shape 4 · Blob 5
1833
+ [
1834
+ { x: -0.734, y: 1.127 },
1835
+ { x: -0.656, y: 1.146 },
1836
+ { x: -0.567, y: 1.142 },
1837
+ { x: -0.468, y: 1.115 },
1838
+ { x: -0.358, y: 1.079 },
1839
+ { x: -0.238, y: 1.034 },
1840
+ { x: -0.107, y: 0.989 },
1841
+ { x: 0.035, y: 0.953 },
1842
+ { x: 0.188, y: 0.929 },
1843
+ { x: 0.325, y: 0.916 },
1844
+ { x: 0.484, y: 0.896 },
1845
+ { x: 0.65, y: 0.854 },
1846
+ { x: 0.811, y: 0.77 },
1847
+ { x: 0.955, y: 0.631 },
1848
+ { x: 1.068, y: 0.422 },
1849
+ { x: 1.138, y: 0.126 },
1850
+ { x: 1.152, y: -0.272 },
1851
+ { x: 1.121, y: -0.451 },
1852
+ { x: 1.046, y: -0.602 },
1853
+ { x: 0.928, y: -0.727 },
1854
+ { x: 0.767, y: -0.833 },
1855
+ { x: 0.565, y: -0.925 },
1856
+ { x: 0.323, y: -1.001 },
1857
+ { x: 0.042, y: -1.075 },
1858
+ { x: -0.277, y: -1.142 },
1859
+ { x: -0.368, y: -1.147 },
1860
+ { x: -0.475, y: -1.136 },
1861
+ { x: -0.589, y: -1.104 },
1862
+ { x: -0.704, y: -1.05 },
1863
+ { x: -0.815, y: -0.973 },
1864
+ { x: -0.913, y: -0.871 },
1865
+ { x: -0.993, y: -0.746 },
1866
+ { x: -1.048, y: -0.593 },
1867
+ { x: -1.109, y: -0.298 },
1868
+ { x: -1.145, y: -0.018 },
1869
+ { x: -1.154, y: 0.243 },
1870
+ { x: -1.135, y: 0.482 },
1871
+ { x: -1.085, y: 0.695 },
1872
+ { x: -1.003, y: 0.874 },
1873
+ { x: -0.886, y: 1.02 }
1874
+ ],
1875
+ // Shape 5 · Blob 6
1876
+ [
1877
+ { x: 1e-3, y: -1.006 },
1878
+ { x: 0.134, y: -1.001 },
1879
+ { x: 0.262, y: -1.024 },
1880
+ { x: 0.382, y: -1.073 },
1881
+ { x: 0.498, y: -1.107 },
1882
+ { x: 0.614, y: -1.105 },
1883
+ { x: 0.733, y: -1.049 },
1884
+ { x: 0.85, y: -0.932 },
1885
+ { x: 0.953, y: -0.755 },
1886
+ { x: 1.03, y: -0.529 },
1887
+ { x: 1.067, y: -0.265 },
1888
+ { x: 1.054, y: 0.018 },
1889
+ { x: 0.986, y: 0.3 },
1890
+ { x: 0.868, y: 0.557 },
1891
+ { x: 0.715, y: 0.766 },
1892
+ { x: 0.55, y: 0.909 },
1893
+ { x: 0.396, y: 0.984 },
1894
+ { x: 0.262, y: 1.005 },
1895
+ { x: 0.148, y: 1.003 },
1896
+ { x: 0.037, y: 1.008 },
1897
+ { x: -0.083, y: 1.04 },
1898
+ { x: -0.22, y: 1.118 },
1899
+ { x: -0.36, y: 1.192 },
1900
+ { x: -0.5, y: 1.234 },
1901
+ { x: -0.65, y: 1.175 },
1902
+ { x: -0.81, y: 1.045 },
1903
+ { x: -0.99, y: 0.855 },
1904
+ { x: -1.14, y: 0.61 },
1905
+ { x: -1.24, y: 0.33 },
1906
+ { x: -1.27, y: 0.04 },
1907
+ { x: -1.275, y: -0.25 },
1908
+ { x: -1.225, y: -0.5 },
1909
+ { x: -1.08, y: -0.72 },
1910
+ { x: -0.89, y: -0.92 },
1911
+ { x: -0.734, y: -1.053 },
1912
+ { x: -0.614, y: -1.112 },
1913
+ { x: -0.498, y: -1.127 },
1914
+ { x: -0.381, y: -1.11 },
1915
+ { x: -0.261, y: -1.095 },
1916
+ { x: -0.133, y: -1.058 },
1917
+ { x: 1e-3, y: -1.01 },
1918
+ { x: 0.134, y: -1.001 }
1919
+ ]
1920
+ ];
1921
+ var ACTIVE_BLOB_TYPES = [0, 1, 2, 3, 4, 5];
1922
+ var BLOB_TEXT_OFFSETS = [0, 0, 0, 0, 0, -0.1];
1923
+ function getBlobTypeForLayer(level) {
1924
+ const numTypes = ACTIVE_BLOB_TYPES.length;
1925
+ if (numTypes <= 1) return ACTIVE_BLOB_TYPES[0] ?? 0;
1926
+ let currentType = 0;
1927
+ for (let i = 1; i <= level; i++) {
1928
+ const pseudoRandom = Math.floor(Math.abs(Math.sin((i + 1) * 12.9898) * 43758.5453));
1929
+ const stepOffset = pseudoRandom % (numTypes - 1) + 1;
1930
+ currentType = (currentType + stepOffset) % numTypes;
1931
+ }
1932
+ return ACTIVE_BLOB_TYPES[currentType];
1933
+ }
1934
+ function drawBlobPath(ctx, radiusX, radiusY, blobType) {
1935
+ const srcPts = ALL_BLOB_SHAPES[blobType] ?? ALL_BLOB_SHAPES[0];
1936
+ const pts = srcPts.map((p) => ({ x: p.x * radiusX, y: p.y * radiusY }));
1937
+ ctx.beginPath();
1938
+ const last = pts[pts.length - 1];
1939
+ const first = pts[0];
1940
+ ctx.moveTo((last.x + first.x) / 2, (last.y + first.y) / 2);
1941
+ for (let i = 0; i < pts.length; i++) {
1942
+ const curr = pts[i];
1943
+ const next = pts[(i + 1) % pts.length];
1944
+ ctx.quadraticCurveTo(curr.x, curr.y, (curr.x + next.x) / 2, (curr.y + next.y) / 2);
1945
+ }
1946
+ ctx.closePath();
1947
+ }
1948
+ export {
1949
+ ACTIVE_BLOB_TYPES,
1950
+ ALL_BLOB_SHAPES,
1951
+ ANIMATION_DURATION,
1952
+ ANIMATION_PHASES,
1953
+ BLOB_LAYER_CONFIG,
1954
+ BLOB_TEXT_OFFSETS,
1955
+ BORDER_WIDTH_NORMAL,
1956
+ BORDER_WIDTH_SELECTED,
1957
+ BRAND_COLORS,
1958
+ CANVAS_COLORS,
1959
+ CHAR_WIDTH,
1960
+ CIRCULAR_REFERENCE_MAX_DEPTH,
1961
+ CLICK_THRESHOLD,
1962
+ COLOR_BORDER_DARK,
1963
+ COLOR_BORDER_LIGHT,
1964
+ COLOR_DEFAULT_NODE,
1965
+ COLOR_SELECTED,
1966
+ CONTAINER_LABEL_BASE_FONT_PX,
1967
+ COOLDOWN,
1968
+ CORNER_RADIUS,
1969
+ CULLING_QUERY_TIME_BUDGET,
1970
+ DEFAULT_CANVAS_HEIGHT,
1971
+ DEFAULT_CANVAS_WIDTH,
1972
+ DEFAULT_RENDER_CONFIG,
1973
+ DESCRIPTION_TEXT_BASE_FONT_PX,
1974
+ DOUBLE_CLICK_DELAY,
1975
+ DRAG_START_DISTANCE,
1976
+ DS_COLORS,
1977
+ DS_RADII,
1978
+ DS_TYPOGRAPHY,
1979
+ EDGE_LABEL_BASE_FONT_PX,
1980
+ FEATURE_FLAGS,
1981
+ FONTS,
1982
+ FONT_SIZES,
1983
+ FPSMonitor,
1984
+ FPS_CRITICAL_THRESHOLD,
1985
+ FPS_DOWNGRADE_THRESHOLD,
1986
+ FPS_SAMPLE_WINDOW,
1987
+ FPS_STABILIZATION_FRAMES,
1988
+ FPS_TARGET,
1989
+ FPS_TARGETS,
1990
+ FPS_UPDATE_INTERVAL,
1991
+ FPS_UPGRADE_THRESHOLD,
1992
+ FPS_WARNING_THRESHOLD,
1993
+ LAYOUT_SPACING,
1994
+ LEVEL_COLORS,
1995
+ LEVEL_COLOR_ARRAY,
1996
+ MAX_FRAME_TIME,
1997
+ MAX_NODES,
1998
+ MAX_VISIBLE_NODES,
1999
+ MEMORY_CHECK_INTERVAL,
2000
+ MEMORY_CRITICAL_MB,
2001
+ MEMORY_LIMITS,
2002
+ MEMORY_TARGET_MB,
2003
+ MEMORY_WARNING_MB,
2004
+ MemoryMonitor,
2005
+ NODE_COUNT_THRESHOLDS,
2006
+ NODE_HEIGHT,
2007
+ NODE_LAYER_CONFIG,
2008
+ NODE_MIN_WIDTH,
2009
+ NODE_PADDING,
2010
+ NODE_TEXT_BASE_FONT_PX,
2011
+ OPACITY,
2012
+ OVERLAY_FONT_UPPER_BOOST_PX,
2013
+ PARENT_FALLBACK_COLORS,
2014
+ PERFORMANCE_TOAST_COOLDOWN,
2015
+ PerformanceProfiler,
2016
+ QUALITY_HIGH,
2017
+ QUALITY_LEVELS,
2018
+ QUALITY_LOW,
2019
+ QUALITY_MEDIUM,
2020
+ QUALITY_SETTINGS,
2021
+ RBUSH_MAX_ENTRIES,
2022
+ RBushSpatialIndex,
2023
+ RENDER_MODES,
2024
+ RESIZE_DEBOUNCE,
2025
+ SCROLL_THROTTLE,
2026
+ SEARCH_DEBOUNCE,
2027
+ SEMANTIC_COLORS,
2028
+ SIMPLIFIED_RENDER_CONFIG,
2029
+ SPACING,
2030
+ SPATIAL_INDEX_BUILD_BUDGET,
2031
+ TARGET_FRAME_TIME,
2032
+ TARGET_ZOOM,
2033
+ TEXT_LABEL_HIDE_BELOW_ZOOM,
2034
+ THEME_COLORS,
2035
+ TIMING_THRESHOLDS,
2036
+ TITLE_MAX_LENGTH,
2037
+ VIEWPORT_CONFIG,
2038
+ VIEWPORT_FRAMING,
2039
+ VIEWPORT_MARGIN,
2040
+ VisualliParseError,
2041
+ ZOOM_DEFAULT,
2042
+ ZOOM_MAX,
2043
+ ZOOM_MIN,
2044
+ ZOOM_NAV_IN_THRESHOLD,
2045
+ ZOOM_NAV_OUT_THRESHOLD,
2046
+ ZOOM_STEP,
2047
+ ZOOM_THRESHOLDS,
2048
+ ZOOM_WHEEL_SENSITIVITY,
2049
+ applyCircularLayout,
2050
+ applyLinearHorizontalLayout,
2051
+ applyLinearVerticalLayout,
2052
+ calculateDistance,
2053
+ calculateFitCenter,
2054
+ calculateFitView,
2055
+ calculateFitZoom,
2056
+ calculateMinimumRadius,
2057
+ calculateOptimalRadiusPercentage,
2058
+ calculateViewportBounds,
2059
+ checkCollision,
2060
+ clampZoom,
2061
+ computeEdgeLabelScale,
2062
+ computeNodeTextScreenScale,
2063
+ computeNodeTextWorldScale,
2064
+ computeOverlayScale,
2065
+ convertLayerToFlatNodes,
2066
+ convertVisualliToFlatNodes,
2067
+ countLayersBeneath,
2068
+ createCubicBezier,
2069
+ createMindMapConfig,
2070
+ darkenHexColor,
2071
+ drawBlobPath,
2072
+ easeInCubic,
2073
+ easeInOutCubic,
2074
+ easeInOutQuart,
2075
+ easeInOutSine,
2076
+ easeOutCubic,
2077
+ easeOutQuad,
2078
+ easeZoomTransition,
2079
+ flattenNodes,
2080
+ focusOnNode,
2081
+ generateSampleConfig,
2082
+ getBlobTypeForLayer,
2083
+ getChildLayerForNode,
2084
+ getChildLayers,
2085
+ getColorForLevel,
2086
+ getConnectionsForLayer,
2087
+ getContainersForLayer,
2088
+ getLayerForNavigation,
2089
+ getNodeBounds,
2090
+ getNodeConnections,
2091
+ getNodesForLayer,
2092
+ getOptimalQualityLevel,
2093
+ getRecommendedRenderConfig,
2094
+ getRenderConfigForQuality,
2095
+ getThemeBackground,
2096
+ getThemeBorder,
2097
+ getThemeText,
2098
+ getTopLevelConnections,
2099
+ getViewportVisibleNodes,
2100
+ getVisibleNodes,
2101
+ isFeatureEnabled,
2102
+ isNodeInViewport,
2103
+ loadVisualliFile,
2104
+ loadVisualliFileFromFile,
2105
+ optimizeLayout,
2106
+ panViewport,
2107
+ parseVisualliFile,
2108
+ parseVisualliFromMongoDB,
2109
+ recalculateViewportBounds,
2110
+ renderConfigStore,
2111
+ resolveCollisions,
2112
+ resolveNodeOverlaps,
2113
+ screenToWorld,
2114
+ selectionStore,
2115
+ setViewportCenter,
2116
+ worldToScreen,
2117
+ zoomViewport
2118
+ };
2119
+ //# sourceMappingURL=index.js.map