generated from nhcarrigan/template
feat: convert WSL Linux paths to Windows UNC paths when opening binary files
Adds open_binary_file Tauri command that translates WSL Linux-style paths (e.g. /tmp/mcp_output_abc123.pdf) to Windows UNC paths via wslpath -w before opening, so binary file links work correctly on Windows/WSL. Non-Windows platforms pass the path through unchanged. Markdown.svelte now invokes this command instead of calling openPath directly.
This commit is contained in:
@@ -2578,6 +2578,32 @@ pub async fn scan_project(working_dir: String) -> Result<ProjectScan, String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn open_binary_file(app: AppHandle, path: String) -> Result<(), String> {
|
||||||
|
use tauri_plugin_opener::OpenerExt;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
// Convert the WSL Linux path (e.g. /tmp/file.pdf) to a Windows UNC path
|
||||||
|
// (e.g. \\wsl.localhost\Ubuntu\tmp\file.pdf) so the Windows shell can open it.
|
||||||
|
let output = std::process::Command::new("wsl")
|
||||||
|
.args(["wslpath", "-w", &path])
|
||||||
|
.output()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let windows_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
|
app.opener()
|
||||||
|
.open_path(windows_path, None::<&str>)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
app.opener()
|
||||||
|
.open_path(path, None::<&str>)
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -3292,4 +3318,39 @@ gitea: gitea-mcp -t stdio (STDIO) - ✓ Connected"#;
|
|||||||
Some("Indented Heading".to_string())
|
Some("Indented Heading".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== open_binary_file E2E path conversion tests ====================
|
||||||
|
|
||||||
|
/// Build the wslpath command structure without executing it, for cross-platform CI testing.
|
||||||
|
#[cfg(test)]
|
||||||
|
fn build_wslpath_command(path: &str) -> (String, Vec<String>) {
|
||||||
|
(
|
||||||
|
"wsl".to_string(),
|
||||||
|
vec!["wslpath".to_string(), "-w".to_string(), path.to_string()],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_e2e_wslpath_command_structure_pdf() {
|
||||||
|
let (command, args) = build_wslpath_command("/tmp/mcp_output_abc123.pdf");
|
||||||
|
assert_eq!(command, "wsl");
|
||||||
|
assert_eq!(args.len(), 3);
|
||||||
|
assert_eq!(args[0], "wslpath");
|
||||||
|
assert_eq!(args[1], "-w");
|
||||||
|
assert_eq!(args[2], "/tmp/mcp_output_abc123.pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_e2e_wslpath_command_structure_audio() {
|
||||||
|
let (command, args) = build_wslpath_command("/tmp/mcp_output_xyz789.mp3");
|
||||||
|
assert_eq!(command, "wsl");
|
||||||
|
assert_eq!(args[2], "/tmp/mcp_output_xyz789.mp3");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_e2e_wslpath_command_structure_preserves_path() {
|
||||||
|
let path = "/home/naomi/documents/report with spaces.pdf";
|
||||||
|
let (_, args) = build_wslpath_command(path);
|
||||||
|
assert_eq!(args[2], path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ pub fn run() {
|
|||||||
delete_draft,
|
delete_draft,
|
||||||
delete_all_drafts,
|
delete_all_drafts,
|
||||||
scan_project,
|
scan_project,
|
||||||
|
open_binary_file,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { openUrl, openPath } from "@tauri-apps/plugin-opener";
|
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { clipboardStore } from "$lib/stores/clipboard";
|
import { clipboardStore } from "$lib/stores/clipboard";
|
||||||
import { linkifyFilePaths } from "$lib/utils/filePaths";
|
import { linkifyFilePaths } from "$lib/utils/filePaths";
|
||||||
|
|
||||||
@@ -148,7 +149,7 @@
|
|||||||
|
|
||||||
const filePath = anchor.dataset.filepath;
|
const filePath = anchor.dataset.filepath;
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
void openPath(filePath);
|
void invoke("open_binary_file", { path: filePath });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
* - [ ] Code blocks render with syntax highlighting and a copy button
|
* - [ ] Code blocks render with syntax highlighting and a copy button
|
||||||
* - [ ] ||spoiler text|| renders as a hidden span revealed on click
|
* - [ ] ||spoiler text|| renders as a hidden span revealed on click
|
||||||
* - [ ] Search query highlights matching text in non-code content
|
* - [ ] Search query highlights matching text in non-code content
|
||||||
* - [ ] Links open in the system browser via the Tauri opener
|
* - [ ] Regular links open in the system browser via the Tauri opener
|
||||||
|
* - [ ] Binary file links invoke open_binary_file (WSL-path-aware) instead of openPath
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
|
|||||||
Reference in New Issue
Block a user