dsh-thinking-guard 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/client.d.ts +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +416 -377
- package/lib/types/usage/call-index-runtime.d.ts.map +1 -1
- package/lib/usage/call-index-runtime.js +56 -4
- package/lib/usage/call-index-runtime.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3919,6 +3919,30 @@ button.v3-tag{border:0;cursor:pointer;font-family:inherit}
|
|
|
3919
3919
|
button.v3-tag:disabled{opacity:.55;cursor:default}
|
|
3920
3920
|
.v3-group>summary:focus-visible{outline:2px solid var(--v3-info);outline-offset:-2px}
|
|
3921
3921
|
.v3-tools{display:flex;flex-direction:column}
|
|
3922
|
+
/* ---- v3 catalog rework: 一行工具栏 / 能力诊断卡片 / 卡片向总览看齐 ---- */
|
|
3923
|
+
.mc-cat-root{display:flex;flex-direction:column;gap:12px;min-width:0}
|
|
3924
|
+
.mc-cat-root .v3-catstat{margin-bottom:0}
|
|
3925
|
+
.mc-cat-bar{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;min-width:0}
|
|
3926
|
+
.mc-cat-bar>.mc-cat-actions{flex:none}
|
|
3927
|
+
.mc-cat-bar .mc-cat-actions button{font-size:11px;white-space:nowrap}
|
|
3928
|
+
.mc-cat-tabs{display:flex;align-items:center;flex:none;min-width:0}
|
|
3929
|
+
.mc-cat-tabs .mc-main-tabs{border-bottom:0}
|
|
3930
|
+
.mc-cat-search{flex:1 1 auto;width:auto;min-width:96px;max-width:220px;display:flex}
|
|
3931
|
+
.mc-cat-search input{box-sizing:border-box;width:100%;height:28px;padding:0 9px;border:1px solid var(--v3-line-2);border-radius:var(--v3-r-sm);background:var(--v3-panel);color:var(--v3-t1);font:inherit;font-size:12.5px}
|
|
3932
|
+
.mc-cat-search input:hover{border-color:var(--v3-line-2)}
|
|
3933
|
+
.mc-cat-search input:focus-visible{outline:2px solid var(--v3-info);outline-offset:1px;border-color:transparent}
|
|
3934
|
+
.mc-cat-actions{display:flex;align-items:center;gap:6px;flex:none;margin-left:auto;flex-wrap:nowrap}
|
|
3935
|
+
.mc-cat-diag{background:var(--v3-panel);border:1px solid var(--v3-line);border-radius:var(--v3-r-lg);overflow:hidden}
|
|
3936
|
+
.mc-cat-diag>summary{display:flex;align-items:center;gap:8px;padding:11px 15px;min-height:38px;cursor:pointer;list-style:none;font-size:12.5px;font-weight:600;color:var(--v3-t2)}
|
|
3937
|
+
.mc-cat-diag>summary::-webkit-details-marker{display:none}
|
|
3938
|
+
.mc-cat-diag>summary:hover{background:var(--v3-panel-2)}
|
|
3939
|
+
.mc-cat-diag[open]>summary{border-bottom:1px solid var(--v3-line)}
|
|
3940
|
+
.mc-cat-diag-body{display:flex;flex-direction:column;gap:4px;padding:11px 15px;font-size:11.5px;line-height:18px;color:var(--v3-t3)}
|
|
3941
|
+
.v3-root .v3-catgrid{gap:14px}
|
|
3942
|
+
.v3-root .v3-group{border-radius:var(--v3-r-lg);margin-bottom:0}
|
|
3943
|
+
.v3-root .v3-group>summary{padding:11px 15px;min-height:38px}
|
|
3944
|
+
.v3-root .v3-tag{font-size:11px;height:19px;line-height:19px;padding:0 7px}
|
|
3945
|
+
.v3-root .v3-trow{min-height:34px;font-size:12.5px}
|
|
3922
3946
|
`;
|
|
3923
3947
|
if (typeof document !== "undefined" && document.querySelector(`style[data-css-id="${CSS_ID}"]`) === null) {
|
|
3924
3948
|
const tag = document.createElement("style");
|
|
@@ -3991,14 +4015,6 @@ function MarkdownFilePreview(props) {
|
|
|
3991
4015
|
})] });
|
|
3992
4016
|
}
|
|
3993
4017
|
/**
|
|
3994
|
-
* Which build of the plugin is on screen. Several rounds of "is my profile
|
|
3995
|
-
* running the version I just installed?" had to be answered by grepping
|
|
3996
|
-
* `node_modules`, because the panel said nothing about itself. The value is
|
|
3997
|
-
* inlined by `tsdown` (see `src/client/env.d.ts`).
|
|
3998
|
-
*/
|
|
3999
|
-
const PLUGIN_VERSION = "0.1.21";
|
|
4000
|
-
const PLUGIN_TITLE = `capability-menu v${PLUGIN_VERSION}`;
|
|
4001
|
-
/**
|
|
4002
4018
|
* dsh's skill source labels, shown on rows this plugin cannot edit — so "why is
|
|
4003
4019
|
* there no 编辑 button" has a visible answer. Labels that name one directory are
|
|
4004
4020
|
* shown as that path; the rest name a category and go through the dictionary, so
|
|
@@ -4882,128 +4898,116 @@ function ReadyBody(props) {
|
|
|
4882
4898
|
}
|
|
4883
4899
|
];
|
|
4884
4900
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4885
|
-
className: "v3-root",
|
|
4901
|
+
className: "v3-root mc-cat-root",
|
|
4886
4902
|
children: [
|
|
4887
4903
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(V3Styles, {}),
|
|
4888
4904
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4889
|
-
className: "mc-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
children: t("newVersion")
|
|
4905
|
+
className: "mc-cat-bar",
|
|
4906
|
+
"data-v3-catalog-bar": "true",
|
|
4907
|
+
children: [
|
|
4908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4909
|
+
className: "mc-cat-tabs",
|
|
4910
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4911
|
+
className: "mc-main-tabs",
|
|
4912
|
+
items: [{
|
|
4913
|
+
value: "tools",
|
|
4914
|
+
id: "mc-tab-tools",
|
|
4915
|
+
panelId: "mc-panel-tools",
|
|
4916
|
+
label: t("toolsGroup")
|
|
4917
|
+
}, {
|
|
4918
|
+
value: "skills",
|
|
4919
|
+
id: "mc-tab-skills",
|
|
4920
|
+
panelId: "mc-panel-skills",
|
|
4921
|
+
label: t("skillsGroup")
|
|
4922
|
+
}],
|
|
4923
|
+
value: activeTab,
|
|
4924
|
+
onChange: onTabChange,
|
|
4925
|
+
label: t("title")
|
|
4911
4926
|
})
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
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")
|
|
4927
|
+
}),
|
|
4928
|
+
snapshot.rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4929
|
+
className: "mc-cat-search",
|
|
4930
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4931
|
+
type: "search",
|
|
4932
|
+
value: filter,
|
|
4933
|
+
placeholder: t("filterByName"),
|
|
4934
|
+
"aria-label": t("filterByName"),
|
|
4935
|
+
title: t("filterHint"),
|
|
4936
|
+
onChange: (e) => setFilter(e.target.value)
|
|
4935
4937
|
})
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
+
}),
|
|
4939
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4940
|
+
className: "mc-cat-actions",
|
|
4941
|
+
children: [
|
|
4942
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4943
|
+
size: "sm",
|
|
4944
|
+
variant: "outline",
|
|
4945
|
+
onClick: () => setRegisterOpen(true),
|
|
4946
|
+
children: t("registerCapability")
|
|
4947
|
+
}),
|
|
4948
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4949
|
+
size: "sm",
|
|
4950
|
+
variant: "outline",
|
|
4951
|
+
onClick: () => void openCatalogDocs(),
|
|
4952
|
+
children: t("viewCatalog")
|
|
4953
|
+
}),
|
|
4954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4955
|
+
type: "button",
|
|
4956
|
+
size: "sm",
|
|
4957
|
+
variant: "outline",
|
|
4958
|
+
onClick: () => window.open("https://github.com/PKUfudawei/dsh-capability-menu", "_blank", "noopener,noreferrer"),
|
|
4959
|
+
children: t("feedback")
|
|
4960
|
+
}),
|
|
4961
|
+
updateStatus?.updateAvailable && (updateStatus.updatable || updateStatus.reason === "local") && updatePhase !== "done" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4962
|
+
type: "button",
|
|
4963
|
+
variant: "primary",
|
|
4964
|
+
size: "sm",
|
|
4965
|
+
className: "mc-update-check",
|
|
4966
|
+
title: t("updateAvailable", { version: updateStatus.latest }),
|
|
4967
|
+
disabled: updatePhase === "installing",
|
|
4968
|
+
onClick: () => setUpdateDialogOpen(true),
|
|
4969
|
+
children: t("newVersion")
|
|
4970
|
+
})
|
|
4971
|
+
]
|
|
4972
|
+
})
|
|
4973
|
+
]
|
|
4938
4974
|
}),
|
|
4939
4975
|
(updatePhase === "done" || updateStatus?.updateAvailable && !updateStatus.updatable && updateStatus.reason !== "local") && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4940
4976
|
className: "mc-update-status",
|
|
4941
4977
|
role: "status",
|
|
4942
4978
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: updatePhase === "done" ? t(updateStatus?.reason === "local" ? "pluginSwitched" : "pluginUpdated") : t("updateAvailable", { version: updateStatus?.latest ?? "" }) }), updatePhase !== "done" && updateStatus?.updateAvailable && !updateStatus.updatable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(updateStatus.reason === "non-registry" ? "pluginNonRegistrySource" : "pluginProfileUnavailable") })]
|
|
4943
4979
|
}),
|
|
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",
|
|
4989
|
-
label: t("skillsGroup")
|
|
4990
|
-
}],
|
|
4991
|
-
value: activeTab,
|
|
4992
|
-
onChange: onTabChange,
|
|
4993
|
-
label: t("title")
|
|
4994
|
-
})
|
|
4995
|
-
}),
|
|
4996
4980
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CatalogStatStrip, { cells: statCells }),
|
|
4997
|
-
|
|
4998
|
-
className: "mc-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
4981
|
+
diagnostics !== null && skillDiagnosticCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
4982
|
+
className: "mc-cat-diag",
|
|
4983
|
+
"data-v3-catalog-diagnostics": "true",
|
|
4984
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("diagnosticTitle", { count: skillDiagnosticCount }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4985
|
+
className: "mc-cat-diag-body",
|
|
4986
|
+
children: [
|
|
4987
|
+
diagnostics.unavailableSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnavailable", {
|
|
4988
|
+
name: item.name,
|
|
4989
|
+
source: item.source,
|
|
4990
|
+
preset: item.preset ?? ""
|
|
4991
|
+
}) }, `unavailable-${item.name}-${item.preset ?? ""}-${index}`)),
|
|
4992
|
+
diagnostics.shadowedSkills.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticShadowed", {
|
|
4993
|
+
name: item.name,
|
|
4994
|
+
winner: item.selectedFrom,
|
|
4995
|
+
hidden: item.shadowedFrom
|
|
4996
|
+
}) }, `shadowed-${item.name}-${item.shadowedFrom}-${index}`)),
|
|
4997
|
+
diagnostics.brokenPresets.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticBroken", {
|
|
4998
|
+
id: item.id,
|
|
4999
|
+
reason: item.reason
|
|
5000
|
+
}) }, `broken-${item.id}`)),
|
|
5001
|
+
diagnostics.unbrowseableSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUnbrowseable", {
|
|
5002
|
+
name: item.name,
|
|
5003
|
+
reason: item.reason
|
|
5004
|
+
}) }, `browse-${item.name}`)),
|
|
5005
|
+
diagnostics.undocumentedSkills.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t("diagnosticUndocumented", {
|
|
5006
|
+
name: item.name,
|
|
5007
|
+
summary: item.summary
|
|
5008
|
+
}) }, `undocumented-${item.name}`))
|
|
5009
|
+
]
|
|
5010
|
+
})]
|
|
5007
5011
|
}),
|
|
5008
5012
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5009
5013
|
role: "tabpanel",
|
|
@@ -6513,11 +6517,7 @@ const COVERAGE_LABELS = Object.freeze({
|
|
|
6513
6517
|
partial: "部分",
|
|
6514
6518
|
unknown: "未知"
|
|
6515
6519
|
});
|
|
6516
|
-
|
|
6517
|
-
* Zero-count placeholders. Required for `plugin-tool` / `skill`; the other
|
|
6518
|
-
* kinds get a neutral line so no zero is ever presented as "未使用".
|
|
6519
|
-
*/
|
|
6520
|
-
const CALL_INDEX_ZERO_SOURCE_NOTES = Object.freeze({
|
|
6520
|
+
Object.freeze({
|
|
6521
6521
|
mcp: "本次索引未解析到 MCP 来源的调用;覆盖不完整时不能据此判定未使用。",
|
|
6522
6522
|
native: "本次索引未解析到原生工具调用;覆盖不完整时不能据此判定未使用。",
|
|
6523
6523
|
"plugin-tool": "本次索引未解析出插件工具归属(插件 owner 不可证明),0 在这里不等于未使用。",
|
|
@@ -6556,10 +6556,6 @@ function formatCallIndexCount(value, totalExact) {
|
|
|
6556
6556
|
const text = Math.trunc(value).toLocaleString("en-US");
|
|
6557
6557
|
return totalExact === true ? text : `≥${text}`;
|
|
6558
6558
|
}
|
|
6559
|
-
/** Human label for the time filter. */
|
|
6560
|
-
function callIndexRangeLabel(range) {
|
|
6561
|
-
return range === "all" ? "全部" : `${range} 天`;
|
|
6562
|
-
}
|
|
6563
6559
|
/**
|
|
6564
6560
|
* Local-day window for `range`, expressed as the wire's half-open
|
|
6565
6561
|
* `[fromMs, toMs)`. `'all'` carries no lower bound.
|
|
@@ -6804,24 +6800,6 @@ function callIndexOutcomeMix(summary) {
|
|
|
6804
6800
|
total: summary.total
|
|
6805
6801
|
};
|
|
6806
6802
|
}
|
|
6807
|
-
/**
|
|
6808
|
-
* `bySource` for display. Always returns all five kinds in a fixed order, so
|
|
6809
|
-
* the distribution never reshuffles between reads; a zero carries its
|
|
6810
|
-
* placeholder instead of disappearing.
|
|
6811
|
-
*/
|
|
6812
|
-
function callIndexSourceData(bySource) {
|
|
6813
|
-
const total = bySource === void 0 ? 0 : SOURCE_DISPLAY_ORDER.reduce((sum, kind) => sum + bySource[kind], 0);
|
|
6814
|
-
return SOURCE_DISPLAY_ORDER.map((kind) => {
|
|
6815
|
-
const count = bySource?.[kind] ?? 0;
|
|
6816
|
-
return {
|
|
6817
|
-
kind,
|
|
6818
|
-
label: CALL_INDEX_SOURCE_LABELS[kind],
|
|
6819
|
-
count,
|
|
6820
|
-
share: total > 0 ? count / total : 0,
|
|
6821
|
-
...count === 0 ? { zeroNote: CALL_INDEX_ZERO_SOURCE_NOTES[kind] } : {}
|
|
6822
|
-
};
|
|
6823
|
-
});
|
|
6824
|
-
}
|
|
6825
6803
|
/** Fixed chart box; the SVG scales with CSS but the geometry is deterministic. */
|
|
6826
6804
|
const CALL_INDEX_TREND_GEOMETRY = Object.freeze({
|
|
6827
6805
|
width: 640,
|
|
@@ -7002,18 +6980,13 @@ function callIndexOverviewMetrics(overview, status) {
|
|
|
7002
6980
|
...status?.task === void 0 ? {} : { tasks: status.task }
|
|
7003
6981
|
};
|
|
7004
6982
|
}
|
|
7005
|
-
/** True when `plugin-tool` and `skill` both resolved to zero. */
|
|
7006
|
-
function callIndexHasUnexplainedSources(aggregates) {
|
|
7007
|
-
if (aggregates === void 0) return false;
|
|
7008
|
-
return aggregates.bySource["plugin-tool"] === 0 && aggregates.bySource.skill === 0;
|
|
7009
|
-
}
|
|
7010
6983
|
/** Short Session id for a dense table cell; the full id stays in `title`. */
|
|
7011
6984
|
function shortCallIndexSessionId(sessionId) {
|
|
7012
6985
|
return sessionId.length <= 12 ? sessionId : sessionId.slice(0, 12);
|
|
7013
6986
|
}
|
|
7014
6987
|
/** Inline stylesheet; a `<style>` element keeps this file dependency-free. */
|
|
7015
6988
|
function CallIndexStyles() {
|
|
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)}");
|
|
6989
|
+
return (0, react.createElement)("style", { "data-call-index-style": "true" }, ".mc-ci-filter{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin:8px 0}.mc-ci-filter label{display:flex;gap:4px;align-items:center;font-size:12px}.mc-ci-table-scroll{width:100%;overflow:auto;max-height:min(60vh,560px);overscroll-behavior:contain}.mc-ci-table{width:100%;border-collapse:collapse;font-size:12px}.mc-ci-table thead th{position:sticky;top:0;z-index:1;background:var(--mc-surface,Canvas);box-shadow:inset 0 -1px 0 color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress{display:flex;flex-direction:column;gap:4px;margin:8px 0}.mc-ci-progress-track{height:6px;border-radius:999px;overflow:hidden;background:color-mix(in srgb,currentColor 12%,transparent)}.mc-ci-progress-fill{height:100%;border-radius:999px;background:color-mix(in srgb,currentColor 55%,transparent);transition:width .3s ease}.mc-ci-progress-fill.is-indeterminate{animation:mc-ci-indeterminate 1.1s ease-in-out infinite}@keyframes mc-ci-indeterminate{0%{transform:translateX(-60%)}100%{transform:translateX(340%)}}@media (prefers-reduced-motion:reduce){.mc-ci-progress-fill{transition:none}.mc-ci-progress-fill.is-indeterminate{animation:none;width:100%!important;opacity:.4}}.mc-ci-session-picker{display:inline-flex;align-items:center;gap:6px;font-size:12px}.mc-ci-session-picker-label{opacity:.7}.mc-ci-session-select{max-width:280px;padding:3px 6px;border-radius:6px;font-size:12px;border:1px solid color-mix(in srgb,currentColor 22%,transparent);background:transparent;color:inherit}.mc-ci-session-select:disabled{opacity:.5}.mc-ci-progress-meta{display:flex;gap:8px;align-items:baseline;font-size:12px}.mc-ci-progress-value{font-variant-numeric:tabular-nums;font-weight:600}.mc-ci-progress-detail{opacity:.72}.mc-ci-table th,.mc-ci-table td{padding:4px 6px;text-align:left;border-bottom:1px solid color-mix(in srgb,currentColor 12%,transparent);white-space:nowrap}.mc-ci-table tbody tr{cursor:pointer}.mc-ci-table tbody tr[aria-selected=\"true\"]{background:color-mix(in srgb,currentColor 8%,transparent)}.mc-ci-table td:nth-child(2),.mc-ci-table td:nth-child(4){max-width:220px;overflow:hidden;text-overflow:ellipsis}.mc-ci-table th:nth-child(2),.mc-ci-table th:nth-child(4){max-width:220px}.mc-ci-num{font-variant-numeric:tabular-nums}.mc-ci-bar{display:flex;height:8px;gap:1px;margin:4px 0}.mc-ci-bar > span{display:block;height:100%;min-width:2px}.mc-ci-aside{border-left:1px solid color-mix(in srgb,currentColor 15%,transparent);padding-left:12px;min-width:240px;font-size:12px}.mc-ci-metric{display:inline-flex;flex-direction:column;margin-right:18px;min-width:110px;vertical-align:top}.mc-ci-metric-label{font-size:11px;opacity:.7}.mc-ci-metric-value{font-size:18px;font-variant-numeric:tabular-nums}.mc-ci-metric-hint{font-size:11px;opacity:.75}.mc-ci-trend svg{width:100%;max-width:640px;height:auto}.v3-status-pills{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:12px}.v3-statusline.v3-status-dash{border-style:dashed;color:var(--v3-t4)}.v3-filtercard{padding:11px 14px 13px}.v3-filter-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:9px}.v3-search{display:flex;align-items:center;gap:6px;height:26px;padding:0 8px;border:1px solid var(--v3-line);border-radius:var(--v3-r-sm);background:var(--v3-panel-2);min-width:150px;flex:1 1 150px}.v3-search input{border:0;background:transparent;color:var(--v3-t1);font:inherit;font-size:12px;width:100%;outline:0;min-width:0}.v3-search input::placeholder{color:var(--v3-t4)}.v3-kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px}.v3-kv-l{font-size:11px;color:var(--v3-t4)}.v3-kv-v{font-size:12px;color:var(--v3-t2);margin-top:2px;overflow-wrap:anywhere}.v3-ts.v3-ts--h330{max-height:330px}.v3-chartview{overflow-x:auto;cursor:grab;overscroll-behavior-x:contain;scrollbar-width:thin;user-select:none;-webkit-user-select:none}.v3-chartview:active{cursor:grabbing}.v3-chartview svg{display:block}.v3-src i[data-zero=\"true\"]{background:var(--v3-panel-2)}");
|
|
7017
6990
|
}
|
|
7018
6991
|
/** One v3 `.v3-stat` cell: label, big value, optional hint line, optional sparkline. */
|
|
7019
6992
|
function V3Stat(props) {
|
|
@@ -7064,21 +7037,32 @@ function toolDisplayLabel(toolName) {
|
|
|
7064
7037
|
}
|
|
7065
7038
|
return toolName;
|
|
7066
7039
|
}
|
|
7040
|
+
/** Pixel width of the trend for `days` buckets: days x min column, floored. */
|
|
7041
|
+
function callIndexTrendWidth(days) {
|
|
7042
|
+
return Math.max(320, (Number.isFinite(days) && days > 0 ? Math.floor(days) : 1) * 34);
|
|
7043
|
+
}
|
|
7067
7044
|
/**
|
|
7068
|
-
* The v3 overview trend: a
|
|
7069
|
-
*
|
|
7070
|
-
* so
|
|
7045
|
+
* The v3 overview trend: a line + area (calls), failure底柱 (error), a dashed
|
|
7046
|
+
* "recent week" band and a peak label, drawn inside a horizontally scrollable
|
|
7047
|
+
* strip so a long series can be panned instead of squeezed.
|
|
7048
|
+
*
|
|
7049
|
+
* Geometry comes from `callIndexTrend`'s bars (one data path). The SVG is
|
|
7050
|
+
* days x min-column wide (never narrower than the card), so day labels stay
|
|
7051
|
+
* legible; the wrapper scrolls and supports pointer drag-to-pan. The last day
|
|
7052
|
+
* label is right-anchored at the padding edge so it cannot overlap the one
|
|
7053
|
+
* before it.
|
|
7071
7054
|
*/
|
|
7072
7055
|
function OverviewTrendSvg(props) {
|
|
7073
|
-
const { chart } = props
|
|
7074
|
-
const plotW = 676;
|
|
7075
|
-
const plotH = 174;
|
|
7056
|
+
const { chart } = props;
|
|
7076
7057
|
const bars = chart.bars;
|
|
7077
7058
|
const n = bars.length;
|
|
7059
|
+
const W = callIndexTrendWidth(n), H = 212, padL = 34, padR = 22, padT = 12;
|
|
7060
|
+
const plotW = W - padL - padR;
|
|
7061
|
+
const plotH = 174;
|
|
7078
7062
|
const maxV = chart.maxCalls > 0 ? Math.ceil(chart.maxCalls / 10) * 10 : 1;
|
|
7079
7063
|
const xAt = (i) => padL + (n <= 1 ? plotW / 2 : i * plotW / (n - 1));
|
|
7080
7064
|
const yAt = (v) => 186 - v / maxV * plotH;
|
|
7081
|
-
const points = bars.map((bar, i) =>
|
|
7065
|
+
const points = bars.map((bar, i) => xAt(i).toFixed(1) + "," + yAt(bar.calls).toFixed(1));
|
|
7082
7066
|
let peak = 0;
|
|
7083
7067
|
for (let i = 1; i < n; i++) if (bars[i].calls > bars[peak].calls) peak = i;
|
|
7084
7068
|
const children = [];
|
|
@@ -7086,7 +7070,7 @@ function OverviewTrendSvg(props) {
|
|
|
7086
7070
|
const start = Math.max(0, n - 7);
|
|
7087
7071
|
const slot = n > 1 ? plotW / (n - 1) : plotW;
|
|
7088
7072
|
const left = Math.max(padL, xAt(start) - slot / 2);
|
|
7089
|
-
const right = Math.min(
|
|
7073
|
+
const right = Math.min(W - padR, xAt(n - 1) + slot / 2);
|
|
7090
7074
|
children.push((0, react.createElement)("rect", {
|
|
7091
7075
|
key: "hl",
|
|
7092
7076
|
className: "v3-hlbox",
|
|
@@ -7101,15 +7085,15 @@ function OverviewTrendSvg(props) {
|
|
|
7101
7085
|
const value = maxV * g / 4;
|
|
7102
7086
|
const y = yAt(value);
|
|
7103
7087
|
children.push((0, react.createElement)("line", {
|
|
7104
|
-
key:
|
|
7088
|
+
key: "grid-" + g,
|
|
7105
7089
|
className: "v3-grid",
|
|
7106
7090
|
x1: padL,
|
|
7107
7091
|
y1: y.toFixed(1),
|
|
7108
|
-
x2:
|
|
7092
|
+
x2: W - padR,
|
|
7109
7093
|
y2: y.toFixed(1)
|
|
7110
7094
|
}));
|
|
7111
7095
|
children.push((0, react.createElement)("text", {
|
|
7112
|
-
key:
|
|
7096
|
+
key: "tick-" + g,
|
|
7113
7097
|
className: "v3-axis",
|
|
7114
7098
|
x: 27,
|
|
7115
7099
|
y: (y + 3.5).toFixed(1),
|
|
@@ -7121,19 +7105,19 @@ function OverviewTrendSvg(props) {
|
|
|
7121
7105
|
if (bar.error <= 0) return;
|
|
7122
7106
|
const errH = Math.max(2.5, bar.error / maxV * plotH);
|
|
7123
7107
|
children.push((0, react.createElement)("rect", {
|
|
7124
|
-
key:
|
|
7108
|
+
key: "err-" + bar.day,
|
|
7125
7109
|
className: "v3-bar-er",
|
|
7126
7110
|
x: (xAt(i) - barWidth / 2).toFixed(1),
|
|
7127
7111
|
y: (186 - errH).toFixed(1),
|
|
7128
7112
|
width: barWidth.toFixed(1),
|
|
7129
7113
|
height: errH.toFixed(1),
|
|
7130
7114
|
rx: 1.5
|
|
7131
|
-
}, (0, react.createElement)("title", null,
|
|
7115
|
+
}, (0, react.createElement)("title", null, bar.day + ":失败 " + bar.error + " 次")));
|
|
7132
7116
|
});
|
|
7133
7117
|
children.push((0, react.createElement)("polygon", {
|
|
7134
7118
|
key: "area",
|
|
7135
7119
|
className: "v3-area",
|
|
7136
|
-
points:
|
|
7120
|
+
points: "34,186 " + points.join(" ") + " " + (W - padR) + ",186"
|
|
7137
7121
|
}));
|
|
7138
7122
|
children.push((0, react.createElement)("polyline", {
|
|
7139
7123
|
key: "line",
|
|
@@ -7143,12 +7127,12 @@ function OverviewTrendSvg(props) {
|
|
|
7143
7127
|
bars.forEach((bar, i) => {
|
|
7144
7128
|
if (i % 4 !== 0 && i !== n - 1 && i !== peak) return;
|
|
7145
7129
|
children.push((0, react.createElement)("circle", {
|
|
7146
|
-
key:
|
|
7130
|
+
key: "pt-" + bar.day,
|
|
7147
7131
|
className: "v3-pt",
|
|
7148
7132
|
cx: xAt(i).toFixed(1),
|
|
7149
7133
|
cy: yAt(bar.calls).toFixed(1),
|
|
7150
7134
|
r: 2.6
|
|
7151
|
-
}, (0, react.createElement)("title", null,
|
|
7135
|
+
}, (0, react.createElement)("title", null, bar.day + ":" + bar.calls + " 次调用,失败 " + bar.error + " 次")));
|
|
7152
7136
|
});
|
|
7153
7137
|
if (n > 0) {
|
|
7154
7138
|
children.push((0, react.createElement)("text", {
|
|
@@ -7157,9 +7141,10 @@ function OverviewTrendSvg(props) {
|
|
|
7157
7141
|
x: (xAt(peak) - 8).toFixed(1),
|
|
7158
7142
|
y: (yAt(bars[peak].calls) - 9).toFixed(1),
|
|
7159
7143
|
"text-anchor": "end"
|
|
7160
|
-
},
|
|
7161
|
-
|
|
7162
|
-
|
|
7144
|
+
}, "峰值 " + bars[peak].calls));
|
|
7145
|
+
const step = Math.max(1, Math.ceil(n / 6));
|
|
7146
|
+
for (let m = 0; m < n - 1; m += step) children.push((0, react.createElement)("text", {
|
|
7147
|
+
key: "xl-" + m,
|
|
7163
7148
|
className: "v3-axis",
|
|
7164
7149
|
x: xAt(m).toFixed(1),
|
|
7165
7150
|
y: 204,
|
|
@@ -7168,35 +7153,106 @@ function OverviewTrendSvg(props) {
|
|
|
7168
7153
|
children.push((0, react.createElement)("text", {
|
|
7169
7154
|
key: "xl-last",
|
|
7170
7155
|
className: "v3-axis",
|
|
7171
|
-
x:
|
|
7156
|
+
x: (W - padR).toFixed(1),
|
|
7172
7157
|
y: 204,
|
|
7173
|
-
"text-anchor": "end"
|
|
7158
|
+
"text-anchor": "end",
|
|
7159
|
+
"data-call-index-lasttick": "true"
|
|
7174
7160
|
}, bars[n - 1].day.slice(5)));
|
|
7175
7161
|
}
|
|
7176
|
-
|
|
7162
|
+
const svg = (0, react.createElement)("svg", {
|
|
7177
7163
|
className: "v3-chart",
|
|
7178
|
-
viewBox:
|
|
7164
|
+
viewBox: "0 0 " + W + " 212",
|
|
7179
7165
|
preserveAspectRatio: "none",
|
|
7180
7166
|
role: "img",
|
|
7181
|
-
|
|
7182
|
-
|
|
7167
|
+
width: W,
|
|
7168
|
+
height: H,
|
|
7169
|
+
style: {
|
|
7170
|
+
width: W + "px",
|
|
7171
|
+
minWidth: "100%",
|
|
7172
|
+
height: "212px"
|
|
7173
|
+
},
|
|
7174
|
+
"aria-label": "每日调用与失败数:" + n + " 天,峰值 " + chart.maxCalls + " 次",
|
|
7175
|
+
"data-call-index-svg": "trend",
|
|
7176
|
+
"data-call-index-width": String(W)
|
|
7183
7177
|
}, ...children);
|
|
7178
|
+
let dragging = false;
|
|
7179
|
+
let startX = 0;
|
|
7180
|
+
let startScroll = 0;
|
|
7181
|
+
return (0, react.createElement)("div", {
|
|
7182
|
+
className: "v3-chartview",
|
|
7183
|
+
"data-call-index-chartview": "true",
|
|
7184
|
+
onPointerDown: (event) => {
|
|
7185
|
+
dragging = true;
|
|
7186
|
+
startX = event.clientX;
|
|
7187
|
+
startScroll = event.currentTarget.scrollLeft;
|
|
7188
|
+
event.currentTarget.style.cursor = "grabbing";
|
|
7189
|
+
if (event.pointerId !== void 0 && typeof event.currentTarget.setPointerCapture === "function") try {
|
|
7190
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
7191
|
+
} catch {}
|
|
7192
|
+
},
|
|
7193
|
+
onPointerMove: (event) => {
|
|
7194
|
+
if (!dragging) return;
|
|
7195
|
+
event.currentTarget.scrollLeft = startScroll - (event.clientX - startX);
|
|
7196
|
+
},
|
|
7197
|
+
onPointerUp: (event) => {
|
|
7198
|
+
dragging = false;
|
|
7199
|
+
event.currentTarget.style.cursor = "grab";
|
|
7200
|
+
},
|
|
7201
|
+
onPointerLeave: (event) => {
|
|
7202
|
+
if (!dragging) return;
|
|
7203
|
+
dragging = false;
|
|
7204
|
+
event.currentTarget.style.cursor = "grab";
|
|
7205
|
+
}
|
|
7206
|
+
}, svg);
|
|
7184
7207
|
}
|
|
7185
|
-
/**
|
|
7208
|
+
/**
|
|
7209
|
+
* 来源构成,按工具名前缀启发式分类。
|
|
7210
|
+
*
|
|
7211
|
+
* NOT Host attribution: the Host cannot prove a plugin owner, so this card does
|
|
7212
|
+
* not pretend to. It splits the already-read per-tool rows by a plain rule —
|
|
7213
|
+
* a `mcp__` prefix is MCP, everything else is native — and says so at the foot.
|
|
7214
|
+
* When the per-tool read has not happened there is nothing to classify, so the
|
|
7215
|
+
* card shows 「来源未解析」 and explains that this is not a zero count.
|
|
7216
|
+
*/
|
|
7186
7217
|
function OverviewSourcePanel(props) {
|
|
7187
|
-
|
|
7218
|
+
const note = (0, react.createElement)("p", { className: "v3-src-note" }, (0, react.createElement)("span", null, "按工具名前缀推断,不是 Host 归属。"), (0, react.createElement)("b", { style: { color: "var(--v3-t3)" } }, " 0 在这里不等于未使用"));
|
|
7219
|
+
if (props.tools === void 0) return (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "来源构成")), (0, react.createElement)("p", {
|
|
7188
7220
|
className: "v3-hint",
|
|
7189
7221
|
"data-call-index-missing": "by-source"
|
|
7190
|
-
}, "
|
|
7191
|
-
const
|
|
7192
|
-
const
|
|
7193
|
-
const
|
|
7194
|
-
const
|
|
7195
|
-
|
|
7222
|
+
}, "尚未读取逐工具明细,来源未解析;这不是「全部为 0」。"), note);
|
|
7223
|
+
const mcpCalls = props.tools.filter((row) => row.toolName.startsWith("mcp__")).reduce((sum, row) => sum + row.calls, 0);
|
|
7224
|
+
const nativeCalls = props.tools.reduce((sum, row) => sum + row.calls, 0) - mcpCalls;
|
|
7225
|
+
const total = mcpCalls + nativeCalls;
|
|
7226
|
+
const data = [{
|
|
7227
|
+
kind: "mcp",
|
|
7228
|
+
label: CALL_INDEX_SOURCE_LABELS.mcp,
|
|
7229
|
+
count: mcpCalls,
|
|
7230
|
+
color: "var(--v3-info)"
|
|
7231
|
+
}, {
|
|
7232
|
+
kind: "native",
|
|
7233
|
+
label: CALL_INDEX_SOURCE_LABELS.native,
|
|
7234
|
+
count: nativeCalls,
|
|
7235
|
+
color: "var(--v3-ok)"
|
|
7236
|
+
}];
|
|
7196
7237
|
const radius = 43;
|
|
7197
7238
|
const circumference = 2 * Math.PI * radius;
|
|
7198
|
-
|
|
7199
|
-
const
|
|
7239
|
+
let offset = 0;
|
|
7240
|
+
const segments = data.filter((item) => item.count > 0).map((item) => {
|
|
7241
|
+
const length = total > 0 ? item.count / total * circumference : 0;
|
|
7242
|
+
const circle = (0, react.createElement)("circle", {
|
|
7243
|
+
key: item.kind,
|
|
7244
|
+
className: "v3-seg",
|
|
7245
|
+
cx: 52,
|
|
7246
|
+
cy: 52,
|
|
7247
|
+
r: radius,
|
|
7248
|
+
"data-seg": item.kind,
|
|
7249
|
+
strokeDasharray: `${length.toFixed(1)} ${(circumference - length).toFixed(1)}`,
|
|
7250
|
+
strokeDashoffset: (-offset).toFixed(1),
|
|
7251
|
+
style: { stroke: item.color }
|
|
7252
|
+
});
|
|
7253
|
+
offset += length;
|
|
7254
|
+
return circle;
|
|
7255
|
+
});
|
|
7200
7256
|
return (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "来源构成"), (0, react.createElement)("span", { className: "v3-pn" }, `${formatCallIndexCount(total, props.totalExact)} 次`)), (0, react.createElement)("div", { className: "v3-donut-wrap" }, (0, react.createElement)("div", { className: "v3-donut" }, (0, react.createElement)("svg", {
|
|
7201
7257
|
viewBox: "0 0 104 104",
|
|
7202
7258
|
"aria-hidden": "true"
|
|
@@ -7205,19 +7261,12 @@ function OverviewSourcePanel(props) {
|
|
|
7205
7261
|
cx: 52,
|
|
7206
7262
|
cy: 52,
|
|
7207
7263
|
r: radius
|
|
7208
|
-
}), (0, react.createElement)("
|
|
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", {
|
|
7264
|
+
}), ...segments), (0, react.createElement)("div", { className: "v3-mid" }, (0, react.createElement)("b", { className: "v3-num" }, formatCallIndexCount(total, props.totalExact)), (0, react.createElement)("span", null, "次调用"))), (0, react.createElement)("div", { className: "v3-src-list" }, ...data.map((item) => (0, react.createElement)("div", {
|
|
7217
7265
|
key: item.kind,
|
|
7218
7266
|
className: "v3-src",
|
|
7219
|
-
"data-source-row": item.kind
|
|
7220
|
-
|
|
7267
|
+
"data-source-row": item.kind,
|
|
7268
|
+
"data-source-count": String(item.count)
|
|
7269
|
+
}, (0, react.createElement)("i", item.count === 0 ? { "data-zero": "true" } : {}), (0, react.createElement)("span", { className: "v3-nm" }, item.label), (0, react.createElement)("b", null, formatCallIndexCount(item.count, props.totalExact)), (0, react.createElement)("em", null, total > 0 ? `${Math.round(item.count / total * 100)}%` : "0%"))))), note);
|
|
7221
7270
|
}
|
|
7222
7271
|
function callIndexDiagnostics(input) {
|
|
7223
7272
|
const items = [];
|
|
@@ -7552,9 +7601,8 @@ function CallIndexDetailPanelView(props) {
|
|
|
7552
7601
|
...failure === void 0 ? {} : { failure }
|
|
7553
7602
|
});
|
|
7554
7603
|
const totalText = page === void 0 ? void 0 : formatCallIndexCount(page.total ?? page.summary.total, page.totalExact);
|
|
7555
|
-
const coverageLabel = page === void 0 ? void 0 : COVERAGE_LABELS[page.coverage];
|
|
7556
7604
|
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 ? "正在读取调用明细…" : "尚未读取调用明细" :
|
|
7605
|
+
const statusText = page === void 0 ? loading === true ? "正在读取调用明细…" : "尚未读取调用明细" : scanning ? "索引仍在扫描,数字会继续增加" : "索引已完成";
|
|
7558
7606
|
const rates = callIndexRates(page?.summary);
|
|
7559
7607
|
const successStats = rates.success;
|
|
7560
7608
|
const failureStats = rates.failure;
|
|
@@ -7621,7 +7669,7 @@ function CallIndexDetailPanelView(props) {
|
|
|
7621
7669
|
className: "v3-dot",
|
|
7622
7670
|
"data-tone": state.kind === "failure" || state.kind === "disconnected" ? "bad" : scanning ? "run" : "ok",
|
|
7623
7671
|
"aria-hidden": "true"
|
|
7624
|
-
}),
|
|
7672
|
+
}), (0, react.createElement)("span", null, statusText))), (0, react.createElement)("div", { className: "v3-panel v3-filtercard" }, (0, react.createElement)("div", { className: "v3-filter-row" }, CallIndexFilters({
|
|
7625
7673
|
query,
|
|
7626
7674
|
...onQueryChange === void 0 ? {} : { onChange: onQueryChange },
|
|
7627
7675
|
...onResolveScope === void 0 ? {} : { onResolveScope },
|
|
@@ -7710,7 +7758,7 @@ function CallIndexDetailPanelView(props) {
|
|
|
7710
7758
|
}, `条长 = 失败次数(不是失败率)。共 ${failingRows.length} 个工具出现过失败。`))), (0, react.createElement)("div", {
|
|
7711
7759
|
className: "v3-panel",
|
|
7712
7760
|
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} 个工具 · 表内滚动`)
|
|
7761
|
+
}, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "完整工具表"), (0, react.createElement)("span", { className: "v3-pn" }, `${rows.length} 行 · 表内滚动`)), (0, react.createElement)(CallIndexToolTable, { rows }), (0, react.createElement)("div", { className: "v3-tfoot" }, (0, react.createElement)("span", null, `${rows.length} 个工具 · 表内滚动`))), page?.nextCursor !== void 0 && loadingMore === true ? (0, react.createElement)("p", {
|
|
7714
7762
|
className: "v3-hint",
|
|
7715
7763
|
"data-call-index-note": "loading-more",
|
|
7716
7764
|
style: { marginTop: 8 }
|
|
@@ -7725,14 +7773,15 @@ function CallIndexDetailPanelView(props) {
|
|
|
7725
7773
|
function CallIndexOverviewPanelView(props) {
|
|
7726
7774
|
const { status, overview, loading, failure, onRefresh, tools, toolsLoading, toolsFailure } = props;
|
|
7727
7775
|
const metrics = callIndexOverviewMetrics(overview, status);
|
|
7728
|
-
const rangeLabel = callIndexRangeLabel("all");
|
|
7729
7776
|
const exact = metrics.callsTotalExact;
|
|
7730
7777
|
const mix = callIndexOutcomeMix(overview?.summary);
|
|
7731
7778
|
const aggregates = metrics.aggregates;
|
|
7732
7779
|
const notice = failure === void 0 ? void 0 : failure.disconnected ? `与 Host 断连:${callIndexFailureText(failure)}${failure.hostCode === void 0 ? "" : `(${failure.hostCode})`}` : callIndexFailureText(failure);
|
|
7733
7780
|
const coverageLabel = status === void 0 ? "未知" : COVERAGE_LABELS[status.coverage];
|
|
7734
7781
|
const scanning = status !== void 0 && (status.phase === "running" || (status.sessions?.pending ?? 0) > 0 || (status.sessions?.pendingLive ?? 0) > 0);
|
|
7735
|
-
const
|
|
7782
|
+
const pendingSessions = status?.sessions?.pending ?? 0;
|
|
7783
|
+
const scanPhrase = status !== void 0 && status.phase === "running" ? "仍在扫描" : pendingSessions > 0 ? `还有 ${pendingSessions} 个会话待补扫` : "扫描已完成";
|
|
7784
|
+
const statusSentence = `已索引 ${metrics.sessionsListed ?? "—"} 个会话 · 覆盖${coverageLabel} · ${scanPhrase}`;
|
|
7736
7785
|
const coverageHint = exact === true ? (0, react.createElement)("span", { style: {
|
|
7737
7786
|
display: "inline-flex",
|
|
7738
7787
|
alignItems: "center",
|
|
@@ -7773,11 +7822,6 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7773
7822
|
const failingTop = failingRows === void 0 ? [] : failingRows.slice(0, 3);
|
|
7774
7823
|
const failingRest = failingRows === void 0 ? [] : failingRows.slice(3);
|
|
7775
7824
|
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
7825
|
const toolsNotice = tools === void 0 ? (0, react.createElement)("p", {
|
|
7782
7826
|
className: "v3-hint",
|
|
7783
7827
|
"data-call-index-note": "tools"
|
|
@@ -7795,24 +7839,23 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7795
7839
|
className: "v3-root mc-section",
|
|
7796
7840
|
"aria-label": "调用统计总览",
|
|
7797
7841
|
"aria-busy": loading === true
|
|
7798
|
-
}, CallIndexStyles(), V3Styles(), (0, react.createElement)("
|
|
7842
|
+
}, CallIndexStyles(), V3Styles(), (0, react.createElement)("div", { style: {
|
|
7843
|
+
display: "flex",
|
|
7844
|
+
justifyContent: "flex-end"
|
|
7845
|
+
} }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
7799
7846
|
size: "sm",
|
|
7800
7847
|
variant: "outline",
|
|
7801
7848
|
disabled: loading === true || onRefresh === void 0,
|
|
7802
7849
|
onClick: () => onRefresh?.()
|
|
7803
|
-
}, "刷新"))
|
|
7850
|
+
}, "刷新")), notice === void 0 ? null : (0, react.createElement)("div", {
|
|
7804
7851
|
className: "mc-notice",
|
|
7805
7852
|
role: failure?.disconnected === true ? "alert" : "status",
|
|
7806
7853
|
"data-call-index-state": failure?.disconnected === true ? "disconnected" : "failure"
|
|
7807
7854
|
}, 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
7855
|
className: "v3-dot",
|
|
7813
7856
|
"data-tone": scanning ? "run" : "ok",
|
|
7814
7857
|
"aria-hidden": "true"
|
|
7815
|
-
}),
|
|
7858
|
+
}), (0, react.createElement)("span", null, statusSentence))), (0, react.createElement)("div", {
|
|
7816
7859
|
className: "v3-stats",
|
|
7817
7860
|
"data-call-index-metrics": "overview"
|
|
7818
7861
|
}, V3Stat({
|
|
@@ -7843,11 +7886,7 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7843
7886
|
label: "不同工具",
|
|
7844
7887
|
value: formatCallIndexCount(metrics.distinctTools, exact),
|
|
7845
7888
|
hint: "按 toolName 去重"
|
|
7846
|
-
})),
|
|
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", {
|
|
7889
|
+
})), (0, react.createElement)("div", {
|
|
7851
7890
|
className: "v3-board",
|
|
7852
7891
|
style: { marginTop: 12 }
|
|
7853
7892
|
}, (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "按日调用趋势"), (0, react.createElement)("span", { className: "v3-pn" }, aggregates === void 0 ? "趋势不可用" : `合计 ${trend.totalCalls} 次 · 峰值 ${trend.maxCalls} 次`)), aggregates === void 0 ? (0, react.createElement)("p", {
|
|
@@ -7865,7 +7904,7 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7865
7904
|
height: 8,
|
|
7866
7905
|
borderRadius: 2
|
|
7867
7906
|
} }), "最近一周"))), OverviewSourcePanel({
|
|
7868
|
-
...
|
|
7907
|
+
...tools === void 0 ? {} : { tools },
|
|
7869
7908
|
...exact === void 0 ? {} : { totalExact: exact }
|
|
7870
7909
|
})), (0, react.createElement)("div", {
|
|
7871
7910
|
className: "v3-board",
|
|
@@ -7884,7 +7923,10 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7884
7923
|
...bad ? { "data-bad": "true" } : {},
|
|
7885
7924
|
...row.error === 0 ? { "data-zero": "true" } : {}
|
|
7886
7925
|
}, `失败 ${row.error}`));
|
|
7887
|
-
}))), (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "
|
|
7926
|
+
}))), (0, react.createElement)("div", { className: "v3-panel" }, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "工具调用失败"), (0, react.createElement)("span", { className: "v3-pn" }, tools === void 0 ? "按失败数排序" : `${formatCallIndexCount(metrics.failed, exact)} 次失败`)), toolsNotice !== null ? toolsNotice : failingRows === void 0 || failingRows.length === 0 ? (0, react.createElement)("p", { className: "v3-hint" }, "当前范围没有失败调用。") : (0, react.createElement)("div", {
|
|
7927
|
+
className: "v3-rank",
|
|
7928
|
+
"data-call-index-failures": "true"
|
|
7929
|
+
}, ...failingTop.map((row) => (0, react.createElement)("div", {
|
|
7888
7930
|
key: "fc-" + row.toolName + "::" + row.sourceKind,
|
|
7889
7931
|
className: "v3-rank-row"
|
|
7890
7932
|
}, (0, react.createElement)("span", {
|
|
@@ -7899,7 +7941,7 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7899
7941
|
color: "var(--v3-bad)",
|
|
7900
7942
|
fontWeight: 600
|
|
7901
7943
|
}
|
|
7902
|
-
},
|
|
7944
|
+
}, `${row.error} 次`))), ...failingRest.length === 0 ? [] : [(0, react.createElement)("div", {
|
|
7903
7945
|
key: "fc-rest",
|
|
7904
7946
|
className: "v3-rank-row"
|
|
7905
7947
|
}, (0, react.createElement)("span", {
|
|
@@ -7911,42 +7953,7 @@ function CallIndexOverviewPanelView(props) {
|
|
|
7911
7953
|
}, `其余 ${failingRest.length} 个工具合计`), (0, react.createElement)("span", {
|
|
7912
7954
|
className: "v3-n",
|
|
7913
7955
|
style: { color: "var(--v3-t2)" }
|
|
7914
|
-
},
|
|
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", {
|
|
7956
|
+
}, `${failingRestErrors} 次`))]))), (0, react.createElement)("div", {
|
|
7950
7957
|
className: "v3-panel",
|
|
7951
7958
|
style: { marginTop: 12 }
|
|
7952
7959
|
}, (0, react.createElement)("div", { className: "v3-ph" }, (0, react.createElement)("span", { className: "v3-pt" }, "索引状态"), (0, react.createElement)("span", { className: "v3-pn" }, metrics.tasks === void 0 ? "最近扫描未记录" : `最近扫描 ${metrics.tasks.mode} · ${metrics.tasks.phase}`)), (0, react.createElement)("div", { className: "v3-kv" }, (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "数据范围"), (0, react.createElement)("div", { className: "v3-kv-v" }, overview?.corpus === "persisted-plus-observed-live" ? "已持久化 + 挂载后实时观测" : "Host 未声明")), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "Session 列出 / 可见"), (0, react.createElement)("div", { className: "v3-kv-v v3-num" }, sessionInfo === void 0 ? "—" : `${sessionInfo.listed} / ${sessionInfo.visible}`)), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "待处理 / 失败"), (0, react.createElement)("div", { className: "v3-kv-v v3-num" }, sessionInfo === void 0 ? "—" : `${sessionInfo.pending} / ${sessionInfo.failed}`)), (0, react.createElement)("div", null, (0, react.createElement)("div", { className: "v3-kv-l" }, "覆盖(事件 / 枚举 / 语义)"), (0, react.createElement)("div", { className: "v3-kv-v" }, status === void 0 ? "—" : `${COVERAGE_LABELS[status.eventCoverage]} / ${COVERAGE_LABELS[status.enumerationCoverage]} / ${COVERAGE_LABELS[status.coverage]}`))), taskNote));
|
|
@@ -8007,6 +8014,14 @@ function detailAfterRefresh(previous, page) {
|
|
|
8007
8014
|
detailFailure: void 0
|
|
8008
8015
|
};
|
|
8009
8016
|
}
|
|
8017
|
+
/** Pure policy decision, so the container's refresh strategy is testable. */
|
|
8018
|
+
function callIndexRefreshPolicy(status) {
|
|
8019
|
+
const scanning = status !== void 0 && (status.phase === "running" || status.sessions.pending > 0 || status.sessions.pendingLive > 0);
|
|
8020
|
+
return {
|
|
8021
|
+
scanning,
|
|
8022
|
+
mode: scanning ? "status-only" : "full"
|
|
8023
|
+
};
|
|
8024
|
+
}
|
|
8010
8025
|
/**
|
|
8011
8026
|
* Container for 调用明细: owns the query, the paging cursor and the detail read.
|
|
8012
8027
|
* Keeps the previous page visible while the next one loads, so a range click
|
|
@@ -8082,23 +8097,35 @@ function CallIndexDetailPanel({ api, initialQuery, resolveScopeTarget, resolveSe
|
|
|
8082
8097
|
refreshToken
|
|
8083
8098
|
]);
|
|
8084
8099
|
/**
|
|
8085
|
-
*
|
|
8100
|
+
* Refresh status/progress while a scan is running — WITHOUT re-reading calls.
|
|
8086
8101
|
*
|
|
8087
|
-
* The
|
|
8088
|
-
*
|
|
8089
|
-
*
|
|
8090
|
-
*
|
|
8102
|
+
* The old loop bumped `refreshToken`, which re-read status + calls + sessions
|
|
8103
|
+
* every 2s and replaced the data page under the operator mid-scan. Now it polls
|
|
8104
|
+
* `getStatus` only, keeps the current rows untouched, and triggers ONE full read
|
|
8105
|
+
* when the scan finishes (or on the explicit 刷新 button).
|
|
8091
8106
|
*/
|
|
8092
|
-
const scanningNow =
|
|
8107
|
+
const scanningNow = callIndexRefreshPolicy(state.status).scanning;
|
|
8093
8108
|
(0, react.useEffect)(() => {
|
|
8094
8109
|
if (!scanningNow) return;
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8110
|
+
let active = true;
|
|
8111
|
+
const id = setInterval(() => {
|
|
8112
|
+
(async () => {
|
|
8113
|
+
const result = await api.getStatus();
|
|
8114
|
+
if (!active || !result.ok) return;
|
|
8115
|
+
const next = result.value;
|
|
8116
|
+
const stillScanning = callIndexRefreshPolicy(next).scanning;
|
|
8117
|
+
setState((previous) => ({
|
|
8118
|
+
...previous,
|
|
8119
|
+
status: next
|
|
8120
|
+
}));
|
|
8121
|
+
if (!stillScanning) setRefreshToken((value) => value + 1);
|
|
8122
|
+
})();
|
|
8123
|
+
}, 2e3);
|
|
8124
|
+
return () => {
|
|
8125
|
+
active = false;
|
|
8126
|
+
clearInterval(id);
|
|
8127
|
+
};
|
|
8128
|
+
}, [scanningNow, api]);
|
|
8102
8129
|
/**
|
|
8103
8130
|
* Append the next page.
|
|
8104
8131
|
*
|
|
@@ -8330,6 +8357,28 @@ function CallIndexOverviewPanel({ api }) {
|
|
|
8330
8357
|
active = false;
|
|
8331
8358
|
};
|
|
8332
8359
|
}, [api, refreshToken]);
|
|
8360
|
+
const scanning = callIndexRefreshPolicy(state.status).scanning;
|
|
8361
|
+
(0, react.useEffect)(() => {
|
|
8362
|
+
if (!scanning) return;
|
|
8363
|
+
let active = true;
|
|
8364
|
+
const id = setInterval(() => {
|
|
8365
|
+
(async () => {
|
|
8366
|
+
const result = await api.getStatus();
|
|
8367
|
+
if (!active || !result.ok) return;
|
|
8368
|
+
const next = result.value;
|
|
8369
|
+
const stillScanning = callIndexRefreshPolicy(next).scanning;
|
|
8370
|
+
setState((previous) => ({
|
|
8371
|
+
...previous,
|
|
8372
|
+
status: next
|
|
8373
|
+
}));
|
|
8374
|
+
if (!stillScanning) setRefreshToken((value) => value + 1);
|
|
8375
|
+
})();
|
|
8376
|
+
}, 2e3);
|
|
8377
|
+
return () => {
|
|
8378
|
+
active = false;
|
|
8379
|
+
clearInterval(id);
|
|
8380
|
+
};
|
|
8381
|
+
}, [scanning, api]);
|
|
8333
8382
|
return (0, react.createElement)(CallIndexOverviewPanelView, {
|
|
8334
8383
|
...state.status === void 0 ? {} : { status: state.status },
|
|
8335
8384
|
...state.overview === void 0 ? {} : { overview: state.overview },
|
|
@@ -8468,27 +8517,24 @@ function OverviewContainer(props) {
|
|
|
8468
8517
|
/**
|
|
8469
8518
|
* 思考限制 — the Guard configuration surface (v3 · 数据看板, D-044).
|
|
8470
8519
|
*
|
|
8471
|
-
* Three sections, in
|
|
8472
|
-
*
|
|
8473
|
-
*
|
|
8474
|
-
*
|
|
8475
|
-
*
|
|
8476
|
-
* ties the two together, so it is displayed rather than hidden.
|
|
8520
|
+
* Three sections, in product order: repeated identical calls, alternating
|
|
8521
|
+
* cycles, no-progress protection. Each carries a model-side identifier
|
|
8522
|
+
* ('duplicate_call' / 'alternating_cycle' / 'no_progress') shown as plain text,
|
|
8523
|
+
* because the reminder injected into the conversation is English while the label
|
|
8524
|
+
* a user reads here is Chinese. The identifier ties the two together.
|
|
8477
8525
|
*
|
|
8478
|
-
* v3 SHAPE:
|
|
8479
|
-
*
|
|
8480
|
-
*
|
|
8481
|
-
*
|
|
8482
|
-
*
|
|
8483
|
-
* a switch, so the grid fills evenly.
|
|
8526
|
+
* v3 SHAPE: a two-column grid of .v3-gitem cards (>900px two columns, auto single
|
|
8527
|
+
* column via the container query in v3-ui.ts). The Guard counters sit at the very
|
|
8528
|
+
* top as a four-cell .v3-stats strip, followed by one short boundary hint. No page
|
|
8529
|
+
* title, no "activity" sub-title, no "counting since" line — the tab already says
|
|
8530
|
+
* what this page is.
|
|
8484
8531
|
*
|
|
8485
8532
|
* DOM CONTRACT kept for the shared test suite: nine <li class="mc-guard-point">
|
|
8486
8533
|
* whose text still comes from t(); the three data-guard-section values in order;
|
|
8487
|
-
*
|
|
8488
|
-
* four data-guard-stat cells and data-guard-stats
|
|
8534
|
+
* the threshold editor wrapped in data-guard-thresholds (now three number inputs,
|
|
8535
|
+
* values from the Host); the four data-guard-stat cells and data-guard-stats; and
|
|
8489
8536
|
* three role="switch" buttons whose aria-checked follows the props. No Chinese
|
|
8490
|
-
* colon (':') is introduced by this file — spacing
|
|
8491
|
-
* the separating instead.
|
|
8537
|
+
* colon (':') is introduced by this file — spacing does the separating instead.
|
|
8492
8538
|
*
|
|
8493
8539
|
* Kept hook-free and prop-driven so the settings test suite (no DOM harness) can
|
|
8494
8540
|
* walk the returned tree.
|
|
@@ -8496,13 +8542,12 @@ function OverviewContainer(props) {
|
|
|
8496
8542
|
/**
|
|
8497
8543
|
* Section id → localized title plus the lines that explain what the switch does.
|
|
8498
8544
|
*
|
|
8499
|
-
*
|
|
8545
|
+
* the descriptions are part of the contract, not decoration: a bare switch does
|
|
8500
8546
|
* not tell a reader whether turning it off stops reminders only or also stops
|
|
8501
8547
|
* local denials. Each bullet states the real behaviour the detector implements.
|
|
8502
8548
|
*
|
|
8503
8549
|
* 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.
|
|
8550
|
+
* shared suite asserts) but lays them out as one compact wrapped row per card.
|
|
8506
8551
|
*/
|
|
8507
8552
|
const SECTIONS = [
|
|
8508
8553
|
{
|
|
@@ -8535,9 +8580,10 @@ const SECTIONS = [
|
|
|
8535
8580
|
];
|
|
8536
8581
|
/**
|
|
8537
8582
|
* 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
|
|
8539
|
-
* threshold editor chrome. Colours are always v3
|
|
8540
|
-
* is hard-coded, so light and dark themes both
|
|
8583
|
+
* v3-ui.ts; the only local vocabulary here is the compact bullet row, the
|
|
8584
|
+
* threshold editor chrome and the per-card "fired" foot. Colours are always v3
|
|
8585
|
+
* semantic variables — nothing is hard-coded, so light and dark themes both
|
|
8586
|
+
* track the host.
|
|
8541
8587
|
*/
|
|
8542
8588
|
const GUARD_CSS = [
|
|
8543
8589
|
".mc-section.v3-root{display:flex;flex-direction:column;gap:12px}",
|
|
@@ -8547,13 +8593,14 @@ const GUARD_CSS = [
|
|
|
8547
8593
|
".v3-gitem--info{background:var(--v3-panel-2)}",
|
|
8548
8594
|
".v3-gitem .v3-gh .v3-sw{margin-left:auto}",
|
|
8549
8595
|
".v3-gfoot-edit{margin-top:9px}",
|
|
8550
|
-
".v3-guard-stats{margin:
|
|
8596
|
+
".v3-guard-stats{margin:0}",
|
|
8597
|
+
".v3-gfoot-note{color:var(--v3-t4)}",
|
|
8551
8598
|
".mc-guard-threshold-edit{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
|
|
8552
8599
|
".mc-guard-threshold-edit-label{font-size:11.5px;color:var(--v3-t3)}",
|
|
8553
|
-
".mc-guard-threshold-
|
|
8554
|
-
".mc-guard-threshold-
|
|
8555
|
-
".mc-guard-threshold-
|
|
8556
|
-
".mc-guard-threshold-
|
|
8600
|
+
".mc-guard-threshold-num{width:56px;padding:3px 6px;border-radius:var(--v3-r-sm);font-size:12px;text-align:center;font-family:var(--v3-font-code);color:var(--v3-t1);background:var(--v3-panel-2);border:1px solid var(--v3-line)}",
|
|
8601
|
+
".mc-guard-threshold-num:focus-visible{outline:2px solid var(--v3-info);outline-offset:1px}",
|
|
8602
|
+
".mc-guard-threshold-num:disabled{opacity:.6}",
|
|
8603
|
+
".mc-guard-threshold-hint{margin:6px 0 0}"
|
|
8557
8604
|
].join("");
|
|
8558
8605
|
/** '—' for a counter the Host did not report; never a fabricated 0. */
|
|
8559
8606
|
function statText(value) {
|
|
@@ -8563,10 +8610,11 @@ function statText(value) {
|
|
|
8563
8610
|
* The Guard's real activity board, restyled as a v3 stat strip.
|
|
8564
8611
|
*
|
|
8565
8612
|
* Answers the two questions the page previously could not: 「它是否在工作」
|
|
8566
|
-
* (observations
|
|
8567
|
-
*
|
|
8613
|
+
* (observations) and 「调用成功还是失败」 (outcome split). Every number comes
|
|
8614
|
+
* from the Host; a missing counter renders '—', so an older Host reads as
|
|
8568
8615
|
* "unknown" rather than "zero". The four data-guard-stat cells and the
|
|
8569
|
-
* data-guard-stats
|
|
8616
|
+
* data-guard-stats hook are the published contract. No sub-title and no
|
|
8617
|
+
* "counting since" line: the strip is the first thing on the page.
|
|
8570
8618
|
*/
|
|
8571
8619
|
function GuardStatsBoard(props) {
|
|
8572
8620
|
const { t, stats } = props;
|
|
@@ -8576,35 +8624,21 @@ function GuardStatsBoard(props) {
|
|
|
8576
8624
|
className: "v3-stat",
|
|
8577
8625
|
"data-guard-stat": label
|
|
8578
8626
|
}, (0, react.createElement)("div", { className: "v3-stat-l" }, label), (0, react.createElement)("div", { className: "v3-stat-v" }, value), hint === void 0 ? null : (0, react.createElement)("div", { className: "v3-stat-d" }, hint));
|
|
8579
|
-
return (0, react.createElement)("div", { className: "v3-guard-stats" }, (0, react.createElement)("div", {
|
|
8627
|
+
return (0, react.createElement)("div", { className: "v3-guard-stats" }, (0, react.createElement)("div", {
|
|
8580
8628
|
className: "v3-stats",
|
|
8581
8629
|
"data-guard-stats": "true",
|
|
8582
8630
|
role: "group",
|
|
8583
8631
|
"aria-label": t("guardStatsTitle")
|
|
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")))
|
|
8585
|
-
className: "v3-src-note",
|
|
8586
|
-
"data-guard-stats-since": "true"
|
|
8587
|
-
}, t("guardStatsSince") + " " + formatGuardTime(stats.since)));
|
|
8588
|
-
}
|
|
8589
|
-
/** 'YYYY-MM-DD HH:mm' in the browser zone; '—' when unknown. */
|
|
8590
|
-
function formatGuardTime(time) {
|
|
8591
|
-
if (time === void 0 || !Number.isFinite(time)) return "—";
|
|
8592
|
-
const date = new Date(time);
|
|
8593
|
-
if (Number.isNaN(date.getTime())) return "—";
|
|
8594
|
-
const pad = (value) => String(value).padStart(2, "0");
|
|
8595
|
-
return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()) + " " + pad(date.getHours()) + ":" + pad(date.getMinutes());
|
|
8632
|
+
}, cell(t("guardStatObservations"), statText(stats?.observations), t("guardStatObservationsHint")), cell(t("guardStatOutcomes"), statText(outcomes?.success) + " / " + statText(outcomes?.error), t("guardStatOutcomesHint")), cell(t("guardStatReminders"), statText(reminders?.repeat) + " / " + statText(reminders?.cycle), t("guardStatRemindersHint")), cell(t("guardStatDenials"), statText(stats?.denials), t("guardStatDenialsHint"))));
|
|
8596
8633
|
}
|
|
8597
8634
|
/**
|
|
8598
8635
|
* Editable reminder thresholds.
|
|
8599
8636
|
*
|
|
8600
|
-
*
|
|
8601
|
-
*
|
|
8602
|
-
*
|
|
8603
|
-
*
|
|
8604
|
-
*
|
|
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).
|
|
8637
|
+
* Three plain number inputs instead of one comma-separated text field: the
|
|
8638
|
+
* operator sees one box per firing point and never has to learn a syntax. Values
|
|
8639
|
+
* always come from the Host; a keystroke is assembled into a fresh array and
|
|
8640
|
+
* handed to onThresholdsChange, and an older Host that does not accept a write
|
|
8641
|
+
* gets disabled boxes. data-guard-thresholds marks the wrapper.
|
|
8608
8642
|
*/
|
|
8609
8643
|
function GuardThresholdEditor(props) {
|
|
8610
8644
|
const { t, thresholds, onChange } = props;
|
|
@@ -8613,52 +8647,32 @@ function GuardThresholdEditor(props) {
|
|
|
8613
8647
|
return (0, react.createElement)("div", {
|
|
8614
8648
|
className: "mc-guard-threshold-edit",
|
|
8615
8649
|
"data-guard-thresholds": "true"
|
|
8616
|
-
}, (0, react.createElement)("label", {
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
value: thresholds.join(", "),
|
|
8650
|
+
}, (0, react.createElement)("label", { className: "mc-guard-threshold-edit-label" }, t("guardThresholds")), ...thresholds.map((value, index) => (0, react.createElement)("input", {
|
|
8651
|
+
key: index,
|
|
8652
|
+
type: "number",
|
|
8653
|
+
min: 2,
|
|
8654
|
+
max: 50,
|
|
8655
|
+
step: 1,
|
|
8656
|
+
className: "mc-guard-threshold-num",
|
|
8657
|
+
value: String(value),
|
|
8625
8658
|
readOnly: !editable,
|
|
8626
8659
|
disabled: !editable,
|
|
8627
|
-
"aria-label": t("guardThresholds"),
|
|
8660
|
+
"aria-label": t("guardThresholds") + " " + String(index + 1),
|
|
8628
8661
|
onChange: (event) => {
|
|
8629
8662
|
if (!editable) return;
|
|
8630
|
-
const
|
|
8631
|
-
if (
|
|
8632
|
-
|
|
8663
|
+
const raw = Number(event.target.value);
|
|
8664
|
+
if (!Number.isSafeInteger(raw) || raw < 2 || raw > 50) return;
|
|
8665
|
+
const next = thresholds.slice();
|
|
8666
|
+
next[index] = raw;
|
|
8667
|
+
onChange(next);
|
|
8633
8668
|
}
|
|
8634
|
-
})
|
|
8635
|
-
}
|
|
8636
|
-
/**
|
|
8637
|
-
* Parse '3, 5, 8' into a threshold list.
|
|
8638
|
-
*
|
|
8639
|
-
* Returns 'undefined' for anything the Host would reject (empty, non-integer,
|
|
8640
|
-
* out of range, not strictly increasing). Parsing here as well as on the Host is
|
|
8641
|
-
* deliberate: it keeps an invalid keystroke from round-tripping through the
|
|
8642
|
-
* remote and flickering the field.
|
|
8643
|
-
*/
|
|
8644
|
-
function parseGuardThresholds(text) {
|
|
8645
|
-
const parts = text.split(",").map((part) => part.trim()).filter((part) => part !== "");
|
|
8646
|
-
if (parts.length === 0 || parts.length > 5) return void 0;
|
|
8647
|
-
const parsed = [];
|
|
8648
|
-
for (const part of parts) {
|
|
8649
|
-
if (!/^\d+$/.test(part)) return void 0;
|
|
8650
|
-
const value = Number(part);
|
|
8651
|
-
if (!Number.isSafeInteger(value) || value < 2 || value > 50) return void 0;
|
|
8652
|
-
if (parsed.length > 0 && value <= parsed[parsed.length - 1]) return void 0;
|
|
8653
|
-
parsed.push(value);
|
|
8654
|
-
}
|
|
8655
|
-
return parsed;
|
|
8669
|
+
})));
|
|
8656
8670
|
}
|
|
8657
|
-
/** The
|
|
8658
|
-
function
|
|
8659
|
-
if (id === "
|
|
8660
|
-
if (id === "
|
|
8661
|
-
return
|
|
8671
|
+
/** The counter a card reports as "fired", per the product mapping. */
|
|
8672
|
+
function firedValue(id, stats) {
|
|
8673
|
+
if (id === "duplicate_call") return stats?.reminders?.repeat;
|
|
8674
|
+
if (id === "alternating_cycle") return stats?.reminders?.cycle;
|
|
8675
|
+
return stats?.denials;
|
|
8662
8676
|
}
|
|
8663
8677
|
function GuardPanel(props) {
|
|
8664
8678
|
const { t, enabled, duplicateThresholds, onToggle, onThresholdsChange, stats, onReset } = props;
|
|
@@ -8673,6 +8687,12 @@ function GuardPanel(props) {
|
|
|
8673
8687
|
className: "v3-dot",
|
|
8674
8688
|
"aria-hidden": "true"
|
|
8675
8689
|
});
|
|
8690
|
+
const belowBullets = entry.id === "duplicate_call" ? (0, react.createElement)("div", { className: "v3-gfoot-edit" }, (0, react.createElement)(GuardThresholdEditor, {
|
|
8691
|
+
t,
|
|
8692
|
+
thresholds: duplicateThresholds,
|
|
8693
|
+
...onThresholdsChange === void 0 ? {} : { onChange: onThresholdsChange }
|
|
8694
|
+
}), (0, react.createElement)("p", { className: "v3-hint mc-guard-threshold-hint" }, t("guardThresholdHint"))) : (0, react.createElement)("p", { className: "v3-hint" }, t(entry.id === "alternating_cycle" ? "guardPurposeB" : "guardPurposeC"));
|
|
8695
|
+
const footNote = entry.id === "alternating_cycle" ? (0, react.createElement)("span", { className: "v3-gfoot-note" }, t("guardFootCycle")) : entry.id === "no_progress" ? (0, react.createElement)("span", { className: "v3-gfoot-note" }, t("guardFootNoProgress")) : null;
|
|
8676
8696
|
return (0, react.createElement)("div", {
|
|
8677
8697
|
key: entry.id,
|
|
8678
8698
|
className: "v3-gitem",
|
|
@@ -8688,27 +8708,23 @@ function GuardPanel(props) {
|
|
|
8688
8708
|
})), (0, react.createElement)("ul", { className: "v3-gpoints" }, ...entry.bodyKeys.map((key) => (0, react.createElement)("li", {
|
|
8689
8709
|
key,
|
|
8690
8710
|
className: "mc-guard-point"
|
|
8691
|
-
}, t(key)))),
|
|
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, {
|
|
8695
|
-
t,
|
|
8696
|
-
thresholds: duplicateThresholds,
|
|
8697
|
-
...onThresholdsChange === void 0 ? {} : { onChange: onThresholdsChange }
|
|
8698
|
-
})) : null);
|
|
8711
|
+
}, t(key)))), belowBullets, (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardFired")), (0, react.createElement)("span", { className: "v3-kbd" }, statText(firedValue(entry.id, stats))), footNote));
|
|
8699
8712
|
};
|
|
8700
8713
|
const scopeCell = (0, react.createElement)("div", {
|
|
8701
8714
|
key: "scope",
|
|
8702
8715
|
className: "v3-gitem v3-gitem--info"
|
|
8703
8716
|
}, (0, react.createElement)("div", { className: "v3-gh" }, (0, react.createElement)("span", { className: "v3-gnm" }, t("guardScopeTitle"))), (0, react.createElement)("p", null, t("guardScopeBody")), (0, react.createElement)("div", { className: "v3-gfoot" }, (0, react.createElement)("span", null, t("guardScopeFoot"))));
|
|
8704
|
-
return (0, react.createElement)("section", { className: "mc-section v3-root" }, V3Styles(), (0, react.createElement)("style", { "data-guard-style": "true" }, GUARD_CSS),
|
|
8717
|
+
return (0, react.createElement)("section", { className: "mc-section v3-root" }, V3Styles(), (0, react.createElement)("style", { "data-guard-style": "true" }, GUARD_CSS), onReset === void 0 ? null : (0, react.createElement)("div", { style: {
|
|
8718
|
+
display: "flex",
|
|
8719
|
+
justifyContent: "flex-end"
|
|
8720
|
+
} }, (0, react.createElement)("button", {
|
|
8705
8721
|
type: "button",
|
|
8706
8722
|
className: "mc-count mc-count--action",
|
|
8707
8723
|
onClick: onReset
|
|
8708
|
-
}, t("guardReset")))
|
|
8724
|
+
}, t("guardReset"))), (0, react.createElement)(GuardStatsBoard, {
|
|
8709
8725
|
t,
|
|
8710
8726
|
...stats === void 0 ? {} : { stats }
|
|
8711
|
-
}), (0, react.createElement)("div", { className: "v3-gpanel" }, ...SECTIONS.map(section), scopeCell));
|
|
8727
|
+
}), (0, react.createElement)("p", { className: "v3-hint" }, t("guardBoundaryShort")), (0, react.createElement)("div", { className: "v3-gpanel" }, ...SECTIONS.map(section), scopeCell));
|
|
8712
8728
|
}
|
|
8713
8729
|
//#endregion
|
|
8714
8730
|
//#region src/client/GuardContainer.tsx
|
|
@@ -9033,6 +9049,19 @@ function PluginSection(props) {
|
|
|
9033
9049
|
live = false;
|
|
9034
9050
|
};
|
|
9035
9051
|
}, [callIndexApi]);
|
|
9052
|
+
/**
|
|
9053
|
+
* Reset the settings content scroller when the category changes.
|
|
9054
|
+
*
|
|
9055
|
+
* The shell owns the scrolling column and swaps panels with `hidden`, so the
|
|
9056
|
+
* previous tab's scrollTop survives the switch and the next page opens halfway
|
|
9057
|
+
* down (observed: 执行保护 opened below its counter board). Reset the nearest
|
|
9058
|
+
* scrollable ancestor of the active panel.
|
|
9059
|
+
*/
|
|
9060
|
+
(0, react.useEffect)(() => {
|
|
9061
|
+
let node = document.getElementById("mc-category-panel-" + active);
|
|
9062
|
+
while (node !== null && !(node.scrollHeight > node.clientHeight + 4)) node = node.parentElement;
|
|
9063
|
+
if (node !== null) node.scrollTop = 0;
|
|
9064
|
+
}, [active]);
|
|
9036
9065
|
return (0, react.createElement)(PluginSectionView, {
|
|
9037
9066
|
capability: props.capability,
|
|
9038
9067
|
usageApi: props.usageApi,
|
|
@@ -9173,6 +9202,11 @@ async function apply(ctx) {
|
|
|
9173
9202
|
guardScopeBody: "三个开关按 Agent / Turn 隔离,不跨 Session 继承;关闭只停止对应的提醒与局部拒绝,不改动任何配置或历史。",
|
|
9174
9203
|
guardScopeFoot: "恢复默认只写回开关值,不重写其他配置",
|
|
9175
9204
|
guardBoundaryLabel: "边界",
|
|
9205
|
+
guardBoundaryShort: "这里的数字只用于展示,不构成硬停止;三档阈值只参与校验与计数,不产生提醒也不停止任务。",
|
|
9206
|
+
guardThresholdHint: "默认 3 / 5 / 8 次;改的是第几次重复时提醒,只提醒、不拦截。",
|
|
9207
|
+
guardFired: "实际触发",
|
|
9208
|
+
guardPurposeB: "作用:识别 A→B→A→B 这类交替循环并提醒,避免来回空转。",
|
|
9209
|
+
guardPurposeC: "作用:证据明确时拦下已证实的重复操作,其余情况只提醒。",
|
|
9176
9210
|
guardFootCycle: "命中后只给出说明,不阻断调用",
|
|
9177
9211
|
guardFootNoProgress: "降级条件 日志缺失 · 结果不可比较 · 识别不确定",
|
|
9178
9212
|
guardSectionA: "A · 相同调用重复提醒",
|
|
@@ -9223,7 +9257,7 @@ async function apply(ctx) {
|
|
|
9223
9257
|
emptyGlobalSkills: "暂无全局技能",
|
|
9224
9258
|
emptyProjectSkills: "暂无项目技能",
|
|
9225
9259
|
emptyPresetSkills: "暂无预设技能",
|
|
9226
|
-
filterByName: "
|
|
9260
|
+
filterByName: "筛选名称或分组",
|
|
9227
9261
|
filterHint: "忽略大小写。匹配工具/技能名称、MCP 服务器、技能来源标签与预设 ID;也可以输入正则表达式,例如 cordis|ptc 或 /^mcp__dbx/。",
|
|
9228
9262
|
filterNoMatch: "没有匹配的能力(清空过滤框可恢复完整列表)",
|
|
9229
9263
|
emptyTools: "暂无工具",
|
|
@@ -9326,7 +9360,7 @@ async function apply(ctx) {
|
|
|
9326
9360
|
summaryLimitInvalid: "请输入不小于 20 的整数",
|
|
9327
9361
|
summaryLimitDisabled: "检索信息存储未启用",
|
|
9328
9362
|
summaryLimitSaved: "检索描述上限已保存",
|
|
9329
|
-
diagnosticTitle: "
|
|
9363
|
+
diagnosticTitle: "能力诊断 · {count} 项",
|
|
9330
9364
|
diagnosticUnavailable: "不可调用:{name}(来源 {source}{preset})",
|
|
9331
9365
|
diagnosticShadowed: "同名覆盖:{name} 保留 {winner},隐藏 {hidden}",
|
|
9332
9366
|
diagnosticBroken: "损坏的预设:{id}({reason};受影响技能数量由 DSH 未提供)",
|
|
@@ -9379,6 +9413,11 @@ async function apply(ctx) {
|
|
|
9379
9413
|
guardScopeBody: "The three switches are isolated per Agent / turn and never inherited across Sessions; turning one off stops only its reminders and local denials, and changes no configuration or history.",
|
|
9380
9414
|
guardScopeFoot: "Restoring defaults writes only the switch values; no other configuration is rewritten.",
|
|
9381
9415
|
guardBoundaryLabel: "Boundary",
|
|
9416
|
+
guardBoundaryShort: "These numbers are informational only and never stop a task; the three tier thresholds participate in validation and counting only.",
|
|
9417
|
+
guardThresholdHint: "Defaults are 3 / 5 / 8; editing changes when the reminder fires. Reminders never block a call.",
|
|
9418
|
+
guardFired: "Fired",
|
|
9419
|
+
guardPurposeB: "Purpose: detect alternating A→B→A→B cycles and remind, so the agent stops going in circles.",
|
|
9420
|
+
guardPurposeC: "Purpose: block a confirmed repeat when the evidence is clear; otherwise only remind.",
|
|
9382
9421
|
guardFootCycle: "Fires an explanation only; it never blocks the call.",
|
|
9383
9422
|
guardFootNoProgress: "Degrades when: logs missing · results incomparable · match uncertain",
|
|
9384
9423
|
guardSectionA: "A · Repeated identical calls",
|
|
@@ -9429,7 +9468,7 @@ async function apply(ctx) {
|
|
|
9429
9468
|
emptyGlobalSkills: "No global skills",
|
|
9430
9469
|
emptyProjectSkills: "No project skills",
|
|
9431
9470
|
emptyPresetSkills: "No preset skills",
|
|
9432
|
-
filterByName: "Filter
|
|
9471
|
+
filterByName: "Filter name or group",
|
|
9433
9472
|
filterHint: "Case-insensitive. Matches tool/skill names, MCP servers, skill source labels and preset ids; a regular expression works too, e.g. cordis|ptc or /^mcp__dbx/.",
|
|
9434
9473
|
filterNoMatch: "No capability matches (clear the filter to see the full list)",
|
|
9435
9474
|
emptyTools: "No tools",
|