refactor(tui): 重构 Overview、Sessions 与 Daily 视图 - #154
Conversation
Walkthrough本次变更以 Sessions 替代旧 Issues 视图,新增会话投影、来源与详情表格、独立视图状态,并重构 Overview、Stats、Daily Profile、Hourly Profile、导航、页脚及定价诊断展示。 ChangesTUI 信息架构重构
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
17e0c89 to
31e0869
Compare
Takes PR #154's two-section Stats skeleton (contribution graph with in-graph metrics, Day Insights driven by selected_graph_cell with its scrollable harness/model breakdown) as-is, and ports this branch's additions into it: weekday/month crosshair labels, top model/agent lines, the 24-hour activity strip, and the top-3 model radar chart. app.rs is restored to the cell-selection state #154 builds on; the usage 5 Hour label fixes are unchanged.
2e9547b to
260877f
Compare
260877f to
37f9a59
Compare
|
@codex review |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 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/mod.rs`:
- Around line 94-97: 更新 refresh_session_data 及其 BackgroundLoad 调用链,不要仅记录并吞掉
session_data::refresh 的错误;保留错误或警告并向 TUI 展示,使刷新失败不会返回 Loaded 或
Unchanged。补充回归测试,验证刷新失败时界面显示错误且不会将失败标记为成功。
- Around line 111-117: 更新后台加载流程中的 refresh_session_data 与摘要状态管理:记录 Sessions
投影是否已初始化及其对应摘要。首次从缓存恢复时仍执行刷新,之后仅在投影缺失或摘要发生变化时刷新;摘要匹配且投影已初始化时不要调用
refresh_session_data,保留快速路径。
In `@crates/tokscale-cli/src/tui/session_data.rs`:
- Around line 113-123: Update snapshot_store and snapshot so repeated reads do
not deep-clone the full SessionSnapshot. Store the snapshot behind Arc within
the lock and have snapshot clone only the Arc, or expose an equivalent read-only
callback API; update callers such as source_rows, session_rows, and footer
rendering to use the optimized access path while preserving snapshot contents
and thread-safety.
In `@crates/tokscale-cli/src/tui/ui/overview_snapshot.rs`:
- Around line 69-79: 调整 Snapshot 的布局分配,确保其内部高度至少保留 10 行,避免在 30 行终端中因高度不足而裁掉
Harness、Partial/Failed 和 Rejected 等必需指标;更新当前构造 lines 的流程,移除或改为不会静默隐藏这些指标的 take
限制。增加覆盖 render_with_state 的回归测试,验证紧凑终端高度下所有 Snapshot 指标仍会渲染。
In `@crates/tokscale-cli/src/tui/ui/overview.rs`:
- Around line 188-203: 更新 overview 图例渲染中基于 limit 的固定条目截断逻辑,使用可用区域的 area.width
累计每个模型条目及分隔符的实际显示宽度,仅在放入后仍不超出区域时渲染;保留 app.is_narrow() 对名称宽度的影响,并确保不再因固定渲染 5
个条目导致行内容被裁剪。
In `@crates/tokscale-cli/src/tui/ui/stats.rs`:
- Around line 356-381: Update the model list rendering around the models
collection and loop to use source.models.iter() so each model_key remains
available. Select the display identifier in display_name → color_key → model_key
order when display_name is empty, and use that same identifier for both
truncate_model_display_name and app.model_color_for while preserving the
existing sorting and metrics.
- Around line 119-145: 仅在统计网格单元格匹配到 Some(day) 时注册 GraphCell 点击区域;对于 None
占位格继续渲染现有样式,但不要将其加入 click_areas,避免触发无日期可用的 Day Insights 交互。
In `@crates/tokscale-cli/src/tui/ui/view_footer.rs`:
- Around line 8-25: 将页脚改为单次、状态感知渲染:在 view_footer::render 中不要先调用通用 footer::render
再覆盖,而应根据 ViewState 和当前标签页直接选择最终行渲染器;更新 view_footer.rs
中的状态化渲染器,使其仅按实际显示的字段、顺序和宽度注册点击区域。同步调整 footer.rs
的通用渲染流程,避免为状态化页脚预注册通用排序控件,并让排序可见性接收视图状态或由状态化页脚决定,确保 Daily Profile
不显示无效排序控件。受影响位置:crates/tokscale-cli/src/tui/ui/view_footer.rs:8-25、40-114,以及
crates/tokscale-cli/src/tui/ui/footer.rs:59-69、135-145。
🪄 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: 2d43eec9-dc45-4782-9aee-b422cd348095
📒 Files selected for processing (19)
crates/tokscale-cli/src/tui/app.rscrates/tokscale-cli/src/tui/data/mod.rscrates/tokscale-cli/src/tui/mod.rscrates/tokscale-cli/src/tui/session_data.rscrates/tokscale-cli/src/tui/themes.rscrates/tokscale-cli/src/tui/ui/daily_profile.rscrates/tokscale-cli/src/tui/ui/dialog/group_by_picker.rscrates/tokscale-cli/src/tui/ui/footer.rscrates/tokscale-cli/src/tui/ui/header.rscrates/tokscale-cli/src/tui/ui/hourly_profile.rscrates/tokscale-cli/src/tui/ui/issues.rscrates/tokscale-cli/src/tui/ui/mod.rscrates/tokscale-cli/src/tui/ui/overview.rscrates/tokscale-cli/src/tui/ui/overview_snapshot.rscrates/tokscale-cli/src/tui/ui/sessions.rscrates/tokscale-cli/src/tui/ui/stats.rscrates/tokscale-cli/src/tui/ui/usage_profile.rscrates/tokscale-cli/src/tui/ui/view_footer.rscrates/tokscale-cli/src/tui/view_state.rs
💤 Files with no reviewable changes (3)
- crates/tokscale-cli/src/tui/ui/issues.rs
- crates/tokscale-cli/src/tui/themes.rs
- crates/tokscale-cli/src/tui/app.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0e7f1bc6b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Preserve the last successful Sessions projection when refresh fails and render its degraded state inside the Sessions panel. Align the source header with the panel title.
Track the source digest owned by the Sessions projection and skip rebuilding a ready matching snapshot. Pending, changed, forced, and degraded projections continue to refresh, with failures remaining retryable.
Publish immutable Sessions snapshots through Arc, precompute source summaries and source indexes during refresh, and use constant-time counts for navigation and footer rendering.
Keep placeholder contribution cells visually unchanged while excluding them from Day Insights click targets.
Select the final footer content before rendering, derive sort hitboxes from the displayed labels, and omit table-only controls from Daily Profile.
Route pricing warnings through structured diagnostics so background refreshes do not write into the alternate screen. Keep pricing availability global and independent of Sessions projection health.
Share Sessions navigation between keyboard and mouse input. Consume wheel events in Daily Profile so they cannot mutate the hidden table selection.
Rename the public TuiTab value and internal Tab variant to Sessions, remove the obsolete header translation, and reject the former issues spelling instead of retaining a compatibility alias.
Store the Daily Profile text viewport in ViewState, route keyboard and wheel movement through it, and render a scrollbar when the profile exceeds the available body height without changing the hidden table selection.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
crates/tokscale-cli/src/tui/ui/footer.rs (1)
136-181: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win排序控件为空时让摘要使用整行宽度。
Overview、Stats、Usage 和 Daily Profile 会传入空控件,但这里仍预留 40% 空白,导致摘要在中窄终端被不必要地截断。
建议修改
+ let sort_column_percent = if sort_controls.is_empty() { + 0 + } else { + sort_column_percent + }; let chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ Constraint::Percentage(sort_column_percent), Constraint::Percentage(100u16.saturating_sub(sort_column_percent)), ])🤖 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 `@crates/tokscale-cli/src/tui/ui/footer.rs` around lines 136 - 181, Update the footer layout around chunks and sort_controls so that when sort_controls is empty, the summary uses the entire area width instead of reserving the sort-column percentage. Preserve the existing two-column layout and sort-button rendering when controls are present, and keep rendering the summary through the existing chunks[1] path or an equivalent full-width region.crates/tokscale-core/src/pricing/mod.rs (1)
155-166: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win将自定义定价计入缓存服务的可用性判断。
当前三个远程缓存均缺失时会直接返回
None,即使custom包含有效价格。这样 cache-only 或 stale 路径会丢弃用户自定义定价,并把相关成本算成零。请仅在custom也为空时返回None。建议修改
- if litellm_data.is_none() && openrouter_data.is_none() && models_dev_data.is_none() { + if custom.is_empty() + && litellm_data.is_none() + && openrouter_data.is_none() + && models_dev_data.is_none() + { return None; }同时增加仅提供
CustomPricing、三个远程缓存均为None的回归测试。As per coding guidelines, “Add focused Rust unit tests near the implementation for pure logic.”
Also applies to: 173-191
🤖 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 `@crates/tokscale-core/src/pricing/mod.rs` around lines 155 - 166, Update from_cached_datasets so it returns None only when custom is empty and all three remote datasets (litellm_data, openrouter_data, and models_dev_data) are absent; otherwise construct the pricing instance with the existing new_with_custom_and_models_dev flow. Add a focused Rust unit test near this implementation covering custom-only pricing with all remote caches set to None.Source: Coding guidelines
crates/tokscale-cli/src/tui/ui/daily_profile.rs (1)
199-212: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win将测试的
home_dir改为隔离目录这里的
home_dir: None会让App::new_with_cached_data走系统默认 home/config 目录,容易把本机状态带进测试。改成tempfile::TempDir或固定 dummy 路径,避免测试互相污染。🤖 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 `@crates/tokscale-cli/src/tui/ui/daily_profile.rs` around lines 199 - 212, Update the test helper make_app and its TuiConfig setup to use an isolated temporary or dummy home_dir instead of None when calling App::new_with_cached_data. Ensure the directory is available for the app initialization and does not rely on or modify the system default home/config location.Source: Coding guidelines
🤖 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.
Outside diff comments:
In `@crates/tokscale-cli/src/tui/ui/daily_profile.rs`:
- Around line 199-212: Update the test helper make_app and its TuiConfig setup
to use an isolated temporary or dummy home_dir instead of None when calling
App::new_with_cached_data. Ensure the directory is available for the app
initialization and does not rely on or modify the system default home/config
location.
In `@crates/tokscale-cli/src/tui/ui/footer.rs`:
- Around line 136-181: Update the footer layout around chunks and sort_controls
so that when sort_controls is empty, the summary uses the entire area width
instead of reserving the sort-column percentage. Preserve the existing
two-column layout and sort-button rendering when controls are present, and keep
rendering the summary through the existing chunks[1] path or an equivalent
full-width region.
In `@crates/tokscale-core/src/pricing/mod.rs`:
- Around line 155-166: Update from_cached_datasets so it returns None only when
custom is empty and all three remote datasets (litellm_data, openrouter_data,
and models_dev_data) are absent; otherwise construct the pricing instance with
the existing new_with_custom_and_models_dev flow. Add a focused Rust unit test
near this implementation covering custom-only pricing with all remote caches set
to None.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e4ddda8b-b158-4a11-bcad-ee62410b54d4
📒 Files selected for processing (18)
crates/tokscale-cli/src/cli.rscrates/tokscale-cli/src/main_tests.rscrates/tokscale-cli/src/tui/app.rscrates/tokscale-cli/src/tui/mod.rscrates/tokscale-cli/src/tui/session_data.rscrates/tokscale-cli/src/tui/ui/daily_profile.rscrates/tokscale-cli/src/tui/ui/footer.rscrates/tokscale-cli/src/tui/ui/header.rscrates/tokscale-cli/src/tui/ui/mod.rscrates/tokscale-cli/src/tui/ui/overview.rscrates/tokscale-cli/src/tui/ui/overview_snapshot.rscrates/tokscale-cli/src/tui/ui/sessions.rscrates/tokscale-cli/src/tui/ui/stats.rscrates/tokscale-cli/src/tui/ui/view_footer.rscrates/tokscale-cli/src/tui/view_state.rscrates/tokscale-core/src/lib.rscrates/tokscale-core/src/pricing/custom.rscrates/tokscale-core/src/pricing/mod.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/tokscale-cli/src/tui/ui/mod.rs
- crates/tokscale-cli/src/tui/ui/stats.rs
- crates/tokscale-cli/src/tui/ui/overview.rs
背景与目标
Partially addresses #153;剩余事项继续在该 Issue 中跟踪。
原 TUI 的页面职责存在重叠:Overview 同时堆叠多个统计面板,Issues 展示的是 scanner 问题与扁平的 model-session links,Daily 的 Profile 常驻在 Table 上方,页面级交互与通用 Footer 之间也会互相覆盖。
本 PR 重新划分信息架构:
旧 Issues 页面及其 model-session links 表被删除;CLI 的对应标签名由
issues正式替换为sessions。Overview
Overview Graph 始终按清洗后的模型身份聚合,不受当前 Workspace、Harness 或 Provider Group By 影响。图例只渲染能够完整放入可用宽度的项目,移除额外分隔符,并与 Snapshot 内容左边距对齐。
Graph 下方统一为一个响应式、纯文字的
Snapshot:Peak Daily Tokens与Peak Daily Cost来自报告范围内的单日最大值。Clean / (Clean + Degraded + Partial + Failed);Rejected 单独展示记录数。Sessions
Sessions 使用 Source-first 两层导航,替代无法表达真实会话身份的旧 Issues / model-session links 页面:
数据语义
(source, session_id):同一 Source 中使用多个模型的同一 session 只生成一个实体;不同 Source 的同名 session 不会合并。UnifiedMessage,不再从UsageModelEntry.session_count反推。last_seen。刷新与失败行为
Arc<SessionSnapshot>与按 Source 建立的索引。Unavailable;已有成功快照后的刷新失败显示非阻塞Degraded,保留最后一次有效数据,并在 Sessions 面板底部显示单行诊断。导航与布局
tokscale tui --tab sessions是唯一 CLI 名称,旧issues不再作为别名接受。Daily 与 Hourly Profile
Daily 与 Hourly 都采用互斥的 Table / Profile 结构,通过
v切换:Daily Profile 与 Hourly Profile 共享
usage_profile渲染模块,统一生成日期范围、权威总量、响应式柱宽、Peak 行和切换提示。UsageData.total_tokens / total_cost,不在页面内重新实现汇总逻辑。j被当前视图消费,不会触发隐藏 Daily Table 的详情或 today 定位。状态化交互与 Footer
ViewState管理 Daily Profile viewport、Sessions 两层选择与滚动状态。定价诊断与成本可用性
None。其他调整
明确暂缓
验证
cargo fmt --all -- --checkcargo clippy --locked --workspace --all-features -- -D warningscargo test --locked --workspace --all-features(使用隔离的XDG_CONFIG_HOME)bun run build:core