diff --git a/content/about-treehouse.tree b/content/about-treehouse.tree index 2644613..e060e50 100644 --- a/content/about-treehouse.tree +++ b/content/about-treehouse.tree @@ -19,6 +19,13 @@ % id = "01H89RFHCQDC73N0MD2ZF629MF" - wouldn't you make yourself at home? + % id = "01H8VWEFHZA94G0DNPD79YV535" + + … + + % content.link = "about/emoji" + id = "01H8VWEFHZ7Z71WJ347WFMC9YT" + + by the way did you know this website has custom emojis? and quite a lot of them, too + % id = "01H89RFHCQKAPHSGCDN832QRMD" + ### the treehouse is a statement of artistic expression @@ -125,6 +132,7 @@ % id = "01H89RFHCQ48R7BCZV8JWPVFCY" + have I invented something new here? + % id = "01H8VWEFJ1BGA21FBVHC4TFF3V" - the "Choose Your Own Poem" lol % id = "01H89RFHCQAXJ0ST31TP1A104V" diff --git a/content/about/emoji.tree b/content/about/emoji.tree new file mode 100644 index 0000000..9fbcd77 --- /dev/null +++ b/content/about/emoji.tree @@ -0,0 +1,8 @@ +% id = "01H8VWE6M29SZXJDDAX50K99V7" +- ### the emojipedia + + % id = "01H8VWE6M2EN8YBGPT1RTWE638" + - (no, not that one.) + + % id = "emoji/hueh" + - :hueh: - stolen from the Hat in Time Discord server diff --git a/content/index.tree b/content/index.tree index c0e61c7..c406604 100644 --- a/content/index.tree +++ b/content/index.tree @@ -4,31 +4,32 @@ % id = "01H8V556P1PND8DQ73XBTZZJH7" - welcome! make yourself at home + % id = "01H8VWEHX501SNYQTE61WX7YJC" - _"owo, what's this?"_ % id = "about" - content.link = "about" + content.link = "about" + ## about me % id = "about-treehouse" - content.link = "about-treehouse" + content.link = "about-treehouse" + ## about this % id = "01H8V556P1GRAA3717VH3QJFMV" - hobby corners % id = "programming" - content.link = "2023-08-20-under-construction" + content.link = "2023-08-20-under-construction" + ## programming % id = "music" - content.link = "2023-08-20-under-construction" + content.link = "2023-08-20-under-construction" + ## music % id = "games" - content.link = "2023-08-20-under-construction" + content.link = "2023-08-20-under-construction" + ## games % id = "3d-printing" - content.link = "2023-08-20-under-construction" + content.link = "2023-08-20-under-construction" + ## 3D printing diff --git a/crates/treehouse/src/config.rs b/crates/treehouse/src/config.rs index d92a401..01cf00d 100644 --- a/crates/treehouse/src/config.rs +++ b/crates/treehouse/src/config.rs @@ -15,6 +15,12 @@ pub struct Config { /// Links exported to Markdown for use with reference syntax `[text][def:key]`. pub defs: HashMap, + + /// Overrides for emoji filenames. Useful for setting up aliases. + /// + /// On top of this, emojis are autodiscovered by walking the `static/emoji` directory. + #[serde(default)] + pub emoji: HashMap, } impl Config { diff --git a/crates/treehouse/src/html/markdown.rs b/crates/treehouse/src/html/markdown.rs index 019566a..29700ae 100644 --- a/crates/treehouse/src/html/markdown.rs +++ b/crates/treehouse/src/html/markdown.rs @@ -30,12 +30,18 @@ use pulldown_cmark::escape::{escape_href, escape_html, StrWrite}; use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Tag}; use pulldown_cmark::{CowStr, Event::*}; +use crate::config::Config; +use crate::state::Treehouse; + enum TableState { Head, Body, } struct HtmlWriter<'a, I, W> { + treehouse: &'a Treehouse, + config: &'a Config, + /// Iterator supplying events. iter: I, @@ -49,6 +55,8 @@ struct HtmlWriter<'a, I, W> { table_alignments: Vec, table_cell_index: usize, numbers: HashMap, usize>, + + in_code_block: bool, } impl<'a, I, W> HtmlWriter<'a, I, W> @@ -56,8 +64,10 @@ where I: Iterator>, W: StrWrite, { - fn new(iter: I, writer: W) -> Self { + fn new(treehouse: &'a Treehouse, config: &'a Config, iter: I, writer: W) -> Self { Self { + treehouse, + config, iter, writer, end_newline: true, @@ -65,6 +75,7 @@ where table_alignments: vec![], table_cell_index: 0, numbers: HashMap::new(), + in_code_block: false, } } @@ -95,7 +106,7 @@ where self.end_tag(tag)?; } Text(text) => { - escape_html(&mut self.writer, &text)?; + self.run_text(&text)?; self.end_newline = text.ends_with('\n'); } Code(text) => { @@ -211,6 +222,7 @@ where } } Tag::CodeBlock(info) => { + self.in_code_block = true; if !self.end_newline { self.write_newline()?; } @@ -342,6 +354,7 @@ where } Tag::CodeBlock(_) => { self.write("\n")?; + self.in_code_block = false; } Tag::List(Some(_)) => { self.write("\n")?; @@ -372,6 +385,107 @@ where Ok(()) } + fn run_text(&mut self, text: &str) -> io::Result<()> { + struct EmojiParser<'a> { + text: &'a str, + position: usize, + } + + enum Token<'a> { + Text(&'a str), + Emoji(&'a str), + } + + impl<'a> EmojiParser<'a> { + fn current(&self) -> Option { + self.text[self.position..].chars().next() + } + + fn next_token(&mut self) -> Option> { + match self.current() { + Some(':') => { + let text_start = self.position; + self.position += 1; + if self.current().is_some_and(|c| c.is_alphabetic()) { + let name_start = self.position; + while let Some(c) = self.current() { + if c.is_alphanumeric() || c == '_' { + self.position += c.len_utf8(); + } else { + break; + } + } + if self.current() == Some(':') { + let name_end = self.position; + self.position += 1; + Some(Token::Emoji(&self.text[name_start..name_end])) + } else { + Some(Token::Text(&self.text[text_start..self.position])) + } + } else { + Some(Token::Text(&self.text[text_start..self.position])) + } + } + Some(_) => { + let start = self.position; + while let Some(c) = self.current() { + if c == ':' { + break; + } else { + self.position += c.len_utf8(); + } + } + let end = self.position; + Some(Token::Text(&self.text[start..end])) + } + None => None, + } + } + } + + if self.in_code_block { + escape_html(&mut self.writer, text)?; + } else { + let mut parser = EmojiParser { text, position: 0 }; + while let Some(token) = parser.next_token() { + match token { + Token::Text(text) => escape_html(&mut self.writer, text)?, + Token::Emoji(name) => { + if let Some(filename) = self.config.emoji.get(name) { + let branch_id = self + .treehouse + .branches_by_named_id + .get(&format!("emoji/{name}")) + .copied(); + if let Some(branch) = branch_id.map(|id| self.treehouse.tree.branch(id)) + { + self.writer.write_str("")?; + } + self.writer.write_str("")?; + if branch_id.is_some() { + self.writer.write_str("")?; + } + } else { + self.writer.write_str(":")?; + escape_html(&mut self.writer, name)?; + self.writer.write_str(":")?; + } + } + } + } + } + + Ok(()) + } + // run raw text, consuming end tag fn raw_text(&mut self) -> io::Result<()> { let mut nest = 0; @@ -431,9 +545,9 @@ where /// /// "#); /// ``` -pub fn push_html<'a, I>(s: &mut String, iter: I) +pub fn push_html<'a, I>(s: &mut String, treehouse: &'a Treehouse, config: &'a Config, iter: I) where I: Iterator>, { - HtmlWriter::new(iter, s).run().unwrap(); + HtmlWriter::new(treehouse, config, iter, s).run().unwrap(); } diff --git a/crates/treehouse/src/html/tree.rs b/crates/treehouse/src/html/tree.rs index 44caa32..50f7abf 100644 --- a/crates/treehouse/src/html/tree.rs +++ b/crates/treehouse/src/html/tree.rs @@ -111,7 +111,7 @@ pub fn branch_to_html( }, Some(broken_link_callback), ); - markdown::push_html(s, markdown_parser); + markdown::push_html(s, treehouse, config, markdown_parser); if let Content::Link(link) = &branch.attributes.content { write!( diff --git a/static/css/main.css b/static/css/main.css index 19d4ec7..605dcb0 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -237,3 +237,10 @@ nav .logo { opacity: 100%; color: var(--text-color); } + +/* Style emojis to be readable */ + +img.emoji { + max-height: 1.375em; + vertical-align: bottom; +} diff --git a/static/emoji/hueh.png b/static/emoji/hueh.png new file mode 100755 index 0000000..55677a8 Binary files /dev/null and b/static/emoji/hueh.png differ diff --git a/static/js/tree.js b/static/js/tree.js index f3bcb87..1afad4a 100644 --- a/static/js/tree.js +++ b/static/js/tree.js @@ -135,6 +135,7 @@ async function navigateToBranch(fragment) { let [_root, ...path] = fullPath; if (path !== undefined) { let isNotAtIndexHtml = window.location.pathname != "/index.html"; + let lastBranch = null; for (let linked of path) { let branch = LinkedBranch.byLink.get(linked); @@ -144,7 +145,10 @@ async function navigateToBranch(fragment) { } await branch.loadTree("navigateToBranch"); - branch.details.open = true; + lastBranch = branch; + } + if (lastBranch != null) { + expandDetailsRecursively(lastBranch.details); } window.location.hash = window.location.hash; } diff --git a/treehouse.toml b/treehouse.toml index 8d4fb5a..a5d026a 100644 --- a/treehouse.toml +++ b/treehouse.toml @@ -7,3 +7,6 @@ author = "liquidex" [defs] "stitchkit/repo" = "https://github.com/liquidev/stitchkit" "dawd3/repo" = "https://github.com/liquidev/dawd3" + +[emoji] +hueh = "hueh.png"