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