CodexBar 成本窗口对比功能解析:基于本地日志的 7/30/90 天对比周期设计
【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar
导读
CodexBar 是一款无需登录即可展示 OpenAI Codex 与 Claude Code 用量统计的菜单栏工具。本文围绕docs/cost-window-comparisons.md中的成本窗口对比(cost window comparison)设计方案,深入讲解"在保留历史窗口作为最大扫描范围的前提下,通过一个默认关闭的可选偏好,为成本卡片额外添加 7/30/90 天固定周期总计"这一产品形态的来龙去脉。读完本文,你将理解该功能的完整产品决策、源码级聚合原理、UI 落地方式,以及它为何刻意不宣称"终身成本"。
背景:两个 Issue 驱动的成本窗口诉求
成本窗口对比功能源自两个产品诉求(Issue #1500 与 #1708):
- #1500:希望在既有历史窗口之外,提供更短周期的对比数据,让用户快速看到"最近 7 天 vs 最近 30 天"的用量与费用走势。
- #1708:更进一步,希望展示"自安装以来的全部成本",即生命周期(lifetime)账单。
两者看似相近,但在 CodexBar 的本地日志模型下,契约完全不同。文档的核心结论是:#1500 可以通过对已加载数据的纯派生计算低成本落地;而 #1708 需要一份全新的追加式本地账本(append-only local ledger),必须先获得数据保留契约层面的审批,不能混为一谈。
产品形态:最大扫描窗口 + 可选的更短对比周期
设计方案的核心约束是不改变现有的扫描语义:
- 保留现有的"历史窗口(History window)"设置,它继续作为本地扫描的最大时间范围。
- 新增一个**默认关闭(opt-in)**的偏好项Show shorter comparison periods(显示更短的对比周期)。
- 开启后,成本卡片会在既有周期行之外,追加固定为7 天、30 天、90 天的三个总计,且只显示落在所选历史窗口之内的周期。
文档给出的三种典型窗口下的效果示例:
| 历史窗口设置 | 成本卡片显示的周期 |
|---|---|
| 30 天历史 | Today、Last 30 days、Last 7 days |
| 90 天历史 | Today、Last 90 days、Last 7 days、Last 30 days |
| 365 天历史 | Today、Last 365 days、Last 7 days、Last 30 days、Last 90 days |
可以看到:对比周期是"嵌套"在历史窗口内部的,永远小于等于主窗口天数;例如历史窗口只有 30 天时,就不会出现 90 天对比行,因为那已经超出本地可扫描的数据范围。
实现原理:一切对比都从已加载的每日报告派生
该功能最核心的设计承诺是零额外成本:
实现从已经加载的每日报告(daily report)中派生每一个对比周期。它不会扩大扫描范围、不会增加网络请求、不会保留新数据,也不会改变 provider 来源的选择。
这一承诺在源码中得到了完整印证。核心聚合逻辑位于 Sources/CodexBarCore/CostUsageModels.swift:
public func comparisonSummaries( periods: [Int] = [7, 30, 90], calendar: Calendar = .current) -> [CostUsageWindowSummary] { Array(Set(periods.map { max(1, $0) })) .filter { $0 < self.historyDays } .sorted() .map { self.summary(forLastDays: $0, calendar: calendar) } }逐行解读:
periods默认值即文档约定的7、30、90;max(1, $0)与Set(...)负责去重并防御非法值(如 0 或负数);filter { $0 < self.historyDays }是"只显示落在历史窗口内"的硬性过滤——90 天历史下只会产出 7 和 30 两个对比周期,与文档示例完全一致;- 随后按升序排列,逐个调用
summary(forLastDays:)完成窗口聚合。
而单窗口聚合summary(forLastDays:)(Sources/CodexBarCore/CostUsageModels.swift)揭示了"缺失日历日 = 零用量日"的具体含义:它基于calendar.startOfDay(for:)计算今天,向前回溯days - 1天得到起始日,再通过CostUsageLocalDay.key(from:calendar:)生成起止键,仅筛选出落在该键区间内的每日条目进行累加。没有记录的日历日本来就不在daily数组中,因此不会贡献任何成本或 token——"Last 7 days" 统计的是真实的 7 个日历日,而不是"最近 7 个有记录的行"。
另外值得注意的实现细节:当窗口天数覆盖完整历史(coversFullHistory = days >= self.historyDays)时,聚合结果会附带meteredCostUSD(计费口径成本),并携带CostProvenance.forWindow来源标记,保证同一份数据在菜单与内联面板中口径一致。
设置项与 UI 落地
偏好存储:默认关闭
偏好键为costComparisonPeriodsEnabled,读取逻辑位于 Sources/CodexBar/SettingsStore.swift:
let costComparisonPeriodsEnabled = userDefaults.object( forKey: "costComparisonPeriodsEnabled") as? Bool ?? false?? false即"默认关闭、用户主动开启"的 opt-in 语义,与文档"现有 UI 与扫描成本在用户选择开启前保持不变"的要求一一对应。
偏好设置界面
该开关出现在菜单偏好设置面板中,见 Sources/CodexBar/PreferencesMenuPane.swift,文案取自本地化字符串表:
- 标题:
cost_comparison_periods_title= "Show shorter comparison periods" - 副标题:
cost_comparison_periods_subtitle= "Add 7, 30, and 90-day totals when they fit inside the selected history window. These totals reuse the same local scan."
这两条文案定义于 Sources/CodexBar/Resources/en.lproj/Localizable.strings。副标题精确传达了产品承诺:"仅当落在所选历史窗口内才显示""复用同一次本地扫描"。该字符串已被同步翻译到仓库内的 20 余种语言(zh-Hans、ja、de、fr、es 等),例如简体中文为"显示更短的对比周期"、副标题"当 7 天、30 天和 90 天处于所选历史窗口内时,添加相应汇总。这些汇总复用同一次本地扫描。"
渲染接入点
开启偏好后,对比行在两类界面中同时生效:
- 菜单成本卡片:Sources/CodexBar/MenuCardView+Costs.swift 中
comparisonLines: comparisonPeriodsEnabled ? snapshot.comparisonSummaries().map { ... } : nil,周期标签经由localizedPeriodLabel本地化(如 "last 7 days" → 对应语言文案); - 内联用量面板:Sources/CodexBar/InlineUsageDashboardContent.swift 同样在开启时把
comparisonSummaries()的产出追加到明细行。
两处都通过MenuCardView的输入管道(见 Sources/CodexBar/MenuCardView.swift 的input.costComparisonPeriodsEnabled)传递开关状态,因此渲染层无需关心开关的持久化细节。
为什么这个功能不宣称"终身成本"
文档用一整节厘清一个容易混淆的概念:"所有可用本地日志"与"自安装以来的终身成本"是两种不同的契约。原因包括:
- 本地 Codex、Claude 与 Pi 的日志可能被移动、修剪、排除,也可能在 CodexBar 安装之前就已存在;
- 现有的 plan-utilization 历史同样有上限,而且不包含 token 或成本账本(ledger);
- 因此,即便开启 365 天历史窗口,365 天总计也不能诚实地标注为终身账单。
基于此,一个真正的 #1708 实现需要单独的审批流程,并配套一份追加式本地账本,文档列出了该账本必须具备的要素:
- 明确的收集起始日期与完整性状态(completeness state);
- provider/account 的所有权与重置行为;
- 迁移、保留、导出与删除控制;
- 隐私文档与有边界的存储测试;
- UI 措辞必须区分"观察到的用量快照"与"本地日志估算成本"。
最终建议是:#1500 的可选对比行可以独立发布,而 #1708 保持开放,直到账本/数据保留契约获批;任何未来仅基于扫描数据的总计,都应标注为Available local logs(可用本地日志),绝不可标注为Lifetime(终身)。
维护者决策选项
文档为落地提供了三个递进的选择:
- 推荐方案:偏好默认关闭后合并。现有 UI 与扫描成本在用户主动开启前完全不变,风险最低;
- 偏好默认开启。开箱即用更有价值,但会为存量用户增加菜单垂直密度;
- 仅保留数据模型、暂不发布设置项,先复访展示方案。这样既能保留已测试的日历窗口聚合能力,又不需要引入新设置。
从源码现状看,comparisonSummaries()作为CostUsageDailyReport的纯派生方法已经就绪,渲染层两处接入点也已具备,落地的剩余成本集中在"开关默认值"与"菜单密度权衡"这两个产品决策上。
总结
成本窗口对比是 CodexBar 在"不扩大扫描、不新增网络请求、不改变 provider 来源"约束下,通过纯派生计算增强成本卡片可读性的一个典型案例。其设计精髓在于严格划分了三种边界:历史窗口负责扫描上限,对比周期负责视角嵌套,而"终身成本"则被明确划归到未来的独立账本契约。理解这一分层,既能帮助使用者正确解读菜单卡片上的每一行数字,也能为在本地日志模型上设计类似的聚合功能提供可复用的决策框架。
【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考