ratatui_toolkit/services/theme/loader/
load_theme_str.rs1use std::collections::HashMap;
4
5use ratatui::style::Color;
6
7use crate::services::theme::app_theme::AppTheme;
8use crate::services::theme::diff_colors::DiffColors;
9use crate::services::theme::loader::resolve_defs::resolve_color_value;
10use crate::services::theme::loader::theme_json::{ColorValue, ThemeJson};
11use crate::services::theme::markdown_colors::MarkdownColors;
12use crate::services::theme::syntax_colors::SyntaxColors;
13use crate::services::theme::ThemeVariant;
14
15pub fn load_theme_str(json: &str, variant: ThemeVariant) -> Result<AppTheme, String> {
54 let theme_json: ThemeJson =
55 serde_json::from_str(json).map_err(|e| format!("Failed to parse JSON: {}", e))?;
56
57 build_theme_from_json(&theme_json, variant)
58}
59
60pub(crate) fn build_theme_from_json(
62 theme_json: &ThemeJson,
63 variant: ThemeVariant,
64) -> Result<AppTheme, String> {
65 let defs = &theme_json.defs;
66 let theme = &theme_json.theme;
67
68 let resolve = |key: &str, default: Color| -> Color {
70 theme
71 .get(key)
72 .and_then(|v| resolve_color_value(v, defs, variant))
73 .unwrap_or(default)
74 };
75
76 let default = AppTheme::default();
78
79 let primary = resolve("primary", default.primary);
81 let secondary = resolve("secondary", default.secondary);
82 let accent = resolve("accent", default.accent);
83 let error = resolve("error", default.error);
84 let warning = resolve("warning", default.warning);
85 let success = resolve("success", default.success);
86 let info = resolve("info", default.info);
87
88 let text = resolve("text", default.text);
90 let text_muted = resolve("textMuted", default.text_muted);
91 let selected_text = theme
93 .get("selectedText")
94 .and_then(|v| resolve_color_value(v, defs, variant))
95 .unwrap_or(text);
96
97 let background = resolve("background", default.background);
99 let background_panel = resolve("backgroundPanel", default.background_panel);
100 let background_element = resolve("backgroundElement", default.background_element);
101 let background_menu = theme
103 .get("backgroundMenu")
104 .and_then(|v| resolve_color_value(v, defs, variant))
105 .unwrap_or(background_panel);
106
107 let border = resolve("border", default.border);
109 let border_active = resolve("borderActive", default.border_active);
110 let border_subtle = resolve("borderSubtle", default.border_subtle);
111
112 let diff = build_diff_colors(theme, defs, variant);
114
115 let markdown = build_markdown_colors(theme, defs, variant);
117
118 let syntax = build_syntax_colors(theme, defs, variant);
120
121 Ok(AppTheme {
122 primary,
123 secondary,
124 accent,
125 error,
126 warning,
127 success,
128 info,
129 text,
130 text_muted,
131 selected_text,
132 background,
133 background_panel,
134 background_element,
135 background_menu,
136 border,
137 border_active,
138 border_subtle,
139 diff,
140 markdown,
141 syntax,
142 })
143}
144
145fn build_diff_colors(
147 theme: &HashMap<String, ColorValue>,
148 defs: &HashMap<String, String>,
149 variant: ThemeVariant,
150) -> DiffColors {
151 let default = DiffColors::default();
152
153 let resolve = |key: &str, default: Color| -> Color {
154 theme
155 .get(key)
156 .and_then(|v| resolve_color_value(v, defs, variant))
157 .unwrap_or(default)
158 };
159
160 DiffColors {
161 added: resolve("diffAdded", default.added),
162 removed: resolve("diffRemoved", default.removed),
163 context: resolve("diffContext", default.context),
164 hunk_header: resolve("diffHunkHeader", default.hunk_header),
165 highlight_added: resolve("diffHighlightAdded", default.highlight_added),
166 highlight_removed: resolve("diffHighlightRemoved", default.highlight_removed),
167 added_bg: resolve("diffAddedBg", default.added_bg),
168 removed_bg: resolve("diffRemovedBg", default.removed_bg),
169 context_bg: resolve("diffContextBg", default.context_bg),
170 line_number: resolve("diffLineNumber", default.line_number),
171 added_line_number_bg: resolve("diffAddedLineNumberBg", default.added_line_number_bg),
172 removed_line_number_bg: resolve("diffRemovedLineNumberBg", default.removed_line_number_bg),
173 }
174}
175
176fn build_markdown_colors(
178 theme: &HashMap<String, ColorValue>,
179 defs: &HashMap<String, String>,
180 variant: ThemeVariant,
181) -> MarkdownColors {
182 let default = MarkdownColors::default();
183
184 let resolve = |key: &str, default: Color| -> Color {
185 theme
186 .get(key)
187 .and_then(|v| resolve_color_value(v, defs, variant))
188 .unwrap_or(default)
189 };
190
191 MarkdownColors {
192 text: resolve("markdownText", default.text),
193 heading: resolve("markdownHeading", default.heading),
194 link: resolve("markdownLink", default.link),
195 link_text: resolve("markdownLinkText", default.link_text),
196 code: resolve("markdownCode", default.code),
197 block_quote: resolve("markdownBlockQuote", default.block_quote),
198 emph: resolve("markdownEmph", default.emph),
199 strong: resolve("markdownStrong", default.strong),
200 horizontal_rule: resolve("markdownHorizontalRule", default.horizontal_rule),
201 list_item: resolve("markdownListItem", default.list_item),
202 list_enumeration: resolve("markdownListEnumeration", default.list_enumeration),
203 image: resolve("markdownImage", default.image),
204 image_text: resolve("markdownImageText", default.image_text),
205 code_block: resolve("markdownCodeBlock", default.code_block),
206 }
207}
208
209fn build_syntax_colors(
211 theme: &HashMap<String, ColorValue>,
212 defs: &HashMap<String, String>,
213 variant: ThemeVariant,
214) -> SyntaxColors {
215 let default = SyntaxColors::default();
216
217 let resolve = |key: &str, default: Color| -> Color {
218 theme
219 .get(key)
220 .and_then(|v| resolve_color_value(v, defs, variant))
221 .unwrap_or(default)
222 };
223
224 SyntaxColors {
225 comment: resolve("syntaxComment", default.comment),
226 keyword: resolve("syntaxKeyword", default.keyword),
227 function: resolve("syntaxFunction", default.function),
228 variable: resolve("syntaxVariable", default.variable),
229 string: resolve("syntaxString", default.string),
230 number: resolve("syntaxNumber", default.number),
231 type_: resolve("syntaxType", default.type_),
232 operator: resolve("syntaxOperator", default.operator),
233 punctuation: resolve("syntaxPunctuation", default.punctuation),
234 }
235}