From adaa19a1efb8af2a6a0c59da1fa70f7238e81b14 Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sun, 12 Apr 2026 00:57:18 +0200 Subject: [PATCH] fix: Durchsuchen-Button, Tray-Icon, Minimize statt Close, .cloud Handler 1. Durchsuchen-Button: dialog:allow-open Permission in capabilities 2. Tray-Icon: Nutzt das App-Icon (32x32.png) statt leer 3. Close = Minimize: Fenster wird versteckt statt App beendet, Doppelklick auf Tray-Icon oeffnet wieder 4. .cloud Datei-Handler: - fileAssociations in tauri.conf.json registriert .cloud Extension - NSIS-Installer registriert den Handler automatisch - Doppelklick auf .cloud -> App startet, laedt Datei runter, oeffnet mit Standard-App (Word/Excel/etc.) - Wenn App laeuft: Event wird emitted, Frontend verarbeitet es Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src-tauri/capabilities/default.json | 5 ++- clients/desktop/src-tauri/src/lib.rs | 36 +++++++++++++++++++ clients/desktop/src-tauri/tauri.conf.json | 16 ++++++++- clients/desktop/src/App.vue | 14 ++++++-- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/clients/desktop/src-tauri/capabilities/default.json b/clients/desktop/src-tauri/capabilities/default.json index 4cdbf49..958937a 100644 --- a/clients/desktop/src-tauri/capabilities/default.json +++ b/clients/desktop/src-tauri/capabilities/default.json @@ -5,6 +5,9 @@ "windows": ["main"], "permissions": [ "core:default", - "opener:default" + "opener:default", + "dialog:default", + "dialog:allow-open", + "notification:default" ] } diff --git a/clients/desktop/src-tauri/src/lib.rs b/clients/desktop/src-tauri/src/lib.rs index ee04d25..6850371 100644 --- a/clients/desktop/src-tauri/src/lib.rs +++ b/clients/desktop/src-tauri/src/lib.rs @@ -402,13 +402,40 @@ pub fn run() { locked_files: Mutex::new(Vec::new()), sync_paths: Mutex::new(Vec::new()), }) + .on_window_event(|window, event| { + // Close button = minimize to tray instead of quit + if let tauri::WindowEvent::CloseRequested { api, .. } = event { + api.prevent_close(); + let _ = window.hide(); + } + }) .setup(|app| { let quit = MenuItem::with_id(app, "quit", "Beenden", true, None::<&str>)?; let show = MenuItem::with_id(app, "show", "Oeffnen", true, None::<&str>)?; let sync_now = MenuItem::with_id(app, "sync", "Jetzt synchronisieren", true, None::<&str>)?; let menu = Menu::with_items(app, &[&show, &sync_now, &quit])?; + // Use bundled icon for tray + let icon = app.default_window_icon().cloned() + .unwrap_or_else(|| tauri::image::Image::from_bytes(include_bytes!("../icons/32x32.png")).unwrap()); + + // Handle .cloud file opened via file association (double-click) + let args: Vec = std::env::args().collect(); + if args.len() > 1 { + let file_arg = &args[1]; + if file_arg.ends_with(".cloud") { + let cloud_path = file_arg.to_string(); + let app_handle = app.handle().clone(); + // Open the .cloud file after app is ready + std::thread::spawn(move || { + std::thread::sleep(Duration::from_secs(2)); + let _ = app_handle.emit("open-cloud-file", cloud_path); + }); + } + } + TrayIconBuilder::new() + .icon(icon) .tooltip("Mini-Cloud Sync") .menu(&menu) .on_menu_event(|app, event| { @@ -424,6 +451,15 @@ pub fn run() { _ => {} } }) + .on_tray_icon_event(|tray, event| { + // Double-click on tray icon = show window + if let tauri::tray::TrayIconEvent::DoubleClick { .. } = event { + if let Some(w) = tray.app_handle().get_webview_window("main") { + let _ = w.show(); + let _ = w.set_focus(); + } + } + }) .build(app)?; Ok(()) diff --git a/clients/desktop/src-tauri/tauri.conf.json b/clients/desktop/src-tauri/tauri.conf.json index b052629..c51aa6b 100644 --- a/clients/desktop/src-tauri/tauri.conf.json +++ b/clients/desktop/src-tauri/tauri.conf.json @@ -32,6 +32,20 @@ "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico" - ] + ], + "fileAssociations": [ + { + "ext": ["cloud"], + "mimeType": "application/x-minicloud", + "description": "Mini-Cloud Platzhalter" + } + ], + "windows": { + "nsis": { + "installerIcon": "icons/icon.ico", + "headerImage": null, + "sidebarImage": null + } + } } } diff --git a/clients/desktop/src/App.vue b/clients/desktop/src/App.vue index bfa3062..16a6bf3 100644 --- a/clients/desktop/src/App.vue +++ b/clients/desktop/src/App.vue @@ -28,7 +28,7 @@ const newPathServerId = ref(null); const newPathMode = ref("virtual"); const serverFolders = ref([]); -let unlistenStatus, unlistenLog, unlistenError, unlistenFileChange, unlistenTrigger; +let unlistenStatus, unlistenLog, unlistenError, unlistenFileChange, unlistenTrigger, unlistenCloudOpen; async function handleLogin() { loginError.value = ""; @@ -166,8 +166,18 @@ onMounted(async () => { fileChanges.value = [`[${ts()}] ${e.payload}`, ...fileChanges.value].slice(0, 50); }); unlistenTrigger = await listen("trigger-sync", () => syncNow()); + unlistenCloudOpen = await listen("open-cloud-file", async (e) => { + const cloudPath = e.payload; + syncLog.value = [`[${ts()}] Oeffne: ${cloudPath}`, ...syncLog.value].slice(0, 200); + try { + const realPath = await invoke("open_cloud_file", { cloudPath }); + syncLog.value = [`[${ts()}] Geoeffnet: ${realPath}`, ...syncLog.value].slice(0, 200); + } catch (err) { + syncLog.value = [`[${ts()}] Fehler: ${err}`, ...syncLog.value].slice(0, 200); + } + }); }); -onUnmounted(() => { unlistenStatus?.(); unlistenLog?.(); unlistenError?.(); unlistenFileChange?.(); unlistenTrigger?.(); }); +onUnmounted(() => { unlistenStatus?.(); unlistenLog?.(); unlistenError?.(); unlistenFileChange?.(); unlistenTrigger?.(); unlistenCloudOpen?.(); });