1#![allow(missing_docs)]
2
3use crate::schema::{status_colors_refinement, syntax_overrides, theme_colors_refinement};
4use crate::{merge_accent_colors, merge_player_colors};
5use collections::HashMap;
6use gpui::{
7 App, Context, Font, FontFallbacks, FontStyle, Global, Pixels, SharedString, Subscription,
8 Window, px,
9};
10use refineable::Refineable;
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};
14use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};
15use std::sync::Arc;
16use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, UiDensity};
17
18const MIN_FONT_SIZE: Pixels = px(6.0);
19const MAX_FONT_SIZE: Pixels = px(100.0);
20const MIN_LINE_HEIGHT: f32 = 1.0;
21
22pub(crate) fn ui_density_from_settings(val: settings::UiDensity) -> UiDensity {
23 match val {
24 settings::UiDensity::Compact => UiDensity::Compact,
25 settings::UiDensity::Default => UiDensity::Default,
26 settings::UiDensity::Comfortable => UiDensity::Comfortable,
27 }
28}
29
30pub fn appearance_to_mode(appearance: Appearance) -> ThemeAppearanceMode {
31 match appearance {
32 Appearance::Light => ThemeAppearanceMode::Light,
33 Appearance::Dark => ThemeAppearanceMode::Dark,
34 }
35}
36
37/// Customizable settings for the UI and theme system.
38#[derive(Clone, PartialEq, RegisterSetting)]
39pub struct ThemeSettings {
40 /// The UI font size. Determines the size of text in the UI,
41 /// as well as the size of a [gpui::Rems] unit.
42 ///
43 /// Changing this will impact the size of all UI elements.
44 ui_font_size: Pixels,
45 /// The font used for UI elements.
46 pub ui_font: Font,
47 /// The font size used for buffers, and the terminal.
48 ///
49 /// The terminal font size can be overridden using it's own setting.
50 buffer_font_size: Pixels,
51 /// The font used for buffers, and the terminal.
52 ///
53 /// The terminal font family can be overridden using it's own setting.
54 pub buffer_font: Font,
55 /// The agent font size. Determines the size of text in the agent panel. Falls back to the UI font size if unset.
56 agent_ui_font_size: Option<Pixels>,
57 /// The agent buffer font size. Determines the size of user messages in the agent panel.
58 agent_buffer_font_size: Option<Pixels>,
59 /// The font family to use for rendering in the markdown preview.
60 /// Falls back to the UI font family if unset.
61 markdown_preview_font_family: Option<SharedString>,
62 /// The theme to use for the markdown preview.
63 /// Falls back to the main editor theme if unset.
64 pub markdown_preview_theme: Option<ThemeSelection>,
65 /// The line height for buffers, and the terminal.
66 ///
67 /// Changing this may affect the spacing of some UI elements.
68 ///
69 /// The terminal font family can be overridden using it's own setting.
70 pub buffer_line_height: BufferLineHeight,
71 /// The current theme selection.
72 pub theme: ThemeSelection,
73 /// Manual overrides for the active theme.
74 ///
75 /// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
76 pub experimental_theme_overrides: Option<settings::ThemeStyleContent>,
77 /// Manual overrides per theme
78 pub theme_overrides: HashMap<String, settings::ThemeStyleContent>,
79 /// The current icon theme selection.
80 pub icon_theme: IconThemeSelection,
81 /// The density of the UI.
82 /// Note: This setting is still experimental. See [this tracking issue](
83 pub ui_density: UiDensity,
84 /// The amount of fading applied to unnecessary code.
85 pub unnecessary_code_fade: f32,
86}
87
88/// Returns the name of the default theme for the given [`Appearance`].
89pub fn default_theme(appearance: Appearance) -> &'static str {
90 match appearance {
91 Appearance::Light => settings::DEFAULT_LIGHT_THEME,
92 Appearance::Dark => settings::DEFAULT_DARK_THEME,
93 }
94}
95
96#[derive(Default)]
97struct BufferFontSize(Pixels);
98
99impl Global for BufferFontSize {}
100
101#[derive(Default)]
102pub(crate) struct UiFontSize(Pixels);
103
104impl Global for UiFontSize {}
105
106/// In-memory override for the UI font size in the agent panel.
107#[derive(Default)]
108pub struct AgentUiFontSize(Pixels);
109
110impl Global for AgentUiFontSize {}
111
112/// In-memory override for the buffer font size in the agent panel.
113#[derive(Default)]
114pub struct AgentBufferFontSize(Pixels);
115
116impl Global for AgentBufferFontSize {}
117
118/// Represents the selection of a theme, which can be either static or dynamic.
119#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
120#[serde(untagged)]
121pub enum ThemeSelection {
122 /// A static theme selection, represented by a single theme name.
123 Static(ThemeName),
124 /// A dynamic theme selection, which can change based the [ThemeMode].
125 Dynamic {
126 /// The mode used to determine which theme to use.
127 #[serde(default)]
128 mode: ThemeAppearanceMode,
129 /// The theme to use for light mode.
130 light: ThemeName,
131 /// The theme to use for dark mode.
132 dark: ThemeName,
133 },
134}
135
136impl From<settings::ThemeSelection> for ThemeSelection {
137 fn from(selection: settings::ThemeSelection) -> Self {
138 match selection {
139 settings::ThemeSelection::Static(theme) => ThemeSelection::Static(theme),
140 settings::ThemeSelection::Dynamic { mode, light, dark } => {
141 ThemeSelection::Dynamic { mode, light, dark }
142 }
143 }
144 }
145}
146
147impl ThemeSelection {
148 /// Returns the theme name for the selected [ThemeMode].
149 pub fn name(&self, system_appearance: Appearance) -> ThemeName {
150 match self {
151 Self::Static(theme) => theme.clone(),
152 Self::Dynamic { mode, light, dark } => match mode {
153 ThemeAppearanceMode::Light => light.clone(),
154 ThemeAppearanceMode::Dark => dark.clone(),
155 ThemeAppearanceMode::System => match system_appearance {
156 Appearance::Light => light.clone(),
157 Appearance::Dark => dark.clone(),
158 },
159 },
160 }
161 }
162
163 /// Returns the [ThemeMode] for the [ThemeSelection].
164 pub fn mode(&self) -> Option<ThemeAppearanceMode> {
165 match self {
166 ThemeSelection::Static(_) => None,
167 ThemeSelection::Dynamic { mode, .. } => Some(*mode),
168 }
169 }
170}
171
172/// Represents the selection of an icon theme, which can be either static or dynamic.
173#[derive(Clone, Debug, PartialEq, Eq)]
174pub enum IconThemeSelection {
175 /// A static icon theme selection, represented by a single icon theme name.
176 Static(IconThemeName),
177 /// A dynamic icon theme selection, which can change based on the [`ThemeMode`].
178 Dynamic {
179 /// The mode used to determine which theme to use.
180 mode: ThemeAppearanceMode,
181 /// The icon theme to use for light mode.
182 light: IconThemeName,
183 /// The icon theme to use for dark mode.
184 dark: IconThemeName,
185 },
186}
187
188impl From<settings::IconThemeSelection> for IconThemeSelection {
189 fn from(selection: settings::IconThemeSelection) -> Self {
190 match selection {
191 settings::IconThemeSelection::Static(theme) => IconThemeSelection::Static(theme),
192 settings::IconThemeSelection::Dynamic { mode, light, dark } => {
193 IconThemeSelection::Dynamic { mode, light, dark }
194 }
195 }
196 }
197}
198
199impl IconThemeSelection {
200 /// Returns the icon theme name based on the given [`Appearance`].
201 pub fn name(&self, system_appearance: Appearance) -> IconThemeName {
202 match self {
203 Self::Static(theme) => theme.clone(),
204 Self::Dynamic { mode, light, dark } => match mode {
205 ThemeAppearanceMode::Light => light.clone(),
206 ThemeAppearanceMode::Dark => dark.clone(),
207 ThemeAppearanceMode::System => match system_appearance {
208 Appearance::Light => light.clone(),
209 Appearance::Dark => dark.clone(),
210 },
211 },
212 }
213 }
214
215 /// Returns the [`ThemeMode`] for the [`IconThemeSelection`].
216 pub fn mode(&self) -> Option<ThemeAppearanceMode> {
217 match self {
218 IconThemeSelection::Static(_) => None,
219 IconThemeSelection::Dynamic { mode, .. } => Some(*mode),
220 }
221 }
222}
223
224/// Sets the theme for the given appearance to the theme with the specified name.
225///
226/// The caller should make sure that the [`Appearance`] matches the theme associated with the name.
227///
228/// If the current [`ThemeAppearanceMode`] is set to [`System`] and the user's system [`Appearance`]
229/// is different than the new theme's [`Appearance`], this function will update the
230/// [`ThemeAppearanceMode`] to the new theme's appearance in order to display the new theme.
231///
232/// [`System`]: ThemeAppearanceMode::System
233pub fn set_theme(
234 current: &mut SettingsContent,
235 theme_name: impl Into<Arc<str>>,
236 theme_appearance: Appearance,
237 system_appearance: Appearance,
238) {
239 let theme_name = ThemeName(theme_name.into());
240
241 let Some(selection) = current.theme.theme.as_mut() else {
242 current.theme.theme = Some(settings::ThemeSelection::Static(theme_name));
243 return;
244 };
245
246 match selection {
247 settings::ThemeSelection::Static(theme) => {
248 *theme = theme_name;
249 }
250 settings::ThemeSelection::Dynamic { mode, light, dark } => {
251 match theme_appearance {
252 Appearance::Light => *light = theme_name,
253 Appearance::Dark => *dark = theme_name,
254 }
255
256 let should_update_mode =
257 !(mode == &ThemeAppearanceMode::System && theme_appearance == system_appearance);
258
259 if should_update_mode {
260 *mode = appearance_to_mode(theme_appearance);
261 }
262 }
263 }
264}
265
266/// Sets the icon theme for the given appearance to the icon theme with the specified name.
267pub fn set_icon_theme(
268 current: &mut SettingsContent,
269 icon_theme_name: IconThemeName,
270 appearance: Appearance,
271) {
272 if let Some(selection) = current.theme.icon_theme.as_mut() {
273 let icon_theme_to_update = match selection {
274 settings::IconThemeSelection::Static(theme) => theme,
275 settings::IconThemeSelection::Dynamic { mode, light, dark } => match mode {
276 ThemeAppearanceMode::Light => light,
277 ThemeAppearanceMode::Dark => dark,
278 ThemeAppearanceMode::System => match appearance {
279 Appearance::Light => light,
280 Appearance::Dark => dark,
281 },
282 },
283 };
284
285 *icon_theme_to_update = icon_theme_name;
286 } else {
287 current.theme.icon_theme = Some(settings::IconThemeSelection::Static(icon_theme_name));
288 }
289}
290
291/// Sets the mode for the theme.
292pub fn set_mode(content: &mut SettingsContent, mode: ThemeAppearanceMode) {
293 let theme = content.theme.as_mut();
294
295 if let Some(selection) = theme.theme.as_mut() {
296 match selection {
297 settings::ThemeSelection::Static(_) => {
298 *selection = settings::ThemeSelection::Dynamic {
299 mode: ThemeAppearanceMode::System,
300 light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()),
301 dark: ThemeName(settings::DEFAULT_DARK_THEME.into()),
302 };
303 }
304 settings::ThemeSelection::Dynamic {
305 mode: mode_to_update,
306 ..
307 } => *mode_to_update = mode,
308 }
309 } else {
310 theme.theme = Some(settings::ThemeSelection::Dynamic {
311 mode,
312 light: ThemeName(settings::DEFAULT_LIGHT_THEME.into()),
313 dark: ThemeName(settings::DEFAULT_DARK_THEME.into()),
314 });
315 }
316
317 if let Some(selection) = theme.icon_theme.as_mut() {
318 match selection {
319 settings::IconThemeSelection::Static(icon_theme) => {
320 *selection = settings::IconThemeSelection::Dynamic {
321 mode,
322 light: icon_theme.clone(),
323 dark: icon_theme.clone(),
324 };
325 }
326 settings::IconThemeSelection::Dynamic {
327 mode: mode_to_update,
328 ..
329 } => *mode_to_update = mode,
330 }
331 } else {
332 theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName(
333 DEFAULT_ICON_THEME_NAME.into(),
334 )));
335 }
336}
337
338/// The buffer's line height.
339#[derive(Clone, Copy, Debug, PartialEq, Default)]
340pub enum BufferLineHeight {
341 /// A less dense line height.
342 #[default]
343 Comfortable,
344 /// The default line height.
345 Standard,
346 /// A custom line height, where 1.0 is the font's height. Must be at least 1.0.
347 Custom(f32),
348}
349
350impl From<settings::BufferLineHeight> for BufferLineHeight {
351 fn from(value: settings::BufferLineHeight) -> Self {
352 match value {
353 settings::BufferLineHeight::Comfortable => BufferLineHeight::Comfortable,
354 settings::BufferLineHeight::Standard => BufferLineHeight::Standard,
355 settings::BufferLineHeight::Custom(line_height) => {
356 BufferLineHeight::Custom(line_height)
357 }
358 }
359 }
360}
361
362impl BufferLineHeight {
363 /// Returns the value of the line height.
364 pub fn value(&self) -> f32 {
365 match self {
366 BufferLineHeight::Comfortable => 1.618,
367 BufferLineHeight::Standard => 1.3,
368 BufferLineHeight::Custom(line_height) => *line_height,
369 }
370 }
371}
372
373impl ThemeSettings {
374 /// Returns the buffer font size.
375 pub fn buffer_font_size(&self, cx: &App) -> Pixels {
376 let font_size = cx
377 .try_global::<BufferFontSize>()
378 .map(|size| size.0)
379 .unwrap_or(self.buffer_font_size);
380 clamp_font_size(font_size)
381 }
382
383 /// Returns the UI font size.
384 pub fn ui_font_size(&self, cx: &App) -> Pixels {
385 let font_size = cx
386 .try_global::<UiFontSize>()
387 .map(|size| size.0)
388 .unwrap_or(self.ui_font_size);
389 clamp_font_size(font_size)
390 }
391
392 /// Returns the agent panel font size. Falls back to the UI font size if unset.
393 pub fn agent_ui_font_size(&self, cx: &App) -> Pixels {
394 cx.try_global::<AgentUiFontSize>()
395 .map(|size| size.0)
396 .or(self.agent_ui_font_size)
397 .map(clamp_font_size)
398 .unwrap_or_else(|| self.ui_font_size(cx))
399 }
400
401 /// Returns the agent panel buffer font size.
402 pub fn agent_buffer_font_size(&self, cx: &App) -> Pixels {
403 cx.try_global::<AgentBufferFontSize>()
404 .map(|size| size.0)
405 .or(self.agent_buffer_font_size)
406 .map(clamp_font_size)
407 .unwrap_or_else(|| self.buffer_font_size(cx))
408 }
409
410 /// Returns the font family to use in the markdown preview,
411 /// falling back to the UI font family when unset.
412 pub fn markdown_preview_font_family(&self) -> &SharedString {
413 self.markdown_preview_font_family
414 .as_ref()
415 .unwrap_or(&self.ui_font.family)
416 }
417
418 /// Returns the buffer font size, read from the settings.
419 ///
420 /// The real buffer font size is stored in-memory, to support temporary font size changes.
421 /// Use [`Self::buffer_font_size`] to get the real font size.
422 pub fn buffer_font_size_settings(&self) -> Pixels {
423 self.buffer_font_size
424 }
425
426 /// Returns the UI font size, read from the settings.
427 ///
428 /// The real UI font size is stored in-memory, to support temporary font size changes.
429 /// Use [`Self::ui_font_size`] to get the real font size.
430 pub fn ui_font_size_settings(&self) -> Pixels {
431 self.ui_font_size
432 }
433
434 /// Returns the agent font size, read from the settings.
435 ///
436 /// The real agent font size is stored in-memory, to support temporary font size changes.
437 /// Use [`Self::agent_ui_font_size`] to get the real font size.
438 pub fn agent_ui_font_size_settings(&self) -> Option<Pixels> {
439 self.agent_ui_font_size
440 }
441
442 /// Returns the agent buffer font size, read from the settings.
443 ///
444 /// The real agent buffer font size is stored in-memory, to support temporary font size changes.
445 /// Use [`Self::agent_buffer_font_size`] to get the real font size.
446 pub fn agent_buffer_font_size_settings(&self) -> Option<Pixels> {
447 self.agent_buffer_font_size
448 }
449
450 /// Returns the buffer's line height.
451 pub fn line_height(&self) -> f32 {
452 f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
453 }
454
455 /// Applies the theme overrides, if there are any, to the current theme.
456 pub fn apply_theme_overrides(&self, mut arc_theme: Arc<Theme>) -> Arc<Theme> {
457 if let Some(experimental_theme_overrides) = &self.experimental_theme_overrides {
458 let mut theme = (*arc_theme).clone();
459 ThemeSettings::modify_theme(&mut theme, experimental_theme_overrides);
460 arc_theme = Arc::new(theme);
461 }
462
463 if let Some(theme_overrides) = self.theme_overrides.get(arc_theme.name.as_ref()) {
464 let mut theme = (*arc_theme).clone();
465 ThemeSettings::modify_theme(&mut theme, theme_overrides);
466 arc_theme = Arc::new(theme);
467 }
468
469 arc_theme
470 }
471
472 fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) {
473 if let Some(window_background_appearance) = theme_overrides.window_background_appearance {
474 base_theme.styles.window_background_appearance =
475 window_background_appearance.into_gpui();
476 }
477 let status_color_refinement = status_colors_refinement(&theme_overrides.status);
478
479 base_theme.styles.colors.refine(&theme_colors_refinement(
480 &theme_overrides.colors,
481 &status_color_refinement,
482 ));
483 base_theme.styles.status.refine(&status_color_refinement);
484 merge_player_colors(&mut base_theme.styles.player, &theme_overrides.players);
485 merge_accent_colors(&mut base_theme.styles.accents, &theme_overrides.accents);
486 base_theme.styles.syntax = SyntaxTheme::merge(
487 base_theme.styles.syntax.clone(),
488 syntax_overrides(theme_overrides),
489 );
490 }
491}
492
493/// Observe changes to the adjusted buffer font size.
494pub fn observe_buffer_font_size_adjustment<V: 'static>(
495 cx: &mut Context<V>,
496 f: impl 'static + Fn(&mut V, &mut Context<V>),
497) -> Subscription {
498 cx.observe_global::<BufferFontSize>(f)
499}
500
501/// Gets the font size, adjusted by the difference between the current buffer font size and the one set in the settings.
502pub fn adjusted_font_size(size: Pixels, cx: &App) -> Pixels {
503 let adjusted_font_size =
504 if let Some(BufferFontSize(adjusted_size)) = cx.try_global::<BufferFontSize>() {
505 let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
506 let delta = *adjusted_size - buffer_font_size;
507 size + delta
508 } else {
509 size
510 };
511 clamp_font_size(adjusted_font_size)
512}
513
514/// Adjusts the buffer font size, without persisting the result in the settings.
515/// This will be effective until the app is restarted.
516pub fn adjust_buffer_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
517 let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
518 let adjusted_size = cx
519 .try_global::<BufferFontSize>()
520 .map_or(buffer_font_size, |adjusted_size| adjusted_size.0);
521 cx.set_global(BufferFontSize(clamp_font_size(f(adjusted_size))));
522 cx.refresh_windows();
523}
524
525/// Resets the buffer font size to the default value.
526pub fn reset_buffer_font_size(cx: &mut App) {
527 if cx.has_global::<BufferFontSize>() {
528 cx.remove_global::<BufferFontSize>();
529 cx.refresh_windows();
530 }
531}
532
533#[allow(missing_docs)]
534pub fn setup_ui_font(window: &mut Window, cx: &mut App) -> gpui::Font {
535 let (ui_font, ui_font_size) = {
536 let theme_settings = ThemeSettings::get_global(cx);
537 let font = theme_settings.ui_font.clone();
538 (font, theme_settings.ui_font_size(cx))
539 };
540
541 window.set_rem_size(ui_font_size);
542 ui_font
543}
544
545/// Sets the adjusted UI font size.
546pub fn adjust_ui_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
547 let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
548 let adjusted_size = cx
549 .try_global::<UiFontSize>()
550 .map_or(ui_font_size, |adjusted_size| adjusted_size.0);
551 cx.set_global(UiFontSize(clamp_font_size(f(adjusted_size))));
552 cx.refresh_windows();
553}
554
555/// Resets the UI font size to the default value.
556pub fn reset_ui_font_size(cx: &mut App) {
557 if cx.has_global::<UiFontSize>() {
558 cx.remove_global::<UiFontSize>();
559 cx.refresh_windows();
560 }
561}
562
563/// Sets the adjusted font size of agent responses in the agent panel.
564pub fn adjust_agent_ui_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
565 let agent_ui_font_size = ThemeSettings::get_global(cx).agent_ui_font_size(cx);
566 let adjusted_size = cx
567 .try_global::<AgentUiFontSize>()
568 .map_or(agent_ui_font_size, |adjusted_size| adjusted_size.0);
569 cx.set_global(AgentUiFontSize(clamp_font_size(f(adjusted_size))));
570 cx.refresh_windows();
571}
572
573/// Resets the agent response font size in the agent panel to the default value.
574pub fn reset_agent_ui_font_size(cx: &mut App) {
575 if cx.has_global::<AgentUiFontSize>() {
576 cx.remove_global::<AgentUiFontSize>();
577 cx.refresh_windows();
578 }
579}
580
581/// Sets the adjusted font size of user messages in the agent panel.
582pub fn adjust_agent_buffer_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) {
583 let agent_buffer_font_size = ThemeSettings::get_global(cx).agent_buffer_font_size(cx);
584 let adjusted_size = cx
585 .try_global::<AgentBufferFontSize>()
586 .map_or(agent_buffer_font_size, |adjusted_size| adjusted_size.0);
587 cx.set_global(AgentBufferFontSize(clamp_font_size(f(adjusted_size))));
588 cx.refresh_windows();
589}
590
591/// Resets the user message font size in the agent panel to the default value.
592pub fn reset_agent_buffer_font_size(cx: &mut App) {
593 if cx.has_global::<AgentBufferFontSize>() {
594 cx.remove_global::<AgentBufferFontSize>();
595 cx.refresh_windows();
596 }
597}
598
599/// Ensures font size is within the valid range.
600pub fn clamp_font_size(size: Pixels) -> Pixels {
601 size.clamp(MIN_FONT_SIZE, MAX_FONT_SIZE)
602}
603
604fn font_fallbacks_from_settings(
605 fallbacks: Option<Vec<settings::FontFamilyName>>,
606) -> Option<FontFallbacks> {
607 fallbacks.map(|fallbacks| {
608 FontFallbacks::from_fonts(
609 fallbacks
610 .into_iter()
611 .map(|font_family| font_family.0.to_string())
612 .collect(),
613 )
614 })
615}
616
617impl settings::Settings for ThemeSettings {
618 fn from_settings(content: &settings::SettingsContent) -> Self {
619 let content = &content.theme;
620 let theme_selection: ThemeSelection = content.theme.clone().unwrap().into();
621 let icon_theme_selection: IconThemeSelection = content.icon_theme.clone().unwrap().into();
622 Self {
623 ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into_gpui()),
624 ui_font: Font {
625 family: content.ui_font_family.as_ref().unwrap().0.clone().into(),
626 features: content.ui_font_features.clone().unwrap().into_gpui(),
627 fallbacks: font_fallbacks_from_settings(content.ui_font_fallbacks.clone()),
628 weight: content.ui_font_weight.unwrap().into_gpui(),
629 style: Default::default(),
630 },
631 buffer_font: Font {
632 family: content
633 .buffer_font_family
634 .as_ref()
635 .unwrap()
636 .0
637 .clone()
638 .into(),
639 features: content.buffer_font_features.clone().unwrap().into_gpui(),
640 fallbacks: font_fallbacks_from_settings(content.buffer_font_fallbacks.clone()),
641 weight: content.buffer_font_weight.unwrap().into_gpui(),
642 style: FontStyle::default(),
643 },
644 buffer_font_size: clamp_font_size(content.buffer_font_size.unwrap().into_gpui()),
645 buffer_line_height: content.buffer_line_height.unwrap().into(),
646 agent_ui_font_size: content.agent_ui_font_size.map(|s| s.into_gpui()),
647 agent_buffer_font_size: content.agent_buffer_font_size.map(|s| s.into_gpui()),
648 markdown_preview_font_family: content
649 .markdown_preview_font_family
650 .as_ref()
651 .map(|f| f.0.clone().into()),
652 markdown_preview_theme: content
653 .markdown_preview_theme
654 .clone()
655 .map(ThemeSelection::from),
656 theme: theme_selection,
657 experimental_theme_overrides: content.experimental_theme_overrides.clone(),
658 theme_overrides: content.theme_overrides.clone(),
659 icon_theme: icon_theme_selection,
660 ui_density: ui_density_from_settings(content.ui_density.unwrap_or_default()),
661 unnecessary_code_fade: content.unnecessary_code_fade.unwrap().0.clamp(0.0, 0.9),
662 }
663 }
664}