From 987598a47c9415d1e37eaf6a9f27a02e9b89e60b Mon Sep 17 00:00:00 2001 From: Hikari Date: Fri, 6 Mar 2026 23:50:22 -0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20community=20preset=20themes=20(Dr?= =?UTF-8?q?acula,=20Catppuccin,=20Nord,=20Solarized,=20Gruvbox,=20Ros?= =?UTF-8?q?=C3=A9=20Pine)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/config.rs | 89 +++++- src/app.css | 392 ++++++++++++++++++++++++ src/lib/components/ConfigSidebar.svelte | 98 +++++- src/lib/stores/config.test.ts | 56 ++++ src/lib/stores/config.ts | 14 +- 5 files changed, 635 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index cfb4f7f..0a6b8ef 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -258,6 +258,18 @@ pub enum Theme { #[serde(rename = "high-contrast")] HighContrast, Custom, + Dracula, + Catppuccin, + Nord, + Solarized, + #[serde(rename = "solarized-light")] + SolarizedLight, + #[serde(rename = "catppuccin-latte")] + CatppuccinLatte, + #[serde(rename = "gruvbox-light")] + GruvboxLight, + #[serde(rename = "rose-pine-dawn")] + RosePineDawn, } #[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] @@ -381,18 +393,77 @@ mod tests { #[test] fn test_theme_serialization() { - let dark = Theme::Dark; - let light = Theme::Light; - let high_contrast = Theme::HighContrast; - - assert_eq!(serde_json::to_string(&dark).unwrap(), "\"dark\""); - assert_eq!(serde_json::to_string(&light).unwrap(), "\"light\""); + assert_eq!(serde_json::to_string(&Theme::Dark).unwrap(), "\"dark\""); + assert_eq!(serde_json::to_string(&Theme::Light).unwrap(), "\"light\""); assert_eq!( - serde_json::to_string(&high_contrast).unwrap(), + serde_json::to_string(&Theme::HighContrast).unwrap(), "\"high-contrast\"" ); + assert_eq!(serde_json::to_string(&Theme::Custom).unwrap(), "\"custom\""); + assert_eq!( + serde_json::to_string(&Theme::Dracula).unwrap(), + "\"dracula\"" + ); + assert_eq!( + serde_json::to_string(&Theme::Catppuccin).unwrap(), + "\"catppuccin\"" + ); + assert_eq!(serde_json::to_string(&Theme::Nord).unwrap(), "\"nord\""); + assert_eq!( + serde_json::to_string(&Theme::Solarized).unwrap(), + "\"solarized\"" + ); + assert_eq!( + serde_json::to_string(&Theme::SolarizedLight).unwrap(), + "\"solarized-light\"" + ); + assert_eq!( + serde_json::to_string(&Theme::CatppuccinLatte).unwrap(), + "\"catppuccin-latte\"" + ); + assert_eq!( + serde_json::to_string(&Theme::GruvboxLight).unwrap(), + "\"gruvbox-light\"" + ); + assert_eq!( + serde_json::to_string(&Theme::RosePineDawn).unwrap(), + "\"rose-pine-dawn\"" + ); + } - let custom = Theme::Custom; - assert_eq!(serde_json::to_string(&custom).unwrap(), "\"custom\""); + #[test] + fn test_theme_deserialization() { + assert_eq!( + serde_json::from_str::("\"dracula\"").unwrap(), + Theme::Dracula + ); + assert_eq!( + serde_json::from_str::("\"catppuccin\"").unwrap(), + Theme::Catppuccin + ); + assert_eq!( + serde_json::from_str::("\"nord\"").unwrap(), + Theme::Nord + ); + assert_eq!( + serde_json::from_str::("\"solarized\"").unwrap(), + Theme::Solarized + ); + assert_eq!( + serde_json::from_str::("\"solarized-light\"").unwrap(), + Theme::SolarizedLight + ); + assert_eq!( + serde_json::from_str::("\"catppuccin-latte\"").unwrap(), + Theme::CatppuccinLatte + ); + assert_eq!( + serde_json::from_str::("\"gruvbox-light\"").unwrap(), + Theme::GruvboxLight + ); + assert_eq!( + serde_json::from_str::("\"rose-pine-dawn\"").unwrap(), + Theme::RosePineDawn + ); } } diff --git a/src/app.css b/src/app.css index 88bf299..a4f01e0 100644 --- a/src/app.css +++ b/src/app.css @@ -148,6 +148,398 @@ --hljs-meta: #cccccc; } +[data-theme="dracula"] { + --bg-primary: #282a36; + --bg-secondary: #1e1f29; + --bg-terminal: #191a21; + --bg-hover: #44475a; + --bg-code: #282a36; + --accent-primary: #bd93f9; + --accent-secondary: #ff79c6; + --text-primary: #f8f8f2; + --text-secondary: #6272a4; + --text-tertiary: #44475a; + --border-color: #44475a; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #8be9fd; + --terminal-tool: #bd93f9; + --terminal-tool-name: #caa9fa; + --terminal-error: #ff5555; + + /* Syntax highlighting colors (Dracula) */ + --hljs-keyword: #ff79c6; + --hljs-string: #f1fa8c; + --hljs-number: #bd93f9; + --hljs-comment: #6272a4; + --hljs-function: #50fa7b; + --hljs-type: #8be9fd; + --hljs-variable: #ffb86c; + --hljs-meta: #94a3b8; +} + +[data-theme="catppuccin"] { + --bg-primary: #1e1e2e; + --bg-secondary: #181825; + --bg-terminal: #11111b; + --bg-hover: #313244; + --bg-code: #1e1e2e; + --accent-primary: #cba6f7; + --accent-secondary: #f5c2e7; + --text-primary: #cdd6f4; + --text-secondary: #a6adc8; + --text-tertiary: #6c7086; + --border-color: #313244; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #89dceb; + --terminal-tool: #cba6f7; + --terminal-tool-name: #d9b3ff; + --terminal-error: #f38ba8; + + /* Syntax highlighting colors (Catppuccin Mocha) */ + --hljs-keyword: #cba6f7; + --hljs-string: #a6e3a1; + --hljs-number: #fab387; + --hljs-comment: #6c7086; + --hljs-function: #89b4fa; + --hljs-type: #89dceb; + --hljs-variable: #fab387; + --hljs-meta: #a6adc8; +} + +[data-theme="nord"] { + --bg-primary: #2e3440; + --bg-secondary: #3b4252; + --bg-terminal: #242933; + --bg-hover: #434c5e; + --bg-code: #2e3440; + --accent-primary: #88c0d0; + --accent-secondary: #81a1c1; + --text-primary: #eceff4; + --text-secondary: #d8dee9; + --text-tertiary: #4c566a; + --border-color: #434c5e; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #88c0d0; + --terminal-tool: #b48ead; + --terminal-tool-name: #c7a8c9; + --terminal-error: #bf616a; + + /* Syntax highlighting colors (Nord) */ + --hljs-keyword: #81a1c1; + --hljs-string: #a3be8c; + --hljs-number: #b48ead; + --hljs-comment: #4c566a; + --hljs-function: #88c0d0; + --hljs-type: #8fbcbb; + --hljs-variable: #d08770; + --hljs-meta: #616e88; +} + +[data-theme="solarized"] { + --bg-primary: #002b36; + --bg-secondary: #073642; + --bg-terminal: #00212b; + --bg-hover: #094656; + --bg-code: #002b36; + --accent-primary: #268bd2; + --accent-secondary: #2aa198; + --text-primary: #fdf6e3; + --text-secondary: #93a1a1; + --text-tertiary: #657b83; + --border-color: #094656; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #2aa198; + --terminal-tool: #6c71c4; + --terminal-tool-name: #9395d0; + --terminal-error: #dc322f; + + /* Syntax highlighting colors (Solarized Dark) */ + --hljs-keyword: #859900; + --hljs-string: #2aa198; + --hljs-number: #d33682; + --hljs-comment: #586e75; + --hljs-function: #268bd2; + --hljs-type: #b58900; + --hljs-variable: #cb4b16; + --hljs-meta: #657b83; +} + +[data-theme="solarized-light"] { + --bg-primary: #fdf6e3; + --bg-secondary: #eee8d5; + --bg-terminal: #f9f3d7; + --bg-hover: #d8d1be; + --bg-code: #eee8d5; + --accent-primary: #268bd2; + --accent-secondary: #2aa198; + --text-primary: #657b83; + --text-secondary: #839496; + --text-tertiary: #93a1a1; + --border-color: #cfc9b5; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #268bd2; + --terminal-tool: #6c71c4; + --terminal-tool-name: #8f94cc; + --terminal-error: #dc322f; + + /* Syntax highlighting colors (Solarized Light) */ + --hljs-keyword: #859900; + --hljs-string: #2aa198; + --hljs-number: #d33682; + --hljs-comment: #93a1a1; + --hljs-function: #268bd2; + --hljs-type: #b58900; + --hljs-variable: #cb4b16; + --hljs-meta: #657b83; +} + +[data-theme="catppuccin-latte"] { + --bg-primary: #eff1f5; + --bg-secondary: #e6e9ef; + --bg-terminal: #dce0e8; + --bg-hover: #ccd0da; + --bg-code: #e6e9ef; + --accent-primary: #8839ef; + --accent-secondary: #ea76cb; + --text-primary: #4c4f69; + --text-secondary: #6c6f85; + --text-tertiary: #9ca0b0; + --border-color: #bcc0cc; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #209fb5; + --terminal-tool: #8839ef; + --terminal-tool-name: #a259f1; + --terminal-error: #d20f39; + + /* Syntax highlighting colors (Catppuccin Latte) */ + --hljs-keyword: #8839ef; + --hljs-string: #40a02b; + --hljs-number: #fe640b; + --hljs-comment: #8c8fa1; + --hljs-function: #1e66f5; + --hljs-type: #209fb5; + --hljs-variable: #fe640b; + --hljs-meta: #5c5f77; +} + +[data-theme="gruvbox-light"] { + --bg-primary: #fbf1c7; + --bg-secondary: #ebdbb2; + --bg-terminal: #f9f5d7; + --bg-hover: #d5c4a1; + --bg-code: #ebdbb2; + --accent-primary: #458588; + --accent-secondary: #689d6a; + --text-primary: #3c3836; + --text-secondary: #665c54; + --text-tertiary: #7c6f64; + --border-color: #bdae93; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #458588; + --terminal-tool: #b16286; + --terminal-tool-name: #c37aa0; + --terminal-error: #cc241d; + + /* Syntax highlighting colors (Gruvbox Light) */ + --hljs-keyword: #d65d0e; + --hljs-string: #98971a; + --hljs-number: #b16286; + --hljs-comment: #928374; + --hljs-function: #458588; + --hljs-type: #d79921; + --hljs-variable: #af3a03; + --hljs-meta: #7c6f64; +} + +[data-theme="rose-pine-dawn"] { + --bg-primary: #faf4ed; + --bg-secondary: #fffaf3; + --bg-terminal: #f2e9e1; + --bg-hover: #dfdad9; + --bg-code: #fffaf3; + --accent-primary: #907aa9; + --accent-secondary: #d7827e; + --text-primary: #575279; + --text-secondary: #797593; + --text-tertiary: #9893a5; + --border-color: #cecacd; + + /* Trans pride colors */ + --trans-blue: #5bcefa; + --trans-pink: #f5a9b8; + --trans-white: #ffffff; + --trans-gradient: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 50%, + var(--trans-white) 100% + ); + --trans-gradient-vibrant: linear-gradient( + 135deg, + var(--trans-blue) 0%, + var(--trans-pink) 35%, + var(--trans-white) 50%, + var(--trans-pink) 65%, + var(--trans-blue) 100% + ); + + /* Terminal specific colors */ + --terminal-user: #56949f; + --terminal-tool: #907aa9; + --terminal-tool-name: #a48abf; + --terminal-error: #b4637a; + + /* Syntax highlighting colors (Rosé Pine Dawn) */ + --hljs-keyword: #286983; + --hljs-string: #56949f; + --hljs-number: #ea9d34; + --hljs-comment: #9893a5; + --hljs-function: #907aa9; + --hljs-type: #d7827e; + --hljs-variable: #b4637a; + --hljs-meta: #797593; +} + html, body { margin: 0; diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index 1328fbb..4be0c1e 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -730,7 +730,7 @@
+ + + Dark Presets +
+ + + + +
+ + + Light Presets +
+ + + + +
diff --git a/src/lib/stores/config.test.ts b/src/lib/stores/config.test.ts index a96a5d7..e2aee51 100644 --- a/src/lib/stores/config.test.ts +++ b/src/lib/stores/config.test.ts @@ -337,6 +337,62 @@ describe("config store", () => { expect(document.documentElement.getAttribute("data-theme")).toBe("dark"); }); + it("sets data-theme attribute for dracula theme", () => { + applyTheme("dracula"); + expect(document.documentElement.getAttribute("data-theme")).toBe("dracula"); + }); + + it("sets data-theme attribute for catppuccin theme", () => { + applyTheme("catppuccin"); + expect(document.documentElement.getAttribute("data-theme")).toBe("catppuccin"); + }); + + it("sets data-theme attribute for nord theme", () => { + applyTheme("nord"); + expect(document.documentElement.getAttribute("data-theme")).toBe("nord"); + }); + + it("sets data-theme attribute for solarized theme", () => { + applyTheme("solarized"); + expect(document.documentElement.getAttribute("data-theme")).toBe("solarized"); + }); + + it("sets data-theme attribute for solarized-light theme", () => { + applyTheme("solarized-light"); + expect(document.documentElement.getAttribute("data-theme")).toBe("solarized-light"); + }); + + it("sets data-theme attribute for catppuccin-latte theme", () => { + applyTheme("catppuccin-latte"); + expect(document.documentElement.getAttribute("data-theme")).toBe("catppuccin-latte"); + }); + + it("sets data-theme attribute for gruvbox-light theme", () => { + applyTheme("gruvbox-light"); + expect(document.documentElement.getAttribute("data-theme")).toBe("gruvbox-light"); + }); + + it("sets data-theme attribute for rose-pine-dawn theme", () => { + applyTheme("rose-pine-dawn"); + expect(document.documentElement.getAttribute("data-theme")).toBe("rose-pine-dawn"); + }); + + it("does not apply custom colors for preset themes", () => { + const colors: CustomThemeColors = { + bg_primary: "#ff0000", + bg_secondary: null, + bg_terminal: null, + accent_primary: null, + accent_secondary: null, + text_primary: null, + text_secondary: null, + border_color: null, + }; + + applyTheme("dracula", colors); + expect(document.documentElement.style.getPropertyValue("--bg-primary")).toBe(""); + }); + it("applies custom colors when theme is custom", () => { const colors: CustomThemeColors = { bg_primary: "#1a1a2e", diff --git a/src/lib/stores/config.ts b/src/lib/stores/config.ts index bd71637..ca824fe 100644 --- a/src/lib/stores/config.ts +++ b/src/lib/stores/config.ts @@ -2,7 +2,19 @@ import { writable, derived } from "svelte/store"; import { invoke } from "@tauri-apps/api/core"; import { readFile } from "@tauri-apps/plugin-fs"; -export type Theme = "dark" | "light" | "high-contrast" | "custom"; +export type Theme = + | "dark" + | "light" + | "high-contrast" + | "custom" + | "dracula" + | "catppuccin" + | "nord" + | "solarized" + | "solarized-light" + | "catppuccin-latte" + | "gruvbox-light" + | "rose-pine-dawn"; export type BudgetAction = "warn" | "block"; export interface CustomThemeColors {