From 4299c8e602f5db6a44f5efc3e7c93b3f0c327af2 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Tue, 27 Oct 2026 12:02:44 +0700 Subject: [PATCH] feat: drop the OpenCode Go provider placeholder OpenCode Go never had a usage source, so the provider list, its colours, label, window lengths, default position and settings fields go. With it goes the live/non-live split it introduced: every provider is now live, and toggling still keeps at least one enabled. Saved settings that carry the old OpenCode Go keys still load; the keys are dropped on next save. --- README.md | 3 -- src/badge.rs | 4 +- src/bubble.rs | 3 +- src/commands.rs | 24 +++++----- src/core/geom.rs | 7 +-- src/core/i18n.rs | 3 -- src/core/menu_model.rs | 9 +--- src/core/payload.rs | 10 ---- src/core/settings.rs | 102 ++++++++++++++-------------------------- src/core/state.rs | 4 -- src/core/usage_color.rs | 2 - src/runtime.rs | 26 +++++----- src/tray.rs | 16 ++----- src/usage/registry.rs | 20 +------- src/usage/types.rs | 52 +------------------- 15 files changed, 69 insertions(+), 216 deletions(-) diff --git a/README.md b/README.md index eb2d415..b952aa8 100644 --- a/README.md +++ b/README.md @@ -182,9 +182,6 @@ Use the right-click **Providers** menu to choose what is shown: - **Claude Code** is enabled by default - **Codex** can be enabled alongside Claude Code or shown by itself -- **OpenCode Go** is listed for future support; usage bars remain disabled - until OpenCode exposes a stable quota source or console integration is - explicitly enabled When multiple providers are shown, each gets its own bubble that you can position independently. diff --git a/src/badge.rs b/src/badge.rs index 1340021..893c882 100644 --- a/src/badge.rs +++ b/src/badge.rs @@ -126,8 +126,6 @@ fn base_color(kind: ProviderId) -> [u8; 3] { ProviderId::Claude => [0x2a, 0x1f, 0x1c], // Cool dark slate for Codex. ProviderId::ChatGpt => [0x1a, 0x1f, 0x26], - // Deep green for OpenCode Go. - ProviderId::OpenCodeGo => [0x12, 0x2a, 0x20], } } @@ -178,7 +176,7 @@ mod tests { #[test] fn arc_starts_at_twelve_o_clock_for_any_percent() { - for kind in ProviderId::LIVE_USAGE { + for kind in ProviderId::ALL { for pct in [0.5, 1.0, 25.0, 59.9, 60.0, 80.0, 95.0, 100.0, 150.0] { let pixmap = render_pixmap(kind, Some(pct)); assert_eq!( diff --git a/src/bubble.rs b/src/bubble.rs index 2b8837f..5dd570b 100644 --- a/src/bubble.rs +++ b/src/bubble.rs @@ -263,7 +263,7 @@ fn set_size(window: &WebviewWindow, (w, h): (i32, i32)) { /// Bubble windows that exist, with their provider, in display order. fn windows(app: &AppHandle) -> Vec<(ProviderId, WebviewWindow)> { - ProviderId::LIVE_USAGE + ProviderId::ALL .into_iter() .filter_map(|p| { app.get_webview_window(&runtime::bubble_label(p)) @@ -438,7 +438,6 @@ pub fn mark_ready(app: &AppHandle, label: &str) -> Result<(), String> { let provider = label .strip_prefix(BUBBLE_LABEL_PREFIX) .and_then(ProviderId::from_slug) - .filter(|p| p.metadata().live_usage) .ok_or_else(|| "not a bubble window".to_string())?; if app.get_webview_window(label).is_none() { return Err("no such bubble window".into()); diff --git a/src/commands.rs b/src/commands.rs index 7719658..29cafe7 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -24,7 +24,7 @@ pub fn get_bubble_state( runtime: State<'_, Arc>, provider: String, ) -> Result { - let provider = live_provider(&provider)?; + let provider = parse_provider(&provider)?; let scale = window.scale_factor().map_err(|err| err.to_string())?; Ok(runtime.bubble_payload(provider, scale)) } @@ -66,7 +66,7 @@ pub fn bubble_ready(app: AppHandle, label: String) -> Result<(), String> { /// running inside the webview's IPC callback. #[tauri::command] pub async fn bubble_click(runtime: State<'_, Arc>, slug: String) -> Result<(), String> { - let provider = live_provider(&slug)?; + let provider = parse_provider(&slug)?; runtime.bubble_clicked(provider); Ok(()) } @@ -74,7 +74,7 @@ pub async fn bubble_click(runtime: State<'_, Arc>, slug: String) -> Res /// The page is about to start an OS drag of a bubble. #[tauri::command] pub fn bubble_drag_start(app: AppHandle, slug: String) -> Result<(), String> { - let provider = live_provider(&slug)?; + let provider = parse_provider(&slug)?; bubble::drag_start(&app, provider); Ok(()) } @@ -96,10 +96,8 @@ fn resize_delta(dir: i32) -> Result { } } -fn live_provider(slug: &str) -> Result { - ProviderId::from_slug(slug) - .filter(|p| p.metadata().live_usage) - .ok_or_else(|| "unknown provider".to_string()) +fn parse_provider(slug: &str) -> Result { + ProviderId::from_slug(slug).ok_or_else(|| "unknown provider".to_string()) } /// One log line: control characters become spaces, so a message cannot @@ -117,12 +115,12 @@ mod tests { use super::*; #[test] - fn only_live_providers_are_accepted() { - assert_eq!(live_provider("claude"), Ok(ProviderId::Claude)); - assert_eq!(live_provider("chatgpt"), Ok(ProviderId::ChatGpt)); - assert!(live_provider("opencode-go").is_err()); - assert!(live_provider("").is_err()); - assert!(live_provider("CLAUDE").is_err()); + fn only_known_providers_are_accepted() { + assert_eq!(parse_provider("claude"), Ok(ProviderId::Claude)); + assert_eq!(parse_provider("chatgpt"), Ok(ProviderId::ChatGpt)); + assert!(parse_provider("codex").is_err()); + assert!(parse_provider("").is_err()); + assert!(parse_provider("CLAUDE").is_err()); } #[test] diff --git a/src/core/geom.rs b/src/core/geom.rs index e86995e..69e0a14 100644 --- a/src/core/geom.rs +++ b/src/core/geom.rs @@ -163,7 +163,6 @@ pub fn default_position( let stagger = match model { ProviderId::Claude => 0, ProviderId::ChatGpt => height_px + STAGGER_GAP, - ProviderId::OpenCodeGo => 2 * (height_px + STAGGER_GAP), }; let x = work_area.r - width_px - STAGGER_GAP; let y = work_area.b - height_px - STAGGER_GAP - stagger; @@ -210,7 +209,7 @@ pub struct SnapInput { pub dpi: u32, /// Docked taskbar edge and rect, when the platform reports one. pub taskbar: Option<(Edge, Rect)>, - /// Outer rects of the other bubbles, in `ProviderId::LIVE_USAGE` order. + /// Outer rects of the other bubbles, in `ProviderId::ALL` order. pub peers: Vec, } @@ -499,10 +498,6 @@ mod tests { default_position(WA, w, h, ProviderId::ChatGpt), Point::new(1696, 1040 - 66 - 24 - 90) ); - assert_eq!( - default_position(WA, w, h, ProviderId::OpenCodeGo), - Point::new(1696, 1040 - 66 - 24 - 180) - ); } #[test] diff --git a/src/core/i18n.rs b/src/core/i18n.rs index 917d7e2..ed03656 100644 --- a/src/core/i18n.rs +++ b/src/core/i18n.rs @@ -24,7 +24,6 @@ pub struct LocaleStrings { pub providers: String, pub claude_label: String, pub chatgpt_label: String, - pub opencode_go_label: String, pub settings: String, pub start_with_windows: String, pub start_at_login: String, @@ -124,7 +123,6 @@ impl LocaleStrings { match provider { ProviderId::Claude => &self.claude_label, ProviderId::ChatGpt => &self.chatgpt_label, - ProviderId::OpenCodeGo => &self.opencode_go_label, } } } @@ -141,7 +139,6 @@ fn english_strings() -> LocaleStrings { providers: "Providers".into(), claude_label: "Claude Code".into(), chatgpt_label: "Codex".into(), - opencode_go_label: "OpenCode Go".into(), settings: "Settings".into(), start_with_windows: "Start with Windows".into(), start_at_login: "Start at login".into(), diff --git a/src/core/menu_model.rs b/src/core/menu_model.rs index 9087c83..214944f 100644 --- a/src/core/menu_model.rs +++ b/src/core/menu_model.rs @@ -205,7 +205,7 @@ pub fn build(input: MenuInput<'_>) -> Vec { MenuCommand::ToggleProvider(p), s.provider_label(p), settings.is_provider_enabled(p), - p.metadata().live_usage, + true, ) }) .collect(); @@ -360,7 +360,6 @@ mod tests { poll_interval_ms: POLL_15_MIN, update_check_interval_secs: Some(UPDATE_CHECK_DAILY_SECS), show_codex: true, - show_opencode_go: true, widget_visible: false, ..Settings::default() }; @@ -371,9 +370,6 @@ mod tests { assert!(!checked(&m, "update-interval:off")); assert!(checked(&m, "provider:claude")); assert!(checked(&m, "provider:chatgpt")); - // OpenCode Go mirrors its flag but stays greyed. - assert!(checked(&m, "provider:opencode-go")); - assert!(!enabled(&m, "provider:opencode-go")); assert!(checked(&m, "autostart")); assert!(!checked(&m, "toggle-widget")); } @@ -524,7 +520,6 @@ mod tests { "Providers >", " Claude Code [provider:claude] on", " Codex [provider:chatgpt] off", - " OpenCode Go [provider:opencode-go] off grey", "Settings >", &autostart, " Reset position [reset-position]", @@ -572,7 +567,7 @@ mod tests { } let mut commands = Vec::new(); walk(&m, &mut commands); - assert_eq!(commands.len(), 21); + assert_eq!(commands.len(), 20); for c in commands { assert_eq!(MenuCommand::parse(&c.id()), Some(c), "{}", c.id()); } diff --git a/src/core/payload.rs b/src/core/payload.rs index e518376..7b2a4f8 100644 --- a/src/core/payload.rs +++ b/src/core/payload.rs @@ -14,7 +14,6 @@ use crate::usage::ProviderId; const FIVE_HOURS_SECS: u64 = 5 * 60 * 60; const SEVEN_DAYS_SECS: u64 = 7 * 24 * 60 * 60; -const THIRTY_DAYS_SECS: u64 = 30 * 24 * 60 * 60; /// Placeholder for the head label and weekly countdown before the first poll. const PENDING: &str = "…"; @@ -25,7 +24,6 @@ const PENDING: &str = "…"; pub fn window_durations_secs(provider: ProviderId) -> (u64, u64) { match provider { ProviderId::Claude | ProviderId::ChatGpt => (FIVE_HOURS_SECS, SEVEN_DAYS_SECS), - ProviderId::OpenCodeGo => (SEVEN_DAYS_SECS, THIRTY_DAYS_SECS), } } @@ -339,12 +337,4 @@ mod tests { assert!(panel.get(key).is_some(), "missing {key}"); } } - - #[test] - fn opencode_go_uses_weekly_and_monthly_windows() { - assert_eq!( - window_durations_secs(ProviderId::OpenCodeGo), - (604_800, 2_592_000) - ); - } } diff --git a/src/core/settings.rs b/src/core/settings.rs index 5e7370e..53a8468 100644 --- a/src/core/settings.rs +++ b/src/core/settings.rs @@ -40,9 +40,6 @@ fn default_show_claude() -> bool { fn default_show_codex() -> bool { false } -fn default_show_opencode_go() -> bool { - false -} fn default_widget_visible() -> bool { true } @@ -60,7 +57,6 @@ fn default_update_check_interval_secs() -> Option { pub struct BubblePositions { pub claude: Option<(i32, i32)>, pub codex: Option<(i32, i32)>, - pub opencode_go: Option<(i32, i32)>, } impl BubblePositions { @@ -68,27 +64,23 @@ impl BubblePositions { match provider { ProviderId::Claude => self.claude, ProviderId::ChatGpt => self.codex, - ProviderId::OpenCodeGo => self.opencode_go, } } pub fn set(&mut self, provider: ProviderId, pos: (i32, i32)) { match provider { ProviderId::Claude => self.claude = Some(pos), ProviderId::ChatGpt => self.codex = Some(pos), - ProviderId::OpenCodeGo => self.opencode_go = Some(pos), } } pub fn reset(&mut self, provider: ProviderId) { match provider { ProviderId::Claude => self.claude = None, ProviderId::ChatGpt => self.codex = None, - ProviderId::OpenCodeGo => self.opencode_go = None, } } pub fn reset_all(&mut self) { self.claude = None; self.codex = None; - self.opencode_go = None; } /// Drop any saved position whose top-left no longer falls on one of @@ -115,8 +107,6 @@ pub struct Settings { pub show_claude_code: bool, #[serde(default = "default_show_codex")] pub show_codex: bool, - #[serde(default = "default_show_opencode_go")] - pub show_opencode_go: bool, #[serde(default)] pub bubble_positions: BubblePositions, #[serde(default = "default_bubble_size")] @@ -136,7 +126,6 @@ impl Default for Settings { Self { show_claude_code: default_show_claude(), show_codex: default_show_codex(), - show_opencode_go: default_show_opencode_go(), bubble_positions: BubblePositions::default(), bubble_size_logical: default_bubble_size(), poll_interval_ms: default_poll_interval_ms(), @@ -152,7 +141,6 @@ impl Settings { match provider { ProviderId::Claude => self.show_claude_code, ProviderId::ChatGpt => self.show_codex, - ProviderId::OpenCodeGo => self.show_opencode_go, } } @@ -160,30 +148,23 @@ impl Settings { match provider { ProviderId::Claude => self.show_claude_code = enabled, ProviderId::ChatGpt => self.show_codex = enabled, - ProviderId::OpenCodeGo => self.show_opencode_go = enabled, } } - pub fn toggle_provider(&mut self, provider: ProviderId) { + /// Menu toggle for a provider. Unchecking the last enabled provider is + /// undone so the app always shows something. Returns the provider's new + /// state. + pub fn toggle_provider(&mut self, provider: ProviderId) -> bool { let enabled = !self.is_provider_enabled(provider); self.set_provider_enabled(provider, enabled); - } - - /// Menu toggle for a provider. Providers without live usage cannot be - /// toggled, and unchecking the last enabled live provider is undone so - /// the app always shows something. Returns the provider's new state. - pub fn toggle_live_provider(&mut self, provider: ProviderId) -> bool { - if provider.metadata().live_usage { - self.toggle_provider(provider); - if !self.has_enabled_live_provider() { - self.set_provider_enabled(provider, true); - } + if !self.has_enabled_provider() { + self.set_provider_enabled(provider, true); } self.is_provider_enabled(provider) } - pub fn has_enabled_live_provider(&self) -> bool { - ProviderId::LIVE_USAGE + pub fn has_enabled_provider(&self) -> bool { + ProviderId::ALL .iter() .any(|provider| self.is_provider_enabled(*provider)) } @@ -310,8 +291,8 @@ fn parse(content: &str) -> Option { /// Enforce the invariants a hand-edited file may break. fn normalise(mut settings: Settings) -> Settings { - // At least one live provider must be visible. Otherwise the app has nothing to show. - if !settings.has_enabled_live_provider() { + // At least one provider must be visible. Otherwise the app has nothing to show. + if !settings.has_enabled_provider() { settings.show_claude_code = true; } // Clamp bubble size to safe range in case settings.json was hand-edited. @@ -408,7 +389,6 @@ mod tests { assert!(!settings.show_claude_code); assert!(settings.show_codex); - assert!(!settings.show_opencode_go); assert_eq!( settings.bubble_positions.get(ProviderId::Claude), Some((10, 20)) @@ -417,30 +397,32 @@ mod tests { settings.bubble_positions.get(ProviderId::ChatGpt), Some((30, 40)) ); - assert_eq!(settings.bubble_positions.get(ProviderId::OpenCodeGo), None); } #[test] - fn three_provider_settings_json_loads() { + fn retired_opencode_go_keys_are_ignored() { let json = r#"{ - "show_claude_code": true, - "show_codex": false, + "show_claude_code": false, + "show_codex": true, "show_opencode_go": true, "bubble_positions": { + "codex": [30, 40], "opencode_go": [50, 60] } }"#; let settings: Settings = - serde_json::from_str(json).expect("new settings json should parse"); + serde_json::from_str(json).expect("settings with retired keys should parse"); - assert!(settings.show_claude_code); - assert!(!settings.show_codex); - assert!(settings.show_opencode_go); + assert!(!settings.show_claude_code); + assert!(settings.show_codex); assert_eq!( - settings.bubble_positions.get(ProviderId::OpenCodeGo), - Some((50, 60)) + settings.bubble_positions.get(ProviderId::ChatGpt), + Some((30, 40)) ); + let keys = serde_json::to_value(&settings).expect("serialise"); + assert!(keys.get("show_opencode_go").is_none()); + assert!(keys["bubble_positions"].get("opencode_go").is_none()); } #[test] @@ -518,10 +500,9 @@ mod tests { } #[test] - fn normalise_restores_a_live_provider_and_clamps_size() { + fn normalise_restores_a_provider_and_clamps_size() { let settings = parse_and_normalise( - r#"{"show_claude_code": false, "show_codex": false, "show_opencode_go": true, - "bubble_size_logical": 9000}"#, + r#"{"show_claude_code": false, "show_codex": false, "bubble_size_logical": 9000}"#, ); assert!(settings.show_claude_code); assert_eq!(settings.bubble_size_logical, MAX_BUBBLE_SIZE); @@ -543,7 +524,6 @@ mod tests { "poll_interval_ms", "show_claude_code", "show_codex", - "show_opencode_go", "update_check_interval_secs", "widget_visible", ] @@ -557,15 +537,13 @@ mod tests { #[test] fn offscreen_positions_are_dropped_against_monitors() { let mut settings = parse_and_normalise( - r#"{"bubble_positions": {"claude": [100, 100], "codex": [-3000, 100], - "opencode_go": [2000, 500]}}"#, + r#"{"bubble_positions": {"claude": [100, 100], "codex": [-3000, 100]}}"#, ); let monitors = [Rect::new(0, 0, 1920, 1080), Rect::new(1920, 0, 3840, 1080)]; drop_offscreen_positions(&mut settings, &monitors); let p = &settings.bubble_positions; assert_eq!(p.get(ProviderId::Claude), Some((100, 100))); assert_eq!(p.get(ProviderId::ChatGpt), None); - assert_eq!(p.get(ProviderId::OpenCodeGo), Some((2000, 500))); } #[test] @@ -617,27 +595,15 @@ mod tests { } #[test] - fn toggle_keeps_provider_flags_independent() { + fn toggle_never_disables_the_last_provider() { let mut settings = Settings::default(); - settings.toggle_provider(ProviderId::ChatGpt); - assert!(settings.is_provider_enabled(ProviderId::ChatGpt)); - settings.toggle_provider(ProviderId::Claude); - assert!(!settings.is_provider_enabled(ProviderId::Claude)); - assert!(settings.has_enabled_live_provider()); - settings.toggle_provider(ProviderId::ChatGpt); - assert!(!settings.has_enabled_live_provider()); - } - - #[test] - fn live_toggle_never_disables_the_last_live_provider() { - let mut settings = Settings::default(); - assert!(settings.toggle_live_provider(ProviderId::Claude)); - assert!(settings.show_claude_code); - assert!(settings.toggle_live_provider(ProviderId::ChatGpt)); - assert!(!settings.toggle_live_provider(ProviderId::Claude)); - assert!(settings.toggle_live_provider(ProviderId::ChatGpt)); - // OpenCode Go has no live usage, so its flag never changes. - assert!(!settings.toggle_live_provider(ProviderId::OpenCodeGo)); - assert!(!settings.show_opencode_go); + assert!(settings.toggle_provider(ProviderId::ChatGpt)); + assert!(!settings.toggle_provider(ProviderId::Claude)); + assert!(settings.has_enabled_provider()); + // ChatGPT is now the only enabled provider, so it stays on. + assert!(settings.toggle_provider(ProviderId::ChatGpt)); + assert!(settings.show_codex); + assert!(settings.toggle_provider(ProviderId::Claude)); + assert!(!settings.toggle_provider(ProviderId::ChatGpt)); } } diff --git a/src/core/state.rs b/src/core/state.rs index 549c8df..fba0f99 100644 --- a/src/core/state.rs +++ b/src/core/state.rs @@ -79,10 +79,6 @@ impl Alert { strings.chatgpt_token_expired_title.clone(), strings.chatgpt_token_expired_body.clone(), ), - ProviderId::OpenCodeGo => ( - strings.opencode_go_label.clone(), - strings.update_failed.clone(), - ), }, Alert::UpdateApplied { version } => ( strings.update_applied_title.clone(), diff --git a/src/core/usage_color.rs b/src/core/usage_color.rs index b82525a..37d4c93 100644 --- a/src/core/usage_color.rs +++ b/src/core/usage_color.rs @@ -19,7 +19,6 @@ pub fn accent_color_for(model: ProviderId, is_dark: bool) -> Color { Color::from_hex("#1A1A1A") } } - ProviderId::OpenCodeGo => Color::from_hex("#3BAE75"), } } @@ -74,7 +73,6 @@ mod tests { fn safe_band_uses_theme_aware_accent() { assert_eq!(hex(ProviderId::ChatGpt, true, 10.0), "#E5E5E5"); assert_eq!(hex(ProviderId::ChatGpt, false, 10.0), "#1A1A1A"); - assert_eq!(hex(ProviderId::OpenCodeGo, false, 0.0), "#3BAE75"); assert_eq!( accent_color_for(ProviderId::Claude, false), accent_color_for(ProviderId::Claude, true) diff --git a/src/runtime.rs b/src/runtime.rs index e38ea1e..e2839af 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -139,7 +139,7 @@ struct BubbleSnapshot { impl BubbleSnapshot { fn of(state: &AppState) -> Self { Self { - providers: enabled_live_providers(&state.settings), + providers: enabled_providers(&state.settings), usage: state.usage.clone(), strings: state.strings.clone(), is_dark: state.is_dark, @@ -264,7 +264,7 @@ impl Runtime { }; let (views, menu_snapshot) = runtime.with_state(|s| { ( - tray::views(&enabled_live_providers(&s.settings), &s.usage, &s.strings), + tray::views(&enabled_providers(&s.settings), &s.usage, &s.strings), MenuSnapshot::of(s), ) }); @@ -468,13 +468,10 @@ impl Runtime { }); } - /// Toggle a live provider, keeping at least one enabled, then create or + /// Toggle a provider, keeping at least one enabled, then create or /// destroy its bubble, update the tray and poll. fn toggle_provider(self: &Arc, provider: ProviderId) { - if !provider.metadata().live_usage { - return; - } - let enabled = self.update_settings(|s| s.toggle_live_provider(provider)); + let enabled = self.update_settings(|s| s.toggle_provider(provider)); if enabled { bubble::spawn_create(&self.ui.app, provider); } else if let Some(window) = self.ui.app.get_webview_window(&bubble_label(provider)) { @@ -835,9 +832,9 @@ pub fn bubble_label(provider: ProviderId) -> String { format!("{BUBBLE_LABEL_PREFIX}{}", provider.slug()) } -/// Live providers the user has enabled, in display order. -pub fn enabled_live_providers(settings: &Settings) -> Vec { - ProviderId::LIVE_USAGE +/// Providers the user has enabled, in display order. +pub fn enabled_providers(settings: &Settings) -> Vec { + ProviderId::ALL .into_iter() .filter(|p| settings.is_provider_enabled(*p)) .collect() @@ -1008,7 +1005,7 @@ fn setup(app: &mut tauri::App, cli: &CliArgs) -> Result<(), Box> { update_interval_secs: state.settings.update_check_interval_secs, last_update_check_unix: state.settings.last_update_check_unix, }; - let providers = enabled_live_providers(&state.settings); + let providers = enabled_providers(&state.settings); let (tx, rx) = mpsc::channel(); let runtime = Arc::new(Runtime { @@ -1150,18 +1147,17 @@ mod tests { } #[test] - fn enabled_providers_keep_display_order_and_skip_non_live_ones() { + fn enabled_providers_keep_display_order() { let mut settings = Settings { show_codex: true, - show_opencode_go: true, ..Settings::default() }; assert_eq!( - enabled_live_providers(&settings), + enabled_providers(&settings), [ProviderId::Claude, ProviderId::ChatGpt] ); settings.show_claude_code = false; - assert_eq!(enabled_live_providers(&settings), [ProviderId::ChatGpt]); + assert_eq!(enabled_providers(&settings), [ProviderId::ChatGpt]); } #[test] diff --git a/src/tray.rs b/src/tray.rs index 0d2bac8..e9e049f 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -37,7 +37,6 @@ pub struct TrayView { pub fn views(enabled: &[ProviderId], usage: &UsageState, strings: &LocaleStrings) -> Vec { enabled .iter() - .filter(|p| p.metadata().live_usage) .map(|&provider| { let entry = usage.snapshots.get(&provider); TrayView { @@ -106,7 +105,7 @@ struct Plan { fn plan(shown: &HashMap, views: &[TrayView]) -> Plan { let mut plan = Plan { - remove: ProviderId::LIVE_USAGE + remove: ProviderId::ALL .into_iter() .filter(|p| shown.contains_key(p) && !views.iter().any(|v| v.provider == *p)) .collect(), @@ -153,7 +152,7 @@ impl Trays { /// The providers that currently have an icon. pub fn providers(&self) -> Vec { let shown = self.shown(); - ProviderId::LIVE_USAGE + ProviderId::ALL .into_iter() .filter(|p| shown.contains_key(p)) .collect() @@ -319,13 +318,8 @@ mod tests { #[test] fn views_follow_enabled_providers_and_last_poll() { let s = strings(); - let all = [ - ProviderId::Claude, - ProviderId::ChatGpt, - ProviderId::OpenCodeGo, - ]; - let v = views(&all, &usage(42.0, true), &s); - assert_eq!(v.len(), 2, "OpenCode Go never gets an icon"); + let v = views(&ProviderId::ALL, &usage(42.0, true), &s); + assert_eq!(v.len(), 2); assert_eq!(v[0].provider, ProviderId::Claude); assert_eq!(v[0].percent, Some(42.0)); assert_eq!( @@ -335,7 +329,7 @@ mod tests { assert_eq!(v[1].provider, ProviderId::ChatGpt); assert_eq!(v[1].percent, None); // A failed last poll greys every badge to the track. - let v = views(&all, &usage(42.0, false), &s); + let v = views(&ProviderId::ALL, &usage(42.0, false), &s); assert_eq!(v[0].percent, None); assert!(views(&[ProviderId::ChatGpt], &usage(1.0, true), &s) .iter() diff --git a/src/usage/registry.rs b/src/usage/registry.rs index 18cc62a..0ad20c3 100644 --- a/src/usage/registry.rs +++ b/src/usage/registry.rs @@ -31,16 +31,13 @@ impl Registry { settings: &Settings, ) -> Vec<(ProviderId, Result)> { let mut out = Vec::new(); - for provider in ProviderId::LIVE_USAGE { + for provider in ProviderId::ALL { if !settings.is_provider_enabled(provider) { continue; } let result = match provider { ProviderId::Claude => self.claude.poll(http), ProviderId::ChatGpt => self.chatgpt.poll(http), - ProviderId::OpenCodeGo => { - unreachable!("OpenCode Go has no live usage provider yet") - } }; out.push((provider, result)); } @@ -56,7 +53,6 @@ impl Registry { let locator = match id { ProviderId::Claude => self.claude.locator(), ProviderId::ChatGpt => self.chatgpt.locator(), - ProviderId::OpenCodeGo => return refresh::Outcome::CliMissing, }; match locator.first_available() { Some(src) => orchestrator.refresh(src), @@ -80,18 +76,6 @@ mod tests { use super::*; use crate::net::USER_AGENT; - #[test] - fn live_usage_excludes_opencode_go() { - // `poll_enabled` relies on this for its `unreachable!` arm. - assert!(!ProviderId::LIVE_USAGE.contains(&ProviderId::OpenCodeGo)); - for provider in ProviderId::ALL { - assert_eq!( - ProviderId::LIVE_USAGE.contains(&provider), - provider.metadata().live_usage - ); - } - } - /// Polls the real providers with the credentials on this machine. Run by /// hand: `cargo test -- --ignored live_poll --nocapture`. #[test] @@ -104,7 +88,7 @@ mod tests { } let mut registry = Registry::with_defaults(); let results = registry.poll_enabled(&http, &settings); - assert_eq!(results.len(), ProviderId::LIVE_USAGE.len()); + assert_eq!(results.len(), ProviderId::ALL.len()); for (provider, result) in results { match result { Ok(w) => println!( diff --git a/src/usage/types.rs b/src/usage/types.rs index 3e54ff8..6f2b1e9 100644 --- a/src/usage/types.rs +++ b/src/usage/types.rs @@ -12,19 +12,15 @@ pub enum ProviderId { Claude, #[serde(rename = "chatgpt")] ChatGpt, - #[serde(rename = "opencode-go")] - OpenCodeGo, } impl ProviderId { - pub const ALL: [Self; 3] = [Self::Claude, Self::ChatGpt, Self::OpenCodeGo]; - pub const LIVE_USAGE: [Self; 2] = [Self::Claude, Self::ChatGpt]; + pub const ALL: [Self; 2] = [Self::Claude, Self::ChatGpt]; pub fn slug(self) -> &'static str { match self { Self::Claude => "claude", Self::ChatGpt => "chatgpt", - Self::OpenCodeGo => "opencode-go", } } @@ -32,51 +28,6 @@ impl ProviderId { pub fn from_slug(slug: &str) -> Option { Self::ALL.into_iter().find(|p| p.slug() == slug) } - - pub fn metadata(self) -> ProviderMetadata { - match self { - Self::Claude => ProviderMetadata { - id: self, - slug: "claude", - default_enabled: true, - tray_icon_id: 1, - display_mode: ProviderDisplayMode::TwoWindow, - live_usage: true, - }, - Self::ChatGpt => ProviderMetadata { - id: self, - slug: "chatgpt", - default_enabled: false, - tray_icon_id: 2, - display_mode: ProviderDisplayMode::TwoWindow, - live_usage: true, - }, - Self::OpenCodeGo => ProviderMetadata { - id: self, - slug: "opencode-go", - default_enabled: false, - tray_icon_id: 3, - display_mode: ProviderDisplayMode::WeeklyMonthlyFourBar, - live_usage: false, - }, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum ProviderDisplayMode { - TwoWindow, - WeeklyMonthlyFourBar, -} - -#[derive(Clone, Copy, Debug)] -pub struct ProviderMetadata { - pub id: ProviderId, - pub slug: &'static str, - pub default_enabled: bool, - pub tray_icon_id: u32, - pub display_mode: ProviderDisplayMode, - pub live_usage: bool, } /// One usage window: how much you've consumed (0–100), and when it resets. @@ -110,7 +61,6 @@ mod tests { let json = serde_json::to_string(&provider).expect("serialise"); assert_eq!(json, format!("\"{}\"", provider.slug())); assert_eq!(ProviderId::from_slug(provider.slug()), Some(provider)); - assert_eq!(provider.metadata().slug, provider.slug()); } assert_eq!(ProviderId::from_slug("codex"), None); }