From 1a0a675b132695fa9879dfd2b1624faf2c1bf580 Mon Sep 17 00:00:00 2001 From: hailxl Date: Thu, 27 Nov 2025 16:22:03 +0800 Subject: [PATCH] fix(macos): fix can't open file by double click file in Finder and open -a in commandline Changes: - Add decode_file_url() function using percent_encoding crate - Handle file:/// and file://localhost/ URL formats used by macOS - Add percent-encoding = "2.3" to Cargo.toml (explicit dependency) - Update Cargo.lock with percent-encoding dependency - Add error logging for file path validation failures This patch ensures proper decoding of URL-encoded file paths including UTF-8 multi-byte characters (e.g., Chinese filenames) when files are opened via `open -a markdown-viewer FILE.md` command. The percent_encoding crate is already included as a transitive dependency through Tauri, so this doesn't add a new dependency to the final binary. --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/src/lib.rs | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4ba7bbb..ecb13f8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2070,6 +2070,7 @@ version = "1.1.0" dependencies = [ "html-escape", "notify", + "percent-encoding", "pulldown-cmark", "regex", "serde", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9494b3c..43ad473 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -30,4 +30,5 @@ notify = "6.0" tokio = { version = "1.0", features = ["fs"] } html-escape = "0.2" regex = "1.0" +percent-encoding = "2.3" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a585ec5..28f0c35 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,6 +11,7 @@ use syntect::parsing::SyntaxSet; use syntect::highlighting::ThemeSet; use syntect::html::highlighted_html_for_string; use regex; +use percent_encoding::percent_decode_str; // Security constants @@ -129,6 +130,31 @@ fn create_secure_regex(pattern: &str) -> Result { .map_err(|e| format!("Failed to create regex: {}", e)) } +// Decode file:// URL using percent_encoding crate (already included via Tauri dependencies) +// Supports UTF-8 multi-byte characters including Chinese characters +fn decode_file_url(url_str: &str) -> String { + // Step 1: Decode URL-encoded string to UTF-8 using percent_encoding crate + // This automatically handles UTF-8 multi-byte characters (including Chinese) + let decoded_url = percent_decode_str(url_str) + .decode_utf8_lossy() + .to_string(); + + // Step 2: Now handle file:// prefix and format on the decoded string + let mut path = decoded_url.trim_start_matches("file://").to_string(); + + // Handle file:/// or file://localhost/ formats + // macOS typically uses file:///absolute/path format (three slashes) + if path.starts_with("//") { + // file:////path -> /path (remove extra slashes) + path = path.trim_start_matches("//").to_string(); + } else if path.starts_with("localhost/") { + // file://localhost/path -> path + path = path.trim_start_matches("localhost/").to_string(); + } + + path +} + // Security validation functions fn validate_file_path(file_path: &str) -> Result { let path = Path::new(file_path); @@ -746,10 +772,9 @@ pub fn run() { let app_handle = _app_handle; // Find the first markdown file in the opened URLs for url in urls { - // Convert URL to string and handle file:// URLs let url_str = url.as_str(); let file_path = if url_str.starts_with("file://") { - url_str.trim_start_matches("file://").to_string() + decode_file_url(url_str) } else { url_str.to_string() }; @@ -766,6 +791,9 @@ pub fn run() { // Also try to emit the event to the frontend if it's ready let _ = app_handle.emit("file-opened-via-os", &validated_str); + } else { + // Add error log for debugging + eprintln!("Failed to validate file path: {}", file_path); } break; }