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