From e314c023b37cbc5583f8e7ab042bd88bee63213b Mon Sep 17 00:00:00 2001 From: Abhiraj Damodare Date: Thu, 19 Mar 2026 21:45:44 +0530 Subject: [PATCH] gpui: Add `grid_cols_max_content`for content-based column widths (#50839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary Add a new grid_cols_max_content GPUI styling API that uses minmax(0, max-content) for grid column sizing. This allows columns to automatically size based on their content width while remaining responsive when the container shrinks. Applied the fix to both markdown preview (markdown_renderer.rs) and agent panel (markdown.rs) table rendering. Table borders now wrap tightly around content instead of stretching to full container width. Fixes #50044 Approach A new grid_cols_max_content API is added (as discussed with @MikaylaMaki): style.rs — New grid_cols_max_content: Option field styled.rs — New .grid_cols_max_content(cols) builder method taffy.rs — New to_grid_repeat_max_content() using minmax(0, max-content) markdown_renderer.rs — Swapped .grid_cols() → .grid_cols_max_content(), moved border to grid div, wrapped in v_flex().items_start() so border hugs content markdown.rs — Applied same fix for agent panel tables: grid_cols_max_content, border on grid div, wrapped in div().flex().flex_col().items_start() container Screenshots Before (equal-width columns, border stretches full width): Screenshot 2026-03-06 at 2 17 54 PM original issue After — Markdown Preview and Agent Panel Screenshot 2026-03-07 at 2 29
28 PM Before you mark this PR as ready for review, make sure that you have: Added a solid test coverage and/or screenshots from doing manual testing Done a self-review taking into account security and performance aspects Aligned any UI changes with the UI checklist Release Notes: Fixed markdown table columns to use content-based auto-width instead of equal-width distribution in both markdown preview and agent panel (#50044). --------- Co-authored-by: Mikayla Maki --- crates/gpui/src/style.rs | 47 ++++++++++++++---- crates/gpui/src/styled.rs | 30 +++++++++--- crates/gpui/src/taffy.rs | 48 +++++++++++-------- crates/markdown/src/markdown.rs | 10 +++- .../markdown_preview/src/markdown_renderer.rs | 11 ++--- 5 files changed, 104 insertions(+), 42 deletions(-) diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index dda49d990ac525f8b9f14b8a61a9c55c43e58e3b..97acc6f92bc2b3ba08a087486f21e193cb94e64d 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -138,6 +138,42 @@ impl ObjectFit { } } +/// The minimum size of a column or row in a grid layout +#[derive( + Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, JsonSchema, Serialize, Deserialize, +)] +pub enum TemplateColumnMinSize { + /// The column size may be 0 + #[default] + Zero, + /// The column size can be determined by the min content + MinContent, + /// The column size can be determined by the max content + MaxContent, +} + +/// A simplified representation of the grid-template-* value +#[derive( + Copy, + Clone, + Refineable, + PartialEq, + Eq, + PartialOrd, + Ord, + Debug, + Default, + JsonSchema, + Serialize, + Deserialize, +)] +pub struct GridTemplate { + /// How this template directive should be repeated + pub repeat: u16, + /// The minimum size in the repeat(<>, minmax(_, 1fr)) equation + pub min_size: TemplateColumnMinSize, +} + /// The CSS styling that can be applied to an element via the `Styled` trait #[derive(Clone, Refineable, Debug)] #[refineable(Debug, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -262,16 +298,12 @@ pub struct Style { pub opacity: Option, /// The grid columns of this element - /// Equivalent to the Tailwind `grid-cols-` - pub grid_cols: Option, - - /// The grid columns with min-content minimum sizing. - /// Unlike grid_cols, it won't shrink to width 0 in AvailableSpace::MinContent constraints. - pub grid_cols_min_content: Option, + /// Roughly equivalent to the Tailwind `grid-cols-` + pub grid_cols: Option, /// The row span of this element /// Equivalent to the Tailwind `grid-rows-` - pub grid_rows: Option, + pub grid_rows: Option, /// The grid location of this element pub grid_location: Option, @@ -790,7 +822,6 @@ impl Default for Style { opacity: None, grid_rows: None, grid_cols: None, - grid_cols_min_content: None, grid_location: None, #[cfg(debug_assertions)] diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index bc394271585f1e392353187692b1b25df198d130..687e71a94ce4d19a1795baed3381e0452c376a89 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -1,9 +1,9 @@ use crate::{ self as gpui, AbsoluteLength, AlignContent, AlignItems, AlignSelf, BorderStyle, CursorStyle, DefiniteLength, Display, Fill, FlexDirection, FlexWrap, Font, FontFeatures, FontStyle, - FontWeight, GridPlacement, Hsla, JustifyContent, Length, SharedString, StrikethroughStyle, - StyleRefinement, TextAlign, TextOverflow, TextStyleRefinement, UnderlineStyle, WhiteSpace, px, - relative, rems, + FontWeight, GridPlacement, GridTemplate, Hsla, JustifyContent, Length, SharedString, + StrikethroughStyle, StyleRefinement, TemplateColumnMinSize, TextAlign, TextOverflow, + TextStyleRefinement, UnderlineStyle, WhiteSpace, px, relative, rems, }; pub use gpui_macros::{ border_style_methods, box_shadow_style_methods, cursor_style_methods, margin_style_methods, @@ -711,20 +711,38 @@ pub trait Styled: Sized { /// Sets the grid columns of this element. fn grid_cols(mut self, cols: u16) -> Self { - self.style().grid_cols = Some(cols); + self.style().grid_cols = Some(GridTemplate { + repeat: cols, + min_size: TemplateColumnMinSize::Zero, + }); self } /// Sets the grid columns with min-content minimum sizing. /// Unlike grid_cols, it won't shrink to width 0 in AvailableSpace::MinContent constraints. fn grid_cols_min_content(mut self, cols: u16) -> Self { - self.style().grid_cols_min_content = Some(cols); + self.style().grid_cols = Some(GridTemplate { + repeat: cols, + min_size: TemplateColumnMinSize::MinContent, + }); + self + } + + /// Sets the grid columns with max-content maximum sizing for content-based column widths. + fn grid_cols_max_content(mut self, cols: u16) -> Self { + self.style().grid_cols = Some(GridTemplate { + repeat: cols, + min_size: TemplateColumnMinSize::MaxContent, + }); self } /// Sets the grid rows of this element. fn grid_rows(mut self, rows: u16) -> Self { - self.style().grid_rows = Some(rows); + self.style().grid_rows = Some(GridTemplate { + repeat: rows, + min_size: TemplateColumnMinSize::Zero, + }); self } diff --git a/crates/gpui/src/taffy.rs b/crates/gpui/src/taffy.rs index 99a50b87c8aa9f40a7694f1c2084b10f6d0a9315..094b65553d9abac1c0b32fc44333fddde12ed64c 100644 --- a/crates/gpui/src/taffy.rs +++ b/crates/gpui/src/taffy.rs @@ -1,6 +1,6 @@ use crate::{ - AbsoluteLength, App, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style, Window, - point, size, + AbsoluteLength, App, Bounds, DefiniteLength, Edges, GridTemplate, Length, Pixels, Point, Size, + Style, Window, point, size, }; use collections::{FxHashMap, FxHashSet}; use stacksafe::{StackSafe, stacksafe}; @@ -8,7 +8,7 @@ use std::{fmt::Debug, ops::Range}; use taffy::{ TaffyTree, TraversePartialTree as _, geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize}, - prelude::min_content, + prelude::{max_content, min_content}, style::AvailableSpace as TaffyAvailableSpace, tree::NodeId, }; @@ -308,19 +308,31 @@ impl ToTaffy for Style { } fn to_grid_repeat( - unit: &Option, + unit: &Option, ) -> Vec> { - // grid-template-columns: repeat(, minmax(0, 1fr)); - unit.map(|count| vec![repeat(count, vec![minmax(length(0.0), fr(1.0))])]) - .unwrap_or_default() - } - - fn to_grid_repeat_min_content( - unit: &Option, - ) -> Vec> { - // grid-template-columns: repeat(, minmax(min-content, 1fr)); - unit.map(|count| vec![repeat(count, vec![minmax(min_content(), fr(1.0))])]) - .unwrap_or_default() + unit.map(|template| { + match template.min_size { + // grid-template-*: repeat(, minmax(0, 1fr)); + crate::TemplateColumnMinSize::Zero => { + vec![repeat(template.repeat, vec![minmax(length(0.0), fr(1.0))])] + } + // grid-template-*: repeat(, minmax(min-content, 1fr)); + crate::TemplateColumnMinSize::MinContent => { + vec![repeat( + template.repeat, + vec![minmax(min_content(), fr(1.0))], + )] + } + // grid-template-*: repeat(, minmax(0, max-content)) + crate::TemplateColumnMinSize::MaxContent => { + vec![repeat( + template.repeat, + vec![minmax(length(0.0), max_content())], + )] + } + } + }) + .unwrap_or_default() } taffy::style::Style { @@ -347,11 +359,7 @@ impl ToTaffy for Style { flex_grow: self.flex_grow, flex_shrink: self.flex_shrink, grid_template_rows: to_grid_repeat(&self.grid_rows), - grid_template_columns: if self.grid_cols_min_content.is_some() { - to_grid_repeat_min_content(&self.grid_cols_min_content) - } else { - to_grid_repeat(&self.grid_cols) - }, + grid_template_columns: to_grid_repeat(&self.grid_cols), grid_row: self .grid_location .as_ref() diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index 7605c53c788363b4eec42a2151a258a137ce84a6..cc4a0187c540a149693e696663bd8756408e5d64 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -1271,18 +1271,23 @@ impl Element for MarkdownElement { builder.table.start(alignments.clone()); let column_count = alignments.len(); + builder.push_div( + div().flex().flex_col().items_start(), + range, + markdown_end, + ); builder.push_div( div() .id(("table", range.start)) + .min_w_0() .grid() .grid_cols(column_count as u16) .when(self.style.table_columns_min_size, |this| { this.grid_cols_min_content(column_count as u16) }) .when(!self.style.table_columns_min_size, |this| { - this.grid_cols(column_count as u16) + this.grid_cols_max_content(column_count as u16) }) - .w_full() .mb_2() .border(px(1.5)) .border_color(cx.theme().colors().border) @@ -1430,6 +1435,7 @@ impl Element for MarkdownElement { } } MarkdownTagEnd::Table => { + builder.pop_div(); builder.pop_div(); builder.table.end(); } diff --git a/crates/markdown_preview/src/markdown_renderer.rs b/crates/markdown_preview/src/markdown_renderer.rs index 96adc670d91f51b343c388403fa5aa7ebad678ee..59837621a6827f7cbc5840ac9b8f150dd4b59513 100644 --- a/crates/markdown_preview/src/markdown_renderer.rs +++ b/crates/markdown_preview/src/markdown_renderer.rs @@ -698,16 +698,15 @@ fn render_markdown_table(parsed: &ParsedMarkdownTable, cx: &mut RenderContext) - .when_some(parsed.caption.as_ref(), |this, caption| { this.children(render_markdown_text(caption, cx)) }) - .border_1() - .border_color(cx.border_color) - .rounded_sm() - .overflow_hidden() .child( div() + .rounded_sm() + .overflow_hidden() + .border_1() + .border_color(cx.border_color) .min_w_0() - .w_full() .grid() - .grid_cols(max_column_count as u16) + .grid_cols_max_content(max_column_count as u16) .children(cells), ) .into_any()