ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Operit Token 统计页 Activity-First 重构实战:贡献网格布局、三主题趋势卡与性能统计移除

Operit Token 统计页 Activity-First 重构实战:贡献网格布局、三主题趋势卡与性能统计移除 AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载导读本文基于 Operit 仓库中 token_stats_activity_refresh_20260816 方案文档 及其子文档系统讲解 Token 统计页的一次信息架构重构将「活动控制 四项摘要指标 可视化」整体上移到页面首屏把每日热力图改造成右对齐、可选中、带前置空单元格的贡献网格并将趋势区收敛为三张独立主题卡片、彻底移除性能统计链路。读完本文你将掌握 Operit 中 Token 统计页的页面编排顺序、贡献网格的宽度填充与交互算法、三种活动视图每日/每周/累计的日期范围策略以及一次数据采集 → Room 存储 → 聚合查询 → UI → 资源 → 测试全链路字段移除的完整做法并能在 TokenUsageStatisticsScreen.kt 等源码中一一印证。一、重构背景与范围界定1.1 Previous State旧布局的三个问题根据 1_activity_layout.md 的「Previous State」描述重构前的 Token 统计页存在三处明显缺陷活动区域位置靠后活动控制、四张摘要卡片与可视化被一起塞进生命周期总计lifetime totals与筛选器filters之后的单一范围分析卡片range-analysis card中用户进入页面后首先看到的是汇总数字而非活动概览短范围热力图左空右空每日热力图只绘制查询区间内的真实日期导致短范围如最近几天时网格左侧的日历起始位置整片空白看起来不像一个完整的贡献网格contribution grid趋势区混入无用视图趋势区包含成本、请求、Token 与性能四张卡片其中性能卡片对用户没有使用价值见 2_trends_and_performance_removal.md。1.2 重构意图与范围约束方案索引 明确了本任务的 Intent 与 Scope将活动控制、四项摘要值与可视化移动到页面顶部让可视化成为一张独立的主题卡片将短日范围渲染成满宽、右对齐的贡献网格成本、请求、Token 趋势保留为紧凑的独立主题卡片从采集capture、Room 存储到 UI彻底移除性能统计范围被严格限定为 PR #950 引入的 token-statistics 功能本身聊天域chat domain的消息时长字段保留不动只是不再复制进 token 用量记录不新增数据库版本迁移或兼容路径直接编辑未发布的 v20→v21 schema 为最终形态。这一小步、聚焦、不破坏未发布 schema的范围界定是后续所有代码改动的前提也解释了为什么性能字段可以放心删除——因为该功能从未发布没有外部兼容负担。二、Activity-First 布局页面首屏重排2.1 Intended Change 与 Expected State1_activity_layout.md 定义了四条布局意图把活动控制、摘要卡片与可视化放到所有其他 token 统计区块之前控制项与活动保持在一起——因为它们定义了可视化模式与日期范围可视化拥有独立主题卡片短范围内用不可交互的无历史单元格no-history cells填充可用宽度将真实活动推到右边缘选中范围内零 Token 日期与无历史单元格在视觉上必须可区分。期望态Expected State页面第一眼看到的就是活动概览短范围呈现完整的贡献网格真实日期可选、前置单元格可见、且不引入任何人为活动值。2.2 源码印证TokenStatsPageContent 的 LazyColumn 顺序这一布局在 TokenUsageStatisticsScreen.kt 的TokenStatsPageContent中落地。页面主体是一个LazyColumnitem 顺序严格按照设计规范文件头注释中标注信息架构重构版设计规范 2026-08-18顺序区块对应 item 源码位置1时间控制行三段式视图 日期范围 日历TokenStatsTimeControlRow第 197-205 行2周期总览总 Token 总费用 连续天数 面积趋势TokenStatsOverviewCard第 208-227 行32×2 核心指标峰值 Token / 总请求 / 缓存率 / 输出TokenStatsMetricGrid第 230-244 行4活跃记录热力图 / 每周 / 累计TokenActivitySection第 247-252 行5Token 构成缓存读取 / 未缓存输入 / 输出进度条TokenStatsCompositionCard第 255-260 行6模型累计动态模型列表与筛选联动TokenStatsModelRankSection第 263-271 行7范围分析模型筛选TokenStatsFilterBar第 274-291 行8/9趋势分析单卡片指标切换 配置详情TokenStatsTrendCard/TokenStatsModelDetailsSection第 294-315 行10统计设置币种 汇率 保存TokenStatsSettingsCard第 318-333 行可以看出时间控制、周期总览、2×2 指标与活跃记录构成了页面的首屏四件套生命周期总计与筛选器被安排到模型排名、范围分析等靠后区块这正是「activity-first」的直观体现。同时时间控制行第 1 项与活动区块第 4 项之间隔着总览与指标卡但控制项通过state.activity.viewMode直接驱动活动卡的可视化模式说明控制定义模式与范围的设计意图由 ViewModel 状态贯穿实现。三、贡献网格短范围右对齐的实现算法3.1 设计目标从左空到满宽右对齐旧实现只画查询区间内的日期短范围左侧一片空白。新实现要求短范围也要铺满可用宽度用不可交互的占位单元格补齐让真实日期停在右边缘从而获得类似 GitHub 贡献图的完整观感同时零 Token 的真实日期必须与占位格在视觉上区分零 Token 显示为未激活灰格占位格不参与任何数据语义。3.2 源码级实现leadingColumns 前置列计算核心算法位于 TokenActivitySection.kt 的TokenActivityDailyHeatmap周一对齐padding firstDate.dayOfWeek.value - 1把网格与左侧星期标签统一到周一为首列的排列旧实现沿用周日首列偏移导致标签与日期错行历史列数historyColumns (padding days.size 6) / 7把日期序列换算为周7 行列数可见列数visibleColumns由BoxWithConstraints提供的maxWidth减去星期标签列宽后按(block gap)的单元格步长反推即容器能放下多少列前置空列leadingColumns (visibleColumns - historyColumns).coerceAtLeast(0)网格构造总列数columns historyColumns leadingColumns构建List(columns)的二维网格当sourceColumn column - leadingColumns 0时该单元格为null——这就是无历史单元格的数据形态。// TokenActivitySection.kt关键逻辑示意 val historyColumns ((padding days.size 6) / 7).coerceAtLeast(1) val visibleColumns ((heatmapViewportWidth.value gap.value) / (block.value gap.value)) .toInt().coerceAtLeast(1) val leadingColumns (visibleColumns - historyColumns).coerceAtLeast(0) val columns historyColumns leadingColumns val grid remember(days, padding, leadingColumns, columns) { List(columns) { column - val sourceColumn column - leadingColumns ListTokenActivityDay?(7) { row - if (sourceColumn 0) null else days.getOrNull(sourceColumn * 7 row - padding) } } }因此范围越短leadingColumns越大真实日期被整体推到右侧范围足够长时leadingColumns 0网格退化为纯历史网格并支持横向滚动。3.3 绘制与色阶level 0 灰格 vs 主色透明度阶单元格绘制第 383-408 行对day null的占位格与真实日期统一调用levelColor(day?.level ?: 0)。色阶策略在 TokenStatsColors.kt 中有明确注释level 0 未激活灰格heatmapInactive覆盖两类单元格占位格无历史与范围内零 Token 的真实日期level 1..5 主色透明度阶heatmapLevels5 档少→多只分配给真实产生 Token 的日期。但两者在交互语义上被严格区分占位格null不可选中、不显示 indicator真实日期即使 level 0可以点击选中并显示描边指示。这是零 Token 日期与无历史单元格可区分的实现基础。等级划分由数据层 TokenActivityModels.kt 中的QuantileLevels分位数算法完成不硬编码色值。3.4 长范围横向滚动并默认打开到最新数据当历史列数超过可见列数时网格外层使用horizontalScroll(scroll)第 271 行。关键细节在第 234-237 行LaunchedEffect(columns, days, state.viewMode) { snapshotFlow { scroll.maxValue }.first { it 0 } scroll.scrollTo(scroll.maxValue) }即等待滚动范围就绪后直接滚动到最大值——打开页面时热力图自动定位到最新数据右端符合活动在最右的心智模型。滚动与查看/选中的手势仲裁通过pointerInput中的HeatmapDragModeVIEW/SCROLL实现横向快速滑动进入 SCROLL 模式慢速拖拽或按住进入 VIEW 模式悬停 indicator点按切换选中日期第 278-381 行。3.5 月份标签与连续天数徽章月份标签第 206-225 行随网格横向滚动同步绘制并做靠边自动收缩处理首尾标签距边缘不足 3 列时隐藏避免贴边裁切。卡片标题行右侧展示currentStreak/longestStreak两个胶囊徽章数据来自activity.stats——该统计由 TokenActivityAggregator.stats 在选定的显式日历范围内计算total饱和加法、peak、currentStreak从末日开始倒数的连续活跃天数、longestStreak。四、三种活动视图模式与日期范围策略4.1 视图模式枚举与范围锚点活动区支持三种模式定义于 TokenActivityModels.ktenum class TokenActivityViewMode { DAILY, WEEKLY, CUMULATIVE }模式切换由页面顶部 TokenStatsTimeControlRow 的三段式控件驱动TokenStatsSegmentedControl与日期范围同处一行——这正是文档中控制项定义可视化模式与日期范围的 UI 落实。活动卡内部用Crossfade(150ms)在三视图间平滑过渡。4.2 范围推导ActivityDateRangePolicyActivityDateRangePolicy.kt 提供两个关键函数activityRangeAnchorDate取range.endMs - 1作为锚点日期因为时间范围是半开区间[startMs, endMs)见 TokenStatsTimeRange.kt 的注释约定保证锚点日落在范围内最后一个自然日activityRangeForMode按模式推导活动查询范围——DAILY直接以锚点日为起点WEEKLY回退到锚点日所在周的周日anchorDate.minusDays(anchorDate.dayOfWeek.value % 7)与TokenActivityAggregator的周日为首周对齐保持一致CUMULATIVE以historyStartDate全生命周期最早记录日来自 DAO 的getEarliestOccurredAtMs为起点无历史时返回 null。三种模式最终都调用TokenStatsTimeRanges.customRange(startMs, endMs)生成半开区间的显式日历范围再交给TokenActivityAggregator.rangeData从同一个范围构建 daily/weekly/cumulative 三份视图数据保证口径一致。4.3 数据来源SQL 按本地日期分桶活动数据不落地新表而是对现有token_usage_records实时聚合。TokenUsageDao.kt 的getActivityDaysInRange用 SQLite 的strftime(%Y-%m-%d, occurredAtMs / 1000, unixepoch, localtime)按本地时区自然日分桶日 Token 数为totalInputTokens或未缓存缓存缓存写入的兜底求和加outputTokens并支持providerModels模型筛选。查询服务 TokenStatsQueryService.kt 的activitySnapshot将 DAO 行按日期折叠为MapLocalDate, Long的TokenActivitySnapshot聚合时使用TokenCostCalculator.saturatedAdd饱和加法防溢出。五、三个主题趋势卡与性能统计移除5.1 趋势区收敛Primary / Secondary / Tertiary2_trends_and_performance_removal.md 明确保留成本、请求、Token 三张趋势卡并让它们的容器与强调色绑定 Material 主题的 primary、secondary、tertiary 三种角色移除性能卡片。这与配色体系的约束一致——TokenStatsColors.kt 规定主色只出现在关键数字、图表线条柱形、进度条、选中态与图标上图表、热力图、堆叠分量颜色全部由主色/次色透明度阶推导无硬编码色值三张趋势卡由此获得视觉层级上的区分度。趋势卡采用单卡片指标切换形态TokenStatsTrendCard含ChartDetailMetricCOST / REQUESTS / TOKENS定义于 TokenUsageStatisticsScreen.kt 的私有枚举图表组件为纯 Compose Canvas 实现的TokenStatsStackedBarChart不引入第三方图表依赖紧凑模式通过chartHeight、maxLabels、tooltipCard参数控制堆叠分量的明细口径与展示合计canonical total分离确保 tooltip 与无障碍描述口径正确见 TokenStatsCharts.kt 的注释说明。5.2 性能统计的整链移除方案要求性能统计从采集、存储、聚合、UI、资源、测试、文档七处同时消失采集token tracker 不再记录 TTFT 与生成时长generation-duration存储token 用量记录表不再包含 timing 列。这一点可在当前仓库的实体定义中得到印证——TokenUsageRecordEntity.kt 的字段仅有requestCount、uncachedInputTokens、cachedInputTokens、cacheWriteTokens、totalInputTokens、outputTokens不存在任何 timing/TTFT/时长字段聚合Room 迁移列、DAO 投影与聚合模型均不含性能数据——TokenUsageDao.kt 的全部查询投影如aggregateModelsForLifetime、aggregateModelsInRange、getActivityDaysInRange只涉及请求数与各类 Token 数查询模型 TokenStatsQueryModels.kt 中的TokenStatsTotals、TokenStatsTrendBucket、TokenStatsModelBucket同样只有 Token/请求/成本维度UI 与资源删除性能卡组件、相关字符串与可访问性描述边界保留聊天域chat domain自身的消息时长字段不动只是不再复制进 token 统计——即时长计时仍归聊天模型所有token 统计不再越界采集。5.3 数据库迁移策略由于 PR #950 尚未发布token-statistics 相关的 v20→v21 schema 创建属于未发布产物因此直接编辑该创建脚本为最终 schema删掉 timing 列不新增v21→v22 的迁移或兼容路径。方案在 index.md 中明确声明了这一点配套的验证要求是使用干净的应用程序数据手动验证修订后的未发布 v21 Room schema——即不依赖旧数据升级直接以全新 schema 建库验证。六、验证清单与交付记录3_verification.md 给出了严格的验收清单在窄屏手机布局上逐一检查每日daily、每周weekly、累计cumulative三种活动模式确认短日范围显示前置无历史单元格且真实日期停在右边缘确认长日范围保留横向滚动且打开时定位到最新数据在浅色/深色主题下检查三张趋势卡确认性能趋势、timing 字段与 timing 聚合引用在 token-statistics 实现中彻底消失手动验证未发布 v21 Room schema 时使用干净的应用程序数据。交付记录显示git diff --check无 diff 错误源码与资源检索确认已无残留的 token 统计 timing 字段、聚合类型、性能 UI 或性能字符串本地编译与测试按仓库政策未执行交由推送触发的 GitHub Actions 构建完成。在测试层面TokenActivityAggregatorTest.kt 覆盖了活动聚合的核心语义range data contains every selected calendar day and excludes surrounding activity验证范围外日期不混入、范围内每一天含零 Token 日都存在range data calculates streaks and cumulative totals inside the selected range验证连续天数与累计值的计算口径。这两条测试正是贡献网格只反映所选范围内真实活动、不引入人为活动值的单元级保证。七、关键文件导航关注点仓库路径方案总纲Background/Intent/Scope/Stepsdocs/TODO/token_stats_activity_refresh_20260816/index.mdActivity-First 布局与贡献网格设计docs/TODO/token_stats_activity_refresh_20260816/1_activity_layout.md三主题趋势卡与性能移除设计docs/TODO/token_stats_activity_refresh_20260816/2_trends_and_performance_removal.md验证清单与执行记录docs/TODO/token_stats_activity_refresh_20260816/3_verification.md页面编排LazyColumn 顺序TokenUsageStatisticsScreen.kt贡献网格绘制与交互TokenActivitySection.kt视图模式与范围推导ActivityDateRangePolicy.kt活动聚合与分位数色阶TokenActivityModels.kt按日 SQL 聚合TokenUsageDao.kt活动快照查询服务TokenStatsQueryService.kt时间范围半开区间与桶粒度TokenStatsTimeRange.kt配色令牌含热力图色阶TokenStatsColors.kt用量记录实体无 timing 字段TokenUsageRecordEntity.kt活动聚合单元测试TokenActivityAggregatorTest.kt结语本次重构的核心方法论可以归纳为三点布局上把用户最关心的活动概览提到首屏用控制 摘要 可视化三位一体的方式组织渲染上用前置空单元格算法把任意短范围都铺成满宽右对齐的贡献网格同时严守无历史 ≠ 零 Token的语义边界收敛上趋势区只保留有决策价值的成本/请求/Token 三卡并以主题色分层性能统计则趁未发布之机从采集到测试整链移除避免了遗留兼容包袱。文中所有算法与结论均可在上述源码路径中直接验证感兴趣的读者可以顺着文件导航逐行对照阅读。赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐Metallb社区贡献统计贡献者数量与代码提交趋势Metallb社区贡献统计贡献者数量与代码提交趋势 项目概述 Metallb是Kubernetes环境下基于标准路由协议的网络负载均衡实现项目路径为gh_m云原生网络qdata天眼查数据获取企业信息爬虫的快速实现方案qdata天眼查数据获取企业信息爬虫的快速实现方案 qdata天眼查数据获取是一款高效的企业信息爬虫工具能够帮助用户快速实现企业数据的采集与分析。无论是市场Spleeter社区统计分析贡献者分布与代码提交趋势研究Spleeter社区统计分析贡献者分布与代码提交趋势研究 引言 你是否曾好奇Spleeter这款强大的音乐源分离工具背后的开发力量本文将深入剖析Spleet人工智能深度学习音频处理预训练上一篇GHelper华硕笔记本轻量级控制神器3分钟学会替代Armoury Crate下一篇终极指南用 dayspan-vuetify 快速构建智能日历应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表