Files
claude-code-usage-bubble/src/lib.rs
T
tiennm99 9e2666ff0e feat: port the data layer to ureq with the Windows system proxy
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.
2026-10-21 12:41:08 +07:00

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();
}
});
}