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.
- package/dist/dev-plan-document-store.d.ts +13 -1
- package/dist/dev-plan-document-store.d.ts.map +1 -1
- package/dist/dev-plan-document-store.js +119 -0
- package/dist/dev-plan-document-store.js.map +1 -1
- package/dist/dev-plan-factory.d.ts.map +1 -1
- package/dist/dev-plan-factory.js +2 -1
- package/dist/dev-plan-factory.js.map +1 -1
- package/dist/dev-plan-graph-store.d.ts +64 -1
- package/dist/dev-plan-graph-store.d.ts.map +1 -1
- package/dist/dev-plan-graph-store.js +364 -2
- package/dist/dev-plan-graph-store.js.map +1 -1
- package/dist/dev-plan-interface.d.ts +24 -1
- package/dist/dev-plan-interface.d.ts.map +1 -1
- package/dist/dev-plan-migrate.d.ts +1 -0
- package/dist/dev-plan-migrate.d.ts.map +1 -1
- package/dist/dev-plan-migrate.js +28 -2
- package/dist/dev-plan-migrate.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/index.js +119 -0
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/types.d.ts +88 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/api-compat.js +22 -12
- package/dist/visualize/graph-canvas/api-compat.js.map +1 -1
- package/dist/visualize/graph-canvas/core.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/core.js +296 -4
- package/dist/visualize/graph-canvas/core.js.map +1 -1
- package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/interaction.js +11 -0
- package/dist/visualize/graph-canvas/interaction.js.map +1 -1
- package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/layout-worker.js +45 -9
- package/dist/visualize/graph-canvas/layout-worker.js.map +1 -1
- package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/renderer.js +164 -33
- package/dist/visualize/graph-canvas/renderer.js.map +1 -1
- package/dist/visualize/graph-canvas/styles.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/styles.js +136 -121
- package/dist/visualize/graph-canvas/styles.js.map +1 -1
- package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -1
- package/dist/visualize/graph-canvas/viewport.js +10 -0
- package/dist/visualize/graph-canvas/viewport.js.map +1 -1
- package/dist/visualize/server.js +149 -32
- package/dist/visualize/server.js.map +1 -1
- package/dist/visualize/template-core.d.ts +9 -0
- package/dist/visualize/template-core.d.ts.map +1 -0
- package/dist/visualize/template-core.js +714 -0
- package/dist/visualize/template-core.js.map +1 -0
- package/dist/visualize/template-data-loading.d.ts +7 -0
- package/dist/visualize/template-data-loading.d.ts.map +1 -0
- package/dist/visualize/template-data-loading.js +677 -0
- package/dist/visualize/template-data-loading.js.map +1 -0
- package/dist/visualize/template-detail-panel.d.ts +14 -0
- package/dist/visualize/template-detail-panel.d.ts.map +1 -0
- package/dist/visualize/template-detail-panel.js +553 -0
- package/dist/visualize/template-detail-panel.js.map +1 -0
- package/dist/visualize/template-graph-3d.d.ts +7 -0
- package/dist/visualize/template-graph-3d.d.ts.map +1 -0
- package/dist/visualize/template-graph-3d.js +1112 -0
- package/dist/visualize/template-graph-3d.js.map +1 -0
- package/dist/visualize/template-graph-vis.d.ts +8 -0
- package/dist/visualize/template-graph-vis.d.ts.map +1 -0
- package/dist/visualize/template-graph-vis.js +1204 -0
- package/dist/visualize/template-graph-vis.js.map +1 -0
- package/dist/visualize/template-html.d.ts +9 -0
- package/dist/visualize/template-html.d.ts.map +1 -0
- package/dist/visualize/template-html.js +484 -0
- package/dist/visualize/template-html.js.map +1 -0
- package/dist/visualize/template-pages.d.ts +7 -0
- package/dist/visualize/template-pages.d.ts.map +1 -0
- package/dist/visualize/template-pages.js +806 -0
- package/dist/visualize/template-pages.js.map +1 -0
- package/dist/visualize/template-stats-modal.d.ts +7 -0
- package/dist/visualize/template-stats-modal.d.ts.map +1 -0
- package/dist/visualize/template-stats-modal.js +406 -0
- package/dist/visualize/template-stats-modal.js.map +1 -0
- package/dist/visualize/template-styles.d.ts +9 -0
- package/dist/visualize/template-styles.d.ts.map +1 -0
- package/dist/visualize/template-styles.js +487 -0
- package/dist/visualize/template-styles.js.map +1 -0
- package/dist/visualize/template.d.ts +14 -3
- package/dist/visualize/template.d.ts.map +1 -1
- package/dist/visualize/template.js +38 -3475
- package/dist/visualize/template.js.map +1 -1
- 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
|
|
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
|
-
|
|
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].
|
|
275
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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 >
|
|
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
|
|
70
|
-
if (nodes.length >
|
|
71
|
-
opts.maxIterations = Math.min(opts.maxIterations,
|
|
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.
|
|
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,
|
|
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,
|
|
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"}
|