news 2026/9/11 23:08:59

qwen-code 统计仪表盘重构实战:Activity / Efficiency 双 Tab 的设计、数据层扩展与源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
qwen-code 统计仪表盘重构实战:Activity / Efficiency 双 Tab 的设计、数据层扩展与源码实现

qwen-code 统计仪表盘重构实战:Activity / Efficiency 双 Tab 的设计、数据层扩展与源码实现

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

导读

本文以 qwen-code 开源仓库中的 Stats Dashboard Redesign 设计规格 为核心骨架,系统讲解 TUI 中/stats统计仪表盘的重构方案:如何把原有的 Overview / Models 双 Tab 重构为 Session / Activity / Efficiency 三 Tab,如何引入时间范围选择与环比 Delta 指标,以及如何在不破坏旧数据的前提下扩展usage_record.jsonl的数据模型。读完本文,你将掌握这套仪表盘从规格设计、数据结构演进到键盘交互与热力图渲染的完整实现路径,并能直接对照仓库源码进行二次开发或移植。


一、重构目标与总体设计

规格文档(2025-06-03-stats-dashboard-redesign.md)定义了本次重构的核心目标:重新设计/statsTUI 仪表盘,改进布局层级,新增效率指标、工具使用明细与趋势对比能力,同时保持 Session 标签页原样不动。

重构后的标签页结构如下:

Tab 1: Session (不变——实时当前会话指标) Tab 2: Activity (基于时间的趋势与使用模式) Tab 3: Efficiency (性能指标与工具分析)

在仓库源码中,这一结构已经落地。查看 StatsDialog.tsx 可以看到三个标签页组件被分别拆分为独立的SessionTabActivityTabEfficiencyTab,由StatsDialog统一调度;TAB_DEFS(来自stats-helpers.js)定义了 Tab 枚举与循环切换顺序。StatsDialog内部还通过handleTabChange实现了Tab/Shift+Tab的循环切换((idx + direction + TAB_DEFS.length) % TAB_DEFS.length),保证焦点不会在边界卡死。

值得注意的是,StatsDialog还支持嵌入模式availableHeight属性):当它被嵌入到设置对话框的 Stats 页时,通过EFFICIENCY_CHROME_ROWS = 24MAX_EMBEDDED_TOOL_ROWS = 5CODE_IMPACT_ROWS = 1等常量精确预算高度,防止模型表溢出宿主视图——这是规格文档之外、源码中体现的工程细节。


二、时间范围选择器与环比 Delta 计算

2.1 范围切换机制

所有 Activity 与 Efficiency 标签页的数据都受同一个时间范围约束,通过按r键循环切换:

Today → Week → Month → All

在 StatsDialog.tsx 中,r键处理逻辑为setRangeIndex((i) => (i + 1) % RANGE_CYCLE.length),即对RANGE_CYCLE数组取模循环;range状态驱动loadStatsData(range, liveRecord)重新加载数据(useEffect依赖[range, stats.sessionId],即只在范围或会话变化时重载,而不是每次指标 tick 都刷新)。对话框底部的RangeIndicator会高亮当前选中的范围(bold+underline+ accent 色),并用·分隔各选项。

2.2 Delta 环比规则

每个 KPI 卡片都会显示一个趋势箭头,将当前范围上一个等价范围对比:

范围对比基准
Today今天 vs 昨天
Week最近 7 天 vs 前 7 天
Month最近 30 天 vs 前 30 天
All不显示 Delta

显示规则:正向变化用绿色▲ +12%,负向变化用红色▼ -3%唯独延迟(Latency)例外——越低越好,因此颜色取反

实现路径:从usage_record.jsonl加载两个时间片,分别聚合后计算百分比变化。规格文档建议的 delta 字段在 statsDataService.ts 的StatsData接口中已完整落地:delta对象包含sessionsdurationtokenscacheRatetoolSuccessavgLatency六个number | null字段——null即表示该范围下无上一周期可比数据(如all范围)。


三、Activity 标签页:时间维度下的使用趋势

Activity 页自上而下分为四块:KPI 行、GitHub 风格热力图、Token 趋势折线图、项目排行榜。

3.1 KPI 行

三个横向排列的指标卡,每个都带数值 + Delta 箭头:

指标数据来源示例
Sessionsreport.sessionCount42 ▲+8
Durationreport.totalDurationMs18h 32m ▲+2h
Tokens求和report.models[*].totalTokens2.4m ▲+12%

其中 Duration 的展示会自动格式化为人性化时长(小时/分钟),Tokens 则缩写成2.4m这样的紧凑形式。

3.2 GitHub 风格热力图

  • 全宽渲染,网格布局仿 GitHub contribution graph;
  • 颜色强度 = 每日总 Token 消耗(注意:不是会话数);
  • 今天的格子有特殊边框或标记字符(如用[ ]而非 ,或更亮的描边色);
  • 右侧对齐元信息:streak: 12d │ best: 23d
  • 图例行:Less ░░░░░ More
  • 列标签显示月份缩写 + 日期数字;
  • 行标签为 Mon / Wed / Fri(紧凑 3 行模式);
  • 展示周数由终端宽度自适应:min(26, max(8, floor((bodyWidth - 4) / 2)))

这段公式在 StatsDialog.tsx 有对应实现:safeWidth = Math.max(72, width ?? 100)bodyWidth = safeWidth - 6,宽度下限被钳制在 72 列,保证窄终端下布局不崩。

源码印证:statsDataService.ts 中的buildHeatmapYYYY-MM-DD为 key,对每条记录内所有模型的totalTokens(缺失时回退为inputTokens + outputTokens + thoughtsTokens)求和,产出Record<string, number>——确认热力图数值语义就是“每日总 Token”。

Streak(连续使用天数)的计算在 statsDataService.ts 的calculateStreaks中实现:将日期去重排序后逐日比对,diff === 1则当前 streak 累加,diff > 1则重置;若最后一条数据距今超过 1 天,当前 streak 归零,从而同时得出currentStreaklongestStreak

3.3 Token 趋势折线图

  • 使用仓库既有的buildLineChartData生成 Braille 子像素折线图;
  • 单一序列:每日总 Token;
  • 高度 6 行;
  • 当范围为all时,可用/按月翻页导航,月份标签形如← Jun 2025 →

月导航的逻辑在 StatsDialog.tsx:/hchartMonthOffset递增(最多到months.length - 1,months 由data.tokensPerDay中所有date.slice(0, 7)去重得出),/l递减(最小为 0),并且仅当activeTab === 'activity' && range === 'all'时生效——与规格完全一致。

3.4 项目排行榜

展示 Top 5 项目,数据源为report.projectstotalTokens降序排列:

Project Sessions Tokens Duration qwen-code 28 1.8m 12h web-app 10 420k 4h infra 4 180k 2h

四、Efficiency 标签页:性能与工具效率分析

Efficiency 页自上而下分为四块:性能卡片行、工具排行榜、模型对比表、代码影响。

4.1 性能卡片行

三个盒式指标卡:

指标计算公式数据来源
Cache Hit RatecachedTokens / inputTokens * 100report.models[*].cachedTokens/inputTokens
Tool Success RatetotalSuccess / totalCalls * 100report.tools.totalSuccess/totalCalls
Avg LatencytotalLatencyMs / totalRequests持久化记录中的totalLatencyMs,或按模型数据计算

每个卡片展示:标签、加粗数值/百分比、Delta 箭头。

关于 Avg Latency 的关键设计决策:重构前UsageSummaryRecord并不持久化延迟数据,规格文档给出了两个候选方案——

  1. 仅从实时SessionMetrics计算当前会话延迟(历史数据一律显示);
  2. 为持久化记录新增totalLatencyMs字段(旧记录迁移后显示)。

最终决策:方案 2——扩展UsageSummaryRecord增加可选的totalLatencyMs字段,旧记录因缺少该字段,延迟 Delta 显示

源码印证:这一决策已完全落地。usageHistoryService.ts 中UsageSummaryRecord接口包含可选字段totalLatencyMs?: number,且tools.byName中每个工具的聚合也带totalDurationMs?: number(见第 61-64 行);AggregatedReport则进一步把延迟与工具耗时升级为必填:顶层totalLatencyMs: number、每个模型的totalLatencyMs: numbertopTools数组元素包含totalDurationMs: number(见第 86-117 行)——说明数据层在聚合阶段已统一补齐默认值,UI 层无需再判空。规格文档中“当前topTools只有 count/success/fail,需要为聚合增加totalDurationMs”的备注,在源码中同样已经解决。

4.2 工具排行榜

按调用次数展示 Top 8 工具:

Tool Calls Time Success edit 847 42.3s ██████████ 98% read 612 8.1s ██████████ 99% bash 431 67.8s █████████░ 89% glob 298 2.4s ██████████ 99% grep 256 3.1s █████████░ 97% write 189 12.5s ██████████ 96% agent 45 89.2s ████████░░ 82%
  • 成功率用 10 字符条形图可视化:实心+ 空心
  • 颜色规则:≥95% 绿色,≥80% 橙色,<80% 红色;
  • 数据源:report.tools.topTools(聚合时补充了 duration)。

该表格的数据结构在 statsDataService.ts 的StatsData.toolLeaderboard中定义为{ name, count, totalDurationMs, successRate },与规格文档的数据契约完全一致。

4.3 模型对比表

Model Reqs In/Out Cache Latency ● qwen-max 186 1.2m/340k 91% 2.1s ● qwen-plus 124 890k/210k 84% 1.2s ● qwen-turbo 67 310k/89k 72% 0.8s
  • totalTokens降序排列;
  • 系列色圆点标识();
  • Cache 列颜色规则:≥85% 绿色,≥70% 橙色,<70% 红色;
  • 数据源:report.models

Efficiency 页的模型表在嵌入模式下还会受到行数上限约束(maxModelRows),高度预算由 StatsDialog.tsx 动态计算:从availableHeight中减去固定的 chrome 行数(24)、工具排行榜占用的行数(含截断时的+N more提示)以及 Code Impact 占用的 1 行,结果下限钳制为 3 行。

4.4 代码影响

单行汇总,数据源为report.files.linesAdded/report.files.linesRemoved

Code +2,847 lines / -1,203 lines net: +1,644

五、键盘控制总览

按键动作
Tab/Shift+Tab切换标签页
r循环切换范围:today → week → month → all
/h上一月(图表导航,仅 range=all)
/l下一月(图表导航,仅 range=all)
Esc关闭对话框

这些按键全部在 StatsDialog.tsx 的useKeypress中注册,并通过isFocused属性控制是否消费键盘事件(嵌入模式下避免与宿主视图抢焦点)。对话框底部还会根据当前 Tab 与范围动态显示快捷键提示,例如 Activity 页且 range=all 时提示tab · r dates · ←→ month · esc


六、数据层变更:向后兼容的 schema 扩展

6.1 UsageSummaryRecord v1 扩展

在既有 schema 上仅新增可选字段,保证旧记录可继续读取:

interface UsageSummaryRecord { // ... existing fields ... totalLatencyMs?: number; // NEW: sum of all API response latencies tools: { // ... existing fields ... byName: Record<string, { count: number; success: number; fail: number; totalDurationMs?: number; // NEW: sum of tool execution time }>; }; }

对照 usageHistoryService.ts 的实际实现,totalLatencyMsbyName[].totalDurationMs均已存在,且UsageSummaryRecord还带version: 1版本标记与可选的skills字段——仓库对“旧记录缺字段”的处理模式是统一的:所有新增字段一律 optional,UI 侧按缺省值兜底。

另外,usageHistoryService.ts顶部注释(第 19-35 行)揭示了一个重要的工程权衡:LIVE_REBUILD_WINDOW_DAYS = 35——把未持久化的 daemon / Web Shell / 进行中会话合并进历史时,只回放最近 35 天的 transcript(覆盖 month=30 天范围加余量),而已持久化的usage_record.jsonl记录不受此窗口限制,始终全量并入,从而热力图保留完整历史。这是规格文档未涉及、但直接决定“热力图能看到多久历史”的实现细节。

6.2 StatsData 扩展

interface StatsData { // ... existing fields ... delta?: { sessions: number | null; // percentage change duration: number | null; tokens: number | null; cacheRate: number | null; toolSuccess: number | null; avgLatency: number | null; }; efficiency: { cacheHitRate: number; toolSuccessRate: number; avgLatencyMs: number | null; }; toolLeaderboard: Array<{ name: string; count: number; totalDurationMs: number; successRate: number; }>; }

对照 statsDataService.ts 的实现,StatsData接口包含reportAggregatedReport)、heatmapcurrentStreaklongestStreaktokensPerDaydeltaefficiencytoolLeaderboard全部字段,其中delta已从规格的“可选”升级为结构化的| null联合类型,语义更明确。

6.3 热力图数据语义变更与强度标定

变更前:buildHeatmapData接收Record<string, number>,value = 当日会话数;变更后:value = 当日总 Token,0-4 级强度映射需要重新标定:

  • 0:无使用
  • 1:< 10k tokens
  • 2:10k - 50k tokens
  • 3:50k - 200k tokens
  • 4:> 200k tokens

规格同时强调:阈值应基于数据分布动态计算(百分位法),而非硬编码,以适配不同的使用模式。

源码印证:asciiCharts.ts 中存在intensityLevel(count, thresholds)函数,说明强度分级已抽象为“给定阈值数组映射到 0-4 级”的通用逻辑,支持按数据分布注入阈值。

6.4 今日高亮(Today Highlight)

buildHeatmapData需要把今天的格子打上特殊标记。源码实现为 asciiCharts.ts:通过todayKey = dayKey(new Date())生成今日 key,与当前格子比对得到isToday标记,渲染时用更明亮的描边字符(如[▓]而非▓▓)突出显示——与规格文档的字符方案完全对应。


七、国际化:新增 i18n 键

所有用户可见字符串统一包裹在t()中,新增键如下:

stats.activity = "Activity" stats.efficiency = "Efficiency" stats.today = "Today" stats.sessions = "Sessions" stats.duration = "Duration" stats.tokens = "Tokens" stats.cacheHitRate = "Cache Hit Rate" stats.toolSuccessRate = "Tool Success" stats.avgLatency = "Avg Latency" stats.toolLeaderboard = "Tool Leaderboard" stats.calls = "Calls" stats.time = "Time" stats.success = "Success" stats.models = "Models" stats.reqs = "Reqs" stats.cache = "Cache" stats.latency = "Latency" stats.codeImpact = "Code Impact" stats.net = "net" stats.streak = "streak" stats.best = "best" stats.tokenTrend = "Token Trend" stats.projects = "Projects" stats.project = "Project"

在 UI 层,StatsDialog通过t('(Tab to switch)')t('Loading stats...')t('Failed to load stats. Press r to retry.')等调用印证了该模式,且加载失败时提示“按 r 重试”恰好复用了范围切换键,交互自洽。


八、改动文件清单与源码对应

规格文档列出的改动清单,在仓库中的落点如下:

文件(规格)实际代码位置变更内容
StatsDialog.tsxpackages/cli/src/ui/components/StatsDialog.tsxSessionTab/ActivityTab/EfficiencyTab替换原 Overview / Models,承载 Tab 切换、范围循环、月导航、嵌入模式高度预算
usageHistoryService.tspackages/core/src/services/usageHistoryService.tsUsageSummaryRecord增加totalLatencyMsbyName[].totalDurationMsAggregatedReport增加聚合后的延迟与工具耗时
statsDataService.tspackages/cli/src/ui/utils/statsDataService.tsStatsData增加delta/efficiency/toolLeaderboardbuildHeatmap改为按每日总 Token 统计;新增 streak 计算
asciiCharts.tspackages/cli/src/ui/utils/asciiCharts.tsintensityLevel强度映射、isToday今日高亮标记、Braille 折线图复用
uiTelemetry.tspackages/core/src/telemetry/uiTelemetry.ts确保延迟数据进入持久化路径(被usageHistoryService.ts引用)

三个标签页组件的拆分实现分别位于 StatsSessionTab.tsx、StatsActivityTab.tsx、StatsEfficiencyTab.tsx。


九、明确不在本次范围内

为避免范围蔓延,规格明确排除了以下能力:

  • 成本估算——依赖用户自配的价格体系,可后续添加;
  • 单文件级变更追踪——当前数据模型不支持;
  • 上下文窗口用量 / 压缩指标——当前未跟踪;
  • 单个会话的下钻交互——本次不实现。

这些排除项也为后续演进划清了边界:任何想在这四个方向扩展的开发者,都需要先补齐对应的数据采集层。


总结

qwen-code 的/stats仪表盘重构是一套典型的“数据模型先行、UI 分层落地”的演进案例:规格先敲定UsageSummaryRecord的可选字段扩展与StatsData的契约,再通过r键驱动的范围循环支撑 Delta 环比,最后在 TUI 层以三 Tab 结构(Session / Activity / Efficiency)承载热力图、Braille 趋势图、工具排行榜与模型对比表。对照仓库源码可以确认,规格中的每一项决策——包括延迟持久化的方案 2、热力图按 Token 而非会话计数、今日格子高亮、强度阈值百分位化——均已实现,且工程上额外处理了窄终端钳制、嵌入模式高度预算与未持久化会话的回放窗口等边界问题。对于希望深度定制统计面板或移植到其他 TUI 应用的开发者,这份规格与其源码实现构成了完整的参考闭环。

【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:08:09

SpringBoot旅游平台开发实战:UGC社区架构与优化

1. 项目概述&#xff1a;SpringBoot旅游攻略分享平台 这个旅游攻略分享平台是我去年带队开发的一个实战项目&#xff0c;核心目标是为旅行爱好者构建一个内容共创社区。不同于传统的旅游资讯网站&#xff0c;我们采用SpringBootVue前后端分离架构&#xff0c;实现了用户生成内容…

作者头像 李华
网站建设 2026/9/11 23:04:32

Flask生产级文件上传下载系统实战指南

简介&#xff1a;这是一份面向Python Web开发初学者与Flask入门实践者的完整项目资源&#xff0c;聚焦文件上传下载核心功能的系统化实现&#xff0c;帮助开发者掌握Web应用中常见的文件管理场景。资源包含78个文件&#xff0c;涵盖14个核心Python源码&#xff08;含Flask路由、…

作者头像 李华
网站建设 2026/9/11 23:02:42

Element Plus 安装指南:从包管理器到 CDN 的完整接入方案

Element Plus 安装指南&#xff1a;从包管理器到 CDN 的完整接入方案 【免费下载链接】element-plus &#x1f389; A Vue.js 3 UI Library made by Element team 项目地址: https://gitcode.com/GitHub_Trending/el/element-plus Element Plus 是基于 Vue 3 的组件库&a…

作者头像 李华