dsh-thinking-guard 0.1.20 → 0.1.21

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/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$1 = `
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$1;
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,18 @@ 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}
3720
3922
  `;
3721
3923
  if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
3722
3924
  const tag = document.createElement("style");
@@ -3794,7 +3996,7 @@ function MarkdownFilePreview(props) {
3794
3996
  * `node_modules`, because the panel said nothing about itself. The value is
3795
3997
  * inlined by `tsdown` (see `src/client/env.d.ts`).
3796
3998
  */
3797
- const PLUGIN_VERSION = "0.1.20";
3999
+ const PLUGIN_VERSION = "0.1.21";
3798
4000
  const PLUGIN_TITLE = `capability-menu v${PLUGIN_VERSION}`;
3799
4001
  /**
3800
4002
  * dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
@@ -3821,6 +4023,200 @@ function sourceLabel(source, t) {
3821
4023
  const key = SOURCE_LABEL_KEYS[source];
3822
4024
  return key === void 0 ? source : t(key);
3823
4025
  }
4026
+ /** Tone of a class chip / state dot, mapped to the v3 `data-tone` palette. */
4027
+ const CLASS_TONE = {
4028
+ resident: "info",
4029
+ "on-demand": "warn",
4030
+ disabled: "bad"
4031
+ };
4032
+ /** State-dot tone inside a row: resident=info, on-demand=warn, disabled=neutral. */
4033
+ const CLASS_DOT_TONE = {
4034
+ resident: "info",
4035
+ "on-demand": "warn",
4036
+ disabled: ""
4037
+ };
4038
+ /** The four-cell `.v3-catstat` strip at the top of the catalog page. */
4039
+ function CatalogStatStrip(props) {
4040
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4041
+ className: "v3-catstat",
4042
+ "data-v3-catstat": "true",
4043
+ children: props.cells.map((cell, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4044
+ className: "v3-l",
4045
+ children: cell.label
4046
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4047
+ className: "v3-v",
4048
+ children: [cell.value, cell.hint !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: cell.hint }) : null]
4049
+ })] }, index))
4050
+ });
4051
+ }
4052
+ /** Zero-call capability count when a usage map exists, else an honest dash. */
4053
+ function zeroCallCount(usage, rows) {
4054
+ if (usage === void 0) return "—";
4055
+ return rows.filter((row) => {
4056
+ const entry = usage.get(row.id);
4057
+ return entry === void 0 || entry.uses === 0;
4058
+ }).length;
4059
+ }
4060
+ /** Strip a tool’s mcp__<server>__ prefix for display, like the old row did. */
4061
+ function displayToolName(server, name) {
4062
+ const prefix = "mcp__" + server + "__";
4063
+ return server !== "built-in" && name.startsWith(prefix) ? name.slice(prefix.length) : name;
4064
+ }
4065
+ /**
4066
+ * Two-column `.v3-catgrid` of native `<details>` server groups, each summary
4067
+ * carrying the chevron and the class-count tags, each row a 30px `.v3-trow`.
4068
+ * Pure: every interaction is a callback.
4069
+ */
4070
+ function ToolGroupsView(props) {
4071
+ const { t, groups, openServers, busy, usage } = props;
4072
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4073
+ className: "v3-catgrid",
4074
+ "data-v3-catgrid": "true",
4075
+ children: groups.map(({ server, tools }) => {
4076
+ const open = openServers.has(server);
4077
+ const counts = CLASS_KEYS.map((cls) => ({
4078
+ cls,
4079
+ count: countByClass(tools, cls)
4080
+ }));
4081
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
4082
+ className: "v3-group",
4083
+ open,
4084
+ "data-v3-group": server,
4085
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
4086
+ onClick: (e) => {
4087
+ e.preventDefault();
4088
+ props.onToggle(server);
4089
+ },
4090
+ children: [
4091
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
4092
+ className: "v3-chev",
4093
+ viewBox: "0 0 12 12",
4094
+ fill: "none",
4095
+ "aria-hidden": "true",
4096
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
4097
+ d: "M4.5 2.5 8 6l-3.5 3.5",
4098
+ stroke: "currentColor",
4099
+ strokeWidth: 1.4,
4100
+ strokeLinecap: "round",
4101
+ strokeLinejoin: "round"
4102
+ })
4103
+ }),
4104
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4105
+ className: "v3-gnm",
4106
+ children: server === "built-in" ? t("builtInGroup") : server
4107
+ }),
4108
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4109
+ className: "v3-cnt",
4110
+ children: tools.length
4111
+ }),
4112
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4113
+ className: "v3-right",
4114
+ children: [counts.filter(({ count }) => count > 0).map(({ cls, count }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4115
+ type: "button",
4116
+ className: "v3-tag",
4117
+ "data-tone": CLASS_TONE[cls],
4118
+ disabled: busy,
4119
+ title: t("cycleHint"),
4120
+ onClick: (e) => {
4121
+ e.stopPropagation();
4122
+ props.onCycle(tools.filter((tool) => tool.class === cls).map((tool) => tool.id), "tool");
4123
+ },
4124
+ children: [
4125
+ t(CLASS_SHORT_KEYS[cls]),
4126
+ " ",
4127
+ count
4128
+ ]
4129
+ }, cls)), server !== "built-in" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4130
+ type: "button",
4131
+ className: "v3-tag v3-tag--act",
4132
+ disabled: busy,
4133
+ onClick: (e) => {
4134
+ e.stopPropagation();
4135
+ props.onEditServer(server);
4136
+ },
4137
+ children: t("edit")
4138
+ })]
4139
+ })
4140
+ ]
4141
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4142
+ className: "v3-tools",
4143
+ children: tools.map((tool) => {
4144
+ const rowUsage = usage === void 0 ? void 0 : usage.get(tool.id);
4145
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4146
+ className: "v3-trow",
4147
+ title: tool.classLabel,
4148
+ role: "button",
4149
+ tabIndex: 0,
4150
+ "aria-label": tool.name,
4151
+ onClick: () => props.onOpenDetail(tool.id),
4152
+ onKeyDown: (e) => {
4153
+ if (e.key === "Enter" || e.key === " ") {
4154
+ e.preventDefault();
4155
+ props.onOpenDetail(tool.id);
4156
+ }
4157
+ },
4158
+ children: [
4159
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4160
+ className: "v3-dot",
4161
+ "data-tone": CLASS_DOT_TONE[tool.class],
4162
+ "aria-hidden": "true"
4163
+ }),
4164
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4165
+ className: "v3-nm",
4166
+ title: tool.name,
4167
+ children: displayToolName(server, tool.name)
4168
+ }),
4169
+ rowUsage !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4170
+ className: "v3-uses",
4171
+ children: [
4172
+ rowUsage.uses,
4173
+ " 次",
4174
+ rowUsage.failures !== void 0 && rowUsage.failures > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4175
+ className: "v3-uses-bad",
4176
+ children: [" · 失败 ", rowUsage.failures]
4177
+ }) : null
4178
+ ]
4179
+ }),
4180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4181
+ className: "v3-racts",
4182
+ children: [
4183
+ tool.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4184
+ className: "v3-tag",
4185
+ children: t("mandatory")
4186
+ }),
4187
+ tool.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4188
+ type: "button",
4189
+ className: "v3-tag v3-tag--act",
4190
+ title: t("editSearchMetadata"),
4191
+ onClick: (e) => {
4192
+ e.stopPropagation();
4193
+ props.onEditSearch(tool.id);
4194
+ },
4195
+ children: t("searchMetadataButton")
4196
+ }),
4197
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
4198
+ type: "button",
4199
+ className: "v3-tag",
4200
+ "data-tone": CLASS_TONE[tool.class],
4201
+ disabled: busy || tool.mandatory,
4202
+ title: tool.classLabel,
4203
+ "aria-label": tool.classLabel,
4204
+ onClick: (e) => {
4205
+ e.stopPropagation();
4206
+ props.onCycle([tool.id], "tool");
4207
+ },
4208
+ children: t(CLASS_SHORT_KEYS[tool.class])
4209
+ })
4210
+ ]
4211
+ })
4212
+ ]
4213
+ }, tool.id);
4214
+ })
4215
+ })]
4216
+ }, server);
4217
+ })
4218
+ });
4219
+ }
3824
4220
  /**
3825
4221
  * Whether the document is visible. A hidden settings tab does no polling and
3826
4222
  * takes no re-reads; coming back is itself a revalidation trigger.
@@ -3836,7 +4232,7 @@ function useDocumentVisible() {
3836
4232
  return visible;
3837
4233
  }
3838
4234
  function CapabilitySection(props) {
3839
- const { remote, t, mountError, subscribeSignals } = props;
4235
+ const { remote, t, mountError, subscribeSignals, usage } = props;
3840
4236
  const [state, setState] = (0, react.useState)(() => {
3841
4237
  const cached = cachedSnapshot();
3842
4238
  return cached === void 0 ? { status: "loading" } : {
@@ -4169,13 +4565,14 @@ function CapabilitySection(props) {
4169
4565
  onCycle: cycleClass,
4170
4566
  onRefresh: () => void refreshCatalog(),
4171
4567
  onRefreshMcp: (serverName) => void refreshRegisteredMcp(serverName),
4172
- onNotice: setNotice
4568
+ onNotice: setNotice,
4569
+ ...usage === void 0 ? {} : { usage }
4173
4570
  })
4174
4571
  ]
4175
4572
  });
4176
4573
  }
4177
4574
  function ReadyBody(props) {
4178
- const { remote, snapshot, diagnostics, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, onRefreshMcp, onNotice } = props;
4575
+ const { remote, snapshot, diagnostics, openServers, busy, activeTab, t, onTabChange, onToggleServer, onCycle, onRefresh, onRefreshMcp, onNotice, usage } = props;
4179
4576
  const [updateStatus, setUpdateStatus] = (0, react.useState)(null);
4180
4577
  const [updatePhase, setUpdatePhase] = (0, react.useState)("idle");
4181
4578
  const [updateError, setUpdateError] = (0, react.useState)("");
@@ -4463,347 +4860,216 @@ function ReadyBody(props) {
4463
4860
  t
4464
4861
  ]);
4465
4862
  const skillDiagnosticCount = diagnostics === null ? 0 : diagnostics.unavailableSkills.length + diagnostics.shadowedSkills.length + diagnostics.brokenPresets.length + diagnostics.unbrowseableSkills.length + diagnostics.undocumentedSkills.length;
4466
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4467
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4468
- className: "mc-heading-row",
4469
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4470
- className: "mc-heading-meta",
4471
- children: [
4472
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
4473
- className: "mc-heading",
4474
- children: t("title")
4475
- }),
4476
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4477
- className: "mc-version",
4478
- title: PLUGIN_TITLE,
4479
- children: ["v", PLUGIN_VERSION]
4480
- }),
4481
- updateStatus?.updateAvailable && (updateStatus.updatable || updateStatus.reason === "local") && updatePhase !== "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4482
- type: "button",
4483
- variant: "primary",
4484
- size: "sm",
4485
- className: "mc-update-check",
4486
- title: t("updateAvailable", { version: updateStatus.latest }),
4487
- disabled: updatePhase === "installing",
4488
- onClick: () => setUpdateDialogOpen(true),
4489
- children: t("newVersion")
4490
- })
4491
- ]
4492
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4493
- className: "mc-heading-actions",
4494
- children: [
4495
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4496
- size: "sm",
4497
- variant: "outline",
4498
- onClick: () => setRegisterOpen(true),
4499
- children: t("registerCapability")
4500
- }),
4501
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4502
- size: "sm",
4503
- variant: "outline",
4504
- onClick: () => void openCatalogDocs(),
4505
- children: t("viewCatalog")
4506
- }),
4507
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
4508
- className: "mc-feedback-link",
4509
- href: "https://github.com/PKUfudawei/dsh-capability-menu",
4510
- target: "_blank",
4511
- rel: "noopener noreferrer",
4512
- "aria-label": t("feedback"),
4513
- children: t("feedback")
4514
- })
4515
- ]
4516
- })]
4517
- }),
4518
- (updatePhase === "done" || updateStatus?.updateAvailable && !updateStatus.updatable && updateStatus.reason !== "local") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4519
- className: "mc-update-status",
4520
- role: "status",
4521
- 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") })]
4522
- }),
4523
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4524
- className: "mc-desc",
4525
- children: t("desc")
4526
- }),
4527
- diagnostics !== null && skillDiagnosticCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
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}`,
4863
+ const catalogUsage = usage;
4864
+ const classCount = (cls) => summary.find((entry) => entry.cls === cls)?.count ?? 0;
4865
+ const statCells = [
4866
+ {
4867
+ label: t("tierResident"),
4868
+ value: classCount("resident")
4869
+ },
4870
+ {
4871
+ label: t("tierOnDemand"),
4872
+ value: classCount("on-demand")
4873
+ },
4874
+ {
4875
+ label: t("tierDisabled"),
4876
+ value: classCount("disabled")
4877
+ },
4878
+ {
4879
+ label: t("catalogZeroCall"),
4880
+ value: zeroCallCount(catalogUsage, statRows),
4881
+ hint: t("catalogZeroCallHint")
4882
+ }
4883
+ ];
4884
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4885
+ className: "v3-root",
4886
+ children: [
4887
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(V3Styles, {}),
4888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4889
+ className: "mc-heading-row",
4890
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4891
+ className: "mc-heading-meta",
4577
4892
  children: [
4578
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4579
- className: `mc-dot mc-dot--${cls}`,
4580
- "aria-hidden": "true"
4893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
4894
+ className: "mc-heading",
4895
+ children: t("title")
4581
4896
  }),
4582
- t(CLASS_SHORT_KEYS[cls]),
4583
- " · ",
4584
- count
4897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4898
+ className: "mc-version",
4899
+ title: PLUGIN_TITLE,
4900
+ children: ["v", PLUGIN_VERSION]
4901
+ }),
4902
+ updateStatus?.updateAvailable && (updateStatus.updatable || updateStatus.reason === "local") && updatePhase !== "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4903
+ type: "button",
4904
+ variant: "primary",
4905
+ size: "sm",
4906
+ className: "mc-update-check",
4907
+ title: t("updateAvailable", { version: updateStatus.latest }),
4908
+ disabled: updatePhase === "installing",
4909
+ onClick: () => setUpdateDialogOpen(true),
4910
+ children: t("newVersion")
4911
+ })
4585
4912
  ]
4586
- }, cls))
4587
- })]
4588
- }),
4589
- snapshot.rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4590
- className: "mc-filter",
4591
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
4592
- type: "search",
4593
- value: filter,
4594
- placeholder: t("filterByName"),
4595
- "aria-label": t("filterByName"),
4596
- title: t("filterHint"),
4597
- onChange: (e) => setFilter(e.target.value)
4598
- })
4599
- }),
4600
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4601
- role: "tabpanel",
4602
- id: "mc-panel-tools",
4603
- "aria-labelledby": "mc-tab-tools",
4604
- hidden: activeTab !== "tools",
4605
- className: "mc-panel",
4606
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4607
- className: "mc-panel-inner",
4608
- children: servers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4609
- className: "mc-empty",
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,
4913
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4914
+ className: "mc-heading-actions",
4915
+ children: [
4916
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4917
+ size: "sm",
4918
+ variant: "outline",
4919
+ onClick: () => setRegisterOpen(true),
4920
+ children: t("registerCapability")
4921
+ }),
4922
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4923
+ size: "sm",
4924
+ variant: "outline",
4925
+ onClick: () => void openCatalogDocs(),
4926
+ children: t("viewCatalog")
4927
+ }),
4928
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
4929
+ className: "mc-feedback-link",
4930
+ href: "https://github.com/PKUfudawei/dsh-capability-menu",
4931
+ target: "_blank",
4932
+ rel: "noopener noreferrer",
4933
+ "aria-label": t("feedback"),
4934
+ children: t("feedback")
4935
+ })
4936
+ ]
4937
+ })]
4938
+ }),
4939
+ (updatePhase === "done" || updateStatus?.updateAvailable && !updateStatus.updatable && updateStatus.reason !== "local") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4940
+ className: "mc-update-status",
4941
+ role: "status",
4942
+ 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") })]
4943
+ }),
4944
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4945
+ className: "mc-desc",
4946
+ children: t("desc")
4947
+ }),
4948
+ diagnostics !== null && skillDiagnosticCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
4949
+ className: "mc-diagnostics",
4950
+ children: [
4951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("diagnosticTitle", { count: skillDiagnosticCount }) }),
4952
+ diagnostics.unavailableSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnavailable", {
4953
+ name: item.name,
4954
+ source: item.source,
4955
+ preset: item.preset ?? ""
4956
+ }) }, `unavailable-${item.name}-${item.preset ?? ""}-${index}`)),
4957
+ diagnostics.shadowedSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticShadowed", {
4958
+ name: item.name,
4959
+ winner: item.selectedFrom,
4960
+ hidden: item.shadowedFrom
4961
+ }) }, `shadowed-${item.name}-${item.shadowedFrom}-${index}`)),
4962
+ diagnostics.brokenPresets.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticBroken", {
4963
+ id: item.id,
4964
+ reason: item.reason
4965
+ }) }, `broken-${item.id}`)),
4966
+ diagnostics.unbrowseableSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnbrowseable", {
4967
+ name: item.name,
4968
+ reason: item.reason
4969
+ }) }, `browse-${item.name}`)),
4970
+ diagnostics.undocumentedSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUndocumented", {
4971
+ name: item.name,
4972
+ summary: item.summary
4973
+ }) }, `undocumented-${item.name}`))
4974
+ ]
4975
+ }),
4976
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4977
+ className: "mc-tabs",
4978
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
4979
+ className: "mc-main-tabs",
4980
+ items: [{
4981
+ value: "tools",
4982
+ id: "mc-tab-tools",
4983
+ panelId: "mc-panel-tools",
4984
+ label: t("toolsGroup")
4985
+ }, {
4986
+ value: "skills",
4987
+ id: "mc-tab-skills",
4988
+ panelId: "mc-panel-skills",
4772
4989
  label: t("skillsGroup")
4990
+ }],
4991
+ value: activeTab,
4992
+ onChange: onTabChange,
4993
+ label: t("title")
4994
+ })
4995
+ }),
4996
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CatalogStatStrip, { cells: statCells }),
4997
+ snapshot.rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4998
+ className: "mc-filter",
4999
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
5000
+ type: "search",
5001
+ value: filter,
5002
+ placeholder: t("filterByName"),
5003
+ "aria-label": t("filterByName"),
5004
+ title: t("filterHint"),
5005
+ onChange: (e) => setFilter(e.target.value)
5006
+ })
5007
+ }),
5008
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5009
+ role: "tabpanel",
5010
+ id: "mc-panel-tools",
5011
+ "aria-labelledby": "mc-tab-tools",
5012
+ hidden: activeTab !== "tools",
5013
+ className: "mc-panel",
5014
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5015
+ className: "mc-panel-inner",
5016
+ children: servers.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5017
+ className: "mc-empty",
5018
+ children: needle === "" ? t("emptyTools") : t("filterNoMatch")
5019
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolGroupsView, {
5020
+ t,
5021
+ groups: servers,
5022
+ openServers,
5023
+ busy,
5024
+ usage: catalogUsage,
5025
+ onToggle: onToggleServer,
5026
+ onCycle,
5027
+ onOpenDetail: (id) => void openToolDetail(id),
5028
+ onEditServer: (server) => void openMcpEdit(server),
5029
+ onEditSearch: (id) => void openSearchEdit(id, "tool")
4773
5030
  })
4774
- }), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
4775
- skills: globalSkills,
4776
- remote,
4777
- busy,
4778
- t,
4779
- onCycle,
4780
- editableSkills,
4781
- onEditSkill: openSkillEdit,
4782
- onAdoptSkill: adoptSkill,
4783
- onEditSearch: (id) => void openSearchEdit(id, "skill")
4784
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4785
- className: "mc-empty",
4786
- children: t("emptyGlobalSkills")
4787
- }) : skillTab === "project" ? projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
4788
- skills: projectSkills,
4789
- remote,
4790
- busy,
4791
- t,
4792
- onCycle,
4793
- editableSkills,
4794
- onEditSkill: openSkillEdit,
4795
- onAdoptSkill: adoptSkill,
4796
- onEditSearch: (id) => void openSearchEdit(id, "skill")
4797
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4798
- className: "mc-empty",
4799
- children: t("emptyProjectSkills")
4800
- }) : presetGroups.length > 0 ? presetGroups.map(([preset, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4801
- className: "mc-skill-group",
4802
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4803
- className: "mc-skill-group-title",
4804
- children: preset
4805
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
4806
- skills: group,
5031
+ })
5032
+ }),
5033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5034
+ role: "tabpanel",
5035
+ id: "mc-panel-skills",
5036
+ "aria-labelledby": "mc-tab-skills",
5037
+ hidden: activeTab !== "skills",
5038
+ className: `mc-panel${skills.length > 0 ? " mc-panel--tight" : ""}`,
5039
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5040
+ className: "mc-panel-inner",
5041
+ children: skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5042
+ className: "mc-empty",
5043
+ children: needle === "" ? t("emptySkills") : t("filterNoMatch")
5044
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5045
+ className: "mc-subtabs",
5046
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
5047
+ items: [
5048
+ {
5049
+ value: "global",
5050
+ id: "mc-skill-tab-global",
5051
+ panelId: "mc-skill-panel-global",
5052
+ label: t("globalSkills")
5053
+ },
5054
+ {
5055
+ value: "project",
5056
+ id: "mc-skill-tab-project",
5057
+ panelId: "mc-skill-panel-project",
5058
+ label: t("projectSkills")
5059
+ },
5060
+ {
5061
+ value: "preset",
5062
+ id: "mc-skill-tab-preset",
5063
+ panelId: "mc-skill-panel-preset",
5064
+ label: t("presetSkills")
5065
+ }
5066
+ ],
5067
+ value: skillTab,
5068
+ onChange: setSkillTab,
5069
+ label: t("skillsGroup")
5070
+ })
5071
+ }), skillTab === "global" ? globalSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5072
+ skills: globalSkills,
4807
5073
  remote,
4808
5074
  busy,
4809
5075
  t,
@@ -4812,307 +5078,339 @@ function ReadyBody(props) {
4812
5078
  onEditSkill: openSkillEdit,
4813
5079
  onAdoptSkill: adoptSkill,
4814
5080
  onEditSearch: (id) => void openSearchEdit(id, "skill")
4815
- })]
4816
- }, preset)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4817
- className: "mc-empty",
4818
- children: t("emptyPresetSkills")
4819
- })] })
4820
- })
4821
- }),
4822
- toolDetail !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
4823
- open: true,
4824
- onClose: () => setToolDetail(null),
4825
- title: toolDetail.detail?.name ?? toolDetail.id,
4826
- closeLabel: t("previewClose"),
4827
- children: [
4828
- toolDetail.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4829
- className: "mc-preview-hint",
4830
- children: "…"
4831
- }),
4832
- toolDetail.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4833
- className: "mc-preview-hint",
4834
- children: toolDetail.message
4835
- }),
4836
- toolDetail.status === "ready" && toolDetail.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4837
- className: "mc-preview-body",
4838
- children: [toolDetail.detail.description.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4839
- className: "mc-tool-description",
4840
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
4841
- text: toolDetail.detail.description,
4842
- t
4843
- })
4844
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
4845
- className: "mc-preview-plain",
4846
- children: JSON.stringify({
4847
- type: "function",
4848
- function: {
4849
- name: toolDetail.detail.name,
4850
- parameters: toolDetail.detail.parameters
4851
- }
4852
- }, null, 2)
4853
- })]
5081
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5082
+ className: "mc-empty",
5083
+ children: t("emptyGlobalSkills")
5084
+ }) : skillTab === "project" ? projectSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5085
+ skills: projectSkills,
5086
+ remote,
5087
+ busy,
5088
+ t,
5089
+ onCycle,
5090
+ editableSkills,
5091
+ onEditSkill: openSkillEdit,
5092
+ onAdoptSkill: adoptSkill,
5093
+ onEditSearch: (id) => void openSearchEdit(id, "skill")
5094
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5095
+ className: "mc-empty",
5096
+ children: t("emptyProjectSkills")
5097
+ }) : presetGroups.length > 0 ? presetGroups.map(([preset, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5098
+ className: "mc-skill-group",
5099
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5100
+ className: "mc-skill-group-title",
5101
+ children: preset
5102
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillList, {
5103
+ skills: group,
5104
+ remote,
5105
+ busy,
5106
+ t,
5107
+ onCycle,
5108
+ editableSkills,
5109
+ onEditSkill: openSkillEdit,
5110
+ onAdoptSkill: adoptSkill,
5111
+ onEditSearch: (id) => void openSearchEdit(id, "skill")
5112
+ })]
5113
+ }, preset)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5114
+ className: "mc-empty",
5115
+ children: t("emptyPresetSkills")
5116
+ })] })
4854
5117
  })
4855
- ]
4856
- }),
4857
- updateDialogOpen && updateStatus?.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
4858
- open: true,
4859
- onClose: closeUpdateDialog,
4860
- title: t("updateDialogTitle"),
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",
5118
+ }),
5119
+ toolDetail !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5120
+ open: true,
5121
+ onClose: () => setToolDetail(null),
5122
+ title: toolDetail.detail?.name ?? toolDetail.id,
5123
+ closeLabel: t("previewClose"),
4865
5124
  children: [
4866
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4867
- type: "button",
4868
- variant: "outline",
4869
- size: "sm",
4870
- className: "mc-update-cancel",
4871
- disabled: updatePhase === "installing",
4872
- onClick: closeUpdateDialog,
4873
- children: t("cancel")
5125
+ toolDetail.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5126
+ className: "mc-preview-hint",
5127
+ children: "…"
4874
5128
  }),
4875
- updateStatus.reason === "local" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4876
- type: "button",
4877
- variant: "outline",
4878
- size: "sm",
4879
- disabled: updatePhase === "installing",
4880
- onClick: () => setSourceStepsOpen((value) => !value),
4881
- children: t(sourceStepsOpen ? "updateSourceStepsHide" : "updateSourceSteps")
5129
+ toolDetail.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5130
+ className: "mc-preview-hint",
5131
+ children: toolDetail.message
4882
5132
  }),
4883
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4884
- type: "button",
4885
- variant: "primary",
4886
- size: "sm",
4887
- disabled: updatePhase === "installing",
4888
- onClick: () => void installUpdate(updateStatus.reason === "local" ? "local" : "registry"),
4889
- children: t(updateStatus.reason === "local" ? "switchAndInstall" : "installUpdate")
5133
+ toolDetail.status === "ready" && toolDetail.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5134
+ className: "mc-preview-body",
5135
+ children: [toolDetail.detail.description.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5136
+ className: "mc-tool-description",
5137
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownPreview, {
5138
+ text: toolDetail.detail.description,
5139
+ t
5140
+ })
5141
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
5142
+ className: "mc-preview-plain",
5143
+ children: JSON.stringify({
5144
+ type: "function",
5145
+ function: {
5146
+ name: toolDetail.detail.name,
5147
+ parameters: toolDetail.detail.parameters
5148
+ }
5149
+ }, null, 2)
5150
+ })]
4890
5151
  })
4891
5152
  ]
4892
5153
  }),
4893
- children: [
4894
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateVersionComparison", {
4895
- current: updateStatus.current,
4896
- latest: updateStatus.latest
4897
- }) }),
4898
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(updateStatus.reason === "local" ? "updateLocalNote" : "updateNpmNote") }),
4899
- updateStatus.reason === "local" && sourceStepsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4900
- className: "mc-update-steps",
4901
- children: [
4902
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsIntro") }),
4903
- updateStatus.sourcePath && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4904
- className: "mc-confirm-path",
4905
- children: updateStatus.sourcePath
4906
- }),
4907
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: "git pull\npnpm install\npnpm run build" }),
4908
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsRestart") })
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",
5154
+ updateDialogOpen && updateStatus?.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5155
+ open: true,
5156
+ onClose: closeUpdateDialog,
5157
+ title: t("updateDialogTitle"),
5158
+ closeLabel: t("previewClose"),
5159
+ className: "mc-update-dialog",
5160
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5161
+ className: "mc-confirm-actions mc-update-actions",
4918
5162
  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
- ]
4971
- }),
4972
- searchEdit.message !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4973
- className: "mc-error",
4974
- children: searchEdit.message
5163
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5164
+ type: "button",
5165
+ variant: "outline",
5166
+ size: "sm",
5167
+ className: "mc-update-cancel",
5168
+ disabled: updatePhase === "installing",
5169
+ onClick: closeUpdateDialog,
5170
+ children: t("cancel")
4975
5171
  }),
4976
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4977
- className: "mc-confirm-actions",
4978
- children: [searchDescription !== searchEdit.metadata?.defaultDescription && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
4979
- size: "sm",
4980
- variant: "ghost",
4981
- disabled: searchSaving,
4982
- title: t("searchMetadataRestoreHint"),
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
- })]
4992
- })
4993
- ]
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
5172
+ updateStatus.reason === "local" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5173
+ type: "button",
5174
+ variant: "outline",
5175
+ size: "sm",
5176
+ disabled: updatePhase === "installing",
5177
+ onClick: () => setSourceStepsOpen((value) => !value),
5178
+ children: t(sourceStepsOpen ? "updateSourceStepsHide" : "updateSourceSteps")
5179
+ }),
5180
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5181
+ type: "button",
5182
+ variant: "primary",
5183
+ size: "sm",
5184
+ disabled: updatePhase === "installing",
5185
+ onClick: () => void installUpdate(updateStatus.reason === "local" ? "local" : "registry"),
5186
+ children: t(updateStatus.reason === "local" ? "switchAndInstall" : "installUpdate")
5187
+ })
5188
+ ]
5027
5189
  }),
5028
- catalogDocs.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5029
- className: "mc-subtabs mc-catalog-tabs",
5030
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
5031
- items: [{
5032
- value: "policy",
5033
- id: "mc-cat-tab-policy",
5034
- panelId: "mc-cat-panel-policy",
5035
- label: t("catalogPolicy")
5036
- }, {
5037
- value: "catalog",
5038
- id: "mc-cat-tab-catalog",
5039
- panelId: "mc-cat-panel-catalog",
5040
- label: t("catalogOnDemand")
5041
- }],
5042
- value: catalogTab,
5043
- onChange: setCatalogTab,
5044
- label: t("viewCatalog")
5190
+ children: [
5191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateVersionComparison", {
5192
+ current: updateStatus.current,
5193
+ latest: updateStatus.latest
5194
+ }) }),
5195
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(updateStatus.reason === "local" ? "updateLocalNote" : "updateNpmNote") }),
5196
+ updateStatus.reason === "local" && sourceStepsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5197
+ className: "mc-update-steps",
5198
+ children: [
5199
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsIntro") }),
5200
+ updateStatus.sourcePath && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5201
+ className: "mc-confirm-path",
5202
+ children: updateStatus.sourcePath
5203
+ }),
5204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: "git pull\npnpm install\npnpm run build" }),
5205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("updateSourceStepsRestart") })
5206
+ ]
5207
+ }),
5208
+ updatePhase === "installing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5209
+ role: "status",
5210
+ children: t("updatingPlugin")
5211
+ }),
5212
+ updatePhase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
5213
+ className: "mc-error",
5214
+ role: "alert",
5215
+ children: [
5216
+ t("pluginUpdateFailed"),
5217
+ " ",
5218
+ updateError
5219
+ ]
5045
5220
  })
5046
- }), catalogTab === "policy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5047
- className: "mc-catalog-path",
5048
- children: t("catalogPolicyNote")
5049
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
5050
- className: "mc-preview-body",
5051
- children: catalogDocs.docs.policyYaml
5052
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5053
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5054
- className: "mc-catalog-toolbar",
5055
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5056
- className: "mc-catalog-path",
5057
- children: t("catalogSearchNote", { count: catalogDocs.docs.onDemand.length })
5058
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5059
- className: "mc-summary-setting",
5060
- children: [
5061
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
5062
- htmlFor: "mc-summary-limit",
5063
- children: t("summaryLimit")
5064
- }),
5065
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5066
- id: "mc-summary-limit",
5067
- type: "number",
5068
- min: 20,
5069
- step: 1,
5070
- value: summaryLimitDraft,
5071
- disabled: !catalogDocs.docs.searchSettingsEnabled || summaryLimitSaving,
5072
- onChange: (e) => {
5073
- setSummaryLimitDraft(e.target.value);
5074
- setSummaryLimitError(void 0);
5075
- }
5076
- }),
5077
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitUnit") }),
5078
- catalogDocs.docs.searchSettingsEnabled && summaryLimitDraft !== String(catalogDocs.docs.summaryMaxChars) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5221
+ ]
5222
+ }),
5223
+ searchEdit !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5224
+ open: true,
5225
+ onClose: () => setSearchEdit(null),
5226
+ title: `${t("editSearchMetadata")} · ${searchEdit.id}`,
5227
+ closeLabel: t("previewClose"),
5228
+ className: "mc-search-dialog",
5229
+ children: [
5230
+ searchEdit.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5231
+ className: "mc-preview-hint",
5232
+ children: "…"
5233
+ }),
5234
+ searchEdit.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5235
+ className: "mc-preview-hint",
5236
+ children: searchEdit.message
5237
+ }),
5238
+ searchEdit.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5239
+ className: "mc-confirm-body",
5240
+ children: [
5241
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5242
+ className: "mc-search-field",
5243
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5244
+ className: "mc-search-label-row",
5245
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
5246
+ htmlFor: "mc-search-description",
5247
+ children: t("searchDescription")
5248
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5249
+ id: "mc-search-description-note",
5250
+ children: t("searchMetadataNote")
5251
+ })]
5252
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
5253
+ id: "mc-search-description",
5254
+ "aria-describedby": "mc-search-description-note",
5255
+ value: searchDescription,
5256
+ maxLength: searchEdit.metadata?.maxChars,
5257
+ disabled: searchSaving,
5258
+ onChange: (e) => setSearchDescription(e.target.value)
5259
+ })]
5260
+ }),
5261
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5262
+ className: "mc-search-hint",
5263
+ children: [
5264
+ searchDescription.length,
5265
+ "/",
5266
+ searchEdit.metadata?.maxChars
5267
+ ]
5268
+ }),
5269
+ searchEdit.message !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5270
+ className: "mc-error",
5271
+ children: searchEdit.message
5272
+ }),
5273
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5274
+ className: "mc-confirm-actions",
5275
+ children: [searchDescription !== searchEdit.metadata?.defaultDescription && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5276
+ size: "sm",
5277
+ variant: "ghost",
5278
+ disabled: searchSaving,
5279
+ title: t("searchMetadataRestoreHint"),
5280
+ onClick: () => setSearchDescription(searchEdit.metadata?.defaultDescription ?? ""),
5281
+ children: t("searchMetadataRestore")
5282
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5079
5283
  size: "sm",
5080
5284
  variant: "primary",
5081
- disabled: summaryLimitSaving,
5082
- onClick: () => void saveSummaryLimit(),
5285
+ disabled: searchSaving,
5286
+ onClick: () => void saveSearchEdit(),
5083
5287
  children: t("searchMetadataSave")
5084
- }),
5085
- !catalogDocs.docs.searchSettingsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitDisabled") })
5086
- ]
5087
- })]
5288
+ })]
5289
+ })
5290
+ ]
5291
+ })
5292
+ ]
5293
+ }),
5294
+ (registerOpen || editMcp !== void 0 || editSkill !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LocationModal, {
5295
+ remote,
5296
+ t,
5297
+ defaultKind: activeTab === "skills" ? "skill" : "mcp",
5298
+ ...editMcp !== void 0 ? { editMcp } : {},
5299
+ ...editSkill !== void 0 ? { editSkill } : {},
5300
+ onClose: () => {
5301
+ setRegisterOpen(false);
5302
+ setEditMcp(void 0);
5303
+ setEditSkill(void 0);
5304
+ },
5305
+ onChanged: (notice, refreshMcpServer) => {
5306
+ if (refreshMcpServer !== void 0) onRefreshMcp(refreshMcpServer);
5307
+ else onRefresh();
5308
+ if (notice !== void 0) onNotice(notice);
5309
+ }
5310
+ }),
5311
+ catalogDocs !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5312
+ open: true,
5313
+ onClose: () => setCatalogDocs(null),
5314
+ title: t("viewCatalog"),
5315
+ closeLabel: t("previewClose"),
5316
+ children: [
5317
+ catalogDocs.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5318
+ className: "mc-preview-hint",
5319
+ children: "…"
5088
5320
  }),
5089
- summaryLimitError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5090
- className: "mc-error mc-catalog-limit-error",
5091
- children: summaryLimitError
5321
+ catalogDocs.status === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5322
+ className: "mc-preview-hint",
5323
+ children: catalogDocs.message
5092
5324
  }),
5093
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5325
+ catalogDocs.status === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5326
+ className: "mc-subtabs mc-catalog-tabs",
5327
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
5328
+ items: [{
5329
+ value: "policy",
5330
+ id: "mc-cat-tab-policy",
5331
+ panelId: "mc-cat-panel-policy",
5332
+ label: t("catalogPolicy")
5333
+ }, {
5334
+ value: "catalog",
5335
+ id: "mc-cat-tab-catalog",
5336
+ panelId: "mc-cat-panel-catalog",
5337
+ label: t("catalogOnDemand")
5338
+ }],
5339
+ value: catalogTab,
5340
+ onChange: setCatalogTab,
5341
+ label: t("viewCatalog")
5342
+ })
5343
+ }), catalogTab === "policy" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5344
+ className: "mc-catalog-path",
5345
+ children: t("catalogPolicyNote")
5346
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
5094
5347
  className: "mc-preview-body",
5095
- children: [catalogDocs.docs.onDemand.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5096
- className: "mc-preview-hint",
5097
- children: t("catalogEmpty")
5098
- }), catalogDocs.docs.onDemand.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5099
- className: "mc-search-result",
5100
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5101
- className: "mc-search-result-head",
5102
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.kind === "tool" ? "Tool" : "Skill" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5103
- className: "mc-search-result-id",
5104
- children: row.id
5105
- })]
5106
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5107
- className: "mc-search-result-summary",
5108
- children: row.summary
5348
+ children: catalogDocs.docs.policyYaml
5349
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5350
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5351
+ className: "mc-catalog-toolbar",
5352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5353
+ className: "mc-catalog-path",
5354
+ children: t("catalogSearchNote", { count: catalogDocs.docs.onDemand.length })
5355
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5356
+ className: "mc-summary-setting",
5357
+ children: [
5358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
5359
+ htmlFor: "mc-summary-limit",
5360
+ children: t("summaryLimit")
5361
+ }),
5362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
5363
+ id: "mc-summary-limit",
5364
+ type: "number",
5365
+ min: 20,
5366
+ step: 1,
5367
+ value: summaryLimitDraft,
5368
+ disabled: !catalogDocs.docs.searchSettingsEnabled || summaryLimitSaving,
5369
+ onChange: (e) => {
5370
+ setSummaryLimitDraft(e.target.value);
5371
+ setSummaryLimitError(void 0);
5372
+ }
5373
+ }),
5374
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitUnit") }),
5375
+ catalogDocs.docs.searchSettingsEnabled && summaryLimitDraft !== String(catalogDocs.docs.summaryMaxChars) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5376
+ size: "sm",
5377
+ variant: "primary",
5378
+ disabled: summaryLimitSaving,
5379
+ onClick: () => void saveSummaryLimit(),
5380
+ children: t("searchMetadataSave")
5381
+ }),
5382
+ !catalogDocs.docs.searchSettingsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("summaryLimitDisabled") })
5383
+ ]
5109
5384
  })]
5110
- }, `${row.kind}:${row.id}`))]
5111
- })
5112
- ] })] })
5113
- ]
5114
- })
5115
- ] });
5385
+ }),
5386
+ summaryLimitError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5387
+ className: "mc-error mc-catalog-limit-error",
5388
+ children: summaryLimitError
5389
+ }),
5390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5391
+ className: "mc-preview-body",
5392
+ children: [catalogDocs.docs.onDemand.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5393
+ className: "mc-preview-hint",
5394
+ children: t("catalogEmpty")
5395
+ }), catalogDocs.docs.onDemand.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5396
+ className: "mc-search-result",
5397
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5398
+ className: "mc-search-result-head",
5399
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row.kind === "tool" ? "Tool" : "Skill" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5400
+ className: "mc-search-result-id",
5401
+ children: row.id
5402
+ })]
5403
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5404
+ className: "mc-search-result-summary",
5405
+ children: row.summary
5406
+ })]
5407
+ }, `${row.kind}:${row.id}`))]
5408
+ })
5409
+ ] })] })
5410
+ ]
5411
+ })
5412
+ ]
5413
+ });
5116
5414
  }
5117
5415
  function projectRootForSkill(path) {
5118
5416
  return path.match(/^(.*)\/(?:\.dsh|\.agents)\/skills(?:\/.*)?$/)?.[1] ?? path;
@@ -5298,111 +5596,118 @@ function SkillList(props) {
5298
5596
  show the directory the click will link. */
5299
5597
  const confirmAdoptRow = confirmAdopt === null ? void 0 : skills.find((row) => row.id === confirmAdopt);
5300
5598
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5301
- skills.map((skill) => {
5302
- const open = openSkill === skill.id;
5303
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5304
- className: "mc-skill",
5305
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5306
- className: "mc-skill-row",
5307
- role: "button",
5308
- tabIndex: 0,
5309
- "aria-expanded": open,
5310
- onClick: () => void toggleSkill(skill.id),
5311
- onKeyDown: (e) => {
5312
- if (e.key === "Enter" || e.key === " ") {
5599
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5600
+ className: "v3-catgrid",
5601
+ children: skills.map((skill) => {
5602
+ const open = openSkill === skill.id;
5603
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
5604
+ className: "v3-group",
5605
+ open,
5606
+ "data-v3-skill": skill.id,
5607
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
5608
+ onClick: (e) => {
5313
5609
  e.preventDefault();
5314
5610
  toggleSkill(skill.id);
5315
- }
5316
- },
5317
- children: [
5318
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTriangleRightFill, {
5319
- size: 12,
5320
- className: `mc-chevron${open ? " mc-chevron--open" : ""}`
5321
- }),
5322
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5323
- className: "mc-skill-name",
5324
- children: skill.name
5325
- }),
5326
- PROJECT_SKILL_SOURCES.has(skill.source ?? "") && skill.path !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5327
- className: "mc-source",
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")
5611
+ },
5612
+ children: [
5613
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5614
+ className: "v3-chev",
5615
+ viewBox: "0 0 12 12",
5616
+ fill: "none",
5617
+ "aria-hidden": "true",
5618
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5619
+ d: "M4.5 2.5 8 6l-3.5 3.5",
5620
+ stroke: "currentColor",
5621
+ strokeWidth: 1.4,
5622
+ strokeLinecap: "round",
5623
+ strokeLinejoin: "round"
5388
5624
  })
5389
- ]
5625
+ }),
5626
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5627
+ className: "v3-gnm",
5628
+ children: skill.name
5629
+ }),
5630
+ PROJECT_SKILL_SOURCES.has(skill.source ?? "") && skill.path !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5631
+ className: "v3-cnt",
5632
+ title: skill.path,
5633
+ children: t("projectRootLabel", { path: projectRootForSkill(skill.path) })
5634
+ }),
5635
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5636
+ className: "v3-right",
5637
+ children: [
5638
+ skill.mandatory && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5639
+ className: "v3-tag",
5640
+ children: t("mandatory")
5641
+ }),
5642
+ skill.class === "on-demand" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5643
+ type: "button",
5644
+ className: "v3-tag v3-tag--act",
5645
+ title: t("editSearchMetadata"),
5646
+ onClick: (e) => {
5647
+ e.stopPropagation();
5648
+ onEditSearch(skill.id);
5649
+ },
5650
+ children: t("searchMetadataButton")
5651
+ }),
5652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5653
+ type: "button",
5654
+ className: "v3-tag",
5655
+ "data-tone": CLASS_TONE[skill.class],
5656
+ disabled: busy || skill.mandatory,
5657
+ title: skill.classLabel,
5658
+ onClick: (e) => {
5659
+ e.stopPropagation();
5660
+ onCycle([skill.id], "skill");
5661
+ },
5662
+ children: t(CLASS_SHORT_KEYS[skill.class])
5663
+ }),
5664
+ !editableSkills.has(skill.id) && (skill.preset !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5665
+ className: "v3-tag",
5666
+ title: t("skillFromPresetHint"),
5667
+ children: t("skillFromPreset", { preset: skill.preset })
5668
+ }) : ADOPTABLE_SKILL_SOURCES.has(skill.source ?? "") ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5669
+ type: "button",
5670
+ className: "v3-tag v3-tag--act",
5671
+ disabled: busy,
5672
+ title: t("adoptSkillHint"),
5673
+ onClick: (e) => {
5674
+ e.stopPropagation();
5675
+ setConfirmAdopt(skill.id);
5676
+ },
5677
+ children: t("adoptSkill")
5678
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5679
+ className: "v3-tag",
5680
+ title: t("skillSourceHint"),
5681
+ children: t("skillSource", { source: sourceLabel(skill.source, t) })
5682
+ })),
5683
+ editableSkills.has(skill.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5684
+ type: "button",
5685
+ className: "v3-tag v3-tag--act",
5686
+ disabled: busy,
5687
+ onClick: (e) => {
5688
+ e.stopPropagation();
5689
+ onEditSkill(skill.id, skill.source);
5690
+ },
5691
+ children: t("edit")
5692
+ })
5693
+ ]
5694
+ })
5695
+ ]
5696
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5697
+ className: "mc-skill-body",
5698
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5699
+ className: "mc-tree",
5700
+ children: [browseErrors[`${skill.id}:`] !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5701
+ className: "mc-preview-hint",
5702
+ children: browseErrors[`${skill.id}:`]
5703
+ }), renderEntries(tree.dirs[`${skill.id}:`], skill.id, "") ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5704
+ className: "mc-tree-row",
5705
+ children: "…"
5706
+ })]
5390
5707
  })
5391
- ]
5392
- }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5393
- className: "mc-skill-body",
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);
5708
+ })]
5709
+ }, skill.id);
5710
+ })
5406
5711
  }),
5407
5712
  confirmAdopt !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
5408
5713
  open: true,
@@ -6708,14 +7013,211 @@ function shortCallIndexSessionId(sessionId) {
6708
7013
  }
6709
7014
  /** Inline stylesheet; a `<style>` element keeps this file dependency-free. */
6710
7015
  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}");
7016
+ 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-src i[data-zero=\"true\"]{background:var(--v3-panel-2)}");
6712
7017
  }
6713
- /** Small label + value cell used by both pages. */
6714
- function Metric(label, value, hint) {
7018
+ /** One v3 `.v3-stat` cell: label, big value, optional hint line, optional sparkline. */
7019
+ function V3Stat(props) {
6715
7020
  return (0, react.createElement)("div", {
6716
- className: "mc-ci-metric",
6717
- "data-call-index-metric": label
6718
- }, (0, react.createElement)("div", { className: "mc-ci-metric-label" }, label), (0, react.createElement)("div", { className: "mc-ci-metric-value mc-ci-num" }, value), hint === void 0 ? null : (0, react.createElement)("div", { className: "mc-ci-metric-hint" }, hint));
7021
+ className: "v3-stat",
7022
+ "data-call-index-metric": props.label
7023
+ }, (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);
7024
+ }
7025
+ /** Values → `x,y` points inside a 100×22 viewBox, baselined at 0. */
7026
+ function sparkPoints(values) {
7027
+ const W = 100, H = 22;
7028
+ const max = values.reduce((m, v) => Math.max(m, v), 0);
7029
+ const scale = max > 0 ? max : 1;
7030
+ const xAt = (i) => values.length <= 1 ? W / 2 : i * W / (values.length - 1);
7031
+ const yAt = (v) => 20 - v / scale * 18;
7032
+ const pts = values.map((v, i) => `${xAt(i).toFixed(1)},${yAt(v).toFixed(1)}`);
7033
+ return {
7034
+ line: pts.join(" "),
7035
+ area: `0,${H} ${pts.join(" ")} ${W},${H}`
7036
+ };
7037
+ }
7038
+ /** 22px trend sparkline: a line plus a translucent area fill, coloured by tone. */
7039
+ function Sparkline(props) {
7040
+ if (props.values.length < 2) return null;
7041
+ const points = sparkPoints(props.values);
7042
+ const colour = props.tone === "ok" ? "var(--v3-ok)" : props.tone === "bad" ? "var(--v3-bad)" : "var(--v3-info)";
7043
+ return (0, react.createElement)("svg", {
7044
+ className: "v3-spark",
7045
+ viewBox: "0 0 100 22",
7046
+ preserveAspectRatio: "none",
7047
+ "aria-hidden": "true",
7048
+ "data-call-index-spark": props.tone
7049
+ }, (0, react.createElement)("polygon", {
7050
+ className: "v3-spark-fl",
7051
+ points: points.area,
7052
+ fill: colour
7053
+ }), (0, react.createElement)("polyline", {
7054
+ className: "v3-spark-ln",
7055
+ points: points.line,
7056
+ stroke: colour
7057
+ }));
7058
+ }
7059
+ /** `server · tool` for an `mcp__` name, otherwise the bare tool name. */
7060
+ function toolDisplayLabel(toolName) {
7061
+ if (toolName.startsWith("mcp__")) {
7062
+ const parts = toolName.split("__");
7063
+ return parts.length >= 3 ? `${parts[1]} · ${parts.slice(2).join("__")}` : toolName;
7064
+ }
7065
+ return toolName;
7066
+ }
7067
+ /**
7068
+ * The v3 overview trend: a 720×212 line + area (calls), failure底柱 (error), a
7069
+ * dashed "recent week" band and a peak label. Drawn from `callIndexTrend`'s bars
7070
+ * so its numbers are the same ones the caption quotes — no second data path.
7071
+ */
7072
+ function OverviewTrendSvg(props) {
7073
+ const { chart } = props, W = 720, H = 212, padL = 34, padT = 12;
7074
+ const plotW = 676;
7075
+ const plotH = 174;
7076
+ const bars = chart.bars;
7077
+ const n = bars.length;
7078
+ const maxV = chart.maxCalls > 0 ? Math.ceil(chart.maxCalls / 10) * 10 : 1;
7079
+ const xAt = (i) => padL + (n <= 1 ? plotW / 2 : i * plotW / (n - 1));
7080
+ const yAt = (v) => 186 - v / maxV * plotH;
7081
+ const points = bars.map((bar, i) => `${xAt(i).toFixed(1)},${yAt(bar.calls).toFixed(1)}`);
7082
+ let peak = 0;
7083
+ for (let i = 1; i < n; i++) if (bars[i].calls > bars[peak].calls) peak = i;
7084
+ const children = [];
7085
+ if (n > 0) {
7086
+ const start = Math.max(0, n - 7);
7087
+ const slot = n > 1 ? plotW / (n - 1) : plotW;
7088
+ const left = Math.max(padL, xAt(start) - slot / 2);
7089
+ const right = Math.min(710, xAt(n - 1) + slot / 2);
7090
+ children.push((0, react.createElement)("rect", {
7091
+ key: "hl",
7092
+ className: "v3-hlbox",
7093
+ x: left.toFixed(1),
7094
+ y: padT,
7095
+ width: Math.max(0, right - left).toFixed(1),
7096
+ height: plotH.toFixed(1),
7097
+ rx: 4
7098
+ }));
7099
+ }
7100
+ for (let g = 0; g <= 4; g++) {
7101
+ const value = maxV * g / 4;
7102
+ const y = yAt(value);
7103
+ children.push((0, react.createElement)("line", {
7104
+ key: `grid-${g}`,
7105
+ className: "v3-grid",
7106
+ x1: padL,
7107
+ y1: y.toFixed(1),
7108
+ x2: 710,
7109
+ y2: y.toFixed(1)
7110
+ }));
7111
+ children.push((0, react.createElement)("text", {
7112
+ key: `tick-${g}`,
7113
+ className: "v3-axis",
7114
+ x: 27,
7115
+ y: (y + 3.5).toFixed(1),
7116
+ "text-anchor": "end"
7117
+ }, String(Math.round(value))));
7118
+ }
7119
+ const barWidth = Math.max(4, plotW / Math.max(1, n) - 8);
7120
+ bars.forEach((bar, i) => {
7121
+ if (bar.error <= 0) return;
7122
+ const errH = Math.max(2.5, bar.error / maxV * plotH);
7123
+ children.push((0, react.createElement)("rect", {
7124
+ key: `err-${bar.day}`,
7125
+ className: "v3-bar-er",
7126
+ x: (xAt(i) - barWidth / 2).toFixed(1),
7127
+ y: (186 - errH).toFixed(1),
7128
+ width: barWidth.toFixed(1),
7129
+ height: errH.toFixed(1),
7130
+ rx: 1.5
7131
+ }, (0, react.createElement)("title", null, `${bar.day}:失败 ${bar.error} 次`)));
7132
+ });
7133
+ children.push((0, react.createElement)("polygon", {
7134
+ key: "area",
7135
+ className: "v3-area",
7136
+ points: `${padL},186 ${points.join(" ")} 710,186`
7137
+ }));
7138
+ children.push((0, react.createElement)("polyline", {
7139
+ key: "line",
7140
+ className: "v3-line",
7141
+ points: points.join(" ")
7142
+ }));
7143
+ bars.forEach((bar, i) => {
7144
+ if (i % 4 !== 0 && i !== n - 1 && i !== peak) return;
7145
+ children.push((0, react.createElement)("circle", {
7146
+ key: `pt-${bar.day}`,
7147
+ className: "v3-pt",
7148
+ cx: xAt(i).toFixed(1),
7149
+ cy: yAt(bar.calls).toFixed(1),
7150
+ r: 2.6
7151
+ }, (0, react.createElement)("title", null, `${bar.day}:${bar.calls} 次调用,失败 ${bar.error} 次`)));
7152
+ });
7153
+ if (n > 0) {
7154
+ children.push((0, react.createElement)("text", {
7155
+ key: "peak",
7156
+ className: "v3-lbl-b",
7157
+ x: (xAt(peak) - 8).toFixed(1),
7158
+ y: (yAt(bars[peak].calls) - 9).toFixed(1),
7159
+ "text-anchor": "end"
7160
+ }, `峰值 ${bars[peak].calls}`));
7161
+ for (let m = 0; m < n; m += 5) children.push((0, react.createElement)("text", {
7162
+ key: `xl-${m}`,
7163
+ className: "v3-axis",
7164
+ x: xAt(m).toFixed(1),
7165
+ y: 204,
7166
+ "text-anchor": "middle"
7167
+ }, bars[m].day.slice(5)));
7168
+ children.push((0, react.createElement)("text", {
7169
+ key: "xl-last",
7170
+ className: "v3-axis",
7171
+ x: 710,
7172
+ y: 204,
7173
+ "text-anchor": "end"
7174
+ }, bars[n - 1].day.slice(5)));
7175
+ }
7176
+ return (0, react.createElement)("svg", {
7177
+ className: "v3-chart",
7178
+ viewBox: `0 0 ${W} ${H}`,
7179
+ preserveAspectRatio: "none",
7180
+ role: "img",
7181
+ "aria-label": `每日调用与失败数:${n} 天,峰值 ${chart.maxCalls} 次`,
7182
+ "data-call-index-svg": "trend"
7183
+ }, ...children);
7184
+ }
7185
+ /** 来源构成: a one-segment donut (未解析) + five-source list + an honesty note. */
7186
+ function OverviewSourcePanel(props) {
7187
+ if (props.bySource === 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", {
7188
+ className: "v3-hint",
7189
+ "data-call-index-missing": "by-source"
7190
+ }, "Host 未返回来源分布(bySource);这不是「全部为 0」。"));
7191
+ const data = callIndexSourceData(props.bySource);
7192
+ const total = data.reduce((sum, item) => sum + item.count, 0);
7193
+ const unknownCount = data.find((item) => item.kind === "unknown")?.count ?? 0;
7194
+ const share = total > 0 ? unknownCount / total : 0;
7195
+ const percent = Math.round(share * 100);
7196
+ const radius = 43;
7197
+ const circumference = 2 * Math.PI * radius;
7198
+ const segment = share * circumference;
7199
+ const allUnresolved = total > 0 && unknownCount === total;
7200
+ 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", {
7201
+ viewBox: "0 0 104 104",
7202
+ "aria-hidden": "true"
7203
+ }, (0, react.createElement)("circle", {
7204
+ className: "v3-trk",
7205
+ cx: 52,
7206
+ cy: 52,
7207
+ r: radius
7208
+ }), (0, react.createElement)("circle", {
7209
+ className: "v3-seg",
7210
+ cx: 52,
7211
+ cy: 52,
7212
+ r: radius,
7213
+ "data-seg": "unknown",
7214
+ strokeDasharray: `${segment.toFixed(1)} ${(circumference - segment).toFixed(1)}`,
7215
+ strokeDashoffset: 0
7216
+ })), (0, react.createElement)("div", { className: "v3-mid" }, (0, react.createElement)("b", { className: "v3-num" }, `${percent}%`), (0, react.createElement)("span", null, "未解析"))), (0, react.createElement)("div", { className: "v3-src-list" }, ...data.map((item) => (0, react.createElement)("div", {
7217
+ key: item.kind,
7218
+ className: "v3-src",
7219
+ "data-source-row": item.kind
7220
+ }, (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.share * 100)}%` : "0%"))))), (0, react.createElement)("p", { className: "v3-src-note" }, allUnresolved ? `Host 无法证明插件 owner,非 mcp__ 前缀的调用也不带归属证据,所以 ${formatCallIndexCount(total, props.totalExact)} 次调用全部落在「来源未解析」——这是当前唯一真实的分布形态,不是抽样。` : "Host 无法证明插件 owner,非 mcp__ 前缀的调用也不带归属证据;未解析出归属的调用单列为「来源未解析」,其余按 Host 能证明的 kind 归类。", (0, react.createElement)("b", { style: { color: "var(--v3-t3)" } }, " 0 在这里不等于未使用")));
6719
7221
  }
6720
7222
  function callIndexDiagnostics(input) {
6721
7223
  const items = [];
@@ -6775,96 +7277,6 @@ function StateNotice({ state, failure }) {
6775
7277
  "data-call-index-state": state.kind
6776
7278
  }, (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
7279
  }
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
7280
  /**
6869
7281
  * Turn the index census into a progress percentage.
6870
7282
  *
@@ -7033,26 +7445,73 @@ function callIndexToolRows(page) {
7033
7445
  ...entry.durationCount === 0 ? {} : { averageDurationMs: entry.durationSum / entry.durationCount }
7034
7446
  })).sort((a, b) => b.calls - a.calls || a.toolName.localeCompare(b.toolName));
7035
7447
  }
7036
- function CallIndexCallTable(props) {
7037
- const { page } = props;
7038
- const rows = callIndexToolRows(page);
7039
- return (0, react.createElement)("div", { className: "mc-ci-table-scroll" }, (0, react.createElement)("table", {
7040
- className: "mc-ci-table",
7448
+ /** Short source marker for the tool cell: MCP / 技能 / 插件 / 来源未解析. */
7449
+ function sourceNoteForKind(kind) {
7450
+ switch (kind) {
7451
+ case "mcp": return "MCP";
7452
+ case "skill": return "技能";
7453
+ case "plugin-tool": return "插件";
7454
+ case "unknown": return "来源未解析";
7455
+ default: return;
7456
+ }
7457
+ }
7458
+ /**
7459
+ * The v3 "完整工具表": one sticky-header row per TOOL, inside a bounded
7460
+ * `.v3-ts` scroller (330px here — the two charts above it already tell the big
7461
+ * picture). Takes pre-computed rows so the same filtered set feeds the charts.
7462
+ */
7463
+ function CallIndexToolTable(props) {
7464
+ return (0, react.createElement)("div", { className: "v3-ts v3-ts--h330" }, (0, react.createElement)("table", {
7465
+ className: "v3-table",
7041
7466
  "data-call-index-table": "tools"
7042
7467
  }, (0, react.createElement)("thead", null, (0, react.createElement)("tr", null, ...[
7043
- "时间",
7044
- "工具",
7045
- "来源",
7046
- "成功",
7047
- "失败",
7048
- "平均耗时"
7049
- ].map((title) => (0, react.createElement)("th", {
7050
- key: title,
7051
- scope: "col"
7052
- }, title)))), (0, react.createElement)("tbody", null, ...rows.map((row) => (0, react.createElement)("tr", {
7053
- key: row.toolName + "::" + row.sourceKind,
7054
- "data-call-index-tool": row.toolName
7055
- }, (0, react.createElement)("td", { className: "mc-ci-num" }, formatCallIndexTime(row.lastAt)), (0, react.createElement)("td", { title: row.toolName }, row.toolName), (0, react.createElement)("td", null, CALL_INDEX_SOURCE_LABELS[row.sourceKind]), (0, react.createElement)("td", { className: "mc-ci-num" }, String(row.success)), (0, react.createElement)("td", { className: "mc-ci-num" }, String(row.error)), (0, react.createElement)("td", { className: "mc-ci-num" }, formatCallIndexDuration(row.averageDurationMs)))))));
7468
+ {
7469
+ label: "工具",
7470
+ right: false
7471
+ },
7472
+ {
7473
+ label: "调用",
7474
+ right: true
7475
+ },
7476
+ {
7477
+ label: "失败",
7478
+ right: true
7479
+ },
7480
+ {
7481
+ label: "成功率",
7482
+ right: true
7483
+ },
7484
+ {
7485
+ label: "平均耗时",
7486
+ right: true
7487
+ },
7488
+ {
7489
+ label: "最近调用",
7490
+ right: true
7491
+ }
7492
+ ].map((col) => (0, react.createElement)("th", {
7493
+ key: col.label,
7494
+ scope: "col",
7495
+ ...col.right ? { className: "v3-r" } : {}
7496
+ }, col.label)))), (0, react.createElement)("tbody", null, ...props.rows.map((row) => {
7497
+ const known = row.success + row.error;
7498
+ const rate = known > 0 ? row.success / known : void 0;
7499
+ const bad = row.error > 0 && row.error / row.calls >= .2;
7500
+ const note = sourceNoteForKind(row.sourceKind);
7501
+ const server = row.toolName.startsWith("mcp__") ? row.toolName.split("__")[1] : void 0;
7502
+ const shortName = row.sourceKind === "mcp" && row.toolName.startsWith("mcp__") ? row.toolName.split("__").slice(2).join("__") : row.toolName;
7503
+ return (0, react.createElement)("tr", {
7504
+ key: row.toolName + "::" + row.sourceKind,
7505
+ "data-call-index-tool": row.toolName
7506
+ }, (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", {
7507
+ className: "v3-nm",
7508
+ title: row.toolName
7509
+ }, 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", {
7510
+ className: "v3-fl",
7511
+ ...bad ? { "data-bad": "true" } : {},
7512
+ style: { width: `${Math.round((rate ?? 0) * 100)}%` }
7513
+ })), (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)));
7514
+ }))));
7056
7515
  }
7057
7516
  function callIndexRates(summary) {
7058
7517
  if (summary === void 0) return {};
@@ -7084,7 +7543,7 @@ function formatCallIndexRate(value) {
7084
7543
  * Modal; the container decides it from a media query, so this stays pure.
7085
7544
  */
7086
7545
  function CallIndexDetailPanelView(props) {
7087
- const { query, status, page, loading, loadingMore, failure, onQueryChange, onResolveScope, catalog, moreFailure, startFailure, startTask } = props;
7546
+ const { query, status, page, loading, loadingMore, failure, onQueryChange, onResolveScope, catalog, moreFailure, startFailure, startTask, search, onSearchChange } = props;
7088
7547
  const state = callIndexDetailState({
7089
7548
  status,
7090
7549
  page,
@@ -7093,74 +7552,250 @@ function CallIndexDetailPanelView(props) {
7093
7552
  ...failure === void 0 ? {} : { failure }
7094
7553
  });
7095
7554
  const totalText = page === void 0 ? void 0 : formatCallIndexCount(page.total ?? page.summary.total, page.totalExact);
7096
- const activity = loading === true ? page === void 0 ? "正在读取…" : "正在刷新…" : page === void 0 ? "尚未读取" : `快照代 ${page.generation}`;
7555
+ const coverageLabel = page === void 0 ? void 0 : COVERAGE_LABELS[page.coverage];
7556
+ const scanning = status !== void 0 && (status.phase === "running" || (status.sessions?.pending ?? 0) > 0 || (status.sessions?.pendingLive ?? 0) > 0);
7557
+ const statusText = page === void 0 ? loading === true ? "正在读取调用明细…" : "尚未读取调用明细" : `快照代 ${page.generation} · 覆盖 ${coverageLabel ?? "未知"} · 按工具聚合`;
7097
7558
  const rates = callIndexRates(page?.summary);
7098
7559
  const successStats = rates.success;
7099
7560
  const failureStats = rates.failure;
7100
7561
  const rateText = (value) => formatCallIndexRate(value);
7101
7562
  const rateHint = (stats) => stats === void 0 ? "没有已知结果的调用,无法计算比例" : `${stats.numerator} / ${stats.denominator} 次已知结果`;
7102
7563
  const suggestionCount = page === void 0 ? void 0 : (page.summary.unresolved ?? 0) + page.summary.error;
7103
- const suggestionText = () => suggestionCount === void 0 ? "—" : String(suggestionCount);
7104
7564
  const suggestionHint = () => page === void 0 ? "尚未读取" : `失败 ${page.summary.error} + 未配对 ${page.summary.unresolved ?? 0};未配对表示没有捕获到结果,不等于失败`;
7565
+ const allRows = page === void 0 ? [] : callIndexToolRows(page);
7566
+ const term = (search ?? "").trim().toLowerCase();
7567
+ const rows = term === "" ? allRows : allRows.filter((row) => row.toolName.toLowerCase().includes(term));
7568
+ const durations = page === void 0 ? [] : page.items.map((item) => item.durationMs).filter((ms) => ms !== void 0 && Number.isFinite(ms));
7569
+ const averageDuration = durations.length > 0 ? durations.reduce((sum, ms) => sum + ms, 0) / durations.length : void 0;
7570
+ const readCalls = rows.reduce((sum, row) => sum + row.calls, 0);
7571
+ const totalCalls = page === void 0 ? void 0 : page.total ?? page.summary.total;
7572
+ const readIsPartial = totalCalls !== void 0 && readCalls < totalCalls;
7573
+ const rankRows = rows.slice(0, 12);
7574
+ const rankMax = rankRows.length > 0 ? rankRows[0].calls : 1;
7575
+ const failingRows = rows.filter((row) => row.error > 0).slice().sort((a, b) => b.error - a.error);
7576
+ const failMax = failingRows.length > 0 ? failingRows[0].error : 1;
7577
+ const searchInput = (0, react.createElement)("label", { className: "v3-search" }, (0, react.createElement)("svg", {
7578
+ width: 12,
7579
+ height: 12,
7580
+ viewBox: "0 0 14 14",
7581
+ fill: "none",
7582
+ "aria-hidden": "true",
7583
+ style: {
7584
+ color: "var(--v3-t4)",
7585
+ flex: "none"
7586
+ }
7587
+ }, (0, react.createElement)("circle", {
7588
+ cx: 6.2,
7589
+ cy: 6.2,
7590
+ r: 4.4,
7591
+ stroke: "currentColor",
7592
+ "stroke-width": 1.3
7593
+ }), (0, react.createElement)("path", {
7594
+ d: "m9.6 9.6 3 3",
7595
+ stroke: "currentColor",
7596
+ "stroke-width": 1.3,
7597
+ "stroke-linecap": "round"
7598
+ })), (0, react.createElement)("input", onSearchChange === void 0 ? {
7599
+ type: "search",
7600
+ placeholder: "筛选工具名…",
7601
+ "aria-label": "筛选工具名",
7602
+ defaultValue: search ?? "",
7603
+ "data-call-index-search": "true"
7604
+ } : {
7605
+ type: "search",
7606
+ placeholder: "筛选工具名…",
7607
+ "aria-label": "筛选工具名",
7608
+ value: search ?? "",
7609
+ "data-call-index-search": "true",
7610
+ onChange: (event) => onSearchChange(event.target.value)
7611
+ }));
7105
7612
  const indexBlockedReason = status === void 0 ? "尚未收到索引状态,无法判断索引进度(Host 未应答或回复被拒绝)" : status.scope.status !== "ready" ? "索引范围未启用,插件不会自动索引历史会话" : void 0;
7106
7613
  return (0, react.createElement)("section", {
7107
- className: "mc-section",
7614
+ className: "v3-root mc-section",
7108
7615
  "aria-label": "调用明细",
7109
7616
  "aria-busy": loading === true
7110
- }, CallIndexStyles(), (0, react.createElement)("header", { className: "mc-heading-row" }, (0, react.createElement)("div", { className: "mc-heading-meta" }, (0, react.createElement)("h2", { className: "mc-heading" }, "调用明细"), (0, react.createElement)("span", { className: "mc-version" }, activity)), null), (0, react.createElement)("p", { className: "mc-desc" }, "按工具调用记录会话内的每一次调用;只统计调用次数与元数据,不代表 Token 数,也不包含参数与返回值。"), indexBlockedReason === void 0 ? null : (0, react.createElement)("p", {
7111
- className: "mc-desc",
7112
- "data-call-index-note": "index-blocked",
7113
- role: "status"
7114
- }, `自动索引不可用:${indexBlockedReason}`), CallIndexFilters({
7617
+ }, CallIndexStyles(), V3Styles(), (0, react.createElement)("div", { className: "v3-status-pills" }, (0, react.createElement)("span", {
7618
+ className: "v3-statusline",
7619
+ ...state.kind === "failure" || state.kind === "disconnected" ? { "data-tone": "bad" } : {}
7620
+ }, (0, react.createElement)("span", {
7621
+ className: "v3-dot",
7622
+ "data-tone": state.kind === "failure" || state.kind === "disconnected" ? "bad" : scanning ? "run" : "ok",
7623
+ "aria-hidden": "true"
7624
+ }), statusText), (0, react.createElement)("span", { className: "v3-statusline v3-status-dash" }, `${rows.length} 个工具 · ${formatCallIndexCount(readCalls, page?.totalExact)} 次调用${readIsPartial ? `(全部 ${formatCallIndexCount(totalCalls, page?.totalExact)} 次)` : ""}`)), (0, react.createElement)("div", { className: "v3-panel v3-filtercard" }, (0, react.createElement)("div", { className: "v3-filter-row" }, CallIndexFilters({
7115
7625
  query,
7116
7626
  ...onQueryChange === void 0 ? {} : { onChange: onQueryChange },
7117
7627
  ...onResolveScope === void 0 ? {} : { onResolveScope },
7118
7628
  ...catalog === void 0 ? {} : { catalog }
7119
- }), CallIndexScanBar({ ...status === void 0 ? {} : { status } }), (0, react.createElement)(StateNotice, {
7629
+ }), searchInput), CallIndexScanBar({ ...status === void 0 ? {} : { status } })), (0, react.createElement)(StateNotice, {
7120
7630
  state,
7121
7631
  ...failure === void 0 ? {} : { failure }
7122
7632
  }), startFailure === void 0 ? null : (0, react.createElement)("p", {
7123
- className: "mc-desc",
7633
+ className: "v3-hint",
7124
7634
  "data-call-index-note": "start-failure",
7125
7635
  role: "status"
7126
7636
  }, `自动索引失败:${callIndexFailureText(startFailure)}`), startTask === void 0 ? null : (0, react.createElement)("p", {
7127
- className: "mc-desc",
7637
+ className: "v3-hint",
7128
7638
  "data-call-index-note": "start-task"
7129
- }, `自动索引:${startTask.phase}${startTask.mode === "full" ? "(全量)" : "(增量)"} · 任务 ${startTask.taskId.slice(0, 8)}`), CallIndexDiagnostics({
7639
+ }, `自动索引:${startTask.phase}${startTask.mode === "full" ? "(全量)" : "(增量)"} · 任务 ${startTask.taskId.slice(0, 8)}`), indexBlockedReason === void 0 ? null : (0, react.createElement)("p", {
7640
+ className: "v3-hint",
7641
+ "data-call-index-note": "index-blocked",
7642
+ role: "status"
7643
+ }, `自动索引不可用:${indexBlockedReason}`), CallIndexDiagnostics({
7130
7644
  ...status === void 0 ? {} : { status },
7131
7645
  ...page === void 0 ? {} : { page },
7132
7646
  ...failure === void 0 ? {} : { failure },
7133
7647
  ...startFailure === void 0 ? {} : { startFailure },
7134
7648
  ...moreFailure === void 0 ? {} : { moreFailure }
7135
7649
  }), (0, react.createElement)("div", {
7136
- className: "mc-ci-summary",
7650
+ className: "v3-stats",
7137
7651
  "data-call-index-summary": "rates"
7138
- }, Metric("成功率", rateText(successStats?.rate), `${rateHint(successStats)}${totalText === void 0 ? "" : ` · 本页合计 ${totalText}`}`), Metric("失败率", rateText(failureStats?.rate), rateHint(failureStats)), Metric("建议", suggestionText(), suggestionHint())), page !== void 0 && page.droppedItems > 0 ? (0, react.createElement)("p", {
7139
- className: "mc-desc",
7140
- "data-call-index-note": "dropped"
7141
- }, `本页有 ${page.droppedItems} 行未通过白名单投影(字段不合法),已丢弃,未计入合计。`) : null, (0, react.createElement)("div", null, (0, react.createElement)("div", { style: { minWidth: 0 } }, page !== void 0 && page.items.length > 0 ? (0, react.createElement)(CallIndexCallTable, { page }) : null, page?.nextCursor !== void 0 && loadingMore === true ? (0, react.createElement)("p", {
7142
- className: "mc-desc",
7143
- "data-call-index-note": "loading-more"
7652
+ }, V3Stat({
7653
+ label: "成功率",
7654
+ value: rateText(successStats?.rate),
7655
+ hint: totalText === void 0 ? rateHint(successStats) : `${rateHint(successStats)} · 本页合计 ${totalText}`
7656
+ }), V3Stat({
7657
+ label: "失败率",
7658
+ value: rateText(failureStats?.rate),
7659
+ hint: rateHint(failureStats)
7660
+ }), V3Stat({
7661
+ label: "需要关注",
7662
+ value: suggestionCount === void 0 ? "—" : String(suggestionCount),
7663
+ hint: suggestionHint()
7664
+ }), V3Stat({
7665
+ label: "平均耗时",
7666
+ value: formatCallIndexDuration(averageDuration),
7667
+ hint: "按有耗时的调用平均"
7668
+ })), page !== void 0 && page.droppedItems > 0 ? (0, react.createElement)("p", {
7669
+ className: "v3-hint",
7670
+ "data-call-index-note": "dropped",
7671
+ style: { marginTop: 8 }
7672
+ }, `本页有 ${page.droppedItems} 行未通过白名单投影(字段不合法),已丢弃,未计入合计。`) : null, (0, react.createElement)("div", {
7673
+ className: "v3-board",
7674
+ style: { marginTop: 12 }
7675
+ }, (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) => {
7676
+ const bad = row.error > 0 && row.error / row.calls >= .2;
7677
+ return (0, react.createElement)("div", {
7678
+ key: row.toolName + "::" + row.sourceKind,
7679
+ className: "v3-rank-row",
7680
+ title: row.toolName
7681
+ }, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
7682
+ ...bad ? { "data-bad": "true" } : {},
7683
+ style: { width: `${Math.max(3, Math.round(row.calls / rankMax * 100))}%` }
7684
+ })), (0, react.createElement)("span", { className: "v3-n" }, String(row.calls)));
7685
+ })), (0, react.createElement)("p", {
7686
+ className: "v3-hint",
7687
+ style: { marginTop: 9 }
7688
+ }, `条长 = 调用量,按最大值归一;失败率 ≥ 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) => {
7689
+ const perTool = Math.round(row.error / row.calls * 100);
7690
+ return (0, react.createElement)("div", {
7691
+ key: "fail-" + row.toolName + "::" + row.sourceKind,
7692
+ className: "v3-rank-row",
7693
+ title: row.toolName
7694
+ }, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
7695
+ "data-bad": "true",
7696
+ style: { width: `${Math.max(4, Math.round(row.error / failMax * 100))}%` }
7697
+ })), (0, react.createElement)("span", {
7698
+ className: "v3-n",
7699
+ style: {
7700
+ color: "var(--v3-bad)",
7701
+ fontWeight: 600
7702
+ }
7703
+ }, `${row.error} 次`), (0, react.createElement)("span", {
7704
+ className: "v3-e",
7705
+ "data-bad": "true"
7706
+ }, `占该工具 ${perTool}%`));
7707
+ })), (0, react.createElement)("p", {
7708
+ className: "v3-hint",
7709
+ style: { marginTop: 9 }
7710
+ }, `条长 = 失败次数(不是失败率)。共 ${failingRows.length} 个工具出现过失败。`))), (0, react.createElement)("div", {
7711
+ className: "v3-panel",
7712
+ style: { marginTop: 12 }
7713
+ }, (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 === void 0 ? null : (0, react.createElement)("span", { style: { marginLeft: "auto" } }, `快照代 ${page.generation} · 覆盖 ${coverageLabel ?? "未知"}`))), page?.nextCursor !== void 0 && loadingMore === true ? (0, react.createElement)("p", {
7714
+ className: "v3-hint",
7715
+ "data-call-index-note": "loading-more",
7716
+ style: { marginTop: 8 }
7144
7717
  }, "正在加载其余工具…") : null, moreFailure === void 0 ? null : (0, react.createElement)("p", {
7145
- className: "mc-desc",
7718
+ className: "v3-hint",
7146
7719
  "data-call-index-note": "more-failure",
7147
- role: "status"
7148
- }, `加载失败:${callIndexFailureText(moreFailure)}`))));
7720
+ role: "status",
7721
+ style: { marginTop: 8 }
7722
+ }, `加载失败:${callIndexFailureText(moreFailure)}`));
7149
7723
  }
7150
- /** Pure view for 总览. */
7724
+ /** Pure view for 总览 (v3 看板). */
7151
7725
  function CallIndexOverviewPanelView(props) {
7152
- const { status, overview, loading, failure, onRefresh } = props;
7726
+ const { status, overview, loading, failure, onRefresh, tools, toolsLoading, toolsFailure } = props;
7153
7727
  const metrics = callIndexOverviewMetrics(overview, status);
7154
7728
  const rangeLabel = callIndexRangeLabel("all");
7155
7729
  const exact = metrics.callsTotalExact;
7156
7730
  const mix = callIndexOutcomeMix(overview?.summary);
7157
7731
  const aggregates = metrics.aggregates;
7158
7732
  const notice = failure === void 0 ? void 0 : failure.disconnected ? `与 Host 断连:${callIndexFailureText(failure)}${failure.hostCode === void 0 ? "" : `(${failure.hostCode})`}` : callIndexFailureText(failure);
7733
+ const coverageLabel = status === void 0 ? "未知" : COVERAGE_LABELS[status.coverage];
7734
+ const scanning = status !== void 0 && (status.phase === "running" || (status.sessions?.pending ?? 0) > 0 || (status.sessions?.pendingLive ?? 0) > 0);
7735
+ const sessionsText = metrics.sessionsListed === void 0 ? "Session 未读取" : `${metrics.sessionsListed} 列出 / ${metrics.sessionsVisible ?? "—"} 可见`;
7736
+ const coverageHint = exact === true ? (0, react.createElement)("span", { style: {
7737
+ display: "inline-flex",
7738
+ alignItems: "center",
7739
+ gap: 5
7740
+ } }, (0, react.createElement)("span", {
7741
+ className: "v3-dot",
7742
+ "data-tone": "ok",
7743
+ "aria-hidden": "true"
7744
+ }), "覆盖完整,计数精确") : (0, react.createElement)("span", { style: {
7745
+ display: "inline-flex",
7746
+ alignItems: "center",
7747
+ gap: 5
7748
+ } }, (0, react.createElement)("span", {
7749
+ className: "v3-dot",
7750
+ "data-tone": "warn",
7751
+ "aria-hidden": "true"
7752
+ }), "覆盖部分,实际更多");
7753
+ const failHint = (0, react.createElement)("span", { style: {
7754
+ display: "inline-flex",
7755
+ alignItems: "center",
7756
+ gap: 5
7757
+ } }, (0, react.createElement)("span", {
7758
+ className: "v3-dot",
7759
+ "data-tone": "bad",
7760
+ "aria-hidden": "true"
7761
+ }), `另有 ${formatCallIndexCount(mix?.unresolved, exact)} 次未配对`);
7762
+ const rates = callIndexRates(overview?.summary);
7763
+ const rateHint = (stats) => stats === void 0 ? "没有已知结果的调用" : `${stats.numerator} / ${stats.denominator} 次已知结果`;
7764
+ const trend = callIndexTrend(aggregates?.byDay);
7765
+ const callSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.calls);
7766
+ const successSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.calls > 0 ? bucket.success / bucket.calls : 0);
7767
+ const errorSpark = aggregates?.byDay === void 0 ? void 0 : aggregates.byDay.map((bucket) => bucket.error);
7768
+ const totalToolCalls = tools === void 0 ? 0 : tools.reduce((sum, row) => sum + row.calls, 0);
7769
+ const topRows = tools === void 0 ? void 0 : tools.slice(0, 12);
7770
+ const rankMax = topRows !== void 0 && topRows.length > 0 ? topRows[0].calls : 1;
7771
+ const topCoverage = topRows === void 0 || totalToolCalls === 0 ? void 0 : Math.round(topRows.reduce((sum, row) => sum + row.calls, 0) / totalToolCalls * 100);
7772
+ const failingRows = tools === void 0 ? void 0 : tools.filter((row) => row.error > 0).slice().sort((a, b) => b.error - a.error);
7773
+ const failingTop = failingRows === void 0 ? [] : failingRows.slice(0, 3);
7774
+ const failingRest = failingRows === void 0 ? [] : failingRows.slice(3);
7775
+ const failingRestErrors = failingRest.reduce((sum, row) => sum + row.error, 0);
7776
+ const failingReadErrors = failingRows === void 0 ? 0 : failingRows.reduce((sum, row) => sum + row.error, 0);
7777
+ const failDenominator = metrics.failed !== void 0 && metrics.failed > 0 ? metrics.failed : failingReadErrors;
7778
+ const readCalls = tools === void 0 ? 0 : totalToolCalls;
7779
+ const readIsPartial = metrics.calls !== void 0 && readCalls < metrics.calls;
7780
+ const worstRateTool = failingRows === void 0 || failingRows.length === 0 ? void 0 : failingRows.slice().sort((a, b) => b.error / b.calls - a.error / a.calls)[0];
7781
+ const toolsNotice = tools === void 0 ? (0, react.createElement)("p", {
7782
+ className: "v3-hint",
7783
+ "data-call-index-note": "tools"
7784
+ }, toolsFailure !== void 0 ? `逐工具明细读取失败:${callIndexFailureText(toolsFailure)}` : toolsLoading === true ? "正在读取逐工具明细…" : "尚未读取逐工具明细;上方调用总量与趋势来自总览快照,排行需要逐工具数据。") : tools.length === 0 ? (0, react.createElement)("p", {
7785
+ className: "v3-hint",
7786
+ "data-call-index-note": "tools"
7787
+ }, "该范围没有逐工具调用记录。") : null;
7788
+ const sessionInfo = status?.sessions;
7789
+ const taskNote = metrics.tasks === void 0 ? null : (0, react.createElement)("p", {
7790
+ className: "v3-hint",
7791
+ "data-call-index-note": "task",
7792
+ style: { marginTop: 9 }
7793
+ }, `最近一次扫描:${metrics.tasks.mode} / ${metrics.tasks.phase}(开始 ${formatCallIndexTime(metrics.tasks.startedAt)})`);
7159
7794
  return (0, react.createElement)("section", {
7160
- className: "mc-section",
7795
+ className: "v3-root mc-section",
7161
7796
  "aria-label": "调用统计总览",
7162
7797
  "aria-busy": loading === true
7163
- }, CallIndexStyles(), (0, react.createElement)("header", { className: "mc-heading-row" }, (0, react.createElement)("div", { className: "mc-heading-meta" }, (0, react.createElement)("h2", { className: "mc-heading" }, "总览"), (0, react.createElement)("span", { className: "mc-version" }, loading === true ? "正在读取…" : rangeLabel === "全部" ? "范围:全部已索引会话" : `范围:最近 ${rangeLabel}`)), (0, react.createElement)("div", { className: "mc-heading-actions" }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
7798
+ }, CallIndexStyles(), V3Styles(), (0, react.createElement)("header", { className: "mc-heading-row" }, (0, react.createElement)("div", { className: "mc-heading-meta" }, (0, react.createElement)("h2", { className: "mc-heading" }, "总览"), (0, react.createElement)("span", { className: "mc-version" }, loading === true ? "正在读取…" : rangeLabel === "全部" ? "范围:全部已索引会话" : `范围:最近 ${rangeLabel}`)), (0, react.createElement)("div", { className: "mc-heading-actions" }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
7164
7799
  size: "sm",
7165
7800
  variant: "outline",
7166
7801
  disabled: loading === true || onRefresh === void 0,
@@ -7169,19 +7804,152 @@ function CallIndexOverviewPanelView(props) {
7169
7804
  className: "mc-notice",
7170
7805
  role: failure?.disconnected === true ? "alert" : "status",
7171
7806
  "data-call-index-state": failure?.disconnected === true ? "disconnected" : "failure"
7172
- }, notice), (0, react.createElement)("div", {
7173
- className: "mc-ci-metrics",
7807
+ }, notice), (0, react.createElement)("div", { className: "v3-status-pills" }, (0, react.createElement)("span", { className: "v3-statusline" }, (0, react.createElement)("span", {
7808
+ className: "v3-dot",
7809
+ "data-tone": coverageLabel === "完整" ? "ok" : "warn",
7810
+ "aria-hidden": "true"
7811
+ }), (0, react.createElement)("span", null, "覆盖 ", (0, react.createElement)("b", null, coverageLabel), ` · ${sessionsText}${exact === true ? "" : " · 所有总量为下界"}`)), (0, react.createElement)("span", { className: "v3-statusline" }, (0, react.createElement)("span", {
7812
+ className: "v3-dot",
7813
+ "data-tone": scanning ? "run" : "ok",
7814
+ "aria-hidden": "true"
7815
+ }), scanning ? "正在扫描" : "扫描已结束", ` · ${formatCallIndexCount(sessionInfo?.pending, true)} 个待处理`), (0, react.createElement)("span", { className: "v3-statusline v3-status-dash" }, `口径:${overview?.corpus === "persisted-plus-observed-live" ? "已持久化 + 挂载后实时观测" : "Host 未声明 corpus"}`)), (0, react.createElement)("div", {
7816
+ className: "v3-stats",
7174
7817
  "data-call-index-metrics": "overview"
7175
- }, Metric("总调用", formatCallIndexCount(metrics.calls, exact), mix === void 0 ? void 0 : `成功 ${formatCallIndexCount(mix.succeeded, exact)}`), Metric("失败 / 未完成", metrics.failed === void 0 ? "—" : `${formatCallIndexCount(metrics.failed, exact)} / ${formatCallIndexCount(metrics.unfinished, exact)}`, "未完成 = 未配对结果 + 进行中"), Metric("已索引 / 可见 Session", metrics.sessionsListed === void 0 ? "—" : `${formatCallIndexCount(metrics.sessionsVisible)} / ${formatCallIndexCount(metrics.sessionsListed)}`, "可见 = 当前 Scope 仍可读,已索引 = 扫描覆盖到的"), Metric("不同工具数", formatCallIndexCount(metrics.distinctTools, exact), aggregates === void 0 ? "Host 未返回聚合" : "按 toolName 去重")), callIndexHasUnexplainedSources(aggregates) ? (0, react.createElement)("p", {
7176
- className: "mc-desc",
7177
- "data-call-index-note": "unexplained-sources"
7178
- }, "插件工具与技能执行入口的归属目前都解析为 0:Host 无法证明插件 owner,技能执行归属也未验证,所以这两个 0 表示「未解析出」,不表示「未使用」。") : null, (0, react.createElement)(CallIndexTrendChart, { aggregates }), (0, react.createElement)(CallIndexSourceBars, {
7179
- bySource: aggregates?.bySource,
7818
+ }, V3Stat({
7819
+ label: "调用次数",
7820
+ value: formatCallIndexCount(metrics.calls, exact),
7821
+ hint: coverageHint,
7822
+ ...callSpark === void 0 ? {} : { spark: Sparkline({
7823
+ values: callSpark,
7824
+ tone: "info"
7825
+ }) }
7826
+ }), V3Stat({
7827
+ label: "成功率",
7828
+ value: formatCallIndexRate(rates.success?.rate),
7829
+ hint: rateHint(rates.success),
7830
+ ...successSpark === void 0 ? {} : { spark: Sparkline({
7831
+ values: successSpark,
7832
+ tone: "ok"
7833
+ }) }
7834
+ }), V3Stat({
7835
+ label: "失败",
7836
+ value: formatCallIndexCount(metrics.failed, exact),
7837
+ hint: failHint,
7838
+ ...errorSpark === void 0 ? {} : { spark: Sparkline({
7839
+ values: errorSpark,
7840
+ tone: "bad"
7841
+ }) }
7842
+ }), V3Stat({
7843
+ label: "不同工具",
7844
+ value: formatCallIndexCount(metrics.distinctTools, exact),
7845
+ hint: "按 toolName 去重"
7846
+ })), callIndexHasUnexplainedSources(aggregates) ? (0, react.createElement)("p", {
7847
+ className: "v3-hint",
7848
+ "data-call-index-note": "unexplained-sources",
7849
+ style: { marginTop: 10 }
7850
+ }, "插件工具与技能执行入口的归属目前都解析为 0:Host 无法证明插件 owner,技能执行归属也未验证,所以这两个 0 表示「未解析出」,不表示「未使用」。") : null, (0, react.createElement)("div", {
7851
+ className: "v3-board",
7852
+ style: { marginTop: 12 }
7853
+ }, (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", {
7854
+ className: "v3-hint",
7855
+ "data-call-index-missing": "aggregates"
7856
+ }, "Host 未返回按日聚合(aggregates);这不是「没有调用」,趋势不可用。") : !trend.available ? (0, react.createElement)("p", {
7857
+ className: "v3-hint",
7858
+ "data-call-index-missing": "by-day"
7859
+ }, "该筛选在所选范围内没有任何按日桶,趋势为空;单个日粒度为 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", {
7860
+ "data-mark": "square",
7861
+ style: { background: "var(--v3-bad)" }
7862
+ }), "每日失败数(底柱)"), (0, react.createElement)("span", null, (0, react.createElement)("i", { style: {
7863
+ background: "var(--v3-info)",
7864
+ opacity: .3,
7865
+ height: 8,
7866
+ borderRadius: 2
7867
+ } }), "最近一周"))), OverviewSourcePanel({
7868
+ ...aggregates?.bySource === void 0 ? {} : { bySource: aggregates.bySource },
7180
7869
  ...exact === void 0 ? {} : { totalExact: exact }
7181
- }), metrics.tasks === void 0 ? null : (0, react.createElement)("p", {
7182
- className: "mc-desc",
7183
- "data-call-index-note": "task"
7184
- }, `最近一次扫描:${metrics.tasks.mode} / ${metrics.tasks.phase}(开始 ${formatCallIndexTime(metrics.tasks.startedAt)})`));
7870
+ })), (0, react.createElement)("div", {
7871
+ className: "v3-board",
7872
+ style: { marginTop: 12 }
7873
+ }, (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) => {
7874
+ const bad = row.error > 0 && row.error / row.calls >= .2;
7875
+ return (0, react.createElement)("div", {
7876
+ key: "rank-" + row.toolName + "::" + row.sourceKind,
7877
+ className: "v3-rank-row",
7878
+ title: row.toolName
7879
+ }, (0, react.createElement)("span", { className: "v3-nm" }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", { className: "v3-bar" }, (0, react.createElement)("span", {
7880
+ ...bad ? { "data-bad": "true" } : {},
7881
+ style: { width: `${Math.max(3, Math.round(row.calls / rankMax * 100))}%` }
7882
+ })), (0, react.createElement)("span", { className: "v3-n" }, String(row.calls)), (0, react.createElement)("span", {
7883
+ className: "v3-e",
7884
+ ...bad ? { "data-bad": "true" } : {},
7885
+ ...row.error === 0 ? { "data-zero": "true" } : {}
7886
+ }, `失败 ${row.error}`));
7887
+ }))), (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", null, (0, react.createElement)("div", { className: "v3-rank" }, ...failingTop.map((row) => (0, react.createElement)("div", {
7888
+ key: "fc-" + row.toolName + "::" + row.sourceKind,
7889
+ className: "v3-rank-row"
7890
+ }, (0, react.createElement)("span", {
7891
+ className: "v3-nm",
7892
+ style: {
7893
+ width: "auto",
7894
+ flex: "1 1 auto"
7895
+ }
7896
+ }, toolDisplayLabel(row.toolName)), (0, react.createElement)("span", {
7897
+ className: "v3-n",
7898
+ style: {
7899
+ color: "var(--v3-bad)",
7900
+ fontWeight: 600
7901
+ }
7902
+ }, String(row.error)))), ...failingRest.length === 0 ? [] : [(0, react.createElement)("div", {
7903
+ key: "fc-rest",
7904
+ className: "v3-rank-row"
7905
+ }, (0, react.createElement)("span", {
7906
+ className: "v3-nm",
7907
+ style: {
7908
+ width: "auto",
7909
+ flex: "1 1 auto"
7910
+ }
7911
+ }, `其余 ${failingRest.length} 个工具合计`), (0, react.createElement)("span", {
7912
+ className: "v3-n",
7913
+ style: { color: "var(--v3-t2)" }
7914
+ }, String(failingRestErrors)))]), failDenominator > 0 ? (0, react.createElement)("p", {
7915
+ className: "v3-hint",
7916
+ style: { marginTop: 9 }
7917
+ }, `这些失败合计占全部失败的 ${Math.round(failingTop.reduce((sum, row) => sum + row.error, 0) / failDenominator * 100)}%(占全部失败,不是占单个工具)${readIsPartial ? ";分母是全部失败数,分子只含已读取的逐工具行" : ""}。`) : null, worstRateTool === void 0 ? null : (0, react.createElement)("div", { style: {
7918
+ marginTop: 11,
7919
+ paddingTop: 11,
7920
+ borderTop: "1px solid var(--v3-line)"
7921
+ } }, (0, react.createElement)("div", {
7922
+ className: "v3-src",
7923
+ style: { fontSize: 11.5 }
7924
+ }, (0, react.createElement)("span", {
7925
+ className: "v3-nm",
7926
+ style: {
7927
+ color: "var(--v3-t4)",
7928
+ flex: "1 1 auto"
7929
+ }
7930
+ }, "失败率最高的工具"), (0, react.createElement)("b", { style: { color: "var(--v3-bad)" } }, `${Math.round(worstRateTool.error / worstRateTool.calls * 100)}%`)), (0, react.createElement)("p", {
7931
+ className: "v3-hint",
7932
+ style: { marginTop: 4 }
7933
+ }, `${worstRateTool.toolName} ${worstRateTool.error} / ${worstRateTool.calls}(占该工具失败率,不是占全部失败)。`)), (0, react.createElement)("div", { style: {
7934
+ marginTop: 11,
7935
+ paddingTop: 11,
7936
+ borderTop: "1px solid var(--v3-line)"
7937
+ } }, (0, react.createElement)("div", {
7938
+ className: "v3-src",
7939
+ style: { fontSize: 11.5 }
7940
+ }, (0, react.createElement)("span", {
7941
+ className: "v3-nm",
7942
+ style: {
7943
+ color: "var(--v3-t4)",
7944
+ flex: "1 1 auto"
7945
+ }
7946
+ }, "未配对结果"), (0, react.createElement)("b", { style: { color: "var(--v3-warn)" } }, formatCallIndexCount(mix?.unresolved, exact))), (0, react.createElement)("p", {
7947
+ className: "v3-hint",
7948
+ style: { marginTop: 4 }
7949
+ }, "没有捕获到结果,不计入成功率分母,也不等于失败。"))))), (0, react.createElement)("div", {
7950
+ className: "v3-panel",
7951
+ style: { marginTop: 12 }
7952
+ }, (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
7953
  }
7186
7954
  function useNarrowViewport() {
7187
7955
  const [narrow, setNarrow] = (0, react.useState)(false);
@@ -7252,6 +8020,7 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
7252
8020
  loadingDetail: false
7253
8021
  });
7254
8022
  const [refreshToken, setRefreshToken] = (0, react.useState)(0);
8023
+ const [search, setSearch] = (0, react.useState)("");
7255
8024
  const generation = (0, react.useRef)(0);
7256
8025
  const pageAnchor = (0, react.useRef)(void 0);
7257
8026
  /** How many extra pages 加载更多 has appended; 0 means only the first page. */
@@ -7488,6 +8257,8 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
7488
8257
  loadingMore: state.loadingMore,
7489
8258
  loadingDetail: state.loadingDetail,
7490
8259
  narrow,
8260
+ search,
8261
+ onSearchChange: setSearch,
7491
8262
  onQueryChange: setQuery,
7492
8263
  onRefresh: () => setRefreshToken((value) => value + 1),
7493
8264
  onStartIndex: startIndex,
@@ -7498,8 +8269,12 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
7498
8269
  });
7499
8270
  }
7500
8271
  /**
7501
- * Container for 总览. Reads the status once and one 30-day summary; the window
7502
- * is stated in the header so no metric is silently scoped.
8272
+ * Container for 总览. Reads the status once, one all-time summary, AND one
8273
+ * `listCalls` snapshot — because the overview reply carries no per-tool counts,
8274
+ * and the v3 ranking / fault panels need them. The per-tool read fills up to
8275
+ * `MAX_AUTO_PAGES` pages (same bound the detail page uses) then aggregates with
8276
+ * the shared pure `callIndexToolRows`. Failing to read rows never blocks the
8277
+ * headline numbers; it only shows an honest empty state in the two panels.
7503
8278
  */
7504
8279
  function CallIndexOverviewPanel({ api }) {
7505
8280
  const [state, setState] = (0, react.useState)({ loading: true });
@@ -7511,15 +8286,44 @@ function CallIndexOverviewPanel({ api }) {
7511
8286
  loading: true
7512
8287
  }));
7513
8288
  const query = { range: "all" };
8289
+ const anchor = Date.now();
7514
8290
  (async () => {
7515
- const [statusResult, overviewResult] = await Promise.all([api.getStatus(), api.getOverview(query)]);
8291
+ const [statusResult, overviewResult, callsResult] = await Promise.all([
8292
+ api.getStatus(),
8293
+ api.getOverview(query),
8294
+ api.listCalls(query, void 0, anchor)
8295
+ ]);
7516
8296
  if (!active) return;
7517
8297
  const failure = overviewResult.ok ? statusResult.ok ? void 0 : statusResult : overviewResult;
8298
+ let tools;
8299
+ let toolsFailure;
8300
+ if (callsResult.ok) {
8301
+ const items = [...callsResult.value.items];
8302
+ let cursor = callsResult.value.nextCursor;
8303
+ let pages = 0;
8304
+ while (cursor !== void 0 && pages < 20) {
8305
+ const next = await api.listCalls(query, cursor, anchor);
8306
+ if (!next.ok) {
8307
+ toolsFailure = next;
8308
+ break;
8309
+ }
8310
+ items.push(...next.value.items);
8311
+ cursor = next.value.nextCursor;
8312
+ pages += 1;
8313
+ }
8314
+ tools = callIndexToolRows({
8315
+ ...callsResult.value,
8316
+ items
8317
+ });
8318
+ } else toolsFailure = callsResult;
8319
+ if (!active) return;
7518
8320
  setState({
7519
8321
  loading: false,
7520
8322
  ...statusResult.ok ? { status: statusResult.value } : {},
7521
8323
  ...overviewResult.ok ? { overview: overviewResult.value } : {},
7522
- ...failure === void 0 ? {} : { failure }
8324
+ ...failure === void 0 ? {} : { failure },
8325
+ ...tools === void 0 ? {} : { tools },
8326
+ ...toolsFailure === void 0 ? {} : { toolsFailure }
7523
8327
  });
7524
8328
  })();
7525
8329
  return () => {
@@ -7529,8 +8333,11 @@ function CallIndexOverviewPanel({ api }) {
7529
8333
  return (0, react.createElement)(CallIndexOverviewPanelView, {
7530
8334
  ...state.status === void 0 ? {} : { status: state.status },
7531
8335
  ...state.overview === void 0 ? {} : { overview: state.overview },
8336
+ ...state.tools === void 0 ? {} : { tools: state.tools },
8337
+ ...state.toolsFailure === void 0 ? {} : { toolsFailure: state.toolsFailure },
7532
8338
  ...state.failure === void 0 ? {} : { failure: state.failure },
7533
8339
  loading: state.loading,
8340
+ toolsLoading: state.loading,
7534
8341
  onRefresh: () => setRefreshToken((value) => value + 1)
7535
8342
  });
7536
8343
  }
@@ -7659,18 +8466,29 @@ function OverviewContainer(props) {
7659
8466
  //#endregion
7660
8467
  //#region src/client/GuardPanel.tsx
7661
8468
  /**
7662
- * 思考限制 — the Guard configuration surface.
8469
+ * 思考限制 — the Guard configuration surface (v3 · 数据看板, D-044).
7663
8470
  *
7664
8471
  * Three sections, in the order the product specified: repeated identical calls,
7665
8472
  * alternating cycles, no-progress protection. Each carries a model-side
7666
- * identifier (`duplicate_call` / `alternating_cycle` / `no_progress`) shown as
8473
+ * identifier ('duplicate_call' / 'alternating_cycle' / 'no_progress') shown as
7667
8474
  * plain text, because the reminder text injected into the conversation is
7668
8475
  * English while the label a user reads here is Chinese. The identifier is what
7669
8476
  * ties the two together, so it is displayed rather than hidden.
7670
8477
  *
7671
- * NO summary panel: the current effective state is not repeated here. The
7672
- * 总览 page owns "what is on right now"; duplicating it produced two places to
7673
- * read the same thing and a second place to keep in sync.
8478
+ * v3 SHAPE: the switches are no longer a single-column stack of .mc-guard-card.
8479
+ * They become a two-column grid of .v3-gitem cards (>900px two columns, auto
8480
+ * single column via the container query already in v3-ui.ts). Each card has a
8481
+ * StateDot + title + model id + switch head, a compact body, and a thin
8482
+ * supplementary foot. The fourth grid cell is the 「生效范围」 explanation, not
8483
+ * a switch, so the grid fills evenly.
8484
+ *
8485
+ * DOM CONTRACT kept for the shared test suite: nine <li class="mc-guard-point">
8486
+ * whose text still comes from t(); the three data-guard-section values in order;
8487
+ * exactly one threshold <input> (value '3, 5, 8', readOnly tracks onChange); the
8488
+ * four data-guard-stat cells and data-guard-stats / data-guard-stats-since; and
8489
+ * three role="switch" buttons whose aria-checked follows the props. No Chinese
8490
+ * colon (':') is introduced by this file — spacing and full-width spaces do
8491
+ * the separating instead.
7674
8492
  *
7675
8493
  * Kept hook-free and prop-driven so the settings test suite (no DOM harness) can
7676
8494
  * walk the returned tree.
@@ -7681,6 +8499,10 @@ function OverviewContainer(props) {
7681
8499
  * The descriptions are part of the contract, not decoration: a bare switch does
7682
8500
  * not tell a reader whether turning it off stops reminders only or also stops
7683
8501
  * local denials. Each bullet states the real behaviour the detector implements.
8502
+ *
8503
+ * v3 keeps all three bullets (nine <li class="mc-guard-point"> in total, as the
8504
+ * shared suite asserts) but lays them out as one compact wrapped row per card,
8505
+ * so the paragraph reads like a sentence rather than a three-line list.
7684
8506
  */
7685
8507
  const SECTIONS = [
7686
8508
  {
@@ -7711,43 +8533,66 @@ const SECTIONS = [
7711
8533
  ]
7712
8534
  }
7713
8535
  ];
7714
- /** `—` for a counter the Host did not report; never a fabricated 0. */
8536
+ /**
8537
+ * Page-specific rules for the v3 Guard grid. Everything else comes from
8538
+ * v3-ui.ts; the only local vocabulary here is the compact bullet row and the
8539
+ * threshold editor chrome. Colours are always v3 semantic variables — nothing
8540
+ * is hard-coded, so light and dark themes both track the host.
8541
+ */
8542
+ const GUARD_CSS = [
8543
+ ".mc-section.v3-root{display:flex;flex-direction:column;gap:12px}",
8544
+ ".v3-gpoints{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;align-items:baseline;gap:0 5px}",
8545
+ ".v3-gpoints>.mc-guard-point{font-size:11.5px;line-height:18px;color:var(--v3-t3)}",
8546
+ ".v3-gpoints>.mc-guard-point+.mc-guard-point::before{content:'·';margin-right:5px;color:var(--v3-t4)}",
8547
+ ".v3-gitem--info{background:var(--v3-panel-2)}",
8548
+ ".v3-gitem .v3-gh .v3-sw{margin-left:auto}",
8549
+ ".v3-gfoot-edit{margin-top:9px}",
8550
+ ".v3-guard-stats{margin:12px 0}",
8551
+ ".mc-guard-threshold-edit{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
8552
+ ".mc-guard-threshold-edit-label{font-size:11.5px;color:var(--v3-t3)}",
8553
+ ".mc-guard-threshold-edit-input{width:150px;padding:3px 7px;border-radius:var(--v3-r-sm);font-size:12px;font-family:var(--v3-font-code);color:var(--v3-t1);background:var(--v3-panel-2);border:1px solid var(--v3-line)}",
8554
+ ".mc-guard-threshold-edit-input:focus-visible{outline:2px solid var(--v3-info);outline-offset:1px}",
8555
+ ".mc-guard-threshold-edit-input:disabled{opacity:.6}",
8556
+ ".mc-guard-threshold-edit-hint{font-size:10.5px;color:var(--v3-t4)}"
8557
+ ].join("");
8558
+ /** '—' for a counter the Host did not report; never a fabricated 0. */
7715
8559
  function statText(value) {
7716
8560
  return value === void 0 || !Number.isFinite(value) ? "—" : String(value);
7717
8561
  }
7718
8562
  /**
7719
- * The Guard's real activity board.
8563
+ * The Guard's real activity board, restyled as a v3 stat strip.
7720
8564
  *
7721
8565
  * Answers the two questions the page previously could not: 「它是否在工作」
7722
8566
  * (observations / since) and 「调用成功还是失败」 (outcome split). Every number
7723
- * comes from the Host; a missing counter renders `—`, so an older Host reads as
7724
- * "unknown" rather than "zero".
8567
+ * comes from the Host; a missing counter renders '—', so an older Host reads as
8568
+ * "unknown" rather than "zero". The four data-guard-stat cells and the
8569
+ * data-guard-stats / data-guard-stats-since hooks are the published contract.
7725
8570
  */
7726
8571
  function GuardStatsBoard(props) {
7727
8572
  const { t, stats } = props;
7728
8573
  const outcomes = stats?.outcomes;
7729
8574
  const reminders = stats?.reminders;
7730
8575
  const cell = (label, value, hint) => (0, react.createElement)("div", {
7731
- className: "mc-guard-stat",
8576
+ className: "v3-stat",
7732
8577
  "data-guard-stat": label
7733
- }, (0, react.createElement)("div", { className: "mc-guard-stat-label" }, label), (0, react.createElement)("div", { className: "mc-guard-stat-value" }, value), hint === void 0 ? null : (0, react.createElement)("div", { className: "mc-guard-stat-hint" }, hint));
7734
- return (0, react.createElement)("div", {
7735
- className: "mc-guard-stats",
8578
+ }, (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));
8579
+ return (0, react.createElement)("div", { className: "v3-guard-stats" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("h3", { className: "v3-pt" }, t("guardStatsTitle"))), (0, react.createElement)("div", {
8580
+ className: "v3-stats",
7736
8581
  "data-guard-stats": "true",
7737
8582
  role: "group",
7738
8583
  "aria-label": t("guardStatsTitle")
7739
- }, 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")), stats?.since === void 0 ? null : (0, react.createElement)("p", {
7740
- className: "mc-guard-stats-since",
8584
+ }, 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"))), stats?.since === void 0 ? null : (0, react.createElement)("p", {
8585
+ className: "v3-src-note",
7741
8586
  "data-guard-stats-since": "true"
7742
- }, `${t("guardStatsSince")} ${formatGuardTime(stats.since)}`));
8587
+ }, t("guardStatsSince") + " " + formatGuardTime(stats.since)));
7743
8588
  }
7744
- /** `YYYY-MM-DD HH:mm` in the browser zone; `—` when unknown. */
8589
+ /** 'YYYY-MM-DD HH:mm' in the browser zone; '—' when unknown. */
7745
8590
  function formatGuardTime(time) {
7746
8591
  if (time === void 0 || !Number.isFinite(time)) return "—";
7747
8592
  const date = new Date(time);
7748
8593
  if (Number.isNaN(date.getTime())) return "—";
7749
8594
  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())}`;
8595
+ return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
7751
8596
  }
7752
8597
  /**
7753
8598
  * Editable reminder thresholds.
@@ -7757,6 +8602,9 @@ function formatGuardTime(time) {
7757
8602
  * field: the list is short, order matters, and a number-per-input row would be
7758
8603
  * more chrome than content. Invalid input is refused by the Host and reported
7759
8604
  * back, so this input never claims a value the detector rejected.
8605
+ *
8606
+ * Kept as its own component so the shared suite's expanding walk can reach the
8607
+ * single <input> (data-guard-thresholds marks the wrapper).
7760
8608
  */
7761
8609
  function GuardThresholdEditor(props) {
7762
8610
  const { t, thresholds, onChange } = props;
@@ -7786,9 +8634,9 @@ function GuardThresholdEditor(props) {
7786
8634
  }), (0, react.createElement)("span", { className: "mc-guard-threshold-edit-hint" }, editable ? t("guardThresholdsEditableHint") : t("guardThresholdsFixedHint")));
7787
8635
  }
7788
8636
  /**
7789
- * Parse `3, 5, 8` into a threshold list.
8637
+ * Parse '3, 5, 8' into a threshold list.
7790
8638
  *
7791
- * Returns `undefined` for anything the Host would reject (empty, non-integer,
8639
+ * Returns 'undefined' for anything the Host would reject (empty, non-integer,
7792
8640
  * out of range, not strictly increasing). Parsing here as well as on the Host is
7793
8641
  * deliberate: it keeps an invalid keystroke from round-tripping through the
7794
8642
  * remote and flickering the field.
@@ -7806,40 +8654,61 @@ function parseGuardThresholds(text) {
7806
8654
  }
7807
8655
  return parsed;
7808
8656
  }
8657
+ /** The supplementary foot line for a section, per the v3 design copy. */
8658
+ function sectionFoot(id, t) {
8659
+ if (id === "alternating_cycle") return (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardFootCycle")));
8660
+ if (id === "no_progress") return (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardFootNoProgress")));
8661
+ return (0, react.createElement)("div", { className: "v3-gfoot" });
8662
+ }
7809
8663
  function GuardPanel(props) {
7810
8664
  const { t, enabled, duplicateThresholds, onToggle, onThresholdsChange, stats, onReset } = props;
7811
8665
  const section = (entry) => {
7812
8666
  const isOn = enabled?.[entry.id] ?? false;
7813
8667
  const canToggle = onToggle !== void 0;
8668
+ const dot = isOn ? (0, react.createElement)("span", {
8669
+ className: "v3-dot",
8670
+ "data-tone": "ok",
8671
+ "aria-hidden": "true"
8672
+ }) : (0, react.createElement)("span", {
8673
+ className: "v3-dot",
8674
+ "aria-hidden": "true"
8675
+ });
7814
8676
  return (0, react.createElement)("div", {
7815
8677
  key: entry.id,
7816
- className: "mc-guard-card",
8678
+ className: "v3-gitem",
7817
8679
  "data-guard-section": entry.id
7818
- }, (0, react.createElement)("div", { className: "mc-guard-head" }, (0, react.createElement)("span", { className: "mc-guard-title" }, t(entry.titleKey)), (0, react.createElement)("span", { className: "mc-guard-model" }, `${t("guardModelLabel")} ${entry.id}`), (0, react.createElement)("button", {
8680
+ }, (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
8681
  type: "button",
7820
8682
  role: "switch",
7821
8683
  "aria-checked": isOn ? "true" : "false",
7822
8684
  "aria-label": t(entry.titleKey),
7823
- className: `mc-guard-switch${isOn ? " mc-guard-switch--on" : ""}`,
8685
+ className: "v3-sw",
7824
8686
  disabled: !canToggle,
7825
8687
  onClick: () => canToggle && onToggle(entry.id, !isOn)
7826
- })), (0, react.createElement)("div", { className: "mc-guard-body" }, (0, react.createElement)("ul", { className: "mc-guard-points" }, ...entry.bodyKeys.map((key) => (0, react.createElement)("li", {
8688
+ })), (0, react.createElement)("ul", { className: "v3-gpoints" }, ...entry.bodyKeys.map((key) => (0, react.createElement)("li", {
7827
8689
  key,
7828
8690
  className: "mc-guard-point"
7829
- }, t(key)))), entry.id === "duplicate_call" && duplicateThresholds !== void 0 && (0, react.createElement)("div", { className: "mc-guard-thresholds" }, (0, react.createElement)(GuardThresholdEditor, {
8691
+ }, t(key)))), entry.id === "duplicate_call" ? (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardThresholds")), ...(duplicateThresholds ?? []).map((value, index) => (0, react.createElement)("span", {
8692
+ key: index,
8693
+ className: "v3-kbd"
8694
+ }, String(value)))) : sectionFoot(entry.id, t), entry.id === "duplicate_call" && duplicateThresholds !== void 0 ? (0, react.createElement)("div", { className: "v3-gfoot-edit" }, (0, react.createElement)(GuardThresholdEditor, {
7830
8695
  t,
7831
8696
  thresholds: duplicateThresholds,
7832
8697
  ...onThresholdsChange === void 0 ? {} : { onChange: onThresholdsChange }
7833
- }))));
8698
+ })) : null);
7834
8699
  };
7835
- return (0, react.createElement)("section", { className: "mc-section" }, (0, react.createElement)("header", { className: "mc-heading-row" }, (0, react.createElement)("h2", { className: "mc-heading" }, t("guardTitle")), (0, react.createElement)("div", { className: "mc-heading-actions" }, onReset !== void 0 && (0, react.createElement)("button", {
8700
+ const scopeCell = (0, react.createElement)("div", {
8701
+ key: "scope",
8702
+ className: "v3-gitem v3-gitem--info"
8703
+ }, (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"))));
8704
+ return (0, react.createElement)("section", { className: "mc-section v3-root" }, V3Styles(), (0, react.createElement)("style", { "data-guard-style": "true" }, GUARD_CSS), (0, react.createElement)("header", { className: "mc-heading-row" }, (0, react.createElement)("h2", { className: "mc-heading" }, t("guardTitle")), (0, react.createElement)("div", { className: "mc-heading-actions" }, onReset !== void 0 && (0, react.createElement)("button", {
7836
8705
  type: "button",
7837
8706
  className: "mc-count mc-count--action",
7838
8707
  onClick: onReset
7839
- }, t("guardReset")))), (0, react.createElement)("p", { className: "mc-desc" }, t("guardDesc")), (0, react.createElement)("p", { className: "mc-guard-boundary" }, t("guardBoundary")), (0, react.createElement)(GuardStatsBoard, {
8708
+ }, t("guardReset")))), (0, react.createElement)("p", { className: "v3-notebox" }, (0, react.createElement)("b", null, t("guardBoundaryLabel")), " ", t("guardBoundary")), (0, react.createElement)(GuardStatsBoard, {
7840
8709
  t,
7841
8710
  ...stats === void 0 ? {} : { stats }
7842
- }), ...SECTIONS.map(section));
8711
+ }), (0, react.createElement)("div", { className: "v3-gpanel" }, ...SECTIONS.map(section), scopeCell));
7843
8712
  }
7844
8713
  //#endregion
7845
8714
  //#region src/client/GuardContainer.tsx
@@ -8136,6 +9005,7 @@ function PluginSection(props) {
8136
9005
  const [active, setActive] = (0, react.useState)("overview");
8137
9006
  const callIndexApi = callIndexApiFor(props.callIndexRemote);
8138
9007
  const [diagnostics, setDiagnostics] = (0, react.useState)(void 0);
9008
+ const [catalogUsage, setCatalogUsage] = (0, react.useState)(void 0);
8139
9009
  (0, react.useEffect)(() => {
8140
9010
  let live = true;
8141
9011
  (async () => {
@@ -8147,6 +9017,22 @@ function PluginSection(props) {
8147
9017
  live = false;
8148
9018
  };
8149
9019
  }, [callIndexApi]);
9020
+ (0, react.useEffect)(() => {
9021
+ let live = true;
9022
+ (async () => {
9023
+ const result = await callIndexApi.listCalls({ range: "all" });
9024
+ if (!live || !result.ok) return;
9025
+ const map = /* @__PURE__ */ new Map();
9026
+ for (const row of callIndexToolRows(result.value)) map.set(row.toolName, {
9027
+ uses: row.calls,
9028
+ failures: row.error
9029
+ });
9030
+ setCatalogUsage(map);
9031
+ })();
9032
+ return () => {
9033
+ live = false;
9034
+ };
9035
+ }, [callIndexApi]);
8150
9036
  return (0, react.createElement)(PluginSectionView, {
8151
9037
  capability: props.capability,
8152
9038
  usageApi: props.usageApi,
@@ -8157,7 +9043,8 @@ function PluginSection(props) {
8157
9043
  t: props.t,
8158
9044
  active,
8159
9045
  onSelect: setActive,
8160
- ...diagnostics === void 0 ? {} : { diagnostics }
9046
+ ...diagnostics === void 0 ? {} : { diagnostics },
9047
+ ...catalogUsage === void 0 ? {} : { catalogUsage }
8161
9048
  });
8162
9049
  }
8163
9050
  /**
@@ -8166,7 +9053,7 @@ function PluginSection(props) {
8166
9053
  * `UsagePanelView`.
8167
9054
  */
8168
9055
  function PluginSectionView(props) {
8169
- const { capability, usageApi, callIndexRemote, scopeTarget, sessionCatalog, guardRemote, t, active, onSelect, diagnostics } = props;
9056
+ const { capability, usageApi, callIndexRemote, scopeTarget, sessionCatalog, guardRemote, t, active, onSelect, diagnostics, catalogUsage } = props;
8170
9057
  const callIndexApi = callIndexApiFor(callIndexRemote);
8171
9058
  const panel = (category, children) => (0, react.createElement)("div", {
8172
9059
  role: "tabpanel",
@@ -8199,7 +9086,10 @@ function PluginSectionView(props) {
8199
9086
  5,
8200
9087
  8
8201
9088
  ]
8202
- })), panel("catalog", (0, react.createElement)(CapabilitySection, capability)), panel("usage", callIndexRemote === void 0 && usageApi !== void 0 ? (0, react.createElement)(UsagePanel, { api: usageApi }) : (0, react.createElement)(CallIndexDetailPanel, {
9089
+ })), panel("catalog", (0, react.createElement)(CapabilitySection, {
9090
+ ...capability,
9091
+ ...catalogUsage === void 0 ? {} : { usage: catalogUsage }
9092
+ })), panel("usage", callIndexRemote === void 0 && usageApi !== void 0 ? (0, react.createElement)(UsagePanel, { api: usageApi }) : (0, react.createElement)(CallIndexDetailPanel, {
8203
9093
  api: callIndexApi,
8204
9094
  ...scopeTarget === void 0 ? {} : { resolveScopeTarget: scopeTarget },
8205
9095
  ...sessionCatalog === void 0 ? {} : { resolveSessionCatalog: sessionCatalog }
@@ -8279,6 +9169,12 @@ async function apply(ctx) {
8279
9169
  guardDesc: "只针对有证据的重复与无进展拦截具体操作;不因总步数、工具总次数或任务时长终止任务。",
8280
9170
  guardBoundary: "统计与限制是两件事:Step、工具调用次数、任务时长只用于统计与诊断,不构成硬停止条件。Quick / Normal / Deep 三档里的 Step、工具次数、时长阈值(soft* / hard*)是兼容字段与统计口径,只参与配置校验与计数,不产生提醒,也不停止任务。单个工具自身的超时另由宿主或该工具负责。",
8281
9171
  guardReset: "恢复默认",
9172
+ guardScopeTitle: "生效范围",
9173
+ guardScopeBody: "三个开关按 Agent / Turn 隔离,不跨 Session 继承;关闭只停止对应的提醒与局部拒绝,不改动任何配置或历史。",
9174
+ guardScopeFoot: "恢复默认只写回开关值,不重写其他配置",
9175
+ guardBoundaryLabel: "边界",
9176
+ guardFootCycle: "命中后只给出说明,不阻断调用",
9177
+ guardFootNoProgress: "降级条件 日志缺失 · 结果不可比较 · 识别不确定",
8282
9178
  guardSectionA: "A · 相同调用重复提醒",
8283
9179
  guardSectionB: "B · 交替循环识别",
8284
9180
  guardSectionC: "C · 无进展保护",
@@ -8336,6 +9232,8 @@ async function apply(ctx) {
8336
9232
  residentShort: "常驻",
8337
9233
  onDemandShort: "按需",
8338
9234
  disabledShort: "禁用",
9235
+ catalogZeroCall: "零调用",
9236
+ catalogZeroCallHint: "按已索引调用统计",
8339
9237
  cycleHint: "点击标签切换分类(按需 → 禁用 → 常驻)",
8340
9238
  notPreviewable: "该文件不是可预览的文本文件",
8341
9239
  previewClose: "关闭",
@@ -8477,6 +9375,12 @@ async function apply(ctx) {
8477
9375
  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
9376
  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
9377
  guardReset: "Restore defaults",
9378
+ guardScopeTitle: "Scope",
9379
+ 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.",
9380
+ guardScopeFoot: "Restoring defaults writes only the switch values; no other configuration is rewritten.",
9381
+ guardBoundaryLabel: "Boundary",
9382
+ guardFootCycle: "Fires an explanation only; it never blocks the call.",
9383
+ guardFootNoProgress: "Degrades when: logs missing · results incomparable · match uncertain",
8480
9384
  guardSectionA: "A · Repeated identical calls",
8481
9385
  guardSectionB: "B · Alternating cycle detection",
8482
9386
  guardSectionC: "C · No-progress protection",
@@ -8534,6 +9438,8 @@ async function apply(ctx) {
8534
9438
  residentShort: "Resident",
8535
9439
  onDemandShort: "On-demand",
8536
9440
  disabledShort: "Disabled",
9441
+ catalogZeroCall: "No calls",
9442
+ catalogZeroCallHint: "Based on indexed calls",
8537
9443
  cycleHint: "Click a tag to cycle its classification (On-demand → Disabled → Resident)",
8538
9444
  notPreviewable: "This file is not a previewable text file",
8539
9445
  previewClose: "Close",