cosmic_theme/output/
vs_code.rs

1use serde::{Deserialize, Serialize};
2
3use crate::Theme;
4
5use super::{to_hex, OutputError};
6
7/// Represents the workbench.colorCustomizations section of a VS Code settings.json file
8#[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    // text colors
104    #[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    // accent colors
152    #[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    // accent fg colors
164    #[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        // just add the json entry for workbench.colorCustomizations
279        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        // just remove the json entry for workbench.colorCustomizations
299        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}