mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-07 13:07:57 +02:00
Add files via upload
This commit is contained in:
@@ -9574,6 +9574,369 @@ html[data-theme="dark"] .openapi-doc-btn:hover {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* MCP 监控统计重构:工具卡 + 柱线趋势 */
|
||||
.mcp-stats-combined {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.mcp-stats-combined__head {
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
.mcp-stats-combined__body--full {
|
||||
min-height: 248px;
|
||||
}
|
||||
|
||||
.mcp-stats-combined__main {
|
||||
padding: 14px 16px 16px;
|
||||
}
|
||||
|
||||
.mcp-stats-combined__body--full .mcp-stats-combined__timeline {
|
||||
gap: 8px;
|
||||
padding: 14px 16px 12px;
|
||||
}
|
||||
|
||||
.mcp-stats-tools-panel {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mcp-stats-proportion-bar {
|
||||
height: 12px;
|
||||
box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.mcp-stats-tools-panel__caption {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.mcp-stats-tools-panel__list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
min-height: 132px;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.16);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(248, 250, 252, 0.92), rgba(255, 255, 255, 0.96));
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8) inset;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item:hover,
|
||||
.mcp-stats-tool-item.is-highlighted {
|
||||
background:
|
||||
linear-gradient(180deg, rgba(239, 246, 255, 0.96), rgba(255, 255, 255, 0.98));
|
||||
border-color: rgba(59, 130, 246, 0.26);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item.is-active {
|
||||
background: rgba(59, 130, 246, 0.08);
|
||||
border-color: rgba(59, 130, 246, 0.38);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__top,
|
||||
.mcp-stats-tool-item__middle,
|
||||
.mcp-stats-tool-item__bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__top {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__rank {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__name {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__share {
|
||||
margin-left: auto;
|
||||
padding: 2px 7px;
|
||||
border-radius: 999px;
|
||||
background: rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__middle {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__calls {
|
||||
font-size: 1.45rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__calls-label {
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__track {
|
||||
flex: 1 1 auto;
|
||||
width: auto;
|
||||
height: 6px;
|
||||
background: rgba(15, 23, 42, 0.07);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__bottom {
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 20px;
|
||||
padding: 2px 7px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
background: rgba(148, 163, 184, 0.13);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__pill.is-success {
|
||||
color: #15803d;
|
||||
background: rgba(34, 197, 94, 0.13);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__pill.is-danger {
|
||||
color: #b91c1c;
|
||||
background: rgba(239, 68, 68, 0.12);
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__rate {
|
||||
margin-left: auto;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mcp-stats-tool-item__fail {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moments {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
margin: 2px 0 0;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moments__label {
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moments__list {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
max-width: 144px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(59, 130, 246, 0.08);
|
||||
color: #1d4ed8;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment__time {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment__count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
padding: 0 5px;
|
||||
border-radius: 999px;
|
||||
color: #fff;
|
||||
background: #2563eb;
|
||||
box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment__fail {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 18px;
|
||||
padding: 0 6px;
|
||||
border-radius: 999px;
|
||||
color: #b91c1c;
|
||||
background: rgba(239, 68, 68, 0.12);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-moment--more {
|
||||
color: var(--text-secondary);
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline__chart-wrap {
|
||||
min-height: 146px;
|
||||
padding: 4px 0 0;
|
||||
}
|
||||
|
||||
.mcp-stats-combined__timeline .mcp-stats-timeline__chart-wrap,
|
||||
.mcp-stats-combined__timeline .mcp-stats-timeline__chart {
|
||||
min-height: 132px;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-bar {
|
||||
fill: rgba(59, 130, 246, 0.14);
|
||||
cursor: crosshair;
|
||||
transition: fill 0.12s ease, opacity 0.12s ease;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-bar.is-active {
|
||||
fill: rgba(59, 130, 246, 0.42);
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-bar.is-hover {
|
||||
fill: rgba(37, 99, 235, 0.68);
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-bar-fail {
|
||||
fill: rgba(239, 68, 68, 0.72);
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-bar-fail.is-hover {
|
||||
fill: rgba(220, 38, 38, 0.9);
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-area {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-line {
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mcp-stats-timeline-dot {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.mcp-stats-tools-panel__list {
|
||||
grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.mcp-stats-tools-panel__list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.mcp-stats-combined__main {
|
||||
max-width: none;
|
||||
}
|
||||
.mcp-stats-combined__timeline {
|
||||
max-width: none !important;
|
||||
}
|
||||
.mcp-stats-timeline-moments {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
.mcp-stats-timeline-moments__list {
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item {
|
||||
background: linear-gradient(180deg, #111827 0%, #0f172a 100%);
|
||||
border-color: #263244;
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item:hover,
|
||||
html[data-theme="dark"] .mcp-stats-tool-item.is-highlighted {
|
||||
background: linear-gradient(180deg, #172033 0%, #111827 100%);
|
||||
border-color: rgba(96, 165, 250, 0.36);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item.is-active {
|
||||
background: rgba(96, 165, 250, 0.12);
|
||||
border-color: rgba(96, 165, 250, 0.46);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item__share,
|
||||
html[data-theme="dark"] .mcp-stats-tool-item__track {
|
||||
background: rgba(148, 163, 184, 0.12);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item__pill {
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item__pill.is-success {
|
||||
color: #86efac;
|
||||
background: rgba(34, 197, 94, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-tool-item__pill.is-danger,
|
||||
html[data-theme="dark"] .mcp-stats-timeline-moment__fail {
|
||||
color: #fca5a5;
|
||||
background: rgba(239, 68, 68, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-timeline-moment {
|
||||
color: #93c5fd;
|
||||
background: rgba(96, 165, 250, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-timeline-moment--more {
|
||||
color: var(--text-secondary);
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-timeline-bar {
|
||||
fill: rgba(96, 165, 250, 0.16);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-timeline-bar.is-active {
|
||||
fill: rgba(96, 165, 250, 0.46);
|
||||
}
|
||||
|
||||
html[data-theme="dark"] .mcp-stats-timeline-bar.is-hover {
|
||||
fill: rgba(96, 165, 250, 0.78);
|
||||
}
|
||||
|
||||
/* ── 工具统计面板:表格 + 饼图 ── */
|
||||
.mcp-stats-panel {
|
||||
background: #fff;
|
||||
|
||||
+69
-21
@@ -4377,6 +4377,8 @@ function buildMcpTimelineSvg(points, rangeKey) {
|
||||
const maxVal = Math.max(1, ...points.map((p) => p.total || 0));
|
||||
const hasFailed = points.some((p) => (p.failed || 0) > 0);
|
||||
const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
|
||||
const barGap = points.length > 48 ? 1 : 2;
|
||||
const barW = Math.max(1.6, Math.min(8, (plotW / Math.max(1, points.length)) - barGap));
|
||||
|
||||
const coords = points.map((p, i) => {
|
||||
const x = padL + (points.length <= 1 ? plotW / 2 : (i / (points.length - 1)) * plotW);
|
||||
@@ -4433,6 +4435,21 @@ function buildMcpTimelineSvg(points, rangeKey) {
|
||||
data-failed="${c.p.failed || 0}" />`;
|
||||
}).join('');
|
||||
|
||||
const bars = coords.map((c) => {
|
||||
const total = c.p.total || 0;
|
||||
const failed = c.p.failed || 0;
|
||||
const h = total > 0 ? Math.max(3, (total / maxVal) * plotH) : 1;
|
||||
const y = baseY - h;
|
||||
const failedH = failed > 0 ? Math.max(2, (failed / maxVal) * plotH) : 0;
|
||||
const tipTime = formatMcpTimelineLabel(c.p.t, rangeKey, locale);
|
||||
return `<g class="mcp-stats-timeline-bar-group">
|
||||
<rect class="mcp-stats-timeline-bar${total > 0 ? ' is-active' : ''}" x="${(c.x - barW / 2).toFixed(2)}" y="${y.toFixed(2)}" width="${barW.toFixed(2)}" height="${h.toFixed(2)}" rx="1.6"
|
||||
data-time="${escapeHtml(tipTime)}" data-total="${total}" data-failed="${failed}" />
|
||||
${failedH > 0 ? `<rect class="mcp-stats-timeline-bar-fail" x="${(c.x - barW / 2).toFixed(2)}" y="${(baseY - failedH).toFixed(2)}" width="${barW.toFixed(2)}" height="${failedH.toFixed(2)}" rx="1.6"
|
||||
data-time="${escapeHtml(tipTime)}" data-total="${total}" data-failed="${failed}" />` : ''}
|
||||
</g>`;
|
||||
}).join('');
|
||||
|
||||
const peakC = coords[peakIdx];
|
||||
const peakMarker = (peakC.p.total || 0) > 0
|
||||
? `<circle class="mcp-stats-timeline-peak-glow" cx="${peakC.x.toFixed(2)}" cy="${peakC.y.toFixed(2)}" r="5" />`
|
||||
@@ -4453,6 +4470,7 @@ function buildMcpTimelineSvg(points, rangeKey) {
|
||||
</defs>
|
||||
${yLines}
|
||||
<path class="mcp-stats-timeline-area" d="${areaPath}" fill="url(#mcpTimelineAreaFill)" />
|
||||
${bars}
|
||||
${peakMarker}
|
||||
<path class="mcp-stats-timeline-line" d="${linePath}" stroke="url(#mcpTimelineLineStroke)" />
|
||||
${hasFailed ? `<path class="mcp-stats-timeline-line mcp-stats-timeline-line--fail" d="${failPath}" />` : ''}
|
||||
@@ -4484,14 +4502,17 @@ function bindMcpStatsTimelineEvents() {
|
||||
}
|
||||
|
||||
root.addEventListener('mousemove', function (e) {
|
||||
const dot = e.target.closest('.mcp-stats-timeline-dot');
|
||||
const dot = e.target.closest('.mcp-stats-timeline-dot, .mcp-stats-timeline-bar, .mcp-stats-timeline-bar-fail');
|
||||
if (!dot || !mcpTimelineTooltipEl) {
|
||||
root.querySelectorAll('.mcp-stats-timeline-dot.is-active').forEach((d) => d.classList.remove('is-active'));
|
||||
root.querySelectorAll('.mcp-stats-timeline-bar.is-hover, .mcp-stats-timeline-bar-fail.is-hover').forEach((d) => d.classList.remove('is-hover'));
|
||||
mcpTimelineTooltipEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
root.querySelectorAll('.mcp-stats-timeline-dot.is-active').forEach((d) => d.classList.remove('is-active'));
|
||||
root.querySelectorAll('.mcp-stats-timeline-bar.is-hover, .mcp-stats-timeline-bar-fail.is-hover').forEach((d) => d.classList.remove('is-hover'));
|
||||
dot.classList.add('is-active');
|
||||
dot.classList.add('is-hover');
|
||||
const time = dot.getAttribute('data-time') || '';
|
||||
const total = dot.getAttribute('data-total') || '0';
|
||||
const failed = dot.getAttribute('data-failed') || '0';
|
||||
@@ -4507,6 +4528,7 @@ function bindMcpStatsTimelineEvents() {
|
||||
if (!e.target.closest || !e.target.closest('.mcp-stats-combined__timeline, .mcp-stats-timeline')) return;
|
||||
if (e.relatedTarget && root.contains(e.relatedTarget)) return;
|
||||
root.querySelectorAll('.mcp-stats-timeline-dot.is-active').forEach((d) => d.classList.remove('is-active'));
|
||||
root.querySelectorAll('.mcp-stats-timeline-bar.is-hover, .mcp-stats-timeline-bar-fail.is-hover').forEach((d) => d.classList.remove('is-hover'));
|
||||
if (mcpTimelineTooltipEl) mcpTimelineTooltipEl.style.display = 'none';
|
||||
});
|
||||
|
||||
@@ -4568,6 +4590,37 @@ function buildTimelineSparseHint(points, timeline) {
|
||||
|| `该时段多数时间为 0,峰值 ${peak} 次出现在 ${peakTime}`;
|
||||
}
|
||||
|
||||
function renderMcpTimelineActiveMoments(points, rangeKey) {
|
||||
if (!Array.isArray(points) || points.length === 0) return '';
|
||||
const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
|
||||
const active = points
|
||||
.map((p, i) => ({ ...p, i }))
|
||||
.filter((p) => (p.total || 0) > 0)
|
||||
.sort((a, b) => (b.total || 0) - (a.total || 0) || b.i - a.i)
|
||||
const shown = active.slice(0, 4);
|
||||
const hiddenCount = Math.max(0, active.length - shown.length);
|
||||
if (!active.length) return '';
|
||||
const label = mcpMonitorT('timelineActiveMoments') || monitorFallback('活跃时段', 'Active moments');
|
||||
const moreLabel = mcpMonitorT('timelineMoreMoments', { n: hiddenCount }) || `+${hiddenCount}`;
|
||||
const chips = shown.map((p) => {
|
||||
const time = formatMcpTimelineLabel(p.t, rangeKey, locale);
|
||||
const failed = p.failed || 0;
|
||||
const failedLabel = mcpMonitorT('failedCount', { n: failed }) || `失败 ${failed}`;
|
||||
return `<span class="mcp-stats-timeline-moment" title="${escapeHtml(time)}">
|
||||
<span class="mcp-stats-timeline-moment__time">${escapeHtml(time)}</span>
|
||||
<span class="mcp-stats-timeline-moment__count">${p.total || 0}</span>
|
||||
${failed > 0 ? `<span class="mcp-stats-timeline-moment__fail">${escapeHtml(failedLabel)}</span>` : ''}
|
||||
</span>`;
|
||||
}).join('');
|
||||
const moreChip = hiddenCount > 0
|
||||
? `<span class="mcp-stats-timeline-moment mcp-stats-timeline-moment--more" title="${escapeHtml(mcpMonitorT('timelineMoreMomentsTitle', { n: hiddenCount }) || `还有 ${hiddenCount} 个活跃时段`)}">${escapeHtml(moreLabel)}</span>`
|
||||
: '';
|
||||
return `<div class="mcp-stats-timeline-moments">
|
||||
<span class="mcp-stats-timeline-moments__label">${escapeHtml(label)}</span>
|
||||
<div class="mcp-stats-timeline-moments__list">${chips}${moreChip}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
async function setMcpMonitorTimelineRange(range) {
|
||||
if (!MCP_TIMELINE_RANGES.includes(range)) return;
|
||||
localStorage.setItem('mcpMonitorTimelineRange', range);
|
||||
@@ -4643,12 +4696,14 @@ function renderMcpStatsTimelineBody(timeline, timelineError, compactEmpty, loadi
|
||||
const failLegend = mcpMonitorT('timelineFailedLegend') || '失败';
|
||||
const hasFailed = points.some((p) => (p.failed || 0) > 0);
|
||||
const sparseHint = buildTimelineSparseHint(points, timeline);
|
||||
const momentsHtml = renderMcpTimelineActiveMoments(points, rangeKey);
|
||||
const sparseHtml = sparseHint
|
||||
? `<p class="mcp-stats-timeline__sparse-hint">${escapeHtml(sparseHint)}</p>`
|
||||
: '';
|
||||
|
||||
return `
|
||||
<p class="mcp-stats-timeline__inline-meta">${escapeHtml(hint)} · ${escapeHtml(summaryText)}</p>
|
||||
${momentsHtml}
|
||||
<div class="mcp-stats-timeline__chart-wrap">${chartSvg}</div>
|
||||
${sparseHtml}
|
||||
<div class="mcp-stats-timeline__legend">
|
||||
@@ -5294,10 +5349,6 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
||||
const caption = mcpMonitorT('rankingSummary', { n: MCP_STATS_TOP_N, pct: topNSharePct, total: totals.total })
|
||||
|| `Top ${MCP_STATS_TOP_N} 占 ${topNSharePct}% · 共 ${totals.total} 次`;
|
||||
const unknownToolLabel = mcpMonitorT('unknownTool') || '未知工具';
|
||||
const colTool = mcpMonitorT('columnTool') || '工具';
|
||||
const colCalls = mcpMonitorT('columnCalls') || '调用';
|
||||
const colShare = mcpMonitorT('columnShare') || '占比';
|
||||
const colRate = mcpMonitorT('columnSuccessRate') || '成功率';
|
||||
const distAria = mcpMonitorT('distTitle') || '调用分布';
|
||||
|
||||
const stackedHtml = segments.map((s) => {
|
||||
@@ -5336,20 +5387,27 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
||||
const failNote = failed > 0
|
||||
? `<span class="mcp-stats-tool-item__fail">${escapeHtml(mcpMonitorT('failedCount', { n: failed }) || `失败 ${failed}`)}</span>`
|
||||
: '';
|
||||
const successLabel = mcpMonitorT('successCount', { n: success }) || `成功 ${success}`;
|
||||
const failedLabel = mcpMonitorT('failedCount', { n: failed }) || `失败 ${failed}`;
|
||||
return `<li class="mcp-stats-tool-item${isActive ? ' is-active' : ''}"
|
||||
data-tool-name="${escapeHtml(rawName)}" tabindex="0" role="button"
|
||||
aria-label="${escapeHtml(rowAria)}" aria-pressed="${isActive ? 'true' : 'false'}">
|
||||
<span class="mcp-stats-tool-item__rank mcp-stats-rank${rankClass}">${index + 1}</span>
|
||||
<span class="mcp-stats-tool-item__dot" style="background:${color}" aria-hidden="true"></span>
|
||||
<div class="mcp-stats-tool-item__body">
|
||||
<div class="mcp-stats-tool-item__top">
|
||||
<span class="mcp-stats-tool-item__rank mcp-stats-rank${rankClass}">${index + 1}</span>
|
||||
<span class="mcp-stats-tool-item__dot" style="background:${color}" aria-hidden="true"></span>
|
||||
<span class="mcp-stats-tool-item__name" title="${escapeHtml(name)}">${escapeHtml(name)}</span>
|
||||
<span class="mcp-stats-tool-item__share">${sharePct}%</span>
|
||||
</div>
|
||||
<div class="mcp-stats-tool-item__middle">
|
||||
<strong class="mcp-stats-tool-item__calls">${total}</strong>
|
||||
<span class="mcp-stats-tool-item__calls-label">${escapeHtml(mcpMonitorT('columnCalls') || '调用')}</span>
|
||||
<span class="mcp-stats-tool-item__track" aria-hidden="true">
|
||||
<span class="mcp-stats-tool-item__fill" style="width:${barPct}%;background:${color}"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mcp-stats-tool-item__metrics">
|
||||
<span class="mcp-stats-tool-item__share">${sharePct}%</span>
|
||||
<span class="mcp-stats-tool-item__calls">${total}</span>
|
||||
<div class="mcp-stats-tool-item__bottom">
|
||||
<span class="mcp-stats-tool-item__pill is-success">${escapeHtml(successLabel)}</span>
|
||||
<span class="mcp-stats-tool-item__pill${failed > 0 ? ' is-danger' : ''}">${escapeHtml(failedLabel)}</span>
|
||||
<span class="mcp-stats-tool-item__rate ${rateClass}">${toolRate}%${failNote}</span>
|
||||
</div>
|
||||
</li>`;
|
||||
@@ -5364,16 +5422,6 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
||||
${escapeHtml(caption)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="mcp-stats-tools-panel__list-head" aria-hidden="true">
|
||||
<span>#</span>
|
||||
<span></span>
|
||||
<span>${escapeHtml(colTool)}</span>
|
||||
<span class="mcp-stats-tool-item__metrics-head">
|
||||
<span>${escapeHtml(colShare)}</span>
|
||||
<span>${escapeHtml(colCalls)}</span>
|
||||
<span>${escapeHtml(colRate)}</span>
|
||||
</span>
|
||||
</div>
|
||||
<ol class="mcp-stats-tools-panel__list">${listHtml}</ol>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user