generated from nhcarrigan/template
fix: suppress terminal window flash on Windows for all subprocesses (#165)
This commit is contained in:
@@ -7,6 +7,7 @@ use tauri_plugin_store::StoreExt;
|
|||||||
use crate::achievements::{get_achievement_info, load_achievements, AchievementUnlockedEvent};
|
use crate::achievements::{get_achievement_info, load_achievements, AchievementUnlockedEvent};
|
||||||
use crate::bridge_manager::SharedBridgeManager;
|
use crate::bridge_manager::SharedBridgeManager;
|
||||||
use crate::config::{ClaudeStartOptions, HikariConfig};
|
use crate::config::{ClaudeStartOptions, HikariConfig};
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
use crate::stats::UsageStats;
|
use crate::stats::UsageStats;
|
||||||
use crate::temp_manager::SharedTempFileManager;
|
use crate::temp_manager::SharedTempFileManager;
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ fn create_claude_command() -> std::process::Command {
|
|||||||
// Non-login shells launched by `wsl` don't inherit the full user PATH,
|
// Non-login shells launched by `wsl` don't inherit the full user PATH,
|
||||||
// so we need to use a login shell to get the correct PATH
|
// so we need to use a login shell to get the correct PATH
|
||||||
let which_output = std::process::Command::new("wsl")
|
let which_output = std::process::Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "bash", "-l", "-c", "which claude"])
|
.args(["-e", "bash", "-l", "-c", "which claude"])
|
||||||
.output();
|
.output();
|
||||||
|
|
||||||
@@ -66,6 +68,7 @@ fn create_claude_command() -> std::process::Command {
|
|||||||
Ok(output) if output.status.success() => {
|
Ok(output) if output.status.success() => {
|
||||||
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
let mut cmd = std::process::Command::new("wsl");
|
let mut cmd = std::process::Command::new("wsl");
|
||||||
|
cmd.hide_window();
|
||||||
cmd.arg(claude_path);
|
cmd.arg(claude_path);
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
@@ -73,6 +76,7 @@ fn create_claude_command() -> std::process::Command {
|
|||||||
// Fallback to just "claude" if which fails
|
// Fallback to just "claude" if which fails
|
||||||
// This maintains backwards compatibility
|
// This maintains backwards compatibility
|
||||||
let mut cmd = std::process::Command::new("wsl");
|
let mut cmd = std::process::Command::new("wsl");
|
||||||
|
cmd.hide_window();
|
||||||
cmd.arg("claude");
|
cmd.arg("claude");
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
@@ -85,18 +89,23 @@ fn create_claude_command() -> std::process::Command {
|
|||||||
// This works regardless of how Claude Code was installed (standalone, npm, etc.)
|
// This works regardless of how Claude Code was installed (standalone, npm, etc.)
|
||||||
// and avoids hardcoding paths
|
// and avoids hardcoding paths
|
||||||
let which_output = std::process::Command::new("which")
|
let which_output = std::process::Command::new("which")
|
||||||
|
.hide_window()
|
||||||
.arg("claude")
|
.arg("claude")
|
||||||
.output();
|
.output();
|
||||||
|
|
||||||
match which_output {
|
match which_output {
|
||||||
Ok(output) if output.status.success() => {
|
Ok(output) if output.status.success() => {
|
||||||
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let claude_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
std::process::Command::new(claude_path)
|
let mut cmd = std::process::Command::new(claude_path);
|
||||||
|
cmd.hide_window();
|
||||||
|
cmd
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Fallback to just "claude" if which fails
|
// Fallback to just "claude" if which fails
|
||||||
// This maintains backwards compatibility
|
// This maintains backwards compatibility
|
||||||
std::process::Command::new("claude")
|
let mut cmd = std::process::Command::new("claude");
|
||||||
|
cmd.hide_window();
|
||||||
|
cmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,6 +421,7 @@ async fn list_workspace_commands(working_dir: &str, use_wsl: bool) -> Vec<String
|
|||||||
commands_dir
|
commands_dir
|
||||||
);
|
);
|
||||||
let Ok(output) = std::process::Command::new("wsl")
|
let Ok(output) = std::process::Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "sh", "-c", &script])
|
.args(["-e", "sh", "-c", &script])
|
||||||
.output()
|
.output()
|
||||||
else {
|
else {
|
||||||
@@ -495,6 +505,7 @@ async fn list_skills_via_wsl() -> Result<Vec<String>, String> {
|
|||||||
|
|
||||||
// Use WSL to list directories in ~/.claude/skills that contain SKILL.md
|
// Use WSL to list directories in ~/.claude/skills that contain SKILL.md
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args([
|
.args([
|
||||||
"-e",
|
"-e",
|
||||||
"sh",
|
"sh",
|
||||||
@@ -794,6 +805,7 @@ async fn list_directory_via_wsl(path: &str) -> Result<Vec<FileEntry>, String> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "sh", "-c", &script])
|
.args(["-e", "sh", "-c", &script])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -856,6 +868,7 @@ async fn read_file_via_wsl(path: &str) -> Result<String, String> {
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "cat", path])
|
.args(["-e", "cat", path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -887,6 +900,7 @@ async fn write_file_via_wsl(path: &str, content: &str) -> Result<(), String> {
|
|||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
let mut child = Command::new("wsl")
|
let mut child = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "sh", "-c", &format!("cat > '{}'", path)])
|
.args(["-e", "sh", "-c", &format!("cat > '{}'", path)])
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
@@ -935,6 +949,7 @@ async fn create_file_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if file exists first
|
// Check if file exists first
|
||||||
let check = Command::new("wsl")
|
let check = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", path])
|
.args(["-e", "test", "-e", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -944,6 +959,7 @@ async fn create_file_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "touch", path])
|
.args(["-e", "touch", path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -984,6 +1000,7 @@ async fn create_directory_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if directory exists first
|
// Check if directory exists first
|
||||||
let check = Command::new("wsl")
|
let check = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", path])
|
.args(["-e", "test", "-e", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -993,6 +1010,7 @@ async fn create_directory_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "mkdir", "-p", path])
|
.args(["-e", "mkdir", "-p", path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1037,6 +1055,7 @@ async fn delete_file_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if path exists
|
// Check if path exists
|
||||||
let check_exists = Command::new("wsl")
|
let check_exists = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", path])
|
.args(["-e", "test", "-e", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1047,6 +1066,7 @@ async fn delete_file_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if path is a directory
|
// Check if path is a directory
|
||||||
let check_dir = Command::new("wsl")
|
let check_dir = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-d", path])
|
.args(["-e", "test", "-d", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1056,6 +1076,7 @@ async fn delete_file_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "rm", path])
|
.args(["-e", "rm", path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1100,6 +1121,7 @@ async fn delete_directory_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if path exists
|
// Check if path exists
|
||||||
let check_exists = Command::new("wsl")
|
let check_exists = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", path])
|
.args(["-e", "test", "-e", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1110,6 +1132,7 @@ async fn delete_directory_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Check if path is a directory
|
// Check if path is a directory
|
||||||
let check_dir = Command::new("wsl")
|
let check_dir = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-d", path])
|
.args(["-e", "test", "-d", path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1119,6 +1142,7 @@ async fn delete_directory_via_wsl(path: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "rm", "-rf", path])
|
.args(["-e", "rm", "-rf", path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1164,6 +1188,7 @@ async fn rename_path_via_wsl(old_path: &str, new_path: &str) -> Result<(), Strin
|
|||||||
|
|
||||||
// Check if old path exists
|
// Check if old path exists
|
||||||
let check_old = Command::new("wsl")
|
let check_old = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", old_path])
|
.args(["-e", "test", "-e", old_path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1174,6 +1199,7 @@ async fn rename_path_via_wsl(old_path: &str, new_path: &str) -> Result<(), Strin
|
|||||||
|
|
||||||
// Check if new path already exists
|
// Check if new path already exists
|
||||||
let check_new = Command::new("wsl")
|
let check_new = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-e", new_path])
|
.args(["-e", "test", "-e", new_path])
|
||||||
.status()
|
.status()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1183,6 +1209,7 @@ async fn rename_path_via_wsl(old_path: &str, new_path: &str) -> Result<(), Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "mv", old_path, new_path])
|
.args(["-e", "mv", old_path, new_path])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
@@ -1360,6 +1387,7 @@ async fn list_memory_files_via_wsl() -> Result<MemoryFilesResponse, String> {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let output = Command::new("wsl")
|
let output = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "bash", "-l", "-c", script])
|
.args(["-e", "bash", "-l", "-c", script])
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
.map_err(|e| format!("Failed to execute WSL command: {}", e))?;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GitStatus {
|
pub struct GitStatus {
|
||||||
pub is_repo: bool,
|
pub is_repo: bool,
|
||||||
@@ -37,6 +39,7 @@ pub struct GitLogEntry {
|
|||||||
|
|
||||||
fn run_git_command(working_dir: &str, args: &[&str]) -> Result<String, String> {
|
fn run_git_command(working_dir: &str, args: &[&str]) -> Result<String, String> {
|
||||||
let output = Command::new("git")
|
let output = Command::new("git")
|
||||||
|
.hide_window()
|
||||||
.args(args)
|
.args(args)
|
||||||
.current_dir(working_dir)
|
.current_dir(working_dir)
|
||||||
.output()
|
.output()
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ mod debug_logger;
|
|||||||
mod discord_rpc;
|
mod discord_rpc;
|
||||||
mod git;
|
mod git;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
mod process_ext;
|
||||||
mod quick_actions;
|
mod quick_actions;
|
||||||
mod sessions;
|
mod sessions;
|
||||||
mod snippets;
|
mod snippets;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tauri::command;
|
use tauri::command;
|
||||||
|
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
|
|
||||||
/// Generate PowerShell script for Windows Toast Notification
|
/// Generate PowerShell script for Windows Toast Notification
|
||||||
fn generate_powershell_toast_script(title: &str, body: &str) -> String {
|
fn generate_powershell_toast_script(title: &str, body: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
@@ -82,6 +84,7 @@ fn build_simple_notification_command(title: &str, body: &str) -> (String, Vec<St
|
|||||||
pub async fn send_notify_send(title: String, body: String) -> Result<(), String> {
|
pub async fn send_notify_send(title: String, body: String) -> Result<(), String> {
|
||||||
// Use notify-send for Linux/WSL
|
// Use notify-send for Linux/WSL
|
||||||
let output = Command::new("notify-send")
|
let output = Command::new("notify-send")
|
||||||
|
.hide_window()
|
||||||
.arg(&title)
|
.arg(&title)
|
||||||
.arg(&body)
|
.arg(&body)
|
||||||
.arg("--urgency=normal")
|
.arg("--urgency=normal")
|
||||||
@@ -109,6 +112,7 @@ pub async fn send_windows_notification(title: String, body: String) -> Result<()
|
|||||||
|
|
||||||
// Try PowerShell Core first (pwsh), then fall back to Windows PowerShell
|
// Try PowerShell Core first (pwsh), then fall back to Windows PowerShell
|
||||||
let output = Command::new("pwsh.exe")
|
let output = Command::new("pwsh.exe")
|
||||||
|
.hide_window()
|
||||||
.arg("-NoProfile")
|
.arg("-NoProfile")
|
||||||
.arg("-WindowStyle")
|
.arg("-WindowStyle")
|
||||||
.arg("Hidden")
|
.arg("Hidden")
|
||||||
@@ -117,6 +121,7 @@ pub async fn send_windows_notification(title: String, body: String) -> Result<()
|
|||||||
.output()
|
.output()
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
Command::new("powershell.exe")
|
Command::new("powershell.exe")
|
||||||
|
.hide_window()
|
||||||
.arg("-NoProfile")
|
.arg("-NoProfile")
|
||||||
.arg("-WindowStyle")
|
.arg("-WindowStyle")
|
||||||
.arg("Hidden")
|
.arg("Hidden")
|
||||||
@@ -140,6 +145,7 @@ pub async fn send_simple_notification(title: String, body: String) -> Result<(),
|
|||||||
let message = format_simple_notification(&title, &body);
|
let message = format_simple_notification(&title, &body);
|
||||||
|
|
||||||
Command::new("cmd.exe")
|
Command::new("cmd.exe")
|
||||||
|
.hide_window()
|
||||||
.arg("/c")
|
.arg("/c")
|
||||||
.arg("msg")
|
.arg("msg")
|
||||||
.arg("*")
|
.arg("*")
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
/// Extension trait for `Command` that hides the console window on Windows.
|
||||||
|
///
|
||||||
|
/// On non-Windows platforms this is a no-op, so callers can unconditionally
|
||||||
|
/// chain `.hide_window()` without any `#[cfg]` guards at the call sites.
|
||||||
|
pub trait HideWindow {
|
||||||
|
fn hide_window(&mut self) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HideWindow for Command {
|
||||||
|
fn hide_window(&mut self) -> &mut Self {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
self.creation_flags(CREATE_NO_WINDOW);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ use std::process::Command;
|
|||||||
use tauri::command;
|
use tauri::command;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn send_vbs_notification(title: String, body: String) -> Result<(), String> {
|
pub async fn send_vbs_notification(title: String, body: String) -> Result<(), String> {
|
||||||
// Create a VBScript that shows a Windows notification
|
// Create a VBScript that shows a Windows notification
|
||||||
@@ -40,7 +42,7 @@ objShell.Popup "{}" & vbCrLf & vbCrLf & "{}", 5, "{}", 64
|
|||||||
} else if temp_path.starts_with("/tmp/") {
|
} else if temp_path.starts_with("/tmp/") {
|
||||||
// WSL temp files might be in a different location
|
// WSL temp files might be in a different location
|
||||||
// Try to use wslpath to convert
|
// Try to use wslpath to convert
|
||||||
let output = Command::new("wslpath").arg("-w").arg(&temp_path).output();
|
let output = Command::new("wslpath").hide_window().arg("-w").arg(&temp_path).output();
|
||||||
|
|
||||||
if let Ok(result) = output {
|
if let Ok(result) = output {
|
||||||
if result.status.success() {
|
if result.status.success() {
|
||||||
@@ -57,6 +59,7 @@ objShell.Popup "{}" & vbCrLf & vbCrLf & "{}", 5, "{}", 64
|
|||||||
|
|
||||||
// Execute the VBScript using wscript.exe
|
// Execute the VBScript using wscript.exe
|
||||||
let output = Command::new("/mnt/c/Windows/System32/wscript.exe")
|
let output = Command::new("/mnt/c/Windows/System32/wscript.exe")
|
||||||
|
.hide_window()
|
||||||
.arg("//NoLogo")
|
.arg("//NoLogo")
|
||||||
.arg(&windows_path)
|
.arg(&windows_path)
|
||||||
.output()
|
.output()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::os::windows::process::CommandExt;
|
|||||||
use crate::achievements::{get_achievement_info, AchievementUnlockedEvent};
|
use crate::achievements::{get_achievement_info, AchievementUnlockedEvent};
|
||||||
use crate::commands::record_cost;
|
use crate::commands::record_cost;
|
||||||
use crate::config::ClaudeStartOptions;
|
use crate::config::ClaudeStartOptions;
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
use crate::stats::{calculate_cost, StatsUpdateEvent, UsageStats};
|
use crate::stats::{calculate_cost, StatsUpdateEvent, UsageStats};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
AgentEndEvent, AgentStartEvent, CharacterState, ClaudeMessage, ConnectionEvent,
|
AgentEndEvent, AgentStartEvent, CharacterState, ClaudeMessage, ConnectionEvent,
|
||||||
@@ -89,7 +90,7 @@ fn find_claude_binary() -> Option<String> {
|
|||||||
|
|
||||||
// Use a login shell to resolve claude via the user's PATH - GUI apps don't
|
// Use a login shell to resolve claude via the user's PATH - GUI apps don't
|
||||||
// inherit shell PATH, so bare `which` may miss ~/.local/bin entries
|
// inherit shell PATH, so bare `which` may miss ~/.local/bin entries
|
||||||
if let Ok(output) = Command::new("bash").args(["-lc", "which claude"]).output() {
|
if let Ok(output) = Command::new("bash").hide_window().args(["-lc", "which claude"]).output() {
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
@@ -224,6 +225,7 @@ impl WslBridge {
|
|||||||
tracing::debug!("Working dir: {}", working_dir);
|
tracing::debug!("Working dir: {}", working_dir);
|
||||||
|
|
||||||
let mut cmd = Command::new(&claude_path);
|
let mut cmd = Command::new(&claude_path);
|
||||||
|
cmd.hide_window();
|
||||||
cmd.args([
|
cmd.args([
|
||||||
"--output-format",
|
"--output-format",
|
||||||
"stream-json",
|
"stream-json",
|
||||||
@@ -291,6 +293,7 @@ impl WslBridge {
|
|||||||
|
|
||||||
// Check if Claude binary is installed inside WSL
|
// Check if Claude binary is installed inside WSL
|
||||||
let binary_check = Command::new("wsl")
|
let binary_check = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "bash", "-lc", "which claude"])
|
.args(["-e", "bash", "-lc", "which claude"])
|
||||||
.output();
|
.output();
|
||||||
if let Ok(output) = binary_check {
|
if let Ok(output) = binary_check {
|
||||||
@@ -301,6 +304,7 @@ impl WslBridge {
|
|||||||
|
|
||||||
// Validate the working directory exists inside WSL before spawning
|
// Validate the working directory exists inside WSL before spawning
|
||||||
let dir_check = Command::new("wsl")
|
let dir_check = Command::new("wsl")
|
||||||
|
.hide_window()
|
||||||
.args(["-e", "test", "-d", working_dir])
|
.args(["-e", "test", "-d", working_dir])
|
||||||
.output();
|
.output();
|
||||||
if let Ok(output) = dir_check {
|
if let Ok(output) = dir_check {
|
||||||
@@ -375,8 +379,7 @@ impl WslBridge {
|
|||||||
cmd.args(["-e", "bash", "-lc", &claude_cmd]);
|
cmd.args(["-e", "bash", "-lc", &claude_cmd]);
|
||||||
|
|
||||||
// Hide the console window on Windows
|
// Hide the console window on Windows
|
||||||
#[cfg(target_os = "windows")]
|
cmd.hide_window();
|
||||||
cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
|
|
||||||
|
|
||||||
cmd
|
cmd
|
||||||
};
|
};
|
||||||
@@ -2059,7 +2062,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_stale_process_detection_with_try_wait() {
|
fn test_stale_process_detection_with_try_wait() {
|
||||||
// Spawn a real process that exits immediately so we can verify try_wait detects it
|
// Spawn a real process that exits immediately so we can verify try_wait detects it
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
let mut child = Command::new("true").hide_window().spawn().expect("Failed to spawn 'true'");
|
||||||
|
|
||||||
// Wait for it to exit
|
// Wait for it to exit
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
@@ -2078,7 +2081,7 @@ mod tests {
|
|||||||
fn test_stale_process_is_some_after_exit() {
|
fn test_stale_process_is_some_after_exit() {
|
||||||
// Verify the logic used in start(): a process that has exited is detected
|
// Verify the logic used in start(): a process that has exited is detected
|
||||||
// and the handle is cleaned up so start() can proceed
|
// and the handle is cleaned up so start() can proceed
|
||||||
let mut child = Command::new("true").spawn().expect("Failed to spawn 'true'");
|
let mut child = Command::new("true").hide_window().spawn().expect("Failed to spawn 'true'");
|
||||||
|
|
||||||
// Let it exit
|
// Let it exit
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tauri::command;
|
use tauri::command;
|
||||||
|
|
||||||
|
use crate::process_ext::HideWindow;
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
pub async fn send_wsl_notification(title: String, body: String) -> Result<(), String> {
|
pub async fn send_wsl_notification(title: String, body: String) -> Result<(), String> {
|
||||||
// Method 1: Try Windows 10/11 toast notification using PowerShell
|
// Method 1: Try Windows 10/11 toast notification using PowerShell
|
||||||
@@ -36,6 +38,7 @@ $notifier.Show($toast)
|
|||||||
|
|
||||||
// Try PowerShell.exe through WSL
|
// Try PowerShell.exe through WSL
|
||||||
let output = Command::new("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe")
|
let output = Command::new("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe")
|
||||||
|
.hide_window()
|
||||||
.arg("-NoProfile")
|
.arg("-NoProfile")
|
||||||
.arg("-ExecutionPolicy")
|
.arg("-ExecutionPolicy")
|
||||||
.arg("Bypass")
|
.arg("Bypass")
|
||||||
@@ -65,6 +68,7 @@ $notifier.Show($toast)
|
|||||||
|
|
||||||
// Method 3: Try wsl-notify-send if available
|
// Method 3: Try wsl-notify-send if available
|
||||||
let notify_result = Command::new("wsl-notify-send")
|
let notify_result = Command::new("wsl-notify-send")
|
||||||
|
.hide_window()
|
||||||
.arg("--appId")
|
.arg("--appId")
|
||||||
.arg("HikariDesktop")
|
.arg("HikariDesktop")
|
||||||
.arg("--category")
|
.arg("--category")
|
||||||
|
|||||||
Reference in New Issue
Block a user