Compare commits

..

No commits in common. "main" and "v1.13.1" have entirely different histories.

2 changed files with 13 additions and 35 deletions

View File

@ -2,12 +2,9 @@ use tauri::AppHandle;
use tauri::WebviewWindowBuilder; use tauri::WebviewWindowBuilder;
/// Open a child window from the Rust side using 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] #[tauri::command]
pub fn open_child_window( pub async fn open_child_window(
app_handle: AppHandle, app_handle: AppHandle,
label: String, label: String,
title: String, title: String,
@ -15,26 +12,13 @@ pub fn open_child_window(
width: f64, width: f64,
height: f64, height: f64,
) -> Result<(), String> { ) -> Result<(), String> {
// Split "index.html#/tool/ping?sessionId=abc" into path and fragment let webview_url = tauri::WebviewUrl::App(url.into());
let (path, hash) = match url.split_once('#') { WebviewWindowBuilder::new(&app_handle, &label, webview_url)
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) .title(&title)
.inner_size(width, height) .inner_size(width, height)
.resizable(true) .resizable(true)
.center() .center()
.build() .build()
.map_err(|e| format!("Failed to create window '{}': {}", label, e))?; .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(()) Ok(())
} }

View File

@ -16,6 +16,7 @@ const DetachedSession = defineAsyncComponent({
const app = useAppStore(); const app = useAppStore();
const appError = ref<string | null>(null); const appError = ref<string | null>(null);
// Tool window mode detected from URL hash: #/tool/network-scanner?sessionId=abc
const isToolMode = ref(false); const isToolMode = ref(false);
const isDetachedMode = ref(false); const isDetachedMode = ref(false);
const toolName = ref(""); const toolName = ref("");
@ -27,39 +28,32 @@ onErrorCaptured((err) => {
return false; return false;
}); });
/** Parse hash and set mode flags. Called on mount and on hashchange. */ onMounted(async () => {
function applyHash(hash: string): void { const hash = window.location.hash;
if (hash.startsWith("#/tool/")) { if (hash.startsWith("#/tool/")) {
isToolMode.value = true; isToolMode.value = true;
const rest = hash.substring(7); const rest = hash.substring(7); // after "#/tool/"
const [name, query] = rest.split("?"); const [name, query] = rest.split("?");
toolName.value = name; toolName.value = name;
toolSessionId.value = new URLSearchParams(query || "").get("sessionId") || ""; toolSessionId.value = new URLSearchParams(query || "").get("sessionId") || "";
} else if (hash.startsWith("#/detached-session")) { } else if (hash.startsWith("#/detached-session")) {
isDetachedMode.value = true; 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(); await app.checkVaultState();
} }
}); });
</script> </script>
<template> <template>
<!-- Error display for debugging -->
<div v-if="appError" class="fixed inset-0 z-50 flex items-center justify-center bg-[#0d1117] text-red-400 p-8 text-sm font-mono whitespace-pre-wrap"> <div v-if="appError" class="fixed inset-0 z-50 flex items-center justify-center bg-[#0d1117] text-red-400 p-8 text-sm font-mono whitespace-pre-wrap">
{{ appError }} {{ appError }}
</div> </div>
<!-- Detached session window mode -->
<DetachedSession v-else-if="isDetachedMode" /> <DetachedSession v-else-if="isDetachedMode" />
<!-- Tool popup window mode -->
<ToolWindow v-else-if="isToolMode" :tool="toolName" :session-id="toolSessionId" /> <ToolWindow v-else-if="isToolMode" :tool="toolName" :session-id="toolSessionId" />
<!-- Normal app mode -->
<div v-else class="app-root"> <div v-else class="app-root">
<UnlockLayout v-if="!app.isUnlocked" /> <UnlockLayout v-if="!app.isUnlocked" />
<MainLayout v-else /> <MainLayout v-else />