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