diff --git a/apps/tauri/src-tauri/Cargo.toml b/apps/tauri/src-tauri/Cargo.toml index 3752fda..19b69ec 100644 --- a/apps/tauri/src-tauri/Cargo.toml +++ b/apps/tauri/src-tauri/Cargo.toml @@ -19,14 +19,16 @@ tauri-plugin-dialog = "2" tauri-plugin-os = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -onyx-core = { path = "../../../crates/onyx-core" } +onyx-core = { path = "../../../crates/onyx-core", default-features = false } tokio = { version = "1", features = ["full"] } uuid = { version = "1", features = ["serde", "v4"] } chrono = { version = "0.4", features = ["serde"] } -notify = "7" -notify-debouncer-mini = "0.5" +notify = { version = "7", optional = true } +notify-debouncer-mini = { version = "0.5", optional = true } [package.metadata.tauri] [features] +default = ["desktop"] +desktop = ["notify", "notify-debouncer-mini", "onyx-core/keyring-storage"] custom-protocol = ["tauri/custom-protocol"] diff --git a/apps/tauri/src-tauri/src/lib.rs b/apps/tauri/src-tauri/src/lib.rs index 75bf467..7cbe413 100644 --- a/apps/tauri/src-tauri/src/lib.rs +++ b/apps/tauri/src-tauri/src/lib.rs @@ -4,6 +4,7 @@ use std::time::Instant; use chrono::Utc; +#[cfg(not(target_os = "android"))] use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use serde::{Deserialize, Serialize}; use tauri::{Emitter, Manager, State}; @@ -17,10 +18,12 @@ use onyx_core::{ webdav, }; +#[cfg(not(target_os = "android"))] /// Active file watcher stored globally so it lives for the app lifetime. static WATCHER: Mutex>> = Mutex::new(None); +#[cfg(not(target_os = "android"))] /// Shared mute timestamp — set before writes, checked by the watcher. static LAST_WRITE: Mutex> = Mutex::new(None); @@ -55,10 +58,14 @@ impl From for SyncResult { } /// Suppress file watcher events for the next second (call before writes). +#[cfg(not(target_os = "android"))] fn mute_watcher(_state: &mut AppState) { *LAST_WRITE.lock().unwrap() = Some(Instant::now()); } +#[cfg(target_os = "android")] +fn mute_watcher(_state: &mut AppState) {} + /// Helper: get or open a TaskRepository for the current workspace. fn ensure_repo(state: &mut AppState) -> Result<(), String> { if state.repo.is_some() { @@ -463,6 +470,7 @@ async fn sync_workspace( // ── File watcher ──────────────────────────────────────────────────── +#[cfg(not(target_os = "android"))] fn start_watcher(handle: tauri::AppHandle, path: PathBuf) { let handle = handle.clone(); let debouncer = new_debouncer( @@ -492,12 +500,19 @@ fn start_watcher(handle: tauri::AppHandle, path: PathBuf) { } } +#[cfg(not(target_os = "android"))] #[tauri::command] fn watch_workspace(path: String, app_handle: tauri::AppHandle) -> Result<(), String> { start_watcher(app_handle, PathBuf::from(path)); Ok(()) } +#[cfg(target_os = "android")] +#[tauri::command] +fn watch_workspace(_path: String, _app_handle: tauri::AppHandle) -> Result<(), String> { + Ok(()) +} + // ── App entry ──────────────────────────────────────────────────────── #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -517,6 +532,7 @@ pub fn run() { let s = state.lock().unwrap(); s.config.get_current_workspace().ok().map(|(_, ws)| ws.path.clone()) }; + #[cfg(not(target_os = "android"))] if let Some(path) = workspace_path { start_watcher(handle, path); } diff --git a/crates/onyx-core/Cargo.toml b/crates/onyx-core/Cargo.toml index 0100d0a..4949aec 100644 --- a/crates/onyx-core/Cargo.toml +++ b/crates/onyx-core/Cargo.toml @@ -6,6 +6,10 @@ description = "Core library for local-first task management with markdown storag license = "GPL-3.0-or-later" repository = "https://github.com/SteelDynamite/onyx" +[features] +default = ["keyring-storage"] +keyring-storage = ["keyring"] + [dependencies] serde = { workspace = true } serde_json = "1.0" @@ -17,7 +21,7 @@ reqwest = { workspace = true } sha2 = { workspace = true } quick-xml = { workspace = true } tokio = { workspace = true } -keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service"] } +keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service"], optional = true } [dev-dependencies] tempfile = "3.0" diff --git a/crates/onyx-core/src/webdav.rs b/crates/onyx-core/src/webdav.rs index fc852e5..3a3d532 100644 --- a/crates/onyx-core/src/webdav.rs +++ b/crates/onyx-core/src/webdav.rs @@ -379,6 +379,7 @@ fn extract_relative_path(href: &str, base_url: &str, request_path: &str) -> Stri // --- Credential Storage --- +#[cfg(feature = "keyring-storage")] /// Store WebDAV credentials in the platform keychain. pub fn store_credentials(domain: &str, username: &str, password: &str) -> Result<()> { let service = format!("com.onyx.webdav.{}", domain); @@ -396,6 +397,13 @@ pub fn store_credentials(domain: &str, username: &str, password: &str) -> Result Ok(()) } +#[cfg(not(feature = "keyring-storage"))] +/// Store WebDAV credentials (not available without keyring-storage feature). +pub fn store_credentials(_domain: &str, _username: &str, _password: &str) -> Result<()> { + Err(Error::Credential("Credential storage not available on this platform".into())) +} + +#[cfg(feature = "keyring-storage")] /// Load WebDAV credentials from the platform keychain, falling back to env vars. pub fn load_credentials(domain: &str) -> Result<(String, String)> { let service = format!("com.onyx.webdav.{}", domain); @@ -423,6 +431,23 @@ pub fn load_credentials(domain: &str) -> Result<(String, String)> { ))) } +#[cfg(not(feature = "keyring-storage"))] +/// Load WebDAV credentials from env vars only (keyring not available). +pub fn load_credentials(domain: &str) -> Result<(String, String)> { + if let (Ok(user), Ok(pass)) = ( + std::env::var("ONYX_WEBDAV_USER"), + std::env::var("ONYX_WEBDAV_PASS"), + ) { + return Ok((user, pass)); + } + + Err(Error::Credential(format!( + "No credentials found for '{}'. Set ONYX_WEBDAV_USER and ONYX_WEBDAV_PASS.", + domain + ))) +} + +#[cfg(feature = "keyring-storage")] /// Delete WebDAV credentials from the platform keychain. pub fn delete_credentials(domain: &str) -> Result<()> { let service = format!("com.onyx.webdav.{}", domain); @@ -437,6 +462,12 @@ pub fn delete_credentials(domain: &str) -> Result<()> { Ok(()) } +#[cfg(not(feature = "keyring-storage"))] +/// Delete WebDAV credentials (no-op without keyring-storage feature). +pub fn delete_credentials(_domain: &str) -> Result<()> { + Ok(()) +} + #[cfg(test)] mod tests { use super::*;