From 9c3afa39bd9f67539c58dc3967f5e12e305febe0 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Thu, 26 Mar 2026 14:07:45 -0400 Subject: [PATCH] feat: Help menu + fix tab detach rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Help menu (File → Help): - Getting Started guide (connections, SFTP, copilot, tabs) - Keyboard Shortcuts reference table - MCP Integration page (setup command, all 18 tools documented, bridge path auto-populated, architecture explanation) - About page with version and tech stack - Opens as a tabbed popup window Tab detach fixes: - Added detached-*, editor-*, help-* to capabilities window list (detached windows had no event permissions — silent failure) - SessionContainer filters out detached sessions (active=false) so the main window stops rendering the terminal when detached - Terminal now only renders in the detached popup window Co-Authored-By: Claude Opus 4.6 (1M context) --- src-tauri/capabilities/default.json | 2 +- src-tauri/gen/schemas/capabilities.json | 2 +- src/components/session/SessionContainer.vue | 7 +- src/components/tools/HelpWindow.vue | 219 ++++++++++++++++++++ src/components/tools/ToolWindow.vue | 2 + src/layouts/MainLayout.vue | 60 ++++++ 6 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 src/components/tools/HelpWindow.vue diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index cbb7a41..6aa9115 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -1,7 +1,7 @@ { "identifier": "default", "description": "Default capabilities for the main Wraith window", - "windows": ["main", "tool-*"], + "windows": ["main", "tool-*", "detached-*", "editor-*", "help-*"], "permissions": [ "core:default", "core:event:default", diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json index eb80d06..df63b60 100644 --- a/src-tauri/gen/schemas/capabilities.json +++ b/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"default":{"identifier":"default","description":"Default capabilities for the main Wraith window","local":true,"windows":["main","tool-*"],"permissions":["core:default","core:event:default","core:window:default","core:window:allow-create","core:webview:default","core:webview:allow-create-webview-window","shell:allow-open","updater:default"]}} \ No newline at end of file +{"default":{"identifier":"default","description":"Default capabilities for the main Wraith window","local":true,"windows":["main","tool-*","detached-*","editor-*","help-*"],"permissions":["core:default","core:event:default","core:window:default","core:window:allow-create","core:webview:default","core:webview:allow-create-webview-window","shell:allow-open","updater:default"]}} \ No newline at end of file diff --git a/src/components/session/SessionContainer.vue b/src/components/session/SessionContainer.vue index 751d501..0b65c37 100644 --- a/src/components/session/SessionContainer.vue +++ b/src/components/session/SessionContainer.vue @@ -91,16 +91,17 @@ function setTerminalRef(sessionId: string, el: unknown): void { const sessionStore = useSessionStore(); +// Only render sessions that are active (not detached to separate windows) const sshSessions = computed(() => - sessionStore.sessions.filter((s) => s.protocol === "ssh"), + sessionStore.sessions.filter((s) => s.protocol === "ssh" && s.active), ); const localSessions = computed(() => - sessionStore.sessions.filter((s) => s.protocol === "local"), + sessionStore.sessions.filter((s) => s.protocol === "local" && s.active), ); const rdpSessions = computed(() => - sessionStore.sessions.filter((s) => s.protocol === "rdp"), + sessionStore.sessions.filter((s) => s.protocol === "rdp" && s.active), ); /** diff --git a/src/components/tools/HelpWindow.vue b/src/components/tools/HelpWindow.vue new file mode 100644 index 0000000..b14dcb5 --- /dev/null +++ b/src/components/tools/HelpWindow.vue @@ -0,0 +1,219 @@ + + + + + diff --git a/src/components/tools/ToolWindow.vue b/src/components/tools/ToolWindow.vue index 64e2aca..edf1900 100644 --- a/src/components/tools/ToolWindow.vue +++ b/src/components/tools/ToolWindow.vue @@ -13,6 +13,7 @@ +
Unknown tool: {{ tool }}
@@ -33,6 +34,7 @@ import DockerPanel from "./DockerPanel.vue"; import FileEditor from "./FileEditor.vue"; import SshKeyGen from "./SshKeyGen.vue"; import PasswordGen from "./PasswordGen.vue"; +import HelpWindow from "./HelpWindow.vue"; defineProps<{ tool: string; diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index f66ad5b..ba707f9 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -141,6 +141,47 @@ + + +
+ +
+ + + +
+ +
+
@@ -309,6 +350,7 @@ const sessionContainer = ref | null>(null) const showFileMenu = ref(false); const showToolsMenu = ref(false); +const showHelpMenu = ref(false); function closeFileMenuDeferred(): void { setTimeout(() => { showFileMenu.value = false; }, 150); @@ -318,6 +360,24 @@ function closeToolsMenuDeferred(): void { setTimeout(() => { showToolsMenu.value = false; }, 150); } +function closeHelpMenuDeferred(): void { + setTimeout(() => { showHelpMenu.value = false; }, 150); +} + +async function handleHelpAction(page: string): Promise { + showHelpMenu.value = false; + const { WebviewWindow } = await import("@tauri-apps/api/webviewWindow"); + const label = `help-${page}-${Date.now()}`; + new WebviewWindow(label, { + title: `Wraith — Help`, + width: 750, + height: 600, + resizable: true, + center: true, + url: `index.html#/tool/help?page=${page}`, + }); +} + async function handleToolAction(tool: string): Promise { showToolsMenu.value = false;