@kage-core/kage-graph-mcp 1.1.8 → 1.1.9
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/package.json +1 -1
- package/viewer/app.js +118 -19
package/package.json
CHANGED
package/viewer/app.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
edges: [],
|
|
8
8
|
episodesById: new Map(),
|
|
9
9
|
entityById: new Map(),
|
|
10
|
+
edgeById: new Map(),
|
|
11
|
+
degreeById: new Map(),
|
|
10
12
|
positions: new Map(),
|
|
11
13
|
visibleEntityIds: new Set(),
|
|
12
14
|
visibleEdgeIds: new Set(),
|
|
@@ -27,8 +29,12 @@
|
|
|
27
29
|
panning: null,
|
|
28
30
|
hoverNode: null,
|
|
29
31
|
raf: null,
|
|
32
|
+
drawRaf: null,
|
|
30
33
|
running: false,
|
|
31
|
-
lastSignature: ""
|
|
34
|
+
lastSignature: "",
|
|
35
|
+
tick: 0,
|
|
36
|
+
idleFrames: 0,
|
|
37
|
+
adjacency: new Map()
|
|
32
38
|
},
|
|
33
39
|
pan: null
|
|
34
40
|
};
|
|
@@ -104,7 +110,7 @@
|
|
|
104
110
|
};
|
|
105
111
|
|
|
106
112
|
els.graphFile.addEventListener("change", handleFile);
|
|
107
|
-
els.searchInput.addEventListener("input",
|
|
113
|
+
els.searchInput.addEventListener("input", scheduleRender);
|
|
108
114
|
els.viewMode.addEventListener("change", render);
|
|
109
115
|
els.typeFilter.addEventListener("change", render);
|
|
110
116
|
els.relationFilter.addEventListener("change", render);
|
|
@@ -183,6 +189,10 @@
|
|
|
183
189
|
state.entityById = new Map(entities.map(function (entity) {
|
|
184
190
|
return [entity.id, entity];
|
|
185
191
|
}));
|
|
192
|
+
state.edgeById = new Map(edges.map(function (edge) {
|
|
193
|
+
return [edge.id, edge];
|
|
194
|
+
}));
|
|
195
|
+
state.degreeById = buildDegreeMap(edges);
|
|
186
196
|
state.episodesById = new Map(episodes.map(function (episode) {
|
|
187
197
|
return [episode.id, episode];
|
|
188
198
|
}));
|
|
@@ -195,6 +205,15 @@
|
|
|
195
205
|
render();
|
|
196
206
|
}
|
|
197
207
|
|
|
208
|
+
function buildDegreeMap(edges) {
|
|
209
|
+
var degrees = new Map();
|
|
210
|
+
edges.forEach(function (edge) {
|
|
211
|
+
degrees.set(edge.from, (degrees.get(edge.from) || 0) + 1);
|
|
212
|
+
degrees.set(edge.to, (degrees.get(edge.to) || 0) + 1);
|
|
213
|
+
});
|
|
214
|
+
return degrees;
|
|
215
|
+
}
|
|
216
|
+
|
|
198
217
|
function loadFromUrlParams() {
|
|
199
218
|
var params = new URLSearchParams(window.location.search);
|
|
200
219
|
var graphPaths = []
|
|
@@ -471,6 +490,7 @@
|
|
|
471
490
|
if (!state.graph) return;
|
|
472
491
|
|
|
473
492
|
var query = parseSearchQuery(els.searchInput.value);
|
|
493
|
+
state.renderQuery = query;
|
|
474
494
|
var mode = els.viewMode.value;
|
|
475
495
|
var type = els.typeFilter.value;
|
|
476
496
|
var relation = els.relationFilter.value;
|
|
@@ -509,6 +529,7 @@
|
|
|
509
529
|
type: type,
|
|
510
530
|
relation: relation,
|
|
511
531
|
scope: els.scopeFilter.value,
|
|
532
|
+
mode: mode,
|
|
512
533
|
maxNodes: Number(els.maxNodes.value || 90),
|
|
513
534
|
showDependencies: els.showDependencies.checked
|
|
514
535
|
});
|
|
@@ -528,6 +549,14 @@
|
|
|
528
549
|
renderProof();
|
|
529
550
|
}
|
|
530
551
|
|
|
552
|
+
function scheduleRender() {
|
|
553
|
+
if (state.renderRaf) return;
|
|
554
|
+
state.renderRaf = window.requestAnimationFrame(function () {
|
|
555
|
+
state.renderRaf = null;
|
|
556
|
+
render();
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
531
560
|
function refineVisibleGraph(entityIds, edgeIds, options) {
|
|
532
561
|
var entities = new Set(entityIds);
|
|
533
562
|
var edges = new Set(edgeIds);
|
|
@@ -552,7 +581,9 @@
|
|
|
552
581
|
return entityImportance(state.entityById.get(b)) - entityImportance(state.entityById.get(a)) ||
|
|
553
582
|
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
554
583
|
});
|
|
555
|
-
entities =
|
|
584
|
+
entities = options.mode === "combined"
|
|
585
|
+
? balancedSignalEntities(ranked, options.maxNodes)
|
|
586
|
+
: new Set(ranked.slice(0, options.maxNodes));
|
|
556
587
|
if (state.selected && state.selected.kind === "entity") entities.add(state.selected.id);
|
|
557
588
|
edges = edgesWithVisibleEndpoints(edges, entities);
|
|
558
589
|
}
|
|
@@ -568,6 +599,33 @@
|
|
|
568
599
|
return { entities: entities, edges: edgesWithVisibleEndpoints(edges, entities) };
|
|
569
600
|
}
|
|
570
601
|
|
|
602
|
+
function balancedSignalEntities(rankedIds, maxNodes) {
|
|
603
|
+
var result = new Set();
|
|
604
|
+
var memoryIds = rankedIds.filter(function (id) {
|
|
605
|
+
var entity = state.entityById.get(id);
|
|
606
|
+
return entity && entity.graph_kind === "memory" && ["memory", "repo", "memory_type", "command"].indexOf(entity.type) !== -1;
|
|
607
|
+
});
|
|
608
|
+
var codeIds = rankedIds.filter(function (id) {
|
|
609
|
+
var entity = state.entityById.get(id);
|
|
610
|
+
return entity && entity.graph_kind === "code";
|
|
611
|
+
});
|
|
612
|
+
var otherMemoryIds = rankedIds.filter(function (id) {
|
|
613
|
+
var entity = state.entityById.get(id);
|
|
614
|
+
return entity && entity.graph_kind === "memory" && !memoryIds.includes(id);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
var memoryBudget = clamp(Math.round(maxNodes * 0.34), 18, Math.min(44, maxNodes));
|
|
618
|
+
memoryIds.slice(0, memoryBudget).forEach(function (id) { result.add(id); });
|
|
619
|
+
otherMemoryIds.slice(0, Math.max(0, Math.round(memoryBudget * 0.35))).forEach(function (id) { result.add(id); });
|
|
620
|
+
codeIds.forEach(function (id) {
|
|
621
|
+
if (result.size < maxNodes) result.add(id);
|
|
622
|
+
});
|
|
623
|
+
rankedIds.forEach(function (id) {
|
|
624
|
+
if (result.size < maxNodes) result.add(id);
|
|
625
|
+
});
|
|
626
|
+
return result;
|
|
627
|
+
}
|
|
628
|
+
|
|
571
629
|
function focusSelection(entityIds, edgeIds, selection) {
|
|
572
630
|
var entities = new Set();
|
|
573
631
|
var edges = new Set();
|
|
@@ -599,7 +657,7 @@
|
|
|
599
657
|
|
|
600
658
|
function edgesWithVisibleEndpoints(edgeIds, entityIds) {
|
|
601
659
|
return new Set(Array.from(edgeIds).filter(function (id) {
|
|
602
|
-
var edge = state.
|
|
660
|
+
var edge = state.edgeById.get(id);
|
|
603
661
|
return edge && entityIds.has(edge.from) && entityIds.has(edge.to);
|
|
604
662
|
}));
|
|
605
663
|
}
|
|
@@ -656,6 +714,22 @@
|
|
|
656
714
|
state.sim.edges = state.edges.filter(function (edge) {
|
|
657
715
|
return state.visibleEdgeIds.has(edge.id) && state.sim.nodeById.has(edge.from) && state.sim.nodeById.has(edge.to);
|
|
658
716
|
});
|
|
717
|
+
state.sim.adjacency = buildAdjacency(state.sim.edges);
|
|
718
|
+
if (forceReset) {
|
|
719
|
+
state.sim.tick = 0;
|
|
720
|
+
state.sim.idleFrames = 0;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function buildAdjacency(edges) {
|
|
725
|
+
var adjacency = new Map();
|
|
726
|
+
edges.forEach(function (edge) {
|
|
727
|
+
if (!adjacency.has(edge.from)) adjacency.set(edge.from, new Set());
|
|
728
|
+
if (!adjacency.has(edge.to)) adjacency.set(edge.to, new Set());
|
|
729
|
+
adjacency.get(edge.from).add(edge.to);
|
|
730
|
+
adjacency.get(edge.to).add(edge.from);
|
|
731
|
+
});
|
|
732
|
+
return adjacency;
|
|
659
733
|
}
|
|
660
734
|
|
|
661
735
|
function seededPosition(index, total) {
|
|
@@ -673,16 +747,28 @@
|
|
|
673
747
|
state.sim.raf = window.requestAnimationFrame(simulationTick);
|
|
674
748
|
}
|
|
675
749
|
|
|
750
|
+
function stopSimulation() {
|
|
751
|
+
state.sim.running = false;
|
|
752
|
+
if (state.sim.raf) window.cancelAnimationFrame(state.sim.raf);
|
|
753
|
+
state.sim.raf = null;
|
|
754
|
+
}
|
|
755
|
+
|
|
676
756
|
function simulationTick() {
|
|
677
757
|
if (!state.sim.running) return;
|
|
678
|
-
stepSimulation();
|
|
758
|
+
var maxVelocity = stepSimulation();
|
|
679
759
|
drawCanvasGraph();
|
|
760
|
+
state.sim.tick += 1;
|
|
761
|
+
state.sim.idleFrames = maxVelocity < 0.035 ? state.sim.idleFrames + 1 : 0;
|
|
762
|
+
if (!state.sim.dragNode && (state.sim.idleFrames > 24 || state.sim.tick > 220)) {
|
|
763
|
+
stopSimulation();
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
680
766
|
state.sim.raf = window.requestAnimationFrame(simulationTick);
|
|
681
767
|
}
|
|
682
768
|
|
|
683
769
|
function stepSimulation() {
|
|
684
770
|
var nodes = state.sim.nodes;
|
|
685
|
-
if (!nodes.length) return;
|
|
771
|
+
if (!nodes.length) return 0;
|
|
686
772
|
var nodeMap = state.sim.nodeById;
|
|
687
773
|
var count = nodes.length;
|
|
688
774
|
var repulsion = count > 120 ? 3600 : count > 70 ? 2500 : 1700;
|
|
@@ -736,11 +822,16 @@
|
|
|
736
822
|
}
|
|
737
823
|
});
|
|
738
824
|
|
|
825
|
+
var maxVelocity = 0;
|
|
739
826
|
nodes.forEach(function (node) {
|
|
740
827
|
if (state.sim.dragNode === node) return;
|
|
741
|
-
|
|
742
|
-
|
|
828
|
+
var vx = clamp(node.vx, -8, 8);
|
|
829
|
+
var vy = clamp(node.vy, -8, 8);
|
|
830
|
+
node.x += vx;
|
|
831
|
+
node.y += vy;
|
|
832
|
+
maxVelocity = Math.max(maxVelocity, Math.abs(vx), Math.abs(vy));
|
|
743
833
|
});
|
|
834
|
+
return maxVelocity;
|
|
744
835
|
}
|
|
745
836
|
|
|
746
837
|
function drawCanvasGraph() {
|
|
@@ -803,7 +894,7 @@
|
|
|
803
894
|
function drawCanvasEdges(ctx) {
|
|
804
895
|
var nodeMap = state.sim.nodeById;
|
|
805
896
|
var focusId = focusedCanvasNodeId();
|
|
806
|
-
var query = parseSearchQuery(els.searchInput.value);
|
|
897
|
+
var query = state.renderQuery || parseSearchQuery(els.searchInput.value);
|
|
807
898
|
var dense = state.sim.nodes.length > 55;
|
|
808
899
|
state.sim.edges.forEach(function (edge) {
|
|
809
900
|
var from = nodeMap.get(edge.from);
|
|
@@ -832,15 +923,14 @@
|
|
|
832
923
|
|
|
833
924
|
function drawCanvasNodes(ctx) {
|
|
834
925
|
var focusId = focusedCanvasNodeId();
|
|
835
|
-
var query = parseSearchQuery(els.searchInput.value);
|
|
926
|
+
var query = state.renderQuery || parseSearchQuery(els.searchInput.value);
|
|
836
927
|
var dense = state.sim.nodes.length > 55;
|
|
928
|
+
var focusNeighbors = focusId ? state.sim.adjacency.get(focusId) : null;
|
|
837
929
|
state.sim.nodes.forEach(function (node) {
|
|
838
930
|
var entity = node.entity;
|
|
839
931
|
var selected = state.selected && state.selected.kind === "entity" && state.selected.id === node.id;
|
|
840
932
|
var hovered = state.sim.hoverNode && state.sim.hoverNode.id === node.id;
|
|
841
|
-
var connected = focusId && (node.id === focusId ||
|
|
842
|
-
return (edge.from === focusId && edge.to === node.id) || (edge.to === focusId && edge.from === node.id);
|
|
843
|
-
}));
|
|
933
|
+
var connected = focusId && (node.id === focusId || (focusNeighbors && focusNeighbors.has(node.id)));
|
|
844
934
|
var matches = matchesSearchQuery(entity, query);
|
|
845
935
|
var alpha = !matches ? 0.12 : focusId && !connected ? 0.20 : 1;
|
|
846
936
|
var color = nodeThemeColor(entity);
|
|
@@ -1376,9 +1466,7 @@
|
|
|
1376
1466
|
}
|
|
1377
1467
|
|
|
1378
1468
|
function degreeOf(id) {
|
|
1379
|
-
return state.
|
|
1380
|
-
return sum + (edge.from === id || edge.to === id ? 1 : 0);
|
|
1381
|
-
}, 0);
|
|
1469
|
+
return state.degreeById.get(id) || 0;
|
|
1382
1470
|
}
|
|
1383
1471
|
|
|
1384
1472
|
function entityImportance(entity) {
|
|
@@ -1442,6 +1530,9 @@
|
|
|
1442
1530
|
state.sim.dragNode.y = world.y;
|
|
1443
1531
|
state.sim.dragNode.vx = 0;
|
|
1444
1532
|
state.sim.dragNode.vy = 0;
|
|
1533
|
+
state.sim.tick = 0;
|
|
1534
|
+
state.sim.idleFrames = 0;
|
|
1535
|
+
startSimulation();
|
|
1445
1536
|
} else if (state.sim.panning) {
|
|
1446
1537
|
var dx = event.clientX - state.sim.panning.x;
|
|
1447
1538
|
var dy = event.clientY - state.sim.panning.y;
|
|
@@ -1451,7 +1542,7 @@
|
|
|
1451
1542
|
}
|
|
1452
1543
|
state.sim.hoverNode = findCanvasNode(world.x, world.y);
|
|
1453
1544
|
updateCanvasTooltip(event);
|
|
1454
|
-
|
|
1545
|
+
scheduleCanvasDraw();
|
|
1455
1546
|
}
|
|
1456
1547
|
|
|
1457
1548
|
function endCanvasPointer() {
|
|
@@ -1471,7 +1562,7 @@
|
|
|
1471
1562
|
state.sim.dragNode = null;
|
|
1472
1563
|
state.sim.panning = null;
|
|
1473
1564
|
if (els.tooltip) els.tooltip.classList.remove("visible");
|
|
1474
|
-
|
|
1565
|
+
scheduleCanvasDraw();
|
|
1475
1566
|
}
|
|
1476
1567
|
|
|
1477
1568
|
function handleCanvasWheel(event) {
|
|
@@ -1482,7 +1573,15 @@
|
|
|
1482
1573
|
state.sim.zoom = clamp(state.sim.zoom * factor, 0.14, 4.5);
|
|
1483
1574
|
state.sim.panX = event.clientX - rect.left - before.x * state.sim.zoom;
|
|
1484
1575
|
state.sim.panY = event.clientY - rect.top - before.y * state.sim.zoom;
|
|
1485
|
-
|
|
1576
|
+
scheduleCanvasDraw();
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function scheduleCanvasDraw() {
|
|
1580
|
+
if (state.sim.drawRaf) return;
|
|
1581
|
+
state.sim.drawRaf = window.requestAnimationFrame(function () {
|
|
1582
|
+
state.sim.drawRaf = null;
|
|
1583
|
+
drawCanvasGraph();
|
|
1584
|
+
});
|
|
1486
1585
|
}
|
|
1487
1586
|
|
|
1488
1587
|
function handleCanvasDoubleClick(event) {
|