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