Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
All notable changes to usage are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Fixed
- **Windows panel card order now follows across themes**: reloading a theme after dragging a quota card now rereads the shared saved order before state injection, so every draggable panel opens with the same Claude, Codex, and Antigravity card order.

## [0.28.3] - 2026-07-16

### Fixed
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

本檔記錄 usage 所有重要變更。格式參考 [Keep a Changelog](https://keepachangelog.com/)。

## [Unreleased]

### 修正
- **Windows 面板的卡片排序現在會跨主題同步**:拖曳額度卡後重載其他主題時,會在注入 state 前重新讀取共用的已儲存排序,因此所有支援拖曳的面板都會使用相同的 Claude、Codex 與 Antigravity 卡片順序。

## [0.28.3] - 2026-07-16

### 修正
Expand Down
41 changes: 41 additions & 0 deletions tests/test_wintray.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

from __future__ import annotations

import json
import sys
import threading
from types import SimpleNamespace

import pytest

import menubar_prefs
import menubar_state
import wintray

Expand Down Expand Up @@ -214,6 +216,45 @@ def start(self) -> None:
assert len(scheduled) == 1


@pytest.mark.parametrize("panel_id", ["matrix", "aquarium", "win95"])
def test_card_order_persists_into_the_next_loaded_panel(
monkeypatch: pytest.MonkeyPatch,
panel_id: str,
) -> None:
preferences: dict[str, object] = {}
injected: list[str] = []
loaded: list[str] = []
window = SimpleNamespace(
evaluate_js=injected.append,
load_html=loaded.append,
)
controller = wintray._WindowsTrayController(mock=True, interval=60)
controller.window = window
controller.visible = True
order = ["codex", "claude", "agy"]

monkeypatch.setattr(wintray, "_load_preferences", lambda: preferences.copy())
monkeypatch.setattr(menubar_prefs, "_load_preferences", lambda: preferences.copy())
monkeypatch.setattr(
wintray,
"_save_preferences",
lambda updated: preferences.update(updated),
)
monkeypatch.setattr(controller, "_place_window", lambda: None)

controller.handle_panel_message(
json.dumps({"action": "set_card_order", "order": order})
)
controller.switch_panel(panel_id)
controller.on_loaded()

assert preferences["quota_card_order"] == order
assert controller.latest_state.card_order == tuple(order)
assert len(loaded) == 1
payload = injected[-1].removeprefix("window.usageApplyState(").removesuffix(")")
assert json.loads(payload)["cardOrder"] == order


def test_run_app_wires_pystray_and_pywebview(
monkeypatch: pytest.MonkeyPatch,
) -> None:
Expand Down
5 changes: 5 additions & 0 deletions wintray.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,11 @@ def show_panel(self, _icon: Any = None, _item: Any = None) -> None:
def switch_panel(self, panel_id: str) -> None:
self.active_panel_id = panel_id
_save_active_panel_id(panel_id)
# A panel reload is initialized from ``latest_state`` in ``on_loaded``.
# Card order is changed directly by the JS bridge, outside the refresh
# worker, so refresh this field from the shared preferences before the
# next theme receives that state.
self.latest_state.card_order = _quota_card_order()
self.window.load_html(panel_html(self.panel_filename()))

def _deferred_switch_panel(self) -> None:
Expand Down