-
+
+
-
${title}
+
+ ${formatTokenCount(d.output)}
+ ${formatTokenCount(d.input)}
+
+
${d.label}
`;
}).join('');
@@ -284,17 +288,19 @@ function renderGlobalChart(stats: AllSessionsTokenStats): void {
输出 Token
输入 Token
- ${sessions.length > 20 ? `(仅显示最近 20 个会话)` : ''}
+ ${note ? `${note}` : ''}
-
-
- ${formatTokenCount(maxTokens)}
- ${formatTokenCount(Math.round(maxTokens / 2))}
- 0
-
+
+ ${gridLines}
${barsHtml}
`;
+
+ // 恢复滚动位置
+ if (savedScrollLeft && savedScrollLeft > 0) {
+ const newBarsEl = chartEl.querySelector('.td-chart-bars') as HTMLElement | null;
+ if (newBarsEl) newBarsEl.scrollLeft = savedScrollLeft;
+ }
}
/** 渲染全局明细表格 */
@@ -379,62 +385,22 @@ function renderSessionView(): void {
`;
- // 柱状图标题
- const chartTitleEl = dashboardModalEl.querySelector('#tdChartTitle');
- if (chartTitleEl) chartTitleEl.textContent = '📈 Token 消耗趋势 — 按轮次';
-
// 柱状图
const chartEl = dashboardModalEl.querySelector('#tdChart')!;
const barsEl = chartEl.querySelector('.td-chart-bars') as HTMLElement | null;
const savedScrollLeft = barsEl?.scrollLeft ?? 0;
- if (rounds.length === 0) {
- chartEl.innerHTML = '
暂无数据,发送消息后开始统计
';
- } else {
- const maxTokens = Math.max(...rounds.map(r => r.eval_count + r.prompt_eval_count), 1);
- const barsHtml = rounds.map(r => {
- const total = r.eval_count + r.prompt_eval_count;
- const heightPct = Math.max((total / maxTokens) * 100, 2);
- const inputPct = total > 0 ? (r.prompt_eval_count / total) * 100 : 0;
- const tooltip = `第${r.round}轮 · 输入 ${formatTokenCount(r.prompt_eval_count)} + 输出 ${formatTokenCount(r.eval_count)} = ${formatTokenCount(total)} tokens · ${formatDuration(r.total_duration)}`;
- return `
-
-
- ${formatTokenCount(r.eval_count)}
- ${formatTokenCount(r.prompt_eval_count)}
-
-
-
R${r.round}
-
- `;
- }).join('');
- chartEl.innerHTML = `
-
- 输出 Token
- 输入 Token
-
-
-
- ${formatTokenCount(maxTokens)}
- ${formatTokenCount(Math.round(maxTokens / 2))}
- 0
-
-
${barsHtml}
-
- `;
- }
+ const chartData = rounds.map(r => ({
+ label: `R${r.round}`,
+ input: r.prompt_eval_count,
+ output: r.eval_count,
+ tooltip: `第${r.round}轮 · 输入 ${formatTokenCount(r.prompt_eval_count)} + 输出 ${formatTokenCount(r.eval_count)} = ${formatTokenCount(r.eval_count + r.prompt_eval_count)} tokens · ${formatDuration(r.total_duration)}`,
+ }));
- // 恢复滚动位置
- if (savedScrollLeft > 0) {
- const newBarsEl = chartEl.querySelector('.td-chart-bars') as HTMLElement | null;
- if (newBarsEl) newBarsEl.scrollLeft = savedScrollLeft;
- }
+ const chartTitleEl = dashboardModalEl.querySelector('#tdChartTitle');
+ if (chartTitleEl) chartTitleEl.textContent = '📈 Token 消耗趋势 — 按轮次';
+
+ renderBarChart(chartEl, chartData, '按轮次', undefined, savedScrollLeft);
// 表格标题
const tableTitleEl = dashboardModalEl.querySelector('#tdTableTitle');
diff --git a/src/renderer/styles/style.css b/src/renderer/styles/style.css
index aaa6250..cb54332 100644
--- a/src/renderer/styles/style.css
+++ b/src/renderer/styles/style.css
@@ -3615,102 +3615,115 @@ html, body {
border-radius: var(--radius-md);
padding: 16px;
box-shadow: var(--shadow-card);
- min-height: 180px;
+ min-height: 200px;
}
-.td-chart-container {
- display: flex;
- gap: 8px;
- height: 180px;
+.td-chart-grid {
+ position: relative;
+ height: 200px;
+ border-left: 1px solid var(--border-subtle);
+ border-bottom: 1px solid var(--border-subtle);
+ margin-left: 40px;
+ margin-bottom: 4px;
}
-.td-chart-y-axis {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- font-size: 10px;
+/* 网格线 */
+.td-gridline {
+ position: absolute;
+ left: 0;
+ right: 0;
+ border-top: 1px dashed var(--border-subtle);
+ pointer-events: none;
+ opacity: 0.5;
+}
+
+.td-gridline:first-child {
+ border-top: none;
+}
+
+.td-gridline-label {
+ position: absolute;
+ right: calc(100% + 6px);
+ transform: translateY(-50%);
+ font-size: 9px;
color: var(--text-tertiary);
font-family: var(--font-mono);
- padding: 2px 0;
- min-width: 36px;
- text-align: right;
+ white-space: nowrap;
}
.td-chart-bars {
- flex: 1;
+ position: absolute;
+ inset: 0;
display: flex;
align-items: flex-end;
- gap: 6px;
- height: 180px;
- border-left: 1px solid var(--border-subtle);
- border-bottom: 1px solid var(--border-subtle);
- padding: 0 8px 0 4px;
+ gap: 4px;
+ padding: 0 8px;
overflow-x: auto;
+ scroll-behavior: smooth;
}
.td-bar-col {
display: flex;
flex-direction: column;
align-items: center;
- flex: 1;
- min-width: 36px;
- max-width: 60px;
- height: 100%;
+ flex: 0 0 auto;
+ width: 40px;
cursor: default;
}
-.td-bar-values {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 1px;
- margin-bottom: 2px;
- font-size: 9px;
- font-family: var(--font-mono);
- white-space: nowrap;
- flex-shrink: 0;
-}
-
-.td-val-output {
- color: #E8734A;
-}
-
-.td-val-input {
- color: #9B7ED8;
-}
-
-.td-bar-wrapper {
+.td-bar-area {
flex: 1;
width: 100%;
display: flex;
align-items: flex-end;
}
-.td-bar {
+.td-bar-stack {
width: 100%;
- border-radius: 4px 4px 0 0;
+ border-radius: 3px 3px 0 0;
overflow: hidden;
- position: relative;
- min-height: 2px;
-}
-
-.td-bar-fill {
- width: 100%;
- height: 100%;
- background: linear-gradient(180deg, #E8734A 0%, #F0976E 100%);
- border-radius: 4px 4px 0 0;
- transition: height 0.3s ease;
display: flex;
flex-direction: column-reverse;
+ transition: height 0.3s ease;
}
-.td-bar-input {
+.td-bar-segment {
width: 100%;
- background: linear-gradient(180deg, #9B7ED8 0%, #B99BE5 100%);
- border-radius: 4px 4px 0 0;
- min-height: 0;
+ transition: height 0.2s ease;
}
+.td-bar-output {
+ background: linear-gradient(180deg, #E8734A 0%, #F0976E 100%);
+}
+
+.td-bar-input-seg {
+ background: linear-gradient(180deg, #9B7ED8 0%, #B99BE5 100%);
+}
+
+.td-bar-labels {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1px;
+ margin-top: 3px;
+ font-size: 9px;
+ font-family: var(--font-mono);
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.td-bar-label {
+ font-size: 10px;
+ color: var(--text-tertiary);
+ margin-top: 2px;
+ font-family: var(--font-mono);
+}
+
+.td-bar-col:hover .td-bar-stack {
+ filter: brightness(1.1);
+}
+
+/* 图例 */
.td-chart-legend {
display: flex;
gap: 16px;
@@ -3739,15 +3752,13 @@ html, body {
background: #9B7ED8;
}
-.td-bar-label {
- font-size: 10px;
- color: var(--text-tertiary);
- margin-top: 4px;
- font-family: var(--font-mono);
+/* 柱子数值标签颜色 */
+.td-val-output {
+ color: #E8734A;
}
-.td-bar-col:hover .td-bar-fill {
- background: linear-gradient(180deg, #D4623A 0%, #E8734A 100%);
+.td-val-input {
+ color: #9B7ED8;
}
/* 空状态 */