Smart links

This commit is contained in:
2025-11-05 22:05:24 -08:00
parent 399fccb73b
commit dfffe824d1
3 changed files with 78 additions and 117 deletions

View File

@@ -1,4 +1,5 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use markdown_it::generics::inline::full_link;
use markdown_it::parser::block::{BlockRule, BlockState}; use markdown_it::parser::block::{BlockRule, BlockState};
use markdown_it::parser::core::Root; use markdown_it::parser::core::Root;
use markdown_it::parser::inline::{InlineRule, InlineState}; use markdown_it::parser::inline::{InlineRule, InlineState};
@@ -9,12 +10,47 @@ use std::str::FromStr;
use crate::components::fa::FAIcon; use crate::components::fa::FAIcon;
use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail}; use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail};
use crate::components::misc::{Backlinks, FarLink}; use crate::components::misc::Backlinks;
lazy_static! { lazy_static! {
static ref MdParser: MarkdownIt = { static ref MdParser: MarkdownIt = {
let mut md = markdown_it::MarkdownIt::new(); 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::<false>(&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); markdown_it::plugins::html::add(&mut md);
md.block.add_rule::<YamlFrontMatter>().before_all(); md.block.add_rule::<YamlFrontMatter>().before_all();
@@ -100,6 +136,39 @@ pub fn page_from_markdown(md: impl Into<String>, default_image: Option<String>)
// MARK: extensions // MARK: extensions
// //
//
// MARK: smart link
//
#[derive(Debug)]
pub struct SmartLink {
pub url: String,
pub title: Option<String>,
}
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 // MARK: emote
// //
@@ -159,10 +228,6 @@ impl NodeValue for InlineMdx {
return; return;
} }
if mdx_external(&self.0, node, fmt) {
return;
}
fmt.open("code", &[]); fmt.open("code", &[]);
fmt.text(&self.0); fmt.text(&self.0);
fmt.close("code"); fmt.close("code");
@@ -340,105 +405,6 @@ fn mdx_include(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
return true; 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 // MARK: yaml frontmatter
// //

View File

@@ -31,4 +31,4 @@ A snippet of the [_Endless Sky_][es] map is below.
<br/> <br/>
<img alt="betalupi map" class="image" src="/assets/img/betalupi.png"></img> <img alt="betalupi map" src="/assets/img/betalupi.png"></img>

View File

@@ -6,15 +6,12 @@ slug = "handouts"
# Mark's 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 This page lists all the handouts I've written for my classes at the [ORMC],
{{external("ORMC", "https://circles.math.ucla.edu/circles")}},
arguably the best math circle in the western world. We teach students mathematics arguably the best math circle in the western world. We teach students mathematics
far beyond the regular school curriculum, much like far beyond the regular school curriculum, much like [AOPS](https://artofproblemsolving.com)
{{external("AOPS", "https://artofproblemsolving.com")}} and the [BMC](https://mathcircle.berkeley.edu).
and the
{{external("BMC", "https://mathcircle.berkeley.edu")}}
<br></br> <br></br>
@@ -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. 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. If the class finishes early, the lesson is either too short or too easy.
The sources for all these handouts are available The sources for all these handouts are available [here](https://git.betalupi.com/mark/handouts).\
{{external("here", "https://git.betalupi.com/mark/handouts")}} \ Some are written in LaTeX, some are in [Typst](https://typst.app). \
Some are written in LaTeX, some are in
{{external("Typst", "https://typst.app")}}. \
The latter is vastly superior. The latter is vastly superior.
<br></br> <br></br>