diff --git a/src-tauri/src/command_utils.rs b/src-tauri/src/command_utils.rs new file mode 100644 index 0000000..f7edcb2 --- /dev/null +++ b/src-tauri/src/command_utils.rs @@ -0,0 +1,206 @@ +/** + * Command utilities for hiding console windows in production builds + * + * This module provides utilities to configure std::process::Command instances + * to hide console windows on Windows in production builds, preventing the + * flash of command prompt windows when executing external processes. + */ + +use std::process::Command; + +// Windows-specific imports for hiding console window +#[cfg(target_os = "windows")] +use std::os::windows::process::CommandExt; + +// Windows process creation flags +#[cfg(target_os = "windows")] +const CREATE_NO_WINDOW: u32 = 0x08000000; + +/// Configure a Command to hide console window in production builds on Windows +/// +/// This function applies the CREATE_NO_WINDOW flag to prevent console window +/// flashing when spawning processes from a GUI application. The console window +/// is only hidden in release builds to preserve debugging capabilities during +/// development. +/// +/// # Arguments +/// * `cmd` - Mutable reference to the Command to configure +/// +/// # Example +/// ```rust +/// use std::process::Command; +/// use crate::command_utils::configure_no_window; +/// +/// let mut cmd = Command::new("python"); +/// configure_no_window(&mut cmd); +/// let output = cmd.output(); +/// ``` +pub fn configure_no_window(cmd: &mut Command) { + #[cfg(target_os = "windows")] + { + // Only hide console window in release builds + // In debug builds, keep console visible for debugging + #[cfg(not(debug_assertions))] + { + cmd.creation_flags(CREATE_NO_WINDOW); + println!("Console window hidden for release build"); + } + + #[cfg(debug_assertions)] + { + println!("Console window visible for debug build"); + } + } + + // On non-Windows platforms, this function does nothing + #[cfg(not(target_os = "windows"))] + { + // No-op on non-Windows platforms + let _ = cmd; // Suppress unused variable warning + } +} + +/// Create a new Command with console window hiding configured +/// +/// This is a convenience function that creates a new Command and immediately +/// configures it to hide console windows in production builds. +/// +/// # Arguments +/// * `program` - The program to execute +/// +/// # Returns +/// A configured Command instance +/// +/// # Example +/// ```rust +/// use crate::command_utils::create_hidden_command; +/// +/// let mut cmd = create_hidden_command("python"); +/// cmd.args(&["-c", "print('Hello, World!')"]); +/// let output = cmd.output(); +/// ``` +pub fn create_hidden_command>(program: S) -> Command { + let mut cmd = Command::new(program); + configure_no_window(&mut cmd); + cmd +} + +/// Configure a Command for Python execution with console window hiding +/// +/// This function configures a Command specifically for Python execution, +/// including proper encoding setup and console window hiding. +/// +/// # Arguments +/// * `cmd` - Mutable reference to the Command to configure +/// * `project_root` - The working directory for the command +/// * `args` - Command line arguments +/// +/// # Example +/// ```rust +/// use std::process::Command; +/// use std::path::Path; +/// use crate::command_utils::configure_python_command; +/// +/// let mut cmd = Command::new("python"); +/// let args = vec!["-c".to_string(), "print('Hello')".to_string()]; +/// configure_python_command(&mut cmd, Path::new("."), &args); +/// ``` +pub fn configure_python_command(cmd: &mut Command, project_root: &std::path::Path, args: &[String]) { + cmd.current_dir(project_root); + cmd.args(args); + cmd.stdout(std::process::Stdio::piped()); + cmd.stderr(std::process::Stdio::piped()); + + // Set environment variables for consistent encoding + cmd.env("PYTHONIOENCODING", "utf-8"); + cmd.env("PYTHONUNBUFFERED", "1"); + + // Platform-specific configurations + if cfg!(target_os = "windows") { + cmd.env("PYTHONUTF8", "1"); + } + + // Configure console window hiding + configure_no_window(cmd); +} + +/// Configure a Command for system operations (file/folder opening) +/// +/// This function configures a Command for system operations like opening +/// files or folders, with appropriate console window hiding. +/// +/// # Arguments +/// * `cmd` - Mutable reference to the Command to configure +/// +/// # Example +/// ```rust +/// use std::process::Command; +/// use crate::command_utils::configure_system_command; +/// +/// let mut cmd = Command::new("explorer"); +/// cmd.arg("C:\\"); +/// configure_system_command(&mut cmd); +/// ``` +pub fn configure_system_command(cmd: &mut Command) { + // Configure console window hiding for system commands + configure_no_window(cmd); +} + +#[cfg(test)] +mod tests { + use super::*; + use std::process::Command; + + #[test] + fn test_configure_no_window() { + let mut cmd = Command::new("echo"); + configure_no_window(&mut cmd); + // Test should not panic + } + + #[test] + fn test_create_hidden_command() { + let cmd = create_hidden_command("echo"); + // Test should not panic and should return a Command + assert_eq!(cmd.get_program(), "echo"); + } + + #[test] + fn test_configure_python_command() { + let mut cmd = Command::new("python"); + let args = vec!["-c".to_string(), "print('test')".to_string()]; + configure_python_command(&mut cmd, std::path::Path::new("."), &args); + // Test should not panic + } + + #[test] + fn test_configure_system_command() { + let mut cmd = Command::new("echo"); + configure_system_command(&mut cmd); + // Test should not panic + } +} + +/// Utility macro for creating a hidden command with arguments +/// +/// This macro provides a convenient way to create and configure a Command +/// with console window hiding in a single expression. +/// +/// # Example +/// ```rust +/// use crate::command_utils::hidden_command; +/// +/// let output = hidden_command!("python", "-c", "print('Hello')").output(); +/// ``` +#[macro_export] +macro_rules! hidden_command { + ($program:expr $(, $arg:expr)*) => { + { + let mut cmd = $crate::command_utils::create_hidden_command($program); + $(cmd.arg($arg);)* + cmd + } + }; +} + +// Note: The hidden_command macro is automatically available when this module is imported diff --git a/src-tauri/src/commands/ai_video.rs b/src-tauri/src/commands/ai_video.rs index 1ed3e56..934159f 100644 --- a/src-tauri/src/commands/ai_video.rs +++ b/src-tauri/src/commands/ai_video.rs @@ -1,14 +1,7 @@ use serde::Deserialize; use std::process::Command; use crate::python_executor::execute_python_command; - -// Windows-specific imports for hiding console window -#[cfg(target_os = "windows")] -use std::os::windows::process::CommandExt; - -// Windows process creation flags -#[cfg(target_os = "windows")] -const CREATE_NO_WINDOW: u32 = 0x08000000; +use crate::command_utils::configure_no_window; #[derive(Debug, Deserialize)] pub struct AIVideoRequest { @@ -121,14 +114,8 @@ pub async fn test_ai_video_environment(_app: tauri::AppHandle) -> Result Result { use tauri_plugin_dialog::{DialogExt}; @@ -52,10 +54,14 @@ pub async fn open_folder(folder_path: String) -> Result { #[cfg(target_os = "windows")] { use std::process::Command; - let result = Command::new("explorer") - .arg(&folder_path) - .spawn(); - + let mut cmd = Command::new("explorer"); + cmd.arg(&folder_path); + + // Configure console window hiding + configure_system_command(&mut cmd); + + let result = cmd.spawn(); + match result { Ok(_) => Ok(format!("Opened folder: {}", folder_path)), Err(e) => Err(format!("Failed to open folder: {}", e)) diff --git a/src-tauri/src/commands/project.rs b/src-tauri/src/commands/project.rs index 90b7f05..f67a3a8 100644 --- a/src-tauri/src/commands/project.rs +++ b/src-tauri/src/commands/project.rs @@ -1,14 +1,7 @@ use serde::{Deserialize, Serialize}; use tauri::{command, AppHandle}; use crate::python_executor::execute_python_command; - -// Windows-specific imports for hiding console window -#[cfg(target_os = "windows")] -use std::os::windows::process::CommandExt; - -// Windows process creation flags -#[cfg(target_os = "windows")] -const CREATE_NO_WINDOW: u32 = 0x08000000; +use crate::command_utils::configure_system_command; #[derive(Debug, Serialize, Deserialize)] pub struct ProjectInfo { @@ -82,11 +75,8 @@ pub async fn open_file_in_system(file_path: String) -> Result<(), String> { let mut cmd = std::process::Command::new("cmd"); cmd.args(["/C", "start", "", &file_path]); - // Hide console window in release builds - #[cfg(not(debug_assertions))] - { - cmd.creation_flags(CREATE_NO_WINDOW); - } + // Configure console window hiding + configure_system_command(&mut cmd); cmd.spawn() .map_err(|e| format!("Failed to open file: {}", e))?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0989946..339d688 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ mod commands; mod python_executor; mod macros; +mod command_utils; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { diff --git a/src-tauri/src/python_executor.rs b/src-tauri/src/python_executor.rs index 4c90ea9..b10662a 100644 --- a/src-tauri/src/python_executor.rs +++ b/src-tauri/src/python_executor.rs @@ -6,46 +6,7 @@ use std::sync::{Arc, Mutex}; use std::sync::mpsc; use serde::{Deserialize, Serialize}; use tauri::{AppHandle, Emitter}; - -// Windows-specific imports for hiding console window -#[cfg(target_os = "windows")] -use std::os::windows::process::CommandExt; - -// Windows process creation flags -#[cfg(target_os = "windows")] -const CREATE_NO_WINDOW: u32 = 0x08000000; - -/// Configure a Command for Python execution with proper console window handling -fn configure_python_command(cmd: &mut Command, project_root: &std::path::Path, args: &[String]) { - cmd.current_dir(project_root); - cmd.args(args); - cmd.stdout(std::process::Stdio::piped()); - cmd.stderr(std::process::Stdio::piped()); - - // Set environment variables for consistent encoding - cmd.env("PYTHONIOENCODING", "utf-8"); - cmd.env("PYTHONUNBUFFERED", "1"); - - // Platform-specific configurations - if cfg!(target_os = "windows") { - cmd.env("PYTHONUTF8", "1"); - - // Hide console window in release builds to prevent flashing - // In debug builds, keep console visible for debugging - #[cfg(target_os = "windows")] - { - #[cfg(not(debug_assertions))] - { - cmd.creation_flags(CREATE_NO_WINDOW); - println!("Console window hidden for release build"); - } - #[cfg(debug_assertions)] - { - println!("Console window visible for debug build"); - } - } - } -} +use crate::command_utils::configure_python_command; /// Generic progress information from Python processes #[derive(Debug, Serialize, Deserialize, Clone)]