Smart links
This commit is contained in:
@@ -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
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user