theme.rs

   1mod theme_registry;
   2
   3use gpui::{
   4    color::Color,
   5    elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
   6    fonts::{HighlightStyle, TextStyle},
   7    Border, MouseState,
   8};
   9use serde::{de::DeserializeOwned, Deserialize};
  10use serde_json::Value;
  11use std::{collections::HashMap, sync::Arc};
  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}