diff --git a/src-tauri/src/commands/window_commands.rs b/src-tauri/src/commands/window_commands.rs index 8273528..0d37af3 100644 --- a/src-tauri/src/commands/window_commands.rs +++ b/src-tauri/src/commands/window_commands.rs @@ -2,9 +2,12 @@ use tauri::AppHandle; use tauri::WebviewWindowBuilder; /// Open a child window from the Rust side using WebviewWindowBuilder. -/// This is more reliable than JS-side WebviewWindow on macOS WKWebView. +/// +/// The `url` parameter supports hash fragments (e.g. "index.html#/tool/ping?sessionId=abc"). +/// WebviewUrl::App takes a PathBuf and cannot handle hash/query — so we load plain +/// index.html and set the hash via JS after the window is created. #[tauri::command] -pub async fn open_child_window( +pub fn open_child_window( app_handle: AppHandle, label: String, title: String, @@ -12,13 +15,26 @@ pub async fn open_child_window( width: f64, height: f64, ) -> Result<(), String> { - let webview_url = tauri::WebviewUrl::App(url.into()); - WebviewWindowBuilder::new(&app_handle, &label, webview_url) + // Split "index.html#/tool/ping?sessionId=abc" into path and fragment + let (path, hash) = match url.split_once('#') { + Some((p, h)) => (p.to_string(), Some(format!("#{}", h))), + None => (url.clone(), None), + }; + + let webview_url = tauri::WebviewUrl::App(path.into()); + let window = WebviewWindowBuilder::new(&app_handle, &label, webview_url) .title(&title) .inner_size(width, height) .resizable(true) .center() .build() .map_err(|e| format!("Failed to create window '{}': {}", label, e))?; + + // Set the hash fragment after the window loads — this triggers App.vue's + // onMounted hash detection to render the correct tool/detached component. + if let Some(hash) = hash { + let _ = window.eval(&format!("window.location.hash = '{}';", hash)); + } + Ok(()) } diff --git a/src/App.vue b/src/App.vue index e2e914e..fed6728 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,7 +16,6 @@ const DetachedSession = defineAsyncComponent({ const app = useAppStore(); const appError = ref(null); -// Tool window mode — detected from URL hash: #/tool/network-scanner?sessionId=abc const isToolMode = ref(false); const isDetachedMode = ref(false); const toolName = ref(""); @@ -28,32 +27,39 @@ onErrorCaptured((err) => { return false; }); -onMounted(async () => { - const hash = window.location.hash; +/** Parse hash and set mode flags. Called on mount and on hashchange. */ +function applyHash(hash: string): void { if (hash.startsWith("#/tool/")) { isToolMode.value = true; - const rest = hash.substring(7); // after "#/tool/" + const rest = hash.substring(7); const [name, query] = rest.split("?"); toolName.value = name; toolSessionId.value = new URLSearchParams(query || "").get("sessionId") || ""; } else if (hash.startsWith("#/detached-session")) { isDetachedMode.value = true; - } else { + } +} + +onMounted(async () => { + // Check hash at load time (present if JS-side WebviewWindow set it in the URL) + applyHash(window.location.hash); + + // Also listen for hash changes (Rust-side window sets hash via eval after load) + window.addEventListener("hashchange", () => applyHash(window.location.hash)); + + // Only init vault for the main app window (no hash) + if (!isToolMode.value && !isDetachedMode.value) { await app.checkVaultState(); } });