Skip to content

Commit 7a2ee07

Browse files
committed
Add applyTheme API and apply theme immediately after save
- Add PluginCommand::ApplyTheme variant to plugin API - Add op_fresh_apply_theme operation in Deno runtime - Update apply_theme() to also persist theme to config file - Update theme_editor.ts to use applyTheme API This allows the theme editor to apply theme changes immediately without requiring a restart.
1 parent 6bae578 commit 7a2ee07

File tree

7 files changed

+84
-29
lines changed

7 files changed

+84
-29
lines changed

docs/plugin-api.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,22 @@ setPromptSuggestions(suggestions: PromptSuggestion[]): boolean
567567

568568
### Buffer Mutations
569569

570+
#### `applyTheme`
571+
572+
Apply a theme by name
573+
Loads and applies the specified theme immediately. The theme can be a built-in
574+
theme name or a custom theme from the themes directory.
575+
576+
```typescript
577+
applyTheme(theme_name: string): void
578+
```
579+
580+
**Parameters:**
581+
582+
| Name | Type | Description |
583+
|------|------|-------------|
584+
| `theme_name` | `string` | Name of the theme to apply (e.g., "dark", "light", "my-custom-theme") |
585+
570586
#### `setClipboard`
571587

572588
Copy text to the system clipboard

plugins/lib/fresh.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,14 @@ interface EditorAPI {
395395
setPromptSuggestions(suggestions: PromptSuggestion[]): boolean;
396396

397397
// === Buffer Mutations ===
398+
/**
399+
* Apply a theme by name
400+
*
401+
* Loads and applies the specified theme immediately. The theme can be a built-in
402+
* theme name or a custom theme from the themes directory.
403+
* @param theme_name - Name of the theme to apply (e.g., "dark", "light", "my-custom-theme")
404+
*/
405+
applyTheme(theme_name: string): void;
398406
/**
399407
* Copy text to the system clipboard
400408
*

plugins/theme_editor.ts

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -993,37 +993,15 @@ async function saveTheme(name?: string): Promise<boolean> {
993993
}
994994

995995
/**
996-
* Set a theme as the default in config
996+
* Set a theme as the default in config and apply it immediately
997997
*/
998998
async function setThemeAsDefault(themeName: string): Promise<void> {
999-
// Find config file
1000-
const home = editor.getEnv("HOME");
1001-
let configPath = editor.pathJoin(editor.getCwd(), "config.json");
1002-
1003-
if (home) {
1004-
const userConfig = editor.pathJoin(home, ".config", "fresh", "config.json");
1005-
if (editor.fileExists(userConfig)) {
1006-
configPath = userConfig;
1007-
}
1008-
}
1009-
1010999
try {
1011-
let config: Record<string, unknown> = {};
1012-
1013-
if (editor.fileExists(configPath)) {
1014-
const content = await editor.readFile(configPath);
1015-
config = JSON.parse(content);
1016-
}
1017-
1018-
config.theme = themeName;
1019-
const content = JSON.stringify(config, null, 2);
1020-
await editor.writeFile(configPath, content);
1021-
1022-
// Note: The plugin API doesn't currently support applying themes at runtime.
1023-
// Users need to restart or use the Select Theme command (Ctrl+P -> "Select Theme")
1024-
editor.setStatus(`Default theme set to "${themeName}". Use 'Select Theme' command or restart to apply.`);
1000+
// Use the editor API to apply and persist the theme
1001+
editor.applyTheme(themeName);
1002+
editor.setStatus(`Theme "${themeName}" applied and saved as default.`);
10251003
} catch (e) {
1026-
editor.setStatus(`Failed to update config: ${e}`);
1004+
editor.setStatus(`Failed to apply theme: ${e}`);
10271005
}
10281006
}
10291007

src/app/input.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3611,14 +3611,44 @@ impl Editor {
36113611
}
36123612
}
36133613

3614-
/// Apply a theme by name
3615-
fn apply_theme(&mut self, theme_name: &str) {
3614+
/// Apply a theme by name and persist it to config
3615+
pub(super) fn apply_theme(&mut self, theme_name: &str) {
36163616
if !theme_name.is_empty() {
36173617
self.theme = crate::view::theme::Theme::from_name(theme_name);
3618+
3619+
// Update the config in memory
3620+
self.config.theme = self.theme.name.clone();
3621+
3622+
// Persist to config file
3623+
self.save_theme_to_config();
3624+
36183625
self.set_status_message(format!("Theme changed to '{}'", self.theme.name));
36193626
}
36203627
}
36213628

3629+
/// Save the current theme setting to the user's config file
3630+
fn save_theme_to_config(&mut self) {
3631+
// Find the config path
3632+
let config_dir = if let Some(home) = dirs::home_dir() {
3633+
home.join(".config").join("fresh")
3634+
} else {
3635+
std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."))
3636+
};
3637+
3638+
let config_path = config_dir.join("config.json");
3639+
3640+
// Create the directory if it doesn't exist
3641+
if let Err(e) = std::fs::create_dir_all(&config_dir) {
3642+
tracing::warn!("Failed to create config directory: {}", e);
3643+
return;
3644+
}
3645+
3646+
// Save the config
3647+
if let Err(e) = self.config.save_to_file(&config_path) {
3648+
tracing::warn!("Failed to save theme to config: {}", e);
3649+
}
3650+
}
3651+
36223652
/// Switch to the previously active tab in the current split
36233653
fn switch_to_previous_tab(&mut self) {
36243654
let active_split = self.split_manager.active_split();

src/app/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4516,6 +4516,9 @@ impl Editor {
45164516
PluginCommand::SetStatus { message } => {
45174517
self.handle_set_status(message);
45184518
}
4519+
PluginCommand::ApplyTheme { theme_name } => {
4520+
self.apply_theme(&theme_name);
4521+
}
45194522
PluginCommand::StartPrompt { label, prompt_type } => {
45204523
self.handle_start_prompt(label, prompt_type);
45214524
}

src/services/plugins/api.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ pub enum PluginCommand {
242242
/// Set status message
243243
SetStatus { message: String },
244244

245+
/// Apply a theme by name
246+
ApplyTheme { theme_name: String },
247+
245248
/// Register a custom command
246249
RegisterCommand { command: Command },
247250

src/services/plugins/runtime.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,22 @@ fn op_fresh_set_status(state: &mut OpState, #[string] message: String) {
178178
tracing::info!("TypeScript plugin set_status: {}", message);
179179
}
180180

181+
/// Apply a theme by name
182+
///
183+
/// Loads and applies the specified theme immediately. The theme can be a built-in
184+
/// theme name or a custom theme from the themes directory.
185+
/// @param theme_name - Name of the theme to apply (e.g., "dark", "light", "my-custom-theme")
186+
#[op2(fast)]
187+
fn op_fresh_apply_theme(state: &mut OpState, #[string] theme_name: String) {
188+
if let Some(runtime_state) = state.try_borrow::<Rc<RefCell<TsRuntimeState>>>() {
189+
let runtime_state = runtime_state.borrow();
190+
let _ = runtime_state.command_sender.send(PluginCommand::ApplyTheme {
191+
theme_name: theme_name.clone(),
192+
});
193+
}
194+
tracing::info!("TypeScript plugin apply_theme: {}", theme_name);
195+
}
196+
181197
/// Log a debug message to the editor's trace output
182198
///
183199
/// Messages appear in stderr when running with RUST_LOG=debug.
@@ -2625,6 +2641,7 @@ extension!(
26252641
fresh_runtime,
26262642
ops = [
26272643
op_fresh_set_status,
2644+
op_fresh_apply_theme,
26282645
op_fresh_debug,
26292646
op_fresh_set_clipboard,
26302647
op_fresh_get_active_buffer_id,

0 commit comments

Comments
 (0)