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 {
let (_, html) = parse(INTRO);
maud!(
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 crate::md::Post;
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
'fm: '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" {
img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24";
}
// Navigation tree
// <Tree heading="Personal Wiki" pages={pages} headings={headings} />
section .link-tree {
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)*/ {

View file

@ -1,9 +1,14 @@
use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable};
use crate::md::Wiki;
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
'data: 'html,
T: Renderable + 'data

View file

@ -6,6 +6,7 @@ use grass;
use html::LinkableData;
use hypertext::{Raw, Renderable};
use once_cell::sync::Lazy;
use text::md::Outline;
mod md;
mod html;
@ -61,7 +62,7 @@ impl Everything<'_> {
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
'f: 'html,
'm: 'html,
@ -69,16 +70,16 @@ trait Transformable {
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 {
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
'f: 'html,
'm: 'html,
T: Renderable + 'm {
html::post(self, content)
html::post(self, content, outline)
}
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)
}
}
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
'f: 'html,
'm: 'html,
@ -113,32 +114,33 @@ impl Transformable for md::Slide {
})
}
fn render(data: &str) -> String {
data
fn render(data: &str) -> (Outline, String) {
let html = data
.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 {
true => format!("<section>{}</section>", stack.into_iter().map(|slide| format!("<section>{slide}</section>")).collect::<String>()),
false => format!("<section>{}</section>", stack[0])
})
.collect::<String>()
.collect::<String>();
(Outline(vec![]), html)
}
}
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
'f: 'html,
'm: 'html,
T: Renderable + 'm {
html::wiki(self, content)
html::wiki(self, content, outline)
}
fn as_link(&self, _: String) -> Option<html::LinkableData> {
None
}
fn render(data: &str) -> String {
fn render(data: &str) -> (Outline, String) {
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 call = move |_: &Everything| {
let data = T::render(&md);
let data = T::transform(&fm, Raw(data)).render().into();
data
let (outline, html) = T::render(&md);
T::transform(&fm, Raw(html), outline).render().into()
};
gen::Asset {
@ -247,8 +248,8 @@ fn main() {
gen::Asset {
kind: gen::AssetKind::Html(Box::new(|_| {
let data = std::fs::read_to_string("content/index.md").unwrap();
let data = text::md::parse(&data);
html::home(Raw(data)).render().to_owned().into()
let (outline, html) = text::md::parse(&data);
html::home(Raw(html)).render().to_owned().into()
})),
out: "index.html".into(),
link: None,

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use hypertext::Renderable;
use once_cell::sync::Lazy;
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd, TextMergeStream};
@ -30,10 +32,18 @@ static KATEX_B: Lazy<katex::Opts> = Lazy::new(||
.unwrap()
);
pub struct Outline(pub Vec<(String, String)>);
pub fn parse(text: &str) -> String {
let stream = Parser::new_ext(text, *OPTS);
let stream = TextMergeStream::new(stream)
pub fn parse(text: &str) -> (Outline, String) {
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_emoji)
.collect::<Vec<_>>();
@ -44,7 +54,42 @@ pub fn parse(text: &str) -> String {
let mut html = String::new();
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 {