diff --git a/backend/syft_space/components/dataset_types/chromadb_local/chromadb_provisioner.py b/backend/syft_space/components/dataset_types/chromadb_local/chromadb_provisioner.py index a52a67b..929defc 100644 --- a/backend/syft_space/components/dataset_types/chromadb_local/chromadb_provisioner.py +++ b/backend/syft_space/components/dataset_types/chromadb_local/chromadb_provisioner.py @@ -63,6 +63,9 @@ async def start(cls, config: dict[str, Any]) -> dict[str, Any]: data_path = home / ".syft-space" / "chromadb" await data_path.mkdir(parents=True, exist_ok=True) + # Recover from corrupted (0-byte) database before anything else + await cls._verify_database_integrity(data_path) + # PID file for process tracking pid_file = data_path / "chromadb.pid" @@ -139,8 +142,21 @@ async def _log_stream(stream: asyncio.StreamReader, level: str) -> None: if proc.stderr: asyncio.create_task(_log_stream(proc.stderr, "err")) - # Wait for health - await cls._wait_for_healthy(http_port) + # Wait for health — clean up on any startup failure + try: + await cls._wait_for_healthy(http_port, proc=proc) + except (TimeoutError, RuntimeError): + logger.error(f"ChromaDB process {proc.pid} failed to start, cleaning up") + await cls.stop({"pid": proc.pid, "pid_file": str(pid_file)}) + raise + + # Final liveness check: ensure our child is still the one serving + if proc.returncode is not None: + await cls.stop({"pid": proc.pid, "pid_file": str(pid_file)}) + raise RuntimeError( + f"ChromaDB process {proc.pid} exited (code {proc.returncode}) " + "after passing health check — another process may hold the port" + ) return { "pid": proc.pid, @@ -167,15 +183,19 @@ async def stop(cls, state: dict[str, Any]) -> None: else: os.kill(pid, signal.SIGTERM) - # Wait for graceful shutdown - await asyncio.sleep(2.0) - - # Check if still running, force kill if needed - if await cls._is_process_running(pid): - os.kill(pid, signal.SIGKILL if sys.platform != "win32" else signal.SIGTERM) - logger.info(f"Force killed ChromaDB process {pid}") + # Poll for graceful exit (up to 10s) to avoid SIGKILL corruption + for _ in range(20): # 20 * 0.5s = 10s + await asyncio.sleep(0.5) + if not await cls._is_process_running(pid): + logger.info(f"ChromaDB process {pid} stopped gracefully") + break else: - logger.info(f"ChromaDB process {pid} stopped gracefully") + # Still running after 10s — force kill as last resort + os.kill( + pid, + signal.SIGKILL if sys.platform != "win32" else signal.SIGTERM, + ) + logger.warning(f"Force killed ChromaDB process {pid} after 10s") except (OSError, ProcessLookupError): logger.info(f"ChromaDB process {pid} already stopped") @@ -243,6 +263,24 @@ async def wait_until_ready(cls, state: dict[str, Any]) -> None: raise TimeoutError("No HTTP port in provisioner state") await cls._wait_for_healthy(int(http_port)) + @classmethod + async def _verify_database_integrity(cls, data_path: AsyncPath) -> None: + """Delete ChromaDB's internal database if it is 0 bytes (corrupted). + + A 0-byte ``chroma.sqlite3`` is produced when the process is killed + mid-write (e.g. by process-wick SIGKILL). ChromaDB cannot recover + from this on its own and will fail with "no such table: tenants". + Removing the file lets ChromaDB recreate it on the next start. + """ + db_file = data_path / "chroma.sqlite3" + if await db_file.exists(): + stat = await db_file.stat() + if stat.st_size == 0: + logger.warning( + "Detected 0-byte chroma.sqlite3 — deleting corrupted database" + ) + await db_file.unlink() + @classmethod async def _is_process_running(cls, pid: int) -> bool: """Check if a ChromaDB process is running. @@ -275,19 +313,32 @@ async def _is_process_running(cls, pid: int) -> bool: return False @classmethod - async def _wait_for_healthy(cls, http_port: int, timeout: float = 60.0) -> None: + async def _wait_for_healthy( + cls, + http_port: int, + timeout: float = 60.0, + proc: asyncio.subprocess.Process | None = None, + ) -> None: """Wait for ChromaDB to be healthy. Args: http_port: HTTP port to check timeout: Timeout in seconds + proc: Optional subprocess to monitor for early exit Raises: TimeoutError: If not healthy within timeout + RuntimeError: If the subprocess exits before becoming healthy """ start_time = time.time() while time.time() - start_time < timeout: + # Fail fast if the child process already exited + if proc is not None and proc.returncode is not None: + raise RuntimeError( + f"ChromaDB process exited with code {proc.returncode} " + "before becoming healthy" + ) if await cls._check_health(http_port): logger.info("ChromaDB is healthy") return diff --git a/backend/syft_space/components/ingestion/manager.py b/backend/syft_space/components/ingestion/manager.py index bd43f62..3625811 100644 --- a/backend/syft_space/components/ingestion/manager.py +++ b/backend/syft_space/components/ingestion/manager.py @@ -518,9 +518,7 @@ async def _process_single_job(self, job: IngestionJob) -> None: job.dataset_id, job.tenant_id ) if not dataset: - logger.info( - f"Dataset deleted during ingestion prep for job {job.id}" - ) + logger.info(f"Dataset deleted during ingestion prep for job {job.id}") await self._ingestion_repository.update_status( job.id, IngestionJobStatus.CANCELLED, @@ -529,7 +527,12 @@ async def _process_single_job(self, job: IngestionJob) -> None: return # Call ingest (native async) - await dataset_type.ingest(ctx, ingest_request) + try: + await dataset_type.ingest(ctx, ingest_request) + except Exception as ingest_err: + raise RuntimeError( + f"[{dataset.dtype}] Ingestion error: {ingest_err}" + ) from ingest_err # Success await self._ingestion_repository.update_status( diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6bc5843..881ed86 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "tauri-plugin-autostart", "tauri-plugin-log", "tauri-plugin-shell", + "tauri-plugin-single-instance", "tauri-plugin-updater", "time", "time-macros", @@ -135,6 +136,137 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "async-signal" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "atk" version = "0.18.2" @@ -253,6 +385,19 @@ dependencies = [ "objc2 0.6.3", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "borsh" version = "1.5.7" @@ -539,6 +684,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -926,6 +1080,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -969,6 +1150,27 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1156,6 +1358,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1563,6 +1778,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -2719,6 +2940,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_pipe" version = "1.2.3" @@ -2768,6 +2999,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -2949,6 +3186,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2994,6 +3242,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -4468,6 +4730,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc61e4822b8f74d68278e09161d3e3fdd1b14b9eb781e24edccaabf10c420e8c" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.17", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + [[package]] name = "tauri-plugin-updater" version = "2.10.0" @@ -4933,9 +5210,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "tracing-core" version = "0.1.34" @@ -4985,6 +5274,17 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -6028,6 +6328,67 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.13", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.110", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.13", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.8.27" @@ -6140,3 +6501,43 @@ checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] + +[[package]] +name = "zvariant" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 0.7.13", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.110", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.110", + "winnow 0.7.13", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c98c4dc..dd070c9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -36,6 +36,7 @@ arboard = "3.4" [target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies] tauri-plugin-updater = "2.10.0" tauri-plugin-autostart = "2.5.0" +tauri-plugin-single-instance = "2.3" [target."cfg(target_os = \"macos\")".dependencies] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 59b86be..c402dcd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -51,6 +51,28 @@ fn resolve_backend_path(app: &tauri::App) -> std::path::PathBuf { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { + if let Some(window) = app.get_webview_window("main") { + if let Err(e) = window.set_skip_taskbar(false) { + log::error!("Failed to restore taskbar: {}", e); + } + if let Err(e) = window.show() { + log::error!("Failed to show window: {}", e); + } + if let Err(e) = window.unminimize() { + log::error!("Failed to unminimize window: {}", e); + } + if let Err(e) = window.set_focus() { + log::error!("Failed to focus window: {}", e); + } + #[cfg(target_os = "macos")] + { + if let Err(e) = app.set_activation_policy(tauri::ActivationPolicy::Regular) { + log::error!("Failed to set activation policy: {}", e); + } + } + } + })) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_autostart::init( @@ -283,12 +305,19 @@ pub fn run() { if let tauri::WindowEvent::CloseRequested { api, .. } = event { // Hide the window instead of closing it api.prevent_close(); - let _ = window.hide(); + if let Err(e) = window.hide() { + log::error!("Failed to hide window: {}", e); + } + if let Err(e) = window.set_skip_taskbar(true) { + log::error!("Failed to skip taskbar: {}", e); + } #[cfg(target_os = "macos")] { let app = window.app_handle(); - let _ = app.set_activation_policy(tauri::ActivationPolicy::Accessory); + if let Err(e) = app.set_activation_policy(tauri::ActivationPolicy::Accessory) { + log::error!("Failed to set activation policy: {}", e); + } } } } @@ -299,9 +328,18 @@ pub fn run() { #[cfg(target_os = "macos")] if let tauri::RunEvent::Reopen { .. } = event { if let Some(window) = app.get_webview_window("main") { - let _ = window.show(); - let _ = window.set_focus(); - let _ = app.set_activation_policy(tauri::ActivationPolicy::Regular); + if let Err(e) = window.set_skip_taskbar(false) { + log::error!("Failed to restore taskbar: {}", e); + } + if let Err(e) = window.show() { + log::error!("Failed to show window: {}", e); + } + if let Err(e) = window.set_focus() { + log::error!("Failed to focus window: {}", e); + } + if let Err(e) = app.set_activation_policy(tauri::ActivationPolicy::Regular) { + log::error!("Failed to set activation policy: {}", e); + } } } // Suppress unused variable warnings on non-macOS diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index e9d69b0..c0dc304 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -191,13 +191,24 @@ fn handle_menu_event(app: &AppHandle, id: &str) { /// Show (and focus) the main window, adjusting macOS activation policy. fn show_main_window(app: &AppHandle) { if let Some(window) = app.get_webview_window("main") { - let _ = window.show(); - let _ = window.unminimize(); - let _ = window.set_focus(); + if let Err(e) = window.set_skip_taskbar(false) { + log::error!("Failed to restore taskbar: {}", e); + } + if let Err(e) = window.show() { + log::error!("Failed to show window: {}", e); + } + if let Err(e) = window.unminimize() { + log::error!("Failed to unminimize window: {}", e); + } + if let Err(e) = window.set_focus() { + log::error!("Failed to focus window: {}", e); + } #[cfg(target_os = "macos")] { - let _ = app.set_activation_policy(tauri::ActivationPolicy::Regular); + if let Err(e) = app.set_activation_policy(tauri::ActivationPolicy::Regular) { + log::error!("Failed to set activation policy: {}", e); + } } } }