All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m45s
ROOT CAUSE FOUND: WebviewUrl::App takes a PathBuf, not a URL. Passing "index.html#/tool/ping?sessionId=abc" treated the ENTIRE string including # and ? as a file path. Tauri looked for a file literally named "index.html#/tool/ping?sessionId=abc" which doesn't exist. The webview loaded an empty/404 page and WKWebView killed the content process, closing the window instantly. Fix: - Rust: split URL at '#' — pass only "index.html" to WebviewUrl::App, then set the hash fragment via window.eval() after build() - Vue: App.vue now listens for 'hashchange' event in addition to checking hash on mount, so the eval-injected hash triggers the correct tool/detached mode This was NEVER a CSP issue, focus issue, crossorigin issue, or async chunk loading issue. It was always a bad file path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
76 lines
2.4 KiB
Vue
76 lines
2.4 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted, onErrorCaptured, defineAsyncComponent } from "vue";
|
|
import { useAppStore } from "@/stores/app.store";
|
|
import UnlockLayout from "@/layouts/UnlockLayout.vue";
|
|
import ToolWindow from "@/components/tools/ToolWindow.vue";
|
|
|
|
const MainLayout = defineAsyncComponent({
|
|
loader: () => import("@/layouts/MainLayout.vue"),
|
|
onError(error) { console.error("[App] MainLayout load failed:", error); },
|
|
});
|
|
const DetachedSession = defineAsyncComponent({
|
|
loader: () => import("@/components/session/DetachedSession.vue"),
|
|
onError(error) { console.error("[App] DetachedSession load failed:", error); },
|
|
});
|
|
|
|
const app = useAppStore();
|
|
const appError = ref<string | null>(null);
|
|
|
|
const isToolMode = ref(false);
|
|
const isDetachedMode = ref(false);
|
|
const toolName = ref("");
|
|
const toolSessionId = ref("");
|
|
|
|
onErrorCaptured((err) => {
|
|
appError.value = err instanceof Error ? err.message : String(err);
|
|
console.error("[App] Uncaught error:", err);
|
|
return false;
|
|
});
|
|
|
|
/** 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);
|
|
const [name, query] = rest.split("?");
|
|
toolName.value = name;
|
|
toolSessionId.value = new URLSearchParams(query || "").get("sessionId") || "";
|
|
} else if (hash.startsWith("#/detached-session")) {
|
|
isDetachedMode.value = true;
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<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 }}
|
|
</div>
|
|
<DetachedSession v-else-if="isDetachedMode" />
|
|
<ToolWindow v-else-if="isToolMode" :tool="toolName" :session-id="toolSessionId" />
|
|
<div v-else class="app-root">
|
|
<UnlockLayout v-if="!app.isUnlocked" />
|
|
<MainLayout v-else />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.app-root {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
</style>
|