From 6b56db7cf2bc2aca344d7f586961b0afa8de0a94 Mon Sep 17 00:00:00 2001 From: Youwes09 Date: Sat, 16 May 2026 15:31:13 -0500 Subject: [PATCH] Fix: Exit Button Works --- nix/frontend.nix | 2 +- package.json | 1 + pnpm-lock.yaml | 10 ++++ src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 5 +- src-tauri/src/lib.rs | 93 ++++++++++++++++++++++++++++- src/App.svelte | 44 +++----------- src/shared/chrome/TitleBar.svelte | 6 +- 9 files changed, 121 insertions(+), 42 deletions(-) diff --git a/nix/frontend.nix b/nix/frontend.nix index b53c48b..c15f4f1 100644 --- a/nix/frontend.nix +++ b/nix/frontend.nix @@ -10,7 +10,7 @@ stdenv.mkDerivation { pname = "moku-frontend"; inherit version src; fetcherVersion = 1; - hash = "sha256-eRuSSRhNmJ09mp/uhbG+NFeiOZ5dTOdJ94OwdP6IkN0="; + hash = "sha256-vM//1/qe9nKDwwlmFbqvBFqF8cCjIIdNKEtktyzBFB8="; }; buildPhase = "pnpm build"; diff --git a/package.json b/package.json index 9d82a01..d41ed24 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@tauri-apps/api": "^2.11.0", "@tauri-apps/plugin-http": "^2.5.8", "@tauri-apps/plugin-os": "^2.3.2", + "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-shell": "^2.3.5", "@tauri-apps/plugin-store": "~2.4.2", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21b48d3..a6084c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@tauri-apps/plugin-os': specifier: ^2.3.2 version: 2.3.2 + '@tauri-apps/plugin-process': + specifier: ^2.3.1 + version: 2.3.1 '@tauri-apps/plugin-shell': specifier: ^2.3.5 version: 2.3.5 @@ -289,6 +292,9 @@ packages: '@tauri-apps/plugin-os@2.3.2': resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==} + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + '@tauri-apps/plugin-shell@2.3.5': resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} @@ -763,6 +769,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.11.0 + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.11.0 + '@tauri-apps/plugin-shell@2.3.5': dependencies: '@tauri-apps/api': 2.11.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index abac972..f42041b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2008,6 +2008,7 @@ name = "moku" version = "0.9.3" dependencies = [ "dirs 5.0.1", + "libc", "reqwest 0.12.28", "serde", "serde_json", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 91a6d2e..917064d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -28,6 +28,7 @@ serde_json = "1" walkdir = "2" sysinfo = "0.32" dirs = "5" +libc = "0.2" urlencoding = "2" tokio = { version = "1", features = ["rt-multi-thread"] } reqwest = { version = "0.12", features = ["blocking"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index baeca1a..df276b5 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,7 +2,9 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Default permissions for Moku", - "windows": ["main"], + "windows": [ + "main" + ], "permissions": [ "core:default", "core:tray:default", @@ -31,6 +33,7 @@ "core:window:allow-outer-position", "core:window:allow-scale-factor", "process:default", + "process:allow-exit", "process:allow-restart", "http:default", "http:allow-fetch", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bb39fc4..557e60a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,11 +2,20 @@ mod commands; mod server; use std::sync::Mutex; -use tauri::{Manager, WindowEvent}; +use tauri::{ + menu::{Menu, MenuItem, PredefinedMenuItem}, + tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, + Manager, WindowEvent, +}; use tauri_plugin_shell::process::CommandChild; pub struct ServerState(pub Mutex>); +fn do_quit(app: &tauri::AppHandle) { + server::kill_tachidesk(app); + app.exit(0); +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -44,7 +53,87 @@ pub fn run() { commands::biometric::windows_hello_authenticate, commands::biometric::windows_hello_available, ]) - .setup(|_app| Ok(())) + .setup(|app| { + let lock_path = app + .path() + .app_data_dir() + .unwrap_or_default() + .join(".moku.lock"); + + let lock_file = std::fs::OpenOptions::new() + .create(true) + .write(true) + .open(&lock_path) + .ok(); + + let already_running = lock_file.as_ref().map(|f| { + #[cfg(unix)] + { + use std::os::unix::io::AsRawFd; + unsafe { libc::flock(f.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) != 0 } + } + #[cfg(windows)] + { + use std::os::windows::io::AsRawHandle; + use windows::Win32::Foundation::HANDLE; + use windows::Win32::Storage::FileSystem::{ + LockFileEx, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, + }; + let handle = HANDLE(f.as_raw_handle() as isize); + let mut overlapped = windows::Win32::System::IO::OVERLAPPED::default(); + !unsafe { + LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &mut overlapped) + }.as_bool() + } + #[cfg(not(any(unix, windows)))] + { false } + }).unwrap_or(false); + + if already_running { + app.handle().exit(0); + return Ok(()); + } + + std::mem::forget(lock_file); + + let show = MenuItem::with_id(app, "show", "Show Moku", true, None::<&str>)?; + let sep = PredefinedMenuItem::separator(app)?; + let quit = MenuItem::with_id(app, "quit", "Quit Moku", true, None::<&str>)?; + let menu = Menu::with_items(app, &[&show, &sep, &quit])?; + + TrayIconBuilder::new() + .icon(app.default_window_icon().unwrap().clone()) + .menu(&menu) + .show_menu_on_left_click(false) + .tooltip("Moku") + .on_menu_event(|app, event| match event.id.as_ref() { + "show" => { + if let Some(win) = app.get_webview_window("main") { + let _ = win.show(); + let _ = win.set_focus(); + } + } + "quit" => do_quit(app), + _ => {} + }) + .on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = event + { + let app = tray.app_handle(); + if let Some(win) = app.get_webview_window("main") { + let _ = win.show(); + let _ = win.set_focus(); + } + } + }) + .build(app)?; + + Ok(()) + }) .on_window_event(|window, event| { if let WindowEvent::Destroyed = event { server::kill_tachidesk(window.app_handle()); diff --git a/src/App.svelte b/src/App.svelte index df4ac37..8422487 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -3,9 +3,6 @@ import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; import { getCurrentWindow } from "@tauri-apps/api/window"; - import { defaultWindowIcon } from "@tauri-apps/api/app"; - import { TrayIcon } from "@tauri-apps/api/tray"; - import { Menu } from "@tauri-apps/api/menu"; import { platform } from "@tauri-apps/plugin-os"; import { store, updateSettings, setActiveDownloads } from "@store/state.svelte"; import { downloadStore } from "@features/downloads/store/downloadState.svelte"; @@ -48,8 +45,13 @@ } async function doQuit() { - if (store.settings.autoStartServer) await invoke("kill_server").catch(() => {}); - await win.destroy(); + if (store.settings.autoStartServer) { + await Promise.race([ + invoke("kill_server").catch(() => {}), + new Promise(res => setTimeout(res, 2000)), + ]); + } + await invoke("exit_app"); } async function doHide() { @@ -123,36 +125,6 @@ applyZoom(); }); - const menu = await Menu.new({ - items: [ - { - id: "show", - text: "Show Moku", - action: async () => { - await win.show(); - await win.setFocus(); - }, - }, - { - id: "quit", - text: "Quit", - action: doQuit, - }, - ], - }); - - await TrayIcon.new({ - icon: await defaultWindowIcon(), - menu, - menuOnLeftClick: false, - tooltip: "Moku", - action: async (e) => { - if (e.type === "Click") { - await win.show(); - await win.setFocus(); - } - }, - }); const unlistenClose = await win.listen("tauri://close-requested", handleCloseRequested); @@ -215,7 +187,7 @@ {/if}
- {#if !store.activeChapter}{/if} + {#if !store.activeChapter}{/if}
{#if store.activeChapter}{:else}{/if}
diff --git a/src/shared/chrome/TitleBar.svelte b/src/shared/chrome/TitleBar.svelte index 772164d..92f3917 100644 --- a/src/shared/chrome/TitleBar.svelte +++ b/src/shared/chrome/TitleBar.svelte @@ -3,6 +3,8 @@ import { getCurrentWindow } from "@tauri-apps/api/window"; import { platform } from "@tauri-apps/plugin-os"; + const { onClose }: { onClose: () => void } = $props(); + const win = getCurrentWindow(); const os = platform(); const isMac = os === "macos"; @@ -31,7 +33,7 @@ - -