markdown_preview: Add max-width setting (#59512)

This PR introduces a markdown preview max-width setting in which we
allow to limit by a given pixel number how wide the content within the
preview tab should go. Before this, content would render edge-to-edge
and if you're reading long markdown docs with no split panes, having it
be full width like that was a horrible reading experience.

So, right now, you can control that through the
`zed://settings/markdown_preview.max_width` setting.

<img width="600" alt="Screenshot 2026-06-18 at 1  24@2x"
src="https://github.com/user-attachments/assets/7f0216e1-e4ac-47f7-975b-5de7a0dd4c6d"
/>

Release Notes:

- Added setting to control max-width of content within the markdown
preview.
This commit is contained in:
Danilo Leal authored and GitHub committed 2026-06-18 10:54:13 +00:00
1 parent aed5023e80
commit fb2e66f61b
7 files changed
+122 -3

No files matched your search

+10
View File
@@ -104,6 +104,16 @@
// The unit for image file sizes: "binary" (KiB, MiB) or decimal (KB, MB)
"unit": "binary",
},
// Markdown preview settings
"markdown_preview": {
// Whether to limit the width of the rendered markdown content. When
// enabled, content is constrained to `max_width` and centered
// horizontally within the preview pane.
"limit_content_width": true,
// The maximum width, in pixels, of the rendered markdown content when
// limit_content_width is enabled.
"max_width": 800,
},
// Determines the modifier to be used to add multiple cursors with the mouse. The open hover link mouse gestures will adapt such that it do not conflict with the multicursor modifier.
//
// 1. Maps to `Alt` on Linux and Windows and to `Option` on MacOS:
@@ -1,6 +1,7 @@
use gpui::{App, actions};
use workspace::Workspace;
pub mod markdown_preview_settings;
pub mod markdown_preview_view;
pub use zed_actions::preview::markdown::{OpenPreview, OpenPreviewToTheSide};
@@ -0,0 +1,22 @@
use gpui::{Pixels, px};
use settings::{RegisterSetting, Settings};
/// The settings for the markdown preview.
#[derive(Clone, Copy, Debug, Default, RegisterSetting)]
pub struct MarkdownPreviewSettings {
/// The maximum width of the rendered markdown content, or `None` to render
/// content edge to edge.
pub max_width: Option<Pixels>,
}
impl Settings for MarkdownPreviewSettings {
fn from_settings(content: &settings::SettingsContent) -> Self {
let content = content.markdown_preview.clone().unwrap_or_default();
let max_width = if content.limit_content_width.unwrap_or(true) {
content.max_width.map(px)
} else {
None
};
Self { max_width }
}
}
@@ -32,6 +32,7 @@ use workspace::searchable::{
};
use workspace::{ItemId, Pane, Workspace, WorkspaceId, delete_unloaded_items};
use crate::markdown_preview_settings::MarkdownPreviewSettings;
use crate::{
OpenFollowingPreview, OpenPreview, OpenPreviewToTheSide, ScrollDown, ScrollDownByItem,
};
@@ -1132,7 +1133,8 @@ impl Render for MarkdownPreviewView {
let markdown_element =
self.render_markdown_element(&preview_theme, window, cx);
let markdown = self.markdown.clone();
right_click_menu("markdown-preview-context-menu")
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);
@@ -1148,7 +1150,11 @@ impl Render for MarkdownPreviewView {
})
})
})
})
});
div()
.w_full()
.when_some(max_width, |this, max_width| this.max_w(max_width).mx_auto())
.child(content)
}),
)
.vertical_scrollbar_for(&self.scroll_handle, window, cx)
+1
View File
@@ -194,6 +194,7 @@ impl VsCodeSettings {
helix_mode: None,
hide_mouse: None,
image_viewer: None,
markdown_preview: None,
journal: None,
language_models: None,
line_indicator_format: None,
@@ -178,6 +178,9 @@ pub struct SettingsContent {
/// The settings for the image viewer.
pub image_viewer: Option<ImageViewerSettingsContent>,
/// The settings for the markdown preview.
pub markdown_preview: Option<MarkdownPreviewSettingsContent>,
pub repl: Option<ReplSettingsContent>,
/// Whether or not to enable Helix mode.
@@ -1094,6 +1097,23 @@ pub enum LineIndicatorFormat {
Long,
}
/// The settings for the markdown preview.
#[with_fallible_options]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)]
pub struct MarkdownPreviewSettingsContent {
/// Whether to limit the width of the rendered markdown content. When
/// enabled, content is constrained to `max_width` and centered
/// horizontally within the preview pane, for optimal readability.
///
/// Default: true
pub limit_content_width: Option<bool>,
/// The maximum width, in pixels, of the rendered markdown content when
/// `limit_content_width` is enabled.
///
/// Default: 800
pub max_width: Option<f32>,
}
/// The settings for the image viewer.
#[with_fallible_options]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, MergeFrom, Default, PartialEq)]
+60 -1
View File
@@ -9654,7 +9654,7 @@ fn language_settings_data() -> Box<[SettingsPageItem]> {
]
}
fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 3] {
fn global_only_miscellaneous_sub_section() -> [SettingsPageItem; 4] {
[
SettingsPageItem::SettingItem(SettingItem {
title: "Image Viewer",
@@ -9675,6 +9675,65 @@ fn language_settings_data() -> Box<[SettingsPageItem]> {
metadata: None,
files: USER,
}),
SettingsPageItem::DynamicItem(DynamicItem {
discriminant: SettingItem {
files: USER,
title: "Limit Markdown Preview Width",
description: "Whether to constrain the markdown preview content to a maximum width, centering it when the pane is wider, for optimal readability.",
field: Box::new(SettingField::<bool> {
organization_override: None,
json_path: Some("markdown_preview.limit_content_width"),
pick: |settings_content| {
settings_content
.markdown_preview
.as_ref()?
.limit_content_width
.as_ref()
},
write: |settings_content, value, _| {
settings_content
.markdown_preview
.get_or_insert_default()
.limit_content_width = value;
},
}),
metadata: None,
},
pick_discriminant: |settings_content| {
let enabled = settings_content
.markdown_preview
.as_ref()?
.limit_content_width
.unwrap_or(true);
Some(if enabled { 1 } else { 0 })
},
fields: vec![
vec![],
vec![SettingItem {
files: USER,
title: "Max Width",
description: "Maximum content width in pixels. Content will be centered when the pane is wider than this value.",
field: Box::new(SettingField {
organization_override: None,
json_path: Some("markdown_preview.max_width"),
pick: |settings_content| {
settings_content
.markdown_preview
.as_ref()?
.max_width
.as_ref()
},
write: |settings_content, value, _| {
settings_content
.markdown_preview
.get_or_insert_default()
.max_width = value;
},
}),
metadata: None,
}],
],
}),
SettingsPageItem::SettingItem(SettingItem {
title: "Auto Replace Emoji Shortcode",
description: "Whether to automatically replace emoji shortcodes with emoji characters.",