render outline
This commit is contained in:
parent
6f9ae677b1
commit
febde8d449
|
@ -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))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)*/ {
|
||||||
|
|
|
@ -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
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue