theme.rs

  1mod theme_registry;
  2
  3use gpui::{
  4    color::Color,
  5    elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
  6    fonts::{HighlightStyle, TextStyle},
  7    Border, MouseState,
  8};
  9use serde::{de::DeserializeOwned, Deserialize};
 10use serde_json::Value;
 11use std::{collections::HashMap, sync::Arc};
 12
 13pub use theme_registry::*;
 14
 15#[derive(Deserialize, Default)]
 16pub struct Theme {
 17    #[serde(default)]
 18    pub meta: ThemeMeta,
 19    pub workspace: Workspace,
 20    pub context_menu: ContextMenu,
 21    pub contacts_popover: ContactsPopover,
 22    pub contact_list: ContactList,
 23    pub contact_finder: ContactFinder,
 24    pub project_panel: ProjectPanel,
 25    pub command_palette: CommandPalette,
 26    pub picker: Picker,
 27    pub editor: Editor,
 28    pub search: Search,
 29    pub project_diagnostics: ProjectDiagnostics,
 30    pub breadcrumbs: ContainedText,
 31    pub shared_screen: ContainerStyle,
 32    pub contact_notification: ContactNotification,
 33    pub update_notification: UpdateNotification,
 34    pub simple_message_notification: MessageNotification,
 35    pub project_shared_notification: ProjectSharedNotification,
 36    pub incoming_call_notification: IncomingCallNotification,
 37    pub tooltip: TooltipStyle,
 38    pub terminal: TerminalStyle,
 39    pub feedback: FeedbackStyle,
 40    pub color_scheme: ColorScheme,
 41}
 42
 43#[derive(Deserialize, Default, Clone)]
 44pub struct ThemeMeta {
 45    pub name: String,
 46    pub is_light: bool,
 47}
 48
 49#[derive(Deserialize, Default)]
 50pub struct Workspace {
 51    pub background: Color,
 52    pub titlebar: Titlebar,
 53    pub tab_bar: TabBar,
 54    pub pane_divider: Border,
 55    pub leader_border_opacity: f32,
 56    pub leader_border_width: f32,
 57    pub sidebar: Sidebar,
 58    pub status_bar: StatusBar,
 59    pub toolbar: Toolbar,
 60    pub disconnected_overlay: ContainedText,
 61    pub modal: ContainerStyle,
 62    pub notification: ContainerStyle,
 63    pub notifications: Notifications,
 64    pub joining_project_avatar: ImageStyle,
 65    pub joining_project_message: ContainedText,
 66    pub external_location_message: ContainedText,
 67    pub dock: Dock,
 68    pub drop_target_overlay_color: Color,
 69}
 70
 71#[derive(Clone, Deserialize, Default)]
 72pub struct Titlebar {
 73    #[serde(flatten)]
 74    pub container: ContainerStyle,
 75    pub height: f32,
 76    pub title: TextStyle,
 77    pub avatar_margin: f32,
 78    pub avatar_ribbon: AvatarRibbon,
 79    pub follower_avatar_overlap: f32,
 80    pub offline_icon: OfflineIcon,
 81    pub avatar: AvatarStyle,
 82    pub inactive_avatar: AvatarStyle,
 83    pub follower_avatar: AvatarStyle,
 84    pub sign_in_prompt: Interactive<ContainedText>,
 85    pub outdated_warning: ContainedText,
 86    pub share_button: Interactive<ContainedText>,
 87    pub call_control: Interactive<IconButton>,
 88    pub toggle_contacts_button: Interactive<IconButton>,
 89    pub toggle_contacts_badge: ContainerStyle,
 90}
 91
 92#[derive(Clone, Deserialize, Default)]
 93pub struct AvatarStyle {
 94    #[serde(flatten)]
 95    pub image: ImageStyle,
 96    pub width: f32,
 97}
 98
 99#[derive(Deserialize, Default)]
100pub struct ContactsPopover {
101    #[serde(flatten)]
102    pub container: ContainerStyle,
103    pub height: f32,
104    pub width: f32,
105    pub invite_row_height: f32,
106    pub invite_row: Interactive<ContainedLabel>,
107}
108
109#[derive(Deserialize, Default)]
110pub struct ContactList {
111    pub user_query_editor: FieldEditor,
112    pub user_query_editor_height: f32,
113    pub add_contact_button: IconButton,
114    pub header_row: Interactive<ContainedText>,
115    pub leave_call: Interactive<ContainedText>,
116    pub contact_row: Interactive<ContainerStyle>,
117    pub row_height: f32,
118    pub project_row: Interactive<ProjectRow>,
119    pub tree_branch: Interactive<TreeBranch>,
120    pub contact_avatar: ImageStyle,
121    pub contact_status_free: ContainerStyle,
122    pub contact_status_busy: ContainerStyle,
123    pub contact_username: ContainedText,
124    pub contact_button: Interactive<IconButton>,
125    pub contact_button_spacing: f32,
126    pub disabled_button: IconButton,
127    pub section_icon_size: f32,
128    pub calling_indicator: ContainedText,
129}
130
131#[derive(Deserialize, Default)]
132pub struct ProjectRow {
133    #[serde(flatten)]
134    pub container: ContainerStyle,
135    pub icon: Icon,
136    pub name: ContainedText,
137}
138
139#[derive(Deserialize, Default, Clone, Copy)]
140pub struct TreeBranch {
141    pub width: f32,
142    pub color: Color,
143}
144
145#[derive(Deserialize, Default)]
146pub struct ContactFinder {
147    pub picker: Picker,
148    pub row_height: f32,
149    pub contact_avatar: ImageStyle,
150    pub contact_username: ContainerStyle,
151    pub contact_button: IconButton,
152    pub disabled_contact_button: IconButton,
153}
154
155#[derive(Clone, Deserialize, Default)]
156pub struct TabBar {
157    #[serde(flatten)]
158    pub container: ContainerStyle,
159    pub pane_button: Interactive<IconButton>,
160    pub pane_button_container: ContainerStyle,
161    pub active_pane: TabStyles,
162    pub inactive_pane: TabStyles,
163    pub dragged_tab: Tab,
164    pub height: f32,
165}
166
167impl TabBar {
168    pub fn tab_style(&self, pane_active: bool, tab_active: bool) -> &Tab {
169        let tabs = if pane_active {
170            &self.active_pane
171        } else {
172            &self.inactive_pane
173        };
174
175        if tab_active {
176            &tabs.active_tab
177        } else {
178            &tabs.inactive_tab
179        }
180    }
181}
182
183#[derive(Clone, Deserialize, Default)]
184pub struct TabStyles {
185    pub active_tab: Tab,
186    pub inactive_tab: Tab,
187}
188
189#[derive(Clone, Deserialize, Default)]
190pub struct AvatarRibbon {
191    #[serde(flatten)]
192    pub container: ContainerStyle,
193    pub width: f32,
194    pub height: f32,
195}
196
197#[derive(Clone, Deserialize, Default)]
198pub struct OfflineIcon {
199    #[serde(flatten)]
200    pub container: ContainerStyle,
201    pub width: f32,
202    pub color: Color,
203}
204
205#[derive(Clone, Deserialize, Default)]
206pub struct Tab {
207    pub height: f32,
208    #[serde(flatten)]
209    pub container: ContainerStyle,
210    #[serde(flatten)]
211    pub label: LabelStyle,
212    pub description: ContainedText,
213    pub spacing: f32,
214    pub icon_width: f32,
215    pub icon_close: Color,
216    pub icon_close_active: Color,
217    pub icon_dirty: Color,
218    pub icon_conflict: Color,
219}
220
221#[derive(Clone, Deserialize, Default)]
222pub struct Toolbar {
223    #[serde(flatten)]
224    pub container: ContainerStyle,
225    pub height: f32,
226    pub item_spacing: f32,
227    pub nav_button: Interactive<IconButton>,
228}
229
230#[derive(Clone, Deserialize, Default)]
231pub struct Dock {
232    pub initial_size_right: f32,
233    pub initial_size_bottom: f32,
234    pub wash_color: Color,
235    pub panel: ContainerStyle,
236    pub maximized: ContainerStyle,
237}
238
239#[derive(Clone, Deserialize, Default)]
240pub struct Notifications {
241    #[serde(flatten)]
242    pub container: ContainerStyle,
243    pub width: f32,
244}
245
246#[derive(Clone, Deserialize, Default)]
247pub struct Search {
248    #[serde(flatten)]
249    pub container: ContainerStyle,
250    pub editor: FindEditor,
251    pub invalid_editor: ContainerStyle,
252    pub option_button_group: ContainerStyle,
253    pub option_button: Interactive<ContainedText>,
254    pub match_background: Color,
255    pub match_index: ContainedText,
256    pub results_status: TextStyle,
257    pub tab_icon_width: f32,
258    pub tab_icon_spacing: f32,
259    pub dismiss_button: Interactive<IconButton>,
260}
261
262#[derive(Clone, Deserialize, Default)]
263pub struct FindEditor {
264    #[serde(flatten)]
265    pub input: FieldEditor,
266    pub min_width: f32,
267    pub max_width: f32,
268}
269
270#[derive(Deserialize, Default)]
271pub struct StatusBar {
272    #[serde(flatten)]
273    pub container: ContainerStyle,
274    pub height: f32,
275    pub item_spacing: f32,
276    pub cursor_position: TextStyle,
277    pub auto_update_progress_message: TextStyle,
278    pub auto_update_done_message: TextStyle,
279    pub lsp_status: Interactive<StatusBarLspStatus>,
280    pub feedback: Interactive<TextStyle>,
281    pub sidebar_buttons: StatusBarSidebarButtons,
282    pub diagnostic_summary: Interactive<StatusBarDiagnosticSummary>,
283    pub diagnostic_message: Interactive<ContainedText>,
284}
285
286#[derive(Deserialize, Default)]
287pub struct StatusBarSidebarButtons {
288    pub group_left: ContainerStyle,
289    pub group_right: ContainerStyle,
290    pub item: Interactive<SidebarItem>,
291    pub badge: ContainerStyle,
292}
293
294#[derive(Deserialize, Default)]
295pub struct StatusBarDiagnosticSummary {
296    pub container_ok: ContainerStyle,
297    pub container_warning: ContainerStyle,
298    pub container_error: ContainerStyle,
299    pub text: TextStyle,
300    pub icon_color_ok: Color,
301    pub icon_color_warning: Color,
302    pub icon_color_error: Color,
303    pub height: f32,
304    pub icon_width: f32,
305    pub icon_spacing: f32,
306    pub summary_spacing: f32,
307}
308
309#[derive(Deserialize, Default)]
310pub struct StatusBarLspStatus {
311    #[serde(flatten)]
312    pub container: ContainerStyle,
313    pub height: f32,
314    pub icon_spacing: f32,
315    pub icon_color: Color,
316    pub icon_width: f32,
317    pub message: TextStyle,
318}
319
320#[derive(Deserialize, Default)]
321pub struct Sidebar {
322    pub initial_size: f32,
323    #[serde(flatten)]
324    pub container: ContainerStyle,
325}
326
327#[derive(Clone, Copy, Deserialize, Default)]
328pub struct SidebarItem {
329    #[serde(flatten)]
330    pub container: ContainerStyle,
331    pub icon_color: Color,
332    pub icon_size: f32,
333}
334
335#[derive(Deserialize, Default)]
336pub struct ProjectPanel {
337    #[serde(flatten)]
338    pub container: ContainerStyle,
339    pub entry: Interactive<ProjectPanelEntry>,
340    pub dragged_entry: ProjectPanelEntry,
341    pub ignored_entry: Interactive<ProjectPanelEntry>,
342    pub cut_entry: Interactive<ProjectPanelEntry>,
343    pub filename_editor: FieldEditor,
344    pub indent_width: f32,
345}
346
347#[derive(Clone, Debug, Deserialize, Default)]
348pub struct ProjectPanelEntry {
349    pub height: f32,
350    #[serde(flatten)]
351    pub container: ContainerStyle,
352    pub text: TextStyle,
353    pub icon_color: Color,
354    pub icon_size: f32,
355    pub icon_spacing: f32,
356}
357
358#[derive(Clone, Debug, Deserialize, Default)]
359pub struct ContextMenu {
360    #[serde(flatten)]
361    pub container: ContainerStyle,
362    pub item: Interactive<ContextMenuItem>,
363    pub keystroke_margin: f32,
364    pub separator: ContainerStyle,
365}
366
367#[derive(Clone, Debug, Deserialize, Default)]
368pub struct ContextMenuItem {
369    #[serde(flatten)]
370    pub container: ContainerStyle,
371    pub label: TextStyle,
372    pub keystroke: ContainedText,
373    pub icon_width: f32,
374    pub icon_spacing: f32,
375}
376
377#[derive(Debug, Deserialize, Default)]
378pub struct CommandPalette {
379    pub key: Interactive<ContainedLabel>,
380    pub keystroke_spacing: f32,
381}
382
383#[derive(Deserialize, Default)]
384pub struct InviteLink {
385    #[serde(flatten)]
386    pub container: ContainerStyle,
387    #[serde(flatten)]
388    pub label: LabelStyle,
389    pub icon: Icon,
390}
391
392#[derive(Deserialize, Clone, Copy, Default)]
393pub struct Icon {
394    #[serde(flatten)]
395    pub container: ContainerStyle,
396    pub color: Color,
397    pub width: f32,
398}
399
400#[derive(Deserialize, Clone, Copy, Default)]
401pub struct IconButton {
402    #[serde(flatten)]
403    pub container: ContainerStyle,
404    pub color: Color,
405    pub icon_width: f32,
406    pub button_width: f32,
407}
408
409#[derive(Deserialize, Default)]
410pub struct ChatMessage {
411    #[serde(flatten)]
412    pub container: ContainerStyle,
413    pub body: TextStyle,
414    pub sender: ContainedText,
415    pub timestamp: ContainedText,
416}
417
418#[derive(Deserialize, Default)]
419pub struct ChannelSelect {
420    #[serde(flatten)]
421    pub container: ContainerStyle,
422    pub header: ChannelName,
423    pub item: ChannelName,
424    pub active_item: ChannelName,
425    pub hovered_item: ChannelName,
426    pub hovered_active_item: ChannelName,
427    pub menu: ContainerStyle,
428}
429
430#[derive(Deserialize, Default)]
431pub struct ChannelName {
432    #[serde(flatten)]
433    pub container: ContainerStyle,
434    pub hash: ContainedText,
435    pub name: TextStyle,
436}
437
438#[derive(Clone, Deserialize, Default)]
439pub struct Picker {
440    #[serde(flatten)]
441    pub container: ContainerStyle,
442    pub empty_container: ContainerStyle,
443    pub input_editor: FieldEditor,
444    pub empty_input_editor: FieldEditor,
445    pub no_matches: ContainedLabel,
446    pub item: Interactive<ContainedLabel>,
447}
448
449#[derive(Clone, Debug, Deserialize, Default)]
450pub struct ContainedText {
451    #[serde(flatten)]
452    pub container: ContainerStyle,
453    #[serde(flatten)]
454    pub text: TextStyle,
455}
456
457#[derive(Clone, Debug, Deserialize, Default)]
458pub struct ContainedLabel {
459    #[serde(flatten)]
460    pub container: ContainerStyle,
461    #[serde(flatten)]
462    pub label: LabelStyle,
463}
464
465#[derive(Clone, Deserialize, Default)]
466pub struct ProjectDiagnostics {
467    #[serde(flatten)]
468    pub container: ContainerStyle,
469    pub empty_message: TextStyle,
470    pub tab_icon_width: f32,
471    pub tab_icon_spacing: f32,
472    pub tab_summary_spacing: f32,
473}
474
475#[derive(Deserialize, Default)]
476pub struct ContactNotification {
477    pub header_avatar: ImageStyle,
478    pub header_message: ContainedText,
479    pub header_height: f32,
480    pub body_message: ContainedText,
481    pub button: Interactive<ContainedText>,
482    pub dismiss_button: Interactive<IconButton>,
483}
484
485#[derive(Deserialize, Default)]
486pub struct UpdateNotification {
487    pub message: ContainedText,
488    pub action_message: Interactive<ContainedText>,
489    pub dismiss_button: Interactive<IconButton>,
490}
491
492#[derive(Deserialize, Default)]
493pub struct MessageNotification {
494    pub message: ContainedText,
495    pub action_message: Interactive<ContainedText>,
496    pub dismiss_button: Interactive<IconButton>,
497}
498
499#[derive(Deserialize, Default)]
500pub struct ProjectSharedNotification {
501    pub window_height: f32,
502    pub window_width: f32,
503    #[serde(default)]
504    pub background: Color,
505    pub owner_container: ContainerStyle,
506    pub owner_avatar: ImageStyle,
507    pub owner_metadata: ContainerStyle,
508    pub owner_username: ContainedText,
509    pub message: ContainedText,
510    pub worktree_roots: ContainedText,
511    pub button_width: f32,
512    pub open_button: ContainedText,
513    pub dismiss_button: ContainedText,
514}
515
516#[derive(Deserialize, Default)]
517pub struct IncomingCallNotification {
518    pub window_height: f32,
519    pub window_width: f32,
520    #[serde(default)]
521    pub background: Color,
522    pub caller_container: ContainerStyle,
523    pub caller_avatar: ImageStyle,
524    pub caller_metadata: ContainerStyle,
525    pub caller_username: ContainedText,
526    pub caller_message: ContainedText,
527    pub worktree_roots: ContainedText,
528    pub button_width: f32,
529    pub accept_button: ContainedText,
530    pub decline_button: ContainedText,
531}
532
533#[derive(Clone, Deserialize, Default)]
534pub struct Editor {
535    pub text_color: Color,
536    #[serde(default)]
537    pub background: Color,
538    pub selection: SelectionStyle,
539    pub gutter_background: Color,
540    pub gutter_padding_factor: f32,
541    pub active_line_background: Color,
542    pub highlighted_line_background: Color,
543    pub rename_fade: f32,
544    pub document_highlight_read_background: Color,
545    pub document_highlight_write_background: Color,
546    pub diff: DiffStyle,
547    pub line_number: Color,
548    pub line_number_active: Color,
549    pub guest_selections: Vec<SelectionStyle>,
550    pub syntax: Arc<SyntaxTheme>,
551    pub diagnostic_path_header: DiagnosticPathHeader,
552    pub diagnostic_header: DiagnosticHeader,
553    pub error_diagnostic: DiagnosticStyle,
554    pub invalid_error_diagnostic: DiagnosticStyle,
555    pub warning_diagnostic: DiagnosticStyle,
556    pub invalid_warning_diagnostic: DiagnosticStyle,
557    pub information_diagnostic: DiagnosticStyle,
558    pub invalid_information_diagnostic: DiagnosticStyle,
559    pub hint_diagnostic: DiagnosticStyle,
560    pub invalid_hint_diagnostic: DiagnosticStyle,
561    pub autocomplete: AutocompleteStyle,
562    pub code_actions: CodeActions,
563    pub unnecessary_code_fade: f32,
564    pub hover_popover: HoverPopover,
565    pub link_definition: HighlightStyle,
566    pub composition_mark: HighlightStyle,
567    pub jump_icon: Interactive<IconButton>,
568    pub scrollbar: Scrollbar,
569}
570
571#[derive(Clone, Deserialize, Default)]
572pub struct Scrollbar {
573    pub track: ContainerStyle,
574    pub thumb: ContainerStyle,
575    pub width: f32,
576    pub min_height_factor: f32,
577}
578
579#[derive(Clone, Deserialize, Default)]
580pub struct DiagnosticPathHeader {
581    #[serde(flatten)]
582    pub container: ContainerStyle,
583    pub filename: ContainedText,
584    pub path: ContainedText,
585    pub text_scale_factor: f32,
586}
587
588#[derive(Clone, Deserialize, Default)]
589pub struct DiagnosticHeader {
590    #[serde(flatten)]
591    pub container: ContainerStyle,
592    pub message: ContainedLabel,
593    pub code: ContainedText,
594    pub text_scale_factor: f32,
595    pub icon_width_factor: f32,
596}
597
598#[derive(Clone, Deserialize, Default)]
599pub struct DiagnosticStyle {
600    pub message: LabelStyle,
601    #[serde(default)]
602    pub header: ContainerStyle,
603    pub text_scale_factor: f32,
604}
605
606#[derive(Clone, Deserialize, Default)]
607pub struct AutocompleteStyle {
608    #[serde(flatten)]
609    pub container: ContainerStyle,
610    pub item: ContainerStyle,
611    pub selected_item: ContainerStyle,
612    pub hovered_item: ContainerStyle,
613    pub match_highlight: HighlightStyle,
614}
615
616#[derive(Clone, Copy, Default, Deserialize)]
617pub struct SelectionStyle {
618    pub cursor: Color,
619    pub selection: Color,
620}
621
622#[derive(Clone, Deserialize, Default)]
623pub struct FieldEditor {
624    #[serde(flatten)]
625    pub container: ContainerStyle,
626    pub text: TextStyle,
627    #[serde(default)]
628    pub placeholder_text: Option<TextStyle>,
629    pub selection: SelectionStyle,
630}
631
632#[derive(Clone, Deserialize, Default)]
633pub struct CodeActions {
634    #[serde(default)]
635    pub indicator: Color,
636    pub vertical_scale: f32,
637}
638
639#[derive(Clone, Deserialize, Default)]
640pub struct DiffStyle {
641    pub inserted: Color,
642    pub modified: Color,
643    pub deleted: Color,
644    pub removed_width_em: f32,
645    pub width_em: f32,
646    pub corner_radius: f32,
647}
648
649#[derive(Debug, Default, Clone, Copy)]
650pub struct Interactive<T> {
651    pub default: T,
652    pub hover: Option<T>,
653    pub clicked: Option<T>,
654    pub active: Option<T>,
655    pub disabled: Option<T>,
656}
657
658impl<T> Interactive<T> {
659    pub fn style_for(&self, state: &mut MouseState, active: bool) -> &T {
660        if active {
661            self.active.as_ref().unwrap_or(&self.default)
662        } else if state.clicked() == Some(gpui::MouseButton::Left) && self.clicked.is_some() {
663            self.clicked.as_ref().unwrap()
664        } else if state.hovered() {
665            self.hover.as_ref().unwrap_or(&self.default)
666        } else {
667            &self.default
668        }
669    }
670
671    pub fn disabled_style(&self) -> &T {
672        self.disabled.as_ref().unwrap_or(&self.default)
673    }
674}
675
676impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
677    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
678    where
679        D: serde::Deserializer<'de>,
680    {
681        #[derive(Deserialize)]
682        struct Helper {
683            #[serde(flatten)]
684            default: Value,
685            hover: Option<Value>,
686            clicked: Option<Value>,
687            active: Option<Value>,
688            disabled: Option<Value>,
689        }
690
691        let json = Helper::deserialize(deserializer)?;
692
693        let deserialize_state = |state_json: Option<Value>| -> Result<Option<T>, D::Error> {
694            if let Some(mut state_json) = state_json {
695                if let Value::Object(state_json) = &mut state_json {
696                    if let Value::Object(default) = &json.default {
697                        for (key, value) in default {
698                            if !state_json.contains_key(key) {
699                                state_json.insert(key.clone(), value.clone());
700                            }
701                        }
702                    }
703                }
704                Ok(Some(
705                    serde_json::from_value::<T>(state_json).map_err(serde::de::Error::custom)?,
706                ))
707            } else {
708                Ok(None)
709            }
710        };
711
712        let hover = deserialize_state(json.hover)?;
713        let clicked = deserialize_state(json.clicked)?;
714        let active = deserialize_state(json.active)?;
715        let disabled = deserialize_state(json.disabled)?;
716        let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
717
718        Ok(Interactive {
719            default,
720            hover,
721            clicked,
722            active,
723            disabled,
724        })
725    }
726}
727
728impl Editor {
729    pub fn replica_selection_style(&self, replica_id: u16) -> &SelectionStyle {
730        let style_ix = replica_id as usize % (self.guest_selections.len() + 1);
731        if style_ix == 0 {
732            &self.selection
733        } else {
734            &self.guest_selections[style_ix - 1]
735        }
736    }
737}
738
739#[derive(Default)]
740pub struct SyntaxTheme {
741    pub highlights: Vec<(String, HighlightStyle)>,
742}
743
744impl SyntaxTheme {
745    pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
746        Self { highlights }
747    }
748}
749
750impl<'de> Deserialize<'de> for SyntaxTheme {
751    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
752    where
753        D: serde::Deserializer<'de>,
754    {
755        let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
756
757        let mut result = Self::new(Vec::new());
758        for (key, style) in syntax_data {
759            match result
760                .highlights
761                .binary_search_by(|(needle, _)| needle.cmp(&key))
762            {
763                Ok(i) | Err(i) => {
764                    result.highlights.insert(i, (key, style));
765                }
766            }
767        }
768
769        Ok(result)
770    }
771}
772
773#[derive(Clone, Deserialize, Default)]
774pub struct HoverPopover {
775    pub container: ContainerStyle,
776    pub info_container: ContainerStyle,
777    pub warning_container: ContainerStyle,
778    pub error_container: ContainerStyle,
779    pub block_style: ContainerStyle,
780    pub prose: TextStyle,
781    pub highlight: Color,
782}
783
784#[derive(Clone, Deserialize, Default)]
785pub struct TerminalStyle {
786    pub black: Color,
787    pub red: Color,
788    pub green: Color,
789    pub yellow: Color,
790    pub blue: Color,
791    pub magenta: Color,
792    pub cyan: Color,
793    pub white: Color,
794    pub bright_black: Color,
795    pub bright_red: Color,
796    pub bright_green: Color,
797    pub bright_yellow: Color,
798    pub bright_blue: Color,
799    pub bright_magenta: Color,
800    pub bright_cyan: Color,
801    pub bright_white: Color,
802    pub foreground: Color,
803    pub background: Color,
804    pub modal_background: Color,
805    pub cursor: Color,
806    pub dim_black: Color,
807    pub dim_red: Color,
808    pub dim_green: Color,
809    pub dim_yellow: Color,
810    pub dim_blue: Color,
811    pub dim_magenta: Color,
812    pub dim_cyan: Color,
813    pub dim_white: Color,
814    pub bright_foreground: Color,
815    pub dim_foreground: Color,
816}
817
818#[derive(Clone, Deserialize, Default)]
819pub struct FeedbackStyle {
820    pub submit_button: Interactive<ContainedText>,
821    pub button_margin: f32,
822    pub info_text: ContainedText,
823}
824
825#[derive(Clone, Deserialize, Default)]
826pub struct ColorScheme {
827    pub name: String,
828    pub is_light: bool,
829
830    pub ramps: RampSet,
831
832    pub lowest: Layer,
833    pub middle: Layer,
834    pub highest: Layer,
835
836    pub popover_shadow: Shadow,
837    pub modal_shadow: Shadow,
838
839    pub players: Vec<Player>,
840}
841
842#[derive(Clone, Deserialize, Default)]
843pub struct Player {
844    pub cursor: Color,
845    pub selection: Color,
846}
847
848#[derive(Clone, Deserialize, Default)]
849pub struct RampSet {
850    pub neutral: Vec<Color>,
851    pub red: Vec<Color>,
852    pub orange: Vec<Color>,
853    pub yellow: Vec<Color>,
854    pub green: Vec<Color>,
855    pub cyan: Vec<Color>,
856    pub blue: Vec<Color>,
857    pub violet: Vec<Color>,
858    pub magenta: Vec<Color>,
859}
860
861#[derive(Clone, Deserialize, Default)]
862pub struct Layer {
863    pub base: StyleSet,
864    pub variant: StyleSet,
865    pub on: StyleSet,
866    pub accent: StyleSet,
867    pub positive: StyleSet,
868    pub warning: StyleSet,
869    pub negative: StyleSet,
870}
871
872#[derive(Clone, Deserialize, Default)]
873pub struct StyleSet {
874    pub default: Style,
875    pub active: Style,
876    pub disabled: Style,
877    pub hovered: Style,
878    pub pressed: Style,
879    pub inverted: Style,
880}
881
882#[derive(Clone, Deserialize, Default)]
883pub struct Style {
884    pub background: Color,
885    pub border: Color,
886    pub foreground: Color,
887}