feat: E1002 宽版用量配色改三档绿/黑/红+今日实时与近7天版式重做

- 用量与 pace 配色由「红/黑」二档、黄「注意」档,改为绿(健康)/黑(正常)/红(危险)三档,新增 USAGE_OK/PACE_TOL 阈值,去掉黄色进度条
- Claude/ChatGPT 用量行标签改用 statusline 同款长标签(all 5h/fable 7d 等)
- 今日实时区块改为指标名角标+数值与环比同比整组居中;近7天区块改为真实数据表(取30天趋势缓存最后7天),不画折线
- renderer.py 抽出 _cmp_at/_pace_color 钩子+新增 F36 字体供宽版复用;CLAUDE.md 同步版式与配置项说明

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 15:42:17 +08:00
parent 110f710ff0
commit 6dc89b504e
5 changed files with 90 additions and 50 deletions

35
data.py
View File

@@ -218,13 +218,14 @@ def _fmt_reset(resets_at, window):
return dt.strftime("%H:%M") if window <= 86400 else dt.strftime("%m/%d %H:%M")
# Claude 用量窗口:(快照键,标签窗口长度秒scopedscoped=True 为「按模型限定」的额度
# Fable 7d来自 /api/oauth/usage 的 limits[],由 statusline 写入快照 seven_day_fable
# 400x300 三色小屏版面放不下第三行,只在 800x480 宽版展示。
# Claude 用量窗口:(快照键,短标签,长标签窗口长度秒scoped
# 短标签 k 给 400x300 小屏(标签列只有 26px长标签 label 给 800x480 宽版,沿用 statusline 的写法
# all 5h / all 7d / fable 7d小写开头。scoped=True 为「按模型限定」的额度Fable 7d来自
# /api/oauth/usage 的 limits[],由 statusline 写入快照 seven_day_fable小屏放不下第三行只在宽版展示。
USAGE_WINDOWS = (
("five_hour", "5h", FIVE_HOUR, False),
("seven_day", "7d", SEVEN_DAY, False),
("seven_day_fable", "Fable 7d", SEVEN_DAY, True),
("five_hour", "5h", "all 5h", FIVE_HOUR, False),
("seven_day", "7d", "all 7d", SEVEN_DAY, False),
("seven_day_fable", "Fable 7d", "fable 7d", SEVEN_DAY, True),
)
@@ -234,8 +235,8 @@ def _usage(now):
utilization 来自 statusline 从 Claude Code stdin 落盘的权威实时值Fable 行来自其转存的 API limits
time_pct(时间已流逝%) 与 pace 每次按当前时间**现算**resets_at 为绝对时间)。
文件缺失/损坏 → 回退占位 pct=Nonerenderer 显示 "--");快照里没有某窗口(如旧版 statusline 无 Fable同样 "--"
结构契约renderer 依赖):{title, warn, caution, updated?, bars:[{k, pct(0~100 或 None), time_pct?, reset?, scoped}]}
pct ≥ warn 时该条进度条与百分比转红pace = pct time_pct>0 超前↑ / <0 节余↓);
结构契约renderer 依赖):{title, warn, ok, pace_tol, updated?, bars:[{k, label, pct(0~100 或 None), time_pct?, reset?, scoped}]}
pct ≥ warn 时该条进度条与百分比转红(宽版另有 pct ≤ ok 绿档)pace = pct time_pct>0 超前↑ / <0 节余↓,宽版按 ±pace_tol 分三色
reset 为重置时刻文本updated 为快照落盘时刻 HH:MM活跃使用时持续刷新空闲时停在最后一次
"""
raw = {}
@@ -245,11 +246,11 @@ def _usage(now):
print(f"[警告] Claude 用量读取失败:{e}")
bars = []
for key, label, win, scoped in USAGE_WINDOWS:
for key, k, label, win, scoped in USAGE_WINDOWS:
seg = raw.get(key) or {}
util = seg.get("utilization")
pct = int(round(float(util))) if util is not None else None
bar = {"k": label, "pct": pct, "scoped": scoped}
bar = {"k": k, "label": label, "pct": pct, "scoped": scoped}
tp = _window_time_pct(seg.get("resets_at"), win, now)
if pct is not None and tp is not None:
bar["time_pct"] = tp
@@ -257,7 +258,7 @@ def _usage(now):
if rs:
bar["reset"] = rs
bars.append(bar)
out = {"title": "Claude Usage", "warn": config.USAGE_WARN, "caution": config.USAGE_CAUTION, "bars": bars}
out = {"title": "Claude Usage", "warn": config.USAGE_WARN, "ok": config.USAGE_OK, "pace_tol": config.PACE_TOL, "bars": bars}
upd = _parse_reset_ts(raw.get("updated_at"))
if upd:
out["updated"] = datetime.fromtimestamp(upd).strftime("%H:%M")
@@ -267,16 +268,16 @@ def _usage(now):
def _usage_gpt(now):
"""ChatGPT 用量——**目前为 mock 占位**mock=Truerenderer 在标题右侧标「示例数据」)。
结构与 _usage 完全一致,后续接入真实数据源时只需替换本函数、保持返回结构不变。
mock 取值刻意覆盖多种配色5h 42%、7d 71%黄「注意」、Codex 7d 55%scoped 与左侧 Fable 行对称)。"""
mock 取值刻意覆盖宽版三档配色5h 88%≥warn 红、7d 71%、Codex 7d 22%≤ok 绿scoped与左侧 fable 行对称)。"""
bars = []
for label, pct, win, remain, scoped in (("5h", 42, FIVE_HOUR, 2 * 3600, False),
("7d", 71, SEVEN_DAY, 3 * 86400, False),
("Codex 7d", 55, SEVEN_DAY, 3 * 86400, True)):
for k, label, pct, win, remain, scoped in (("5h", "all 5h", 88, FIVE_HOUR, 2 * 3600, False),
("7d", "all 7d", 71, SEVEN_DAY, 3 * 86400, False),
("Codex 7d", "codex 7d", 22, SEVEN_DAY, 3 * 86400, True)):
reset_ts = now.timestamp() + remain
bars.append({"k": label, "pct": pct, "scoped": scoped,
bars.append({"k": k, "label": label, "pct": pct, "scoped": scoped,
"time_pct": _window_time_pct(reset_ts, win, now),
"reset": _fmt_reset(reset_ts, win)})
return {"title": "ChatGPT Usage", "warn": config.USAGE_WARN, "caution": config.USAGE_CAUTION,
return {"title": "ChatGPT Usage", "warn": config.USAGE_WARN, "ok": config.USAGE_OK, "pace_tol": config.PACE_TOL,
"bars": bars, "mock": True}