1use serde::{Deserialize, Serialize};
2
3use crate::Theme;
4
5use super::{to_hex, OutputError};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct VsTheme {
10 #[serde(rename = "editor.background")]
11 editor_background: String,
12 #[serde(rename = "sideBar.background")]
13 sidebar_background: String,
14 #[serde(rename = "activityBar.background")]
15 activity_bar_background: String,
16 #[serde(rename = "notificationCenterHeader.background")]
17 notification_center_header_background: String,
18 #[serde(rename = "notifications.background")]
19 notifications_background: String,
20 #[serde(rename = "activityBarTop.activeBackground")]
21 activity_bar_top_active_background: String,
22 #[serde(rename = "editorGroupHeader.tabsBackground")]
23 editor_group_header_tabs_background: String,
24 #[serde(rename = "editorGroupHeader.noTabsBackground")]
25 editor_group_header_no_tabs_background: String,
26 #[serde(rename = "titleBar.activeBackground")]
27 title_bar_active_background: String,
28 #[serde(rename = "titleBar.inactiveBackground")]
29 title_bar_inactive_background: String,
30 #[serde(rename = "statusBar.background")]
31 status_bar_background: String,
32 #[serde(rename = "statusBar.noFolderBackground")]
33 status_bar_no_folder_background: String,
34 #[serde(rename = "statusBar.debuggingBackground")]
35 status_bar_debugging_background: String,
36 #[serde(rename = "tab.activeBackground")]
37 tab_active_background: String,
38 #[serde(rename = "tab.activeBorder")]
39 tab_active_border: String,
40 #[serde(rename = "tab.activeBorderTop")]
41 tab_active_border_top: String,
42 #[serde(rename = "tab.hoverBackground")]
43 tab_hover_background: String,
44 #[serde(rename = "quickInput.background")]
45 quick_input_background: String,
46 #[serde(rename = "tab.inactiveBackground")]
47 tab_inactive_background: String,
48 #[serde(rename = "sideBarSectionHeader.background")]
49 side_bar_section_header_background: String,
50 #[serde(rename = "list.focusOutline")]
51 list_focus_outline: String,
52 #[serde(rename = "banner.background")]
53 banner_background: String,
54 #[serde(rename = "breadcrumb.background")]
55 breadcrumb_background: String,
56 #[serde(rename = "commandCenter.background")]
57 command_center_background: String,
58 #[serde(rename = "terminal.background")]
59 terminal_background: String,
60 #[serde(rename = "menu.background")]
61 menu_background: String,
62 #[serde(rename = "panel.background")]
63 panel_background: String,
64 #[serde(rename = "peekViewEditorGutter.background")]
65 peek_view_editor_gutter_background: String,
66 #[serde(rename = "peekViewResult.background")]
67 peek_view_result_background: String,
68 #[serde(rename = "peekViewTitle.background")]
69 peek_view_title_background: String,
70 #[serde(rename = "peekViewEditor.background")]
71 peek_view_editor_background: String,
72 #[serde(rename = "peekViewResult.selectionBackground")]
73 peek_view_result_selection_background: String,
74 #[serde(rename = "editorWidget.background")]
75 editor_widget_background: String,
76 #[serde(rename = "editorSuggestWidget.background")]
77 editor_suggest_widget_background: String,
78 #[serde(rename = "editorHoverWidget.background")]
79 editor_hover_widget_background: String,
80 #[serde(rename = "input.background")]
81 input_background: String,
82 #[serde(rename = "dropdown.background")]
83 dropdown_background: String,
84 #[serde(rename = "settings.checkboxBackground")]
85 settings_checkbox_background: String,
86 #[serde(rename = "settings.textInputBackground")]
87 settings_text_input_background: String,
88 #[serde(rename = "settings.numberInputBackground")]
89 settings_number_input_background: String,
90 #[serde(rename = "settings.dropdownBackground")]
91 settings_dropdown_background: String,
92 #[serde(rename = "sideBar.dropBackground")]
93 side_bar_drop_background: String,
94 #[serde(rename = "list.activeSelectionBackground")]
95 list_active_selection_background: String,
96 #[serde(rename = "list.inactiveSelectionBackground")]
97 list_inactive_selection_background: String,
98 #[serde(rename = "list.focusBackground")]
99 list_focus_background: String,
100 #[serde(rename = "list.hoverBackground")]
101 list_hover_background: String,
102
103 #[serde(rename = "editor.foreground")]
105 editor_foreground: String,
106 #[serde(rename = "editorLineNumber.foreground")]
107 editor_line_number_foreground: String,
108 #[serde(rename = "editorCursor.foreground")]
109 editor_cursor_foreground: String,
110 #[serde(rename = "sideBar.foreground")]
111 side_bar_foreground: String,
112 #[serde(rename = "activityBar.foreground")]
113 activity_bar_foreground: String,
114 #[serde(rename = "statusBar.foreground")]
115 status_bar_foreground: String,
116 #[serde(rename = "tab.activeForeground")]
117 tab_active_foreground: String,
118 #[serde(rename = "tab.inactiveForeground")]
119 tab_inactive_foreground: String,
120 #[serde(rename = "editorGroupHeader.tabsForeground")]
121 editor_group_header_tabs_foreground: String,
122 #[serde(rename = "sideBarSectionHeader.foreground")]
123 side_bar_section_header_foreground: String,
124 #[serde(rename = "statusBar.debuggingForeground")]
125 status_bar_debugging_foreground: String,
126 #[serde(rename = "statusBar.noFolderForeground")]
127 status_bar_no_folder_foreground: String,
128 #[serde(rename = "editorWidget.foreground")]
129 editor_widget_foreground: String,
130 #[serde(rename = "editorSuggestWidget.foreground")]
131 editor_suggest_widget_foreground: String,
132 #[serde(rename = "editorHoverWidget.foreground")]
133 editor_hover_widget_foreground: String,
134 #[serde(rename = "input.foreground")]
135 input_foreground: String,
136 #[serde(rename = "dropdown.foreground")]
137 dropdown_foreground: String,
138 #[serde(rename = "terminal.foreground")]
139 terminal_foreground: String,
140 #[serde(rename = "menu.foreground")]
141 menu_foreground: String,
142 #[serde(rename = "panel.foreground")]
143 panel_foreground: String,
144 #[serde(rename = "peekViewEditorGutter.foreground")]
145 peek_view_editor_gutter_foreground: String,
146 #[serde(rename = "peekViewResult.selectionForeground")]
147 peek_view_result_selection_foreground: String,
148 #[serde(rename = "inputOption.activeBorder")]
149 input_option_active_border: String,
150
151 #[serde(rename = "activityBarBadge.background")]
153 activity_bar_badge_background: String,
154 #[serde(rename = "statusBar.debuggingBorder")]
155 status_bar_debugging_border: String,
156 #[serde(rename = "button.background")]
157 button_background: String,
158 #[serde(rename = "button.hoverBackground")]
159 button_hover_background: String,
160 #[serde(rename = "statusBarItem.remoteBackground")]
161 status_bar_item_remote_background: String,
162
163 #[serde(rename = "activityBarBadge.foreground")]
165 activity_bar_badge_foreground: String,
166 #[serde(rename = "button.foreground")]
167 button_foreground: String,
168 #[serde(rename = "textLink.foreground")]
169 text_link_foreground: String,
170 #[serde(rename = "textLink.activeForeground")]
171 text_link_active_foreground: String,
172 #[serde(rename = "peekView.border")]
173 peek_view_border: String,
174 #[serde(rename = "settings.checkboxForeground")]
175 settings_checkbox_foreground: String,
176}
177
178impl From<Theme> for VsTheme {
179 fn from(theme: Theme) -> Self {
180 Self {
181 editor_background: format!("#{}", to_hex(theme.background.base)),
182 sidebar_background: format!("#{}", to_hex(theme.primary.base)),
183 activity_bar_background: format!("#{}", to_hex(theme.primary.base)),
184 notification_center_header_background: format!("#{}", to_hex(theme.background.base)),
185 notifications_background: format!("#{}", to_hex(theme.background.base)),
186 activity_bar_top_active_background: format!("#{}", to_hex(theme.primary.base)),
187 editor_group_header_tabs_background: format!("#{}", to_hex(theme.background.base)),
188 editor_group_header_no_tabs_background: format!("#{}", to_hex(theme.background.base)),
189 title_bar_active_background: format!("#{}", to_hex(theme.background.component.base)),
190 title_bar_inactive_background: format!(
191 "#{}",
192 to_hex(theme.background.component.disabled)
193 ),
194 status_bar_background: format!("#{}", to_hex(theme.background.base)),
195 status_bar_no_folder_background: format!("#{}", to_hex(theme.background.base)),
196 status_bar_debugging_background: format!("#{}", to_hex(theme.background.base)),
197 tab_active_background: format!("#{}", to_hex(theme.primary.component.pressed)),
198 tab_active_border: format!("#{}", to_hex(theme.accent.base)),
199 tab_active_border_top: format!("#{}", to_hex(theme.accent.base)),
200 tab_hover_background: format!("#{}", to_hex(theme.primary.component.hover)),
201 tab_inactive_background: format!("#{}", to_hex(theme.primary.component.base)),
202 quick_input_background: format!("#{}", to_hex(theme.primary.base)),
203 side_bar_section_header_background: format!("#{}", to_hex(theme.primary.base)),
204 banner_background: format!("#{}", to_hex(theme.primary.base)),
205 breadcrumb_background: format!("#{}", to_hex(theme.primary.base)),
206 command_center_background: format!("#{}", to_hex(theme.primary.base)),
207 terminal_background: format!("#{}", to_hex(theme.primary.base)),
208 menu_background: format!("#{}", to_hex(theme.primary.base)),
209 panel_background: format!("#{}", to_hex(theme.primary.base)),
210 peek_view_editor_gutter_background: format!("#{}", to_hex(theme.background.base)),
211 peek_view_result_background: format!("#{}", to_hex(theme.background.base)),
212 peek_view_title_background: format!("#{}", to_hex(theme.background.base)),
213 peek_view_editor_background: format!("#{}", to_hex(theme.background.base)),
214 peek_view_result_selection_background: format!("#{}", to_hex(theme.background.base)),
215 editor_widget_background: format!("#{}", to_hex(theme.background.base)),
216 editor_suggest_widget_background: format!("#{}", to_hex(theme.background.base)),
217 editor_hover_widget_background: format!("#{}", to_hex(theme.background.base)),
218 input_background: format!("#{}", to_hex(theme.background.base)),
219 dropdown_background: format!("#{}", to_hex(theme.background.base)),
220 settings_checkbox_background: format!("#{}", to_hex(theme.background.base)),
221 settings_text_input_background: format!("#{}", to_hex(theme.background.base)),
222 settings_number_input_background: format!("#{}", to_hex(theme.background.base)),
223 settings_dropdown_background: format!("#{}", to_hex(theme.background.base)),
224 side_bar_drop_background: format!("#{}", to_hex(theme.background.base)),
225 list_active_selection_background: format!("#{}", to_hex(theme.primary.base)),
226 list_inactive_selection_background: format!("#{}", to_hex(theme.primary.base)),
227 list_focus_background: format!("#{}", to_hex(theme.primary.base)),
228 list_hover_background: format!("#{}", to_hex(theme.primary.base)),
229 editor_foreground: format!("#{}", to_hex(theme.background.on)),
230 editor_line_number_foreground: format!("#{}", to_hex(theme.background.on)),
231 editor_cursor_foreground: format!("#{}", to_hex(theme.background.on)),
232 side_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
233 activity_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
234 status_bar_foreground: format!("#{}", to_hex(theme.primary.on)),
235 tab_active_foreground: format!("#{}", to_hex(theme.primary.on)),
236 tab_inactive_foreground: format!("#{}", to_hex(theme.primary.on)),
237 editor_group_header_tabs_foreground: format!("#{}", to_hex(theme.primary.on)),
238 side_bar_section_header_foreground: format!("#{}", to_hex(theme.primary.on)),
239 status_bar_debugging_foreground: format!("#{}", to_hex(theme.primary.on)),
240 status_bar_no_folder_foreground: format!("#{}", to_hex(theme.primary.on)),
241 editor_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
242 editor_suggest_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
243 editor_hover_widget_foreground: format!("#{}", to_hex(theme.primary.on)),
244 input_foreground: format!("#{}", to_hex(theme.primary.on)),
245 dropdown_foreground: format!("#{}", to_hex(theme.primary.on)),
246 terminal_foreground: format!("#{}", to_hex(theme.primary.on)),
247 menu_foreground: format!("#{}", to_hex(theme.primary.on)),
248 panel_foreground: format!("#{}", to_hex(theme.primary.on)),
249 peek_view_editor_gutter_foreground: format!("#{}", to_hex(theme.primary.on)),
250 peek_view_result_selection_foreground: format!("#{}", to_hex(theme.primary.on)),
251 input_option_active_border: format!("#{}", to_hex(theme.accent.base)),
252 activity_bar_badge_background: format!("#{}", to_hex(theme.accent.base)),
253 activity_bar_badge_foreground: format!("#{}", to_hex(theme.accent.on)),
254 status_bar_debugging_border: format!("#{}", to_hex(theme.accent.base)),
255 list_focus_outline: format!("#{}", to_hex(theme.accent.base)),
256 button_background: format!("#{}", to_hex(theme.accent_button.base)),
257 button_hover_background: format!("#{}", to_hex(theme.accent_button.hover)),
258 status_bar_item_remote_background: format!("#{}", to_hex(theme.accent.base)),
259 button_foreground: format!("#{}", to_hex(theme.accent_button.on)),
260 text_link_foreground: format!("#{}", to_hex(theme.accent.base)),
261 text_link_active_foreground: format!("#{}", to_hex(theme.accent.base)),
262 peek_view_border: format!("#{}", to_hex(theme.accent.base)),
263 settings_checkbox_foreground: format!("#{}", to_hex(theme.accent.base)),
264 }
265 }
266}
267
268impl Theme {
269 #[cold]
270 pub fn apply_vs_code(self) -> Result<(), OutputError> {
271 let vs_theme = VsTheme::from(self);
272 let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
273 let vs_code_dir = config_dir.join("Code").join("User");
274 if !vs_code_dir.exists() {
275 std::fs::create_dir_all(&vs_code_dir).map_err(OutputError::Io)?;
276 }
277
278 let settings_file = vs_code_dir.join("settings.json");
280 let settings = std::fs::read_to_string(&settings_file).unwrap_or_default();
281 let mut settings: serde_json::Value = serde_json::from_str(&settings)?;
282 settings["workbench.colorCustomizations"] = serde_json::to_value(vs_theme).unwrap();
283 settings["window.autoDetectColorScheme"] = serde_json::Value::Bool(true);
284 std::fs::write(
285 &settings_file,
286 serde_json::to_string_pretty(&settings).unwrap(),
287 )
288 .map_err(OutputError::Io)?;
289
290 Ok(())
291 }
292
293 #[cold]
294 pub fn reset_vs_code() -> Result<(), OutputError> {
295 let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
296 let vs_code_dir = config_dir.join("Code").join("User");
297 let settings_file = vs_code_dir.join("settings.json");
298 let settings = std::fs::read_to_string(&settings_file).unwrap_or_default();
300 let mut settings: serde_json::Value = serde_json::from_str(&settings).unwrap_or_default();
301 settings["workbench.colorCustomizations"] = serde_json::Value::Null;
302
303 std::fs::write(
304 &settings_file,
305 serde_json::to_string_pretty(&settings).unwrap(),
306 )
307 .map_err(OutputError::Io)?;
308
309 Ok(())
310 }
311}