Merge branch 'worktree-wf_093aad93-ad0-7' into review-fixes

This commit is contained in:
tiennm99 committed 2026-10-23 03:41:26 +07:00
commit 30446570c9
3 files changed
+63 -7

No files matched your search

+25 -6
View File
@@ -37,7 +37,7 @@ self-updater are all written from scratch against the same public APIs
- Left-click the bubble for an expanded panel with both **5h** and **7d**
bars plus reset countdowns
- Right-click for refresh, displayed providers, update frequency, startup,
updates, exit
**Reset position**, updates, **Show widget**, **Restart** and **Exit**
- A system tray icon per enabled provider, with a usage badge
- Desktop notifications when usage crosses a threshold, when a sign-in
expires and after an update was applied
@@ -55,7 +55,11 @@ right-click **Providers** menu.
- Windows 10 or Windows 11, or Linux x86_64 (see below)
- Claude Code (CLI or App) installed and authenticated
- Optional: Codex CLI installed and authenticated, if you want Codex usage
- The `claude` CLI, for automatic token refresh. The app looks for it on
`PATH`, and on Linux also in `~/.local/bin` and `~/.claude/local`. Without
it an expired sign-in is not refreshed and you have to sign in again.
- Optional: Codex CLI installed and authenticated, if you want Codex usage.
Its token is refreshed through the `codex` CLI, found the same way.
### Windows
@@ -161,9 +165,11 @@ primary monitor on first launch. Drag it where you want it, release to snap
to the nearest edge if you let go close to one.
- **Left-click** the bubble to open the expanded panel (5h + 7d + countdowns)
- **Right-click** for refresh, providers, refresh frequency, "Start with
Windows" ("Start at login" on Linux), controls, auto-update check
(Disabled / Hourly / Daily / Weekly), manual "Check for updates", exit
- **Right-click** for **Refresh**, **Update frequency**, **Providers**,
**Settings**, **Controls**, **Show widget**, **Restart** and **Exit**.
**Settings** holds "Start with Windows" ("Start at login" on Linux),
**Reset position**, a manual **Check for updates** and the
**Auto-update check** interval (Disabled / Hourly / Daily / Weekly)
- **Drag** anywhere — it floats on top of all other windows
- **Ctrl + MouseWheel** on the bubble, or **Controls** in the right-click
menu, to resize it
@@ -244,13 +250,26 @@ What the app sends over the network:
- Requests to Anthropic's Claude endpoints to read your usage
- Requests to ChatGPT's Codex usage endpoint, if Codex is enabled
- Requests to GitHub only if you use the app's update-check feature
- Requests for `latest.json` from GitHub Releases, at first launch and then
hourly by default. Set **Settings** → **Auto-update check** to
**Disabled** in the right-click menu to stop them; **Check for updates**
still works when you click it.
What the app runs:
- When a usage request is rejected because the sign-in has expired, the app
runs `claude -p .` for Claude Code and `codex exec .` for Codex, for each
provider that was rejected. They run hidden in the background, so the CLI
refreshes its token. Each run sends one tiny prompt, which counts toward
your usage.
What the app stores locally:
- Bubble position(s) per model
- Bubble size
- Whether the bubbles are shown (**Show widget**)
- Polling frequency
- Auto-update check interval
- Last update check time
- Displayed provider preferences
+30
View File
@@ -38,6 +38,9 @@ pub struct LocaleStrings {
pub control_drag: String,
pub control_ctrl_wheel: String,
pub control_tray_click: String,
/// Linux variant of `control_tray_click`: AppIndicator delivers no tray
/// clicks, so every click opens the menu.
pub control_tray_click_linux: String,
pub tray_left_click: String,
pub check_for_updates: String,
pub checking_for_updates: String,
@@ -105,6 +108,17 @@ impl LocaleStrings {
}
}
/// Controls hint for the tray icon on the platform this build targets.
/// Linux tray icons (AppIndicator) only open the menu, so the
/// show/hide click is not offered there.
pub fn tray_click_hint(&self) -> &str {
if cfg!(target_os = "linux") {
&self.control_tray_click_linux
} else {
&self.control_tray_click
}
}
/// Display name of a provider.
pub fn provider_label(&self, provider: ProviderId) -> &str {
match provider {
@@ -141,6 +155,7 @@ fn english_strings() -> LocaleStrings {
control_drag: "Drag: move/snap".into(),
control_ctrl_wheel: "Ctrl+Wheel: resize".into(),
control_tray_click: "Tray click: show/hide".into(),
control_tray_click_linux: "Tray click: menu".into(),
tray_left_click: "Left-click: show/hide".into(),
check_for_updates: "Check for updates".into(),
checking_for_updates: "Checking for updates...".into(),
@@ -286,6 +301,10 @@ mod tests {
("control_drag", strings.control_drag.as_str()),
("control_ctrl_wheel", strings.control_ctrl_wheel.as_str()),
("control_tray_click", strings.control_tray_click.as_str()),
(
"control_tray_click_linux",
strings.control_tray_click_linux.as_str(),
),
("tray_left_click", strings.tray_left_click.as_str()),
] {
assert!(!value.trim().is_empty(), "empty string: {name}");
@@ -372,4 +391,15 @@ mod tests {
assert_eq!(s.autostart_label(), expected);
assert_eq!(s.provider_label(ProviderId::ChatGpt), "Codex");
}
#[test]
fn tray_click_hint_matches_the_platform() {
let s = english_strings();
let expected = if cfg!(target_os = "linux") {
"Tray click: menu"
} else {
"Tray click: show/hide"
};
assert_eq!(s.tray_click_hint(), expected);
}
}
+8 -1
View File
@@ -267,7 +267,7 @@ pub fn build(input: MenuInput<'_>) -> Vec<MenuEntry> {
hint(&s.control_right_click),
hint(&s.control_drag),
hint(&s.control_ctrl_wheel),
hint(&s.control_tray_click),
hint(s.tray_click_hint()),
];
vec![
@@ -453,6 +453,13 @@ mod tests {
assert!(entries[4..]
.iter()
.all(|e| matches!(e, MenuEntry::Hint { .. })));
let strings = I18n::load().strings().clone();
assert_eq!(
entries[8],
MenuEntry::Hint {
label: strings.tray_click_hint().to_string()
}
);
}
/// One line per node, indented by depth: `label [id] kind`.