diff --git a/Cargo.lock b/Cargo.lock index b6ffa1b..67b5705 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -971,6 +971,7 @@ dependencies = [ "abi_stable", "dirs", "owlry-plugin-api", + "toml", ] [[package]] @@ -1007,7 +1008,9 @@ name = "owlry-plugin-websearch" version = "1.0.0" dependencies = [ "abi_stable", + "dirs", "owlry-plugin-api", + "toml", ] [[package]] diff --git a/crates/owlry-plugin-calculator/src/lib.rs b/crates/owlry-plugin-calculator/src/lib.rs index 359d32e..7288fe7 100644 --- a/crates/owlry-plugin-calculator/src/lib.rs +++ b/crates/owlry-plugin-calculator/src/lib.rs @@ -136,7 +136,7 @@ fn evaluate_expression(expr: &str) -> Option { PluginItem::new( format!("calc:{}", expr), result_str.clone(), - format!("sh -c 'echo -n \"{}\" | wl-copy'", result_str), + format!("printf '%s' '{}' | wl-copy", result_str.replace('\'', "'\\''")), ) .with_description(format!("= {}", expr)) .with_icon(PROVIDER_ICON) diff --git a/crates/owlry-plugin-converter/src/lib.rs b/crates/owlry-plugin-converter/src/lib.rs index 139e40a..e7df754 100644 --- a/crates/owlry-plugin-converter/src/lib.rs +++ b/crates/owlry-plugin-converter/src/lib.rs @@ -92,7 +92,7 @@ extern "C" fn provider_query(_handle: ProviderHandle, query: RStr<'_>) -> RVec

Self { - Self { items: Vec::new() } + let mut state = Self { items: Vec::new() }; + state.load_emojis(); + state } fn load_emojis(&mut self) { @@ -471,12 +473,9 @@ extern "C" fn provider_refresh(handle: ProviderHandle) -> RVec { } // SAFETY: We created this handle from Box - let state = unsafe { &mut *(handle.ptr as *mut EmojiState) }; + let state = unsafe { &*(handle.ptr as *const EmojiState) }; - // Load emojis - state.load_emojis(); - - // Return items + // Return cached items (loaded once at init) state.items.to_vec().into() } @@ -515,20 +514,15 @@ mod tests { #[test] fn test_emoji_state_new() { let state = EmojiState::new(); - assert!(state.items.is_empty()); - } - - #[test] - fn test_emoji_count() { - let mut state = EmojiState::new(); - state.load_emojis(); - assert!(state.items.len() > 100, "Should have more than 100 emojis"); + assert!( + state.items.len() > 100, + "Should have more than 100 emojis loaded at init" + ); } #[test] fn test_emoji_has_grinning_face() { - let mut state = EmojiState::new(); - state.load_emojis(); + let state = EmojiState::new(); let grinning = state .items @@ -542,8 +536,7 @@ mod tests { #[test] fn test_emoji_command_format() { - let mut state = EmojiState::new(); - state.load_emojis(); + let state = EmojiState::new(); let item = &state.items[0]; assert!(item.command.as_str().contains("wl-copy")); @@ -552,8 +545,7 @@ mod tests { #[test] fn test_emojis_have_keywords() { - let mut state = EmojiState::new(); - state.load_emojis(); + let state = EmojiState::new(); // Check that items have keywords for searching let heart = state.items.iter().find(|i| i.name.as_str() == "red heart"); diff --git a/crates/owlry-plugin-ssh/Cargo.toml b/crates/owlry-plugin-ssh/Cargo.toml index b6bdb47..9bf9194 100644 --- a/crates/owlry-plugin-ssh/Cargo.toml +++ b/crates/owlry-plugin-ssh/Cargo.toml @@ -21,3 +21,6 @@ abi_stable = "0.11" # For finding ~/.ssh/config dirs = "5.0" + +# For reading owlry config.toml +toml = "0.8" diff --git a/crates/owlry-plugin-ssh/src/lib.rs b/crates/owlry-plugin-ssh/src/lib.rs index 26edf99..da62711 100644 --- a/crates/owlry-plugin-ssh/src/lib.rs +++ b/crates/owlry-plugin-ssh/src/lib.rs @@ -28,9 +28,6 @@ const PROVIDER_PREFIX: &str = ":ssh"; const PROVIDER_ICON: &str = "utilities-terminal"; const PROVIDER_TYPE_ID: &str = "ssh"; -// Default terminal command (TODO: make configurable via plugin config) -const DEFAULT_TERMINAL: &str = "kitty"; - /// SSH provider state - holds cached items struct SshState { items: Vec, @@ -39,15 +36,36 @@ struct SshState { impl SshState { fn new() -> Self { - // Try to detect terminal from environment, fall back to default - let terminal = std::env::var("TERMINAL").unwrap_or_else(|_| DEFAULT_TERMINAL.to_string()); - + let terminal = Self::load_terminal_from_config(); Self { items: Vec::new(), terminal_command: terminal, } } + fn load_terminal_from_config() -> String { + // Try [plugins.ssh] in config.toml + let config_path = dirs::config_dir().map(|d| d.join("owlry").join("config.toml")); + if let Some(content) = config_path.and_then(|p| fs::read_to_string(p).ok()) + && let Ok(toml) = content.parse::() + { + if let Some(plugins) = toml.get("plugins").and_then(|v| v.as_table()) + && let Some(ssh) = plugins.get("ssh").and_then(|v| v.as_table()) + && let Some(terminal) = ssh.get("terminal").and_then(|v| v.as_str()) + { + return terminal.to_string(); + } + } + + // Fall back to $TERMINAL env var + if let Ok(terminal) = std::env::var("TERMINAL") { + return terminal; + } + + // Last resort + "xdg-terminal-exec".to_string() + } + fn ssh_config_path() -> Option { dirs::home_dir().map(|h| h.join(".ssh").join("config")) } diff --git a/crates/owlry-plugin-websearch/Cargo.toml b/crates/owlry-plugin-websearch/Cargo.toml index 5b781fb..a70aada 100644 --- a/crates/owlry-plugin-websearch/Cargo.toml +++ b/crates/owlry-plugin-websearch/Cargo.toml @@ -18,3 +18,7 @@ owlry-plugin-api = { git = "https://somegit.dev/Owlibou/owlry.git", tag = "plugi # ABI-stable types (re-exported from owlry-plugin-api, but needed for RString etc) abi_stable = "0.11" + +# For reading owlry config.toml +toml = "0.8" +dirs = "5.0" diff --git a/crates/owlry-plugin-websearch/src/lib.rs b/crates/owlry-plugin-websearch/src/lib.rs index 8de6b85..d0b5850 100644 --- a/crates/owlry-plugin-websearch/src/lib.rs +++ b/crates/owlry-plugin-websearch/src/lib.rs @@ -13,6 +13,7 @@ use owlry_plugin_api::{ API_VERSION, PluginInfo, PluginItem, ProviderHandle, ProviderInfo, ProviderKind, ProviderPosition, owlry_plugin, }; +use std::fs; // Plugin metadata const PLUGIN_ID: &str = "websearch"; @@ -143,6 +144,21 @@ impl WebSearchState { } } +fn load_engine_from_config() -> String { + let config_path = dirs::config_dir().map(|d| d.join("owlry").join("config.toml")); + if let Some(content) = config_path.and_then(|p| fs::read_to_string(p).ok()) + && let Ok(toml) = content.parse::() + { + if let Some(plugins) = toml.get("plugins").and_then(|v| v.as_table()) + && let Some(websearch) = plugins.get("websearch").and_then(|v| v.as_table()) + && let Some(engine) = websearch.get("engine").and_then(|v| v.as_str()) + { + return engine.to_string(); + } + } + DEFAULT_ENGINE.to_string() +} + // ============================================================================ // Plugin Interface Implementation // ============================================================================ @@ -172,8 +188,8 @@ extern "C" fn plugin_providers() -> RVec { } extern "C" fn provider_init(_provider_id: RStr<'_>) -> ProviderHandle { - // TODO: Read search engine from config when plugin config is available - let state = Box::new(WebSearchState::new()); + let engine = load_engine_from_config(); + let state = Box::new(WebSearchState::with_engine(&engine)); ProviderHandle::from_box(state) }