Skip to main content

fresh/services/plugins/
bridge.rs

1use crate::config_io::DirectoryContext;
2use crate::i18n;
3use crate::input::command_registry::CommandRegistry;
4use crate::services::signal_handler;
5use crate::view::theme;
6use fresh_core::services::PluginServiceBridge;
7use std::any::Any;
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::sync::{Arc, RwLock};
11
12pub struct EditorServiceBridge {
13    pub command_registry: Arc<RwLock<CommandRegistry>>,
14    pub dir_context: DirectoryContext,
15    pub theme_cache: Arc<RwLock<std::collections::HashMap<String, serde_json::Value>>>,
16}
17
18impl PluginServiceBridge for EditorServiceBridge {
19    fn as_any(&self) -> &dyn Any {
20        self
21    }
22
23    fn translate(&self, plugin_name: &str, key: &str, args: &HashMap<String, String>) -> String {
24        i18n::translate_plugin_string(plugin_name, key, args)
25    }
26
27    fn current_locale(&self) -> String {
28        i18n::current_locale()
29    }
30
31    fn set_js_execution_state(&self, state: String) {
32        signal_handler::set_js_execution_state(state);
33    }
34
35    fn clear_js_execution_state(&self) {
36        signal_handler::clear_js_execution_state();
37    }
38
39    fn get_theme_schema(&self) -> serde_json::Value {
40        theme::get_theme_schema()
41    }
42
43    fn get_builtin_themes(&self) -> serde_json::Value {
44        theme::get_builtin_themes()
45    }
46
47    fn register_plugin_strings(
48        &self,
49        plugin_name: &str,
50        strings: HashMap<String, HashMap<String, String>>,
51    ) {
52        i18n::register_plugin_strings(plugin_name, strings);
53    }
54
55    fn unregister_plugin_strings(&self, plugin_name: &str) {
56        i18n::unregister_plugin_strings(plugin_name);
57    }
58
59    fn register_command(&self, command: fresh_core::command::Command) {
60        // Convert fresh_core::command::Command to crate::input::commands::Command
61        use crate::input::commands::{Command as EditorCommand, CommandSource};
62        use crate::input::keybindings::{Action, KeyContext};
63
64        let editor_command = EditorCommand {
65            name: command.name,
66            description: command.description,
67            action: Action::PluginAction(command.action_name),
68            contexts: vec![KeyContext::Global],
69            custom_contexts: command.custom_contexts,
70            source: CommandSource::Plugin(command.plugin_name),
71        };
72        self.command_registry
73            .read()
74            .unwrap()
75            .register(editor_command);
76    }
77
78    fn unregister_command(&self, name: &str) {
79        self.command_registry.read().unwrap().unregister(name);
80    }
81
82    fn unregister_commands_by_prefix(&self, prefix: &str) {
83        self.command_registry
84            .read()
85            .unwrap()
86            .unregister_by_prefix(prefix);
87    }
88
89    fn unregister_commands_by_plugin(&self, plugin_name: &str) {
90        self.command_registry
91            .read()
92            .unwrap()
93            .unregister_by_plugin(plugin_name);
94    }
95
96    fn plugins_dir(&self) -> PathBuf {
97        self.dir_context.plugins_dir()
98    }
99
100    fn config_dir(&self) -> PathBuf {
101        self.dir_context.config_dir.clone()
102    }
103
104    fn get_theme_data(&self, key_or_name: &str) -> Option<serde_json::Value> {
105        let cache = self.theme_cache.read().unwrap();
106        // Exact key match
107        if let Some(v) = cache.get(key_or_name) {
108            return Some(v.clone());
109        }
110        // Fallback: match by theme name inside the cached values
111        let normalized = key_or_name.to_lowercase().replace(['_', ' '], "-");
112        cache
113            .values()
114            .find(|v| {
115                v.get("name")
116                    .and_then(|n| n.as_str())
117                    .is_some_and(|n| n.to_lowercase().replace(['_', ' '], "-") == normalized)
118            })
119            .cloned()
120    }
121
122    fn save_theme_file(&self, name: &str, content: &str) -> Result<String, String> {
123        let themes_dir = self.dir_context.themes_dir();
124        if !themes_dir.exists() {
125            std::fs::create_dir_all(&themes_dir).map_err(|e| e.to_string())?;
126        }
127        let path = themes_dir.join(format!("{}.json", name));
128        std::fs::write(&path, content).map_err(|e| e.to_string())?;
129        Ok(path.to_string_lossy().to_string())
130    }
131
132    fn theme_file_exists(&self, name: &str) -> bool {
133        let themes_dir = self.dir_context.themes_dir();
134        themes_dir.join(format!("{}.json", name)).exists()
135    }
136}