diff --git a/crates/service/service-webpage/src/components/md.rs b/crates/service/service-webpage/src/components/md.rs index 89be1c0..f57ef5f 100644 --- a/crates/service/service-webpage/src/components/md.rs +++ b/crates/service/service-webpage/src/components/md.rs @@ -1,4 +1,5 @@ use lazy_static::lazy_static; +use markdown_it::generics::inline::full_link; use markdown_it::parser::block::{BlockRule, BlockState}; use markdown_it::parser::core::Root; use markdown_it::parser::inline::{InlineRule, InlineState}; @@ -9,12 +10,47 @@ use std::str::FromStr; use crate::components::fa::FAIcon; use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail}; -use crate::components::misc::{Backlinks, FarLink}; +use crate::components::misc::Backlinks; lazy_static! { static ref MdParser: MarkdownIt = { let mut md = markdown_it::MarkdownIt::new(); - markdown_it::plugins::cmark::add(&mut md); + + { + + use markdown_it::plugins::cmark::*; + + inline::newline::add(&mut md); + inline::escape::add(&mut md); + inline::backticks::add(&mut md); + inline::emphasis::add(&mut md); + + // Replaced with smart links + //inline::link::add(&mut md); + full_link::add::(&mut md, |href, title| { + Node::new(SmartLink { + url: href.unwrap_or_default(), + title, + }) + }); + + + inline::image::add(&mut md); + inline::autolink::add(&mut md); + inline::entity::add(&mut md); + + block::code::add(&mut md); + block::fence::add(&mut md); + block::blockquote::add(&mut md); + block::hr::add(&mut md); + block::list::add(&mut md); + block::reference::add(&mut md); + block::heading::add(&mut md); + block::lheading::add(&mut md); + block::paragraph::add(&mut md); + + } + markdown_it::plugins::html::add(&mut md); md.block.add_rule::().before_all(); @@ -100,6 +136,39 @@ pub fn page_from_markdown(md: impl Into, default_image: Option) // MARK: extensions // +// +// MARK: smart link +// + +#[derive(Debug)] +pub struct SmartLink { + pub url: String, + pub title: Option, +} + +impl NodeValue for SmartLink { + fn render(&self, node: &Node, fmt: &mut dyn Renderer) { + let mut attrs = node.attrs.clone(); + attrs.push(("href", self.url.clone())); + + if let Some(title) = &self.title { + attrs.push(("title", title.clone())); + } + + let external = !(self.url.starts_with(".") || self.url.starts_with("/")); + + // Open external links in a new tab + if external { + attrs.push(("target", "_blank".into())); + attrs.push(("rel", "noopener noreferrer".into())); + } + + fmt.open("a", &attrs); + fmt.contents(&node.children); + fmt.close("a"); + } +} + // // MARK: emote // @@ -159,10 +228,6 @@ impl NodeValue for InlineMdx { return; } - if mdx_external(&self.0, node, fmt) { - return; - } - fmt.open("code", &[]); fmt.text(&self.0); fmt.close("code"); @@ -340,105 +405,6 @@ fn mdx_include(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool { return true; } -fn mdx_external(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool { - // Parse inside of mdx: `external("text", "link")` - let args = { - let mdx = mdx - .trim() - .trim_start_matches('{') - .trim_end_matches('}') - .trim(); - - if !mdx.starts_with("external(") { - return false; - } - - let skip = 9; - let mut balance = 1; - let mut end = skip; - for i in mdx[skip..].bytes() { - match i { - b')' => balance -= 1, - b'(' => balance += 1, - _ => {} - } - - if balance == 0 { - break; - } - - end += 1; - } - - if balance != 0 { - return false; - } - - let args = mdx[skip..end].trim(); - let trail = mdx[end + 1..].trim(); - if !trail.is_empty() { - return false; - } - - args - }; - - // TODO: better parsing, handle , in string - let mut split = args.split(","); - - let title = match split.next() { - Some(mut x) => { - x = x.trim(); - - if &x[0..1] == "\"" { - x = &x[1..] - } else { - return false; - } - - if &x[x.len() - 1..] == "\"" { - x = &x[0..x.len() - 1] - } else { - return false; - } - - x - } - - None => return false, - }; - - let link = match split.next() { - Some(mut x) => { - x = x.trim(); - - if &x[0..1] == "\"" { - x = &x[1..] - } else { - return false; - } - - if &x[x.len() - 1..] == "\"" { - x = &x[0..x.len() - 1] - } else { - return false; - } - - x - } - - None => return false, - }; - - if split.next().is_some() { - return false; - } - - fmt.text_raw(&FarLink(link, title).render().0); - - return true; -} - // // MARK: yaml frontmatter // diff --git a/crates/service/service-webpage/src/pages/betalupi.md b/crates/service/service-webpage/src/pages/betalupi.md index 01f1625..c93abaf 100644 --- a/crates/service/service-webpage/src/pages/betalupi.md +++ b/crates/service/service-webpage/src/pages/betalupi.md @@ -31,4 +31,4 @@ A snippet of the [_Endless Sky_][es] map is below.
-betalupi map +betalupi map diff --git a/crates/service/service-webpage/src/pages/handouts.md b/crates/service/service-webpage/src/pages/handouts.md index b786173..b44b10d 100644 --- a/crates/service/service-webpage/src/pages/handouts.md +++ b/crates/service/service-webpage/src/pages/handouts.md @@ -6,15 +6,12 @@ slug = "handouts" # Mark's Handouts +[ORMC]: https://circles.math.ucla.edu/circles -This page lists all the handouts I've written for my classes at the -{{external("ORMC", "https://circles.math.ucla.edu/circles")}}, +This page lists all the handouts I've written for my classes at the [ORMC], arguably the best math circle in the western world. We teach students mathematics -far beyond the regular school curriculum, much like -{{external("AOPS", "https://artofproblemsolving.com")}} -and the -{{external("BMC", "https://mathcircle.berkeley.edu")}} - +far beyond the regular school curriculum, much like [AOPS](https://artofproblemsolving.com) +and the [BMC](https://mathcircle.berkeley.edu).

@@ -36,10 +33,8 @@ are written with this in mind.\ I do not expect the average student to finish all problems during this two-hour session. If the class finishes early, the lesson is either too short or too easy. -The sources for all these handouts are available -{{external("here", "https://git.betalupi.com/mark/handouts")}} \ -Some are written in LaTeX, some are in -{{external("Typst", "https://typst.app")}}. \ +The sources for all these handouts are available [here](https://git.betalupi.com/mark/handouts).\ +Some are written in LaTeX, some are in [Typst](https://typst.app). \ The latter is vastly superior.