From 7e1b41924354c4297301a8bbe45b4b41d51aa26e Mon Sep 17 00:00:00 2001 From: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:35:24 -0300 Subject: [PATCH] markdown: Add ability to customize individual heading level (#28733) This PR adds a new field in the `MarkdownStyle` struct, `heading_level_styles`, allowing, via the newly added function `apply_heading_style` and struct `HeadingLevelStyles` to customize each individual heading level in Markdown rendering/styling function. Things like this should now be possible: ```rust MarkdownStyle { heading_level_styles: Some(HeadingLevelStyles { h1: Some(TextStyleRefinement { font_size: Some(rems(1.15).into()), ..Default::default() }), }), ..Default::default() } ``` Release Notes: - N/A --- crates/markdown/src/markdown.rs | 67 ++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 85dcefc40a..16f4f621e0 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -32,6 +32,17 @@ use crate::parser::CodeBlockKind; /// If the callback returns `None`, the default link style will be used. type LinkStyleCallback = Rc Option>; +/// Defines custom style refinements for each heading level (H1-H6) +#[derive(Clone, Default)] +pub struct HeadingLevelStyles { + pub h1: Option, + pub h2: Option, + pub h3: Option, + pub h4: Option, + pub h5: Option, + pub h6: Option, +} + #[derive(Clone)] pub struct MarkdownStyle { pub base_text_style: TextStyle, @@ -46,6 +57,7 @@ pub struct MarkdownStyle { pub syntax: Arc, pub selection_background_color: Hsla, pub heading: StyleRefinement, + pub heading_level_styles: Option, pub table_overflow_x_scroll: bool, } @@ -64,6 +76,7 @@ impl Default for MarkdownStyle { syntax: Arc::new(SyntaxTheme::default()), selection_background_color: Default::default(), heading: Default::default(), + heading_level_styles: None, table_overflow_x_scroll: false, } } @@ -628,17 +641,19 @@ impl Element for MarkdownElement { } MarkdownTag::Heading { level, .. } => { let mut heading = div().mb_2(); - heading = match level { - pulldown_cmark::HeadingLevel::H1 => heading.text_3xl(), - pulldown_cmark::HeadingLevel::H2 => heading.text_2xl(), - pulldown_cmark::HeadingLevel::H3 => heading.text_xl(), - pulldown_cmark::HeadingLevel::H4 => heading.text_lg(), - _ => heading, - }; - heading.style().refine(&self.style.heading); - builder.push_text_style( - self.style.heading.text_style().clone().unwrap_or_default(), + + heading = apply_heading_style( + heading, + *level, + self.style.heading_level_styles.as_ref(), ); + + heading.style().refine(&self.style.heading); + + let text_style = + self.style.heading.text_style().clone().unwrap_or_default(); + + builder.push_text_style(text_style); builder.push_div(heading, range, markdown_end); } MarkdownTag::BlockQuote => { @@ -1043,6 +1058,38 @@ impl Element for MarkdownElement { } } +fn apply_heading_style( + mut heading: Div, + level: pulldown_cmark::HeadingLevel, + custom_styles: Option<&HeadingLevelStyles>, +) -> Div { + heading = match level { + pulldown_cmark::HeadingLevel::H1 => heading.text_3xl(), + pulldown_cmark::HeadingLevel::H2 => heading.text_2xl(), + pulldown_cmark::HeadingLevel::H3 => heading.text_xl(), + pulldown_cmark::HeadingLevel::H4 => heading.text_lg(), + pulldown_cmark::HeadingLevel::H5 => heading.text_base(), + pulldown_cmark::HeadingLevel::H6 => heading.text_sm(), + }; + + if let Some(styles) = custom_styles { + let style_opt = match level { + pulldown_cmark::HeadingLevel::H1 => &styles.h1, + pulldown_cmark::HeadingLevel::H2 => &styles.h2, + pulldown_cmark::HeadingLevel::H3 => &styles.h3, + pulldown_cmark::HeadingLevel::H4 => &styles.h4, + pulldown_cmark::HeadingLevel::H5 => &styles.h5, + pulldown_cmark::HeadingLevel::H6 => &styles.h6, + }; + + if let Some(style) = style_opt { + heading.style().text = Some(style.clone()); + } + } + + heading +} + fn render_copy_code_block_button( id: usize, code: String,