aifastdb-devplan 1.5.0 → 1.6.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 (78) hide show
  1. package/dist/autopilot.d.ts +58 -0
  2. package/dist/autopilot.d.ts.map +1 -0
  3. package/dist/autopilot.js +250 -0
  4. package/dist/autopilot.js.map +1 -0
  5. package/dist/dev-plan-document-store.d.ts +2 -0
  6. package/dist/dev-plan-document-store.d.ts.map +1 -1
  7. package/dist/dev-plan-document-store.js +3 -0
  8. package/dist/dev-plan-document-store.js.map +1 -1
  9. package/dist/dev-plan-factory.d.ts +69 -3
  10. package/dist/dev-plan-factory.d.ts.map +1 -1
  11. package/dist/dev-plan-factory.js +111 -18
  12. package/dist/dev-plan-factory.js.map +1 -1
  13. package/dist/dev-plan-graph-store.d.ts +15 -0
  14. package/dist/dev-plan-graph-store.d.ts.map +1 -1
  15. package/dist/dev-plan-graph-store.js +57 -2
  16. package/dist/dev-plan-graph-store.js.map +1 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +14 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp-server/index.d.ts +3 -0
  22. package/dist/mcp-server/index.d.ts.map +1 -1
  23. package/dist/mcp-server/index.js +278 -4
  24. package/dist/mcp-server/index.js.map +1 -1
  25. package/dist/types.d.ts +72 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js +9 -1
  28. package/dist/types.js.map +1 -1
  29. package/dist/visualize/graph-canvas/api-compat.d.ts +20 -0
  30. package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -0
  31. package/dist/visualize/graph-canvas/api-compat.js +334 -0
  32. package/dist/visualize/graph-canvas/api-compat.js.map +1 -0
  33. package/dist/visualize/graph-canvas/clusterer.d.ts +16 -0
  34. package/dist/visualize/graph-canvas/clusterer.d.ts.map +1 -0
  35. package/dist/visualize/graph-canvas/clusterer.js +460 -0
  36. package/dist/visualize/graph-canvas/clusterer.js.map +1 -0
  37. package/dist/visualize/graph-canvas/core.d.ts +11 -0
  38. package/dist/visualize/graph-canvas/core.d.ts.map +1 -0
  39. package/dist/visualize/graph-canvas/core.js +844 -0
  40. package/dist/visualize/graph-canvas/core.js.map +1 -0
  41. package/dist/visualize/graph-canvas/index.d.ts +22 -0
  42. package/dist/visualize/graph-canvas/index.d.ts.map +1 -0
  43. package/dist/visualize/graph-canvas/index.js +69 -0
  44. package/dist/visualize/graph-canvas/index.js.map +1 -0
  45. package/dist/visualize/graph-canvas/interaction.d.ts +13 -0
  46. package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -0
  47. package/dist/visualize/graph-canvas/interaction.js +446 -0
  48. package/dist/visualize/graph-canvas/interaction.js.map +1 -0
  49. package/dist/visualize/graph-canvas/layout-worker.d.ts +17 -0
  50. package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -0
  51. package/dist/visualize/graph-canvas/layout-worker.js +541 -0
  52. package/dist/visualize/graph-canvas/layout-worker.js.map +1 -0
  53. package/dist/visualize/graph-canvas/lod.d.ts +10 -0
  54. package/dist/visualize/graph-canvas/lod.d.ts.map +1 -0
  55. package/dist/visualize/graph-canvas/lod.js +111 -0
  56. package/dist/visualize/graph-canvas/lod.js.map +1 -0
  57. package/dist/visualize/graph-canvas/renderer.d.ts +12 -0
  58. package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -0
  59. package/dist/visualize/graph-canvas/renderer.js +682 -0
  60. package/dist/visualize/graph-canvas/renderer.js.map +1 -0
  61. package/dist/visualize/graph-canvas/spatial-index.d.ts +13 -0
  62. package/dist/visualize/graph-canvas/spatial-index.d.ts.map +1 -0
  63. package/dist/visualize/graph-canvas/spatial-index.js +482 -0
  64. package/dist/visualize/graph-canvas/spatial-index.js.map +1 -0
  65. package/dist/visualize/graph-canvas/styles.d.ts +11 -0
  66. package/dist/visualize/graph-canvas/styles.d.ts.map +1 -0
  67. package/dist/visualize/graph-canvas/styles.js +137 -0
  68. package/dist/visualize/graph-canvas/styles.js.map +1 -0
  69. package/dist/visualize/graph-canvas/viewport.d.ts +17 -0
  70. package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -0
  71. package/dist/visualize/graph-canvas/viewport.js +375 -0
  72. package/dist/visualize/graph-canvas/viewport.js.map +1 -0
  73. package/dist/visualize/server.js +619 -6
  74. package/dist/visualize/server.js.map +1 -1
  75. package/dist/visualize/template.d.ts.map +1 -1
  76. package/dist/visualize/template.js +604 -18
  77. package/dist/visualize/template.js.map +1 -1
  78. package/package.json +1 -1
@@ -0,0 +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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * GraphCanvas — 高性能图谱渲染引擎
3
+ *
4
+ * 替换 vis-network 的自研 Canvas2D 渲染器。
5
+ * 导出为内联 JavaScript 字符串,由 template.ts 嵌入 HTML。
6
+ *
7
+ * 核心架构 (借鉴 LeaferJS):
8
+ * - GraphCanvas: 主引擎,管理 Canvas、渲染循环、数据
9
+ * - ViewportManager: 平移/缩放/坐标变换
10
+ * - SpatialIndex: R-tree 空间索引 (O(log n) 视口查询)
11
+ * - RenderPipeline: 视口裁剪 + 脏区域局部渲染
12
+ * - LODManager: 多级细节渲染
13
+ * - LayoutEngine: Web Worker 力导向布局
14
+ * - Clusterer: 节点聚合
15
+ * - InteractionManager: 高效 hit-test + hover/click/drag
16
+ */
17
+ /**
18
+ * 返回完整的 GraphCanvas 引擎 JavaScript 代码。
19
+ * 嵌入方式: `<script>${getGraphCanvasScript()}</script>`
20
+ */
21
+ export declare function getGraphCanvasScript(): string;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAaH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAkC7C"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * GraphCanvas — 高性能图谱渲染引擎
4
+ *
5
+ * 替换 vis-network 的自研 Canvas2D 渲染器。
6
+ * 导出为内联 JavaScript 字符串,由 template.ts 嵌入 HTML。
7
+ *
8
+ * 核心架构 (借鉴 LeaferJS):
9
+ * - GraphCanvas: 主引擎,管理 Canvas、渲染循环、数据
10
+ * - ViewportManager: 平移/缩放/坐标变换
11
+ * - SpatialIndex: R-tree 空间索引 (O(log n) 视口查询)
12
+ * - RenderPipeline: 视口裁剪 + 脏区域局部渲染
13
+ * - LODManager: 多级细节渲染
14
+ * - LayoutEngine: Web Worker 力导向布局
15
+ * - Clusterer: 节点聚合
16
+ * - InteractionManager: 高效 hit-test + hover/click/drag
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.getGraphCanvasScript = getGraphCanvasScript;
20
+ const core_1 = require("./core");
21
+ const viewport_1 = require("./viewport");
22
+ const spatial_index_1 = require("./spatial-index");
23
+ const renderer_1 = require("./renderer");
24
+ const lod_1 = require("./lod");
25
+ const interaction_1 = require("./interaction");
26
+ const layout_worker_1 = require("./layout-worker");
27
+ const clusterer_1 = require("./clusterer");
28
+ const styles_1 = require("./styles");
29
+ const api_compat_1 = require("./api-compat");
30
+ /**
31
+ * 返回完整的 GraphCanvas 引擎 JavaScript 代码。
32
+ * 嵌入方式: `<script>${getGraphCanvasScript()}</script>`
33
+ */
34
+ function getGraphCanvasScript() {
35
+ return `
36
+ // ============================================================================
37
+ // GraphCanvas Engine — 高性能图谱渲染引擎 v1.0
38
+ // ============================================================================
39
+ (function(global) {
40
+ 'use strict';
41
+
42
+ ${(0, core_1.getCoreScript)()}
43
+
44
+ ${(0, viewport_1.getViewportScript)()}
45
+
46
+ ${(0, spatial_index_1.getSpatialIndexScript)()}
47
+
48
+ ${(0, renderer_1.getRendererScript)()}
49
+
50
+ ${(0, lod_1.getLODScript)()}
51
+
52
+ ${(0, interaction_1.getInteractionScript)()}
53
+
54
+ ${(0, layout_worker_1.getLayoutWorkerScript)()}
55
+
56
+ ${(0, clusterer_1.getClustererScript)()}
57
+
58
+ ${(0, styles_1.getStylesScript)()}
59
+
60
+ ${(0, api_compat_1.getApiCompatScript)()}
61
+
62
+ // Export to global
63
+ global.GraphCanvas = GraphCanvas;
64
+ global.DevPlanGraph = DevPlanGraph;
65
+
66
+ })(typeof window !== 'undefined' ? window : this);
67
+ `;
68
+ }
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;AAiBH,oDAkCC;AAjDD,iCAAuC;AACvC,yCAA+C;AAC/C,mDAAwD;AACxD,yCAA+C;AAC/C,+BAAqC;AACrC,+CAAqD;AACrD,mDAAwD;AACxD,2CAAiD;AACjD,qCAA2C;AAC3C,6CAAkD;AAElD;;;GAGG;AACH,SAAgB,oBAAoB;IAClC,OAAO;;;;;;;EAOP,IAAA,oBAAa,GAAE;;EAEf,IAAA,4BAAiB,GAAE;;EAEnB,IAAA,qCAAqB,GAAE;;EAEvB,IAAA,4BAAiB,GAAE;;EAEnB,IAAA,kBAAY,GAAE;;EAEd,IAAA,kCAAoB,GAAE;;EAEtB,IAAA,qCAAqB,GAAE;;EAEvB,IAAA,8BAAkB,GAAE;;EAEpB,IAAA,wBAAe,GAAE;;EAEjB,IAAA,+BAAkB,GAAE;;;;;;;CAOrB,CAAC;AACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * InteractionManager — 交互管理器
3
+ *
4
+ * 基于空间索引实现 O(log n) hit-test:
5
+ * - hover: 鼠标悬停高亮 (脏区域重绘)
6
+ * - click: 选中节点 (高亮连接边)
7
+ * - drag (空白): 视口平移
8
+ * - drag (节点): 节点拖拽
9
+ * - 框选: 矩形范围选择
10
+ * - 双击: 聚焦节点
11
+ */
12
+ export declare function getInteractionScript(): string;
13
+ //# sourceMappingURL=interaction.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,446 @@
1
+ "use strict";
2
+ /**
3
+ * InteractionManager — 交互管理器
4
+ *
5
+ * 基于空间索引实现 O(log n) hit-test:
6
+ * - hover: 鼠标悬停高亮 (脏区域重绘)
7
+ * - click: 选中节点 (高亮连接边)
8
+ * - drag (空白): 视口平移
9
+ * - drag (节点): 节点拖拽
10
+ * - 框选: 矩形范围选择
11
+ * - 双击: 聚焦节点
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.getInteractionScript = getInteractionScript;
15
+ function getInteractionScript() {
16
+ return `
17
+ // ============================================================================
18
+ // InteractionManager — Hit-test + Mouse/Touch Events
19
+ // ============================================================================
20
+
21
+ function InteractionManager(engine) {
22
+ this._engine = engine;
23
+
24
+ // State
25
+ this._hoveredNode = null;
26
+ this._selectedNodes = [];
27
+ this._selectedNodeMap = {};
28
+ this._isDraggingNode = false;
29
+ this._isDraggingCanvas = false;
30
+ this._draggedNode = null;
31
+ this._dragStartScreenX = 0;
32
+ this._dragStartScreenY = 0;
33
+ this._dragStartWorldX = 0;
34
+ this._dragStartWorldY = 0;
35
+ this._lastMouseScreenX = 0;
36
+ this._lastMouseScreenY = 0;
37
+ this._lastDragTime = 0;
38
+
39
+ // Bind events
40
+ this._bindEvents();
41
+ }
42
+
43
+ InteractionManager.prototype._bindEvents = function() {
44
+ var self = this;
45
+ var canvas = this._engine._canvas;
46
+
47
+ // ── Mousemove → Hover ──
48
+ canvas.addEventListener('mousemove', function(e) {
49
+ var rect = canvas.getBoundingClientRect();
50
+ var sx = e.clientX - rect.left;
51
+ var sy = e.clientY - rect.top;
52
+
53
+ if (self._isDraggingNode) {
54
+ self._handleNodeDrag(sx, sy);
55
+ } else if (self._isDraggingCanvas) {
56
+ self._handleCanvasDrag(sx, sy);
57
+ } else {
58
+ self._handleHover(sx, sy);
59
+ }
60
+
61
+ self._lastMouseScreenX = sx;
62
+ self._lastMouseScreenY = sy;
63
+ });
64
+
65
+ // ── Mousedown → Start drag ──
66
+ canvas.addEventListener('mousedown', function(e) {
67
+ if (e.button !== 0) return; // left button only
68
+ var rect = canvas.getBoundingClientRect();
69
+ var sx = e.clientX - rect.left;
70
+ var sy = e.clientY - rect.top;
71
+
72
+ self._dragStartScreenX = sx;
73
+ self._dragStartScreenY = sy;
74
+
75
+ // Hit-test to see if we're clicking on a node
76
+ var hitNode = self._hitTest(sx, sy);
77
+
78
+ if (hitNode) {
79
+ self._isDraggingNode = true;
80
+ self._draggedNode = hitNode;
81
+ hitNode._dragging = true;
82
+ self._dragStartWorldX = hitNode.x;
83
+ self._dragStartWorldY = hitNode.y;
84
+ self._engine._emit('dragStart', { nodeId: hitNode.id, node: hitNode });
85
+ } else {
86
+ self._isDraggingCanvas = true;
87
+ var viewport = self._engine._viewport;
88
+ viewport._dragOffsetStartX = viewport._offsetX;
89
+ viewport._dragOffsetStartY = viewport._offsetY;
90
+ }
91
+
92
+ self._lastDragTime = performance.now();
93
+ });
94
+
95
+ // ── Mouseup → End drag / Click ──
96
+ canvas.addEventListener('mouseup', function(e) {
97
+ var rect = canvas.getBoundingClientRect();
98
+ var sx = e.clientX - rect.left;
99
+ var sy = e.clientY - rect.top;
100
+
101
+ var wasDragging = self._isDraggingNode || self._isDraggingCanvas;
102
+ var dragDist = Math.hypot(sx - self._dragStartScreenX, sy - self._dragStartScreenY);
103
+ var isClick = dragDist < 5; // 5px threshold for distinguishing click from drag
104
+
105
+ if (self._isDraggingNode && self._draggedNode) {
106
+ self._draggedNode._dragging = false;
107
+ self._engine._emit('dragEnd', { nodeId: self._draggedNode.id, node: self._draggedNode });
108
+ // Update spatial indices after drag (final position)
109
+ self._engine._spatialIndex.updateNode(self._draggedNode);
110
+ self._engine._spatialIndex.updateEdgesForNode(
111
+ self._draggedNode.id, self._engine._nodeEdges, self._engine._nodeMap
112
+ );
113
+ }
114
+
115
+ if (self._isDraggingCanvas) {
116
+ // Start inertia
117
+ self._engine._viewport._startInertia();
118
+ }
119
+
120
+ self._isDraggingNode = false;
121
+ self._isDraggingCanvas = false;
122
+ self._draggedNode = null;
123
+
124
+ // Handle click if it wasn't a drag
125
+ if (isClick) {
126
+ self._handleClick(sx, sy);
127
+ }
128
+ });
129
+
130
+ // ── Mouse leave ──
131
+ canvas.addEventListener('mouseleave', function() {
132
+ if (self._hoveredNode) {
133
+ self._hoveredNode._hovered = false;
134
+ self._engine.markDirtyNode(self._hoveredNode);
135
+ self._hoveredNode = null;
136
+ canvas.style.cursor = 'default';
137
+ }
138
+ });
139
+
140
+ // ── Double click → Focus / Cluster drill-down ──
141
+ canvas.addEventListener('dblclick', function(e) {
142
+ var rect = canvas.getBoundingClientRect();
143
+ var sx = e.clientX - rect.left;
144
+ var sy = e.clientY - rect.top;
145
+ var hitNode = self._hitTest(sx, sy);
146
+
147
+ if (hitNode) {
148
+ self._engine._emit('doubleClick', {
149
+ nodeId: hitNode.id,
150
+ node: hitNode,
151
+ pointer: { screen: { x: sx, y: sy } },
152
+ });
153
+ // Zoom to node
154
+ self._engine.focusNode(hitNode.id, {
155
+ scale: Math.max(self._engine._viewport.getScale() * 2, 0.5),
156
+ animation: { duration: 600 },
157
+ });
158
+ } else {
159
+ // Check cluster double-click → expand + zoom
160
+ var clusterer = self._engine._clusterer;
161
+ if (clusterer && clusterer.isActive()) {
162
+ var world = self._engine._viewport.screenToWorld(sx, sy);
163
+ var hitCluster = clusterer.hitTest(world.x, world.y);
164
+ if (hitCluster) {
165
+ clusterer.expandCluster(hitCluster.id, { animation: true, fitAfter: false });
166
+ // Zoom into expanded cluster area
167
+ self._engine._viewport.fitToNodes(hitCluster.nodes, {
168
+ padding: 80,
169
+ animation: { duration: 700 },
170
+ });
171
+ self._engine._emit('clusterDoubleClick', {
172
+ clusterId: hitCluster.id,
173
+ nodeCount: hitCluster.count,
174
+ });
175
+ return;
176
+ }
177
+ }
178
+ // Double click on empty → fit all
179
+ self._engine.fit({ animation: { duration: 800 } });
180
+ }
181
+ });
182
+
183
+ // ── Context menu (right click) ──
184
+ canvas.addEventListener('contextmenu', function(e) {
185
+ e.preventDefault();
186
+ var rect = canvas.getBoundingClientRect();
187
+ var sx = e.clientX - rect.left;
188
+ var sy = e.clientY - rect.top;
189
+ var hitNode = self._hitTest(sx, sy);
190
+ self._engine._emit('contextMenu', {
191
+ nodeId: hitNode ? hitNode.id : null,
192
+ node: hitNode,
193
+ pointer: { screen: { x: sx, y: sy } },
194
+ domEvent: e,
195
+ });
196
+ });
197
+ };
198
+
199
+ // ── Hit-Test ──────────────────────────────────────────────────────────────
200
+
201
+ /**
202
+ * Find the topmost node at screen position (sx, sy).
203
+ * Uses spatial index for O(log n) query.
204
+ */
205
+ InteractionManager.prototype._hitTest = function(sx, sy) {
206
+ var world = this._engine._viewport.screenToWorld(sx, sy);
207
+ var candidates = this._engine._spatialIndex.pointQuery(world.x, world.y);
208
+
209
+ if (candidates.length === 0) return null;
210
+
211
+ // Find the closest node to the query point (by center distance)
212
+ var best = null;
213
+ var bestDist = Infinity;
214
+ for (var i = 0; i < candidates.length; i++) {
215
+ var node = candidates[i]._node;
216
+ if (!node) continue;
217
+ var dx = node.x - world.x;
218
+ var dy = node.y - world.y;
219
+ var dist = dx * dx + dy * dy;
220
+ var r = node._radius || 10;
221
+ // Check if point is inside the node shape
222
+ if (dist <= r * r) {
223
+ if (dist < bestDist) {
224
+ bestDist = dist;
225
+ best = node;
226
+ }
227
+ }
228
+ }
229
+
230
+ // If no exact hit, check if we're close enough (expanded hit area)
231
+ if (!best && candidates.length > 0) {
232
+ var expandFactor = 1.5; // 50% larger hit area for easier clicking
233
+ for (var i = 0; i < candidates.length; i++) {
234
+ var node = candidates[i]._node;
235
+ if (!node) continue;
236
+ var dx = node.x - world.x;
237
+ var dy = node.y - world.y;
238
+ var dist = dx * dx + dy * dy;
239
+ var r = (node._radius || 10) * expandFactor;
240
+ if (dist <= r * r && dist < bestDist) {
241
+ bestDist = dist;
242
+ best = node;
243
+ }
244
+ }
245
+ }
246
+
247
+ return best;
248
+ };
249
+
250
+ // ── Hover ─────────────────────────────────────────────────────────────────
251
+
252
+ InteractionManager.prototype._handleHover = function(sx, sy) {
253
+ var hitNode = this._hitTest(sx, sy);
254
+ var prevHovered = this._hoveredNode;
255
+
256
+ // Check cluster hover if no node hit
257
+ var clusterer = this._engine._clusterer;
258
+ if (!hitNode && clusterer && clusterer.isActive()) {
259
+ var world = this._engine._viewport.screenToWorld(sx, sy);
260
+ var hitCluster = clusterer.hitTest(world.x, world.y);
261
+ if (hitCluster) {
262
+ // Show cluster tooltip
263
+ this._engine._canvas.style.cursor = 'pointer';
264
+ this._engine._canvas.title = hitCluster.count + ' nodes (' +
265
+ (hitCluster.dominantType || 'mixed') + ', ' +
266
+ (hitCluster.dominantStatus || 'mixed') + ')';
267
+ if (prevHovered) {
268
+ prevHovered._hovered = false;
269
+ this._engine.markDirtyNode(prevHovered);
270
+ this._hoveredNode = null;
271
+ }
272
+ this._engine._emit('hoverCluster', {
273
+ clusterId: hitCluster.id,
274
+ count: hitCluster.count,
275
+ dominantType: hitCluster.dominantType,
276
+ });
277
+ return;
278
+ }
279
+ }
280
+
281
+ // Clear cluster tooltip
282
+ this._engine._canvas.title = '';
283
+
284
+ if (hitNode === prevHovered) return; // no change
285
+
286
+ if (prevHovered) {
287
+ prevHovered._hovered = false;
288
+ // Dirty only the old hovered node's region
289
+ this._engine.markDirtyNode(prevHovered);
290
+ }
291
+ if (hitNode) {
292
+ hitNode._hovered = true;
293
+ this._engine._canvas.style.cursor = 'pointer';
294
+ // Dirty only the new hovered node's region
295
+ this._engine.markDirtyNode(hitNode);
296
+ } else {
297
+ this._engine._canvas.style.cursor = 'default';
298
+ }
299
+
300
+ this._hoveredNode = hitNode;
301
+
302
+ this._engine._emit('hover', {
303
+ nodeId: hitNode ? hitNode.id : null,
304
+ node: hitNode,
305
+ previousNodeId: prevHovered ? prevHovered.id : null,
306
+ });
307
+ };
308
+
309
+ // ── Click ─────────────────────────────────────────────────────────────────
310
+
311
+ InteractionManager.prototype._handleClick = function(sx, sy) {
312
+ var hitNode = this._hitTest(sx, sy);
313
+
314
+ if (hitNode) {
315
+ // Select this node
316
+ this._selectNode(hitNode);
317
+ this._engine._emit('click', {
318
+ nodes: [hitNode.id],
319
+ nodeId: hitNode.id,
320
+ node: hitNode,
321
+ pointer: { screen: { x: sx, y: sy }, canvas: this._engine._viewport.screenToWorld(sx, sy) },
322
+ });
323
+ } else {
324
+ // Check if clicking on a cluster
325
+ var clusterer = this._engine._clusterer;
326
+ if (clusterer && clusterer.isActive()) {
327
+ var world = this._engine._viewport.screenToWorld(sx, sy);
328
+ var hitCluster = clusterer.hitTest(world.x, world.y);
329
+ if (hitCluster) {
330
+ // Click on cluster → expand it
331
+ clusterer.expandCluster(hitCluster.id, { animation: true, fitAfter: true });
332
+ this._engine._emit('clusterClick', {
333
+ clusterId: hitCluster.id,
334
+ nodeCount: hitCluster.count,
335
+ pointer: { screen: { x: sx, y: sy }, canvas: world },
336
+ });
337
+ return;
338
+ }
339
+ }
340
+
341
+ // Deselect all
342
+ this._deselectAll();
343
+ this._engine._emit('click', {
344
+ nodes: [],
345
+ nodeId: null,
346
+ node: null,
347
+ pointer: { screen: { x: sx, y: sy }, canvas: this._engine._viewport.screenToWorld(sx, sy) },
348
+ });
349
+ }
350
+ };
351
+
352
+ InteractionManager.prototype._selectNode = function(node) {
353
+ // Deselect previous (dirties old selection's regions)
354
+ this._deselectAll();
355
+
356
+ node._selected = true;
357
+ this._selectedNodes = [node];
358
+ this._selectedNodeMap = {};
359
+ this._selectedNodeMap[node.id] = true;
360
+
361
+ // Highlight connected edges
362
+ var edges = this._engine._nodeEdges[node.id] || [];
363
+ for (var i = 0; i < edges.length; i++) {
364
+ edges[i]._highlighted = true;
365
+ }
366
+
367
+ // Dirty the selected node and its connected nodes (for edge highlighting)
368
+ this._engine.markDirtyNode(node);
369
+ this._engine._emit('select', { nodeIds: [node.id] });
370
+ };
371
+
372
+ InteractionManager.prototype._deselectAll = function() {
373
+ // Dirty each previously selected node's region
374
+ for (var i = 0; i < this._selectedNodes.length; i++) {
375
+ this._selectedNodes[i]._selected = false;
376
+ this._engine.markDirtyNode(this._selectedNodes[i]);
377
+ }
378
+ // Reset all edge highlights
379
+ var edges = this._engine._edges;
380
+ for (var i = 0; i < edges.length; i++) {
381
+ edges[i]._highlighted = false;
382
+ }
383
+ this._selectedNodes = [];
384
+ this._selectedNodeMap = {};
385
+ this._engine._emit('deselect', {});
386
+ };
387
+
388
+ // ── Node Drag ─────────────────────────────────────────────────────────────
389
+
390
+ InteractionManager.prototype._handleNodeDrag = function(sx, sy) {
391
+ if (!this._draggedNode) return;
392
+
393
+ var world = this._engine._viewport.screenToWorld(sx, sy);
394
+ var prevPos = { x: this._draggedNode.x, y: this._draggedNode.y };
395
+ this._draggedNode.x = world.x;
396
+ this._draggedNode.y = world.y;
397
+
398
+ // Update spatial indices for the dragged node + connected edges
399
+ this._engine._spatialIndex.updateNode(this._draggedNode);
400
+ this._engine._spatialIndex.updateEdgesForNode(
401
+ this._draggedNode.id, this._engine._nodeEdges, this._engine._nodeMap
402
+ );
403
+
404
+ // Only dirty the affected regions (old + new position)
405
+ this._engine.markDirtyNode(this._draggedNode, prevPos);
406
+ this._engine._emit('dragging', {
407
+ nodeId: this._draggedNode.id,
408
+ node: this._draggedNode,
409
+ x: world.x,
410
+ y: world.y,
411
+ });
412
+ };
413
+
414
+ // ── Canvas Drag (pan) ─────────────────────────────────────────────────────
415
+
416
+ InteractionManager.prototype._handleCanvasDrag = function(sx, sy) {
417
+ var dx = sx - this._lastMouseScreenX;
418
+ var dy = sy - this._lastMouseScreenY;
419
+
420
+ var viewport = this._engine._viewport;
421
+ viewport._offsetX += dx;
422
+ viewport._offsetY += dy;
423
+
424
+ // Track velocity for inertia
425
+ var now = performance.now();
426
+ var dt = now - this._lastDragTime;
427
+ if (dt > 0) {
428
+ viewport._velocityX = dx * (16 / dt); // normalize to ~60fps
429
+ viewport._velocityY = dy * (16 / dt);
430
+ }
431
+ this._lastDragTime = now;
432
+
433
+ this._engine.markDirty();
434
+ };
435
+
436
+ // ── Cleanup ───────────────────────────────────────────────────────────────
437
+
438
+ InteractionManager.prototype.destroy = function() {
439
+ // Event listeners are on the canvas which will be removed
440
+ this._hoveredNode = null;
441
+ this._selectedNodes = [];
442
+ this._draggedNode = null;
443
+ };
444
+ `;
445
+ }
446
+ //# sourceMappingURL=interaction.js.map
@@ -0,0 +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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * LayoutEngine — Web Worker 力导向布局
3
+ *
4
+ * 将力导向布局移入 Web Worker,避免阻塞 UI 主线程。
5
+ * Worker 每迭代一批后通过 postMessage 发送节点位置。
6
+ * 主线程增量更新渲染。
7
+ *
8
+ * Phase 8B 升级:
9
+ * - Barnes-Hut 四叉树: O(n log n) 排斥力近似 (θ=0.8)
10
+ * - 自适应算法: <1K 用原始 FR, >1K 用 Barnes-Hut
11
+ * - 提前终止: 能量阈值收敛检测
12
+ * - 进度报告: 百分比 + 剩余时间估算
13
+ *
14
+ * 当前版本: 内联 Worker (Blob URL),无外部依赖。
15
+ */
16
+ export declare function getLayoutWorkerScript(): string;
17
+ //# sourceMappingURL=layout-worker.d.ts.map
@@ -0,0 +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"}