ui/components/ai/
agent_markdown.rs1use gpui::Entity;
2use markdown::{Markdown, MarkdownElement, MarkdownFont, MarkdownFontConfig, MarkdownStyle};
3
4use crate::prelude::*;
5
6pub fn agent_markdown_font_config(cx: &App) -> MarkdownFontConfig {
13 let settings = theme::theme_settings(cx);
14 let ui_font = settings.ui_font(cx).clone();
15 let buffer_font = settings.buffer_font(cx).clone();
16 let ui_font_size = settings.ui_font_size(cx);
17 let buffer_font_size = settings.buffer_font_size(cx);
18
19 MarkdownFontConfig {
20 ui_font_family: ui_font.family.clone(),
21 ui_font_fallbacks: ui_font.fallbacks.clone(),
22 ui_font_features: ui_font.features.clone(),
23 ui_font_size,
24 buffer_font_family: buffer_font.family.clone(),
25 buffer_font_fallbacks: buffer_font.fallbacks.clone(),
26 buffer_font_features: buffer_font.features.clone(),
27 buffer_font_weight: buffer_font.weight,
28 buffer_font_size,
29 agent_buffer_font_size: buffer_font_size,
30 agent_ui_font_size: ui_font_size,
31 markdown_preview_font_size: ui_font_size,
32 markdown_preview_font_family: ui_font.family,
33 markdown_preview_code_font_family: buffer_font.family,
34 }
35}
36
37pub fn agent_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
40 let fonts = agent_markdown_font_config(cx);
41 MarkdownStyle::themed(MarkdownFont::Agent, &fonts, window, cx)
42}
43
44pub fn agent_markdown_entity(text: impl Into<SharedString>, cx: &mut App) -> Entity<Markdown> {
55 cx.new(|cx| Markdown::new(text.into(), cx))
56}
57
58#[derive(IntoElement, RegisterComponent)]
66pub struct AgentMarkdown {
67 id: ElementId,
68 markdown: Entity<Markdown>,
69}
70
71impl AgentMarkdown {
72 pub fn new(id: impl Into<ElementId>, markdown: Entity<Markdown>) -> Self {
73 Self {
74 id: id.into(),
75 markdown,
76 }
77 }
78}
79
80impl RenderOnce for AgentMarkdown {
81 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
82 let style = agent_markdown_style(window, cx);
83 div()
84 .id(self.id)
85 .w_full()
86 .child(MarkdownElement::new(self.markdown, style))
87 }
88}
89
90impl Component for AgentMarkdown {
91 fn scope() -> ComponentScope {
92 ComponentScope::Agent
93 }
94
95 fn description() -> Option<&'static str> {
96 Some(
97 "Renders a single agent chat message body as full CommonMark/GFM \
98 markdown (tables, footnotes, task lists, syntax-highlighted \
99 fenced code) via boltz-markdown, wrapping a caller-owned \
100 Entity<Markdown>.",
101 )
102 }
103
104 fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
105 let source = "# Heading One\n\n\
106 Some **bold** text with `inline code` and a [link](https://example.com).\n\n\
107 | Left | Right |\n|---|---|\n| a | b |\n\n\
108 - [x] Done item\n\
109 - [ ] Pending item\n\n\
110 1. Step one\n\
111 2. Step two\n\n\
112 ```rust\nfn main() {\n println!(\"hi\");\n}\n```\n\n\
113 A footnote reference[^1].\n\n[^1]: The footnote body.";
114 let entity = agent_markdown_entity(source, cx);
115
116 Some(
117 v_flex()
118 .w_96()
119 .gap_4()
120 .child(single_example(
121 "Full document",
122 AgentMarkdown::new("am-preview-1", entity).into_any_element(),
123 ))
124 .into_any_element(),
125 )
126 }
127}