Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

feat(tui): add monthly and weekly period tabs - #57

Merged
makoMakoGo merged 3 commits into
personal/local-clientsfrom
feat/weekly-monthly-tabs
Jun 14, 2026
Merged

feat(tui): add monthly and weekly period tabs#57
makoMakoGo merged 3 commits into
personal/local-clientsfrom
feat/weekly-monthly-tabs

Conversation

@makoMakoGo

@makoMakoGo makoMakoGo commented Jun 14, 2026

Copy link
Copy Markdown
Owner

What Changed

  • Add top-level Monthly and Weekly TUI tabs.
  • Derive monthly and ISO-week period views from existing daily aggregates.
  • Add period detail views aligned with Daily detail behavior.
  • Remove the Minutely tab, settings flag, data aggregation, cache special-case, and obsolete time-table UI helper.
  • Update TUI tab/header/footer behavior and docs to match the new view set.
  • Record ADR 0010: period views coarser than daily derive from daily instead of adding per-message hot-loop accumulators.

Why

Monthly and Weekly are coarser than daily, so they can be derived losslessly from daily usage instead of folding every message again. This keeps the per-message aggregation loop lean and avoids recreating the old Minutely gating/caching complexity.

Validation

  • rtk cargo fmt --check
  • rtk cargo test -p tokscale-cli
  • rtk rg -n "Minutely|minutely|minutelyTabEnabled|with_minutely_enabled|minute_bucket|MinutelyUsage|get_sorted_minutely|Tab::Minutely" .

Notes

This PR targets personal/local-clients in makoMakoGo/tokscale; opening against main would include unrelated branch history.

Summary by Sourcery

Add monthly and weekly period-based usage views to the TUI and remove the legacy minutely view and its per-message aggregation path, deriving coarse period data from existing daily aggregates instead.

New Features:

  • Introduce Monthly and Weekly tabs in the TUI for period-based usage summaries.
  • Add period detail views that mirror Daily detail behavior, including per-model breakdowns and sorting.

Enhancements:

  • Derive monthly and ISO-week period usage from existing daily aggregates via reusable period-building helpers.
  • Update tab navigation, header rendering, footer help text, and default initial tab to account for the new Monthly and Weekly views.
  • Simplify data loading and background refresh by removing the minutely-enabled toggle, related cache behavior, and sort cache plumbing.

Documentation:

  • Update README files (EN and localized variants) to document Monthly and Weekly views and remove references to the Minutely tab and its configuration.
  • Record ADR 0010 specifying that coarser-than-daily period views are derived from daily aggregates rather than per-message accumulation.

Tests:

  • Adjust and extend TUI, layout, and cache tests to cover the new Monthly/Weekly period behavior and detail views while removing Minutely-specific coverage.

Summary by CodeRabbit

  • 新功能

    • 新增Monthly和Weekly视图,支持按月度和周度浏览token使用数据。
    • 添加周期详情视图,深钻查看特定月份/周的数据分解(按提供方、模型、来源分类)。
    • TUI交互视图从6个扩展至8个核心视图。
  • 移除

    • 移除Minutely分钟级视图及相关配置选项。

@sourcery-ai

sourcery-ai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduce Monthly and Weekly TUI period views derived from existing daily aggregates, add matching detail views and layout logic, and remove the Minutely tab along with its data aggregation, caching, settings and documentation, while documenting the architectural decision that coarser-than-daily period views must be built from daily data.

Sequence diagram for opening monthly/weekly period detail view

sequenceDiagram
    actor User
    participant App
    participant Data as tui_data
    participant PeriodUi as period_rs

    User->>App: handle_key_event(KeyCode::Enter)
    App->>App: current_tab == Tab::Monthly | Tab::Weekly
    App->>App: open_selected_period_detail(PeriodKind)
    App->>App: get_sorted_periods(kind)
    App->>Data: build_period_usage(&data.daily, kind)
    Data-->>App: Vec<PeriodUsage>
    App->>App: store PeriodDetailSelection
    App->>App: enter_period_detail_sort_context()
    App-->>User: status "Viewing period details..."

    loop subsequent frames
        App->>PeriodUi: render_monthly/render_weekly
        PeriodUi->>App: is_period_detail_active()
        PeriodUi->>App: get_sorted_period_detail_rows()
        App-->>PeriodUi: Vec<PeriodDetailRow>
    end
Loading

File-Level Changes

Change Details Files
Add Monthly and Weekly tabs and navigation, including period list/detail modes with sorting and keyboard/mouse behavior aligned to the existing Daily detail UX.
  • Extend Tab enum with Monthly and Weekly entries, update tab ordering, labels, and keyboard cycling logic.
  • Track period selection and sort context in App state (selection, scroll offsets, sort-before-detail, and detail sort state) analogous to daily detail handling.
  • Wire Enter/Esc on Monthly/Weekly tabs to open/close period detail mode and ensure tab switches and refreshes correctly enter/exit detail state.
crates/tokscale-cli/src/tui/app.rs
Implement reusable period (monthly/weekly) aggregation and detail model derived from daily usage, and remove minutely aggregation from the data loader and model.
  • Introduce PeriodKind and PeriodUsage types plus build_period_usage and helper utilities to fold DailyUsage into monthly or ISO weekly aggregates, preserving per-source/model breakdown.
  • Add PeriodDetailSelection/PeriodDetailRow and getters for sorted period lists and period detail rows, including sort behavior by cost/tokens/date.
  • Delete MinutelyUsage, the minutely map, timestamp/minute bucketing helpers, and all uses of the minutely_enabled flag in DataLoader and UsageData.
crates/tokscale-cli/src/tui/app.rs
crates/tokscale-cli/src/tui/data/mod.rs
Add period (monthly/weekly) list and detail table rendering, and adjust header/footer UI and help text to reflect the new period views and removal of Minutely.
  • Introduce tui/ui/period.rs to render monthly/weekly tables with adaptive column layouts, section headers by year, highlighted current period, and model detail tables sharing the existing model-usage layout primitives.
  • Update header tab rendering to compute label mode (full vs short) based on available width and include Monthly/Weekly tabs in click areas and tests.
  • Update footer counts and help row to show months/weeks and period detail enter/exit shortcuts, removing Minutely-specific label and tests.
crates/tokscale-cli/src/tui/ui/period.rs
crates/tokscale-cli/src/tui/ui/header.rs
crates/tokscale-cli/src/tui/ui/footer.rs
crates/tokscale-cli/src/tui/ui/mod.rs
Remove Minutely tab feature flagging, caching behavior, and CLI/TUI wiring, and update documentation and plans to match the new period-view architecture.
  • Drop minutelyTabEnabled from Settings (including serde schema and tests) and stop passing a minutely_enabled parameter through background_data_loader, run_loop_with_background, and cache loading.
  • Simplify cache loading semantics by removing the "minutely required for freshness" path and all tests that relied on minutely fields.
  • Update CLI default initial tab to Monthly, adjust architecture/memory plan docs to no longer mention minutely-specific aggregation, and add ADR 0010 describing the rule that coarse period views derive from daily.
crates/tokscale-cli/src/tui/settings.rs
crates/tokscale-cli/src/tui/mod.rs
crates/tokscale-cli/src/tui/cache.rs
crates/tokscale-cli/src/main.rs
docs/plans/2026-06-13-architecture-track.md
docs/plans/2026-06-12-memory-optimization.md
docs/adr/0010-period-views-derive-from-daily.md
Align README documentation in multiple languages with the new Monthly/Weekly views and the removal of Minutely configuration.
  • Update English README to list Monthly/Weekly as core TUI views, describe Enter behavior as opening period/day detail, and remove the minutelyTabEnabled configuration and section.
  • Update Chinese, Japanese, and Korean READMEs to mirror the same changes: describe 8 core views including Monthly/Weekly, adjust keyboard help, and drop Minutely-related settings/sections.
README.md
README.zh-cn.md
README.ja.md
README.ko.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f9cead9-5a64-4d33-bfae-30d4b0accba7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/weekly-monthly-tabs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • There are several call sites that recompute build_period_usage directly (e.g., update_data, get_current_list_len, period_detail_label, get_sorted_period_detail_rows, current_count_label); consider routing these through get_sorted_periods (or a shared helper) so the aggregation and ordering logic live in one place and you avoid repeated recomputation per render.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There are several call sites that recompute `build_period_usage` directly (e.g., `update_data`, `get_current_list_len`, `period_detail_label`, `get_sorted_period_detail_rows`, `current_count_label`); consider routing these through `get_sorted_periods` (or a shared helper) so the aggregation and ordering logic live in one place and you avoid repeated recomputation per render.

## Individual Comments

### Comment 1
<location path="crates/tokscale-cli/src/tui/app.rs" line_range="1795-1804" />
<code_context>
+    pub fn get_sorted_periods(&self, kind: PeriodKind) -> Vec<PeriodUsage> {
</code_context>
<issue_to_address>
**question (bug_risk):** Ascending cost/tokens sorts always prefer newer years; confirm this cross-year ordering is intentional.

In `get_sorted_periods`, the `Cost` and `Tokens` ascending branches still sort `section_year` in descending order:

```rust
(SortField::Cost, SortDirection::Ascending) => periods.sort_by(|a, b| {
    b.section_year
        .cmp(&a.section_year)
        .then_with(|| a.cost.total_cmp(&b.cost))
        .then_with(|| b.start_date.cmp(&a.start_date))
}),
```

So newer years always come first, and only within a year is the ordering truly ascending. If global ascending order is desired, you’d need to flip the year comparison for ascending sorts (or drop the year as a key). If the "newest-year-first" grouping is intentional, consider adding a short comment near this sort to document that behavior.
</issue_to_address>

### Comment 2
<location path="crates/tokscale-cli/src/tui/ui/period.rs" line_range="587-591" />
<code_context>
+    )
+    .height(1);
+
+    let detail_len = rows_data.len();
+    let start = scroll_offset.min(detail_len);
+    let end = (start + visible_height).min(detail_len);
+
+    if start >= detail_len {
+        return;
+    }
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid early-return when scroll_offset exceeds current detail length to prevent a blank body.

In `render_detail`, the early `return` when `start >= detail_len` means a stale `scroll_offset` (e.g., after resize or data refresh) can render only the border/title with no rows or empty-state message. Instead of returning, either clamp `scroll_offset` here (e.g., derive `start` from `detail_len.saturating_sub(visible_height)` and recompute) or fall back to `start = 0` when `detail_len > 0` so some rows are always shown. The same applies to the `if start >= period_len { return; }` branch in `render_period`, which should use similar clamping to avoid blank views when indices drift.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread crates/tokscale-cli/src/tui/app.rs
Comment thread crates/tokscale-cli/src/tui/ui/period.rs Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the high-cardinality, per-message "Minutely" tab with new "Monthly" and "Weekly" period views in the TUI. Following the newly introduced ADR 0010, these coarser period views are derived efficiently on-demand from the already-aggregated daily buckets rather than being re-folded per-message in the main loop. This change allows for the removal of the minutely aggregation logic, its configuration settings, and associated caching mechanisms. A review comment identifies an issue in the Period Detail view where the model name could appear blank if display_name is empty, suggesting a fallback to the map's model key.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/tokscale-cli/src/tui/app.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
README.md (1)

233-233: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

monthly 示例改成“月/周期视图”描述。

现在四个 README 里 tokscale monthly 都被写成了“Daily view/每日视图”,会误导读者。请把这行注释统一改成和新月/周视图一致的文案,必要时再补一个 weekly 示例。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 233, The comment for the tokscale monthly command is
incorrectly labeled as "Daily view (shows daily breakdown)" when it should
describe a monthly or periodic view instead. This misleading documentation
appears across multiple README files in different languages. Update the comment
at README.md line 233 to correctly describe monthly/periodic view behavior, then
apply the same correction to the corresponding lines in README.zh-cn.md (line
228), README.ja.md (line 228), and README.ko.md (line 227). Optionally add a
separate weekly example command if appropriate to provide complete documentation
of the time period options.
README.zh-cn.md (1)

552-570: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

把简中配置示例补上 scanner.extraScanPaths

英文版已经把这个配置项正式写进配置文档了,但简中 README 里还没出现,用户会找不到这项设置。请在 JSON 示例和表格里一起补上。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.zh-cn.md` around lines 552 - 570, The Simplified Chinese README is
missing the scanner.extraScanPaths configuration entry that already exists in
the English version. Add scanner.extraScanPaths to both the JSON configuration
example and the configuration table in README.zh-cn.md. Include it in the JSON
example with an appropriate default value (empty array), and add a corresponding
row to the configuration documentation table with the proper type (string[]),
default value, and Chinese description matching the functionality described in
the English version.
README.ja.md (1)

248-270: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

把日文/韩文 README 同步到新的 8 视图 + 配置项。

这两份本地化文档还停留在旧的 6 视图文案,而且配置块里还缺 scanner.extraScanPathsusageTabEnabled;读者在这些语言版本里看不到完整的新入口。

  • README.ja.md#L248-L270: 改成 8 视图说明,去掉旧的 6 视图表述。
  • README.ja.md#L520-L538: 补回 scanner.extraScanPathsusageTabEnabled
  • README.ko.md#L248-L269: 同样更新。
  • README.ko.md#L520-L537: 同样补回这两项。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.ja.md` around lines 248 - 270, Update the localized Japanese and
Korean README files to reflect the expanded interactive TUI mode and new
configuration options. At README.ja.md lines 248-270 (anchor site), update the
"6つのビュー" section to describe 8 views instead of 6, and remove outdated view
descriptions to match the current feature set. At README.ja.md lines 520-538
(sibling site), add the missing configuration items for scanner.extraScanPaths
and usageTabEnabled to the settings documentation block. Apply the identical
changes to README.ko.md: update lines 248-269 to change from 6 views to 8 views
with accurate descriptions, and at lines 520-537, add the same two missing
configuration items (scanner.extraScanPaths and usageTabEnabled) to maintain
parity with the Japanese and English versions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tokscale-cli/src/tui/app.rs`:
- Around line 1798-1828: The sorting logic for Cost and Tokens is not performing
global sorting because it always compares section_year first before comparing
the actual sort field, causing items from later years to always rank before
items from earlier years regardless of their cost or token values. To fix this,
remove the section_year comparison from the beginning of all Cost and Tokens
sorting cases (both Ascending and Descending variants) in the match statement,
so that the primary sort is directly by the cost or tokens value with start_date
as the secondary criterion. The section_year grouping should only be applied
when sorting by the Date field. Additionally, add a test case that verifies
items are sorted correctly across different years when sorting by Cost or Tokens
to prevent regression.

In `@crates/tokscale-cli/src/tui/data/mod.rs`:
- Around line 953-955: The current logic in the active_days counting only
considers a day active if day.tokens.total() > 0, but this misses days that have
user activity (messages or conversation rounds) even when no tokens were
consumed. Modify the condition in the block where entry.active_days is being
incremented to check for actual activity such as message_count or interaction
rounds first, before checking tokens, so that days with conversations but zero
token consumption are still correctly counted as active days.

In `@crates/tokscale-cli/src/tui/ui/period.rs`:
- Around line 433-475: The function top_period_model incorrectly aggregates
models using only model_key as the BTreeMap entry key, which causes models with
the same name from different providers to be merged together, leading to
inflated token counts and incorrect provider/color information. Change the
aggregation key in the models BTreeMap from just model_key to a compound key
that includes the provider (such as a tuple of provider and model_key, or use
color_key as the aggregation key). Update the entry call to use this new
compound key while maintaining the original label, provider, and color_key
values in the inserted TopPeriodModel records.

---

Outside diff comments:
In `@README.ja.md`:
- Around line 248-270: Update the localized Japanese and Korean README files to
reflect the expanded interactive TUI mode and new configuration options. At
README.ja.md lines 248-270 (anchor site), update the "6つのビュー" section to
describe 8 views instead of 6, and remove outdated view descriptions to match
the current feature set. At README.ja.md lines 520-538 (sibling site), add the
missing configuration items for scanner.extraScanPaths and usageTabEnabled to
the settings documentation block. Apply the identical changes to README.ko.md:
update lines 248-269 to change from 6 views to 8 views with accurate
descriptions, and at lines 520-537, add the same two missing configuration items
(scanner.extraScanPaths and usageTabEnabled) to maintain parity with the
Japanese and English versions.

In `@README.md`:
- Line 233: The comment for the tokscale monthly command is incorrectly labeled
as "Daily view (shows daily breakdown)" when it should describe a monthly or
periodic view instead. This misleading documentation appears across multiple
README files in different languages. Update the comment at README.md line 233 to
correctly describe monthly/periodic view behavior, then apply the same
correction to the corresponding lines in README.zh-cn.md (line 228),
README.ja.md (line 228), and README.ko.md (line 227). Optionally add a separate
weekly example command if appropriate to provide complete documentation of the
time period options.

In `@README.zh-cn.md`:
- Around line 552-570: The Simplified Chinese README is missing the
scanner.extraScanPaths configuration entry that already exists in the English
version. Add scanner.extraScanPaths to both the JSON configuration example and
the configuration table in README.zh-cn.md. Include it in the JSON example with
an appropriate default value (empty array), and add a corresponding row to the
configuration documentation table with the proper type (string[]), default
value, and Chinese description matching the functionality described in the
English version.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60f109ca-cdd8-46ee-9e4f-711d09037f69

📥 Commits

Reviewing files that changed from the base of the PR and between 98495b1 and 7ea7d8b.

📒 Files selected for processing (19)
  • README.ja.md
  • README.ko.md
  • README.md
  • README.zh-cn.md
  • crates/tokscale-cli/src/main.rs
  • crates/tokscale-cli/src/tui/app.rs
  • crates/tokscale-cli/src/tui/cache.rs
  • crates/tokscale-cli/src/tui/data/mod.rs
  • crates/tokscale-cli/src/tui/mod.rs
  • crates/tokscale-cli/src/tui/settings.rs
  • crates/tokscale-cli/src/tui/ui/footer.rs
  • crates/tokscale-cli/src/tui/ui/header.rs
  • crates/tokscale-cli/src/tui/ui/minutely.rs
  • crates/tokscale-cli/src/tui/ui/mod.rs
  • crates/tokscale-cli/src/tui/ui/period.rs
  • crates/tokscale-cli/src/tui/ui/time_table.rs
  • docs/adr/0010-period-views-derive-from-daily.md
  • docs/plans/2026-06-12-memory-optimization.md
  • docs/plans/2026-06-13-architecture-track.md
💤 Files with no reviewable changes (3)
  • crates/tokscale-cli/src/tui/ui/minutely.rs
  • crates/tokscale-cli/src/tui/ui/time_table.rs
  • crates/tokscale-cli/src/tui/settings.rs

Comment thread crates/tokscale-cli/src/tui/app.rs
Comment thread crates/tokscale-cli/src/tui/data/mod.rs Outdated
Comment thread crates/tokscale-cli/src/tui/ui/period.rs
@makoMakoGo
makoMakoGo merged commit 2a645ac into personal/local-clients Jun 14, 2026
4 checks passed
@makoMakoGo
makoMakoGo deleted the feat/weekly-monthly-tabs branch June 14, 2026 21:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant