making it look better
This commit is contained in:
		
							parent
							
								
									ad84a79335
								
							
						
					
					
						commit
						30255be018
					
				
					 22 changed files with 2567 additions and 72 deletions
				
			
		
							
								
								
									
										12
									
								
								.editorconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.editorconfig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
# EditorConfig is awesome: https://EditorConfig.org
 | 
			
		||||
 | 
			
		||||
# top-most EditorConfig file
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
[*]
 | 
			
		||||
indent_style = space
 | 
			
		||||
indent_size = 4
 | 
			
		||||
end_of_line = lf
 | 
			
		||||
charset = utf-8
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
insert_final_newline = true
 | 
			
		||||
							
								
								
									
										1849
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1849
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -4,6 +4,4 @@ resolver = "2"
 | 
			
		|||
 | 
			
		||||
[workspace.dependencies]
 | 
			
		||||
 | 
			
		||||
log = "0.4.20"
 | 
			
		||||
 | 
			
		||||
treehouse-format = { path = "crates/treehouse-format" }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
 | 
			
		||||
        - treehouse is a brand new static website generator, inspired by the likes of Jekyll and Hugo, but offering a writing experience more close to Logseq
 | 
			
		||||
 | 
			
		||||
            - ie. a public braindump
 | 
			
		||||
            - ie. a public braindump adsadasdsad
 | 
			
		||||
 | 
			
		||||
    - since you're here, you're probably just setting up
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,7 @@
 | 
			
		|||
 | 
			
		||||
            - this special file is almost like your index.html
 | 
			
		||||
 | 
			
		||||
            + the syntax is pretty simple
 | 
			
		||||
            + the .tree syntax is pretty simple
 | 
			
		||||
 | 
			
		||||
                - separate blocks are delimited with a blank line
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +28,35 @@
 | 
			
		|||
 | 
			
		||||
                    - a plus `+` means that the block is hidden by default
 | 
			
		||||
 | 
			
		||||
                - before the block content, there can be an arbitrary amount of TOML specifying the block config
 | 
			
		||||
                - before the block content, there can be an arbitrary amount of TOML pecifying the block attributes
 | 
			
		||||
 | 
			
		||||
                    - many keys are available but they aren't really documented outside of code
 | 
			
		||||
 | 
			
		||||
                - blocks can span multiple lines as long as they are not broken apart with a blank line
 | 
			
		||||
 | 
			
		||||
                    - that means each block can contain at most one paragraph, unless you use dirty HTML hacks (cheater!)
 | 
			
		||||
 | 
			
		||||
            - .tree composes together with Markdown to let you format text however you want
 | 
			
		||||
 | 
			
		||||
                - here's a bunch of stuff formatted
 | 
			
		||||
 | 
			
		||||
                    - # heading 1
 | 
			
		||||
 | 
			
		||||
                    - ## heading 2
 | 
			
		||||
 | 
			
		||||
                    - ### heading 3
 | 
			
		||||
                      headings lower than this aren't really supported because c'mon who would be this crazy
 | 
			
		||||
 | 
			
		||||
                    - <https://liquidev.net>
 | 
			
		||||
 | 
			
		||||
                    - here is my favorite fluffy boy 
 | 
			
		||||
 | 
			
		||||
                    - also a block quote
 | 
			
		||||
 | 
			
		||||
                        - > Enough You Foolish Children
 | 
			
		||||
 | 
			
		||||
                    - yes i will totally abuse you with deltarune references and you cannot stop me
 | 
			
		||||
 | 
			
		||||
                    - ```
 | 
			
		||||
                    this is some block of code it looks pretty cool doesn't it
 | 
			
		||||
                    ```
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,4 +5,3 @@ edition = "2021"
 | 
			
		|||
 | 
			
		||||
[dependencies]
 | 
			
		||||
thiserror = "1.0.47"
 | 
			
		||||
log = { workspace = true }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ impl Roots {
 | 
			
		|||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Branch {
 | 
			
		||||
    pub indent_level: usize,
 | 
			
		||||
    pub attributes: Range<usize>,
 | 
			
		||||
    pub kind: BranchKind,
 | 
			
		||||
    pub kind_span: Range<usize>,
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +36,7 @@ pub struct Branch {
 | 
			
		|||
impl From<BranchEvent> for Branch {
 | 
			
		||||
    fn from(branch: BranchEvent) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            indent_level: branch.indent_level,
 | 
			
		||||
            attributes: branch.attributes,
 | 
			
		||||
            kind: branch.kind,
 | 
			
		||||
            kind_span: branch.kind_span,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,17 @@ version = "0.1.0"
 | 
			
		|||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.75"
 | 
			
		||||
axum = "0.6.20"
 | 
			
		||||
codespan-reporting = "0.11.1"
 | 
			
		||||
copy_dir = "0.1.3"
 | 
			
		||||
handlebars = "4.3.7"
 | 
			
		||||
pulldown-cmark = { version = "0.9.3", default-features = false }
 | 
			
		||||
serde = { version = "1.0.183", features = ["derive"] }
 | 
			
		||||
thiserror = "1.0.47"
 | 
			
		||||
tokio = "1.32.0"
 | 
			
		||||
tower-http = { version = "0.4.3", features = ["fs"] }
 | 
			
		||||
tower-livereload = "0.8.0"
 | 
			
		||||
treehouse-format = { workspace = true }
 | 
			
		||||
watchexec = "2.3.0"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										439
									
								
								crates/treehouse/src/html/markdown.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										439
									
								
								crates/treehouse/src/html/markdown.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,439 @@
 | 
			
		|||
// NOTE: This code is pasted pretty much verbatim from pulldown-cmark but tweaked to have my own
 | 
			
		||||
// cool additions.
 | 
			
		||||
 | 
			
		||||
// Copyright 2015 Google Inc. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
// of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
// in the Software without restriction, including without limitation the rights
 | 
			
		||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
// copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
// furnished to do so, subject to the following conditions:
 | 
			
		||||
//
 | 
			
		||||
// The above copyright notice and this permission notice shall be included in
 | 
			
		||||
// all copies or substantial portions of the Software.
 | 
			
		||||
//
 | 
			
		||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
// THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
//! HTML renderer that takes an iterator of events as input.
 | 
			
		||||
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::io;
 | 
			
		||||
 | 
			
		||||
use pulldown_cmark::escape::{escape_href, escape_html, StrWrite};
 | 
			
		||||
use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Tag};
 | 
			
		||||
use pulldown_cmark::{CowStr, Event::*};
 | 
			
		||||
 | 
			
		||||
enum TableState {
 | 
			
		||||
    Head,
 | 
			
		||||
    Body,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct HtmlWriter<'a, I, W> {
 | 
			
		||||
    /// Iterator supplying events.
 | 
			
		||||
    iter: I,
 | 
			
		||||
 | 
			
		||||
    /// Writer to write to.
 | 
			
		||||
    writer: W,
 | 
			
		||||
 | 
			
		||||
    /// Whether or not the last write wrote a newline.
 | 
			
		||||
    end_newline: bool,
 | 
			
		||||
 | 
			
		||||
    table_state: TableState,
 | 
			
		||||
    table_alignments: Vec<Alignment>,
 | 
			
		||||
    table_cell_index: usize,
 | 
			
		||||
    numbers: HashMap<CowStr<'a>, usize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, I, W> HtmlWriter<'a, I, W>
 | 
			
		||||
where
 | 
			
		||||
    I: Iterator<Item = Event<'a>>,
 | 
			
		||||
    W: StrWrite,
 | 
			
		||||
{
 | 
			
		||||
    fn new(iter: I, writer: W) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            iter,
 | 
			
		||||
            writer,
 | 
			
		||||
            end_newline: true,
 | 
			
		||||
            table_state: TableState::Head,
 | 
			
		||||
            table_alignments: vec![],
 | 
			
		||||
            table_cell_index: 0,
 | 
			
		||||
            numbers: HashMap::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes a new line.
 | 
			
		||||
    fn write_newline(&mut self) -> io::Result<()> {
 | 
			
		||||
        self.end_newline = true;
 | 
			
		||||
        self.writer.write_str("\n")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes a buffer, and tracks whether or not a newline was written.
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn write(&mut self, s: &str) -> io::Result<()> {
 | 
			
		||||
        self.writer.write_str(s)?;
 | 
			
		||||
 | 
			
		||||
        if !s.is_empty() {
 | 
			
		||||
            self.end_newline = s.ends_with('\n');
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn run(mut self) -> io::Result<()> {
 | 
			
		||||
        while let Some(event) = self.iter.next() {
 | 
			
		||||
            match event {
 | 
			
		||||
                Start(tag) => {
 | 
			
		||||
                    self.start_tag(tag)?;
 | 
			
		||||
                }
 | 
			
		||||
                End(tag) => {
 | 
			
		||||
                    self.end_tag(tag)?;
 | 
			
		||||
                }
 | 
			
		||||
                Text(text) => {
 | 
			
		||||
                    escape_html(&mut self.writer, &text)?;
 | 
			
		||||
                    self.end_newline = text.ends_with('\n');
 | 
			
		||||
                }
 | 
			
		||||
                Code(text) => {
 | 
			
		||||
                    self.write("<code>")?;
 | 
			
		||||
                    escape_html(&mut self.writer, &text)?;
 | 
			
		||||
                    self.write("</code>")?;
 | 
			
		||||
                }
 | 
			
		||||
                Html(html) => {
 | 
			
		||||
                    self.write(&html)?;
 | 
			
		||||
                }
 | 
			
		||||
                SoftBreak => {
 | 
			
		||||
                    self.write_newline()?;
 | 
			
		||||
                }
 | 
			
		||||
                HardBreak => {
 | 
			
		||||
                    self.write("<br />\n")?;
 | 
			
		||||
                }
 | 
			
		||||
                Rule => {
 | 
			
		||||
                    if self.end_newline {
 | 
			
		||||
                        self.write("<hr />\n")?;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        self.write("\n<hr />\n")?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                FootnoteReference(name) => {
 | 
			
		||||
                    let len = self.numbers.len() + 1;
 | 
			
		||||
                    self.write("<sup class=\"footnote-reference\"><a href=\"#")?;
 | 
			
		||||
                    escape_html(&mut self.writer, &name)?;
 | 
			
		||||
                    self.write("\">")?;
 | 
			
		||||
                    let number = *self.numbers.entry(name).or_insert(len);
 | 
			
		||||
                    write!(&mut self.writer, "{}", number)?;
 | 
			
		||||
                    self.write("</a></sup>")?;
 | 
			
		||||
                }
 | 
			
		||||
                TaskListMarker(true) => {
 | 
			
		||||
                    self.write("<input disabled=\"\" type=\"checkbox\" checked=\"\"/>\n")?;
 | 
			
		||||
                }
 | 
			
		||||
                TaskListMarker(false) => {
 | 
			
		||||
                    self.write("<input disabled=\"\" type=\"checkbox\"/>\n")?;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes the start of an HTML tag.
 | 
			
		||||
    fn start_tag(&mut self, tag: Tag<'a>) -> io::Result<()> {
 | 
			
		||||
        match tag {
 | 
			
		||||
            Tag::Paragraph => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<p>")
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<p>")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Heading(level, id, classes) => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.end_newline = false;
 | 
			
		||||
                    self.write("<")?;
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<")?;
 | 
			
		||||
                }
 | 
			
		||||
                write!(&mut self.writer, "{}", level)?;
 | 
			
		||||
                if let Some(id) = id {
 | 
			
		||||
                    self.write(" id=\"")?;
 | 
			
		||||
                    escape_html(&mut self.writer, id)?;
 | 
			
		||||
                    self.write("\"")?;
 | 
			
		||||
                }
 | 
			
		||||
                let mut classes = classes.iter();
 | 
			
		||||
                if let Some(class) = classes.next() {
 | 
			
		||||
                    self.write(" class=\"")?;
 | 
			
		||||
                    escape_html(&mut self.writer, class)?;
 | 
			
		||||
                    for class in classes {
 | 
			
		||||
                        self.write(" ")?;
 | 
			
		||||
                        escape_html(&mut self.writer, class)?;
 | 
			
		||||
                    }
 | 
			
		||||
                    self.write("\"")?;
 | 
			
		||||
                }
 | 
			
		||||
                self.write(">")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Table(alignments) => {
 | 
			
		||||
                self.table_alignments = alignments;
 | 
			
		||||
                self.write("<table>")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableHead => {
 | 
			
		||||
                self.table_state = TableState::Head;
 | 
			
		||||
                self.table_cell_index = 0;
 | 
			
		||||
                self.write("<thead><tr>")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableRow => {
 | 
			
		||||
                self.table_cell_index = 0;
 | 
			
		||||
                self.write("<tr>")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableCell => {
 | 
			
		||||
                match self.table_state {
 | 
			
		||||
                    TableState::Head => {
 | 
			
		||||
                        self.write("<th")?;
 | 
			
		||||
                    }
 | 
			
		||||
                    TableState::Body => {
 | 
			
		||||
                        self.write("<td")?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                match self.table_alignments.get(self.table_cell_index) {
 | 
			
		||||
                    Some(&Alignment::Left) => self.write(" style=\"text-align: left\">"),
 | 
			
		||||
                    Some(&Alignment::Center) => self.write(" style=\"text-align: center\">"),
 | 
			
		||||
                    Some(&Alignment::Right) => self.write(" style=\"text-align: right\">"),
 | 
			
		||||
                    _ => self.write(">"),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::BlockQuote => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<blockquote>\n")
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<blockquote>\n")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::CodeBlock(info) => {
 | 
			
		||||
                if !self.end_newline {
 | 
			
		||||
                    self.write_newline()?;
 | 
			
		||||
                }
 | 
			
		||||
                match info {
 | 
			
		||||
                    CodeBlockKind::Fenced(info) => {
 | 
			
		||||
                        let lang = info.split(' ').next().unwrap();
 | 
			
		||||
                        if lang.is_empty() {
 | 
			
		||||
                            self.write("<pre><code>")
 | 
			
		||||
                        } else {
 | 
			
		||||
                            self.write("<pre><code class=\"language-")?;
 | 
			
		||||
                            escape_html(&mut self.writer, lang)?;
 | 
			
		||||
                            self.write("\">")
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    CodeBlockKind::Indented => self.write("<pre><code>"),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::List(Some(1)) => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<ol>\n")
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<ol>\n")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::List(Some(start)) => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<ol start=\"")?;
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<ol start=\"")?;
 | 
			
		||||
                }
 | 
			
		||||
                write!(&mut self.writer, "{}", start)?;
 | 
			
		||||
                self.write("\">\n")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::List(None) => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<ul>\n")
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<ul>\n")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Item => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<li>")
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<li>")
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Emphasis => self.write("<em>"),
 | 
			
		||||
            Tag::Strong => self.write("<strong>"),
 | 
			
		||||
            Tag::Strikethrough => self.write("<del>"),
 | 
			
		||||
            Tag::Link(LinkType::Email, dest, title) => {
 | 
			
		||||
                self.write("<a href=\"mailto:")?;
 | 
			
		||||
                escape_href(&mut self.writer, &dest)?;
 | 
			
		||||
                if !title.is_empty() {
 | 
			
		||||
                    self.write("\" title=\"")?;
 | 
			
		||||
                    escape_html(&mut self.writer, &title)?;
 | 
			
		||||
                }
 | 
			
		||||
                self.write("\">")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Link(_link_type, dest, title) => {
 | 
			
		||||
                self.write("<a href=\"")?;
 | 
			
		||||
                escape_href(&mut self.writer, &dest)?;
 | 
			
		||||
                if !title.is_empty() {
 | 
			
		||||
                    self.write("\" title=\"")?;
 | 
			
		||||
                    escape_html(&mut self.writer, &title)?;
 | 
			
		||||
                }
 | 
			
		||||
                self.write("\">")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Image(_link_type, dest, title) => {
 | 
			
		||||
                self.write("<img src=\"")?;
 | 
			
		||||
                escape_href(&mut self.writer, &dest)?;
 | 
			
		||||
                self.write("\" alt=\"")?;
 | 
			
		||||
                self.raw_text()?;
 | 
			
		||||
                if !title.is_empty() {
 | 
			
		||||
                    self.write("\" title=\"")?;
 | 
			
		||||
                    escape_html(&mut self.writer, &title)?;
 | 
			
		||||
                }
 | 
			
		||||
                self.write("\" />")
 | 
			
		||||
            }
 | 
			
		||||
            Tag::FootnoteDefinition(name) => {
 | 
			
		||||
                if self.end_newline {
 | 
			
		||||
                    self.write("<div class=\"footnote-definition\" id=\"")?;
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.write("\n<div class=\"footnote-definition\" id=\"")?;
 | 
			
		||||
                }
 | 
			
		||||
                escape_html(&mut self.writer, &name)?;
 | 
			
		||||
                self.write("\"><sup class=\"footnote-definition-label\">")?;
 | 
			
		||||
                let len = self.numbers.len() + 1;
 | 
			
		||||
                let number = *self.numbers.entry(name).or_insert(len);
 | 
			
		||||
                write!(&mut self.writer, "{}", number)?;
 | 
			
		||||
                self.write("</sup>")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn end_tag(&mut self, tag: Tag) -> io::Result<()> {
 | 
			
		||||
        match tag {
 | 
			
		||||
            Tag::Paragraph => {
 | 
			
		||||
                self.write("</p>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Heading(level, _id, _classes) => {
 | 
			
		||||
                self.write("</")?;
 | 
			
		||||
                write!(&mut self.writer, "{}", level)?;
 | 
			
		||||
                self.write(">\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Table(_) => {
 | 
			
		||||
                self.write("</tbody></table>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableHead => {
 | 
			
		||||
                self.write("</tr></thead><tbody>\n")?;
 | 
			
		||||
                self.table_state = TableState::Body;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableRow => {
 | 
			
		||||
                self.write("</tr>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::TableCell => {
 | 
			
		||||
                match self.table_state {
 | 
			
		||||
                    TableState::Head => {
 | 
			
		||||
                        self.write("</th>")?;
 | 
			
		||||
                    }
 | 
			
		||||
                    TableState::Body => {
 | 
			
		||||
                        self.write("</td>")?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                self.table_cell_index += 1;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::BlockQuote => {
 | 
			
		||||
                self.write("</blockquote>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::CodeBlock(_) => {
 | 
			
		||||
                self.write("</code></pre>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::List(Some(_)) => {
 | 
			
		||||
                self.write("</ol>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::List(None) => {
 | 
			
		||||
                self.write("</ul>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Item => {
 | 
			
		||||
                self.write("</li>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Emphasis => {
 | 
			
		||||
                self.write("</em>")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Strong => {
 | 
			
		||||
                self.write("</strong>")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Strikethrough => {
 | 
			
		||||
                self.write("</del>")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Link(_, _, _) => {
 | 
			
		||||
                self.write("</a>")?;
 | 
			
		||||
            }
 | 
			
		||||
            Tag::Image(_, _, _) => (), // shouldn't happen, handled in start
 | 
			
		||||
            Tag::FootnoteDefinition(_) => {
 | 
			
		||||
                self.write("</div>\n")?;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // run raw text, consuming end tag
 | 
			
		||||
    fn raw_text(&mut self) -> io::Result<()> {
 | 
			
		||||
        let mut nest = 0;
 | 
			
		||||
        while let Some(event) = self.iter.next() {
 | 
			
		||||
            match event {
 | 
			
		||||
                Start(_) => nest += 1,
 | 
			
		||||
                End(_) => {
 | 
			
		||||
                    if nest == 0 {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    nest -= 1;
 | 
			
		||||
                }
 | 
			
		||||
                Html(text) | Code(text) | Text(text) => {
 | 
			
		||||
                    escape_html(&mut self.writer, &text)?;
 | 
			
		||||
                    self.end_newline = text.ends_with('\n');
 | 
			
		||||
                }
 | 
			
		||||
                SoftBreak | HardBreak | Rule => {
 | 
			
		||||
                    self.write(" ")?;
 | 
			
		||||
                }
 | 
			
		||||
                FootnoteReference(name) => {
 | 
			
		||||
                    let len = self.numbers.len() + 1;
 | 
			
		||||
                    let number = *self.numbers.entry(name).or_insert(len);
 | 
			
		||||
                    write!(&mut self.writer, "[{}]", number)?;
 | 
			
		||||
                }
 | 
			
		||||
                TaskListMarker(true) => self.write("[x]")?,
 | 
			
		||||
                TaskListMarker(false) => self.write("[ ]")?,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and
 | 
			
		||||
/// push it to a `String`.
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// use pulldown_cmark::{html, Parser};
 | 
			
		||||
///
 | 
			
		||||
/// let markdown_str = r#"
 | 
			
		||||
/// hello
 | 
			
		||||
/// =====
 | 
			
		||||
///
 | 
			
		||||
/// * alpha
 | 
			
		||||
/// * beta
 | 
			
		||||
/// "#;
 | 
			
		||||
/// let parser = Parser::new(markdown_str);
 | 
			
		||||
///
 | 
			
		||||
/// let mut html_buf = String::new();
 | 
			
		||||
/// html::push_html(&mut html_buf, parser);
 | 
			
		||||
///
 | 
			
		||||
/// assert_eq!(html_buf, r#"<h1>hello</h1>
 | 
			
		||||
/// <ul>
 | 
			
		||||
/// <li>alpha</li>
 | 
			
		||||
/// <li>beta</li>
 | 
			
		||||
/// </ul>
 | 
			
		||||
/// "#);
 | 
			
		||||
/// ```
 | 
			
		||||
pub fn push_html<'a, I>(s: &mut String, iter: I)
 | 
			
		||||
where
 | 
			
		||||
    I: Iterator<Item = Event<'a>>,
 | 
			
		||||
{
 | 
			
		||||
    HtmlWriter::new(iter, s).run().unwrap();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								crates/treehouse/src/html/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								crates/treehouse/src/html/mod.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
mod markdown;
 | 
			
		||||
pub mod tree;
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
use treehouse_format::{ast::Branch, pull::BranchKind};
 | 
			
		||||
 | 
			
		||||
use super::markdown;
 | 
			
		||||
 | 
			
		||||
pub fn branch_to_html(s: &mut String, branch: &Branch, source: &str) {
 | 
			
		||||
    s.push_str("<li>");
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +12,18 @@ pub fn branch_to_html(s: &mut String, branch: &Branch, source: &str) {
 | 
			
		|||
            });
 | 
			
		||||
            s.push_str("<summary>");
 | 
			
		||||
        }
 | 
			
		||||
        s.push_str(&source[branch.content.clone()]);
 | 
			
		||||
 | 
			
		||||
        let raw_block_content = &source[branch.content.clone()];
 | 
			
		||||
        let mut unindented_block_content = String::with_capacity(raw_block_content.len());
 | 
			
		||||
        let indent = " ".repeat(branch.indent_level);
 | 
			
		||||
        for line in raw_block_content.lines() {
 | 
			
		||||
            unindented_block_content.push_str(line.strip_prefix(&indent).unwrap_or(line));
 | 
			
		||||
            unindented_block_content.push('\n');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let markdown_parser = pulldown_cmark::Parser::new(&unindented_block_content);
 | 
			
		||||
        markdown::push_html(s, markdown_parser);
 | 
			
		||||
 | 
			
		||||
        if !branch.children.is_empty() {
 | 
			
		||||
            s.push_str("</summary>");
 | 
			
		||||
            branches_to_html(s, &branch.children, source);
 | 
			
		||||
| 
						 | 
				
			
			@ -1,46 +1,33 @@
 | 
			
		|||
mod tree_html;
 | 
			
		||||
mod html;
 | 
			
		||||
 | 
			
		||||
use axum::Router;
 | 
			
		||||
use codespan_reporting::{
 | 
			
		||||
    diagnostic::{Diagnostic, Label, LabelStyle, Severity},
 | 
			
		||||
    files::SimpleFile,
 | 
			
		||||
    term::termcolor::{ColorChoice, StandardStream},
 | 
			
		||||
};
 | 
			
		||||
use tree_html::branches_to_html;
 | 
			
		||||
use treehouse_format::{
 | 
			
		||||
    ast::{Branch, Roots},
 | 
			
		||||
    pull::Parser,
 | 
			
		||||
};
 | 
			
		||||
use copy_dir::copy_dir;
 | 
			
		||||
use handlebars::Handlebars;
 | 
			
		||||
use html::tree::branches_to_html;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use tower_http::services::ServeDir;
 | 
			
		||||
use tower_livereload::LiveReloadLayer;
 | 
			
		||||
use treehouse_format::{ast::Roots, pull::Parser};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, thiserror::Error)]
 | 
			
		||||
enum Error {
 | 
			
		||||
    #[error("I/O error: {0}")]
 | 
			
		||||
    Io(#[from] std::io::Error),
 | 
			
		||||
 | 
			
		||||
    #[error("treehouse parsing error: {0}")]
 | 
			
		||||
    Parse(#[from] treehouse_format::ParseError),
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct TemplateData {
 | 
			
		||||
    pub tree: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn print_branch(branch: &Branch, source: &str) {
 | 
			
		||||
    fn inner(branch: &Branch, source: &str, indent_level: usize) {
 | 
			
		||||
        for _ in 0..indent_level {
 | 
			
		||||
            print!("  ");
 | 
			
		||||
        }
 | 
			
		||||
        println!(
 | 
			
		||||
            "{} {:?}",
 | 
			
		||||
            branch.kind.char(),
 | 
			
		||||
            &source[branch.content.clone()]
 | 
			
		||||
        );
 | 
			
		||||
        for child in &branch.children {
 | 
			
		||||
            inner(child, source, indent_level + 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    inner(branch, source, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
fn regenerate() -> anyhow::Result<()> {
 | 
			
		||||
    let _ = std::fs::remove_dir_all("target/site");
 | 
			
		||||
    std::fs::create_dir_all("target/site")?;
 | 
			
		||||
 | 
			
		||||
    copy_dir("static", "target/site/static")?;
 | 
			
		||||
 | 
			
		||||
    let mut handlebars = Handlebars::new();
 | 
			
		||||
    handlebars.register_template_file("template/index.hbs", "template/index.hbs")?;
 | 
			
		||||
 | 
			
		||||
    let root_file = std::fs::read_to_string("content/tree/root.tree")?;
 | 
			
		||||
    let parse_result = Roots::parse(&mut Parser {
 | 
			
		||||
        input: &root_file,
 | 
			
		||||
| 
						 | 
				
			
			@ -49,13 +36,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		|||
 | 
			
		||||
    match parse_result {
 | 
			
		||||
        Ok(roots) => {
 | 
			
		||||
            let mut html = String::from("<!DOCTYPE html><html><head></head><body>");
 | 
			
		||||
            for root in &roots.branches {
 | 
			
		||||
                print_branch(root, &root_file);
 | 
			
		||||
            }
 | 
			
		||||
            branches_to_html(&mut html, &roots.branches, &root_file);
 | 
			
		||||
            std::fs::write("target/site/index.html", &html)?;
 | 
			
		||||
            html.push_str("</body></html>")
 | 
			
		||||
            let mut tree = String::new();
 | 
			
		||||
            branches_to_html(&mut tree, &roots.branches, &root_file);
 | 
			
		||||
 | 
			
		||||
            let index_html = handlebars.render("template/index.hbs", &TemplateData { tree })?;
 | 
			
		||||
 | 
			
		||||
            std::fs::write("target/site/index.html", index_html)?;
 | 
			
		||||
        }
 | 
			
		||||
        Err(error) => {
 | 
			
		||||
            let writer = StandardStream::stderr(ColorChoice::Auto);
 | 
			
		||||
| 
						 | 
				
			
			@ -77,29 +63,43 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // let mut parser = treehouse_format::Parser {
 | 
			
		||||
    //     input: &root_file,
 | 
			
		||||
    //     position: 0,
 | 
			
		||||
    // };
 | 
			
		||||
    // let mut generator = HtmlGenerator::default();
 | 
			
		||||
    // while let Some(branch) = parser.next_branch()? {
 | 
			
		||||
    //     for _ in 0..branch.indent_level {
 | 
			
		||||
    //         print!(" ");
 | 
			
		||||
    //     }
 | 
			
		||||
    //     println!(
 | 
			
		||||
    //         "{} {:?}",
 | 
			
		||||
    //         branch.kind.char(),
 | 
			
		||||
    //         &root_file[branch.content.clone()]
 | 
			
		||||
    //     );
 | 
			
		||||
    //     generator.add(&root_file, &branch);
 | 
			
		||||
    // }
 | 
			
		||||
    // std::fs::write(
 | 
			
		||||
    //     "target/site/index.html",
 | 
			
		||||
    //     format!(
 | 
			
		||||
    //         "<!DOCTYPE html><html><head></head><body>{}</body></html>",
 | 
			
		||||
    //         generator.finish()
 | 
			
		||||
    //     ),
 | 
			
		||||
    // )?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn regenerate_or_report_error() {
 | 
			
		||||
    eprintln!("regenerating");
 | 
			
		||||
 | 
			
		||||
    match regenerate() {
 | 
			
		||||
        Ok(_) => (),
 | 
			
		||||
        Err(error) => eprintln!("error: {error:?}"),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn web_server() -> anyhow::Result<()> {
 | 
			
		||||
    let app = Router::new().nest_service("/", ServeDir::new("target/site"));
 | 
			
		||||
 | 
			
		||||
    #[cfg(debug_assertions)]
 | 
			
		||||
    let app = app.layer(LiveReloadLayer::new());
 | 
			
		||||
 | 
			
		||||
    Ok(axum::Server::bind(&([0, 0, 0, 0], 8080).into())
 | 
			
		||||
        .serve(app.into_make_service())
 | 
			
		||||
        .await?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn fallible_main() -> anyhow::Result<()> {
 | 
			
		||||
    regenerate_or_report_error();
 | 
			
		||||
 | 
			
		||||
    web_server().await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    match fallible_main().await {
 | 
			
		||||
        Ok(_) => (),
 | 
			
		||||
        Err(error) => eprintln!("fatal: {error:?}"),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										75
									
								
								static/css/main.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								static/css/main.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
/* Choose more pretty colors than vanilla HTML */
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
    background-color: rgb(255, 253, 246);
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Set up fonts */
 | 
			
		||||
 | 
			
		||||
body,
 | 
			
		||||
pre,
 | 
			
		||||
code {
 | 
			
		||||
    font-family: 'RecVar', sans-serif;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:root {
 | 
			
		||||
    --recursive-mono: 0.0;
 | 
			
		||||
    --recursive-casl: 1.0;
 | 
			
		||||
    --recursive-wght: 400;
 | 
			
		||||
    --recursive-slnt: -2.0;
 | 
			
		||||
    --recursive-crsv: 0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
*,
 | 
			
		||||
*:before,
 | 
			
		||||
*:after {
 | 
			
		||||
    font-variation-settings:
 | 
			
		||||
        "MONO" var(--recursive-mono),
 | 
			
		||||
        "CASL" var(--recursive-casl),
 | 
			
		||||
        "wght" var(--recursive-wght),
 | 
			
		||||
        "slnt" var(--recursive-slnt),
 | 
			
		||||
        "CRSV" var(--recursive-crsv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
    --recursive-slnt: 0.0;
 | 
			
		||||
    --recursive-casl: 0.0;
 | 
			
		||||
    --recursive-crsv: 0.0;
 | 
			
		||||
    --recursive-wght: 900;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pre,
 | 
			
		||||
code {
 | 
			
		||||
    --recursive-mono: 1.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Make the tree have + and - instead of the default details/summary arrow */
 | 
			
		||||
 | 
			
		||||
.tree details>summary {
 | 
			
		||||
    list-style: none;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tree li {
 | 
			
		||||
    list-style: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tree details::before {
 | 
			
		||||
    content: '+';
 | 
			
		||||
    opacity: 0.5;
 | 
			
		||||
 | 
			
		||||
    padding-right: 8px;
 | 
			
		||||
    vertical-align: text-bottom;
 | 
			
		||||
 | 
			
		||||
    --recursive-mono: 1.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tree details[open]::before {
 | 
			
		||||
    content: '-';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tree details *:first-child {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_english_basic.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_english_basic.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_1.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_1.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_1_punc.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_1_punc.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_ext.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_latin_ext.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_remaining.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_remaining.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_vietnamese.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/font/Recursive_VF_1.085--subset_range_vietnamese.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										59
									
								
								static/font/font.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								static/font/font.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
/* The bare minimum English subset, plus copyright & arrows (← ↑ → ↓) & quotes (“ ” ‘ ’) & bullet (•) */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_english_basic.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+0020-007F, U+00A9, U+2190-2193, U+2018, U+2019, U+201C, U+201D, U+2022;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* unicode latin-1 letters, basic european diacritics */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_latin_1.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+00C0-00FF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* unicode latin-1, punc/symbols & arrows (↔ ↕ ↖ ↗ ↘ ↙) */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_latin_1_punc.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+00A0-00A8, U+00AA-00BF, U+2194-2199;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* unicode latin A extended */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_latin_ext.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+0100-017F;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* unicodes for vietnamese */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_vietnamese.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* remaining Unicodes */
 | 
			
		||||
@font-face {
 | 
			
		||||
    font-family: 'RecVar';
 | 
			
		||||
    font-style: oblique 0deg 15deg;
 | 
			
		||||
    font-weight: 300 1000;
 | 
			
		||||
    font-display: swap;
 | 
			
		||||
    src: url('./Recursive_VF_1.085--subset_range_remaining.woff2') format('woff2');
 | 
			
		||||
    unicode-range: U+2007, U+2008, U+2009, U+200A, U+200B, U+D, U+2010, U+2012, U+2013, U+2014, U+2015, U+201A, U+201E, U+2020, U+2021, U+2026, U+2030, U+2032, U+2033, U+2039, U+203A, U+203E, U+2044, U+2052, U+2070, U+2074, U+2075, U+2076, U+2077, U+2078, U+2079, U+207B, U+2080, U+2081, U+2082, U+2083, U+2084, U+2085, U+2086, U+2087, U+2088, U+2089, U+20A1, U+20A6, U+20A8, U+20A9, U+20AA, U+20AC, U+20AD, U+20B1, U+20B2, U+20B4, U+20B5, U+20B8, U+20B9, U+20BA, U+20BC, U+20BD, U+20BF, U+F8FF, U+2113, U+2116, U+2122, U+2126, U+212E, U+E132, U+E133, U+2153, U+2154, U+215B, U+215C, U+215D, U+215E, U+18F, U+192, U+19D, U+1C4, U+1C5, U+1C6, U+1C7, U+1C8, U+1C9, U+1CA, U+1CB, U+1CC, U+1E6, U+1E7, U+1EA, U+1EB, U+1F1, U+1F2, U+1F3, U+1FA, U+1FB, U+1FC, U+1FD, U+1FE, U+1FF, U+200, U+201, U+202, U+203, U+204, U+205, U+206, U+207, U+208, U+209, U+20A, U+20B, U+20C, U+20D, U+20E, U+20F, U+210, U+211, U+212, U+213, U+214, U+215, U+216, U+217, U+218, U+219, U+21A, U+21B, U+2215, U+2219, U+221E, U+221A, U+22A, U+22B, U+22C, U+22D, U+222B, U+230, U+231, U+232, U+233, U+2236, U+237, U+2248, U+259, U+2260, U+2261, U+2264, U+2265, U+272, U+2B9, U+2BA, U+2BB, U+2BC, U+2BE, U+2BF, U+2C6, U+2C7, U+2C8, U+2C9, U+2CA, U+2CB, U+2D8, U+2D9, U+2DA, U+2DB, U+2DC, U+2DD, U+300, U+301, U+FB02, U+FB03, U+302, U+303, U+304, U+FB01, U+306, U+307, U+308, U+309, U+30A, U+30B, U+30C, U+30F, U+311, U+312, U+315, U+31B, U+2202, U+323, U+324, U+325, U+326, U+327, U+328, U+329, U+2205, U+32E, U+2206, U+331, U+335, U+220F, U+2211, U+2212, U+391, U+392, U+393, U+394, U+398, U+39B, U+39C, U+39D, U+3A0, U+3A6, U+3B1, U+3B2, U+3B3, U+3B4, U+3B8, U+3BB, U+3BC, U+3BD, U+3C0, U+3C6, U+25A0, U+25A1, U+25B2, U+25B3, U+25B6, U+25B7, U+25BC, U+25BD, U+25C0, U+25C1, U+25C6, U+25C7, U+25CA, U+1E08, U+1E09, U+1E0C, U+1E0D, U+1E0E, U+1E0F, U+2610, U+2611, U+1E14, U+1E15, U+1E16, U+1E17, U+1E1C, U+1E1D, U+1E20, U+1E21, U+1E24, U+1E25, U+1E2A, U+1E2B, U+1E2E, U+1E2F, U+1E36, U+1E37, U+1E3A, U+1E3B, U+E3F, U+1E42, U+1E43, U+1E44, U+1E45, U+1E46, U+1E47, U+1E48, U+1E49, U+1E4C, U+1E4D, U+1E4E, U+1E4F, U+1E50, U+1E51, U+1E52, U+1E53, U+1E5A, U+1E5B, U+1E5E, U+1E5F, U+1E60, U+2661, U+1E61, U+1E62, U+1E63, U+1E64, U+1E65, U+1E66, U+1E67, U+1E68, U+1E69, U+2665, U+1E6C, U+1E6D, U+1E6E, U+1E6F, U+1E78, U+1E79, U+1E7A, U+1E7B, U+1E80, U+1E81, U+1E82, U+1E83, U+1E84, U+1E85, U+1E8E, U+1E8F, U+1E92, U+1E93, U+1E97, U+1E9E, U+2713, U+27E8, U+27E9;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								static/js/usability.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								static/js/usability.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
// Bits and pieces to make vanilla HTML just a bit more usable.
 | 
			
		||||
 | 
			
		||||
// We want to let the user have a selection on collapsible blocks without collapsing them when
 | 
			
		||||
// the user finishes marking their selection.
 | 
			
		||||
document.addEventListener("click", event => {
 | 
			
		||||
    console.log(getSelection());
 | 
			
		||||
    if (getSelection().type == "Range") {
 | 
			
		||||
        event.preventDefault();
 | 
			
		||||
    }
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -3,12 +3,20 @@
 | 
			
		|||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
 | 
			
		||||
    <title>{{ config.user.title }}</title>
 | 
			
		||||
    <link rel="stylesheet" href="{{ local 'static/main.css' }}">
 | 
			
		||||
 | 
			
		||||
    <link rel="stylesheet" href="{{ site }}/static/css/main.css">
 | 
			
		||||
    <link rel="stylesheet" href="{{ site }}/static/font/font.css">
 | 
			
		||||
 | 
			
		||||
    <script type="module" src="{{ site }}/static/js/usability.js"></script>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    {{{ tree }}}
 | 
			
		||||
    <main class="tree">
 | 
			
		||||
        {{{ tree }}}
 | 
			
		||||
    </main>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue