schema.rs

   1use anyhow::Result;
   2use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla};
   3use indexmap::IndexMap;
   4use palette::FromColor;
   5use schemars::gen::SchemaGenerator;
   6use schemars::schema::{Schema, SchemaObject};
   7use schemars::JsonSchema;
   8use serde::{Deserialize, Deserializer, Serialize};
   9use serde_json::Value;
  10use serde_repr::{Deserialize_repr, Serialize_repr};
  11
  12use crate::{StatusColorsRefinement, ThemeColorsRefinement};
  13
  14pub(crate) fn try_parse_color(color: &str) -> Result<Hsla> {
  15    let rgba = gpui::Rgba::try_from(color)?;
  16    let rgba = palette::rgb::Srgba::from_components((rgba.r, rgba.g, rgba.b, rgba.a));
  17    let hsla = palette::Hsla::from_color(rgba);
  18
  19    let hsla = gpui::hsla(
  20        hsla.hue.into_positive_degrees() / 360.,
  21        hsla.saturation,
  22        hsla.lightness,
  23        hsla.alpha,
  24    );
  25
  26    Ok(hsla)
  27}
  28
  29#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, JsonSchema)]
  30#[serde(rename_all = "snake_case")]
  31pub enum AppearanceContent {
  32    Light,
  33    Dark,
  34}
  35
  36/// The content of a serialized theme family.
  37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
  38pub struct ThemeFamilyContent {
  39    pub name: String,
  40    pub author: String,
  41    pub themes: Vec<ThemeContent>,
  42}
  43
  44/// The content of a serialized theme.
  45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
  46pub struct ThemeContent {
  47    pub name: String,
  48    pub appearance: AppearanceContent,
  49    pub style: ThemeStyleContent,
  50}
  51
  52/// The content of a serialized theme.
  53#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
  54#[serde(default)]
  55pub struct ThemeStyleContent {
  56    #[serde(flatten, default)]
  57    pub colors: ThemeColorsContent,
  58
  59    #[serde(flatten, default)]
  60    pub status: StatusColorsContent,
  61
  62    #[serde(default)]
  63    pub players: Vec<PlayerColorContent>,
  64
  65    /// The styles for syntax nodes.
  66    #[serde(default)]
  67    pub syntax: IndexMap<String, HighlightStyleContent>,
  68}
  69
  70impl ThemeStyleContent {
  71    /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeContent`].
  72    #[inline(always)]
  73    pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement {
  74        self.colors.theme_colors_refinement()
  75    }
  76
  77    /// Returns a [`StatusColorsRefinement`] based on the colors in the [`ThemeContent`].
  78    #[inline(always)]
  79    pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
  80        self.status.status_colors_refinement()
  81    }
  82
  83    /// Returns the syntax style overrides in the [`ThemeContent`].
  84    pub fn syntax_overrides(&self) -> Vec<(String, HighlightStyle)> {
  85        self.syntax
  86            .iter()
  87            .map(|(key, style)| {
  88                (
  89                    key.clone(),
  90                    HighlightStyle {
  91                        color: style
  92                            .color
  93                            .as_ref()
  94                            .and_then(|color| try_parse_color(color).ok()),
  95                        font_style: style
  96                            .font_style
  97                            .map(|font_style| FontStyle::from(font_style)),
  98                        font_weight: style
  99                            .font_weight
 100                            .map(|font_weight| FontWeight::from(font_weight)),
 101                        ..Default::default()
 102                    },
 103                )
 104            })
 105            .collect()
 106    }
 107}
 108
 109#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
 110#[serde(default)]
 111pub struct ThemeColorsContent {
 112    /// Border color. Used for most borders, is usually a high contrast color.
 113    #[serde(rename = "border")]
 114    pub border: Option<String>,
 115
 116    /// Border color. Used for deemphasized borders, like a visual divider between two sections
 117    #[serde(rename = "border.variant")]
 118    pub border_variant: Option<String>,
 119
 120    /// Border color. Used for focused elements, like keyboard focused list item.
 121    #[serde(rename = "border.focused")]
 122    pub border_focused: Option<String>,
 123
 124    /// Border color. Used for selected elements, like an active search filter or selected checkbox.
 125    #[serde(rename = "border.selected")]
 126    pub border_selected: Option<String>,
 127
 128    /// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
 129    #[serde(rename = "border.transparent")]
 130    pub border_transparent: Option<String>,
 131
 132    /// Border color. Used for disabled elements, like a disabled input or button.
 133    #[serde(rename = "border.disabled")]
 134    pub border_disabled: Option<String>,
 135
 136    /// Border color. Used for elevated surfaces, like a context menu, popup, or dialog.
 137    #[serde(rename = "elevated_surface.background")]
 138    pub elevated_surface_background: Option<String>,
 139
 140    /// Background Color. Used for grounded surfaces like a panel or tab.
 141    #[serde(rename = "surface.background")]
 142    pub surface_background: Option<String>,
 143
 144    /// Background Color. Used for the app background and blank panels or windows.
 145    #[serde(rename = "background")]
 146    pub background: Option<String>,
 147
 148    /// Background Color. Used for the background of an element that should have a different background than the surface it's on.
 149    ///
 150    /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
 151    ///
 152    /// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
 153    #[serde(rename = "element.background")]
 154    pub element_background: Option<String>,
 155
 156    /// Background Color. Used for the hover state of an element that should have a different background than the surface it's on.
 157    ///
 158    /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
 159    #[serde(rename = "element.hover")]
 160    pub element_hover: Option<String>,
 161
 162    /// Background Color. Used for the active state of an element that should have a different background than the surface it's on.
 163    ///
 164    /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
 165    #[serde(rename = "element.active")]
 166    pub element_active: Option<String>,
 167
 168    /// Background Color. Used for the selected state of an element that should have a different background than the surface it's on.
 169    ///
 170    /// Selected states are triggered by the element being selected (or "activated") by the user.
 171    ///
 172    /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
 173    #[serde(rename = "element.selected")]
 174    pub element_selected: Option<String>,
 175
 176    /// Background Color. Used for the disabled state of an element that should have a different background than the surface it's on.
 177    ///
 178    /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
 179    #[serde(rename = "element.disabled")]
 180    pub element_disabled: Option<String>,
 181
 182    /// Background Color. Used for the area that shows where a dragged element will be dropped.
 183    #[serde(rename = "drop_target.background")]
 184    pub drop_target_background: Option<String>,
 185
 186    /// Used for the background of a ghost element that should have the same background as the surface it's on.
 187    ///
 188    /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
 189    ///
 190    /// For an element that should have a different background than the surface it's on, use `element_background`.
 191    #[serde(rename = "ghost_element.background")]
 192    pub ghost_element_background: Option<String>,
 193
 194    /// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
 195    ///
 196    /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
 197    #[serde(rename = "ghost_element.hover")]
 198    pub ghost_element_hover: Option<String>,
 199
 200    /// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
 201    ///
 202    /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
 203    #[serde(rename = "ghost_element.active")]
 204    pub ghost_element_active: Option<String>,
 205
 206    /// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
 207    ///
 208    /// Selected states are triggered by the element being selected (or "activated") by the user.
 209    ///
 210    /// This could include a selected checkbox, a toggleable button that is toggled on, etc.
 211    #[serde(rename = "ghost_element.selected")]
 212    pub ghost_element_selected: Option<String>,
 213
 214    /// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
 215    ///
 216    /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
 217    #[serde(rename = "ghost_element.disabled")]
 218    pub ghost_element_disabled: Option<String>,
 219
 220    /// Text Color. Default text color used for most text.
 221    #[serde(rename = "text")]
 222    pub text: Option<String>,
 223
 224    /// Text Color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
 225    #[serde(rename = "text.muted")]
 226    pub text_muted: Option<String>,
 227
 228    /// Text Color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
 229    #[serde(rename = "text.placeholder")]
 230    pub text_placeholder: Option<String>,
 231
 232    /// Text Color. Color used for text denoting disabled elements. Typically, the color is faded or grayed out to emphasize the disabled state.
 233    #[serde(rename = "text.disabled")]
 234    pub text_disabled: Option<String>,
 235
 236    /// Text Color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
 237    #[serde(rename = "text.accent")]
 238    pub text_accent: Option<String>,
 239
 240    /// Fill Color. Used for the default fill color of an icon.
 241    #[serde(rename = "icon")]
 242    pub icon: Option<String>,
 243
 244    /// Fill Color. Used for the muted or deemphasized fill color of an icon.
 245    ///
 246    /// This might be used to show an icon in an inactive pane, or to demphasize a series of icons to give them less visual weight.
 247    #[serde(rename = "icon.muted")]
 248    pub icon_muted: Option<String>,
 249
 250    /// Fill Color. Used for the disabled fill color of an icon.
 251    ///
 252    /// Disabled states are shown when a user cannot interact with an element, like a icon button.
 253    #[serde(rename = "icon.disabled")]
 254    pub icon_disabled: Option<String>,
 255
 256    /// Fill Color. Used for the placeholder fill color of an icon.
 257    ///
 258    /// This might be used to show an icon in an input that disappears when the user enters text.
 259    #[serde(rename = "icon.placeholder")]
 260    pub icon_placeholder: Option<String>,
 261
 262    /// Fill Color. Used for the accent fill color of an icon.
 263    ///
 264    /// This might be used to show when a toggleable icon button is selected.
 265    #[serde(rename = "icon.accent")]
 266    pub icon_accent: Option<String>,
 267
 268    #[serde(rename = "status_bar.background")]
 269    pub status_bar_background: Option<String>,
 270
 271    #[serde(rename = "title_bar.background")]
 272    pub title_bar_background: Option<String>,
 273
 274    #[serde(rename = "toolbar.background")]
 275    pub toolbar_background: Option<String>,
 276
 277    #[serde(rename = "tab_bar.background")]
 278    pub tab_bar_background: Option<String>,
 279
 280    #[serde(rename = "tab.inactive_background")]
 281    pub tab_inactive_background: Option<String>,
 282
 283    #[serde(rename = "tab.active_background")]
 284    pub tab_active_background: Option<String>,
 285
 286    #[serde(rename = "search.match_background")]
 287    pub search_match_background: Option<String>,
 288
 289    #[serde(rename = "panel.background")]
 290    pub panel_background: Option<String>,
 291
 292    #[serde(rename = "panel.focused_border")]
 293    pub panel_focused_border: Option<String>,
 294
 295    #[serde(rename = "pane.focused_border")]
 296    pub pane_focused_border: Option<String>,
 297
 298    /// The color of the scrollbar thumb.
 299    #[serde(
 300        rename = "scrollbar.thumb.background",
 301        alias = "scrollbar_thumb.background"
 302    )]
 303    pub scrollbar_thumb_background: Option<String>,
 304
 305    /// The color of the scrollbar thumb when hovered over.
 306    #[serde(rename = "scrollbar.thumb.hover_background")]
 307    pub scrollbar_thumb_hover_background: Option<String>,
 308
 309    /// The border color of the scrollbar thumb.
 310    #[serde(rename = "scrollbar.thumb.border")]
 311    pub scrollbar_thumb_border: Option<String>,
 312
 313    /// The background color of the scrollbar track.
 314    #[serde(rename = "scrollbar.track.background")]
 315    pub scrollbar_track_background: Option<String>,
 316
 317    /// The border color of the scrollbar track.
 318    #[serde(rename = "scrollbar.track.border")]
 319    pub scrollbar_track_border: Option<String>,
 320
 321    #[serde(rename = "editor.foreground")]
 322    pub editor_foreground: Option<String>,
 323
 324    #[serde(rename = "editor.background")]
 325    pub editor_background: Option<String>,
 326
 327    #[serde(rename = "editor.gutter.background")]
 328    pub editor_gutter_background: Option<String>,
 329
 330    #[serde(rename = "editor.subheader.background")]
 331    pub editor_subheader_background: Option<String>,
 332
 333    #[serde(rename = "editor.active_line.background")]
 334    pub editor_active_line_background: Option<String>,
 335
 336    #[serde(rename = "editor.highlighted_line.background")]
 337    pub editor_highlighted_line_background: Option<String>,
 338
 339    /// Text Color. Used for the text of the line number in the editor gutter.
 340    #[serde(rename = "editor.line_number")]
 341    pub editor_line_number: Option<String>,
 342
 343    /// Text Color. Used for the text of the line number in the editor gutter when the line is highlighted.
 344    #[serde(rename = "editor.active_line_number")]
 345    pub editor_active_line_number: Option<String>,
 346
 347    /// Text Color. Used to mark invisible characters in the editor.
 348    ///
 349    /// Example: spaces, tabs, carriage returns, etc.
 350    #[serde(rename = "editor.invisible")]
 351    pub editor_invisible: Option<String>,
 352
 353    #[serde(rename = "editor.wrap_guide")]
 354    pub editor_wrap_guide: Option<String>,
 355
 356    #[serde(rename = "editor.active_wrap_guide")]
 357    pub editor_active_wrap_guide: Option<String>,
 358
 359    /// Read-access of a symbol, like reading a variable.
 360    ///
 361    /// A document highlight is a range inside a text document which deserves
 362    /// special attention. Usually a document highlight is visualized by changing
 363    /// the background color of its range.
 364    #[serde(rename = "editor.document_highlight.read_background")]
 365    pub editor_document_highlight_read_background: Option<String>,
 366
 367    /// Read-access of a symbol, like reading a variable.
 368    ///
 369    /// A document highlight is a range inside a text document which deserves
 370    /// special attention. Usually a document highlight is visualized by changing
 371    /// the background color of its range.
 372    #[serde(rename = "editor.document_highlight.write_background")]
 373    pub editor_document_highlight_write_background: Option<String>,
 374
 375    /// Terminal background color.
 376    #[serde(rename = "terminal.background")]
 377    pub terminal_background: Option<String>,
 378
 379    /// Terminal foreground color.
 380    #[serde(rename = "terminal.foreground")]
 381    pub terminal_foreground: Option<String>,
 382
 383    /// Bright terminal foreground color.
 384    #[serde(rename = "terminal.bright_foreground")]
 385    pub terminal_bright_foreground: Option<String>,
 386
 387    /// Dim terminal foreground color.
 388    #[serde(rename = "terminal.dim_foreground")]
 389    pub terminal_dim_foreground: Option<String>,
 390
 391    /// Black ANSI terminal color.
 392    #[serde(rename = "terminal.ansi.black")]
 393    pub terminal_ansi_black: Option<String>,
 394
 395    /// Bright black ANSI terminal color.
 396    #[serde(rename = "terminal.ansi.bright_black")]
 397    pub terminal_ansi_bright_black: Option<String>,
 398
 399    /// Dim black ANSI terminal color.
 400    #[serde(rename = "terminal.ansi.dim_black")]
 401    pub terminal_ansi_dim_black: Option<String>,
 402
 403    /// Red ANSI terminal color.
 404    #[serde(rename = "terminal.ansi.red")]
 405    pub terminal_ansi_red: Option<String>,
 406
 407    /// Bright red ANSI terminal color.
 408    #[serde(rename = "terminal.ansi.bright_red")]
 409    pub terminal_ansi_bright_red: Option<String>,
 410
 411    /// Dim red ANSI terminal color.
 412    #[serde(rename = "terminal.ansi.dim_red")]
 413    pub terminal_ansi_dim_red: Option<String>,
 414
 415    /// Green ANSI terminal color.
 416    #[serde(rename = "terminal.ansi.green")]
 417    pub terminal_ansi_green: Option<String>,
 418
 419    /// Bright green ANSI terminal color.
 420    #[serde(rename = "terminal.ansi.bright_green")]
 421    pub terminal_ansi_bright_green: Option<String>,
 422
 423    /// Dim green ANSI terminal color.
 424    #[serde(rename = "terminal.ansi.dim_green")]
 425    pub terminal_ansi_dim_green: Option<String>,
 426
 427    /// Yellow ANSI terminal color.
 428    #[serde(rename = "terminal.ansi.yellow")]
 429    pub terminal_ansi_yellow: Option<String>,
 430
 431    /// Bright yellow ANSI terminal color.
 432    #[serde(rename = "terminal.ansi.bright_yellow")]
 433    pub terminal_ansi_bright_yellow: Option<String>,
 434
 435    /// Dim yellow ANSI terminal color.
 436    #[serde(rename = "terminal.ansi.dim_yellow")]
 437    pub terminal_ansi_dim_yellow: Option<String>,
 438
 439    /// Blue ANSI terminal color.
 440    #[serde(rename = "terminal.ansi.blue")]
 441    pub terminal_ansi_blue: Option<String>,
 442
 443    /// Bright blue ANSI terminal color.
 444    #[serde(rename = "terminal.ansi.bright_blue")]
 445    pub terminal_ansi_bright_blue: Option<String>,
 446
 447    /// Dim blue ANSI terminal color.
 448    #[serde(rename = "terminal.ansi.dim_blue")]
 449    pub terminal_ansi_dim_blue: Option<String>,
 450
 451    /// Magenta ANSI terminal color.
 452    #[serde(rename = "terminal.ansi.magenta")]
 453    pub terminal_ansi_magenta: Option<String>,
 454
 455    /// Bright magenta ANSI terminal color.
 456    #[serde(rename = "terminal.ansi.bright_magenta")]
 457    pub terminal_ansi_bright_magenta: Option<String>,
 458
 459    /// Dim magenta ANSI terminal color.
 460    #[serde(rename = "terminal.ansi.dim_magenta")]
 461    pub terminal_ansi_dim_magenta: Option<String>,
 462
 463    /// Cyan ANSI terminal color.
 464    #[serde(rename = "terminal.ansi.cyan")]
 465    pub terminal_ansi_cyan: Option<String>,
 466
 467    /// Bright cyan ANSI terminal color.
 468    #[serde(rename = "terminal.ansi.bright_cyan")]
 469    pub terminal_ansi_bright_cyan: Option<String>,
 470
 471    /// Dim cyan ANSI terminal color.
 472    #[serde(rename = "terminal.ansi.dim_cyan")]
 473    pub terminal_ansi_dim_cyan: Option<String>,
 474
 475    /// White ANSI terminal color.
 476    #[serde(rename = "terminal.ansi.white")]
 477    pub terminal_ansi_white: Option<String>,
 478
 479    /// Bright white ANSI terminal color.
 480    #[serde(rename = "terminal.ansi.bright_white")]
 481    pub terminal_ansi_bright_white: Option<String>,
 482
 483    /// Dim white ANSI terminal color.
 484    #[serde(rename = "terminal.ansi.dim_white")]
 485    pub terminal_ansi_dim_white: Option<String>,
 486
 487    #[serde(rename = "link_text.hover")]
 488    pub link_text_hover: Option<String>,
 489}
 490
 491impl ThemeColorsContent {
 492    /// Returns a [`ThemeColorsRefinement`] based on the colors in the [`ThemeColorsContent`].
 493    pub fn theme_colors_refinement(&self) -> ThemeColorsRefinement {
 494        ThemeColorsRefinement {
 495            border: self
 496                .border
 497                .as_ref()
 498                .and_then(|color| try_parse_color(color).ok()),
 499            border_variant: self
 500                .border_variant
 501                .as_ref()
 502                .and_then(|color| try_parse_color(color).ok()),
 503            border_focused: self
 504                .border_focused
 505                .as_ref()
 506                .and_then(|color| try_parse_color(color).ok()),
 507            border_selected: self
 508                .border_selected
 509                .as_ref()
 510                .and_then(|color| try_parse_color(color).ok()),
 511            border_transparent: self
 512                .border_transparent
 513                .as_ref()
 514                .and_then(|color| try_parse_color(color).ok()),
 515            border_disabled: self
 516                .border_disabled
 517                .as_ref()
 518                .and_then(|color| try_parse_color(color).ok()),
 519            elevated_surface_background: self
 520                .elevated_surface_background
 521                .as_ref()
 522                .and_then(|color| try_parse_color(color).ok()),
 523            surface_background: self
 524                .surface_background
 525                .as_ref()
 526                .and_then(|color| try_parse_color(color).ok()),
 527            background: self
 528                .background
 529                .as_ref()
 530                .and_then(|color| try_parse_color(color).ok()),
 531            element_background: self
 532                .element_background
 533                .as_ref()
 534                .and_then(|color| try_parse_color(color).ok()),
 535            element_hover: self
 536                .element_hover
 537                .as_ref()
 538                .and_then(|color| try_parse_color(color).ok()),
 539            element_active: self
 540                .element_active
 541                .as_ref()
 542                .and_then(|color| try_parse_color(color).ok()),
 543            element_selected: self
 544                .element_selected
 545                .as_ref()
 546                .and_then(|color| try_parse_color(color).ok()),
 547            element_disabled: self
 548                .element_disabled
 549                .as_ref()
 550                .and_then(|color| try_parse_color(color).ok()),
 551            drop_target_background: self
 552                .drop_target_background
 553                .as_ref()
 554                .and_then(|color| try_parse_color(color).ok()),
 555            ghost_element_background: self
 556                .ghost_element_background
 557                .as_ref()
 558                .and_then(|color| try_parse_color(color).ok()),
 559            ghost_element_hover: self
 560                .ghost_element_hover
 561                .as_ref()
 562                .and_then(|color| try_parse_color(color).ok()),
 563            ghost_element_active: self
 564                .ghost_element_active
 565                .as_ref()
 566                .and_then(|color| try_parse_color(color).ok()),
 567            ghost_element_selected: self
 568                .ghost_element_selected
 569                .as_ref()
 570                .and_then(|color| try_parse_color(color).ok()),
 571            ghost_element_disabled: self
 572                .ghost_element_disabled
 573                .as_ref()
 574                .and_then(|color| try_parse_color(color).ok()),
 575            text: self
 576                .text
 577                .as_ref()
 578                .and_then(|color| try_parse_color(color).ok()),
 579            text_muted: self
 580                .text_muted
 581                .as_ref()
 582                .and_then(|color| try_parse_color(color).ok()),
 583            text_placeholder: self
 584                .text_placeholder
 585                .as_ref()
 586                .and_then(|color| try_parse_color(color).ok()),
 587            text_disabled: self
 588                .text_disabled
 589                .as_ref()
 590                .and_then(|color| try_parse_color(color).ok()),
 591            text_accent: self
 592                .text_accent
 593                .as_ref()
 594                .and_then(|color| try_parse_color(color).ok()),
 595            icon: self
 596                .icon
 597                .as_ref()
 598                .and_then(|color| try_parse_color(color).ok()),
 599            icon_muted: self
 600                .icon_muted
 601                .as_ref()
 602                .and_then(|color| try_parse_color(color).ok()),
 603            icon_disabled: self
 604                .icon_disabled
 605                .as_ref()
 606                .and_then(|color| try_parse_color(color).ok()),
 607            icon_placeholder: self
 608                .icon_placeholder
 609                .as_ref()
 610                .and_then(|color| try_parse_color(color).ok()),
 611            icon_accent: self
 612                .icon_accent
 613                .as_ref()
 614                .and_then(|color| try_parse_color(color).ok()),
 615            status_bar_background: self
 616                .status_bar_background
 617                .as_ref()
 618                .and_then(|color| try_parse_color(color).ok()),
 619            title_bar_background: self
 620                .title_bar_background
 621                .as_ref()
 622                .and_then(|color| try_parse_color(color).ok()),
 623            toolbar_background: self
 624                .toolbar_background
 625                .as_ref()
 626                .and_then(|color| try_parse_color(color).ok()),
 627            tab_bar_background: self
 628                .tab_bar_background
 629                .as_ref()
 630                .and_then(|color| try_parse_color(color).ok()),
 631            tab_inactive_background: self
 632                .tab_inactive_background
 633                .as_ref()
 634                .and_then(|color| try_parse_color(color).ok()),
 635            tab_active_background: self
 636                .tab_active_background
 637                .as_ref()
 638                .and_then(|color| try_parse_color(color).ok()),
 639            search_match_background: self
 640                .search_match_background
 641                .as_ref()
 642                .and_then(|color| try_parse_color(color).ok()),
 643            panel_background: self
 644                .panel_background
 645                .as_ref()
 646                .and_then(|color| try_parse_color(color).ok()),
 647            panel_focused_border: self
 648                .panel_focused_border
 649                .as_ref()
 650                .and_then(|color| try_parse_color(color).ok()),
 651            pane_focused_border: self
 652                .pane_focused_border
 653                .as_ref()
 654                .and_then(|color| try_parse_color(color).ok()),
 655            scrollbar_thumb_background: self
 656                .scrollbar_thumb_background
 657                .as_ref()
 658                .and_then(|color| try_parse_color(color).ok()),
 659            scrollbar_thumb_hover_background: self
 660                .scrollbar_thumb_hover_background
 661                .as_ref()
 662                .and_then(|color| try_parse_color(color).ok()),
 663            scrollbar_thumb_border: self
 664                .scrollbar_thumb_border
 665                .as_ref()
 666                .and_then(|color| try_parse_color(color).ok()),
 667            scrollbar_track_background: self
 668                .scrollbar_track_background
 669                .as_ref()
 670                .and_then(|color| try_parse_color(color).ok()),
 671            scrollbar_track_border: self
 672                .scrollbar_track_border
 673                .as_ref()
 674                .and_then(|color| try_parse_color(color).ok()),
 675            editor_foreground: self
 676                .editor_foreground
 677                .as_ref()
 678                .and_then(|color| try_parse_color(color).ok()),
 679            editor_background: self
 680                .editor_background
 681                .as_ref()
 682                .and_then(|color| try_parse_color(color).ok()),
 683            editor_gutter_background: self
 684                .editor_gutter_background
 685                .as_ref()
 686                .and_then(|color| try_parse_color(color).ok()),
 687            editor_subheader_background: self
 688                .editor_subheader_background
 689                .as_ref()
 690                .and_then(|color| try_parse_color(color).ok()),
 691            editor_active_line_background: self
 692                .editor_active_line_background
 693                .as_ref()
 694                .and_then(|color| try_parse_color(color).ok()),
 695            editor_highlighted_line_background: self
 696                .editor_highlighted_line_background
 697                .as_ref()
 698                .and_then(|color| try_parse_color(color).ok()),
 699            editor_line_number: self
 700                .editor_line_number
 701                .as_ref()
 702                .and_then(|color| try_parse_color(color).ok()),
 703            editor_active_line_number: self
 704                .editor_active_line_number
 705                .as_ref()
 706                .and_then(|color| try_parse_color(color).ok()),
 707            editor_invisible: self
 708                .editor_invisible
 709                .as_ref()
 710                .and_then(|color| try_parse_color(color).ok()),
 711            editor_wrap_guide: self
 712                .editor_wrap_guide
 713                .as_ref()
 714                .and_then(|color| try_parse_color(color).ok()),
 715            editor_active_wrap_guide: self
 716                .editor_active_wrap_guide
 717                .as_ref()
 718                .and_then(|color| try_parse_color(color).ok()),
 719            editor_document_highlight_read_background: self
 720                .editor_document_highlight_read_background
 721                .as_ref()
 722                .and_then(|color| try_parse_color(color).ok()),
 723            editor_document_highlight_write_background: self
 724                .editor_document_highlight_write_background
 725                .as_ref()
 726                .and_then(|color| try_parse_color(color).ok()),
 727            terminal_background: self
 728                .terminal_background
 729                .as_ref()
 730                .and_then(|color| try_parse_color(color).ok()),
 731            terminal_foreground: self
 732                .terminal_foreground
 733                .as_ref()
 734                .and_then(|color| try_parse_color(color).ok()),
 735            terminal_bright_foreground: self
 736                .terminal_bright_foreground
 737                .as_ref()
 738                .and_then(|color| try_parse_color(color).ok()),
 739            terminal_dim_foreground: self
 740                .terminal_dim_foreground
 741                .as_ref()
 742                .and_then(|color| try_parse_color(color).ok()),
 743            terminal_ansi_black: self
 744                .terminal_ansi_black
 745                .as_ref()
 746                .and_then(|color| try_parse_color(color).ok()),
 747            terminal_ansi_bright_black: self
 748                .terminal_ansi_bright_black
 749                .as_ref()
 750                .and_then(|color| try_parse_color(color).ok()),
 751            terminal_ansi_dim_black: self
 752                .terminal_ansi_dim_black
 753                .as_ref()
 754                .and_then(|color| try_parse_color(color).ok()),
 755            terminal_ansi_red: self
 756                .terminal_ansi_red
 757                .as_ref()
 758                .and_then(|color| try_parse_color(color).ok()),
 759            terminal_ansi_bright_red: self
 760                .terminal_ansi_bright_red
 761                .as_ref()
 762                .and_then(|color| try_parse_color(color).ok()),
 763            terminal_ansi_dim_red: self
 764                .terminal_ansi_dim_red
 765                .as_ref()
 766                .and_then(|color| try_parse_color(color).ok()),
 767            terminal_ansi_green: self
 768                .terminal_ansi_green
 769                .as_ref()
 770                .and_then(|color| try_parse_color(color).ok()),
 771            terminal_ansi_bright_green: self
 772                .terminal_ansi_bright_green
 773                .as_ref()
 774                .and_then(|color| try_parse_color(color).ok()),
 775            terminal_ansi_dim_green: self
 776                .terminal_ansi_dim_green
 777                .as_ref()
 778                .and_then(|color| try_parse_color(color).ok()),
 779            terminal_ansi_yellow: self
 780                .terminal_ansi_yellow
 781                .as_ref()
 782                .and_then(|color| try_parse_color(color).ok()),
 783            terminal_ansi_bright_yellow: self
 784                .terminal_ansi_bright_yellow
 785                .as_ref()
 786                .and_then(|color| try_parse_color(color).ok()),
 787            terminal_ansi_dim_yellow: self
 788                .terminal_ansi_dim_yellow
 789                .as_ref()
 790                .and_then(|color| try_parse_color(color).ok()),
 791            terminal_ansi_blue: self
 792                .terminal_ansi_blue
 793                .as_ref()
 794                .and_then(|color| try_parse_color(color).ok()),
 795            terminal_ansi_bright_blue: self
 796                .terminal_ansi_bright_blue
 797                .as_ref()
 798                .and_then(|color| try_parse_color(color).ok()),
 799            terminal_ansi_dim_blue: self
 800                .terminal_ansi_dim_blue
 801                .as_ref()
 802                .and_then(|color| try_parse_color(color).ok()),
 803            terminal_ansi_magenta: self
 804                .terminal_ansi_magenta
 805                .as_ref()
 806                .and_then(|color| try_parse_color(color).ok()),
 807            terminal_ansi_bright_magenta: self
 808                .terminal_ansi_bright_magenta
 809                .as_ref()
 810                .and_then(|color| try_parse_color(color).ok()),
 811            terminal_ansi_dim_magenta: self
 812                .terminal_ansi_dim_magenta
 813                .as_ref()
 814                .and_then(|color| try_parse_color(color).ok()),
 815            terminal_ansi_cyan: self
 816                .terminal_ansi_cyan
 817                .as_ref()
 818                .and_then(|color| try_parse_color(color).ok()),
 819            terminal_ansi_bright_cyan: self
 820                .terminal_ansi_bright_cyan
 821                .as_ref()
 822                .and_then(|color| try_parse_color(color).ok()),
 823            terminal_ansi_dim_cyan: self
 824                .terminal_ansi_dim_cyan
 825                .as_ref()
 826                .and_then(|color| try_parse_color(color).ok()),
 827            terminal_ansi_white: self
 828                .terminal_ansi_white
 829                .as_ref()
 830                .and_then(|color| try_parse_color(color).ok()),
 831            terminal_ansi_bright_white: self
 832                .terminal_ansi_bright_white
 833                .as_ref()
 834                .and_then(|color| try_parse_color(color).ok()),
 835            terminal_ansi_dim_white: self
 836                .terminal_ansi_dim_white
 837                .as_ref()
 838                .and_then(|color| try_parse_color(color).ok()),
 839            link_text_hover: self
 840                .link_text_hover
 841                .as_ref()
 842                .and_then(|color| try_parse_color(color).ok()),
 843        }
 844    }
 845}
 846
 847#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
 848#[serde(default)]
 849pub struct StatusColorsContent {
 850    /// Indicates some kind of conflict, like a file changed on disk while it was open, or
 851    /// merge conflicts in a Git repository.
 852    #[serde(rename = "conflict")]
 853    pub conflict: Option<String>,
 854
 855    #[serde(rename = "conflict.background")]
 856    pub conflict_background: Option<String>,
 857
 858    #[serde(rename = "conflict.border")]
 859    pub conflict_border: Option<String>,
 860
 861    /// Indicates something new, like a new file added to a Git repository.
 862    #[serde(rename = "created")]
 863    pub created: Option<String>,
 864
 865    #[serde(rename = "created.background")]
 866    pub created_background: Option<String>,
 867
 868    #[serde(rename = "created.border")]
 869    pub created_border: Option<String>,
 870
 871    /// Indicates that something no longer exists, like a deleted file.
 872    #[serde(rename = "deleted")]
 873    pub deleted: Option<String>,
 874
 875    #[serde(rename = "deleted.background")]
 876    pub deleted_background: Option<String>,
 877
 878    #[serde(rename = "deleted.border")]
 879    pub deleted_border: Option<String>,
 880
 881    /// Indicates a system error, a failed operation or a diagnostic error.
 882    #[serde(rename = "error")]
 883    pub error: Option<String>,
 884
 885    #[serde(rename = "error.background")]
 886    pub error_background: Option<String>,
 887
 888    #[serde(rename = "error.border")]
 889    pub error_border: Option<String>,
 890
 891    /// Represents a hidden status, such as a file being hidden in a file tree.
 892    #[serde(rename = "hidden")]
 893    pub hidden: Option<String>,
 894
 895    #[serde(rename = "hidden.background")]
 896    pub hidden_background: Option<String>,
 897
 898    #[serde(rename = "hidden.border")]
 899    pub hidden_border: Option<String>,
 900
 901    /// Indicates a hint or some kind of additional information.
 902    #[serde(rename = "hint")]
 903    pub hint: Option<String>,
 904
 905    #[serde(rename = "hint.background")]
 906    pub hint_background: Option<String>,
 907
 908    #[serde(rename = "hint.border")]
 909    pub hint_border: Option<String>,
 910
 911    /// Indicates that something is deliberately ignored, such as a file or operation ignored by Git.
 912    #[serde(rename = "ignored")]
 913    pub ignored: Option<String>,
 914
 915    #[serde(rename = "ignored.background")]
 916    pub ignored_background: Option<String>,
 917
 918    #[serde(rename = "ignored.border")]
 919    pub ignored_border: Option<String>,
 920
 921    /// Represents informational status updates or messages.
 922    #[serde(rename = "info")]
 923    pub info: Option<String>,
 924
 925    #[serde(rename = "info.background")]
 926    pub info_background: Option<String>,
 927
 928    #[serde(rename = "info.border")]
 929    pub info_border: Option<String>,
 930
 931    /// Indicates a changed or altered status, like a file that has been edited.
 932    #[serde(rename = "modified")]
 933    pub modified: Option<String>,
 934
 935    #[serde(rename = "modified.background")]
 936    pub modified_background: Option<String>,
 937
 938    #[serde(rename = "modified.border")]
 939    pub modified_border: Option<String>,
 940
 941    /// Indicates something that is predicted, like automatic code completion, or generated code.
 942    #[serde(rename = "predictive")]
 943    pub predictive: Option<String>,
 944
 945    #[serde(rename = "predictive.background")]
 946    pub predictive_background: Option<String>,
 947
 948    #[serde(rename = "predictive.border")]
 949    pub predictive_border: Option<String>,
 950
 951    /// Represents a renamed status, such as a file that has been renamed.
 952    #[serde(rename = "renamed")]
 953    pub renamed: Option<String>,
 954
 955    #[serde(rename = "renamed.background")]
 956    pub renamed_background: Option<String>,
 957
 958    #[serde(rename = "renamed.border")]
 959    pub renamed_border: Option<String>,
 960
 961    /// Indicates a successful operation or task completion.
 962    #[serde(rename = "success")]
 963    pub success: Option<String>,
 964
 965    #[serde(rename = "success.background")]
 966    pub success_background: Option<String>,
 967
 968    #[serde(rename = "success.border")]
 969    pub success_border: Option<String>,
 970
 971    /// Indicates some kind of unreachable status, like a block of code that can never be reached.
 972    #[serde(rename = "unreachable")]
 973    pub unreachable: Option<String>,
 974
 975    #[serde(rename = "unreachable.background")]
 976    pub unreachable_background: Option<String>,
 977
 978    #[serde(rename = "unreachable.border")]
 979    pub unreachable_border: Option<String>,
 980
 981    /// Represents a warning status, like an operation that is about to fail.
 982    #[serde(rename = "warning")]
 983    pub warning: Option<String>,
 984
 985    #[serde(rename = "warning.background")]
 986    pub warning_background: Option<String>,
 987
 988    #[serde(rename = "warning.border")]
 989    pub warning_border: Option<String>,
 990}
 991
 992impl StatusColorsContent {
 993    /// Returns a [`StatusColorsRefinement`] based on the colors in the [`StatusColorsContent`].
 994    pub fn status_colors_refinement(&self) -> StatusColorsRefinement {
 995        StatusColorsRefinement {
 996            conflict: self
 997                .conflict
 998                .as_ref()
 999                .and_then(|color| try_parse_color(color).ok()),
1000            conflict_background: self
1001                .conflict_background
1002                .as_ref()
1003                .and_then(|color| try_parse_color(color).ok()),
1004            conflict_border: self
1005                .conflict_border
1006                .as_ref()
1007                .and_then(|color| try_parse_color(color).ok()),
1008            created: self
1009                .created
1010                .as_ref()
1011                .and_then(|color| try_parse_color(color).ok()),
1012            created_background: self
1013                .created_background
1014                .as_ref()
1015                .and_then(|color| try_parse_color(color).ok()),
1016            created_border: self
1017                .created_border
1018                .as_ref()
1019                .and_then(|color| try_parse_color(color).ok()),
1020            deleted: self
1021                .deleted
1022                .as_ref()
1023                .and_then(|color| try_parse_color(color).ok()),
1024            deleted_background: self
1025                .deleted_background
1026                .as_ref()
1027                .and_then(|color| try_parse_color(color).ok()),
1028            deleted_border: self
1029                .deleted_border
1030                .as_ref()
1031                .and_then(|color| try_parse_color(color).ok()),
1032            error: self
1033                .error
1034                .as_ref()
1035                .and_then(|color| try_parse_color(color).ok()),
1036            error_background: self
1037                .error_background
1038                .as_ref()
1039                .and_then(|color| try_parse_color(color).ok()),
1040            error_border: self
1041                .error_border
1042                .as_ref()
1043                .and_then(|color| try_parse_color(color).ok()),
1044            hidden: self
1045                .hidden
1046                .as_ref()
1047                .and_then(|color| try_parse_color(color).ok()),
1048            hidden_background: self
1049                .hidden_background
1050                .as_ref()
1051                .and_then(|color| try_parse_color(color).ok()),
1052            hidden_border: self
1053                .hidden_border
1054                .as_ref()
1055                .and_then(|color| try_parse_color(color).ok()),
1056            hint: self
1057                .hint
1058                .as_ref()
1059                .and_then(|color| try_parse_color(color).ok()),
1060            hint_background: self
1061                .hint_background
1062                .as_ref()
1063                .and_then(|color| try_parse_color(color).ok()),
1064            hint_border: self
1065                .hint_border
1066                .as_ref()
1067                .and_then(|color| try_parse_color(color).ok()),
1068            ignored: self
1069                .ignored
1070                .as_ref()
1071                .and_then(|color| try_parse_color(color).ok()),
1072            ignored_background: self
1073                .ignored_background
1074                .as_ref()
1075                .and_then(|color| try_parse_color(color).ok()),
1076            ignored_border: self
1077                .ignored_border
1078                .as_ref()
1079                .and_then(|color| try_parse_color(color).ok()),
1080            info: self
1081                .info
1082                .as_ref()
1083                .and_then(|color| try_parse_color(color).ok()),
1084            info_background: self
1085                .info_background
1086                .as_ref()
1087                .and_then(|color| try_parse_color(color).ok()),
1088            info_border: self
1089                .info_border
1090                .as_ref()
1091                .and_then(|color| try_parse_color(color).ok()),
1092            modified: self
1093                .modified
1094                .as_ref()
1095                .and_then(|color| try_parse_color(color).ok()),
1096            modified_background: self
1097                .modified_background
1098                .as_ref()
1099                .and_then(|color| try_parse_color(color).ok()),
1100            modified_border: self
1101                .modified_border
1102                .as_ref()
1103                .and_then(|color| try_parse_color(color).ok()),
1104            predictive: self
1105                .predictive
1106                .as_ref()
1107                .and_then(|color| try_parse_color(color).ok()),
1108            predictive_background: self
1109                .predictive_background
1110                .as_ref()
1111                .and_then(|color| try_parse_color(color).ok()),
1112            predictive_border: self
1113                .predictive_border
1114                .as_ref()
1115                .and_then(|color| try_parse_color(color).ok()),
1116            renamed: self
1117                .renamed
1118                .as_ref()
1119                .and_then(|color| try_parse_color(color).ok()),
1120            renamed_background: self
1121                .renamed_background
1122                .as_ref()
1123                .and_then(|color| try_parse_color(color).ok()),
1124            renamed_border: self
1125                .renamed_border
1126                .as_ref()
1127                .and_then(|color| try_parse_color(color).ok()),
1128            success: self
1129                .success
1130                .as_ref()
1131                .and_then(|color| try_parse_color(color).ok()),
1132            success_background: self
1133                .success_background
1134                .as_ref()
1135                .and_then(|color| try_parse_color(color).ok()),
1136            success_border: self
1137                .success_border
1138                .as_ref()
1139                .and_then(|color| try_parse_color(color).ok()),
1140            unreachable: self
1141                .unreachable
1142                .as_ref()
1143                .and_then(|color| try_parse_color(color).ok()),
1144            unreachable_background: self
1145                .unreachable_background
1146                .as_ref()
1147                .and_then(|color| try_parse_color(color).ok()),
1148            unreachable_border: self
1149                .unreachable_border
1150                .as_ref()
1151                .and_then(|color| try_parse_color(color).ok()),
1152            warning: self
1153                .warning
1154                .as_ref()
1155                .and_then(|color| try_parse_color(color).ok()),
1156            warning_background: self
1157                .warning_background
1158                .as_ref()
1159                .and_then(|color| try_parse_color(color).ok()),
1160            warning_border: self
1161                .warning_border
1162                .as_ref()
1163                .and_then(|color| try_parse_color(color).ok()),
1164        }
1165    }
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1169pub struct PlayerColorContent {
1170    pub cursor: Option<String>,
1171    pub background: Option<String>,
1172    pub selection: Option<String>,
1173}
1174
1175#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
1176#[serde(rename_all = "snake_case")]
1177pub enum FontStyleContent {
1178    Normal,
1179    Italic,
1180    Oblique,
1181}
1182
1183impl From<FontStyleContent> for FontStyle {
1184    fn from(value: FontStyleContent) -> Self {
1185        match value {
1186            FontStyleContent::Normal => FontStyle::Normal,
1187            FontStyleContent::Italic => FontStyle::Italic,
1188            FontStyleContent::Oblique => FontStyle::Oblique,
1189        }
1190    }
1191}
1192
1193#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
1194#[repr(u16)]
1195pub enum FontWeightContent {
1196    Thin = 100,
1197    ExtraLight = 200,
1198    Light = 300,
1199    Normal = 400,
1200    Medium = 500,
1201    Semibold = 600,
1202    Bold = 700,
1203    ExtraBold = 800,
1204    Black = 900,
1205}
1206
1207impl JsonSchema for FontWeightContent {
1208    fn schema_name() -> String {
1209        "FontWeightContent".to_owned()
1210    }
1211
1212    fn is_referenceable() -> bool {
1213        false
1214    }
1215
1216    fn json_schema(_: &mut SchemaGenerator) -> Schema {
1217        SchemaObject {
1218            enum_values: Some(vec![
1219                100.into(),
1220                200.into(),
1221                300.into(),
1222                400.into(),
1223                500.into(),
1224                600.into(),
1225                700.into(),
1226                800.into(),
1227                900.into(),
1228            ]),
1229            ..Default::default()
1230        }
1231        .into()
1232    }
1233}
1234
1235impl From<FontWeightContent> for FontWeight {
1236    fn from(value: FontWeightContent) -> Self {
1237        match value {
1238            FontWeightContent::Thin => FontWeight::THIN,
1239            FontWeightContent::ExtraLight => FontWeight::EXTRA_LIGHT,
1240            FontWeightContent::Light => FontWeight::LIGHT,
1241            FontWeightContent::Normal => FontWeight::NORMAL,
1242            FontWeightContent::Medium => FontWeight::MEDIUM,
1243            FontWeightContent::Semibold => FontWeight::SEMIBOLD,
1244            FontWeightContent::Bold => FontWeight::BOLD,
1245            FontWeightContent::ExtraBold => FontWeight::EXTRA_BOLD,
1246            FontWeightContent::Black => FontWeight::BLACK,
1247        }
1248    }
1249}
1250
1251#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
1252#[serde(default)]
1253pub struct HighlightStyleContent {
1254    pub color: Option<String>,
1255
1256    #[serde(deserialize_with = "treat_error_as_none")]
1257    pub font_style: Option<FontStyleContent>,
1258
1259    #[serde(deserialize_with = "treat_error_as_none")]
1260    pub font_weight: Option<FontWeightContent>,
1261}
1262
1263impl HighlightStyleContent {
1264    pub fn is_empty(&self) -> bool {
1265        self.color.is_none() && self.font_style.is_none() && self.font_weight.is_none()
1266    }
1267}
1268
1269fn treat_error_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
1270where
1271    T: Deserialize<'de>,
1272    D: Deserializer<'de>,
1273{
1274    let value: Value = Deserialize::deserialize(deserializer)?;
1275    Ok(T::deserialize(value).ok())
1276}