markdown: Don't retain `MarkdownStyle` in favor of using `MarkdownElement` directly (#28255)

Marshall Bowers , Antonio Scandurra , and Agus Zubiaga created

This PR removes the retained `MarkdownStyle` on the `Markdown` entity in
favor of using the `MarkdownElement` directly and passing the
`MarkdownStyle` to it.

This makes it so switching themes will be reflected live in the code
block styles.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>

Change summary

crates/agent/src/active_thread.rs             | 285 ++++++++++----------
crates/editor/src/code_context_menus.rs       |  19 
crates/editor/src/element.rs                  |  10 
crates/editor/src/hover_popover.rs            |  78 +++--
crates/git_ui/src/blame_ui.rs                 |   6 
crates/git_ui/src/commit_tooltip.rs           |  19 
crates/git_ui/src/git_panel.rs                |   4 
crates/markdown/examples/markdown.rs          | 103 +++----
crates/markdown/examples/markdown_as_child.rs | 113 ++++----
crates/markdown/src/markdown.rs               |  20 
crates/recent_projects/src/ssh_connections.rs |  35 +
crates/ui_prompt/src/ui_prompt.rs             |  50 +-
12 files changed, 369 insertions(+), 373 deletions(-)

Detailed changes

crates/agent/src/active_thread.rs πŸ”—

@@ -22,7 +22,7 @@ use gpui::{
 };
 use language::{Buffer, LanguageRegistry};
 use language_model::{ConfiguredModel, LanguageModelRegistry, LanguageModelToolUseId, Role};
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use project::ProjectItem as _;
 use settings::{Settings as _, update_settings_file};
 use std::rc::Rc;
@@ -77,7 +77,6 @@ impl RenderedMessage {
         segments: &[MessageSegment],
         language_registry: Arc<LanguageRegistry>,
         workspace: WeakEntity<Workspace>,
-        window: &Window,
         cx: &mut App,
     ) -> Self {
         let mut this = Self {
@@ -85,18 +84,12 @@ impl RenderedMessage {
             segments: Vec::with_capacity(segments.len()),
         };
         for segment in segments {
-            this.push_segment(segment, workspace.clone(), window, cx);
+            this.push_segment(segment, workspace.clone(), cx);
         }
         this
     }
 
-    fn append_thinking(
-        &mut self,
-        text: &String,
-        workspace: WeakEntity<Workspace>,
-        window: &Window,
-        cx: &mut App,
-    ) {
+    fn append_thinking(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
         if let Some(RenderedMessageSegment::Thinking {
             content,
             scroll_handle,
@@ -112,7 +105,6 @@ impl RenderedMessage {
                     text.into(),
                     self.language_registry.clone(),
                     workspace,
-                    window,
                     cx,
                 ),
                 scroll_handle: ScrollHandle::default(),
@@ -120,13 +112,7 @@ impl RenderedMessage {
         }
     }
 
-    fn append_text(
-        &mut self,
-        text: &String,
-        workspace: WeakEntity<Workspace>,
-        window: &Window,
-        cx: &mut App,
-    ) {
+    fn append_text(&mut self, text: &String, workspace: WeakEntity<Workspace>, cx: &mut App) {
         if let Some(RenderedMessageSegment::Text(markdown)) = self.segments.last_mut() {
             markdown.update(cx, |markdown, cx| markdown.append(text, cx));
         } else {
@@ -135,7 +121,6 @@ impl RenderedMessage {
                     SharedString::from(text),
                     self.language_registry.clone(),
                     workspace,
-                    window,
                     cx,
                 )));
         }
@@ -145,7 +130,6 @@ impl RenderedMessage {
         &mut self,
         segment: &MessageSegment,
         workspace: WeakEntity<Workspace>,
-        window: &Window,
         cx: &mut App,
     ) {
         let rendered_segment = match segment {
@@ -154,7 +138,6 @@ impl RenderedMessage {
                     text.into(),
                     self.language_registry.clone(),
                     workspace,
-                    window,
                     cx,
                 ),
                 scroll_handle: ScrollHandle::default(),
@@ -163,7 +146,6 @@ impl RenderedMessage {
                 text.into(),
                 self.language_registry.clone(),
                 workspace,
-                window,
                 cx,
             )),
         };
@@ -183,9 +165,16 @@ fn render_markdown(
     text: SharedString,
     language_registry: Arc<LanguageRegistry>,
     workspace: WeakEntity<Workspace>,
-    window: &Window,
     cx: &mut App,
 ) -> Entity<Markdown> {
+    cx.new(|cx| {
+        Markdown::new(text, Some(language_registry), None, cx).open_url(move |text, window, cx| {
+            open_markdown_link(text, workspace.clone(), window, cx);
+        })
+    })
+}
+
+fn default_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
     let theme_settings = ThemeSettings::get_global(cx);
     let colors = cx.theme().colors();
     let ui_font_size = TextSize::Default.rems(cx);
@@ -201,7 +190,7 @@ fn render_markdown(
         ..Default::default()
     });
 
-    let markdown_style = MarkdownStyle {
+    MarkdownStyle {
         base_text_style: text_style,
         syntax: cx.theme().syntax().clone(),
         selection_background_color: cx.theme().players().local().selection,
@@ -266,24 +255,23 @@ fn render_markdown(
             }
         })),
         ..Default::default()
-    };
-
-    cx.new(|cx| {
-        Markdown::new(text, markdown_style, Some(language_registry), None, cx).open_url(
-            move |text, window, cx| {
-                open_markdown_link(text, workspace.clone(), window, cx);
-            },
-        )
-    })
+    }
 }
 
 fn render_tool_use_markdown(
     text: SharedString,
     language_registry: Arc<LanguageRegistry>,
     workspace: WeakEntity<Workspace>,
-    window: &Window,
     cx: &mut App,
 ) -> Entity<Markdown> {
+    cx.new(|cx| {
+        Markdown::new(text, Some(language_registry), None, cx).open_url(move |text, window, cx| {
+            open_markdown_link(text, workspace.clone(), window, cx);
+        })
+    })
+}
+
+fn tool_use_markdown_style(window: &Window, cx: &mut App) -> MarkdownStyle {
     let theme_settings = ThemeSettings::get_global(cx);
     let colors = cx.theme().colors();
     let ui_font_size = TextSize::Default.rems(cx);
@@ -299,7 +287,7 @@ fn render_tool_use_markdown(
         ..Default::default()
     });
 
-    let markdown_style = MarkdownStyle {
+    MarkdownStyle {
         base_text_style: text_style,
         syntax: cx.theme().syntax().clone(),
         selection_background_color: cx.theme().players().local().selection,
@@ -334,15 +322,7 @@ fn render_tool_use_markdown(
             ..Default::default()
         },
         ..Default::default()
-    };
-
-    cx.new(|cx| {
-        Markdown::new(text, markdown_style, Some(language_registry), None, cx).open_url(
-            move |text, window, cx| {
-                open_markdown_link(text, workspace.clone(), window, cx);
-            },
-        )
-    })
+    }
 }
 
 fn open_markdown_link(
@@ -473,7 +453,6 @@ impl ActiveThread {
                     tool_use.ui_text.clone(),
                     &tool_use.input,
                     tool_use.status.text(),
-                    window,
                     cx,
                 );
             }
@@ -516,7 +495,7 @@ impl ActiveThread {
         &mut self,
         id: &MessageId,
         segments: &[MessageSegment],
-        window: &mut Window,
+        _window: &mut Window,
         cx: &mut Context<Self>,
     ) {
         let old_len = self.messages.len();
@@ -527,7 +506,6 @@ impl ActiveThread {
             segments,
             self.language_registry.clone(),
             self.workspace.clone(),
-            window,
             cx,
         );
         self.rendered_messages_by_id.insert(*id, rendered_message);
@@ -537,7 +515,7 @@ impl ActiveThread {
         &mut self,
         id: &MessageId,
         segments: &[MessageSegment],
-        window: &mut Window,
+        _window: &mut Window,
         cx: &mut Context<Self>,
     ) {
         let Some(index) = self.messages.iter().position(|message_id| message_id == id) else {
@@ -548,7 +526,6 @@ impl ActiveThread {
             segments,
             self.language_registry.clone(),
             self.workspace.clone(),
-            window,
             cx,
         );
         self.rendered_messages_by_id.insert(*id, rendered_message);
@@ -569,7 +546,6 @@ impl ActiveThread {
         tool_label: impl Into<SharedString>,
         tool_input: &serde_json::Value,
         tool_output: SharedString,
-        window: &mut Window,
         cx: &mut Context<Self>,
     ) {
         let rendered = RenderedToolUse {
@@ -577,7 +553,6 @@ impl ActiveThread {
                 tool_label.into(),
                 self.language_registry.clone(),
                 self.workspace.clone(),
-                window,
                 cx,
             ),
             input: render_tool_use_markdown(
@@ -588,14 +563,12 @@ impl ActiveThread {
                 .into(),
                 self.language_registry.clone(),
                 self.workspace.clone(),
-                window,
                 cx,
             ),
             output: render_tool_use_markdown(
                 tool_output,
                 self.language_registry.clone(),
                 self.workspace.clone(),
-                window,
                 cx,
             ),
         };
@@ -640,12 +613,12 @@ impl ActiveThread {
             }
             ThreadEvent::StreamedAssistantText(message_id, text) => {
                 if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {
-                    rendered_message.append_text(text, self.workspace.clone(), window, cx);
+                    rendered_message.append_text(text, self.workspace.clone(), cx);
                 }
             }
             ThreadEvent::StreamedAssistantThinking(message_id, text) => {
                 if let Some(rendered_message) = self.rendered_messages_by_id.get_mut(&message_id) {
-                    rendered_message.append_thinking(text, self.workspace.clone(), window, cx);
+                    rendered_message.append_thinking(text, self.workspace.clone(), cx);
                 }
             }
             ThreadEvent::MessageAdded(message_id) => {
@@ -690,7 +663,6 @@ impl ActiveThread {
                         tool_use.ui_text.clone(),
                         &tool_use.input,
                         "".into(),
-                        window,
                         cx,
                     );
                 }
@@ -711,7 +683,6 @@ impl ActiveThread {
                             .tool_result(&tool_use.id)
                             .map(|result| result.content.clone().into())
                             .unwrap_or("".into()),
-                        window,
                         cx,
                     );
                 }
@@ -1204,6 +1175,7 @@ impl ActiveThread {
                                         message_id,
                                         rendered_message,
                                         has_tool_uses,
+                                        window,
                                         cx,
                                     ))
                                     .into_any()
@@ -1370,7 +1342,7 @@ impl ActiveThread {
                         div().children(
                             tool_uses
                                 .into_iter()
-                                .map(|tool_use| self.render_tool_use(tool_use, cx)),
+                                .map(|tool_use| self.render_tool_use(tool_use, window, cx)),
                         ),
                     )
                 }),
@@ -1540,6 +1512,7 @@ impl ActiveThread {
         message_id: MessageId,
         rendered_message: &RenderedMessage,
         has_tool_uses: bool,
+        window: &Window,
         cx: &Context<Self>,
     ) -> impl IntoElement {
         let is_last_message = self.messages.last() == Some(&message_id);
@@ -1572,12 +1545,16 @@ impl ActiveThread {
                                 content.clone(),
                                 &scroll_handle,
                                 Some(index) == pending_thinking_segment_index,
+                                window,
                                 cx,
                             )
                             .into_any_element(),
-                        RenderedMessageSegment::Text(markdown) => {
-                            div().child(markdown.clone()).into_any_element()
-                        }
+                        RenderedMessageSegment::Text(markdown) => div()
+                            .child(MarkdownElement::new(
+                                markdown.clone(),
+                                default_markdown_style(window, cx),
+                            ))
+                            .into_any_element(),
                     },
                 ),
             )
@@ -1601,6 +1578,7 @@ impl ActiveThread {
         markdown: Entity<Markdown>,
         scroll_handle: &ScrollHandle,
         pending: bool,
+        window: &Window,
         cx: &Context<Self>,
     ) -> impl IntoElement {
         let is_open = self
@@ -1734,7 +1712,10 @@ impl ActiveThread {
                                     .h_20()
                                     .track_scroll(scroll_handle)
                                     .text_ui_sm(cx)
-                                    .child(markdown.clone())
+                                    .child(MarkdownElement::new(
+                                        markdown.clone(),
+                                        default_markdown_style(window, cx),
+                                    ))
                                     .overflow_hidden(),
                             )
                             .child(gradient_overlay),
@@ -1749,7 +1730,10 @@ impl ActiveThread {
                             .rounded_b_lg()
                             .bg(editor_bg)
                             .text_ui_sm(cx)
-                            .child(markdown.clone()),
+                            .child(MarkdownElement::new(
+                                markdown.clone(),
+                                default_markdown_style(window, cx),
+                            )),
                     )
                 }),
         )
@@ -1758,6 +1742,7 @@ impl ActiveThread {
     fn render_tool_use(
         &self,
         tool_use: ToolUse,
+        window: &mut Window,
         cx: &mut Context<Self>,
     ) -> impl IntoElement + use<> {
         let is_open = self
@@ -1804,103 +1789,109 @@ impl ActiveThread {
         let rendered_tool_use = self.rendered_tool_uses.get(&tool_use.id).cloned();
         let results_content_container = || v_flex().p_2().gap_0p5();
 
-        let results_content = v_flex()
-            .gap_1()
-            .child(
-                results_content_container()
-                    .child(
-                        Label::new("Input")
-                            .size(LabelSize::XSmall)
-                            .color(Color::Muted)
-                            .buffer_font(cx),
-                    )
-                    .child(
-                        div().w_full().text_ui_sm(cx).children(
-                            rendered_tool_use
-                                .as_ref()
-                                .map(|rendered| rendered.input.clone()),
-                        ),
-                    ),
-            )
-            .map(|container| match tool_use.status {
-                ToolUseStatus::Finished(_) => container.child(
+        let results_content =
+            v_flex()
+                .gap_1()
+                .child(
                     results_content_container()
-                        .border_t_1()
-                        .border_color(self.tool_card_border_color(cx))
                         .child(
-                            Label::new("Result")
+                            Label::new("Input")
                                 .size(LabelSize::XSmall)
                                 .color(Color::Muted)
                                 .buffer_font(cx),
                         )
-                        .child(
-                            div().w_full().text_ui_sm(cx).children(
-                                rendered_tool_use
-                                    .as_ref()
-                                    .map(|rendered| rendered.output.clone()),
-                            ),
-                        ),
-                ),
-                ToolUseStatus::Running => container.child(
-                    results_content_container().child(
-                        h_flex()
-                            .gap_1()
-                            .pb_1()
+                        .child(div().w_full().text_ui_sm(cx).children(
+                            rendered_tool_use.as_ref().map(|rendered| {
+                                MarkdownElement::new(
+                                    rendered.input.clone(),
+                                    tool_use_markdown_style(window, cx),
+                                )
+                            }),
+                        )),
+                )
+                .map(|container| match tool_use.status {
+                    ToolUseStatus::Finished(_) => container.child(
+                        results_content_container()
                             .border_t_1()
                             .border_color(self.tool_card_border_color(cx))
                             .child(
-                                Icon::new(IconName::ArrowCircle)
-                                    .size(IconSize::Small)
-                                    .color(Color::Accent)
-                                    .with_animation(
-                                        "arrow-circle",
-                                        Animation::new(Duration::from_secs(2)).repeat(),
-                                        |icon, delta| {
-                                            icon.transform(Transformation::rotate(percentage(
-                                                delta,
-                                            )))
-                                        },
-                                    ),
-                            )
-                            .child(
-                                Label::new("Running…")
+                                Label::new("Result")
                                     .size(LabelSize::XSmall)
                                     .color(Color::Muted)
                                     .buffer_font(cx),
-                            ),
+                            )
+                            .child(div().w_full().text_ui_sm(cx).children(
+                                rendered_tool_use.as_ref().map(|rendered| {
+                                    MarkdownElement::new(
+                                        rendered.output.clone(),
+                                        tool_use_markdown_style(window, cx),
+                                    )
+                                }),
+                            )),
                     ),
-                ),
-                ToolUseStatus::Error(_) => container.child(
-                    results_content_container()
-                        .border_t_1()
-                        .border_color(self.tool_card_border_color(cx))
-                        .child(
-                            Label::new("Error")
-                                .size(LabelSize::XSmall)
-                                .color(Color::Muted)
-                                .buffer_font(cx),
+                    ToolUseStatus::Running => container.child(
+                        results_content_container().child(
+                            h_flex()
+                                .gap_1()
+                                .pb_1()
+                                .border_t_1()
+                                .border_color(self.tool_card_border_color(cx))
+                                .child(
+                                    Icon::new(IconName::ArrowCircle)
+                                        .size(IconSize::Small)
+                                        .color(Color::Accent)
+                                        .with_animation(
+                                            "arrow-circle",
+                                            Animation::new(Duration::from_secs(2)).repeat(),
+                                            |icon, delta| {
+                                                icon.transform(Transformation::rotate(percentage(
+                                                    delta,
+                                                )))
+                                            },
+                                        ),
+                                )
+                                .child(
+                                    Label::new("Running…")
+                                        .size(LabelSize::XSmall)
+                                        .color(Color::Muted)
+                                        .buffer_font(cx),
+                                ),
+                        ),
+                    ),
+                    ToolUseStatus::Error(_) => {
+                        container.child(
+                            results_content_container()
+                                .border_t_1()
+                                .border_color(self.tool_card_border_color(cx))
+                                .child(
+                                    Label::new("Error")
+                                        .size(LabelSize::XSmall)
+                                        .color(Color::Muted)
+                                        .buffer_font(cx),
+                                )
+                                .child(div().text_ui_sm(cx).children(
+                                    rendered_tool_use.as_ref().map(|rendered| {
+                                        MarkdownElement::new(
+                                            rendered.output.clone(),
+                                            tool_use_markdown_style(window, cx),
+                                        )
+                                    }),
+                                )),
                         )
-                        .child(
-                            div().text_ui_sm(cx).children(
-                                rendered_tool_use
-                                    .as_ref()
-                                    .map(|rendered| rendered.output.clone()),
+                    }
+                    ToolUseStatus::Pending => container,
+                    ToolUseStatus::NeedsConfirmation => container.child(
+                        results_content_container()
+                            .border_t_1()
+                            .border_color(self.tool_card_border_color(cx))
+                            .child(
+                                Label::new("Asking Permission")
+                                    .size(LabelSize::Small)
+                                    .color(Color::Muted)
+                                    .buffer_font(cx),
                             ),
-                        ),
-                ),
-                ToolUseStatus::Pending => container,
-                ToolUseStatus::NeedsConfirmation => container.child(
-                    results_content_container()
-                        .border_t_1()
-                        .border_color(self.tool_card_border_color(cx))
-                        .child(
-                            Label::new("Asking Permission")
-                                .size(LabelSize::Small)
-                                .color(Color::Muted)
-                                .buffer_font(cx),
-                        ),
-                ),
-            });
+                    ),
+                });
 
         let gradient_overlay = |color: Hsla| {
             div()
@@ -1948,7 +1939,7 @@ impl ActiveThread {
                                         )
                                         .child(
                                             h_flex().pr_8().text_ui_sm(cx).children(
-                                                rendered_tool_use.map(|rendered| rendered.label)
+                                                rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)))
                                             ),
                                         ),
                                 )
@@ -2036,7 +2027,7 @@ impl ActiveThread {
                                     )
                                     .child(
                                         h_flex().pr_8().text_ui_sm(cx).children(
-                                            rendered_tool_use.map(|rendered| rendered.label)
+                                            rendered_tool_use.map(|rendered| MarkdownElement::new(rendered.label, tool_use_markdown_style(window, cx)))
                                         ),
                                     ),
                             )

crates/editor/src/code_context_menus.rs πŸ”—

@@ -7,7 +7,7 @@ use gpui::{
 };
 use language::Buffer;
 use language::CodeLabel;
-use markdown::Markdown;
+use markdown::{Markdown, MarkdownElement};
 use multi_buffer::{Anchor, ExcerptId};
 use ordered_float::OrderedFloat;
 use project::CompletionSource;
@@ -622,21 +622,18 @@ impl CompletionsMenu {
                         let language = editor
                             .language_at(self.initial_position, cx)
                             .map(|l| l.name().to_proto());
-                        Markdown::new(
-                            SharedString::default(),
-                            hover_markdown_style(window, cx),
-                            languages,
-                            language,
-                            cx,
-                        )
-                        .copy_code_block_buttons(false)
-                        .open_url(open_markdown_url)
+                        Markdown::new(SharedString::default(), languages, language, cx)
+                            .copy_code_block_buttons(false)
+                            .open_url(open_markdown_url)
                     })
                 });
                 markdown.update(cx, |markdown, cx| {
                     markdown.reset(parsed.clone(), cx);
                 });
-                div().child(markdown.clone())
+                div().child(MarkdownElement::new(
+                    markdown.clone(),
+                    hover_markdown_style(window, cx),
+                ))
             }
             CompletionDocumentation::MultiLineMarkdown(_) => return None,
             CompletionDocumentation::SingleLine(_) => return None,

crates/editor/src/element.rs πŸ”—

@@ -3912,9 +3912,13 @@ impl EditorElement {
         );
 
         let hover_popovers = self.editor.update(cx, |editor, cx| {
-            editor
-                .hover_state
-                .render(snapshot, visible_display_row_range.clone(), max_size, cx)
+            editor.hover_state.render(
+                snapshot,
+                visible_display_row_range.clone(),
+                max_size,
+                window,
+                cx,
+            )
         });
         let Some((position, hover_popovers)) = hover_popovers else {
             return;

crates/editor/src/hover_popover.rs πŸ”—

@@ -14,7 +14,7 @@ use gpui::{
 use itertools::Itertools;
 use language::{DiagnosticEntry, Language, LanguageRegistry};
 use lsp::DiagnosticSeverity;
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use multi_buffer::{MultiOrSingleBufferOffsetRange, ToOffset};
 use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
 use settings::Settings;
@@ -310,7 +310,7 @@ fn show_hover(
                 let mut background_color: Option<Hsla> = None;
 
                 let parsed_content = cx
-                    .new_window_entity(|window, cx| {
+                    .new_window_entity(|_window, cx| {
                         let status_colors = cx.theme().status();
 
                         match local_diagnostic.diagnostic.severity {
@@ -335,32 +335,8 @@ fn show_hover(
                                 border_color = Some(status_colors.ignored_border);
                             }
                         };
-                        let settings = ThemeSettings::get_global(cx);
-                        let mut base_text_style = window.text_style();
-                        base_text_style.refine(&TextStyleRefinement {
-                            font_family: Some(settings.ui_font.family.clone()),
-                            font_fallbacks: settings.ui_font.fallbacks.clone(),
-                            font_size: Some(settings.ui_font_size(cx).into()),
-                            color: Some(cx.theme().colors().editor_foreground),
-                            background_color: Some(gpui::transparent_black()),
 
-                            ..Default::default()
-                        });
-                        let markdown_style = MarkdownStyle {
-                            base_text_style,
-                            selection_background_color: { cx.theme().players().local().selection },
-                            link: TextStyleRefinement {
-                                underline: Some(gpui::UnderlineStyle {
-                                    thickness: px(1.),
-                                    color: Some(cx.theme().colors().editor_foreground),
-                                    wavy: false,
-                                }),
-                                ..Default::default()
-                            },
-                            ..Default::default()
-                        };
-                        Markdown::new_text(SharedString::new(text), markdown_style.clone(), cx)
-                            .open_url(open_markdown_url)
+                        Markdown::new_text(SharedString::new(text), cx).open_url(open_markdown_url)
                     })
                     .ok();
 
@@ -563,10 +539,9 @@ async fn parse_blocks(
         .join("\n\n");
 
     let rendered_block = cx
-        .new_window_entity(|window, cx| {
+        .new_window_entity(|_window, cx| {
             Markdown::new(
                 combined_text.into(),
-                hover_markdown_style(window, cx),
                 Some(language_registry.clone()),
                 fallback_language_name,
                 cx,
@@ -704,6 +679,7 @@ impl HoverState {
         snapshot: &EditorSnapshot,
         visible_rows: Range<DisplayRow>,
         max_size: Size<Pixels>,
+        window: &mut Window,
         cx: &mut Context<Editor>,
     ) -> Option<(DisplayPoint, Vec<AnyElement>)> {
         // If there is a diagnostic, position the popovers based on that.
@@ -738,10 +714,10 @@ impl HoverState {
         let mut elements = Vec::new();
 
         if let Some(diagnostic_popover) = self.diagnostic_popover.as_ref() {
-            elements.push(diagnostic_popover.render(max_size, cx));
+            elements.push(diagnostic_popover.render(max_size, window, cx));
         }
         for info_popover in &mut self.info_popovers {
-            elements.push(info_popover.render(max_size, cx));
+            elements.push(info_popover.render(max_size, window, cx));
         }
 
         Some((point, elements))
@@ -781,6 +757,7 @@ impl InfoPopover {
     pub(crate) fn render(
         &mut self,
         max_size: Size<Pixels>,
+        window: &mut Window,
         cx: &mut Context<Editor>,
     ) -> AnyElement {
         let keyboard_grace = Rc::clone(&self.keyboard_grace);
@@ -806,7 +783,10 @@ impl InfoPopover {
                         .max_h(max_size.height)
                         .p_2()
                         .track_scroll(&self.scroll_handle)
-                        .child(markdown.clone()),
+                        .child(MarkdownElement::new(
+                            markdown.clone(),
+                            hover_markdown_style(window, cx),
+                        )),
                 )
                 .child(self.render_vertical_scrollbar(cx));
         }
@@ -868,11 +848,41 @@ pub struct DiagnosticPopover {
 }
 
 impl DiagnosticPopover {
-    pub fn render(&self, max_size: Size<Pixels>, cx: &mut Context<Editor>) -> AnyElement {
+    pub fn render(
+        &self,
+        max_size: Size<Pixels>,
+        window: &mut Window,
+        cx: &mut Context<Editor>,
+    ) -> AnyElement {
         let keyboard_grace = Rc::clone(&self.keyboard_grace);
         let mut markdown_div = div().py_1().px_2();
         if let Some(markdown) = &self.parsed_content {
-            markdown_div = markdown_div.child(markdown.clone());
+            let settings = ThemeSettings::get_global(cx);
+            let mut base_text_style = window.text_style();
+            base_text_style.refine(&TextStyleRefinement {
+                font_family: Some(settings.ui_font.family.clone()),
+                font_fallbacks: settings.ui_font.fallbacks.clone(),
+                font_size: Some(settings.ui_font_size(cx).into()),
+                color: Some(cx.theme().colors().editor_foreground),
+                background_color: Some(gpui::transparent_black()),
+                ..Default::default()
+            });
+            let markdown_style = MarkdownStyle {
+                base_text_style,
+                selection_background_color: { cx.theme().players().local().selection },
+                link: TextStyleRefinement {
+                    underline: Some(gpui::UnderlineStyle {
+                        thickness: px(1.),
+                        color: Some(cx.theme().colors().editor_foreground),
+                        wavy: false,
+                    }),
+                    ..Default::default()
+                },
+                ..Default::default()
+            };
+
+            markdown_div =
+                markdown_div.child(MarkdownElement::new(markdown.clone(), markdown_style));
         }
 
         if let Some(background_color) = &self.background_color {

crates/git_ui/src/blame_ui.rs πŸ”—

@@ -95,14 +95,13 @@ impl BlameRenderer for GitBlameRenderer {
                         )
                     }
                 })
-                .hoverable_tooltip(move |window, cx| {
+                .hoverable_tooltip(move |_window, cx| {
                     cx.new(|cx| {
                         CommitTooltip::blame_entry(
                             &blame_entry,
                             details.clone(),
                             repository.clone(),
                             workspace.clone(),
-                            window,
                             cx,
                         )
                     })
@@ -145,14 +144,13 @@ impl BlameRenderer for GitBlameRenderer {
                 .child(Icon::new(IconName::FileGit).color(Color::Hint))
                 .child(text)
                 .gap_2()
-                .hoverable_tooltip(move |window, cx| {
+                .hoverable_tooltip(move |_window, cx| {
                     let tooltip = cx.new(|cx| {
                         CommitTooltip::blame_entry(
                             &blame_entry,
                             details.clone(),
                             repository.clone(),
                             workspace.clone(),
-                            window,
                             cx,
                         )
                     });

crates/git_ui/src/commit_tooltip.rs πŸ”—

@@ -8,7 +8,7 @@ use gpui::{
     App, Asset, ClipboardItem, Element, Entity, MouseButton, ParentElement, Render, ScrollHandle,
     StatefulInteractiveElement, WeakEntity, prelude::*,
 };
-use markdown::Markdown;
+use markdown::{Markdown, MarkdownElement};
 use project::git_store::Repository;
 use settings::Settings;
 use std::hash::Hash;
@@ -118,7 +118,6 @@ impl CommitTooltip {
         details: Option<ParsedCommitMessage>,
         repository: Entity<Repository>,
         workspace: WeakEntity<Workspace>,
-        window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
         let commit_time = blame
@@ -140,7 +139,6 @@ impl CommitTooltip {
             },
             repository,
             workspace,
-            window,
             cx,
         )
     }
@@ -149,13 +147,8 @@ impl CommitTooltip {
         commit: CommitDetails,
         repository: Entity<Repository>,
         workspace: WeakEntity<Workspace>,
-        window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
-        let mut style = hover_markdown_style(window, cx);
-        if let Some(code_block) = &style.code_block.text {
-            style.base_text_style.refine(code_block);
-        }
         let markdown = cx.new(|cx| {
             Markdown::new(
                 commit
@@ -163,7 +156,6 @@ impl CommitTooltip {
                     .as_ref()
                     .map(|message| message.message.clone())
                     .unwrap_or_default(),
-                style,
                 None,
                 None,
                 cx,
@@ -199,12 +191,19 @@ impl Render for CommitTooltip {
             OffsetDateTime::now_utc(),
             time_format::TimestampFormat::MediumAbsolute,
         );
+        let markdown_style = {
+            let mut style = hover_markdown_style(window, cx);
+            if let Some(code_block) = &style.code_block.text {
+                style.base_text_style.refine(code_block);
+            }
+            style
+        };
 
         let message = self
             .commit
             .message
             .as_ref()
-            .map(|_| self.markdown.clone().into_any_element())
+            .map(|_| MarkdownElement::new(self.markdown.clone(), markdown_style).into_any())
             .unwrap_or("<no commit message>".into_any());
 
         let pull_request = self

crates/git_ui/src/git_panel.rs πŸ”—

@@ -3927,9 +3927,9 @@ impl GitPanelMessageTooltip {
                     }),
                 };
 
-                this.update_in(cx, |this: &mut GitPanelMessageTooltip, window, cx| {
+                this.update(cx, |this: &mut GitPanelMessageTooltip, cx| {
                     this.commit_tooltip = Some(cx.new(move |cx| {
-                        CommitTooltip::new(commit_details, repository, workspace, window, cx)
+                        CommitTooltip::new(commit_details, repository, workspace, cx)
                     }));
                     cx.notify();
                 })

crates/markdown/examples/markdown.rs πŸ”—

@@ -1,7 +1,7 @@
 use assets::Assets;
 use gpui::{Application, Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb};
 use language::{LanguageRegistry, language_settings::AllLanguageSettings};
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use node_runtime::NodeRuntime;
 use settings::SettingsStore;
 use std::sync::Arc;
@@ -47,54 +47,7 @@ pub fn main() {
 
         cx.activate(true);
         cx.open_window(WindowOptions::default(), |_, cx| {
-            cx.new(|cx| {
-                let markdown_style = MarkdownStyle {
-                    base_text_style: gpui::TextStyle {
-                        font_family: "Zed Plex Sans".into(),
-                        color: cx.theme().colors().terminal_ansi_black,
-                        ..Default::default()
-                    },
-                    code_block: StyleRefinement::default()
-                        .font_family("Zed Plex Mono")
-                        .m(rems(1.))
-                        .bg(rgb(0xAAAAAAA)),
-                    inline_code: gpui::TextStyleRefinement {
-                        font_family: Some("Zed Mono".into()),
-                        color: Some(cx.theme().colors().editor_foreground),
-                        background_color: Some(cx.theme().colors().editor_background),
-                        ..Default::default()
-                    },
-                    rule_color: Color::Muted.color(cx),
-                    block_quote_border_color: Color::Muted.color(cx),
-                    block_quote: gpui::TextStyleRefinement {
-                        color: Some(Color::Muted.color(cx)),
-                        ..Default::default()
-                    },
-                    link: gpui::TextStyleRefinement {
-                        color: Some(Color::Accent.color(cx)),
-                        underline: Some(gpui::UnderlineStyle {
-                            thickness: px(1.),
-                            color: Some(Color::Accent.color(cx)),
-                            wavy: false,
-                        }),
-                        ..Default::default()
-                    },
-                    syntax: cx.theme().syntax().clone(),
-                    selection_background_color: {
-                        let mut selection = cx.theme().players().local().selection;
-                        selection.fade_out(0.7);
-                        selection
-                    },
-                    ..Default::default()
-                };
-
-                MarkdownExample::new(
-                    MARKDOWN_EXAMPLE.into(),
-                    markdown_style,
-                    language_registry,
-                    cx,
-                )
-            })
+            cx.new(|cx| MarkdownExample::new(MARKDOWN_EXAMPLE.into(), language_registry, cx))
         })
         .unwrap();
     });
@@ -105,16 +58,10 @@ struct MarkdownExample {
 }
 
 impl MarkdownExample {
-    pub fn new(
-        text: SharedString,
-        style: MarkdownStyle,
-        language_registry: Arc<LanguageRegistry>,
-        cx: &mut App,
-    ) -> Self {
+    pub fn new(text: SharedString, language_registry: Arc<LanguageRegistry>, cx: &mut App) -> Self {
         let markdown = cx.new(|cx| {
             Markdown::new(
                 text,
-                style,
                 Some(language_registry),
                 Some("TypeScript".to_string()),
                 cx,
@@ -125,7 +72,47 @@ impl MarkdownExample {
 }
 
 impl Render for MarkdownExample {
-    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let markdown_style = MarkdownStyle {
+            base_text_style: gpui::TextStyle {
+                font_family: "Zed Plex Sans".into(),
+                color: cx.theme().colors().terminal_ansi_black,
+                ..Default::default()
+            },
+            code_block: StyleRefinement::default()
+                .font_family("Zed Plex Mono")
+                .m(rems(1.))
+                .bg(rgb(0xAAAAAAA)),
+            inline_code: gpui::TextStyleRefinement {
+                font_family: Some("Zed Mono".into()),
+                color: Some(cx.theme().colors().editor_foreground),
+                background_color: Some(cx.theme().colors().editor_background),
+                ..Default::default()
+            },
+            rule_color: Color::Muted.color(cx),
+            block_quote_border_color: Color::Muted.color(cx),
+            block_quote: gpui::TextStyleRefinement {
+                color: Some(Color::Muted.color(cx)),
+                ..Default::default()
+            },
+            link: gpui::TextStyleRefinement {
+                color: Some(Color::Accent.color(cx)),
+                underline: Some(gpui::UnderlineStyle {
+                    thickness: px(1.),
+                    color: Some(Color::Accent.color(cx)),
+                    wavy: false,
+                }),
+                ..Default::default()
+            },
+            syntax: cx.theme().syntax().clone(),
+            selection_background_color: {
+                let mut selection = cx.theme().players().local().selection;
+                selection.fade_out(0.7);
+                selection
+            },
+            ..Default::default()
+        };
+
         div()
             .id("markdown-example")
             .debug_selector(|| "foo".into())
@@ -134,6 +121,6 @@ impl Render for MarkdownExample {
             .size_full()
             .p_4()
             .overflow_y_scroll()
-            .child(self.markdown.clone())
+            .child(MarkdownElement::new(self.markdown.clone(), markdown_style))
     }
 }

crates/markdown/examples/markdown_as_child.rs πŸ”—

@@ -1,7 +1,7 @@
 use assets::Assets;
 use gpui::{Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb};
 use language::{LanguageRegistry, language_settings::AllLanguageSettings};
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use node_runtime::NodeRuntime;
 use settings::SettingsStore;
 use std::sync::Arc;
@@ -37,58 +37,7 @@ pub fn main() {
         cx.activate(true);
         let _ = cx.open_window(WindowOptions::default(), |_, cx| {
             cx.new(|cx| {
-                let markdown_style = MarkdownStyle {
-                    base_text_style: gpui::TextStyle {
-                        font_family: "Zed Mono".into(),
-                        color: cx.theme().colors().text,
-                        ..Default::default()
-                    },
-                    code_block: StyleRefinement {
-                        text: Some(gpui::TextStyleRefinement {
-                            font_family: Some("Zed Mono".into()),
-                            background_color: Some(cx.theme().colors().editor_background),
-                            ..Default::default()
-                        }),
-                        margin: gpui::EdgesRefinement {
-                            top: Some(Length::Definite(rems(4.).into())),
-                            left: Some(Length::Definite(rems(4.).into())),
-                            right: Some(Length::Definite(rems(4.).into())),
-                            bottom: Some(Length::Definite(rems(4.).into())),
-                        },
-                        ..Default::default()
-                    },
-                    inline_code: gpui::TextStyleRefinement {
-                        font_family: Some("Zed Mono".into()),
-                        background_color: Some(cx.theme().colors().editor_background),
-                        ..Default::default()
-                    },
-                    rule_color: Color::Muted.color(cx),
-                    block_quote_border_color: Color::Muted.color(cx),
-                    block_quote: gpui::TextStyleRefinement {
-                        color: Some(Color::Muted.color(cx)),
-                        ..Default::default()
-                    },
-                    link: gpui::TextStyleRefinement {
-                        color: Some(Color::Accent.color(cx)),
-                        underline: Some(gpui::UnderlineStyle {
-                            thickness: px(1.),
-                            color: Some(Color::Accent.color(cx)),
-                            wavy: false,
-                        }),
-                        ..Default::default()
-                    },
-                    syntax: cx.theme().syntax().clone(),
-                    selection_background_color: {
-                        let mut selection = cx.theme().players().local().selection;
-                        selection.fade_out(0.7);
-                        selection
-                    },
-                    heading: Default::default(),
-                    ..Default::default()
-                };
-                let markdown = cx.new(|cx| {
-                    Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, None, cx)
-                });
+                let markdown = cx.new(|cx| Markdown::new(MARKDOWN_EXAMPLE.into(), None, None, cx));
 
                 HelloWorld { markdown }
             })
@@ -100,7 +49,57 @@ struct HelloWorld {
 }
 
 impl Render for HelloWorld {
-    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let markdown_style = MarkdownStyle {
+            base_text_style: gpui::TextStyle {
+                font_family: "Zed Mono".into(),
+                color: cx.theme().colors().text,
+                ..Default::default()
+            },
+            code_block: StyleRefinement {
+                text: Some(gpui::TextStyleRefinement {
+                    font_family: Some("Zed Mono".into()),
+                    background_color: Some(cx.theme().colors().editor_background),
+                    ..Default::default()
+                }),
+                margin: gpui::EdgesRefinement {
+                    top: Some(Length::Definite(rems(4.).into())),
+                    left: Some(Length::Definite(rems(4.).into())),
+                    right: Some(Length::Definite(rems(4.).into())),
+                    bottom: Some(Length::Definite(rems(4.).into())),
+                },
+                ..Default::default()
+            },
+            inline_code: gpui::TextStyleRefinement {
+                font_family: Some("Zed Mono".into()),
+                background_color: Some(cx.theme().colors().editor_background),
+                ..Default::default()
+            },
+            rule_color: Color::Muted.color(cx),
+            block_quote_border_color: Color::Muted.color(cx),
+            block_quote: gpui::TextStyleRefinement {
+                color: Some(Color::Muted.color(cx)),
+                ..Default::default()
+            },
+            link: gpui::TextStyleRefinement {
+                color: Some(Color::Accent.color(cx)),
+                underline: Some(gpui::UnderlineStyle {
+                    thickness: px(1.),
+                    color: Some(Color::Accent.color(cx)),
+                    wavy: false,
+                }),
+                ..Default::default()
+            },
+            syntax: cx.theme().syntax().clone(),
+            selection_background_color: {
+                let mut selection = cx.theme().players().local().selection;
+                selection.fade_out(0.7);
+                selection
+            },
+            heading: Default::default(),
+            ..Default::default()
+        };
+
         div()
             .flex()
             .bg(rgb(0x2e7d32))
@@ -112,6 +111,10 @@ impl Render for HelloWorld {
             .border_color(rgb(0x0000ff))
             .text_xl()
             .text_color(rgb(0xffffff))
-            .child(div().child(self.markdown.clone()).p_20())
+            .child(
+                div()
+                    .child(MarkdownElement::new(self.markdown.clone(), markdown_style))
+                    .p_20(),
+            )
     }
 }

crates/markdown/src/markdown.rs πŸ”—

@@ -14,7 +14,7 @@ use std::time::Duration;
 use gpui::{
     AnyElement, App, BorderStyle, Bounds, ClipboardItem, CursorStyle, DispatchPhase, Edges, Entity,
     FocusHandle, Focusable, FontStyle, FontWeight, GlobalElementId, Hitbox, Hsla, KeyContext,
-    Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent, Point, Render, Stateful,
+    Length, MouseDownEvent, MouseEvent, MouseMoveEvent, MouseUpEvent, Point, Stateful,
     StrikethroughStyle, StyleRefinement, StyledText, Task, TextLayout, TextRun, TextStyle,
     TextStyleRefinement, actions, point, quad,
 };
@@ -74,7 +74,6 @@ pub struct Markdown {
     selection: Selection,
     pressed_link: Option<RenderedLink>,
     autoscroll_request: Option<usize>,
-    style: MarkdownStyle,
     parsed_markdown: ParsedMarkdown,
     should_reparse: bool,
     pending_parse: Option<Task<Option<()>>>,
@@ -97,7 +96,6 @@ actions!(markdown, [Copy]);
 impl Markdown {
     pub fn new(
         source: SharedString,
-        style: MarkdownStyle,
         language_registry: Option<Arc<LanguageRegistry>>,
         fallback_code_block_language: Option<String>,
         cx: &mut Context<Self>,
@@ -108,7 +106,6 @@ impl Markdown {
             selection: Selection::default(),
             pressed_link: None,
             autoscroll_request: None,
-            style,
             should_reparse: false,
             parsed_markdown: ParsedMarkdown::default(),
             pending_parse: None,
@@ -136,14 +133,13 @@ impl Markdown {
         }
     }
 
-    pub fn new_text(source: SharedString, style: MarkdownStyle, cx: &mut Context<Self>) -> Self {
+    pub fn new_text(source: SharedString, cx: &mut Context<Self>) -> Self {
         let focus_handle = cx.focus_handle();
         let mut this = Self {
             source,
             selection: Selection::default(),
             pressed_link: None,
             autoscroll_request: None,
-            style,
             should_reparse: false,
             parsed_markdown: ParsedMarkdown::default(),
             pending_parse: None,
@@ -275,12 +271,6 @@ impl Markdown {
     }
 }
 
-impl Render for Markdown {
-    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        MarkdownElement::new(cx.entity().clone(), self.style.clone())
-    }
-}
-
 impl Focusable for Markdown {
     fn focus_handle(&self, _cx: &App) -> FocusHandle {
         self.focus_handle.clone()
@@ -341,7 +331,7 @@ pub struct MarkdownElement {
 }
 
 impl MarkdownElement {
-    fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self {
+    pub fn new(markdown: Entity<Markdown>, style: MarkdownStyle) -> Self {
         Self { markdown, style }
     }
 
@@ -638,6 +628,10 @@ impl Element for MarkdownElement {
                                     // If the path actually exists in the project, render a link to it.
                                     if let Some(project_path) =
                                         window.root::<Workspace>().flatten().and_then(|workspace| {
+                                            if path_range.path.is_absolute() {
+                                                return None;
+                                            }
+
                                             workspace
                                                 .read(cx)
                                                 .project()

crates/recent_projects/src/ssh_connections.rs πŸ”—

@@ -13,7 +13,7 @@ use gpui::{
 };
 
 use language::CursorShape;
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use release_channel::ReleaseChannel;
 use remote::ssh_session::{ConnectionIdentifier, SshPortForwardOption};
 use remote::{SshConnectionOptions, SshPlatform, SshRemoteClient};
@@ -182,7 +182,6 @@ impl SshPrompt {
     ) {
         let theme = ThemeSettings::get_global(cx);
 
-        let mut text_style = window.text_style();
         let refinement = TextStyleRefinement {
             font_family: Some(theme.buffer_font.family.clone()),
             font_features: Some(FontFeatures::disable_ligatures()),
@@ -192,7 +191,6 @@ impl SshPrompt {
             ..Default::default()
         };
 
-        text_style.refine(&refinement);
         self.editor.update(cx, |editor, cx| {
             if prompt.contains("yes/no") {
                 editor.set_masked(false, cx);
@@ -202,12 +200,8 @@ impl SshPrompt {
             editor.set_text_style_refinement(refinement);
             editor.set_cursor_shape(CursorShape::Block, cx);
         });
-        let markdown_style = MarkdownStyle {
-            base_text_style: text_style,
-            selection_background_color: cx.theme().players().local().selection,
-            ..Default::default()
-        };
-        let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), markdown_style, cx));
+
+        let markdown = cx.new(|cx| Markdown::new_text(prompt.into(), cx));
         self.prompt = Some((markdown, tx));
         self.status_message.take();
         window.focus(&self.editor.focus_handle(cx));
@@ -231,7 +225,26 @@ impl SshPrompt {
 }
 
 impl Render for SshPrompt {
-    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let theme = ThemeSettings::get_global(cx);
+
+        let mut text_style = window.text_style();
+        let refinement = TextStyleRefinement {
+            font_family: Some(theme.buffer_font.family.clone()),
+            font_features: Some(FontFeatures::disable_ligatures()),
+            font_size: Some(theme.buffer_font_size(cx).into()),
+            color: Some(cx.theme().colors().editor_foreground),
+            background_color: Some(gpui::transparent_black()),
+            ..Default::default()
+        };
+
+        text_style.refine(&refinement);
+        let markdown_style = MarkdownStyle {
+            base_text_style: text_style,
+            selection_background_color: cx.theme().players().local().selection,
+            ..Default::default()
+        };
+
         v_flex()
             .key_context("PasswordPrompt")
             .py_2()
@@ -266,7 +279,7 @@ impl Render for SshPrompt {
                     div()
                         .size_full()
                         .overflow_hidden()
-                        .child(prompt.0.clone())
+                        .child(MarkdownElement::new(prompt.0.clone(), markdown_style))
                         .child(self.editor.clone()),
                 )
             })

crates/ui_prompt/src/ui_prompt.rs πŸ”—

@@ -4,7 +4,7 @@ use gpui::{
     Refineable, Render, RenderablePromptHandle, SharedString, Styled, TextStyleRefinement, Window,
     div,
 };
-use markdown::{Markdown, MarkdownStyle};
+use markdown::{Markdown, MarkdownElement, MarkdownStyle};
 use settings::{Settings, SettingsStore};
 use theme::ThemeSettings;
 use ui::{
@@ -47,24 +47,9 @@ fn zed_prompt_renderer(
             actions: actions.iter().map(ToString::to_string).collect(),
             focus: cx.focus_handle(),
             active_action_id: 0,
-            detail: detail.filter(|text| !text.is_empty()).map(|text| {
-                cx.new(|cx| {
-                    let settings = ThemeSettings::get_global(cx);
-                    let mut base_text_style = window.text_style();
-                    base_text_style.refine(&TextStyleRefinement {
-                        font_family: Some(settings.ui_font.family.clone()),
-                        font_size: Some(settings.ui_font_size(cx).into()),
-                        color: Some(ui::Color::Muted.color(cx)),
-                        ..Default::default()
-                    });
-                    let markdown_style = MarkdownStyle {
-                        base_text_style,
-                        selection_background_color: { cx.theme().players().local().selection },
-                        ..Default::default()
-                    };
-                    Markdown::new(SharedString::new(text), markdown_style, None, None, cx)
-                })
-            }),
+            detail: detail
+                .filter(|text| !text.is_empty())
+                .map(|text| cx.new(|cx| Markdown::new(SharedString::new(text), None, None, cx))),
         }
     });
 
@@ -127,7 +112,7 @@ impl ZedPromptRenderer {
 }
 
 impl Render for ZedPromptRenderer {
-    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let font_family = settings.ui_font.family.clone();
         let prompt = v_flex()
@@ -153,11 +138,26 @@ impl Render for ZedPromptRenderer {
                     .child(self.message.clone())
                     .text_color(ui::Color::Default.color(cx)),
             )
-            .children(
-                self.detail
-                    .clone()
-                    .map(|detail| div().w_full().text_xs().child(detail)),
-            )
+            .children(self.detail.clone().map(|detail| {
+                div()
+                    .w_full()
+                    .text_xs()
+                    .child(MarkdownElement::new(detail, {
+                        let settings = ThemeSettings::get_global(cx);
+                        let mut base_text_style = window.text_style();
+                        base_text_style.refine(&TextStyleRefinement {
+                            font_family: Some(settings.ui_font.family.clone()),
+                            font_size: Some(settings.ui_font_size(cx).into()),
+                            color: Some(ui::Color::Muted.color(cx)),
+                            ..Default::default()
+                        });
+                        MarkdownStyle {
+                            base_text_style,
+                            selection_background_color: { cx.theme().players().local().selection },
+                            ..Default::default()
+                        }
+                    }))
+            }))
             .child(h_flex().justify_end().gap_2().children(
                 self.actions.iter().enumerate().rev().map(|(ix, action)| {
                     ui::Button::new(ix, action.clone())