settings.rs

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