Skip to content

Add Idle Hours and Pacing Indicator#13

Open
owovouo wants to merge 13 commits intoCodeZeno:mainfrom
owovouo:feat/traditional-chinese-and-quiet-hours
Open

Add Idle Hours and Pacing Indicator#13
owovouo wants to merge 13 commits intoCodeZeno:mainfrom
owovouo:feat/traditional-chinese-and-quiet-hours

Conversation

@owovouo
Copy link
Copy Markdown

@owovouo owovouo commented Apr 15, 2026

Summary

Two independent features plus a bug fix, contributed on top of upstream v1.4.0.

Note: Traditional Chinese (zh-TW) localization was already merged by upstream independently. Our version extends it with fields added in v1.3.6–v1.4.0 (Codex support, token-expiry strings, etc.) and the new strings introduced by this PR.


1. Idle Hours

Suppresses Windows toast notifications during a user-configured time window — without affecting the widget display.

Concept: "Idle Hours" describes a period when the user is not actively using Claude Code. Because no conversation is started during this window, the rate-limit reset clock is unaffected — it fires at its scheduled time regardless. The name was chosen to communicate this intent more clearly than "Quiet Hours" (which implies only notification silencing).

Behaviour during Idle Hours:

Situation Usage bar (orange) Pacing bar (green)
Before resets_at Frozen at last polled value Continues growing (time-based)
At resets_at Resets to 0 % Resets to 0 %
After resets_at (still idle) Stays 0 % Stays 0 % (no conversation started, window not yet active)
Idle Hours end Resumes polling Resumes polling

Applies to both Claude Code and Codex widgets.

Localization: en · nl · fr · de · ja · ko · es · zh-TW


2. Pacing Indicator

A green overlay on each progress bar showing the expected usage at the current point in time, based on how much of the 5-hour (or 7-day) window has elapsed.

  • If your actual usage (orange) is below the green line → you are under budget for this window.
  • If your actual usage is above the green line → you are ahead of pace and may exhaust the limit before reset.

This addresses the concern raised in issue #21 (Estimated Time to Depletion): rather than a numeric ETD, it gives an at-a-glance visual answer to "am I on track?".

Toggled via right-click menu → Show Pacing Indicator.

Applies to both Claude Code and Codex widgets.


3. Bug fix: widget drag freezes taskbar

Root cause: WM_MOUSEMOVE held the AppState mutex while calling total_widget_width(), which internally called lock_state() again. Rust's std::sync::Mutex (backed by SRWLOCK on Windows) is not re-entrant — the second acquisition deadlocked the message thread.

Symptoms: After clicking the drag handle, the resize cursor (↔) became permanently stuck, the widget position did not move, and all other taskbar icons became unclickable.

Fix: replaced the inner total_widget_width() call with total_widget_width_for(active_model_count(...)) using values already read from the locked state — no second lock needed.

Additional safety: WM_CAPTURECHANGED and WM_CANCELMODE handlers reset drag state if capture is lost for any reason; WM_SETCURSOR and WM_MOUSEMOVE self-correct if the left button is no longer held.

This bug is present in upstream v1.4.0 as well.

@aa333
Copy link
Copy Markdown

aa333 commented Apr 16, 2026

Hah, I've completely missed that =) feel free to close #14 then.
I'll still keep it in my fork since I need a visual indicator aside from the text

owovouo added 2 commits April 17, 2026 11:55
Replace preset quiet hours selection with a custom HH:MM input dialog,
allowing users to set any start/end time for quiet hours.
Fix non-reentrant mutex deadlock where MoveWindow dispatches WM_PAINT
synchronously, causing wnd_proc to re-enter lock_state() on the same thread.

Credit: aa333 (PR CodeZeno#14)
CodeZeno added a commit that referenced this pull request Apr 19, 2026
fix: resolve taskbar positioning deadlock

- Thanks to @owovouo and @aa333 for surfacing and working on this in PR #13 and PR #14
CodeZeno added a commit that referenced this pull request Apr 19, 2026
feat: add Traditional Chinese localisation

- Thanks to @owovouo for the translation work in PR #13
owovouo and others added 9 commits April 20, 2026 13:40
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows a green zone between actual usage and expected usage based on
elapsed time within the 5h/7d rolling windows. Updated at each poll
interval so the indicator grows in discrete steps matching the chosen
update frequency. Toggle via Settings > Show Pacing Indicator.

Also add Win32_System_SystemInformation feature (required for GetLocalTime)
and TIMER_QUIET_BOUNDARY constant to native_interop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Green pacing zone is now suppressed in both render paths when quiet
hours are active, consistent with the quiet-hours design intent of
pausing all live activity indicators.
Merged upstream changes:
- v1.3.6-v1.3.9: token expiry notifications, Dutch localization, UI improvements
- v1.4.0: Codex model support via model menu

Resolved conflicts by preserving both:
- Our features: quiet hours, pacing indicator, Traditional Chinese localization
- Upstream features: Codex model support, Dutch localization, new UI fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three related fixes:

1. Fix thread deadlock during drag (root cause of stuck cursor):
   WM_MOUSEMOVE held lock_state() mutex and called total_widget_width(),
   which also called lock_state(). Rust's std::sync::Mutex (SRWLOCK on
   Windows) is non-reentrant, causing the message thread to deadlock.
   Fixed by using total_widget_width_for() with active_models read from
   the already-held state.

2. Add WM_CAPTURECHANGED and WM_CANCELMODE handlers:
   Reset dragging state whenever mouse capture is transferred away for
   any reason, providing a safety net beyond WM_LBUTTONUP.

3. Add left_button_held() self-correction in WM_SETCURSOR and WM_MOUSEMOVE:
   If dragging=true but the left button is not physically held, cancel
   the drag immediately rather than waiting for a message that may never arrive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codex now has the same behavior as Claude Code during quiet hours:
- Usage bar frozen at last polled value before reset_at
- Pacing bar (green) continues growing time-based during quiet hours
- At reset_at: both bars go to 0% (window reset, no conversation started)
- After reset_at (still quiet): both bars stay at 0%

Also adds codex_session_pacing_pct / codex_weekly_pacing_pct fields
so the Codex pacing indicator updates correctly on every poll and
when the user toggles the pacing setting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The feature is fundamentally about time periods when the user is not
actively using Claude Code — the reset clock is unaffected because no
conversation is started. "Idle Hours" conveys this intent more clearly
than "Quiet Hours" (which implies only notification silencing).

Translations:
  en: Idle Hours
  nl: Inactieve uren
  fr: Heures inactives
  de: Inaktive Stunden
  ja: アイドル時間
  ko: 유휴 시간
  es: Horas inactivas
  zh-TW: 閒置時段

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@owovouo owovouo changed the title Add Traditional Chinese localization and Quiet Hours feature Add Idle Hours and Pacing Indicator May 9, 2026
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