diff --git a/CHANGELOG.md b/CHANGELOG.md index b4002d5..b00fd3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CHANGELOG.zh-TW.md b/CHANGELOG.zh-TW.md index 1f837e2..e13686f 100644 --- a/CHANGELOG.zh-TW.md +++ b/CHANGELOG.zh-TW.md @@ -4,6 +4,11 @@ 本檔記錄 usage 所有重要變更。格式參考 [Keep a Changelog](https://keepachangelog.com/)。 +## [Unreleased] + +### 修正 +- **Windows 面板的卡片排序現在會跨主題同步**:拖曳額度卡後重載其他主題時,會在注入 state 前重新讀取共用的已儲存排序,因此所有支援拖曳的面板都會使用相同的 Claude、Codex 與 Antigravity 卡片順序。 + ## [0.28.3] - 2026-07-16 ### 修正 diff --git a/tests/test_wintray.py b/tests/test_wintray.py index 2e1d6a1..7d016e7 100644 --- a/tests/test_wintray.py +++ b/tests/test_wintray.py @@ -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 @@ -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: diff --git a/wintray.py b/wintray.py index 2e7d8ad..b925949 100644 --- a/wintray.py +++ b/wintray.py @@ -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: