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.js
CHANGED
|
@@ -2397,6 +2397,65 @@ body[data-ds-dark-theme] .cg-type-badge.cg-function {
|
|
|
2397
2397
|
color: var(--cg-success);
|
|
2398
2398
|
text-align: center;
|
|
2399
2399
|
}
|
|
2400
|
+
|
|
2401
|
+
/* ---- Batch action bar (cycle-149 多选链路消费面) -------------------------- */
|
|
2402
|
+
/*
|
|
2403
|
+
* 悬浮在画布底部、状态栏上方的批量操作条。仅在 \`multiSelectedIds.size > 0\` 时由
|
|
2404
|
+
* BatchActionBar 渲染;空选区返回 null,因此这里无需 hidden 态样式。
|
|
2405
|
+
* 定位锚定 \`.cg-panel\`(position: absolute + z-index: var(--cg-z-panel)),
|
|
2406
|
+
* 与 health-score-card 等面板级浮层同层——不遮 ContextMenu(z-context-menu 更高)。
|
|
2407
|
+
*/
|
|
2408
|
+
|
|
2409
|
+
.cg-batch-bar {
|
|
2410
|
+
position: absolute;
|
|
2411
|
+
bottom: 44px; /* 状态栏上方(状态栏高度 ≈ 32px + 间距) */
|
|
2412
|
+
left: 50%;
|
|
2413
|
+
transform: translateX(-50%);
|
|
2414
|
+
display: flex;
|
|
2415
|
+
align-items: center;
|
|
2416
|
+
gap: 8px;
|
|
2417
|
+
padding: 6px 10px;
|
|
2418
|
+
border-radius: var(--cg-radius-sm);
|
|
2419
|
+
border: 1px solid var(--cg-border-strong);
|
|
2420
|
+
background: var(--cg-surface);
|
|
2421
|
+
box-shadow: var(--cg-shadow-lg);
|
|
2422
|
+
z-index: var(--cg-z-panel);
|
|
2423
|
+
max-width: calc(100% - 32px);
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
.cg-batch-bar-count {
|
|
2427
|
+
font-size: 12px;
|
|
2428
|
+
font-weight: 600;
|
|
2429
|
+
color: var(--cg-accent);
|
|
2430
|
+
white-space: nowrap;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
.cg-batch-bar-btn {
|
|
2434
|
+
padding: 4px 10px;
|
|
2435
|
+
border-radius: var(--cg-radius-xs);
|
|
2436
|
+
border: 1px solid var(--cg-border-strong);
|
|
2437
|
+
background: var(--cg-surface);
|
|
2438
|
+
color: var(--cg-text);
|
|
2439
|
+
font-size: 12px;
|
|
2440
|
+
cursor: pointer;
|
|
2441
|
+
white-space: nowrap;
|
|
2442
|
+
transition: background var(--cg-transition), border-color var(--cg-transition);
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
.cg-batch-bar-btn:hover {
|
|
2446
|
+
background: var(--cg-surface-hover);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
.cg-batch-bar-btn:focus-visible {
|
|
2450
|
+
outline: 2px solid var(--cg-accent);
|
|
2451
|
+
outline-offset: 1px;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
/* 清除按钮的破坏性语义:hover 提示「这会丢掉选区」,但不用 error 底色(非错误态)。 */
|
|
2455
|
+
.cg-batch-bar-btn-clear:hover {
|
|
2456
|
+
border-color: var(--cg-error);
|
|
2457
|
+
color: var(--cg-error);
|
|
2458
|
+
}
|
|
2400
2459
|
|
|
2401
2460
|
/* ---- Graph container ----------------------------------------------------- */
|
|
2402
2461
|
|
|
@@ -6141,993 +6200,1050 @@ button.cg-legend-row.cg-legend-toggle:hover {
|
|
|
6141
6200
|
opacity: 0.4;
|
|
6142
6201
|
cursor: not-allowed;
|
|
6143
6202
|
}
|
|
6144
|
-
/* ---- Responsive ---------------------------------------------------------- */
|
|
6145
|
-
|
|
6146
|
-
/* Extra-narrow panels (DSH sidebar tab minimal width).
|
|
6147
|
-
* Hide the remaining toolbar extras to prioritize graph canvas.
|
|
6148
|
-
* The center group is already gone at the 600px tier below. */
|
|
6149
|
-
@container graphpanel (max-width: 380px) {
|
|
6150
|
-
.cg-tour-btn,
|
|
6151
|
-
.cg-minimap-btn,
|
|
6152
|
-
.cg-legend-btn {
|
|
6153
|
-
display: none;
|
|
6154
|
-
}
|
|
6155
|
-
|
|
6156
|
-
/* The separators are siblings of the \`.cg-toolbar-group\` elements, not descendants,
|
|
6157
|
-
so the previous \`.cg-toolbar-group ... .cg-toolbar-separator\` selector never
|
|
6158
|
-
matched anything. */
|
|
6159
|
-
.cg-toolbar-separator {
|
|
6160
|
-
display: none;
|
|
6161
|
-
}
|
|
6162
|
-
}
|
|
6163
|
-
|
|
6164
|
-
/* Small panels (DSH sidebar tab, narrow bottom panel).
|
|
6165
|
-
* Container queries respond to the PANEL width, not the viewport — the plugin
|
|
6166
|
-
* renders inside DSH panels whose width is unrelated to the window size. */
|
|
6167
|
-
@container graphpanel (max-width: 480px) {
|
|
6168
|
-
.cg-graph-panel {
|
|
6169
|
-
border-radius: var(--cg-radius-sm);
|
|
6170
|
-
}
|
|
6171
|
-
|
|
6172
|
-
.cg-graph-toolbar {
|
|
6173
|
-
padding: 0 8px;
|
|
6174
|
-
gap: 4px;
|
|
6175
|
-
}
|
|
6176
|
-
|
|
6177
|
-
.cg-toolbar-left {
|
|
6178
|
-
font-size: 11px;
|
|
6179
|
-
}
|
|
6180
|
-
|
|
6181
|
-
.cg-toolbar-center {
|
|
6182
|
-
gap: 4px;
|
|
6183
|
-
}
|
|
6184
|
-
|
|
6185
|
-
.cg-toolbar-right {
|
|
6186
|
-
gap: 4px;
|
|
6187
|
-
}
|
|
6188
|
-
|
|
6189
|
-
.cg-layout-btn,
|
|
6190
|
-
.cg-graph-type-btn,
|
|
6191
|
-
.cg-rank-dir-btn {
|
|
6192
|
-
padding: 3px 6px;
|
|
6193
|
-
font-size: 10px;
|
|
6194
|
-
}
|
|
6195
|
-
|
|
6196
|
-
.cg-search-btn,
|
|
6197
|
-
.cg-theme-btn,
|
|
6198
|
-
.cg-export-btn,
|
|
6199
|
-
.cg-collapse-btn,
|
|
6200
|
-
.cg-chain-btn,
|
|
6201
|
-
.cg-cycle-btn,
|
|
6202
|
-
.cg-impact-btn,
|
|
6203
|
-
.cg-inheritance-btn,
|
|
6204
|
-
.cg-minimap-btn,
|
|
6205
|
-
.cg-refresh-btn,
|
|
6206
|
-
.cg-undo-btn,
|
|
6207
|
-
.cg-redo-btn,
|
|
6208
|
-
.cg-import-btn,
|
|
6209
|
-
.cg-legend-btn,
|
|
6210
|
-
.cg-lang-btn,
|
|
6211
|
-
.cg-pathfind-btn,
|
|
6212
|
-
.cg-health-btn,
|
|
6213
|
-
.cg-rules-btn,
|
|
6214
|
-
.cg-hotspot-btn,
|
|
6215
|
-
.cg-godmodule-btn,
|
|
6216
|
-
.cg-corefunction-btn,
|
|
6217
|
-
.cg-analysis-dropdown-btn,
|
|
6218
|
-
.cg-quality-dropdown-btn,
|
|
6219
|
-
.cg-tour-btn {
|
|
6220
|
-
width: 26px;
|
|
6221
|
-
height: 26px;
|
|
6222
|
-
font-size: 12px;
|
|
6223
|
-
}
|
|
6224
|
-
|
|
6225
|
-
.cg-dropdown-content {
|
|
6226
|
-
min-width: 140px;
|
|
6227
|
-
}
|
|
6228
|
-
|
|
6229
|
-
.cg-node-detail-panel {
|
|
6230
|
-
width: calc(100% - 24px);
|
|
6231
|
-
left: 12px;
|
|
6232
|
-
top: 12px;
|
|
6233
|
-
}
|
|
6234
|
-
|
|
6235
|
-
.cg-legend-panel {
|
|
6236
|
-
width: 140px;
|
|
6237
|
-
}
|
|
6238
|
-
|
|
6239
|
-
.cg-mini-map {
|
|
6240
|
-
width: 140px;
|
|
6241
|
-
}
|
|
6242
|
-
|
|
6243
|
-
.cg-type-badge {
|
|
6244
|
-
display: none;
|
|
6245
|
-
}
|
|
6246
|
-
|
|
6247
|
-
.cg-toolbar-separator {
|
|
6248
|
-
display: none;
|
|
6249
|
-
}
|
|
6250
|
-
|
|
6251
|
-
.cg-graph-type-buttons {
|
|
6252
|
-
display: none;
|
|
6253
|
-
}
|
|
6254
|
-
|
|
6255
|
-
.cg-view-mode-buttons {
|
|
6256
|
-
gap: 1px;
|
|
6257
|
-
}
|
|
6258
|
-
|
|
6259
|
-
.cg-view-mode-btn {
|
|
6260
|
-
padding: 2px 4px;
|
|
6261
|
-
font-size: 9px;
|
|
6262
|
-
}
|
|
6263
|
-
|
|
6264
|
-
.cg-search-panel,
|
|
6265
|
-
.cg-path-panel {
|
|
6266
|
-
width: calc(100% - 16px);
|
|
6267
|
-
max-width: 320px;
|
|
6268
|
-
}
|
|
6269
|
-
|
|
6270
|
-
.cg-filter-panel {
|
|
6271
|
-
width: calc(100% - 16px);
|
|
6272
|
-
}
|
|
6273
|
-
|
|
6274
|
-
.cg-heatmap-btn--activate,
|
|
6275
|
-
.cg-heatmap-btn--deactivate {
|
|
6276
|
-
width: 26px;
|
|
6277
|
-
height: 26px;
|
|
6278
|
-
font-size: 12px;
|
|
6279
|
-
}
|
|
6280
|
-
|
|
6281
|
-
}
|
|
6282
|
-
|
|
6283
|
-
/* Medium panels (DSH sidebar medium width) */
|
|
6284
|
-
@container graphpanel (max-width: 768px) {
|
|
6285
|
-
.cg-graph-toolbar {
|
|
6286
|
-
padding: 0 8px;
|
|
6287
|
-
gap: 6px;
|
|
6288
|
-
}
|
|
6289
|
-
|
|
6290
|
-
.cg-toolbar-center {
|
|
6291
|
-
gap: 6px;
|
|
6292
|
-
}
|
|
6293
|
-
|
|
6294
|
-
.cg-toolbar-right {
|
|
6295
|
-
gap: 6px;
|
|
6296
|
-
}
|
|
6297
|
-
|
|
6298
|
-
.cg-layout-btn,
|
|
6299
|
-
.cg-graph-type-btn {
|
|
6300
|
-
padding: 3px 8px;
|
|
6301
|
-
font-size: 11px;
|
|
6302
|
-
}
|
|
6303
|
-
|
|
6304
|
-
.cg-node-detail-panel {
|
|
6305
|
-
width: 220px;
|
|
6306
|
-
}
|
|
6307
|
-
|
|
6308
|
-
.cg-legend-panel {
|
|
6309
|
-
width: 160px;
|
|
6310
|
-
}
|
|
6311
|
-
|
|
6312
|
-
.cg-mini-map {
|
|
6313
|
-
width: 160px;
|
|
6314
|
-
}
|
|
6315
|
-
|
|
6316
|
-
.cg-filter-select {
|
|
6317
|
-
font-size: 11px;
|
|
6318
|
-
}
|
|
6319
|
-
|
|
6320
|
-
.cg-toolbar-separator {
|
|
6321
|
-
margin: 0 2px;
|
|
6322
|
-
}
|
|
6323
|
-
|
|
6324
|
-
.cg-tour-btn {
|
|
6325
|
-
display: none;
|
|
6326
|
-
}
|
|
6327
|
-
|
|
6328
|
-
}
|
|
6329
|
-
|
|
6330
|
-
/* Medium-narrow panels — hide tour and reduce analysis button clutter */
|
|
6331
|
-
@container graphpanel (max-width: 600px) {
|
|
6332
|
-
.cg-tour-btn,
|
|
6333
|
-
.cg-minimap-btn {
|
|
6334
|
-
display: none;
|
|
6335
|
-
}
|
|
6336
|
-
|
|
6337
|
-
.cg-toolbar-left .cg-type-badge {
|
|
6338
|
-
display: none;
|
|
6339
|
-
}
|
|
6340
|
-
|
|
6341
|
-
/* The center group (layout / view-mode / level) is the widest fixed-width block in the
|
|
6342
|
-
toolbar: 150px for its three icon-only triggers, measured on the real host. At a 520px
|
|
6343
|
-
panel it was being squeezed to 115px, which wrapped the level trigger onto two lines
|
|
6344
|
-
and clipped it; at 600px it still has 45px of slack. Hiding it here instead of letting
|
|
6345
|
-
it be crushed keeps the workspace chip and every right-hand control (export / more /
|
|
6346
|
-
collapse) reachable. Of the three controls, only the layout keeps an alternative entry
|
|
6347
|
-
point (Ctrl+L cycles it, see \`usePanelKeyboard\`); view mode and hierarchy level lose
|
|
6348
|
-
their mouse entry below this tier — see the note in \`docs/需求分析.md\` §28.3. */
|
|
6349
|
-
.cg-toolbar-center {
|
|
6350
|
-
display: none;
|
|
6351
|
-
}
|
|
6352
|
-
}
|
|
6353
|
-
|
|
6354
|
-
/* Very small panels */
|
|
6355
|
-
@container graphpanel (max-width: 320px) {
|
|
6356
|
-
.cg-graph-toolbar {
|
|
6357
|
-
gap: 2px;
|
|
6358
|
-
flex-wrap: wrap;
|
|
6359
|
-
padding: 4px 4px;
|
|
6360
|
-
}
|
|
6361
|
-
|
|
6362
|
-
.cg-toolbar-left,
|
|
6363
|
-
.cg-toolbar-center,
|
|
6364
|
-
.cg-toolbar-right {
|
|
6365
|
-
flex-wrap: wrap;
|
|
6366
|
-
gap: 2px;
|
|
6367
|
-
}
|
|
6368
|
-
|
|
6369
|
-
.cg-layout-buttons,
|
|
6370
|
-
.cg-graph-type-buttons {
|
|
6371
|
-
padding: 1px;
|
|
6372
|
-
}
|
|
6373
|
-
|
|
6374
|
-
.cg-layout-btn,
|
|
6375
|
-
.cg-graph-type-btn {
|
|
6376
|
-
padding: 2px 4px;
|
|
6377
|
-
font-size: 9px;
|
|
6378
|
-
}
|
|
6379
|
-
|
|
6380
|
-
.cg-filter-select {
|
|
6381
|
-
padding: 3px 20px 3px 6px;
|
|
6382
|
-
font-size: 10px;
|
|
6383
|
-
}
|
|
6384
|
-
|
|
6385
|
-
.cg-node-count {
|
|
6386
|
-
font-size: 10px;
|
|
6387
|
-
}
|
|
6388
|
-
|
|
6389
|
-
.cg-legend-panel,
|
|
6390
|
-
.cg-mini-map {
|
|
6391
|
-
width: 120px;
|
|
6392
|
-
}
|
|
6393
|
-
|
|
6394
|
-
/* 下拉内容已 portal 到 document.body,不再是本容器的后代,此处的
|
|
6395
|
-
* \`@container\` 抽屉规则永远匹配不到;抽屉形态改由 Dropdown 组件测量面板
|
|
6396
|
-
* 宽度后加 \`.cg-portal-drawer\`(见 layout.css)实现。 */
|
|
6397
|
-
|
|
6398
|
-
.cg-search-panel,
|
|
6399
|
-
.cg-path-panel,
|
|
6400
|
-
.cg-filter-panel {
|
|
6401
|
-
width: calc(100% - 8px);
|
|
6402
|
-
left: 4px;
|
|
6403
|
-
right: 4px;
|
|
6404
|
-
}
|
|
6405
|
-
|
|
6406
|
-
.cg-view-mode-buttons {
|
|
6407
|
-
flex-wrap: wrap;
|
|
6408
|
-
}
|
|
6409
|
-
}
|
|
6410
|
-
|
|
6411
|
-
/* Large screens — allow fixed floating panel */
|
|
6412
|
-
@media (min-width: 1200px) {
|
|
6413
|
-
.cg-graph-panel.cg-floating {
|
|
6414
|
-
position: fixed;
|
|
6415
|
-
right: 16px;
|
|
6416
|
-
bottom: 16px;
|
|
6417
|
-
width: 640px;
|
|
6418
|
-
height: 520px;
|
|
6419
|
-
}
|
|
6420
|
-
}
|
|
6421
|
-
|
|
6422
|
-
/* ---- Sidebar tab mode (short height) -------------------------------------- */
|
|
6423
|
-
/* When the plugin renders inside a DSH sidebar tab, the panel height can be
|
|
6424
|
-
* very limited. Adapt the layout to prioritize the graph canvas. */
|
|
6425
|
-
@container graphpanel (max-height: 400px) {
|
|
6426
|
-
.cg-graph-toolbar {
|
|
6427
|
-
padding: 0 4px;
|
|
6428
|
-
gap: 2px;
|
|
6429
|
-
}
|
|
6430
|
-
|
|
6431
|
-
.cg-toolbar-left .cg-node-count {
|
|
6432
|
-
font-size: 10px;
|
|
6433
|
-
}
|
|
6434
|
-
|
|
6435
|
-
.cg-type-badge {
|
|
6436
|
-
display: none;
|
|
6437
|
-
}
|
|
6438
|
-
|
|
6439
|
-
.cg-toolbar-separator {
|
|
6440
|
-
display: none;
|
|
6441
|
-
}
|
|
6442
|
-
|
|
6443
|
-
.cg-graph-type-buttons {
|
|
6444
|
-
display: none;
|
|
6445
|
-
}
|
|
6446
|
-
|
|
6447
|
-
.cg-view-mode-buttons {
|
|
6448
|
-
gap: 1px;
|
|
6449
|
-
}
|
|
6450
|
-
|
|
6451
|
-
.cg-view-mode-btn {
|
|
6452
|
-
padding: 2px 4px;
|
|
6453
|
-
font-size: 9px;
|
|
6454
|
-
}
|
|
6455
|
-
|
|
6456
|
-
.cg-node-detail-panel {
|
|
6457
|
-
max-height: calc(100% - 60px);
|
|
6458
|
-
overflow-y: auto;
|
|
6459
|
-
}
|
|
6460
|
-
|
|
6461
|
-
.cg-legend-panel {
|
|
6462
|
-
max-height: 120px;
|
|
6463
|
-
overflow-y: auto;
|
|
6464
|
-
}
|
|
6465
|
-
|
|
6466
|
-
.cg-mini-map {
|
|
6467
|
-
max-height: 100px;
|
|
6468
|
-
}
|
|
6469
|
-
|
|
6470
|
-
.cg-search-panel,
|
|
6471
|
-
.cg-path-panel,
|
|
6472
|
-
.cg-filter-panel {
|
|
6473
|
-
max-height: calc(100% - 60px);
|
|
6474
|
-
overflow-y: auto;
|
|
6475
|
-
}
|
|
6476
|
-
}
|
|
6477
|
-
|
|
6478
|
-
@container graphpanel (max-height: 240px) {
|
|
6479
|
-
.cg-graph-toolbar {
|
|
6480
|
-
flex-wrap: wrap;
|
|
6481
|
-
padding: 2px;
|
|
6482
|
-
}
|
|
6483
|
-
|
|
6484
|
-
.cg-toolbar-left,
|
|
6485
|
-
.cg-toolbar-center,
|
|
6486
|
-
.cg-toolbar-right {
|
|
6487
|
-
flex-wrap: wrap;
|
|
6488
|
-
gap: 2px;
|
|
6489
|
-
}
|
|
6490
|
-
|
|
6491
|
-
.cg-layout-buttons {
|
|
6492
|
-
display: none;
|
|
6493
|
-
}
|
|
6494
|
-
|
|
6495
|
-
.cg-rank-dir-buttons {
|
|
6496
|
-
display: none;
|
|
6497
|
-
}
|
|
6498
|
-
|
|
6499
|
-
.cg-cluster-depth-buttons {
|
|
6500
|
-
display: none;
|
|
6501
|
-
}
|
|
6502
|
-
|
|
6503
|
-
.cg-node-detail-panel {
|
|
6504
|
-
width: calc(100% - 8px);
|
|
6505
|
-
left: 4px;
|
|
6506
|
-
top: 4px;
|
|
6507
|
-
max-height: calc(100% - 40px);
|
|
6508
|
-
}
|
|
6509
|
-
}
|
|
6510
|
-
|
|
6511
|
-
/* ---- Reduced motion ------------------------------------------------------ */
|
|
6512
|
-
|
|
6513
|
-
@media (prefers-reduced-motion: reduce) {
|
|
6514
|
-
.cg-graph-panel *,
|
|
6515
|
-
.cg-graph-panel *::before,
|
|
6516
|
-
.cg-graph-panel *::after,
|
|
6517
|
-
.cg-floating-panel *,
|
|
6518
|
-
.cg-floating-panel *::before,
|
|
6519
|
-
.cg-floating-panel *::after,
|
|
6520
|
-
.cg-floating-container *,
|
|
6521
|
-
.cg-floating-container *::before,
|
|
6522
|
-
.cg-floating-container *::after {
|
|
6523
|
-
animation-duration: 0.01ms !important;
|
|
6524
|
-
transition-duration: 0.01ms !important;
|
|
6525
|
-
}
|
|
6526
|
-
}
|
|
6527
|
-
|
|
6528
|
-
/* ---- Touch device adaptation --------------------------------------------- */
|
|
6529
|
-
|
|
6530
|
-
@media (pointer: coarse) {
|
|
6531
|
-
.cg-layout-btn,
|
|
6532
|
-
.cg-graph-type-btn,
|
|
6533
|
-
.cg-search-btn,
|
|
6534
|
-
.cg-theme-btn,
|
|
6535
|
-
.cg-export-btn,
|
|
6536
|
-
.cg-collapse-btn,
|
|
6537
|
-
.cg-chain-btn,
|
|
6538
|
-
.cg-cycle-btn,
|
|
6539
|
-
.cg-impact-btn,
|
|
6540
|
-
.cg-inheritance-btn,
|
|
6541
|
-
.cg-minimap-btn,
|
|
6542
|
-
.cg-refresh-btn,
|
|
6543
|
-
.cg-undo-btn,
|
|
6544
|
-
.cg-redo-btn,
|
|
6545
|
-
.cg-import-btn,
|
|
6546
|
-
.cg-legend-btn,
|
|
6547
|
-
.cg-lang-btn,
|
|
6548
|
-
.cg-pathfind-btn,
|
|
6549
|
-
.cg-health-btn,
|
|
6550
|
-
.cg-rules-btn,
|
|
6551
|
-
.cg-hotspot-btn,
|
|
6552
|
-
.cg-godmodule-btn,
|
|
6553
|
-
.cg-corefunction-btn,
|
|
6554
|
-
.cg-analysis-dropdown-btn,
|
|
6555
|
-
.cg-quality-dropdown-btn,
|
|
6556
|
-
.cg-tour-btn {
|
|
6557
|
-
min-width: 44px;
|
|
6558
|
-
min-height: 44px;
|
|
6559
|
-
}
|
|
6560
|
-
|
|
6561
|
-
.cg-node-detail-panel .cg-close-btn,
|
|
6562
|
-
.cg-legend-close,
|
|
6563
|
-
.cg-import-close,
|
|
6564
|
-
.cg-close-btn {
|
|
6565
|
-
min-width: 44px;
|
|
6566
|
-
min-height: 44px;
|
|
6567
|
-
}
|
|
6568
|
-
}
|
|
6569
|
-
/* ---- Responsive for new components ---------------------------------------- */
|
|
6570
|
-
|
|
6571
|
-
@container graphpanel (max-width: 480px) {
|
|
6572
|
-
.cg-path-bar,
|
|
6573
|
-
.cg-health-score-card,
|
|
6574
|
-
.cg-rule-panel {
|
|
6575
|
-
width: calc(100% - 24px);
|
|
6576
|
-
}
|
|
6577
|
-
|
|
6578
|
-
.cg-analysis-panel {
|
|
6579
|
-
width: calc(100% - 24px);
|
|
6580
|
-
}
|
|
6581
|
-
|
|
6582
|
-
.cg-focus-label {
|
|
6583
|
-
max-width: 120px;
|
|
6584
|
-
}
|
|
6585
|
-
}
|
|
6586
|
-
|
|
6587
|
-
@container graphpanel (max-width: 768px) {
|
|
6588
|
-
.cg-path-bar {
|
|
6589
|
-
width: 240px;
|
|
6590
|
-
}
|
|
6591
|
-
|
|
6592
|
-
.cg-health-score-card {
|
|
6593
|
-
width: 220px;
|
|
6594
|
-
}
|
|
6595
|
-
|
|
6596
|
-
.cg-rule-panel {
|
|
6597
|
-
width: 240px;
|
|
6598
|
-
}
|
|
6599
|
-
|
|
6600
|
-
.cg-analysis-panel {
|
|
6601
|
-
width: 200px;
|
|
6602
|
-
}
|
|
6603
|
-
}
|
|
6604
|
-
/* ---- Container Queries fallback ------------------------------------------- */
|
|
6605
|
-
@supports not (container-type: inline-size) {
|
|
6606
|
-
@media (max-width: 380px) {
|
|
6607
|
-
.cg-tour-btn,
|
|
6608
|
-
.cg-minimap-btn,
|
|
6609
|
-
.cg-legend-btn {
|
|
6610
|
-
display: none;
|
|
6611
|
-
}
|
|
6612
|
-
}
|
|
6613
|
-
|
|
6614
|
-
@media (max-width: 480px) {
|
|
6615
|
-
.cg-graph-panel {
|
|
6616
|
-
border-radius: var(--cg-radius-sm);
|
|
6617
|
-
}
|
|
6618
|
-
.cg-graph-toolbar {
|
|
6619
|
-
padding: 0 8px;
|
|
6620
|
-
gap: 4px;
|
|
6621
|
-
}
|
|
6622
|
-
.cg-toolbar-left {
|
|
6623
|
-
font-size: 11px;
|
|
6624
|
-
}
|
|
6625
|
-
.cg-toolbar-center {
|
|
6626
|
-
gap: 4px;
|
|
6627
|
-
}
|
|
6628
|
-
.cg-toolbar-right {
|
|
6629
|
-
gap: 4px;
|
|
6630
|
-
}
|
|
6631
|
-
.cg-layout-btn,
|
|
6632
|
-
.cg-graph-type-btn,
|
|
6633
|
-
.cg-rank-dir-btn {
|
|
6634
|
-
padding: 3px 6px;
|
|
6635
|
-
font-size: 10px;
|
|
6636
|
-
}
|
|
6637
|
-
.cg-search-btn,
|
|
6638
|
-
.cg-theme-btn,
|
|
6639
|
-
.cg-export-btn,
|
|
6640
|
-
.cg-collapse-btn,
|
|
6641
|
-
.cg-chain-btn,
|
|
6642
|
-
.cg-cycle-btn,
|
|
6643
|
-
.cg-impact-btn,
|
|
6644
|
-
.cg-inheritance-btn,
|
|
6645
|
-
.cg-minimap-btn,
|
|
6646
|
-
.cg-refresh-btn,
|
|
6647
|
-
|
|
6648
|
-
.cg-undo-btn,
|
|
6649
|
-
.cg-redo-btn,
|
|
6650
|
-
.cg-import-btn,
|
|
6651
|
-
.cg-legend-btn,
|
|
6652
|
-
.cg-lang-btn,
|
|
6653
|
-
.cg-pathfind-btn,
|
|
6654
|
-
.cg-health-btn,
|
|
6655
|
-
.cg-rules-btn,
|
|
6656
|
-
.cg-hotspot-btn,
|
|
6657
|
-
.cg-godmodule-btn,
|
|
6658
|
-
.cg-corefunction-btn,
|
|
6659
|
-
.cg-analysis-dropdown-btn,
|
|
6660
|
-
.cg-quality-dropdown-btn,
|
|
6661
|
-
.cg-tour-btn {
|
|
6662
|
-
width: 26px;
|
|
6663
|
-
height: 26px;
|
|
6664
|
-
font-size: 12px;
|
|
6665
|
-
}
|
|
6666
|
-
.cg-node-detail-panel {
|
|
6667
|
-
width: calc(100% - 24px);
|
|
6668
|
-
left: 12px;
|
|
6669
|
-
top: 12px;
|
|
6670
|
-
}
|
|
6671
|
-
.cg-legend-panel {
|
|
6672
|
-
width: 140px;
|
|
6673
|
-
}
|
|
6674
|
-
.cg-mini-map {
|
|
6675
|
-
width: 140px;
|
|
6676
|
-
}
|
|
6677
|
-
.cg-type-badge {
|
|
6678
|
-
display: none;
|
|
6679
|
-
}
|
|
6680
|
-
.cg-toolbar-separator {
|
|
6681
|
-
display: none;
|
|
6682
|
-
}
|
|
6683
|
-
.cg-graph-type-buttons {
|
|
6684
|
-
display: none;
|
|
6685
|
-
}
|
|
6686
|
-
.cg-dropdown-content {
|
|
6687
|
-
min-width: 140px;
|
|
6688
|
-
}
|
|
6689
|
-
.cg-view-mode-buttons {
|
|
6690
|
-
gap: 1px;
|
|
6691
|
-
}
|
|
6692
|
-
.cg-view-mode-btn {
|
|
6693
|
-
padding: 2px 4px;
|
|
6694
|
-
font-size: 9px;
|
|
6695
|
-
}
|
|
6696
|
-
.cg-search-panel,
|
|
6697
|
-
.cg-path-panel {
|
|
6698
|
-
width: calc(100% - 16px);
|
|
6699
|
-
max-width: 320px;
|
|
6700
|
-
}
|
|
6701
|
-
.cg-filter-panel {
|
|
6702
|
-
width: calc(100% - 16px);
|
|
6703
|
-
}
|
|
6704
|
-
.cg-heatmap-btn--activate,
|
|
6705
|
-
.cg-heatmap-btn--deactivate {
|
|
6706
|
-
width: 26px;
|
|
6707
|
-
height: 26px;
|
|
6708
|
-
font-size: 12px;
|
|
6709
|
-
}
|
|
6710
|
-
.cg-path-bar,
|
|
6711
|
-
.cg-health-score-card,
|
|
6712
|
-
.cg-rule-panel {
|
|
6713
|
-
width: calc(100% - 24px);
|
|
6714
|
-
}
|
|
6715
|
-
.cg-analysis-panel {
|
|
6716
|
-
width: calc(100% - 24px);
|
|
6717
|
-
}
|
|
6718
|
-
.cg-focus-label {
|
|
6719
|
-
max-width: 120px;
|
|
6720
|
-
}
|
|
6721
|
-
}
|
|
6722
|
-
|
|
6723
|
-
@media (max-width: 768px) {
|
|
6724
|
-
.cg-graph-toolbar {
|
|
6725
|
-
padding: 0 8px;
|
|
6726
|
-
gap: 6px;
|
|
6727
|
-
}
|
|
6728
|
-
.cg-toolbar-center {
|
|
6729
|
-
gap: 6px;
|
|
6730
|
-
}
|
|
6731
|
-
.cg-toolbar-right {
|
|
6732
|
-
gap: 6px;
|
|
6733
|
-
}
|
|
6734
|
-
.cg-layout-btn,
|
|
6735
|
-
.cg-graph-type-btn {
|
|
6736
|
-
padding: 3px 8px;
|
|
6737
|
-
font-size: 11px;
|
|
6738
|
-
}
|
|
6739
|
-
.cg-node-detail-panel {
|
|
6740
|
-
width: 220px;
|
|
6741
|
-
}
|
|
6742
|
-
.cg-legend-panel {
|
|
6743
|
-
width: 160px;
|
|
6744
|
-
}
|
|
6745
|
-
.cg-mini-map {
|
|
6746
|
-
width: 160px;
|
|
6747
|
-
}
|
|
6748
|
-
.cg-filter-select {
|
|
6749
|
-
font-size: 11px;
|
|
6750
|
-
}
|
|
6751
|
-
.cg-toolbar-separator {
|
|
6752
|
-
margin: 0 2px;
|
|
6753
|
-
}
|
|
6754
|
-
.cg-tour-btn {
|
|
6755
|
-
display: none;
|
|
6756
|
-
}
|
|
6757
|
-
.cg-path-bar {
|
|
6758
|
-
width: 240px;
|
|
6759
|
-
}
|
|
6760
|
-
.cg-health-score-card {
|
|
6761
|
-
width: 220px;
|
|
6762
|
-
}
|
|
6763
|
-
.cg-rule-panel {
|
|
6764
|
-
width: 240px;
|
|
6765
|
-
}
|
|
6766
|
-
.cg-analysis-panel {
|
|
6767
|
-
width: 200px;
|
|
6768
|
-
}
|
|
6769
|
-
}
|
|
6770
|
-
|
|
6771
|
-
@media (max-width: 600px) {
|
|
6772
|
-
.cg-tour-btn,
|
|
6773
|
-
.cg-minimap-btn {
|
|
6774
|
-
display: none;
|
|
6775
|
-
}
|
|
6776
|
-
.cg-toolbar-left .cg-type-badge {
|
|
6777
|
-
display: none;
|
|
6778
|
-
}
|
|
6779
|
-
/* Mirrors the 600px container tier: the center group needs 150px for its three
|
|
6780
|
-
icon-only triggers and gets crushed below that. */
|
|
6781
|
-
.cg-toolbar-center {
|
|
6782
|
-
display: none;
|
|
6783
|
-
}
|
|
6784
|
-
}
|
|
6785
|
-
|
|
6786
|
-
@media (max-width: 320px) {
|
|
6787
|
-
.cg-graph-toolbar {
|
|
6788
|
-
gap: 2px;
|
|
6789
|
-
flex-wrap: wrap;
|
|
6790
|
-
padding: 4px 4px;
|
|
6791
|
-
}
|
|
6792
|
-
.cg-toolbar-left,
|
|
6793
|
-
.cg-toolbar-center,
|
|
6794
|
-
.cg-toolbar-right {
|
|
6795
|
-
flex-wrap: wrap;
|
|
6796
|
-
gap: 2px;
|
|
6797
|
-
}
|
|
6798
|
-
.cg-layout-buttons,
|
|
6799
|
-
.cg-graph-type-buttons {
|
|
6800
|
-
padding: 1px;
|
|
6801
|
-
}
|
|
6802
|
-
.cg-layout-btn,
|
|
6803
|
-
.cg-graph-type-btn {
|
|
6804
|
-
padding: 2px 4px;
|
|
6805
|
-
font-size: 9px;
|
|
6806
|
-
}
|
|
6807
|
-
.cg-filter-select {
|
|
6808
|
-
padding: 3px 20px 3px 6px;
|
|
6809
|
-
font-size: 10px;
|
|
6810
|
-
}
|
|
6811
|
-
.cg-node-count {
|
|
6812
|
-
font-size: 10px;
|
|
6813
|
-
}
|
|
6814
|
-
.cg-legend-panel,
|
|
6815
|
-
.cg-mini-map {
|
|
6816
|
-
width: 120px;
|
|
6817
|
-
}
|
|
6818
|
-
/* 同上:portal 到 body 后这里也匹配不到,抽屉由 \`.cg-portal-drawer\` 接管。 */
|
|
6819
|
-
.cg-search-panel,
|
|
6820
|
-
.cg-path-panel,
|
|
6821
|
-
.cg-filter-panel {
|
|
6822
|
-
width: calc(100% - 8px);
|
|
6823
|
-
left: 4px;
|
|
6824
|
-
right: 4px;
|
|
6825
|
-
}
|
|
6826
|
-
.cg-view-mode-buttons {
|
|
6827
|
-
flex-wrap: wrap;
|
|
6828
|
-
}
|
|
6829
|
-
}
|
|
6830
|
-
|
|
6831
|
-
@media (max-height: 400px) {
|
|
6832
|
-
.cg-graph-toolbar {
|
|
6833
|
-
padding: 0 4px;
|
|
6834
|
-
gap: 2px;
|
|
6835
|
-
}
|
|
6836
|
-
.cg-toolbar-left .cg-node-count {
|
|
6837
|
-
font-size: 10px;
|
|
6838
|
-
}
|
|
6839
|
-
.cg-type-badge {
|
|
6840
|
-
display: none;
|
|
6841
|
-
}
|
|
6842
|
-
.cg-toolbar-separator {
|
|
6843
|
-
display: none;
|
|
6844
|
-
}
|
|
6845
|
-
.cg-graph-type-buttons {
|
|
6846
|
-
display: none;
|
|
6847
|
-
}
|
|
6848
|
-
.cg-view-mode-buttons {
|
|
6849
|
-
gap: 1px;
|
|
6850
|
-
}
|
|
6851
|
-
.cg-view-mode-btn {
|
|
6852
|
-
padding: 2px 4px;
|
|
6853
|
-
font-size: 9px;
|
|
6854
|
-
}
|
|
6855
|
-
.cg-node-detail-panel {
|
|
6856
|
-
max-height: calc(100% - 60px);
|
|
6857
|
-
overflow-y: auto;
|
|
6858
|
-
}
|
|
6859
|
-
.cg-legend-panel {
|
|
6860
|
-
max-height: 120px;
|
|
6861
|
-
overflow-y: auto;
|
|
6862
|
-
}
|
|
6863
|
-
.cg-mini-map {
|
|
6864
|
-
max-height: 100px;
|
|
6865
|
-
}
|
|
6866
|
-
.cg-search-panel,
|
|
6867
|
-
.cg-path-panel,
|
|
6868
|
-
.cg-filter-panel {
|
|
6869
|
-
max-height: calc(100% - 60px);
|
|
6870
|
-
overflow-y: auto;
|
|
6871
|
-
}
|
|
6872
|
-
}
|
|
6873
|
-
|
|
6874
|
-
@media (max-height: 240px) {
|
|
6875
|
-
.cg-graph-toolbar {
|
|
6876
|
-
flex-wrap: wrap;
|
|
6877
|
-
padding: 2px;
|
|
6878
|
-
}
|
|
6879
|
-
.cg-toolbar-left,
|
|
6880
|
-
.cg-toolbar-center,
|
|
6881
|
-
.cg-toolbar-right {
|
|
6882
|
-
flex-wrap: wrap;
|
|
6883
|
-
gap: 2px;
|
|
6884
|
-
}
|
|
6885
|
-
.cg-layout-buttons {
|
|
6886
|
-
display: none;
|
|
6887
|
-
}
|
|
6888
|
-
.cg-rank-dir-buttons {
|
|
6889
|
-
display: none;
|
|
6890
|
-
}
|
|
6891
|
-
.cg-cluster-depth-buttons {
|
|
6892
|
-
display: none;
|
|
6893
|
-
}
|
|
6894
|
-
.cg-node-detail-panel {
|
|
6895
|
-
width: calc(100% - 8px);
|
|
6896
|
-
left: 4px;
|
|
6897
|
-
top: 4px;
|
|
6898
|
-
max-height: calc(100% - 40px);
|
|
6899
|
-
}
|
|
6900
|
-
}
|
|
6901
|
-
}
|
|
6902
|
-
/* ---- Conversation View Integration Mode ----------------------------------- */
|
|
6903
|
-
|
|
6904
|
-
/* When integrated into DSH's conversation.view slot, the panel fills the
|
|
6905
|
-
* conversation area. Toolbar is always visible; graph canvas fills remaining
|
|
6906
|
-
* space. The panel must not collapse or float — it's the primary view. */
|
|
6907
|
-
[data-cg-integration="conversation-view"] {
|
|
6908
|
-
width: 100%;
|
|
6909
|
-
height: 100%;
|
|
6910
|
-
min-height: 200px;
|
|
6911
|
-
display: flex;
|
|
6912
|
-
flex-direction: column;
|
|
6913
|
-
}
|
|
6914
|
-
|
|
6915
|
-
[data-cg-integration="conversation-view"] .cg-graph-toolbar {
|
|
6916
|
-
display: flex;
|
|
6917
|
-
flex-shrink: 0;
|
|
6918
|
-
}
|
|
6919
|
-
|
|
6920
|
-
[data-cg-integration="conversation-view"] .cg-graph-container {
|
|
6921
|
-
flex: 1;
|
|
6922
|
-
min-height: 0;
|
|
6923
|
-
}
|
|
6924
|
-
|
|
6925
|
-
[data-cg-integration="conversation-view"] .cg-collapse-fab {
|
|
6926
|
-
display: none;
|
|
6927
|
-
}
|
|
6928
|
-
|
|
6929
|
-
[data-cg-integration="conversation-view"] .cg-status-bar {
|
|
6930
|
-
flex-shrink: 0;
|
|
6931
|
-
}
|
|
6932
|
-
|
|
6933
|
-
/* Conversation view integration (conversation.view slot) */
|
|
6934
|
-
.cg-conversation-view-wrapper {
|
|
6935
|
-
display: flex;
|
|
6936
|
-
flex-direction: column;
|
|
6937
|
-
width: 100%;
|
|
6938
|
-
height: 100%;
|
|
6939
|
-
min-height: 0;
|
|
6940
|
-
}
|
|
6941
|
-
|
|
6942
|
-
.cg-conversation-view-wrapper[data-cg-visible="false"] {
|
|
6943
|
-
display: none;
|
|
6944
|
-
}
|
|
6945
|
-
|
|
6946
|
-
/* Generic host container for host-managed embedded panels
|
|
6947
|
-
* (conversation.view / sidebar-right / better-sidebar). */
|
|
6948
|
-
.cg-integration-host {
|
|
6949
|
-
display: block;
|
|
6950
|
-
width: 100%;
|
|
6951
|
-
height: 100%;
|
|
6952
|
-
min-height: 0;
|
|
6953
|
-
}
|
|
6954
|
-
|
|
6955
|
-
.cg-integration-host[data-cg-visible="false"] {
|
|
6956
|
-
display: none;
|
|
6957
|
-
}
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
/* In conversation-view mode, the panel should not have its own border/radius
|
|
6961
|
-
* since it's embedded in the DSH conversation area */
|
|
6962
|
-
[data-cg-integration="conversation-view"].cg-graph-panel:not(.cg-collapsed) {
|
|
6963
|
-
border: none;
|
|
6964
|
-
border-radius: 0;
|
|
6965
|
-
}
|
|
6966
|
-
/* ---- Sidebar Right Tab Integration Mode ----------------------------------- */
|
|
6967
|
-
|
|
6968
|
-
/* When integrated into DSH's sidebar.right.pane.tab, the panel is in a
|
|
6969
|
-
* right sidebar tab. Width is typically 300-500px, height is full window. */
|
|
6970
|
-
[data-cg-integration="sidebar-right"] {
|
|
6971
|
-
width: 100%;
|
|
6972
|
-
height: 100%;
|
|
6973
|
-
min-height: 300px;
|
|
6974
|
-
display: flex;
|
|
6975
|
-
flex-direction: column;
|
|
6976
|
-
}
|
|
6977
|
-
|
|
6978
|
-
[data-cg-integration="sidebar-right"] .cg-graph-toolbar {
|
|
6979
|
-
display: flex;
|
|
6980
|
-
flex-shrink: 0;
|
|
6981
|
-
}
|
|
6982
|
-
|
|
6983
|
-
[data-cg-integration="sidebar-right"] .cg-graph-container {
|
|
6984
|
-
flex: 1;
|
|
6985
|
-
min-height: 0;
|
|
6986
|
-
}
|
|
6987
|
-
|
|
6988
|
-
[data-cg-integration="sidebar-right"] .cg-collapse-fab {
|
|
6989
|
-
display: none;
|
|
6990
|
-
}
|
|
6991
|
-
|
|
6992
|
-
[data-cg-integration="sidebar-right"].cg-graph-panel:not(.cg-collapsed) {
|
|
6993
|
-
border: none;
|
|
6994
|
-
border-radius: 0;
|
|
6995
|
-
}
|
|
6996
|
-
|
|
6997
|
-
/* ---- Better Sidebar Integration Mode -------------------------------------- */
|
|
6998
|
-
|
|
6999
|
-
/* When integrated via betterSidebar API, the panel is in a sidebar tab.
|
|
7000
|
-
* Similar to sidebar-right but may have different container constraints. */
|
|
7001
|
-
[data-cg-integration="better-sidebar"] {
|
|
7002
|
-
width: 100%;
|
|
7003
|
-
height: 100%;
|
|
7004
|
-
min-height: 200px;
|
|
7005
|
-
display: flex;
|
|
7006
|
-
flex-direction: column;
|
|
7007
|
-
}
|
|
7008
|
-
|
|
7009
|
-
[data-cg-integration="better-sidebar"] .cg-graph-toolbar {
|
|
7010
|
-
display: flex;
|
|
7011
|
-
flex-shrink: 0;
|
|
7012
|
-
}
|
|
7013
|
-
|
|
7014
|
-
[data-cg-integration="better-sidebar"] .cg-graph-container {
|
|
7015
|
-
flex: 1;
|
|
7016
|
-
min-height: 0;
|
|
7017
|
-
}
|
|
7018
|
-
|
|
7019
|
-
[data-cg-integration="better-sidebar"] .cg-collapse-fab {
|
|
7020
|
-
display: none;
|
|
7021
|
-
}
|
|
7022
|
-
|
|
7023
|
-
[data-cg-integration="better-sidebar"].cg-graph-panel:not(.cg-collapsed) {
|
|
7024
|
-
border: none;
|
|
7025
|
-
border-radius: 0;
|
|
7026
|
-
}
|
|
7027
|
-
|
|
7028
|
-
/* ---- Remote narrow-screen carrier adaptation ------------------------------- */
|
|
7029
|
-
|
|
7030
|
-
/* \`@linxin666/dsh-remote-web-ui\` is a real, widely-installed plugin, but it
|
|
7031
|
-
* exposes no panel/tab API at all — its client half only occupies the
|
|
7032
|
-
* \`sidebar.footer.action\` seat and writes DOM adaptation marks
|
|
7033
|
-
* (\`lib/client.js:4155 ACTIVE_CLASS = "dsh-remote-portrait"\`, added to
|
|
7034
|
-
* \`document.body\` at \`:4368\`). So the honest relationship is "detect it and let
|
|
7035
|
-
* the layout adapt", not "call its API". The former \`remote-web-ui\`
|
|
7036
|
-
* integration mode was a phantom (\`ctx.get('remoteWebUI')\` exists in no DSH
|
|
7037
|
-
* generation) and has been removed; these rules replace it.
|
|
7038
|
-
*
|
|
7039
|
-
* The toolbar must stay reachable on a phone-width carrier, so it is allowed to
|
|
7040
|
-
* wrap instead of overflowing. */
|
|
7041
|
-
body.dsh-remote-portrait .cg-graph-toolbar {
|
|
7042
|
-
flex-wrap: wrap;
|
|
7043
|
-
row-gap: 4px;
|
|
7044
|
-
}
|
|
7045
|
-
|
|
7046
|
-
body.dsh-remote-portrait .cg-graph-panel:not(.cg-collapsed) {
|
|
7047
|
-
border: none;
|
|
7048
|
-
border-radius: 0;
|
|
7049
|
-
}
|
|
7050
|
-
|
|
7051
|
-
/* Below 480px the toolbar's own compact breakpoint (see Toolbar.tsx) already
|
|
7052
|
-
* collapses secondary controls; the graph area just needs the remaining
|
|
7053
|
-
* height. */
|
|
7054
|
-
body.dsh-remote-portrait .cg-graph-container {
|
|
7055
|
-
flex: 1;
|
|
7056
|
-
min-height: 0;
|
|
7057
|
-
}
|
|
7058
|
-
|
|
7059
|
-
/* ---- Toolbar always visible in non-floating modes -------------------------- */
|
|
7060
|
-
/* In all sidebar/slot integration modes, the toolbar must remain visible
|
|
7061
|
-
* even when there's no graph data. The toolbar is the primary interaction
|
|
7062
|
-
* surface and should not disappear.
|
|
7063
|
-
* Expressed as one rule over \`[data-cg-integration]\` (the attribute lives on
|
|
7064
|
-
* \`.cg-graph-panel\` itself, see GraphPanel.tsx) so every current and future
|
|
7065
|
-
* integration mode is covered. The \`:not(.cg-collapsed)\` guard is load-bearing:
|
|
7066
|
-
* without it this \`!important\` declaration outranks
|
|
7067
|
-
* \`.cg-graph-panel.cg-collapsed .cg-graph-toolbar { display: none }\` and the
|
|
7068
|
-
* toolbar keeps painting inside the 52px collapsed FAB. */
|
|
7069
|
-
.cg-graph-panel[data-cg-integration]:not(.cg-collapsed) .cg-graph-toolbar {
|
|
7070
|
-
display: flex !important;
|
|
7071
|
-
}
|
|
7072
|
-
|
|
7073
|
-
/* ---- Desktop vs Web distinction ------------------------------------------- */
|
|
7074
|
-
/* On desktop platforms, the panel may have more space. On web, it's
|
|
7075
|
-
* constrained by the browser window. */
|
|
7076
|
-
@media (min-width: 1024px) {
|
|
7077
|
-
[data-cg-integration="conversation-view"] .cg-graph-toolbar {
|
|
7078
|
-
padding: 0 12px;
|
|
7079
|
-
gap: 8px;
|
|
7080
|
-
}
|
|
7081
|
-
}
|
|
7082
|
-
|
|
7083
|
-
/* ---- Narrow-tier sheets (cycle-110 / T3) ---------------------------------- */
|
|
7084
|
-
/*
|
|
7085
|
-
* 窄面板(≤480px)下的两处版式修正:
|
|
7086
|
-
* 1. 节点详情面板改为**底部抽屉**(贴边、限高),不再 \`max-height: calc(100% - 24px)\`
|
|
7087
|
-
* 从上往下盖住整个画布;
|
|
7088
|
-
* 2. 抽屉打开时小地图与图例**临时让位**,关闭后回归。
|
|
7089
|
-
*
|
|
7090
|
-
* 为什么两条都要:三者都是 \`position: absolute\` + \`z-index: var(--cg-z-panel)\` 的绝对定位
|
|
7091
|
-
* 元素,窄档下面板几乎占满高度,小地图(右下 200px)与图例(左下 180px)会被盖住一半,
|
|
7092
|
-
* 面板底部的按钮也会与图例重叠 —— 点击落在错误元素上,而所有 DOM 断言照旧通过。
|
|
7093
|
-
*
|
|
7094
|
-
* 两套机制**声明必须一致**:
|
|
7095
|
-
* - \`@container graphpanel (max-width: 480px)\`:主机制,按面板宽度响应(与视口无关);
|
|
7096
|
-
* - \`[data-cg-tier="narrow"]\`:宿主不支持容器查询、或容器查询尺寸不可读时的兜底,
|
|
7097
|
-
* 由 \`hooks/usePanelTier.ts\` 实测宽度后写在根元素上。
|
|
7098
|
-
* \`tests/unit/panelTierContract.test.ts\` 会逐条比对这两处的声明,任一侧改动都会变红。
|
|
7099
|
-
*/
|
|
7100
|
-
@container graphpanel (max-width: 480px) {
|
|
7101
|
-
.cg-node-detail-panel {
|
|
7102
|
-
top: auto;
|
|
7103
|
-
bottom: calc(var(--cg-statusbar-h) + 8px);
|
|
7104
|
-
left: 8px;
|
|
7105
|
-
right: 8px;
|
|
7106
|
-
width: auto;
|
|
7107
|
-
max-height: 52%;
|
|
7108
|
-
}
|
|
7109
|
-
|
|
7110
|
-
/* 抽屉打开 ⇒ 两个角落的叠加层让位(关闭后自动回归,不需要 JS 复位) */
|
|
7111
|
-
.cg-graph-panel[data-cg-sheet="open"] .cg-mini-map,
|
|
7112
|
-
.cg-graph-panel[data-cg-sheet="open"] .cg-legend-panel {
|
|
7113
|
-
display: none;
|
|
7114
|
-
}
|
|
7115
|
-
}
|
|
7116
|
-
|
|
7117
|
-
/* 兜底机制(JS 实测档位):与上面的容器查询分支**逐条等价** */
|
|
7118
|
-
.cg-graph-panel[data-cg-tier="narrow"] .cg-node-detail-panel {
|
|
7119
|
-
top: auto;
|
|
7120
|
-
bottom: calc(var(--cg-statusbar-h) + 8px);
|
|
7121
|
-
left: 8px;
|
|
7122
|
-
right: 8px;
|
|
7123
|
-
width: auto;
|
|
7124
|
-
max-height: 52%;
|
|
7125
|
-
}
|
|
7126
|
-
|
|
7127
|
-
.cg-graph-panel[data-cg-tier="narrow"][data-cg-sheet="open"] .cg-mini-map,
|
|
7128
|
-
.cg-graph-panel[data-cg-tier="narrow"][data-cg-sheet="open"] .cg-legend-panel {
|
|
7129
|
-
display: none;
|
|
7130
|
-
}
|
|
6203
|
+
/* ---- Responsive ---------------------------------------------------------- */
|
|
6204
|
+
|
|
6205
|
+
/* Extra-narrow panels (DSH sidebar tab minimal width).
|
|
6206
|
+
* Hide the remaining toolbar extras to prioritize graph canvas.
|
|
6207
|
+
* The center group is already gone at the 600px tier below. */
|
|
6208
|
+
@container graphpanel (max-width: 380px) {
|
|
6209
|
+
.cg-tour-btn,
|
|
6210
|
+
.cg-minimap-btn,
|
|
6211
|
+
.cg-legend-btn {
|
|
6212
|
+
display: none;
|
|
6213
|
+
}
|
|
6214
|
+
|
|
6215
|
+
/* The separators are siblings of the \`.cg-toolbar-group\` elements, not descendants,
|
|
6216
|
+
so the previous \`.cg-toolbar-group ... .cg-toolbar-separator\` selector never
|
|
6217
|
+
matched anything. */
|
|
6218
|
+
.cg-toolbar-separator {
|
|
6219
|
+
display: none;
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6223
|
+
/* Small panels (DSH sidebar tab, narrow bottom panel).
|
|
6224
|
+
* Container queries respond to the PANEL width, not the viewport — the plugin
|
|
6225
|
+
* renders inside DSH panels whose width is unrelated to the window size. */
|
|
6226
|
+
@container graphpanel (max-width: 480px) {
|
|
6227
|
+
.cg-graph-panel {
|
|
6228
|
+
border-radius: var(--cg-radius-sm);
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
.cg-graph-toolbar {
|
|
6232
|
+
padding: 0 8px;
|
|
6233
|
+
gap: 4px;
|
|
6234
|
+
}
|
|
6235
|
+
|
|
6236
|
+
.cg-toolbar-left {
|
|
6237
|
+
font-size: 11px;
|
|
6238
|
+
}
|
|
6239
|
+
|
|
6240
|
+
.cg-toolbar-center {
|
|
6241
|
+
gap: 4px;
|
|
6242
|
+
}
|
|
6243
|
+
|
|
6244
|
+
.cg-toolbar-right {
|
|
6245
|
+
gap: 4px;
|
|
6246
|
+
}
|
|
6247
|
+
|
|
6248
|
+
.cg-layout-btn,
|
|
6249
|
+
.cg-graph-type-btn,
|
|
6250
|
+
.cg-rank-dir-btn {
|
|
6251
|
+
padding: 3px 6px;
|
|
6252
|
+
font-size: 10px;
|
|
6253
|
+
}
|
|
6254
|
+
|
|
6255
|
+
.cg-search-btn,
|
|
6256
|
+
.cg-theme-btn,
|
|
6257
|
+
.cg-export-btn,
|
|
6258
|
+
.cg-collapse-btn,
|
|
6259
|
+
.cg-chain-btn,
|
|
6260
|
+
.cg-cycle-btn,
|
|
6261
|
+
.cg-impact-btn,
|
|
6262
|
+
.cg-inheritance-btn,
|
|
6263
|
+
.cg-minimap-btn,
|
|
6264
|
+
.cg-refresh-btn,
|
|
6265
|
+
.cg-undo-btn,
|
|
6266
|
+
.cg-redo-btn,
|
|
6267
|
+
.cg-import-btn,
|
|
6268
|
+
.cg-legend-btn,
|
|
6269
|
+
.cg-lang-btn,
|
|
6270
|
+
.cg-pathfind-btn,
|
|
6271
|
+
.cg-health-btn,
|
|
6272
|
+
.cg-rules-btn,
|
|
6273
|
+
.cg-hotspot-btn,
|
|
6274
|
+
.cg-godmodule-btn,
|
|
6275
|
+
.cg-corefunction-btn,
|
|
6276
|
+
.cg-analysis-dropdown-btn,
|
|
6277
|
+
.cg-quality-dropdown-btn,
|
|
6278
|
+
.cg-tour-btn {
|
|
6279
|
+
width: 26px;
|
|
6280
|
+
height: 26px;
|
|
6281
|
+
font-size: 12px;
|
|
6282
|
+
}
|
|
6283
|
+
|
|
6284
|
+
.cg-dropdown-content {
|
|
6285
|
+
min-width: 140px;
|
|
6286
|
+
}
|
|
6287
|
+
|
|
6288
|
+
.cg-node-detail-panel {
|
|
6289
|
+
width: calc(100% - 24px);
|
|
6290
|
+
left: 12px;
|
|
6291
|
+
top: 12px;
|
|
6292
|
+
}
|
|
6293
|
+
|
|
6294
|
+
.cg-legend-panel {
|
|
6295
|
+
width: 140px;
|
|
6296
|
+
}
|
|
6297
|
+
|
|
6298
|
+
.cg-mini-map {
|
|
6299
|
+
width: 140px;
|
|
6300
|
+
}
|
|
6301
|
+
|
|
6302
|
+
.cg-type-badge {
|
|
6303
|
+
display: none;
|
|
6304
|
+
}
|
|
6305
|
+
|
|
6306
|
+
.cg-toolbar-separator {
|
|
6307
|
+
display: none;
|
|
6308
|
+
}
|
|
6309
|
+
|
|
6310
|
+
.cg-graph-type-buttons {
|
|
6311
|
+
display: none;
|
|
6312
|
+
}
|
|
6313
|
+
|
|
6314
|
+
.cg-view-mode-buttons {
|
|
6315
|
+
gap: 1px;
|
|
6316
|
+
}
|
|
6317
|
+
|
|
6318
|
+
.cg-view-mode-btn {
|
|
6319
|
+
padding: 2px 4px;
|
|
6320
|
+
font-size: 9px;
|
|
6321
|
+
}
|
|
6322
|
+
|
|
6323
|
+
.cg-search-panel,
|
|
6324
|
+
.cg-path-panel {
|
|
6325
|
+
width: calc(100% - 16px);
|
|
6326
|
+
max-width: 320px;
|
|
6327
|
+
}
|
|
6328
|
+
|
|
6329
|
+
.cg-filter-panel {
|
|
6330
|
+
width: calc(100% - 16px);
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
.cg-heatmap-btn--activate,
|
|
6334
|
+
.cg-heatmap-btn--deactivate {
|
|
6335
|
+
width: 26px;
|
|
6336
|
+
height: 26px;
|
|
6337
|
+
font-size: 12px;
|
|
6338
|
+
}
|
|
6339
|
+
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
/* Medium panels (DSH sidebar medium width) */
|
|
6343
|
+
@container graphpanel (max-width: 768px) {
|
|
6344
|
+
.cg-graph-toolbar {
|
|
6345
|
+
padding: 0 8px;
|
|
6346
|
+
gap: 6px;
|
|
6347
|
+
}
|
|
6348
|
+
|
|
6349
|
+
.cg-toolbar-center {
|
|
6350
|
+
gap: 6px;
|
|
6351
|
+
}
|
|
6352
|
+
|
|
6353
|
+
.cg-toolbar-right {
|
|
6354
|
+
gap: 6px;
|
|
6355
|
+
}
|
|
6356
|
+
|
|
6357
|
+
.cg-layout-btn,
|
|
6358
|
+
.cg-graph-type-btn {
|
|
6359
|
+
padding: 3px 8px;
|
|
6360
|
+
font-size: 11px;
|
|
6361
|
+
}
|
|
6362
|
+
|
|
6363
|
+
.cg-node-detail-panel {
|
|
6364
|
+
width: 220px;
|
|
6365
|
+
}
|
|
6366
|
+
|
|
6367
|
+
.cg-legend-panel {
|
|
6368
|
+
width: 160px;
|
|
6369
|
+
}
|
|
6370
|
+
|
|
6371
|
+
.cg-mini-map {
|
|
6372
|
+
width: 160px;
|
|
6373
|
+
}
|
|
6374
|
+
|
|
6375
|
+
.cg-filter-select {
|
|
6376
|
+
font-size: 11px;
|
|
6377
|
+
}
|
|
6378
|
+
|
|
6379
|
+
.cg-toolbar-separator {
|
|
6380
|
+
margin: 0 2px;
|
|
6381
|
+
}
|
|
6382
|
+
|
|
6383
|
+
.cg-tour-btn {
|
|
6384
|
+
display: none;
|
|
6385
|
+
}
|
|
6386
|
+
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
/* Medium-narrow panels — hide tour and reduce analysis button clutter */
|
|
6390
|
+
@container graphpanel (max-width: 600px) {
|
|
6391
|
+
.cg-tour-btn,
|
|
6392
|
+
.cg-minimap-btn {
|
|
6393
|
+
display: none;
|
|
6394
|
+
}
|
|
6395
|
+
|
|
6396
|
+
.cg-toolbar-left .cg-type-badge {
|
|
6397
|
+
display: none;
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6400
|
+
/* The center group (layout / view-mode / level) is the widest fixed-width block in the
|
|
6401
|
+
toolbar: 150px for its three icon-only triggers, measured on the real host. At a 520px
|
|
6402
|
+
panel it was being squeezed to 115px, which wrapped the level trigger onto two lines
|
|
6403
|
+
and clipped it; at 600px it still has 45px of slack. Hiding it here instead of letting
|
|
6404
|
+
it be crushed keeps the workspace chip and every right-hand control (export / more /
|
|
6405
|
+
collapse) reachable. Of the three controls, only the layout keeps an alternative entry
|
|
6406
|
+
point (Ctrl+L cycles it, see \`usePanelKeyboard\`); view mode and hierarchy level lose
|
|
6407
|
+
their mouse entry below this tier — see the note in \`docs/需求分析.md\` §28.3. */
|
|
6408
|
+
.cg-toolbar-center {
|
|
6409
|
+
display: none;
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
|
|
6413
|
+
/* Very small panels */
|
|
6414
|
+
@container graphpanel (max-width: 320px) {
|
|
6415
|
+
.cg-graph-toolbar {
|
|
6416
|
+
gap: 2px;
|
|
6417
|
+
flex-wrap: wrap;
|
|
6418
|
+
padding: 4px 4px;
|
|
6419
|
+
}
|
|
6420
|
+
|
|
6421
|
+
.cg-toolbar-left,
|
|
6422
|
+
.cg-toolbar-center,
|
|
6423
|
+
.cg-toolbar-right {
|
|
6424
|
+
flex-wrap: wrap;
|
|
6425
|
+
gap: 2px;
|
|
6426
|
+
}
|
|
6427
|
+
|
|
6428
|
+
.cg-layout-buttons,
|
|
6429
|
+
.cg-graph-type-buttons {
|
|
6430
|
+
padding: 1px;
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6433
|
+
.cg-layout-btn,
|
|
6434
|
+
.cg-graph-type-btn {
|
|
6435
|
+
padding: 2px 4px;
|
|
6436
|
+
font-size: 9px;
|
|
6437
|
+
}
|
|
6438
|
+
|
|
6439
|
+
.cg-filter-select {
|
|
6440
|
+
padding: 3px 20px 3px 6px;
|
|
6441
|
+
font-size: 10px;
|
|
6442
|
+
}
|
|
6443
|
+
|
|
6444
|
+
.cg-node-count {
|
|
6445
|
+
font-size: 10px;
|
|
6446
|
+
}
|
|
6447
|
+
|
|
6448
|
+
.cg-legend-panel,
|
|
6449
|
+
.cg-mini-map {
|
|
6450
|
+
width: 120px;
|
|
6451
|
+
}
|
|
6452
|
+
|
|
6453
|
+
/* 下拉内容已 portal 到 document.body,不再是本容器的后代,此处的
|
|
6454
|
+
* \`@container\` 抽屉规则永远匹配不到;抽屉形态改由 Dropdown 组件测量面板
|
|
6455
|
+
* 宽度后加 \`.cg-portal-drawer\`(见 layout.css)实现。 */
|
|
6456
|
+
|
|
6457
|
+
.cg-search-panel,
|
|
6458
|
+
.cg-path-panel,
|
|
6459
|
+
.cg-filter-panel {
|
|
6460
|
+
width: calc(100% - 8px);
|
|
6461
|
+
left: 4px;
|
|
6462
|
+
right: 4px;
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6465
|
+
.cg-view-mode-buttons {
|
|
6466
|
+
flex-wrap: wrap;
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
|
|
6470
|
+
/* Large screens — allow fixed floating panel */
|
|
6471
|
+
@media (min-width: 1200px) {
|
|
6472
|
+
.cg-graph-panel.cg-floating {
|
|
6473
|
+
position: fixed;
|
|
6474
|
+
right: 16px;
|
|
6475
|
+
bottom: 16px;
|
|
6476
|
+
width: 640px;
|
|
6477
|
+
height: 520px;
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6481
|
+
/* ---- Sidebar tab mode (short height) -------------------------------------- */
|
|
6482
|
+
/* When the plugin renders inside a DSH sidebar tab, the panel height can be
|
|
6483
|
+
* very limited. Adapt the layout to prioritize the graph canvas. */
|
|
6484
|
+
@container graphpanel (max-height: 400px) {
|
|
6485
|
+
.cg-graph-toolbar {
|
|
6486
|
+
padding: 0 4px;
|
|
6487
|
+
gap: 2px;
|
|
6488
|
+
}
|
|
6489
|
+
|
|
6490
|
+
.cg-toolbar-left .cg-node-count {
|
|
6491
|
+
font-size: 10px;
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
.cg-type-badge {
|
|
6495
|
+
display: none;
|
|
6496
|
+
}
|
|
6497
|
+
|
|
6498
|
+
.cg-toolbar-separator {
|
|
6499
|
+
display: none;
|
|
6500
|
+
}
|
|
6501
|
+
|
|
6502
|
+
.cg-graph-type-buttons {
|
|
6503
|
+
display: none;
|
|
6504
|
+
}
|
|
6505
|
+
|
|
6506
|
+
.cg-view-mode-buttons {
|
|
6507
|
+
gap: 1px;
|
|
6508
|
+
}
|
|
6509
|
+
|
|
6510
|
+
.cg-view-mode-btn {
|
|
6511
|
+
padding: 2px 4px;
|
|
6512
|
+
font-size: 9px;
|
|
6513
|
+
}
|
|
6514
|
+
|
|
6515
|
+
.cg-node-detail-panel {
|
|
6516
|
+
max-height: calc(100% - 60px);
|
|
6517
|
+
overflow-y: auto;
|
|
6518
|
+
}
|
|
6519
|
+
|
|
6520
|
+
.cg-legend-panel {
|
|
6521
|
+
max-height: 120px;
|
|
6522
|
+
overflow-y: auto;
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
.cg-mini-map {
|
|
6526
|
+
max-height: 100px;
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
.cg-search-panel,
|
|
6530
|
+
.cg-path-panel,
|
|
6531
|
+
.cg-filter-panel {
|
|
6532
|
+
max-height: calc(100% - 60px);
|
|
6533
|
+
overflow-y: auto;
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
|
|
6537
|
+
@container graphpanel (max-height: 240px) {
|
|
6538
|
+
.cg-graph-toolbar {
|
|
6539
|
+
flex-wrap: wrap;
|
|
6540
|
+
padding: 2px;
|
|
6541
|
+
}
|
|
6542
|
+
|
|
6543
|
+
.cg-toolbar-left,
|
|
6544
|
+
.cg-toolbar-center,
|
|
6545
|
+
.cg-toolbar-right {
|
|
6546
|
+
flex-wrap: wrap;
|
|
6547
|
+
gap: 2px;
|
|
6548
|
+
}
|
|
6549
|
+
|
|
6550
|
+
.cg-layout-buttons {
|
|
6551
|
+
display: none;
|
|
6552
|
+
}
|
|
6553
|
+
|
|
6554
|
+
.cg-rank-dir-buttons {
|
|
6555
|
+
display: none;
|
|
6556
|
+
}
|
|
6557
|
+
|
|
6558
|
+
.cg-cluster-depth-buttons {
|
|
6559
|
+
display: none;
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6562
|
+
.cg-node-detail-panel {
|
|
6563
|
+
width: calc(100% - 8px);
|
|
6564
|
+
left: 4px;
|
|
6565
|
+
top: 4px;
|
|
6566
|
+
max-height: calc(100% - 40px);
|
|
6567
|
+
}
|
|
6568
|
+
}
|
|
6569
|
+
|
|
6570
|
+
/* ---- Reduced motion ------------------------------------------------------ */
|
|
6571
|
+
|
|
6572
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6573
|
+
.cg-graph-panel *,
|
|
6574
|
+
.cg-graph-panel *::before,
|
|
6575
|
+
.cg-graph-panel *::after,
|
|
6576
|
+
.cg-floating-panel *,
|
|
6577
|
+
.cg-floating-panel *::before,
|
|
6578
|
+
.cg-floating-panel *::after,
|
|
6579
|
+
.cg-floating-container *,
|
|
6580
|
+
.cg-floating-container *::before,
|
|
6581
|
+
.cg-floating-container *::after {
|
|
6582
|
+
animation-duration: 0.01ms !important;
|
|
6583
|
+
transition-duration: 0.01ms !important;
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
|
|
6587
|
+
/* ---- Touch device adaptation --------------------------------------------- */
|
|
6588
|
+
|
|
6589
|
+
@media (pointer: coarse) {
|
|
6590
|
+
.cg-layout-btn,
|
|
6591
|
+
.cg-graph-type-btn,
|
|
6592
|
+
.cg-search-btn,
|
|
6593
|
+
.cg-theme-btn,
|
|
6594
|
+
.cg-export-btn,
|
|
6595
|
+
.cg-collapse-btn,
|
|
6596
|
+
.cg-chain-btn,
|
|
6597
|
+
.cg-cycle-btn,
|
|
6598
|
+
.cg-impact-btn,
|
|
6599
|
+
.cg-inheritance-btn,
|
|
6600
|
+
.cg-minimap-btn,
|
|
6601
|
+
.cg-refresh-btn,
|
|
6602
|
+
.cg-undo-btn,
|
|
6603
|
+
.cg-redo-btn,
|
|
6604
|
+
.cg-import-btn,
|
|
6605
|
+
.cg-legend-btn,
|
|
6606
|
+
.cg-lang-btn,
|
|
6607
|
+
.cg-pathfind-btn,
|
|
6608
|
+
.cg-health-btn,
|
|
6609
|
+
.cg-rules-btn,
|
|
6610
|
+
.cg-hotspot-btn,
|
|
6611
|
+
.cg-godmodule-btn,
|
|
6612
|
+
.cg-corefunction-btn,
|
|
6613
|
+
.cg-analysis-dropdown-btn,
|
|
6614
|
+
.cg-quality-dropdown-btn,
|
|
6615
|
+
.cg-tour-btn {
|
|
6616
|
+
min-width: 44px;
|
|
6617
|
+
min-height: 44px;
|
|
6618
|
+
}
|
|
6619
|
+
|
|
6620
|
+
.cg-node-detail-panel .cg-close-btn,
|
|
6621
|
+
.cg-legend-close,
|
|
6622
|
+
.cg-import-close,
|
|
6623
|
+
.cg-close-btn {
|
|
6624
|
+
min-width: 44px;
|
|
6625
|
+
min-height: 44px;
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
/* ---- Responsive for new components ---------------------------------------- */
|
|
6629
|
+
|
|
6630
|
+
@container graphpanel (max-width: 480px) {
|
|
6631
|
+
.cg-path-bar,
|
|
6632
|
+
.cg-health-score-card,
|
|
6633
|
+
.cg-rule-panel {
|
|
6634
|
+
width: calc(100% - 24px);
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6637
|
+
.cg-analysis-panel {
|
|
6638
|
+
width: calc(100% - 24px);
|
|
6639
|
+
}
|
|
6640
|
+
|
|
6641
|
+
.cg-focus-label {
|
|
6642
|
+
max-width: 120px;
|
|
6643
|
+
}
|
|
6644
|
+
}
|
|
6645
|
+
|
|
6646
|
+
@container graphpanel (max-width: 768px) {
|
|
6647
|
+
.cg-path-bar {
|
|
6648
|
+
width: 240px;
|
|
6649
|
+
}
|
|
6650
|
+
|
|
6651
|
+
.cg-health-score-card {
|
|
6652
|
+
width: 220px;
|
|
6653
|
+
}
|
|
6654
|
+
|
|
6655
|
+
.cg-rule-panel {
|
|
6656
|
+
width: 240px;
|
|
6657
|
+
}
|
|
6658
|
+
|
|
6659
|
+
.cg-analysis-panel {
|
|
6660
|
+
width: 200px;
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
/* ---- Container Queries fallback ------------------------------------------- */
|
|
6664
|
+
@supports not (container-type: inline-size) {
|
|
6665
|
+
@media (max-width: 380px) {
|
|
6666
|
+
.cg-tour-btn,
|
|
6667
|
+
.cg-minimap-btn,
|
|
6668
|
+
.cg-legend-btn {
|
|
6669
|
+
display: none;
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
|
|
6673
|
+
@media (max-width: 480px) {
|
|
6674
|
+
.cg-graph-panel {
|
|
6675
|
+
border-radius: var(--cg-radius-sm);
|
|
6676
|
+
}
|
|
6677
|
+
.cg-graph-toolbar {
|
|
6678
|
+
padding: 0 8px;
|
|
6679
|
+
gap: 4px;
|
|
6680
|
+
}
|
|
6681
|
+
.cg-toolbar-left {
|
|
6682
|
+
font-size: 11px;
|
|
6683
|
+
}
|
|
6684
|
+
.cg-toolbar-center {
|
|
6685
|
+
gap: 4px;
|
|
6686
|
+
}
|
|
6687
|
+
.cg-toolbar-right {
|
|
6688
|
+
gap: 4px;
|
|
6689
|
+
}
|
|
6690
|
+
.cg-layout-btn,
|
|
6691
|
+
.cg-graph-type-btn,
|
|
6692
|
+
.cg-rank-dir-btn {
|
|
6693
|
+
padding: 3px 6px;
|
|
6694
|
+
font-size: 10px;
|
|
6695
|
+
}
|
|
6696
|
+
.cg-search-btn,
|
|
6697
|
+
.cg-theme-btn,
|
|
6698
|
+
.cg-export-btn,
|
|
6699
|
+
.cg-collapse-btn,
|
|
6700
|
+
.cg-chain-btn,
|
|
6701
|
+
.cg-cycle-btn,
|
|
6702
|
+
.cg-impact-btn,
|
|
6703
|
+
.cg-inheritance-btn,
|
|
6704
|
+
.cg-minimap-btn,
|
|
6705
|
+
.cg-refresh-btn,
|
|
6706
|
+
|
|
6707
|
+
.cg-undo-btn,
|
|
6708
|
+
.cg-redo-btn,
|
|
6709
|
+
.cg-import-btn,
|
|
6710
|
+
.cg-legend-btn,
|
|
6711
|
+
.cg-lang-btn,
|
|
6712
|
+
.cg-pathfind-btn,
|
|
6713
|
+
.cg-health-btn,
|
|
6714
|
+
.cg-rules-btn,
|
|
6715
|
+
.cg-hotspot-btn,
|
|
6716
|
+
.cg-godmodule-btn,
|
|
6717
|
+
.cg-corefunction-btn,
|
|
6718
|
+
.cg-analysis-dropdown-btn,
|
|
6719
|
+
.cg-quality-dropdown-btn,
|
|
6720
|
+
.cg-tour-btn {
|
|
6721
|
+
width: 26px;
|
|
6722
|
+
height: 26px;
|
|
6723
|
+
font-size: 12px;
|
|
6724
|
+
}
|
|
6725
|
+
.cg-node-detail-panel {
|
|
6726
|
+
width: calc(100% - 24px);
|
|
6727
|
+
left: 12px;
|
|
6728
|
+
top: 12px;
|
|
6729
|
+
}
|
|
6730
|
+
.cg-legend-panel {
|
|
6731
|
+
width: 140px;
|
|
6732
|
+
}
|
|
6733
|
+
.cg-mini-map {
|
|
6734
|
+
width: 140px;
|
|
6735
|
+
}
|
|
6736
|
+
.cg-type-badge {
|
|
6737
|
+
display: none;
|
|
6738
|
+
}
|
|
6739
|
+
.cg-toolbar-separator {
|
|
6740
|
+
display: none;
|
|
6741
|
+
}
|
|
6742
|
+
.cg-graph-type-buttons {
|
|
6743
|
+
display: none;
|
|
6744
|
+
}
|
|
6745
|
+
.cg-dropdown-content {
|
|
6746
|
+
min-width: 140px;
|
|
6747
|
+
}
|
|
6748
|
+
.cg-view-mode-buttons {
|
|
6749
|
+
gap: 1px;
|
|
6750
|
+
}
|
|
6751
|
+
.cg-view-mode-btn {
|
|
6752
|
+
padding: 2px 4px;
|
|
6753
|
+
font-size: 9px;
|
|
6754
|
+
}
|
|
6755
|
+
.cg-search-panel,
|
|
6756
|
+
.cg-path-panel {
|
|
6757
|
+
width: calc(100% - 16px);
|
|
6758
|
+
max-width: 320px;
|
|
6759
|
+
}
|
|
6760
|
+
.cg-filter-panel {
|
|
6761
|
+
width: calc(100% - 16px);
|
|
6762
|
+
}
|
|
6763
|
+
.cg-heatmap-btn--activate,
|
|
6764
|
+
.cg-heatmap-btn--deactivate {
|
|
6765
|
+
width: 26px;
|
|
6766
|
+
height: 26px;
|
|
6767
|
+
font-size: 12px;
|
|
6768
|
+
}
|
|
6769
|
+
.cg-path-bar,
|
|
6770
|
+
.cg-health-score-card,
|
|
6771
|
+
.cg-rule-panel {
|
|
6772
|
+
width: calc(100% - 24px);
|
|
6773
|
+
}
|
|
6774
|
+
.cg-analysis-panel {
|
|
6775
|
+
width: calc(100% - 24px);
|
|
6776
|
+
}
|
|
6777
|
+
.cg-focus-label {
|
|
6778
|
+
max-width: 120px;
|
|
6779
|
+
}
|
|
6780
|
+
}
|
|
6781
|
+
|
|
6782
|
+
@media (max-width: 768px) {
|
|
6783
|
+
.cg-graph-toolbar {
|
|
6784
|
+
padding: 0 8px;
|
|
6785
|
+
gap: 6px;
|
|
6786
|
+
}
|
|
6787
|
+
.cg-toolbar-center {
|
|
6788
|
+
gap: 6px;
|
|
6789
|
+
}
|
|
6790
|
+
.cg-toolbar-right {
|
|
6791
|
+
gap: 6px;
|
|
6792
|
+
}
|
|
6793
|
+
.cg-layout-btn,
|
|
6794
|
+
.cg-graph-type-btn {
|
|
6795
|
+
padding: 3px 8px;
|
|
6796
|
+
font-size: 11px;
|
|
6797
|
+
}
|
|
6798
|
+
.cg-node-detail-panel {
|
|
6799
|
+
width: 220px;
|
|
6800
|
+
}
|
|
6801
|
+
.cg-legend-panel {
|
|
6802
|
+
width: 160px;
|
|
6803
|
+
}
|
|
6804
|
+
.cg-mini-map {
|
|
6805
|
+
width: 160px;
|
|
6806
|
+
}
|
|
6807
|
+
.cg-filter-select {
|
|
6808
|
+
font-size: 11px;
|
|
6809
|
+
}
|
|
6810
|
+
.cg-toolbar-separator {
|
|
6811
|
+
margin: 0 2px;
|
|
6812
|
+
}
|
|
6813
|
+
.cg-tour-btn {
|
|
6814
|
+
display: none;
|
|
6815
|
+
}
|
|
6816
|
+
.cg-path-bar {
|
|
6817
|
+
width: 240px;
|
|
6818
|
+
}
|
|
6819
|
+
.cg-health-score-card {
|
|
6820
|
+
width: 220px;
|
|
6821
|
+
}
|
|
6822
|
+
.cg-rule-panel {
|
|
6823
|
+
width: 240px;
|
|
6824
|
+
}
|
|
6825
|
+
.cg-analysis-panel {
|
|
6826
|
+
width: 200px;
|
|
6827
|
+
}
|
|
6828
|
+
}
|
|
6829
|
+
|
|
6830
|
+
@media (max-width: 600px) {
|
|
6831
|
+
.cg-tour-btn,
|
|
6832
|
+
.cg-minimap-btn {
|
|
6833
|
+
display: none;
|
|
6834
|
+
}
|
|
6835
|
+
.cg-toolbar-left .cg-type-badge {
|
|
6836
|
+
display: none;
|
|
6837
|
+
}
|
|
6838
|
+
/* Mirrors the 600px container tier: the center group needs 150px for its three
|
|
6839
|
+
icon-only triggers and gets crushed below that. */
|
|
6840
|
+
.cg-toolbar-center {
|
|
6841
|
+
display: none;
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6845
|
+
@media (max-width: 320px) {
|
|
6846
|
+
.cg-graph-toolbar {
|
|
6847
|
+
gap: 2px;
|
|
6848
|
+
flex-wrap: wrap;
|
|
6849
|
+
padding: 4px 4px;
|
|
6850
|
+
}
|
|
6851
|
+
.cg-toolbar-left,
|
|
6852
|
+
.cg-toolbar-center,
|
|
6853
|
+
.cg-toolbar-right {
|
|
6854
|
+
flex-wrap: wrap;
|
|
6855
|
+
gap: 2px;
|
|
6856
|
+
}
|
|
6857
|
+
.cg-layout-buttons,
|
|
6858
|
+
.cg-graph-type-buttons {
|
|
6859
|
+
padding: 1px;
|
|
6860
|
+
}
|
|
6861
|
+
.cg-layout-btn,
|
|
6862
|
+
.cg-graph-type-btn {
|
|
6863
|
+
padding: 2px 4px;
|
|
6864
|
+
font-size: 9px;
|
|
6865
|
+
}
|
|
6866
|
+
.cg-filter-select {
|
|
6867
|
+
padding: 3px 20px 3px 6px;
|
|
6868
|
+
font-size: 10px;
|
|
6869
|
+
}
|
|
6870
|
+
.cg-node-count {
|
|
6871
|
+
font-size: 10px;
|
|
6872
|
+
}
|
|
6873
|
+
.cg-legend-panel,
|
|
6874
|
+
.cg-mini-map {
|
|
6875
|
+
width: 120px;
|
|
6876
|
+
}
|
|
6877
|
+
/* 同上:portal 到 body 后这里也匹配不到,抽屉由 \`.cg-portal-drawer\` 接管。 */
|
|
6878
|
+
.cg-search-panel,
|
|
6879
|
+
.cg-path-panel,
|
|
6880
|
+
.cg-filter-panel {
|
|
6881
|
+
width: calc(100% - 8px);
|
|
6882
|
+
left: 4px;
|
|
6883
|
+
right: 4px;
|
|
6884
|
+
}
|
|
6885
|
+
.cg-view-mode-buttons {
|
|
6886
|
+
flex-wrap: wrap;
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
|
|
6890
|
+
@media (max-height: 400px) {
|
|
6891
|
+
.cg-graph-toolbar {
|
|
6892
|
+
padding: 0 4px;
|
|
6893
|
+
gap: 2px;
|
|
6894
|
+
}
|
|
6895
|
+
.cg-toolbar-left .cg-node-count {
|
|
6896
|
+
font-size: 10px;
|
|
6897
|
+
}
|
|
6898
|
+
.cg-type-badge {
|
|
6899
|
+
display: none;
|
|
6900
|
+
}
|
|
6901
|
+
.cg-toolbar-separator {
|
|
6902
|
+
display: none;
|
|
6903
|
+
}
|
|
6904
|
+
.cg-graph-type-buttons {
|
|
6905
|
+
display: none;
|
|
6906
|
+
}
|
|
6907
|
+
.cg-view-mode-buttons {
|
|
6908
|
+
gap: 1px;
|
|
6909
|
+
}
|
|
6910
|
+
.cg-view-mode-btn {
|
|
6911
|
+
padding: 2px 4px;
|
|
6912
|
+
font-size: 9px;
|
|
6913
|
+
}
|
|
6914
|
+
.cg-node-detail-panel {
|
|
6915
|
+
max-height: calc(100% - 60px);
|
|
6916
|
+
overflow-y: auto;
|
|
6917
|
+
}
|
|
6918
|
+
.cg-legend-panel {
|
|
6919
|
+
max-height: 120px;
|
|
6920
|
+
overflow-y: auto;
|
|
6921
|
+
}
|
|
6922
|
+
.cg-mini-map {
|
|
6923
|
+
max-height: 100px;
|
|
6924
|
+
}
|
|
6925
|
+
.cg-search-panel,
|
|
6926
|
+
.cg-path-panel,
|
|
6927
|
+
.cg-filter-panel {
|
|
6928
|
+
max-height: calc(100% - 60px);
|
|
6929
|
+
overflow-y: auto;
|
|
6930
|
+
}
|
|
6931
|
+
}
|
|
6932
|
+
|
|
6933
|
+
@media (max-height: 240px) {
|
|
6934
|
+
.cg-graph-toolbar {
|
|
6935
|
+
flex-wrap: wrap;
|
|
6936
|
+
padding: 2px;
|
|
6937
|
+
}
|
|
6938
|
+
.cg-toolbar-left,
|
|
6939
|
+
.cg-toolbar-center,
|
|
6940
|
+
.cg-toolbar-right {
|
|
6941
|
+
flex-wrap: wrap;
|
|
6942
|
+
gap: 2px;
|
|
6943
|
+
}
|
|
6944
|
+
.cg-layout-buttons {
|
|
6945
|
+
display: none;
|
|
6946
|
+
}
|
|
6947
|
+
.cg-rank-dir-buttons {
|
|
6948
|
+
display: none;
|
|
6949
|
+
}
|
|
6950
|
+
.cg-cluster-depth-buttons {
|
|
6951
|
+
display: none;
|
|
6952
|
+
}
|
|
6953
|
+
.cg-node-detail-panel {
|
|
6954
|
+
width: calc(100% - 8px);
|
|
6955
|
+
left: 4px;
|
|
6956
|
+
top: 4px;
|
|
6957
|
+
max-height: calc(100% - 40px);
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
/* ---- Conversation View Integration Mode ----------------------------------- */
|
|
6962
|
+
|
|
6963
|
+
/* When integrated into DSH's conversation.view slot, the panel fills the
|
|
6964
|
+
* conversation area. Toolbar is always visible; graph canvas fills remaining
|
|
6965
|
+
* space. The panel must not collapse or float — it's the primary view. */
|
|
6966
|
+
[data-cg-integration="conversation-view"] {
|
|
6967
|
+
width: 100%;
|
|
6968
|
+
height: 100%;
|
|
6969
|
+
min-height: 200px;
|
|
6970
|
+
display: flex;
|
|
6971
|
+
flex-direction: column;
|
|
6972
|
+
}
|
|
6973
|
+
|
|
6974
|
+
[data-cg-integration="conversation-view"] .cg-graph-toolbar {
|
|
6975
|
+
display: flex;
|
|
6976
|
+
flex-shrink: 0;
|
|
6977
|
+
}
|
|
6978
|
+
|
|
6979
|
+
[data-cg-integration="conversation-view"] .cg-graph-container {
|
|
6980
|
+
flex: 1;
|
|
6981
|
+
min-height: 0;
|
|
6982
|
+
}
|
|
6983
|
+
|
|
6984
|
+
[data-cg-integration="conversation-view"] .cg-collapse-fab {
|
|
6985
|
+
display: none;
|
|
6986
|
+
}
|
|
6987
|
+
|
|
6988
|
+
[data-cg-integration="conversation-view"] .cg-status-bar {
|
|
6989
|
+
flex-shrink: 0;
|
|
6990
|
+
}
|
|
6991
|
+
|
|
6992
|
+
/* Conversation view integration (conversation.view slot) */
|
|
6993
|
+
.cg-conversation-view-wrapper {
|
|
6994
|
+
display: flex;
|
|
6995
|
+
flex-direction: column;
|
|
6996
|
+
width: 100%;
|
|
6997
|
+
height: 100%;
|
|
6998
|
+
min-height: 0;
|
|
6999
|
+
}
|
|
7000
|
+
|
|
7001
|
+
.cg-conversation-view-wrapper[data-cg-visible="false"] {
|
|
7002
|
+
display: none;
|
|
7003
|
+
}
|
|
7004
|
+
|
|
7005
|
+
/* Generic host container for host-managed embedded panels
|
|
7006
|
+
* (conversation.view / sidebar-right / better-sidebar). */
|
|
7007
|
+
.cg-integration-host {
|
|
7008
|
+
display: block;
|
|
7009
|
+
width: 100%;
|
|
7010
|
+
height: 100%;
|
|
7011
|
+
min-height: 0;
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
.cg-integration-host[data-cg-visible="false"] {
|
|
7015
|
+
display: none;
|
|
7016
|
+
}
|
|
7017
|
+
|
|
7018
|
+
|
|
7019
|
+
/* In conversation-view mode, the panel should not have its own border/radius
|
|
7020
|
+
* since it's embedded in the DSH conversation area */
|
|
7021
|
+
[data-cg-integration="conversation-view"].cg-graph-panel:not(.cg-collapsed) {
|
|
7022
|
+
border: none;
|
|
7023
|
+
border-radius: 0;
|
|
7024
|
+
}
|
|
7025
|
+
/* ---- Sidebar Right Tab Integration Mode ----------------------------------- */
|
|
7026
|
+
|
|
7027
|
+
/* When integrated into DSH's sidebar.right.pane.tab, the panel is in a
|
|
7028
|
+
* right sidebar tab. Width is typically 300-500px, height is full window. */
|
|
7029
|
+
[data-cg-integration="sidebar-right"] {
|
|
7030
|
+
width: 100%;
|
|
7031
|
+
height: 100%;
|
|
7032
|
+
min-height: 300px;
|
|
7033
|
+
display: flex;
|
|
7034
|
+
flex-direction: column;
|
|
7035
|
+
}
|
|
7036
|
+
|
|
7037
|
+
[data-cg-integration="sidebar-right"] .cg-graph-toolbar {
|
|
7038
|
+
display: flex;
|
|
7039
|
+
flex-shrink: 0;
|
|
7040
|
+
}
|
|
7041
|
+
|
|
7042
|
+
[data-cg-integration="sidebar-right"] .cg-graph-container {
|
|
7043
|
+
flex: 1;
|
|
7044
|
+
min-height: 0;
|
|
7045
|
+
}
|
|
7046
|
+
|
|
7047
|
+
[data-cg-integration="sidebar-right"] .cg-collapse-fab {
|
|
7048
|
+
display: none;
|
|
7049
|
+
}
|
|
7050
|
+
|
|
7051
|
+
[data-cg-integration="sidebar-right"].cg-graph-panel:not(.cg-collapsed) {
|
|
7052
|
+
border: none;
|
|
7053
|
+
border-radius: 0;
|
|
7054
|
+
}
|
|
7055
|
+
|
|
7056
|
+
/* ---- Better Sidebar Integration Mode -------------------------------------- */
|
|
7057
|
+
|
|
7058
|
+
/* When integrated via betterSidebar API, the panel is in a sidebar tab.
|
|
7059
|
+
* Similar to sidebar-right but may have different container constraints. */
|
|
7060
|
+
[data-cg-integration="better-sidebar"] {
|
|
7061
|
+
width: 100%;
|
|
7062
|
+
height: 100%;
|
|
7063
|
+
min-height: 200px;
|
|
7064
|
+
display: flex;
|
|
7065
|
+
flex-direction: column;
|
|
7066
|
+
}
|
|
7067
|
+
|
|
7068
|
+
[data-cg-integration="better-sidebar"] .cg-graph-toolbar {
|
|
7069
|
+
display: flex;
|
|
7070
|
+
flex-shrink: 0;
|
|
7071
|
+
}
|
|
7072
|
+
|
|
7073
|
+
[data-cg-integration="better-sidebar"] .cg-graph-container {
|
|
7074
|
+
flex: 1;
|
|
7075
|
+
min-height: 0;
|
|
7076
|
+
}
|
|
7077
|
+
|
|
7078
|
+
[data-cg-integration="better-sidebar"] .cg-collapse-fab {
|
|
7079
|
+
display: none;
|
|
7080
|
+
}
|
|
7081
|
+
|
|
7082
|
+
[data-cg-integration="better-sidebar"].cg-graph-panel:not(.cg-collapsed) {
|
|
7083
|
+
border: none;
|
|
7084
|
+
border-radius: 0;
|
|
7085
|
+
}
|
|
7086
|
+
|
|
7087
|
+
/* ---- Remote narrow-screen carrier adaptation ------------------------------- */
|
|
7088
|
+
|
|
7089
|
+
/* \`@linxin666/dsh-remote-web-ui\` is a real, widely-installed plugin, but it
|
|
7090
|
+
* exposes no panel/tab API at all — its client half only occupies the
|
|
7091
|
+
* \`sidebar.footer.action\` seat and writes DOM adaptation marks
|
|
7092
|
+
* (\`lib/client.js:4155 ACTIVE_CLASS = "dsh-remote-portrait"\`, added to
|
|
7093
|
+
* \`document.body\` at \`:4368\`). So the honest relationship is "detect it and let
|
|
7094
|
+
* the layout adapt", not "call its API". The former \`remote-web-ui\`
|
|
7095
|
+
* integration mode was a phantom (\`ctx.get('remoteWebUI')\` exists in no DSH
|
|
7096
|
+
* generation) and has been removed; these rules replace it.
|
|
7097
|
+
*
|
|
7098
|
+
* The toolbar must stay reachable on a phone-width carrier, so it is allowed to
|
|
7099
|
+
* wrap instead of overflowing. */
|
|
7100
|
+
body.dsh-remote-portrait .cg-graph-toolbar {
|
|
7101
|
+
flex-wrap: wrap;
|
|
7102
|
+
row-gap: 4px;
|
|
7103
|
+
}
|
|
7104
|
+
|
|
7105
|
+
body.dsh-remote-portrait .cg-graph-panel:not(.cg-collapsed) {
|
|
7106
|
+
border: none;
|
|
7107
|
+
border-radius: 0;
|
|
7108
|
+
}
|
|
7109
|
+
|
|
7110
|
+
/* Below 480px the toolbar's own compact breakpoint (see Toolbar.tsx) already
|
|
7111
|
+
* collapses secondary controls; the graph area just needs the remaining
|
|
7112
|
+
* height. */
|
|
7113
|
+
body.dsh-remote-portrait .cg-graph-container {
|
|
7114
|
+
flex: 1;
|
|
7115
|
+
min-height: 0;
|
|
7116
|
+
}
|
|
7117
|
+
|
|
7118
|
+
/* ---- Toolbar always visible in non-floating modes -------------------------- */
|
|
7119
|
+
/* In all sidebar/slot integration modes, the toolbar must remain visible
|
|
7120
|
+
* even when there's no graph data. The toolbar is the primary interaction
|
|
7121
|
+
* surface and should not disappear.
|
|
7122
|
+
* Expressed as one rule over \`[data-cg-integration]\` (the attribute lives on
|
|
7123
|
+
* \`.cg-graph-panel\` itself, see GraphPanel.tsx) so every current and future
|
|
7124
|
+
* integration mode is covered. The \`:not(.cg-collapsed)\` guard is load-bearing:
|
|
7125
|
+
* without it this \`!important\` declaration outranks
|
|
7126
|
+
* \`.cg-graph-panel.cg-collapsed .cg-graph-toolbar { display: none }\` and the
|
|
7127
|
+
* toolbar keeps painting inside the 52px collapsed FAB. */
|
|
7128
|
+
.cg-graph-panel[data-cg-integration]:not(.cg-collapsed) .cg-graph-toolbar {
|
|
7129
|
+
display: flex !important;
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
/* ---- Desktop vs Web distinction ------------------------------------------- */
|
|
7133
|
+
/* On desktop platforms, the panel may have more space. On web, it's
|
|
7134
|
+
* constrained by the browser window. */
|
|
7135
|
+
@media (min-width: 1024px) {
|
|
7136
|
+
[data-cg-integration="conversation-view"] .cg-graph-toolbar {
|
|
7137
|
+
padding: 0 12px;
|
|
7138
|
+
gap: 8px;
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
|
|
7142
|
+
/* ---- Narrow-tier sheets (cycle-110 / T3) ---------------------------------- */
|
|
7143
|
+
/*
|
|
7144
|
+
* 窄面板(≤480px)下的两处版式修正:
|
|
7145
|
+
* 1. 节点详情面板改为**底部抽屉**(贴边、限高),不再 \`max-height: calc(100% - 24px)\`
|
|
7146
|
+
* 从上往下盖住整个画布;
|
|
7147
|
+
* 2. 抽屉打开时小地图与图例**临时让位**,关闭后回归。
|
|
7148
|
+
*
|
|
7149
|
+
* 为什么两条都要:三者都是 \`position: absolute\` + \`z-index: var(--cg-z-panel)\` 的绝对定位
|
|
7150
|
+
* 元素,窄档下面板几乎占满高度,小地图(右下 200px)与图例(左下 180px)会被盖住一半,
|
|
7151
|
+
* 面板底部的按钮也会与图例重叠 —— 点击落在错误元素上,而所有 DOM 断言照旧通过。
|
|
7152
|
+
*
|
|
7153
|
+
* 两套机制**声明必须一致**:
|
|
7154
|
+
* - \`@container graphpanel (max-width: 480px)\`:主机制,按面板宽度响应(与视口无关);
|
|
7155
|
+
* - \`[data-cg-tier="narrow"]\`:宿主不支持容器查询、或容器查询尺寸不可读时的兜底,
|
|
7156
|
+
* 由 \`hooks/usePanelTier.ts\` 实测宽度后写在根元素上。
|
|
7157
|
+
* \`tests/unit/panelTierContract.test.ts\` 会逐条比对这两处的声明,任一侧改动都会变红。
|
|
7158
|
+
*/
|
|
7159
|
+
@container graphpanel (max-width: 480px) {
|
|
7160
|
+
.cg-node-detail-panel {
|
|
7161
|
+
top: auto;
|
|
7162
|
+
bottom: calc(var(--cg-statusbar-h) + 8px);
|
|
7163
|
+
left: 8px;
|
|
7164
|
+
right: 8px;
|
|
7165
|
+
width: auto;
|
|
7166
|
+
max-height: 52%;
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7169
|
+
/* 抽屉打开 ⇒ 两个角落的叠加层让位(关闭后自动回归,不需要 JS 复位) */
|
|
7170
|
+
.cg-graph-panel[data-cg-sheet="open"] .cg-mini-map,
|
|
7171
|
+
.cg-graph-panel[data-cg-sheet="open"] .cg-legend-panel {
|
|
7172
|
+
display: none;
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
|
|
7176
|
+
/* 兜底机制(JS 实测档位):与上面的容器查询分支**逐条等价** */
|
|
7177
|
+
.cg-graph-panel[data-cg-tier="narrow"] .cg-node-detail-panel {
|
|
7178
|
+
top: auto;
|
|
7179
|
+
bottom: calc(var(--cg-statusbar-h) + 8px);
|
|
7180
|
+
left: 8px;
|
|
7181
|
+
right: 8px;
|
|
7182
|
+
width: auto;
|
|
7183
|
+
max-height: 52%;
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
.cg-graph-panel[data-cg-tier="narrow"][data-cg-sheet="open"] .cg-mini-map,
|
|
7187
|
+
.cg-graph-panel[data-cg-tier="narrow"][data-cg-sheet="open"] .cg-legend-panel {
|
|
7188
|
+
display: none;
|
|
7189
|
+
}
|
|
7190
|
+
|
|
7191
|
+
/* ---- FBX-05: panel stack responsive tiers --------------------------------- */
|
|
7192
|
+
/*
|
|
7193
|
+
* 面板堆栈与栈内面板在窄容器下的收窄:只**新增**规则、不改任何现有行;面板根
|
|
7194
|
+
* 自身的固定 px 宽(160-300px)不在此重写,统一用 \`max-width: calc(100% - 16px)\`
|
|
7195
|
+
* 封顶(保留左右 8px 呼吸边)。两档阈值必须与 hooks/usePanelTier.ts 的
|
|
7196
|
+
* PANEL_TIER_* 常量同源——tests/unit/panelStackResponsiveContract.test.ts
|
|
7197
|
+
* 逐条比对,任一侧漂移即红。
|
|
7198
|
+
*/
|
|
7199
|
+
@container graphpanel (max-width: 768px) {
|
|
7200
|
+
.cg-panel-stack-left .cg-search-bar,
|
|
7201
|
+
.cg-panel-stack-left .cg-path-bar,
|
|
7202
|
+
.cg-panel-stack-left .cg-health-score-card,
|
|
7203
|
+
.cg-panel-stack-left .cg-rule-panel,
|
|
7204
|
+
.cg-panel-stack-left .cg-node-detail-panel,
|
|
7205
|
+
.cg-panel-stack-left .cg-legend-panel,
|
|
7206
|
+
.cg-panel-stack-left .cg-import-panel {
|
|
7207
|
+
max-width: calc(100% - 16px);
|
|
7208
|
+
}
|
|
7209
|
+
|
|
7210
|
+
.cg-panel-stack-left .cg-impact-panel,
|
|
7211
|
+
.cg-panel-stack-left .cg-cycle-panel,
|
|
7212
|
+
.cg-panel-stack-right .cg-analysis-panel,
|
|
7213
|
+
.cg-panel-stack-right .cg-cycle-panel,
|
|
7214
|
+
.cg-panel-stack-right .cg-filter-panel,
|
|
7215
|
+
.cg-panel-stack-right .cg-deadcode-panel {
|
|
7216
|
+
max-width: calc(100% - 16px);
|
|
7217
|
+
}
|
|
7218
|
+
}
|
|
7219
|
+
|
|
7220
|
+
@container graphpanel (max-width: 480px) {
|
|
7221
|
+
.cg-panel-stack-left,
|
|
7222
|
+
.cg-panel-stack-right {
|
|
7223
|
+
left: 8px;
|
|
7224
|
+
right: 8px;
|
|
7225
|
+
max-width: calc(100% - 16px);
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
.cg-panel-stack-left .cg-search-bar,
|
|
7229
|
+
.cg-panel-stack-left .cg-path-bar,
|
|
7230
|
+
.cg-panel-stack-left .cg-health-score-card,
|
|
7231
|
+
.cg-panel-stack-left .cg-rule-panel,
|
|
7232
|
+
.cg-panel-stack-left .cg-node-detail-panel,
|
|
7233
|
+
.cg-panel-stack-left .cg-legend-panel,
|
|
7234
|
+
.cg-panel-stack-left .cg-import-panel {
|
|
7235
|
+
max-width: calc(100% - 16px);
|
|
7236
|
+
}
|
|
7237
|
+
|
|
7238
|
+
.cg-panel-stack-left .cg-impact-panel,
|
|
7239
|
+
.cg-panel-stack-left .cg-cycle-panel,
|
|
7240
|
+
.cg-panel-stack-right .cg-analysis-panel,
|
|
7241
|
+
.cg-panel-stack-right .cg-cycle-panel,
|
|
7242
|
+
.cg-panel-stack-right .cg-filter-panel,
|
|
7243
|
+
.cg-panel-stack-right .cg-deadcode-panel {
|
|
7244
|
+
max-width: calc(100% - 16px);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7131
7247
|
|
|
7132
7248
|
/* Visually hidden — for screen reader only content */
|
|
7133
7249
|
.cg-visually-hidden {
|
|
@@ -11586,7 +11702,7 @@ function resetLoggerListeners() {
|
|
|
11586
11702
|
|
|
11587
11703
|
//#endregion
|
|
11588
11704
|
//#region src/client/cache/IndexedDBCache.ts
|
|
11589
|
-
const log$
|
|
11705
|
+
const log$46 = scoped("indexeddb-cache");
|
|
11590
11706
|
const DB_PREFIX = "codegraph-cache-";
|
|
11591
11707
|
const CLUSTER_STORE = "cluster-cache";
|
|
11592
11708
|
const DIAGRAM_STORE = "diagram-cache";
|
|
@@ -11637,7 +11753,7 @@ var IndexedDBCache = class {
|
|
|
11637
11753
|
}
|
|
11638
11754
|
getDB() {
|
|
11639
11755
|
if (!this.dbPromise) this.dbPromise = openDB(this.repoId).catch((err) => {
|
|
11640
|
-
log$
|
|
11756
|
+
log$46.warn("failed to open IndexedDB", {
|
|
11641
11757
|
repoId: this.repoId,
|
|
11642
11758
|
error: err
|
|
11643
11759
|
});
|
|
@@ -11668,7 +11784,7 @@ var IndexedDBCache = class {
|
|
|
11668
11784
|
tx.onerror = () => reject(tx.error);
|
|
11669
11785
|
});
|
|
11670
11786
|
} catch (e) {
|
|
11671
|
-
log$
|
|
11787
|
+
log$46.warn("IndexedDB invalidateCluster failed", { error: e });
|
|
11672
11788
|
}
|
|
11673
11789
|
}
|
|
11674
11790
|
async invalidateDiagram(viewMode) {
|
|
@@ -11695,7 +11811,7 @@ var IndexedDBCache = class {
|
|
|
11695
11811
|
tx.onerror = () => reject(tx.error);
|
|
11696
11812
|
});
|
|
11697
11813
|
} catch (e) {
|
|
11698
|
-
log$
|
|
11814
|
+
log$46.warn("IndexedDB invalidateDiagram failed", {
|
|
11699
11815
|
viewMode,
|
|
11700
11816
|
error: e
|
|
11701
11817
|
});
|
|
@@ -11722,7 +11838,7 @@ var IndexedDBCache = class {
|
|
|
11722
11838
|
async setMany(storeName, entries) {
|
|
11723
11839
|
if (entries.length === 0) return;
|
|
11724
11840
|
if (entries.length > INDEXED_DB.BATCH.MAX_ENTRIES) {
|
|
11725
|
-
log$
|
|
11841
|
+
log$46.warn("setMany batch exceeds limit, splitting", {
|
|
11726
11842
|
count: entries.length,
|
|
11727
11843
|
limit: INDEXED_DB.BATCH.MAX_ENTRIES
|
|
11728
11844
|
});
|
|
@@ -11748,7 +11864,7 @@ var IndexedDBCache = class {
|
|
|
11748
11864
|
tx.onerror = () => reject(tx.error);
|
|
11749
11865
|
});
|
|
11750
11866
|
} catch (e) {
|
|
11751
|
-
log$
|
|
11867
|
+
log$46.warn(`IndexedDB setMany(${storeName}) failed`, {
|
|
11752
11868
|
count: entries.length,
|
|
11753
11869
|
error: e
|
|
11754
11870
|
});
|
|
@@ -11764,7 +11880,7 @@ var IndexedDBCache = class {
|
|
|
11764
11880
|
usageRatio: quota > 0 ? usage / quota : 0
|
|
11765
11881
|
};
|
|
11766
11882
|
} catch (e) {
|
|
11767
|
-
log$
|
|
11883
|
+
log$46.warn("storage.estimate() failed", { error: e });
|
|
11768
11884
|
return null;
|
|
11769
11885
|
}
|
|
11770
11886
|
}
|
|
@@ -11776,7 +11892,7 @@ var IndexedDBCache = class {
|
|
|
11776
11892
|
async purgeExpired() {
|
|
11777
11893
|
let totalPurged = 0;
|
|
11778
11894
|
for (const storeName of ALL_STORES) totalPurged += await this.purgeExpiredStore(storeName);
|
|
11779
|
-
if (totalPurged > 0) log$
|
|
11895
|
+
if (totalPurged > 0) log$46.info("purged expired entries", { count: totalPurged });
|
|
11780
11896
|
return totalPurged;
|
|
11781
11897
|
}
|
|
11782
11898
|
async purgeExpiredStore(storeName) {
|
|
@@ -11808,7 +11924,7 @@ var IndexedDBCache = class {
|
|
|
11808
11924
|
});
|
|
11809
11925
|
return expiredKeys.length;
|
|
11810
11926
|
} catch (e) {
|
|
11811
|
-
log$
|
|
11927
|
+
log$46.warn(`purgeExpiredStore(${storeName}) failed`, { error: e });
|
|
11812
11928
|
return 0;
|
|
11813
11929
|
}
|
|
11814
11930
|
}
|
|
@@ -11831,7 +11947,7 @@ var IndexedDBCache = class {
|
|
|
11831
11947
|
}
|
|
11832
11948
|
return raw;
|
|
11833
11949
|
} catch (e) {
|
|
11834
|
-
log$
|
|
11950
|
+
log$46.warn(`IndexedDB getFromStore(${storeName}) failed`, {
|
|
11835
11951
|
key,
|
|
11836
11952
|
error: e
|
|
11837
11953
|
});
|
|
@@ -11854,7 +11970,7 @@ var IndexedDBCache = class {
|
|
|
11854
11970
|
tx.onerror = () => reject(tx.error);
|
|
11855
11971
|
});
|
|
11856
11972
|
} catch (e) {
|
|
11857
|
-
log$
|
|
11973
|
+
log$46.warn(`IndexedDB setToStore(${storeName}) failed`, {
|
|
11858
11974
|
key,
|
|
11859
11975
|
error: e
|
|
11860
11976
|
});
|
|
@@ -11870,7 +11986,7 @@ var IndexedDBCache = class {
|
|
|
11870
11986
|
tx.onerror = () => reject(tx.error);
|
|
11871
11987
|
});
|
|
11872
11988
|
} catch (e) {
|
|
11873
|
-
log$
|
|
11989
|
+
log$46.warn(`IndexedDB deleteFromStore(${storeName}) failed`, {
|
|
11874
11990
|
key,
|
|
11875
11991
|
error: e
|
|
11876
11992
|
});
|
|
@@ -11886,7 +12002,7 @@ var IndexedDBCache = class {
|
|
|
11886
12002
|
|
|
11887
12003
|
//#endregion
|
|
11888
12004
|
//#region src/client/cache/DiagramCache.ts
|
|
11889
|
-
const log$
|
|
12005
|
+
const log$45 = scoped("cache");
|
|
11890
12006
|
function computeDataHash(nodes, edges) {
|
|
11891
12007
|
let nodeHash = 2166136261;
|
|
11892
12008
|
for (const n of nodes) {
|
|
@@ -11978,7 +12094,7 @@ var DiagramCache = class {
|
|
|
11978
12094
|
return diagram;
|
|
11979
12095
|
}
|
|
11980
12096
|
} catch (e) {
|
|
11981
|
-
log$
|
|
12097
|
+
log$45.warn("IndexedDB diagram getAsync failed", e);
|
|
11982
12098
|
}
|
|
11983
12099
|
return null;
|
|
11984
12100
|
}
|
|
@@ -11990,13 +12106,13 @@ var DiagramCache = class {
|
|
|
11990
12106
|
if (firstKey) this.cache.delete(firstKey);
|
|
11991
12107
|
}
|
|
11992
12108
|
this.cache.set(key, diagram);
|
|
11993
|
-
if (this.idbCache) this.idbCache.setDiagram(key, diagram).catch((e) => log$
|
|
12109
|
+
if (this.idbCache) this.idbCache.setDiagram(key, diagram).catch((e) => log$45.warn("IndexedDB diagram set failed", e));
|
|
11994
12110
|
}
|
|
11995
12111
|
invalidate(viewMode) {
|
|
11996
12112
|
if (viewMode) {
|
|
11997
12113
|
for (const key of this.cache.keys()) if (key.startsWith(`${viewMode}:`)) this.cache.delete(key);
|
|
11998
12114
|
} else this.cache.clear();
|
|
11999
|
-
if (this.idbCache) this.idbCache.invalidateDiagram(viewMode).catch((e) => log$
|
|
12115
|
+
if (this.idbCache) this.idbCache.invalidateDiagram(viewMode).catch((e) => log$45.warn("IndexedDB diagram invalidate failed", e));
|
|
12000
12116
|
}
|
|
12001
12117
|
size() {
|
|
12002
12118
|
return this.cache.size;
|
|
@@ -12041,7 +12157,7 @@ var ClusterCache = class {
|
|
|
12041
12157
|
return result;
|
|
12042
12158
|
}
|
|
12043
12159
|
} catch (e) {
|
|
12044
|
-
log$
|
|
12160
|
+
log$45.warn("IndexedDB cluster getAsync failed", e);
|
|
12045
12161
|
}
|
|
12046
12162
|
return null;
|
|
12047
12163
|
}
|
|
@@ -12053,11 +12169,11 @@ var ClusterCache = class {
|
|
|
12053
12169
|
if (firstKey) this.cache.delete(firstKey);
|
|
12054
12170
|
}
|
|
12055
12171
|
this.cache.set(key, result);
|
|
12056
|
-
if (this.idbCache) this.idbCache.setCluster(key, result).catch((e) => log$
|
|
12172
|
+
if (this.idbCache) this.idbCache.setCluster(key, result).catch((e) => log$45.warn("IndexedDB cluster set failed", e));
|
|
12057
12173
|
}
|
|
12058
12174
|
invalidate() {
|
|
12059
12175
|
this.cache.clear();
|
|
12060
|
-
if (this.idbCache) this.idbCache.invalidateCluster().catch((e) => log$
|
|
12176
|
+
if (this.idbCache) this.idbCache.invalidateCluster().catch((e) => log$45.warn("IndexedDB cluster invalidate failed", e));
|
|
12061
12177
|
}
|
|
12062
12178
|
size() {
|
|
12063
12179
|
return this.cache.size;
|
|
@@ -13711,6 +13827,25 @@ function buildStylesheet(theme, nodeCount = 0) {
|
|
|
13711
13827
|
"border-color": successColor
|
|
13712
13828
|
}
|
|
13713
13829
|
},
|
|
13830
|
+
{
|
|
13831
|
+
selector: "node.multi-selected",
|
|
13832
|
+
style: {
|
|
13833
|
+
"border-width": 3,
|
|
13834
|
+
"border-color": accentColor,
|
|
13835
|
+
"background-color": accentColor,
|
|
13836
|
+
"background-opacity": .35,
|
|
13837
|
+
"z-index": 26
|
|
13838
|
+
}
|
|
13839
|
+
},
|
|
13840
|
+
{
|
|
13841
|
+
selector: "node.canvas-focused",
|
|
13842
|
+
style: {
|
|
13843
|
+
"border-width": 2,
|
|
13844
|
+
"border-color": accentColor,
|
|
13845
|
+
"border-style": "dashed",
|
|
13846
|
+
"z-index": 24
|
|
13847
|
+
}
|
|
13848
|
+
},
|
|
13714
13849
|
{
|
|
13715
13850
|
selector: ".call-chain",
|
|
13716
13851
|
style: {
|
|
@@ -15039,7 +15174,7 @@ var CallChainAggregator = class {
|
|
|
15039
15174
|
|
|
15040
15175
|
//#endregion
|
|
15041
15176
|
//#region src/shared/PerformanceMonitor.ts
|
|
15042
|
-
const log$
|
|
15177
|
+
const log$44 = scoped("perf");
|
|
15043
15178
|
const MAX_HISTORY = 100;
|
|
15044
15179
|
const measurements = [];
|
|
15045
15180
|
const thresholds = new Map();
|
|
@@ -15141,8 +15276,8 @@ function recordMeasurement(name$1, durationMs) {
|
|
|
15141
15276
|
if (measurements.length > MAX_HISTORY) measurements.shift();
|
|
15142
15277
|
const threshold = thresholds.get(name$1);
|
|
15143
15278
|
if (threshold) {
|
|
15144
|
-
if (threshold.degradeMs !== void 0 && durationMs >= threshold.degradeMs && threshold.degradationKey) log$
|
|
15145
|
-
else if (threshold.warnMs !== void 0 && durationMs >= threshold.warnMs) log$
|
|
15279
|
+
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`);
|
|
15280
|
+
else if (threshold.warnMs !== void 0 && durationMs >= threshold.warnMs) log$44.info(`${name$1} exceeded warn threshold: ${durationMs.toFixed(1)}ms >= ${threshold.warnMs}ms`);
|
|
15146
15281
|
}
|
|
15147
15282
|
}
|
|
15148
15283
|
function getMeasurements(name$1) {
|
|
@@ -15152,7 +15287,7 @@ function getMeasurements(name$1) {
|
|
|
15152
15287
|
|
|
15153
15288
|
//#endregion
|
|
15154
15289
|
//#region src/client/store/internals.ts
|
|
15155
|
-
const log$
|
|
15290
|
+
const log$43 = scoped("store");
|
|
15156
15291
|
const diagramCache = new DiagramCache(10);
|
|
15157
15292
|
const clusterCache = new ClusterCache(20);
|
|
15158
15293
|
const callChainAggregator = new CallChainAggregator();
|
|
@@ -15270,7 +15405,7 @@ function mergeAndRecluster(newNodes, newEdges, set$1, logLabel, logDetail) {
|
|
|
15270
15405
|
nodes: result.nodes,
|
|
15271
15406
|
edges: result.edges
|
|
15272
15407
|
});
|
|
15273
|
-
log$
|
|
15408
|
+
log$43.info(logLabel, {
|
|
15274
15409
|
...logDetail,
|
|
15275
15410
|
newNodes: filteredNodes.length,
|
|
15276
15411
|
newEdges: filteredEdges.length
|
|
@@ -15442,7 +15577,7 @@ function normalizeEdgeType(type) {
|
|
|
15442
15577
|
|
|
15443
15578
|
//#endregion
|
|
15444
15579
|
//#region src/shared/GraphValidator.ts
|
|
15445
|
-
const log$
|
|
15580
|
+
const log$42 = scoped("validator");
|
|
15446
15581
|
function coerceNode(raw, i$1) {
|
|
15447
15582
|
if (!raw || typeof raw !== "object") return null;
|
|
15448
15583
|
const o = raw;
|
|
@@ -16702,6 +16837,7 @@ const en = {
|
|
|
16702
16837
|
"canvasControls.shortcutZoomIn": "+ Zoom in",
|
|
16703
16838
|
"canvasControls.shortcutZoomOut": "- Zoom out",
|
|
16704
16839
|
"canvasControls.shortcutFit": "F Fit to view",
|
|
16840
|
+
"canvasControls.shortcutBoxSelect": "Shift+Drag Box-select nodes",
|
|
16705
16841
|
"canvasControls.multiSelect": "Multi-select mode",
|
|
16706
16842
|
"micro.closeView": "Close micro view",
|
|
16707
16843
|
"micro.functionBar": "Micro view toolbar",
|
|
@@ -16865,6 +17001,12 @@ const en = {
|
|
|
16865
17001
|
"contextMenu.findPath": "Find Path From Here",
|
|
16866
17002
|
"contextMenu.showAll": "Show All Hidden Nodes",
|
|
16867
17003
|
"contextMenu.openSource": "Open Source",
|
|
17004
|
+
"contextMenu.expandHere": "Expand From Here",
|
|
17005
|
+
"contextMenu.collapseHere": "Collapse To Here",
|
|
17006
|
+
"contextMenu.pinNode": "Pin Node",
|
|
17007
|
+
"contextMenu.unpinNode": "Unpin Node",
|
|
17008
|
+
"contextMenu.copyId": "Copy Symbol ID",
|
|
17009
|
+
"contextMenu.copied": "Copied",
|
|
16868
17010
|
"a11y.graphContainer": "Code graph interactive area",
|
|
16869
17011
|
"a11y.graphContainerStats": "Code graph interactive area: {viewMode}, {nodeCount} nodes, {edgeCount} edges",
|
|
16870
17012
|
"a11y.selectedNode": "Selected node",
|
|
@@ -17046,7 +17188,17 @@ const en = {
|
|
|
17046
17188
|
"views.srcAuto": "Collected by host",
|
|
17047
17189
|
"views.srcQuery": "Provided by caller",
|
|
17048
17190
|
"views.srcDuration": "{ms}ms",
|
|
17049
|
-
"views.srcTruncated": "truncated"
|
|
17191
|
+
"views.srcTruncated": "truncated",
|
|
17192
|
+
"batchbar.ariaLabel": "Batch actions",
|
|
17193
|
+
"batchbar.count": "{count} selected",
|
|
17194
|
+
"batchbar.expand": "Expand",
|
|
17195
|
+
"batchbar.expandAria": "Expand all selected nodes",
|
|
17196
|
+
"batchbar.collapse": "Collapse",
|
|
17197
|
+
"batchbar.collapseAria": "Collapse all selected nodes",
|
|
17198
|
+
"batchbar.export": "Export JSON",
|
|
17199
|
+
"batchbar.exportAria": "Export selected nodes as JSON",
|
|
17200
|
+
"batchbar.clear": "Clear",
|
|
17201
|
+
"batchbar.clearAria": "Clear multi-selection"
|
|
17050
17202
|
};
|
|
17051
17203
|
|
|
17052
17204
|
//#endregion
|
|
@@ -17534,6 +17686,7 @@ const zh = {
|
|
|
17534
17686
|
"canvasControls.shortcutZoomIn": "+ 放大",
|
|
17535
17687
|
"canvasControls.shortcutZoomOut": "- 缩小",
|
|
17536
17688
|
"canvasControls.shortcutFit": "F 适应窗口",
|
|
17689
|
+
"canvasControls.shortcutBoxSelect": "Shift+拖拽 框选节点",
|
|
17537
17690
|
"canvasControls.multiSelect": "多选模式",
|
|
17538
17691
|
"micro.closeView": "关闭微观视图",
|
|
17539
17692
|
"micro.functionBar": "微观视图工具栏",
|
|
@@ -17697,6 +17850,12 @@ const zh = {
|
|
|
17697
17850
|
"contextMenu.findPath": "从此节点查找路径",
|
|
17698
17851
|
"contextMenu.showAll": "显示所有隐藏节点",
|
|
17699
17852
|
"contextMenu.openSource": "打开源码",
|
|
17853
|
+
"contextMenu.expandHere": "从此展开",
|
|
17854
|
+
"contextMenu.collapseHere": "收起到这里",
|
|
17855
|
+
"contextMenu.pinNode": "固定节点",
|
|
17856
|
+
"contextMenu.unpinNode": "取消固定",
|
|
17857
|
+
"contextMenu.copyId": "复制符号 ID",
|
|
17858
|
+
"contextMenu.copied": "已复制",
|
|
17700
17859
|
"a11y.graphContainer": "代码图谱交互区域",
|
|
17701
17860
|
"a11y.graphContainerStats": "代码图谱交互区域: {viewMode}, {nodeCount} 节点, {edgeCount} 边",
|
|
17702
17861
|
"a11y.selectedNode": "已选中节点",
|
|
@@ -17878,7 +18037,17 @@ const zh = {
|
|
|
17878
18037
|
"views.srcAuto": "宿主自动采集",
|
|
17879
18038
|
"views.srcQuery": "调用方传入",
|
|
17880
18039
|
"views.srcDuration": "耗时 {ms}ms",
|
|
17881
|
-
"views.srcTruncated": "已截断"
|
|
18040
|
+
"views.srcTruncated": "已截断",
|
|
18041
|
+
"batchbar.ariaLabel": "批量操作条",
|
|
18042
|
+
"batchbar.count": "已选 {count} 个",
|
|
18043
|
+
"batchbar.expand": "展开",
|
|
18044
|
+
"batchbar.expandAria": "批量展开选中的节点",
|
|
18045
|
+
"batchbar.collapse": "收起",
|
|
18046
|
+
"batchbar.collapseAria": "批量收起选中的节点",
|
|
18047
|
+
"batchbar.export": "导出 JSON",
|
|
18048
|
+
"batchbar.exportAria": "导出选中节点为 JSON 文件",
|
|
18049
|
+
"batchbar.clear": "清除",
|
|
18050
|
+
"batchbar.clearAria": "清除多选"
|
|
17882
18051
|
};
|
|
17883
18052
|
|
|
17884
18053
|
//#endregion
|
|
@@ -18886,14 +19055,14 @@ function etagKeyForEdges(fromId, scope, cursor, limit$1, detail) {
|
|
|
18886
19055
|
|
|
18887
19056
|
//#endregion
|
|
18888
19057
|
//#region src/client/api/requestCore.ts
|
|
18889
|
-
const log$
|
|
19058
|
+
const log$41 = scoped("client");
|
|
18890
19059
|
const DEFAULT_TIMEOUT = API.DEFAULT_TIMEOUT_MS;
|
|
18891
19060
|
async function withAbortGuard(name$1, run$3, fallback, logContext) {
|
|
18892
19061
|
try {
|
|
18893
19062
|
return await run$3();
|
|
18894
19063
|
} catch (e) {
|
|
18895
19064
|
if (e instanceof DOMException && e.name === "AbortError") return fallback;
|
|
18896
|
-
log$
|
|
19065
|
+
log$41.warn(`${name$1} failed`, {
|
|
18897
19066
|
...logContext,
|
|
18898
19067
|
error: e instanceof Error ? e.message : String(e)
|
|
18899
19068
|
});
|
|
@@ -19226,7 +19395,7 @@ async function fetchPathEnumerate(source, target, options = {}) {
|
|
|
19226
19395
|
|
|
19227
19396
|
//#endregion
|
|
19228
19397
|
//#region src/client/api/index.ts
|
|
19229
|
-
const log$
|
|
19398
|
+
const log$40 = scoped("client");
|
|
19230
19399
|
/** 构造一个空 sink;调用方先建后传,取数函数负责填充。 */
|
|
19231
19400
|
function createEtagSink() {
|
|
19232
19401
|
return {
|
|
@@ -19427,7 +19596,7 @@ async function fetchGraphDataIncremental(clientEtag, scopeRepoId) {
|
|
|
19427
19596
|
const data$2 = coerceGraphData(await res.json());
|
|
19428
19597
|
if (!data$2) return null;
|
|
19429
19598
|
if (isForeignRepo(data$2.metadata.repoId, scopeRepoId)) {
|
|
19430
|
-
log$
|
|
19599
|
+
log$40.info("incremental graph fetch ignored for another workspace", {
|
|
19431
19600
|
repoId: data$2.metadata.repoId,
|
|
19432
19601
|
activeRepoId: scopeRepoId
|
|
19433
19602
|
});
|
|
@@ -19568,7 +19737,7 @@ async function fetchEdges(fromId, scope = "file", cursor, limit$1, signal, works
|
|
|
19568
19737
|
|
|
19569
19738
|
//#endregion
|
|
19570
19739
|
//#region src/client/diagram/index.ts
|
|
19571
|
-
const log$
|
|
19740
|
+
const log$39 = scoped("diagram");
|
|
19572
19741
|
const macroAssemblerRegistry = new Map([
|
|
19573
19742
|
["inheritance", new InheritanceAssembler()],
|
|
19574
19743
|
["callgraph", new CallGraphAssembler()],
|
|
@@ -19768,7 +19937,7 @@ async function assembleMicroDiagram(viewMode, filePath, functionName, workspace)
|
|
|
19768
19937
|
body: JSON.stringify({ filePath })
|
|
19769
19938
|
});
|
|
19770
19939
|
} catch (e) {
|
|
19771
|
-
log$
|
|
19940
|
+
log$39.warn("AST fetch failed", e);
|
|
19772
19941
|
throw new Error(`Network error: unable to reach AST endpoint`);
|
|
19773
19942
|
}
|
|
19774
19943
|
if (!response.ok) {
|
|
@@ -19783,7 +19952,7 @@ async function assembleMicroDiagram(viewMode, filePath, functionName, workspace)
|
|
|
19783
19952
|
const parsed = JSON.parse(errorBody);
|
|
19784
19953
|
serverMsg = parsed.error || "";
|
|
19785
19954
|
} catch (e) {
|
|
19786
|
-
log$
|
|
19955
|
+
log$39.warn("AST error body parse failed", e);
|
|
19787
19956
|
serverMsg = errorBody;
|
|
19788
19957
|
}
|
|
19789
19958
|
throw new Error(serverMsg || `AST parsing failed for "${filePath}"`);
|
|
@@ -19918,7 +20087,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
19918
20087
|
nodeEdgeTotals: totals$1,
|
|
19919
20088
|
nodeEdgeCursors: cursors$1
|
|
19920
20089
|
});
|
|
19921
|
-
log$
|
|
20090
|
+
log$43.info("node edges loaded (cache)", {
|
|
19922
20091
|
nodeId,
|
|
19923
20092
|
scope,
|
|
19924
20093
|
edges: data$3.edges.length
|
|
@@ -19931,7 +20100,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
19931
20100
|
const data$2 = await fetchEdges(nodeId, scope, cursor, EDGE_DETAIL.PAGE_SIZE, controller.signal, _get().currentWorkspace, detail, sink);
|
|
19932
20101
|
if (!isCurrent() || gen !== internals.expandGeneration) return;
|
|
19933
20102
|
if (sink.notModified) {
|
|
19934
|
-
log$
|
|
20103
|
+
log$43.info("node edges not modified (304)", {
|
|
19935
20104
|
nodeId,
|
|
19936
20105
|
scope,
|
|
19937
20106
|
cursor,
|
|
@@ -19940,7 +20109,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
19940
20109
|
return;
|
|
19941
20110
|
}
|
|
19942
20111
|
if (!data$2) {
|
|
19943
|
-
log$
|
|
20112
|
+
log$43.warn("loadNodeEdges: no response", {
|
|
19944
20113
|
nodeId,
|
|
19945
20114
|
scope,
|
|
19946
20115
|
cursor,
|
|
@@ -19967,7 +20136,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
19967
20136
|
edges: merged,
|
|
19968
20137
|
total: data$2.total
|
|
19969
20138
|
});
|
|
19970
|
-
log$
|
|
20139
|
+
log$43.info("node edges loaded", {
|
|
19971
20140
|
nodeId,
|
|
19972
20141
|
scope,
|
|
19973
20142
|
detail,
|
|
@@ -19978,7 +20147,7 @@ function createEdgeSlice(_set, _get) {
|
|
|
19978
20147
|
});
|
|
19979
20148
|
commitEtag(sink);
|
|
19980
20149
|
} catch (err) {
|
|
19981
|
-
if (!(err instanceof DOMException && err.name === "AbortError")) log$
|
|
20150
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) log$43.warn("loadNodeEdges failed", {
|
|
19982
20151
|
nodeId,
|
|
19983
20152
|
scope,
|
|
19984
20153
|
err
|
|
@@ -20101,7 +20270,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
20101
20270
|
...emptyDiffState()
|
|
20102
20271
|
});
|
|
20103
20272
|
} catch (err) {
|
|
20104
|
-
log$
|
|
20273
|
+
log$43.error("setGraphData failed", { err });
|
|
20105
20274
|
_set({
|
|
20106
20275
|
error: err instanceof Error ? err.message : String(err),
|
|
20107
20276
|
isLoading: false
|
|
@@ -20170,12 +20339,12 @@ function createGraphDataSlice(_set, _get) {
|
|
|
20170
20339
|
...emptyEdgeState(),
|
|
20171
20340
|
...emptyDiffState()
|
|
20172
20341
|
});
|
|
20173
|
-
log$
|
|
20342
|
+
log$43.info("bootstrap data set", {
|
|
20174
20343
|
rootNodes: sanitized.nodes.length,
|
|
20175
20344
|
totalNodeCount: metadata.totalNodeCount
|
|
20176
20345
|
});
|
|
20177
20346
|
} catch (err) {
|
|
20178
|
-
log$
|
|
20347
|
+
log$43.error("setBootstrapData failed", { err });
|
|
20179
20348
|
_set({
|
|
20180
20349
|
error: err instanceof Error ? err.message : String(err),
|
|
20181
20350
|
isLoading: false
|
|
@@ -20218,7 +20387,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
20218
20387
|
nodes: rawNodes,
|
|
20219
20388
|
edges: rawEdges
|
|
20220
20389
|
}, delta, { minRetainRatio: GRAPH_DELTA_SHRINK.MIN_RETAIN_RATIO })) {
|
|
20221
|
-
log$
|
|
20390
|
+
log$43.info("applyGraphDelta refused: delta does not match the current revision or would shrink the graph", {
|
|
20222
20391
|
storedNodes: rawNodes.length,
|
|
20223
20392
|
storedEdges: rawEdges.length,
|
|
20224
20393
|
...graphDeltaSize(delta)
|
|
@@ -20280,7 +20449,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
20280
20449
|
...emptyEdgeState(),
|
|
20281
20450
|
...emptyDiffState()
|
|
20282
20451
|
});
|
|
20283
|
-
log$
|
|
20452
|
+
log$43.info("applied graph delta", {
|
|
20284
20453
|
addedNodes: delta.addedNodes.length,
|
|
20285
20454
|
removedNodes: delta.removedNodes.length,
|
|
20286
20455
|
modifiedNodes: delta.modifiedNodes.length,
|
|
@@ -20290,7 +20459,7 @@ function createGraphDataSlice(_set, _get) {
|
|
|
20290
20459
|
});
|
|
20291
20460
|
return true;
|
|
20292
20461
|
} catch (err) {
|
|
20293
|
-
log$
|
|
20462
|
+
log$43.error("applyGraphDelta failed", { err });
|
|
20294
20463
|
_set({
|
|
20295
20464
|
error: err instanceof Error ? err.message : String(err),
|
|
20296
20465
|
isLoading: false
|
|
@@ -20636,7 +20805,7 @@ function createSessionSlice(_set, _get) {
|
|
|
20636
20805
|
|
|
20637
20806
|
//#endregion
|
|
20638
20807
|
//#region src/client/utils/urlState.ts
|
|
20639
|
-
const log$
|
|
20808
|
+
const log$38 = scoped("url-state");
|
|
20640
20809
|
const PREFIX_DIR = "d:";
|
|
20641
20810
|
const PREFIX_FILE = "f:";
|
|
20642
20811
|
const PREFIX_SYMBOL = "s:";
|
|
@@ -20693,7 +20862,7 @@ function updateUrlExpand(expandedNodeIds) {
|
|
|
20693
20862
|
if (urlString.length > URL_STATE.MAX_LENGTH) {
|
|
20694
20863
|
const trimmed = expandParam.split(",").filter((p$1) => !p$1.startsWith(PREFIX_SYMBOL));
|
|
20695
20864
|
url.searchParams.set("expand", trimmed.join(","));
|
|
20696
|
-
log$
|
|
20865
|
+
log$38.warn("URL too long, trimmed symbol-level expansions");
|
|
20697
20866
|
}
|
|
20698
20867
|
window.history.replaceState(null, "", url.toString());
|
|
20699
20868
|
}
|
|
@@ -20788,7 +20957,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
20788
20957
|
const data$2 = await measureAsync("ast-fetch", () => fetchSymbolAST(nodeId, signal, _get().currentWorkspace, cursor, sink));
|
|
20789
20958
|
if (gen !== internals.expandGeneration || !isCurrent()) return;
|
|
20790
20959
|
if (sink.notModified) {
|
|
20791
|
-
log$
|
|
20960
|
+
log$43.info("symbol AST not modified (304)", {
|
|
20792
20961
|
nodeId,
|
|
20793
20962
|
cursor
|
|
20794
20963
|
});
|
|
@@ -20796,7 +20965,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
20796
20965
|
}
|
|
20797
20966
|
if (!data$2) return;
|
|
20798
20967
|
const { astNodes: truncatedAstNodes, astEdges: truncatedAstEdges, sourceLines: truncatedSourceLines } = truncateAstData(data$2);
|
|
20799
|
-
if (truncatedAstNodes.length < data$2.astNodes.length) log$
|
|
20968
|
+
if (truncatedAstNodes.length < data$2.astNodes.length) log$43.warn("symbol AST truncated", {
|
|
20800
20969
|
nodeId,
|
|
20801
20970
|
original: data$2.astNodes.length,
|
|
20802
20971
|
truncated: truncatedAstNodes.length
|
|
@@ -20816,7 +20985,7 @@ async function loadFunctionBodyAst(nodeId, signal, gen, _set, _get, cursor = 0)
|
|
|
20816
20985
|
if (internals.persistentCache && !data$2.hasMore && cursor === 0) await internals.persistentCache.setAST(nodeId, data$2);
|
|
20817
20986
|
commitEtag(sink);
|
|
20818
20987
|
} catch (err) {
|
|
20819
|
-
log$
|
|
20988
|
+
log$43.warn("expandNode: fetchSymbolAST failed", {
|
|
20820
20989
|
nodeId,
|
|
20821
20990
|
err
|
|
20822
20991
|
});
|
|
@@ -20843,7 +21012,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20843
21012
|
if (expandingFunctionBody) enforceFunctionBodyBudget(effectiveExpanded, rawNodes, FUNCTION_BODY.MAX_EXPANDED - 1, pinnedIds);
|
|
20844
21013
|
effectiveExpanded.add(nodeId);
|
|
20845
21014
|
collapseExcessClusters(nodes, effectiveExpanded, maxVisible, cost, pinnedIds);
|
|
20846
|
-
if (cost > CLUSTER.EXPAND_BUDGET_SINGLE) log$
|
|
21015
|
+
if (cost > CLUSTER.EXPAND_BUDGET_SINGLE) log$43.warn("expand budget exceeded", {
|
|
20847
21016
|
nodeId,
|
|
20848
21017
|
cost,
|
|
20849
21018
|
budget: CLUSTER.EXPAND_BUDGET_SINGLE
|
|
@@ -20858,7 +21027,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20858
21027
|
const expandController = replaceController(internals.expandAbortControllers, nodeId);
|
|
20859
21028
|
_get().loadNodeChildren(nodeId, { signal: expandController.signal }).catch((err) => {
|
|
20860
21029
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
20861
|
-
log$
|
|
21030
|
+
log$43.warn("expandNode: loadNodeChildren failed", {
|
|
20862
21031
|
nodeId,
|
|
20863
21032
|
err
|
|
20864
21033
|
});
|
|
@@ -20880,7 +21049,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20880
21049
|
fetchFileOutline(nodeId, expandController.signal, _get().currentWorkspace, sink).then(async (data$2) => {
|
|
20881
21050
|
if (gen !== internals.expandGeneration) return;
|
|
20882
21051
|
if (sink.notModified) {
|
|
20883
|
-
log$
|
|
21052
|
+
log$43.info("file outline not modified (304)", { nodeId });
|
|
20884
21053
|
return;
|
|
20885
21054
|
}
|
|
20886
21055
|
if (data$2 && data$2.symbols.length > 0) {
|
|
@@ -20889,7 +21058,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20889
21058
|
commitEtag(sink);
|
|
20890
21059
|
}
|
|
20891
21060
|
}).catch((err) => {
|
|
20892
|
-
log$
|
|
21061
|
+
log$43.warn("expandNode: fetchFileOutline failed", {
|
|
20893
21062
|
nodeId,
|
|
20894
21063
|
err
|
|
20895
21064
|
});
|
|
@@ -20927,7 +21096,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20927
21096
|
loadedChildrenByNodeId: newLoaded$1,
|
|
20928
21097
|
pendingRequests: clearedPending$1
|
|
20929
21098
|
});
|
|
20930
|
-
log$
|
|
21099
|
+
log$43.info("node children not modified (304)", { nodeId });
|
|
20931
21100
|
return;
|
|
20932
21101
|
}
|
|
20933
21102
|
if (!data$2 || data$2.nodes.length === 0) {
|
|
@@ -20976,7 +21145,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20976
21145
|
loadedChildrenByNodeId: newLoaded,
|
|
20977
21146
|
pendingRequests: clearedPending
|
|
20978
21147
|
});
|
|
20979
|
-
log$
|
|
21148
|
+
log$43.info("node children loaded", {
|
|
20980
21149
|
nodeId,
|
|
20981
21150
|
newNodes: newNodes.length,
|
|
20982
21151
|
newEdges: newEdges.length
|
|
@@ -20995,7 +21164,7 @@ function createExpandSlice(_set, _get) {
|
|
|
20995
21164
|
pendingRequests: clearedPending,
|
|
20996
21165
|
loadErrorsByNodeId: newErrors
|
|
20997
21166
|
});
|
|
20998
|
-
log$
|
|
21167
|
+
log$43.warn("loadNodeChildren failed", {
|
|
20999
21168
|
nodeId,
|
|
21000
21169
|
err
|
|
21001
21170
|
});
|
|
@@ -21006,7 +21175,7 @@ function createExpandSlice(_set, _get) {
|
|
|
21006
21175
|
const ws = _get().currentWorkspace;
|
|
21007
21176
|
const data$2 = await fetchBootstrap(void 0, ws);
|
|
21008
21177
|
if (!data$2) {
|
|
21009
|
-
log$
|
|
21178
|
+
log$43.info("bootstrap returned no payload, falling back to full load");
|
|
21010
21179
|
return false;
|
|
21011
21180
|
}
|
|
21012
21181
|
_get().setIndexStatus(data$2.indexStatus);
|
|
@@ -21017,11 +21186,11 @@ function createExpandSlice(_set, _get) {
|
|
|
21017
21186
|
timestamp: data$2.metadata.timestamp,
|
|
21018
21187
|
graphLayer: data$2.metadata.graphLayer
|
|
21019
21188
|
});
|
|
21020
|
-
log$
|
|
21189
|
+
log$43.info("bootstrap: index building, empty skeleton installed", { repoId: data$2.metadata.repoId });
|
|
21021
21190
|
return true;
|
|
21022
21191
|
}
|
|
21023
21192
|
if (data$2.topLevelNodes.length === 0) {
|
|
21024
|
-
log$
|
|
21193
|
+
log$43.info("bootstrap returned empty, falling back to full load");
|
|
21025
21194
|
return false;
|
|
21026
21195
|
}
|
|
21027
21196
|
const state = _get();
|
|
@@ -21031,14 +21200,14 @@ function createExpandSlice(_set, _get) {
|
|
|
21031
21200
|
timestamp: data$2.metadata.timestamp,
|
|
21032
21201
|
graphLayer: data$2.metadata.graphLayer
|
|
21033
21202
|
});
|
|
21034
|
-
log$
|
|
21203
|
+
log$43.info("bootstrap loaded", {
|
|
21035
21204
|
rootNodes: data$2.topLevelNodes.length,
|
|
21036
21205
|
totalNodeCount: data$2.metadata.totalNodeCount,
|
|
21037
21206
|
indexStatus: data$2.indexStatus
|
|
21038
21207
|
});
|
|
21039
21208
|
return true;
|
|
21040
21209
|
} catch (err) {
|
|
21041
|
-
log$
|
|
21210
|
+
log$43.warn("loadBootstrap failed, falling back to full load", { err });
|
|
21042
21211
|
return false;
|
|
21043
21212
|
}
|
|
21044
21213
|
},
|
|
@@ -21198,7 +21367,7 @@ function createExpandSlice(_set, _get) {
|
|
|
21198
21367
|
if (!exists) {
|
|
21199
21368
|
const parentId = nodeId.includes("/") ? nodeId.substring(0, nodeId.lastIndexOf("/")) : null;
|
|
21200
21369
|
if (parentId && !state.loadedChildrenByNodeId.has(parentId)) await state.loadNodeChildren(parentId, { signal: restoreController.signal }).catch((err) => {
|
|
21201
|
-
if (!(err instanceof DOMException && err.name === "AbortError")) log$
|
|
21370
|
+
if (!(err instanceof DOMException && err.name === "AbortError")) log$43.warn("restoreFromUrl: loadNodeChildren failed", {
|
|
21202
21371
|
parentId,
|
|
21203
21372
|
err
|
|
21204
21373
|
});
|
|
@@ -21223,7 +21392,7 @@ function createExpandSlice(_set, _get) {
|
|
|
21223
21392
|
}
|
|
21224
21393
|
internals.restoreTimeoutHandle = null;
|
|
21225
21394
|
_set({ urlRestored: true });
|
|
21226
|
-
log$
|
|
21395
|
+
log$43.info("URL restore completed", { restoredCount: sortedIds.length });
|
|
21227
21396
|
}
|
|
21228
21397
|
};
|
|
21229
21398
|
}
|
|
@@ -21405,7 +21574,7 @@ function createAnalysisSlice(_set, _get) {
|
|
|
21405
21574
|
isLoading: false
|
|
21406
21575
|
});
|
|
21407
21576
|
} catch (e) {
|
|
21408
|
-
log$
|
|
21577
|
+
log$43.warn("assembleMultiFunction failed", e);
|
|
21409
21578
|
_set({
|
|
21410
21579
|
error: t("error.callChainFailed"),
|
|
21411
21580
|
isLoading: false
|
|
@@ -21491,7 +21660,7 @@ function createPanelLodSlice(_set, _get) {
|
|
|
21491
21660
|
|
|
21492
21661
|
//#endregion
|
|
21493
21662
|
//#region src/client/api/keywordQuery.ts
|
|
21494
|
-
const log$
|
|
21663
|
+
const log$37 = scoped("client");
|
|
21495
21664
|
/**
|
|
21496
21665
|
|
|
21497
21666
|
* 条件请求的请求键(与宿主 `computeQueryEtag` 的**参数域**对齐)。
|
|
@@ -21550,7 +21719,7 @@ async function fetchKeywordQuery(params, signal) {
|
|
|
21550
21719
|
res = await fetchWithTimeout(`/api/v1/codegraph/query?${search.toString()}`, options);
|
|
21551
21720
|
} catch (e) {
|
|
21552
21721
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
21553
|
-
log$
|
|
21722
|
+
log$37.warn("fetchKeywordQuery failed", {
|
|
21554
21723
|
aborted,
|
|
21555
21724
|
externalAborted: signal?.aborted === true,
|
|
21556
21725
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -21567,7 +21736,7 @@ async function fetchKeywordQuery(params, signal) {
|
|
|
21567
21736
|
try {
|
|
21568
21737
|
payload = await res.json();
|
|
21569
21738
|
} catch (e) {
|
|
21570
|
-
log$
|
|
21739
|
+
log$37.warn("fetchKeywordQuery: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
21571
21740
|
return {
|
|
21572
21741
|
kind: "failed",
|
|
21573
21742
|
status: res.status
|
|
@@ -21705,7 +21874,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
21705
21874
|
});
|
|
21706
21875
|
if (seq !== _get().keywordQuerySeq) return false;
|
|
21707
21876
|
if (outcome.kind === "not-modified") {
|
|
21708
|
-
log$
|
|
21877
|
+
log$43.info("keyword query not modified (304)", { q: trimmed });
|
|
21709
21878
|
const previous = _get().keywordQueryResult;
|
|
21710
21879
|
_set({
|
|
21711
21880
|
keywordQueryStatus: previous ? "ready" : "idle",
|
|
@@ -21754,7 +21923,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
21754
21923
|
const state = _get();
|
|
21755
21924
|
const exists = state.rawNodes.some((node) => node.id === nodeId);
|
|
21756
21925
|
if (!exists) {
|
|
21757
|
-
log$
|
|
21926
|
+
log$43.warn("selectKeywordSeed: node not in current graph", { nodeId });
|
|
21758
21927
|
return;
|
|
21759
21928
|
}
|
|
21760
21929
|
_set({
|
|
@@ -21776,7 +21945,7 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
21776
21945
|
...workspace && workspace !== "." ? { workspace } : {}
|
|
21777
21946
|
});
|
|
21778
21947
|
if (outcome.kind !== "data") {
|
|
21779
|
-
log$
|
|
21948
|
+
log$43.warn("revealRemoteNode: query failed", {
|
|
21780
21949
|
nodeId,
|
|
21781
21950
|
kind: outcome.kind
|
|
21782
21951
|
});
|
|
@@ -21784,12 +21953,12 @@ function createKeywordQuerySlice(_set, _get) {
|
|
|
21784
21953
|
}
|
|
21785
21954
|
const target = outcome.data.nodes.find((n) => n.id === nodeId);
|
|
21786
21955
|
if (!target) {
|
|
21787
|
-
log$
|
|
21956
|
+
log$43.warn("revealRemoteNode: payload does not contain the requested node", { nodeId });
|
|
21788
21957
|
return false;
|
|
21789
21958
|
}
|
|
21790
21959
|
mergeAndRecluster(outcome.data.nodes, outcome.data.edges, _set, "remote search hit merged", { nodeId });
|
|
21791
21960
|
if (!_get().rawNodes.some((node) => node.id === nodeId)) {
|
|
21792
|
-
log$
|
|
21961
|
+
log$43.warn("revealRemoteNode: node still absent after merge", { nodeId });
|
|
21793
21962
|
return false;
|
|
21794
21963
|
}
|
|
21795
21964
|
_set({
|
|
@@ -21839,7 +22008,7 @@ function truncationReasonKey(reason) {
|
|
|
21839
22008
|
|
|
21840
22009
|
//#endregion
|
|
21841
22010
|
//#region src/client/api/pathPattern.ts
|
|
21842
|
-
const log$
|
|
22011
|
+
const log$36 = scoped("client");
|
|
21843
22012
|
/**
|
|
21844
22013
|
* 把宿主响应归一成 `PathPatternData`。
|
|
21845
22014
|
*
|
|
@@ -21899,7 +22068,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
21899
22068
|
res = await fetchWithTimeout(`/api/v1/codegraph/paths?${search.toString()}`, options);
|
|
21900
22069
|
} catch (e) {
|
|
21901
22070
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
21902
|
-
log$
|
|
22071
|
+
log$36.warn("fetchPathsByPattern failed", {
|
|
21903
22072
|
aborted,
|
|
21904
22073
|
externalAborted: signal?.aborted === true,
|
|
21905
22074
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -21920,7 +22089,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
21920
22089
|
const record$1 = body && typeof body === "object" ? body : {};
|
|
21921
22090
|
const errorCode = typeof record$1.error === "string" ? record$1.error : "invalid-pattern";
|
|
21922
22091
|
const diagnostic = typeof record$1.diagnostic === "string" ? record$1.diagnostic : typeof record$1.message === "string" ? record$1.message : "";
|
|
21923
|
-
log$
|
|
22092
|
+
log$36.warn("fetchPathsByPattern: invalid pattern", {
|
|
21924
22093
|
errorCode,
|
|
21925
22094
|
diagnostic
|
|
21926
22095
|
});
|
|
@@ -21938,7 +22107,7 @@ async function fetchPathsByPattern(params, signal) {
|
|
|
21938
22107
|
try {
|
|
21939
22108
|
payload = await res.json();
|
|
21940
22109
|
} catch (e) {
|
|
21941
|
-
log$
|
|
22110
|
+
log$36.warn("fetchPathsByPattern: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
21942
22111
|
return {
|
|
21943
22112
|
kind: "failed",
|
|
21944
22113
|
status: res.status
|
|
@@ -21967,7 +22136,7 @@ async function fetchPathPatternSyntax(pattern, signal) {
|
|
|
21967
22136
|
res = await fetchWithTimeout(`/api/v1/codegraph/path-pattern/syntax?${search.toString()}`, options);
|
|
21968
22137
|
} catch (e) {
|
|
21969
22138
|
const aborted = e instanceof DOMException && e.name === "AbortError";
|
|
21970
|
-
log$
|
|
22139
|
+
log$36.warn("fetchPathPatternSyntax failed", {
|
|
21971
22140
|
aborted,
|
|
21972
22141
|
externalAborted: signal?.aborted === true,
|
|
21973
22142
|
error: e instanceof Error ? e.message : String(e)
|
|
@@ -21990,7 +22159,7 @@ async function fetchPathPatternSyntax(pattern, signal) {
|
|
|
21990
22159
|
try {
|
|
21991
22160
|
payload = await res.json();
|
|
21992
22161
|
} catch (e) {
|
|
21993
|
-
log$
|
|
22162
|
+
log$36.warn("fetchPathPatternSyntax: non-JSON body", { error: e instanceof Error ? e.message : String(e) });
|
|
21994
22163
|
return {
|
|
21995
22164
|
ok: false,
|
|
21996
22165
|
kind: "failed",
|
|
@@ -22076,7 +22245,7 @@ function applyOutcome(outcome, pattern, workspace, basePaths, set$1) {
|
|
|
22076
22245
|
return false;
|
|
22077
22246
|
}
|
|
22078
22247
|
if (outcome.kind === "invalid") {
|
|
22079
|
-
log$
|
|
22248
|
+
log$43.warn("ρ pattern rejected (invalid syntax)", {
|
|
22080
22249
|
pattern,
|
|
22081
22250
|
diagnostic: outcome.diagnostic
|
|
22082
22251
|
});
|
|
@@ -22213,9 +22382,76 @@ function summarizePathPatternResult(result) {
|
|
|
22213
22382
|
};
|
|
22214
22383
|
}
|
|
22215
22384
|
|
|
22385
|
+
//#endregion
|
|
22386
|
+
//#region src/client/store/slices/multiSelectSlice.ts
|
|
22387
|
+
/**
|
|
22388
|
+
* 多选链路的**消费面**(cycle-149)。
|
|
22389
|
+
*
|
|
22390
|
+
* cycle-141 D 在渲染器侧交付了 Shift+Click 多选(`InteractionController.multiSelectedIds`
|
|
22391
|
+
* + `multi-selected` class + `onSelectionToggle` 回调),但该回调在全仓**零消费方**——
|
|
22392
|
+
* 多选集合只活在渲染器内部,store/UI 全都看不到。本切片把这条链路接完:
|
|
22393
|
+
*
|
|
22394
|
+
* - `multiSelectedIds` 是渲染器多选集合的**镜像**(单一事实源仍是渲染器,
|
|
22395
|
+
* store 只做镜像 + 批量动作的消费面;两边经 `syncMultiSelection`/`clearMultiSelection`
|
|
22396
|
+
* 同步,避免双集合各自演化后漂移)。
|
|
22397
|
+
* - `batchExpandSelected` 复用 expandSlice 的 `batchExpandNodes`(函数体预算 / pinned
|
|
22398
|
+
* 语义不重写——两处实现必然漂移,CLAUDE.md「Two data paths for the same semantics
|
|
22399
|
+
* must share ONE implementation」)。
|
|
22400
|
+
* - `batchCollapseSelected` 与 expandSlice `collapseAll` 同形:**单次** recluster
|
|
22401
|
+
* (逐个 collapseNode 会 N 次重聚类),pinned 节点不收(与 `collapseNode` 语义一致)。
|
|
22402
|
+
* - `exportSelectedNodes` 从 `rawNodes` 过滤导出;**聚合节点(cluster)不在 rawNodes 里**,
|
|
22403
|
+
* 选中聚合节点时该 id 被静默跳过——这是有意的诚实缺口(渲染器合并态 store 不可见),
|
|
22404
|
+
* UI 侧以「导出 N 个」计数如实呈现,不伪造。
|
|
22405
|
+
*
|
|
22406
|
+
* `multiSelectedIds` **不持久化**:画布选区是会话瞬时态,reload 后节点可能已变
|
|
22407
|
+
* (与 `xuanjiSignal` 同判据);`partialize` 白名单不收录即天然排除。
|
|
22408
|
+
*/
|
|
22409
|
+
function createMultiSelectSlice(_set, _get) {
|
|
22410
|
+
return {
|
|
22411
|
+
multiSelectedIds: new Set(),
|
|
22412
|
+
syncMultiSelection: (ids) => {
|
|
22413
|
+
const current$2 = _get().multiSelectedIds;
|
|
22414
|
+
if (current$2.size === ids.length && [...ids].every((id) => current$2.has(id))) return;
|
|
22415
|
+
_set({ multiSelectedIds: new Set(ids) });
|
|
22416
|
+
},
|
|
22417
|
+
clearMultiSelection: () => {
|
|
22418
|
+
if (_get().multiSelectedIds.size === 0) return;
|
|
22419
|
+
_set({ multiSelectedIds: new Set() });
|
|
22420
|
+
},
|
|
22421
|
+
batchExpandSelected: () => {
|
|
22422
|
+
const ids = [..._get().multiSelectedIds];
|
|
22423
|
+
if (ids.length === 0) return 0;
|
|
22424
|
+
_get().batchExpandNodes(ids);
|
|
22425
|
+
return ids.length;
|
|
22426
|
+
},
|
|
22427
|
+
batchCollapseSelected: () => {
|
|
22428
|
+
const { multiSelectedIds, expandedNodeIds, expandedNodeIdsByViewMode, viewMode, pinnedIds } = _get();
|
|
22429
|
+
if (multiSelectedIds.size === 0) return 0;
|
|
22430
|
+
const newExpanded = new Set(expandedNodeIds);
|
|
22431
|
+
let collapsed = 0;
|
|
22432
|
+
for (const id of multiSelectedIds) {
|
|
22433
|
+
if (pinnedIds.has(id)) continue;
|
|
22434
|
+
if (newExpanded.delete(id)) collapsed++;
|
|
22435
|
+
}
|
|
22436
|
+
if (collapsed === 0) return 0;
|
|
22437
|
+
const newExpandedByViewMode = {
|
|
22438
|
+
...expandedNodeIdsByViewMode,
|
|
22439
|
+
[viewMode]: newExpanded
|
|
22440
|
+
};
|
|
22441
|
+
reclusterWith(_get, _set, newExpanded, newExpandedByViewMode);
|
|
22442
|
+
return collapsed;
|
|
22443
|
+
},
|
|
22444
|
+
exportSelectedNodes: () => {
|
|
22445
|
+
const { multiSelectedIds, rawNodes } = _get();
|
|
22446
|
+
if (multiSelectedIds.size === 0) return [];
|
|
22447
|
+
return rawNodes.filter((n) => multiSelectedIds.has(n.id));
|
|
22448
|
+
}
|
|
22449
|
+
};
|
|
22450
|
+
}
|
|
22451
|
+
|
|
22216
22452
|
//#endregion
|
|
22217
22453
|
//#region src/client/utils/safeStorage.ts
|
|
22218
|
-
const log$
|
|
22454
|
+
const log$35 = scoped("client");
|
|
22219
22455
|
/** 纯内存实现:同一页面的多次读写一致,刷新即丢。 */
|
|
22220
22456
|
function createMemoryStorage() {
|
|
22221
22457
|
const map$1 = new Map();
|
|
@@ -22273,7 +22509,7 @@ function getSafeStorage(label = "generic") {
|
|
|
22273
22509
|
try {
|
|
22274
22510
|
return local.getItem(name$1);
|
|
22275
22511
|
} catch (e) {
|
|
22276
|
-
log$
|
|
22512
|
+
log$35.debug("safeStorage.getItem failed", {
|
|
22277
22513
|
label,
|
|
22278
22514
|
error: e
|
|
22279
22515
|
});
|
|
@@ -22284,7 +22520,7 @@ function getSafeStorage(label = "generic") {
|
|
|
22284
22520
|
try {
|
|
22285
22521
|
local.setItem(name$1, value);
|
|
22286
22522
|
} catch (e) {
|
|
22287
|
-
log$
|
|
22523
|
+
log$35.debug("safeStorage.setItem failed", {
|
|
22288
22524
|
label,
|
|
22289
22525
|
error: e
|
|
22290
22526
|
});
|
|
@@ -22294,7 +22530,7 @@ function getSafeStorage(label = "generic") {
|
|
|
22294
22530
|
try {
|
|
22295
22531
|
local.removeItem(name$1);
|
|
22296
22532
|
} catch (e) {
|
|
22297
|
-
log$
|
|
22533
|
+
log$35.debug("safeStorage.removeItem failed", {
|
|
22298
22534
|
label,
|
|
22299
22535
|
error: e
|
|
22300
22536
|
});
|
|
@@ -22303,7 +22539,7 @@ function getSafeStorage(label = "generic") {
|
|
|
22303
22539
|
};
|
|
22304
22540
|
const session = isStorageUsable(() => window.sessionStorage);
|
|
22305
22541
|
if (session) {
|
|
22306
|
-
log$
|
|
22542
|
+
log$35.info("localStorage unavailable, falling back to sessionStorage", { label });
|
|
22307
22543
|
return {
|
|
22308
22544
|
getItem: (name$1) => {
|
|
22309
22545
|
try {
|
|
@@ -22324,7 +22560,7 @@ function getSafeStorage(label = "generic") {
|
|
|
22324
22560
|
}
|
|
22325
22561
|
};
|
|
22326
22562
|
}
|
|
22327
|
-
log$
|
|
22563
|
+
log$35.info("no Web Storage available, falling back to in-memory storage", { label });
|
|
22328
22564
|
return createMemoryStorage();
|
|
22329
22565
|
}
|
|
22330
22566
|
/**
|
|
@@ -22385,7 +22621,8 @@ function disposeStore() {
|
|
|
22385
22621
|
nodeEdgeTotals: new Map(),
|
|
22386
22622
|
nodeEdgeCursors: new Map(),
|
|
22387
22623
|
loadingEdgeNodeIds: new Set(),
|
|
22388
|
-
detailEdgeNodeIds: new Set()
|
|
22624
|
+
detailEdgeNodeIds: new Set(),
|
|
22625
|
+
multiSelectedIds: new Set()
|
|
22389
22626
|
});
|
|
22390
22627
|
if (internals.restoreTimeoutHandle !== null) {
|
|
22391
22628
|
clearTimeout(internals.restoreTimeoutHandle);
|
|
@@ -22424,7 +22661,8 @@ const useGraphStore = create$1()(persist((set$1, get$3) => {
|
|
|
22424
22661
|
...createAnalysisSlice(set$1, get$3),
|
|
22425
22662
|
...createPanelLodSlice(set$1, get$3),
|
|
22426
22663
|
...createKeywordQuerySlice(set$1, get$3),
|
|
22427
|
-
...createPathPatternSlice(set$1, get$3)
|
|
22664
|
+
...createPathPatternSlice(set$1, get$3),
|
|
22665
|
+
...createMultiSelectSlice(set$1, get$3)
|
|
22428
22666
|
};
|
|
22429
22667
|
registerStoreGetter(get$3);
|
|
22430
22668
|
return store;
|
|
@@ -67884,7 +68122,7 @@ var OverlapRemover = class {
|
|
|
67884
68122
|
|
|
67885
68123
|
//#endregion
|
|
67886
68124
|
//#region src/client/renderer/LayoutController.ts
|
|
67887
|
-
const log$
|
|
68125
|
+
const log$34 = scoped("layout");
|
|
67888
68126
|
var LayoutController = class {
|
|
67889
68127
|
cy;
|
|
67890
68128
|
layoutRaf = null;
|
|
@@ -67927,17 +68165,17 @@ var LayoutController = class {
|
|
|
67927
68165
|
this.worker?.terminate();
|
|
67928
68166
|
this.worker = null;
|
|
67929
68167
|
this.workerInitFailed = true;
|
|
67930
|
-
log$
|
|
68168
|
+
log$34.warn("layout worker error — falling back to main-thread layout");
|
|
67931
68169
|
this.failPendingLayout();
|
|
67932
68170
|
this.retryWorkerInit();
|
|
67933
68171
|
};
|
|
67934
68172
|
} else {
|
|
67935
68173
|
this.workerInitFailed = true;
|
|
67936
|
-
log$
|
|
68174
|
+
log$34.warn("layout worker not available — using main-thread layout");
|
|
67937
68175
|
}
|
|
67938
68176
|
} catch (e) {
|
|
67939
68177
|
this.workerInitFailed = true;
|
|
67940
|
-
log$
|
|
68178
|
+
log$34.warn("layout worker init failed — using main-thread layout", e);
|
|
67941
68179
|
}
|
|
67942
68180
|
}
|
|
67943
68181
|
/** Resolve an in-flight worker layout with no positions, so the caller falls back immediately. */
|
|
@@ -67963,7 +68201,7 @@ var LayoutController = class {
|
|
|
67963
68201
|
if (this.workerGaveUp) return;
|
|
67964
68202
|
if (this.workerRetryCount >= RENDERER.WORKER_MAX_INIT_RETRIES) {
|
|
67965
68203
|
this.workerGaveUp = true;
|
|
67966
|
-
log$
|
|
68204
|
+
log$34.warn(`layout worker unavailable after ${RENDERER.WORKER_MAX_INIT_RETRIES} retries — main-thread layout for the rest of this session`);
|
|
67967
68205
|
return;
|
|
67968
68206
|
}
|
|
67969
68207
|
this.workerRetryCount++;
|
|
@@ -67984,13 +68222,13 @@ var LayoutController = class {
|
|
|
67984
68222
|
const timeoutMs = this.workerReady ? RENDERER.LAYOUT_TIMEOUT_MS : RENDERER.WORKER_HANDSHAKE_TIMEOUT_MS;
|
|
67985
68223
|
return new Promise((resolve) => {
|
|
67986
68224
|
this.workerTimeout = setTimeout(() => {
|
|
67987
|
-
log$
|
|
68225
|
+
log$34.warn("layout worker timeout — falling back to main-thread layout");
|
|
67988
68226
|
this.failPendingLayout();
|
|
67989
68227
|
if (this.worker) {
|
|
67990
68228
|
this.worker.terminate();
|
|
67991
68229
|
this.worker = null;
|
|
67992
68230
|
this.workerInitFailed = true;
|
|
67993
|
-
log$
|
|
68231
|
+
log$34.warn("layout worker terminated due to timeout — retrying within the retry budget");
|
|
67994
68232
|
this.retryWorkerInit();
|
|
67995
68233
|
}
|
|
67996
68234
|
}, timeoutMs);
|
|
@@ -68064,7 +68302,7 @@ var LayoutController = class {
|
|
|
68064
68302
|
try {
|
|
68065
68303
|
this.runIncrementalLayout(layout$2, newNodeIds);
|
|
68066
68304
|
} catch (e) {
|
|
68067
|
-
log$
|
|
68305
|
+
log$34.warn("incremental layout failed — keeping the previous positions", e);
|
|
68068
68306
|
} finally {
|
|
68069
68307
|
this.layoutRaf = null;
|
|
68070
68308
|
}
|
|
@@ -68828,7 +69066,7 @@ function detectCyclesDFS(adjacency, maxCycles = MAX_CYCLES_DEFAULT) {
|
|
|
68828
69066
|
|
|
68829
69067
|
//#endregion
|
|
68830
69068
|
//#region src/client/renderer/CycleDetector.ts
|
|
68831
|
-
const log$
|
|
69069
|
+
const log$33 = scoped("cycle-detector");
|
|
68832
69070
|
var CycleDetector = class {
|
|
68833
69071
|
cy;
|
|
68834
69072
|
adjacencyCache;
|
|
@@ -68870,7 +69108,7 @@ var CycleDetector = class {
|
|
|
68870
69108
|
const cycles = new Set();
|
|
68871
69109
|
const nodeCount = this.cy.nodes().length;
|
|
68872
69110
|
if (nodeCount > CYCLE_DETECTION.MAX_NODES_FOR_DETECTION) {
|
|
68873
|
-
log$
|
|
69111
|
+
log$33.warn("cycle detection skipped: graph too large", { nodeCount });
|
|
68874
69112
|
return cycles;
|
|
68875
69113
|
}
|
|
68876
69114
|
const adjacencySet = this.getForwardAdjacency();
|
|
@@ -68883,7 +69121,7 @@ var CycleDetector = class {
|
|
|
68883
69121
|
detectCyclesDetailed() {
|
|
68884
69122
|
const nodeCount = this.cy.nodes().length;
|
|
68885
69123
|
if (nodeCount > CYCLE_DETECTION.MAX_NODES_FOR_DETECTION) {
|
|
68886
|
-
log$
|
|
69124
|
+
log$33.warn("detailed cycle detection skipped: graph too large", { nodeCount });
|
|
68887
69125
|
return [];
|
|
68888
69126
|
}
|
|
68889
69127
|
const adjacencySet = this.getForwardAdjacency();
|
|
@@ -69447,7 +69685,7 @@ function pushId(map$1, key, edgeId) {
|
|
|
69447
69685
|
|
|
69448
69686
|
//#endregion
|
|
69449
69687
|
//#region src/client/renderer/DataController.ts
|
|
69450
|
-
const log$
|
|
69688
|
+
const log$32 = scoped("data-controller");
|
|
69451
69689
|
/** 缺省结果:任何未表态路径都必须回到「必须失效」的安全方向。 */
|
|
69452
69690
|
const DIRTY = { kind: "dirty" };
|
|
69453
69691
|
var DataController = class {
|
|
@@ -69705,7 +69943,7 @@ var DataController = class {
|
|
|
69705
69943
|
expectedVersion: cache$1.getVersion()
|
|
69706
69944
|
};
|
|
69707
69945
|
} catch (err) {
|
|
69708
|
-
log$
|
|
69946
|
+
log$32.warn("adjacency reconcile failed — invalidating cache", { err: String(err) });
|
|
69709
69947
|
cache$1.invalidate();
|
|
69710
69948
|
return DIRTY;
|
|
69711
69949
|
}
|
|
@@ -69771,7 +70009,7 @@ var DataController = class {
|
|
|
69771
70009
|
}
|
|
69772
70010
|
addEdge(e) {
|
|
69773
70011
|
if (this.cy.getElementById(e.source).empty() || this.cy.getElementById(e.target).empty()) {
|
|
69774
|
-
log$
|
|
70012
|
+
log$32.warn("edge dropped: missing endpoint", {
|
|
69775
70013
|
edgeId: e.id,
|
|
69776
70014
|
source: e.source,
|
|
69777
70015
|
target: e.target
|
|
@@ -69934,7 +70172,7 @@ var DataController = class {
|
|
|
69934
70172
|
|
|
69935
70173
|
//#endregion
|
|
69936
70174
|
//#region src/client/renderer/FilterController.ts
|
|
69937
|
-
const log$
|
|
70175
|
+
const log$31 = scoped("filter");
|
|
69938
70176
|
var FilterController = class {
|
|
69939
70177
|
cy;
|
|
69940
70178
|
fanInCache = null;
|
|
@@ -70091,7 +70329,7 @@ var FilterController = class {
|
|
|
70091
70329
|
if (/(\+|\*|\{)\{/.test(pattern)) return null;
|
|
70092
70330
|
return new RegExp(pattern);
|
|
70093
70331
|
} catch (e) {
|
|
70094
|
-
log$
|
|
70332
|
+
log$31.warn("invalid regex pattern", e);
|
|
70095
70333
|
return null;
|
|
70096
70334
|
}
|
|
70097
70335
|
}
|
|
@@ -70259,7 +70497,7 @@ var Trie = class {
|
|
|
70259
70497
|
|
|
70260
70498
|
//#endregion
|
|
70261
70499
|
//#region src/client/renderer/SearchController.ts
|
|
70262
|
-
const log$
|
|
70500
|
+
const log$30 = scoped("search");
|
|
70263
70501
|
const BUILD_CHUNK_SIZE = 200;
|
|
70264
70502
|
const BUILD_CHUNK_DELAY = 16;
|
|
70265
70503
|
function requestIdleCallbackCompat(cb) {
|
|
@@ -70301,7 +70539,7 @@ var SearchController = class {
|
|
|
70301
70539
|
this.trieBuilt = true;
|
|
70302
70540
|
this.indexBuilding = false;
|
|
70303
70541
|
this.buildHandle = null;
|
|
70304
|
-
log$
|
|
70542
|
+
log$30.info(`Trie index built: ${entries.length} nodes`);
|
|
70305
70543
|
}
|
|
70306
70544
|
};
|
|
70307
70545
|
processChunk();
|
|
@@ -71072,10 +71310,7 @@ var InteractionController = class InteractionController {
|
|
|
71072
71310
|
this.callbacks.onSelectionToggle?.(id, selected, [...this.multiSelectedIds]);
|
|
71073
71311
|
this.callbacks.onNodeTap?.(id);
|
|
71074
71312
|
} else {
|
|
71075
|
-
|
|
71076
|
-
this.multiSelectedIds.clear();
|
|
71077
|
-
this.cy.nodes().removeClass("multi-selected");
|
|
71078
|
-
}
|
|
71313
|
+
this.clearMultiSelect();
|
|
71079
71314
|
this.callbacks.onNodeTap?.(id);
|
|
71080
71315
|
}
|
|
71081
71316
|
this.focusNodeId = id;
|
|
@@ -71126,6 +71361,14 @@ var InteractionController = class InteractionController {
|
|
|
71126
71361
|
y: pos.y
|
|
71127
71362
|
});
|
|
71128
71363
|
});
|
|
71364
|
+
this.cy.on("boxend", () => {
|
|
71365
|
+
const ids = this.cy.$(":selected").nodes().map((n) => n.id());
|
|
71366
|
+
for (const id of ids) this.multiSelectedIds.add(id);
|
|
71367
|
+
this.cy.nodes().forEach((n) => {
|
|
71368
|
+
if (this.multiSelectedIds.has(n.id())) n.addClass("multi-selected");
|
|
71369
|
+
});
|
|
71370
|
+
this.callbacks.onSelectionToggle?.(ids[0] ?? "", ids.length > 0, [...this.multiSelectedIds]);
|
|
71371
|
+
});
|
|
71129
71372
|
}
|
|
71130
71373
|
/**
|
|
71131
71374
|
* 绑定画布容器上的键盘 / 焦点事件。
|
|
@@ -71274,6 +71517,20 @@ var InteractionController = class InteractionController {
|
|
|
71274
71517
|
this.callbacks = callbacks;
|
|
71275
71518
|
}
|
|
71276
71519
|
/**
|
|
71520
|
+
* 清空多选集合(cycle-149):集合 + `multi-selected` class + 通知消费方一体完成。
|
|
71521
|
+
*
|
|
71522
|
+
* 两个触发方共用本方法(单一实现,防漂移):普通点击的静默清空、
|
|
71523
|
+
* 批量操作条「清除」按钮经渲染器 `clearMultiSelect` 的显式清空。
|
|
71524
|
+
* `onSelectionClear` 让 store 镜像同步——缺了它消费方留下幽灵计数
|
|
71525
|
+
* (「画布无高亮但 BatchActionBar 还在」)。
|
|
71526
|
+
*/
|
|
71527
|
+
clearMultiSelect() {
|
|
71528
|
+
if (this.multiSelectedIds.size === 0) return;
|
|
71529
|
+
this.multiSelectedIds.clear();
|
|
71530
|
+
this.cy.nodes().removeClass("multi-selected");
|
|
71531
|
+
this.callbacks.onSelectionClear?.();
|
|
71532
|
+
}
|
|
71533
|
+
/**
|
|
71277
71534
|
* 切换节点 / 边标签的可见性(cycle-141 / D)。
|
|
71278
71535
|
*
|
|
71279
71536
|
* 实现放在本控制器而不是 `StyleController`:标签显隐是**交互态**而非主题态,
|
|
@@ -71305,6 +71562,7 @@ var InteractionController = class InteractionController {
|
|
|
71305
71562
|
this.cy.off("mouseover", "edge");
|
|
71306
71563
|
this.cy.off("mouseout", "edge");
|
|
71307
71564
|
this.cy.off("cxttap", "node");
|
|
71565
|
+
this.cy.off("boxend");
|
|
71308
71566
|
}
|
|
71309
71567
|
};
|
|
71310
71568
|
|
|
@@ -71802,7 +72060,7 @@ var VirtualViewportController = class {
|
|
|
71802
72060
|
|
|
71803
72061
|
//#endregion
|
|
71804
72062
|
//#region src/client/renderer/CytoscapeRenderer.ts
|
|
71805
|
-
const log$
|
|
72063
|
+
const log$29 = scoped("cytoscape-renderer");
|
|
71806
72064
|
function ensureExtensionsRegistered() {
|
|
71807
72065
|
const cy = cytoscape$1;
|
|
71808
72066
|
if (!cy.__dagreRegistered) {
|
|
@@ -71837,7 +72095,7 @@ var CytoscapeRenderer = class {
|
|
|
71837
72095
|
try {
|
|
71838
72096
|
return fn$7();
|
|
71839
72097
|
} catch (err) {
|
|
71840
|
-
log$
|
|
72098
|
+
log$29.error(`${name$1} failed`, err);
|
|
71841
72099
|
this.lastError = err instanceof Error ? err : new Error(String(err));
|
|
71842
72100
|
return fallback;
|
|
71843
72101
|
}
|
|
@@ -71846,7 +72104,7 @@ var CytoscapeRenderer = class {
|
|
|
71846
72104
|
try {
|
|
71847
72105
|
fn$7();
|
|
71848
72106
|
} catch (err) {
|
|
71849
|
-
log$
|
|
72107
|
+
log$29.error(`${name$1} failed`, err);
|
|
71850
72108
|
this.lastError = err instanceof Error ? err : new Error(String(err));
|
|
71851
72109
|
}
|
|
71852
72110
|
}
|
|
@@ -71893,7 +72151,9 @@ var CytoscapeRenderer = class {
|
|
|
71893
72151
|
onNodeHoverOut: this.options.onNodeHoverOut ?? (() => {}),
|
|
71894
72152
|
onEdgeHover: this.options.onEdgeHover ?? (() => {}),
|
|
71895
72153
|
onEdgeHoverOut: this.options.onEdgeHoverOut ?? (() => {}),
|
|
71896
|
-
...this.options.onNodeContextTap ? { onNodeContextTap: this.options.onNodeContextTap } : {}
|
|
72154
|
+
...this.options.onNodeContextTap ? { onNodeContextTap: this.options.onNodeContextTap } : {},
|
|
72155
|
+
onSelectionToggle: this.options.onSelectionToggle ?? (() => {}),
|
|
72156
|
+
onSelectionClear: this.options.onSelectionClear
|
|
71897
72157
|
});
|
|
71898
72158
|
this.pathFinder = new PathFinder(cy, this.adjacencyCache);
|
|
71899
72159
|
this.virtualViewportCtrl = new VirtualViewportController(cy);
|
|
@@ -71972,27 +72232,16 @@ var CytoscapeRenderer = class {
|
|
|
71972
72232
|
this.layoutCtrl?.applyWorkerLayout(positions);
|
|
71973
72233
|
}
|
|
71974
72234
|
/**
|
|
71975
|
-
|
|
71976
72235
|
* 开启虚拟视口(视口外的节点不渲染)。
|
|
71977
|
-
|
|
71978
72236
|
*
|
|
71979
|
-
|
|
71980
72237
|
* 这里额外往容器上写一个 `data-cg-virtual-scroll` 标记,原因是这个特性在 DOM 上
|
|
71981
|
-
|
|
71982
72238
|
* **完全不可观测**:它只改 cytoscape 内部的 `display` 状态,元素还在、canvas 还在,
|
|
71983
|
-
|
|
71984
72239
|
* 任何「元素可见」类断言在开启与未开启时结果相同。cycle-93 之前 E2E 的 J35
|
|
71985
|
-
|
|
71986
72240
|
* 因此只能断言「容器可见」,零鉴别力。
|
|
71987
|
-
|
|
71988
72241
|
*
|
|
71989
|
-
|
|
71990
72242
|
* 标记沿用本仓库既有的 `data-cg-*` 集成标记约定(`useGraphDerivedValues.ts:71` 的
|
|
71991
|
-
|
|
71992
72243
|
* `data-cg-integration`)。写成「只有真的启用成功才标记」,避免
|
|
71993
|
-
|
|
71994
72244
|
* `virtualViewportCtrl` 为 null 时留下一个骗人的标记。
|
|
71995
|
-
|
|
71996
72245
|
*/
|
|
71997
72246
|
enableVirtualScroll() {
|
|
71998
72247
|
this.virtualViewportCtrl?.enable();
|
|
@@ -72039,17 +72288,11 @@ var CytoscapeRenderer = class {
|
|
|
72039
72288
|
this.viewportCtrl?.focusOnNode(nodeId);
|
|
72040
72289
|
}
|
|
72041
72290
|
/**
|
|
72042
|
-
|
|
72043
72291
|
* Bring a node the user explicitly asked for (a search hit, a host symbol link) into view even
|
|
72044
|
-
|
|
72045
72292
|
* when it is currently hidden, then centre the viewport on it. Returns `false` when the node is
|
|
72046
|
-
|
|
72047
72293
|
* not in the rendered graph at all, or when it is excluded by an active type/connection filter —
|
|
72048
|
-
|
|
72049
72294
|
* silently panning to an element the filter keeps invisible is what made "search a hidden node"
|
|
72050
|
-
|
|
72051
72295
|
* look like a no-op.
|
|
72052
|
-
|
|
72053
72296
|
*/
|
|
72054
72297
|
revealNode(nodeId) {
|
|
72055
72298
|
return this.safeCall("revealNode", () => {
|
|
@@ -72073,15 +72316,10 @@ var CytoscapeRenderer = class {
|
|
|
72073
72316
|
return el.data("typeFiltered") === true || el.data("connectionFiltered") === true;
|
|
72074
72317
|
}
|
|
72075
72318
|
/**
|
|
72076
|
-
|
|
72077
72319
|
* Viewport-space position used to anchor the context menu when the user opens it without a
|
|
72078
|
-
|
|
72079
72320
|
* pointer. Cytoscape keeps reporting the last computed position for `display: none` elements,
|
|
72080
|
-
|
|
72081
72321
|
* and the menu clamps itself into the viewport, so only a node that is really absent returns
|
|
72082
|
-
|
|
72083
72322
|
* `null`. Cytoscape 的渲染坐标相对画布容器,这里叠加容器原点换算成视口坐标。
|
|
72084
|
-
|
|
72085
72323
|
*/
|
|
72086
72324
|
getNodeRenderedPosition(nodeId) {
|
|
72087
72325
|
return this.safeCall("getNodeRenderedPosition", () => {
|
|
@@ -72132,6 +72370,10 @@ var CytoscapeRenderer = class {
|
|
|
72132
72370
|
focusNeighborhood(nodeId) {
|
|
72133
72371
|
this.safeCallVoid("focusNeighborhood", () => this.viewportCtrl?.focusNeighborhood(nodeId));
|
|
72134
72372
|
}
|
|
72373
|
+
/** 清空多选集合(cycle-149):委托 InteractionController(集合+class+回调一体)。 */
|
|
72374
|
+
clearMultiSelect() {
|
|
72375
|
+
this.safeCallVoid("clearMultiSelect", () => this.interactionCtrl?.clearMultiSelect());
|
|
72376
|
+
}
|
|
72135
72377
|
getThumbnail() {
|
|
72136
72378
|
return this.viewportCtrl?.getThumbnail() ?? null;
|
|
72137
72379
|
}
|
|
@@ -72201,7 +72443,7 @@ var CytoscapeRenderer = class {
|
|
|
72201
72443
|
try {
|
|
72202
72444
|
fn$7();
|
|
72203
72445
|
} catch (err) {
|
|
72204
|
-
log$
|
|
72446
|
+
log$29.warn(`destroy: ${label} cleanup failed`, err);
|
|
72205
72447
|
}
|
|
72206
72448
|
};
|
|
72207
72449
|
safeDestroy("interactionCtrl", () => this.interactionCtrl?.destroy());
|
|
@@ -72295,21 +72537,13 @@ var CytoscapeRenderer = class {
|
|
|
72295
72537
|
});
|
|
72296
72538
|
}
|
|
72297
72539
|
/**
|
|
72298
|
-
|
|
72299
72540
|
* diff 标记的渲染器入口(研究报告 §六 #13)。
|
|
72300
|
-
|
|
72301
72541
|
*
|
|
72302
|
-
|
|
72303
72542
|
* 实现全部委托给 `HighlightController`:它已经持有 `cy` 并且是「视图态 class」的归属地,
|
|
72304
|
-
|
|
72305
72543
|
* 把标记逻辑写两份必然漂移(`markDeadCode` 就是直接写在这里的,代价是它没有复用任何
|
|
72306
|
-
|
|
72307
72544
|
* 控制器设施)。渲染器本身只做 `safeCall` 包裹与「控制器不存在时给空统计」的兜底——
|
|
72308
|
-
|
|
72309
72545
|
* 后者很重要:控制器为 `null` 时若返回 `undefined`,调用方读 `.counts.added` 会抛错,
|
|
72310
|
-
|
|
72311
72546
|
* 而这条路径在「渲染器尚未完成 init」时是真实可达的。
|
|
72312
|
-
|
|
72313
72547
|
*/
|
|
72314
72548
|
applyDiffMarkers(index) {
|
|
72315
72549
|
const empty = {
|
|
@@ -72333,7 +72567,7 @@ var CytoscapeRenderer = class {
|
|
|
72333
72567
|
this.cy.nodes().stop();
|
|
72334
72568
|
this.cy.edges().stop();
|
|
72335
72569
|
} catch (e) {
|
|
72336
|
-
log$
|
|
72570
|
+
log$29.warn("pauseAnimations failed", e);
|
|
72337
72571
|
}
|
|
72338
72572
|
}
|
|
72339
72573
|
resumeAnimations() {
|
|
@@ -72341,7 +72575,7 @@ var CytoscapeRenderer = class {
|
|
|
72341
72575
|
try {
|
|
72342
72576
|
this.cy.resize();
|
|
72343
72577
|
} catch (e) {
|
|
72344
|
-
log$
|
|
72578
|
+
log$29.warn("resumeAnimations failed", e);
|
|
72345
72579
|
}
|
|
72346
72580
|
}
|
|
72347
72581
|
/** 缩放画布:factor > 1 放大,factor < 1 缩小。带 200ms 动画过渡。 */
|
|
@@ -72380,7 +72614,7 @@ var CytoscapeRenderer = class {
|
|
|
72380
72614
|
|
|
72381
72615
|
//#endregion
|
|
72382
72616
|
//#region src/client/hooks/useGraphRenderer.ts
|
|
72383
|
-
const log$
|
|
72617
|
+
const log$28 = scoped("renderer-hook");
|
|
72384
72618
|
function useGraphRenderer(config) {
|
|
72385
72619
|
const { data: data$2, display, interaction, filters, analysis, callbacks, showCallChainRef } = config;
|
|
72386
72620
|
const { nodes, edges, rawNodes, repoId } = data$2;
|
|
@@ -72422,17 +72656,19 @@ function useGraphRenderer(config) {
|
|
|
72422
72656
|
onNodeHoverOut: () => callbacksRef.current.onNodeHoverOut(),
|
|
72423
72657
|
onEdgeTap: (edgeId, pos) => callbacksRef.current.onEdgeTap(edgeId, pos),
|
|
72424
72658
|
onEdgeHover: (edgeId, pos) => callbacksRef.current.onEdgeHover(edgeId, pos),
|
|
72425
|
-
onEdgeHoverOut: () => callbacksRef.current.onEdgeHoverOut()
|
|
72659
|
+
onEdgeHoverOut: () => callbacksRef.current.onEdgeHoverOut(),
|
|
72660
|
+
onSelectionToggle: (nodeId, selected, selectedIds) => callbacksRef.current.onSelectionToggle?.(nodeId, selected, selectedIds),
|
|
72661
|
+
onSelectionClear: () => callbacksRef.current.onSelectionClear?.()
|
|
72426
72662
|
});
|
|
72427
72663
|
renderer$1.init();
|
|
72428
72664
|
rendererRef.current = renderer$1;
|
|
72429
|
-
log$
|
|
72665
|
+
log$28.info("renderer initialized", {
|
|
72430
72666
|
theme,
|
|
72431
72667
|
w,
|
|
72432
72668
|
h
|
|
72433
72669
|
});
|
|
72434
72670
|
} catch (err) {
|
|
72435
|
-
log$
|
|
72671
|
+
log$28.error("renderer init failed", { err });
|
|
72436
72672
|
renderer$1?.destroy();
|
|
72437
72673
|
renderer$1 = null;
|
|
72438
72674
|
useGraphStore.getState().setError(err instanceof Error ? err.message : String(err));
|
|
@@ -72680,7 +72916,7 @@ function useGraphRenderer(config) {
|
|
|
72680
72916
|
a.download = `codegraph-${Date.now()}.${extension$1}`;
|
|
72681
72917
|
a.click();
|
|
72682
72918
|
setTimeout(() => URL.revokeObjectURL(url), UI.URL_REVOKE_DELAY_MS);
|
|
72683
|
-
log$
|
|
72919
|
+
log$28.info(`exported ${format}`);
|
|
72684
72920
|
}
|
|
72685
72921
|
}, [theme]);
|
|
72686
72922
|
const updateTheme = (0, react.useCallback)((newTheme) => {
|
|
@@ -72816,7 +73052,7 @@ function useFocusMode(opts) {
|
|
|
72816
73052
|
|
|
72817
73053
|
//#endregion
|
|
72818
73054
|
//#region src/client/hooks/useNodeInteraction.ts
|
|
72819
|
-
const log$
|
|
73055
|
+
const log$27 = scoped("panel");
|
|
72820
73056
|
function useNodeInteraction(opts) {
|
|
72821
73057
|
const { setSelectedNode, setSelectedNodeData, getDataRef, adjacencyMap, setTooltip, expandedNodeIds, collapseNode, expandNode, handleSelectFunction, setFocusNodeId, handleNodeDoubleTapRef } = opts;
|
|
72822
73058
|
const handleNodeTap = (0, react.useCallback)((id) => {
|
|
@@ -72825,7 +73061,7 @@ function useNodeInteraction(opts) {
|
|
|
72825
73061
|
const data$2 = getDataRef.current?.getNodeData(id) ?? null;
|
|
72826
73062
|
setSelectedNodeData(data$2);
|
|
72827
73063
|
} catch (err) {
|
|
72828
|
-
log$
|
|
73064
|
+
log$27.error("handleNodeTap failed", {
|
|
72829
73065
|
id,
|
|
72830
73066
|
err
|
|
72831
73067
|
});
|
|
@@ -72857,7 +73093,7 @@ function useNodeInteraction(opts) {
|
|
|
72857
73093
|
});
|
|
72858
73094
|
}
|
|
72859
73095
|
} catch (err) {
|
|
72860
|
-
log$
|
|
73096
|
+
log$27.error("handleNodeHover failed", {
|
|
72861
73097
|
nodeId,
|
|
72862
73098
|
err
|
|
72863
73099
|
});
|
|
@@ -72908,7 +73144,7 @@ function useNodeInteraction(opts) {
|
|
|
72908
73144
|
});
|
|
72909
73145
|
}
|
|
72910
73146
|
} catch (err) {
|
|
72911
|
-
log$
|
|
73147
|
+
log$27.error("handleEdgeTap failed", {
|
|
72912
73148
|
edgeId,
|
|
72913
73149
|
err
|
|
72914
73150
|
});
|
|
@@ -72935,7 +73171,7 @@ function useNodeInteraction(opts) {
|
|
|
72935
73171
|
return nodeId;
|
|
72936
73172
|
});
|
|
72937
73173
|
} catch (err) {
|
|
72938
|
-
log$
|
|
73174
|
+
log$27.error("handleNodeDoubleTap failed", {
|
|
72939
73175
|
nodeId,
|
|
72940
73176
|
err
|
|
72941
73177
|
});
|
|
@@ -73301,7 +73537,7 @@ function useDeadCodeMarker({ showDeadCode, deadCodeResult, rendererRef }) {
|
|
|
73301
73537
|
|
|
73302
73538
|
//#endregion
|
|
73303
73539
|
//#region src/client/hooks/useDiffComparison.ts
|
|
73304
|
-
const log$
|
|
73540
|
+
const log$26 = scoped("diff");
|
|
73305
73541
|
/**
|
|
73306
73542
|
|
|
73307
73543
|
* 版本对比的编排层(研究报告 §六 #13)。
|
|
@@ -73394,7 +73630,7 @@ function useDiffComparison(params) {
|
|
|
73394
73630
|
return next$1;
|
|
73395
73631
|
} catch (e) {
|
|
73396
73632
|
if (!aliveRef.current || gen !== generationRef.current) return null;
|
|
73397
|
-
log$
|
|
73633
|
+
log$26.warn("diff comparison failed", e);
|
|
73398
73634
|
setError(e instanceof Error ? e.message : String(e));
|
|
73399
73635
|
setIsComparing(false);
|
|
73400
73636
|
return null;
|
|
@@ -73901,19 +74137,12 @@ const KEYBOARD_SHORTCUTS = [
|
|
|
73901
74137
|
}
|
|
73902
74138
|
];
|
|
73903
74139
|
/**
|
|
73904
|
-
|
|
73905
74140
|
* 画布级快捷键(cycle-141 / D)。
|
|
73906
|
-
|
|
73907
74141
|
*
|
|
73908
|
-
|
|
73909
74142
|
* 与 `KEYBOARD_SHORTCUTS` 分开声明,**不注册全局监听**:面板级快捷键挂在
|
|
73910
|
-
|
|
73911
74143
|
* `document` 上、任何焦点下都生效;画布级只在 `.cg-graph-container` 聚焦时由
|
|
73912
|
-
|
|
73913
74144
|
* `InteractionController` 接管。此处仅作为键盘提示浮层与文档的元数据,
|
|
73914
|
-
|
|
73915
74145
|
* 避免方向键劫持页面滚动、也避免 `usePanelKeyboard` 的测试长度断言被悄悄改变。
|
|
73916
|
-
|
|
73917
74146
|
*/
|
|
73918
74147
|
const CANVAS_KEYBOARD_SHORTCUTS = [
|
|
73919
74148
|
{
|
|
@@ -73951,6 +74180,10 @@ const CANVAS_KEYBOARD_SHORTCUTS = [
|
|
|
73951
74180
|
{
|
|
73952
74181
|
key: "F",
|
|
73953
74182
|
descKey: "canvasControls.shortcutFit"
|
|
74183
|
+
},
|
|
74184
|
+
{
|
|
74185
|
+
key: "Shift+Drag",
|
|
74186
|
+
descKey: "canvasControls.shortcutBoxSelect"
|
|
73954
74187
|
}
|
|
73955
74188
|
];
|
|
73956
74189
|
function formatShortcutKey(s) {
|
|
@@ -74014,7 +74247,7 @@ function usePanelKeyboard(layout$2, setLayout, rendererRef, handlers) {
|
|
|
74014
74247
|
|
|
74015
74248
|
//#endregion
|
|
74016
74249
|
//#region src/client/hooks/useViewMode.ts
|
|
74017
|
-
const log$
|
|
74250
|
+
const log$25 = scoped("viewMode");
|
|
74018
74251
|
const MICRO_MODES = [
|
|
74019
74252
|
"cfg",
|
|
74020
74253
|
"flowchart",
|
|
@@ -74057,7 +74290,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
74057
74290
|
if (controller.signal.aborted) return;
|
|
74058
74291
|
if (!data$2.nodes || data$2.nodes.length === 0) return;
|
|
74059
74292
|
if (isForeignRepo(data$2.metadata?.repoId, requestedWorkspace)) {
|
|
74060
|
-
log$
|
|
74293
|
+
log$25.info("dependency graph ignored for another workspace", {
|
|
74061
74294
|
repoId: data$2.metadata?.repoId,
|
|
74062
74295
|
requestedWorkspace,
|
|
74063
74296
|
activeWorkspace: currentWorkspace
|
|
@@ -74082,7 +74315,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
74082
74315
|
else setError(t$1("error.loadGraphData"));
|
|
74083
74316
|
setLoading(false);
|
|
74084
74317
|
}).catch((e) => {
|
|
74085
|
-
log$
|
|
74318
|
+
log$25.warn("loadBootstrap failed", e);
|
|
74086
74319
|
setError(t$1("error.loadGraph"));
|
|
74087
74320
|
setLoading(false);
|
|
74088
74321
|
});
|
|
@@ -74111,7 +74344,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
74111
74344
|
}).then((data$2) => {
|
|
74112
74345
|
if (controller.signal.aborted) return;
|
|
74113
74346
|
if (isForeignRepo(data$2.metadata?.repoId, requestedWorkspace)) {
|
|
74114
|
-
log$
|
|
74347
|
+
log$25.info("macro view graph ignored for another workspace", {
|
|
74115
74348
|
repoId: data$2.metadata?.repoId,
|
|
74116
74349
|
requestedWorkspace,
|
|
74117
74350
|
activeWorkspace: currentWorkspace
|
|
@@ -74119,7 +74352,7 @@ function useViewMode(viewMode, currentWorkspace, depsFetchAbortRef) {
|
|
|
74119
74352
|
return;
|
|
74120
74353
|
}
|
|
74121
74354
|
if (!data$2.nodes || data$2.nodes.length === 0) return;
|
|
74122
|
-
log$
|
|
74355
|
+
log$25.info("macro view data loaded", {
|
|
74123
74356
|
viewMode: newMode,
|
|
74124
74357
|
nodes: data$2.nodes.length,
|
|
74125
74358
|
edges: data$2.edges?.length ?? 0,
|
|
@@ -76166,7 +76399,7 @@ function useWorkspaceCache(opts) {
|
|
|
76166
76399
|
|
|
76167
76400
|
//#endregion
|
|
76168
76401
|
//#region src/client/hooks/useSymbolSelectListener.ts
|
|
76169
|
-
const log$
|
|
76402
|
+
const log$24 = scoped("symbol-select");
|
|
76170
76403
|
function useSymbolSelectListener(opts) {
|
|
76171
76404
|
(0, react.useEffect)(() => {
|
|
76172
76405
|
const symbolHandler = (e) => {
|
|
@@ -76178,7 +76411,7 @@ function useSymbolSelectListener(opts) {
|
|
|
76178
76411
|
if (!match$1) return;
|
|
76179
76412
|
store.setSelectedNode(NodeId(match$1.id));
|
|
76180
76413
|
const revealed = opts.rendererRef.current?.revealNode(match$1.id) ?? false;
|
|
76181
|
-
if (!revealed) log$
|
|
76414
|
+
if (!revealed) log$24.warn("symbol match is not revealable in the rendered graph", {
|
|
76182
76415
|
id: match$1.id,
|
|
76183
76416
|
symbol: sym
|
|
76184
76417
|
});
|
|
@@ -76274,7 +76507,7 @@ function useMiniMapSync(opts) {
|
|
|
76274
76507
|
|
|
76275
76508
|
//#endregion
|
|
76276
76509
|
//#region src/client/hooks/useAutoBootstrap.ts
|
|
76277
|
-
const log$
|
|
76510
|
+
const log$23 = scoped("auto-bootstrap");
|
|
76278
76511
|
function useAutoBootstrap(opts) {
|
|
76279
76512
|
const hasAutoScannedRef = (0, react.useRef)(false);
|
|
76280
76513
|
(0, react.useEffect)(() => {
|
|
@@ -76284,10 +76517,10 @@ function useAutoBootstrap(opts) {
|
|
|
76284
76517
|
if (!opts.codegraphReady) return;
|
|
76285
76518
|
if (!opts.currentWorkspace) return;
|
|
76286
76519
|
hasAutoScannedRef.current = true;
|
|
76287
|
-
log$
|
|
76520
|
+
log$23.info("auto-fill: triggering bootstrap on first open", { path: opts.currentWorkspace });
|
|
76288
76521
|
useGraphStore.getState().loadBootstrap().then((ok) => {
|
|
76289
76522
|
if (!ok) {
|
|
76290
|
-
log$
|
|
76523
|
+
log$23.info("auto-fill: bootstrap failed, falling back to scan", { path: opts.currentWorkspace });
|
|
76291
76524
|
opts.handleScanWorkspace();
|
|
76292
76525
|
}
|
|
76293
76526
|
});
|
|
@@ -76542,7 +76775,7 @@ function useViewModePanelGuard(opts) {
|
|
|
76542
76775
|
|
|
76543
76776
|
//#endregion
|
|
76544
76777
|
//#region src/client/hooks/useSearchInteraction.ts
|
|
76545
|
-
const log$
|
|
76778
|
+
const log$22 = scoped("panel");
|
|
76546
76779
|
function useSearchInteraction(opts) {
|
|
76547
76780
|
const { searchQuery, rawNodes, setSelectedNode, setSelectedNodeData, getDataRef, setShowSearch, isRemoteHit } = opts;
|
|
76548
76781
|
const debouncedSearch = useDebounce(searchQuery, UI.SEARCH_DEBOUNCE_MS);
|
|
@@ -76594,7 +76827,7 @@ function useSearchInteraction(opts) {
|
|
|
76594
76827
|
setShowSearch(false);
|
|
76595
76828
|
const hitLabel = mergedResults.find((r) => r.id === id)?.label ?? id;
|
|
76596
76829
|
getDataRef.current.revealRemoteNode(id, hitLabel).then((ok) => {
|
|
76597
|
-
if (!ok) log$
|
|
76830
|
+
if (!ok) log$22.warn("remote search hit could not be revealed", { id });
|
|
76598
76831
|
});
|
|
76599
76832
|
return;
|
|
76600
76833
|
}
|
|
@@ -76602,10 +76835,10 @@ function useSearchInteraction(opts) {
|
|
|
76602
76835
|
const data$2 = getDataRef.current?.getNodeData(NodeId(id)) ?? null;
|
|
76603
76836
|
setSelectedNodeData(data$2);
|
|
76604
76837
|
const revealed = getDataRef.current?.revealNode(id) ?? false;
|
|
76605
|
-
if (!revealed) log$
|
|
76838
|
+
if (!revealed) log$22.warn("search result is not revealable in the rendered graph", { id });
|
|
76606
76839
|
setShowSearch(false);
|
|
76607
76840
|
} catch (err) {
|
|
76608
|
-
log$
|
|
76841
|
+
log$22.error("handleSelectSearchResult failed", {
|
|
76609
76842
|
id,
|
|
76610
76843
|
err
|
|
76611
76844
|
});
|
|
@@ -76627,7 +76860,7 @@ function useSearchInteraction(opts) {
|
|
|
76627
76860
|
|
|
76628
76861
|
//#endregion
|
|
76629
76862
|
//#region src/client/components/ErrorBoundary.tsx
|
|
76630
|
-
const log$
|
|
76863
|
+
const log$21 = scoped("boundary");
|
|
76631
76864
|
function ErrorFallback({ error: error$1, onRetry }) {
|
|
76632
76865
|
const t$1 = useT();
|
|
76633
76866
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -76670,7 +76903,7 @@ var GraphErrorBoundary = class extends react.Component {
|
|
|
76670
76903
|
}
|
|
76671
76904
|
componentDidCatch(error$1, errorInfo) {
|
|
76672
76905
|
this.props.onError?.(error$1, errorInfo);
|
|
76673
|
-
log$
|
|
76906
|
+
log$21.error("render error caught by boundary", {
|
|
76674
76907
|
error: error$1,
|
|
76675
76908
|
componentStack: errorInfo.componentStack
|
|
76676
76909
|
});
|
|
@@ -78683,6 +78916,10 @@ const ContextMenu = (0, react.memo)(function ContextMenu$1(props) {
|
|
|
78683
78916
|
const menuRef = (0, react.useRef)(null);
|
|
78684
78917
|
const [focusedIndex, setFocusedIndex] = (0, react.useState)(0);
|
|
78685
78918
|
const triggerRef = (0, react.useRef)(null);
|
|
78919
|
+
const isPinned = useGraphStore((s) => s.pinnedIds.has(props.nodeId));
|
|
78920
|
+
const pinNode = useGraphStore((s) => s.pinNode);
|
|
78921
|
+
const unpinNode = useGraphStore((s) => s.unpinNode);
|
|
78922
|
+
const [copyState, setCopyState] = (0, react.useState)("idle");
|
|
78686
78923
|
const onCloseRef = (0, react.useRef)(props.onClose);
|
|
78687
78924
|
onCloseRef.current = props.onClose;
|
|
78688
78925
|
const handleClose = (0, react.useCallback)(() => {
|
|
@@ -78841,6 +79078,43 @@ const ContextMenu = (0, react.memo)(function ContextMenu$1(props) {
|
|
|
78841
79078
|
handleClose();
|
|
78842
79079
|
}
|
|
78843
79080
|
});
|
|
79081
|
+
actions.push({
|
|
79082
|
+
id: "pinNode",
|
|
79083
|
+
label: t$1(isPinned ? "contextMenu.unpinNode" : "contextMenu.pinNode"),
|
|
79084
|
+
onClick: () => {
|
|
79085
|
+
if (isPinned) unpinNode(props.nodeId);
|
|
79086
|
+
else pinNode(props.nodeId);
|
|
79087
|
+
handleClose();
|
|
79088
|
+
}
|
|
79089
|
+
});
|
|
79090
|
+
actions.push({
|
|
79091
|
+
id: "copyId",
|
|
79092
|
+
label: copyState === "copied" ? t$1("contextMenu.copied") : t$1("contextMenu.copyId"),
|
|
79093
|
+
onClick: () => {
|
|
79094
|
+
try {
|
|
79095
|
+
navigator.clipboard?.writeText(props.nodeId);
|
|
79096
|
+
setCopyState("copied");
|
|
79097
|
+
window.setTimeout(() => setCopyState("idle"), 1500);
|
|
79098
|
+
} catch {}
|
|
79099
|
+
}
|
|
79100
|
+
});
|
|
79101
|
+
actions.push({
|
|
79102
|
+
id: "expandHere",
|
|
79103
|
+
label: t$1("contextMenu.expandHere"),
|
|
79104
|
+
onClick: () => {
|
|
79105
|
+
props.onExpandNode(props.nodeId);
|
|
79106
|
+
handleClose();
|
|
79107
|
+
},
|
|
79108
|
+
separatorAfter: true
|
|
79109
|
+
});
|
|
79110
|
+
actions.push({
|
|
79111
|
+
id: "collapseHere",
|
|
79112
|
+
label: t$1("contextMenu.collapseHere"),
|
|
79113
|
+
onClick: () => {
|
|
79114
|
+
props.onCollapseNode(props.nodeId);
|
|
79115
|
+
handleClose();
|
|
79116
|
+
}
|
|
79117
|
+
});
|
|
78844
79118
|
const menuStyle = {
|
|
78845
79119
|
position: "fixed",
|
|
78846
79120
|
left: `${props.position.x}px`,
|
|
@@ -78854,6 +79128,7 @@ const ContextMenu = (0, react.memo)(function ContextMenu$1(props) {
|
|
|
78854
79128
|
children: actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.default.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
78855
79129
|
className: "cg-context-menu-item",
|
|
78856
79130
|
role: "menuitem",
|
|
79131
|
+
"data-action-id": action.id,
|
|
78857
79132
|
disabled: action.disabled,
|
|
78858
79133
|
onClick: action.onClick,
|
|
78859
79134
|
children: [action.icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -78868,6 +79143,87 @@ const ContextMenu = (0, react.memo)(function ContextMenu$1(props) {
|
|
|
78868
79143
|
}) });
|
|
78869
79144
|
});
|
|
78870
79145
|
|
|
79146
|
+
//#endregion
|
|
79147
|
+
//#region src/client/components/BatchActionBar.tsx
|
|
79148
|
+
const log$20 = scoped("batch-bar");
|
|
79149
|
+
function BatchActionBar({ onClearSelection }) {
|
|
79150
|
+
const t$1 = useT();
|
|
79151
|
+
const multiSelectedIds = useGraphStore((s) => s.multiSelectedIds);
|
|
79152
|
+
const batchExpandSelected = useGraphStore((s) => s.batchExpandSelected);
|
|
79153
|
+
const batchCollapseSelected = useGraphStore((s) => s.batchCollapseSelected);
|
|
79154
|
+
const exportSelectedNodes = useGraphStore((s) => s.exportSelectedNodes);
|
|
79155
|
+
const clearMultiSelection = useGraphStore((s) => s.clearMultiSelection);
|
|
79156
|
+
if (multiSelectedIds.size === 0) return null;
|
|
79157
|
+
const handleClear = () => {
|
|
79158
|
+
if (onClearSelection) onClearSelection();
|
|
79159
|
+
else clearMultiSelection();
|
|
79160
|
+
};
|
|
79161
|
+
const handleExport = () => {
|
|
79162
|
+
try {
|
|
79163
|
+
const nodes = exportSelectedNodes();
|
|
79164
|
+
const payload = {
|
|
79165
|
+
exportedAt: new Date().toISOString(),
|
|
79166
|
+
selectedCount: multiSelectedIds.size,
|
|
79167
|
+
exportedCount: nodes.length,
|
|
79168
|
+
nodes
|
|
79169
|
+
};
|
|
79170
|
+
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
|
79171
|
+
const url = URL.createObjectURL(blob);
|
|
79172
|
+
const a = document.createElement("a");
|
|
79173
|
+
a.href = url;
|
|
79174
|
+
a.download = `codegraph-selection-${Date.now()}.json`;
|
|
79175
|
+
a.click();
|
|
79176
|
+
URL.revokeObjectURL(url);
|
|
79177
|
+
} catch (err) {
|
|
79178
|
+
log$20.error("export selection failed", { err });
|
|
79179
|
+
}
|
|
79180
|
+
};
|
|
79181
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
79182
|
+
className: "cg-batch-bar",
|
|
79183
|
+
role: "toolbar",
|
|
79184
|
+
"aria-label": t$1("batchbar.ariaLabel"),
|
|
79185
|
+
children: [
|
|
79186
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
79187
|
+
className: "cg-batch-bar-count",
|
|
79188
|
+
"aria-live": "polite",
|
|
79189
|
+
children: t$1("batchbar.count", { count: multiSelectedIds.size })
|
|
79190
|
+
}),
|
|
79191
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
79192
|
+
type: "button",
|
|
79193
|
+
className: "cg-batch-bar-btn",
|
|
79194
|
+
onClick: () => {
|
|
79195
|
+
batchExpandSelected();
|
|
79196
|
+
},
|
|
79197
|
+
"aria-label": t$1("batchbar.expandAria"),
|
|
79198
|
+
children: t$1("batchbar.expand")
|
|
79199
|
+
}),
|
|
79200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
79201
|
+
type: "button",
|
|
79202
|
+
className: "cg-batch-bar-btn",
|
|
79203
|
+
onClick: () => {
|
|
79204
|
+
batchCollapseSelected();
|
|
79205
|
+
},
|
|
79206
|
+
"aria-label": t$1("batchbar.collapseAria"),
|
|
79207
|
+
children: t$1("batchbar.collapse")
|
|
79208
|
+
}),
|
|
79209
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
79210
|
+
type: "button",
|
|
79211
|
+
className: "cg-batch-bar-btn",
|
|
79212
|
+
onClick: handleExport,
|
|
79213
|
+
"aria-label": t$1("batchbar.exportAria"),
|
|
79214
|
+
children: t$1("batchbar.export")
|
|
79215
|
+
}),
|
|
79216
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
79217
|
+
type: "button",
|
|
79218
|
+
className: "cg-batch-bar-btn cg-batch-bar-btn-clear",
|
|
79219
|
+
onClick: handleClear,
|
|
79220
|
+
"aria-label": t$1("batchbar.clearAria"),
|
|
79221
|
+
children: t$1("batchbar.clear")
|
|
79222
|
+
})
|
|
79223
|
+
]
|
|
79224
|
+
});
|
|
79225
|
+
}
|
|
79226
|
+
|
|
78871
79227
|
//#endregion
|
|
78872
79228
|
//#region src/client/components/LoadingOverlay.tsx
|
|
78873
79229
|
const LoadingOverlay = (0, react.memo)(function LoadingOverlay$1({ visible }) {
|
|
@@ -79265,7 +79621,7 @@ const StatusBar = (0, react.memo)(function StatusBar$1({ error: error$1, isLoadi
|
|
|
79265
79621
|
const statusText = error$1 ? t$1("state.error") : isLoading ? t$1("state.loading") : t$1("state.ready");
|
|
79266
79622
|
const wsName = workspaceName ? deriveWorkspaceName(workspaceName) : "";
|
|
79267
79623
|
const hasDegradations = performanceDegradations && performanceDegradations.length > 0;
|
|
79268
|
-
const degradationLabels = hasDegradations ? performanceDegradations.map((key) => DEGRADATION_KEYS[key] ? t$1(DEGRADATION_KEYS[key]) : key).join(", ") : "";
|
|
79624
|
+
const degradationLabels = hasDegradations && performanceDegradations ? performanceDegradations.map((key) => DEGRADATION_KEYS[key] ? t$1(DEGRADATION_KEYS[key]) : key).join(", ") : "";
|
|
79269
79625
|
const degradedRules = xuanjiSignal?.degradedRules ?? [];
|
|
79270
79626
|
const violatedRules = xuanjiSignal?.invariantViolations ?? [];
|
|
79271
79627
|
const showXuanji = xuanjiSignal?.applied === true;
|
|
@@ -86711,8 +87067,8 @@ function InteractiveMiniMap({ thumbnail, viewportInfo, onFocusArea, nodePosition
|
|
|
86711
87067
|
whiteSpace: "nowrap",
|
|
86712
87068
|
background: "var(--cg-bg-elevated)",
|
|
86713
87069
|
color: "var(--cg-text)",
|
|
86714
|
-
border: "1px solid var(--cg-border-strong)
|
|
86715
|
-
boxShadow: "var(--cg-shadow-sm)
|
|
87070
|
+
border: "1px solid var(--cg-border-strong)",
|
|
87071
|
+
boxShadow: "var(--cg-shadow-sm)",
|
|
86716
87072
|
pointerEvents: "none"
|
|
86717
87073
|
},
|
|
86718
87074
|
children: t$1("canvasControls.minimapFocusHint")
|
|
@@ -86743,8 +87099,8 @@ function KeyboardHintOverlay({ onClose }) {
|
|
|
86743
87099
|
padding: "10px 12px",
|
|
86744
87100
|
borderRadius: "var(--cg-radius-sm)",
|
|
86745
87101
|
background: "var(--cg-surface)",
|
|
86746
|
-
border: "1px solid var(--cg-border-strong)
|
|
86747
|
-
boxShadow: "var(--cg-shadow)
|
|
87102
|
+
border: "1px solid var(--cg-border-strong)",
|
|
87103
|
+
boxShadow: "var(--cg-shadow)",
|
|
86748
87104
|
pointerEvents: "auto"
|
|
86749
87105
|
},
|
|
86750
87106
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -87258,7 +87614,9 @@ function GraphPanelInner({ className = "", sessionId, useSessions }) {
|
|
|
87258
87614
|
onNodeContextTap: (nodeId, pos) => setContextMenu({
|
|
87259
87615
|
nodeId,
|
|
87260
87616
|
position: pos
|
|
87261
|
-
})
|
|
87617
|
+
}),
|
|
87618
|
+
onSelectionToggle: (_nodeId, _selected, selectedIds) => useGraphStore.getState().syncMultiSelection(selectedIds),
|
|
87619
|
+
onSelectionClear: () => useGraphStore.getState().clearMultiSelection()
|
|
87262
87620
|
},
|
|
87263
87621
|
showCallChainRef
|
|
87264
87622
|
});
|
|
@@ -87871,6 +88229,7 @@ function GraphPanelInner({ className = "", sessionId, useSessions }) {
|
|
|
87871
88229
|
}
|
|
87872
88230
|
}),
|
|
87873
88231
|
tooltip && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GraphTooltip, { data: tooltip }),
|
|
88232
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatchActionBar, { onClearSelection: () => renderer$1.rendererRef.current?.clearMultiSelect() }),
|
|
87874
88233
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyboardHelp, {
|
|
87875
88234
|
visible: showHelp,
|
|
87876
88235
|
onClose: () => setShowHelp(false)
|
|
@@ -87901,7 +88260,7 @@ function GraphPanel(props) {
|
|
|
87901
88260
|
|
|
87902
88261
|
//#endregion
|
|
87903
88262
|
//#region src/generated/version.ts
|
|
87904
|
-
const PLUGIN_VERSION = "0.3.
|
|
88263
|
+
const PLUGIN_VERSION = "0.3.1";
|
|
87905
88264
|
|
|
87906
88265
|
//#endregion
|
|
87907
88266
|
//#region src/client/utils/safeCheck.ts
|