markdown/preview.rs
1//! Who describes a link.
2//!
3//! `markdown` fetches nothing. Resolving a URL's Open Graph data is an HTTP
4//! request and an HTML parse, neither of which a component library has any
5//! business carrying, and `wasm32-unknown-unknown` would spell both differently
6//! anyway. Installed once at boot like the highlighter and read at paint: the
7//! app answers from its own cache and notifies when a fetch lands.
8
9use gpui::{App, Global, SharedString};
10
11/// What a bookmark paints beyond the URL it already has.
12///
13/// Every field is optional because a preview arrives in pieces, and a card
14/// holding none of them still shows its host.
15#[derive(Clone, Debug, Default, PartialEq, Eq)]
16pub struct Preview {
17 pub title: Option<SharedString>,
18 pub description: Option<SharedString>,
19 pub image: Option<SharedString>,
20 pub icon: Option<SharedString>,
21 /// The footer's identity where the host is not the most specific one — a
22 /// repository, a subreddit. Which path names a *unit* is the app's
23 /// knowledge, not this crate's.
24 pub label: Option<SharedString>,
25}
26
27/// `None` for a URL the caller has nothing for *yet*: the card paints its host
28/// and repaints when the answer arrives.
29///
30/// `cx` is how the answer is found: a cache lives in the app, so a bare call
31/// with only the URL would have to reach for a `static` to read its own.
32/// Shared rather than exclusive because this is asked at paint — the fetch it
33/// misses belongs to whatever notices the `None`, not to the paint that
34/// returned it.
35pub type LinkPreview = fn(url: &str, cx: &App) -> Option<Preview>;
36
37struct Installed(LinkPreview);
38
39impl Global for Installed {}
40
41/// `markdown::set_link_preview(cx, my_previews)` — call once at boot. Without
42/// it a bookmark shows its host and its URL, which is what a link looks like
43/// before anyone has resolved it.
44pub fn set_link_preview(cx: &mut App, preview: LinkPreview) {
45 cx.set_global(Installed(preview));
46}
47
48pub(crate) fn of(cx: &App, url: &str) -> Option<Preview> {
49 // Copied out before the call: a preview reads its own globals off the same
50 // `cx` this borrows.
51 let preview = cx.try_global::<Installed>()?.0;
52 preview(url, cx)
53}
54
55/// The host, without its `www.` — all a card can say about a URL nobody has
56/// resolved.
57pub(crate) fn host(url: &str) -> &str {
58 let after = url.split_once("://").map_or(url, |(_, rest)| rest);
59 let host = after.split(['/', '?', '#']).next().unwrap_or(after);
60 host.strip_prefix("www.").unwrap_or(host)
61}