agent_ui: Fix show markdown list checked state (#43567)
Closes #37527 This PR adds support for showing the list state of a list item inside the agent UI. **Before** <img width="643" height="505" alt="Screenshot 2025-11-26 at 16 21 31" src="https://github.com/user-attachments/assets/30c78022-4096-4fe4-a6cc-db208d03900f" /> **After** <img width="640" height="503" alt="Screenshot 2025-11-26 at 16 41 32" src="https://github.com/user-attachments/assets/ece14172-79a5-4d5e-a577-4b87db04280f" /> Release Notes: - Agent UI now show the checked state of a list item --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
co-authored by
Danilo Leal
parent
e6214b2b71
commit
2260b87ea8
@@ -7,6 +7,7 @@ use gpui::HitboxBehavior;
|
||||
use language::LanguageName;
|
||||
use log::Level;
|
||||
pub use path_range::{LineCol, PathWithRange};
|
||||
use ui::Checkbox;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
@@ -795,7 +796,7 @@ impl Element for MarkdownElement {
|
||||
let mut code_block_ids = HashSet::default();
|
||||
|
||||
let mut current_img_block_range: Option<Range<usize>> = None;
|
||||
for (range, event) in parsed_markdown.events.iter() {
|
||||
for (index, (range, event)) in parsed_markdown.events.iter().enumerate() {
|
||||
// Skip alt text for images that rendered
|
||||
if let Some(current_img_block_range) = ¤t_img_block_range
|
||||
&& current_img_block_range.end > range.end
|
||||
@@ -945,13 +946,29 @@ impl Element for MarkdownElement {
|
||||
MarkdownTag::HtmlBlock => builder.push_div(div(), range, markdown_end),
|
||||
MarkdownTag::List(bullet_index) => {
|
||||
builder.push_list(*bullet_index);
|
||||
builder.push_div(div().pl_4(), range, markdown_end);
|
||||
builder.push_div(div().pl_2p5(), range, markdown_end);
|
||||
}
|
||||
MarkdownTag::Item => {
|
||||
let bullet = if let Some(bullet_index) = builder.next_bullet_index() {
|
||||
format!("{}.", bullet_index)
|
||||
let bullet = if let Some((_, MarkdownEvent::TaskListMarker(checked))) =
|
||||
parsed_markdown.events.get(index.saturating_add(1))
|
||||
{
|
||||
let source = &parsed_markdown.source()[range.clone()];
|
||||
|
||||
Checkbox::new(
|
||||
ElementId::Name(source.to_string().into()),
|
||||
if *checked {
|
||||
ToggleState::Selected
|
||||
} else {
|
||||
ToggleState::Unselected
|
||||
},
|
||||
)
|
||||
.fill()
|
||||
.visualization_only(true)
|
||||
.into_any_element()
|
||||
} else if let Some(bullet_index) = builder.next_bullet_index() {
|
||||
div().child(format!("{}.", bullet_index)).into_any_element()
|
||||
} else {
|
||||
"•".to_string()
|
||||
div().child("•").into_any_element()
|
||||
};
|
||||
builder.push_div(
|
||||
div()
|
||||
@@ -1226,6 +1243,9 @@ impl Element for MarkdownElement {
|
||||
}
|
||||
MarkdownEvent::SoftBreak => builder.push_text(" ", range.clone()),
|
||||
MarkdownEvent::HardBreak => builder.push_text("\n", range.clone()),
|
||||
MarkdownEvent::TaskListMarker(_) => {
|
||||
// handled inside the `MarkdownTag::Item` case
|
||||
}
|
||||
_ => log::debug!("unsupported markdown event {:?}", event),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,15 +44,16 @@ pub enum ToggleStyle {
|
||||
pub struct Checkbox {
|
||||
id: ElementId,
|
||||
toggle_state: ToggleState,
|
||||
style: ToggleStyle,
|
||||
disabled: bool,
|
||||
placeholder: bool,
|
||||
on_click: Option<Box<dyn Fn(&ToggleState, &ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
filled: bool,
|
||||
style: ToggleStyle,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
visualization: bool,
|
||||
label: Option<SharedString>,
|
||||
label_size: LabelSize,
|
||||
label_color: Color,
|
||||
tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
on_click: Option<Box<dyn Fn(&ToggleState, &ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
}
|
||||
|
||||
impl Checkbox {
|
||||
@@ -61,15 +62,16 @@ impl Checkbox {
|
||||
Self {
|
||||
id: id.into(),
|
||||
toggle_state: checked,
|
||||
disabled: false,
|
||||
on_click: None,
|
||||
filled: false,
|
||||
style: ToggleStyle::default(),
|
||||
tooltip: None,
|
||||
disabled: false,
|
||||
placeholder: false,
|
||||
filled: false,
|
||||
visualization: false,
|
||||
label: None,
|
||||
label_size: LabelSize::Default,
|
||||
label_color: Color::Muted,
|
||||
placeholder: false,
|
||||
tooltip: None,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +112,13 @@ impl Checkbox {
|
||||
self
|
||||
}
|
||||
|
||||
/// Makes the checkbox look enabled but without pointer cursor and hover styles.
|
||||
/// Primarily used for uninteractive markdown previews.
|
||||
pub fn visualization_only(mut self, visualization: bool) -> Self {
|
||||
self.visualization = visualization;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of the checkbox using the specified [`ToggleStyle`].
|
||||
pub fn style(mut self, style: ToggleStyle) -> Self {
|
||||
self.style = style;
|
||||
@@ -209,11 +218,10 @@ impl RenderOnce for Checkbox {
|
||||
let size = Self::container_size();
|
||||
|
||||
let checkbox = h_flex()
|
||||
.id(self.id.clone())
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.size(size)
|
||||
.group(group_id.clone())
|
||||
.id(self.id.clone())
|
||||
.size(size)
|
||||
.justify_center()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -230,7 +238,7 @@ impl RenderOnce for Checkbox {
|
||||
.when(self.disabled, |this| {
|
||||
this.bg(cx.theme().colors().element_disabled.opacity(0.6))
|
||||
})
|
||||
.when(!self.disabled, |this| {
|
||||
.when(!self.disabled && !self.visualization, |this| {
|
||||
this.group_hover(group_id.clone(), |el| el.border_color(hover_border_color))
|
||||
})
|
||||
.when(self.placeholder, |this| {
|
||||
@@ -250,20 +258,14 @@ impl RenderOnce for Checkbox {
|
||||
.map(|this| {
|
||||
if self.disabled {
|
||||
this.cursor_not_allowed()
|
||||
} else if self.visualization {
|
||||
this.cursor_default()
|
||||
} else {
|
||||
this.cursor_pointer()
|
||||
}
|
||||
})
|
||||
.gap(DynamicSpacing::Base06.rems(cx))
|
||||
.child(checkbox)
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_click(move |click, window, cx| {
|
||||
on_click(&self.toggle_state.inverse(), click, window, cx)
|
||||
})
|
||||
},
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
Label::new(label)
|
||||
@@ -274,6 +276,14 @@ impl RenderOnce for Checkbox {
|
||||
.when_some(self.tooltip, |this, tooltip| {
|
||||
this.tooltip(move |window, cx| tooltip(window, cx))
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_click(move |click, window, cx| {
|
||||
on_click(&self.toggle_state.inverse(), click, window, cx)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,6 +924,15 @@ impl Component for Checkbox {
|
||||
.into_any_element(),
|
||||
)],
|
||||
),
|
||||
example_group_with_title(
|
||||
"Extra",
|
||||
vec![single_example(
|
||||
"Visualization-Only",
|
||||
Checkbox::new("viz_only", ToggleState::Selected)
|
||||
.visualization_only(true)
|
||||
.into_any_element(),
|
||||
)],
|
||||
),
|
||||
])
|
||||
.into_any_element(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user