From 512f46aec865b51a14bfaf401cdea0d68aeb00aa Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 25 Sep 2025 17:11:14 +0800 Subject: [PATCH] text_view: Add to support custom code block style. (#1288) And remove bg, padding for editor hover code blocks. image --- crates/ui/src/input/popovers/mod.rs | 14 ++++-- crates/ui/src/text/mod.rs | 2 + crates/ui/src/text/node.rs | 13 +++--- crates/ui/src/text/style.rs | 67 +++++++++++++++++++++++++++++ crates/ui/src/text/text_view.rs | 65 +++------------------------- 5 files changed, 94 insertions(+), 67 deletions(-) create mode 100644 crates/ui/src/text/style.rs diff --git a/crates/ui/src/input/popovers/mod.rs b/crates/ui/src/input/popovers/mod.rs index 8887d7cd..d281e8e0 100644 --- a/crates/ui/src/input/popovers/mod.rs +++ b/crates/ui/src/input/popovers/mod.rs @@ -11,13 +11,13 @@ pub(crate) use diagnostic_popover::*; pub(crate) use hover_popover::*; use gpui::{ - div, rems, App, Div, ElementId, Entity, InteractiveElement as _, IntoElement, SharedString, - Stateful, Styled as _, Window, + div, px, rems, App, Div, ElementId, Entity, InteractiveElement as _, IntoElement, SharedString, + Stateful, StyleRefinement, Styled as _, Window, }; use crate::{ text::{TextView, TextViewStyle}, - StyledExt as _, + ActiveTheme, StyledExt as _, }; pub(crate) enum ContextMenu { @@ -58,7 +58,13 @@ pub(super) fn render_markdown( 1..=3 => rem_size * 1, 4 => rem_size * 0.9, _ => rem_size * 0.8, - }), + }) + .code_block( + StyleRefinement::default() + .bg(cx.theme().transparent) + .p_0() + .text_size(px(11.)), + ), ) .selectable() } diff --git a/crates/ui/src/text/mod.rs b/crates/ui/src/text/mod.rs index 8f481993..88682fbf 100644 --- a/crates/ui/src/text/mod.rs +++ b/crates/ui/src/text/mod.rs @@ -1,10 +1,12 @@ mod format; mod inline; mod node; +mod style; mod text_view; mod utils; use gpui::App; +pub use style::*; pub use text_view::*; pub(crate) fn init(cx: &mut App) { diff --git a/crates/ui/src/text/node.rs b/crates/ui/src/text/node.rs index 37ac22c1..cb5601fc 100644 --- a/crates/ui/src/text/node.rs +++ b/crates/ui/src/text/node.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, ops::Range}; use gpui::{ div, img, prelude::FluentBuilder as _, px, relative, rems, AnyElement, App, DefiniteLength, Div, ElementId, FontStyle, FontWeight, Half, HighlightStyle, InteractiveElement as _, - IntoElement, Length, ObjectFit, ParentElement, Rems, SharedString, SharedUri, + IntoElement, Length, ObjectFit, ParentElement, SharedString, SharedUri, StatefulInteractiveElement, Styled, StyledImage as _, Window, }; use markdown::mdast; @@ -14,7 +14,7 @@ use crate::{ highlighter::SyntaxHighlighter, text::inline::{Inline, InlineState}, tooltip::Tooltip, - v_flex, ActiveTheme as _, Icon, IconName, + v_flex, ActiveTheme as _, Icon, IconName, StyledExt, }; use super::{utils::list_item_prefix, TextViewStyle}; @@ -318,16 +318,19 @@ impl CodeBlock { text } - fn render(&self, mb: Rems, _: &mut Window, cx: &mut App) -> AnyElement { + fn render(&self, node_cx: &NodeContext, _: &mut Window, cx: &mut App) -> AnyElement { + let style = &node_cx.style; + div() .id("codeblock") - .mb(mb) + .mb(style.paragraph_gap) .p_3() .rounded(cx.theme().radius) .bg(cx.theme().accent) .font_family("Menlo, Monaco, Consolas, monospace") .text_size(rems(0.875)) .relative() + .refine_style(&style.code_block) .child(Inline::new( "code", self.state.clone(), @@ -927,7 +930,7 @@ impl Node { items }) .into_any_element(), - Node::CodeBlock(code_block) => code_block.render(mb, window, cx), + Node::CodeBlock(code_block) => code_block.render(node_cx, window, cx), Node::Table { .. } => Self::render_table(&self, node_cx, window, cx).into_any_element(), Node::Divider => div() .id("divider") diff --git a/crates/ui/src/text/style.rs b/crates/ui/src/text/style.rs new file mode 100644 index 00000000..3a0186e6 --- /dev/null +++ b/crates/ui/src/text/style.rs @@ -0,0 +1,67 @@ +use std::{rc::Rc, sync::Arc}; + +use gpui::{px, rems, Pixels, Rems, StyleRefinement}; + +use crate::highlighter::HighlightTheme; + +/// TextViewStyle used to customize the style for [`TextView`]. +#[derive(Clone)] +pub struct TextViewStyle { + /// Gap of each paragraphs, default is 1 rem. + pub paragraph_gap: Rems, + /// Base font size for headings, default is 14px. + pub heading_base_font_size: Pixels, + /// Function to calculate heading font size based on heading level (1-6). + /// + /// The first parameter is the heading level (1-6), the second parameter is the base font size. + /// The second parameter is the base font size. + pub heading_font_size: Option Pixels + 'static>>, + /// Highlight theme for code blocks. Default: [`HighlightTheme::default_light()`] + pub highlight_theme: Arc, + /// The style refinement for code blocks. + pub code_block: StyleRefinement, + pub is_dark: bool, +} + +impl PartialEq for TextViewStyle { + fn eq(&self, other: &Self) -> bool { + self.paragraph_gap == other.paragraph_gap + && self.heading_base_font_size == other.heading_base_font_size + && self.highlight_theme == other.highlight_theme + } +} + +impl Default for TextViewStyle { + fn default() -> Self { + Self { + paragraph_gap: rems(1.), + heading_base_font_size: px(14.), + heading_font_size: None, + highlight_theme: HighlightTheme::default_light().clone(), + code_block: StyleRefinement::default(), + is_dark: false, + } + } +} + +impl TextViewStyle { + /// Set paragraph gap, default is 1 rem. + pub fn paragraph_gap(mut self, gap: Rems) -> Self { + self.paragraph_gap = gap; + self + } + + pub fn heading_font_size(mut self, f: F) -> Self + where + F: Fn(u8, Pixels) -> Pixels + 'static, + { + self.heading_font_size = Some(Rc::new(f)); + self + } + + /// Set style for code blocks. + pub fn code_block(mut self, style: StyleRefinement) -> Self { + self.code_block = style; + self + } +} diff --git a/crates/ui/src/text/text_view.rs b/crates/ui/src/text/text_view.rs index 04952a56..361f60d9 100644 --- a/crates/ui/src/text/text_view.rs +++ b/crates/ui/src/text/text_view.rs @@ -1,18 +1,20 @@ -use std::{rc::Rc, sync::Arc, time::Instant}; +use std::{rc::Rc, time::Instant}; use gpui::{ - div, px, rems, AnyElement, App, Bounds, ClipboardItem, Element, ElementId, Entity, FocusHandle, + div, AnyElement, App, Bounds, ClipboardItem, Element, ElementId, Entity, FocusHandle, GlobalElementId, InspectorElementId, InteractiveElement, IntoElement, KeyBinding, LayoutId, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Rems, RenderOnce, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, RenderOnce, SharedString, Size, Window, }; use super::format::{html::HtmlElement, markdown::MarkdownElement}; use crate::{ global_state::GlobalState, - highlighter::HighlightTheme, input::{self}, - text::node::{self, NodeContext}, + text::{ + node::{self, NodeContext}, + TextViewStyle, + }, }; const CONTEXT: &'static str = "TextView"; @@ -270,59 +272,6 @@ impl RenderOnce for Text { } } -/// TextViewStyle used to customize the style for [`TextView`]. -#[derive(Clone)] -pub struct TextViewStyle { - /// Gap of each paragraphs, default is 1 rem. - pub paragraph_gap: Rems, - /// Base font size for headings, default is 14px. - pub heading_base_font_size: Pixels, - /// Function to calculate heading font size based on heading level (1-6). - /// - /// The first parameter is the heading level (1-6), the second parameter is the base font size. - /// The second parameter is the base font size. - pub heading_font_size: Option Pixels + 'static>>, - /// Highlight theme for code blocks. Default: [`HighlightTheme::default_light()`] - pub highlight_theme: Arc, - pub is_dark: bool, -} - -impl PartialEq for TextViewStyle { - fn eq(&self, other: &Self) -> bool { - self.paragraph_gap == other.paragraph_gap - && self.heading_base_font_size == other.heading_base_font_size - && self.highlight_theme == other.highlight_theme - } -} - -impl Default for TextViewStyle { - fn default() -> Self { - Self { - paragraph_gap: rems(1.), - heading_base_font_size: px(14.), - heading_font_size: None, - highlight_theme: HighlightTheme::default_light().clone(), - is_dark: false, - } - } -} - -impl TextViewStyle { - /// Set paragraph gap, default is 1 rem. - pub fn paragraph_gap(mut self, gap: Rems) -> Self { - self.paragraph_gap = gap; - self - } - - pub fn heading_font_size(mut self, f: F) -> Self - where - F: Fn(u8, Pixels) -> Pixels + 'static, - { - self.heading_font_size = Some(Rc::new(f)); - self - } -} - impl TextView { /// Create a new markdown text view. pub fn markdown(