aifastdb-devplan 1.6.1 → 1.6.2

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 (89) hide show
  1. package/dist/dev-plan-document-store.d.ts +13 -1
  2. package/dist/dev-plan-document-store.d.ts.map +1 -1
  3. package/dist/dev-plan-document-store.js +119 -0
  4. package/dist/dev-plan-document-store.js.map +1 -1
  5. package/dist/dev-plan-factory.d.ts.map +1 -1
  6. package/dist/dev-plan-factory.js +2 -1
  7. package/dist/dev-plan-factory.js.map +1 -1
  8. package/dist/dev-plan-graph-store.d.ts +64 -1
  9. package/dist/dev-plan-graph-store.d.ts.map +1 -1
  10. package/dist/dev-plan-graph-store.js +364 -2
  11. package/dist/dev-plan-graph-store.js.map +1 -1
  12. package/dist/dev-plan-interface.d.ts +24 -1
  13. package/dist/dev-plan-interface.d.ts.map +1 -1
  14. package/dist/dev-plan-migrate.d.ts +1 -0
  15. package/dist/dev-plan-migrate.d.ts.map +1 -1
  16. package/dist/dev-plan-migrate.js +28 -2
  17. package/dist/dev-plan-migrate.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp-server/index.js +119 -0
  22. package/dist/mcp-server/index.js.map +1 -1
  23. package/dist/types.d.ts +88 -1
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/types.js.map +1 -1
  26. package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -1
  27. package/dist/visualize/graph-canvas/api-compat.js +22 -12
  28. package/dist/visualize/graph-canvas/api-compat.js.map +1 -1
  29. package/dist/visualize/graph-canvas/core.d.ts.map +1 -1
  30. package/dist/visualize/graph-canvas/core.js +296 -4
  31. package/dist/visualize/graph-canvas/core.js.map +1 -1
  32. package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -1
  33. package/dist/visualize/graph-canvas/interaction.js +11 -0
  34. package/dist/visualize/graph-canvas/interaction.js.map +1 -1
  35. package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -1
  36. package/dist/visualize/graph-canvas/layout-worker.js +45 -9
  37. package/dist/visualize/graph-canvas/layout-worker.js.map +1 -1
  38. package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -1
  39. package/dist/visualize/graph-canvas/renderer.js +164 -33
  40. package/dist/visualize/graph-canvas/renderer.js.map +1 -1
  41. package/dist/visualize/graph-canvas/styles.d.ts.map +1 -1
  42. package/dist/visualize/graph-canvas/styles.js +136 -121
  43. package/dist/visualize/graph-canvas/styles.js.map +1 -1
  44. package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -1
  45. package/dist/visualize/graph-canvas/viewport.js +10 -0
  46. package/dist/visualize/graph-canvas/viewport.js.map +1 -1
  47. package/dist/visualize/server.js +149 -32
  48. package/dist/visualize/server.js.map +1 -1
  49. package/dist/visualize/template-core.d.ts +9 -0
  50. package/dist/visualize/template-core.d.ts.map +1 -0
  51. package/dist/visualize/template-core.js +714 -0
  52. package/dist/visualize/template-core.js.map +1 -0
  53. package/dist/visualize/template-data-loading.d.ts +7 -0
  54. package/dist/visualize/template-data-loading.d.ts.map +1 -0
  55. package/dist/visualize/template-data-loading.js +677 -0
  56. package/dist/visualize/template-data-loading.js.map +1 -0
  57. package/dist/visualize/template-detail-panel.d.ts +14 -0
  58. package/dist/visualize/template-detail-panel.d.ts.map +1 -0
  59. package/dist/visualize/template-detail-panel.js +553 -0
  60. package/dist/visualize/template-detail-panel.js.map +1 -0
  61. package/dist/visualize/template-graph-3d.d.ts +7 -0
  62. package/dist/visualize/template-graph-3d.d.ts.map +1 -0
  63. package/dist/visualize/template-graph-3d.js +1112 -0
  64. package/dist/visualize/template-graph-3d.js.map +1 -0
  65. package/dist/visualize/template-graph-vis.d.ts +8 -0
  66. package/dist/visualize/template-graph-vis.d.ts.map +1 -0
  67. package/dist/visualize/template-graph-vis.js +1204 -0
  68. package/dist/visualize/template-graph-vis.js.map +1 -0
  69. package/dist/visualize/template-html.d.ts +9 -0
  70. package/dist/visualize/template-html.d.ts.map +1 -0
  71. package/dist/visualize/template-html.js +484 -0
  72. package/dist/visualize/template-html.js.map +1 -0
  73. package/dist/visualize/template-pages.d.ts +7 -0
  74. package/dist/visualize/template-pages.d.ts.map +1 -0
  75. package/dist/visualize/template-pages.js +806 -0
  76. package/dist/visualize/template-pages.js.map +1 -0
  77. package/dist/visualize/template-stats-modal.d.ts +7 -0
  78. package/dist/visualize/template-stats-modal.d.ts.map +1 -0
  79. package/dist/visualize/template-stats-modal.js +406 -0
  80. package/dist/visualize/template-stats-modal.js.map +1 -0
  81. package/dist/visualize/template-styles.d.ts +9 -0
  82. package/dist/visualize/template-styles.d.ts.map +1 -0
  83. package/dist/visualize/template-styles.js +487 -0
  84. package/dist/visualize/template-styles.js.map +1 -0
  85. package/dist/visualize/template.d.ts +14 -3
  86. package/dist/visualize/template.d.ts.map +1 -1
  87. package/dist/visualize/template.js +38 -3475
  88. package/dist/visualize/template.js.map +1 -1
  89. package/package.json +1 -1
@@ -73,6 +73,10 @@ function GraphCanvas(container, options) {
73
73
  this._idleFrames = 0; // consecutive frames with nothing to draw
74
74
  this._idleThreshold = 30; // sleep after N idle frames (~0.5s at 60fps)
75
75
 
76
+ // ── Interaction performance flags (T11.8) ──
77
+ this._isZooming = false; // true during wheel zoom (edges hidden)
78
+ this._zoomTimer = null; // debounce timer for zoom end
79
+
76
80
  // ── Event Callbacks ───────────────────────────────────────────────────
77
81
  this._eventHandlers = {}; // eventName → [callback, ...]
78
82
 
@@ -102,6 +106,8 @@ function GraphCanvas(container, options) {
102
106
  self._metrics.layoutProgress = 0;
103
107
  self._metrics.layoutAlgorithm = '';
104
108
  self._metrics.layoutEta = 0;
109
+ // T11.6: Arrange document mind maps after layout stabilizes
110
+ self._arrangeDocMindMaps();
105
111
  self.markDirty();
106
112
  });
107
113
 
@@ -266,14 +272,236 @@ GraphCanvas.prototype.setData = function(data) {
266
272
  };
267
273
 
268
274
  /**
269
- * Assign random initial positions for layout bootstrapping.
275
+ * Assign initial positions for layout bootstrapping.
276
+ * Uses structured hierarchical layout if typed nodes are available,
277
+ * otherwise falls back to Fibonacci spiral distribution.
270
278
  */
271
279
  GraphCanvas.prototype._assignRandomPositions = function() {
272
- var spread = Math.sqrt(this._nodeCount) * 80;
280
+ // Try structured layout if we have DevPlan-typed nodes
281
+ if (this._hasDevPlanTypes()) {
282
+ this._assignStructuredPositions();
283
+ return;
284
+ }
285
+ // Fallback: Fibonacci spiral (better than pure random)
286
+ this._assignFibonacciPositions();
287
+ };
288
+
289
+ /**
290
+ * Check if nodes have DevPlan type annotations.
291
+ */
292
+ GraphCanvas.prototype._hasDevPlanTypes = function() {
273
293
  for (var i = 0; i < this._nodes.length; i++) {
274
- this._nodes[i].x = (Math.random() - 0.5) * spread;
275
- this._nodes[i].y = (Math.random() - 0.5) * spread;
294
+ var t = this._nodes[i].type;
295
+ if (t === 'project' || t === 'module' || t === 'main-task') return true;
296
+ }
297
+ return false;
298
+ };
299
+
300
+ /**
301
+ * Fibonacci spiral distribution — evenly spaced initial positions.
302
+ * Better than random for force-directed convergence.
303
+ */
304
+ GraphCanvas.prototype._assignFibonacciPositions = function() {
305
+ var n = this._nodes.length;
306
+ if (n === 0) return;
307
+ var goldenAngle = Math.PI * (3 - Math.sqrt(5)); // ~2.39996
308
+ var maxRadius = Math.sqrt(n) * 40;
309
+ for (var i = 0; i < n; i++) {
310
+ var theta = goldenAngle * i;
311
+ var r = Math.sqrt((i + 1) / n) * maxRadius;
312
+ this._nodes[i].x = Math.cos(theta) * r;
313
+ this._nodes[i].y = Math.sin(theta) * r;
314
+ }
315
+ this._spatialIndex.buildFromNodes(this._nodes);
316
+ this._spatialIndex.buildEdgeIndex(this._edges, this._nodeMap);
317
+ };
318
+
319
+ /**
320
+ * Structured hierarchical initial layout for DevPlan graphs.
321
+ *
322
+ * Strategy:
323
+ * - Project node at center (0,0)
324
+ * - Modules in first ring (R1)
325
+ * - Main tasks in second ring (R2), grouped near their parent module
326
+ * - Sub-tasks clustered near their parent main task with small jitter
327
+ * - Documents clustered near their related entity
328
+ *
329
+ * This gives the force-directed layout a MUCH better starting point
330
+ * compared to random positions, enabling fast convergence.
331
+ */
332
+ GraphCanvas.prototype._assignStructuredPositions = function() {
333
+ var nodes = this._nodes;
334
+ var edges = this._edges;
335
+ var nodeCount = nodes.length;
336
+ if (nodeCount === 0) return;
337
+
338
+ // 1. Group nodes by type
339
+ var byType = { 'project': [], 'module': [], 'main-task': [], 'sub-task': [], 'document': [], 'other': [] };
340
+ for (var i = 0; i < nodeCount; i++) {
341
+ var type = nodes[i].type || 'other';
342
+ if (byType[type]) byType[type].push(nodes[i]);
343
+ else byType['other'].push(nodes[i]);
344
+ }
345
+
346
+ // 2. Build child→parent mapping (edges go from parent → child)
347
+ var childToParent = {};
348
+ for (var i = 0; i < edges.length; i++) {
349
+ // First edge wins as primary parent
350
+ if (!childToParent[edges[i].to]) {
351
+ childToParent[edges[i].to] = edges[i].from;
352
+ }
353
+ }
354
+
355
+ // 3. Build parent→children mapping
356
+ var parentToChildren = {};
357
+ for (var i = 0; i < edges.length; i++) {
358
+ if (!parentToChildren[edges[i].from]) parentToChildren[edges[i].from] = [];
359
+ parentToChildren[edges[i].from].push(edges[i].to);
360
+ }
361
+
362
+ // 4. Adaptive ring radii based on node counts
363
+ var moduleCount = byType['module'].length || 1;
364
+ var mainTaskCount = byType['main-task'].length || 1;
365
+ var R1 = Math.max(200, moduleCount * 60); // Module ring
366
+ var R2 = R1 + Math.max(250, mainTaskCount * 18); // Main-task ring
367
+ var R3 = R2 + 150; // Sub-task/doc zone
368
+
369
+ // 5. Place project node at center
370
+ for (var i = 0; i < byType['project'].length; i++) {
371
+ byType['project'][i].x = 0;
372
+ byType['project'][i].y = 0;
373
+ }
374
+
375
+ // 6. Place modules in first ring (evenly spaced)
376
+ for (var i = 0; i < byType['module'].length; i++) {
377
+ var angle = (2 * Math.PI * i) / byType['module'].length - Math.PI / 2;
378
+ byType['module'][i].x = Math.cos(angle) * R1;
379
+ byType['module'][i].y = Math.sin(angle) * R1;
380
+ }
381
+
382
+ // 7. Place main tasks in second ring, grouped by parent module
383
+ var mainTasks = byType['main-task'];
384
+ var mainByParent = {}; // parentId → [mainTaskNode, ...]
385
+ var ungroupedMain = [];
386
+ for (var i = 0; i < mainTasks.length; i++) {
387
+ var parent = childToParent[mainTasks[i].id];
388
+ if (parent && this._nodeMap[parent]) {
389
+ if (!mainByParent[parent]) mainByParent[parent] = [];
390
+ mainByParent[parent].push(mainTasks[i]);
391
+ } else {
392
+ ungroupedMain.push(mainTasks[i]);
393
+ }
394
+ }
395
+
396
+ // Place grouped main tasks in fan around their parent's angle
397
+ var parentKeys = Object.keys(mainByParent);
398
+ var globalMainIdx = 0;
399
+ for (var p = 0; p < parentKeys.length; p++) {
400
+ var parentNode = this._nodeMap[parentKeys[p]];
401
+ var children = mainByParent[parentKeys[p]];
402
+ if (!parentNode) continue;
403
+
404
+ var pAngle = Math.atan2(parentNode.y || 0.001, parentNode.x || 0.001);
405
+ var fanSpread = Math.min(Math.PI * 0.6, children.length * 0.12);
406
+
407
+ for (var c = 0; c < children.length; c++) {
408
+ var childAngle = pAngle - fanSpread / 2 + (fanSpread * c) / Math.max(children.length - 1, 1);
409
+ var radius = R2 + (Math.random() - 0.5) * 60;
410
+ children[c].x = Math.cos(childAngle) * radius;
411
+ children[c].y = Math.sin(childAngle) * radius;
412
+ globalMainIdx++;
413
+ }
276
414
  }
415
+
416
+ // Place ungrouped main tasks evenly in remaining space
417
+ for (var i = 0; i < ungroupedMain.length; i++) {
418
+ var angle = (2 * Math.PI * (globalMainIdx + i)) / Math.max(mainTasks.length, 1) - Math.PI / 2;
419
+ ungroupedMain[i].x = Math.cos(angle) * R2;
420
+ ungroupedMain[i].y = Math.sin(angle) * R2;
421
+ }
422
+
423
+ // 8. Place sub-tasks near their parent main task
424
+ var subTasks = byType['sub-task'];
425
+ var subByParent = {};
426
+ var ungroupedSub = [];
427
+ for (var i = 0; i < subTasks.length; i++) {
428
+ var parent = childToParent[subTasks[i].id];
429
+ if (parent && this._nodeMap[parent]) {
430
+ if (!subByParent[parent]) subByParent[parent] = [];
431
+ subByParent[parent].push(subTasks[i]);
432
+ } else {
433
+ ungroupedSub.push(subTasks[i]);
434
+ }
435
+ }
436
+
437
+ // Place grouped sub-tasks in small cluster around parent
438
+ var subParentKeys = Object.keys(subByParent);
439
+ for (var sp = 0; sp < subParentKeys.length; sp++) {
440
+ var pNode = this._nodeMap[subParentKeys[sp]];
441
+ var subs = subByParent[subParentKeys[sp]];
442
+ if (!pNode) continue;
443
+
444
+ // Arrange in small ring around parent
445
+ var subRadius = Math.max(40, subs.length * 8);
446
+ for (var si = 0; si < subs.length; si++) {
447
+ var subAngle = (2 * Math.PI * si) / subs.length;
448
+ subs[si].x = pNode.x + Math.cos(subAngle) * subRadius;
449
+ subs[si].y = pNode.y + Math.sin(subAngle) * subRadius;
450
+ }
451
+ }
452
+
453
+ // Ungrouped sub-tasks in outer ring
454
+ for (var i = 0; i < ungroupedSub.length; i++) {
455
+ var angle = (2 * Math.PI * i) / Math.max(ungroupedSub.length, 1);
456
+ ungroupedSub[i].x = Math.cos(angle) * R3;
457
+ ungroupedSub[i].y = Math.sin(angle) * R3;
458
+ }
459
+
460
+ // 9. Place documents near their related entity
461
+ var docs = byType['document'];
462
+ var docByParent = {};
463
+ var ungroupedDoc = [];
464
+ for (var i = 0; i < docs.length; i++) {
465
+ var parent = childToParent[docs[i].id];
466
+ if (parent && this._nodeMap[parent]) {
467
+ if (!docByParent[parent]) docByParent[parent] = [];
468
+ docByParent[parent].push(docs[i]);
469
+ } else {
470
+ ungroupedDoc.push(docs[i]);
471
+ }
472
+ }
473
+
474
+ var docParentKeys = Object.keys(docByParent);
475
+ for (var dp = 0; dp < docParentKeys.length; dp++) {
476
+ var dParent = this._nodeMap[docParentKeys[dp]];
477
+ var docChildren = docByParent[docParentKeys[dp]];
478
+ if (!dParent) continue;
479
+
480
+ // Place documents in a horizontal line to the right of parent (mind-map style)
481
+ var vGap = 50;
482
+ var startY = dParent.y - ((docChildren.length - 1) * vGap) / 2;
483
+ for (var di = 0; di < docChildren.length; di++) {
484
+ docChildren[di].x = dParent.x + 120;
485
+ docChildren[di].y = startY + di * vGap;
486
+ }
487
+ }
488
+
489
+ // Ungrouped documents
490
+ for (var i = 0; i < ungroupedDoc.length; i++) {
491
+ var angle = (2 * Math.PI * i) / Math.max(ungroupedDoc.length, 1) + Math.PI / 6;
492
+ ungroupedDoc[i].x = Math.cos(angle) * (R3 + 100);
493
+ ungroupedDoc[i].y = Math.sin(angle) * (R3 + 100);
494
+ }
495
+
496
+ // 10. Place other/unknown type nodes
497
+ var others = byType['other'];
498
+ for (var i = 0; i < others.length; i++) {
499
+ var angle = (2 * Math.PI * i) / Math.max(others.length, 1);
500
+ others[i].x = Math.cos(angle) * (R3 + 200);
501
+ others[i].y = Math.sin(angle) * (R3 + 200);
502
+ }
503
+
504
+ // Rebuild spatial indices
277
505
  this._spatialIndex.buildFromNodes(this._nodes);
278
506
  this._spatialIndex.buildEdgeIndex(this._edges, this._nodeMap);
279
507
  };
@@ -826,6 +1054,70 @@ GraphCanvas.prototype.destroy = function() {
826
1054
  this._eventHandlers = {};
827
1055
  };
828
1056
 
1057
+ // ── Document Mind-Map Arrangement (T11.6) ─────────────────────────────────
1058
+
1059
+ /**
1060
+ * Arrange parent-child document nodes in a horizontal mind-map layout.
1061
+ * Called automatically after force-directed layout stabilizes.
1062
+ */
1063
+ GraphCanvas.prototype._arrangeDocMindMaps = function() {
1064
+ var nodes = this._nodes;
1065
+ var edges = this._edges;
1066
+ if (!nodes || nodes.length === 0) return;
1067
+
1068
+ // 1. Find all parent document nodes (have 'doc_has_child' outgoing edges)
1069
+ var parentDocIds = {}; // parentId -> [childId, ...]
1070
+ for (var i = 0; i < edges.length; i++) {
1071
+ var e = edges[i];
1072
+ if (e.label === 'doc_has_child' || e._label === 'doc_has_child') {
1073
+ if (!parentDocIds[e.from]) parentDocIds[e.from] = [];
1074
+ parentDocIds[e.from].push(e.to);
1075
+ }
1076
+ }
1077
+
1078
+ var parentKeys = Object.keys(parentDocIds);
1079
+ if (parentKeys.length === 0) return;
1080
+
1081
+ var arranged = 0;
1082
+ for (var pi = 0; pi < parentKeys.length; pi++) {
1083
+ var parentId = parentKeys[pi];
1084
+ var parentNode = this._nodeMap[parentId];
1085
+ if (!parentNode) continue;
1086
+
1087
+ var childIds = parentDocIds[parentId];
1088
+ // Filter to existing children
1089
+ var validChildren = [];
1090
+ for (var ci = 0; ci < childIds.length; ci++) {
1091
+ if (this._nodeMap[childIds[ci]]) validChildren.push(childIds[ci]);
1092
+ }
1093
+ if (validChildren.length === 0) continue;
1094
+
1095
+ // 2. Compute layout: children to the right of parent, vertically centered
1096
+ var parentR = parentNode._radius || 20;
1097
+ var hGap = 40; // horizontal gap from parent right edge
1098
+ var vGap = 45; // vertical gap between children
1099
+ var leftEdgeX = parentNode.x + parentR + hGap;
1100
+
1101
+ var count = validChildren.length;
1102
+ var totalHeight = (count - 1) * vGap;
1103
+ var startY = parentNode.y - totalHeight / 2;
1104
+
1105
+ for (var j = 0; j < count; j++) {
1106
+ var childNode = this._nodeMap[validChildren[j]];
1107
+ if (!childNode) continue;
1108
+ var childR = childNode._radius || 15;
1109
+ var cx = leftEdgeX + childR;
1110
+ var cy = startY + j * vGap;
1111
+ this.moveNode(validChildren[j], cx, cy);
1112
+ }
1113
+ arranged++;
1114
+ }
1115
+
1116
+ if (arranged > 0) {
1117
+ this._emit('docMindMapsArranged', { count: arranged });
1118
+ }
1119
+ };
1120
+
829
1121
  /**
830
1122
  * Convert screen coordinates to world coordinates.
831
1123
  */
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/core.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAEH,sCA8zBC;AA9zBD,SAAgB,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4zBR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/core.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAEH,sCAkmCC;AAlmCD,SAAgB,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgmCR,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/interaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,oBAAoB,IAAI,MAAM,CA8a7C"}
1
+ {"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/interaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,oBAAoB,IAAI,MAAM,CAyb7C"}
@@ -250,6 +250,17 @@ InteractionManager.prototype._hitTest = function(sx, sy) {
250
250
  // ── Hover ─────────────────────────────────────────────────────────────────
251
251
 
252
252
  InteractionManager.prototype._handleHover = function(sx, sy) {
253
+ // T11.9: Skip hover detection for large graphs (>800 nodes) to save CPU
254
+ if (this._engine._nodeCount > 800) {
255
+ if (this._hoveredNode) {
256
+ this._hoveredNode._hovered = false;
257
+ this._engine.markDirtyNode(this._hoveredNode);
258
+ this._hoveredNode = null;
259
+ this._engine._canvas.style.cursor = 'default';
260
+ }
261
+ return;
262
+ }
263
+
253
264
  var hitNode = this._hitTest(sx, sy);
254
265
  var prevHovered = this._hoveredNode;
255
266
 
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/interaction.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAEH,oDA8aC;AA9aD,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4aR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/interaction.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAEH,oDAybC;AAzbD,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAubR,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"layout-worker.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/layout-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,qBAAqB,IAAI,MAAM,CAygB9C"}
1
+ {"version":3,"file":"layout-worker.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/layout-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,qBAAqB,IAAI,MAAM,CA6iB9C"}
@@ -41,7 +41,8 @@ function LayoutEngine(engine) {
41
41
  batchSize: 10, // iterations per message
42
42
  idealEdgeLength: 120,
43
43
  theta: 0.8, // Barnes-Hut approximation parameter
44
- earlyTerminationThreshold: 0.5, // stop when energy below this
44
+ earlyTerminationThreshold: 0.3, // stop when energy below this (lowered for better convergence)
45
+ avoidOverlap: 0.8, // overlap avoidance strength (0=off, 1=full)
45
46
  };
46
47
  }
47
48
 
@@ -63,17 +64,21 @@ LayoutEngine.prototype.start = function(options) {
63
64
  var edges = engine._edges;
64
65
 
65
66
  // Auto-select algorithm based on node count
66
- var useBarnesHut = nodes.length > 1000;
67
+ var useBarnesHut = nodes.length > 500; // Lower threshold: BH is better for 500+ nodes
67
68
  opts.useBarnesHut = useBarnesHut;
68
69
 
69
- // Adapt max iterations for large graphs
70
- if (nodes.length > 5000) {
71
- opts.maxIterations = Math.min(opts.maxIterations, 150);
72
- opts.batchSize = 5;
73
- } else if (nodes.length > 10000) {
74
- opts.maxIterations = Math.min(opts.maxIterations, 80);
70
+ // Adapt max iterations based on graph size
71
+ if (nodes.length > 10000) {
72
+ opts.maxIterations = Math.min(opts.maxIterations, 100);
75
73
  opts.batchSize = 3;
74
+ } else if (nodes.length > 5000) {
75
+ opts.maxIterations = Math.min(opts.maxIterations, 200);
76
+ opts.batchSize = 5;
77
+ } else if (nodes.length > 2000) {
78
+ opts.maxIterations = Math.min(opts.maxIterations, 300);
79
+ opts.batchSize = 8;
76
80
  }
81
+ // For medium graphs (<2000), keep higher iteration count for quality
77
82
  this._maxIterations = opts.maxIterations;
78
83
 
79
84
  // Prepare node/edge data for worker
@@ -393,7 +398,8 @@ LayoutEngine.prototype._getWorkerCode = function() {
393
398
  var damping = options.damping || 0.4;
394
399
  var theta = options.theta || 0.8;
395
400
  var useBarnesHut = options.useBarnesHut || false;
396
- var earlyTermThreshold = options.earlyTerminationThreshold || 0.5;
401
+ var earlyTermThreshold = options.earlyTerminationThreshold || 0.3;
402
+ var avoidOverlap = options.avoidOverlap || 0.8;
397
403
  var algorithm = useBarnesHut ? 'barnes-hut' : 'fruchterman-reingold';
398
404
 
399
405
  // Track energy for early termination
@@ -465,6 +471,36 @@ LayoutEngine.prototype._getWorkerCode = function() {
465
471
  nodes[i].fy -= nodes[i].y * gravity;
466
472
  }
467
473
 
474
+ // ── Overlap avoidance ──
475
+ // For Barnes-Hut mode, only check within local neighborhood (using spatial hash)
476
+ if (avoidOverlap > 0) {
477
+ if (!useBarnesHut || nodes.length < 2000) {
478
+ // O(n²) check — acceptable for <2000 nodes
479
+ for (var i = 0; i < nodes.length; i++) {
480
+ var ri = (nodes[i].radius || 10);
481
+ for (var j = i + 1; j < nodes.length; j++) {
482
+ var rj = (nodes[j].radius || 10);
483
+ var minDist = (ri + rj) * avoidOverlap * 1.5;
484
+ var odx = nodes[j].x - nodes[i].x;
485
+ var ody = nodes[j].y - nodes[i].y;
486
+ var oDist = Math.sqrt(odx * odx + ody * ody);
487
+ if (oDist < minDist && oDist > 0.1) {
488
+ var pushForce = (minDist - oDist) * 0.3 * temperature;
489
+ var opx = (odx / oDist) * pushForce;
490
+ var opy = (ody / oDist) * pushForce;
491
+ nodes[i].fx -= opx;
492
+ nodes[i].fy -= opy;
493
+ nodes[j].fx += opx;
494
+ nodes[j].fy += opy;
495
+ }
496
+ }
497
+ }
498
+ } else {
499
+ // For very large graphs, skip full O(n²) overlap check
500
+ // The repulsion force from Barnes-Hut already separates nodes reasonably
501
+ }
502
+ }
503
+
468
504
  // ── Apply forces with damping and temperature ──
469
505
  totalEnergy = 0;
470
506
  for (var i = 0; i < nodes.length; i++) {
@@ -1 +1 @@
1
- {"version":3,"file":"layout-worker.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/layout-worker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAEH,sDAygBC;AAzgBD,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAugBR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"layout-worker.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/layout-worker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAEH,sDA6iBC;AA7iBD,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2iBR,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CA2pB1C"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CA8xB1C"}