⎇ fce630c8d0a50843b6d447531efb106a627fd1c7
crates/modules/pluma/pluma-reader-web/src/lib.rs
Historia
Quién tocó qué
crudo
feat(gioser): sol detrás, título central, drawers MD + pluma agnóstico
1//! Pluma reader — visor de markdown elegante para WASM/web.
2//!
3//! Toma un `<div>` que actúa como contenedor y le inyecta el HTML
4//! producido por `pluma-md`. El styling (fonts, colores, animaciones)
5//! lo provee el CSS del host: este crate no inyecta estilos, sólo
6//! marcado y `data-pluma-theme="…"` para que el CSS reaccione.
7//!
8//! Patrón de uso:
9//!
10//! ```ignore
11//! let container = document.get_element_by_id("drawer-aire-content")?
12//! .dyn_into::<HtmlElement>()?;
13//! let reader = Reader::new(container);
14//! reader.show_loading();
15//! wasm_bindgen_futures::spawn_local(async move {
16//! let _ = reader.open_url("./md/aire.md", "aire").await;
17//! });
18//! ```
19
20use wasm_bindgen::prelude::*;
21use wasm_bindgen::JsCast;
22use wasm_bindgen_futures::JsFuture;
23use web_sys::{HtmlElement, Response};
24
25pub struct Reader {
26 container: HtmlElement,
27}
28
29impl Reader {
30 pub fn new(container: HtmlElement) -> Self {
31 Self { container }
32 }
33
34 pub fn container(&self) -> &HtmlElement {
35 &self.container
36 }
37
38 /// Inyecta un mensaje de carga mientras se resuelve `open_url`.
39 pub fn show_loading(&self) {
40 self.container.set_inner_html(
41 r#"<div class="pluma-loading" aria-live="polite">…</div>"#,
42 );
43 }
44
45 /// Inyecta un mensaje de error visible.
46 pub fn show_error(&self, msg: &str) {
47 let safe: String = msg.replace('<', "<").replace('>', ">");
48 self.container.set_inner_html(&format!(
49 r#"<div class="pluma-error">{}</div>"#,
50 safe
51 ));
52 }
53
54 /// Renderea un string markdown directamente, sin fetch.
55 pub fn render_md(&self, md: &str, theme: &str) {
56 let html = pluma_md::to_themed_html(md, theme);
57 self.container.set_inner_html(&html);
58 }
59
60 /// Inyecta HTML pre-renderizado (sin parsear). Útil si el caller ya
61 /// hizo el parse en otro lado.
62 pub fn render_html(&self, html: &str) {
63 self.container.set_inner_html(html);
64 }
65
66 /// Limpia el contenedor.
67 pub fn clear(&self) {
68 self.container.set_inner_html("");
69 }
70
71 /// Fetcha la URL, parsea el markdown y lo renderea con el tema dado.
72 /// El loader muestra un placeholder mientras la promesa está pendiente.
73 pub async fn open_url(&self, url: &str, theme: &str) -> Result<(), JsValue> {
74 let window = web_sys::window().ok_or_else(|| JsValue::from_str("no window"))?;
75 self.show_loading();
76 let resp_value = JsFuture::from(window.fetch_with_str(url)).await?;
77 let resp: Response = resp_value.dyn_into()?;
78 if !resp.ok() {
79 let err = format!("HTTP {} para {}", resp.status(), url);
80 self.show_error(&err);
81 return Err(JsValue::from_str(&err));
82 }
83 let text_value = JsFuture::from(resp.text()?).await?;
84 let md = text_value.as_string().unwrap_or_default();
85 self.render_md(&md, theme);
86 Ok(())
87 }
88}