render outline

This commit is contained in:
Maciej Jur 2024-04-21 11:45:19 +02:00
parent 6f9ae677b1
commit febde8d449
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
5 changed files with 98 additions and 27 deletions

View file

@ -19,9 +19,10 @@ const INTRO: &str = r#"
fn intro() -> impl Renderable { fn intro() -> impl Renderable {
let (_, html) = parse(INTRO);
maud!( maud!(
section .p-card.intro-jp lang="ja-JP" { section .p-card.intro-jp lang="ja-JP" {
(Raw(parse(INTRO))) (Raw(html))
} }
) )
} }

View file

@ -1,9 +1,14 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable}; use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::md::Post; use crate::md::Post;
use crate::html::page; use crate::html::page;
use crate::text::md::Outline;
pub fn post<'fm, 'md, 'post, T>(fm: &'fm Post, content: T) -> impl Renderable + 'post pub fn post<'fm, 'md, 'post, T>(
fm: &'fm Post,
content: T,
outline: Outline,
) -> impl Renderable + 'post
where where
'fm: 'post, 'fm: 'post,
'md: 'post, 'md: 'post,
@ -20,8 +25,22 @@ pub fn post<'fm, 'md, 'post, T>(fm: &'fm Post, content: T) -> impl Renderable +
label .wiki-aside__slider for="wiki-aside-shown" { label .wiki-aside__slider for="wiki-aside-shown" {
img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24"; img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24";
} }
// Navigation tree section .link-tree {
// <Tree heading="Personal Wiki" pages={pages} headings={headings} /> h2 .link-tree__heading {
a .link-tree__heading-text href="#top" { "Content" }
}
nav #table-of-contents .link-tree__nav {
ul .link-tree__nav-list {
@for (title, id) in outline.0 {
li .link-tree__nav-list-item {
a .link-tree__nav-list-text.link href=(format!("#{id}")) {
(title)
}
}
}
}
}
}
} }
article .wiki-article /*class:list={classlist)*/ { article .wiki-article /*class:list={classlist)*/ {

View file

@ -1,9 +1,14 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable}; use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::md::Wiki; use crate::md::Wiki;
use crate::html::page; use crate::html::page;
use crate::text::md::Outline;
pub fn wiki<'data, 'html, T>(fm: &'data Wiki, content: T) -> impl Renderable + 'html pub fn wiki<'data, 'html, T>(
fm: &'data Wiki,
content: T,
outline: Outline,
) -> impl Renderable + 'html
where where
'data: 'html, 'data: 'html,
T: Renderable + 'data T: Renderable + 'data

View file

@ -6,6 +6,7 @@ use grass;
use html::LinkableData; use html::LinkableData;
use hypertext::{Raw, Renderable}; use hypertext::{Raw, Renderable};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use text::md::Outline;
mod md; mod md;
mod html; mod html;
@ -61,7 +62,7 @@ impl Everything<'_> {
trait Transformable { trait Transformable {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> impl Renderable + 'html fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html
where where
'f: 'html, 'f: 'html,
'm: 'html, 'm: 'html,
@ -69,16 +70,16 @@ trait Transformable {
fn as_link(&self, path: String) -> Option<html::LinkableData>; fn as_link(&self, path: String) -> Option<html::LinkableData>;
fn render(data: &str) -> String; fn render(data: &str) -> (Outline, String);
} }
impl Transformable for md::Post { impl Transformable for md::Post {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> impl Renderable + 'html fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html
where where
'f: 'html, 'f: 'html,
'm: 'html, 'm: 'html,
T: Renderable + 'm { T: Renderable + 'm {
html::post(self, content) html::post(self, content, outline)
} }
fn as_link(&self, path: String) -> Option<html::LinkableData> { fn as_link(&self, path: String) -> Option<html::LinkableData> {
@ -90,13 +91,13 @@ impl Transformable for md::Post {
}) })
} }
fn render(data: &str) -> String { fn render(data: &str) -> (Outline, String) {
text::md::parse(data) text::md::parse(data)
} }
} }
impl Transformable for md::Slide { impl Transformable for md::Slide {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> impl Renderable + 'html fn transform<'f, 'm, 'html, T>(&'f self, content: T, _: Outline) -> impl Renderable + 'html
where where
'f: 'html, 'f: 'html,
'm: 'html, 'm: 'html,
@ -113,32 +114,33 @@ impl Transformable for md::Slide {
}) })
} }
fn render(data: &str) -> String { fn render(data: &str) -> (Outline, String) {
data let html = data
.split("\n-----\n") .split("\n-----\n")
.map(|chunk| chunk.split("\n---\n").map(text::md::parse).collect::<Vec<_>>()) .map(|chunk| chunk.split("\n---\n").map(text::md::parse).map(|e| e.1).collect::<Vec<_>>())
.map(|stack| match stack.len() > 1 { .map(|stack| match stack.len() > 1 {
true => format!("<section>{}</section>", stack.into_iter().map(|slide| format!("<section>{slide}</section>")).collect::<String>()), true => format!("<section>{}</section>", stack.into_iter().map(|slide| format!("<section>{slide}</section>")).collect::<String>()),
false => format!("<section>{}</section>", stack[0]) false => format!("<section>{}</section>", stack[0])
}) })
.collect::<String>() .collect::<String>();
(Outline(vec![]), html)
} }
} }
impl Transformable for md::Wiki { impl Transformable for md::Wiki {
fn transform<'f, 'm, 'html, T>(&'f self, content: T) -> impl Renderable + 'html fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html
where where
'f: 'html, 'f: 'html,
'm: 'html, 'm: 'html,
T: Renderable + 'm { T: Renderable + 'm {
html::wiki(self, content) html::wiki(self, content, outline)
} }
fn as_link(&self, _: String) -> Option<html::LinkableData> { fn as_link(&self, _: String) -> Option<html::LinkableData> {
None None
} }
fn render(data: &str) -> String { fn render(data: &str) -> (Outline, String) {
text::md::parse(data) text::md::parse(data)
} }
} }
@ -182,9 +184,8 @@ fn transform<T>(meta: gen::Source) -> gen::Asset
let link = T::as_link(&fm, Path::new("/").join(&loc).to_str().unwrap().to_owned()); let link = T::as_link(&fm, Path::new("/").join(&loc).to_str().unwrap().to_owned());
let call = move |_: &Everything| { let call = move |_: &Everything| {
let data = T::render(&md); let (outline, html) = T::render(&md);
let data = T::transform(&fm, Raw(data)).render().into(); T::transform(&fm, Raw(html), outline).render().into()
data
}; };
gen::Asset { gen::Asset {
@ -247,8 +248,8 @@ fn main() {
gen::Asset { gen::Asset {
kind: gen::AssetKind::Html(Box::new(|_| { kind: gen::AssetKind::Html(Box::new(|_| {
let data = std::fs::read_to_string("content/index.md").unwrap(); let data = std::fs::read_to_string("content/index.md").unwrap();
let data = text::md::parse(&data); let (outline, html) = text::md::parse(&data);
html::home(Raw(data)).render().to_owned().into() html::home(Raw(html)).render().to_owned().into()
})), })),
out: "index.html".into(), out: "index.html".into(),
link: None, link: None,

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use hypertext::Renderable; use hypertext::Renderable;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd, TextMergeStream}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd, TextMergeStream};
@ -30,10 +32,18 @@ static KATEX_B: Lazy<katex::Opts> = Lazy::new(||
.unwrap() .unwrap()
); );
pub struct Outline(pub Vec<(String, String)>);
pub fn parse(text: &str) -> String {
let stream = Parser::new_ext(text, *OPTS); pub fn parse(text: &str) -> (Outline, String) {
let stream = TextMergeStream::new(stream) let (outline, stream) = {
let stream = Parser::new_ext(text, *OPTS);
let mut stream: Vec<_> = TextMergeStream::new(stream).collect();
let outline = set_heading_ids(&mut stream);
(outline, stream)
};
let stream = stream.into_iter()
.map(make_math) .map(make_math)
.map(make_emoji) .map(make_emoji)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -44,7 +54,42 @@ pub fn parse(text: &str) -> String {
let mut html = String::new(); let mut html = String::new();
pulldown_cmark::html::push_html(&mut html, stream.into_iter()); pulldown_cmark::html::push_html(&mut html, stream.into_iter());
html
(outline, html)
}
fn set_heading_ids(events: &mut [Event]) -> Outline {
let mut cnt = HashMap::<String, i32>::new();
let mut out = Vec::new();
let mut buf = String::new();
let mut ptr = None;
for event in events {
match event {
Event::Start(ref mut tag @ Tag::Heading {..}) => {
ptr = Some(tag);
},
Event::Text(ref text) if ptr.is_some() => {
buf.push_str(text)
},
Event::End(TagEnd::Heading(..)) => {
let txt = std::mem::take(&mut buf);
let url = txt.to_lowercase().replace(' ', "-");
let url = match cnt.get_mut(&url) {
Some(ptr) => { *ptr += 1; format!("{url}-{ptr}") },
None => { cnt.insert(url.clone(), 0); url },
};
match ptr.take().unwrap() {
Tag::Heading { ref mut id, .. } => *id = Some(url.clone().into()),
_ => unreachable!(),
}
out.push((txt, url));
},
_ => (),
}
};
Outline(out)
} }
fn make_math(event: Event) -> Event { fn make_math(event: Event) -> Event {