use std::sync::LazyLock;
use leaf_core::DirectiveInfo;
use leaf_ratatui::{DirectiveContent, DirectiveItem, Theme};
use ratatui::{
style::{Modifier, Style},
text::{Line, Span},
};
pub const EMBED: &str = "embed";
pub static CATALOGUE: LazyLock<Vec<DirectiveItem>> = LazyLock::new(|| {
vec![DirectiveItem::ask(
"directive.embed",
EMBED,
"Embed…",
"⧉",
"Embed URL",
|reply| {
let url = reply.trim();
(!url.is_empty()).then(|| DirectiveContent {
label: None,
attrs: vec![("src".into(), Some(url.into()))],
})
},
)]
});
const MIN_CARD: usize = 16;
pub fn draw(info: &DirectiveInfo, width: u16, theme: &Theme) -> Option<Vec<Line<'static>>> {
if info.name != EMBED {
return None;
}
let src = info.attr("src").filter(|s| !s.is_empty())?;
let width = width as usize;
if width < MIN_CARD {
return None;
}
let inner = (src.chars().count() + 2).clamp(MIN_CARD - 2, width - 2);
let shown = fit(src, inner - 2);
let border = Style::default().fg(theme.image_border);
let title = " Embed ";
let top = format!(
"╭─{title}{}╮",
"─".repeat(inner.saturating_sub(title.chars().count() + 1))
);
let pad = inner - 1 - shown.chars().count();
Some(vec![
Line::from(Span::styled(top, border)),
Line::from(vec![
Span::styled("│ ", border),
Span::styled(
shown,
Style::default()
.fg(theme.link)
.add_modifier(Modifier::UNDERLINED),
),
Span::styled(format!("{}│", " ".repeat(pad)), border),
]),
Line::from(Span::styled(format!("╰{}╯", "─".repeat(inner)), border)),
])
}
fn fit(s: &str, cols: usize) -> String {
if s.chars().count() <= cols {
return s.to_string();
}
let mut out: String = s.chars().take(cols.saturating_sub(1)).collect();
out.push('…');
out
}
#[cfg(test)]
mod tests {
use super::*;
use leaf_core::{Doc, Format};
fn embed(src: &str) -> DirectiveInfo {
let doc_src = format!("::embed{{src=\"{src}\"}}\n");
let mut doc = Doc::from_source(doc_src, Format::Markdown).unwrap();
doc.view = leaf_core::View::Wysiwyg;
doc.build_visual(80);
doc.vmap.directives[0].clone()
}
fn text(lines: &[Line]) -> Vec<String> {
lines.iter().map(|l| l.to_string()).collect()
}
#[test]
fn an_embed_is_a_titled_card_holding_its_url() {
let lines = draw(&embed("https://x.org/a"), 40, &Theme::dark()).unwrap();
assert_eq!(
text(&lines),
[
"╭─ Embed ─────────╮",
"│ https://x.org/a │",
"╰─────────────────╯",
]
);
}
#[test]
fn a_long_url_is_cut_to_the_width() {
let lines = draw(&embed(&"a".repeat(80)), 20, &Theme::dark()).unwrap();
let t = text(&lines);
assert!(t.iter().all(|l| l.chars().count() == 20), "{t:?}");
assert!(t[1].ends_with("a… │"), "{t:?}");
}
#[test]
fn a_short_url_still_has_room_for_the_title() {
let lines = draw(&embed("u"), 40, &Theme::dark()).unwrap();
assert!(text(&lines)[0].contains(" Embed "));
}
#[test]
fn too_narrow_for_a_card_is_left_to_the_placeholder() {
let info = embed("https://x.org/a");
for width in 0..MIN_CARD as u16 {
assert!(draw(&info, width, &Theme::dark()).is_none(), "{width}");
}
let lines = draw(&info, MIN_CARD as u16, &Theme::dark()).unwrap();
let t = text(&lines);
assert!(t.iter().all(|l| l.chars().count() == MIN_CARD), "{t:?}");
}
#[test]
fn anything_else_is_left_to_the_placeholder() {
let mut doc =
Doc::from_source("::other{src=\"u\"}\n\n::embed\n".into(), Format::Markdown).unwrap();
doc.view = leaf_core::View::Wysiwyg;
doc.build_visual(80);
for info in &doc.vmap.directives {
assert!(draw(info, 40, &Theme::dark()).is_none(), "{}", info.name);
}
}
}