aifastdb-devplan 1.3.9 → 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,682 @@
1
+ "use strict";
2
+ /**
3
+ * RenderPipeline — 视口裁剪 + 脏区域局部渲染
4
+ *
5
+ * 借鉴 LeaferJS Renderer 架构:
6
+ * - 全量渲染 (fullRender): 清空并重绘所有可见内容
7
+ * - 局部渲染 (partRender): 仅重绘脏区域内的节点/边
8
+ * - 视口裁剪: 只遍历 SpatialIndex.search(viewport) 返回的节点
9
+ *
10
+ * 渲染顺序: 背景 → 边 → 节点 → 标签 → 覆盖层 (panel/debug)
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.getRendererScript = getRendererScript;
14
+ function getRendererScript() {
15
+ return `
16
+ // ============================================================================
17
+ // RenderPipeline — Viewport Culling + Dirty Rect Rendering
18
+ // ============================================================================
19
+
20
+ function RenderPipeline(engine) {
21
+ this._engine = engine;
22
+ this._bgColor = engine._options.backgroundColor || '#111827';
23
+
24
+ // Cached visible items (refreshed each frame or on viewport change)
25
+ this._visibleNodes = [];
26
+ this._visibleEdges = [];
27
+ this._lastViewportKey = ''; // For detecting viewport changes
28
+ }
29
+
30
+ /**
31
+ * Main render entry point — called by GraphCanvas._renderFrame().
32
+ * @param {CanvasRenderingContext2D} ctx
33
+ * @param {boolean} fullRedraw — true = clear + redraw everything
34
+ * @param {Array} dirtyRects — [{x,y,w,h}] in world coords (merged)
35
+ */
36
+ RenderPipeline.prototype.render = function(ctx, fullRedraw, dirtyRects) {
37
+ var engine = this._engine;
38
+
39
+ // Partial render path: only redraw dirty regions
40
+ if (!fullRedraw && dirtyRects && dirtyRects.length > 0) {
41
+ this._partRender(ctx, dirtyRects);
42
+ return;
43
+ }
44
+
45
+ // Full render path
46
+ this._fullRender(ctx);
47
+ };
48
+
49
+ /**
50
+ * Full render — clears everything and redraws all visible content.
51
+ */
52
+ RenderPipeline.prototype._fullRender = function(ctx) {
53
+ var engine = this._engine;
54
+ var viewport = engine._viewport;
55
+ var lod = engine._lod;
56
+
57
+ // ── 1. Clear ──
58
+ viewport.resetTransform(ctx);
59
+ ctx.fillStyle = this._bgColor;
60
+ ctx.fillRect(0, 0, engine._width, engine._height);
61
+
62
+ // ── 2. Query visible nodes via spatial index ──
63
+ var queryBounds = this._getExpandedViewportBounds();
64
+
65
+ var visibleItems = engine._spatialIndex.search(queryBounds);
66
+ this._visibleNodes = [];
67
+ for (var i = 0; i < visibleItems.length; i++) {
68
+ var item = visibleItems[i];
69
+ if (item._node && item._node._visible) {
70
+ this._visibleNodes.push(item._node);
71
+ }
72
+ }
73
+
74
+ // ── 3. Collect visible edges via edge spatial index (O(log E) instead of O(E)) ──
75
+ this._visibleEdges = [];
76
+ var edgeItems = engine._spatialIndex.searchEdges(queryBounds);
77
+ for (var i = 0; i < edgeItems.length; i++) {
78
+ var ei = edgeItems[i];
79
+ if (ei._edge && ei._edge._visible) {
80
+ this._visibleEdges.push(ei._edge);
81
+ }
82
+ }
83
+
84
+ // Update metrics
85
+ engine._metrics.visibleNodes = this._visibleNodes.length;
86
+ engine._metrics.visibleEdges = this._visibleEdges.length;
87
+
88
+ // ── 4. Determine LOD level ──
89
+ var lodLevel = lod.getLevel(viewport.getScale());
90
+
91
+ // ── 5. Apply viewport transform ──
92
+ viewport.applyTransform(ctx);
93
+
94
+ // ── 5b. Cluster auto-management ──
95
+ var clusterer = engine._clusterer;
96
+ var clusterActive = clusterer && clusterer.isActive();
97
+ if (clusterer && clusterer._enabled) {
98
+ if (clusterActive && clusterer.needsRebuild()) {
99
+ clusterer.rebuild();
100
+ // Re-query visible nodes after cluster hides some
101
+ visibleItems = engine._spatialIndex.search(queryBounds);
102
+ this._visibleNodes = [];
103
+ for (var i = 0; i < visibleItems.length; i++) {
104
+ var item = visibleItems[i];
105
+ if (item._node && item._node._visible) {
106
+ this._visibleNodes.push(item._node);
107
+ }
108
+ }
109
+ } else if (!clusterActive && clusterer._clusters.length > 0) {
110
+ // Scale went above threshold → auto-expand all clusters
111
+ clusterer._clearAllClusters();
112
+ engine._spatialIndex.buildFromNodes(engine._nodes);
113
+ engine._spatialIndex.buildEdgeIndex(engine._edges, engine._nodeMap);
114
+ visibleItems = engine._spatialIndex.search(queryBounds);
115
+ this._visibleNodes = [];
116
+ for (var i = 0; i < visibleItems.length; i++) {
117
+ var item = visibleItems[i];
118
+ if (item._node && item._node._visible) {
119
+ this._visibleNodes.push(item._node);
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ // ── 6. Draw edges (only between visible nodes) ──
126
+ this._drawEdges(ctx, lodLevel);
127
+
128
+ // ── 7. Draw nodes ──
129
+ this._drawNodes(ctx, lodLevel);
130
+
131
+ // ── 7b. Draw clusters (if active) ──
132
+ if (clusterActive) {
133
+ this._drawClusters(ctx, lodLevel);
134
+ }
135
+
136
+ // ── 8. Draw labels (if LOD permits) ──
137
+ if (lodLevel >= 1) {
138
+ this._drawLabels(ctx, lodLevel);
139
+ }
140
+
141
+ // ── 9. UI Overlays (in screen space) ──
142
+ viewport.resetTransform(ctx);
143
+ this._drawOverlays(ctx);
144
+
145
+ // ── 10. Emit afterRender event (for afterDrawing compat) ──
146
+ viewport.applyTransform(ctx);
147
+ engine._emit('afterRender', { ctx: ctx });
148
+ viewport.resetTransform(ctx);
149
+ };
150
+
151
+ /**
152
+ * Partial render — only redraws dirty regions (LeaferJS-inspired partRender + clipRender).
153
+ * For each dirty rect: clip → clear → query affected nodes/edges → redraw → restore.
154
+ */
155
+ RenderPipeline.prototype._partRender = function(ctx, dirtyRects) {
156
+ var engine = this._engine;
157
+ var viewport = engine._viewport;
158
+ var lod = engine._lod;
159
+ var lodLevel = lod.getLevel(viewport.getScale());
160
+ var scale = viewport.getScale();
161
+ var pr = engine._options.pixelRatio;
162
+
163
+ for (var r = 0; r < dirtyRects.length; r++) {
164
+ var rect = dirtyRects[r];
165
+
166
+ // Expand dirty rect slightly to avoid edge artifacts
167
+ var spread = 4 / scale;
168
+ var clipRect = {
169
+ x: rect.x - spread,
170
+ y: rect.y - spread,
171
+ w: rect.w + spread * 2,
172
+ h: rect.h + spread * 2,
173
+ };
174
+
175
+ // Convert dirty rect to query bounds for spatial index
176
+ var queryBounds = {
177
+ minX: clipRect.x,
178
+ minY: clipRect.y,
179
+ maxX: clipRect.x + clipRect.w,
180
+ maxY: clipRect.y + clipRect.h,
181
+ };
182
+
183
+ // Query nodes in dirty region
184
+ var nodeItems = engine._spatialIndex.search(queryBounds);
185
+ var dirtyNodes = [];
186
+ for (var i = 0; i < nodeItems.length; i++) {
187
+ if (nodeItems[i]._node && nodeItems[i]._node._visible) {
188
+ dirtyNodes.push(nodeItems[i]._node);
189
+ }
190
+ }
191
+
192
+ // Query edges in dirty region via edge spatial index
193
+ var edgeItems = engine._spatialIndex.searchEdges(queryBounds);
194
+ var dirtyEdges = [];
195
+ for (var i = 0; i < edgeItems.length; i++) {
196
+ if (edgeItems[i]._edge && edgeItems[i]._edge._visible) {
197
+ dirtyEdges.push(edgeItems[i]._edge);
198
+ }
199
+ }
200
+
201
+ // Also include edges connected to dirty nodes (may extend beyond dirty rect)
202
+ var dirtyNodeIds = {};
203
+ for (var i = 0; i < dirtyNodes.length; i++) {
204
+ dirtyNodeIds[dirtyNodes[i].id] = true;
205
+ }
206
+ var connectedEdges = engine._edges;
207
+ for (var i = 0; i < connectedEdges.length; i++) {
208
+ var e = connectedEdges[i];
209
+ if (!e._visible) continue;
210
+ if ((dirtyNodeIds[e.from] || dirtyNodeIds[e.to]) && !this._edgeInList(e, dirtyEdges)) {
211
+ dirtyEdges.push(e);
212
+ }
213
+ }
214
+
215
+ // ── clipRender: clip to dirty rect, clear, redraw ──
216
+ ctx.save();
217
+
218
+ // Apply viewport transform
219
+ viewport.applyTransform(ctx);
220
+
221
+ // Clip to dirty rect (world coordinates, transformed by viewport)
222
+ ctx.beginPath();
223
+ ctx.rect(clipRect.x, clipRect.y, clipRect.w, clipRect.h);
224
+ ctx.clip();
225
+
226
+ // Clear the dirty region
227
+ ctx.fillStyle = this._bgColor;
228
+ ctx.fillRect(clipRect.x, clipRect.y, clipRect.w, clipRect.h);
229
+
230
+ // Draw affected edges
231
+ var savedVisibleEdges = this._visibleEdges;
232
+ this._visibleEdges = dirtyEdges;
233
+ this._drawEdges(ctx, lodLevel);
234
+ this._visibleEdges = savedVisibleEdges;
235
+
236
+ // Draw affected nodes
237
+ var savedVisibleNodes = this._visibleNodes;
238
+ this._visibleNodes = dirtyNodes;
239
+ this._drawNodes(ctx, lodLevel);
240
+ if (lodLevel >= 1) {
241
+ this._drawLabels(ctx, lodLevel);
242
+ }
243
+ this._visibleNodes = savedVisibleNodes;
244
+
245
+ ctx.restore();
246
+ }
247
+
248
+ // Overlays always in screen space (redraw unconditionally — they're lightweight)
249
+ viewport.resetTransform(ctx);
250
+ this._drawOverlays(ctx);
251
+
252
+ // Emit afterRender
253
+ viewport.applyTransform(ctx);
254
+ engine._emit('afterRender', { ctx: ctx });
255
+ viewport.resetTransform(ctx);
256
+ };
257
+
258
+ /**
259
+ * Check if an edge is already in the list (by id).
260
+ */
261
+ RenderPipeline.prototype._edgeInList = function(edge, list) {
262
+ for (var i = 0; i < list.length; i++) {
263
+ if (list[i].id === edge.id) return true;
264
+ }
265
+ return false;
266
+ };
267
+
268
+ /**
269
+ * Get viewport bounds expanded with padding for edge overflow.
270
+ */
271
+ RenderPipeline.prototype._getExpandedViewportBounds = function() {
272
+ var viewport = this._engine._viewport;
273
+ var worldBounds = viewport.getWorldBounds();
274
+ var pad = 50 / viewport.getScale();
275
+ return {
276
+ minX: worldBounds.minX - pad,
277
+ minY: worldBounds.minY - pad,
278
+ maxX: worldBounds.maxX + pad,
279
+ maxY: worldBounds.maxY + pad,
280
+ };
281
+ };
282
+
283
+ // ── Edge Drawing ──────────────────────────────────────────────────────────
284
+
285
+ RenderPipeline.prototype._drawEdges = function(ctx, lodLevel) {
286
+ var engine = this._engine;
287
+ var nodeMap = engine._nodeMap;
288
+ var edges = this._visibleEdges;
289
+ var styles = engine._styles;
290
+
291
+ ctx.lineCap = 'round';
292
+
293
+ for (var i = 0; i < edges.length; i++) {
294
+ var e = edges[i];
295
+ var fromNode = nodeMap[e.from];
296
+ var toNode = nodeMap[e.to];
297
+ if (!fromNode || !toNode) continue;
298
+
299
+ var style = e._style || styles.getEdgeStyle(e);
300
+ var alpha = e._highlighted ? 1 : (lodLevel === 0 ? 0.15 : 0.4);
301
+
302
+ ctx.globalAlpha = alpha;
303
+ ctx.strokeStyle = e._highlighted ? (style.highlightColor || '#93c5fd') : (style.color || '#4b5563');
304
+ ctx.lineWidth = (style.width || 1) / engine._viewport.getScale();
305
+
306
+ // Dash pattern
307
+ if (style.dashes && lodLevel >= 1) {
308
+ var scale = engine._viewport.getScale();
309
+ var dashScale = 1 / scale;
310
+ ctx.setLineDash(style.dashes.map(function(d) { return d * dashScale; }));
311
+ } else {
312
+ ctx.setLineDash([]);
313
+ }
314
+
315
+ ctx.beginPath();
316
+
317
+ if (lodLevel === 0) {
318
+ // LOD 0: straight lines only (maximum performance)
319
+ ctx.moveTo(fromNode.x, fromNode.y);
320
+ ctx.lineTo(toNode.x, toNode.y);
321
+ } else {
322
+ // LOD 1-2: slight curve for visual separation of parallel edges
323
+ ctx.moveTo(fromNode.x, fromNode.y);
324
+ ctx.lineTo(toNode.x, toNode.y);
325
+ }
326
+
327
+ ctx.stroke();
328
+
329
+ // ── Arrow head (LOD >= 1) ──
330
+ if (lodLevel >= 1 && style.arrows) {
331
+ this._drawArrowHead(ctx, fromNode, toNode, style);
332
+ }
333
+ }
334
+
335
+ ctx.globalAlpha = 1;
336
+ ctx.setLineDash([]);
337
+ };
338
+
339
+ RenderPipeline.prototype._drawArrowHead = function(ctx, fromNode, toNode, style) {
340
+ var scale = this._engine._viewport.getScale();
341
+ var arrowSize = Math.max(4, 8 / scale);
342
+ var dx = toNode.x - fromNode.x;
343
+ var dy = toNode.y - fromNode.y;
344
+ var len = Math.sqrt(dx * dx + dy * dy);
345
+ if (len < 1) return;
346
+
347
+ // Arrow at edge of target node
348
+ var toR = toNode._radius || 10;
349
+ var ratio = (len - toR) / len;
350
+ var ax = fromNode.x + dx * ratio;
351
+ var ay = fromNode.y + dy * ratio;
352
+ var angle = Math.atan2(dy, dx);
353
+
354
+ ctx.fillStyle = ctx.strokeStyle;
355
+ ctx.beginPath();
356
+ ctx.moveTo(ax, ay);
357
+ ctx.lineTo(
358
+ ax - arrowSize * Math.cos(angle - Math.PI / 6),
359
+ ay - arrowSize * Math.sin(angle - Math.PI / 6)
360
+ );
361
+ ctx.lineTo(
362
+ ax - arrowSize * Math.cos(angle + Math.PI / 6),
363
+ ay - arrowSize * Math.sin(angle + Math.PI / 6)
364
+ );
365
+ ctx.closePath();
366
+ ctx.fill();
367
+ };
368
+
369
+ // ── Node Drawing ──────────────────────────────────────────────────────────
370
+
371
+ RenderPipeline.prototype._drawNodes = function(ctx, lodLevel) {
372
+ var nodes = this._visibleNodes;
373
+ var styles = this._engine._styles;
374
+
375
+ for (var i = 0; i < nodes.length; i++) {
376
+ var n = nodes[i];
377
+ var style = n._style || styles.getNodeStyle(n);
378
+ var r = n._radius || style.radius || 10;
379
+ n._radius = r; // cache for hit-test
380
+
381
+ // ── Selection / hover glow ──
382
+ if (n._selected || n._hovered) {
383
+ ctx.globalAlpha = 0.3;
384
+ ctx.fillStyle = n._selected ? '#6366f1' : '#818cf8';
385
+ ctx.beginPath();
386
+ ctx.arc(n.x, n.y, r + 6 / this._engine._viewport.getScale(), 0, Math.PI * 2);
387
+ ctx.fill();
388
+ ctx.globalAlpha = 1;
389
+ }
390
+
391
+ // ── Node shape ──
392
+ ctx.fillStyle = style.bgColor || '#6b7280';
393
+ ctx.strokeStyle = style.borderColor || '#4b5563';
394
+ ctx.lineWidth = (style.borderWidth || 1) / this._engine._viewport.getScale();
395
+
396
+ this._drawShape(ctx, n.x, n.y, r, style.shape || 'circle');
397
+
398
+ ctx.fill();
399
+ if (lodLevel >= 1) ctx.stroke();
400
+ }
401
+ };
402
+
403
+ RenderPipeline.prototype._drawShape = function(ctx, x, y, r, shape) {
404
+ ctx.beginPath();
405
+
406
+ switch (shape) {
407
+ case 'star':
408
+ this._drawStar(ctx, x, y, r, 5);
409
+ break;
410
+ case 'diamond':
411
+ ctx.moveTo(x, y - r);
412
+ ctx.lineTo(x + r, y);
413
+ ctx.lineTo(x, y + r);
414
+ ctx.lineTo(x - r, y);
415
+ ctx.closePath();
416
+ break;
417
+ case 'box':
418
+ case 'square':
419
+ ctx.rect(x - r, y - r * 0.7, r * 2, r * 1.4);
420
+ break;
421
+ case 'triangle':
422
+ ctx.moveTo(x, y - r);
423
+ ctx.lineTo(x + r * 0.866, y + r * 0.5);
424
+ ctx.lineTo(x - r * 0.866, y + r * 0.5);
425
+ ctx.closePath();
426
+ break;
427
+ case 'circle':
428
+ default:
429
+ ctx.arc(x, y, r, 0, Math.PI * 2);
430
+ break;
431
+ }
432
+ };
433
+
434
+ RenderPipeline.prototype._drawStar = function(ctx, cx, cy, r, points) {
435
+ var innerR = r * 0.4;
436
+ var angle = -Math.PI / 2;
437
+ var step = Math.PI / points;
438
+
439
+ ctx.moveTo(
440
+ cx + r * Math.cos(angle),
441
+ cy + r * Math.sin(angle)
442
+ );
443
+
444
+ for (var i = 0; i < points; i++) {
445
+ angle += step;
446
+ ctx.lineTo(
447
+ cx + innerR * Math.cos(angle),
448
+ cy + innerR * Math.sin(angle)
449
+ );
450
+ angle += step;
451
+ ctx.lineTo(
452
+ cx + r * Math.cos(angle),
453
+ cy + r * Math.sin(angle)
454
+ );
455
+ }
456
+
457
+ ctx.closePath();
458
+ };
459
+
460
+ // ── Label Drawing ─────────────────────────────────────────────────────────
461
+
462
+ RenderPipeline.prototype._drawLabels = function(ctx, lodLevel) {
463
+ var nodes = this._visibleNodes;
464
+ var scale = this._engine._viewport.getScale();
465
+ var styles = this._engine._styles;
466
+
467
+ for (var i = 0; i < nodes.length; i++) {
468
+ var n = nodes[i];
469
+ if (!n.label) continue;
470
+
471
+ var style = n._style || styles.getNodeStyle(n);
472
+ var fontSize = (style.fontSize || 12) / scale;
473
+
474
+ // LOD 1: only show labels for larger nodes
475
+ if (lodLevel === 1) {
476
+ var screenSize = (n._radius || 10) * scale;
477
+ if (screenSize < 6) continue; // too small on screen
478
+ }
479
+
480
+ // Truncate long labels
481
+ var label = n.label;
482
+ var maxChars = lodLevel >= 2 ? 30 : 15;
483
+ if (label.length > maxChars) label = label.substring(0, maxChars) + '…';
484
+
485
+ ctx.font = Math.max(fontSize, 2) + 'px -apple-system, sans-serif';
486
+ ctx.fillStyle = style.fontColor || '#e5e7eb';
487
+ ctx.textAlign = 'center';
488
+ ctx.textBaseline = 'middle';
489
+
490
+ var shape = style.shape || 'circle';
491
+ if (shape === 'box' || shape === 'square') {
492
+ // Label inside box
493
+ ctx.fillText(label, n.x, n.y);
494
+ } else {
495
+ // Label below node
496
+ var r = n._radius || 10;
497
+ ctx.fillText(label, n.x, n.y + r + fontSize * 0.8);
498
+ }
499
+ }
500
+ };
501
+
502
+ // ── Cluster Drawing ───────────────────────────────────────────────────────
503
+
504
+ RenderPipeline.prototype._drawClusters = function(ctx, lodLevel) {
505
+ var engine = this._engine;
506
+ var clusterer = engine._clusterer;
507
+ if (!clusterer) return;
508
+
509
+ var clusters = clusterer.getClusters();
510
+ var viewport = engine._viewport;
511
+ var scale = viewport.getScale();
512
+ var worldBounds = viewport.getWorldBounds();
513
+
514
+ for (var i = 0; i < clusters.length; i++) {
515
+ var c = clusters[i];
516
+
517
+ // Viewport culling for clusters
518
+ if (c.x + c.radius < worldBounds.minX || c.x - c.radius > worldBounds.maxX ||
519
+ c.y + c.radius < worldBounds.minY || c.y - c.radius > worldBounds.maxY) {
520
+ continue;
521
+ }
522
+
523
+ var r = c.radius;
524
+
525
+ // ── Outer glow ──
526
+ ctx.globalAlpha = 0.2;
527
+ ctx.fillStyle = c.bgColor || '#6366f1';
528
+ ctx.beginPath();
529
+ ctx.arc(c.x, c.y, r * 1.3, 0, Math.PI * 2);
530
+ ctx.fill();
531
+
532
+ // ── Main circle ──
533
+ ctx.globalAlpha = 0.85;
534
+ ctx.fillStyle = c.bgColor || '#6366f1';
535
+ ctx.beginPath();
536
+ ctx.arc(c.x, c.y, r, 0, Math.PI * 2);
537
+ ctx.fill();
538
+
539
+ // ── Border ──
540
+ ctx.globalAlpha = 1;
541
+ ctx.strokeStyle = c.borderColor || '#4f46e5';
542
+ ctx.lineWidth = 2 / scale;
543
+ ctx.stroke();
544
+
545
+ // ── Status pie chart (mini) — only at higher LOD ──
546
+ if (c.statusBreakdown && lodLevel >= 0) {
547
+ this._drawStatusPie(ctx, c);
548
+ }
549
+
550
+ // ── Count label ──
551
+ var fontSize = Math.max(r * 0.6, 8 / scale);
552
+ ctx.font = 'bold ' + fontSize + 'px -apple-system, sans-serif';
553
+ ctx.fillStyle = c.fontColor || '#fff';
554
+ ctx.textAlign = 'center';
555
+ ctx.textBaseline = 'middle';
556
+ ctx.fillText(c.count.toString(), c.x, c.y);
557
+ }
558
+
559
+ // Update cluster count in metrics
560
+ engine._metrics.visibleClusters = clusters.length;
561
+ };
562
+
563
+ /**
564
+ * Draw a mini status pie chart ring around the cluster.
565
+ */
566
+ RenderPipeline.prototype._drawStatusPie = function(ctx, cluster) {
567
+ var breakdown = cluster.statusBreakdown;
568
+ if (!breakdown) return;
569
+
570
+ var STATUS_COLORS = {
571
+ completed: '#059669',
572
+ in_progress: '#7c3aed',
573
+ pending: '#4b5563',
574
+ cancelled: '#92400e',
575
+ };
576
+
577
+ var total = cluster.count;
578
+ var r = cluster.radius;
579
+ var ringR = r * 1.15;
580
+ var ringWidth = Math.max(r * 0.15, 2 / this._engine._viewport.getScale());
581
+ var startAngle = -Math.PI / 2;
582
+
583
+ ctx.lineWidth = ringWidth;
584
+ var keys = Object.keys(breakdown);
585
+ for (var i = 0; i < keys.length; i++) {
586
+ var status = keys[i];
587
+ var count = breakdown[status];
588
+ var sweepAngle = (count / total) * Math.PI * 2;
589
+
590
+ ctx.globalAlpha = 0.8;
591
+ ctx.strokeStyle = STATUS_COLORS[status] || '#9ca3af';
592
+ ctx.beginPath();
593
+ ctx.arc(cluster.x, cluster.y, ringR, startAngle, startAngle + sweepAngle);
594
+ ctx.stroke();
595
+
596
+ startAngle += sweepAngle;
597
+ }
598
+
599
+ ctx.globalAlpha = 1;
600
+ };
601
+
602
+ // ── Overlay Drawing (screen space) ────────────────────────────────────────
603
+
604
+ RenderPipeline.prototype._drawOverlays = function(ctx) {
605
+ var engine = this._engine;
606
+ if (!engine._options.debugMode) return;
607
+
608
+ // Debug info panel
609
+ var m = engine._metrics;
610
+ var clusterer = engine._clusterer;
611
+ var clusterInfo = clusterer && clusterer.isActive() ? ' [C:' + (m.visibleClusters || 0) + ']' : '';
612
+ ctx.fillStyle = 'rgba(0,0,0,0.7)';
613
+ ctx.fillRect(8, 8, 260, 132);
614
+ ctx.fillStyle = '#10b981';
615
+ ctx.font = '11px monospace';
616
+ ctx.textAlign = 'left';
617
+ ctx.textBaseline = 'top';
618
+ ctx.fillText('FPS: ' + m.fps + (engine._sleeping ? ' (sleeping)' : '') + clusterInfo, 14, 14);
619
+ ctx.fillText('Visible: ' + m.visibleNodes + '/' + m.totalNodes + ' nodes', 14, 28);
620
+ ctx.fillText('Edges: ' + m.visibleEdges + '/' + m.totalEdges, 14, 42);
621
+ ctx.fillText('Render: ' + m.lastRenderMs + 'ms', 14, 56);
622
+ ctx.fillText('Scale: ' + engine._viewport.getScale().toFixed(4), 14, 70);
623
+ ctx.fillText('Culled: ' + (m.totalNodes - m.visibleNodes) + ' nodes', 14, 84);
624
+ ctx.fillText('Idle: ' + engine._idleFrames + '/' + engine._idleThreshold, 14, 98);
625
+ if (clusterer && clusterer._enabled) {
626
+ ctx.fillStyle = '#f59e0b';
627
+ ctx.fillText('Clusters: ' + (clusterer._clusters.length) + (clusterer.isActive() ? ' (active)' : ' (off)'), 14, 112);
628
+ }
629
+ if (m.layoutAlgorithm) {
630
+ ctx.fillStyle = '#38bdf8';
631
+ ctx.fillText('Layout: ' + m.layoutAlgorithm, 14, 126);
632
+ }
633
+
634
+ // ── Layout progress bar (shown during stabilization) ──
635
+ this._drawLayoutProgress(ctx);
636
+ };
637
+ /**
638
+ * Draw layout progress bar at top of canvas (screen space).
639
+ */
640
+ RenderPipeline.prototype._drawLayoutProgress = function(ctx) {
641
+ var engine = this._engine;
642
+ var layout = engine._layoutEngine;
643
+ if (!layout || !layout._isRunning) return;
644
+
645
+ var m = engine._metrics;
646
+ var progress = m.layoutProgress || 0;
647
+ var algo = m.layoutAlgorithm || '';
648
+ var eta = m.layoutEta || 0;
649
+
650
+ var barWidth = engine._width * 0.6;
651
+ var barHeight = 18;
652
+ var barX = (engine._width - barWidth) / 2;
653
+ var barY = engine._height - 40;
654
+
655
+ // Background
656
+ ctx.fillStyle = 'rgba(0,0,0,0.8)';
657
+ ctx.fillRect(barX - 8, barY - 6, barWidth + 16, barHeight + 28);
658
+
659
+ // Progress bar background
660
+ ctx.fillStyle = '#374151';
661
+ ctx.fillRect(barX, barY, barWidth, barHeight);
662
+
663
+ // Progress bar fill
664
+ var fillWidth = barWidth * (progress / 100);
665
+ ctx.fillStyle = progress < 80 ? '#6366f1' : '#10b981';
666
+ ctx.fillRect(barX, barY, fillWidth, barHeight);
667
+
668
+ // Progress text
669
+ ctx.fillStyle = '#fff';
670
+ ctx.font = '11px -apple-system, sans-serif';
671
+ ctx.textAlign = 'center';
672
+ ctx.textBaseline = 'middle';
673
+ ctx.fillText(
674
+ 'Layout: ' + algo + ' ' + progress + '%' +
675
+ (eta > 1000 ? ' ETA: ' + Math.round(eta / 1000) + 's' : ''),
676
+ barX + barWidth / 2,
677
+ barY + barHeight / 2
678
+ );
679
+ };
680
+ `;
681
+ }
682
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAEH,8CA2pBC;AA3pBD,SAAgB,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAypBR,CAAC;AACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * SpatialIndex — 内联 R-tree 空间索引 (无外部依赖)
3
+ *
4
+ * 轻量级 R-tree 实现,支持:
5
+ * - bulk insert (STR 批量构建)
6
+ * - viewport search (矩形范围查询)
7
+ * - point query (hit-test)
8
+ * - 动态 insert/remove/update
9
+ *
10
+ * 基于简化版 rbush 算法,优化为图谱节点场景。
11
+ */
12
+ export declare function getSpatialIndexScript(): string;
13
+ //# sourceMappingURL=spatial-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spatial-index.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/spatial-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,qBAAqB,IAAI,MAAM,CAkd9C"}