From 7b3609afbb0e7f987a2a6fb2f58006978d8bd65a Mon Sep 17 00:00:00 2001 From: blake Date: Tue, 23 Jun 2026 20:19:23 +0200 Subject: [PATCH] Added custom themes, import/export option with json (#159) Reviewed-on: https://codeberg.org/ArkHost/HelixNotes/pulls/159 --- src-tauri/my-theme.json | 21 + src-tauri/src/commands.rs | 52 ++ src-tauri/src/lib.rs | 4 + src-tauri/src/types.rs | 24 + src/lib/api.ts | 17 + src/lib/components/SettingsPanel.svelte | 696 +++++++++++++++++++++++- src/lib/stores/app.ts | 2 + src/lib/types.ts | 20 + src/routes/+layout.svelte | 40 +- 9 files changed, 866 insertions(+), 10 deletions(-) create mode 100644 src-tauri/my-theme.json diff --git a/src-tauri/my-theme.json b/src-tauri/my-theme.json new file mode 100644 index 0000000..bc5a14c --- /dev/null +++ b/src-tauri/my-theme.json @@ -0,0 +1,21 @@ +{ + "themes": [ + { + "colors": { + "bg_active": "#373a52", + "bg_editor": "#1a1b26", + "bg_hover": "#2f3145", + "bg_primary": "#1a1b26", + "bg_secondary": "#1f2029", + "bg_tertiary": "#2a2b3d", + "border_color": "#2f3145", + "text_primary": "#c8cde4", + "text_secondary": "#6b7094" + }, + "id": "custom-1782146306429", + "is_dark": true, + "name": "My theme" + } + ], + "version": 1 +} \ No newline at end of file diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 72639ad..04731b9 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -90,6 +90,58 @@ pub fn set_accent_color(state: State<'_, AppState>, color: String) -> Result<(), Ok(()) } +#[tauri::command] +pub fn save_custom_theme(state: State<'_, AppState>, theme: crate::types::CustomTheme) -> Result<(), String> { + let mut config = state.config.lock().map_err(|e| e.to_string())?; + if let Some(pos) = config.custom_themes.iter().position(|t| t.id == theme.id) { + config.custom_themes[pos] = theme; + } else { + config.custom_themes.push(theme); + } + save_app_config(&config)?; + Ok(()) +} + +#[tauri::command] +pub fn delete_custom_theme(state: State<'_, AppState>, id: String) -> Result<(), String> { + let mut config = state.config.lock().map_err(|e| e.to_string())?; + config.custom_themes.retain(|t| t.id != id); + if config.theme == id { + config.theme = "system".to_string(); + } + save_app_config(&config)?; + Ok(()) +} + +#[tauri::command] +pub fn export_custom_theme(state: State<'_, AppState>, id: String, path: String) -> Result<(), String> { + let config = state.config.lock().map_err(|e| e.to_string())?; + let theme = config.custom_themes.iter().find(|t| t.id == id) + .ok_or_else(|| "Theme not found".to_string())?; + let export = serde_json::json!({ "version": 1, "themes": [theme] }); + let data = serde_json::to_string_pretty(&export).map_err(|e| e.to_string())?; + std::fs::write(&path, data).map_err(|e| e.to_string())?; + Ok(()) +} + +#[tauri::command] +pub fn import_custom_themes(state: State<'_, AppState>, path: String) -> Result, String> { + let data = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; + let parsed: serde_json::Value = serde_json::from_str(&data).map_err(|e| e.to_string())?; + let themes: Vec = serde_json::from_value(parsed["themes"].clone()) + .map_err(|e| format!("Invalid theme file: {}", e))?; + let mut config = state.config.lock().map_err(|e| e.to_string())?; + for theme in &themes { + if let Some(pos) = config.custom_themes.iter().position(|t| t.id == theme.id) { + config.custom_themes[pos] = theme.clone(); + } else { + config.custom_themes.push(theme.clone()); + } + } + save_app_config(&config)?; + Ok(themes) +} + #[tauri::command] pub fn set_font_size(state: State<'_, AppState>, size: u32) -> Result<(), String> { let mut config = state.config.lock().map_err(|e| e.to_string())?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 63ca13b..7a21e86 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -116,6 +116,10 @@ pub fn run() { commands::get_app_config, commands::set_theme, commands::set_accent_color, + commands::save_custom_theme, + commands::delete_custom_theme, + commands::export_custom_theme, + commands::import_custom_themes, commands::set_font_size, commands::set_font_family, commands::set_line_height, diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index ac0f8f2..a49cd84 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -56,6 +56,27 @@ pub struct VaultConfig { pub name: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CustomThemeColors { + pub bg_primary: String, + pub bg_secondary: String, + pub bg_tertiary: String, + pub bg_hover: String, + pub bg_active: String, + pub bg_editor: String, + pub text_primary: String, + pub text_secondary: String, + pub border_color: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CustomTheme { + pub id: String, + pub name: String, + pub is_dark: bool, + pub colors: CustomThemeColors, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { pub vaults: Vec, @@ -168,6 +189,8 @@ pub struct AppConfig { pub sync_interval_minutes: u32, #[serde(default)] pub last_sync_time: Option, + #[serde(default)] + pub custom_themes: Vec, } fn default_true() -> bool { @@ -265,6 +288,7 @@ impl Default for AppConfig { sync_on_change: false, sync_interval_minutes: 0, last_sync_time: None, + custom_themes: Vec::new(), } } } diff --git a/src/lib/api.ts b/src/lib/api.ts index 86677d4..2b45857 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,6 +1,7 @@ import { invoke } from "@tauri-apps/api/core"; import type { AppConfig, + CustomTheme, NoteContent, NoteEntry, NoteMeta, @@ -36,6 +37,22 @@ export async function setAccentColor(color: string): Promise { return invoke("set_accent_color", { color }); } +export async function saveCustomTheme(theme: CustomTheme): Promise { + return invoke("save_custom_theme", { theme }); +} + +export async function deleteCustomTheme(id: string): Promise { + return invoke("delete_custom_theme", { id }); +} + +export async function exportCustomTheme(id: string, path: string): Promise { + return invoke("export_custom_theme", { id, path }); +} + +export async function importCustomThemes(path: string): Promise { + return invoke("import_custom_themes", { path }); +} + export async function setFontSize(size: number): Promise { return invoke("set_font_size", { size }); } diff --git a/src/lib/components/SettingsPanel.svelte b/src/lib/components/SettingsPanel.svelte index 445c972..0670b72 100644 --- a/src/lib/components/SettingsPanel.svelte +++ b/src/lib/components/SettingsPanel.svelte @@ -1,13 +1,13 @@