use crate::theme::ActiveTheme as _;
use crate::Button;
use fluidattacks_designs_domain::button::Variant;
use fluidattacks_designs_domain::code_snippet::{
COPY_ICON, COPY_SIZE, ICON_COLOR, LINE_HEIGHT, PADDING, RADIUS, SURFACE,
};
use fluidattacks_designs_domain::icon::Size as IconSize;
use fluidattacks_designs_domain::markdown::{heading_size, PARAGRAPH_GAP_REM};
use fluidattacks_designs_domain::theme::Mode;
use fluidattacks_designs_domain::typography::{TextSize, FONT_PRIMARY};
use gpui::{
div, px, rems, rgb, rgb_to_hsla, App, ClipboardItem, ElementId, HighlightStyle, IntoElement,
ParentElement as _, RenderOnce, SharedString, StyleRefinement, Styled as _, Window,
};
use gpui_component::highlighter::HighlightTheme;
use gpui_component::text::{TextView, TextViewStyle};
#[derive(IntoElement)]
pub struct Markdown {
id: SharedString,
source: SharedString,
}
impl Markdown {
pub fn new(id: impl Into<SharedString>, source: impl Into<SharedString>) -> Self {
Self {
id: id.into(),
source: source.into(),
}
}
}
impl RenderOnce for Markdown {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
let dark = theme.mode == Mode::Dark;
let surface = theme.shade(SURFACE);
let icon = theme.shade(ICON_COLOR);
let ink = theme.colors.text;
let body = TextSize::default();
let view = TextView::markdown(ElementId::Name(self.id), self.source)
.style(style(dark, surface))
.selectable(true)
.on_link_click(|_, _, _, _| {})
.code_block_actions(move |block, _, _| {
let code = block.code();
Button::new("copy", "")
.variant(Variant::Ghost)
.icon(COPY_ICON)
.icon_color(icon)
.icon_size(IconSize::Lg)
.width(COPY_SIZE)
.on_click(move |_, _, app| {
app.write_to_clipboard(ClipboardItem::new_string(code.to_string()));
})
});
div()
.min_w_0()
.font_family(FONT_PRIMARY)
.text_size(px(body.px()))
.line_height(px(body.line_height_px()))
.text_color(rgb(ink))
.child(view)
}
}
fn style(dark: bool, surface: u32) -> TextViewStyle {
let highlight_theme = if dark {
HighlightTheme::default_dark()
} else {
HighlightTheme::default_light()
};
let code_block = StyleRefinement::default()
.p(px(PADDING))
.rounded(px(RADIUS))
.line_height(px(LINE_HEIGHT));
TextViewStyle {
is_dark: dark,
highlight_theme,
..TextViewStyle::default()
}
.paragraph_gap(rems(PARAGRAPH_GAP_REM))
.heading_font_size(|depth, _| px(heading_size(depth).px()))
.code_block(code_block)
.inline_code(HighlightStyle {
background_color: Some(rgb_to_hsla(rgb(surface))),
..HighlightStyle::default()
})
}