diff --git a/assets/settings/default.json b/assets/settings/default.json index 9bb7d056bb..67aacf3f87 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -73,6 +73,12 @@ "agent_buffer_font_size": 12, // The default font size for the commit editor in the git panel and commit modal. "git_commit_buffer_font_size": 12, + // The default font size for the markdown preview. Falls back to the editor font size if unset. + "markdown_preview_font_size": null, + // The font family for the markdown preview. Falls back to the UI font family if unset. + "markdown_preview_font_family": null, + // The font family for code blocks in the markdown preview. Falls back to the editor font family if unset. + "markdown_preview_code_font_family": null, // How much to fade out unused code. "unnecessary_code_fade": 0.3, // Active pane styling settings. diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 80265834df..6d37ce1c86 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -175,10 +175,14 @@ impl MarkdownStyle { theme_settings.agent_buffer_font_size(cx), theme_settings.agent_ui_font_size(cx), ), - MarkdownFont::Editor | MarkdownFont::Preview => ( + MarkdownFont::Editor => ( theme_settings.buffer_font_size(cx), theme_settings.ui_font_size(cx), ), + MarkdownFont::Preview => ( + theme_settings.markdown_preview_font_size(cx), + theme_settings.ui_font_size(cx), + ), }; let body_font_family = if is_preview { @@ -199,7 +203,11 @@ impl MarkdownStyle { font_family: Some(body_font_family), font_fallbacks: theme_settings.ui_font.fallbacks.clone(), font_features: Some(theme_settings.ui_font.features.clone()), - font_size: Some(ui_font_size.into()), + font_size: Some(if is_preview { + rems(1.0).into() + } else { + ui_font_size.into() + }), line_height: Some(line_height.into()), color: Some(colors.text), ..Default::default() @@ -306,14 +314,14 @@ impl MarkdownStyle { }; if is_preview { - style.with_preview_overrides(ui_font_size, colors) + style.with_preview_overrides(colors) } else { style } } - fn with_preview_overrides(mut self, ui_font_size: Pixels, colors: &theme::ThemeColors) -> Self { - let body_font_size = ui_font_size * 0.92; + fn with_preview_overrides(mut self, colors: &theme::ThemeColors) -> Self { + let body_font_size = rems(0.92); self.base_text_style.font_size = body_font_size.into(); self.container_style.text.font_size = Some(body_font_size.into()); @@ -3966,7 +3974,7 @@ impl RenderedText { #[cfg(test)] mod tests { use super::*; - use gpui::{RenderImage, TestAppContext, size}; + use gpui::{RenderImage, TestAppContext, UpdateGlobal, size}; use language::{Language, LanguageConfig, LanguageMatcher}; use std::sync::{ Arc, @@ -5168,6 +5176,28 @@ mod tests { }); } + #[gpui::test] + fn test_preview_body_font_size_is_rem_based(cx: &mut TestAppContext) { + ensure_theme_initialized(cx); + let (_, cx) = cx.add_window_view(|_, _| TestWindow); + cx.update(|window, cx| { + let style = MarkdownStyle::themed(MarkdownFont::Preview, window, cx); + assert!( + matches!(style.base_text_style.font_size, AbsoluteLength::Rems(_)), + "preview body font size must be rem-based, got {:?}", + style.base_text_style.font_size + ); + assert!( + matches!( + style.container_style.text.font_size, + Some(AbsoluteLength::Rems(_)) + ), + "preview container font size must be rem-based, got {:?}", + style.container_style.text.font_size + ); + }); + } + #[track_caller] fn assert_mappings(rendered: &RenderedText, expected: Vec>) { assert_eq!(rendered.lines.len(), expected.len(), "line count mismatch"); @@ -5276,4 +5306,70 @@ mod tests { "H3 line height ({h3_line_height:?}) should be greater than body text ({body_line_height:?})" ); } + + #[gpui::test] + fn test_editor_zoom_does_not_affect_markdown_preview(cx: &mut TestAppContext) { + ensure_theme_initialized(cx); + + cx.update(|cx| { + settings::SettingsStore::update_global(cx, |store, cx| { + store.update_user_settings(cx, |settings| { + settings.theme.buffer_font_size = Some(16.0.into()); + settings.theme.markdown_preview_font_size = None; + }); + }); + }); + cx.run_until_parked(); + + cx.update(|cx| { + let before = ThemeSettings::get_global(cx).markdown_preview_font_size(cx); + assert_eq!(before, px(16.0)); + + theme_settings::increase_buffer_font_size(cx); + theme_settings::increase_buffer_font_size(cx); + theme_settings::increase_buffer_font_size(cx); + + assert_eq!(ThemeSettings::get_global(cx).buffer_font_size(cx), px(19.0)); + assert_eq!( + ThemeSettings::get_global(cx).markdown_preview_font_size(cx), + before + ); + }); + } + + #[gpui::test] + fn test_markdown_preview_follows_buffer_font_size_setting_when_unset(cx: &mut TestAppContext) { + ensure_theme_initialized(cx); + + cx.update(|cx| { + settings::SettingsStore::update_global(cx, |store, cx| { + store.update_user_settings(cx, |settings| { + settings.theme.buffer_font_size = Some(20.0.into()); + settings.theme.markdown_preview_font_size = None; + }); + }); + }); + cx.run_until_parked(); + cx.update(|cx| { + assert_eq!( + ThemeSettings::get_global(cx).markdown_preview_font_size(cx), + px(20.0) + ); + }); + + cx.update(|cx| { + settings::SettingsStore::update_global(cx, |store, cx| { + store.update_user_settings(cx, |settings| { + settings.theme.buffer_font_size = Some(24.0.into()); + }); + }); + }); + cx.run_until_parked(); + cx.update(|cx| { + assert_eq!( + ThemeSettings::get_global(cx).markdown_preview_font_size(cx), + px(24.0) + ); + }); + } } diff --git a/crates/markdown_preview/src/markdown_preview_view.rs b/crates/markdown_preview/src/markdown_preview_view.rs index 0ecb0cc458..55ba68ecb8 100644 --- a/crates/markdown_preview/src/markdown_preview_view.rs +++ b/crates/markdown_preview/src/markdown_preview_view.rs @@ -12,7 +12,7 @@ use editor::{Editor, EditorEvent, MultiBufferOffset, SelectionEffects}; use gpui::{ App, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, Focusable, ImageSource, InteractiveElement, IntoElement, IsZero, Pixels, Render, Resource, RetainAllImageCache, - ScrollHandle, SharedString, SharedUri, Subscription, Task, WeakEntity, Window, point, + ScrollHandle, SharedString, SharedUri, Subscription, Task, WeakEntity, Window, point, px, }; use language::LanguageRegistry; use markdown::{ @@ -21,9 +21,10 @@ use markdown::{ }; use project::search::SearchQuery; use project::{Project, ProjectPath}; -use settings::{SeedQuerySetting, Settings}; +use settings::{SeedQuerySetting, Settings, update_settings_file}; use theme::{SystemAppearance, Theme, ThemeRegistry}; use theme_settings::ThemeSettings; +use ui::utils::WithRemSize; use ui::{ContextMenu, WithScrollbar, prelude::*, right_click_menu}; use util::markdown::split_local_url_fragment; use workspace::item::{Item, ItemBufferKind, ItemHandle, SaveOptions, SerializableItem}; @@ -31,6 +32,7 @@ use workspace::searchable::{ Direction, SearchEvent, SearchOptions, SearchToken, SearchableItem, SearchableItemHandle, }; use workspace::{ItemId, Pane, Workspace, WorkspaceId, delete_unloaded_items}; +use zed_actions::{DecreaseBufferFontSize, IncreaseBufferFontSize, ResetBufferFontSize}; use crate::markdown_preview_settings::MarkdownPreviewSettings; use crate::{ @@ -589,7 +591,64 @@ impl MarkdownPreviewView { fn line_scroll_amount(&self, cx: &App) -> Pixels { let settings = ThemeSettings::get_global(cx); - settings.buffer_font_size(cx) * settings.buffer_line_height.value() + settings.markdown_preview_font_size(cx) * settings.buffer_line_height.value() + } + + fn increase_font_size( + &mut self, + action: &IncreaseBufferFontSize, + _: &mut Window, + cx: &mut Context, + ) { + self.adjust_font_size(action.persist, px(1.0), cx); + } + + fn decrease_font_size( + &mut self, + action: &DecreaseBufferFontSize, + _: &mut Window, + cx: &mut Context, + ) { + self.adjust_font_size(action.persist, px(-1.0), cx); + } + + fn adjust_font_size(&mut self, persist: bool, delta: Pixels, cx: &mut Context) { + if persist { + let Ok(fs) = self + .workspace + .read_with(cx, |workspace, _| workspace.app_state().fs.clone()) + else { + return; + }; + update_settings_file(fs, cx, move |settings, cx| { + let size = ThemeSettings::get_global(cx).markdown_preview_font_size(cx) + delta; + settings.theme.markdown_preview_font_size = + Some(f32::from(theme_settings::clamp_font_size(size)).into()); + }); + } else { + theme_settings::adjust_markdown_preview_font_size(cx, |size| size + delta); + } + } + + fn reset_font_size( + &mut self, + action: &ResetBufferFontSize, + _: &mut Window, + cx: &mut Context, + ) { + if action.persist { + let Ok(fs) = self + .workspace + .read_with(cx, |workspace, _| workspace.app_state().fs.clone()) + else { + return; + }; + update_settings_file(fs, cx, move |settings, _| { + settings.theme.markdown_preview_font_size = None; + }); + } else { + theme_settings::reset_markdown_preview_font_size(cx); + } } fn scroll_by_amount(&self, distance: Pixels) { @@ -1127,6 +1186,7 @@ impl Render for MarkdownPreviewView { .as_ref() .map(|theme| theme.colors().editor_background) .unwrap_or_else(|| cx.theme().colors().editor_background); + let preview_font_size = ThemeSettings::get_global(cx).markdown_preview_font_size(cx); div() .image_cache(self.image_cache.clone()) .id("MarkdownPreview") @@ -1140,44 +1200,51 @@ impl Render for MarkdownPreviewView { .on_action(cx.listener(MarkdownPreviewView::scroll_down_by_item)) .on_action(cx.listener(MarkdownPreviewView::scroll_to_top)) .on_action(cx.listener(MarkdownPreviewView::scroll_to_bottom)) + .on_action(cx.listener(MarkdownPreviewView::increase_font_size)) + .on_action(cx.listener(MarkdownPreviewView::decrease_font_size)) + .on_action(cx.listener(MarkdownPreviewView::reset_font_size)) .w_full() .flex_1() .min_h_0() .bg(bg_color) .child( - div() - .id("markdown-preview-scroll-container") - .size_full() - .overflow_y_scroll() - .track_scroll(&self.scroll_handle) - .p_4() - .child({ - let markdown_element = - self.render_markdown_element(&preview_theme, window, cx); - let markdown = self.markdown.clone(); - let max_width = MarkdownPreviewSettings::get_global(cx).max_width; - let content = right_click_menu("markdown-preview-context-menu") - .trigger(move |_, _, _| markdown_element) - .menu(move |window, cx| { - let focus = window.focused(cx); - let context_menu_link = - markdown.read(cx).context_menu_link().cloned(); - ContextMenu::build(window, cx, move |menu, _, _cx| { - menu.when_some(focus, |menu, focus| menu.context(focus)) - .when_some(context_menu_link, |menu, url| { - menu.entry("Copy Link", None, move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new_string( - url.to_string(), - )); + WithRemSize::new(preview_font_size).size_full().child( + div() + .id("markdown-preview-scroll-container") + .size_full() + .overflow_y_scroll() + .track_scroll(&self.scroll_handle) + .p_4() + .child({ + let markdown_element = + self.render_markdown_element(&preview_theme, window, cx); + let markdown = self.markdown.clone(); + let max_width = MarkdownPreviewSettings::get_global(cx).max_width; + let content = right_click_menu("markdown-preview-context-menu") + .trigger(move |_, _, _| markdown_element) + .menu(move |window, cx| { + let focus = window.focused(cx); + let context_menu_link = + markdown.read(cx).context_menu_link().cloned(); + ContextMenu::build(window, cx, move |menu, _, _cx| { + menu.when_some(focus, |menu, focus| menu.context(focus)) + .when_some(context_menu_link, |menu, url| { + menu.entry("Copy Link", None, move |_, cx| { + cx.write_to_clipboard( + ClipboardItem::new_string(url.to_string()), + ); + }) }) - }) + }) + }); + div() + .w_full() + .when_some(max_width, |this, max_width| { + this.max_w(max_width).mx_auto() }) - }); - div() - .w_full() - .when_some(max_width, |this, max_width| this.max_w(max_width).mx_auto()) - .child(content) - }), + .child(content) + }), + ), ) .vertical_scrollbar_for(&self.scroll_handle, window, cx) } diff --git a/crates/settings/src/vscode_import.rs b/crates/settings/src/vscode_import.rs index f9ef797b33..75b953e7cb 100644 --- a/crates/settings/src/vscode_import.rs +++ b/crates/settings/src/vscode_import.rs @@ -982,6 +982,7 @@ impl VsCodeSettings { git_commit_buffer_font_size: None, markdown_preview_font_family: None, markdown_preview_code_font_family: None, + markdown_preview_font_size: None, markdown_preview_theme: None, theme: None, icon_theme: None, diff --git a/crates/settings_content/src/theme.rs b/crates/settings_content/src/theme.rs index 305e1d4053..6c40f210c8 100644 --- a/crates/settings_content/src/theme.rs +++ b/crates/settings_content/src/theme.rs @@ -156,6 +156,9 @@ pub struct ThemeSettingsContent { /// The name of a font to use for code (code blocks and inline code) in the /// markdown preview. Falls back to the buffer font if unset. pub markdown_preview_code_font_family: Option, + /// The font size to use for rendering in the markdown preview. + /// Falls back to the buffer font size if unset. + pub markdown_preview_font_size: Option, /// The theme to use for the markdown preview. /// Falls back to the main editor theme if unset. pub markdown_preview_theme: Option, diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 15f28020c5..5bdb50b22e 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -1161,6 +1161,67 @@ fn appearance_page() -> SettingsPage { ] } + fn markdown_preview_font_section() -> [SettingsPageItem; 4] { + [ + SettingsPageItem::SectionHeader("Markdown Preview Font"), + SettingsPageItem::SettingItem(SettingItem { + title: "Font Family", + description: "Font family for the markdown preview. Falls back to the UI font family.", + field: Box::new(SettingField { + organization_override: None, + json_path: Some("markdown_preview_font_family"), + pick: |settings_content| { + settings_content.theme.markdown_preview_font_family.as_ref() + }, + write: |settings_content, value, _| { + settings_content.theme.markdown_preview_font_family = value; + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Code Font Family", + description: "Font family for code blocks in the markdown preview. Falls back to the editor font family.", + field: Box::new(SettingField { + organization_override: None, + json_path: Some("markdown_preview_code_font_family"), + pick: |settings_content| { + settings_content + .theme + .markdown_preview_code_font_family + .as_ref() + }, + write: |settings_content, value, _| { + settings_content.theme.markdown_preview_code_font_family = value; + }, + }), + metadata: None, + files: USER, + }), + SettingsPageItem::SettingItem(SettingItem { + title: "Font Size", + description: "Font size for the markdown preview. Falls back to the editor font size.", + field: Box::new(SettingField { + organization_override: None, + json_path: Some("markdown_preview_font_size"), + pick: |settings_content| { + settings_content + .theme + .markdown_preview_font_size + .as_ref() + .or(settings_content.theme.buffer_font_size.as_ref()) + }, + write: |settings_content, value, _| { + settings_content.theme.markdown_preview_font_size = value; + }, + }), + metadata: None, + files: USER, + }), + ] + } + fn text_rendering_section() -> [SettingsPageItem; 2] { [ SettingsPageItem::SectionHeader("Text Rendering"), @@ -1389,6 +1450,7 @@ fn appearance_page() -> SettingsPage { buffer_font_section(), ui_font_section(), agent_panel_font_section(), + markdown_preview_font_section(), text_rendering_section(), cursor_section(), highlighting_section(), diff --git a/crates/theme_settings/src/settings.rs b/crates/theme_settings/src/settings.rs index 3d36f08aa5..5deadcc108 100644 --- a/crates/theme_settings/src/settings.rs +++ b/crates/theme_settings/src/settings.rs @@ -63,6 +63,9 @@ pub struct ThemeSettings { /// The font family to use for code in the markdown preview. /// Falls back to the buffer font family if unset. markdown_preview_code_font_family: Option, + /// The font size to use for rendering in the markdown preview. + /// Falls back to the buffer font size if unset. + markdown_preview_font_size: Option, /// The theme to use for the markdown preview. /// Falls back to the main editor theme if unset. pub markdown_preview_theme: Option, @@ -124,6 +127,12 @@ pub struct GitCommitBufferFontSize(Pixels); impl Global for GitCommitBufferFontSize {} +/// In-memory override for the markdown preview font size. +#[derive(Default)] +pub struct MarkdownPreviewFontSize(Pixels); + +impl Global for MarkdownPreviewFontSize {} + /// Represents the selection of a theme, which can be either static or dynamic. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(untagged)] @@ -440,6 +449,18 @@ impl ThemeSettings { .unwrap_or(&self.buffer_font.family) } + /// Returns the markdown preview font size. + /// + /// Note: the fallback deliberately uses `self.buffer_font_size` instead of `buffer_font_size(cx)`, + /// so that temporary editor zoom does not also resize the markdown preview. + pub fn markdown_preview_font_size(&self, cx: &App) -> Pixels { + cx.try_global::() + .map(|size| size.0) + .or(self.markdown_preview_font_size) + .map(clamp_font_size) + .unwrap_or_else(|| clamp_font_size(self.buffer_font_size)) + } + /// Returns the buffer font size, read from the settings. /// /// The real buffer font size is stored in-memory, to support temporary font size changes. @@ -476,6 +497,14 @@ impl ThemeSettings { self.git_commit_buffer_font_size } + /// Returns the markdown preview font size, read from the settings. + /// + /// The real markdown preview font size is stored in-memory, to support temporary + /// font size changes. Use [`Self::markdown_preview_font_size`] to get the real font size. + pub fn markdown_preview_font_size_settings(&self) -> Option { + self.markdown_preview_font_size + } + /// Returns the buffer's line height. pub fn line_height(&self) -> f32 { f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT) @@ -643,6 +672,24 @@ pub fn reset_git_commit_buffer_font_size(cx: &mut App) { } } +/// Sets the adjusted font size of the markdown preview. +pub fn adjust_markdown_preview_font_size(cx: &mut App, f: impl FnOnce(Pixels) -> Pixels) { + let markdown_preview_font_size = ThemeSettings::get_global(cx).markdown_preview_font_size(cx); + let adjusted_size = cx + .try_global::() + .map_or(markdown_preview_font_size, |adjusted_size| adjusted_size.0); + cx.set_global(MarkdownPreviewFontSize(clamp_font_size(f(adjusted_size)))); + cx.refresh_windows(); +} + +/// Resets the markdown preview font size to the default value. +pub fn reset_markdown_preview_font_size(cx: &mut App) { + if cx.has_global::() { + cx.remove_global::(); + cx.refresh_windows(); + } +} + /// Ensures font size is within the valid range. pub fn clamp_font_size(size: Pixels) -> Pixels { size.clamp(MIN_FONT_SIZE, MAX_FONT_SIZE) @@ -701,6 +748,7 @@ impl settings::Settings for ThemeSettings { .markdown_preview_code_font_family .as_ref() .map(|f| f.0.clone().into()), + markdown_preview_font_size: content.markdown_preview_font_size.map(|s| s.into_gpui()), markdown_preview_theme: content .markdown_preview_theme .clone() diff --git a/crates/theme_settings/src/theme_settings.rs b/crates/theme_settings/src/theme_settings.rs index 192dfa54c4..ff47093c0c 100644 --- a/crates/theme_settings/src/theme_settings.rs +++ b/crates/theme_settings/src/theme_settings.rs @@ -29,13 +29,14 @@ pub use crate::schema::{ use crate::settings::adjust_buffer_font_size; pub use crate::settings::{ AgentBufferFontSize, AgentUiFontSize, BufferLineHeight, FontFamilyName, - GitCommitBufferFontSize, IconThemeName, IconThemeSelection, ThemeAppearanceMode, ThemeName, - ThemeSelection, ThemeSettings, adjust_agent_buffer_font_size, adjust_agent_ui_font_size, - adjust_git_commit_buffer_font_size, adjust_ui_font_size, adjusted_font_size, - appearance_to_mode, clamp_font_size, default_theme, observe_buffer_font_size_adjustment, + GitCommitBufferFontSize, IconThemeName, IconThemeSelection, MarkdownPreviewFontSize, + ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings, adjust_agent_buffer_font_size, + adjust_agent_ui_font_size, adjust_git_commit_buffer_font_size, + adjust_markdown_preview_font_size, adjust_ui_font_size, adjusted_font_size, appearance_to_mode, + clamp_font_size, default_theme, observe_buffer_font_size_adjustment, reset_agent_buffer_font_size, reset_agent_ui_font_size, reset_buffer_font_size, - reset_git_commit_buffer_font_size, reset_ui_font_size, set_icon_theme, set_mode, set_theme, - setup_ui_font, + reset_git_commit_buffer_font_size, reset_markdown_preview_font_size, reset_ui_font_size, + set_icon_theme, set_mode, set_theme, setup_ui_font, }; pub use theme::UiDensity; @@ -91,6 +92,8 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) { let mut prev_agent_buffer_font_size_settings = settings.agent_buffer_font_size_settings(); let mut prev_git_commit_buffer_font_size_settings = settings.git_commit_buffer_font_size_settings(); + let mut prev_markdown_preview_font_size_settings = + settings.markdown_preview_font_size_settings(); let mut prev_theme_name = settings.theme.name(SystemAppearance::global(cx).0); let mut prev_icon_theme_name = settings.icon_theme.name(SystemAppearance::global(cx).0); let mut prev_theme_overrides = ( @@ -106,6 +109,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) { let agent_ui_font_size_settings = settings.agent_ui_font_size_settings(); let agent_buffer_font_size_settings = settings.agent_buffer_font_size_settings(); let git_commit_buffer_font_size_settings = settings.git_commit_buffer_font_size_settings(); + let markdown_preview_font_size_settings = settings.markdown_preview_font_size_settings(); let theme_name = settings.theme.name(SystemAppearance::global(cx).0); let icon_theme_name = settings.icon_theme.name(SystemAppearance::global(cx).0); let theme_overrides = ( @@ -138,6 +142,11 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) { reset_git_commit_buffer_font_size(cx); } + if markdown_preview_font_size_settings != prev_markdown_preview_font_size_settings { + prev_markdown_preview_font_size_settings = markdown_preview_font_size_settings; + reset_markdown_preview_font_size(cx); + } + if theme_name != prev_theme_name || theme_overrides != prev_theme_overrides { prev_theme_name = theme_name; prev_theme_overrides = theme_overrides; diff --git a/docs/src/visual-customization.md b/docs/src/visual-customization.md index bff03dea5d..9192249848 100644 --- a/docs/src/visual-customization.md +++ b/docs/src/visual-customization.md @@ -71,7 +71,17 @@ If you would like to use distinct themes for light mode/dark mode that can be se "agent_ui_font_size": 15, // Controls the font size for the agent panel's message editor, user message, // and any other snippet of code. - "agent_buffer_font_size": 12 + "agent_buffer_font_size": 12, + + // Controls the font size for the markdown preview. + // If not specified, it falls back to the editor font size. + "markdown_preview_font_size": null, + // Controls the font family for the markdown preview. + // If not specified, it falls back to the UI font family. + "markdown_preview_font_family": null, + // Controls the font family for code blocks in the markdown preview. + // If not specified, it falls back to the editor font family. + "markdown_preview_code_font_family": null ``` ### Font ligatures