diff --git a/src/dashboard/config.rs b/src/dashboard/config.rs index b139dd8b..217d9fe4 100644 --- a/src/dashboard/config.rs +++ b/src/dashboard/config.rs @@ -118,7 +118,7 @@ impl Default for DashboardConfig { slots: vec![ SlotConfig::with_widget("weather_site", 0, 0), SlotConfig::with_widget("pinned_commands", 0, 1), - SlotConfig::with_widget("todos", 0, 2), + SlotConfig::with_widget("todo", 0, 2), SlotConfig::with_widget("recent_commands", 1, 0), SlotConfig::with_widget("frequent_commands", 1, 1), SlotConfig::with_widget("recent_notes", 1, 2), @@ -190,16 +190,16 @@ impl DashboardConfig { fn migrate_todo_widgets(&mut self, registry: &WidgetRegistry, warnings: &mut Vec) { for slot in &mut self.slots { - let Some(default_settings) = registry.default_settings("todos") else { + let Some(default_settings) = registry.default_settings("todo") else { continue; }; match slot.widget.as_str() { "todo" | "todo_list" | "todo_summary" | "todo_burndown" => { let legacy_name = slot.widget.clone(); - slot.widget = "todos".into(); + slot.widget = "todo".into(); slot.settings = merge_json(&default_settings, &slot.settings); warnings.push(format!( - "dashboard widget '{}' migrated to 'todos'", + "dashboard widget '{}' migrated to 'todo'", legacy_name )); } diff --git a/src/dashboard/dashboard.rs b/src/dashboard/dashboard.rs index 08f841a9..7f088349 100644 --- a/src/dashboard/dashboard.rs +++ b/src/dashboard/dashboard.rs @@ -33,6 +33,7 @@ pub struct DashboardContext<'a> { pub actions_by_id: &'a std::collections::HashMap, pub usage: &'a std::collections::HashMap, pub plugins: &'a crate::plugin::PluginManager, + pub enabled_plugins: Option<&'a std::collections::HashSet>, pub default_location: Option<&'a str>, pub data_cache: &'a DashboardDataCache, pub actions_version: u64, @@ -489,6 +490,7 @@ mod tests { actions_by_id: &EMPTY_ACTIONS_BY_ID, usage: &EMPTY_USAGE, plugins, + enabled_plugins: None, default_location: None, data_cache, actions_version: 0, diff --git a/src/dashboard/widgets/mod.rs b/src/dashboard/widgets/mod.rs index e6fd5644..16d59d05 100644 --- a/src/dashboard/widgets/mod.rs +++ b/src/dashboard/widgets/mod.rs @@ -4,7 +4,7 @@ use crate::plugin::PluginManager; use eframe::egui; use serde::{de::DeserializeOwned, Serialize}; use serde_json::{json, Value}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::time::{Duration, Instant}; mod active_timers; @@ -80,6 +80,7 @@ pub struct WidgetSettingsContext<'a> { pub actions: Option<&'a [Action]>, pub usage: Option<&'a std::collections::HashMap>, pub default_location: Option<&'a str>, + pub enabled_plugins: Option<&'a HashSet>, } impl<'a> WidgetSettingsContext<'a> { @@ -91,6 +92,7 @@ impl<'a> WidgetSettingsContext<'a> { actions: None, usage: None, default_location: None, + enabled_plugins: None, } } } @@ -208,7 +210,7 @@ impl WidgetRegistry { .with_settings_ui(FrequentCommandsWidget::settings_ui), ); reg.register( - "todos", + "todo", WidgetFactory::new(TodoWidget::new).with_settings_ui(TodoWidget::settings_ui), ); reg.register( @@ -383,13 +385,24 @@ pub(crate) fn merge_json(base: &Value, updates: &Value) -> Value { pub(crate) fn plugin_names(ctx: &WidgetSettingsContext<'_>) -> Vec { if let Some(infos) = ctx.plugin_infos { - let mut names: Vec = infos.iter().map(|(name, _, _)| name.clone()).collect(); + let mut names: Vec = infos + .iter() + .filter(|(name, _, _)| { + ctx.enabled_plugins + .map(|set| set.contains(name)) + .unwrap_or(true) + }) + .map(|(name, _, _)| name.clone()) + .collect(); names.sort(); names.dedup(); return names; } if let Some(manager) = ctx.plugins { let mut names = manager.plugin_names(); + if let Some(enabled) = ctx.enabled_plugins { + names.retain(|name| enabled.contains(name)); + } names.sort(); names } else { diff --git a/src/dashboard/widgets/pinned_query_results.rs b/src/dashboard/widgets/pinned_query_results.rs index 922720bd..2d151b3f 100644 --- a/src/dashboard/widgets/pinned_query_results.rs +++ b/src/dashboard/widgets/pinned_query_results.rs @@ -91,24 +91,76 @@ impl PinnedQueryResultsWidget { |ui, cfg: &mut PinnedQueryResultsConfig, ctx| { let mut changed = false; - let mut engines = plugin_names(ctx); - if !engines.iter().any(|e| e == "omni_search") { - engines.insert(0, "omni_search".into()); - } - if cfg.engine.trim().is_empty() { - cfg.engine = default_engine(); + let all_engines = if let Some(infos) = ctx.plugin_infos { + infos.iter().map(|(name, _, _)| name.clone()).collect() + } else if let Some(manager) = ctx.plugins { + manager.plugin_names() + } else { + Vec::new() + }; + let engines = plugin_names(ctx); + let original_engine = cfg.engine.trim().to_string(); + let mut warning = None; + + if original_engine.is_empty() { + if let Some(first) = engines.first() { + cfg.engine = first.clone(); + } else { + cfg.engine = default_engine(); + } changed = true; + } else if let Some(enabled) = ctx.enabled_plugins { + if !enabled.contains(original_engine.as_str()) { + if let Some(first) = engines.first() { + if first != &original_engine { + cfg.engine = first.clone(); + changed = true; + warning = Some(format!( + "Engine '{original_engine}' is disabled in plugin settings. Using '{first}'.", + )); + } else { + warning = Some(format!( + "Engine '{original_engine}' is disabled in plugin settings.", + )); + } + } else { + warning = Some(format!( + "Engine '{original_engine}' is disabled in plugin settings.", + )); + } + } + } else if !all_engines.iter().any(|name| name == &original_engine) { + if let Some(first) = engines.first() { + cfg.engine = first.clone(); + changed = true; + warning = Some(format!( + "Engine '{original_engine}' is not available. Using '{first}'.", + )); + } else { + warning = Some(format!("Engine '{original_engine}' is not available.")); + } } - egui::ComboBox::from_label("Engine") - .selected_text(&cfg.engine) - .show_ui(ui, |ui| { - for engine in &engines { - changed |= ui - .selectable_value(&mut cfg.engine, engine.clone(), engine) - .changed(); - } - }); + if engines.is_empty() { + ui.colored_label( + egui::Color32::YELLOW, + "No enabled engines available.", + ); + } else { + egui::ComboBox::from_label("Engine") + .selected_text(&cfg.engine) + .show_ui(ui, |ui| { + for engine in &engines { + changed |= ui + .selectable_value(&mut cfg.engine, engine.clone(), engine) + .changed(); + } + }); + } + + if let Some(warn) = warning { + ui.colored_label(egui::Color32::YELLOW, warn); + } let prefix_candidates = Self::suggestion_prefixes(&cfg.engine); let prefix_refs: Vec<&str> = prefix_candidates.iter().map(|s| s.as_str()).collect(); @@ -218,6 +270,16 @@ impl PinnedQueryResultsWidget { } let engine_name = self.cfg.engine.trim(); + if let Some(enabled) = ctx.enabled_plugins { + if !enabled.contains(engine_name) { + return ( + Vec::new(), + Some(format!( + "Engine '{engine_name}' is disabled in plugin settings." + )), + ); + } + } let Some(plugin) = find_plugin(ctx, engine_name) else { return ( Vec::new(), diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 0674eeb1..1d9abe3b 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -2815,6 +2815,7 @@ impl eframe::App for LauncherApp { actions_by_id: &self.actions_by_id, usage: &self.usage, plugins: &self.plugins, + enabled_plugins: self.enabled_plugins.as_ref(), default_location: self.dashboard_default_location.as_deref(), data_cache: &self.dashboard_data_cache, actions_version: crate::actions::actions_version(), @@ -3270,6 +3271,7 @@ impl eframe::App for LauncherApp { actions: Some(self.actions.as_slice()), usage: Some(&self.usage), default_location: self.dashboard_default_location.as_deref(), + enabled_plugins: self.enabled_plugins.as_ref(), }; let reload = dlg.ui(ctx, ®istry, settings_ctx); self.show_dashboard_editor = dlg.open;