import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; import { BarChart3, CalendarDays, ChevronDown, Check, Info, Layers3, RefreshCcw, Sparkles, TrendingUp } from "lucide-react"; import { sampleSummary } from "./sampleData"; import { formatTokensZh } from "./displayFormat"; import "./styles.css"; const providers = ["Claude", "Codex", "Gemini"]; const providerColors = { Claude: "#2e83ff", Codex: "#9b5cf6", Gemini: "#2dd4cf" }; function App() { const [summary, setSummary] = useState(sampleSummary); const [status, setStatus] = useState("loading"); const [lastUpdated, setLastUpdated] = useState(""); const [refreshCount, setRefreshCount] = useState(0); const [range, setRange] = useState("近 30 天"); const [trendMode, setTrendMode] = useState("每日"); const [shareMode, setShareMode] = useState("按 Tokens"); const [toolFilter, setToolFilter] = useState("全部工具"); const scanningRef = useRef(false); const refresh = useCallback(async ({ silent = false } = {}) => { if (scanningRef.current) return; scanningRef.current = true; if (!silent) setStatus("loading"); try { if (window.tokenLens?.scanUsage) { const data = await window.tokenLens.scanUsage(); setSummary(data); setStatus("ready"); setLastUpdated(new Date(data.generatedAt).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" })); setRefreshCount((count) => count + 1); } else { setSummary(sampleSummary); setStatus("preview"); setLastUpdated("预览数据"); } } catch (error) { setStatus("error"); setLastUpdated("扫描失败"); console.error(error); } finally { scanningRef.current = false; } }, []); useEffect(() => { refresh(); const timer = window.setInterval(() => { refresh({ silent: true }); }, 15_000); return () => window.clearInterval(timer); }, [refresh]); const totalByProvider = useMemo(() => providers.reduce((sum, provider) => { return sum + (summary.providerTotals?.[provider]?.totalTokens || 0); }, 0), [summary]); const sourceRows = summary.sourceRows?.length ? summary.sourceRows : sampleSummary.sourceRows; const filteredRows = toolFilter === "全部工具" ? sourceRows : sourceRows.filter((row) => row.provider === toolFilter); const insights = summary.insights?.length ? summary.insights.slice(0, 3) : sampleSummary.insights; return (
refresh()} status={status} lastUpdated={lastUpdated} refreshCount={refreshCount} range={range} setRange={setRange} />
} > } >
); } function TopBar({ onRefresh, status, lastUpdated, refreshCount, range, setRange }) { return (
TokenLens 用量统计 {status === "error" ? "扫描异常" : "每 15 秒自动刷新"}
} className="toolbar-dropdown" />
{lastUpdated ? `上次更新 ${lastUpdated}` : "准备扫描"} 已刷新 {refreshCount} 次
); } function MetricCards({ summary }) { const cards = [ { label: "今日 Tokens", value: formatTokensZh(summary.cards?.todayTokens || 0), detail: "本地今日已统计", trend: "实时", tone: "blue", icon: Layers3 }, { label: "本月 Tokens", value: formatTokensZh(summary.cards?.monthTokens || 0), detail: "按本地时区汇总", trend: "月度", tone: "green", icon: Sparkles }, { label: "历史总量", value: formatTokensZh(summary.cards?.totalTokens || 0), detail: `已扫描 ${summary.cards?.sessionCount || 0} 个会话`, trend: "全部", tone: "violet", icon: Check }, { label: "缓存命中", value: `${summary.cards?.cacheHitRate || 0}%`, detail: "来自缓存上下文", trend: "缓存", tone: "orange", icon: TrendingUp } ]; return (
{cards.map((card, index) => { const Icon = card.icon; return (
{card.label}
{card.value}
{card.trend} {card.detail}
); })}
); } function Panel({ title, control, children }) { return (

{title}

{control}
{children}
); } function Dropdown({ value, options, onChange, icon, className = "" }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { function close(event) { if (!ref.current?.contains(event.target)) setOpen(false); } document.addEventListener("pointerdown", close); return () => document.removeEventListener("pointerdown", close); }, []); return (
{open ? (
{options.map((option) => ( ))}
) : null}
); } function TrendChart({ data }) { const chartData = data.length ? data.slice(-24) : sampleSummary.dailyTrend; const width = 640; const height = 235; const left = 58; const right = 18; const top = 18; const bottom = 34; const maxValue = Math.max(1, ...chartData.flatMap((item) => providers.map((provider) => item[provider] || 0))); const plotWidth = width - left - right; const plotHeight = height - top - bottom; const x = (index) => left + (index / Math.max(chartData.length - 1, 1)) * plotWidth; const y = (value) => top + plotHeight - (value / maxValue) * plotHeight; const lines = providers.map((provider) => ({ provider, points: chartData.map((item, index) => `${x(index)},${y(item[provider] || 0)}`).join(" ") })); const labelIndexes = [0, 6, 12, 18, chartData.length - 1].filter((index, pos, arr) => index >= 0 && arr.indexOf(index) === pos); return ( <> {[0, 0.25, 0.5, 0.75, 1].map((step) => { const lineY = top + plotHeight * step; const value = maxValue * (1 - step); return ( {formatTokensZh(value)} ); })} {labelIndexes.map((index) => ( {shortDate(chartData[index]?.date)} ))} {lines.map((line) => ( ))} {lines.map((line) => chartData.filter((_, index) => index % 5 === 0 || index === chartData.length - 1).map((item, dotIndex) => ( )))} ); } function UsageShare({ providerTotals, total }) { const safeTotal = total || 1; let cursor = 0; const gradient = providers.map((provider) => { const share = ((providerTotals[provider]?.totalTokens || 0) / safeTotal) * 360; const start = cursor; cursor += share; return `${providerColors[provider]} ${start}deg ${cursor}deg`; }).join(", "); return (
{formatTokensZh(total)} 本月
{providers.map((provider) => { const providerTotal = providerTotals[provider]?.totalTokens || 0; const percent = total ? Math.round((providerTotal / total) * 100) : 0; return (
{provider} {formatTokensZh(providerTotal)} Tokens
{percent}%
); })}
); } function ProviderBreakdown({ rows, toolFilter, setToolFilter }) { return (

工具明细

{rows.map((row, index) => ( ))}
工具 输入 输出 缓存 总量 趋势
{formatTokensZh(row.inputTokens)} {formatTokensZh(row.outputTokens)} {formatTokensZh(row.cachedTokens)} {formatTokensZh(row.totalTokens)}
显示 {rows.length} 个本地工具 · 不上传对话内容
); } function ProviderName({ provider }) { return (
{provider === "Gemini" ? "" : provider[0]} {provider}
); } function Sparkline({ provider, seed }) { const base = [ [17, 12, 16, 8, 12, 6, 10, 4, 8, 5, 9], [18, 11, 15, 17, 10, 13, 7, 12, 6, 8, 4], [16, 16, 10, 18, 14, 9, 12, 6, 5, 8, 8] ][seed % 3]; const points = base.map((value, index) => `${index * 7},${value}`).join(" "); return ( ); } function Insights({ insights }) { return (

洞察提醒

{insights.map((insight, index) => (
{insight.tone === "good" ? "✓" : insight.tone === "warn" ? "!" : "↗"}
{index + 1} 小时
{insight.title} {insight.copy}
))}
); } function Legend() { return (
{providers.map((provider) => ( {provider} ))}
); } function shortDate(value) { if (!value) return ""; const [, month, day] = value.split("-"); return `${month}/${day}`; } createRoot(document.getElementById("root")).render();