1#![allow(unused)]
2
3use std::borrow::Cow;
4use std::collections::HashMap;
5use std::fmt;
6use std::ops::{Deref, DerefMut};
7use std::sync::Arc;
8
9use gpui::serde_json::{self, Value};
10use pathfinder_color::ColorU;
11use serde::de::{self, DeserializeOwned, Unexpected};
12use serde::{Deserialize, Deserializer};
13
14#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
15#[repr(transparent)]
16pub struct Color(pub ColorU);
17
18impl Color {
19 pub fn from_u32(rgba: u32) -> Self {
20 Self(ColorU::from_u32(rgba))
21 }
22}
23
24impl Deref for Color {
25 type Target = ColorU;
26 fn deref(&self) -> &Self::Target {
27 &self.0
28 }
29}
30
31impl DerefMut for Color {
32 fn deref_mut(&mut self) -> &mut Self::Target {
33 &mut self.0
34 }
35}
36
37impl fmt::Debug for Color {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 self.0.fmt(f)
40 }
41}
42
43impl<'de> Deserialize<'de> for Color {
44 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45 where
46 D: Deserializer<'de>,
47 {
48 let literal: Cow<str> = Deserialize::deserialize(deserializer)?;
49 if let Some(digits) = literal.strip_prefix('#') {
50 if let Ok(value) = u32::from_str_radix(digits, 16) {
51 if digits.len() == 6 {
52 return Ok(Color::from_u32((value << 8) | 0xFF));
53 } else if digits.len() == 8 {
54 return Ok(Color::from_u32(value));
55 }
56 }
57 }
58 Err(de::Error::invalid_value(
59 Unexpected::Str(literal.as_ref()),
60 &"#RRGGBB[AA]",
61 ))
62 }
63}
64
65#[derive(Clone, Debug, Default, Deserialize)]
66pub struct TextStyle {
67 pub color: Color,
68}
69
70#[derive(Clone, Debug, Default)]
71pub struct HighlightStyle {
72 pub color: Option<Color>,
73 pub weight: Option<Weight>,
74 pub italic: Option<bool>,
75}
76
77impl<'de> Deserialize<'de> for HighlightStyle {
78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79 where
80 D: serde::Deserializer<'de>,
81 {
82 #[derive(Deserialize)]
83 struct HighlightStyleJson {
84 color: Option<Color>,
85 weight: Option<Weight>,
86 italic: Option<bool>,
87 }
88
89 let json = serde_json::Value::deserialize(deserializer)?;
90 if json.is_object() {
91 let style: HighlightStyleJson =
92 serde_json::from_value(json).map_err(de::Error::custom)?;
93
94 Ok(Self {
95 color: style.color,
96 weight: style.weight,
97 italic: style.italic,
98 })
99 } else {
100 Ok(Self {
101 color: serde_json::from_value(json).map_err(de::Error::custom)?,
102 ..Default::default()
103 })
104 }
105 }
106}
107
108#[allow(non_camel_case_types)]
109#[derive(Clone, Debug, Deserialize)]
110pub enum Weight {
111 thin,
112 extra_light,
113 light,
114 normal,
115 medium,
116 semibold,
117 bold,
118 extra_bold,
119 black,
120}
121
122#[derive(Clone, Copy, Debug, Default, Deserialize)]
123pub struct ContainerStyle {
124 #[serde(rename = "background")]
125 pub background_color: Option<Color>,
126 #[serde(rename = "overlay")]
127 pub overlay_color: Option<Color>,
128 #[serde(default)]
129 pub border: Border,
130}
131
132#[derive(Clone, Deserialize, Default)]
133pub struct SvgStyle {
134 pub color: Color,
135}
136
137#[derive(Clone, Deserialize, Default)]
138pub struct IconStyle {
139 pub icon: SvgStyle,
140 pub container: ContainerStyle,
141}
142
143#[derive(Copy, Clone, Debug, Default)]
144pub struct Border {
145 pub color: Color,
146 pub width: f32,
147 pub overlay: bool,
148 pub top: bool,
149 pub bottom: bool,
150 pub left: bool,
151 pub right: bool,
152}
153
154impl<'de> Deserialize<'de> for Border {
155 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156 where
157 D: serde::Deserializer<'de>,
158 {
159 #[derive(Deserialize)]
160 struct BorderData {
161 pub width: f32,
162 pub color: Color,
163 #[serde(default)]
164 pub overlay: bool,
165 #[serde(default)]
166 pub top: bool,
167 #[serde(default)]
168 pub right: bool,
169 #[serde(default)]
170 pub bottom: bool,
171 #[serde(default)]
172 pub left: bool,
173 }
174
175 let data = BorderData::deserialize(deserializer)?;
176 let mut border = Border {
177 width: data.width,
178 color: data.color,
179 overlay: data.overlay,
180 top: data.top,
181 bottom: data.bottom,
182 left: data.left,
183 right: data.right,
184 };
185 if !border.top && !border.bottom && !border.left && !border.right {
186 border.top = true;
187 border.bottom = true;
188 border.left = true;
189 border.right = true;
190 }
191 Ok(border)
192 }
193}
194
195#[derive(Clone, Deserialize, Default)]
196pub struct TooltipStyle {
197 #[serde(flatten)]
198 pub container: ContainerStyle,
199 pub text: TextStyle,
200 keystroke: KeystrokeStyle,
201 pub max_text_width: Option<f32>,
202}
203
204#[derive(Clone, Deserialize, Default)]
205pub struct KeystrokeStyle {
206 #[serde(flatten)]
207 container: ContainerStyle,
208 #[serde(flatten)]
209 text: TextStyle,
210}
211
212#[derive(Copy, Clone, Default, Deserialize)]
213pub struct ImageStyle {
214 #[serde(default)]
215 pub border: Border,
216 #[serde(default)]
217 pub corner_radius: f32,
218 #[serde(default)]
219 pub height: Option<f32>,
220 #[serde(default)]
221 pub width: Option<f32>,
222 #[serde(default)]
223 pub grayscale: bool,
224}
225
226#[derive(Deserialize, Default)]
227pub struct Theme {
228 #[serde(default)]
229 pub meta: ThemeMeta,
230 pub workspace: Workspace,
231 pub context_menu: ContextMenu,
232 pub toolbar_dropdown_menu: DropdownMenu,
233 pub copilot: Copilot,
234 pub collab_panel: CollabPanel,
235 pub project_panel: ProjectPanel,
236 pub chat_panel: ChatPanel,
237 pub notification_panel: NotificationPanel,
238 pub command_palette: CommandPalette,
239 pub picker: Picker,
240 pub editor: Editor,
241 pub search: Search,
242 pub project_diagnostics: ProjectDiagnostics,
243 pub shared_screen: ContainerStyle,
244 pub contact_notification: ContactNotification,
245 pub update_notification: UpdateNotification,
246 pub simple_message_notification: MessageNotification,
247 pub project_shared_notification: ProjectSharedNotification,
248 pub incoming_call_notification: IncomingCallNotification,
249 pub tooltip: TooltipStyle,
250 pub terminal: TerminalStyle,
251 pub assistant: AssistantStyle,
252 pub feedback: FeedbackStyle,
253 pub welcome: WelcomeStyle,
254 pub titlebar: Titlebar,
255 // Nathan: New elements are styled in Rust, directly from the base theme.
256 // We store it on the legacy theme so we can mix both kinds of elements during the transition.
257 pub base_theme: serde_json::Value,
258}
259
260#[derive(Deserialize, Default, Clone)]
261pub struct ThemeMeta {
262 #[serde(skip_deserializing)]
263 pub id: usize,
264 pub name: String,
265 pub is_light: bool,
266}
267
268#[derive(Deserialize, Default)]
269pub struct Workspace {
270 pub background: Color,
271 pub blank_pane: BlankPaneStyle,
272 pub tab_bar: TabBar,
273 pub pane_divider: Border,
274 pub leader_border_opacity: f32,
275 pub leader_border_width: f32,
276 pub dock: Dock,
277 pub status_bar: StatusBar,
278 pub toolbar: Toolbar,
279 pub disconnected_overlay: ContainedText,
280 pub modal: ContainerStyle,
281 pub zoomed_panel_foreground: ContainerStyle,
282 pub zoomed_pane_foreground: ContainerStyle,
283 pub zoomed_background: ContainerStyle,
284 pub notification: ContainerStyle,
285 pub notifications: Notifications,
286 pub joining_project_avatar: ImageStyle,
287 pub joining_project_message: ContainedText,
288 pub external_location_message: ContainedText,
289 pub drop_target_overlay_color: Color,
290}
291
292#[derive(Clone, Deserialize, Default)]
293pub struct BlankPaneStyle {
294 pub logo: SvgStyle,
295 pub logo_shadow: SvgStyle,
296 pub logo_container: ContainerStyle,
297 pub keyboard_hints: ContainerStyle,
298 pub keyboard_hint: Interactive<ContainedText>,
299 pub keyboard_hint_width: f32,
300}
301
302#[derive(Clone, Deserialize, Default)]
303pub struct Titlebar {
304 #[serde(flatten)]
305 pub container: ContainerStyle,
306 pub height: f32,
307 pub menu: TitlebarMenu,
308 pub project_menu_button: Toggleable<Interactive<ContainedText>>,
309 pub git_menu_button: Toggleable<Interactive<ContainedText>>,
310 pub project_host: Interactive<ContainedText>,
311 pub item_spacing: f32,
312 pub face_pile_spacing: f32,
313 pub avatar_ribbon: AvatarRibbon,
314 pub follower_avatar_overlap: f32,
315 pub leader_selection: ContainerStyle,
316 pub offline_icon: OfflineIcon,
317 pub leader_avatar: AvatarStyle,
318 pub follower_avatar: AvatarStyle,
319 pub inactive_avatar_grayscale: bool,
320 pub sign_in_button: Toggleable<Interactive<ContainedText>>,
321 pub outdated_warning: ContainedText,
322 pub share_button: Toggleable<Interactive<ContainedText>>,
323 pub muted: Color,
324 pub speaking: Color,
325 pub screen_share_button: Toggleable<Interactive<IconButton>>,
326 pub toggle_contacts_button: Toggleable<Interactive<IconButton>>,
327 pub toggle_microphone_button: Toggleable<Interactive<IconButton>>,
328 pub toggle_speakers_button: Toggleable<Interactive<IconButton>>,
329 pub leave_call_button: Interactive<IconButton>,
330 pub toggle_contacts_badge: ContainerStyle,
331 pub user_menu: UserMenu,
332}
333
334#[derive(Clone, Deserialize, Default)]
335pub struct TitlebarMenu {
336 pub width: f32,
337 pub height: f32,
338}
339
340#[derive(Clone, Deserialize, Default)]
341pub struct UserMenu {
342 pub user_menu_button_online: UserMenuButton,
343 pub user_menu_button_offline: UserMenuButton,
344}
345
346#[derive(Clone, Deserialize, Default)]
347pub struct UserMenuButton {
348 pub user_menu: Toggleable<Interactive<Icon>>,
349 pub avatar: AvatarStyle,
350 pub icon: Icon,
351}
352
353#[derive(Copy, Clone, Deserialize, Default)]
354pub struct AvatarStyle {
355 #[serde(flatten)]
356 pub image: ImageStyle,
357 pub outer_width: f32,
358 pub outer_corner_radius: f32,
359}
360
361#[derive(Deserialize, Default, Clone)]
362pub struct Copilot {
363 pub out_link_icon: Interactive<IconStyle>,
364 // pub modal: ModalStyle,
365 pub auth: CopilotAuth,
366}
367
368pub type CopilotCTAButton = Interactive<ContainedText>;
369
370#[derive(Deserialize, Default, Clone)]
371pub struct CopilotAuth {
372 pub content_width: f32,
373 pub prompting: CopilotAuthPrompting,
374 pub not_authorized: CopilotAuthNotAuthorized,
375 pub authorized: CopilotAuthAuthorized,
376 pub cta_button: CopilotCTAButton,
377 pub header: IconStyle,
378}
379
380#[derive(Deserialize, Default, Clone)]
381pub struct CopilotAuthPrompting {
382 pub subheading: ContainedText,
383 pub hint: ContainedText,
384 pub device_code: DeviceCode,
385}
386
387#[derive(Deserialize, Default, Clone)]
388pub struct DeviceCode {
389 pub text: TextStyle,
390 pub cta: CopilotCTAButton,
391 pub left: f32,
392 pub left_container: ContainerStyle,
393 pub right: f32,
394 pub right_container: Interactive<ContainerStyle>,
395}
396
397#[derive(Deserialize, Default, Clone)]
398pub struct CopilotAuthNotAuthorized {
399 pub subheading: ContainedText,
400 pub warning: ContainedText,
401}
402
403#[derive(Deserialize, Default, Clone)]
404pub struct CopilotAuthAuthorized {
405 pub subheading: ContainedText,
406 pub hint: ContainedText,
407}
408
409#[derive(Deserialize, Default)]
410pub struct CollabPanel {
411 #[serde(flatten)]
412 pub container: ContainerStyle,
413 // pub disclosure: DisclosureStyle<()>,
414 pub list_empty_state: Toggleable<Interactive<ContainedText>>,
415 pub list_empty_icon: Icon,
416 pub list_empty_label_container: ContainerStyle,
417 pub log_in_button: Interactive<ContainedText>,
418 pub channel_editor: ContainerStyle,
419 pub channel_hash: Icon,
420 pub channel_note_active_color: Color,
421 pub tabbed_modal: TabbedModal,
422 pub contact_finder: ContactFinder,
423 pub channel_modal: ChannelModal,
424 pub user_query_editor: FieldEditor,
425 pub user_query_editor_height: f32,
426 pub leave_call_button: Toggleable<Interactive<IconButton>>,
427 pub add_contact_button: Toggleable<Interactive<IconButton>>,
428 pub add_channel_button: Toggleable<Interactive<IconButton>>,
429 pub header_row: ContainedText,
430 pub dragged_over_header: ContainerStyle,
431 pub subheader_row: Toggleable<Interactive<ContainedText>>,
432 pub leave_call: Interactive<ContainedText>,
433 pub contact_row: Toggleable<Interactive<ContainerStyle>>,
434 pub channel_row: Toggleable<Interactive<ContainerStyle>>,
435 pub channel_name: Toggleable<ContainedText>,
436 pub row_height: f32,
437 pub project_row: Toggleable<Interactive<ProjectRow>>,
438 pub tree_branch: Toggleable<Interactive<TreeBranch>>,
439 pub contact_avatar: ImageStyle,
440 pub channel_avatar: ImageStyle,
441 pub extra_participant_label: ContainedText,
442 pub contact_status_free: ContainerStyle,
443 pub contact_status_busy: ContainerStyle,
444 pub contact_username: ContainedText,
445 pub contact_button: Interactive<IconButton>,
446 pub contact_button_spacing: f32,
447 pub channel_indent: f32,
448 pub disabled_button: IconButton,
449 pub section_icon_size: f32,
450 pub calling_indicator: ContainedText,
451 pub face_overlap: f32,
452}
453
454#[derive(Deserialize, Default)]
455pub struct TabbedModal {
456 pub tab_button: Toggleable<Interactive<ContainedText>>,
457 pub modal: ContainerStyle,
458 pub header: ContainerStyle,
459 pub body: ContainerStyle,
460 pub title: ContainedText,
461 pub visibility_toggle: Interactive<ContainedText>,
462 pub channel_link: Interactive<ContainedText>,
463 pub picker: Picker,
464 pub max_height: f32,
465 pub max_width: f32,
466 pub row_height: f32,
467}
468
469#[derive(Deserialize, Default)]
470pub struct ChannelModal {
471 pub contact_avatar: ImageStyle,
472 pub contact_username: ContainerStyle,
473 pub remove_member_button: ContainedText,
474 pub cancel_invite_button: ContainedText,
475 pub member_icon: IconButton,
476 pub invitee_icon: IconButton,
477 pub member_tag: ContainedText,
478}
479
480#[derive(Deserialize, Default)]
481pub struct ProjectRow {
482 #[serde(flatten)]
483 pub container: ContainerStyle,
484 pub icon: Icon,
485 pub name: ContainedText,
486}
487
488#[derive(Deserialize, Default, Clone, Copy)]
489pub struct TreeBranch {
490 pub width: f32,
491 pub color: Color,
492}
493
494#[derive(Deserialize, Default)]
495pub struct ContactFinder {
496 pub contact_avatar: ImageStyle,
497 pub contact_username: ContainerStyle,
498 pub contact_button: IconButton,
499 pub disabled_contact_button: IconButton,
500}
501
502#[derive(Deserialize, Default)]
503pub struct DropdownMenu {
504 #[serde(flatten)]
505 pub container: ContainerStyle,
506 pub header: Interactive<DropdownMenuItem>,
507 pub section_header: ContainedText,
508 pub item: Toggleable<Interactive<DropdownMenuItem>>,
509 pub row_height: f32,
510}
511
512#[derive(Deserialize, Default)]
513pub struct DropdownMenuItem {
514 #[serde(flatten)]
515 pub container: ContainerStyle,
516 #[serde(flatten)]
517 pub text: TextStyle,
518 pub secondary_text: Option<TextStyle>,
519 #[serde(default)]
520 pub secondary_text_spacing: f32,
521}
522
523#[derive(Clone, Deserialize, Default)]
524pub struct TabBar {
525 #[serde(flatten)]
526 pub container: ContainerStyle,
527 pub pane_button: Toggleable<Interactive<IconButton>>,
528 pub pane_button_container: ContainerStyle,
529 pub active_pane: TabStyles,
530 pub inactive_pane: TabStyles,
531 pub dragged_tab: Tab,
532 pub height: f32,
533 pub nav_button: Interactive<IconButton>,
534}
535
536impl TabBar {
537 pub fn tab_style(&self, pane_active: bool, tab_active: bool) -> &Tab {
538 let tabs = if pane_active {
539 &self.active_pane
540 } else {
541 &self.inactive_pane
542 };
543
544 if tab_active {
545 &tabs.active_tab
546 } else {
547 &tabs.inactive_tab
548 }
549 }
550}
551
552#[derive(Clone, Deserialize, Default)]
553pub struct TabStyles {
554 pub active_tab: Tab,
555 pub inactive_tab: Tab,
556}
557
558#[derive(Clone, Deserialize, Default)]
559pub struct AvatarRibbon {
560 #[serde(flatten)]
561 pub container: ContainerStyle,
562 pub width: f32,
563 pub height: f32,
564}
565
566#[derive(Clone, Deserialize, Default)]
567pub struct OfflineIcon {
568 #[serde(flatten)]
569 pub container: ContainerStyle,
570 pub width: f32,
571 pub color: Color,
572}
573
574#[derive(Clone, Debug, Deserialize, Default)]
575pub struct LabelStyle {
576 pub text: TextStyle,
577 pub highlight_text: Option<TextStyle>,
578}
579
580#[derive(Clone, Deserialize, Default)]
581pub struct Tab {
582 pub height: f32,
583 #[serde(flatten)]
584 pub container: ContainerStyle,
585 #[serde(flatten)]
586 pub label: LabelStyle,
587 pub description: ContainedText,
588 pub spacing: f32,
589 pub close_icon_width: f32,
590 pub type_icon_width: f32,
591 pub icon_close: Color,
592 pub icon_close_active: Color,
593 pub icon_dirty: Color,
594 pub icon_conflict: Color,
595 pub git: GitProjectStatus,
596}
597
598#[derive(Clone, Deserialize, Default)]
599pub struct Toolbar {
600 #[serde(flatten)]
601 pub container: ContainerStyle,
602 pub height: f32,
603 pub item_spacing: f32,
604 pub toggleable_tool: Toggleable<Interactive<IconButton>>,
605 pub toggleable_text_tool: Toggleable<Interactive<ContainedText>>,
606 pub breadcrumb_height: f32,
607 pub breadcrumbs: Interactive<ContainedText>,
608}
609
610#[derive(Clone, Deserialize, Default)]
611pub struct Notifications {
612 #[serde(flatten)]
613 pub container: ContainerStyle,
614 pub width: f32,
615}
616
617#[derive(Clone, Deserialize, Default)]
618pub struct Search {
619 #[serde(flatten)]
620 pub container: ContainerStyle,
621 pub editor: FindEditor,
622 pub invalid_editor: ContainerStyle,
623 pub option_button_group: ContainerStyle,
624 pub include_exclude_editor: FindEditor,
625 pub invalid_include_exclude_editor: ContainerStyle,
626 pub include_exclude_inputs: ContainedText,
627 pub option_button_component: ToggleIconButtonStyle,
628 pub match_background: Color,
629 pub match_index: ContainedText,
630 pub major_results_status: TextStyle,
631 pub minor_results_status: TextStyle,
632 pub editor_icon: IconStyle,
633 pub mode_button: Toggleable<Interactive<ContainedText>>,
634 pub nav_button: Toggleable<Interactive<ContainedLabel>>,
635 pub search_bar_row_height: f32,
636 pub search_row_spacing: f32,
637 pub option_button_height: f32,
638 pub modes_container: ContainerStyle,
639 pub replace_icon: IconStyle,
640 // Used for filters and replace
641 pub option_button: Toggleable<Interactive<IconButton>>,
642 pub action_button: IconButtonStyle,
643}
644
645#[derive(Clone, Deserialize, Default)]
646pub struct FindEditor {
647 #[serde(flatten)]
648 pub input: FieldEditor,
649 pub min_width: f32,
650 pub max_width: f32,
651}
652
653#[derive(Deserialize, Default)]
654pub struct StatusBar {
655 #[serde(flatten)]
656 pub container: ContainerStyle,
657 pub height: f32,
658 pub item_spacing: f32,
659 pub cursor_position: TextStyle,
660 pub vim_mode_indicator: ContainedText,
661 pub active_language: Interactive<ContainedText>,
662 pub auto_update_progress_message: TextStyle,
663 pub auto_update_done_message: TextStyle,
664 pub lsp_status: Interactive<StatusBarLspStatus>,
665 pub panel_buttons: StatusBarPanelButtons,
666 pub diagnostic_summary: Interactive<StatusBarDiagnosticSummary>,
667 pub diagnostic_message: Interactive<ContainedText>,
668}
669
670#[derive(Deserialize, Default)]
671pub struct StatusBarPanelButtons {
672 pub group_left: ContainerStyle,
673 pub group_bottom: ContainerStyle,
674 pub group_right: ContainerStyle,
675 pub button: Toggleable<Interactive<PanelButton>>,
676}
677
678#[derive(Deserialize, Default)]
679pub struct StatusBarDiagnosticSummary {
680 pub container_ok: ContainerStyle,
681 pub container_warning: ContainerStyle,
682 pub container_error: ContainerStyle,
683 pub text: TextStyle,
684 pub icon_color_ok: Color,
685 pub icon_color_warning: Color,
686 pub icon_color_error: Color,
687 pub height: f32,
688 pub icon_width: f32,
689 pub icon_spacing: f32,
690 pub summary_spacing: f32,
691}
692
693#[derive(Deserialize, Default)]
694pub struct StatusBarLspStatus {
695 #[serde(flatten)]
696 pub container: ContainerStyle,
697 pub height: f32,
698 pub icon_spacing: f32,
699 pub icon_color: Color,
700 pub icon_width: f32,
701 pub message: TextStyle,
702}
703
704#[derive(Deserialize, Default)]
705pub struct Dock {
706 pub left: ContainerStyle,
707 pub bottom: ContainerStyle,
708 pub right: ContainerStyle,
709}
710
711#[derive(Clone, Deserialize, Default)]
712pub struct PanelButton {
713 #[serde(flatten)]
714 pub container: ContainerStyle,
715 pub icon_color: Color,
716 pub icon_size: f32,
717 pub label: ContainedText,
718}
719
720#[derive(Deserialize, Default)]
721pub struct ProjectPanel {
722 #[serde(flatten)]
723 pub container: ContainerStyle,
724 pub entry: Toggleable<Interactive<ProjectPanelEntry>>,
725 pub dragged_entry: ProjectPanelEntry,
726 pub ignored_entry: Toggleable<Interactive<ProjectPanelEntry>>,
727 pub cut_entry: Toggleable<Interactive<ProjectPanelEntry>>,
728 pub filename_editor: FieldEditor,
729 pub indent_width: f32,
730 pub open_project_button: Interactive<ContainedText>,
731}
732
733#[derive(Clone, Debug, Deserialize, Default)]
734pub struct ProjectPanelEntry {
735 pub height: f32,
736 #[serde(flatten)]
737 pub container: ContainerStyle,
738 pub text: TextStyle,
739 pub icon_size: f32,
740 pub icon_color: Color,
741 pub chevron_color: Color,
742 pub chevron_size: f32,
743 pub icon_spacing: f32,
744 pub status: EntryStatus,
745}
746
747#[derive(Clone, Debug, Deserialize, Default)]
748pub struct EntryStatus {
749 pub git: GitProjectStatus,
750}
751
752#[derive(Clone, Debug, Deserialize, Default)]
753pub struct GitProjectStatus {
754 pub modified: Color,
755 pub inserted: Color,
756 pub conflict: Color,
757}
758
759#[derive(Clone, Debug, Deserialize, Default)]
760pub struct ContextMenu {
761 #[serde(flatten)]
762 pub container: ContainerStyle,
763 pub item: Toggleable<Interactive<ContextMenuItem>>,
764 pub keystroke_margin: f32,
765 pub separator: ContainerStyle,
766}
767
768#[derive(Clone, Debug, Deserialize, Default)]
769pub struct ContextMenuItem {
770 #[serde(flatten)]
771 pub container: ContainerStyle,
772 pub label: TextStyle,
773 pub keystroke: ContainedText,
774 pub icon_width: f32,
775 pub icon_spacing: f32,
776}
777
778#[derive(Debug, Deserialize, Default)]
779pub struct CommandPalette {
780 pub key: Toggleable<ContainedLabel>,
781 pub keystroke_spacing: f32,
782}
783
784#[derive(Deserialize, Default)]
785pub struct InviteLink {
786 #[serde(flatten)]
787 pub container: ContainerStyle,
788 #[serde(flatten)]
789 pub label: LabelStyle,
790 pub icon: Icon,
791}
792
793#[derive(Deserialize, Clone, Copy, Default)]
794pub struct Icon {
795 #[serde(flatten)]
796 pub container: ContainerStyle,
797 pub color: Color,
798 pub width: f32,
799}
800
801#[derive(Deserialize, Clone, Copy, Default)]
802pub struct IconButton {
803 #[serde(flatten)]
804 pub container: ContainerStyle,
805 pub color: Color,
806 pub icon_width: f32,
807 pub button_width: f32,
808}
809
810#[derive(Deserialize, Default)]
811pub struct ChatPanel {
812 #[serde(flatten)]
813 pub container: ContainerStyle,
814 pub list: ContainerStyle,
815 pub channel_select: ChannelSelect,
816 pub input_editor: FieldEditor,
817 pub avatar: AvatarStyle,
818 pub avatar_container: ContainerStyle,
819 pub rich_text: RichTextStyle,
820 pub message_sender: ContainedText,
821 pub message_timestamp: ContainedText,
822 pub message: Interactive<ContainerStyle>,
823 pub continuation_message: Interactive<ContainerStyle>,
824 pub pending_message: Interactive<ContainerStyle>,
825 pub last_message_bottom_spacing: f32,
826 pub sign_in_prompt: Interactive<TextStyle>,
827 pub icon_button: Interactive<IconButton>,
828}
829
830#[derive(Clone, Deserialize, Default)]
831pub struct RichTextStyle {
832 pub text: TextStyle,
833 pub mention_highlight: HighlightStyle,
834 pub mention_background: Option<Color>,
835 pub self_mention_highlight: HighlightStyle,
836 pub self_mention_background: Option<Color>,
837 pub code_background: Option<Color>,
838}
839
840#[derive(Deserialize, Default)]
841pub struct NotificationPanel {
842 #[serde(flatten)]
843 pub container: ContainerStyle,
844 pub title: ContainedText,
845 pub title_icon: SvgStyle,
846 pub title_height: f32,
847 pub list: ContainerStyle,
848 pub avatar: AvatarStyle,
849 pub avatar_container: ContainerStyle,
850 pub sign_in_prompt: Interactive<TextStyle>,
851 pub icon_button: Interactive<IconButton>,
852 pub unread_text: ContainedText,
853 pub read_text: ContainedText,
854 pub timestamp: ContainedText,
855 pub button: Interactive<ContainedText>,
856}
857
858#[derive(Deserialize, Default)]
859pub struct ChannelSelect {
860 #[serde(flatten)]
861 pub container: ContainerStyle,
862 pub header: ChannelName,
863 pub item: ChannelName,
864 pub active_item: ChannelName,
865 pub hovered_item: ChannelName,
866 pub menu: ContainerStyle,
867}
868
869#[derive(Deserialize, Default)]
870pub struct ChannelName {
871 #[serde(flatten)]
872 pub container: ContainerStyle,
873 pub hash: ContainedText,
874 pub name: TextStyle,
875}
876
877#[derive(Clone, Deserialize, Default)]
878pub struct Picker {
879 #[serde(flatten)]
880 pub container: ContainerStyle,
881 pub empty_container: ContainerStyle,
882 pub input_editor: FieldEditor,
883 pub empty_input_editor: FieldEditor,
884 pub no_matches: ContainedLabel,
885 pub item: Toggleable<Interactive<ContainedLabel>>,
886 pub header: ContainedLabel,
887 pub footer: Interactive<ContainedLabel>,
888}
889
890#[derive(Clone, Debug, Deserialize, Default)]
891pub struct ContainedText {
892 #[serde(flatten)]
893 pub container: ContainerStyle,
894 #[serde(flatten)]
895 pub text: TextStyle,
896}
897
898#[derive(Clone, Debug, Deserialize, Default)]
899pub struct ContainedLabel {
900 #[serde(flatten)]
901 pub container: ContainerStyle,
902 #[serde(flatten)]
903 pub label: LabelStyle,
904}
905
906#[derive(Clone, Deserialize, Default)]
907pub struct ProjectDiagnostics {
908 #[serde(flatten)]
909 pub container: ContainerStyle,
910 pub empty_message: TextStyle,
911 pub tab_icon_width: f32,
912 pub tab_icon_spacing: f32,
913 pub tab_summary_spacing: f32,
914}
915
916#[derive(Deserialize, Default)]
917pub struct ContactNotification {
918 pub header_avatar: ImageStyle,
919 pub header_message: ContainedText,
920 pub header_height: f32,
921 pub body_message: ContainedText,
922 pub button: Interactive<ContainedText>,
923 pub dismiss_button: Interactive<IconButton>,
924}
925
926#[derive(Deserialize, Default)]
927pub struct UpdateNotification {
928 pub message: ContainedText,
929 pub action_message: Interactive<ContainedText>,
930 pub dismiss_button: Interactive<IconButton>,
931}
932
933#[derive(Deserialize, Default)]
934pub struct MessageNotification {
935 pub message: ContainedText,
936 pub action_message: Interactive<ContainedText>,
937 pub dismiss_button: Interactive<IconButton>,
938}
939
940#[derive(Deserialize, Default)]
941pub struct ProjectSharedNotification {
942 pub window_height: f32,
943 pub window_width: f32,
944 #[serde(default)]
945 pub background: Color,
946 pub owner_container: ContainerStyle,
947 pub owner_avatar: ImageStyle,
948 pub owner_metadata: ContainerStyle,
949 pub owner_username: ContainedText,
950 pub message: ContainedText,
951 pub worktree_roots: ContainedText,
952 pub button_width: f32,
953 pub open_button: ContainedText,
954 pub dismiss_button: ContainedText,
955}
956
957#[derive(Deserialize, Default)]
958pub struct IncomingCallNotification {
959 pub window_height: f32,
960 pub window_width: f32,
961 #[serde(default)]
962 pub background: Color,
963 pub caller_container: ContainerStyle,
964 pub caller_avatar: ImageStyle,
965 pub caller_metadata: ContainerStyle,
966 pub caller_username: ContainedText,
967 pub caller_message: ContainedText,
968 pub worktree_roots: ContainedText,
969 pub button_width: f32,
970 pub accept_button: ContainedText,
971 pub decline_button: ContainedText,
972}
973
974#[derive(Clone, Deserialize, Default)]
975pub struct Editor {
976 pub text_color: Color,
977 #[serde(default)]
978 pub background: Color,
979 pub selection: SelectionStyle,
980 pub gutter_background: Color,
981 pub gutter_padding_factor: f32,
982 pub active_line_background: Color,
983 pub highlighted_line_background: Color,
984 pub rename_fade: f32,
985 pub document_highlight_read_background: Color,
986 pub document_highlight_write_background: Color,
987 pub diff: DiffStyle,
988 pub wrap_guide: Color,
989 pub active_wrap_guide: Color,
990 pub line_number: Color,
991 pub line_number_active: Color,
992 pub guest_selections: Vec<SelectionStyle>,
993 pub absent_selection: SelectionStyle,
994 pub syntax: Arc<SyntaxTheme>,
995 pub hint: HighlightStyle,
996 pub suggestion: HighlightStyle,
997 pub diagnostic_path_header: DiagnosticPathHeader,
998 pub diagnostic_header: DiagnosticHeader,
999 pub error_diagnostic: DiagnosticStyle,
1000 pub invalid_error_diagnostic: DiagnosticStyle,
1001 pub warning_diagnostic: DiagnosticStyle,
1002 pub invalid_warning_diagnostic: DiagnosticStyle,
1003 pub information_diagnostic: DiagnosticStyle,
1004 pub invalid_information_diagnostic: DiagnosticStyle,
1005 pub hint_diagnostic: DiagnosticStyle,
1006 pub invalid_hint_diagnostic: DiagnosticStyle,
1007 pub autocomplete: AutocompleteStyle,
1008 pub code_actions: CodeActions,
1009 pub folds: Folds,
1010 pub unnecessary_code_fade: f32,
1011 pub hover_popover: HoverPopover,
1012 pub link_definition: HighlightStyle,
1013 pub composition_mark: HighlightStyle,
1014 pub jump_icon: Interactive<IconButton>,
1015 pub scrollbar: Scrollbar,
1016 pub whitespace: Color,
1017}
1018
1019#[derive(Clone, Deserialize, Default)]
1020pub struct Scrollbar {
1021 pub track: ContainerStyle,
1022 pub thumb: ContainerStyle,
1023 pub width: f32,
1024 pub min_height_factor: f32,
1025 pub git: BufferGitDiffColors,
1026 pub selections: Color,
1027}
1028
1029#[derive(Clone, Deserialize, Default)]
1030pub struct BufferGitDiffColors {
1031 pub inserted: Color,
1032 pub modified: Color,
1033 pub deleted: Color,
1034}
1035
1036#[derive(Clone, Deserialize, Default)]
1037pub struct DiagnosticPathHeader {
1038 #[serde(flatten)]
1039 pub container: ContainerStyle,
1040 pub filename: ContainedText,
1041 pub path: ContainedText,
1042 pub text_scale_factor: f32,
1043}
1044
1045#[derive(Clone, Deserialize, Default)]
1046pub struct DiagnosticHeader {
1047 #[serde(flatten)]
1048 pub container: ContainerStyle,
1049 pub source: ContainedLabel,
1050 pub message: ContainedLabel,
1051 pub code: ContainedText,
1052 pub text_scale_factor: f32,
1053 pub icon_width_factor: f32,
1054}
1055
1056#[derive(Clone, Deserialize, Default)]
1057pub struct DiagnosticStyle {
1058 pub message: LabelStyle,
1059 #[serde(default)]
1060 pub header: ContainerStyle,
1061 pub text_scale_factor: f32,
1062}
1063
1064#[derive(Clone, Deserialize, Default)]
1065pub struct AutocompleteStyle {
1066 #[serde(flatten)]
1067 pub container: ContainerStyle,
1068 pub item: ContainerStyle,
1069 pub selected_item: ContainerStyle,
1070 pub hovered_item: ContainerStyle,
1071 pub match_highlight: HighlightStyle,
1072 pub completion_min_width: f32,
1073 pub completion_max_width: f32,
1074 pub inline_docs_container: ContainerStyle,
1075 pub inline_docs_color: Color,
1076 pub inline_docs_size_percent: f32,
1077 pub alongside_docs_max_width: f32,
1078 pub alongside_docs_container: ContainerStyle,
1079}
1080
1081#[derive(Clone, Copy, Default, Deserialize)]
1082pub struct SelectionStyle {
1083 pub cursor: Color,
1084 pub selection: Color,
1085}
1086
1087#[derive(Clone, Deserialize, Default)]
1088pub struct FieldEditor {
1089 #[serde(flatten)]
1090 pub container: ContainerStyle,
1091 pub text: TextStyle,
1092 #[serde(default)]
1093 pub placeholder_text: Option<TextStyle>,
1094 pub selection: SelectionStyle,
1095}
1096
1097#[derive(Clone, Deserialize, Default)]
1098pub struct InteractiveColor {
1099 pub color: Color,
1100}
1101
1102#[derive(Clone, Deserialize, Default)]
1103pub struct CodeActions {
1104 #[serde(default)]
1105 pub indicator: Toggleable<Interactive<InteractiveColor>>,
1106 pub vertical_scale: f32,
1107}
1108
1109#[derive(Clone, Deserialize, Default)]
1110pub struct Folds {
1111 pub indicator: Toggleable<Interactive<InteractiveColor>>,
1112 pub ellipses: FoldEllipses,
1113 pub fold_background: Color,
1114 pub icon_margin_scale: f32,
1115 pub folded_icon: String,
1116 pub foldable_icon: String,
1117}
1118
1119#[derive(Clone, Deserialize, Default)]
1120pub struct FoldEllipses {
1121 pub text_color: Color,
1122 pub background: Interactive<InteractiveColor>,
1123 pub corner_radius_factor: f32,
1124}
1125
1126#[derive(Clone, Deserialize, Default)]
1127pub struct DiffStyle {
1128 pub inserted: Color,
1129 pub modified: Color,
1130 pub deleted: Color,
1131 pub removed_width_em: f32,
1132 pub width_em: f32,
1133 pub corner_radius: f32,
1134}
1135
1136#[derive(Debug, Default, Clone, Copy)]
1137pub struct Interactive<T> {
1138 pub default: T,
1139 pub hovered: Option<T>,
1140 pub clicked: Option<T>,
1141 pub disabled: Option<T>,
1142}
1143
1144impl<T> Deref for Interactive<T> {
1145 type Target = T;
1146
1147 fn deref(&self) -> &Self::Target {
1148 &self.default
1149 }
1150}
1151
1152impl Interactive<()> {
1153 pub fn new_blank() -> Self {
1154 Self {
1155 default: (),
1156 hovered: None,
1157 clicked: None,
1158 disabled: None,
1159 }
1160 }
1161}
1162
1163#[derive(Clone, Copy, Debug, Default, Deserialize)]
1164pub struct Toggleable<T> {
1165 active: T,
1166 inactive: T,
1167}
1168
1169impl<T> Deref for Toggleable<T> {
1170 type Target = T;
1171
1172 fn deref(&self) -> &Self::Target {
1173 &self.inactive
1174 }
1175}
1176
1177impl Toggleable<()> {
1178 pub fn new_blank() -> Self {
1179 Self {
1180 active: (),
1181 inactive: (),
1182 }
1183 }
1184}
1185
1186impl<T> Toggleable<T> {
1187 pub fn new(active: T, inactive: T) -> Self {
1188 Self { active, inactive }
1189 }
1190 pub fn in_state(&self, active: bool) -> &T {
1191 if active {
1192 &self.active
1193 } else {
1194 &self.inactive
1195 }
1196 }
1197 pub fn active_state(&self) -> &T {
1198 self.in_state(true)
1199 }
1200
1201 pub fn inactive_state(&self) -> &T {
1202 self.in_state(false)
1203 }
1204}
1205
1206impl<T> Interactive<T> {
1207 pub fn disabled_style(&self) -> &T {
1208 self.disabled.as_ref().unwrap_or(&self.default)
1209 }
1210}
1211
1212impl<T> Toggleable<Interactive<T>> {
1213 pub fn default_style(&self) -> &T {
1214 &self.inactive.default
1215 }
1216}
1217
1218impl<'de, T: DeserializeOwned> Deserialize<'de> for Interactive<T> {
1219 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1220 where
1221 D: serde::Deserializer<'de>,
1222 {
1223 #[derive(Deserialize)]
1224 struct Helper {
1225 default: Value,
1226 hovered: Option<Value>,
1227 clicked: Option<Value>,
1228 disabled: Option<Value>,
1229 }
1230
1231 let json = Helper::deserialize(deserializer)?;
1232
1233 let deserialize_state = |state_json: Option<Value>| -> Result<Option<T>, D::Error> {
1234 if let Some(mut state_json) = state_json {
1235 if let Value::Object(state_json) = &mut state_json {
1236 if let Value::Object(default) = &json.default {
1237 for (key, value) in default {
1238 if !state_json.contains_key(key) {
1239 state_json.insert(key.clone(), value.clone());
1240 }
1241 }
1242 }
1243 }
1244 Ok(Some(
1245 serde_json::from_value::<T>(state_json).map_err(serde::de::Error::custom)?,
1246 ))
1247 } else {
1248 Ok(None)
1249 }
1250 };
1251
1252 let hovered = deserialize_state(json.hovered)?;
1253 let clicked = deserialize_state(json.clicked)?;
1254 let disabled = deserialize_state(json.disabled)?;
1255 let default = serde_json::from_value(json.default).map_err(serde::de::Error::custom)?;
1256
1257 Ok(Interactive {
1258 default,
1259 hovered,
1260 clicked,
1261 disabled,
1262 })
1263 }
1264}
1265
1266impl Editor {
1267 pub fn selection_style_for_room_participant(&self, participant_index: u32) -> SelectionStyle {
1268 if self.guest_selections.is_empty() {
1269 return SelectionStyle::default();
1270 }
1271 let style_ix = participant_index as usize % self.guest_selections.len();
1272 self.guest_selections[style_ix]
1273 }
1274}
1275
1276#[derive(Default)]
1277pub struct SyntaxTheme {
1278 pub highlights: Vec<(String, HighlightStyle)>,
1279}
1280
1281impl SyntaxTheme {
1282 pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
1283 Self { highlights }
1284 }
1285}
1286
1287impl<'de> Deserialize<'de> for SyntaxTheme {
1288 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1289 where
1290 D: serde::Deserializer<'de>,
1291 {
1292 let syntax_data: HashMap<String, HighlightStyle> = Deserialize::deserialize(deserializer)?;
1293
1294 let mut result = Self::new(Vec::new());
1295 for (key, style) in syntax_data {
1296 match result
1297 .highlights
1298 .binary_search_by(|(needle, _)| needle.cmp(&key))
1299 {
1300 Ok(i) | Err(i) => {
1301 result.highlights.insert(i, (key, style));
1302 }
1303 }
1304 }
1305
1306 Ok(result)
1307 }
1308}
1309
1310#[derive(Clone, Deserialize, Default)]
1311pub struct HoverPopover {
1312 pub container: ContainerStyle,
1313 pub info_container: ContainerStyle,
1314 pub warning_container: ContainerStyle,
1315 pub error_container: ContainerStyle,
1316 pub block_style: ContainerStyle,
1317 pub prose: TextStyle,
1318 pub diagnostic_source_highlight: HighlightStyle,
1319 pub highlight: Color,
1320}
1321
1322#[derive(Clone, Deserialize, Default)]
1323pub struct TerminalStyle {
1324 pub black: Color,
1325 pub red: Color,
1326 pub green: Color,
1327 pub yellow: Color,
1328 pub blue: Color,
1329 pub magenta: Color,
1330 pub cyan: Color,
1331 pub white: Color,
1332 pub bright_black: Color,
1333 pub bright_red: Color,
1334 pub bright_green: Color,
1335 pub bright_yellow: Color,
1336 pub bright_blue: Color,
1337 pub bright_magenta: Color,
1338 pub bright_cyan: Color,
1339 pub bright_white: Color,
1340 pub foreground: Color,
1341 pub background: Color,
1342 pub modal_background: Color,
1343 pub cursor: Color,
1344 pub dim_black: Color,
1345 pub dim_red: Color,
1346 pub dim_green: Color,
1347 pub dim_yellow: Color,
1348 pub dim_blue: Color,
1349 pub dim_magenta: Color,
1350 pub dim_cyan: Color,
1351 pub dim_white: Color,
1352 pub bright_foreground: Color,
1353 pub dim_foreground: Color,
1354}
1355
1356#[derive(Clone, Deserialize, Default)]
1357pub struct AssistantStyle {
1358 pub container: ContainerStyle,
1359 pub hamburger_button: Interactive<IconStyle>,
1360 pub split_button: Interactive<IconStyle>,
1361 pub assist_button: Interactive<IconStyle>,
1362 pub quote_button: Interactive<IconStyle>,
1363 pub zoom_in_button: Interactive<IconStyle>,
1364 pub zoom_out_button: Interactive<IconStyle>,
1365 pub plus_button: Interactive<IconStyle>,
1366 pub title: ContainedText,
1367 pub message_header: ContainerStyle,
1368 pub sent_at: ContainedText,
1369 pub user_sender: Interactive<ContainedText>,
1370 pub assistant_sender: Interactive<ContainedText>,
1371 pub system_sender: Interactive<ContainedText>,
1372 pub model: Interactive<ContainedText>,
1373 pub remaining_tokens: ContainedText,
1374 pub low_remaining_tokens: ContainedText,
1375 pub no_remaining_tokens: ContainedText,
1376 pub error_icon: Icon,
1377 pub api_key_editor: FieldEditor,
1378 pub api_key_prompt: ContainedText,
1379 pub saved_conversation: SavedConversation,
1380 pub inline: InlineAssistantStyle,
1381}
1382
1383#[derive(Clone, Deserialize, Default)]
1384pub struct ButtonStyle<C> {
1385 #[serde(flatten)]
1386 pub container: ContainerStyle,
1387 // TODO: These are incorrect for the intended usage of the buttons.
1388 // The size should be constant, but putting them here duplicates them
1389 // across the states the buttons can be in
1390 pub button_width: Option<f32>,
1391 pub button_height: Option<f32>,
1392 #[serde(flatten)]
1393 contents: C,
1394}
1395
1396pub type IconButtonStyle = Interactive<ButtonStyle<SvgStyle>>;
1397pub type ToggleIconButtonStyle = Toggleable<IconButtonStyle>;
1398
1399#[derive(Clone, Deserialize, Default)]
1400pub struct InlineAssistantStyle {
1401 #[serde(flatten)]
1402 pub container: ContainerStyle,
1403 pub editor: FieldEditor,
1404 pub disabled_editor: FieldEditor,
1405 pub pending_edit_background: Color,
1406 pub include_conversation: ToggleIconButtonStyle,
1407 pub retrieve_context: ToggleIconButtonStyle,
1408 pub context_status: ContextStatusStyle,
1409}
1410
1411#[derive(Clone, Deserialize, Default)]
1412pub struct ContextStatusStyle {
1413 pub error_icon: Icon,
1414 pub in_progress_icon: Icon,
1415 pub complete_icon: Icon,
1416}
1417
1418#[derive(Clone, Deserialize, Default)]
1419pub struct Contained<T> {
1420 container: ContainerStyle,
1421 contained: T,
1422}
1423
1424#[derive(Clone, Deserialize, Default)]
1425pub struct SavedConversation {
1426 pub container: Interactive<ContainerStyle>,
1427 pub saved_at: ContainedText,
1428 pub title: ContainedText,
1429}
1430
1431#[derive(Clone, Deserialize, Default)]
1432pub struct FeedbackStyle {
1433 pub submit_button: Interactive<ContainedText>,
1434 pub button_margin: f32,
1435 pub info_text_default: ContainedText,
1436 pub link_text_default: ContainedText,
1437 pub link_text_hover: ContainedText,
1438}
1439
1440#[derive(Clone, Deserialize, Default)]
1441pub struct CheckboxStyle {
1442 pub icon: SvgStyle,
1443 pub label: ContainedText,
1444 pub default: ContainerStyle,
1445 pub checked: ContainerStyle,
1446 pub hovered: ContainerStyle,
1447 pub hovered_and_checked: ContainerStyle,
1448}
1449
1450#[derive(Clone, Deserialize, Default)]
1451pub struct WelcomeStyle {
1452 pub page_width: f32,
1453 pub logo: SvgStyle,
1454 pub logo_subheading: ContainedText,
1455 pub usage_note: ContainedText,
1456 pub checkbox: CheckboxStyle,
1457 pub checkbox_container: ContainerStyle,
1458 pub button: Interactive<ContainedText>,
1459 pub button_group: ContainerStyle,
1460 pub heading_group: ContainerStyle,
1461 pub checkbox_group: ContainerStyle,
1462}
1463
1464#[derive(Clone, Deserialize, Default)]
1465pub struct ColorScheme {
1466 pub name: String,
1467 pub is_light: bool,
1468 pub lowest: Layer,
1469 pub middle: Layer,
1470 pub highest: Layer,
1471}
1472
1473#[derive(Clone, Deserialize, Default)]
1474pub struct Player {
1475 pub cursor: Color,
1476 pub selection: Color,
1477}
1478
1479#[derive(Clone, Deserialize, Default)]
1480pub struct RampSet {
1481 pub neutral: Vec<Color>,
1482 pub red: Vec<Color>,
1483 pub orange: Vec<Color>,
1484 pub yellow: Vec<Color>,
1485 pub green: Vec<Color>,
1486 pub cyan: Vec<Color>,
1487 pub blue: Vec<Color>,
1488 pub violet: Vec<Color>,
1489 pub magenta: Vec<Color>,
1490}
1491
1492#[derive(Clone, Deserialize, Default)]
1493pub struct Layer {
1494 pub base: StyleSet,
1495 pub variant: StyleSet,
1496 pub on: StyleSet,
1497 pub accent: StyleSet,
1498 pub positive: StyleSet,
1499 pub warning: StyleSet,
1500 pub negative: StyleSet,
1501}
1502
1503#[derive(Clone, Deserialize, Default)]
1504pub struct StyleSet {
1505 pub default: Style,
1506 pub active: Style,
1507 pub disabled: Style,
1508 pub hovered: Style,
1509 pub pressed: Style,
1510 pub inverted: Style,
1511}
1512
1513#[derive(Clone, Deserialize, Default)]
1514pub struct Style {
1515 pub background: Color,
1516 pub border: Color,
1517 pub foreground: Color,
1518}