1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2024-11-22 07:31:09 +00:00

Adds math parsing (#61)

* Adds LaTex parsing to HTML with KaTex

* Adds display mode
Remove commented block parser rule

* Applies Nutomic suggestions
Adds fonts to assets
Adds KaTex CSS to assets
Makes katex crate inclusion depending on build type less verbose

* Woodpecker checks

---------

Co-authored-by: Bintou Ali <bintou005@protonmail.com>
This commit is contained in:
Silver-Sorbet 2024-10-15 07:39:29 +00:00 committed by GitHub
parent abd89bfdc4
commit 94a07cd7bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 179 additions and 5 deletions

87
Cargo.lock generated
View file

@ -14,7 +14,7 @@ dependencies = [
"base64 0.22.1",
"bytes",
"chrono",
"derive_builder",
"derive_builder 0.20.1",
"diesel",
"dyn-clone",
"enum_delegate",
@ -456,6 +456,12 @@ dependencies = [
"libc",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -900,13 +906,34 @@ dependencies = [
"syn 2.0.51",
]
[[package]]
name = "derive_builder"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
dependencies = [
"derive_builder_macro 0.12.0",
]
[[package]]
name = "derive_builder"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b"
dependencies = [
"derive_builder_macro",
"derive_builder_macro 0.20.1",
]
[[package]]
name = "derive_builder_core"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
dependencies = [
"darling 0.14.4",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
@ -921,13 +948,23 @@ dependencies = [
"syn 2.0.51",
]
[[package]]
name = "derive_builder_macro"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
"derive_builder_core 0.12.0",
"syn 1.0.109",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc"
dependencies = [
"derive_builder_core",
"derive_builder_core 0.20.1",
"syn 2.0.51",
]
@ -1088,6 +1125,25 @@ dependencies = [
"syn 2.0.51",
]
[[package]]
name = "ducc"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41bc1f8a30712eb6a7454f85747f218d9dfb41d173bb223a8c4f18daff829207"
dependencies = [
"cesu8",
"ducc-sys",
]
[[package]]
name = "ducc-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cdea834bf6a0fde522374db4404695c5f0465fc0ee814f2878d76eaabd4ffed"
dependencies = [
"cc",
]
[[package]]
name = "dyn-clone"
version = "1.0.17"
@ -1671,6 +1727,7 @@ dependencies = [
"futures",
"hex",
"jsonwebtoken",
"katex",
"leptos",
"leptos_axum",
"leptos_meta",
@ -1769,6 +1826,15 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
@ -1828,6 +1894,21 @@ dependencies = [
"simple_asn1",
]
[[package]]
name = "katex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bdbc7a1823f188f56ac9486993536b70a2686a58d47095dcc10507a7d242bf5"
dependencies = [
"cfg-if",
"derive_builder 0.12.0",
"ducc",
"itertools 0.10.5",
"js-sys",
"thiserror",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"

View file

@ -17,8 +17,9 @@ ssr = [
"leptos_axum",
"activitypub_federation",
"jsonwebtoken",
"katex/duktape",
]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr"]
csr = ["leptos/csr", "leptos_meta/csr", "leptos_router/csr", "katex/wasm-js"]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
[lints.clippy]
@ -78,6 +79,7 @@ config = { version = "0.14.0", features = ["toml"] }
doku = "0.21.1"
smart-default = "0.7.1"
tower-layer = "0.3.3"
katex = { version = "0.4", default-features = false }
[dev-dependencies]
pretty_assertions = "1.4.1"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
assets/katex.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -24,6 +24,7 @@ use activitypub_federation::{
use api::api_routes;
use axum::{
body::Body,
extract::Path,
http::{HeaderValue, Request},
middleware::Next,
response::{IntoResponse, Response},
@ -119,8 +120,13 @@ pub fn asset_routes() -> MyResult<Router<()>> {
)
.route(
"/assets/simple.css",
get((css_headers, include_str!("../../assets/simple.css"))),
get((css_headers.clone(), include_str!("../../assets/simple.css"))),
)
.route(
"/assets/katex.min.css",
get((css_headers, include_str!("../../assets/katex.min.css"))),
)
.route("/assets/fonts/*font", get(get_font))
.route(
"/assets/index.html",
get(include_str!("../../assets/index.html")),
@ -145,6 +151,23 @@ async fn serve_wasm() -> MyResult<impl IntoResponse> {
Ok((headers, content))
}
#[debug_handler]
async fn get_font(Path(font): Path<String>) -> MyResult<impl IntoResponse> {
let mut headers = HeaderMap::new();
let content_type = if font.ends_with(".ttf") {
"font/ttf"
} else if font.ends_with(".woff") {
"font/woff"
} else if font.ends_with(".woff2") {
"font/woff2"
} else {
""
};
headers.insert("Content-type", content_type.parse()?);
let content = std::fs::read("assets/fonts/".to_owned() + &font).expect("Can't open katex font");
Ok((headers, content))
}
const MAIN_PAGE_DEFAULT_TEXT: &str = "Welcome to Ibis, the federated Wikipedia alternative!
This main page can only be edited by the admin. Use it as an introduction for new users, \

View file

@ -93,6 +93,7 @@ pub fn App() -> impl IntoView {
<>
<Stylesheet id="simple" href="/assets/simple.css" />
<Stylesheet id="ibis" href="/assets/ibis.css" />
<Stylesheet id="katex" href="/assets/katex.min.css" />
<Router>
<Nav />
<main>

View file

@ -1,3 +1,4 @@
use katex;
use markdown_it::{
parser::inline::{InlineRule, InlineState},
MarkdownIt,
@ -14,6 +15,7 @@ pub fn markdown_parser() -> MarkdownIt {
markdown_it::plugins::extra::tables::add(&mut parser);
markdown_it::plugins::extra::typographer::add(&mut parser);
parser.inline.add_rule::<ArticleLinkScanner>();
parser.inline.add_rule::<MathEquationScanner>();
parser
}
@ -65,6 +67,56 @@ impl InlineRule for ArticleLinkScanner {
}
}
#[derive(Debug)]
pub struct MathEquation {
equation: String,
display_mode: bool,
}
impl NodeValue for MathEquation {
fn render(&self, _node: &Node, fmt: &mut dyn Renderer) {
let opts = katex::Opts::builder()
.throw_on_error(false)
.display_mode(self.display_mode)
.build()
.unwrap();
let katex_equation = katex::render_with_opts(&self.equation, opts).unwrap();
fmt.text_raw(&katex_equation)
}
}
struct MathEquationScanner;
impl InlineRule for MathEquationScanner {
const MARKER: char = '$';
fn run(state: &mut InlineState) -> Option<(Node, usize)> {
let input = &state.src[state.pos..state.pos_max];
if !input.starts_with("$$") {
return None;
}
let mut display_mode = false;
if input.starts_with("$$\n") {
display_mode = true;
}
const SEPARATOR_LENGTH: usize = 2;
input[SEPARATOR_LENGTH - 1..].find("$$").map(|length| {
let start = state.pos + SEPARATOR_LENGTH;
let i = start + length - SEPARATOR_LENGTH + 1;
if start > i {
return None;
}
let content = &state.src[start..i];
let node = Node::new(MathEquation {
equation: content.to_string(),
display_mode,
});
Some((node, length + SEPARATOR_LENGTH + 1))
})?
}
}
#[test]
fn test_markdown_article_link() {
let parser = markdown_parser();
@ -76,3 +128,17 @@ fn test_markdown_article_link() {
rendered
);
}
#[test]
fn test_markdown_equation_katex() {
let parser = markdown_parser();
let rendered = parser
.parse("here is a math equation: $$E=mc^2$$. Pretty cool, right?")
.render();
assert_eq!(
"<p>here is a math equation: ".to_owned()
+ &katex::render("E=mc^2").unwrap()
+ ". Pretty cool, right?</p>\n",
rendered
);
}