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