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