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