From 160e08d4a8ebf1664b25aa82f28acdec65f9adb8 Mon Sep 17 00:00:00 2001 From: megaproxy Date: Sat, 9 May 2026 16:03:40 +0100 Subject: [PATCH] Suppress console-window flash on subprocess spawn (CREATE_NO_WINDOW); bump 0.1.2 paths::quiet_command sets CREATE_NO_WINDOW (0x08000000) on Windows so short-lived child processes (wsl.exe -l -q, where claude, wsl.exe -d X bash -lc 'command -v claude') don't briefly allocate a real console window and flash on click. Used by both paths.rs and cli_usage.rs. --- package.json | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/cli_usage.rs | 9 +++------ src-tauri/src/paths.rs | 19 ++++++++++++++++++- src-tauri/tauri.conf.json | 2 +- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 395c7af..fae27cd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "claude-usage-widget", "private": true, - "version": "0.1.1", + "version": "0.1.2", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 350272a..019fe63 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "claude-usage-widget" -version = "0.1.1" +version = "0.1.2" description = "Always-on-top Windows widget visualizing local Claude Code usage" authors = ["megaproxy"] edition = "2021" diff --git a/src-tauri/src/cli_usage.rs b/src-tauri/src/cli_usage.rs index f0d6a7a..26287e6 100644 --- a/src-tauri/src/cli_usage.rs +++ b/src-tauri/src/cli_usage.rs @@ -114,9 +114,8 @@ pub fn autodetect_command() -> Option> { } fn which_exists(name: &str) -> bool { - use std::process::Command; let probe = if cfg!(windows) { "where" } else { "which" }; - Command::new(probe) + crate::paths::quiet_command(probe) .arg(name) .output() .map(|o| o.status.success() && !o.stdout.is_empty()) @@ -124,8 +123,7 @@ fn which_exists(name: &str) -> bool { } fn list_wsl_distros() -> Vec { - use std::process::Command; - let Ok(out) = Command::new("wsl.exe").args(["-l", "-q"]).output() else { + let Ok(out) = crate::paths::quiet_command("wsl.exe").args(["-l", "-q"]).output() else { return Vec::new(); }; if !out.status.success() { @@ -145,8 +143,7 @@ fn list_wsl_distros() -> Vec { } fn probe_claude_in_wsl(distro: &str) -> bool { - use std::process::Command; - Command::new("wsl.exe") + crate::paths::quiet_command("wsl.exe") .args(["-d", distro, "bash", "-lc", "command -v claude"]) .output() .map(|o| o.status.success() && !o.stdout.is_empty()) diff --git a/src-tauri/src/paths.rs b/src-tauri/src/paths.rs index b8d8e6e..dbbea8b 100644 --- a/src-tauri/src/paths.rs +++ b/src-tauri/src/paths.rs @@ -10,6 +10,23 @@ use serde::Serialize; use std::path::{Path, PathBuf}; +/// Construct a `Command` that won't flash a console window on Windows. +/// +/// `Command::new(...).output()` allocates a console for the child process +/// before it exits, which appears as a black flash for short-lived processes +/// like `wsl.exe -l -q` or `where claude`. The CREATE_NO_WINDOW flag (Win32 +/// process-creation flag 0x08000000) suppresses that. +pub fn quiet_command(program: &str) -> std::process::Command { + let mut c = std::process::Command::new(program); + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const CREATE_NO_WINDOW: u32 = 0x08000000; + c.creation_flags(CREATE_NO_WINDOW); + } + c +} + #[derive(Debug, Clone, Serialize)] pub struct ResolvedRoots { pub roots: Vec, @@ -24,7 +41,7 @@ pub fn list_wsl_distros() -> anyhow::Result> { return Ok(Vec::new()); } - let out = match std::process::Command::new("wsl.exe") + let out = match quiet_command("wsl.exe") .args(["-l", "-q"]) .output() { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 667e7a3..68612ba 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Claude Usage Widget", - "version": "0.1.1", + "version": "0.1.2", "identifier": "com.megaproxy.claude-usage-widget", "build": { "beforeDevCommand": "pnpm dev",