dsh-codegraph-visualizer 0.3.0 → 0.3.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.
- package/dist/{CodeGraphAdapter-eyrhs0K0.js → CodeGraphAdapter-BNIK0xHU.js} +3 -3
- package/dist/{CodeGraphAdapter-eyrhs0K0.js.map → CodeGraphAdapter-BNIK0xHU.js.map} +1 -1
- package/dist/{CodeGraphAdapter-C8JvcS7m.js → CodeGraphAdapter-CXj1hbdm.js} +3 -3
- package/dist/client/index.css +1103 -987
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.esm.js +432 -189
- package/dist/client/index.js.map +1 -1
- package/dist/client.js +1535 -1176
- package/dist/{codeGraphDb-Bg7bWud2.js → codeGraphDb-B-vpZmpm.js} +2 -2
- package/dist/{codeGraphDb-BT9nZCEO.js → codeGraphDb-T6K3dK5D.js} +2 -2
- package/dist/{codeGraphDb-BT9nZCEO.js.map → codeGraphDb-T6K3dK5D.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +225 -146
- package/dist/index.js.map +1 -1
- package/dist/{nativeIndex-DlSpD611.js → nativeIndex-CABbgERW.js} +2 -2
- package/dist/nativeIndex-CABbgERW.js.map +1 -0
- package/dist/{nativeIndex-Cx7RLXUp.js → nativeIndex-esaDOsq8.js} +1 -1
- package/package.json +1 -1
- package/dist/nativeIndex-DlSpD611.js.map +0 -1
package/dist/client/index.esm.js
CHANGED
|
@@ -2049,7 +2049,7 @@ function resetLoggerListeners() {
|
|
|
2049
2049
|
|
|
2050
2050
|
//#endregion
|
|
2051
2051
|
//#region src/client/cache/IndexedDBCache.ts
|
|
2052
|
-
const log$
|
|
2052
|
+
const log$46 = scoped("indexeddb-cache");
|
|
2053
2053
|
const DB_PREFIX = "codegraph-cache-";
|
|
2054
2054
|
const CLUSTER_STORE = "cluster-cache";
|
|
2055
2055
|
const DIAGRAM_STORE = "diagram-cache";
|
|
@@ -2100,7 +2100,7 @@ var IndexedDBCache = class {
|
|
|
2100
2100
|
}
|
|
2101
2101
|
getDB() {
|
|
2102
2102
|
if (!this.dbPromise) this.dbPromise = openDB(this.repoId).catch((err) => {
|
|
2103
|
-
log$
|
|
2103
|
+
log$46.warn("failed to open IndexedDB", {
|
|
2104
2104
|
repoId: this.repoId,
|
|
2105
2105
|
error: err
|
|
2106
2106
|
});
|
|
@@ -2131,7 +2131,7 @@ var IndexedDBCache = class {
|
|
|
2131
2131
|
tx.onerror = () => reject(tx.error);
|
|
2132
2132
|
});
|
|
2133
2133
|
} catch (e) {
|
|
2134
|
-
log$
|
|
2134
|
+
log$46.warn("IndexedDB invalidateCluster failed", { error: e });
|
|
2135
2135
|
}
|
|
2136
2136
|
}
|
|
2137
2137
|
async invalidateDiagram(viewMode) {
|
|
@@ -2158,7 +2158,7 @@ var IndexedDBCache = class {
|
|
|
2158
2158
|
tx.onerror = () => reject(tx.error);
|
|
2159
2159
|
});
|
|
2160
2160
|
} catch (e) {
|
|
2161
|
-
log$
|
|
2161
|
+
log$46.warn("IndexedDB invalidateDiagram failed", {
|
|
2162
2162
|
viewMode,
|
|
2163
2163
|
error: e
|
|
2164
2164
|
});
|
|
@@ -2185,7 +2185,7 @@ var IndexedDBCache = class {
|
|
|
2185
2185
|
async setMany(storeName, entries) {
|
|
2186
2186
|
if (entries.length === 0) return;
|
|
2187
2187
|
if (entries.length > INDEXED_DB.BATCH.MAX_ENTRIES) {
|
|
2188
|
-
log$
|
|
2188
|
+
log$46.warn("setMany batch exceeds limit, splitting", {
|
|
2189
2189
|
count: entries.length,
|
|
2190
2190
|
limit: INDEXED_DB.BATCH.MAX_ENTRIES
|
|
2191
2191
|
});
|
|
@@ -2211,7 +2211,7 @@ var IndexedDBCache = class {
|
|
|
2211
2211
|
tx.onerror = () => reject(tx.error);
|
|
2212
2212
|
});
|
|
2213
2213
|
} catch (e) {
|
|
2214
|
-
log$
|
|
2214
|
+
log$46.warn(`IndexedDB setMany(${storeName}) failed`, {
|
|
2215
2215
|
count: entries.length,
|
|
2216
2216
|
error: e
|
|
2217
2217
|
});
|
|
@@ -2227,7 +2227,7 @@ var IndexedDBCache = class {
|
|
|
2227
2227
|
usageRatio: quota > 0 ? usage / quota : 0
|
|
2228
2228
|
};
|
|
2229
2229
|
} catch (e) {
|
|
2230
|
-
log$
|
|
2230
|
+
log$46.warn("storage.estimate() failed", { error: e });
|
|
2231
2231
|
return null;
|
|
2232
2232
|
}
|
|
2233
2233
|
}
|
|
@@ -2239,7 +2239,7 @@ var IndexedDBCache = class {
|
|
|
2239
2239
|
async purgeExpired() {
|
|
2240
2240
|
let totalPurged = 0;
|
|
2241
2241
|
for (const storeName of ALL_STORES) totalPurged += await this.purgeExpiredStore(storeName);
|
|
2242
|
-
if (totalPurged > 0) log$
|
|
2242
|
+
if (totalPurged > 0) log$46.info("purged expired entries", { count: totalPurged });
|
|
2243
2243
|
return totalPurged;
|
|
2244
2244
|
}
|
|
2245
2245
|
async purgeExpiredStore(storeName) {
|
|
@@ -2271,7 +2271,7 @@ var IndexedDBCache = class {
|
|
|
2271
2271
|
});
|
|
2272
2272
|
return expiredKeys.length;
|
|
2273
2273
|
} catch (e) {
|
|
2274
|
-
log$
|
|
2274
|
+
log$46.warn(`purgeExpiredStore(${storeName}) failed`, { error: e });
|
|
2275
2275
|
return 0;
|
|
2276
2276
|
}
|
|
2277
2277
|
}
|
|
@@ -2294,7 +2294,7 @@ var IndexedDBCache = class {
|
|
|
2294
2294
|
}
|
|
2295
2295
|
return raw;
|
|
2296
2296
|
} catch (e) {
|
|
2297
|
-
log$
|
|
2297
|
+
log$46.warn(`IndexedDB getFromStore(${storeName}) failed`, {
|
|
2298
2298
|
key,
|
|
2299
2299
|
error: e
|
|
2300
2300
|
});
|
|
@@ -2317,7 +2317,7 @@ var IndexedDBCache = class {
|
|
|
2317
2317
|
tx.onerror = () => reject(tx.error);
|
|
2318
2318
|
});
|
|
2319
2319
|
} catch (e) {
|
|
2320
|
-
log$
|
|
2320
|
+
log$46.warn(`IndexedDB setToStore(${storeName}) failed`, {
|
|
2321
2321
|
key,
|
|
2322
2322
|
error: e
|
|
2323
2323
|
});
|
|
@@ -2333,7 +2333,7 @@ var IndexedDBCache = class {
|
|
|
2333
2333
|
tx.onerror = () => reject(tx.error);
|
|
2334
2334
|
});
|
|
2335
2335
|
} catch (e) {
|
|
2336
|
-
log$
|
|
2336
|
+
log$46.warn(`IndexedDB deleteFromStore(${storeName}) failed`, {
|
|
2337
2337
|
key,
|
|
2338
2338
|
error: e
|
|
2339
2339
|
});
|
|
@@ -2349,7 +2349,7 @@ var IndexedDBCache = class {
|
|
|
2349
2349
|
|
|
2350
2350
|
//#endregion
|
|
2351
2351
|
//#region src/client/cache/DiagramCache.ts
|
|
2352
|
-
const log$
|
|
2352
|
+
const log$45 = scoped("cache");
|
|
2353
2353
|
function computeDataHash(nodes, edges) {
|
|
2354
2354
|
let nodeHash = 2166136261;
|
|
2355
2355
|
for (const n of nodes) {
|
|
@@ -2441,7 +2441,7 @@ var DiagramCache = class {
|
|
|
2441
2441
|
return diagram;
|
|
2442
2442
|
}
|
|
2443
2443
|
} catch (e) {
|
|
2444
|
-
log$
|
|
2444
|
+
log$45.warn("IndexedDB diagram getAsync failed", e);
|
|
2445
2445
|
}
|
|
2446
2446
|
return null;
|
|
2447
2447
|
}
|
|
@@ -2453,13 +2453,13 @@ var DiagramCache = class {
|
|
|
2453
2453
|
if (firstKey) this.cache.delete(firstKey);
|
|
2454
2454
|
}
|
|
2455
2455
|
this.cache.set(key, diagram);
|
|
2456
|
-
if (this.idbCache) this.idbCache.setDiagram(key, diagram).catch((e) => log$
|
|
2456
|
+
if (this.idbCache) this.idbCache.setDiagram(key, diagram).catch((e) => log$45.warn("IndexedDB diagram set failed", e));
|
|
2457
2457
|
}
|
|
2458
2458
|
invalidate(viewMode) {
|
|
2459
2459
|
if (viewMode) {
|
|
2460
2460
|
for (const key of this.cache.keys()) if (key.startsWith(`${viewMode}:`)) this.cache.delete(key);
|
|
2461
2461
|
} else this.cache.clear();
|
|
2462
|
-
if (this.idbCache) this.idbCache.invalidateDiagram(viewMode).catch((e) => log$
|
|
2462
|
+
if (this.idbCache) this.idbCache.invalidateDiagram(viewMode).catch((e) => log$45.warn("IndexedDB diagram invalidate failed", e));
|
|
2463
2463
|
}
|
|
2464
2464
|
size() {
|
|
2465
2465
|
return this.cache.size;
|
|
@@ -2504,7 +2504,7 @@ var ClusterCache = class {
|
|
|
2504
2504
|
return result;
|
|
2505
2505
|
}
|
|
2506
2506
|
} catch (e) {
|
|
2507
|
-
log$
|
|
2507
|
+
log$45.warn("IndexedDB cluster getAsync failed", e);
|
|
2508
2508
|
}
|
|
2509
2509
|
return null;
|
|
2510
2510
|
}
|
|
@@ -2516,11 +2516,11 @@ var ClusterCache = class {
|
|
|
2516
2516
|
if (firstKey) this.cache.delete(firstKey);
|
|
2517
2517
|
}
|
|
2518
2518
|
this.cache.set(key, result);
|
|
2519
|
-
if (this.idbCache) this.idbCache.setCluster(key, result).catch((e) => log$
|
|
2519
|
+
if (this.idbCache) this.idbCache.setCluster(key, result).catch((e) => log$45.warn("IndexedDB cluster set failed", e));
|
|
2520
2520
|
}
|
|
2521
2521
|
invalidate() {
|
|
2522
2522
|
this.cache.clear();
|
|
2523
|
-
if (this.idbCache) this.idbCache.invalidateCluster().catch((e) => log$
|
|
2523
|
+
if (this.idbCache) this.idbCache.invalidateCluster().catch((e) => log$45.warn("IndexedDB cluster invalidate failed", e));
|
|
2524
2524
|
}
|
|
2525
2525
|
size() {
|
|
2526
2526
|
return this.cache.size;
|
|
@@ -4174,6 +4174,25 @@ function buildStylesheet(theme, nodeCount = 0) {
|
|
|
4174
4174
|
"border-color": successColor
|
|
4175
4175
|
}
|
|
4176
4176
|
},
|
|
4177
|
+
{
|
|
4178
|
+
selector: "node.multi-selected",
|
|
4179
|
+
style: {
|
|
4180
|
+
"border-width": 3,
|
|
4181
|
+
"border-color": accentColor,
|
|
4182
|
+
"background-color": accentColor,
|
|
4183
|
+
"background-opacity": .35,
|
|
4184
|
+
"z-index": 26
|
|
4185
|
+
}
|
|
4186
|
+
},
|
|
4187
|
+
{
|
|
4188
|
+
selector: "node.canvas-focused",
|
|
4189
|
+
style: {
|
|
4190
|
+
"border-width": 2,
|
|
4191
|
+
"border-color": accentColor,
|
|
4192
|
+
"border-style": "dashed",
|
|
4193
|
+
"z-index": 24
|
|
4194
|
+
}
|
|
4195
|
+
},
|
|
4177
4196
|
{
|
|
4178
4197
|
selector: ".call-chain",
|
|
4179
4198
|
style: {
|
|
@@ -5502,7 +5521,7 @@ var CallChainAggregator = class {
|
|
|
5502
5521
|
|
|
5503
5522
|
//#endregion
|
|
5504
5523
|
//#region src/shared/PerformanceMonitor.ts
|
|
5505
|
-
const log$
|
|
5524
|
+
const log$44 = scoped("perf");
|
|
5506
5525
|
const MAX_HISTORY = 100;
|
|
5507
5526
|
const measurements = [];
|
|
5508
5527
|
const thresholds = new Map();
|
|
@@ -5604,8 +5623,8 @@ function recordMeasurement(name$1, durationMs) {
|
|
|
5604
5623
|
if (measurements.length > MAX_HISTORY) measurements.shift();
|
|
5605
5624
|
const threshold = thresholds.get(name$1);
|
|
5606
5625
|
if (threshold) {
|
|
5607
|
-
if (threshold.degradeMs !== void 0 && durationMs >= threshold.degradeMs && threshold.degradationKey) log$
|
|
5608
|
-
else if (threshold.warnMs !== void 0 && durationMs >= threshold.warnMs) log$
|
|
5626
|
+
if (threshold.degradeMs !== void 0 && durationMs >= threshold.degradeMs && threshold.degradationKey) log$44.warn(`${name$1} exceeded degrade threshold: ${durationMs.toFixed(1)}ms >= ${threshold.degradeMs}ms`);
|
|
5627
|
+
else if (threshold.warnMs !== void 0 && durationMs >= threshold.warnMs) log$44.info(`${name$1} exceeded warn threshold: ${durationMs.toFixed(1)}ms >= ${threshold.warnMs}ms`);
|
|
5609
5628
|
}
|
|
5610
5629
|
}
|
|
5611
5630
|
function getMeasurements(name$1) {
|
|
@@ -5615,7 +5634,7 @@ function getMeasurements(name$1) {
|
|
|
5615
5634
|
|
|
5616
5635
|
//#endregion
|
|
5617
5636
|
//#region src/client/store/internals.ts
|
|
5618
|
-
const log$
|
|
5637
|
+
const log$43 = scoped("store");
|
|
5619
5638
|
const diagramCache = new DiagramCache(10);
|
|
5620
5639
|
const clusterCache = new ClusterCache(20);
|
|
5621
5640
|
const callChainAggregator = new CallChainAggregator();
|
|
@@ -5733,7 +5752,7 @@ function mergeAndRecluster(newNodes, newEdges, set$1, logLabel, logDetail) {
|
|
|
5733
5752
|
nodes: result.nodes,
|
|
5734
5753
|
edges: result.edges
|
|
5735
5754
|
});
|
|
5736
|
-
log$
|
|
5755
|
+
log$43.info(logLabel, {
|
|
5737
5756
|
...logDetail,
|
|
5738
5757
|
newNodes: filteredNodes.length,
|
|
5739
5758
|
newEdges: filteredEdges.length
|
|
@@ -5905,7 +5924,7 @@ function normalizeEdgeType(type) {
|
|
|
5905
5924
|
|
|
5906
5925
|
//#endregion
|
|
5907
5926
|
//#region src/shared/GraphValidator.ts
|
|
5908
|
-
const log$
|
|
5927
|
+
const log$42 = scoped("validator");
|
|
5909
5928
|
function coerceNode(raw, i$1) {
|
|
5910
5929
|
if (!raw || typeof raw !== "object") return null;
|
|
5911
5930
|
const o = raw;
|
|
@@ -7165,6 +7184,7 @@ const en = {
|
|
|
7165
7184
|
"canvasControls.shortcutZoomIn": "+ Zoom in",
|
|
7166
7185
|
"canvasControls.shortcutZoomOut": "- Zoom out",
|
|
7167
7186
|
"canvasControls.shortcutFit": "F Fit to view",
|
|
7187
|
+
"canvasControls.shortcutBoxSelect": "Shift+Drag Box-select nodes",
|
|
7168
7188
|
"canvasControls.multiSelect": "Multi-select mode",
|
|
7169
7189
|
"micro.closeView": "Close micro view",
|
|
7170
7190
|
"micro.functionBar": "Micro view toolbar",
|
|
@@ -7328,6 +7348,12 @@ const en = {
|
|
|
7328
7348
|
"contextMenu.findPath": "Find Path From Here",
|
|
7329
7349
|
"contextMenu.showAll": "Show All Hidden Nodes",
|
|
7330
7350
|
"contextMenu.openSource": "Open Source",
|
|
7351
|
+
"contextMenu.expandHere": "Expand From Here",
|
|
7352
|
+
"contextMenu.collapseHere": "Collapse To Here",
|
|
7353
|
+
"contextMenu.pinNode": "Pin Node",
|
|
7354
|
+
"contextMenu.unpinNode": "Unpin Node",
|
|
7355
|
+
"contextMenu.copyId": "Copy Symbol ID",
|
|
7356
|
+
"contextMenu.copied": "Copied",
|
|
7331
7357
|
"a11y.graphContainer": "Code graph interactive area",
|
|
7332
7358
|
"a11y.graphContainerStats": "Code graph interactive area: {viewMode}, {nodeCount} nodes, {edgeCount} edges",
|
|
7333
7359
|
"a11y.selectedNode": "Selected node",
|
|
@@ -7509,7 +7535,17 @@ const en = {
|
|
|
7509
7535
|
"views.srcAuto": "Collected by host",
|
|
7510
7536
|
"views.srcQuery": "Provided by caller",
|
|
7511
7537
|
"views.srcDuration": "{ms}ms",
|
|
7512
|
-
"views.srcTruncated": "truncated"
|
|
7538
|
+
"views.srcTruncated": "truncated",
|
|
7539
|
+
"batchbar.ariaLabel": "Batch actions",
|
|
7540
|
+
"batchbar.count": "{count} selected",
|
|
7541
|
+
"batchbar.expand": "Expand",
|
|
7542
|
+
"batchbar.expandAria": "Expand all selected nodes",
|
|
7543
|
+
"batchbar.collapse": "Collapse",
|
|
7544
|
+
"batchbar.collapseAria": "Collapse all selected nodes",
|
|
7545
|
+
"batchbar.export": "Export JSON",
|
|
7546
|
+
"batchbar.exportAria": "Export selected nodes as JSON",
|
|
7547
|
+
"batchbar.clear": "Clear",
|
|
7548
|
+
"batchbar.clearAria": "Clear multi-selection"
|
|
7513
7549
|
};
|
|
7514
7550
|
|
|
7515
7551
|
//#endregion
|
|
@@ -7997,6 +8033,7 @@ const zh = {
|
|
|
7997
8033
|
"canvasControls.shortcutZoomIn": "+ 放大",
|
|
7998
8034
|
"canvasControls.shortcutZoomOut": "- 缩小",
|
|
7999
8035
|
"canvasControls.shortcutFit": "F 适应窗口",
|
|
8036
|
+
"canvasControls.shortcutBoxSelect": "Shift+拖拽 框选节点",
|
|
8000
8037
|
"canvasControls.multiSelect": "多选模式",
|
|
8001
8038
|
"micro.closeView": "关闭微观视图",
|
|
8002
8039
|
"micro.functionBar": "微观视图工具栏",
|
|
@@ -8160,6 +8197,12 @@ const zh = {
|
|
|
8160
8197
|
"contextMenu.findPath": "从此节点查找路径",
|
|
8161
8198
|
"contextMenu.showAll": "显示所有隐藏节点",
|
|
8162
8199
|
"contextMenu.openSource": "打开源码",
|
|
8200
|
+
"contextMenu.expandHere": "从此展开",
|
|
8201
|
+
"contextMenu.collapseHere": "收起到这里",
|
|
8202
|
+
"contextMenu.pinNode": "固定节点",
|
|
8203
|
+
"contextMenu.unpinNode": "取消固定",
|
|
8204
|
+
"contextMenu.copyId": "复制符号 ID",
|
|
8205
|
+
"contextMenu.copied": "已复制",
|
|
8163
8206
|
"a11y.graphContainer": "代码图谱交互区域",
|
|
8164
8207
|
"a11y.graphContainerStats": "代码图谱交互区域: {viewMode}, {nodeCount} 节点, {edgeCount} 边",
|
|
8165
8208
|
"a11y.selectedNode": "已选中节点",
|
|
@@ -8341,7 +8384,17 @@ const zh = {
|
|
|
8341
8384
|
"views.srcAuto": "宿主自动采集",
|
|
8342
8385
|
"views.srcQuery": "调用方传入",
|
|
8343
8386
|
"views.srcDuration": "耗时 {ms}ms",
|
|
8344
|
-
"views.srcTruncated": "已截断"
|
|
8387
|
+
"views.srcTruncated": "已截断",
|
|
8388
|
+
"batchbar.ariaLabel": "批量操作条",
|
|
8389
|
+
"batchbar.count": "已选 {count} 个",
|
|
8390
|
+
"batchbar.expand": "展开",
|
|
8391
|
+
"batchbar.expandAria": "批量展开选中的节点",
|
|
8392
|
+
"batchbar.collapse": "收起",
|
|
8393
|
+
"batchbar.collapseAria": "批量收起选中的节点",
|
|
8394
|
+
"batchbar.export": "导出 JSON",
|
|
8395
|
+
"batchbar.exportAria": "导出选中节点为 JSON 文件",
|
|
8396
|
+
"batchbar.clear": "清除",
|
|
8397
|
+
"batchbar.clearAria": "清除多选"
|
|
8345
8398
|
};
|
|
8346
8399
|
|
|
8347
8400
|
//#endregion
|
|
@@ -9349,14 +9402,14 @@ function etagKeyForEdges(fromId, scope, cursor, limit$1, detail) {
|
|
|
9349
9402
|
|
|
9350
9403
|
//#endregion
|
|
9351
9404
|
//#region src/client/api/requestCore.ts
|
|
9352
|
-
const log$
|
|
9405
|
+
const log$41 = scoped("client");
|
|
9353
9406
|
const DEFAULT_TIMEOUT = API.DEFAULT_TIMEOUT_MS;
|
|
9354
9407
|
async function withAbortGuard(name$1, run$3, fallback, logContext) {
|
|
9355
9408
|
try {
|
|
9356
9409
|
return await run$3();
|
|
9357
9410
|
} catch (e) {
|
|
9358
9411
|
if (e instanceof DOMException && e.name === "AbortError") return fallback;
|
|
9359
|
-
log$
|
|
9412
|
+
log$41.warn(`${name$1} failed`, {
|
|
9360
9413
|
...logContext,
|
|
9361
9414
|
error: e instanceof Error ? e.message : String(e)
|
|
9362
9415
|
});
|
|
@@ -9689,7 +9742,7 @@ async function fetchPathEnumerate(source, target, options = {}) {
|
|
|
9689
9742
|
|
|
9690
9743
|
//#endregion
|
|
9691
9744
|
//#region src/client/api/index.ts
|
|
9692
|
-
const log$
|
|
9745
|
+
const log$40 = scoped("client");
|
|
9693
9746
|
/** 构造一个空 sink;调用方先建后传,取数函数负责填充。 */
|
|
9694
9747
|
function createEtagSink() {
|
|
9695
9748
|
return {
|
|
@@ -9890,7 +9943,7 @@ async function fetchGraphDataIncremental(clientEtag, scopeRepoId) {
|
|
|
9890
9943
|
const data$2 = coerceGraphData(await res.json());
|
|
9891
9944
|
if (!data$2) return null;
|
|
9892
9945
|
if (isForeignRepo(data$2.metadata.repoId, scopeRepoId)) {
|
|
9893
|
-
log$
|
|
9946
|
+
log$40.info("incremental graph fetch ignored for another workspace", {
|
|
9894
9947
|
repoId: data$2.metadata.repoId,
|
|
9895
9948
|
activeRepoId: scopeRepoId
|
|
9896
9949
|
});
|
|
@@ -10031,7 +10084,7 @@ async function fetchEdges(fromId, scope = "file", cursor, limit$1, signal, works
|
|
|
10031
10084
|
|
|
10032
10085
|
//#endregion
|
|
10033
10086
|
//#region src/client/diagram/index.ts
|
|
10034
|
-
const log$
|
|
10087
|
+
const log$39 = scoped("diagram");
|
|
10035
10088
|
const macroAssemblerRegistry = new Map([
|
|
10036
10089
|
["inheritance", new InheritanceAssembler()],
|
|
10037
10090
|
["callgraph", new CallGraphAssembler()],
|
|
@@ -10231,7 +10284,7 @@ async function assembleMicroDiagram(viewMode, filePath, functionName, workspace)
|
|
|
10231
10284
|
body: JSON.stringify({ filePath })
|
|
10232
10285
|
});
|
|
10233
10286
|
} catch (e) {
|
|
10234
|
-
log$
|
|
10287
|
+
log$39.warn("AST fetch failed", e);
|
|
10235
10288
|
throw new Error(`Network error: unable to reach AST endpoint`);
|
|
10236
10289
|
}
|
|
10237
10290
|
if (!response.ok) {
|
|
@@ -10246,7 +10299,7 @@ async function assembleMicroDiagram(viewMode, filePath, functionName, workspace)
|
|
|
10246
10299
|
const parsed = JSON.parse(errorBody);
|
|
10247
10300
|
serverMsg = parsed.error || "";
|
|
10248
10301
|
} catch (e) {
|
|
10249
|
-
log$
|
|
10302
|
+
log$39.warn("AST error body parse failed", e);
|
|
10250
10303
|
serverMsg = errorBody;
|
|
10251
10304
|
}
|
|
10252
10305
|
throw new Error(serverMsg || `AST parsing failed for "${filePath}"`);
|
|
@@ -10381,7 +10434,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
10381
10434
|
nodeEdgeTotals: totals$1,
|
|
10382
10435
|
nodeEdgeCursors: cursors$1
|
|
10383
10436
|
});
|
|
10384
|
-
log$
|
|
10437
|
+
log$43.info("node edges loaded (cache)", {
|
|
10385
10438
|
nodeId,
|
|
10386
10439
|
scope,
|
|
10387
10440
|
edges: data$3.edges.length
|
|
@@ -10394,7 +10447,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
10394
10447
|
const data$2 = await fetchEdges(nodeId, scope, cursor, EDGE_DETAIL.PAGE_SIZE, controller.signal, _get().currentWorkspace, detail, sink);
|
|
10395
10448
|
if (!isCurrent() || gen !== internals.expandGeneration) return;
|
|
10396
10449
|
if (sink.notModified) {
|
|
10397
|
-
log$
|
|
10450
|
+
log$43.info("node edges not modified (304)", {
|
|
10398
10451
|
nodeId,
|
|
10399
10452
|
scope,
|
|
10400
10453
|
cursor,
|
|
@@ -10403,7 +10456,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
10403
10456
|
return;
|
|
10404
10457
|
}
|
|
10405
10458
|
if (!data$2) {
|
|
10406
|
-
log$
|
|
10459
|
+
log$43.warn("loadNodeEdges: no response", {
|
|
10407
10460
|
nodeId,
|
|
10408
10461
|
scope,
|
|
10409
10462
|
cursor,
|
|
@@ -10430,7 +10483,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
10430
10483
|
edges: merged,
|
|
10431
10484
|
total: data$2.total
|
|
10432
10485
|
});
|
|
10433
|
-
log$
|
|
10486
|
+
log$43.info("node edges loaded", {
|
|
10434
10487
|
nodeId,
|
|
10435
10488
|
scope,
|
|
10436
10489
|
detail,
|
|
@@ -10441,7 +10494,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
10441
10494
|
});
|
|
10442
10495
|
commitEtag(sink);
|
|
10443
10496
|
} catch (err) {
|
|
10444
|
-
if (!(err instanceof DOMException && err.name === "AbortError")) log$
|
|
10497
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) log$43.warn("loadNodeEdges failed", {
|
|
10445
10498
|
nodeId,
|
|
10446
10499
|
scope,
|
|
10447
10500
|
err
|
|
@@ -10564,7 +10617,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
10564
10617
|
...emptyDiffState()
|
|
10565
10618
|
});
|
|
10566
10619
|
} catch (err) {
|
|
10567
|
-
log$
|
|
10620
|
+
log$43.error("setGraphData failed", { err });
|
|
10568
10621
|
_set({
|
|
10569
10622
|
error: err instanceof Error ? err.message : String(err),
|
|
10570
10623
|
isLoading: false
|
|
@@ -10633,12 +10686,12 @@ function createGraphDataSlice(_set, _get) {
|
|
|
10633
10686
|
...emptyEdgeState(),
|
|
10634
10687
|
...emptyDiffState()
|
|
10635
10688
|
});
|
|
10636
|
-
log$
|
|
10689
|
+
log$43.info("bootstrap data set", {
|
|
10637
10690
|
rootNodes: sanitized.nodes.length,
|
|
10638
10691
|
totalNodeCount: metadata.totalNodeCount
|
|
10639
10692
|
});
|
|
10640
10693
|
} catch (err) {
|
|
10641
|
-
log$
|
|
10694
|
+
log$43.error("setBootstrapData failed", { err });
|
|
10642
10695
|
_set({
|
|
10643
10696
|
error: err instanceof Error ? err.message : String(err),
|
|
10644
10697
|
isLoading: false
|
|
@@ -10681,7 +10734,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
10681
10734
|
nodes: rawNodes,
|
|
10682
10735
|
edges: rawEdges
|
|
10683
10736
|
}, delta, { minRetainRatio: GRAPH_DELTA_SHRINK.MIN_RETAIN_RATIO })) {
|
|
10684
|
-
log$
|
|
10737
|
+
log$43.info("applyGraphDelta refused: delta does not match the current revision or would shrink the graph", {
|
|
10685
10738
|
storedNodes: rawNodes.length,
|
|
10686
10739
|
storedEdges: rawEdges.length,
|
|
10687
10740
|
...graphDeltaSize(delta)
|
|
@@ -10743,7 +10796,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
10743
10796
|
...emptyEdgeState(),
|
|
10744
10797
|
...emptyDiffState()
|
|
10745
10798
|
});
|
|
10746
|
-
log$
|
|
10799
|
+
log$43.info("applied graph delta", {
|
|
10747
10800
|
addedNodes: delta.addedNodes.length,
|
|
10748
10801
|
removedNodes: delta.removedNodes.length,
|
|
10749
10802
|
modifiedNodes: delta.modifiedNodes.length,
|
|
@@ -10753,7 +10806,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
10753
10806
|
});
|
|
10754
10807
|
return true;
|
|
10755
10808
|
} catch (err) {
|
|
10756
|
-
log$
|
|
10809
|
+
log$43.error("applyGraphDelta failed", { err });
|
|
10757
10810
|
_set({
|
|
10758
10811
|
error: err instanceof Error ? err.message : String(err),
|
|
10759
10812
|
isLoading: false
|
|
@@ -11099,7 +11152,7 @@ function createSessionSlice(_set, _get) {
|
|
|
11099
11152
|
|
|
11100
11153
|
//#endregion
|
|
11101
11154
|
//#region src/client/utils/urlState.ts
|
|
11102
|
-
const log$
|
|
11155
|
+
const log$38 = scoped("url-state");
|
|
11103
11156
|
const PREFIX_DIR = "d:";
|
|
11104
11157
|
const PREFIX_FILE = "f:";
|
|
11105
11158
|
const PREFIX_SYMBOL = "s:";
|
|
@@ -11156,7 +11209,7 @@ function updateUrlExpand(expandedNodeIds) {
|
|
|
11156
11209
|
if (urlString.length > URL_STATE.MAX_LENGTH) {
|
|
11157
11210
|
const trimmed = expandParam.split(",").filter((p$1) => !p$1.startsWith(PREFIX_SYMBOL));
|
|
11158
11211
|
url.searchParams.set("expand", trimmed.join(","));
|
|
11159
|
-
log$
|
|
11212
|
+
log$38.warn("URL too long, trimmed symbol-level expansions");
|
|
11160
11213
|
}
|
|
11161
11214
|
window.history.replaceState(null, "", url.toString());
|
|
11162
11215
|
}
|
|
@@ -11251,7 +11304,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
11251
11304
|
const data$2 = await measureAsync("ast-fetch", () => fetchSymbolAST(nodeId, signal, _get().currentWorkspace, cursor, sink));
|
|
11252
11305
|
if (gen !== internals.expandGeneration || !isCurrent()) return;
|
|
11253
11306
|
if (sink.notModified) {
|
|
11254
|
-
log$
|
|
11307
|
+
log$43.info("symbol AST not modified (304)", {
|
|
11255
11308
|
nodeId,
|
|
11256
11309
|
cursor
|
|
11257
11310
|
});
|
|
@@ -11259,7 +11312,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
11259
11312
|
}
|
|
11260
11313
|
if (!data$2) return;
|
|
11261
11314
|
const { astNodes: truncatedAstNodes, astEdges: truncatedAstEdges, sourceLines: truncatedSourceLines } = truncateAstData(data$2);
|
|
11262
|
-
if (truncatedAstNodes.length < data$2.astNodes.length) log$
|
|
11315
|
+
if (truncatedAstNodes.length < data$2.astNodes.length) log$43.warn("symbol AST truncated", {
|
|
11263
11316
|
nodeId,
|
|
11264
11317
|
original: data$2.astNodes.length,
|
|
11265
11318
|
truncated: truncatedAstNodes.length
|
|
@@ -11279,7 +11332,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
11279
11332
|
if (internals.persistentCache && !data$2.hasMore && cursor === 0) await internals.persistentCache.setAST(nodeId, data$2);
|
|
11280
11333
|
commitEtag(sink);
|
|
11281
11334
|
} catch (err) {
|
|
11282
|
-
log$
|
|
11335
|
+
log$43.warn("expandNode: fetchSymbolAST failed", {
|
|
11283
11336
|
nodeId,
|
|
11284
11337
|
err
|
|
11285
11338
|
});
|
|
@@ -11306,7 +11359,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11306
11359
|
if (expandingFunctionBody) enforceFunctionBodyBudget(effectiveExpanded, rawNodes, FUNCTION_BODY.MAX_EXPANDED - 1, pinnedIds);
|
|
11307
11360
|
effectiveExpanded.add(nodeId);
|
|
11308
11361
|
collapseExcessClusters(nodes, effectiveExpanded, maxVisible, cost, pinnedIds);
|
|
11309
|
-
if (cost > CLUSTER.EXPAND_BUDGET_SINGLE) log$
|
|
11362
|
+
if (cost > CLUSTER.EXPAND_BUDGET_SINGLE) log$43.warn("expand budget exceeded", {
|
|
11310
11363
|
nodeId,
|
|
11311
11364
|
cost,
|
|
11312
11365
|
budget: CLUSTER.EXPAND_BUDGET_SINGLE
|
|
@@ -11321,7 +11374,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11321
11374
|
const expandController = replaceController(internals.expandAbortControllers, nodeId);
|
|
11322
11375
|
_get().loadNodeChildren(nodeId, { signal: expandController.signal }).catch((err) => {
|
|
11323
11376
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
11324
|
-
log$
|
|
11377
|
+
log$43.warn("expandNode: loadNodeChildren failed", {
|
|
11325
11378
|
nodeId,
|
|
11326
11379
|
err
|
|
11327
11380
|
});
|
|
@@ -11343,7 +11396,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11343
11396
|
fetchFileOutline(nodeId, expandController.signal, _get().currentWorkspace, sink).then(async (data$2) => {
|
|
11344
11397
|
if (gen !== internals.expandGeneration) return;
|
|
11345
11398
|
if (sink.notModified) {
|
|
11346
|
-
log$
|
|
11399
|
+
log$43.info("file outline not modified (304)", { nodeId });
|
|
11347
11400
|
return;
|
|
11348
11401
|
}
|
|
11349
11402
|
if (data$2 && data$2.symbols.length > 0) {
|
|
@@ -11352,7 +11405,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11352
11405
|
commitEtag(sink);
|
|
11353
11406
|
}
|
|
11354
11407
|
}).catch((err) => {
|
|
11355
|
-
log$
|
|
11408
|
+
log$43.warn("expandNode: fetchFileOutline failed", {
|
|
11356
11409
|
nodeId,
|
|
11357
11410
|
err
|
|
11358
11411
|
});
|
|
@@ -11390,7 +11443,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11390
11443
|
loadedChildrenByNodeId: newLoaded$1,
|
|
11391
11444
|
pendingRequests: clearedPending$1
|
|
11392
11445
|
});
|
|
11393
|
-
log$
|
|
11446
|
+
log$43.info("node children not modified (304)", { nodeId });
|
|
11394
11447
|
return;
|
|
11395
11448
|
}
|
|
11396
11449
|
if (!data$2 || data$2.nodes.length === 0) {
|
|
@@ -11439,7 +11492,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11439
11492
|
loadedChildrenByNodeId: newLoaded,
|
|
11440
11493
|
pendingRequests: clearedPending
|
|
11441
11494
|
});
|
|
11442
|
-
log$
|
|
11495
|
+
log$43.info("node children loaded", {
|
|
11443
11496
|
nodeId,
|
|
11444
11497
|
newNodes: newNodes.length,
|
|
11445
11498
|
newEdges: newEdges.length
|
|
@@ -11458,7 +11511,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11458
11511
|
pendingRequests: clearedPending,
|
|
11459
11512
|
loadErrorsByNodeId: newErrors
|
|
11460
11513
|
});
|
|
11461
|
-
log$
|
|
11514
|
+
log$43.warn("loadNodeChildren failed", {
|
|
11462
11515
|
nodeId,
|
|
11463
11516
|
err
|
|
11464
11517
|
});
|
|
@@ -11469,7 +11522,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11469
11522
|
const ws = _get().currentWorkspace;
|
|
11470
11523
|
const data$2 = await fetchBootstrap(void 0, ws);
|
|
11471
11524
|
if (!data$2) {
|
|
11472
|
-
log$
|
|
11525
|
+
log$43.info("bootstrap returned no payload, falling back to full load");
|
|
11473
11526
|
return false;
|
|
11474
11527
|
}
|
|
11475
11528
|
_get().setIndexStatus(data$2.indexStatus);
|
|
@@ -11480,11 +11533,11 @@ function createExpandSlice(_set, _get) {
|
|
|
11480
11533
|
timestamp: data$2.metadata.timestamp,
|
|
11481
11534
|
graphLayer: data$2.metadata.graphLayer
|
|
11482
11535
|
});
|
|
11483
|
-
log$
|
|
11536
|
+
log$43.info("bootstrap: index building, empty skeleton installed", { repoId: data$2.metadata.repoId });
|
|
11484
11537
|
return true;
|
|
11485
11538
|
}
|
|
11486
11539
|
if (data$2.topLevelNodes.length === 0) {
|
|
11487
|
-
log$
|
|
11540
|
+
log$43.info("bootstrap returned empty, falling back to full load");
|
|
11488
11541
|
return false;
|
|
11489
11542
|
}
|
|
11490
11543
|
const state = _get();
|
|
@@ -11494,14 +11547,14 @@ function createExpandSlice(_set, _get) {
|
|
|
11494
11547
|
timestamp: data$2.metadata.timestamp,
|
|
11495
11548
|
graphLayer: data$2.metadata.graphLayer
|
|
11496
11549
|
});
|
|
11497
|
-
log$
|
|
11550
|
+
log$43.info("bootstrap loaded", {
|
|
11498
11551
|
rootNodes: data$2.topLevelNodes.length,
|
|
11499
11552
|
totalNodeCount: data$2.metadata.totalNodeCount,
|
|
11500
11553
|
indexStatus: data$2.indexStatus
|
|
11501
11554
|
});
|
|
11502
11555
|
return true;
|
|
11503
11556
|
} catch (err) {
|
|
11504
|
-
log$
|
|
11557
|
+
log$43.warn("loadBootstrap failed, falling back to full load", { err });
|
|
11505
11558
|
return false;
|
|
11506
11559
|
}
|
|
11507
11560
|
},
|
|
@@ -11661,7 +11714,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11661
11714
|
if (!exists) {
|
|
11662
11715
|
const parentId = nodeId.includes("/") ? nodeId.substring(0, nodeId.lastIndexOf("/")) : null;
|
|
11663
11716
|
if (parentId && !state.loadedChildrenByNodeId.has(parentId)) await state.loadNodeChildren(parentId, { signal: restoreController.signal }).catch((err) => {
|
|
11664
|
-
if (!(err instanceof DOMException && err.name === "AbortError")) log$
|
|
11717
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) log$43.warn("restoreFromUrl: loadNodeChildren failed", {
|
|
11665
11718
|
parentId,
|
|
11666
11719
|
err
|
|
11667
11720
|
});
|
|
@@ -11686,7 +11739,7 @@ function createExpandSlice(_set, _get) {
|
|
|
11686
11739
|
}
|
|
11687
11740
|
internals.restoreTimeoutHandle = null;
|
|
11688
11741
|
_set({ urlRestored: true });
|
|
11689
|
-
log$
|
|
11742
|
+
log$43.info("URL restore completed", { restoredCount: sortedIds.length });
|
|
11690
11743
|
}
|
|
11691
11744
|
};
|
|
11692
11745
|
}
|
|
@@ -11868,7 +11921,7 @@ function createAnalysisSlice(_set, _get) {
|
|
|
11868
11921
|
isLoading: false
|
|
11869
11922
|
});
|
|
11870
11923
|
} catch (e) {
|
|
11871
|
-
log$
|
|
11924
|
+
log$43.warn("assembleMultiFunction failed", e);
|
|
11872
11925
|
_set({
|
|
11873
11926
|
error: t("error.callChainFailed"),
|
|
11874
11927
|
isLoading: false
|
|
@@ -11954,7 +12007,7 @@ function createPanelLodSlice(_set, _get) {
|
|
|
11954
12007
|
|
|
11955
12008
|
//#endregion
|
|
11956
12009
|
//#region src/client/api/keywordQuery.ts
|
|
11957
|
-
const log$
|
|
12010
|
+
const log$37 = scoped("client");
|
|
11958
12011
|
/**
|
|
11959
12012
|
|
|
11960
12013
|
* 条件请求的请求键(与宿主 `computeQueryEtag` 的**参数域**对齐)。
|
|
@@ -12013,7 +12066,7 @@ async function fetchKeywordQuery(params, signal) {
|
|
|
12013
12066
|
res = await fetchWithTimeout(`/api/v1/codegraph/query?${search.toString()}`, options);
|
|
12014
12067
|
} catch (e) {
|
|
12015
12068
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
12016
|
-
log$
|
|
12069
|
+
log$37.warn("fetchKeywordQuery failed", {
|
|
12017
12070
|
aborted,
|
|
12018
12071
|
externalAborted: signal?.aborted === true,
|
|
12019
12072
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -12030,7 +12083,7 @@ async function fetchKeywordQuery(params, signal) {
|
|
|
12030
12083
|
try {
|
|
12031
12084
|
payload = await res.json();
|
|
12032
12085
|
} catch (e) {
|
|
12033
|
-
log$
|
|
12086
|
+
log$37.warn("fetchKeywordQuery: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
12034
12087
|
return {
|
|
12035
12088
|
kind: "failed",
|
|
12036
12089
|
status: res.status
|
|
@@ -12168,7 +12221,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
12168
12221
|
});
|
|
12169
12222
|
if (seq !== _get().keywordQuerySeq) return false;
|
|
12170
12223
|
if (outcome.kind === "not-modified") {
|
|
12171
|
-
log$
|
|
12224
|
+
log$43.info("keyword query not modified (304)", { q: trimmed });
|
|
12172
12225
|
const previous = _get().keywordQueryResult;
|
|
12173
12226
|
_set({
|
|
12174
12227
|
keywordQueryStatus: previous ? "ready" : "idle",
|
|
@@ -12217,7 +12270,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
12217
12270
|
const state = _get();
|
|
12218
12271
|
const exists = state.rawNodes.some((node) => node.id === nodeId);
|
|
12219
12272
|
if (!exists) {
|
|
12220
|
-
log$
|
|
12273
|
+
log$43.warn("selectKeywordSeed: node not in current graph", { nodeId });
|
|
12221
12274
|
return;
|
|
12222
12275
|
}
|
|
12223
12276
|
_set({
|
|
@@ -12239,7 +12292,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
12239
12292
|
...workspace && workspace !== "." ? { workspace } : {}
|
|
12240
12293
|
});
|
|
12241
12294
|
if (outcome.kind !== "data") {
|
|
12242
|
-
log$
|
|
12295
|
+
log$43.warn("revealRemoteNode: query failed", {
|
|
12243
12296
|
nodeId,
|
|
12244
12297
|
kind: outcome.kind
|
|
12245
12298
|
});
|
|
@@ -12247,12 +12300,12 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
12247
12300
|
}
|
|
12248
12301
|
const target = outcome.data.nodes.find((n) => n.id === nodeId);
|
|
12249
12302
|
if (!target) {
|
|
12250
|
-
log$
|
|
12303
|
+
log$43.warn("revealRemoteNode: payload does not contain the requested node", { nodeId });
|
|
12251
12304
|
return false;
|
|
12252
12305
|
}
|
|
12253
12306
|
mergeAndRecluster(outcome.data.nodes, outcome.data.edges, _set, "remote search hit merged", { nodeId });
|
|
12254
12307
|
if (!_get().rawNodes.some((node) => node.id === nodeId)) {
|
|
12255
|
-
log$
|
|
12308
|
+
log$43.warn("revealRemoteNode: node still absent after merge", { nodeId });
|
|
12256
12309
|
return false;
|
|
12257
12310
|
}
|
|
12258
12311
|
_set({
|
|
@@ -12302,7 +12355,7 @@ function truncationReasonKey(reason) {
|
|
|
12302
12355
|
|
|
12303
12356
|
//#endregion
|
|
12304
12357
|
//#region src/client/api/pathPattern.ts
|
|
12305
|
-
const log$
|
|
12358
|
+
const log$36 = scoped("client");
|
|
12306
12359
|
/**
|
|
12307
12360
|
* 把宿主响应归一成 `PathPatternData`。
|
|
12308
12361
|
*
|
|
@@ -12362,7 +12415,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
12362
12415
|
res = await fetchWithTimeout(`/api/v1/codegraph/paths?${search.toString()}`, options);
|
|
12363
12416
|
} catch (e) {
|
|
12364
12417
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
12365
|
-
log$
|
|
12418
|
+
log$36.warn("fetchPathsByPattern failed", {
|
|
12366
12419
|
aborted,
|
|
12367
12420
|
externalAborted: signal?.aborted === true,
|
|
12368
12421
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -12383,7 +12436,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
12383
12436
|
const record$1 = body && typeof body === "object" ? body : {};
|
|
12384
12437
|
const errorCode = typeof record$1.error === "string" ? record$1.error : "invalid-pattern";
|
|
12385
12438
|
const diagnostic = typeof record$1.diagnostic === "string" ? record$1.diagnostic : typeof record$1.message === "string" ? record$1.message : "";
|
|
12386
|
-
log$
|
|
12439
|
+
log$36.warn("fetchPathsByPattern: invalid pattern", {
|
|
12387
12440
|
errorCode,
|
|
12388
12441
|
diagnostic
|
|
12389
12442
|
});
|
|
@@ -12401,7 +12454,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
12401
12454
|
try {
|
|
12402
12455
|
payload = await res.json();
|
|
12403
12456
|
} catch (e) {
|
|
12404
|
-
log$
|
|
12457
|
+
log$36.warn("fetchPathsByPattern: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
12405
12458
|
return {
|
|
12406
12459
|
kind: "failed",
|
|
12407
12460
|
status: res.status
|
|
@@ -12430,7 +12483,7 @@ async function fetchPathPatternSyntax(pattern, signal) {
|
|
|
12430
12483
|
res = await fetchWithTimeout(`/api/v1/codegraph/path-pattern/syntax?${search.toString()}`, options);
|
|
12431
12484
|
} catch (e) {
|
|
12432
12485
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
12433
|
-
log$
|
|
12486
|
+
log$36.warn("fetchPathPatternSyntax failed", {
|
|
12434
12487
|
aborted,
|
|
12435
12488
|
externalAborted: signal?.aborted === true,
|
|
12436
12489
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -12453,7 +12506,7 @@ async function fetchPathPatternSyntax(pattern, signal) {
|
|
|
12453
12506
|
try {
|
|
12454
12507
|
payload = await res.json();
|
|
12455
12508
|
} catch (e) {
|
|
12456
|
-
log$
|
|
12509
|
+
log$36.warn("fetchPathPatternSyntax: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
12457
12510
|
return {
|
|
12458
12511
|
ok: false,
|
|
12459
12512
|
kind: "failed",
|
|
@@ -12539,7 +12592,7 @@ function applyOutcome(outcome, pattern, workspace, basePaths, set$1) {
|
|
|
12539
12592
|
return false;
|
|
12540
12593
|
}
|
|
12541
12594
|
if (outcome.kind === "invalid") {
|
|
12542
|
-
log$
|
|
12595
|
+
log$43.warn("ρ pattern rejected (invalid syntax)", {
|
|
12543
12596
|
pattern,
|
|
12544
12597
|
diagnostic: outcome.diagnostic
|
|
12545
12598
|
});
|
|
@@ -12676,9 +12729,76 @@ function summarizePathPatternResult(result) {
|
|
|
12676
12729
|
};
|
|
12677
12730
|
}
|
|
12678
12731
|
|
|
12732
|
+
//#endregion
|
|
12733
|
+
//#region src/client/store/slices/multiSelectSlice.ts
|
|
12734
|
+
/**
|
|
12735
|
+
* 多选链路的**消费面**(cycle-149)。
|
|
12736
|
+
*
|
|
12737
|
+
* cycle-141 D 在渲染器侧交付了 Shift+Click 多选(`InteractionController.multiSelectedIds`
|
|
12738
|
+
* + `multi-selected` class + `onSelectionToggle` 回调),但该回调在全仓**零消费方**——
|
|
12739
|
+
* 多选集合只活在渲染器内部,store/UI 全都看不到。本切片把这条链路接完:
|
|
12740
|
+
*
|
|
12741
|
+
* - `multiSelectedIds` 是渲染器多选集合的**镜像**(单一事实源仍是渲染器,
|
|
12742
|
+
* store 只做镜像 + 批量动作的消费面;两边经 `syncMultiSelection`/`clearMultiSelection`
|
|
12743
|
+
* 同步,避免双集合各自演化后漂移)。
|
|
12744
|
+
* - `batchExpandSelected` 复用 expandSlice 的 `batchExpandNodes`(函数体预算 / pinned
|
|
12745
|
+
* 语义不重写——两处实现必然漂移,CLAUDE.md「Two data paths for the same semantics
|
|
12746
|
+
* must share ONE implementation」)。
|
|
12747
|
+
* - `batchCollapseSelected` 与 expandSlice `collapseAll` 同形:**单次** recluster
|
|
12748
|
+
* (逐个 collapseNode 会 N 次重聚类),pinned 节点不收(与 `collapseNode` 语义一致)。
|
|
12749
|
+
* - `exportSelectedNodes` 从 `rawNodes` 过滤导出;**聚合节点(cluster)不在 rawNodes 里**,
|
|
12750
|
+
* 选中聚合节点时该 id 被静默跳过——这是有意的诚实缺口(渲染器合并态 store 不可见),
|
|
12751
|
+
* UI 侧以「导出 N 个」计数如实呈现,不伪造。
|
|
12752
|
+
*
|
|
12753
|
+
* `multiSelectedIds` **不持久化**:画布选区是会话瞬时态,reload 后节点可能已变
|
|
12754
|
+
* (与 `xuanjiSignal` 同判据);`partialize` 白名单不收录即天然排除。
|
|
12755
|
+
*/
|
|
12756
|
+
function createMultiSelectSlice(_set, _get) {
|
|
12757
|
+
return {
|
|
12758
|
+
multiSelectedIds: new Set(),
|
|
12759
|
+
syncMultiSelection: (ids) => {
|
|
12760
|
+
const current$2 = _get().multiSelectedIds;
|
|
12761
|
+
if (current$2.size === ids.length && [...ids].every((id) => current$2.has(id))) return;
|
|
12762
|
+
_set({ multiSelectedIds: new Set(ids) });
|
|
12763
|
+
},
|
|
12764
|
+
clearMultiSelection: () => {
|
|
12765
|
+
if (_get().multiSelectedIds.size === 0) return;
|
|
12766
|
+
_set({ multiSelectedIds: new Set() });
|
|
12767
|
+
},
|
|
12768
|
+
batchExpandSelected: () => {
|
|
12769
|
+
const ids = [..._get().multiSelectedIds];
|
|
12770
|
+
if (ids.length === 0) return 0;
|
|
12771
|
+
_get().batchExpandNodes(ids);
|
|
12772
|
+
return ids.length;
|
|
12773
|
+
},
|
|
12774
|
+
batchCollapseSelected: () => {
|
|
12775
|
+
const { multiSelectedIds, expandedNodeIds, expandedNodeIdsByViewMode, viewMode, pinnedIds } = _get();
|
|
12776
|
+
if (multiSelectedIds.size === 0) return 0;
|
|
12777
|
+
const newExpanded = new Set(expandedNodeIds);
|
|
12778
|
+
let collapsed = 0;
|
|
12779
|
+
for (const id of multiSelectedIds) {
|
|
12780
|
+
if (pinnedIds.has(id)) continue;
|
|
12781
|
+
if (newExpanded.delete(id)) collapsed++;
|
|
12782
|
+
}
|
|
12783
|
+
if (collapsed === 0) return 0;
|
|
12784
|
+
const newExpandedByViewMode = {
|
|
12785
|
+
...expandedNodeIdsByViewMode,
|
|
12786
|
+
[viewMode]: newExpanded
|
|
12787
|
+
};
|
|
12788
|
+
reclusterWith(_get, _set, newExpanded, newExpandedByViewMode);
|
|
12789
|
+
return collapsed;
|
|
12790
|
+
},
|
|
12791
|
+
exportSelectedNodes: () => {
|
|
12792
|
+
const { multiSelectedIds, rawNodes } = _get();
|
|
12793
|
+
if (multiSelectedIds.size === 0) return [];
|
|
12794
|
+
return rawNodes.filter((n) => multiSelectedIds.has(n.id));
|
|
12795
|
+
}
|
|
12796
|
+
};
|
|
12797
|
+
}
|
|
12798
|
+
|
|
12679
12799
|
//#endregion
|
|
12680
12800
|
//#region src/client/utils/safeStorage.ts
|
|
12681
|
-
const log$
|
|
12801
|
+
const log$35 = scoped("client");
|
|
12682
12802
|
/** 纯内存实现:同一页面的多次读写一致,刷新即丢。 */
|
|
12683
12803
|
function createMemoryStorage() {
|
|
12684
12804
|
const map$1 = new Map();
|
|
@@ -12736,7 +12856,7 @@ function getSafeStorage(label = "generic") {
|
|
|
12736
12856
|
try {
|
|
12737
12857
|
return local.getItem(name$1);
|
|
12738
12858
|
} catch (e) {
|
|
12739
|
-
log$
|
|
12859
|
+
log$35.debug("safeStorage.getItem failed", {
|
|
12740
12860
|
label,
|
|
12741
12861
|
error: e
|
|
12742
12862
|
});
|
|
@@ -12747,7 +12867,7 @@ function getSafeStorage(label = "generic") {
|
|
|
12747
12867
|
try {
|
|
12748
12868
|
local.setItem(name$1, value);
|
|
12749
12869
|
} catch (e) {
|
|
12750
|
-
log$
|
|
12870
|
+
log$35.debug("safeStorage.setItem failed", {
|
|
12751
12871
|
label,
|
|
12752
12872
|
error: e
|
|
12753
12873
|
});
|
|
@@ -12757,7 +12877,7 @@ function getSafeStorage(label = "generic") {
|
|
|
12757
12877
|
try {
|
|
12758
12878
|
local.removeItem(name$1);
|
|
12759
12879
|
} catch (e) {
|
|
12760
|
-
log$
|
|
12880
|
+
log$35.debug("safeStorage.removeItem failed", {
|
|
12761
12881
|
label,
|
|
12762
12882
|
error: e
|
|
12763
12883
|
});
|
|
@@ -12766,7 +12886,7 @@ function getSafeStorage(label = "generic") {
|
|
|
12766
12886
|
};
|
|
12767
12887
|
const session = isStorageUsable(() => window.sessionStorage);
|
|
12768
12888
|
if (session) {
|
|
12769
|
-
log$
|
|
12889
|
+
log$35.info("localStorage unavailable, falling back to sessionStorage", { label });
|
|
12770
12890
|
return {
|
|
12771
12891
|
getItem: (name$1) => {
|
|
12772
12892
|
try {
|
|
@@ -12787,7 +12907,7 @@ function getSafeStorage(label = "generic") {
|
|
|
12787
12907
|
}
|
|
12788
12908
|
};
|
|
12789
12909
|
}
|
|
12790
|
-
log$
|
|
12910
|
+
log$35.info("no Web Storage available, falling back to in-memory storage", { label });
|
|
12791
12911
|
return createMemoryStorage();
|
|
12792
12912
|
}
|
|
12793
12913
|
/**
|
|
@@ -12848,7 +12968,8 @@ function disposeStore() {
|
|
|
12848
12968
|
nodeEdgeTotals: new Map(),
|
|
12849
12969
|
nodeEdgeCursors: new Map(),
|
|
12850
12970
|
loadingEdgeNodeIds: new Set(),
|
|
12851
|
-
detailEdgeNodeIds: new Set()
|
|
12971
|
+
detailEdgeNodeIds: new Set(),
|
|
12972
|
+
multiSelectedIds: new Set()
|
|
12852
12973
|
});
|
|
12853
12974
|
if (internals.restoreTimeoutHandle !== null) {
|
|
12854
12975
|
clearTimeout(internals.restoreTimeoutHandle);
|
|
@@ -12887,7 +13008,8 @@ const useGraphStore = create$1()(persist((set$1, get$3) => {
|
|
|
12887
13008
|
...createAnalysisSlice(set$1, get$3),
|
|
12888
13009
|
...createPanelLodSlice(set$1, get$3),
|
|
12889
13010
|
...createKeywordQuerySlice(set$1, get$3),
|
|
12890
|
-
...createPathPatternSlice(set$1, get$3)
|
|
13011
|
+
...createPathPatternSlice(set$1, get$3),
|
|
13012
|
+
...createMultiSelectSlice(set$1, get$3)
|
|
12891
13013
|
};
|
|
12892
13014
|
registerStoreGetter(get$3);
|
|
12893
13015
|
return store;
|
|
@@ -58347,7 +58469,7 @@ var OverlapRemover = class {
|
|
|
58347
58469
|
|
|
58348
58470
|
//#endregion
|
|
58349
58471
|
//#region src/client/renderer/LayoutController.ts
|
|
58350
|
-
const log$
|
|
58472
|
+
const log$34 = scoped("layout");
|
|
58351
58473
|
var LayoutController = class {
|
|
58352
58474
|
cy;
|
|
58353
58475
|
layoutRaf = null;
|
|
@@ -58390,17 +58512,17 @@ var LayoutController = class {
|
|
|
58390
58512
|
this.worker?.terminate();
|
|
58391
58513
|
this.worker = null;
|
|
58392
58514
|
this.workerInitFailed = true;
|
|
58393
|
-
log$
|
|
58515
|
+
log$34.warn("layout worker error — falling back to main-thread layout");
|
|
58394
58516
|
this.failPendingLayout();
|
|
58395
58517
|
this.retryWorkerInit();
|
|
58396
58518
|
};
|
|
58397
58519
|
} else {
|
|
58398
58520
|
this.workerInitFailed = true;
|
|
58399
|
-
log$
|
|
58521
|
+
log$34.warn("layout worker not available — using main-thread layout");
|
|
58400
58522
|
}
|
|
58401
58523
|
} catch (e) {
|
|
58402
58524
|
this.workerInitFailed = true;
|
|
58403
|
-
log$
|
|
58525
|
+
log$34.warn("layout worker init failed — using main-thread layout", e);
|
|
58404
58526
|
}
|
|
58405
58527
|
}
|
|
58406
58528
|
/** Resolve an in-flight worker layout with no positions, so the caller falls back immediately. */
|
|
@@ -58426,7 +58548,7 @@ var LayoutController = class {
|
|
|
58426
58548
|
if (this.workerGaveUp) return;
|
|
58427
58549
|
if (this.workerRetryCount >= RENDERER.WORKER_MAX_INIT_RETRIES) {
|
|
58428
58550
|
this.workerGaveUp = true;
|
|
58429
|
-
log$
|
|
58551
|
+
log$34.warn(`layout worker unavailable after ${RENDERER.WORKER_MAX_INIT_RETRIES} retries — main-thread layout for the rest of this session`);
|
|
58430
58552
|
return;
|
|
58431
58553
|
}
|
|
58432
58554
|
this.workerRetryCount++;
|
|
@@ -58447,13 +58569,13 @@ var LayoutController = class {
|
|
|
58447
58569
|
const timeoutMs = this.workerReady ? RENDERER.LAYOUT_TIMEOUT_MS : RENDERER.WORKER_HANDSHAKE_TIMEOUT_MS;
|
|
58448
58570
|
return new Promise((resolve) => {
|
|
58449
58571
|
this.workerTimeout = setTimeout(() => {
|
|
58450
|
-
log$
|
|
58572
|
+
log$34.warn("layout worker timeout — falling back to main-thread layout");
|
|
58451
58573
|
this.failPendingLayout();
|
|
58452
58574
|
if (this.worker) {
|
|
58453
58575
|
this.worker.terminate();
|
|
58454
58576
|
this.worker = null;
|
|
58455
58577
|
this.workerInitFailed = true;
|
|
58456
|
-
log$
|
|
58578
|
+
log$34.warn("layout worker terminated due to timeout — retrying within the retry budget");
|
|
58457
58579
|
this.retryWorkerInit();
|
|
58458
58580
|
}
|
|
58459
58581
|
}, timeoutMs);
|
|
@@ -58527,7 +58649,7 @@ var LayoutController = class {
|
|
|
58527
58649
|
try {
|
|
58528
58650
|
this.runIncrementalLayout(layout$2, newNodeIds);
|
|
58529
58651
|
} catch (e) {
|
|
58530
|
-
log$
|
|
58652
|
+
log$34.warn("incremental layout failed — keeping the previous positions", e);
|
|
58531
58653
|
} finally {
|
|
58532
58654
|
this.layoutRaf = null;
|
|
58533
58655
|
}
|
|
@@ -59291,7 +59413,7 @@ function detectCyclesDFS(adjacency, maxCycles = MAX_CYCLES_DEFAULT) {
|
|
|
59291
59413
|
|
|
59292
59414
|
//#endregion
|
|
59293
59415
|
//#region src/client/renderer/CycleDetector.ts
|
|
59294
|
-
const log$
|
|
59416
|
+
const log$33 = scoped("cycle-detector");
|
|
59295
59417
|
var CycleDetector = class {
|
|
59296
59418
|
cy;
|
|
59297
59419
|
adjacencyCache;
|
|
@@ -59333,7 +59455,7 @@ var CycleDetector = class {
|
|
|
59333
59455
|
const cycles = new Set();
|
|
59334
59456
|
const nodeCount = this.cy.nodes().length;
|
|
59335
59457
|
if (nodeCount > CYCLE_DETECTION.MAX_NODES_FOR_DETECTION) {
|
|
59336
|
-
log$
|
|
59458
|
+
log$33.warn("cycle detection skipped: graph too large", { nodeCount });
|
|
59337
59459
|
return cycles;
|
|
59338
59460
|
}
|
|
59339
59461
|
const adjacencySet = this.getForwardAdjacency();
|
|
@@ -59346,7 +59468,7 @@ var CycleDetector = class {
|
|
|
59346
59468
|
detectCyclesDetailed() {
|
|
59347
59469
|
const nodeCount = this.cy.nodes().length;
|
|
59348
59470
|
if (nodeCount > CYCLE_DETECTION.MAX_NODES_FOR_DETECTION) {
|
|
59349
|
-
log$
|
|
59471
|
+
log$33.warn("detailed cycle detection skipped: graph too large", { nodeCount });
|
|
59350
59472
|
return [];
|
|
59351
59473
|
}
|
|
59352
59474
|
const adjacencySet = this.getForwardAdjacency();
|
|
@@ -59910,7 +60032,7 @@ function pushId(map$1, key, edgeId) {
|
|
|
59910
60032
|
|
|
59911
60033
|
//#endregion
|
|
59912
60034
|
//#region src/client/renderer/DataController.ts
|
|
59913
|
-
const log$
|
|
60035
|
+
const log$32 = scoped("data-controller");
|
|
59914
60036
|
/** 缺省结果:任何未表态路径都必须回到「必须失效」的安全方向。 */
|
|
59915
60037
|
const DIRTY = { kind: "dirty" };
|
|
59916
60038
|
var DataController = class {
|
|
@@ -60168,7 +60290,7 @@ var DataController = class {
|
|
|
60168
60290
|
expectedVersion: cache$1.getVersion()
|
|
60169
60291
|
};
|
|
60170
60292
|
} catch (err) {
|
|
60171
|
-
log$
|
|
60293
|
+
log$32.warn("adjacency reconcile failed — invalidating cache", { err: String(err) });
|
|
60172
60294
|
cache$1.invalidate();
|
|
60173
60295
|
return DIRTY;
|
|
60174
60296
|
}
|
|
@@ -60234,7 +60356,7 @@ var DataController = class {
|
|
|
60234
60356
|
}
|
|
60235
60357
|
addEdge(e) {
|
|
60236
60358
|
if (this.cy.getElementById(e.source).empty() || this.cy.getElementById(e.target).empty()) {
|
|
60237
|
-
log$
|
|
60359
|
+
log$32.warn("edge dropped: missing endpoint", {
|
|
60238
60360
|
edgeId: e.id,
|
|
60239
60361
|
source: e.source,
|
|
60240
60362
|
target: e.target
|
|
@@ -60397,7 +60519,7 @@ var DataController = class {
|
|
|
60397
60519
|
|
|
60398
60520
|
//#endregion
|
|
60399
60521
|
//#region src/client/renderer/FilterController.ts
|
|
60400
|
-
const log$
|
|
60522
|
+
const log$31 = scoped("filter");
|
|
60401
60523
|
var FilterController = class {
|
|
60402
60524
|
cy;
|
|
60403
60525
|
fanInCache = null;
|
|
@@ -60554,7 +60676,7 @@ var FilterController = class {
|
|
|
60554
60676
|
if (/(\+|\*|\{)\{/.test(pattern)) return null;
|
|
60555
60677
|
return new RegExp(pattern);
|
|
60556
60678
|
} catch (e) {
|
|
60557
|
-
log$
|
|
60679
|
+
log$31.warn("invalid regex pattern", e);
|
|
60558
60680
|
return null;
|
|
60559
60681
|
}
|
|
60560
60682
|
}
|
|
@@ -60722,7 +60844,7 @@ var Trie = class {
|
|
|
60722
60844
|
|
|
60723
60845
|
//#endregion
|
|
60724
60846
|
//#region src/client/renderer/SearchController.ts
|
|
60725
|
-
const log$
|
|
60847
|
+
const log$30 = scoped("search");
|
|
60726
60848
|
const BUILD_CHUNK_SIZE = 200;
|
|
60727
60849
|
const BUILD_CHUNK_DELAY = 16;
|
|
60728
60850
|
function requestIdleCallbackCompat(cb) {
|
|
@@ -60764,7 +60886,7 @@ var SearchController = class {
|
|
|
60764
60886
|
this.trieBuilt = true;
|
|
60765
60887
|
this.indexBuilding = false;
|
|
60766
60888
|
this.buildHandle = null;
|
|
60767
|
-
log$
|
|
60889
|
+
log$30.info(`Trie index built: ${entries.length} nodes`);
|
|
60768
60890
|
}
|
|
60769
60891
|
};
|
|
60770
60892
|
processChunk();
|
|
@@ -61535,10 +61657,7 @@ var InteractionController = class InteractionController {
|
|
|
61535
61657
|
this.callbacks.onSelectionToggle?.(id, selected, [...this.multiSelectedIds]);
|
|
61536
61658
|
this.callbacks.onNodeTap?.(id);
|
|
61537
61659
|
} else {
|
|
61538
|
-
|
|
61539
|
-
this.multiSelectedIds.clear();
|
|
61540
|
-
this.cy.nodes().removeClass("multi-selected");
|
|
61541
|
-
}
|
|
61660
|
+
this.clearMultiSelect();
|
|
61542
61661
|
this.callbacks.onNodeTap?.(id);
|
|
61543
61662
|
}
|
|
61544
61663
|
this.focusNodeId = id;
|
|
@@ -61589,6 +61708,14 @@ var InteractionController = class InteractionController {
|
|
|
61589
61708
|
y: pos.y
|
|
61590
61709
|
});
|
|
61591
61710
|
});
|
|
61711
|
+
this.cy.on("boxend", () => {
|
|
61712
|
+
const ids = this.cy.$(":selected").nodes().map((n) => n.id());
|
|
61713
|
+
for (const id of ids) this.multiSelectedIds.add(id);
|
|
61714
|
+
this.cy.nodes().forEach((n) => {
|
|
61715
|
+
if (this.multiSelectedIds.has(n.id())) n.addClass("multi-selected");
|
|
61716
|
+
});
|
|
61717
|
+
this.callbacks.onSelectionToggle?.(ids[0] ?? "", ids.length > 0, [...this.multiSelectedIds]);
|
|
61718
|
+
});
|
|
61592
61719
|
}
|
|
61593
61720
|
/**
|
|
61594
61721
|
* 绑定画布容器上的键盘 / 焦点事件。
|
|
@@ -61737,6 +61864,20 @@ var InteractionController = class InteractionController {
|
|
|
61737
61864
|
this.callbacks = callbacks;
|
|
61738
61865
|
}
|
|
61739
61866
|
/**
|
|
61867
|
+
* 清空多选集合(cycle-149):集合 + `multi-selected` class + 通知消费方一体完成。
|
|
61868
|
+
*
|
|
61869
|
+
* 两个触发方共用本方法(单一实现,防漂移):普通点击的静默清空、
|
|
61870
|
+
* 批量操作条「清除」按钮经渲染器 `clearMultiSelect` 的显式清空。
|
|
61871
|
+
* `onSelectionClear` 让 store 镜像同步——缺了它消费方留下幽灵计数
|
|
61872
|
+
* (「画布无高亮但 BatchActionBar 还在」)。
|
|
61873
|
+
*/
|
|
61874
|
+
clearMultiSelect() {
|
|
61875
|
+
if (this.multiSelectedIds.size === 0) return;
|
|
61876
|
+
this.multiSelectedIds.clear();
|
|
61877
|
+
this.cy.nodes().removeClass("multi-selected");
|
|
61878
|
+
this.callbacks.onSelectionClear?.();
|
|
61879
|
+
}
|
|
61880
|
+
/**
|
|
61740
61881
|
* 切换节点 / 边标签的可见性(cycle-141 / D)。
|
|
61741
61882
|
*
|
|
61742
61883
|
* 实现放在本控制器而不是 `StyleController`:标签显隐是**交互态**而非主题态,
|
|
@@ -61768,6 +61909,7 @@ var InteractionController = class InteractionController {
|
|
|
61768
61909
|
this.cy.off("mouseover", "edge");
|
|
61769
61910
|
this.cy.off("mouseout", "edge");
|
|
61770
61911
|
this.cy.off("cxttap", "node");
|
|
61912
|
+
this.cy.off("boxend");
|
|
61771
61913
|
}
|
|
61772
61914
|
};
|
|
61773
61915
|
|
|
@@ -62265,7 +62407,7 @@ var VirtualViewportController = class {
|
|
|
62265
62407
|
|
|
62266
62408
|
//#endregion
|
|
62267
62409
|
//#region src/client/renderer/CytoscapeRenderer.ts
|
|
62268
|
-
const log$
|
|
62410
|
+
const log$29 = scoped("cytoscape-renderer");
|
|
62269
62411
|
function ensureExtensionsRegistered() {
|
|
62270
62412
|
const cy = cytoscape$1;
|
|
62271
62413
|
if (!cy.__dagreRegistered) {
|
|
@@ -62300,7 +62442,7 @@ var CytoscapeRenderer = class {
|
|
|
62300
62442
|
try {
|
|
62301
62443
|
return fn$7();
|
|
62302
62444
|
} catch (err) {
|
|
62303
|
-
log$
|
|
62445
|
+
log$29.error(`${name$1} failed`, err);
|
|
62304
62446
|
this.lastError = err instanceof Error ? err : new Error(String(err));
|
|
62305
62447
|
return fallback;
|
|
62306
62448
|
}
|
|
@@ -62309,7 +62451,7 @@ var CytoscapeRenderer = class {
|
|
|
62309
62451
|
try {
|
|
62310
62452
|
fn$7();
|
|
62311
62453
|
} catch (err) {
|
|
62312
|
-
log$
|
|
62454
|
+
log$29.error(`${name$1} failed`, err);
|
|
62313
62455
|
this.lastError = err instanceof Error ? err : new Error(String(err));
|
|
62314
62456
|
}
|
|
62315
62457
|
}
|
|
@@ -62356,7 +62498,9 @@ var CytoscapeRenderer = class {
|
|
|
62356
62498
|
onNodeHoverOut: this.options.onNodeHoverOut ?? (() => {}),
|
|
62357
62499
|
onEdgeHover: this.options.onEdgeHover ?? (() => {}),
|
|
62358
62500
|
onEdgeHoverOut: this.options.onEdgeHoverOut ?? (() => {}),
|
|
62359
|
-
...this.options.onNodeContextTap ? { onNodeContextTap: this.options.onNodeContextTap } : {}
|
|
62501
|
+
...this.options.onNodeContextTap ? { onNodeContextTap: this.options.onNodeContextTap } : {},
|
|
62502
|
+
onSelectionToggle: this.options.onSelectionToggle ?? (() => {}),
|
|
62503
|
+
onSelectionClear: this.options.onSelectionClear
|
|
62360
62504
|
});
|
|
62361
62505
|
this.pathFinder = new PathFinder(cy, this.adjacencyCache);
|
|
62362
62506
|
this.virtualViewportCtrl = new VirtualViewportController(cy);
|
|
@@ -62435,27 +62579,16 @@ var CytoscapeRenderer = class {
|
|
|
62435
62579
|
this.layoutCtrl?.applyWorkerLayout(positions);
|
|
62436
62580
|
}
|
|
62437
62581
|
/**
|
|
62438
|
-
|
|
62439
62582
|
* 开启虚拟视口(视口外的节点不渲染)。
|
|
62440
|
-
|
|
62441
62583
|
*
|
|
62442
|
-
|
|
62443
62584
|
* 这里额外往容器上写一个 `data-cg-virtual-scroll` 标记,原因是这个特性在 DOM 上
|
|
62444
|
-
|
|
62445
62585
|
* **完全不可观测**:它只改 cytoscape 内部的 `display` 状态,元素还在、canvas 还在,
|
|
62446
|
-
|
|
62447
62586
|
* 任何「元素可见」类断言在开启与未开启时结果相同。cycle-93 之前 E2E 的 J35
|
|
62448
|
-
|
|
62449
62587
|
* 因此只能断言「容器可见」,零鉴别力。
|
|
62450
|
-
|
|
62451
62588
|
*
|
|
62452
|
-
|
|
62453
62589
|
* 标记沿用本仓库既有的 `data-cg-*` 集成标记约定(`useGraphDerivedValues.ts:71` 的
|
|
62454
|
-
|
|
62455
62590
|
* `data-cg-integration`)。写成「只有真的启用成功才标记」,避免
|
|
62456
|
-
|
|
62457
62591
|
* `virtualViewportCtrl` 为 null 时留下一个骗人的标记。
|
|
62458
|
-
|
|
62459
62592
|
*/
|
|
62460
62593
|
enableVirtualScroll() {
|
|
62461
62594
|
this.virtualViewportCtrl?.enable();
|
|
@@ -62502,17 +62635,11 @@ var CytoscapeRenderer = class {
|
|
|
62502
62635
|
this.viewportCtrl?.focusOnNode(nodeId);
|
|
62503
62636
|
}
|
|
62504
62637
|
/**
|
|
62505
|
-
|
|
62506
62638
|
* Bring a node the user explicitly asked for (a search hit, a host symbol link) into view even
|
|
62507
|
-
|
|
62508
62639
|
* when it is currently hidden, then centre the viewport on it. Returns `false` when the node is
|
|
62509
|
-
|
|
62510
62640
|
* not in the rendered graph at all, or when it is excluded by an active type/connection filter —
|
|
62511
|
-
|
|
62512
62641
|
* silently panning to an element the filter keeps invisible is what made "search a hidden node"
|
|
62513
|
-
|
|
62514
62642
|
* look like a no-op.
|
|
62515
|
-
|
|
62516
62643
|
*/
|
|
62517
62644
|
revealNode(nodeId) {
|
|
62518
62645
|
return this.safeCall("revealNode", () => {
|
|
@@ -62536,15 +62663,10 @@ var CytoscapeRenderer = class {
|
|
|
62536
62663
|
return el.data("typeFiltered") === true || el.data("connectionFiltered") === true;
|
|
62537
62664
|
}
|
|
62538
62665
|
/**
|
|
62539
|
-
|
|
62540
62666
|
* Viewport-space position used to anchor the context menu when the user opens it without a
|
|
62541
|
-
|
|
62542
62667
|
* pointer. Cytoscape keeps reporting the last computed position for `display: none` elements,
|
|
62543
|
-
|
|
62544
62668
|
* and the menu clamps itself into the viewport, so only a node that is really absent returns
|
|
62545
|
-
|
|
62546
62669
|
* `null`. Cytoscape 的渲染坐标相对画布容器,这里叠加容器原点换算成视口坐标。
|
|
62547
|
-
|
|
62548
62670
|
*/
|
|
62549
62671
|
getNodeRenderedPosition(nodeId) {
|
|
62550
62672
|
return this.safeCall("getNodeRenderedPosition", () => {
|
|
@@ -62595,6 +62717,10 @@ var CytoscapeRenderer = class {
|
|
|
62595
62717
|
focusNeighborhood(nodeId) {
|
|
62596
62718
|
this.safeCallVoid("focusNeighborhood", () => this.viewportCtrl?.focusNeighborhood(nodeId));
|
|
62597
62719
|
}
|
|
62720
|
+
/** 清空多选集合(cycle-149):委托 InteractionController(集合+class+回调一体)。 */
|
|
62721
|
+
clearMultiSelect() {
|
|
62722
|
+
this.safeCallVoid("clearMultiSelect", () => this.interactionCtrl?.clearMultiSelect());
|
|
62723
|
+
}
|
|
62598
62724
|
getThumbnail() {
|
|
62599
62725
|
return this.viewportCtrl?.getThumbnail() ?? null;
|
|
62600
62726
|
}
|
|
@@ -62664,7 +62790,7 @@ var CytoscapeRenderer = class {
|
|
|
62664
62790
|
try {
|
|
62665
62791
|
fn$7();
|
|
62666
62792
|
} catch (err) {
|
|
62667
|
-
log$
|
|
62793
|
+
log$29.warn(`destroy: ${label} cleanup failed`, err);
|
|
62668
62794
|
}
|
|
62669
62795
|
};
|
|
62670
62796
|
safeDestroy("interactionCtrl", () => this.interactionCtrl?.destroy());
|
|
@@ -62758,21 +62884,13 @@ var CytoscapeRenderer = class {
|
|
|
62758
62884
|
});
|
|
62759
62885
|
}
|
|
62760
62886
|
/**
|
|
62761
|
-
|
|
62762
62887
|
* diff 标记的渲染器入口(研究报告 §六 #13)。
|
|
62763
|
-
|
|
62764
62888
|
*
|
|
62765
|
-
|
|
62766
62889
|
* 实现全部委托给 `HighlightController`:它已经持有 `cy` 并且是「视图态 class」的归属地,
|
|
62767
|
-
|
|
62768
62890
|
* 把标记逻辑写两份必然漂移(`markDeadCode` 就是直接写在这里的,代价是它没有复用任何
|
|
62769
|
-
|
|
62770
62891
|
* 控制器设施)。渲染器本身只做 `safeCall` 包裹与「控制器不存在时给空统计」的兜底——
|
|
62771
|
-
|
|
62772
62892
|
* 后者很重要:控制器为 `null` 时若返回 `undefined`,调用方读 `.counts.added` 会抛错,
|
|
62773
|
-
|
|
62774
62893
|
* 而这条路径在「渲染器尚未完成 init」时是真实可达的。
|
|
62775
|
-
|
|
62776
62894
|
*/
|
|
62777
62895
|
applyDiffMarkers(index) {
|
|
62778
62896
|
const empty = {
|
|
@@ -62796,7 +62914,7 @@ var CytoscapeRenderer = class {
|
|
|
62796
62914
|
this.cy.nodes().stop();
|
|
62797
62915
|
this.cy.edges().stop();
|
|
62798
62916
|
} catch (e) {
|
|
62799
|
-
log$
|
|
62917
|
+
log$29.warn("pauseAnimations failed", e);
|
|
62800
62918
|
}
|
|
62801
62919
|
}
|
|
62802
62920
|
resumeAnimations() {
|
|
@@ -62804,7 +62922,7 @@ var CytoscapeRenderer = class {
|
|
|
62804
62922
|
try {
|
|
62805
62923
|
this.cy.resize();
|
|
62806
62924
|
} catch (e) {
|
|
62807
|
-
log$
|
|
62925
|
+
log$29.warn("resumeAnimations failed", e);
|
|
62808
62926
|
}
|
|
62809
62927
|
}
|
|
62810
62928
|
/** 缩放画布:factor > 1 放大,factor < 1 缩小。带 200ms 动画过渡。 */
|
|
@@ -62843,7 +62961,7 @@ var CytoscapeRenderer = class {
|
|
|
62843
62961
|
|
|
62844
62962
|
//#endregion
|
|
62845
62963
|
//#region src/client/hooks/useGraphRenderer.ts
|
|
62846
|
-
const log$
|
|
62964
|
+
const log$28 = scoped("renderer-hook");
|
|
62847
62965
|
function useGraphRenderer(config) {
|
|
62848
62966
|
const { data: data$2, display, interaction, filters, analysis, callbacks, showCallChainRef } = config;
|
|
62849
62967
|
const { nodes, edges, rawNodes, repoId } = data$2;
|
|
@@ -62885,17 +63003,19 @@ function useGraphRenderer(config) {
|
|
|
62885
63003
|
onNodeHoverOut: () => callbacksRef.current.onNodeHoverOut(),
|
|
62886
63004
|
onEdgeTap: (edgeId, pos) => callbacksRef.current.onEdgeTap(edgeId, pos),
|
|
62887
63005
|
onEdgeHover: (edgeId, pos) => callbacksRef.current.onEdgeHover(edgeId, pos),
|
|
62888
|
-
onEdgeHoverOut: () => callbacksRef.current.onEdgeHoverOut()
|
|
63006
|
+
onEdgeHoverOut: () => callbacksRef.current.onEdgeHoverOut(),
|
|
63007
|
+
onSelectionToggle: (nodeId, selected, selectedIds) => callbacksRef.current.onSelectionToggle?.(nodeId, selected, selectedIds),
|
|
63008
|
+
onSelectionClear: () => callbacksRef.current.onSelectionClear?.()
|
|
62889
63009
|
});
|
|
62890
63010
|
renderer$1.init();
|
|
62891
63011
|
rendererRef.current = renderer$1;
|
|
62892
|
-
log$
|
|
63012
|
+
log$28.info("renderer initialized", {
|
|
62893
63013
|
theme,
|
|
62894
63014
|
w,
|
|
62895
63015
|
h
|
|
62896
63016
|
});
|
|
62897
63017
|
} catch (err) {
|
|
62898
|
-
log$
|
|
63018
|
+
log$28.error("renderer init failed", { err });
|
|
62899
63019
|
renderer$1?.destroy();
|
|
62900
63020
|
renderer$1 = null;
|
|
62901
63021
|
useGraphStore.getState().setError(err instanceof Error ? err.message : String(err));
|
|
@@ -63143,7 +63263,7 @@ function useGraphRenderer(config) {
|
|
|
63143
63263
|
a.download = `codegraph-${Date.now()}.${extension$1}`;
|
|
63144
63264
|
a.click();
|
|
63145
63265
|
setTimeout(() => URL.revokeObjectURL(url), UI.URL_REVOKE_DELAY_MS);
|
|
63146
|
-
log$
|
|
63266
|
+
log$28.info(`exported ${format}`);
|
|
63147
63267
|
}
|
|
63148
63268
|
}, [theme]);
|
|
63149
63269
|
const updateTheme = useCallback((newTheme) => {
|
|
@@ -63279,7 +63399,7 @@ function useFocusMode(opts) {
|
|
|
63279
63399
|
|
|
63280
63400
|
//#endregion
|
|
63281
63401
|
//#region src/client/hooks/useNodeInteraction.ts
|
|
63282
|
-
const log$
|
|
63402
|
+
const log$27 = scoped("panel");
|
|
63283
63403
|
function useNodeInteraction(opts) {
|
|
63284
63404
|
const { setSelectedNode, setSelectedNodeData, getDataRef, adjacencyMap, setTooltip, expandedNodeIds, collapseNode, expandNode, handleSelectFunction, setFocusNodeId, handleNodeDoubleTapRef } = opts;
|
|
63285
63405
|
const handleNodeTap = useCallback((id) => {
|
|
@@ -63288,7 +63408,7 @@ function useNodeInteraction(opts) {
|
|
|
63288
63408
|
const data$2 = getDataRef.current?.getNodeData(id) ?? null;
|
|
63289
63409
|
setSelectedNodeData(data$2);
|
|
63290
63410
|
} catch (err) {
|
|
63291
|
-
log$
|
|
63411
|
+
log$27.error("handleNodeTap failed", {
|
|
63292
63412
|
id,
|
|
63293
63413
|
err
|
|
63294
63414
|
});
|
|
@@ -63320,7 +63440,7 @@ function useNodeInteraction(opts) {
|
|
|
63320
63440
|
});
|
|
63321
63441
|
}
|
|
63322
63442
|
} catch (err) {
|
|
63323
|
-
log$
|
|
63443
|
+
log$27.error("handleNodeHover failed", {
|
|
63324
63444
|
nodeId,
|
|
63325
63445
|
err
|
|
63326
63446
|
});
|
|
@@ -63371,7 +63491,7 @@ function useNodeInteraction(opts) {
|
|
|
63371
63491
|
});
|
|
63372
63492
|
}
|
|
63373
63493
|
} catch (err) {
|
|
63374
|
-
log$
|
|
63494
|
+
log$27.error("handleEdgeTap failed", {
|
|
63375
63495
|
edgeId,
|
|
63376
63496
|
err
|
|
63377
63497
|
});
|
|
@@ -63398,7 +63518,7 @@ function useNodeInteraction(opts) {
|
|
|
63398
63518
|
return nodeId;
|
|
63399
63519
|
});
|
|
63400
63520
|
} catch (err) {
|
|
63401
|
-
log$
|
|
63521
|
+
log$27.error("handleNodeDoubleTap failed", {
|
|
63402
63522
|
nodeId,
|
|
63403
63523
|
err
|
|
63404
63524
|
});
|
|
@@ -63764,7 +63884,7 @@ function useDeadCodeMarker({ showDeadCode, deadCodeResult, rendererRef }) {
|
|
|
63764
63884
|
|
|
63765
63885
|
//#endregion
|
|
63766
63886
|
//#region src/client/hooks/useDiffComparison.ts
|
|
63767
|
-
const log$
|
|
63887
|
+
const log$26 = scoped("diff");
|
|
63768
63888
|
/**
|
|
63769
63889
|
|
|
63770
63890
|
* 版本对比的编排层(研究报告 §六 #13)。
|
|
@@ -63857,7 +63977,7 @@ function useDiffComparison(params) {
|
|
|
63857
63977
|
return next$1;
|
|
63858
63978
|
} catch (e) {
|
|
63859
63979
|
if (!aliveRef.current || gen !== generationRef.current) return null;
|
|
63860
|
-
log$
|
|
63980
|
+
log$26.warn("diff comparison failed", e);
|
|
63861
63981
|
setError(e instanceof Error ? e.message : String(e));
|
|
63862
63982
|
setIsComparing(false);
|
|
63863
63983
|
return null;
|
|
@@ -64364,19 +64484,12 @@ const KEYBOARD_SHORTCUTS = [
|
|
|
64364
64484
|
}
|
|
64365
64485
|
];
|
|
64366
64486
|
/**
|
|
64367
|
-
|
|
64368
64487
|
* 画布级快捷键(cycle-141 / D)。
|
|
64369
|
-
|
|
64370
64488
|
*
|
|
64371
|
-
|
|
64372
64489
|
* 与 `KEYBOARD_SHORTCUTS` 分开声明,**不注册全局监听**:面板级快捷键挂在
|
|
64373
|
-
|
|
64374
64490
|
* `document` 上、任何焦点下都生效;画布级只在 `.cg-graph-container` 聚焦时由
|
|
64375
|
-
|
|
64376
64491
|
* `InteractionController` 接管。此处仅作为键盘提示浮层与文档的元数据,
|
|
64377
|
-
|
|
64378
64492
|
* 避免方向键劫持页面滚动、也避免 `usePanelKeyboard` 的测试长度断言被悄悄改变。
|
|
64379
|
-
|
|
64380
64493
|
*/
|
|
64381
64494
|
const CANVAS_KEYBOARD_SHORTCUTS = [
|
|
64382
64495
|
{
|
|
@@ -64414,6 +64527,10 @@ const CANVAS_KEYBOARD_SHORTCUTS = [
|
|
|
64414
64527
|
{
|
|
64415
64528
|
key: "F",
|
|
64416
64529
|
descKey: "canvasControls.shortcutFit"
|
|
64530
|
+
},
|
|
64531
|
+
{
|
|
64532
|
+
key: "Shift+Drag",
|
|
64533
|
+
descKey: "canvasControls.shortcutBoxSelect"
|
|
64417
64534
|
}
|
|
64418
64535
|
];
|
|
64419
64536
|
function formatShortcutKey(s) {
|
|
@@ -64477,7 +64594,7 @@ function usePanelKeyboard(layout$2, setLayout, rendererRef, handlers) {
|
|
|
64477
64594
|
|
|
64478
64595
|
//#endregion
|
|
64479
64596
|
//#region src/client/hooks/useViewMode.ts
|
|
64480
|
-
const log$
|
|
64597
|
+
const log$25 = scoped("viewMode");
|
|
64481
64598
|
const MICRO_MODES = [
|
|
64482
64599
|
"cfg",
|
|
64483
64600
|
"flowchart",
|
|
@@ -64520,7 +64637,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
64520
64637
|
if (controller.signal.aborted) return;
|
|
64521
64638
|
if (!data$2.nodes || data$2.nodes.length === 0) return;
|
|
64522
64639
|
if (isForeignRepo(data$2.metadata?.repoId, requestedWorkspace)) {
|
|
64523
|
-
log$
|
|
64640
|
+
log$25.info("dependency graph ignored for another workspace", {
|
|
64524
64641
|
repoId: data$2.metadata?.repoId,
|
|
64525
64642
|
requestedWorkspace,
|
|
64526
64643
|
activeWorkspace: currentWorkspace
|
|
@@ -64545,7 +64662,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
64545
64662
|
else setError(t$1("error.loadGraphData"));
|
|
64546
64663
|
setLoading(false);
|
|
64547
64664
|
}).catch((e) => {
|
|
64548
|
-
log$
|
|
64665
|
+
log$25.warn("loadBootstrap failed", e);
|
|
64549
64666
|
setError(t$1("error.loadGraph"));
|
|
64550
64667
|
setLoading(false);
|
|
64551
64668
|
});
|
|
@@ -64574,7 +64691,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
64574
64691
|
}).then((data$2) => {
|
|
64575
64692
|
if (controller.signal.aborted) return;
|
|
64576
64693
|
if (isForeignRepo(data$2.metadata?.repoId, requestedWorkspace)) {
|
|
64577
|
-
log$
|
|
64694
|
+
log$25.info("macro view graph ignored for another workspace", {
|
|
64578
64695
|
repoId: data$2.metadata?.repoId,
|
|
64579
64696
|
requestedWorkspace,
|
|
64580
64697
|
activeWorkspace: currentWorkspace
|
|
@@ -64582,7 +64699,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
64582
64699
|
return;
|
|
64583
64700
|
}
|
|
64584
64701
|
if (!data$2.nodes || data$2.nodes.length === 0) return;
|
|
64585
|
-
log$
|
|
64702
|
+
log$25.info("macro view data loaded", {
|
|
64586
64703
|
viewMode: newMode,
|
|
64587
64704
|
nodes: data$2.nodes.length,
|
|
64588
64705
|
edges: data$2.edges?.length ?? 0,
|
|
@@ -66629,7 +66746,7 @@ function useWorkspaceCache(opts) {
|
|
|
66629
66746
|
|
|
66630
66747
|
//#endregion
|
|
66631
66748
|
//#region src/client/hooks/useSymbolSelectListener.ts
|
|
66632
|
-
const log$
|
|
66749
|
+
const log$24 = scoped("symbol-select");
|
|
66633
66750
|
function useSymbolSelectListener(opts) {
|
|
66634
66751
|
useEffect(() => {
|
|
66635
66752
|
const symbolHandler = (e) => {
|
|
@@ -66641,7 +66758,7 @@ function useSymbolSelectListener(opts) {
|
|
|
66641
66758
|
if (!match$1) return;
|
|
66642
66759
|
store.setSelectedNode(NodeId(match$1.id));
|
|
66643
66760
|
const revealed = opts.rendererRef.current?.revealNode(match$1.id) ?? false;
|
|
66644
|
-
if (!revealed) log$
|
|
66761
|
+
if (!revealed) log$24.warn("symbol match is not revealable in the rendered graph", {
|
|
66645
66762
|
id: match$1.id,
|
|
66646
66763
|
symbol: sym
|
|
66647
66764
|
});
|
|
@@ -66737,7 +66854,7 @@ function useMiniMapSync(opts) {
|
|
|
66737
66854
|
|
|
66738
66855
|
//#endregion
|
|
66739
66856
|
//#region src/client/hooks/useAutoBootstrap.ts
|
|
66740
|
-
const log$
|
|
66857
|
+
const log$23 = scoped("auto-bootstrap");
|
|
66741
66858
|
function useAutoBootstrap(opts) {
|
|
66742
66859
|
const hasAutoScannedRef = useRef(false);
|
|
66743
66860
|
useEffect(() => {
|
|
@@ -66747,10 +66864,10 @@ function useAutoBootstrap(opts) {
|
|
|
66747
66864
|
if (!opts.codegraphReady) return;
|
|
66748
66865
|
if (!opts.currentWorkspace) return;
|
|
66749
66866
|
hasAutoScannedRef.current = true;
|
|
66750
|
-
log$
|
|
66867
|
+
log$23.info("auto-fill: triggering bootstrap on first open", { path: opts.currentWorkspace });
|
|
66751
66868
|
useGraphStore.getState().loadBootstrap().then((ok) => {
|
|
66752
66869
|
if (!ok) {
|
|
66753
|
-
log$
|
|
66870
|
+
log$23.info("auto-fill: bootstrap failed, falling back to scan", { path: opts.currentWorkspace });
|
|
66754
66871
|
opts.handleScanWorkspace();
|
|
66755
66872
|
}
|
|
66756
66873
|
});
|
|
@@ -67005,7 +67122,7 @@ function useViewModePanelGuard(opts) {
|
|
|
67005
67122
|
|
|
67006
67123
|
//#endregion
|
|
67007
67124
|
//#region src/client/hooks/useSearchInteraction.ts
|
|
67008
|
-
const log$
|
|
67125
|
+
const log$22 = scoped("panel");
|
|
67009
67126
|
function useSearchInteraction(opts) {
|
|
67010
67127
|
const { searchQuery, rawNodes, setSelectedNode, setSelectedNodeData, getDataRef, setShowSearch, isRemoteHit } = opts;
|
|
67011
67128
|
const debouncedSearch = useDebounce(searchQuery, UI.SEARCH_DEBOUNCE_MS);
|
|
@@ -67057,7 +67174,7 @@ function useSearchInteraction(opts) {
|
|
|
67057
67174
|
setShowSearch(false);
|
|
67058
67175
|
const hitLabel = mergedResults.find((r) => r.id === id)?.label ?? id;
|
|
67059
67176
|
getDataRef.current.revealRemoteNode(id, hitLabel).then((ok) => {
|
|
67060
|
-
if (!ok) log$
|
|
67177
|
+
if (!ok) log$22.warn("remote search hit could not be revealed", { id });
|
|
67061
67178
|
});
|
|
67062
67179
|
return;
|
|
67063
67180
|
}
|
|
@@ -67065,10 +67182,10 @@ function useSearchInteraction(opts) {
|
|
|
67065
67182
|
const data$2 = getDataRef.current?.getNodeData(NodeId(id)) ?? null;
|
|
67066
67183
|
setSelectedNodeData(data$2);
|
|
67067
67184
|
const revealed = getDataRef.current?.revealNode(id) ?? false;
|
|
67068
|
-
if (!revealed) log$
|
|
67185
|
+
if (!revealed) log$22.warn("search result is not revealable in the rendered graph", { id });
|
|
67069
67186
|
setShowSearch(false);
|
|
67070
67187
|
} catch (err) {
|
|
67071
|
-
log$
|
|
67188
|
+
log$22.error("handleSelectSearchResult failed", {
|
|
67072
67189
|
id,
|
|
67073
67190
|
err
|
|
67074
67191
|
});
|
|
@@ -67090,7 +67207,7 @@ function useSearchInteraction(opts) {
|
|
|
67090
67207
|
|
|
67091
67208
|
//#endregion
|
|
67092
67209
|
//#region src/client/components/ErrorBoundary.tsx
|
|
67093
|
-
const log$
|
|
67210
|
+
const log$21 = scoped("boundary");
|
|
67094
67211
|
function ErrorFallback({ error: error$1, onRetry }) {
|
|
67095
67212
|
const t$1 = useT();
|
|
67096
67213
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -67133,7 +67250,7 @@ var GraphErrorBoundary = class extends Component {
|
|
|
67133
67250
|
}
|
|
67134
67251
|
componentDidCatch(error$1, errorInfo) {
|
|
67135
67252
|
this.props.onError?.(error$1, errorInfo);
|
|
67136
|
-
log$
|
|
67253
|
+
log$21.error("render error caught by boundary", {
|
|
67137
67254
|
error: error$1,
|
|
67138
67255
|
componentStack: errorInfo.componentStack
|
|
67139
67256
|
});
|
|
@@ -69146,6 +69263,10 @@ const ContextMenu = memo(function ContextMenu$1(props) {
|
|
|
69146
69263
|
const menuRef = useRef(null);
|
|
69147
69264
|
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
69148
69265
|
const triggerRef = useRef(null);
|
|
69266
|
+
const isPinned = useGraphStore((s) => s.pinnedIds.has(props.nodeId));
|
|
69267
|
+
const pinNode = useGraphStore((s) => s.pinNode);
|
|
69268
|
+
const unpinNode = useGraphStore((s) => s.unpinNode);
|
|
69269
|
+
const [copyState, setCopyState] = useState("idle");
|
|
69149
69270
|
const onCloseRef = useRef(props.onClose);
|
|
69150
69271
|
onCloseRef.current = props.onClose;
|
|
69151
69272
|
const handleClose = useCallback(() => {
|
|
@@ -69304,6 +69425,43 @@ const ContextMenu = memo(function ContextMenu$1(props) {
|
|
|
69304
69425
|
handleClose();
|
|
69305
69426
|
}
|
|
69306
69427
|
});
|
|
69428
|
+
actions.push({
|
|
69429
|
+
id: "pinNode",
|
|
69430
|
+
label: t$1(isPinned ? "contextMenu.unpinNode" : "contextMenu.pinNode"),
|
|
69431
|
+
onClick: () => {
|
|
69432
|
+
if (isPinned) unpinNode(props.nodeId);
|
|
69433
|
+
else pinNode(props.nodeId);
|
|
69434
|
+
handleClose();
|
|
69435
|
+
}
|
|
69436
|
+
});
|
|
69437
|
+
actions.push({
|
|
69438
|
+
id: "copyId",
|
|
69439
|
+
label: copyState === "copied" ? t$1("contextMenu.copied") : t$1("contextMenu.copyId"),
|
|
69440
|
+
onClick: () => {
|
|
69441
|
+
try {
|
|
69442
|
+
navigator.clipboard?.writeText(props.nodeId);
|
|
69443
|
+
setCopyState("copied");
|
|
69444
|
+
window.setTimeout(() => setCopyState("idle"), 1500);
|
|
69445
|
+
} catch {}
|
|
69446
|
+
}
|
|
69447
|
+
});
|
|
69448
|
+
actions.push({
|
|
69449
|
+
id: "expandHere",
|
|
69450
|
+
label: t$1("contextMenu.expandHere"),
|
|
69451
|
+
onClick: () => {
|
|
69452
|
+
props.onExpandNode(props.nodeId);
|
|
69453
|
+
handleClose();
|
|
69454
|
+
},
|
|
69455
|
+
separatorAfter: true
|
|
69456
|
+
});
|
|
69457
|
+
actions.push({
|
|
69458
|
+
id: "collapseHere",
|
|
69459
|
+
label: t$1("contextMenu.collapseHere"),
|
|
69460
|
+
onClick: () => {
|
|
69461
|
+
props.onCollapseNode(props.nodeId);
|
|
69462
|
+
handleClose();
|
|
69463
|
+
}
|
|
69464
|
+
});
|
|
69307
69465
|
const menuStyle = {
|
|
69308
69466
|
position: "fixed",
|
|
69309
69467
|
left: `${props.position.x}px`,
|
|
@@ -69317,6 +69475,7 @@ const ContextMenu = memo(function ContextMenu$1(props) {
|
|
|
69317
69475
|
children: actions.map((action) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsxs("button", {
|
|
69318
69476
|
className: "cg-context-menu-item",
|
|
69319
69477
|
role: "menuitem",
|
|
69478
|
+
"data-action-id": action.id,
|
|
69320
69479
|
disabled: action.disabled,
|
|
69321
69480
|
onClick: action.onClick,
|
|
69322
69481
|
children: [action.icon && /* @__PURE__ */ jsx("span", {
|
|
@@ -69331,6 +69490,87 @@ const ContextMenu = memo(function ContextMenu$1(props) {
|
|
|
69331
69490
|
}) });
|
|
69332
69491
|
});
|
|
69333
69492
|
|
|
69493
|
+
//#endregion
|
|
69494
|
+
//#region src/client/components/BatchActionBar.tsx
|
|
69495
|
+
const log$20 = scoped("batch-bar");
|
|
69496
|
+
function BatchActionBar({ onClearSelection }) {
|
|
69497
|
+
const t$1 = useT();
|
|
69498
|
+
const multiSelectedIds = useGraphStore((s) => s.multiSelectedIds);
|
|
69499
|
+
const batchExpandSelected = useGraphStore((s) => s.batchExpandSelected);
|
|
69500
|
+
const batchCollapseSelected = useGraphStore((s) => s.batchCollapseSelected);
|
|
69501
|
+
const exportSelectedNodes = useGraphStore((s) => s.exportSelectedNodes);
|
|
69502
|
+
const clearMultiSelection = useGraphStore((s) => s.clearMultiSelection);
|
|
69503
|
+
if (multiSelectedIds.size === 0) return null;
|
|
69504
|
+
const handleClear = () => {
|
|
69505
|
+
if (onClearSelection) onClearSelection();
|
|
69506
|
+
else clearMultiSelection();
|
|
69507
|
+
};
|
|
69508
|
+
const handleExport = () => {
|
|
69509
|
+
try {
|
|
69510
|
+
const nodes = exportSelectedNodes();
|
|
69511
|
+
const payload = {
|
|
69512
|
+
exportedAt: new Date().toISOString(),
|
|
69513
|
+
selectedCount: multiSelectedIds.size,
|
|
69514
|
+
exportedCount: nodes.length,
|
|
69515
|
+
nodes
|
|
69516
|
+
};
|
|
69517
|
+
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
|
69518
|
+
const url = URL.createObjectURL(blob);
|
|
69519
|
+
const a = document.createElement("a");
|
|
69520
|
+
a.href = url;
|
|
69521
|
+
a.download = `codegraph-selection-${Date.now()}.json`;
|
|
69522
|
+
a.click();
|
|
69523
|
+
URL.revokeObjectURL(url);
|
|
69524
|
+
} catch (err) {
|
|
69525
|
+
log$20.error("export selection failed", { err });
|
|
69526
|
+
}
|
|
69527
|
+
};
|
|
69528
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
69529
|
+
className: "cg-batch-bar",
|
|
69530
|
+
role: "toolbar",
|
|
69531
|
+
"aria-label": t$1("batchbar.ariaLabel"),
|
|
69532
|
+
children: [
|
|
69533
|
+
/* @__PURE__ */ jsx("span", {
|
|
69534
|
+
className: "cg-batch-bar-count",
|
|
69535
|
+
"aria-live": "polite",
|
|
69536
|
+
children: t$1("batchbar.count", { count: multiSelectedIds.size })
|
|
69537
|
+
}),
|
|
69538
|
+
/* @__PURE__ */ jsx("button", {
|
|
69539
|
+
type: "button",
|
|
69540
|
+
className: "cg-batch-bar-btn",
|
|
69541
|
+
onClick: () => {
|
|
69542
|
+
batchExpandSelected();
|
|
69543
|
+
},
|
|
69544
|
+
"aria-label": t$1("batchbar.expandAria"),
|
|
69545
|
+
children: t$1("batchbar.expand")
|
|
69546
|
+
}),
|
|
69547
|
+
/* @__PURE__ */ jsx("button", {
|
|
69548
|
+
type: "button",
|
|
69549
|
+
className: "cg-batch-bar-btn",
|
|
69550
|
+
onClick: () => {
|
|
69551
|
+
batchCollapseSelected();
|
|
69552
|
+
},
|
|
69553
|
+
"aria-label": t$1("batchbar.collapseAria"),
|
|
69554
|
+
children: t$1("batchbar.collapse")
|
|
69555
|
+
}),
|
|
69556
|
+
/* @__PURE__ */ jsx("button", {
|
|
69557
|
+
type: "button",
|
|
69558
|
+
className: "cg-batch-bar-btn",
|
|
69559
|
+
onClick: handleExport,
|
|
69560
|
+
"aria-label": t$1("batchbar.exportAria"),
|
|
69561
|
+
children: t$1("batchbar.export")
|
|
69562
|
+
}),
|
|
69563
|
+
/* @__PURE__ */ jsx("button", {
|
|
69564
|
+
type: "button",
|
|
69565
|
+
className: "cg-batch-bar-btn cg-batch-bar-btn-clear",
|
|
69566
|
+
onClick: handleClear,
|
|
69567
|
+
"aria-label": t$1("batchbar.clearAria"),
|
|
69568
|
+
children: t$1("batchbar.clear")
|
|
69569
|
+
})
|
|
69570
|
+
]
|
|
69571
|
+
});
|
|
69572
|
+
}
|
|
69573
|
+
|
|
69334
69574
|
//#endregion
|
|
69335
69575
|
//#region src/client/components/LoadingOverlay.tsx
|
|
69336
69576
|
const LoadingOverlay = memo(function LoadingOverlay$1({ visible }) {
|
|
@@ -69728,7 +69968,7 @@ const StatusBar = memo(function StatusBar$1({ error: error$1, isLoading, lastUpd
|
|
|
69728
69968
|
const statusText = error$1 ? t$1("state.error") : isLoading ? t$1("state.loading") : t$1("state.ready");
|
|
69729
69969
|
const wsName = workspaceName ? deriveWorkspaceName(workspaceName) : "";
|
|
69730
69970
|
const hasDegradations = performanceDegradations && performanceDegradations.length > 0;
|
|
69731
|
-
const degradationLabels = hasDegradations ? performanceDegradations.map((key) => DEGRADATION_KEYS[key] ? t$1(DEGRADATION_KEYS[key]) : key).join(", ") : "";
|
|
69971
|
+
const degradationLabels = hasDegradations && performanceDegradations ? performanceDegradations.map((key) => DEGRADATION_KEYS[key] ? t$1(DEGRADATION_KEYS[key]) : key).join(", ") : "";
|
|
69732
69972
|
const degradedRules = xuanjiSignal?.degradedRules ?? [];
|
|
69733
69973
|
const violatedRules = xuanjiSignal?.invariantViolations ?? [];
|
|
69734
69974
|
const showXuanji = xuanjiSignal?.applied === true;
|
|
@@ -77174,8 +77414,8 @@ function InteractiveMiniMap({ thumbnail, viewportInfo, onFocusArea, nodePosition
|
|
|
77174
77414
|
whiteSpace: "nowrap",
|
|
77175
77415
|
background: "var(--cg-bg-elevated)",
|
|
77176
77416
|
color: "var(--cg-text)",
|
|
77177
|
-
border: "1px solid var(--cg-border-strong)
|
|
77178
|
-
boxShadow: "var(--cg-shadow-sm)
|
|
77417
|
+
border: "1px solid var(--cg-border-strong)",
|
|
77418
|
+
boxShadow: "var(--cg-shadow-sm)",
|
|
77179
77419
|
pointerEvents: "none"
|
|
77180
77420
|
},
|
|
77181
77421
|
children: t$1("canvasControls.minimapFocusHint")
|
|
@@ -77206,8 +77446,8 @@ function KeyboardHintOverlay({ onClose }) {
|
|
|
77206
77446
|
padding: "10px 12px",
|
|
77207
77447
|
borderRadius: "var(--cg-radius-sm)",
|
|
77208
77448
|
background: "var(--cg-surface)",
|
|
77209
|
-
border: "1px solid var(--cg-border-strong)
|
|
77210
|
-
boxShadow: "var(--cg-shadow)
|
|
77449
|
+
border: "1px solid var(--cg-border-strong)",
|
|
77450
|
+
boxShadow: "var(--cg-shadow)",
|
|
77211
77451
|
pointerEvents: "auto"
|
|
77212
77452
|
},
|
|
77213
77453
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
@@ -77721,7 +77961,9 @@ function GraphPanelInner({ className = "", sessionId, useSessions }) {
|
|
|
77721
77961
|
onNodeContextTap: (nodeId, pos) => setContextMenu({
|
|
77722
77962
|
nodeId,
|
|
77723
77963
|
position: pos
|
|
77724
|
-
})
|
|
77964
|
+
}),
|
|
77965
|
+
onSelectionToggle: (_nodeId, _selected, selectedIds) => useGraphStore.getState().syncMultiSelection(selectedIds),
|
|
77966
|
+
onSelectionClear: () => useGraphStore.getState().clearMultiSelection()
|
|
77725
77967
|
},
|
|
77726
77968
|
showCallChainRef
|
|
77727
77969
|
});
|
|
@@ -78334,6 +78576,7 @@ function GraphPanelInner({ className = "", sessionId, useSessions }) {
|
|
|
78334
78576
|
}
|
|
78335
78577
|
}),
|
|
78336
78578
|
tooltip && /* @__PURE__ */ jsx(GraphTooltip, { data: tooltip }),
|
|
78579
|
+
/* @__PURE__ */ jsx(BatchActionBar, { onClearSelection: () => renderer$1.rendererRef.current?.clearMultiSelect() }),
|
|
78337
78580
|
/* @__PURE__ */ jsx(KeyboardHelp, {
|
|
78338
78581
|
visible: showHelp,
|
|
78339
78582
|
onClose: () => setShowHelp(false)
|
|
@@ -78364,7 +78607,7 @@ function GraphPanel(props) {
|
|
|
78364
78607
|
|
|
78365
78608
|
//#endregion
|
|
78366
78609
|
//#region src/generated/version.ts
|
|
78367
|
-
const PLUGIN_VERSION = "0.3.
|
|
78610
|
+
const PLUGIN_VERSION = "0.3.1";
|
|
78368
78611
|
|
|
78369
78612
|
//#endregion
|
|
78370
78613
|
//#region src/client/utils/safeCheck.ts
|