build: scaffold the Tauri v2 crate

Turn the package into a lib plus thin bin on Tauri v2 with a root-level
tauri.conf.json, capabilities, generated icons (keeping the hand-tuned
icon.ico) and a placeholder bubble page. The Win32 modules leave the
module tree until each one is ported; their sources stay on disk.

tauri-build now embeds the icon, a VERSIONINFO from the Cargo version and
a PerMonitorV2 manifest, replacing res/icon.rc and its stale 0.1.0.
This commit is contained in:
tiennm99 committed 2026-10-21 11:45:14 +07:00
1 parent bb09765be5
commit f64d81e6a1
18 files changed
+4656 -415

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
//! 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 diag;
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();
}
});
}
+4 -67
View File
@@ -1,70 +1,7 @@
#![windows_subsystem = "windows"]
// Several modules (creds, usage, update, os::dpi, …) expose API that the
// app surface doesn't fully call yet — they're scaffolding for in-progress
// port phases. Allow at the crate root rather than scattering attributes.
#![allow(dead_code)]
// Original infrastructure.
mod creds;
mod diag;
mod i18n;
mod net;
mod os;
mod tray;
mod update;
mod usage;
mod usage_color;
// Application surface.
mod app;
mod bubble;
mod panel;
mod settings;
// Release builds use the GUI subsystem so no console window opens with the
// app. Debug builds keep the console for log output.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
let args: Vec<String> = std::env::args().collect();
let diagnose_enabled = args.iter().any(|a| a == "--diagnose");
if diagnose_enabled {
if let Ok(Some(path)) = diag::init(true) {
log::info!("startup args={args:?} log_path={}", path.display());
}
}
if let Some(exit_code) = update::run_cli(&args) {
if diagnose_enabled {
log::info!("cli mode exited with code {exit_code}");
}
std::process::exit(exit_code);
}
let wait_pid = args
.iter()
.position(|a| a == "--wait-pid")
.and_then(|i| args.get(i + 1))
.and_then(|s| s.parse::<u32>().ok());
if let Some(pid) = wait_pid {
if diagnose_enabled {
log::info!("waiting up to 5s for parent pid {pid} to exit");
}
update::handoff::wait_for_parent_exit(pid, 5_000);
}
let updated_to = args
.iter()
.position(|a| a == "--updated-to")
.and_then(|i| args.get(i + 1))
.cloned();
if diagnose_enabled {
log::info!("entering app::run (wait_pid={wait_pid:?} updated_to={updated_to:?})");
}
app::run(AppArgs {
wait_pid_present: wait_pid.is_some(),
updated_to,
});
}
pub struct AppArgs {
pub wait_pid_present: bool,
pub updated_to: Option<String>,
claude_code_usage_bubble_lib::run();
}