Skip to content

feat: add sessions usage tab#74

Merged
ymkiux merged 6 commits intomainfrom
feat/usage-tab-codex-claude
Apr 6, 2026
Merged

feat: add sessions usage tab#74
ymkiux merged 6 commits intomainfrom
feat/usage-tab-codex-claude

Conversation

@awsl233777
Copy link
Copy Markdown
Collaborator

@awsl233777 awsl233777 commented Apr 6, 2026

Summary

  • add a Usage sub-tab inside the Sessions panel
  • show lightweight local usage charts for Codex and Claude based on existing session summaries
  • keep the implementation read-only and front-end aggregated to avoid extra API or detail scans

What changed

  • add Sessions / Usage sub-tab switching in the sessions panel
  • add 7d / 30d usage range switching
  • add charts/cards for session trend, source share, message trend, and top paths
  • add usage aggregation logic and regression tests
  • update parity/config UI tests for the new tab state

Notes

  • current session summaries do not expose token/cost metadata
  • so this PR implements a local usage overview based on session count/message count/path activity, not a true token-cost dashboard

Verification

  • node tests/unit/run.mjs
  • npm test -- --runInBand

Summary by CodeRabbit

  • New Features

    • Added a "Usage" subtab to the Sessions panel with a Sessions/Usage toggle (default 7-day range), time-range selector (7/30 days), usage summary cards (sessions, messages, active days), trend charts by source, source-share rows, and top-paths list; includes empty-state handling and responsive layout.
  • Tests

    • New unit tests for session usage aggregation/chart data; test runner updated to include them.
  • Style

    • Added stylesheet for sessions usage UI.
  • Documentation

    • README updated to document the new Usage analytics view.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Warning

Rate limit exceeded

@awsl233777 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 21 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 17 minutes and 21 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a9da2cb-bd34-4001-b662-11ac96deb666

📥 Commits

Reviewing files that changed from the base of the PR and between c9cb504 and 13f5556.

📒 Files selected for processing (2)
  • README.en.md
  • README.md
📝 Walkthrough

Walkthrough

Adds a sessions-usage feature: new aggregation logic and tests, computed properties and app state, UI subtabs for "browser" vs "usage" with time-range controls, templates and styles to render usage summary cards, charts, source-share rows, and top paths. (50 words)

Changes

Cohort / File(s) Summary
Tests — runner & UI
tests/unit/run.mjs, tests/unit/config-tabs-ui.test.mjs, tests/unit/web-ui-behavior-parity.test.mjs
Imported new session-usage test into the runner; extended UI test to assert sessions subtab/tablist and usage elements; relaxed parity test to allow/compare specific session-usage computed/data keys when not comparing to HEAD.
New Tests — usage logic
tests/unit/session-usage.test.mjs
Added tests for buildUsageChartGroups: UTC day bucketing for 7d, per-source totals/percentages, top paths, last-bucket values, and filtering of invalid session entries while producing stable empty buckets.
Session aggregation logic
web-ui/logic.sessions.mjs
Added exported buildUsageChartGroups(sessions, options) to normalize sessions, build 7/30-day UTC buckets, aggregate codex/claude counts and messages, compute totals/sourceShare/activeDays, produce topPaths and max buckets.
App state & computed
web-ui/app.js, web-ui/modules/app.computed.session.mjs
Added reactive fields sessionsViewMode ('browser') and sessionsUsageTimeRange ('7d'); added computed sessionUsageCharts() and sessionUsageSummaryCards() deriving chart and summary data.
Sessions panel template
web-ui/partials/index/panel-sessions.html
Inserted subtabs for 会话视图切换 (browser vs usage); conditional usage view with time-range buttons, empty state, summary cards, stacked session/message trend charts, source-share rows, and top-paths list; preserved browsing UI under alternate branch.
Styling
web-ui/styles.css, web-ui/styles/sessions-usage.css
Imported new sessions-usage.css; new stylesheet defines subtabs, toolbar, range buttons, summary cards, chart/grid layouts, stacked bars, legends, lists/progress visuals, empty-state, and responsive rules.
Documentation
README.md, README.en.md
Documented the new Usage analytics view (7d/30d trends, source share, top paths) and the Browser/Usage subview toggle.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant App as Vue App
  participant Logic as buildUsageChartGroups
  participant DOM as Sessions Panel

  User->>App: click "Usage" subtab / select time-range
  App->>Logic: buildUsageChartGroups(sessionsList, { range, now })
  Logic-->>App: return { range, buckets, summary, sourceShare, topPaths, maxBuckets }
  App->>DOM: render sessionUsageSummaryCards and sessionUsageCharts
  DOM-->>User: display charts, summary cards, source share, top paths
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Reviewers

  • SurviveM
  • ymkiux

Poem

🐰 I bucket days by UTC light,
Codex and Claude counted right,
Tabs that toggle, charts that hum,
Seven or thirty — hop and come! 📊✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add sessions usage tab' clearly and concisely summarizes the main feature addition—a new usage analytics tab in the sessions panel—which aligns with the primary changeset objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/usage-tab-codex-claude

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (5)
web-ui/partials/index/panel-sessions.html (3)

100-100: Redundant inline style width:100% on usage bar element.

The style="width:100%" is static and should be defined in CSS rather than inline. The dynamic :style binding for height is appropriate.

🧹 Move static style to CSS
-<div class="usage-bar codex" style="width:100%" :style="{ height: ((bucket.totalMessages / Math.max(sessionUsageCharts.maxMessageBucket, 1)) * 100) + '%' }" :title="`${bucket.totalMessages} messages`"></div>
+<div class="usage-bar codex usage-bar-full" :style="{ height: ((bucket.totalMessages / Math.max(sessionUsageCharts.maxMessageBucket, 1)) * 100) + '%' }" :title="`${bucket.totalMessages} messages`"></div>

Add to CSS:

.usage-bar-full { width: 100%; }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/partials/index/panel-sessions.html` at line 100, Remove the redundant
inline style width:100% from the usage bar element in panel-sessions.html and
instead add a CSS class (e.g., usage-bar-full) that sets width: 100%; then apply
that class to the div with class "usage-bar codex" so the dynamic :style for
height remains untouched; locate the div (the usage bar with :style height
binding and :title using bucket.totalMessages) and update markup to remove the
inline style and add the new CSS class, and add the .usage-bar-full { width:
100%; } rule to the appropriate stylesheet.

25-44: Accessibility: Missing aria-controls and id attributes on tab buttons.

The tablist/tab pattern requires each tab button to have an id attribute and an aria-controls pointing to the corresponding tabpanel. Currently, there's no role="tabpanel" on the content divs either.

♿ Proposed fix for proper ARIA tab pattern
 <div class="sessions-subtabs" role="tablist" aria-label="会话视图切换">
     <button
         class="sessions-subtab"
         :class="{ active: sessionsViewMode === 'browser' }"
         type="button"
         role="tab"
+        id="sessions-tab-browser"
+        aria-controls="sessions-panel-browser"
         :aria-selected="sessionsViewMode === 'browser'"
         `@click`="sessionsViewMode = 'browser'">
         Sessions
     </button>
     <button
         class="sessions-subtab"
         :class="{ active: sessionsViewMode === 'usage' }"
         type="button"
         role="tab"
+        id="sessions-tab-usage"
+        aria-controls="sessions-panel-usage"
         :aria-selected="sessionsViewMode === 'usage'"
         `@click`="sessionsViewMode = 'usage'">
         Usage
     </button>
 </div>

Then add matching id and role="tabpanel" to the content containers:

<div v-if="sessionsViewMode === 'usage'" id="sessions-panel-usage" role="tabpanel" aria-labelledby="sessions-tab-usage">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/partials/index/panel-sessions.html` around lines 25 - 44, The tab
buttons in the sessions subtab UI lack required ARIA relationships; update each
button (class "sessions-subtab", the ones toggling sessionsViewMode) to include
a unique id (e.g., "sessions-tab-browser" and "sessions-tab-usage") and an
aria-controls that points to the corresponding panel id, and update the content
containers that render based on sessionsViewMode to include role="tabpanel",
matching id attributes (e.g., "sessions-panel-browser" / "sessions-panel-usage")
and aria-labelledby that references the tab ids so the tab/tabpanel ARIA pattern
is properly linked.

113-113: Safe navigation already handled, but expression is complex and repeated.

The expression ((item.count / Math.max(sessionUsageCharts.topPaths[0]?.count || 1, 1)) * 100) re-accesses topPaths[0] on each iteration. Consider computing the max count in the computed property sessionUsageCharts.topPaths to simplify the template.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/partials/index/panel-sessions.html` at line 113, The template
recalculates Math.max(sessionUsageCharts.topPaths[0]?.count || 1, 1) on every
render and re-accesses topPaths[0]; add a derived value (e.g.,
sessionUsageCharts.topPathsMaxCount or sessionUsageCharts.maxTopPathCount) in
the computed property that returns the maximum count with a safe fallback of 1,
then replace the complex inline expression in the usage-progress-fill style with
a simple reference using item.count divided by that computed max (multiply by
100) to simplify the template and avoid repeated optional chaining.
web-ui/styles/sessions-usage.css (2)

1-1: Hardcoded color values alongside CSS variables.

Some colors like #4f8cff and #b277ff (for .usage-bar.codex and .usage-bar.claude) and the gradient in .usage-progress-fill are hardcoded rather than using CSS custom properties. This reduces theming flexibility.

🎨 Extract to CSS variables for consistency
.usage-bar.codex { background: var(--color-codex, `#4f8cff`); }
.usage-bar.claude { background: var(--color-claude, `#b277ff`); }
.usage-progress-fill { background: linear-gradient(90deg, var(--color-codex, `#4f8cff`), var(--color-claude, `#b277ff`)); }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/styles/sessions-usage.css` at line 1, The styles use hardcoded colors
in .usage-bar.codex, .usage-bar.claude and .usage-progress-fill which breaks
theming; replace those literal hex values with CSS custom properties (e.g.,
--color-codex and --color-claude) and update .usage-progress-fill to use a
linear-gradient between the two vars, providing the original hex values as
fallbacks so existing visuals remain unchanged.

1-1: Consider formatting CSS for maintainability.

The entire stylesheet is on a single line, making it difficult to read, debug, and maintain. If this is intentional for production builds (minification), consider keeping a formatted source file and minifying during the build process.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/styles/sessions-usage.css` at line 1, The CSS file is fully minified
on a single line (e.g., selectors like .sessions-subtabs, .sessions-subtab,
.usage-toolbar, .usage-summary-grid, .usage-chart-grid, .usage-progress-fill),
which harms readability; restore a human-friendly multi-line format by expanding
each rule and property onto separate lines and grouping related rules, run a
formatter (Prettier/stylelint --fix) to enforce consistent indentation and
ordering, and if minification is required for production ensure you keep the
formatted source file in the repo and add the minification step to the build
pipeline so the committed file remains readable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/logic.sessions.mjs`:
- Around line 93-97: The code currently accepts arbitrary strings into the
variable range and later computes buckets using rangeDays but returns the
original invalid range string; normalize unsupported values so range becomes
only the canonical '7d' or '30d' before any further use. In practice, validate
and map options.range (the variable named range) to either '7d' or '30d' (e.g.,
treat anything not equal to '30d' as '7d'), update derived rangeDays
accordingly, and ensure any metadata or return values that reference range
(including the place around the later usage at the location tied to line 163)
use this normalized range value so buckets and metadata stay in sync. Ensure you
adjust both the initial assignment of range and all subsequent usages/returns
that relied on the original unnormalized range.

In `@web-ui/styles/sessions-usage.css`:
- Line 1: Interactive controls like .sessions-subtab and .usage-range-btn lack
keyboard focus styles; add :focus and/or :focus-visible rules for
.sessions-subtab, .usage-range-btn (and any other interactive classes such as
.usage-legend-dot if interactive) that apply a visible outline (e.g., outline:
2px solid var(--accent-color,`#4f8cff`); outline-offset: 2px) and preserve
existing active styles so keyboard users can see focus without changing layout
or color semantics.

---

Nitpick comments:
In `@web-ui/partials/index/panel-sessions.html`:
- Line 100: Remove the redundant inline style width:100% from the usage bar
element in panel-sessions.html and instead add a CSS class (e.g.,
usage-bar-full) that sets width: 100%; then apply that class to the div with
class "usage-bar codex" so the dynamic :style for height remains untouched;
locate the div (the usage bar with :style height binding and :title using
bucket.totalMessages) and update markup to remove the inline style and add the
new CSS class, and add the .usage-bar-full { width: 100%; } rule to the
appropriate stylesheet.
- Around line 25-44: The tab buttons in the sessions subtab UI lack required
ARIA relationships; update each button (class "sessions-subtab", the ones
toggling sessionsViewMode) to include a unique id (e.g., "sessions-tab-browser"
and "sessions-tab-usage") and an aria-controls that points to the corresponding
panel id, and update the content containers that render based on
sessionsViewMode to include role="tabpanel", matching id attributes (e.g.,
"sessions-panel-browser" / "sessions-panel-usage") and aria-labelledby that
references the tab ids so the tab/tabpanel ARIA pattern is properly linked.
- Line 113: The template recalculates
Math.max(sessionUsageCharts.topPaths[0]?.count || 1, 1) on every render and
re-accesses topPaths[0]; add a derived value (e.g.,
sessionUsageCharts.topPathsMaxCount or sessionUsageCharts.maxTopPathCount) in
the computed property that returns the maximum count with a safe fallback of 1,
then replace the complex inline expression in the usage-progress-fill style with
a simple reference using item.count divided by that computed max (multiply by
100) to simplify the template and avoid repeated optional chaining.

In `@web-ui/styles/sessions-usage.css`:
- Line 1: The styles use hardcoded colors in .usage-bar.codex, .usage-bar.claude
and .usage-progress-fill which breaks theming; replace those literal hex values
with CSS custom properties (e.g., --color-codex and --color-claude) and update
.usage-progress-fill to use a linear-gradient between the two vars, providing
the original hex values as fallbacks so existing visuals remain unchanged.
- Line 1: The CSS file is fully minified on a single line (e.g., selectors like
.sessions-subtabs, .sessions-subtab, .usage-toolbar, .usage-summary-grid,
.usage-chart-grid, .usage-progress-fill), which harms readability; restore a
human-friendly multi-line format by expanding each rule and property onto
separate lines and grouping related rules, run a formatter (Prettier/stylelint
--fix) to enforce consistent indentation and ordering, and if minification is
required for production ensure you keep the formatted source file in the repo
and add the minification step to the build pipeline so the committed file
remains readable.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9230a166-e733-471e-a1ba-0fc58d53303b

📥 Commits

Reviewing files that changed from the base of the PR and between a5ffbfd and f4f6c9c.

📒 Files selected for processing (10)
  • tests/unit/config-tabs-ui.test.mjs
  • tests/unit/run.mjs
  • tests/unit/session-usage.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • web-ui/app.js
  • web-ui/logic.sessions.mjs
  • web-ui/modules/app.computed.session.mjs
  • web-ui/partials/index/panel-sessions.html
  • web-ui/styles.css
  • web-ui/styles/sessions-usage.css
📜 Review details
🔇 Additional comments (10)
web-ui/styles.css (1)

9-9: Stylesheet wiring looks correct.

The new sessions-usage.css import is correctly added to the global cascade.

tests/unit/run.mjs (1)

32-32: Test runner inclusion is correct.

session-usage.test.mjs is properly registered in the unit test bootstrap.

web-ui/app.js (1)

120-121: Session usage state defaults look good.

The added defaults are clear and consistent with the new sessions sub-tab UX.

tests/unit/config-tabs-ui.test.mjs (1)

180-187: Good UI-contract coverage for the new usage subtab.

These assertions validate key markup/state bindings for mode and range switching.

tests/unit/web-ui-behavior-parity.test.mjs (1)

322-322: Parity drift handling is well-structured.

The allowed-extra logic is explicit and still preserves strict checks against HEAD.

Also applies to: 356-373

web-ui/modules/app.computed.session.mjs (1)

3-3: Computed wiring for usage metrics is solid.

The aggregation call and summary-card fallback handling are both implemented cleanly.

Also applies to: 104-118

tests/unit/session-usage.test.mjs (1)

1-43: Nice targeted coverage for usage aggregation behavior.

The tests capture both normal aggregation and invalid-input stability paths.

web-ui/partials/index/panel-sessions.html (3)

74-81: Duplicate :key values in nested v-for loops.

Lines 74 and 98 both use bucket.key as the key. While they're in different v-for scopes, using identical keys can cause Vue reconciliation issues if these templates are ever refactored. Line 98 appends '-messages' which is good practice — ensure consistency.


46-120: LGTM!

The usage view implementation is well-structured with appropriate conditional rendering, summary cards, charts, and empty states. The use of computed properties (sessionUsageSummaryCards, sessionUsageCharts) keeps the template clean and maintainable.


122-122: LGTM!

The v-else template correctly wraps the existing browser mode content, maintaining proper conditional rendering between the two view modes.

Also applies to: 385-385

Comment on lines +93 to +97
const range = typeof options.range === 'string' ? options.range.trim().toLowerCase() : '7d';
const now = Number.isFinite(Number(options.now)) ? Number(options.now) : Date.now();
const dayMs = 24 * 60 * 60 * 1000;
const rangeDays = range === '30d' ? 30 : 7;
const buckets = [];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Normalize unsupported range values to a canonical output ('7d'/'30d').

At Line [93] and Line [163], invalid inputs currently produce 7-day buckets but return the original invalid range string. This can desync metadata from actual aggregation.

💡 Proposed fix
-    const range = typeof options.range === 'string' ? options.range.trim().toLowerCase() : '7d';
+    const rawRange = typeof options.range === 'string' ? options.range.trim().toLowerCase() : '7d';
+    const range = rawRange === '30d' ? '30d' : '7d';
     const now = Number.isFinite(Number(options.now)) ? Number(options.now) : Date.now();
     const dayMs = 24 * 60 * 60 * 1000;
-    const rangeDays = range === '30d' ? 30 : 7;
+    const rangeDays = range === '30d' ? 30 : 7;

Also applies to: 163-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/logic.sessions.mjs` around lines 93 - 97, The code currently accepts
arbitrary strings into the variable range and later computes buckets using
rangeDays but returns the original invalid range string; normalize unsupported
values so range becomes only the canonical '7d' or '30d' before any further use.
In practice, validate and map options.range (the variable named range) to either
'7d' or '30d' (e.g., treat anything not equal to '30d' as '7d'), update derived
rangeDays accordingly, and ensure any metadata or return values that reference
range (including the place around the later usage at the location tied to line
163) use this normalized range value so buckets and metadata stay in sync.
Ensure you adjust both the initial assignment of range and all subsequent
usages/returns that relied on the original unnormalized range.

@@ -0,0 +1 @@
.sessions-subtabs{display:flex;gap:10px;align-items:center;margin:0 0 16px}.sessions-subtab{border:1px solid var(--border-color,#2a3342);background:var(--panel-muted-bg,#11161f);color:var(--text-secondary,#9aa4b2);padding:8px 14px;border-radius:999px;cursor:pointer;font-size:13px;font-weight:600}.sessions-subtab.active{background:var(--accent-soft,#1c2d4a);color:var(--text-primary,#fff);border-color:var(--accent-color,#4f8cff)}.usage-toolbar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}.usage-range-group{display:flex;gap:8px;flex-wrap:wrap}.usage-range-btn{border:1px solid var(--border-color,#2a3342);background:var(--panel-muted-bg,#11161f);color:var(--text-secondary,#9aa4b2);padding:6px 12px;border-radius:999px;cursor:pointer;font-size:12px;font-weight:600}.usage-range-btn.active{background:var(--accent-soft,#1c2d4a);color:var(--text-primary,#fff);border-color:var(--accent-color,#4f8cff)}.usage-summary-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:16px}.usage-summary-card{padding:14px 16px;border-radius:14px;background:var(--panel-muted-bg,#11161f);border:1px solid var(--border-color,#2a3342)}.usage-summary-label{font-size:12px;color:var(--text-secondary,#9aa4b2);margin-bottom:6px}.usage-summary-value{font-size:24px;font-weight:700;color:var(--text-primary,#fff)}.usage-chart-grid{display:grid;grid-template-columns:2fr 1fr;gap:16px}.usage-card{padding:16px;border-radius:16px;background:var(--panel-bg,#0d1117);border:1px solid var(--border-color,#2a3342)}.usage-card-title{font-size:14px;font-weight:700;color:var(--text-primary,#fff);margin-bottom:12px}.usage-bars{display:flex;align-items:flex-end;gap:8px;min-height:180px}.usage-bar-group{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;gap:8px}.usage-bar-stack{width:100%;max-width:36px;height:160px;display:flex;align-items:flex-end;gap:4px}.usage-bar{flex:1;border-radius:10px 10px 4px 4px;min-height:4px}.usage-bar.codex{background:#4f8cff}.usage-bar.claude{background:#b277ff}.usage-bar-label{font-size:11px;color:var(--text-secondary,#9aa4b2)}.usage-legend{display:flex;gap:14px;flex-wrap:wrap;font-size:12px;color:var(--text-secondary,#9aa4b2);margin-bottom:10px}.usage-legend-dot{width:10px;height:10px;border-radius:999px;display:inline-block;margin-right:6px}.usage-list{display:flex;flex-direction:column;gap:10px}.usage-list-row{display:grid;grid-template-columns:72px 1fr 48px;gap:10px;align-items:center}.usage-list-label,.usage-list-value{font-size:12px;color:var(--text-secondary,#9aa4b2)}.usage-progress{height:8px;border-radius:999px;background:rgba(255,255,255,.08);overflow:hidden}.usage-progress-fill{height:100%;border-radius:999px;background:linear-gradient(90deg,#4f8cff,#b277ff)}.usage-empty{padding:24px 16px;border-radius:16px;background:var(--panel-muted-bg,#11161f);border:1px dashed var(--border-color,#2a3342);color:var(--text-secondary,#9aa4b2)}@media (max-width: 960px){.usage-chart-grid{grid-template-columns:1fr}} No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing focus states for interactive elements.

The .sessions-subtab, .usage-range-btn, and other button styles define :hover via .active state but lack explicit :focus or :focus-visible styles. This impacts keyboard navigation accessibility.

♿ Add focus styles for keyboard accessibility
.sessions-subtab:focus-visible,
.usage-range-btn:focus-visible {
  outline: 2px solid var(--accent-color, `#4f8cff`);
  outline-offset: 2px;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/styles/sessions-usage.css` at line 1, Interactive controls like
.sessions-subtab and .usage-range-btn lack keyboard focus styles; add :focus
and/or :focus-visible rules for .sessions-subtab, .usage-range-btn (and any
other interactive classes such as .usage-legend-dot if interactive) that apply a
visible outline (e.g., outline: 2px solid var(--accent-color,`#4f8cff`);
outline-offset: 2px) and preserve existing active styles so keyboard users can
see focus without changing layout or color semantics.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
README.md (1)

54-54: Clarify that Usage metrics are summary-based (not token/cost analytics).

To avoid user misunderstanding, add a short qualifier that these stats are derived from session summaries (sessions/messages/path activity) and do not include token or cost metrics.

✏️ Suggested wording update
-- Usage 子页:近 7 天 / 近 30 天会话趋势、消息趋势、来源占比、高频路径
+- Usage 子页:近 7 天 / 近 30 天会话趋势、消息趋势、来源占比、高频路径(基于会话摘要聚合,不含 token/cost)

-- Usage 视图提供近 7 天 / 近 30 天会话趋势、消息趋势、来源占比与高频路径统计
+- Usage 视图提供近 7 天 / 近 30 天会话趋势、消息趋势、来源占比与高频路径统计(基于会话摘要聚合,不含 token/cost)

Also applies to: 194-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 54, The README line "Usage 子页:近 7 天 / 近 30
天会话趋势、消息趋势、来源占比、高频路径" is ambiguous about what "Usage" measures; update that line
(and the similar occurrence at line 194) to append a short qualifier stating
these metrics are summary-based (derived from session/message/path activity) and
do not include token or cost analytics, e.g. add phrasing like "(基于会话/消息摘要汇总,不包含
token 或成本统计)" immediately after "Usage 子页" to clarify the scope.
README.en.md (1)

54-54: Clarify metric scope to avoid token/cost expectation mismatch.

Please add a short qualifier that this usage view is based on session/message/path activity from local summaries and does not include token or cost metrics. This avoids user confusion in an English-facing README section.

✍️ Suggested doc tweak
-- Usage subview with 7d / 30d session trends, message trends, source share, and top paths
+- Usage subview with 7d / 30d session trends, message trends, source share, and top paths (derived from local session summaries; no token/cost metrics)
-- Usage view includes 7d / 30d session trends, message trends, source share, and top paths
+- Usage view includes 7d / 30d session trends, message trends, source share, and top paths (summary-based; no token/cost metrics)

Also applies to: 202-202

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.en.md` at line 54, Update the "Usage subview with 7d / 30d session
trends, message trends, source share, and top paths" line to include a short
qualifier stating that these metrics are derived from local session/message/path
activity summaries and do not include token or cost metrics (e.g., append "—
based on local session/message/path summaries; does not include token or cost
metrics"). Also apply the same qualifier to the other occurrence referenced by
the reviewer (the similar "Usage" line elsewhere) so users aren’t led to expect
token/cost data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@README.en.md`:
- Line 54: Update the "Usage subview with 7d / 30d session trends, message
trends, source share, and top paths" line to include a short qualifier stating
that these metrics are derived from local session/message/path activity
summaries and do not include token or cost metrics (e.g., append "— based on
local session/message/path summaries; does not include token or cost metrics").
Also apply the same qualifier to the other occurrence referenced by the reviewer
(the similar "Usage" line elsewhere) so users aren’t led to expect token/cost
data.

In `@README.md`:
- Line 54: The README line "Usage 子页:近 7 天 / 近 30 天会话趋势、消息趋势、来源占比、高频路径" is
ambiguous about what "Usage" measures; update that line (and the similar
occurrence at line 194) to append a short qualifier stating these metrics are
summary-based (derived from session/message/path activity) and do not include
token or cost analytics, e.g. add phrasing like "(基于会话/消息摘要汇总,不包含 token 或成本统计)"
immediately after "Usage 子页" to clarify the scope.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58edc2f1-2cbb-4d25-9bf6-c68d72ebb257

📥 Commits

Reviewing files that changed from the base of the PR and between 590a8ee and c9cb504.

📒 Files selected for processing (2)
  • README.en.md
  • README.md
📜 Review details
🧰 Additional context used
🪛 LanguageTool
README.en.md

[grammar] ~27-~27: Ensure spelling is correct
Context: ...ter/export/delete) with Usage analytics overview It works on local files directly and doe...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~199-~199: Ensure spelling is correct
Context: ...aude sessions - Browser / Usage subview switching - Local pin/unpin with persistent storage...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~200-~200: Ensure spelling is correct
Context: ...ith persistent storage and pinned-first ordering - Search, filter, export, delete, batch c...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~201-~201: Ensure spelling is correct
Context: ...- Search, filter, export, delete, batch cleanup - Usage view includes 7d / 30d session tr...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (3)
README.md (1)

27-27: Nice documentation alignment with the new Sessions Usage subview.

The additions clearly surface the Browser/Usage split and make the feature easier to discover from both capability and UI sections.

Also applies to: 191-191

README.en.md (2)

27-27: Nice feature-scope update in the overview list.

This line cleanly reflects the new Sessions Usage analytics capability and matches the implemented UI behavior.


199-199: Subview-switching wording is accurate and consistent.

This matches the actual Browser/Usage tab behavior in the Sessions panel.

@ymkiux
Copy link
Copy Markdown
Collaborator

ymkiux commented Apr 6, 2026

@coderabbitai 如果我直接合并会带来回归性异常吗

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the parity test and session usage test files
echo "=== web-ui-behavior-parity.test.mjs ==="
cat tests/unit/web-ui-behavior-parity.test.mjs

echo ""
echo "=== session-usage.test.mjs ==="
cat tests/unit/session-usage.test.mjs

echo ""
echo "=== config-tabs-ui.test.mjs ==="
cat tests/unit/config-tabs-ui.test.mjs

echo ""
echo "=== logic.sessions.mjs (last 100 lines for buildUsageChartGroups) ==="
tail -120 web-ui/logic.sessions.mjs

echo ""
echo "=== app.computed.session.mjs ==="
cat web-ui/modules/app.computed.session.mjs

echo ""
echo "=== app.js (data section) ==="
grep -n "sessionsViewMode\|sessionsUsageTimeRange\|sessionsList\|sessionUsage" web-ui/app.js | head -30

echo ""
echo "=== run.mjs ==="
cat tests/unit/run.mjs

Length of output: 96267


@ymkiux 从代码审查和测试结构来看,直接合并带来回归性异常的风险极低,原因如下:

1. 变更完全是加法式的(Additive-only)

  • 新增 Vue 数据字段 sessionsViewMode: 'browser'sessionsUsageTimeRange: '7d',均有安全默认值
  • 新增计算属性 sessionUsageChartssessionUsageSummaryCards,对现有属性无改动
  • 新增导出函数 buildUsageChartGroups,未修改任何已有函数

2. 现有会话浏览器 UI 行为不变

  • 原有 Sessions 浏览器 UI 被包裹在 v-else 中,而 sessionsViewMode 默认值为 'browser',因此用户看到的初始状态与合并前完全相同

3. 测试覆盖严密

  • web-ui-behavior-parity.test.mjs 明确将新 data key(sessionsUsageTimeRangesessionsViewMode)和新 computed key(sessionUsageChartssessionUsageSummaryCards)加入 allowlist,与 HEAD 的对比校验不会因此失败
  • config-tabs-ui.test.mjs 已同步断言新子 Tab 结构的存在
  • 新增的 session-usage.test.mjs 覆盖了正常路径(bucket 聚合、topPaths、sourceShare)和异常边界(null、无效 source、非法日期),7-bucket 稳定性验证也通过

唯一需要留意的小细节:
sessionUsageCharts() 计算属性在 sessionsViewMode === 'usage' 视图未激活时也会被 Vue 响应式系统计算(因为它是 computed property),但由于 sessionsList 始终初始化为 []buildUsageChartGroups 对空数组有安全处理,不会有运行时报错。

结论:可以安全合并。

@ymkiux ymkiux merged commit f22b427 into main Apr 6, 2026
7 checks passed
@ymkiux ymkiux deleted the feat/usage-tab-codex-claude branch April 6, 2026 10:40
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.

2 participants