Merge pull request #247 from router-for-me/revert-235-improve-token-breakdown-readability

Revert "Improve token type distribution readability"
This commit is contained in:
Supra4E8C
2026-04-29 23:53:33 +08:00
committed by GitHub
2 changed files with 21 additions and 86 deletions
+21 -84
View File
@@ -1,7 +1,6 @@
import { useState, useMemo } from 'react'; import { useState, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import type { ChartData, ChartOptions } from 'chart.js'; import { Line } from 'react-chartjs-2';
import { Bar } from 'react-chartjs-2';
import { Card } from '@/components/ui/Card'; import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { import {
@@ -13,21 +12,15 @@ import { buildChartOptions, getHourChartMinWidth } from '@/utils/usage/chartConf
import type { UsagePayload } from './hooks/useUsageData'; import type { UsagePayload } from './hooks/useUsageData';
import styles from '@/pages/UsagePage.module.scss'; import styles from '@/pages/UsagePage.module.scss';
const TOKEN_COLORS: Record<TokenCategory, string> = { const TOKEN_COLORS: Record<TokenCategory, { border: string; bg: string }> = {
input: '#8CC21F', input: { border: '#8b8680', bg: 'rgba(139, 134, 128, 0.25)' },
output: '#FA6450', output: { border: '#22c55e', bg: 'rgba(34, 197, 94, 0.25)' },
cached: '#F5ED58', cached: { border: '#f59e0b', bg: 'rgba(245, 158, 11, 0.25)' },
reasoning: '#00ABA5' reasoning: { border: '#8b5cf6', bg: 'rgba(139, 92, 246, 0.25)' }
}; };
const CATEGORIES: TokenCategory[] = ['input', 'output', 'cached', 'reasoning']; const CATEGORIES: TokenCategory[] = ['input', 'output', 'cached', 'reasoning'];
function formatTokens(num: number): string {
if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
return num.toString();
}
export interface TokenBreakdownChartProps { export interface TokenBreakdownChartProps {
usage: UsagePayload | null; usage: UsagePayload | null;
loading: boolean; loading: boolean;
@@ -58,94 +51,38 @@ export function TokenBreakdownChart({
reasoning: t('usage_stats.reasoning_tokens') reasoning: t('usage_stats.reasoning_tokens')
}; };
const categoryTotals: Record<TokenCategory, number> = { const data = {
input: 0,
output: 0,
cached: 0,
reasoning: 0
};
CATEGORIES.forEach((cat) => {
categoryTotals[cat] = series.dataByCategory[cat].reduce(
(sum, val) => sum + (Number(val) || 0),
0,
);
});
const data: ChartData<'bar'> = {
labels: series.labels, labels: series.labels,
datasets: CATEGORIES.map((cat) => ({ datasets: CATEGORIES.map((cat) => ({
label: categoryLabels[cat], label: categoryLabels[cat],
categoryId: cat,
data: series.dataByCategory[cat], data: series.dataByCategory[cat],
backgroundColor: TOKEN_COLORS[cat], borderColor: TOKEN_COLORS[cat].border,
borderColor: isDark ? '#0f172a' : '#ffffff', backgroundColor: TOKEN_COLORS[cat].bg,
borderWidth: 1, pointBackgroundColor: TOKEN_COLORS[cat].border,
borderSkipped: false, pointBorderColor: TOKEN_COLORS[cat].border,
grouped: false, fill: true,
// Higher order is drawn earlier by Chart.js, so larger series stay behind smaller ones. tension: 0.35
order: categoryTotals[cat],
categoryPercentage: 0.78,
barPercentage: 0.82
})) }))
}; };
const baseOptions = buildChartOptions({ period, labels: series.labels, isDark, isMobile }) as ChartOptions<'bar'>; const baseOptions = buildChartOptions({ period, labels: series.labels, isDark, isMobile });
const options: ChartOptions<'bar'> = { const options = {
...baseOptions, ...baseOptions,
scales: { scales: {
...baseOptions.scales, ...baseOptions.scales,
y: { y: {
...baseOptions.scales?.y, ...baseOptions.scales?.y,
stacked: false stacked: true
}, },
x: { x: {
...baseOptions.scales?.x, ...baseOptions.scales?.x,
stacked: false stacked: true
}
},
plugins: {
...baseOptions.plugins,
tooltip: {
...baseOptions.plugins?.tooltip,
itemSort: (a, b) => a.datasetIndex - b.datasetIndex,
callbacks: {
...baseOptions.plugins?.tooltip?.callbacks,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
label: function (context: any) {
const val = Number(context.raw) || 0;
const cat = context.dataset.categoryId;
let text = `${context.dataset.label}: ${formatTokens(val)}`;
if (cat === 'cached') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const inputDs = context.chart.data.datasets.find((ds: any) => ds.categoryId === 'input');
const inputVal = inputDs ? (Number(inputDs.data[context.dataIndex]) || 0) : 0;
if (inputVal > 0) {
const perc = ((val / inputVal) * 100).toFixed(2);
text += ` (${perc}%)`;
}
}
return text;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
footer: function (tooltipItems: any[]) {
let total = 0;
const dataIndex = tooltipItems[0].dataIndex;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tooltipItems[0].chart.data.datasets.forEach((ds: any) => {
total += Number(ds.data[dataIndex]) || 0;
});
return `Total: ${formatTokens(total)}`;
}
}
} }
} }
}; };
return { chartData: data, chartOptions: options }; return { chartData: data, chartOptions: options };
}, [usage, period, isDark, isMobile, hourWindowHours, t]); }, [usage, period, isDark, isMobile, hourWindowHours, t]);
const labels = chartData.labels ?? [];
return ( return (
<Card <Card
@@ -171,7 +108,7 @@ export function TokenBreakdownChart({
> >
{loading ? ( {loading ? (
<div className={styles.hint}>{t('common.loading')}</div> <div className={styles.hint}>{t('common.loading')}</div>
) : labels.length > 0 ? ( ) : chartData.labels.length > 0 ? (
<div className={styles.chartWrapper}> <div className={styles.chartWrapper}>
<div className={styles.chartLegend} aria-label="Chart legend"> <div className={styles.chartLegend} aria-label="Chart legend">
{chartData.datasets.map((dataset, index) => ( {chartData.datasets.map((dataset, index) => (
@@ -180,7 +117,7 @@ export function TokenBreakdownChart({
className={styles.legendItem} className={styles.legendItem}
title={dataset.label} title={dataset.label}
> >
<span className={styles.legendDot} style={{ backgroundColor: TOKEN_COLORS[CATEGORIES[index]] }} /> <span className={styles.legendDot} style={{ backgroundColor: dataset.borderColor }} />
<span className={styles.legendLabel}>{dataset.label}</span> <span className={styles.legendLabel}>{dataset.label}</span>
</div> </div>
))} ))}
@@ -191,11 +128,11 @@ export function TokenBreakdownChart({
className={styles.chartCanvas} className={styles.chartCanvas}
style={ style={
period === 'hour' period === 'hour'
? { minWidth: getHourChartMinWidth(labels.length, isMobile) } ? { minWidth: getHourChartMinWidth(chartData.labels.length, isMobile) }
: undefined : undefined
} }
> >
<Bar data={chartData} options={chartOptions} /> <Line data={chartData} options={chartOptions} />
</div> </div>
</div> </div>
</div> </div>
-2
View File
@@ -2,7 +2,6 @@ import { useState, useMemo, useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
Chart as ChartJS, Chart as ChartJS,
BarElement,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
PointElement, PointElement,
@@ -47,7 +46,6 @@ import styles from './UsagePage.module.scss';
// Register Chart.js components // Register Chart.js components
ChartJS.register( ChartJS.register(
BarElement,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
PointElement, PointElement,