feat: major UI improvements + correct API parsing #5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/improvements"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Major UI improvements + correct API parsing
Six improvements based on real-world testing feedback.
1. Popup positioned near tray icon
Was appearing center-screen. Now uses
QCursor.pos()at click time to position the panel near the tray icon, clamped to screen bounds so it never goes off-screen.2. Panel closes on click-outside
The
eventFilterwas usingunderMouse()which is unreliable forQt.Toolwindows. Changed topanel_rect.contains(QCursor.pos())— a geometry check that works correctly.3. Fixed API client (the 0/0 tokens bug)
The old client was reverse-engineered from a VS Code extension and used the wrong field names (
currentValue,usage,limit). Rewroteapi_client.pybased on the actual API response:TOKENS_LIMITwithnumber=5→ 5-hour windowTOKENS_LIMITwithnumber=1→ weekly windowTIME_LIMITentries are ignored (MCP tool usage)model-usageendpoint entirely (was the source of the 0/0 tokens)4. Next refresh countdown
Footer now shows "Next refresh in Xm" with a live 1-second countdown timer.
5. Unified color scheme
Was: blue icon but green progress bars/peak banner — visually inconsistent. Now all elements use the same palette:
#3b82f6#f59e0b#ef4444SVG icons updated to match.
6. Weekly progress bar + reset times
Weekly section now has its own progress bar (was just text lines). Both 5h and weekly sections show:
in 2h 15m (17:30)Tests: 47 total, all passing
Six improvements based on real-world testing: 1. Popup positioned near tray icon (cursor position) instead of center - Uses QCursor.pos() at activation time - Clamps to screen bounds so it never goes off-screen 2. Panel now closes on click-outside - eventFilter uses geometry.contains(QCursor.pos()) instead of underMouse() which was unreliable for Qt.Tool windows 3. Fixed API client to use real response structure - Rewrote api_client.py based on actual API response: { limits: [ { type, number, percentage, nextResetTime } ], level } - TOKENS_LIMIT number=5 → 5-hour window - TOKENS_LIMIT number=1 → weekly window - Now correctly parses percentage + nextResetTime - Removed old model-usage endpoint (was returning 0/0 tokens) 4. Next refresh countdown - Shows 'Next refresh in Xm' in the panel footer - 1-second countdown timer updates the label live - Resets after each successful fetch 5. Unified color scheme - Normal state is blue (was green for progress bars, blue for icon) - Warning is amber, critical is red - Progress bars, peak banner, and tray icons all follow the same palette - Updated SVG icons to match (#3b82f6 blue, #f59e0b amber, #ef4444 red) 6. Weekly progress bar with reset time - Weekly section now has its own progress bar (was just text) - Shows percentage + reset time for both 5h and weekly windows - Reset time shown as relative + absolute: 'in 2h 15m (17:30)' - Plan level badge (PRO/LITE/MAX) in the header 47 tests, all passing.🤖 Hermes automated review: no blocking issues found
Reviewed the full diff (6 files, +444/−382) between
main(e95fde0) andfeature/improvements(556f858). Focused on the rewritten API client and the GUI changes.Verdict: no blocking issues
Security scan — clean. No hardcoded secrets, shell injection,
eval/exec, unsafe deserialization, or SQL string-formatting in the added lines.Correctness checks performed:
fetch_all,fetch_quota,fetch_usage_stats,QuotaInfo,UsageStats,remaining_tokens). The rename tofetch_usage/LimitInfois fully consistent acrosssrc/andtests/.ApiKeyDialog.key()removal is safe — the key is persisted viastore_api_key()in_save(), and the caller (main.py:563-565) re-reads it withload_api_key()afterAccepted. The removed method was dead code._parse_reset_timecorrectly handlesNone/0/negative values.main.py:541-559) now clamps to screen bounds — no off-screen regression.update_next_refreshwired correctly (_on_data_fetchedresets_seconds_to_refresh).Minor observations (non-blocking)
api_client.py:144,150— limit matching usesnumber == 5/number == 1. Works for the current integer response, but if the API ever returnsnumberas a string (e.g."5"), the==comparison would silently fail to match and both limits would come back asNone. A defensiveint(limit.get("number", 0))would harden it.api_client.py:141—float(limit.get("percentage", 0))raisesValueErrorif the API returns a non-numeric string forpercentage. Low risk given the documented shape, but a try/except around the parse loop would make the client more resilient.api_client.py(LimitInfo.label) — thelabelfield is populated but not rendered anywhere in the UI (section titles are hardcoded inmain.py). Harmless, but currently dead data.Verification
CI/CD: no CI result present for head
556f858at review time (PR opened ~2026-06-28T18:53, 0 comments). Local checks run instead:python3 -m pytest tests/ -q→ 47 passed in 0.83s (15 rewritten api_client tests + 6 config + 9 gui-imports + 17 peak-hours).Automated daily review. I never merge PRs.