8000 feat(web): redesign the pages and keep reads responsive during index rebuilds by InfinityPacer · Pull Request #3 · InfinityPacer/meterleaf · GitHub
[go: up one dir, main page]

Skip to content

feat(web): redesign the pages and keep reads responsive during index rebuilds - #3

Merged
InfinityPacer merged 15 commits into
mainfrom
fix/report-background-rebuild
Sep 24, 2026
Merged

InfinityPacer merged 15 commits into
mainfrom
fix/report-background-rebuild

Conversation

@InfinityPacer
@InfinityPacer InfinityPacer commented Sep 24, 2026 •
Copy link
Copy Markdown
Owner

这个 PR 包含三部分,报表后台重建、计价与账户显示、Web 界面重新设计。NAS 已运行本分支最新提交的候选镜像。

报表后台重建

问题

升级到含 Claude 价格的版本后,NAS 上的页面卡了三分多钟。日志显示,第一次查询在报表线程里全量重建索引,用了 199 秒(report.index_updated initial=true durationMs=199337),其余页面请求全部排在后面,最多等了 124 秒。

有两个原因叠加:

  • 价格表或账户列表变化都会触发全量重建,重建在报表线程里同步进行,期间所有查询排队。
  • 价格表标识从 meterleaf-openai 改为 meterleaf,页面缓存按标识分区,结果整个被清空,连已经打开过的页面也只能等重建。

变化

  • 需要全量重建且同一账本已有旧索引时,改由独立线程把新索引写到旁路文件。报表线程继续用旧索引回答,包括从没缓存过的查询。这些结果标记为刷新中,并标注计算时所用的旧价格版本。新索引建成后替换文件,并刷新全部缓存页面。
  • 两种情况仍然同步重建,旧结果不会跨过这两条边界:账本文件被替换,或者换成了另一套价格表。
  • 价格表新增可选的 supersedes 字段。内置价格表声明接替 meterleaf-openai,从 0.2.x 升级时页面缓存保留。
  • 全量重建时先删除明细表的二级索引,写完再一次性重建。累计索引由新文件重新生成,不再每次改价都在æ– 8000 ‡件里多存一整份。
  • 内置价格表升到 2026-09-25.2,加入 2026-09-22 发布的 GPT-6 Sol 与 GPT-6 Luna。其余 OpenAI 费率对照官方页面未变化。

验证

  • bun run typecheck、bun test tests(298 pass)、bun run build、所改文件 Prettier 与 git diff --check 通过。两个提交各自可以单独通过类型检查与测试。

  • 在生产快照(25.5 万条用量)上模拟从 0.2.1 升级:

    场景 本机耗时 状态
    已缓存页面 37 ms 刷新中,标注旧版本 meterleaf-openai@2026-09-09.3
    未缓存查询 1 ms 刷新中,标注旧版本
    新版本 meterleaf@2026-09-25.2 生效 约 37 s 后 自动切换

    修改前,同样的升级让首个请求阻塞 17.6 s。

  • 全量重建(在已有索引上重建)从 18.8 s 降到 13.3 s。

  • NAS 实测(28.4 万条用量,候选镜像 1372e89):后台重建耗时 67.5 s,之前是 199 s。重建期间,默认视图响应 7–19 ms,从未缓存的日期范围 138–527 ms,均标注旧版本 meterleaf@2026-09-25.1。替换后自动切换到 2026-09-25.2。记录数与自动同步设置不变,无密钥推送仍返回 401。

  • 新增回归测试覆盖以下场景:

    • 后台重建期间,未缓存查询先得到旧版本结果,之后换成新版本结果,并继续接收增量。
    • 替换账本或换另一套价格表时,不会显示旧结果。
    • 旧 id 的缓存按 supersedes 保留。
    • 全量替换后所有二级索引都恢复,失败时回滚。
    • GPT-6 Sol 与 Luna 的订阅 USD、API USD(含长上下文)和 credits。

已知限制

  • 全新安装或旧索引无法判断价格版本时,首次打开仍需等待同步计算。
  • 后台重建期间新写入的用量要等替换后才显示,页面在此期间一直标记为刷新中。

计价与账户显示

  • 内置价格表加入 Codex fast 模式的 credits 费率。
  • 账户可以在 Meterleaf 里起本地别名,不改上游。套餐徽章按用量倍数分三档对齐 Claude 与 ChatGPT(Pro/Plus、Max 5x/Pro 5x、Max 20x/Pro 20x),用上游平台区分 Claude Pro 与 ChatGPT Pro 20x。本机采集器的账户名改为「Claude Code」。

Web 界面重新设计

  • 全部页面换成中性灰底,配色可选翠绿(默认)、靛蓝、黑白,与明暗模式独立。数字用 Geist 字体。
  • 总览改为账本式汇总加账户列表。账户行在 1251px 以上分五列,两个额度窗口的账户在 1001–1250px 分两行,901–1000px 全部分两行。
  • 没有有效额度时显示一条虚线说明,例如「额度快照已过期,最近一次采样 09/16 22:45,上报后自动恢复。」
  • 7d 额度有效且没用完时,上游报过的 5h 窗口即使过期或缺值也保留位置,显示「等待新采样」,不沿用旧百分比。周额度用完时只显示 7d。
  • 趋势图按真实时间排列,在首尾已有数据之间把空档补 0,首尾之外不补。
  • 登录被拦截(重定向、401、403)时显示「登录已过期」并提供重新登录,不再当作读取失败反复重试。离线缓存预存站点图标。
  • 同步失败提示改成出错步骤、原因和建议,不再显示阶段代码和错误编号。
  • 迷你趋势图的提示框不再被卡片裁切。

验证

  • bun run typecheck 通过,bun test tests 313 pass。新增 continuous-points、session、sync-failure-message 测试,更新 quota-display、mobile-home、pwa 测试。
  • 浏览器脚本 quota-pwa-layout(48 个状态,含 16 个双额度窗口和 3 个安全区状态)、browser.smoke、overview-account-usage 通过,截图在已忽略的 test-results。
  • NAS 候选镜像 ff71c86(28.4 万条用量):后台索引重建 66.8 s,期间默认视图 9–15 ms、未缓存范围 154–507 ms。记录数与自动同步设置不变,无密钥推送返回 401。

已知限制

  • 未运行 mobile-app、visual-feedback、appearance-sorting、button-contrast、mobile-request-layout、pwa、account-management、report-preferences、sync-refresh。其中后三个需要先按新结构更新脚本。
  • 「等待新采样」与「登录已过期」两种状态只在单元测试中覆盖,真实页面要等对应情况出现时确认。
  • 视觉验收由维护者确认。

🤖 Generated with Claude Code

https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z

InfinityPacer and others added 14 commits September 25, 2026 02:08
A price book change or a new account forces a full report index rebuild.
On the NAS that took over three minutes on the report worker, and every
page request queued behind it. The rebuild now runs in a separate builder
thread into side files while the worker keeps answering from the previous
index. Those answers are marked as refreshing and labeled with the price
version they were computed with. The files are swapped when the build
completes and every cached view is refreshed. A replaced ledger or a
different price book identity still rebuilds inline, so old results never
cross those boundaries.

A full rebuild also drops and recreates the secondary indexes around the
bulk insert, which is about 30% faster on the production snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The bundled book moves to 2026-09-25.2 with GPT-6 Sol and GPT-6 Luna at
the rates OpenAI published on 2026-09-22. No existing OpenAI rate changed.
It also declares that it supersedes meterleaf-openai, the id used through
0.2.1, so cached report views survive the upgrade instead of being thrown
away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
A first build, a replaced ledger, an unrelated price book or a failed
background build used to rebuild inline and hold every query. Reads now
show the previous index whenever it belongs to the same ledger, and
otherwise answer 202 building so the page shows progress and polls.
The builder switches its files out of WAL before closing, because the
macOS system SQLite keeps WAL files on close and the swap was silently
falling back to an inline rebuild there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Accounts can be renamed inside Meterleaf without touching the upstream.
Plan badges align by usage multiple across Claude and ChatGPT, using the
upstream platform to tell Claude Pro apart from ChatGPT Pro 20x. The
collector names its account "Claude Code" and model colors no longer
collide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The Web and PWA pages follow the accepted redesign. Neutral grays
replace the tinted backgrounds, numbers use Geist, and the palette
option offers brand green, indigo or mono independent of light and
dark. Charts read the active theme colors instead of a dark flag.

The overview becomes a ledger strip plus one account list that
shares the accounts page row, with the 7d estimate drawn as a line.
The Credits card appears only when there is Credits usage, and an
expired quota snapshot shows its last sample time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The 7d estimate column stacks its label, a short line and the amount
without a caption, and an account without a valid quota shows one
dashed note across the quota columns. The overview tabs keep equal
widths and the same offset on both views.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
A five-hour window that expired or lost its value used to disappear
until the next sample, so it flickered with the collection cadence.
While the weekly quota is valid and not used up, the slot stays and
says it is waiting for a new sample, without old percentages or costs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Trend series only carry buckets that had requests, so quiet hours and
days collapsed on the axis. Charts now add zero buckets between the
first and last reported ones. Time outside that span stays absent
because the ledger may not cover it. Account rows also give the name
column the width of the design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Behind an authentication gateway an expired session redirected ledger
reads to the login page, which surfaced as a network failure and a
broken brand icon. Ledger reads now follow redirects manually, show a
re-login prompt without retrying, and the service worker precaches
the brand icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The sync panel showed stage codes and an error id that readers could
not act on. It now names the step that failed and what to check, and
the id stays in the server log. The green palette is renamed 翠绿 to
match the other two-character names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The tooltip of a mini trend was confined to a chart a few dozen pixels
tall and clipped by its card. It now renders on the page body with the
time and value on separate lines. The expired quota note is shorter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
Between 1001 and 1250px the base two-row areas and the desktop
five-column grid applied together, squeezing accounts with two quota
windows into the left half. These rows now use the two-row layout up
to 1250px with the menu in the top corner, and the five-column row
above. The docs describe the overview showing lifetime usage first
for accounts without a quota.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
The smoke, layout, sync and quota specs asserted the old overview
cards, colors and sync error ids. They now check the shared account
rows, the waiting five-hour slot, plain sync failure text and that
quota rows keep one header line at desktop widths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
@InfinityPacer InfinityPacer changed the title fix(report): keep pages responsive while the report index rebuilds feat(web): redesign the pages and keep reads responsive during index rebuilds Sep 24, 2026
9D17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012bEAuLoXyJtm1PRMVvDi3z
@InfinityPacer
InfinityPacer merged commit 7bcb74e into main Sep 24, 2026
2 checks passed
@InfinityPacer
InfinityPacer deleted the fix/report-background-rebuild branch October 21, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0