diff --git a/Cargo.lock b/Cargo.lock index 0501120..0399e54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,6 +955,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1743,15 +1752,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -[[package]] -name = "markdown" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" -dependencies = [ - "unicode-id", -] - [[package]] name = "markup5ever" version = "0.35.0" @@ -2355,6 +2355,25 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +dependencies = [ + "bitflags 2.9.1", + "getopts", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "qoi" version = "0.4.1" @@ -3334,12 +3353,12 @@ dependencies = [ [[package]] name = "tetratto-shared" -version = "12.0.1" +version = "12.0.2" dependencies = [ "ammonia", "chrono", "hex_fmt", - "markdown", + "pulldown-cmark", "rand 0.9.1", "serde", "sha2", @@ -3871,12 +3890,6 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-id" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" - [[package]] name = "unicode-ident" version = "1.0.18" @@ -3898,6 +3911,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index bfac36f..b0098b7 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -36,12 +36,13 @@ pub(crate) type InnerState = (DataManager, Tera, Client, Option); pub(crate) type State = Arc>; fn render_markdown(value: &Value, _: &HashMap) -> tera::Result { - Ok( - tetratto_shared::markdown::render_markdown(&CustomEmoji::replace(value.as_str().unwrap())) - .replace("\\@", "@") - .replace("%5C@", "@") - .into(), + Ok(tetratto_shared::markdown::render_markdown( + &CustomEmoji::replace(value.as_str().unwrap()), + true, ) + .replace("\\@", "@") + .replace("%5C@", "@") + .into()) } fn render_emojis(value: &Value, _: &HashMap) -> tera::Result { diff --git a/crates/app/src/routes/api/v1/notes.rs b/crates/app/src/routes/api/v1/notes.rs index bba335e..979dbf7 100644 --- a/crates/app/src/routes/api/v1/notes.rs +++ b/crates/app/src/routes/api/v1/notes.rs @@ -267,7 +267,7 @@ pub async fn delete_by_dir_request( } pub async fn render_markdown_request(Json(req): Json) -> impl IntoResponse { - tetratto_shared::markdown::render_markdown(&CustomEmoji::replace(&req.content)) + tetratto_shared::markdown::render_markdown(&CustomEmoji::replace(&req.content), true) .replace("\\@", "@") .replace("%5C@", "@") } diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml index c862912..4945db1 100644 --- a/crates/shared/Cargo.toml +++ b/crates/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tetratto-shared" description = "Shared stuff for Tetratto" -version = "12.0.1" +version = "12.0.2" edition = "2024" authors.workspace = true repository.workspace = true @@ -10,8 +10,8 @@ license.workspace = true [dependencies] ammonia = "4.1.1" chrono = "0.4.41" -markdown = "1.0.0" hex_fmt = "0.3.0" +pulldown-cmark = "0.13.0" rand = "0.9.1" serde = { version = "1.0.219", features = ["derive"] } sha2 = "0.10.9" diff --git a/crates/shared/src/markdown.rs b/crates/shared/src/markdown.rs index 540ca97..e1c44c2 100644 --- a/crates/shared/src/markdown.rs +++ b/crates/shared/src/markdown.rs @@ -1,37 +1,24 @@ use ammonia::Builder; -use markdown::{to_html_with_options, Options, CompileOptions, ParseOptions, Constructs}; +use pulldown_cmark::{Parser, Options, html::push_html}; use std::collections::HashSet; /// Render markdown input into HTML -pub fn render_markdown(input: &str) -> String { +pub fn render_markdown(input: &str, proxy_images: bool) -> String { let input = &parse_alignment(input); - let options = Options { - compile: CompileOptions { - allow_any_img_src: false, - allow_dangerous_html: true, - allow_dangerous_protocol: true, - gfm_task_list_item_checkable: false, - gfm_tagfilter: false, - ..Default::default() - }, - parse: ParseOptions { - constructs: Constructs { - math_flow: true, - math_text: true, - ..Constructs::gfm() - }, - gfm_strikethrough_single_tilde: false, - math_text_single_dollar: false, - mdx_expression_parse: None, - mdx_esm_parse: None, - ..Default::default() - }, - }; - let html = match to_html_with_options(input, &options) { - Ok(h) => h, - Err(e) => e.to_string(), - }; + let mut options = Options::empty(); + options.insert(Options::ENABLE_STRIKETHROUGH); + options.insert(Options::ENABLE_GFM); + options.insert(Options::ENABLE_FOOTNOTES); + options.insert(Options::ENABLE_TABLES); + options.insert(Options::ENABLE_HEADING_ATTRIBUTES); + options.insert(Options::ENABLE_SUBSCRIPT); + options.insert(Options::ENABLE_SUPERSCRIPT); + + let parser = Parser::new_ext(input, options); + + let mut html = String::new(); + push_html(&mut html, parser); let mut allowed_attributes = HashSet::new(); allowed_attributes.insert("id"); @@ -43,7 +30,7 @@ pub fn render_markdown(input: &str) -> String { allowed_attributes.insert("align"); allowed_attributes.insert("src"); - Builder::default() + let output = Builder::default() .generic_attributes(allowed_attributes) .add_tags(&[ "video", "source", "img", "b", "span", "p", "i", "strong", "em", "a", "align", @@ -53,11 +40,16 @@ pub fn render_markdown(input: &str) -> String { .add_url_schemes(&["atto"]) .clean(&html) .to_string() - .replace( + .replace("