diff --git a/.changeset/window-close.md b/.changeset/window-close.md new file mode 100644 index 00000000..3eaa5dd0 --- /dev/null +++ b/.changeset/window-close.md @@ -0,0 +1,3 @@ +### Bug Fixes +- Closing the window quits Stroke again. 2.1.0 dropped the tray and the hide-on-close handler that went with it, which let close run through to `destroy` for the first time. `destroy` was never in the capability file, so the ACL refused it and the window stayed put. With no tray and no window decorations, that left no way to quit the app at all. +- Second windows (`Ctrl/Cmd+Shift+N`) respond to their title bar again. The capability only covered the window labelled `main`, so every control on a second window (close, minimize, maximize, and dragging the bar) was denied. diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index ff505f89..29baa69f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -3,7 +3,8 @@ "identifier": "default", "description": "enables the default permissions", "windows": [ - "main" + "main", + "main-*" ], "permissions": [ "core:default", @@ -24,6 +25,7 @@ "core:window:allow-set-fullscreen", "core:window:allow-is-fullscreen", "core:window:allow-close", + "core:window:allow-destroy", "core:window:allow-minimize", "core:window:allow-maximize", "core:window:allow-unmaximize", diff --git a/src/lib/components/WindowControls.svelte b/src/lib/components/WindowControls.svelte index 4e741f00..31240c19 100644 --- a/src/lib/components/WindowControls.svelte +++ b/src/lib/components/WindowControls.svelte @@ -27,19 +27,26 @@ return () => unlisten() }) - async function minimizeWindow() { - const { getCurrentWindow } = await import('@tauri-apps/api/window') - getCurrentWindow().minimize() - } - async function toggleMaximizeWindow() { - const { getCurrentWindow } = await import('@tauri-apps/api/window') - getCurrentWindow().toggleMaximize() - } - async function closeWindow() { - const { getCurrentWindow } = await import('@tauri-apps/api/window') - getCurrentWindow().close() + // Every one of these is an ACL-gated core command, and the window is frameless, + // so these buttons are the only chrome there is - a denied call leaves the user + // with a window they cannot move, resize or close. That is how the missing + // `core:window:allow-destroy` shipped in 2.1.0: `close()` runs through to + // `destroy`, the ACL refused it, and the rejection went nowhere. Await and log + // so the next gap in src-tauri/capabilities/default.json is visible. + /** @param {string} name @param {(w: import('@tauri-apps/api/window').Window) => Promise} fn */ + async function windowAction(name, fn) { + try { + const { getCurrentWindow } = await import('@tauri-apps/api/window') + await fn(getCurrentWindow()) + } catch (err) { + console.error(`window ${name} failed:`, err) + } } + const minimizeWindow = () => windowAction('minimize', (w) => w.minimize()) + const toggleMaximizeWindow = () => windowAction('toggleMaximize', (w) => w.toggleMaximize()) + const closeWindow = () => windowAction('close', (w) => w.close()) + const winBtn = 'inline-flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors duration-150 hover:bg-foreground/[0.08] hover:text-foreground'