mirror of
https://github.com/tiennm99/claude-code-usage-bubble.git
synced 2026-10-21 20:16:50 +00:00
Replace the WinHTTP client with a ureq agent that keeps the same Client/Response surface: status errors come back as responses, gzip, 60 s connect / 30 s receive / 120 s total timeouts, rustls with the platform verifier and a per-request User-Agent override. Environment proxies come from ureq. On Windows, when none is set, the static Internet Settings proxy is read from HKCU and parsed by a pure core module (per-scheme ProxyServer, ProxyOverride globs and <local>); bypassed hosts go through a direct agent. PAC/WPAD is not evaluated. The usage providers and credential sources compile again on every platform. Refresh keeps the Windows candidates and CREATE_NO_WINDOW; Unix spawns claude/codex from PATH plus the usual per-user install directories and reaps the child on a detached thread. An empty CODEX_HOME now counts as unset and a float expiresAt is truncated instead of ignored. Fixture tests cover the OAuth usage body, rate-limit headers (including a 429 over loopback), the ChatGPT envelope, ISO 8601 edge cases, credential JSON and proxy strings. An ignored live_poll test polls the real providers.
77 lines
2.4 KiB
Rust
77 lines
2.4 KiB
Rust
//! Claude Code Usage Bubble on Tauri v2.
|
|
//!
|
|
//! The binary in `src/main.rs` only calls [`run`]. Every module is `pub` so
|
|
//! that items wired up incrementally do not trip `dead_code`.
|
|
|
|
pub mod core;
|
|
pub mod creds;
|
|
pub mod diag;
|
|
pub mod net;
|
|
pub mod platform;
|
|
pub mod usage;
|
|
|
|
use tauri::{RunEvent, WebviewUrl, WebviewWindowBuilder, WindowEvent};
|
|
|
|
/// Label of the scaffold window that proves the webview pipeline works.
|
|
const TEST_WINDOW_LABEL: &str = "bubble-test";
|
|
|
|
/// Starts the application and blocks until it exits.
|
|
pub fn run() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
let diagnose_enabled = args.iter().any(|a| a == "--diagnose");
|
|
match diag::init(diagnose_enabled) {
|
|
Ok(Some(path)) => log::info!("startup args={args:?} log_path={}", path.display()),
|
|
Ok(None) => {}
|
|
Err(err) => eprintln!("diagnostic log could not be opened: {err}"),
|
|
}
|
|
|
|
let app = tauri::Builder::default()
|
|
.setup(|app| {
|
|
let window = WebviewWindowBuilder::new(
|
|
app,
|
|
TEST_WINDOW_LABEL,
|
|
WebviewUrl::App("bubble.html".into()),
|
|
)
|
|
.title("Claude Code Usage Bubble")
|
|
.inner_size(220.0, 66.0)
|
|
.transparent(true)
|
|
.decorations(false)
|
|
.always_on_top(true)
|
|
.skip_taskbar(true)
|
|
.resizable(false)
|
|
.build()?;
|
|
|
|
// The process otherwise outlives its windows (see the exit
|
|
// handler below), so closing the only window must exit
|
|
// explicitly.
|
|
let handle = app.handle().clone();
|
|
window.on_window_event(move |event| {
|
|
if let WindowEvent::CloseRequested { .. } = event {
|
|
handle.exit(0);
|
|
}
|
|
});
|
|
Ok(())
|
|
})
|
|
.build(tauri::generate_context!());
|
|
|
|
let app = match app {
|
|
Ok(app) => app,
|
|
Err(err) => {
|
|
log::error!("tauri builder failed: {err}");
|
|
eprintln!("Claude Code Usage Bubble failed to start: {err}");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
app.run(|_app, event| {
|
|
// Destroying the last window must not end the process; only an
|
|
// explicit `exit` (which carries an exit code) does.
|
|
if let RunEvent::ExitRequested {
|
|
code: None, api, ..
|
|
} = event
|
|
{
|
|
api.prevent_exit();
|
|
}
|
|
});
|
|
}
|