diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.9.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.9.1.md index 4153c332..9c1ba548 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.9.1.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.9.1.md @@ -103,6 +103,7 @@ - Fixed AI Studio following your system into light or dark mode even though you had chosen a fixed color theme in the app settings. - Fixed AI Studio keeping its previous color theme after your computer woke up from sleep, when your system had switched between light and dark mode during that time. - Fixed instructions slipping past the protection against prompt injection when invisible characters were hidden inside their words. Removing those characters used to put such an instruction back together unnoticed. +- Fixed Windows Explorer opening your Documents folder instead of showing the file you wanted to see, such as the startup log on the information page or in the Log Viewer assistant. This happened with every file whose path contains a space. - Upgraded the Visual Briefing assistant (in preview) from the prototype to the beta state. The assistant is now completely implemented and is undergoing a deeper testing phase in preparation for release. To try it, open the app settings, allow preview features down to beta, and then enable the Visual Briefing assistant there. - Upgraded the vector database behind local RAG (Qdrant Edge) to version 0.8.0. - Upgraded to .NET v9.0.20 diff --git a/runtime/src/file_actions.rs b/runtime/src/file_actions.rs index 5bc03c87..1fbc24e3 100644 --- a/runtime/src/file_actions.rs +++ b/runtime/src/file_actions.rs @@ -18,6 +18,9 @@ use ashpd::desktop::open_uri::{OpenDirectoryRequest, OpenFileRequest}; #[cfg(windows)] use std::os::windows::process::CommandExt; +#[cfg(any(windows, test))] +use std::ffi::OsString; + /// Microsoft documents CREATE_NO_WINDOW as a process creation flag with value 0x08000000. #[cfg(windows)] const CREATE_NO_WINDOW: u32 = 0x08000000; @@ -972,7 +975,12 @@ async fn open_path_in_linux_file_manager(target: &FileManagerTarget) -> Result<( fn create_file_manager_command(target: &FileManagerTarget) -> Command { let mut command = Command::new("explorer.exe"); if target.reveal_file { - command.arg(format!("/select,{}", target.path.to_string_lossy())); + // + // Explorer reads its command line on its own. Passed as a regular argument, a path with + // a space would be quoted as a whole, "/select,C:\My Folder\file.txt", which Explorer + // does not understand: it opens the Documents folder instead. + // + command.raw_arg(explorer_select_argument(&target.path)); } else { command.arg(&target.path); } @@ -980,6 +988,18 @@ fn create_file_manager_command(target: &FileManagerTarget) -> Command { command } +/// Builds the argument which makes Explorer show the given file selected in its folder. +/// +/// Only the path is quoted, as in /select,"C:\My Folder\file.txt". Windows allows no quotation +/// mark inside a file or folder name, so the path itself never needs escaping. +#[cfg(any(windows, test))] +fn explorer_select_argument(path: &Path) -> OsString { + let mut argument = OsString::from("/select,\""); + argument.push(path.as_os_str()); + argument.push("\""); + argument +} + #[cfg(target_os = "macos")] fn create_file_manager_command(target: &FileManagerTarget) -> Command { let mut command = Command::new("open"); @@ -1043,6 +1063,13 @@ mod tests { assert_eq!(xdg_open_fallback_path(&target), temp_dir.path()); } + #[test] + fn explorer_select_argument_quotes_only_the_path() { + let argument = explorer_select_argument(Path::new(r"C:\Users\thorsten\AI Studio Events.log")); + + assert_eq!(argument, OsString::from(r#"/select,"C:\Users\thorsten\AI Studio Events.log""#)); + } + #[test] fn existing_directory_is_opened_directly() { let temp_dir = tempfile::tempdir().unwrap();