dsh-thinking-guard 0.1.20 → 0.1.22
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/README.md +3 -3
- package/lib/client.d.ts +13 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +1964 -1019
- package/lib/types/usage/call-index-runtime.d.ts.map +1 -1
- package/lib/usage/call-index-runtime.js +56 -4
- package/lib/usage/call-index-runtime.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -2687,7 +2687,7 @@ async function waitForMcpTools(remote, serverName, retryDelays) {
|
|
|
2687
2687
|
* second enable switch here.
|
|
2688
2688
|
*/
|
|
2689
2689
|
const CSS_ID$1 = "capability-menu-location-css";
|
|
2690
|
-
const CSS$
|
|
2690
|
+
const CSS$2 = `
|
|
2691
2691
|
.lm-block{display:flex;flex-direction:column;gap:8px}
|
|
2692
2692
|
.lm-btn{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:20px;padding:0 10px;cursor:pointer;white-space:nowrap}
|
|
2693
2693
|
.lm-btn:hover{border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -2731,7 +2731,7 @@ textarea.lm-input{resize:vertical;font-family:var(--dsw-font-markdown-code-block
|
|
|
2731
2731
|
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID$1}"]`) === null) {
|
|
2732
2732
|
const tag = document.createElement("style");
|
|
2733
2733
|
tag.dataset.cssId = CSS_ID$1;
|
|
2734
|
-
tag.textContent = CSS$
|
|
2734
|
+
tag.textContent = CSS$2;
|
|
2735
2735
|
document.head.appendChild(tag);
|
|
2736
2736
|
}
|
|
2737
2737
|
/** Render a key/value map as `Key: Value` lines. */
|
|
@@ -3228,6 +3228,196 @@ function LocationModal(props) {
|
|
|
3228
3228
|
})]
|
|
3229
3229
|
});
|
|
3230
3230
|
}
|
|
3231
|
+
//#endregion
|
|
3232
|
+
//#region src/client/v3-ui.ts
|
|
3233
|
+
/**
|
|
3234
|
+
* v3 设计层(D-044)——设置页四个子页面共用的 token、容器脚手架与块级词汇表。
|
|
3235
|
+
*
|
|
3236
|
+
* WHY A SHARED MODULE: v3 的视觉识别来自一套统一的 token 与块(panel / stats /
|
|
3237
|
+
* board / rank / table / gitem / catstat)。四个页面各写一份必然漂移,所以跨页面
|
|
3238
|
+
* 复用的部分集中在这里;页面专属样式仍留在各自文件里(call-index.tsx 的
|
|
3239
|
+
* `CallIndexStyles()`、CapabilitySection.tsx 的 mc-* 样式块)。
|
|
3240
|
+
*
|
|
3241
|
+
* CONTAINER, NOT VIEWPORT: 设置面板是宿主写死的 800px 弹窗,内容列只有 564px,
|
|
3242
|
+
* 与视口宽度无关(D-044 实测)。因此 v3 的断点全部写成 `@container v3`,页面根
|
|
3243
|
+
* 节点加上 `v3-root` 即成为容器。用 `@media` 会在 1200px 窗口里误判成「宽」,
|
|
3244
|
+
* 画出实际放不下的双栏。
|
|
3245
|
+
*
|
|
3246
|
+
* THEME: v3 原始 CSS 写死深色;这里全部映射到宿主 `--dsw-alias-*`,深浅色跟随
|
|
3247
|
+
* 应用。卡片底色用「label-primary 的 4% 叠在 bg-layer-2 上」而不是半透明白,
|
|
3248
|
+
* 否则浅色主题下会发脏。
|
|
3249
|
+
*
|
|
3250
|
+
* 本模块只输出样式与一个 style 元素,不假设任何 DOM 结构。
|
|
3251
|
+
*/
|
|
3252
|
+
/** Every rule the v3 pages share. Class names are global like the existing mc-*. */
|
|
3253
|
+
const CSS$1 = [
|
|
3254
|
+
".v3-root{container:v3 / inline-size;--v3-bg:var(--dsw-alias-bg-layer-2,Canvas);--v3-panel:color-mix(in srgb,var(--dsw-alias-label-primary,CanvasText) 4%,var(--dsw-alias-bg-layer-2,Canvas));--v3-panel-2:color-mix(in srgb,var(--dsw-alias-label-primary,CanvasText) 8%,var(--dsw-alias-bg-layer-2,Canvas));--v3-line:color-mix(in srgb,var(--dsw-alias-label-primary,CanvasText) 10%,transparent);--v3-line-2:color-mix(in srgb,var(--dsw-alias-label-primary,CanvasText) 17%,transparent);--v3-t1:var(--dsw-alias-label-primary,CanvasText);--v3-t2:var(--dsw-alias-label-secondary,var(--dsw-alias-label-primary,CanvasText));--v3-t3:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary,CanvasText));--v3-t4:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,CanvasText));--v3-ok:var(--dsw-alias-state-success-primary,#18894a);--v3-bad:var(--dsw-alias-state-error-primary,#c92b2b);--v3-warn:var(--dsw-alias-state-warn-primary,#a2730a);--v3-info:var(--dsw-alias-link,var(--dsw-alias-brand-primary,#2066c4));--v3-ok-dim:color-mix(in srgb,var(--v3-ok) 16%,transparent);--v3-bad-dim:color-mix(in srgb,var(--v3-bad) 14%,transparent);--v3-warn-dim:color-mix(in srgb,var(--v3-warn) 14%,transparent);--v3-info-dim:color-mix(in srgb,var(--v3-info) 14%,transparent);--v3-r-sm:var(--dsw-radius-sm,6px);--v3-r-md:var(--dsw-radius-md,9px);--v3-r-lg:var(--dsw-radius-lg,12px);--v3-font-code:var(--dsw-font-markdown-code-font-family,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);color:var(--v3-t1);font-size:13px;line-height:20px}",
|
|
3255
|
+
".v3-num{font-variant-numeric:tabular-nums}",
|
|
3256
|
+
".v3-mono{font-family:var(--v3-font-code)}",
|
|
3257
|
+
".v3-statusline{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--v3-t3);padding:5px 10px;border-radius:999px;background:var(--v3-panel);border:1px solid var(--v3-line);min-width:0}",
|
|
3258
|
+
".v3-statusline b{color:var(--v3-t1);font-weight:600}",
|
|
3259
|
+
".v3-statusline[data-tone='bad']{background:var(--v3-bad-dim);border-color:color-mix(in srgb,var(--v3-bad) 30%,transparent)}",
|
|
3260
|
+
".v3-dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex:none;background:var(--v3-t4)}",
|
|
3261
|
+
".v3-dot[data-tone='ok']{background:var(--v3-ok);box-shadow:0 0 0 3px var(--v3-ok-dim)}",
|
|
3262
|
+
".v3-dot[data-tone='bad']{background:var(--v3-bad);box-shadow:0 0 0 3px var(--v3-bad-dim)}",
|
|
3263
|
+
".v3-dot[data-tone='warn']{background:var(--v3-warn);box-shadow:0 0 0 3px var(--v3-warn-dim)}",
|
|
3264
|
+
".v3-dot[data-tone='run']{width:8px;height:8px;background:transparent;border:1.5px solid var(--v3-t3);border-top-color:transparent;animation:v3-spin .9s linear infinite;box-shadow:none}",
|
|
3265
|
+
"@keyframes v3-spin{to{transform:rotate(360deg)}}",
|
|
3266
|
+
".v3-panel{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);padding:13px 15px;min-width:0}",
|
|
3267
|
+
".v3-ph{display:flex;align-items:center;gap:8px;margin-bottom:11px;min-height:18px;flex-wrap:wrap}",
|
|
3268
|
+
".v3-pt{margin:0;font-size:11px;font-weight:600;color:var(--v3-t3);letter-spacing:.07em;text-transform:uppercase}",
|
|
3269
|
+
".v3-pn{margin-left:auto;font-size:11px;color:var(--v3-t4);white-space:nowrap;text-transform:none;letter-spacing:0}",
|
|
3270
|
+
".v3-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--v3-line);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);overflow:hidden}",
|
|
3271
|
+
".v3-stat{background:var(--v3-panel);padding:13px 15px;min-width:0}",
|
|
3272
|
+
".v3-stat-l{font-size:11px;color:var(--v3-t3);letter-spacing:.02em}",
|
|
3273
|
+
".v3-stat-v{font-size:26px;line-height:34px;font-weight:650;letter-spacing:-.025em;margin-top:3px;font-variant-numeric:tabular-nums}",
|
|
3274
|
+
".v3-stat-v small{font-size:12px;font-weight:400;color:var(--v3-t3);margin-left:4px;letter-spacing:0}",
|
|
3275
|
+
".v3-stat-d{font-size:11px;color:var(--v3-t4);margin-top:2px;display:flex;align-items:center;gap:5px}",
|
|
3276
|
+
".v3-spark{margin-top:8px;display:block;width:100%;height:22px;overflow:visible}",
|
|
3277
|
+
".v3-spark .v3-spark-ln{fill:none;stroke-width:1.4;stroke-linejoin:round;stroke-linecap:round}",
|
|
3278
|
+
".v3-spark .v3-spark-fl{opacity:.14}",
|
|
3279
|
+
".v3-board{display:grid;grid-template-columns:minmax(0,2.15fr) minmax(0,1fr);gap:12px;align-items:stretch}",
|
|
3280
|
+
".v3-chartbox{position:relative;min-width:0}",
|
|
3281
|
+
".v3-chart{display:block;width:100%;height:212px;overflow:visible}",
|
|
3282
|
+
".v3-chart .v3-grid{stroke:var(--v3-line);stroke-width:1}",
|
|
3283
|
+
".v3-chart .v3-axis{fill:var(--v3-t4);font-size:9.5px}",
|
|
3284
|
+
".v3-chart .v3-area{fill:var(--v3-info);opacity:.13}",
|
|
3285
|
+
".v3-chart .v3-line{fill:none;stroke:var(--v3-info);stroke-width:1.8;stroke-linejoin:round;stroke-linecap:round}",
|
|
3286
|
+
".v3-chart .v3-pt{fill:var(--v3-panel);stroke:var(--v3-info);stroke-width:1.6}",
|
|
3287
|
+
".v3-chart .v3-bar-er{fill:var(--v3-bad);opacity:.85}",
|
|
3288
|
+
".v3-chart .v3-hlbox{fill:var(--v3-info-dim);stroke:var(--v3-info);stroke-width:1;stroke-dasharray:3 3}",
|
|
3289
|
+
".v3-chart .v3-lbl{fill:var(--v3-t2);font-size:10px}",
|
|
3290
|
+
".v3-chart .v3-lbl-b{fill:var(--v3-t1);font-size:10.5px;font-weight:600}",
|
|
3291
|
+
".v3-legend{display:flex;gap:16px;margin-top:8px;font-size:11px;color:var(--v3-t4);flex-wrap:wrap}",
|
|
3292
|
+
".v3-legend span{display:inline-flex;align-items:center;gap:6px}",
|
|
3293
|
+
".v3-legend i{width:14px;height:2px;border-radius:1px;flex:none;background:var(--v3-info)}",
|
|
3294
|
+
".v3-legend i[data-mark='square']{width:8px;height:8px;border-radius:2px;background:var(--v3-bad)}",
|
|
3295
|
+
".v3-donut-wrap{display:flex;align-items:center;gap:14px;flex-wrap:wrap}",
|
|
3296
|
+
".v3-donut{flex:none;width:104px;height:104px;position:relative}",
|
|
3297
|
+
".v3-donut svg{width:104px;height:104px;transform:rotate(-90deg)}",
|
|
3298
|
+
".v3-donut .v3-trk{fill:none;stroke:var(--v3-panel-2);stroke-width:9}",
|
|
3299
|
+
".v3-donut .v3-seg{fill:none;stroke:var(--v3-t4);stroke-width:9}",
|
|
3300
|
+
".v3-donut .v3-mid{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;pointer-events:none}",
|
|
3301
|
+
".v3-donut .v3-mid b{font-size:17px;font-weight:650;letter-spacing:-.02em;line-height:20px}",
|
|
3302
|
+
".v3-donut .v3-mid span{font-size:9.5px;color:var(--v3-t4);line-height:12px;letter-spacing:.04em}",
|
|
3303
|
+
".v3-src-list{flex:1 1 180px;min-width:0;display:flex;flex-direction:column;gap:5px}",
|
|
3304
|
+
".v3-src{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--v3-t2);min-width:0}",
|
|
3305
|
+
".v3-src i{width:7px;height:7px;border-radius:2px;flex:none;background:var(--v3-t4);opacity:.5}",
|
|
3306
|
+
".v3-src .v3-nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
3307
|
+
".v3-src b{margin-left:auto;color:var(--v3-t1);font-weight:600;font-variant-numeric:tabular-nums}",
|
|
3308
|
+
".v3-src em{font-style:normal;color:var(--v3-t4);min-width:44px;text-align:right;font-variant-numeric:tabular-nums;font-size:11px}",
|
|
3309
|
+
".v3-src-note{margin:9px 0 0;padding-top:9px;border-top:1px solid var(--v3-line);font-size:11px;line-height:17px;color:var(--v3-t4)}",
|
|
3310
|
+
".v3-rank{display:flex;flex-direction:column;gap:1px;margin-top:-2px}",
|
|
3311
|
+
".v3-rank-row{display:flex;align-items:center;gap:9px;min-height:27px;font-size:12px;padding:0 2px;border-radius:4px}",
|
|
3312
|
+
".v3-rank-row:hover{background:var(--v3-panel-2)}",
|
|
3313
|
+
".v3-rank-row .v3-nm{font-family:var(--v3-font-code);font-size:11.5px;color:var(--v3-t2);width:158px;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
3314
|
+
".v3-rank-row .v3-bar{flex:1 1 auto;min-width:30px;height:6px;background:var(--v3-panel-2);border-radius:3px;position:relative;overflow:hidden}",
|
|
3315
|
+
".v3-rank-row .v3-bar span{position:absolute;left:0;top:0;bottom:0;min-width:3px;background:var(--v3-info);opacity:.65;border-radius:3px}",
|
|
3316
|
+
".v3-rank-row .v3-bar span[data-bad='true']{background:var(--v3-bad);opacity:.85}",
|
|
3317
|
+
".v3-rank-row .v3-n{width:52px;flex:none;text-align:right;font-variant-numeric:tabular-nums;color:var(--v3-t2);white-space:nowrap}",
|
|
3318
|
+
".v3-rank-row .v3-e{flex:none;text-align:right;font-variant-numeric:tabular-nums;font-size:11px;color:var(--v3-t4);white-space:nowrap}",
|
|
3319
|
+
".v3-rank-row .v3-e[data-bad='true']{color:var(--v3-bad);font-weight:600}",
|
|
3320
|
+
".v3-ts{max-height:min(60vh,560px);overflow:auto;overscroll-behavior:contain;border-top:1px solid var(--v3-line)}",
|
|
3321
|
+
".v3-table{width:100%;border-collapse:separate;border-spacing:0;font-size:12.5px}",
|
|
3322
|
+
".v3-table thead th{position:sticky;top:0;z-index:2;background:var(--v3-panel);color:var(--v3-t4);font-weight:500;font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;text-align:left;padding:0 11px;height:29px;white-space:nowrap;border-bottom:1px solid var(--v3-line)}",
|
|
3323
|
+
".v3-table thead th.v3-r{text-align:right}",
|
|
3324
|
+
".v3-table tbody td{padding:0 11px;height:32px;border-bottom:1px solid var(--v3-line);vertical-align:middle;white-space:nowrap}",
|
|
3325
|
+
".v3-table tbody tr:last-child td{border-bottom:0}",
|
|
3326
|
+
".v3-table tbody tr:hover td{background:var(--v3-panel-2)}",
|
|
3327
|
+
".v3-table td.v3-r{text-align:right;font-variant-numeric:tabular-nums;color:var(--v3-t2)}",
|
|
3328
|
+
".v3-c-tool{display:flex;align-items:center;gap:7px;min-width:0}",
|
|
3329
|
+
".v3-c-tool .v3-nm{font-family:var(--v3-font-code);font-size:11.5px;overflow:hidden;text-overflow:ellipsis}",
|
|
3330
|
+
".v3-c-tool .v3-srv{font-size:10.5px;color:var(--v3-t4)}",
|
|
3331
|
+
".v3-srcnote{flex:none;font-size:9px;line-height:13px;height:13px;padding:0 4px;border-radius:3px;color:var(--v3-t4);background:var(--v3-panel-2)}",
|
|
3332
|
+
".v3-rt{display:inline-flex;align-items:center;gap:7px;justify-content:flex-end}",
|
|
3333
|
+
".v3-rt .v3-tk{width:34px;height:3px;border-radius:2px;background:var(--v3-panel-2);overflow:hidden;flex:none}",
|
|
3334
|
+
".v3-rt .v3-fl{display:block;height:100%;background:var(--v3-ok);border-radius:2px}",
|
|
3335
|
+
".v3-rt .v3-fl[data-bad='true']{background:var(--v3-bad)}",
|
|
3336
|
+
".v3-rt b{font-weight:500;min-width:40px;text-align:right;font-variant-numeric:tabular-nums}",
|
|
3337
|
+
".v3-tfoot{display:flex;align-items:center;gap:9px;padding:9px 15px;border-top:1px solid var(--v3-line);color:var(--v3-t4);font-size:11px;flex-wrap:wrap}",
|
|
3338
|
+
".v3-empty{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);padding:34px 24px;text-align:center}",
|
|
3339
|
+
".v3-empty h3{margin:0 0 5px;font-size:14px;font-weight:600;color:var(--v3-t1)}",
|
|
3340
|
+
".v3-empty p{margin:0 auto;max-width:56ch;font-size:12.5px;line-height:20px;color:var(--v3-t3)}",
|
|
3341
|
+
".v3-banner{display:flex;align-items:flex-start;gap:10px;padding:11px 13px;border-radius:var(--v3-r-md);background:var(--v3-bad-dim);border:1px solid color-mix(in srgb,var(--v3-bad) 32%,transparent);font-size:12px;line-height:19px;color:var(--v3-t2)}",
|
|
3342
|
+
".v3-banner b{display:block;font-size:12.5px;margin-bottom:2px;color:var(--v3-t1)}",
|
|
3343
|
+
".v3-banner code{font-family:var(--v3-font-code);font-size:10.5px;padding:1px 5px;border-radius:3px;background:var(--v3-panel-2);color:var(--v3-t3);margin-left:5px}",
|
|
3344
|
+
".v3-faultcard{background:var(--v3-panel);border:1px solid var(--v3-line);border-left:3px solid var(--v3-bad);border-radius:var(--v3-r-md);overflow:hidden}",
|
|
3345
|
+
".v3-faultcard .v3-fh{display:flex;align-items:center;gap:8px;padding:9px 13px}",
|
|
3346
|
+
".v3-faultcard .v3-fh b{font-size:12.5px;font-weight:600}",
|
|
3347
|
+
".v3-faultcard .v3-fh code{font-family:var(--v3-font-code);font-size:10.5px;padding:1px 5px;border-radius:3px;background:var(--v3-panel-2);color:var(--v3-t3)}",
|
|
3348
|
+
".v3-faultcard p{margin:0;padding:0 13px 9px;font-size:11.5px;line-height:18px;color:var(--v3-t3)}",
|
|
3349
|
+
".v3-gpanel{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:12px}",
|
|
3350
|
+
".v3-gitem{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);padding:13px 15px;min-width:0}",
|
|
3351
|
+
".v3-gitem .v3-gh{display:flex;align-items:center;gap:8px;margin-bottom:7px;flex-wrap:wrap}",
|
|
3352
|
+
".v3-gitem .v3-gnm{font-size:13px;font-weight:600}",
|
|
3353
|
+
".v3-gitem .v3-gid{font-family:var(--v3-font-code);font-size:10.5px;color:var(--v3-t4)}",
|
|
3354
|
+
".v3-gitem p{margin:0;font-size:11.5px;line-height:18px;color:var(--v3-t3)}",
|
|
3355
|
+
".v3-gitem .v3-gfoot{display:flex;align-items:center;gap:6px;margin-top:9px;font-size:10.5px;color:var(--v3-t4);padding-top:9px;border-top:1px solid var(--v3-line);flex-wrap:wrap}",
|
|
3356
|
+
".v3-kbd{font-family:var(--v3-font-code);font-size:10.5px;padding:1px 5px;border-radius:3px;background:var(--v3-panel-2);color:var(--v3-t2);border:1px solid var(--v3-line)}",
|
|
3357
|
+
".v3-notebox{margin:0;padding:11px 13px;border-radius:var(--v3-r-md);background:var(--v3-panel);border:1px solid var(--v3-line);color:var(--v3-t3);font-size:11.5px;line-height:18px}",
|
|
3358
|
+
".v3-notebox b{color:var(--v3-t2);font-weight:600}",
|
|
3359
|
+
".v3-sw{position:relative;width:34px;height:19px;border:0;border-radius:999px;padding:0;cursor:pointer;flex:none;background:color-mix(in srgb,var(--dsw-alias-label-primary,CanvasText) 16%,transparent);transition:background .16s}",
|
|
3360
|
+
".v3-sw::after{content:'';position:absolute;top:2px;left:2px;width:15px;height:15px;border-radius:50%;background:var(--dsw-alias-switch-thumb,var(--v3-bg));transition:transform .16s}",
|
|
3361
|
+
".v3-sw[aria-checked='true']{background:var(--v3-ok)}",
|
|
3362
|
+
".v3-sw[aria-checked='true']::after{transform:translateX(15px)}",
|
|
3363
|
+
".v3-sw:disabled{opacity:.55;cursor:default}",
|
|
3364
|
+
".v3-sw:focus-visible{outline:2px solid var(--v3-info);outline-offset:2px}",
|
|
3365
|
+
".v3-catgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:12px}",
|
|
3366
|
+
".v3-catstat{display:flex;background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);overflow:hidden;margin-bottom:12px}",
|
|
3367
|
+
".v3-catstat>div{flex:1 1 0;padding:11px 14px;min-width:0}",
|
|
3368
|
+
".v3-catstat>div+div{border-left:1px solid var(--v3-line)}",
|
|
3369
|
+
".v3-catstat .v3-l{font-size:10.5px;color:var(--v3-t4);letter-spacing:.05em;text-transform:uppercase}",
|
|
3370
|
+
".v3-catstat .v3-v{font-size:19px;font-weight:650;margin-top:2px;letter-spacing:-.02em;font-variant-numeric:tabular-nums}",
|
|
3371
|
+
".v3-catstat .v3-v small{font-size:11px;font-weight:400;color:var(--v3-t3);margin-left:4px;letter-spacing:0}",
|
|
3372
|
+
".v3-group{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-md);overflow:hidden;margin-bottom:10px;min-width:0}",
|
|
3373
|
+
".v3-group:last-child{margin-bottom:0}",
|
|
3374
|
+
".v3-group>summary{display:flex;align-items:center;gap:8px;padding:9px 12px;cursor:pointer;list-style:none;min-height:36px;flex-wrap:wrap}",
|
|
3375
|
+
".v3-group>summary::-webkit-details-marker{display:none}",
|
|
3376
|
+
".v3-group>summary:hover{background:var(--v3-panel-2)}",
|
|
3377
|
+
".v3-group[open]>summary{border-bottom:1px solid var(--v3-line)}",
|
|
3378
|
+
".v3-group>summary .v3-chev{width:10px;height:10px;color:var(--v3-t4);flex:none;transition:transform .14s}",
|
|
3379
|
+
".v3-group[open]>summary .v3-chev{transform:rotate(90deg)}",
|
|
3380
|
+
".v3-group>summary .v3-gnm{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}",
|
|
3381
|
+
".v3-group>summary .v3-cnt{font-size:10.5px;color:var(--v3-t4);flex:none}",
|
|
3382
|
+
".v3-group>summary .v3-right{margin-left:auto;display:flex;gap:4px;align-items:center;flex:none}",
|
|
3383
|
+
".v3-trow{display:flex;align-items:center;gap:8px;padding:0 12px 0 29px;min-height:30px;border-top:1px solid var(--v3-line);font-size:12px;min-width:0}",
|
|
3384
|
+
".v3-trow:first-of-type{border-top:0}",
|
|
3385
|
+
".v3-trow:hover{background:var(--v3-panel-2)}",
|
|
3386
|
+
".v3-trow .v3-nm{font-family:var(--v3-font-code);font-size:11.5px;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
3387
|
+
".v3-trow .v3-uses{flex:none;font-size:11px;color:var(--v3-t4);font-variant-numeric:tabular-nums;white-space:nowrap}",
|
|
3388
|
+
".v3-tag{display:inline-flex;align-items:center;gap:4px;height:17px;padding:0 6px;border-radius:4px;font-size:10px;line-height:17px;white-space:nowrap;color:var(--v3-t3);background:var(--v3-panel-2)}",
|
|
3389
|
+
".v3-tag[data-tone='info']{color:var(--v3-info);background:var(--v3-info-dim)}",
|
|
3390
|
+
".v3-tag[data-tone='warn']{color:var(--v3-warn);background:var(--v3-warn-dim)}",
|
|
3391
|
+
".v3-tag[data-tone='bad']{color:var(--v3-bad);background:var(--v3-bad-dim)}",
|
|
3392
|
+
".v3-hint{margin:0;font-size:11px;line-height:17px;color:var(--v3-t4)}",
|
|
3393
|
+
".v3-scopehint{display:flex;align-items:center;gap:7px;margin-top:9px;padding:6px 10px;border-radius:var(--v3-r-sm);background:var(--v3-panel-2);font-size:11.5px;line-height:17px;color:var(--v3-t3);flex-wrap:wrap}",
|
|
3394
|
+
".v3-scopehint .v3-id{font-family:var(--v3-font-code);font-size:11px;color:var(--v3-t2);padding:1px 5px;border-radius:3px;background:var(--v3-panel);border:1px solid var(--v3-line)}",
|
|
3395
|
+
".v3-scopehint[data-tone='warn']{background:var(--v3-warn-dim);color:var(--v3-t2)}",
|
|
3396
|
+
".v3-scan{display:flex;align-items:center;gap:10px;margin-top:9px;font-size:11.5px;color:var(--v3-t3)}",
|
|
3397
|
+
".v3-scan .v3-track{position:relative;flex:1 1 auto;height:5px;border-radius:3px;background:var(--v3-panel-2);overflow:hidden;min-width:70px}",
|
|
3398
|
+
".v3-scan .v3-fill{position:absolute;inset:0 auto 0 0;background:var(--v3-info);border-radius:3px;transition:width .3s ease}",
|
|
3399
|
+
".v3-scan[data-running='true'] .v3-fill{width:34% !important;animation:v3-indet 1.25s ease-in-out infinite}",
|
|
3400
|
+
"@keyframes v3-indet{0%{left:-34%}100%{left:100%}}",
|
|
3401
|
+
".v3-scan .v3-pct{font-variant-numeric:tabular-nums;color:var(--v3-t2);font-weight:600;min-width:38px;text-align:right}",
|
|
3402
|
+
"@container v3 (max-width:980px){.v3-board{grid-template-columns:minmax(0,1fr)}}",
|
|
3403
|
+
"@container v3 (max-width:900px){.v3-gpanel,.v3-catgrid{grid-template-columns:minmax(0,1fr)}}",
|
|
3404
|
+
"@container v3 (max-width:720px){.v3-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.v3-catstat{flex-wrap:wrap}.v3-catstat>div{flex:1 1 45%}.v3-catstat>div+div{border-left:0}}",
|
|
3405
|
+
"@container v3 (max-width:620px){.v3-rank-row .v3-nm{width:104px}}",
|
|
3406
|
+
"@media (prefers-reduced-motion:reduce){.v3-dot[data-tone='run'],.v3-scan[data-running='true'] .v3-fill{animation:none}}",
|
|
3407
|
+
"[hidden]{display:none !important}"
|
|
3408
|
+
].join("");
|
|
3409
|
+
/**
|
|
3410
|
+
* Mount the shared v3 stylesheet once per document.
|
|
3411
|
+
*
|
|
3412
|
+
* React does not dedupe <style> elements across the four pages, and all four
|
|
3413
|
+
* render their own root, so the element carries a marker attribute and an
|
|
3414
|
+
* identical copy in the DOM is harmless. Kept as an element (not a singleton
|
|
3415
|
+
* side effect at import time) so the hook-free views stay pure and the test
|
|
3416
|
+
* suite can still walk the tree.
|
|
3417
|
+
*/
|
|
3418
|
+
function V3Styles() {
|
|
3419
|
+
return (0, react.createElement)("style", { "data-v3-style": "true" }, CSS$1);
|
|
3420
|
+
}
|
|
3231
3421
|
/**
|
|
3232
3422
|
* dsh's source labels for a project's own skill roots — the `project`-prefixed
|
|
3233
3423
|
* providers of `@deepseek-ai/dsh-skill-filesystem`, which scan
|
|
@@ -3717,6 +3907,42 @@ body[data-ds-dark-theme] .mc-count--disabled{color:#b8abad}
|
|
|
3717
3907
|
.mc-guard-switch--on{background:var(--dsw-alias-state-business-primary)}
|
|
3718
3908
|
.mc-guard-switch--on::after{transform:translateX(14px)}
|
|
3719
3909
|
.mc-guard-switch:disabled{opacity:.5;cursor:not-allowed}
|
|
3910
|
+
/* ---- v3 工具与技能(V3-c):仅新增,不改动上面任何 .mc-* / .mc-guard-* ---- */
|
|
3911
|
+
.v3-dot[data-tone='info']{background:var(--v3-info)}
|
|
3912
|
+
.v3-trow{cursor:pointer}
|
|
3913
|
+
.v3-trow:focus-visible{outline:2px solid var(--v3-info);outline-offset:-2px}
|
|
3914
|
+
.v3-racts{display:flex;align-items:center;gap:4px;margin-left:auto;flex:none}
|
|
3915
|
+
.v3-uses-bad{color:var(--v3-bad)}
|
|
3916
|
+
button.v3-tag.v3-tag--act{color:var(--v3-t3);background:transparent}
|
|
3917
|
+
button.v3-tag.v3-tag--act:hover{color:var(--v3-t1);background:var(--v3-panel-2)}
|
|
3918
|
+
button.v3-tag{border:0;cursor:pointer;font-family:inherit}
|
|
3919
|
+
button.v3-tag:disabled{opacity:.55;cursor:default}
|
|
3920
|
+
.v3-group>summary:focus-visible{outline:2px solid var(--v3-info);outline-offset:-2px}
|
|
3921
|
+
.v3-tools{display:flex;flex-direction:column}
|
|
3922
|
+
/* ---- v3 catalog rework: 一行工具栏 / 能力诊断卡片 / 卡片向总览看齐 ---- */
|
|
3923
|
+
.mc-cat-root{display:flex;flex-direction:column;gap:12px;min-width:0}
|
|
3924
|
+
.mc-cat-root .v3-catstat{margin-bottom:0}
|
|
3925
|
+
.mc-cat-bar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;min-width:0}
|
|
3926
|
+
.mc-cat-bar>.mc-cat-actions{flex:none}
|
|
3927
|
+
.mc-cat-bar .mc-cat-actions button{font-size:11px;white-space:nowrap}
|
|
3928
|
+
.mc-cat-tabs{display:flex;align-items:center;flex:none;min-width:0}
|
|
3929
|
+
.mc-cat-tabs .mc-main-tabs{border-bottom:0}
|
|
3930
|
+
.mc-cat-search{flex:1 1 auto;width:auto;min-width:96px;max-width:220px;display:flex}
|
|
3931
|
+
.mc-cat-search input{box-sizing:border-box;width:100%;height:28px;padding:0 9px;border:1px solid var(--v3-line-2);border-radius:var(--v3-r-sm);background:var(--v3-panel);color:var(--v3-t1);font:inherit;font-size:12.5px}
|
|
3932
|
+
.mc-cat-search input:hover{border-color:var(--v3-line-2)}
|
|
3933
|
+
.mc-cat-search input:focus-visible{outline:2px solid var(--v3-info);outline-offset:1px;border-color:transparent}
|
|
3934
|
+
.mc-cat-actions{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto;flex-wrap:nowrap}
|
|
3935
|
+
.mc-cat-diag{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);overflow:hidden}
|
|
3936
|
+
.mc-cat-diag>summary{display:flex;align-items:center;gap:8px;padding:11px 15px;min-height:38px;cursor:pointer;list-style:none;font-size:12.5px;font-weight:600;color:var(--v3-t2)}
|
|
3937
|
+
.mc-cat-diag>summary::-webkit-details-marker{display:none}
|
|
3938
|
+
.mc-cat-diag>summary:hover{background:var(--v3-panel-2)}
|
|
3939
|
+
.mc-cat-diag[open]>summary{border-bottom:1px solid var(--v3-line)}
|
|
3940
|
+
.mc-cat-diag-body{display:flex;flex-direction:column;gap:4px;padding:11px 15px;font-size:11.5px;line-height:18px;color:var(--v3-t3)}
|
|
3941
|
+
.v3-root .v3-catgrid{gap:14px}
|
|
3942
|
+
.v3-root .v3-group{border-radius:var(--v3-r-lg);margin-bottom:0}
|
|
3943
|
+
.v3-root .v3-group>summary{padding:11px 15px;min-height:38px}
|
|
3944
|
+
.v3-root .v3-tag{font-size:11px;height:19px;line-height:19px;padding:0 7px}
|
|
3945
|
+
.v3-root .v3-trow{min-height:34px;font-size:12.5px}
|
|
3720
3946
|
`;
|
|
3721
3947
|
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
|
|
3722
3948
|
const tag = document.createElement("style");
|
|
@@ -3789,14 +4015,6 @@ function MarkdownFilePreview(props) {
|
|
|
3789
4015
|
})] });
|
|
3790
4016
|
}
|
|
3791
4017
|
/**
|
|
3792
|
-
* Which build of the plugin is on screen. Several rounds of "is my profile
|
|
3793
|
-
* running the version I just installed?" had to be answered by grepping
|
|
3794
|
-
* `node_modules`, because the panel said nothing about itself. The value is
|
|
3795
|
-
* inlined by `tsdown` (see `src/client/env.d.ts`).
|
|
3796
|
-
*/
|
|
3797
|
-
const PLUGIN_VERSION = "0.1.20";
|
|
3798
|
-
const PLUGIN_TITLE = `capability-menu v${PLUGIN_VERSION}`;
|
|
3799
|
-
/**
|
|
3800
4018
|
* dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
|
|
3801
4019
|
* there no 编辑 button" has a visible answer. Labels that name one directory are
|
|
3802
4020
|
* shown as that path; the rest name a category and go through the dictionary, so
|
|
@@ -3821,6 +4039,200 @@ function sourceLabel(source, t) {
|
|
|
3821
4039
|
const key = SOURCE_LABEL_KEYS[source];
|
|
3822
4040
|
return key === void 0 ? source : t(key);
|
|
3823
4041
|
}
|
|
4042
|
+
/** Tone of a class chip / state dot, mapped to the v3 `data-tone` palette. */
|
|
4043
|
+
const CLASS_TONE = {
|
|
4044
|
+
resident: "info",
|
|
4045
|
+
"on-demand": "warn",
|
|
4046
|
+
disabled: "bad"
|
|
4047
|
+
};
|
|
4048
|
+
/** State-dot tone inside a row: resident=info, on-demand=warn, disabled=neutral. */
|
|
4049
|
+
const CLASS_DOT_TONE = {
|
|
4050
|
+
resident: "info",
|
|
4051
|
+
"on-demand": "warn",
|
|
4052
|
+
disabled: ""
|
|
4053
|
+
};
|
|
4054
|
+
/** The four-cell `.v3-catstat` strip at the top of the catalog page. */
|
|
4055
|
+
function CatalogStatStrip(props) {
|
|
4056
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4057
|
+
className: "v3-catstat",
|
|
4058
|
+
"data-v3-catstat": "true",
|
|
4059
|
+
children: props.cells.map((cell, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4060
|
+
className: "v3-l",
|
|
4061
|
+
children: cell.label
|
|
4062
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4063
|
+
className: "v3-v",
|
|
4064
|
+
children: [cell.value, cell.hint !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: cell.hint }) : null]
|
|
4065
|
+
})] }, index))
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
/** Zero-call capability count when a usage map exists, else an honest dash. */
|
|
4069
|
+
function zeroCallCount(usage, rows) {
|
|
4070
|
+
if (usage === void 0) return "—";
|
|
4071
|
+
return rows.filter((row) => {
|
|
4072
|
+
const entry = usage.get(row.id);
|
|
4073
|
+
return entry === void 0 || entry.uses === 0;
|
|
4074
|
+
}).length;
|
|
4075
|
+
}
|
|
4076
|
+
/** Strip a tool’s mcp__<server>__ prefix for display, like the old row did. */
|
|
4077
|
+
function displayToolName(server, name) {
|
|
4078
|
+
const prefix = "mcp__" + server + "__";
|
|
4079
|
+
return server !== "built-in" && name.startsWith(prefix) ? name.slice(prefix.length) : name;
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* Two-column `.v3-catgrid` of native `<details>` server groups, each summary
|
|
4083
|
+
* carrying the chevron and the class-count tags, each row a 30px `.v3-trow`.
|
|
4084
|
+
* Pure: every interaction is a callback.
|
|
4085
|
+
*/
|
|
4086
|
+
function ToolGroupsView(props) {
|
|
4087
|
+
const { t, groups, openServers, busy, usage } = props;
|
|
4088
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4089
|
+
className: "v3-catgrid",
|
|
4090
|
+
"data-v3-catgrid": "true",
|
|
4091
|
+
children: groups.map(({ server, tools }) => {
|
|
4092
|
+
const open = openServers.has(server);
|
|
4093
|
+
const counts = CLASS_KEYS.map((cls) => ({
|
|
4094
|
+
cls,
|
|
4095
|
+
count: countByClass(tools, cls)
|
|
4096
|
+
}));
|
|
4097
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
4098
|
+
className: "v3-group",
|
|
4099
|
+
open,
|
|
4100
|
+
"data-v3-group": server,
|
|
4101
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
4102
|
+
onClick: (e) => {
|
|
4103
|
+
e.preventDefault();
|
|
4104
|
+
props.onToggle(server);
|
|
4105
|
+
},
|
|
4106
|
+
children: [
|
|
4107
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4108
|
+
className: "v3-chev",
|
|
4109
|
+
viewBox: "0 0 12 12",
|
|
4110
|
+
fill: "none",
|
|
4111
|
+
"aria-hidden": "true",
|
|
4112
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4113
|
+
d: "M4.5 2.5 8 6l-3.5 3.5",
|
|
4114
|
+
stroke: "currentColor",
|
|
4115
|
+
strokeWidth: 1.4,
|
|
4116
|
+
strokeLinecap: "round",
|
|
4117
|
+
strokeLinejoin: "round"
|
|
4118
|
+
})
|
|
4119
|
+
}),
|
|
4120
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4121
|
+
className: "v3-gnm",
|
|
4122
|
+
children: server === "built-in" ? t("builtInGroup") : server
|
|
4123
|
+
}),
|
|
4124
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4125
|
+
className: "v3-cnt",
|
|
4126
|
+
children: tools.length
|
|
4127
|
+
}),
|
|
4128
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4129
|
+
className: "v3-right",
|
|
4130
|
+
children: [counts.filter(({ count }) => count > 0).map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4131
|
+
type: "button",
|
|
4132
|
+
className: "v3-tag",
|
|
4133
|
+
"data-tone": CLASS_TONE[cls],
|
|
4134
|
+
disabled: busy,
|
|
4135
|
+
title: t("cycleHint"),
|
|
4136
|
+
onClick: (e) => {
|
|
4137
|
+
e.stopPropagation();
|
|
4138
|
+
props.onCycle(tools.filter((tool) => tool.class === cls).map((tool) => tool.id), "tool");
|
|
4139
|
+
},
|
|
4140
|
+
children: [
|
|
4141
|
+
t(CLASS_SHORT_KEYS[cls]),
|
|
4142
|
+
" ",
|
|
4143
|
+
count
|
|
4144
|
+
]
|
|
4145
|
+
}, cls)), server !== "built-in" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4146
|
+
type: "button",
|
|
4147
|
+
className: "v3-tag v3-tag--act",
|
|
4148
|
+
disabled: busy,
|
|
4149
|
+
onClick: (e) => {
|
|
4150
|
+
e.stopPropagation();
|
|
4151
|
+
props.onEditServer(server);
|
|
4152
|
+
},
|
|
4153
|
+
children: t("edit")
|
|
4154
|
+
})]
|
|
4155
|
+
})
|
|
4156
|
+
]
|
|
4157
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4158
|
+
className: "v3-tools",
|
|
4159
|
+
children: tools.map((tool) => {
|
|
4160
|
+
const rowUsage = usage === void 0 ? void 0 : usage.get(tool.id);
|
|
4161
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4162
|
+
className: "v3-trow",
|
|
4163
|
+
title: tool.classLabel,
|
|
4164
|
+
role: "button",
|
|
4165
|
+
tabIndex: 0,
|
|
4166
|
+
"aria-label": tool.name,
|
|
4167
|
+
onClick: () => props.onOpenDetail(tool.id),
|
|
4168
|
+
onKeyDown: (e) => {
|
|
4169
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4170
|
+
e.preventDefault();
|
|
4171
|
+
props.onOpenDetail(tool.id);
|
|
4172
|
+
}
|
|
4173
|
+
},
|
|
4174
|
+
children: [
|
|
4175
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4176
|
+
className: "v3-dot",
|
|
4177
|
+
"data-tone": CLASS_DOT_TONE[tool.class],
|
|
4178
|
+
"aria-hidden": "true"
|
|
4179
|
+
}),
|
|
4180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4181
|
+
className: "v3-nm",
|
|
4182
|
+
title: tool.name,
|
|
4183
|
+
children: displayToolName(server, tool.name)
|
|
4184
|
+
}),
|
|
4185
|
+
rowUsage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4186
|
+
className: "v3-uses",
|
|
4187
|
+
children: [
|
|
4188
|
+
rowUsage.uses,
|
|
4189
|
+
" 次",
|
|
4190
|
+
rowUsage.failures !== void 0 && rowUsage.failures > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4191
|
+
className: "v3-uses-bad",
|
|
4192
|
+
children: [" · 失败 ", rowUsage.failures]
|
|
4193
|
+
}) : null
|
|
4194
|
+
]
|
|
4195
|
+
}),
|
|
4196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4197
|
+
className: "v3-racts",
|
|
4198
|
+
children: [
|
|
4199
|
+
tool.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4200
|
+
className: "v3-tag",
|
|
4201
|
+
children: t("mandatory")
|
|
4202
|
+
}),
|
|
4203
|
+
tool.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4204
|
+
type: "button",
|
|
4205
|
+
className: "v3-tag v3-tag--act",
|
|
4206
|
+
title: t("editSearchMetadata"),
|
|
4207
|
+
onClick: (e) => {
|
|
4208
|
+
e.stopPropagation();
|
|
4209
|
+
props.onEditSearch(tool.id);
|
|
4210
|
+
},
|
|
4211
|
+
children: t("searchMetadataButton")
|
|
4212
|
+
}),
|
|
4213
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4214
|
+
type: "button",
|
|
4215
|
+
className: "v3-tag",
|
|
4216
|
+
"data-tone": CLASS_TONE[tool.class],
|
|
4217
|
+
disabled: busy || tool.mandatory,
|
|
4218
|
+
title: tool.classLabel,
|
|
4219
|
+
"aria-label": tool.classLabel,
|
|
4220
|
+
onClick: (e) => {
|
|
4221
|
+
e.stopPropagation();
|
|
4222
|
+
props.onCycle([tool.id], "tool");
|
|
4223
|
+
},
|
|
4224
|
+
children: t(CLASS_SHORT_KEYS[tool.class])
|
|
4225
|
+
})
|
|
4226
|
+
]
|
|
4227
|
+
})
|
|
4228
|
+
]
|
|
4229
|
+
}, tool.id);
|
|
4230
|
+
})
|
|
4231
|
+
})]
|
|
4232
|
+
}, server);
|
|
4233
|
+
})
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
3824
4236
|
/**
|
|
3825
4237
|
* Whether the document is visible. A hidden settings tab does no polling and
|
|
3826
4238
|
* takes no re-reads; coming back is itself a revalidation trigger.
|
|
@@ -3836,7 +4248,7 @@ function useDocumentVisible() {
|
|
|
3836
4248
|
return visible;
|
|
3837
4249
|
}
|
|
3838
4250
|
function CapabilitySection(props) {
|
|
3839
|
-
const { remote, t, mountError, subscribeSignals } = props;
|
|
4251
|
+
const { remote, t, mountError, subscribeSignals, usage } = props;
|
|
3840
4252
|
const [state, setState] = (0, react.useState)(() => {
|
|
3841
4253
|
const cached = cachedSnapshot();
|
|
3842
4254
|
return cached === void 0 ? { status: "loading" } : {
|
|
@@ -4169,13 +4581,14 @@ function CapabilitySection(props) {
|
|
|
4169
4581
|
onCycle: cycleClass,
|
|
4170
4582
|
onRefresh: () => void refreshCatalog(),
|
|
4171
4583
|
onRefreshMcp: (serverName) => void refreshRegisteredMcp(serverName),
|
|
4172
|
-
onNotice: setNotice
|
|
4584
|
+
onNotice: setNotice,
|
|
4585
|
+
...usage === void 0 ? {} : { usage }
|
|
4173
4586
|
})
|
|
4174
4587
|
]
|
|
4175
4588
|
});
|
|
4176
4589
|
}
|
|
4177
4590
|
function ReadyBody(props) {
|
|
4178
|
-
const { remote, snapshot, diagnostics, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, onRefreshMcp, onNotice } = props;
|
|
4591
|
+
const { remote, snapshot, diagnostics, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, onRefreshMcp, onNotice, usage } = props;
|
|
4179
4592
|
const [updateStatus, setUpdateStatus] = (0, react.useState)(null);
|
|
4180
4593
|
const [updatePhase, setUpdatePhase] = (0, react.useState)("idle");
|
|
4181
4594
|
const [updateError, setUpdateError] = (0, react.useState)("");
|
|
@@ -4463,347 +4876,204 @@ function ReadyBody(props) {
|
|
|
4463
4876
|
t
|
|
4464
4877
|
]);
|
|
4465
4878
|
const skillDiagnosticCount = diagnostics === null ? 0 : diagnostics.unavailableSkills.length + diagnostics.shadowedSkills.length + diagnostics.brokenPresets.length + diagnostics.unbrowseableSkills.length + diagnostics.undocumentedSkills.length;
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4879
|
+
const catalogUsage = usage;
|
|
4880
|
+
const classCount = (cls) => summary.find((entry) => entry.cls === cls)?.count ?? 0;
|
|
4881
|
+
const statCells = [
|
|
4882
|
+
{
|
|
4883
|
+
label: t("tierResident"),
|
|
4884
|
+
value: classCount("resident")
|
|
4885
|
+
},
|
|
4886
|
+
{
|
|
4887
|
+
label: t("tierOnDemand"),
|
|
4888
|
+
value: classCount("on-demand")
|
|
4889
|
+
},
|
|
4890
|
+
{
|
|
4891
|
+
label: t("tierDisabled"),
|
|
4892
|
+
value: classCount("disabled")
|
|
4893
|
+
},
|
|
4894
|
+
{
|
|
4895
|
+
label: t("catalogZeroCall"),
|
|
4896
|
+
value: zeroCallCount(catalogUsage, statRows),
|
|
4897
|
+
hint: t("catalogZeroCallHint")
|
|
4898
|
+
}
|
|
4899
|
+
];
|
|
4900
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4901
|
+
className: "v3-root mc-cat-root",
|
|
4902
|
+
children: [
|
|
4903
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(V3Styles, {}),
|
|
4904
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4905
|
+
className: "mc-cat-bar",
|
|
4906
|
+
"data-v3-catalog-bar": "true",
|
|
4494
4907
|
children: [
|
|
4495
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4909
|
+
className: "mc-cat-tabs",
|
|
4910
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4911
|
+
className: "mc-main-tabs",
|
|
4912
|
+
items: [{
|
|
4913
|
+
value: "tools",
|
|
4914
|
+
id: "mc-tab-tools",
|
|
4915
|
+
panelId: "mc-panel-tools",
|
|
4916
|
+
label: t("toolsGroup")
|
|
4917
|
+
}, {
|
|
4918
|
+
value: "skills",
|
|
4919
|
+
id: "mc-tab-skills",
|
|
4920
|
+
panelId: "mc-panel-skills",
|
|
4921
|
+
label: t("skillsGroup")
|
|
4922
|
+
}],
|
|
4923
|
+
value: activeTab,
|
|
4924
|
+
onChange: onTabChange,
|
|
4925
|
+
label: t("title")
|
|
4926
|
+
})
|
|
4500
4927
|
}),
|
|
4501
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4928
|
+
snapshot.rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4929
|
+
className: "mc-cat-search",
|
|
4930
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4931
|
+
type: "search",
|
|
4932
|
+
value: filter,
|
|
4933
|
+
placeholder: t("filterByName"),
|
|
4934
|
+
"aria-label": t("filterByName"),
|
|
4935
|
+
title: t("filterHint"),
|
|
4936
|
+
onChange: (e) => setFilter(e.target.value)
|
|
4937
|
+
})
|
|
4506
4938
|
}),
|
|
4507
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4508
|
-
className: "mc-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4939
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4940
|
+
className: "mc-cat-actions",
|
|
4941
|
+
children: [
|
|
4942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4943
|
+
size: "sm",
|
|
4944
|
+
variant: "outline",
|
|
4945
|
+
onClick: () => setRegisterOpen(true),
|
|
4946
|
+
children: t("registerCapability")
|
|
4947
|
+
}),
|
|
4948
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4949
|
+
size: "sm",
|
|
4950
|
+
variant: "outline",
|
|
4951
|
+
onClick: () => void openCatalogDocs(),
|
|
4952
|
+
children: t("viewCatalog")
|
|
4953
|
+
}),
|
|
4954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4955
|
+
type: "button",
|
|
4956
|
+
size: "sm",
|
|
4957
|
+
variant: "outline",
|
|
4958
|
+
onClick: () => window.open("https://github.com/PKUfudawei/dsh-capability-menu", "_blank", "noopener,noreferrer"),
|
|
4959
|
+
children: t("feedback")
|
|
4960
|
+
}),
|
|
4961
|
+
updateStatus?.updateAvailable && (updateStatus.updatable || updateStatus.reason === "local") && updatePhase !== "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4962
|
+
type: "button",
|
|
4963
|
+
variant: "primary",
|
|
4964
|
+
size: "sm",
|
|
4965
|
+
className: "mc-update-check",
|
|
4966
|
+
title: t("updateAvailable", { version: updateStatus.latest }),
|
|
4967
|
+
disabled: updatePhase === "installing",
|
|
4968
|
+
onClick: () => setUpdateDialogOpen(true),
|
|
4969
|
+
children: t("newVersion")
|
|
4970
|
+
})
|
|
4971
|
+
]
|
|
4514
4972
|
})
|
|
4515
4973
|
]
|
|
4516
|
-
})
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
className: "mc-diagnostics",
|
|
4529
|
-
children: [
|
|
4530
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("diagnosticTitle", { count: skillDiagnosticCount }) }),
|
|
4531
|
-
diagnostics.unavailableSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnavailable", {
|
|
4532
|
-
name: item.name,
|
|
4533
|
-
source: item.source,
|
|
4534
|
-
preset: item.preset ?? ""
|
|
4535
|
-
}) }, `unavailable-${item.name}-${item.preset ?? ""}-${index}`)),
|
|
4536
|
-
diagnostics.shadowedSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticShadowed", {
|
|
4537
|
-
name: item.name,
|
|
4538
|
-
winner: item.selectedFrom,
|
|
4539
|
-
hidden: item.shadowedFrom
|
|
4540
|
-
}) }, `shadowed-${item.name}-${item.shadowedFrom}-${index}`)),
|
|
4541
|
-
diagnostics.brokenPresets.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticBroken", {
|
|
4542
|
-
id: item.id,
|
|
4543
|
-
reason: item.reason
|
|
4544
|
-
}) }, `broken-${item.id}`)),
|
|
4545
|
-
diagnostics.unbrowseableSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnbrowseable", {
|
|
4546
|
-
name: item.name,
|
|
4547
|
-
reason: item.reason
|
|
4548
|
-
}) }, `browse-${item.name}`)),
|
|
4549
|
-
diagnostics.undocumentedSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUndocumented", {
|
|
4550
|
-
name: item.name,
|
|
4551
|
-
summary: item.summary
|
|
4552
|
-
}) }, `undocumented-${item.name}`))
|
|
4553
|
-
]
|
|
4554
|
-
}),
|
|
4555
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4556
|
-
className: "mc-tabs",
|
|
4557
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4558
|
-
className: "mc-main-tabs",
|
|
4559
|
-
items: [{
|
|
4560
|
-
value: "tools",
|
|
4561
|
-
id: "mc-tab-tools",
|
|
4562
|
-
panelId: "mc-panel-tools",
|
|
4563
|
-
label: t("toolsGroup")
|
|
4564
|
-
}, {
|
|
4565
|
-
value: "skills",
|
|
4566
|
-
id: "mc-tab-skills",
|
|
4567
|
-
panelId: "mc-panel-skills",
|
|
4568
|
-
label: t("skillsGroup")
|
|
4569
|
-
}],
|
|
4570
|
-
value: activeTab,
|
|
4571
|
-
onChange: onTabChange,
|
|
4572
|
-
label: t("title")
|
|
4573
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4574
|
-
className: "mc-summary",
|
|
4575
|
-
children: summary.map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4576
|
-
className: `mc-chip mc-chip--${cls}`,
|
|
4974
|
+
}),
|
|
4975
|
+
(updatePhase === "done" || updateStatus?.updateAvailable && !updateStatus.updatable && updateStatus.reason !== "local") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4976
|
+
className: "mc-update-status",
|
|
4977
|
+
role: "status",
|
|
4978
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: updatePhase === "done" ? t(updateStatus?.reason === "local" ? "pluginSwitched" : "pluginUpdated") : t("updateAvailable", { version: updateStatus?.latest ?? "" }) }), updatePhase !== "done" && updateStatus?.updateAvailable && !updateStatus.updatable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(updateStatus.reason === "non-registry" ? "pluginNonRegistrySource" : "pluginProfileUnavailable") })]
|
|
4979
|
+
}),
|
|
4980
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CatalogStatStrip, { cells: statCells }),
|
|
4981
|
+
diagnostics !== null && skillDiagnosticCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
4982
|
+
className: "mc-cat-diag",
|
|
4983
|
+
"data-v3-catalog-diagnostics": "true",
|
|
4984
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("diagnosticTitle", { count: skillDiagnosticCount }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4985
|
+
className: "mc-cat-diag-body",
|
|
4577
4986
|
children: [
|
|
4578
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
"
|
|
4584
|
-
|
|
4987
|
+
diagnostics.unavailableSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnavailable", {
|
|
4988
|
+
name: item.name,
|
|
4989
|
+
source: item.source,
|
|
4990
|
+
preset: item.preset ?? ""
|
|
4991
|
+
}) }, `unavailable-${item.name}-${item.preset ?? ""}-${index}`)),
|
|
4992
|
+
diagnostics.shadowedSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticShadowed", {
|
|
4993
|
+
name: item.name,
|
|
4994
|
+
winner: item.selectedFrom,
|
|
4995
|
+
hidden: item.shadowedFrom
|
|
4996
|
+
}) }, `shadowed-${item.name}-${item.shadowedFrom}-${index}`)),
|
|
4997
|
+
diagnostics.brokenPresets.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticBroken", {
|
|
4998
|
+
id: item.id,
|
|
4999
|
+
reason: item.reason
|
|
5000
|
+
}) }, `broken-${item.id}`)),
|
|
5001
|
+
diagnostics.unbrowseableSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnbrowseable", {
|
|
5002
|
+
name: item.name,
|
|
5003
|
+
reason: item.reason
|
|
5004
|
+
}) }, `browse-${item.name}`)),
|
|
5005
|
+
diagnostics.undocumentedSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUndocumented", {
|
|
5006
|
+
name: item.name,
|
|
5007
|
+
summary: item.summary
|
|
5008
|
+
}) }, `undocumented-${item.name}`))
|
|
4585
5009
|
]
|
|
4586
|
-
}
|
|
4587
|
-
})
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
children: needle === "" ? t("emptyTools") : t("filterNoMatch")
|
|
4611
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: servers.map(({ server, tools }) => {
|
|
4612
|
-
const open = openServers.has(server);
|
|
4613
|
-
const counts = CLASS_KEYS.map((cls) => ({
|
|
4614
|
-
cls,
|
|
4615
|
-
count: countByClass(tools, cls)
|
|
4616
|
-
}));
|
|
4617
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4618
|
-
className: "mc-group",
|
|
4619
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4620
|
-
className: "mc-group-header",
|
|
4621
|
-
role: "button",
|
|
4622
|
-
tabIndex: 0,
|
|
4623
|
-
"aria-expanded": open,
|
|
4624
|
-
onClick: () => onToggleServer(server),
|
|
4625
|
-
onKeyDown: (e) => {
|
|
4626
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
4627
|
-
e.preventDefault();
|
|
4628
|
-
onToggleServer(server);
|
|
4629
|
-
}
|
|
4630
|
-
},
|
|
4631
|
-
children: [
|
|
4632
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTriangleRightFill, {
|
|
4633
|
-
size: 12,
|
|
4634
|
-
className: `mc-chevron${open ? " mc-chevron--open" : ""}`
|
|
4635
|
-
}),
|
|
4636
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4637
|
-
className: "mc-server-name",
|
|
4638
|
-
children: server === "built-in" ? t("builtInGroup") : server
|
|
4639
|
-
}),
|
|
4640
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4641
|
-
className: "mc-server-count",
|
|
4642
|
-
children: t("toolCount", { count: tools.length })
|
|
4643
|
-
}),
|
|
4644
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4645
|
-
className: "mc-server-meta",
|
|
4646
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4647
|
-
className: "mc-counts",
|
|
4648
|
-
children: counts.filter(({ count }) => count > 0).map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4649
|
-
type: "button",
|
|
4650
|
-
className: `mc-count mc-count--${cls}`,
|
|
4651
|
-
disabled: busy,
|
|
4652
|
-
title: t("cycleHint"),
|
|
4653
|
-
onClick: (e) => {
|
|
4654
|
-
e.stopPropagation();
|
|
4655
|
-
onCycle(tools.filter((t) => t.class === cls).map((t) => t.id), "tool");
|
|
4656
|
-
},
|
|
4657
|
-
children: [
|
|
4658
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4659
|
-
className: `mc-dot mc-dot--${cls}`,
|
|
4660
|
-
"aria-hidden": "true"
|
|
4661
|
-
}),
|
|
4662
|
-
t(CLASS_SHORT_KEYS[cls]),
|
|
4663
|
-
" ",
|
|
4664
|
-
count
|
|
4665
|
-
]
|
|
4666
|
-
}, cls))
|
|
4667
|
-
}), server !== "built-in" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4668
|
-
type: "button",
|
|
4669
|
-
className: "mc-count mc-count--action",
|
|
4670
|
-
disabled: busy,
|
|
4671
|
-
onClick: (e) => {
|
|
4672
|
-
e.stopPropagation();
|
|
4673
|
-
openMcpEdit(server);
|
|
4674
|
-
},
|
|
4675
|
-
children: t("edit")
|
|
4676
|
-
})]
|
|
4677
|
-
})
|
|
4678
|
-
]
|
|
4679
|
-
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4680
|
-
className: "mc-tools",
|
|
4681
|
-
children: tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4682
|
-
className: "mc-tool",
|
|
4683
|
-
title: tool.classLabel,
|
|
4684
|
-
role: "button",
|
|
4685
|
-
tabIndex: 0,
|
|
4686
|
-
"aria-label": tool.name,
|
|
4687
|
-
onClick: () => void openToolDetail(tool.id),
|
|
4688
|
-
onKeyDown: (e) => {
|
|
4689
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
4690
|
-
e.preventDefault();
|
|
4691
|
-
openToolDetail(tool.id);
|
|
4692
|
-
}
|
|
4693
|
-
},
|
|
4694
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4695
|
-
className: "mc-tool-name",
|
|
4696
|
-
title: tool.name,
|
|
4697
|
-
children: server !== "built-in" && tool.name.startsWith(`mcp__${server}__`) ? tool.name.slice(`mcp__${server}__`.length) : tool.name
|
|
4698
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4699
|
-
className: "mc-tool-meta",
|
|
4700
|
-
children: [
|
|
4701
|
-
tool.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("mandatory") }),
|
|
4702
|
-
tool.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4703
|
-
size: "sm",
|
|
4704
|
-
variant: "ghost",
|
|
4705
|
-
className: "mc-count--action",
|
|
4706
|
-
title: t("editSearchMetadata"),
|
|
4707
|
-
onClick: (e) => {
|
|
4708
|
-
e.stopPropagation();
|
|
4709
|
-
openSearchEdit(tool.id, "tool");
|
|
4710
|
-
},
|
|
4711
|
-
children: t("searchMetadataButton")
|
|
4712
|
-
}),
|
|
4713
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4714
|
-
type: "button",
|
|
4715
|
-
className: `mc-dot-btn mc-count--${tool.class}`,
|
|
4716
|
-
disabled: busy || tool.mandatory,
|
|
4717
|
-
title: tool.classLabel,
|
|
4718
|
-
"aria-label": tool.classLabel,
|
|
4719
|
-
onClick: (e) => {
|
|
4720
|
-
e.stopPropagation();
|
|
4721
|
-
onCycle([tool.id], "tool");
|
|
4722
|
-
},
|
|
4723
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4724
|
-
className: `mc-dot mc-dot--${tool.class}`,
|
|
4725
|
-
"aria-hidden": "true"
|
|
4726
|
-
})
|
|
4727
|
-
})
|
|
4728
|
-
]
|
|
4729
|
-
})]
|
|
4730
|
-
}, tool.id))
|
|
4731
|
-
})]
|
|
4732
|
-
}, server);
|
|
4733
|
-
}) })
|
|
4734
|
-
})
|
|
4735
|
-
}),
|
|
4736
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4737
|
-
role: "tabpanel",
|
|
4738
|
-
id: "mc-panel-skills",
|
|
4739
|
-
"aria-labelledby": "mc-tab-skills",
|
|
4740
|
-
hidden: activeTab !== "skills",
|
|
4741
|
-
className: `mc-panel${skills.length > 0 ? " mc-panel--tight" : ""}`,
|
|
4742
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4743
|
-
className: "mc-panel-inner",
|
|
4744
|
-
children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4745
|
-
className: "mc-empty",
|
|
4746
|
-
children: needle === "" ? t("emptySkills") : t("filterNoMatch")
|
|
4747
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4748
|
-
className: "mc-subtabs",
|
|
4749
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4750
|
-
items: [
|
|
4751
|
-
{
|
|
4752
|
-
value: "global",
|
|
4753
|
-
id: "mc-skill-tab-global",
|
|
4754
|
-
panelId: "mc-skill-panel-global",
|
|
4755
|
-
label: t("globalSkills")
|
|
4756
|
-
},
|
|
4757
|
-
{
|
|
4758
|
-
value: "project",
|
|
4759
|
-
id: "mc-skill-tab-project",
|
|
4760
|
-
panelId: "mc-skill-panel-project",
|
|
4761
|
-
label: t("projectSkills")
|
|
4762
|
-
},
|
|
4763
|
-
{
|
|
4764
|
-
value: "preset",
|
|
4765
|
-
id: "mc-skill-tab-preset",
|
|
4766
|
-
panelId: "mc-skill-panel-preset",
|
|
4767
|
-
label: t("presetSkills")
|
|
4768
|
-
}
|
|
4769
|
-
],
|
|
4770
|
-
value: skillTab,
|
|
4771
|
-
onChange: setSkillTab,
|
|
4772
|
-
label: t("skillsGroup")
|
|
5010
|
+
})]
|
|
5011
|
+
}),
|
|
5012
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5013
|
+
role: "tabpanel",
|
|
5014
|
+
id: "mc-panel-tools",
|
|
5015
|
+
"aria-labelledby": "mc-tab-tools",
|
|
5016
|
+
hidden: activeTab !== "tools",
|
|
5017
|
+
className: "mc-panel",
|
|
5018
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5019
|
+
className: "mc-panel-inner",
|
|
5020
|
+
children: servers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5021
|
+
className: "mc-empty",
|
|
5022
|
+
children: needle === "" ? t("emptyTools") : t("filterNoMatch")
|
|
5023
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolGroupsView, {
|
|
5024
|
+
t,
|
|
5025
|
+
groups: servers,
|
|
5026
|
+
openServers,
|
|
5027
|
+
busy,
|
|
5028
|
+
usage: catalogUsage,
|
|
5029
|
+
onToggle: onToggleServer,
|
|
5030
|
+
onCycle,
|
|
5031
|
+
onOpenDetail: (id) => void openToolDetail(id),
|
|
5032
|
+
onEditServer: (server) => void openMcpEdit(server),
|
|
5033
|
+
onEditSearch: (id) => void openSearchEdit(id, "tool")
|
|
4773
5034
|
})
|
|
4774
|
-
})
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
5035
|
+
})
|
|
5036
|
+
}),
|
|
5037
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5038
|
+
role: "tabpanel",
|
|
5039
|
+
id: "mc-panel-skills",
|
|
5040
|
+
"aria-labelledby": "mc-tab-skills",
|
|
5041
|
+
hidden: activeTab !== "skills",
|
|
5042
|
+
className: `mc-panel${skills.length > 0 ? " mc-panel--tight" : ""}`,
|
|
5043
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5044
|
+
className: "mc-panel-inner",
|
|
5045
|
+
children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5046
|
+
className: "mc-empty",
|
|
5047
|
+
children: needle === "" ? t("emptySkills") : t("filterNoMatch")
|
|
5048
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5049
|
+
className: "mc-subtabs",
|
|
5050
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
5051
|
+
items: [
|
|
5052
|
+
{
|
|
5053
|
+
value: "global",
|
|
5054
|
+
id: "mc-skill-tab-global",
|
|
5055
|
+
panelId: "mc-skill-panel-global",
|
|
5056
|
+
label: t("globalSkills")
|
|
5057
|
+
},
|
|
5058
|
+
{
|
|
5059
|
+
value: "project",
|
|
5060
|
+
id: "mc-skill-tab-project",
|
|
5061
|
+
panelId: "mc-skill-panel-project",
|
|
5062
|
+
label: t("projectSkills")
|
|
5063
|
+
},
|
|
5064
|
+
{
|
|
5065
|
+
value: "preset",
|
|
5066
|
+
id: "mc-skill-tab-preset",
|
|
5067
|
+
panelId: "mc-skill-panel-preset",
|
|
5068
|
+
label: t("presetSkills")
|
|
5069
|
+
}
|
|
5070
|
+
],
|
|
5071
|
+
value: skillTab,
|
|
5072
|
+
onChange: setSkillTab,
|
|
5073
|
+
label: t("skillsGroup")
|
|
5074
|
+
})
|
|
5075
|
+
}), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
5076
|
+
skills: globalSkills,
|
|
4807
5077
|
remote,
|
|
4808
5078
|
busy,
|
|
4809
5079
|
t,
|
|
@@ -4812,307 +5082,339 @@ function ReadyBody(props) {
|
|
|
4812
5082
|
onEditSkill: openSkillEdit,
|
|
4813
5083
|
onAdoptSkill: adoptSkill,
|
|
4814
5084
|
onEditSearch: (id) => void openSearchEdit(id, "skill")
|
|
4815
|
-
})
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
}
|
|
4852
|
-
}, null, 2)
|
|
4853
|
-
})]
|
|
5085
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5086
|
+
className: "mc-empty",
|
|
5087
|
+
children: t("emptyGlobalSkills")
|
|
5088
|
+
}) : skillTab === "project" ? projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
5089
|
+
skills: projectSkills,
|
|
5090
|
+
remote,
|
|
5091
|
+
busy,
|
|
5092
|
+
t,
|
|
5093
|
+
onCycle,
|
|
5094
|
+
editableSkills,
|
|
5095
|
+
onEditSkill: openSkillEdit,
|
|
5096
|
+
onAdoptSkill: adoptSkill,
|
|
5097
|
+
onEditSearch: (id) => void openSearchEdit(id, "skill")
|
|
5098
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5099
|
+
className: "mc-empty",
|
|
5100
|
+
children: t("emptyProjectSkills")
|
|
5101
|
+
}) : presetGroups.length > 0 ? presetGroups.map(([preset, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5102
|
+
className: "mc-skill-group",
|
|
5103
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5104
|
+
className: "mc-skill-group-title",
|
|
5105
|
+
children: preset
|
|
5106
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
|
|
5107
|
+
skills: group,
|
|
5108
|
+
remote,
|
|
5109
|
+
busy,
|
|
5110
|
+
t,
|
|
5111
|
+
onCycle,
|
|
5112
|
+
editableSkills,
|
|
5113
|
+
onEditSkill: openSkillEdit,
|
|
5114
|
+
onAdoptSkill: adoptSkill,
|
|
5115
|
+
onEditSearch: (id) => void openSearchEdit(id, "skill")
|
|
5116
|
+
})]
|
|
5117
|
+
}, preset)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5118
|
+
className: "mc-empty",
|
|
5119
|
+
children: t("emptyPresetSkills")
|
|
5120
|
+
})] })
|
|
4854
5121
|
})
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
closeLabel: t("previewClose"),
|
|
4862
|
-
className: "mc-update-dialog",
|
|
4863
|
-
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4864
|
-
className: "mc-confirm-actions mc-update-actions",
|
|
5122
|
+
}),
|
|
5123
|
+
toolDetail !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5124
|
+
open: true,
|
|
5125
|
+
onClose: () => setToolDetail(null),
|
|
5126
|
+
title: toolDetail.detail?.name ?? toolDetail.id,
|
|
5127
|
+
closeLabel: t("previewClose"),
|
|
4865
5128
|
children: [
|
|
4866
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
size: "sm",
|
|
4870
|
-
className: "mc-update-cancel",
|
|
4871
|
-
disabled: updatePhase === "installing",
|
|
4872
|
-
onClick: closeUpdateDialog,
|
|
4873
|
-
children: t("cancel")
|
|
5129
|
+
toolDetail.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5130
|
+
className: "mc-preview-hint",
|
|
5131
|
+
children: "…"
|
|
4874
5132
|
}),
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
size: "sm",
|
|
4879
|
-
disabled: updatePhase === "installing",
|
|
4880
|
-
onClick: () => setSourceStepsOpen((value) => !value),
|
|
4881
|
-
children: t(sourceStepsOpen ? "updateSourceStepsHide" : "updateSourceSteps")
|
|
5133
|
+
toolDetail.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5134
|
+
className: "mc-preview-hint",
|
|
5135
|
+
children: toolDetail.message
|
|
4882
5136
|
}),
|
|
4883
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
5137
|
+
toolDetail.status === "ready" && toolDetail.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5138
|
+
className: "mc-preview-body",
|
|
5139
|
+
children: [toolDetail.detail.description.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5140
|
+
className: "mc-tool-description",
|
|
5141
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
|
|
5142
|
+
text: toolDetail.detail.description,
|
|
5143
|
+
t
|
|
5144
|
+
})
|
|
5145
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
5146
|
+
className: "mc-preview-plain",
|
|
5147
|
+
children: JSON.stringify({
|
|
5148
|
+
type: "function",
|
|
5149
|
+
function: {
|
|
5150
|
+
name: toolDetail.detail.name,
|
|
5151
|
+
parameters: toolDetail.detail.parameters
|
|
5152
|
+
}
|
|
5153
|
+
}, null, 2)
|
|
5154
|
+
})]
|
|
4890
5155
|
})
|
|
4891
5156
|
]
|
|
4892
5157
|
}),
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
className: "mc-update-
|
|
5158
|
+
updateDialogOpen && updateStatus?.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5159
|
+
open: true,
|
|
5160
|
+
onClose: closeUpdateDialog,
|
|
5161
|
+
title: t("updateDialogTitle"),
|
|
5162
|
+
closeLabel: t("previewClose"),
|
|
5163
|
+
className: "mc-update-dialog",
|
|
5164
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5165
|
+
className: "mc-confirm-actions mc-update-actions",
|
|
4901
5166
|
children: [
|
|
4902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
}),
|
|
4911
|
-
updatePhase === "installing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4912
|
-
role: "status",
|
|
4913
|
-
children: t("updatingPlugin")
|
|
4914
|
-
}),
|
|
4915
|
-
updatePhase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4916
|
-
className: "mc-error",
|
|
4917
|
-
role: "alert",
|
|
4918
|
-
children: [
|
|
4919
|
-
t("pluginUpdateFailed"),
|
|
4920
|
-
" ",
|
|
4921
|
-
updateError
|
|
4922
|
-
]
|
|
4923
|
-
})
|
|
4924
|
-
]
|
|
4925
|
-
}),
|
|
4926
|
-
searchEdit !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
4927
|
-
open: true,
|
|
4928
|
-
onClose: () => setSearchEdit(null),
|
|
4929
|
-
title: `${t("editSearchMetadata")} · ${searchEdit.id}`,
|
|
4930
|
-
closeLabel: t("previewClose"),
|
|
4931
|
-
className: "mc-search-dialog",
|
|
4932
|
-
children: [
|
|
4933
|
-
searchEdit.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4934
|
-
className: "mc-preview-hint",
|
|
4935
|
-
children: "…"
|
|
4936
|
-
}),
|
|
4937
|
-
searchEdit.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4938
|
-
className: "mc-preview-hint",
|
|
4939
|
-
children: searchEdit.message
|
|
4940
|
-
}),
|
|
4941
|
-
searchEdit.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4942
|
-
className: "mc-confirm-body",
|
|
4943
|
-
children: [
|
|
4944
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4945
|
-
className: "mc-search-field",
|
|
4946
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4947
|
-
className: "mc-search-label-row",
|
|
4948
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
4949
|
-
htmlFor: "mc-search-description",
|
|
4950
|
-
children: t("searchDescription")
|
|
4951
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4952
|
-
id: "mc-search-description-note",
|
|
4953
|
-
children: t("searchMetadataNote")
|
|
4954
|
-
})]
|
|
4955
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
4956
|
-
id: "mc-search-description",
|
|
4957
|
-
"aria-describedby": "mc-search-description-note",
|
|
4958
|
-
value: searchDescription,
|
|
4959
|
-
maxLength: searchEdit.metadata?.maxChars,
|
|
4960
|
-
disabled: searchSaving,
|
|
4961
|
-
onChange: (e) => setSearchDescription(e.target.value)
|
|
4962
|
-
})]
|
|
4963
|
-
}),
|
|
4964
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4965
|
-
className: "mc-search-hint",
|
|
4966
|
-
children: [
|
|
4967
|
-
searchDescription.length,
|
|
4968
|
-
"/",
|
|
4969
|
-
searchEdit.metadata?.maxChars
|
|
4970
|
-
]
|
|
5167
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5168
|
+
type: "button",
|
|
5169
|
+
variant: "outline",
|
|
5170
|
+
size: "sm",
|
|
5171
|
+
className: "mc-update-cancel",
|
|
5172
|
+
disabled: updatePhase === "installing",
|
|
5173
|
+
onClick: closeUpdateDialog,
|
|
5174
|
+
children: t("cancel")
|
|
4971
5175
|
}),
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
5176
|
+
updateStatus.reason === "local" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5177
|
+
type: "button",
|
|
5178
|
+
variant: "outline",
|
|
5179
|
+
size: "sm",
|
|
5180
|
+
disabled: updatePhase === "installing",
|
|
5181
|
+
onClick: () => setSourceStepsOpen((value) => !value),
|
|
5182
|
+
children: t(sourceStepsOpen ? "updateSourceStepsHide" : "updateSourceSteps")
|
|
4975
5183
|
}),
|
|
4976
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
onClick: () => setSearchDescription(searchEdit.metadata?.defaultDescription ?? ""),
|
|
4984
|
-
children: t("searchMetadataRestore")
|
|
4985
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4986
|
-
size: "sm",
|
|
4987
|
-
variant: "primary",
|
|
4988
|
-
disabled: searchSaving,
|
|
4989
|
-
onClick: () => void saveSearchEdit(),
|
|
4990
|
-
children: t("searchMetadataSave")
|
|
4991
|
-
})]
|
|
5184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5185
|
+
type: "button",
|
|
5186
|
+
variant: "primary",
|
|
5187
|
+
size: "sm",
|
|
5188
|
+
disabled: updatePhase === "installing",
|
|
5189
|
+
onClick: () => void installUpdate(updateStatus.reason === "local" ? "local" : "registry"),
|
|
5190
|
+
children: t(updateStatus.reason === "local" ? "switchAndInstall" : "installUpdate")
|
|
4992
5191
|
})
|
|
4993
5192
|
]
|
|
4994
|
-
})
|
|
4995
|
-
]
|
|
4996
|
-
}),
|
|
4997
|
-
(registerOpen || editMcp !== void 0 || editSkill !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocationModal, {
|
|
4998
|
-
remote,
|
|
4999
|
-
t,
|
|
5000
|
-
defaultKind: activeTab === "skills" ? "skill" : "mcp",
|
|
5001
|
-
...editMcp !== void 0 ? { editMcp } : {},
|
|
5002
|
-
...editSkill !== void 0 ? { editSkill } : {},
|
|
5003
|
-
onClose: () => {
|
|
5004
|
-
setRegisterOpen(false);
|
|
5005
|
-
setEditMcp(void 0);
|
|
5006
|
-
setEditSkill(void 0);
|
|
5007
|
-
},
|
|
5008
|
-
onChanged: (notice, refreshMcpServer) => {
|
|
5009
|
-
if (refreshMcpServer !== void 0) onRefreshMcp(refreshMcpServer);
|
|
5010
|
-
else onRefresh();
|
|
5011
|
-
if (notice !== void 0) onNotice(notice);
|
|
5012
|
-
}
|
|
5013
|
-
}),
|
|
5014
|
-
catalogDocs !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5015
|
-
open: true,
|
|
5016
|
-
onClose: () => setCatalogDocs(null),
|
|
5017
|
-
title: t("viewCatalog"),
|
|
5018
|
-
closeLabel: t("previewClose"),
|
|
5019
|
-
children: [
|
|
5020
|
-
catalogDocs.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5021
|
-
className: "mc-preview-hint",
|
|
5022
|
-
children: "…"
|
|
5023
|
-
}),
|
|
5024
|
-
catalogDocs.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5025
|
-
className: "mc-preview-hint",
|
|
5026
|
-
children: catalogDocs.message
|
|
5027
5193
|
}),
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5194
|
+
children: [
|
|
5195
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateVersionComparison", {
|
|
5196
|
+
current: updateStatus.current,
|
|
5197
|
+
latest: updateStatus.latest
|
|
5198
|
+
}) }),
|
|
5199
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(updateStatus.reason === "local" ? "updateLocalNote" : "updateNpmNote") }),
|
|
5200
|
+
updateStatus.reason === "local" && sourceStepsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5201
|
+
className: "mc-update-steps",
|
|
5202
|
+
children: [
|
|
5203
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsIntro") }),
|
|
5204
|
+
updateStatus.sourcePath && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5205
|
+
className: "mc-confirm-path",
|
|
5206
|
+
children: updateStatus.sourcePath
|
|
5207
|
+
}),
|
|
5208
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: "git pull\npnpm install\npnpm run build" }),
|
|
5209
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsRestart") })
|
|
5210
|
+
]
|
|
5211
|
+
}),
|
|
5212
|
+
updatePhase === "installing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5213
|
+
role: "status",
|
|
5214
|
+
children: t("updatingPlugin")
|
|
5215
|
+
}),
|
|
5216
|
+
updatePhase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
5217
|
+
className: "mc-error",
|
|
5218
|
+
role: "alert",
|
|
5219
|
+
children: [
|
|
5220
|
+
t("pluginUpdateFailed"),
|
|
5221
|
+
" ",
|
|
5222
|
+
updateError
|
|
5223
|
+
]
|
|
5045
5224
|
})
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
5078
|
-
|
|
5225
|
+
]
|
|
5226
|
+
}),
|
|
5227
|
+
searchEdit !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5228
|
+
open: true,
|
|
5229
|
+
onClose: () => setSearchEdit(null),
|
|
5230
|
+
title: `${t("editSearchMetadata")} · ${searchEdit.id}`,
|
|
5231
|
+
closeLabel: t("previewClose"),
|
|
5232
|
+
className: "mc-search-dialog",
|
|
5233
|
+
children: [
|
|
5234
|
+
searchEdit.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5235
|
+
className: "mc-preview-hint",
|
|
5236
|
+
children: "…"
|
|
5237
|
+
}),
|
|
5238
|
+
searchEdit.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5239
|
+
className: "mc-preview-hint",
|
|
5240
|
+
children: searchEdit.message
|
|
5241
|
+
}),
|
|
5242
|
+
searchEdit.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5243
|
+
className: "mc-confirm-body",
|
|
5244
|
+
children: [
|
|
5245
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5246
|
+
className: "mc-search-field",
|
|
5247
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5248
|
+
className: "mc-search-label-row",
|
|
5249
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5250
|
+
htmlFor: "mc-search-description",
|
|
5251
|
+
children: t("searchDescription")
|
|
5252
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5253
|
+
id: "mc-search-description-note",
|
|
5254
|
+
children: t("searchMetadataNote")
|
|
5255
|
+
})]
|
|
5256
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
5257
|
+
id: "mc-search-description",
|
|
5258
|
+
"aria-describedby": "mc-search-description-note",
|
|
5259
|
+
value: searchDescription,
|
|
5260
|
+
maxLength: searchEdit.metadata?.maxChars,
|
|
5261
|
+
disabled: searchSaving,
|
|
5262
|
+
onChange: (e) => setSearchDescription(e.target.value)
|
|
5263
|
+
})]
|
|
5264
|
+
}),
|
|
5265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5266
|
+
className: "mc-search-hint",
|
|
5267
|
+
children: [
|
|
5268
|
+
searchDescription.length,
|
|
5269
|
+
"/",
|
|
5270
|
+
searchEdit.metadata?.maxChars
|
|
5271
|
+
]
|
|
5272
|
+
}),
|
|
5273
|
+
searchEdit.message !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5274
|
+
className: "mc-error",
|
|
5275
|
+
children: searchEdit.message
|
|
5276
|
+
}),
|
|
5277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5278
|
+
className: "mc-confirm-actions",
|
|
5279
|
+
children: [searchDescription !== searchEdit.metadata?.defaultDescription && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5280
|
+
size: "sm",
|
|
5281
|
+
variant: "ghost",
|
|
5282
|
+
disabled: searchSaving,
|
|
5283
|
+
title: t("searchMetadataRestoreHint"),
|
|
5284
|
+
onClick: () => setSearchDescription(searchEdit.metadata?.defaultDescription ?? ""),
|
|
5285
|
+
children: t("searchMetadataRestore")
|
|
5286
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5079
5287
|
size: "sm",
|
|
5080
5288
|
variant: "primary",
|
|
5081
|
-
disabled:
|
|
5082
|
-
onClick: () => void
|
|
5289
|
+
disabled: searchSaving,
|
|
5290
|
+
onClick: () => void saveSearchEdit(),
|
|
5083
5291
|
children: t("searchMetadataSave")
|
|
5084
|
-
})
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5292
|
+
})]
|
|
5293
|
+
})
|
|
5294
|
+
]
|
|
5295
|
+
})
|
|
5296
|
+
]
|
|
5297
|
+
}),
|
|
5298
|
+
(registerOpen || editMcp !== void 0 || editSkill !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocationModal, {
|
|
5299
|
+
remote,
|
|
5300
|
+
t,
|
|
5301
|
+
defaultKind: activeTab === "skills" ? "skill" : "mcp",
|
|
5302
|
+
...editMcp !== void 0 ? { editMcp } : {},
|
|
5303
|
+
...editSkill !== void 0 ? { editSkill } : {},
|
|
5304
|
+
onClose: () => {
|
|
5305
|
+
setRegisterOpen(false);
|
|
5306
|
+
setEditMcp(void 0);
|
|
5307
|
+
setEditSkill(void 0);
|
|
5308
|
+
},
|
|
5309
|
+
onChanged: (notice, refreshMcpServer) => {
|
|
5310
|
+
if (refreshMcpServer !== void 0) onRefreshMcp(refreshMcpServer);
|
|
5311
|
+
else onRefresh();
|
|
5312
|
+
if (notice !== void 0) onNotice(notice);
|
|
5313
|
+
}
|
|
5314
|
+
}),
|
|
5315
|
+
catalogDocs !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5316
|
+
open: true,
|
|
5317
|
+
onClose: () => setCatalogDocs(null),
|
|
5318
|
+
title: t("viewCatalog"),
|
|
5319
|
+
closeLabel: t("previewClose"),
|
|
5320
|
+
children: [
|
|
5321
|
+
catalogDocs.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5322
|
+
className: "mc-preview-hint",
|
|
5323
|
+
children: "…"
|
|
5088
5324
|
}),
|
|
5089
|
-
|
|
5090
|
-
className: "mc-
|
|
5091
|
-
children:
|
|
5325
|
+
catalogDocs.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5326
|
+
className: "mc-preview-hint",
|
|
5327
|
+
children: catalogDocs.message
|
|
5092
5328
|
}),
|
|
5093
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5329
|
+
catalogDocs.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5330
|
+
className: "mc-subtabs mc-catalog-tabs",
|
|
5331
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
5332
|
+
items: [{
|
|
5333
|
+
value: "policy",
|
|
5334
|
+
id: "mc-cat-tab-policy",
|
|
5335
|
+
panelId: "mc-cat-panel-policy",
|
|
5336
|
+
label: t("catalogPolicy")
|
|
5337
|
+
}, {
|
|
5338
|
+
value: "catalog",
|
|
5339
|
+
id: "mc-cat-tab-catalog",
|
|
5340
|
+
panelId: "mc-cat-panel-catalog",
|
|
5341
|
+
label: t("catalogOnDemand")
|
|
5342
|
+
}],
|
|
5343
|
+
value: catalogTab,
|
|
5344
|
+
onChange: setCatalogTab,
|
|
5345
|
+
label: t("viewCatalog")
|
|
5346
|
+
})
|
|
5347
|
+
}), catalogTab === "policy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5348
|
+
className: "mc-catalog-path",
|
|
5349
|
+
children: t("catalogPolicyNote")
|
|
5350
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
5094
5351
|
className: "mc-preview-body",
|
|
5095
|
-
children:
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5352
|
+
children: catalogDocs.docs.policyYaml
|
|
5353
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5354
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5355
|
+
className: "mc-catalog-toolbar",
|
|
5356
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5357
|
+
className: "mc-catalog-path",
|
|
5358
|
+
children: t("catalogSearchNote", { count: catalogDocs.docs.onDemand.length })
|
|
5359
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5360
|
+
className: "mc-summary-setting",
|
|
5361
|
+
children: [
|
|
5362
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
5363
|
+
htmlFor: "mc-summary-limit",
|
|
5364
|
+
children: t("summaryLimit")
|
|
5365
|
+
}),
|
|
5366
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
5367
|
+
id: "mc-summary-limit",
|
|
5368
|
+
type: "number",
|
|
5369
|
+
min: 20,
|
|
5370
|
+
step: 1,
|
|
5371
|
+
value: summaryLimitDraft,
|
|
5372
|
+
disabled: !catalogDocs.docs.searchSettingsEnabled || summaryLimitSaving,
|
|
5373
|
+
onChange: (e) => {
|
|
5374
|
+
setSummaryLimitDraft(e.target.value);
|
|
5375
|
+
setSummaryLimitError(void 0);
|
|
5376
|
+
}
|
|
5377
|
+
}),
|
|
5378
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitUnit") }),
|
|
5379
|
+
catalogDocs.docs.searchSettingsEnabled && summaryLimitDraft !== String(catalogDocs.docs.summaryMaxChars) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5380
|
+
size: "sm",
|
|
5381
|
+
variant: "primary",
|
|
5382
|
+
disabled: summaryLimitSaving,
|
|
5383
|
+
onClick: () => void saveSummaryLimit(),
|
|
5384
|
+
children: t("searchMetadataSave")
|
|
5385
|
+
}),
|
|
5386
|
+
!catalogDocs.docs.searchSettingsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitDisabled") })
|
|
5387
|
+
]
|
|
5109
5388
|
})]
|
|
5110
|
-
},
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5389
|
+
}),
|
|
5390
|
+
summaryLimitError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5391
|
+
className: "mc-error mc-catalog-limit-error",
|
|
5392
|
+
children: summaryLimitError
|
|
5393
|
+
}),
|
|
5394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5395
|
+
className: "mc-preview-body",
|
|
5396
|
+
children: [catalogDocs.docs.onDemand.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5397
|
+
className: "mc-preview-hint",
|
|
5398
|
+
children: t("catalogEmpty")
|
|
5399
|
+
}), catalogDocs.docs.onDemand.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5400
|
+
className: "mc-search-result",
|
|
5401
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5402
|
+
className: "mc-search-result-head",
|
|
5403
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.kind === "tool" ? "Tool" : "Skill" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5404
|
+
className: "mc-search-result-id",
|
|
5405
|
+
children: row.id
|
|
5406
|
+
})]
|
|
5407
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5408
|
+
className: "mc-search-result-summary",
|
|
5409
|
+
children: row.summary
|
|
5410
|
+
})]
|
|
5411
|
+
}, `${row.kind}:${row.id}`))]
|
|
5412
|
+
})
|
|
5413
|
+
] })] })
|
|
5414
|
+
]
|
|
5415
|
+
})
|
|
5416
|
+
]
|
|
5417
|
+
});
|
|
5116
5418
|
}
|
|
5117
5419
|
function projectRootForSkill(path) {
|
|
5118
5420
|
return path.match(/^(.*)\/(?:\.dsh|\.agents)\/skills(?:\/.*)?$/)?.[1] ?? path;
|
|
@@ -5298,111 +5600,118 @@ function SkillList(props) {
|
|
|
5298
5600
|
show the directory the click will link. */
|
|
5299
5601
|
const confirmAdoptRow = confirmAdopt === null ? void 0 : skills.find((row) => row.id === confirmAdopt);
|
|
5300
5602
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
className: "
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
"
|
|
5310
|
-
|
|
5311
|
-
onKeyDown: (e) => {
|
|
5312
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
5603
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5604
|
+
className: "v3-catgrid",
|
|
5605
|
+
children: skills.map((skill) => {
|
|
5606
|
+
const open = openSkill === skill.id;
|
|
5607
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
5608
|
+
className: "v3-group",
|
|
5609
|
+
open,
|
|
5610
|
+
"data-v3-skill": skill.id,
|
|
5611
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
5612
|
+
onClick: (e) => {
|
|
5313
5613
|
e.preventDefault();
|
|
5314
5614
|
toggleSkill(skill.id);
|
|
5315
|
-
}
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
title: skill.path,
|
|
5329
|
-
children: t("projectRootLabel", { path: projectRootForSkill(skill.path) })
|
|
5330
|
-
}),
|
|
5331
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5332
|
-
className: "mc-skill-meta",
|
|
5333
|
-
children: [
|
|
5334
|
-
skill.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, { children: t("mandatory") }),
|
|
5335
|
-
skill.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5336
|
-
size: "sm",
|
|
5337
|
-
variant: "ghost",
|
|
5338
|
-
className: "mc-count--action",
|
|
5339
|
-
title: t("editSearchMetadata"),
|
|
5340
|
-
onClick: (e) => {
|
|
5341
|
-
e.stopPropagation();
|
|
5342
|
-
onEditSearch(skill.id);
|
|
5343
|
-
},
|
|
5344
|
-
children: t("searchMetadataButton")
|
|
5345
|
-
}),
|
|
5346
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5347
|
-
type: "button",
|
|
5348
|
-
className: `mc-count mc-count--${skill.class}`,
|
|
5349
|
-
disabled: busy || skill.mandatory,
|
|
5350
|
-
title: skill.classLabel,
|
|
5351
|
-
onClick: (e) => {
|
|
5352
|
-
e.stopPropagation();
|
|
5353
|
-
onCycle([skill.id], "skill");
|
|
5354
|
-
},
|
|
5355
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5356
|
-
className: `mc-dot mc-dot--${skill.class}`,
|
|
5357
|
-
"aria-hidden": "true"
|
|
5358
|
-
}), t(CLASS_SHORT_KEYS[skill.class])]
|
|
5359
|
-
}),
|
|
5360
|
-
!editableSkills.has(skill.id) && (skill.preset !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5361
|
-
className: "mc-source",
|
|
5362
|
-
title: t("skillFromPresetHint"),
|
|
5363
|
-
children: t("skillFromPreset", { preset: skill.preset })
|
|
5364
|
-
}) : ADOPTABLE_SKILL_SOURCES.has(skill.source ?? "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5365
|
-
type: "button",
|
|
5366
|
-
className: "mc-count mc-count--action",
|
|
5367
|
-
disabled: busy,
|
|
5368
|
-
title: t("adoptSkillHint"),
|
|
5369
|
-
onClick: (e) => {
|
|
5370
|
-
e.stopPropagation();
|
|
5371
|
-
setConfirmAdopt(skill.id);
|
|
5372
|
-
},
|
|
5373
|
-
children: t("adoptSkill")
|
|
5374
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5375
|
-
className: "mc-source",
|
|
5376
|
-
title: t("skillSourceHint"),
|
|
5377
|
-
children: t("skillSource", { source: sourceLabel(skill.source, t) })
|
|
5378
|
-
})),
|
|
5379
|
-
editableSkills.has(skill.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5380
|
-
type: "button",
|
|
5381
|
-
className: "mc-count mc-count--action",
|
|
5382
|
-
disabled: busy,
|
|
5383
|
-
onClick: (e) => {
|
|
5384
|
-
e.stopPropagation();
|
|
5385
|
-
onEditSkill(skill.id, skill.source);
|
|
5386
|
-
},
|
|
5387
|
-
children: t("edit")
|
|
5615
|
+
},
|
|
5616
|
+
children: [
|
|
5617
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
5618
|
+
className: "v3-chev",
|
|
5619
|
+
viewBox: "0 0 12 12",
|
|
5620
|
+
fill: "none",
|
|
5621
|
+
"aria-hidden": "true",
|
|
5622
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
5623
|
+
d: "M4.5 2.5 8 6l-3.5 3.5",
|
|
5624
|
+
stroke: "currentColor",
|
|
5625
|
+
strokeWidth: 1.4,
|
|
5626
|
+
strokeLinecap: "round",
|
|
5627
|
+
strokeLinejoin: "round"
|
|
5388
5628
|
})
|
|
5389
|
-
|
|
5629
|
+
}),
|
|
5630
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5631
|
+
className: "v3-gnm",
|
|
5632
|
+
children: skill.name
|
|
5633
|
+
}),
|
|
5634
|
+
PROJECT_SKILL_SOURCES.has(skill.source ?? "") && skill.path !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5635
|
+
className: "v3-cnt",
|
|
5636
|
+
title: skill.path,
|
|
5637
|
+
children: t("projectRootLabel", { path: projectRootForSkill(skill.path) })
|
|
5638
|
+
}),
|
|
5639
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5640
|
+
className: "v3-right",
|
|
5641
|
+
children: [
|
|
5642
|
+
skill.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5643
|
+
className: "v3-tag",
|
|
5644
|
+
children: t("mandatory")
|
|
5645
|
+
}),
|
|
5646
|
+
skill.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5647
|
+
type: "button",
|
|
5648
|
+
className: "v3-tag v3-tag--act",
|
|
5649
|
+
title: t("editSearchMetadata"),
|
|
5650
|
+
onClick: (e) => {
|
|
5651
|
+
e.stopPropagation();
|
|
5652
|
+
onEditSearch(skill.id);
|
|
5653
|
+
},
|
|
5654
|
+
children: t("searchMetadataButton")
|
|
5655
|
+
}),
|
|
5656
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5657
|
+
type: "button",
|
|
5658
|
+
className: "v3-tag",
|
|
5659
|
+
"data-tone": CLASS_TONE[skill.class],
|
|
5660
|
+
disabled: busy || skill.mandatory,
|
|
5661
|
+
title: skill.classLabel,
|
|
5662
|
+
onClick: (e) => {
|
|
5663
|
+
e.stopPropagation();
|
|
5664
|
+
onCycle([skill.id], "skill");
|
|
5665
|
+
},
|
|
5666
|
+
children: t(CLASS_SHORT_KEYS[skill.class])
|
|
5667
|
+
}),
|
|
5668
|
+
!editableSkills.has(skill.id) && (skill.preset !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5669
|
+
className: "v3-tag",
|
|
5670
|
+
title: t("skillFromPresetHint"),
|
|
5671
|
+
children: t("skillFromPreset", { preset: skill.preset })
|
|
5672
|
+
}) : ADOPTABLE_SKILL_SOURCES.has(skill.source ?? "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5673
|
+
type: "button",
|
|
5674
|
+
className: "v3-tag v3-tag--act",
|
|
5675
|
+
disabled: busy,
|
|
5676
|
+
title: t("adoptSkillHint"),
|
|
5677
|
+
onClick: (e) => {
|
|
5678
|
+
e.stopPropagation();
|
|
5679
|
+
setConfirmAdopt(skill.id);
|
|
5680
|
+
},
|
|
5681
|
+
children: t("adoptSkill")
|
|
5682
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5683
|
+
className: "v3-tag",
|
|
5684
|
+
title: t("skillSourceHint"),
|
|
5685
|
+
children: t("skillSource", { source: sourceLabel(skill.source, t) })
|
|
5686
|
+
})),
|
|
5687
|
+
editableSkills.has(skill.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5688
|
+
type: "button",
|
|
5689
|
+
className: "v3-tag v3-tag--act",
|
|
5690
|
+
disabled: busy,
|
|
5691
|
+
onClick: (e) => {
|
|
5692
|
+
e.stopPropagation();
|
|
5693
|
+
onEditSkill(skill.id, skill.source);
|
|
5694
|
+
},
|
|
5695
|
+
children: t("edit")
|
|
5696
|
+
})
|
|
5697
|
+
]
|
|
5698
|
+
})
|
|
5699
|
+
]
|
|
5700
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5701
|
+
className: "mc-skill-body",
|
|
5702
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5703
|
+
className: "mc-tree",
|
|
5704
|
+
children: [browseErrors[`${skill.id}:`] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5705
|
+
className: "mc-preview-hint",
|
|
5706
|
+
children: browseErrors[`${skill.id}:`]
|
|
5707
|
+
}), renderEntries(tree.dirs[`${skill.id}:`], skill.id, "") ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5708
|
+
className: "mc-tree-row",
|
|
5709
|
+
children: "…"
|
|
5710
|
+
})]
|
|
5390
5711
|
})
|
|
5391
|
-
]
|
|
5392
|
-
}
|
|
5393
|
-
|
|
5394
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5395
|
-
className: "mc-tree",
|
|
5396
|
-
children: [browseErrors[`${skill.id}:`] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5397
|
-
className: "mc-preview-hint",
|
|
5398
|
-
children: browseErrors[`${skill.id}:`]
|
|
5399
|
-
}), renderEntries(tree.dirs[`${skill.id}:`], skill.id, "") ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5400
|
-
className: "mc-tree-row",
|
|
5401
|
-
children: "…"
|
|
5402
|
-
})]
|
|
5403
|
-
})
|
|
5404
|
-
})]
|
|
5405
|
-
}, skill.id);
|
|
5712
|
+
})]
|
|
5713
|
+
}, skill.id);
|
|
5714
|
+
})
|
|
5406
5715
|
}),
|
|
5407
5716
|
confirmAdopt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
5408
5717
|
open: true,
|
|
@@ -6208,11 +6517,7 @@ const COVERAGE_LABELS = Object.freeze({
|
|
|
6208
6517
|
partial: "部分",
|
|
6209
6518
|
unknown: "未知"
|
|
6210
6519
|
});
|
|
6211
|
-
|
|
6212
|
-
* Zero-count placeholders. Required for `plugin-tool` / `skill`; the other
|
|
6213
|
-
* kinds get a neutral line so no zero is ever presented as "未使用".
|
|
6214
|
-
*/
|
|
6215
|
-
const CALL_INDEX_ZERO_SOURCE_NOTES = Object.freeze({
|
|
6520
|
+
Object.freeze({
|
|
6216
6521
|
mcp: "本次索引未解析到 MCP 来源的调用;覆盖不完整时不能据此判定未使用。",
|
|
6217
6522
|
native: "本次索引未解析到原生工具调用;覆盖不完整时不能据此判定未使用。",
|
|
6218
6523
|
"plugin-tool": "本次索引未解析出插件工具归属(插件 owner 不可证明),0 在这里不等于未使用。",
|
|
@@ -6251,10 +6556,6 @@ function formatCallIndexCount(value, totalExact) {
|
|
|
6251
6556
|
const text = Math.trunc(value).toLocaleString("en-US");
|
|
6252
6557
|
return totalExact === true ? text : `≥${text}`;
|
|
6253
6558
|
}
|
|
6254
|
-
/** Human label for the time filter. */
|
|
6255
|
-
function callIndexRangeLabel(range) {
|
|
6256
|
-
return range === "all" ? "全部" : `${range} 天`;
|
|
6257
|
-
}
|
|
6258
6559
|
/**
|
|
6259
6560
|
* Local-day window for `range`, expressed as the wire's half-open
|
|
6260
6561
|
* `[fromMs, toMs)`. `'all'` carries no lower bound.
|
|
@@ -6499,24 +6800,6 @@ function callIndexOutcomeMix(summary) {
|
|
|
6499
6800
|
total: summary.total
|
|
6500
6801
|
};
|
|
6501
6802
|
}
|
|
6502
|
-
/**
|
|
6503
|
-
* `bySource` for display. Always returns all five kinds in a fixed order, so
|
|
6504
|
-
* the distribution never reshuffles between reads; a zero carries its
|
|
6505
|
-
* placeholder instead of disappearing.
|
|
6506
|
-
*/
|
|
6507
|
-
function callIndexSourceData(bySource) {
|
|
6508
|
-
const total = bySource === void 0 ? 0 : SOURCE_DISPLAY_ORDER.reduce((sum, kind) => sum + bySource[kind], 0);
|
|
6509
|
-
return SOURCE_DISPLAY_ORDER.map((kind) => {
|
|
6510
|
-
const count = bySource?.[kind] ?? 0;
|
|
6511
|
-
return {
|
|
6512
|
-
kind,
|
|
6513
|
-
label: CALL_INDEX_SOURCE_LABELS[kind],
|
|
6514
|
-
count,
|
|
6515
|
-
share: total > 0 ? count / total : 0,
|
|
6516
|
-
...count === 0 ? { zeroNote: CALL_INDEX_ZERO_SOURCE_NOTES[kind] } : {}
|
|
6517
|
-
};
|
|
6518
|
-
});
|
|
6519
|
-
}
|
|
6520
6803
|
/** Fixed chart box; the SVG scales with CSS but the geometry is deterministic. */
|
|
6521
6804
|
const CALL_INDEX_TREND_GEOMETRY = Object.freeze({
|
|
6522
6805
|
width: 640,
|
|
@@ -6697,25 +6980,293 @@ function callIndexOverviewMetrics(overview, status) {
|
|
|
6697
6980
|
...status?.task === void 0 ? {} : { tasks: status.task }
|
|
6698
6981
|
};
|
|
6699
6982
|
}
|
|
6700
|
-
/** True when `plugin-tool` and `skill` both resolved to zero. */
|
|
6701
|
-
function callIndexHasUnexplainedSources(aggregates) {
|
|
6702
|
-
if (aggregates === void 0) return false;
|
|
6703
|
-
return aggregates.bySource["plugin-tool"] === 0 && aggregates.bySource.skill === 0;
|
|
6704
|
-
}
|
|
6705
6983
|
/** Short Session id for a dense table cell; the full id stays in `title`. */
|
|
6706
6984
|
function shortCallIndexSessionId(sessionId) {
|
|
6707
6985
|
return sessionId.length <= 12 ? sessionId : sessionId.slice(0, 12);
|
|
6708
6986
|
}
|
|
6709
6987
|
/** Inline stylesheet; a `<style>` element keeps this file dependency-free. */
|
|
6710
6988
|
function CallIndexStyles() {
|
|
6711
|
-
return (0, react.createElement)("style", { "data-call-index-style": "true" }, ".mc-ci-filter{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:8px 0}.mc-ci-filter label{display:flex;gap:4px;align-items:center;font-size:12px}.mc-ci-table-scroll{width:100%;overflow:auto;max-height:min(60vh,560px);overscroll-behavior:contain}.mc-ci-table{width:100%;border-collapse:collapse;font-size:12px}.mc-ci-table thead th{position:sticky;top:0;z-index:1;background:var(--mc-surface,Canvas);box-shadow:inset 0 -1px 0 color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress{display:flex;flex-direction:column;gap:4px;margin:8px 0}.mc-ci-progress-track{height:6px;border-radius:999px;overflow:hidden;background:color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress-fill{height:100%;border-radius:999px;background:color-mix(in srgb,currentColor 55%,transparent);transition:width .3s ease}.mc-ci-progress-fill.is-indeterminate{animation:mc-ci-indeterminate 1.1s ease-in-out infinite}@keyframes mc-ci-indeterminate{0%{transform:translateX(-60%)}100%{transform:translateX(340%)}}@media (prefers-reduced-motion:reduce){.mc-ci-progress-fill{transition:none}.mc-ci-progress-fill.is-indeterminate{animation:none;width:100%!important;opacity:.4}}.mc-ci-session-picker{display:inline-flex;align-items:center;gap:6px;font-size:12px}.mc-ci-session-picker-label{opacity:.7}.mc-ci-session-select{max-width:280px;padding:3px 6px;border-radius:6px;font-size:12px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);background:transparent;color:inherit}.mc-ci-session-select:disabled{opacity:.5}.mc-ci-progress-meta{display:flex;gap:8px;align-items:baseline;font-size:12px}.mc-ci-progress-value{font-variant-numeric:tabular-nums;font-weight:600}.mc-ci-progress-detail{opacity:.72}.mc-ci-table th,.mc-ci-table td{padding:4px 6px;text-align:left;border-bottom:1px solid color-mix(in srgb,currentColor 12%,transparent);white-space:nowrap}.mc-ci-table tbody tr{cursor:pointer}.mc-ci-table tbody tr[aria-selected=\"true\"]{background:color-mix(in srgb,currentColor 8%,transparent)}.mc-ci-table td:nth-child(2),.mc-ci-table td:nth-child(4){max-width:220px;overflow:hidden;text-overflow:ellipsis}.mc-ci-table th:nth-child(2),.mc-ci-table th:nth-child(4){max-width:220px}.mc-ci-num{font-variant-numeric:tabular-nums}.mc-ci-bar{display:flex;height:8px;gap:1px;margin:4px 0}.mc-ci-bar > span{display:block;height:100%;min-width:2px}.mc-ci-aside{border-left:1px solid color-mix(in srgb,currentColor 15%,transparent);padding-left:12px;min-width:240px;font-size:12px}.mc-ci-metric{display:inline-flex;flex-direction:column;margin-right:18px;min-width:110px;vertical-align:top}.mc-ci-metric-label{font-size:11px;opacity:.7}.mc-ci-metric-value{font-size:18px;font-variant-numeric:tabular-nums}.mc-ci-metric-hint{font-size:11px;opacity:.75}.mc-ci-trend svg{width:100%;max-width:640px;height:auto}");
|
|
6989
|
+
return (0, react.createElement)("style", { "data-call-index-style": "true" }, ".mc-ci-filter{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:8px 0}.mc-ci-filter label{display:flex;gap:4px;align-items:center;font-size:12px}.mc-ci-table-scroll{width:100%;overflow:auto;max-height:min(60vh,560px);overscroll-behavior:contain}.mc-ci-table{width:100%;border-collapse:collapse;font-size:12px}.mc-ci-table thead th{position:sticky;top:0;z-index:1;background:var(--mc-surface,Canvas);box-shadow:inset 0 -1px 0 color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress{display:flex;flex-direction:column;gap:4px;margin:8px 0}.mc-ci-progress-track{height:6px;border-radius:999px;overflow:hidden;background:color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress-fill{height:100%;border-radius:999px;background:color-mix(in srgb,currentColor 55%,transparent);transition:width .3s ease}.mc-ci-progress-fill.is-indeterminate{animation:mc-ci-indeterminate 1.1s ease-in-out infinite}@keyframes mc-ci-indeterminate{0%{transform:translateX(-60%)}100%{transform:translateX(340%)}}@media (prefers-reduced-motion:reduce){.mc-ci-progress-fill{transition:none}.mc-ci-progress-fill.is-indeterminate{animation:none;width:100%!important;opacity:.4}}.mc-ci-session-picker{display:inline-flex;align-items:center;gap:6px;font-size:12px}.mc-ci-session-picker-label{opacity:.7}.mc-ci-session-select{max-width:280px;padding:3px 6px;border-radius:6px;font-size:12px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);background:transparent;color:inherit}.mc-ci-session-select:disabled{opacity:.5}.mc-ci-progress-meta{display:flex;gap:8px;align-items:baseline;font-size:12px}.mc-ci-progress-value{font-variant-numeric:tabular-nums;font-weight:600}.mc-ci-progress-detail{opacity:.72}.mc-ci-table th,.mc-ci-table td{padding:4px 6px;text-align:left;border-bottom:1px solid color-mix(in srgb,currentColor 12%,transparent);white-space:nowrap}.mc-ci-table tbody tr{cursor:pointer}.mc-ci-table tbody tr[aria-selected=\"true\"]{background:color-mix(in srgb,currentColor 8%,transparent)}.mc-ci-table td:nth-child(2),.mc-ci-table td:nth-child(4){max-width:220px;overflow:hidden;text-overflow:ellipsis}.mc-ci-table th:nth-child(2),.mc-ci-table th:nth-child(4){max-width:220px}.mc-ci-num{font-variant-numeric:tabular-nums}.mc-ci-bar{display:flex;height:8px;gap:1px;margin:4px 0}.mc-ci-bar > span{display:block;height:100%;min-width:2px}.mc-ci-aside{border-left:1px solid color-mix(in srgb,currentColor 15%,transparent);padding-left:12px;min-width:240px;font-size:12px}.mc-ci-metric{display:inline-flex;flex-direction:column;margin-right:18px;min-width:110px;vertical-align:top}.mc-ci-metric-label{font-size:11px;opacity:.7}.mc-ci-metric-value{font-size:18px;font-variant-numeric:tabular-nums}.mc-ci-metric-hint{font-size:11px;opacity:.75}.mc-ci-trend svg{width:100%;max-width:640px;height:auto}.v3-status-pills{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:12px}.v3-statusline.v3-status-dash{border-style:dashed;color:var(--v3-t4)}.v3-filtercard{padding:11px 14px 13px}.v3-filter-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:9px}.v3-search{display:flex;align-items:center;gap:6px;height:26px;padding:0 8px;border:1px solid var(--v3-line);border-radius:var(--v3-r-sm);background:var(--v3-panel-2);min-width:150px;flex:1 1 150px}.v3-search input{border:0;background:transparent;color:var(--v3-t1);font:inherit;font-size:12px;width:100%;outline:0;min-width:0}.v3-search input::placeholder{color:var(--v3-t4)}.v3-kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px}.v3-kv-l{font-size:11px;color:var(--v3-t4)}.v3-kv-v{font-size:12px;color:var(--v3-t2);margin-top:2px;overflow-wrap:anywhere}.v3-ts.v3-ts--h330{max-height:330px}.v3-chartview{overflow-x:auto;cursor:grab;overscroll-behavior-x:contain;scrollbar-width:thin;user-select:none;-webkit-user-select:none}.v3-chartview:active{cursor:grabbing}.v3-chartview svg{display:block}.v3-src i[data-zero=\"true\"]{background:var(--v3-panel-2)}");
|
|
6990
|
+
}
|
|
6991
|
+
/** One v3 `.v3-stat` cell: label, big value, optional hint line, optional sparkline. */
|
|
6992
|
+
function V3Stat(props) {
|
|
6993
|
+
return (0, react.createElement)("div", {
|
|
6994
|
+
className: "v3-stat",
|
|
6995
|
+
"data-call-index-metric": props.label
|
|
6996
|
+
}, (0, react.createElement)("div", { className: "v3-stat-l" }, props.label), (0, react.createElement)("div", { className: "v3-stat-v v3-num" }, props.value), props.hint === void 0 ? null : (0, react.createElement)("div", { className: "v3-stat-d" }, props.hint), props.spark ?? null);
|
|
6997
|
+
}
|
|
6998
|
+
/** Values → `x,y` points inside a 100×22 viewBox, baselined at 0. */
|
|
6999
|
+
function sparkPoints(values) {
|
|
7000
|
+
const W = 100, H = 22;
|
|
7001
|
+
const max = values.reduce((m, v) => Math.max(m, v), 0);
|
|
7002
|
+
const scale = max > 0 ? max : 1;
|
|
7003
|
+
const xAt = (i) => values.length <= 1 ? W / 2 : i * W / (values.length - 1);
|
|
7004
|
+
const yAt = (v) => 20 - v / scale * 18;
|
|
7005
|
+
const pts = values.map((v, i) => `${xAt(i).toFixed(1)},${yAt(v).toFixed(1)}`);
|
|
7006
|
+
return {
|
|
7007
|
+
line: pts.join(" "),
|
|
7008
|
+
area: `0,${H} ${pts.join(" ")} ${W},${H}`
|
|
7009
|
+
};
|
|
7010
|
+
}
|
|
7011
|
+
/** 22px trend sparkline: a line plus a translucent area fill, coloured by tone. */
|
|
7012
|
+
function Sparkline(props) {
|
|
7013
|
+
if (props.values.length < 2) return null;
|
|
7014
|
+
const points = sparkPoints(props.values);
|
|
7015
|
+
const colour = props.tone === "ok" ? "var(--v3-ok)" : props.tone === "bad" ? "var(--v3-bad)" : "var(--v3-info)";
|
|
7016
|
+
return (0, react.createElement)("svg", {
|
|
7017
|
+
className: "v3-spark",
|
|
7018
|
+
viewBox: "0 0 100 22",
|
|
7019
|
+
preserveAspectRatio: "none",
|
|
7020
|
+
"aria-hidden": "true",
|
|
7021
|
+
"data-call-index-spark": props.tone
|
|
7022
|
+
}, (0, react.createElement)("polygon", {
|
|
7023
|
+
className: "v3-spark-fl",
|
|
7024
|
+
points: points.area,
|
|
7025
|
+
fill: colour
|
|
7026
|
+
}), (0, react.createElement)("polyline", {
|
|
7027
|
+
className: "v3-spark-ln",
|
|
7028
|
+
points: points.line,
|
|
7029
|
+
stroke: colour
|
|
7030
|
+
}));
|
|
7031
|
+
}
|
|
7032
|
+
/** `server · tool` for an `mcp__` name, otherwise the bare tool name. */
|
|
7033
|
+
function toolDisplayLabel(toolName) {
|
|
7034
|
+
if (toolName.startsWith("mcp__")) {
|
|
7035
|
+
const parts = toolName.split("__");
|
|
7036
|
+
return parts.length >= 3 ? `${parts[1]} · ${parts.slice(2).join("__")}` : toolName;
|
|
7037
|
+
}
|
|
7038
|
+
return toolName;
|
|
7039
|
+
}
|
|
7040
|
+
/** Pixel width of the trend for `days` buckets: days x min column, floored. */
|
|
7041
|
+
function callIndexTrendWidth(days) {
|
|
7042
|
+
return Math.max(320, (Number.isFinite(days) && days > 0 ? Math.floor(days) : 1) * 34);
|
|
6712
7043
|
}
|
|
6713
|
-
/**
|
|
6714
|
-
|
|
7044
|
+
/**
|
|
7045
|
+
* The v3 overview trend: a line + area (calls), failure底柱 (error), a dashed
|
|
7046
|
+
* "recent week" band and a peak label, drawn inside a horizontally scrollable
|
|
7047
|
+
* strip so a long series can be panned instead of squeezed.
|
|
7048
|
+
*
|
|
7049
|
+
* Geometry comes from `callIndexTrend`'s bars (one data path). The SVG is
|
|
7050
|
+
* days x min-column wide (never narrower than the card), so day labels stay
|
|
7051
|
+
* legible; the wrapper scrolls and supports pointer drag-to-pan. The last day
|
|
7052
|
+
* label is right-anchored at the padding edge so it cannot overlap the one
|
|
7053
|
+
* before it.
|
|
7054
|
+
*/
|
|
7055
|
+
function OverviewTrendSvg(props) {
|
|
7056
|
+
const { chart } = props;
|
|
7057
|
+
const bars = chart.bars;
|
|
7058
|
+
const n = bars.length;
|
|
7059
|
+
const W = callIndexTrendWidth(n), H = 212, padL = 34, padR = 22, padT = 12;
|
|
7060
|
+
const plotW = W - padL - padR;
|
|
7061
|
+
const plotH = 174;
|
|
7062
|
+
const maxV = chart.maxCalls > 0 ? Math.ceil(chart.maxCalls / 10) * 10 : 1;
|
|
7063
|
+
const xAt = (i) => padL + (n <= 1 ? plotW / 2 : i * plotW / (n - 1));
|
|
7064
|
+
const yAt = (v) => 186 - v / maxV * plotH;
|
|
7065
|
+
const points = bars.map((bar, i) => xAt(i).toFixed(1) + "," + yAt(bar.calls).toFixed(1));
|
|
7066
|
+
let peak = 0;
|
|
7067
|
+
for (let i = 1; i < n; i++) if (bars[i].calls > bars[peak].calls) peak = i;
|
|
7068
|
+
const children = [];
|
|
7069
|
+
if (n > 0) {
|
|
7070
|
+
const start = Math.max(0, n - 7);
|
|
7071
|
+
const slot = n > 1 ? plotW / (n - 1) : plotW;
|
|
7072
|
+
const left = Math.max(padL, xAt(start) - slot / 2);
|
|
7073
|
+
const right = Math.min(W - padR, xAt(n - 1) + slot / 2);
|
|
7074
|
+
children.push((0, react.createElement)("rect", {
|
|
7075
|
+
key: "hl",
|
|
7076
|
+
className: "v3-hlbox",
|
|
7077
|
+
x: left.toFixed(1),
|
|
7078
|
+
y: padT,
|
|
7079
|
+
width: Math.max(0, right - left).toFixed(1),
|
|
7080
|
+
height: plotH.toFixed(1),
|
|
7081
|
+
rx: 4
|
|
7082
|
+
}));
|
|
7083
|
+
}
|
|
7084
|
+
for (let g = 0; g <= 4; g++) {
|
|
7085
|
+
const value = maxV * g / 4;
|
|
7086
|
+
const y = yAt(value);
|
|
7087
|
+
children.push((0, react.createElement)("line", {
|
|
7088
|
+
key: "grid-" + g,
|
|
7089
|
+
className: "v3-grid",
|
|
7090
|
+
x1: padL,
|
|
7091
|
+
y1: y.toFixed(1),
|
|
7092
|
+
x2: W - padR,
|
|
7093
|
+
y2: y.toFixed(1)
|
|
7094
|
+
}));
|
|
7095
|
+
children.push((0, react.createElement)("text", {
|
|
7096
|
+
key: "tick-" + g,
|
|
7097
|
+
className: "v3-axis",
|
|
7098
|
+
x: 27,
|
|
7099
|
+
y: (y + 3.5).toFixed(1),
|
|
7100
|
+
"text-anchor": "end"
|
|
7101
|
+
}, String(Math.round(value))));
|
|
7102
|
+
}
|
|
7103
|
+
const barWidth = Math.max(4, plotW / Math.max(1, n) - 8);
|
|
7104
|
+
bars.forEach((bar, i) => {
|
|
7105
|
+
if (bar.error <= 0) return;
|
|
7106
|
+
const errH = Math.max(2.5, bar.error / maxV * plotH);
|
|
7107
|
+
children.push((0, react.createElement)("rect", {
|
|
7108
|
+
key: "err-" + bar.day,
|
|
7109
|
+
className: "v3-bar-er",
|
|
7110
|
+
x: (xAt(i) - barWidth / 2).toFixed(1),
|
|
7111
|
+
y: (186 - errH).toFixed(1),
|
|
7112
|
+
width: barWidth.toFixed(1),
|
|
7113
|
+
height: errH.toFixed(1),
|
|
7114
|
+
rx: 1.5
|
|
7115
|
+
}, (0, react.createElement)("title", null, bar.day + ":失败 " + bar.error + " 次")));
|
|
7116
|
+
});
|
|
7117
|
+
children.push((0, react.createElement)("polygon", {
|
|
7118
|
+
key: "area",
|
|
7119
|
+
className: "v3-area",
|
|
7120
|
+
points: "34,186 " + points.join(" ") + " " + (W - padR) + ",186"
|
|
7121
|
+
}));
|
|
7122
|
+
children.push((0, react.createElement)("polyline", {
|
|
7123
|
+
key: "line",
|
|
7124
|
+
className: "v3-line",
|
|
7125
|
+
points: points.join(" ")
|
|
7126
|
+
}));
|
|
7127
|
+
bars.forEach((bar, i) => {
|
|
7128
|
+
if (i % 4 !== 0 && i !== n - 1 && i !== peak) return;
|
|
7129
|
+
children.push((0, react.createElement)("circle", {
|
|
7130
|
+
key: "pt-" + bar.day,
|
|
7131
|
+
className: "v3-pt",
|
|
7132
|
+
cx: xAt(i).toFixed(1),
|
|
7133
|
+
cy: yAt(bar.calls).toFixed(1),
|
|
7134
|
+
r: 2.6
|
|
7135
|
+
}, (0, react.createElement)("title", null, bar.day + ":" + bar.calls + " 次调用,失败 " + bar.error + " 次")));
|
|
7136
|
+
});
|
|
7137
|
+
if (n > 0) {
|
|
7138
|
+
children.push((0, react.createElement)("text", {
|
|
7139
|
+
key: "peak",
|
|
7140
|
+
className: "v3-lbl-b",
|
|
7141
|
+
x: (xAt(peak) - 8).toFixed(1),
|
|
7142
|
+
y: (yAt(bars[peak].calls) - 9).toFixed(1),
|
|
7143
|
+
"text-anchor": "end"
|
|
7144
|
+
}, "峰值 " + bars[peak].calls));
|
|
7145
|
+
const step = Math.max(1, Math.ceil(n / 6));
|
|
7146
|
+
for (let m = 0; m < n - 1; m += step) children.push((0, react.createElement)("text", {
|
|
7147
|
+
key: "xl-" + m,
|
|
7148
|
+
className: "v3-axis",
|
|
7149
|
+
x: xAt(m).toFixed(1),
|
|
7150
|
+
y: 204,
|
|
7151
|
+
"text-anchor": "middle"
|
|
7152
|
+
}, bars[m].day.slice(5)));
|
|
7153
|
+
children.push((0, react.createElement)("text", {
|
|
7154
|
+
key: "xl-last",
|
|
7155
|
+
className: "v3-axis",
|
|
7156
|
+
x: (W - padR).toFixed(1),
|
|
7157
|
+
y: 204,
|
|
7158
|
+
"text-anchor": "end",
|
|
7159
|
+
"data-call-index-lasttick": "true"
|
|
7160
|
+
}, bars[n - 1].day.slice(5)));
|
|
7161
|
+
}
|
|
7162
|
+
const svg = (0, react.createElement)("svg", {
|
|
7163
|
+
className: "v3-chart",
|
|
7164
|
+
viewBox: "0 0 " + W + " 212",
|
|
7165
|
+
preserveAspectRatio: "none",
|
|
7166
|
+
role: "img",
|
|
7167
|
+
width: W,
|
|
7168
|
+
height: H,
|
|
7169
|
+
style: {
|
|
7170
|
+
width: W + "px",
|
|
7171
|
+
minWidth: "100%",
|
|
7172
|
+
height: "212px"
|
|
7173
|
+
},
|
|
7174
|
+
"aria-label": "每日调用与失败数:" + n + " 天,峰值 " + chart.maxCalls + " 次",
|
|
7175
|
+
"data-call-index-svg": "trend",
|
|
7176
|
+
"data-call-index-width": String(W)
|
|
7177
|
+
}, ...children);
|
|
7178
|
+
let dragging = false;
|
|
7179
|
+
let startX = 0;
|
|
7180
|
+
let startScroll = 0;
|
|
6715
7181
|
return (0, react.createElement)("div", {
|
|
6716
|
-
className: "
|
|
6717
|
-
"data-call-index-
|
|
6718
|
-
|
|
7182
|
+
className: "v3-chartview",
|
|
7183
|
+
"data-call-index-chartview": "true",
|
|
7184
|
+
onPointerDown: (event) => {
|
|
7185
|
+
dragging = true;
|
|
7186
|
+
startX = event.clientX;
|
|
7187
|
+
startScroll = event.currentTarget.scrollLeft;
|
|
7188
|
+
event.currentTarget.style.cursor = "grabbing";
|
|
7189
|
+
if (event.pointerId !== void 0 && typeof event.currentTarget.setPointerCapture === "function") try {
|
|
7190
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
7191
|
+
} catch {}
|
|
7192
|
+
},
|
|
7193
|
+
onPointerMove: (event) => {
|
|
7194
|
+
if (!dragging) return;
|
|
7195
|
+
event.currentTarget.scrollLeft = startScroll - (event.clientX - startX);
|
|
7196
|
+
},
|
|
7197
|
+
onPointerUp: (event) => {
|
|
7198
|
+
dragging = false;
|
|
7199
|
+
event.currentTarget.style.cursor = "grab";
|
|
7200
|
+
},
|
|
7201
|
+
onPointerLeave: (event) => {
|
|
7202
|
+
if (!dragging) return;
|
|
7203
|
+
dragging = false;
|
|
7204
|
+
event.currentTarget.style.cursor = "grab";
|
|
7205
|
+
}
|
|
7206
|
+
}, svg);
|
|
7207
|
+
}
|
|
7208
|
+
/**
|
|
7209
|
+
* 来源构成,按工具名前缀启发式分类。
|
|
7210
|
+
*
|
|
7211
|
+
* NOT Host attribution: the Host cannot prove a plugin owner, so this card does
|
|
7212
|
+
* not pretend to. It splits the already-read per-tool rows by a plain rule —
|
|
7213
|
+
* a `mcp__` prefix is MCP, everything else is native — and says so at the foot.
|
|
7214
|
+
* When the per-tool read has not happened there is nothing to classify, so the
|
|
7215
|
+
* card shows 「来源未解析」 and explains that this is not a zero count.
|
|
7216
|
+
*/
|
|
7217
|
+
function OverviewSourcePanel(props) {
|
|
7218
|
+
const note = (0, react.createElement)("p", { className: "v3-src-note" }, (0, react.createElement)("span", null, "按工具名前缀推断,不是 Host 归属。"), (0, react.createElement)("b", { style: { color: "var(--v3-t3)" } }, " 0 在这里不等于未使用"));
|
|
7219
|
+
if (props.tools === void 0) return (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "来源构成")), (0, react.createElement)("p", {
|
|
7220
|
+
className: "v3-hint",
|
|
7221
|
+
"data-call-index-missing": "by-source"
|
|
7222
|
+
}, "尚未读取逐工具明细,来源未解析;这不是「全部为 0」。"), note);
|
|
7223
|
+
const mcpCalls = props.tools.filter((row) => row.toolName.startsWith("mcp__")).reduce((sum, row) => sum + row.calls, 0);
|
|
7224
|
+
const nativeCalls = props.tools.reduce((sum, row) => sum + row.calls, 0) - mcpCalls;
|
|
7225
|
+
const total = mcpCalls + nativeCalls;
|
|
7226
|
+
const data = [{
|
|
7227
|
+
kind: "mcp",
|
|
7228
|
+
label: CALL_INDEX_SOURCE_LABELS.mcp,
|
|
7229
|
+
count: mcpCalls,
|
|
7230
|
+
color: "var(--v3-info)"
|
|
7231
|
+
}, {
|
|
7232
|
+
kind: "native",
|
|
7233
|
+
label: CALL_INDEX_SOURCE_LABELS.native,
|
|
7234
|
+
count: nativeCalls,
|
|
7235
|
+
color: "var(--v3-ok)"
|
|
7236
|
+
}];
|
|
7237
|
+
const radius = 43;
|
|
7238
|
+
const circumference = 2 * Math.PI * radius;
|
|
7239
|
+
let offset = 0;
|
|
7240
|
+
const segments = data.filter((item) => item.count > 0).map((item) => {
|
|
7241
|
+
const length = total > 0 ? item.count / total * circumference : 0;
|
|
7242
|
+
const circle = (0, react.createElement)("circle", {
|
|
7243
|
+
key: item.kind,
|
|
7244
|
+
className: "v3-seg",
|
|
7245
|
+
cx: 52,
|
|
7246
|
+
cy: 52,
|
|
7247
|
+
r: radius,
|
|
7248
|
+
"data-seg": item.kind,
|
|
7249
|
+
strokeDasharray: `${length.toFixed(1)} ${(circumference - length).toFixed(1)}`,
|
|
7250
|
+
strokeDashoffset: (-offset).toFixed(1),
|
|
7251
|
+
style: { stroke: item.color }
|
|
7252
|
+
});
|
|
7253
|
+
offset += length;
|
|
7254
|
+
return circle;
|
|
7255
|
+
});
|
|
7256
|
+
return (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "来源构成"), (0, react.createElement)("span", { className: "v3-pn" }, `${formatCallIndexCount(total, props.totalExact)} 次`)), (0, react.createElement)("div", { className: "v3-donut-wrap" }, (0, react.createElement)("div", { className: "v3-donut" }, (0, react.createElement)("svg", {
|
|
7257
|
+
viewBox: "0 0 104 104",
|
|
7258
|
+
"aria-hidden": "true"
|
|
7259
|
+
}, (0, react.createElement)("circle", {
|
|
7260
|
+
className: "v3-trk",
|
|
7261
|
+
cx: 52,
|
|
7262
|
+
cy: 52,
|
|
7263
|
+
r: radius
|
|
7264
|
+
}), ...segments), (0, react.createElement)("div", { className: "v3-mid" }, (0, react.createElement)("b", { className: "v3-num" }, formatCallIndexCount(total, props.totalExact)), (0, react.createElement)("span", null, "次调用"))), (0, react.createElement)("div", { className: "v3-src-list" }, ...data.map((item) => (0, react.createElement)("div", {
|
|
7265
|
+
key: item.kind,
|
|
7266
|
+
className: "v3-src",
|
|
7267
|
+
"data-source-row": item.kind,
|
|
7268
|
+
"data-source-count": String(item.count)
|
|
7269
|
+
}, (0, react.createElement)("i", item.count === 0 ? { "data-zero": "true" } : {}), (0, react.createElement)("span", { className: "v3-nm" }, item.label), (0, react.createElement)("b", null, formatCallIndexCount(item.count, props.totalExact)), (0, react.createElement)("em", null, total > 0 ? `${Math.round(item.count / total * 100)}%` : "0%"))))), note);
|
|
6719
7270
|
}
|
|
6720
7271
|
function callIndexDiagnostics(input) {
|
|
6721
7272
|
const items = [];
|
|
@@ -6775,96 +7326,6 @@ function StateNotice({ state, failure }) {
|
|
|
6775
7326
|
"data-call-index-state": state.kind
|
|
6776
7327
|
}, (0, react.createElement)("strong", null, state.title), state.detail === "" ? null : (0, react.createElement)("p", { className: "mc-desc" }, state.detail), failure?.retryable === true && state.canRetry ? (0, react.createElement)("p", { className: "mc-desc" }, "这是一个可重试的错误;重试会重新读取,不会重放旧游标。") : null);
|
|
6777
7328
|
}
|
|
6778
|
-
/** Source distribution bars (bySource). */
|
|
6779
|
-
function CallIndexSourceBars(props) {
|
|
6780
|
-
const data = callIndexSourceData(props.bySource);
|
|
6781
|
-
const present = data.filter((item) => item.count > 0);
|
|
6782
|
-
return (0, react.createElement)("div", {
|
|
6783
|
-
className: "mc-ci-sources",
|
|
6784
|
-
"data-call-index-block": "by-source"
|
|
6785
|
-
}, (0, react.createElement)("h4", {
|
|
6786
|
-
className: "mc-heading",
|
|
6787
|
-
style: { fontSize: 13 }
|
|
6788
|
-
}, "来源分布"), props.bySource === void 0 ? (0, react.createElement)("p", {
|
|
6789
|
-
className: "mc-empty",
|
|
6790
|
-
"data-call-index-missing": "by-source"
|
|
6791
|
-
}, "Host 未返回来源分布(bySource);这不是「全部为 0」。") : (0, react.createElement)("div", {
|
|
6792
|
-
className: "mc-ci-bar",
|
|
6793
|
-
role: "img",
|
|
6794
|
-
"aria-label": "来源分布条"
|
|
6795
|
-
}, ...present.map((item) => (0, react.createElement)("span", {
|
|
6796
|
-
key: item.kind,
|
|
6797
|
-
"data-source-kind": item.kind,
|
|
6798
|
-
title: `${item.label}:${formatCallIndexCount(item.count, props.totalExact)}`,
|
|
6799
|
-
style: {
|
|
6800
|
-
flexGrow: item.count,
|
|
6801
|
-
background: `var(--mc-source-${item.kind}, currentColor)`,
|
|
6802
|
-
opacity: .65
|
|
6803
|
-
}
|
|
6804
|
-
}))), ...data.map((item) => (0, react.createElement)("div", {
|
|
6805
|
-
className: "mc-ci-source-row",
|
|
6806
|
-
key: item.kind,
|
|
6807
|
-
"data-source-row": item.kind
|
|
6808
|
-
}, (0, react.createElement)("span", { className: "mc-source" }, item.label), (0, react.createElement)("span", { className: "mc-ci-num" }, formatCallIndexCount(item.count, props.totalExact)), item.zeroNote === void 0 ? null : (0, react.createElement)("span", {
|
|
6809
|
-
className: "mc-desc",
|
|
6810
|
-
"data-call-index-zero": item.kind
|
|
6811
|
-
}, item.zeroNote))));
|
|
6812
|
-
}
|
|
6813
|
-
/** Self-drawn SVG trend: no chart library, deterministic geometry. */
|
|
6814
|
-
function CallIndexTrendChart(props) {
|
|
6815
|
-
const chart = callIndexTrend(props.aggregates?.byDay);
|
|
6816
|
-
const geometry = CALL_INDEX_TREND_GEOMETRY;
|
|
6817
|
-
if (props.aggregates === void 0) return (0, react.createElement)("div", {
|
|
6818
|
-
className: "mc-ci-trend",
|
|
6819
|
-
"data-call-index-block": "trend"
|
|
6820
|
-
}, (0, react.createElement)("h4", {
|
|
6821
|
-
className: "mc-heading",
|
|
6822
|
-
style: { fontSize: 13 }
|
|
6823
|
-
}, "按日趋势"), (0, react.createElement)("p", {
|
|
6824
|
-
className: "mc-empty",
|
|
6825
|
-
"data-call-index-missing": "aggregates"
|
|
6826
|
-
}, "Host 未返回按日聚合(aggregates);这不是「没有调用」,趋势不可用。"));
|
|
6827
|
-
if (!chart.available) return (0, react.createElement)("div", {
|
|
6828
|
-
className: "mc-ci-trend",
|
|
6829
|
-
"data-call-index-block": "trend"
|
|
6830
|
-
}, (0, react.createElement)("h4", {
|
|
6831
|
-
className: "mc-heading",
|
|
6832
|
-
style: { fontSize: 13 }
|
|
6833
|
-
}, "按日趋势"), (0, react.createElement)("p", {
|
|
6834
|
-
className: "mc-empty",
|
|
6835
|
-
"data-call-index-missing": "by-day"
|
|
6836
|
-
}, "该筛选在所选范围内没有任何按日桶,趋势为空;单个日粒度为 0 不参与绘制。"));
|
|
6837
|
-
return (0, react.createElement)("div", {
|
|
6838
|
-
className: "mc-ci-trend",
|
|
6839
|
-
"data-call-index-block": "trend"
|
|
6840
|
-
}, (0, react.createElement)("h4", {
|
|
6841
|
-
className: "mc-heading",
|
|
6842
|
-
style: { fontSize: 13 }
|
|
6843
|
-
}, "按日趋势"), (0, react.createElement)("svg", {
|
|
6844
|
-
viewBox: `0 0 ${geometry.width} ${geometry.height}`,
|
|
6845
|
-
role: "img",
|
|
6846
|
-
"aria-label": `按日调用趋势(已索引部分):${chart.bars.length} 天,峰值 ${chart.maxCalls} 次`,
|
|
6847
|
-
"data-call-index-svg": "trend"
|
|
6848
|
-
}, ...chart.bars.map((bar) => (0, react.createElement)("rect", {
|
|
6849
|
-
key: bar.day,
|
|
6850
|
-
x: bar.x,
|
|
6851
|
-
y: bar.y,
|
|
6852
|
-
width: bar.width,
|
|
6853
|
-
height: bar.height,
|
|
6854
|
-
"data-day": bar.day,
|
|
6855
|
-
"data-calls": bar.calls,
|
|
6856
|
-
fill: bar.error > 0 ? "var(--mc-trend-error, currentColor)" : "currentColor",
|
|
6857
|
-
opacity: bar.error > 0 ? .85 : .55
|
|
6858
|
-
})), ...chart.bars.map((bar) => (0, react.createElement)("text", {
|
|
6859
|
-
key: `label-${bar.day}`,
|
|
6860
|
-
x: bar.x + bar.width / 2,
|
|
6861
|
-
y: geometry.height - 6,
|
|
6862
|
-
"font-size": 9,
|
|
6863
|
-
"text-anchor": "middle",
|
|
6864
|
-
fill: "currentColor",
|
|
6865
|
-
opacity: .7
|
|
6866
|
-
}, bar.day.slice(5)))), (0, react.createElement)("p", { className: "mc-desc" }, `图上 ${chart.bars.length} 天、${chart.totalCalls} 次调用;峰值 ${chart.maxCalls} 次/天。柱高按峰值归一。趋势只统计已索引部分,覆盖不完整时右侧天数与总数都可能偏少。`));
|
|
6867
|
-
}
|
|
6868
7329
|
/**
|
|
6869
7330
|
* Turn the index census into a progress percentage.
|
|
6870
7331
|
*
|
|
@@ -7033,26 +7494,73 @@ function callIndexToolRows(page) {
|
|
|
7033
7494
|
...entry.durationCount === 0 ? {} : { averageDurationMs: entry.durationSum / entry.durationCount }
|
|
7034
7495
|
})).sort((a, b) => b.calls - a.calls || a.toolName.localeCompare(b.toolName));
|
|
7035
7496
|
}
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7497
|
+
/** Short source marker for the tool cell: MCP / 技能 / 插件 / 来源未解析. */
|
|
7498
|
+
function sourceNoteForKind(kind) {
|
|
7499
|
+
switch (kind) {
|
|
7500
|
+
case "mcp": return "MCP";
|
|
7501
|
+
case "skill": return "技能";
|
|
7502
|
+
case "plugin-tool": return "插件";
|
|
7503
|
+
case "unknown": return "来源未解析";
|
|
7504
|
+
default: return;
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
/**
|
|
7508
|
+
* The v3 "完整工具表": one sticky-header row per TOOL, inside a bounded
|
|
7509
|
+
* `.v3-ts` scroller (330px here — the two charts above it already tell the big
|
|
7510
|
+
* picture). Takes pre-computed rows so the same filtered set feeds the charts.
|
|
7511
|
+
*/
|
|
7512
|
+
function CallIndexToolTable(props) {
|
|
7513
|
+
return (0, react.createElement)("div", { className: "v3-ts v3-ts--h330" }, (0, react.createElement)("table", {
|
|
7514
|
+
className: "v3-table",
|
|
7041
7515
|
"data-call-index-table": "tools"
|
|
7042
7516
|
}, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, ...[
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7517
|
+
{
|
|
7518
|
+
label: "工具",
|
|
7519
|
+
right: false
|
|
7520
|
+
},
|
|
7521
|
+
{
|
|
7522
|
+
label: "调用",
|
|
7523
|
+
right: true
|
|
7524
|
+
},
|
|
7525
|
+
{
|
|
7526
|
+
label: "失败",
|
|
7527
|
+
right: true
|
|
7528
|
+
},
|
|
7529
|
+
{
|
|
7530
|
+
label: "成功率",
|
|
7531
|
+
right: true
|
|
7532
|
+
},
|
|
7533
|
+
{
|
|
7534
|
+
label: "平均耗时",
|
|
7535
|
+
right: true
|
|
7536
|
+
},
|
|
7537
|
+
{
|
|
7538
|
+
label: "最近调用",
|
|
7539
|
+
right: true
|
|
7540
|
+
}
|
|
7541
|
+
].map((col) => (0, react.createElement)("th", {
|
|
7542
|
+
key: col.label,
|
|
7543
|
+
scope: "col",
|
|
7544
|
+
...col.right ? { className: "v3-r" } : {}
|
|
7545
|
+
}, col.label)))), (0, react.createElement)("tbody", null, ...props.rows.map((row) => {
|
|
7546
|
+
const known = row.success + row.error;
|
|
7547
|
+
const rate = known > 0 ? row.success / known : void 0;
|
|
7548
|
+
const bad = row.error > 0 && row.error / row.calls >= .2;
|
|
7549
|
+
const note = sourceNoteForKind(row.sourceKind);
|
|
7550
|
+
const server = row.toolName.startsWith("mcp__") ? row.toolName.split("__")[1] : void 0;
|
|
7551
|
+
const shortName = row.sourceKind === "mcp" && row.toolName.startsWith("mcp__") ? row.toolName.split("__").slice(2).join("__") : row.toolName;
|
|
7552
|
+
return (0, react.createElement)("tr", {
|
|
7553
|
+
key: row.toolName + "::" + row.sourceKind,
|
|
7554
|
+
"data-call-index-tool": row.toolName
|
|
7555
|
+
}, (0, react.createElement)("td", null, (0, react.createElement)("div", { className: "v3-c-tool" }, note === void 0 ? null : (0, react.createElement)("span", { className: "v3-srcnote" }, note), (0, react.createElement)("span", {
|
|
7556
|
+
className: "v3-nm",
|
|
7557
|
+
title: row.toolName
|
|
7558
|
+
}, shortName), server === void 0 ? null : (0, react.createElement)("span", { className: "v3-srv" }, server))), (0, react.createElement)("td", { className: "v3-r" }, String(row.calls)), (0, react.createElement)("td", { className: "v3-r" }, String(row.error)), (0, react.createElement)("td", { className: "v3-r" }, (0, react.createElement)("span", { className: "v3-rt" }, (0, react.createElement)("span", { className: "v3-tk" }, (0, react.createElement)("span", {
|
|
7559
|
+
className: "v3-fl",
|
|
7560
|
+
...bad ? { "data-bad": "true" } : {},
|
|
7561
|
+
style: { width: `${Math.round((rate ?? 0) * 100)}%` }
|
|
7562
|
+
})), (0, react.createElement)("b", null, formatCallIndexRate(rate)))), (0, react.createElement)("td", { className: "v3-r" }, formatCallIndexDuration(row.averageDurationMs)), (0, react.createElement)("td", { className: "v3-r" }, formatCallIndexTime(row.lastAt)));
|
|
7563
|
+
}))));
|
|
7056
7564
|
}
|
|
7057
7565
|
function callIndexRates(summary) {
|
|
7058
7566
|
if (summary === void 0) return {};
|
|
@@ -7084,7 +7592,7 @@ function formatCallIndexRate(value) {
|
|
|
7084
7592
|
* Modal; the container decides it from a media query, so this stays pure.
|
|
7085
7593
|
*/
|
|
7086
7594
|
function CallIndexDetailPanelView(props) {
|
|
7087
|
-
const { query, status, page, loading, loadingMore, failure, onQueryChange, onResolveScope, catalog, moreFailure, startFailure, startTask } = props;
|
|
7595
|
+
const { query, status, page, loading, loadingMore, failure, onQueryChange, onResolveScope, catalog, moreFailure, startFailure, startTask, search, onSearchChange } = props;
|
|
7088
7596
|
const state = callIndexDetailState({
|
|
7089
7597
|
status,
|
|
7090
7598
|
page,
|
|
@@ -7093,95 +7601,362 @@ function CallIndexDetailPanelView(props) {
|
|
|
7093
7601
|
...failure === void 0 ? {} : { failure }
|
|
7094
7602
|
});
|
|
7095
7603
|
const totalText = page === void 0 ? void 0 : formatCallIndexCount(page.total ?? page.summary.total, page.totalExact);
|
|
7096
|
-
const
|
|
7604
|
+
const scanning = status !== void 0 && (status.phase === "running" || (status.sessions?.pending ?? 0) > 0 || (status.sessions?.pendingLive ?? 0) > 0);
|
|
7605
|
+
const statusText = page === void 0 ? loading === true ? "正在读取调用明细…" : "尚未读取调用明细" : scanning ? "索引仍在扫描,数字会继续增加" : "索引已完成";
|
|
7097
7606
|
const rates = callIndexRates(page?.summary);
|
|
7098
7607
|
const successStats = rates.success;
|
|
7099
7608
|
const failureStats = rates.failure;
|
|
7100
7609
|
const rateText = (value) => formatCallIndexRate(value);
|
|
7101
7610
|
const rateHint = (stats) => stats === void 0 ? "没有已知结果的调用,无法计算比例" : `${stats.numerator} / ${stats.denominator} 次已知结果`;
|
|
7102
7611
|
const suggestionCount = page === void 0 ? void 0 : (page.summary.unresolved ?? 0) + page.summary.error;
|
|
7103
|
-
const suggestionText = () => suggestionCount === void 0 ? "—" : String(suggestionCount);
|
|
7104
7612
|
const suggestionHint = () => page === void 0 ? "尚未读取" : `失败 ${page.summary.error} + 未配对 ${page.summary.unresolved ?? 0};未配对表示没有捕获到结果,不等于失败`;
|
|
7613
|
+
const allRows = page === void 0 ? [] : callIndexToolRows(page);
|
|
7614
|
+
const term = (search ?? "").trim().toLowerCase();
|
|
7615
|
+
const rows = term === "" ? allRows : allRows.filter((row) => row.toolName.toLowerCase().includes(term));
|
|
7616
|
+
const durations = page === void 0 ? [] : page.items.map((item) => item.durationMs).filter((ms) => ms !== void 0 && Number.isFinite(ms));
|
|
7617
|
+
const averageDuration = durations.length > 0 ? durations.reduce((sum, ms) => sum + ms, 0) / durations.length : void 0;
|
|
7618
|
+
const readCalls = rows.reduce((sum, row) => sum + row.calls, 0);
|
|
7619
|
+
const totalCalls = page === void 0 ? void 0 : page.total ?? page.summary.total;
|
|
7620
|
+
const readIsPartial = totalCalls !== void 0 && readCalls < totalCalls;
|
|
7621
|
+
const rankRows = rows.slice(0, 12);
|
|
7622
|
+
const rankMax = rankRows.length > 0 ? rankRows[0].calls : 1;
|
|
7623
|
+
const failingRows = rows.filter((row) => row.error > 0).slice().sort((a, b) => b.error - a.error);
|
|
7624
|
+
const failMax = failingRows.length > 0 ? failingRows[0].error : 1;
|
|
7625
|
+
const searchInput = (0, react.createElement)("label", { className: "v3-search" }, (0, react.createElement)("svg", {
|
|
7626
|
+
width: 12,
|
|
7627
|
+
height: 12,
|
|
7628
|
+
viewBox: "0 0 14 14",
|
|
7629
|
+
fill: "none",
|
|
7630
|
+
"aria-hidden": "true",
|
|
7631
|
+
style: {
|
|
7632
|
+
color: "var(--v3-t4)",
|
|
7633
|
+
flex: "none"
|
|
7634
|
+
}
|
|
7635
|
+
}, (0, react.createElement)("circle", {
|
|
7636
|
+
cx: 6.2,
|
|
7637
|
+
cy: 6.2,
|
|
7638
|
+
r: 4.4,
|
|
7639
|
+
stroke: "currentColor",
|
|
7640
|
+
"stroke-width": 1.3
|
|
7641
|
+
}), (0, react.createElement)("path", {
|
|
7642
|
+
d: "m9.6 9.6 3 3",
|
|
7643
|
+
stroke: "currentColor",
|
|
7644
|
+
"stroke-width": 1.3,
|
|
7645
|
+
"stroke-linecap": "round"
|
|
7646
|
+
})), (0, react.createElement)("input", onSearchChange === void 0 ? {
|
|
7647
|
+
type: "search",
|
|
7648
|
+
placeholder: "筛选工具名…",
|
|
7649
|
+
"aria-label": "筛选工具名",
|
|
7650
|
+
defaultValue: search ?? "",
|
|
7651
|
+
"data-call-index-search": "true"
|
|
7652
|
+
} : {
|
|
7653
|
+
type: "search",
|
|
7654
|
+
placeholder: "筛选工具名…",
|
|
7655
|
+
"aria-label": "筛选工具名",
|
|
7656
|
+
value: search ?? "",
|
|
7657
|
+
"data-call-index-search": "true",
|
|
7658
|
+
onChange: (event) => onSearchChange(event.target.value)
|
|
7659
|
+
}));
|
|
7105
7660
|
const indexBlockedReason = status === void 0 ? "尚未收到索引状态,无法判断索引进度(Host 未应答或回复被拒绝)" : status.scope.status !== "ready" ? "索引范围未启用,插件不会自动索引历史会话" : void 0;
|
|
7106
7661
|
return (0, react.createElement)("section", {
|
|
7107
|
-
className: "mc-section",
|
|
7662
|
+
className: "v3-root mc-section",
|
|
7108
7663
|
"aria-label": "调用明细",
|
|
7109
7664
|
"aria-busy": loading === true
|
|
7110
|
-
}, CallIndexStyles(), (
|
|
7111
|
-
className: "
|
|
7112
|
-
"data-
|
|
7113
|
-
|
|
7114
|
-
|
|
7665
|
+
}, CallIndexStyles(), V3Styles(), (0, react.createElement)("div", { className: "v3-status-pills" }, (0, react.createElement)("span", {
|
|
7666
|
+
className: "v3-statusline",
|
|
7667
|
+
...state.kind === "failure" || state.kind === "disconnected" ? { "data-tone": "bad" } : {}
|
|
7668
|
+
}, (0, react.createElement)("span", {
|
|
7669
|
+
className: "v3-dot",
|
|
7670
|
+
"data-tone": state.kind === "failure" || state.kind === "disconnected" ? "bad" : scanning ? "run" : "ok",
|
|
7671
|
+
"aria-hidden": "true"
|
|
7672
|
+
}), (0, react.createElement)("span", null, statusText))), (0, react.createElement)("div", { className: "v3-panel v3-filtercard" }, (0, react.createElement)("div", { className: "v3-filter-row" }, CallIndexFilters({
|
|
7115
7673
|
query,
|
|
7116
7674
|
...onQueryChange === void 0 ? {} : { onChange: onQueryChange },
|
|
7117
7675
|
...onResolveScope === void 0 ? {} : { onResolveScope },
|
|
7118
7676
|
...catalog === void 0 ? {} : { catalog }
|
|
7119
|
-
}), CallIndexScanBar({ ...status === void 0 ? {} : { status } }), (0, react.createElement)(StateNotice, {
|
|
7677
|
+
}), searchInput), CallIndexScanBar({ ...status === void 0 ? {} : { status } })), (0, react.createElement)(StateNotice, {
|
|
7120
7678
|
state,
|
|
7121
7679
|
...failure === void 0 ? {} : { failure }
|
|
7122
7680
|
}), startFailure === void 0 ? null : (0, react.createElement)("p", {
|
|
7123
|
-
className: "
|
|
7681
|
+
className: "v3-hint",
|
|
7124
7682
|
"data-call-index-note": "start-failure",
|
|
7125
7683
|
role: "status"
|
|
7126
7684
|
}, `自动索引失败:${callIndexFailureText(startFailure)}`), startTask === void 0 ? null : (0, react.createElement)("p", {
|
|
7127
|
-
className: "
|
|
7685
|
+
className: "v3-hint",
|
|
7128
7686
|
"data-call-index-note": "start-task"
|
|
7129
|
-
}, `自动索引:${startTask.phase}${startTask.mode === "full" ? "(全量)" : "(增量)"} · 任务 ${startTask.taskId.slice(0, 8)}`),
|
|
7687
|
+
}, `自动索引:${startTask.phase}${startTask.mode === "full" ? "(全量)" : "(增量)"} · 任务 ${startTask.taskId.slice(0, 8)}`), indexBlockedReason === void 0 ? null : (0, react.createElement)("p", {
|
|
7688
|
+
className: "v3-hint",
|
|
7689
|
+
"data-call-index-note": "index-blocked",
|
|
7690
|
+
role: "status"
|
|
7691
|
+
}, `自动索引不可用:${indexBlockedReason}`), CallIndexDiagnostics({
|
|
7130
7692
|
...status === void 0 ? {} : { status },
|
|
7131
7693
|
...page === void 0 ? {} : { page },
|
|
7132
7694
|
...failure === void 0 ? {} : { failure },
|
|
7133
7695
|
...startFailure === void 0 ? {} : { startFailure },
|
|
7134
7696
|
...moreFailure === void 0 ? {} : { moreFailure }
|
|
7135
7697
|
}), (0, react.createElement)("div", {
|
|
7136
|
-
className: "
|
|
7698
|
+
className: "v3-stats",
|
|
7137
7699
|
"data-call-index-summary": "rates"
|
|
7138
|
-
},
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7700
|
+
}, V3Stat({
|
|
7701
|
+
label: "成功率",
|
|
7702
|
+
value: rateText(successStats?.rate),
|
|
7703
|
+
hint: totalText === void 0 ? rateHint(successStats) : `${rateHint(successStats)} · 本页合计 ${totalText}`
|
|
7704
|
+
}), V3Stat({
|
|
7705
|
+
label: "失败率",
|
|
7706
|
+
value: rateText(failureStats?.rate),
|
|
7707
|
+
hint: rateHint(failureStats)
|
|
7708
|
+
}), V3Stat({
|
|
7709
|
+
label: "需要关注",
|
|
7710
|
+
value: suggestionCount === void 0 ? "—" : String(suggestionCount),
|
|
7711
|
+
hint: suggestionHint()
|
|
7712
|
+
}), V3Stat({
|
|
7713
|
+
label: "平均耗时",
|
|
7714
|
+
value: formatCallIndexDuration(averageDuration),
|
|
7715
|
+
hint: "按有耗时的调用平均"
|
|
7716
|
+
})), page !== void 0 && page.droppedItems > 0 ? (0, react.createElement)("p", {
|
|
7717
|
+
className: "v3-hint",
|
|
7718
|
+
"data-call-index-note": "dropped",
|
|
7719
|
+
style: { marginTop: 8 }
|
|
7720
|
+
}, `本页有 ${page.droppedItems} 行未通过白名单投影(字段不合法),已丢弃,未计入合计。`) : null, (0, react.createElement)("div", {
|
|
7721
|
+
className: "v3-board",
|
|
7722
|
+
style: { marginTop: 12 }
|
|
7723
|
+
}, (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "调用量分布(本范围工具)"), (0, react.createElement)("span", { className: "v3-pn" }, term === "" ? "前 12 名" : "筛选结果")), rankRows.length === 0 ? (0, react.createElement)("p", { className: "v3-hint" }, term === "" ? "当前范围没有工具调用。" : "没有匹配的工具。") : (0, react.createElement)("div", { className: "v3-rank" }, ...rankRows.map((row) => {
|
|
7724
|
+
const bad = row.error > 0 && row.error / row.calls >= .2;
|
|
7725
|
+
return (0, react.createElement)("div", {
|
|
7726
|
+
key: row.toolName + "::" + row.sourceKind,
|
|
7727
|
+
className: "v3-rank-row",
|
|
7728
|
+
title: row.toolName
|
|
7729
|
+
}, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
|
|
7730
|
+
...bad ? { "data-bad": "true" } : {},
|
|
7731
|
+
style: { width: `${Math.max(3, Math.round(row.calls / rankMax * 100))}%` }
|
|
7732
|
+
})), (0, react.createElement)("span", { className: "v3-n" }, String(row.calls)));
|
|
7733
|
+
})), (0, react.createElement)("p", {
|
|
7734
|
+
className: "v3-hint",
|
|
7735
|
+
style: { marginTop: 9 }
|
|
7736
|
+
}, `条长 = 调用量,按最大值归一;失败率 ≥ 20% 的条变红。${readIsPartial ? `本页已读取 ${formatCallIndexCount(readCalls, page?.totalExact)} 次调用,全部共 ${formatCallIndexCount(totalCalls, page?.totalExact)} 次,未读取部分不参与排行。` : ""}`)), (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "失败与耗时"), (0, react.createElement)("span", { className: "v3-pn" }, "按失败数排序")), failingRows.length === 0 ? (0, react.createElement)("p", { className: "v3-hint" }, "当前范围没有失败调用。") : (0, react.createElement)("div", { className: "v3-rank" }, ...failingRows.slice(0, 8).map((row) => {
|
|
7737
|
+
const perTool = Math.round(row.error / row.calls * 100);
|
|
7738
|
+
return (0, react.createElement)("div", {
|
|
7739
|
+
key: "fail-" + row.toolName + "::" + row.sourceKind,
|
|
7740
|
+
className: "v3-rank-row",
|
|
7741
|
+
title: row.toolName
|
|
7742
|
+
}, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
|
|
7743
|
+
"data-bad": "true",
|
|
7744
|
+
style: { width: `${Math.max(4, Math.round(row.error / failMax * 100))}%` }
|
|
7745
|
+
})), (0, react.createElement)("span", {
|
|
7746
|
+
className: "v3-n",
|
|
7747
|
+
style: {
|
|
7748
|
+
color: "var(--v3-bad)",
|
|
7749
|
+
fontWeight: 600
|
|
7750
|
+
}
|
|
7751
|
+
}, `${row.error} 次`), (0, react.createElement)("span", {
|
|
7752
|
+
className: "v3-e",
|
|
7753
|
+
"data-bad": "true"
|
|
7754
|
+
}, `占该工具 ${perTool}%`));
|
|
7755
|
+
})), (0, react.createElement)("p", {
|
|
7756
|
+
className: "v3-hint",
|
|
7757
|
+
style: { marginTop: 9 }
|
|
7758
|
+
}, `条长 = 失败次数(不是失败率)。共 ${failingRows.length} 个工具出现过失败。`))), (0, react.createElement)("div", {
|
|
7759
|
+
className: "v3-panel",
|
|
7760
|
+
style: { marginTop: 12 }
|
|
7761
|
+
}, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "完整工具表"), (0, react.createElement)("span", { className: "v3-pn" }, `${rows.length} 行 · 表内滚动`)), (0, react.createElement)(CallIndexToolTable, { rows }), (0, react.createElement)("div", { className: "v3-tfoot" }, (0, react.createElement)("span", null, `${rows.length} 个工具 · 表内滚动`))), page?.nextCursor !== void 0 && loadingMore === true ? (0, react.createElement)("p", {
|
|
7762
|
+
className: "v3-hint",
|
|
7763
|
+
"data-call-index-note": "loading-more",
|
|
7764
|
+
style: { marginTop: 8 }
|
|
7144
7765
|
}, "正在加载其余工具…") : null, moreFailure === void 0 ? null : (0, react.createElement)("p", {
|
|
7145
|
-
className: "
|
|
7766
|
+
className: "v3-hint",
|
|
7146
7767
|
"data-call-index-note": "more-failure",
|
|
7147
|
-
role: "status"
|
|
7148
|
-
|
|
7768
|
+
role: "status",
|
|
7769
|
+
style: { marginTop: 8 }
|
|
7770
|
+
}, `加载失败:${callIndexFailureText(moreFailure)}`));
|
|
7149
7771
|
}
|
|
7150
|
-
/** Pure view for
|
|
7772
|
+
/** Pure view for 总览 (v3 看板). */
|
|
7151
7773
|
function CallIndexOverviewPanelView(props) {
|
|
7152
|
-
const { status, overview, loading, failure, onRefresh } = props;
|
|
7774
|
+
const { status, overview, loading, failure, onRefresh, tools, toolsLoading, toolsFailure } = props;
|
|
7153
7775
|
const metrics = callIndexOverviewMetrics(overview, status);
|
|
7154
|
-
const rangeLabel = callIndexRangeLabel("all");
|
|
7155
7776
|
const exact = metrics.callsTotalExact;
|
|
7156
7777
|
const mix = callIndexOutcomeMix(overview?.summary);
|
|
7157
7778
|
const aggregates = metrics.aggregates;
|
|
7158
7779
|
const notice = failure === void 0 ? void 0 : failure.disconnected ? `与 Host 断连:${callIndexFailureText(failure)}${failure.hostCode === void 0 ? "" : `(${failure.hostCode})`}` : callIndexFailureText(failure);
|
|
7780
|
+
const coverageLabel = status === void 0 ? "未知" : COVERAGE_LABELS[status.coverage];
|
|
7781
|
+
const scanning = status !== void 0 && (status.phase === "running" || (status.sessions?.pending ?? 0) > 0 || (status.sessions?.pendingLive ?? 0) > 0);
|
|
7782
|
+
const pendingSessions = status?.sessions?.pending ?? 0;
|
|
7783
|
+
const scanPhrase = status !== void 0 && status.phase === "running" ? "仍在扫描" : pendingSessions > 0 ? `还有 ${pendingSessions} 个会话待补扫` : "扫描已完成";
|
|
7784
|
+
const statusSentence = `已索引 ${metrics.sessionsListed ?? "—"} 个会话 · 覆盖${coverageLabel} · ${scanPhrase}`;
|
|
7785
|
+
const coverageHint = exact === true ? (0, react.createElement)("span", { style: {
|
|
7786
|
+
display: "inline-flex",
|
|
7787
|
+
alignItems: "center",
|
|
7788
|
+
gap: 5
|
|
7789
|
+
} }, (0, react.createElement)("span", {
|
|
7790
|
+
className: "v3-dot",
|
|
7791
|
+
"data-tone": "ok",
|
|
7792
|
+
"aria-hidden": "true"
|
|
7793
|
+
}), "覆盖完整,计数精确") : (0, react.createElement)("span", { style: {
|
|
7794
|
+
display: "inline-flex",
|
|
7795
|
+
alignItems: "center",
|
|
7796
|
+
gap: 5
|
|
7797
|
+
} }, (0, react.createElement)("span", {
|
|
7798
|
+
className: "v3-dot",
|
|
7799
|
+
"data-tone": "warn",
|
|
7800
|
+
"aria-hidden": "true"
|
|
7801
|
+
}), "覆盖部分,实际更多");
|
|
7802
|
+
const failHint = (0, react.createElement)("span", { style: {
|
|
7803
|
+
display: "inline-flex",
|
|
7804
|
+
alignItems: "center",
|
|
7805
|
+
gap: 5
|
|
7806
|
+
} }, (0, react.createElement)("span", {
|
|
7807
|
+
className: "v3-dot",
|
|
7808
|
+
"data-tone": "bad",
|
|
7809
|
+
"aria-hidden": "true"
|
|
7810
|
+
}), `另有 ${formatCallIndexCount(mix?.unresolved, exact)} 次未配对`);
|
|
7811
|
+
const rates = callIndexRates(overview?.summary);
|
|
7812
|
+
const rateHint = (stats) => stats === void 0 ? "没有已知结果的调用" : `${stats.numerator} / ${stats.denominator} 次已知结果`;
|
|
7813
|
+
const trend = callIndexTrend(aggregates?.byDay);
|
|
7814
|
+
const callSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.calls);
|
|
7815
|
+
const successSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.calls > 0 ? bucket.success / bucket.calls : 0);
|
|
7816
|
+
const errorSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.error);
|
|
7817
|
+
const totalToolCalls = tools === void 0 ? 0 : tools.reduce((sum, row) => sum + row.calls, 0);
|
|
7818
|
+
const topRows = tools === void 0 ? void 0 : tools.slice(0, 12);
|
|
7819
|
+
const rankMax = topRows !== void 0 && topRows.length > 0 ? topRows[0].calls : 1;
|
|
7820
|
+
const topCoverage = topRows === void 0 || totalToolCalls === 0 ? void 0 : Math.round(topRows.reduce((sum, row) => sum + row.calls, 0) / totalToolCalls * 100);
|
|
7821
|
+
const failingRows = tools === void 0 ? void 0 : tools.filter((row) => row.error > 0).slice().sort((a, b) => b.error - a.error);
|
|
7822
|
+
const failingTop = failingRows === void 0 ? [] : failingRows.slice(0, 3);
|
|
7823
|
+
const failingRest = failingRows === void 0 ? [] : failingRows.slice(3);
|
|
7824
|
+
const failingRestErrors = failingRest.reduce((sum, row) => sum + row.error, 0);
|
|
7825
|
+
const toolsNotice = tools === void 0 ? (0, react.createElement)("p", {
|
|
7826
|
+
className: "v3-hint",
|
|
7827
|
+
"data-call-index-note": "tools"
|
|
7828
|
+
}, toolsFailure !== void 0 ? `逐工具明细读取失败:${callIndexFailureText(toolsFailure)}` : toolsLoading === true ? "正在读取逐工具明细…" : "尚未读取逐工具明细;上方调用总量与趋势来自总览快照,排行需要逐工具数据。") : tools.length === 0 ? (0, react.createElement)("p", {
|
|
7829
|
+
className: "v3-hint",
|
|
7830
|
+
"data-call-index-note": "tools"
|
|
7831
|
+
}, "该范围没有逐工具调用记录。") : null;
|
|
7832
|
+
const sessionInfo = status?.sessions;
|
|
7833
|
+
const taskNote = metrics.tasks === void 0 ? null : (0, react.createElement)("p", {
|
|
7834
|
+
className: "v3-hint",
|
|
7835
|
+
"data-call-index-note": "task",
|
|
7836
|
+
style: { marginTop: 9 }
|
|
7837
|
+
}, `最近一次扫描:${metrics.tasks.mode} / ${metrics.tasks.phase}(开始 ${formatCallIndexTime(metrics.tasks.startedAt)})`);
|
|
7159
7838
|
return (0, react.createElement)("section", {
|
|
7160
|
-
className: "mc-section",
|
|
7839
|
+
className: "v3-root mc-section",
|
|
7161
7840
|
"aria-label": "调用统计总览",
|
|
7162
7841
|
"aria-busy": loading === true
|
|
7163
|
-
}, CallIndexStyles(), (
|
|
7842
|
+
}, CallIndexStyles(), V3Styles(), (0, react.createElement)("div", { style: {
|
|
7843
|
+
display: "flex",
|
|
7844
|
+
justifyContent: "flex-end"
|
|
7845
|
+
} }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
7164
7846
|
size: "sm",
|
|
7165
7847
|
variant: "outline",
|
|
7166
7848
|
disabled: loading === true || onRefresh === void 0,
|
|
7167
7849
|
onClick: () => onRefresh?.()
|
|
7168
|
-
}, "刷新"))
|
|
7850
|
+
}, "刷新")), notice === void 0 ? null : (0, react.createElement)("div", {
|
|
7169
7851
|
className: "mc-notice",
|
|
7170
7852
|
role: failure?.disconnected === true ? "alert" : "status",
|
|
7171
7853
|
"data-call-index-state": failure?.disconnected === true ? "disconnected" : "failure"
|
|
7172
|
-
}, notice), (0, react.createElement)("div", {
|
|
7173
|
-
className: "
|
|
7854
|
+
}, notice), (0, react.createElement)("div", { className: "v3-status-pills" }, (0, react.createElement)("span", { className: "v3-statusline" }, (0, react.createElement)("span", {
|
|
7855
|
+
className: "v3-dot",
|
|
7856
|
+
"data-tone": scanning ? "run" : "ok",
|
|
7857
|
+
"aria-hidden": "true"
|
|
7858
|
+
}), (0, react.createElement)("span", null, statusSentence))), (0, react.createElement)("div", {
|
|
7859
|
+
className: "v3-stats",
|
|
7174
7860
|
"data-call-index-metrics": "overview"
|
|
7175
|
-
},
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7861
|
+
}, V3Stat({
|
|
7862
|
+
label: "调用次数",
|
|
7863
|
+
value: formatCallIndexCount(metrics.calls, exact),
|
|
7864
|
+
hint: coverageHint,
|
|
7865
|
+
...callSpark === void 0 ? {} : { spark: Sparkline({
|
|
7866
|
+
values: callSpark,
|
|
7867
|
+
tone: "info"
|
|
7868
|
+
}) }
|
|
7869
|
+
}), V3Stat({
|
|
7870
|
+
label: "成功率",
|
|
7871
|
+
value: formatCallIndexRate(rates.success?.rate),
|
|
7872
|
+
hint: rateHint(rates.success),
|
|
7873
|
+
...successSpark === void 0 ? {} : { spark: Sparkline({
|
|
7874
|
+
values: successSpark,
|
|
7875
|
+
tone: "ok"
|
|
7876
|
+
}) }
|
|
7877
|
+
}), V3Stat({
|
|
7878
|
+
label: "失败",
|
|
7879
|
+
value: formatCallIndexCount(metrics.failed, exact),
|
|
7880
|
+
hint: failHint,
|
|
7881
|
+
...errorSpark === void 0 ? {} : { spark: Sparkline({
|
|
7882
|
+
values: errorSpark,
|
|
7883
|
+
tone: "bad"
|
|
7884
|
+
}) }
|
|
7885
|
+
}), V3Stat({
|
|
7886
|
+
label: "不同工具",
|
|
7887
|
+
value: formatCallIndexCount(metrics.distinctTools, exact),
|
|
7888
|
+
hint: "按 toolName 去重"
|
|
7889
|
+
})), (0, react.createElement)("div", {
|
|
7890
|
+
className: "v3-board",
|
|
7891
|
+
style: { marginTop: 12 }
|
|
7892
|
+
}, (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "按日调用趋势"), (0, react.createElement)("span", { className: "v3-pn" }, aggregates === void 0 ? "趋势不可用" : `合计 ${trend.totalCalls} 次 · 峰值 ${trend.maxCalls} 次`)), aggregates === void 0 ? (0, react.createElement)("p", {
|
|
7893
|
+
className: "v3-hint",
|
|
7894
|
+
"data-call-index-missing": "aggregates"
|
|
7895
|
+
}, "Host 未返回按日聚合(aggregates);这不是「没有调用」,趋势不可用。") : !trend.available ? (0, react.createElement)("p", {
|
|
7896
|
+
className: "v3-hint",
|
|
7897
|
+
"data-call-index-missing": "by-day"
|
|
7898
|
+
}, "该筛选在所选范围内没有任何按日桶,趋势为空;单个日粒度为 0 不参与绘制。") : (0, react.createElement)(OverviewTrendSvg, { chart: trend }), aggregates === void 0 || !trend.available ? null : (0, react.createElement)("div", { className: "v3-legend" }, (0, react.createElement)("span", null, (0, react.createElement)("i", { style: { background: "var(--v3-info)" } }), "每日调用量(折线 + 面积)"), (0, react.createElement)("span", null, (0, react.createElement)("i", {
|
|
7899
|
+
"data-mark": "square",
|
|
7900
|
+
style: { background: "var(--v3-bad)" }
|
|
7901
|
+
}), "每日失败数(底柱)"), (0, react.createElement)("span", null, (0, react.createElement)("i", { style: {
|
|
7902
|
+
background: "var(--v3-info)",
|
|
7903
|
+
opacity: .3,
|
|
7904
|
+
height: 8,
|
|
7905
|
+
borderRadius: 2
|
|
7906
|
+
} }), "最近一周"))), OverviewSourcePanel({
|
|
7907
|
+
...tools === void 0 ? {} : { tools },
|
|
7180
7908
|
...exact === void 0 ? {} : { totalExact: exact }
|
|
7181
|
-
}),
|
|
7182
|
-
className: "
|
|
7183
|
-
|
|
7184
|
-
},
|
|
7909
|
+
})), (0, react.createElement)("div", {
|
|
7910
|
+
className: "v3-board",
|
|
7911
|
+
style: { marginTop: 12 }
|
|
7912
|
+
}, (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "工具调用排行"), (0, react.createElement)("span", { className: "v3-pn" }, topRows === void 0 ? "前 12 名" : `前 12 名${topCoverage === void 0 ? "" : ` · 占已读取调用的 ${topCoverage}%`}` + (metrics.calls !== void 0 && totalToolCalls < metrics.calls ? ` · 已读取 ${formatCallIndexCount(totalToolCalls, exact)} / 共 ${formatCallIndexCount(metrics.calls, exact)} 次` : ""))), toolsNotice !== null ? toolsNotice : (0, react.createElement)("div", { className: "v3-rank" }, ...topRows.map((row) => {
|
|
7913
|
+
const bad = row.error > 0 && row.error / row.calls >= .2;
|
|
7914
|
+
return (0, react.createElement)("div", {
|
|
7915
|
+
key: "rank-" + row.toolName + "::" + row.sourceKind,
|
|
7916
|
+
className: "v3-rank-row",
|
|
7917
|
+
title: row.toolName
|
|
7918
|
+
}, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
|
|
7919
|
+
...bad ? { "data-bad": "true" } : {},
|
|
7920
|
+
style: { width: `${Math.max(3, Math.round(row.calls / rankMax * 100))}%` }
|
|
7921
|
+
})), (0, react.createElement)("span", { className: "v3-n" }, String(row.calls)), (0, react.createElement)("span", {
|
|
7922
|
+
className: "v3-e",
|
|
7923
|
+
...bad ? { "data-bad": "true" } : {},
|
|
7924
|
+
...row.error === 0 ? { "data-zero": "true" } : {}
|
|
7925
|
+
}, `失败 ${row.error}`));
|
|
7926
|
+
}))), (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "工具调用失败"), (0, react.createElement)("span", { className: "v3-pn" }, tools === void 0 ? "按失败数排序" : `${formatCallIndexCount(metrics.failed, exact)} 次失败`)), toolsNotice !== null ? toolsNotice : failingRows === void 0 || failingRows.length === 0 ? (0, react.createElement)("p", { className: "v3-hint" }, "当前范围没有失败调用。") : (0, react.createElement)("div", {
|
|
7927
|
+
className: "v3-rank",
|
|
7928
|
+
"data-call-index-failures": "true"
|
|
7929
|
+
}, ...failingTop.map((row) => (0, react.createElement)("div", {
|
|
7930
|
+
key: "fc-" + row.toolName + "::" + row.sourceKind,
|
|
7931
|
+
className: "v3-rank-row"
|
|
7932
|
+
}, (0, react.createElement)("span", {
|
|
7933
|
+
className: "v3-nm",
|
|
7934
|
+
style: {
|
|
7935
|
+
width: "auto",
|
|
7936
|
+
flex: "1 1 auto"
|
|
7937
|
+
}
|
|
7938
|
+
}, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", {
|
|
7939
|
+
className: "v3-n",
|
|
7940
|
+
style: {
|
|
7941
|
+
color: "var(--v3-bad)",
|
|
7942
|
+
fontWeight: 600
|
|
7943
|
+
}
|
|
7944
|
+
}, `${row.error} 次`))), ...failingRest.length === 0 ? [] : [(0, react.createElement)("div", {
|
|
7945
|
+
key: "fc-rest",
|
|
7946
|
+
className: "v3-rank-row"
|
|
7947
|
+
}, (0, react.createElement)("span", {
|
|
7948
|
+
className: "v3-nm",
|
|
7949
|
+
style: {
|
|
7950
|
+
width: "auto",
|
|
7951
|
+
flex: "1 1 auto"
|
|
7952
|
+
}
|
|
7953
|
+
}, `其余 ${failingRest.length} 个工具合计`), (0, react.createElement)("span", {
|
|
7954
|
+
className: "v3-n",
|
|
7955
|
+
style: { color: "var(--v3-t2)" }
|
|
7956
|
+
}, `${failingRestErrors} 次`))]))), (0, react.createElement)("div", {
|
|
7957
|
+
className: "v3-panel",
|
|
7958
|
+
style: { marginTop: 12 }
|
|
7959
|
+
}, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "索引状态"), (0, react.createElement)("span", { className: "v3-pn" }, metrics.tasks === void 0 ? "最近扫描未记录" : `最近扫描 ${metrics.tasks.mode} · ${metrics.tasks.phase}`)), (0, react.createElement)("div", { className: "v3-kv" }, (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "数据范围"), (0, react.createElement)("div", { className: "v3-kv-v" }, overview?.corpus === "persisted-plus-observed-live" ? "已持久化 + 挂载后实时观测" : "Host 未声明")), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "Session 列出 / 可见"), (0, react.createElement)("div", { className: "v3-kv-v v3-num" }, sessionInfo === void 0 ? "—" : `${sessionInfo.listed} / ${sessionInfo.visible}`)), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "待处理 / 失败"), (0, react.createElement)("div", { className: "v3-kv-v v3-num" }, sessionInfo === void 0 ? "—" : `${sessionInfo.pending} / ${sessionInfo.failed}`)), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "覆盖(事件 / 枚举 / 语义)"), (0, react.createElement)("div", { className: "v3-kv-v" }, status === void 0 ? "—" : `${COVERAGE_LABELS[status.eventCoverage]} / ${COVERAGE_LABELS[status.enumerationCoverage]} / ${COVERAGE_LABELS[status.coverage]}`))), taskNote));
|
|
7185
7960
|
}
|
|
7186
7961
|
function useNarrowViewport() {
|
|
7187
7962
|
const [narrow, setNarrow] = (0, react.useState)(false);
|
|
@@ -7239,6 +8014,14 @@ function detailAfterRefresh(previous, page) {
|
|
|
7239
8014
|
detailFailure: void 0
|
|
7240
8015
|
};
|
|
7241
8016
|
}
|
|
8017
|
+
/** Pure policy decision, so the container's refresh strategy is testable. */
|
|
8018
|
+
function callIndexRefreshPolicy(status) {
|
|
8019
|
+
const scanning = status !== void 0 && (status.phase === "running" || status.sessions.pending > 0 || status.sessions.pendingLive > 0);
|
|
8020
|
+
return {
|
|
8021
|
+
scanning,
|
|
8022
|
+
mode: scanning ? "status-only" : "full"
|
|
8023
|
+
};
|
|
8024
|
+
}
|
|
7242
8025
|
/**
|
|
7243
8026
|
* Container for 调用明细: owns the query, the paging cursor and the detail read.
|
|
7244
8027
|
* Keeps the previous page visible while the next one loads, so a range click
|
|
@@ -7252,6 +8035,7 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
|
|
|
7252
8035
|
loadingDetail: false
|
|
7253
8036
|
});
|
|
7254
8037
|
const [refreshToken, setRefreshToken] = (0, react.useState)(0);
|
|
8038
|
+
const [search, setSearch] = (0, react.useState)("");
|
|
7255
8039
|
const generation = (0, react.useRef)(0);
|
|
7256
8040
|
const pageAnchor = (0, react.useRef)(void 0);
|
|
7257
8041
|
/** How many extra pages 加载更多 has appended; 0 means only the first page. */
|
|
@@ -7313,23 +8097,35 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
|
|
|
7313
8097
|
refreshToken
|
|
7314
8098
|
]);
|
|
7315
8099
|
/**
|
|
7316
|
-
*
|
|
8100
|
+
* Refresh status/progress while a scan is running — WITHOUT re-reading calls.
|
|
7317
8101
|
*
|
|
7318
|
-
* The
|
|
7319
|
-
*
|
|
7320
|
-
*
|
|
7321
|
-
*
|
|
8102
|
+
* The old loop bumped `refreshToken`, which re-read status + calls + sessions
|
|
8103
|
+
* every 2s and replaced the data page under the operator mid-scan. Now it polls
|
|
8104
|
+
* `getStatus` only, keeps the current rows untouched, and triggers ONE full read
|
|
8105
|
+
* when the scan finishes (or on the explicit 刷新 button).
|
|
7322
8106
|
*/
|
|
7323
|
-
const scanningNow =
|
|
8107
|
+
const scanningNow = callIndexRefreshPolicy(state.status).scanning;
|
|
7324
8108
|
(0, react.useEffect)(() => {
|
|
7325
8109
|
if (!scanningNow) return;
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
8110
|
+
let active = true;
|
|
8111
|
+
const id = setInterval(() => {
|
|
8112
|
+
(async () => {
|
|
8113
|
+
const result = await api.getStatus();
|
|
8114
|
+
if (!active || !result.ok) return;
|
|
8115
|
+
const next = result.value;
|
|
8116
|
+
const stillScanning = callIndexRefreshPolicy(next).scanning;
|
|
8117
|
+
setState((previous) => ({
|
|
8118
|
+
...previous,
|
|
8119
|
+
status: next
|
|
8120
|
+
}));
|
|
8121
|
+
if (!stillScanning) setRefreshToken((value) => value + 1);
|
|
8122
|
+
})();
|
|
8123
|
+
}, 2e3);
|
|
8124
|
+
return () => {
|
|
8125
|
+
active = false;
|
|
8126
|
+
clearInterval(id);
|
|
8127
|
+
};
|
|
8128
|
+
}, [scanningNow, api]);
|
|
7333
8129
|
/**
|
|
7334
8130
|
* Append the next page.
|
|
7335
8131
|
*
|
|
@@ -7488,6 +8284,8 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
|
|
|
7488
8284
|
loadingMore: state.loadingMore,
|
|
7489
8285
|
loadingDetail: state.loadingDetail,
|
|
7490
8286
|
narrow,
|
|
8287
|
+
search,
|
|
8288
|
+
onSearchChange: setSearch,
|
|
7491
8289
|
onQueryChange: setQuery,
|
|
7492
8290
|
onRefresh: () => setRefreshToken((value) => value + 1),
|
|
7493
8291
|
onStartIndex: startIndex,
|
|
@@ -7498,8 +8296,12 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
|
|
|
7498
8296
|
});
|
|
7499
8297
|
}
|
|
7500
8298
|
/**
|
|
7501
|
-
* Container for 总览. Reads the status once
|
|
7502
|
-
*
|
|
8299
|
+
* Container for 总览. Reads the status once, one all-time summary, AND one
|
|
8300
|
+
* `listCalls` snapshot — because the overview reply carries no per-tool counts,
|
|
8301
|
+
* and the v3 ranking / fault panels need them. The per-tool read fills up to
|
|
8302
|
+
* `MAX_AUTO_PAGES` pages (same bound the detail page uses) then aggregates with
|
|
8303
|
+
* the shared pure `callIndexToolRows`. Failing to read rows never blocks the
|
|
8304
|
+
* headline numbers; it only shows an honest empty state in the two panels.
|
|
7503
8305
|
*/
|
|
7504
8306
|
function CallIndexOverviewPanel({ api }) {
|
|
7505
8307
|
const [state, setState] = (0, react.useState)({ loading: true });
|
|
@@ -7511,26 +8313,80 @@ function CallIndexOverviewPanel({ api }) {
|
|
|
7511
8313
|
loading: true
|
|
7512
8314
|
}));
|
|
7513
8315
|
const query = { range: "all" };
|
|
8316
|
+
const anchor = Date.now();
|
|
7514
8317
|
(async () => {
|
|
7515
|
-
const [statusResult, overviewResult] = await Promise.all([
|
|
8318
|
+
const [statusResult, overviewResult, callsResult] = await Promise.all([
|
|
8319
|
+
api.getStatus(),
|
|
8320
|
+
api.getOverview(query),
|
|
8321
|
+
api.listCalls(query, void 0, anchor)
|
|
8322
|
+
]);
|
|
7516
8323
|
if (!active) return;
|
|
7517
8324
|
const failure = overviewResult.ok ? statusResult.ok ? void 0 : statusResult : overviewResult;
|
|
8325
|
+
let tools;
|
|
8326
|
+
let toolsFailure;
|
|
8327
|
+
if (callsResult.ok) {
|
|
8328
|
+
const items = [...callsResult.value.items];
|
|
8329
|
+
let cursor = callsResult.value.nextCursor;
|
|
8330
|
+
let pages = 0;
|
|
8331
|
+
while (cursor !== void 0 && pages < 20) {
|
|
8332
|
+
const next = await api.listCalls(query, cursor, anchor);
|
|
8333
|
+
if (!next.ok) {
|
|
8334
|
+
toolsFailure = next;
|
|
8335
|
+
break;
|
|
8336
|
+
}
|
|
8337
|
+
items.push(...next.value.items);
|
|
8338
|
+
cursor = next.value.nextCursor;
|
|
8339
|
+
pages += 1;
|
|
8340
|
+
}
|
|
8341
|
+
tools = callIndexToolRows({
|
|
8342
|
+
...callsResult.value,
|
|
8343
|
+
items
|
|
8344
|
+
});
|
|
8345
|
+
} else toolsFailure = callsResult;
|
|
8346
|
+
if (!active) return;
|
|
7518
8347
|
setState({
|
|
7519
8348
|
loading: false,
|
|
7520
8349
|
...statusResult.ok ? { status: statusResult.value } : {},
|
|
7521
8350
|
...overviewResult.ok ? { overview: overviewResult.value } : {},
|
|
7522
|
-
...failure === void 0 ? {} : { failure }
|
|
8351
|
+
...failure === void 0 ? {} : { failure },
|
|
8352
|
+
...tools === void 0 ? {} : { tools },
|
|
8353
|
+
...toolsFailure === void 0 ? {} : { toolsFailure }
|
|
7523
8354
|
});
|
|
7524
8355
|
})();
|
|
7525
8356
|
return () => {
|
|
7526
8357
|
active = false;
|
|
7527
8358
|
};
|
|
7528
8359
|
}, [api, refreshToken]);
|
|
8360
|
+
const scanning = callIndexRefreshPolicy(state.status).scanning;
|
|
8361
|
+
(0, react.useEffect)(() => {
|
|
8362
|
+
if (!scanning) return;
|
|
8363
|
+
let active = true;
|
|
8364
|
+
const id = setInterval(() => {
|
|
8365
|
+
(async () => {
|
|
8366
|
+
const result = await api.getStatus();
|
|
8367
|
+
if (!active || !result.ok) return;
|
|
8368
|
+
const next = result.value;
|
|
8369
|
+
const stillScanning = callIndexRefreshPolicy(next).scanning;
|
|
8370
|
+
setState((previous) => ({
|
|
8371
|
+
...previous,
|
|
8372
|
+
status: next
|
|
8373
|
+
}));
|
|
8374
|
+
if (!stillScanning) setRefreshToken((value) => value + 1);
|
|
8375
|
+
})();
|
|
8376
|
+
}, 2e3);
|
|
8377
|
+
return () => {
|
|
8378
|
+
active = false;
|
|
8379
|
+
clearInterval(id);
|
|
8380
|
+
};
|
|
8381
|
+
}, [scanning, api]);
|
|
7529
8382
|
return (0, react.createElement)(CallIndexOverviewPanelView, {
|
|
7530
8383
|
...state.status === void 0 ? {} : { status: state.status },
|
|
7531
8384
|
...state.overview === void 0 ? {} : { overview: state.overview },
|
|
8385
|
+
...state.tools === void 0 ? {} : { tools: state.tools },
|
|
8386
|
+
...state.toolsFailure === void 0 ? {} : { toolsFailure: state.toolsFailure },
|
|
7532
8387
|
...state.failure === void 0 ? {} : { failure: state.failure },
|
|
7533
8388
|
loading: state.loading,
|
|
8389
|
+
toolsLoading: state.loading,
|
|
7534
8390
|
onRefresh: () => setRefreshToken((value) => value + 1)
|
|
7535
8391
|
});
|
|
7536
8392
|
}
|
|
@@ -7659,18 +8515,26 @@ function OverviewContainer(props) {
|
|
|
7659
8515
|
//#endregion
|
|
7660
8516
|
//#region src/client/GuardPanel.tsx
|
|
7661
8517
|
/**
|
|
7662
|
-
* 思考限制 — the Guard configuration surface.
|
|
8518
|
+
* 思考限制 — the Guard configuration surface (v3 · 数据看板, D-044).
|
|
7663
8519
|
*
|
|
7664
|
-
* Three sections, in
|
|
7665
|
-
*
|
|
7666
|
-
*
|
|
7667
|
-
*
|
|
7668
|
-
*
|
|
7669
|
-
* ties the two together, so it is displayed rather than hidden.
|
|
8520
|
+
* Three sections, in product order: repeated identical calls, alternating
|
|
8521
|
+
* cycles, no-progress protection. Each carries a model-side identifier
|
|
8522
|
+
* ('duplicate_call' / 'alternating_cycle' / 'no_progress') shown as plain text,
|
|
8523
|
+
* because the reminder injected into the conversation is English while the label
|
|
8524
|
+
* a user reads here is Chinese. The identifier ties the two together.
|
|
7670
8525
|
*
|
|
7671
|
-
*
|
|
7672
|
-
*
|
|
7673
|
-
*
|
|
8526
|
+
* v3 SHAPE: a two-column grid of .v3-gitem cards (>900px two columns, auto single
|
|
8527
|
+
* column via the container query in v3-ui.ts). The Guard counters sit at the very
|
|
8528
|
+
* top as a four-cell .v3-stats strip, followed by one short boundary hint. No page
|
|
8529
|
+
* title, no "activity" sub-title, no "counting since" line — the tab already says
|
|
8530
|
+
* what this page is.
|
|
8531
|
+
*
|
|
8532
|
+
* DOM CONTRACT kept for the shared test suite: nine <li class="mc-guard-point">
|
|
8533
|
+
* whose text still comes from t(); the three data-guard-section values in order;
|
|
8534
|
+
* the threshold editor wrapped in data-guard-thresholds (now three number inputs,
|
|
8535
|
+
* values from the Host); the four data-guard-stat cells and data-guard-stats; and
|
|
8536
|
+
* three role="switch" buttons whose aria-checked follows the props. No Chinese
|
|
8537
|
+
* colon (':') is introduced by this file — spacing does the separating instead.
|
|
7674
8538
|
*
|
|
7675
8539
|
* Kept hook-free and prop-driven so the settings test suite (no DOM harness) can
|
|
7676
8540
|
* walk the returned tree.
|
|
@@ -7678,9 +8542,12 @@ function OverviewContainer(props) {
|
|
|
7678
8542
|
/**
|
|
7679
8543
|
* Section id → localized title plus the lines that explain what the switch does.
|
|
7680
8544
|
*
|
|
7681
|
-
*
|
|
8545
|
+
* the descriptions are part of the contract, not decoration: a bare switch does
|
|
7682
8546
|
* not tell a reader whether turning it off stops reminders only or also stops
|
|
7683
8547
|
* local denials. Each bullet states the real behaviour the detector implements.
|
|
8548
|
+
*
|
|
8549
|
+
* v3 keeps all three bullets (nine <li class="mc-guard-point"> in total, as the
|
|
8550
|
+
* shared suite asserts) but lays them out as one compact wrapped row per card.
|
|
7684
8551
|
*/
|
|
7685
8552
|
const SECTIONS = [
|
|
7686
8553
|
{
|
|
@@ -7711,52 +8578,67 @@ const SECTIONS = [
|
|
|
7711
8578
|
]
|
|
7712
8579
|
}
|
|
7713
8580
|
];
|
|
7714
|
-
/**
|
|
8581
|
+
/**
|
|
8582
|
+
* Page-specific rules for the v3 Guard grid. Everything else comes from
|
|
8583
|
+
* v3-ui.ts; the only local vocabulary here is the compact bullet row, the
|
|
8584
|
+
* threshold editor chrome and the per-card "fired" foot. Colours are always v3
|
|
8585
|
+
* semantic variables — nothing is hard-coded, so light and dark themes both
|
|
8586
|
+
* track the host.
|
|
8587
|
+
*/
|
|
8588
|
+
const GUARD_CSS = [
|
|
8589
|
+
".mc-section.v3-root{display:flex;flex-direction:column;gap:12px}",
|
|
8590
|
+
".v3-gpoints{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;align-items:baseline;gap:0 5px}",
|
|
8591
|
+
".v3-gpoints>.mc-guard-point{font-size:11.5px;line-height:18px;color:var(--v3-t3)}",
|
|
8592
|
+
".v3-gpoints>.mc-guard-point+.mc-guard-point::before{content:'·';margin-right:5px;color:var(--v3-t4)}",
|
|
8593
|
+
".v3-gitem--info{background:var(--v3-panel-2)}",
|
|
8594
|
+
".v3-gitem .v3-gh .v3-sw{margin-left:auto}",
|
|
8595
|
+
".v3-gfoot-edit{margin-top:9px}",
|
|
8596
|
+
".v3-guard-stats{margin:0}",
|
|
8597
|
+
".v3-gfoot-note{color:var(--v3-t4)}",
|
|
8598
|
+
".mc-guard-threshold-edit{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
|
|
8599
|
+
".mc-guard-threshold-edit-label{font-size:11.5px;color:var(--v3-t3)}",
|
|
8600
|
+
".mc-guard-threshold-num{width:56px;padding:3px 6px;border-radius:var(--v3-r-sm);font-size:12px;text-align:center;font-family:var(--v3-font-code);color:var(--v3-t1);background:var(--v3-panel-2);border:1px solid var(--v3-line)}",
|
|
8601
|
+
".mc-guard-threshold-num:focus-visible{outline:2px solid var(--v3-info);outline-offset:1px}",
|
|
8602
|
+
".mc-guard-threshold-num:disabled{opacity:.6}",
|
|
8603
|
+
".mc-guard-threshold-hint{margin:6px 0 0}"
|
|
8604
|
+
].join("");
|
|
8605
|
+
/** '—' for a counter the Host did not report; never a fabricated 0. */
|
|
7715
8606
|
function statText(value) {
|
|
7716
8607
|
return value === void 0 || !Number.isFinite(value) ? "—" : String(value);
|
|
7717
8608
|
}
|
|
7718
8609
|
/**
|
|
7719
|
-
* The Guard's real activity board.
|
|
8610
|
+
* The Guard's real activity board, restyled as a v3 stat strip.
|
|
7720
8611
|
*
|
|
7721
8612
|
* Answers the two questions the page previously could not: 「它是否在工作」
|
|
7722
|
-
* (observations
|
|
7723
|
-
*
|
|
7724
|
-
* "unknown" rather than "zero".
|
|
8613
|
+
* (observations) and 「调用成功还是失败」 (outcome split). Every number comes
|
|
8614
|
+
* from the Host; a missing counter renders '—', so an older Host reads as
|
|
8615
|
+
* "unknown" rather than "zero". The four data-guard-stat cells and the
|
|
8616
|
+
* data-guard-stats hook are the published contract. No sub-title and no
|
|
8617
|
+
* "counting since" line: the strip is the first thing on the page.
|
|
7725
8618
|
*/
|
|
7726
8619
|
function GuardStatsBoard(props) {
|
|
7727
8620
|
const { t, stats } = props;
|
|
7728
8621
|
const outcomes = stats?.outcomes;
|
|
7729
8622
|
const reminders = stats?.reminders;
|
|
7730
8623
|
const cell = (label, value, hint) => (0, react.createElement)("div", {
|
|
7731
|
-
className: "
|
|
8624
|
+
className: "v3-stat",
|
|
7732
8625
|
"data-guard-stat": label
|
|
7733
|
-
}, (0, react.createElement)("div", { className: "
|
|
7734
|
-
return (0, react.createElement)("div", {
|
|
7735
|
-
className: "
|
|
8626
|
+
}, (0, react.createElement)("div", { className: "v3-stat-l" }, label), (0, react.createElement)("div", { className: "v3-stat-v" }, value), hint === void 0 ? null : (0, react.createElement)("div", { className: "v3-stat-d" }, hint));
|
|
8627
|
+
return (0, react.createElement)("div", { className: "v3-guard-stats" }, (0, react.createElement)("div", {
|
|
8628
|
+
className: "v3-stats",
|
|
7736
8629
|
"data-guard-stats": "true",
|
|
7737
8630
|
role: "group",
|
|
7738
8631
|
"aria-label": t("guardStatsTitle")
|
|
7739
|
-
}, cell(t("guardStatObservations"), statText(stats?.observations), t("guardStatObservationsHint")), cell(t("guardStatOutcomes"),
|
|
7740
|
-
className: "mc-guard-stats-since",
|
|
7741
|
-
"data-guard-stats-since": "true"
|
|
7742
|
-
}, `${t("guardStatsSince")} ${formatGuardTime(stats.since)}`));
|
|
7743
|
-
}
|
|
7744
|
-
/** `YYYY-MM-DD HH:mm` in the browser zone; `—` when unknown. */
|
|
7745
|
-
function formatGuardTime(time) {
|
|
7746
|
-
if (time === void 0 || !Number.isFinite(time)) return "—";
|
|
7747
|
-
const date = new Date(time);
|
|
7748
|
-
if (Number.isNaN(date.getTime())) return "—";
|
|
7749
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
7750
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
8632
|
+
}, cell(t("guardStatObservations"), statText(stats?.observations), t("guardStatObservationsHint")), cell(t("guardStatOutcomes"), statText(outcomes?.success) + " / " + statText(outcomes?.error), t("guardStatOutcomesHint")), cell(t("guardStatReminders"), statText(reminders?.repeat) + " / " + statText(reminders?.cycle), t("guardStatRemindersHint")), cell(t("guardStatDenials"), statText(stats?.denials), t("guardStatDenialsHint"))));
|
|
7751
8633
|
}
|
|
7752
8634
|
/**
|
|
7753
8635
|
* Editable reminder thresholds.
|
|
7754
8636
|
*
|
|
7755
|
-
*
|
|
7756
|
-
*
|
|
7757
|
-
*
|
|
7758
|
-
*
|
|
7759
|
-
*
|
|
8637
|
+
* Three plain number inputs instead of one comma-separated text field: the
|
|
8638
|
+
* operator sees one box per firing point and never has to learn a syntax. Values
|
|
8639
|
+
* always come from the Host; a keystroke is assembled into a fresh array and
|
|
8640
|
+
* handed to onThresholdsChange, and an older Host that does not accept a write
|
|
8641
|
+
* gets disabled boxes. data-guard-thresholds marks the wrapper.
|
|
7760
8642
|
*/
|
|
7761
8643
|
function GuardThresholdEditor(props) {
|
|
7762
8644
|
const { t, thresholds, onChange } = props;
|
|
@@ -7765,81 +8647,84 @@ function GuardThresholdEditor(props) {
|
|
|
7765
8647
|
return (0, react.createElement)("div", {
|
|
7766
8648
|
className: "mc-guard-threshold-edit",
|
|
7767
8649
|
"data-guard-thresholds": "true"
|
|
7768
|
-
}, (0, react.createElement)("label", {
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
value: thresholds.join(", "),
|
|
8650
|
+
}, (0, react.createElement)("label", { className: "mc-guard-threshold-edit-label" }, t("guardThresholds")), ...thresholds.map((value, index) => (0, react.createElement)("input", {
|
|
8651
|
+
key: index,
|
|
8652
|
+
type: "number",
|
|
8653
|
+
min: 2,
|
|
8654
|
+
max: 50,
|
|
8655
|
+
step: 1,
|
|
8656
|
+
className: "mc-guard-threshold-num",
|
|
8657
|
+
value: String(value),
|
|
7777
8658
|
readOnly: !editable,
|
|
7778
8659
|
disabled: !editable,
|
|
7779
|
-
"aria-label": t("guardThresholds"),
|
|
8660
|
+
"aria-label": t("guardThresholds") + " " + String(index + 1),
|
|
7780
8661
|
onChange: (event) => {
|
|
7781
8662
|
if (!editable) return;
|
|
7782
|
-
const
|
|
7783
|
-
if (
|
|
7784
|
-
|
|
8663
|
+
const raw = Number(event.target.value);
|
|
8664
|
+
if (!Number.isSafeInteger(raw) || raw < 2 || raw > 50) return;
|
|
8665
|
+
const next = thresholds.slice();
|
|
8666
|
+
next[index] = raw;
|
|
8667
|
+
onChange(next);
|
|
7785
8668
|
}
|
|
7786
|
-
})
|
|
8669
|
+
})));
|
|
7787
8670
|
}
|
|
7788
|
-
/**
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
* deliberate: it keeps an invalid keystroke from round-tripping through the
|
|
7794
|
-
* remote and flickering the field.
|
|
7795
|
-
*/
|
|
7796
|
-
function parseGuardThresholds(text) {
|
|
7797
|
-
const parts = text.split(",").map((part) => part.trim()).filter((part) => part !== "");
|
|
7798
|
-
if (parts.length === 0 || parts.length > 5) return void 0;
|
|
7799
|
-
const parsed = [];
|
|
7800
|
-
for (const part of parts) {
|
|
7801
|
-
if (!/^\d+$/.test(part)) return void 0;
|
|
7802
|
-
const value = Number(part);
|
|
7803
|
-
if (!Number.isSafeInteger(value) || value < 2 || value > 50) return void 0;
|
|
7804
|
-
if (parsed.length > 0 && value <= parsed[parsed.length - 1]) return void 0;
|
|
7805
|
-
parsed.push(value);
|
|
7806
|
-
}
|
|
7807
|
-
return parsed;
|
|
8671
|
+
/** The counter a card reports as "fired", per the product mapping. */
|
|
8672
|
+
function firedValue(id, stats) {
|
|
8673
|
+
if (id === "duplicate_call") return stats?.reminders?.repeat;
|
|
8674
|
+
if (id === "alternating_cycle") return stats?.reminders?.cycle;
|
|
8675
|
+
return stats?.denials;
|
|
7808
8676
|
}
|
|
7809
8677
|
function GuardPanel(props) {
|
|
7810
8678
|
const { t, enabled, duplicateThresholds, onToggle, onThresholdsChange, stats, onReset } = props;
|
|
7811
8679
|
const section = (entry) => {
|
|
7812
8680
|
const isOn = enabled?.[entry.id] ?? false;
|
|
7813
8681
|
const canToggle = onToggle !== void 0;
|
|
8682
|
+
const dot = isOn ? (0, react.createElement)("span", {
|
|
8683
|
+
className: "v3-dot",
|
|
8684
|
+
"data-tone": "ok",
|
|
8685
|
+
"aria-hidden": "true"
|
|
8686
|
+
}) : (0, react.createElement)("span", {
|
|
8687
|
+
className: "v3-dot",
|
|
8688
|
+
"aria-hidden": "true"
|
|
8689
|
+
});
|
|
8690
|
+
const belowBullets = entry.id === "duplicate_call" ? (0, react.createElement)("div", { className: "v3-gfoot-edit" }, (0, react.createElement)(GuardThresholdEditor, {
|
|
8691
|
+
t,
|
|
8692
|
+
thresholds: duplicateThresholds,
|
|
8693
|
+
...onThresholdsChange === void 0 ? {} : { onChange: onThresholdsChange }
|
|
8694
|
+
}), (0, react.createElement)("p", { className: "v3-hint mc-guard-threshold-hint" }, t("guardThresholdHint"))) : (0, react.createElement)("p", { className: "v3-hint" }, t(entry.id === "alternating_cycle" ? "guardPurposeB" : "guardPurposeC"));
|
|
8695
|
+
const footNote = entry.id === "alternating_cycle" ? (0, react.createElement)("span", { className: "v3-gfoot-note" }, t("guardFootCycle")) : entry.id === "no_progress" ? (0, react.createElement)("span", { className: "v3-gfoot-note" }, t("guardFootNoProgress")) : null;
|
|
7814
8696
|
return (0, react.createElement)("div", {
|
|
7815
8697
|
key: entry.id,
|
|
7816
|
-
className: "
|
|
8698
|
+
className: "v3-gitem",
|
|
7817
8699
|
"data-guard-section": entry.id
|
|
7818
|
-
}, (0, react.createElement)("div", { className: "
|
|
8700
|
+
}, (0, react.createElement)("div", { className: "v3-gh" }, dot, (0, react.createElement)("span", { className: "v3-gnm" }, t(entry.titleKey)), (0, react.createElement)("span", { className: "v3-gid" }, entry.id), (0, react.createElement)("button", {
|
|
7819
8701
|
type: "button",
|
|
7820
8702
|
role: "switch",
|
|
7821
8703
|
"aria-checked": isOn ? "true" : "false",
|
|
7822
8704
|
"aria-label": t(entry.titleKey),
|
|
7823
|
-
className:
|
|
8705
|
+
className: "v3-sw",
|
|
7824
8706
|
disabled: !canToggle,
|
|
7825
8707
|
onClick: () => canToggle && onToggle(entry.id, !isOn)
|
|
7826
|
-
})), (0, react.createElement)("
|
|
8708
|
+
})), (0, react.createElement)("ul", { className: "v3-gpoints" }, ...entry.bodyKeys.map((key) => (0, react.createElement)("li", {
|
|
7827
8709
|
key,
|
|
7828
8710
|
className: "mc-guard-point"
|
|
7829
|
-
}, t(key)))),
|
|
7830
|
-
t,
|
|
7831
|
-
thresholds: duplicateThresholds,
|
|
7832
|
-
...onThresholdsChange === void 0 ? {} : { onChange: onThresholdsChange }
|
|
7833
|
-
}))));
|
|
8711
|
+
}, t(key)))), belowBullets, (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardFired")), (0, react.createElement)("span", { className: "v3-kbd" }, statText(firedValue(entry.id, stats))), footNote));
|
|
7834
8712
|
};
|
|
7835
|
-
|
|
8713
|
+
const scopeCell = (0, react.createElement)("div", {
|
|
8714
|
+
key: "scope",
|
|
8715
|
+
className: "v3-gitem v3-gitem--info"
|
|
8716
|
+
}, (0, react.createElement)("div", { className: "v3-gh" }, (0, react.createElement)("span", { className: "v3-gnm" }, t("guardScopeTitle"))), (0, react.createElement)("p", null, t("guardScopeBody")), (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardScopeFoot"))));
|
|
8717
|
+
return (0, react.createElement)("section", { className: "mc-section v3-root" }, V3Styles(), (0, react.createElement)("style", { "data-guard-style": "true" }, GUARD_CSS), onReset === void 0 ? null : (0, react.createElement)("div", { style: {
|
|
8718
|
+
display: "flex",
|
|
8719
|
+
justifyContent: "flex-end"
|
|
8720
|
+
} }, (0, react.createElement)("button", {
|
|
7836
8721
|
type: "button",
|
|
7837
8722
|
className: "mc-count mc-count--action",
|
|
7838
8723
|
onClick: onReset
|
|
7839
|
-
}, t("guardReset")))
|
|
8724
|
+
}, t("guardReset"))), (0, react.createElement)(GuardStatsBoard, {
|
|
7840
8725
|
t,
|
|
7841
8726
|
...stats === void 0 ? {} : { stats }
|
|
7842
|
-
}), ...SECTIONS.map(section));
|
|
8727
|
+
}), (0, react.createElement)("p", { className: "v3-hint" }, t("guardBoundaryShort")), (0, react.createElement)("div", { className: "v3-gpanel" }, ...SECTIONS.map(section), scopeCell));
|
|
7843
8728
|
}
|
|
7844
8729
|
//#endregion
|
|
7845
8730
|
//#region src/client/GuardContainer.tsx
|
|
@@ -8136,6 +9021,7 @@ function PluginSection(props) {
|
|
|
8136
9021
|
const [active, setActive] = (0, react.useState)("overview");
|
|
8137
9022
|
const callIndexApi = callIndexApiFor(props.callIndexRemote);
|
|
8138
9023
|
const [diagnostics, setDiagnostics] = (0, react.useState)(void 0);
|
|
9024
|
+
const [catalogUsage, setCatalogUsage] = (0, react.useState)(void 0);
|
|
8139
9025
|
(0, react.useEffect)(() => {
|
|
8140
9026
|
let live = true;
|
|
8141
9027
|
(async () => {
|
|
@@ -8147,6 +9033,35 @@ function PluginSection(props) {
|
|
|
8147
9033
|
live = false;
|
|
8148
9034
|
};
|
|
8149
9035
|
}, [callIndexApi]);
|
|
9036
|
+
(0, react.useEffect)(() => {
|
|
9037
|
+
let live = true;
|
|
9038
|
+
(async () => {
|
|
9039
|
+
const result = await callIndexApi.listCalls({ range: "all" });
|
|
9040
|
+
if (!live || !result.ok) return;
|
|
9041
|
+
const map = /* @__PURE__ */ new Map();
|
|
9042
|
+
for (const row of callIndexToolRows(result.value)) map.set(row.toolName, {
|
|
9043
|
+
uses: row.calls,
|
|
9044
|
+
failures: row.error
|
|
9045
|
+
});
|
|
9046
|
+
setCatalogUsage(map);
|
|
9047
|
+
})();
|
|
9048
|
+
return () => {
|
|
9049
|
+
live = false;
|
|
9050
|
+
};
|
|
9051
|
+
}, [callIndexApi]);
|
|
9052
|
+
/**
|
|
9053
|
+
* Reset the settings content scroller when the category changes.
|
|
9054
|
+
*
|
|
9055
|
+
* The shell owns the scrolling column and swaps panels with `hidden`, so the
|
|
9056
|
+
* previous tab's scrollTop survives the switch and the next page opens halfway
|
|
9057
|
+
* down (observed: 执行保护 opened below its counter board). Reset the nearest
|
|
9058
|
+
* scrollable ancestor of the active panel.
|
|
9059
|
+
*/
|
|
9060
|
+
(0, react.useEffect)(() => {
|
|
9061
|
+
let node = document.getElementById("mc-category-panel-" + active);
|
|
9062
|
+
while (node !== null && !(node.scrollHeight > node.clientHeight + 4)) node = node.parentElement;
|
|
9063
|
+
if (node !== null) node.scrollTop = 0;
|
|
9064
|
+
}, [active]);
|
|
8150
9065
|
return (0, react.createElement)(PluginSectionView, {
|
|
8151
9066
|
capability: props.capability,
|
|
8152
9067
|
usageApi: props.usageApi,
|
|
@@ -8157,7 +9072,8 @@ function PluginSection(props) {
|
|
|
8157
9072
|
t: props.t,
|
|
8158
9073
|
active,
|
|
8159
9074
|
onSelect: setActive,
|
|
8160
|
-
...diagnostics === void 0 ? {} : { diagnostics }
|
|
9075
|
+
...diagnostics === void 0 ? {} : { diagnostics },
|
|
9076
|
+
...catalogUsage === void 0 ? {} : { catalogUsage }
|
|
8161
9077
|
});
|
|
8162
9078
|
}
|
|
8163
9079
|
/**
|
|
@@ -8166,7 +9082,7 @@ function PluginSection(props) {
|
|
|
8166
9082
|
* `UsagePanelView`.
|
|
8167
9083
|
*/
|
|
8168
9084
|
function PluginSectionView(props) {
|
|
8169
|
-
const { capability, usageApi, callIndexRemote, scopeTarget, sessionCatalog, guardRemote, t, active, onSelect, diagnostics } = props;
|
|
9085
|
+
const { capability, usageApi, callIndexRemote, scopeTarget, sessionCatalog, guardRemote, t, active, onSelect, diagnostics, catalogUsage } = props;
|
|
8170
9086
|
const callIndexApi = callIndexApiFor(callIndexRemote);
|
|
8171
9087
|
const panel = (category, children) => (0, react.createElement)("div", {
|
|
8172
9088
|
role: "tabpanel",
|
|
@@ -8199,7 +9115,10 @@ function PluginSectionView(props) {
|
|
|
8199
9115
|
5,
|
|
8200
9116
|
8
|
|
8201
9117
|
]
|
|
8202
|
-
})), panel("catalog", (0, react.createElement)(CapabilitySection,
|
|
9118
|
+
})), panel("catalog", (0, react.createElement)(CapabilitySection, {
|
|
9119
|
+
...capability,
|
|
9120
|
+
...catalogUsage === void 0 ? {} : { usage: catalogUsage }
|
|
9121
|
+
})), panel("usage", callIndexRemote === void 0 && usageApi !== void 0 ? (0, react.createElement)(UsagePanel, { api: usageApi }) : (0, react.createElement)(CallIndexDetailPanel, {
|
|
8203
9122
|
api: callIndexApi,
|
|
8204
9123
|
...scopeTarget === void 0 ? {} : { resolveScopeTarget: scopeTarget },
|
|
8205
9124
|
...sessionCatalog === void 0 ? {} : { resolveSessionCatalog: sessionCatalog }
|
|
@@ -8279,6 +9198,17 @@ async function apply(ctx) {
|
|
|
8279
9198
|
guardDesc: "只针对有证据的重复与无进展拦截具体操作;不因总步数、工具总次数或任务时长终止任务。",
|
|
8280
9199
|
guardBoundary: "统计与限制是两件事:Step、工具调用次数、任务时长只用于统计与诊断,不构成硬停止条件。Quick / Normal / Deep 三档里的 Step、工具次数、时长阈值(soft* / hard*)是兼容字段与统计口径,只参与配置校验与计数,不产生提醒,也不停止任务。单个工具自身的超时另由宿主或该工具负责。",
|
|
8281
9200
|
guardReset: "恢复默认",
|
|
9201
|
+
guardScopeTitle: "生效范围",
|
|
9202
|
+
guardScopeBody: "三个开关按 Agent / Turn 隔离,不跨 Session 继承;关闭只停止对应的提醒与局部拒绝,不改动任何配置或历史。",
|
|
9203
|
+
guardScopeFoot: "恢复默认只写回开关值,不重写其他配置",
|
|
9204
|
+
guardBoundaryLabel: "边界",
|
|
9205
|
+
guardBoundaryShort: "这里的数字只用于展示,不构成硬停止;三档阈值只参与校验与计数,不产生提醒也不停止任务。",
|
|
9206
|
+
guardThresholdHint: "默认 3 / 5 / 8 次;改的是第几次重复时提醒,只提醒、不拦截。",
|
|
9207
|
+
guardFired: "实际触发",
|
|
9208
|
+
guardPurposeB: "作用:识别 A→B→A→B 这类交替循环并提醒,避免来回空转。",
|
|
9209
|
+
guardPurposeC: "作用:证据明确时拦下已证实的重复操作,其余情况只提醒。",
|
|
9210
|
+
guardFootCycle: "命中后只给出说明,不阻断调用",
|
|
9211
|
+
guardFootNoProgress: "降级条件 日志缺失 · 结果不可比较 · 识别不确定",
|
|
8282
9212
|
guardSectionA: "A · 相同调用重复提醒",
|
|
8283
9213
|
guardSectionB: "B · 交替循环识别",
|
|
8284
9214
|
guardSectionC: "C · 无进展保护",
|
|
@@ -8327,7 +9257,7 @@ async function apply(ctx) {
|
|
|
8327
9257
|
emptyGlobalSkills: "暂无全局技能",
|
|
8328
9258
|
emptyProjectSkills: "暂无项目技能",
|
|
8329
9259
|
emptyPresetSkills: "暂无预设技能",
|
|
8330
|
-
filterByName: "
|
|
9260
|
+
filterByName: "筛选名称或分组",
|
|
8331
9261
|
filterHint: "忽略大小写。匹配工具/技能名称、MCP 服务器、技能来源标签与预设 ID;也可以输入正则表达式,例如 cordis|ptc 或 /^mcp__dbx/。",
|
|
8332
9262
|
filterNoMatch: "没有匹配的能力(清空过滤框可恢复完整列表)",
|
|
8333
9263
|
emptyTools: "暂无工具",
|
|
@@ -8336,6 +9266,8 @@ async function apply(ctx) {
|
|
|
8336
9266
|
residentShort: "常驻",
|
|
8337
9267
|
onDemandShort: "按需",
|
|
8338
9268
|
disabledShort: "禁用",
|
|
9269
|
+
catalogZeroCall: "零调用",
|
|
9270
|
+
catalogZeroCallHint: "按已索引调用统计",
|
|
8339
9271
|
cycleHint: "点击标签切换分类(按需 → 禁用 → 常驻)",
|
|
8340
9272
|
notPreviewable: "该文件不是可预览的文本文件",
|
|
8341
9273
|
previewClose: "关闭",
|
|
@@ -8428,7 +9360,7 @@ async function apply(ctx) {
|
|
|
8428
9360
|
summaryLimitInvalid: "请输入不小于 20 的整数",
|
|
8429
9361
|
summaryLimitDisabled: "检索信息存储未启用",
|
|
8430
9362
|
summaryLimitSaved: "检索描述上限已保存",
|
|
8431
|
-
diagnosticTitle: "
|
|
9363
|
+
diagnosticTitle: "能力诊断 · {count} 项",
|
|
8432
9364
|
diagnosticUnavailable: "不可调用:{name}(来源 {source}{preset})",
|
|
8433
9365
|
diagnosticShadowed: "同名覆盖:{name} 保留 {winner},隐藏 {hidden}",
|
|
8434
9366
|
diagnosticBroken: "损坏的预设:{id}({reason};受影响技能数量由 DSH 未提供)",
|
|
@@ -8477,6 +9409,17 @@ async function apply(ctx) {
|
|
|
8477
9409
|
guardDesc: "Blocks specific operations only when there is evidence of repetition or no progress; never stops a task for step count, tool count or duration.",
|
|
8478
9410
|
guardBoundary: "Counting and limiting are separate: steps, tool calls and task duration are statistics and diagnostics only, never hard-stop conditions. The per-level (Quick / Normal / Deep) step, tool-call and duration thresholds (soft* / hard*) are compatibility fields and counting units: they only take part in config validation and tallies, never producing a reminder or stopping a task. A single tool timeout stays with the host or that tool.",
|
|
8479
9411
|
guardReset: "Restore defaults",
|
|
9412
|
+
guardScopeTitle: "Scope",
|
|
9413
|
+
guardScopeBody: "The three switches are isolated per Agent / turn and never inherited across Sessions; turning one off stops only its reminders and local denials, and changes no configuration or history.",
|
|
9414
|
+
guardScopeFoot: "Restoring defaults writes only the switch values; no other configuration is rewritten.",
|
|
9415
|
+
guardBoundaryLabel: "Boundary",
|
|
9416
|
+
guardBoundaryShort: "These numbers are informational only and never stop a task; the three tier thresholds participate in validation and counting only.",
|
|
9417
|
+
guardThresholdHint: "Defaults are 3 / 5 / 8; editing changes when the reminder fires. Reminders never block a call.",
|
|
9418
|
+
guardFired: "Fired",
|
|
9419
|
+
guardPurposeB: "Purpose: detect alternating A→B→A→B cycles and remind, so the agent stops going in circles.",
|
|
9420
|
+
guardPurposeC: "Purpose: block a confirmed repeat when the evidence is clear; otherwise only remind.",
|
|
9421
|
+
guardFootCycle: "Fires an explanation only; it never blocks the call.",
|
|
9422
|
+
guardFootNoProgress: "Degrades when: logs missing · results incomparable · match uncertain",
|
|
8480
9423
|
guardSectionA: "A · Repeated identical calls",
|
|
8481
9424
|
guardSectionB: "B · Alternating cycle detection",
|
|
8482
9425
|
guardSectionC: "C · No-progress protection",
|
|
@@ -8525,7 +9468,7 @@ async function apply(ctx) {
|
|
|
8525
9468
|
emptyGlobalSkills: "No global skills",
|
|
8526
9469
|
emptyProjectSkills: "No project skills",
|
|
8527
9470
|
emptyPresetSkills: "No preset skills",
|
|
8528
|
-
filterByName: "Filter
|
|
9471
|
+
filterByName: "Filter name or group",
|
|
8529
9472
|
filterHint: "Case-insensitive. Matches tool/skill names, MCP servers, skill source labels and preset ids; a regular expression works too, e.g. cordis|ptc or /^mcp__dbx/.",
|
|
8530
9473
|
filterNoMatch: "No capability matches (clear the filter to see the full list)",
|
|
8531
9474
|
emptyTools: "No tools",
|
|
@@ -8534,6 +9477,8 @@ async function apply(ctx) {
|
|
|
8534
9477
|
residentShort: "Resident",
|
|
8535
9478
|
onDemandShort: "On-demand",
|
|
8536
9479
|
disabledShort: "Disabled",
|
|
9480
|
+
catalogZeroCall: "No calls",
|
|
9481
|
+
catalogZeroCallHint: "Based on indexed calls",
|
|
8537
9482
|
cycleHint: "Click a tag to cycle its classification (On-demand → Disabled → Resident)",
|
|
8538
9483
|
notPreviewable: "This file is not a previewable text file",
|
|
8539
9484
|
previewClose: "Close",
|