mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-10-25 00:18:48 +00:00
feat: redesign bubble as two-bar rounded rect, finish port wiring
Bubble shape changes from circle to rounded rectangle showing two stacked horizontal bars — top: session (5h), bottom: weekly (7d) — each followed by a right-aligned "X% · Yh Zm" string (percent + countdown). Bubble surface: - BubbleConfig/BubbleState carry session+weekly percents and texts (mirrors PanelData); update_percentage renamed to update_data - Aspect ratio fixed at 3:1; size_logical is interpreted as width with height derived. Clamp is 120..360 (was 32..128 square) - Hit-testing uses a rounded-rect predicate (point_in_rounded_rect) shared with the alpha mask so paint and click area can't drift - New rgb_to_dib helper for direct DIB writes — BI_RGB 32bpp stores B,G,R,X in memory which is the opposite of COLORREF. The previous code wrote COLORREF-packed u32 straight into DIB pixels; invisible while every color was gray, but the new orange/red bar fills would have rendered blue - bar_h capped at h/4 (range 6..18) so the text font derived from it stays small enough that "100% · 23h" fits in right_text_w (= 6×bar_h, min 56); the first iteration had a 19-px font in a 60-px column and ellipsized away the countdown - Initial session_text/weekly_text seeded with "…" so the bubble has visible feedback during the first poll instead of two empty grey tracks Compile + cleanup needed to make the port build at all: - Color::from_hex added back as an infallible wrapper around parse_hex (15 call sites in bubble.rs/panel.rs assumed the old infallible API) - Color::to_colorref → into_colorref at 5 call sites - GetModuleFileNameW added to the LibraryLoader import in bubble.rs - usage::Error gains Creds(#[from] creds::Error) so `?` works in the Anthropic and ChatGPT providers - FlattenBoxed::flatten renamed to flatten_box — the std Option::flatten was shadowing it and yielding Option<Box<T>> instead of Option<T> - PanelState marked unsafe Send (HWND has *mut c_void; state is only touched from the UI thread, Mutex is for OnceLock satisfaction) - Crate-level #![allow(dead_code)] for in-progress port API surface (creds, usage, update, os::dpi); unused pub-use re-exports removed App wiring: - propagate_to_ui now feeds both windows + their formatted texts into update_data (was a single percent)
This commit is contained in:
1 parent
aa6217d2cf
commit
ee4e1c9b26
10 files changed
+1839
-149
No files matched your search
@@ -8,7 +8,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::creds::{CredentialSource, Locator};
|
||||
use crate::creds::Locator;
|
||||
use crate::net::Client;
|
||||
use crate::usage::{headers, Error, ProviderId, UsageProvider, UsageWindows, Window};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::creds::{CredentialSource, Locator};
|
||||
use crate::creds::Locator;
|
||||
use crate::net::Client;
|
||||
use crate::usage::{Error, ProviderId, UsageProvider, UsageWindows, Window};
|
||||
|
||||
@@ -67,16 +67,16 @@ impl UsageProvider for ChatGptProvider {
|
||||
}
|
||||
|
||||
fn envelope_to_windows(envelope: Envelope) -> Option<UsageWindows> {
|
||||
let rl = envelope.rate_limit.flatten()?;
|
||||
let rl = envelope.rate_limit.flatten_box()?;
|
||||
Some(UsageWindows {
|
||||
primary: rl
|
||||
.primary_window
|
||||
.flatten()
|
||||
.flatten_box()
|
||||
.map(window_from)
|
||||
.unwrap_or_default(),
|
||||
secondary: rl
|
||||
.secondary_window
|
||||
.flatten()
|
||||
.flatten_box()
|
||||
.map(window_from)
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
@@ -114,12 +114,14 @@ struct ApiWindow {
|
||||
reset_at: i64,
|
||||
}
|
||||
|
||||
// Helpers used to make `Option<Option<Box<…>>>` flatten cleanly.
|
||||
// Helpers used to make `Option<Option<Box<…>>>` flatten cleanly. We can't
|
||||
// reuse the std `Option::flatten` name — the inherent method (which returns
|
||||
// `Option<Box<T>>`) would shadow this trait method.
|
||||
trait FlattenBoxed<T> {
|
||||
fn flatten(self) -> Option<T>;
|
||||
fn flatten_box(self) -> Option<T>;
|
||||
}
|
||||
impl<T> FlattenBoxed<T> for Option<Option<Box<T>>> {
|
||||
fn flatten(self) -> Option<T> {
|
||||
fn flatten_box(self) -> Option<T> {
|
||||
self.and_then(|inner| inner.map(|b| *b))
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -12,7 +12,7 @@ pub mod registry;
|
||||
pub mod types;
|
||||
|
||||
pub use registry::Registry;
|
||||
pub use types::{ProviderId, ProviderSnapshot, UsageWindows, Window};
|
||||
pub use types::{ProviderId, UsageWindows, Window};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@@ -24,6 +24,8 @@ pub enum Error {
|
||||
TokenExpired,
|
||||
#[error("network: {0}")]
|
||||
Network(#[from] crate::net::Error),
|
||||
#[error("credentials: {0}")]
|
||||
Creds(#[from] crate::creds::Error),
|
||||
#[error("unexpected response shape: {0}")]
|
||||
BadResponse(String),
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user