wraith/src/App.vue
Vantz Stockwell c4335e0b4f
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 4m14s
fix: 6 UX regressions — popups, themes, cursor, selection, status bar
Cursor blinking (ACTUALLY fixed this time):
- handleFocus() was STILL missing due to botched duplicate removal in
  v1.12.1. Now defined once at line 80, no duplicates.
- vue-tsc --noEmit now runs clean (was erroring with TS2393)

Tool windows:
- App.vue now has onErrorCaptured + error display overlay so tool window
  crashes show the error instead of silently closing
- defineAsyncComponent uses object form with onError callback for logging

Selection highlighting:
- Changed from rgba(88,166,255,0.4) transparent to solid #264f78
  (VS Code's selection color) — always visible on dark backgrounds
- Applied to default theme, TerminalView applyTheme, LocalTerminalView

CSP:
- Simplified to 'self' 'unsafe-inline' asset: for default-src
- Separate connect-src for IPC protocols

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:13:54 -04:00

73 lines
2.3 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";
const MainLayout = defineAsyncComponent({
loader: () => import("@/layouts/MainLayout.vue"),
onError(error) { console.error("[App] MainLayout load failed:", error); },
});
const ToolWindow = defineAsyncComponent({
loader: () => import("@/components/tools/ToolWindow.vue"),
onError(error) { console.error("[App] ToolWindow 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);
// Tool window mode — detected from URL hash: #/tool/network-scanner?sessionId=abc
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;
});
onMounted(async () => {
const hash = window.location.hash;
if (hash.startsWith("#/tool/")) {
isToolMode.value = true;
const rest = hash.substring(7); // after "#/tool/"
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 {
await app.checkVaultState();
}
});
</script>
<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">
{{ appError }}
</div>
<!-- Detached session window mode -->
<DetachedSession v-else-if="isDetachedMode" />
<!-- Tool popup window mode -->
<ToolWindow v-else-if="isToolMode" :tool="toolName" :session-id="toolSessionId" />
<!-- Normal app mode -->
<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>