From 36705e7c1e7b858feb2a5d6581b222edac087d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=AA=E3=82=AD=E8=90=8C?= Date: Thu, 10 Jul 2025 16:50:41 +0200 Subject: [PATCH] adding document mode I've been thinking a lot about the treehouse and I feel like it's time to say goodbye to the tree format. --- Justfile | 2 +- content/{about.tree => about/v2.tree} | 0 content/index.dj | 37 +++ content/index.tree | 4 - src/config.rs | 2 + src/generate.rs | 17 +- src/generate/atom.rs | 92 +------ src/generate/doc.rs | 242 +++++++++++++++++ src/html/djot.rs | 50 +--- src/sources.rs | 24 +- src/state.rs | 4 +- src/tree.rs | 1 + src/tree/attributes.rs | 6 +- src/tree/feed.rs | 94 +++++++ static/character/riki/sitting.png | Bin 0 -> 62407 bytes static/css/base.css | 14 +- static/css/doc.css | 84 ++++++ static/css/main.css | 372 +++++++++++++++++++------- static/css/page/index.css | 95 ------- static/css/tree.css | 19 +- template/_doc.hbs | 34 ++- template/_tree.hbs | 33 ++- template/components/_doc.hbs | 17 ++ template/components/_feed.hbs | 20 ++ template/components/_footer.hbs | 3 + template/components/_head.hbs | 19 +- template/components/_jar.hbs | 16 ++ template/components/_nav.hbs | 5 - template/components/_sidebar.hbs | 17 ++ template/components/_tree.hbs | 23 +- treehouse.toml | 3 +- 31 files changed, 940 insertions(+), 409 deletions(-) rename content/{about.tree => about/v2.tree} (100%) create mode 100644 content/index.dj create mode 100644 src/generate/doc.rs create mode 100644 src/tree/feed.rs create mode 100644 static/character/riki/sitting.png create mode 100644 static/css/doc.css create mode 100644 template/components/_doc.hbs create mode 100644 template/components/_feed.hbs create mode 100644 template/components/_jar.hbs delete mode 100644 template/components/_nav.hbs create mode 100644 template/components/_sidebar.hbs diff --git a/Justfile b/Justfile index 7cc2557..e9915ae 100644 --- a/Justfile +++ b/Justfile @@ -1,7 +1,7 @@ port := "8080" serve: - cargo watch -- cargo run -- serve --port {{port}} + RUST_BACKTRACE=1 cargo watch -- cargo run -- serve --port {{port}} fix: cargo run -- fix-all --apply diff --git a/content/about.tree b/content/about/v2.tree similarity index 100% rename from content/about.tree rename to content/about/v2.tree diff --git a/content/index.dj b/content/index.dj new file mode 100644 index 0000000..2fdb873 --- /dev/null +++ b/content/index.dj @@ -0,0 +1,37 @@ +title = "riki's house" +include_feed = { name = "new", title = "Blog" } + ++++ + +My name's *riki moe*, or *リキ萌*! + +I'm a _he/him_-type cat [furry][page:philosophy/furry] doing various things with computers. + +I work on game optimization tools at [[CD PROJEKT RED](https://cdprojektred.com) :rarog:]{.nowrap} but that's just the tip of the iceberg! +After hours, I program [websites](/), [compilers][def:stitchkit/repo], [audio][def:dawd3/repo], and other fun things. + +Such as [*video games.*][page:games] +Like, I probably wouldn't be in the industry if I didn't like them.\ +My personal favourites are [:nap: [DELTARUNE](https://deltarune.com)]{.nowrap}, [:bean: [Animal Well](https://www.animalwell.net/)]{.nowrap}, [:fox: [TUNIC](https://tunicgame.com/)]{.nowrap}, [:hueh: [A Hat in Time](https://hatintime.com)]{.nowrap}, and [:propane: [Noita](https://noitagame.com/)]{.nowrap}. +But also many many more, because I'm really indecisive. + +Or [*music.*][page:music] +I [listen to a lot of it.][def:social/bandcamp] +And I mean, [_a lot_.][def:social/listenbrainz] +A metric fuck tonne.\ +I'm a huge fan of electronic genres, but also jazz and (alternative) rock from time to time.\ +I listen to [_Songs About My Cats_](https://venetiansnares.bandcamp.com/album/songs-about-my-cats) while coding.\ +My favourite artists are [C418](https://c418.bandcamp.com/album/excursions), [The Flashbulb](https://theflashbulb.bandcamp.com/album/kirlian-selections), [Aphex Twin](https://aphextwin.bandcamp.com/album/drukqs), [Squarepusher](https://squarepusher.bandcamp.com/album/ultravisitor), and [False Noise](https://upscalehq.bandcamp.com/album/floral-strobe). + +I kinda also [make music sometimes](https://daknus.bandcamp.com) when I feel like it. + +I also drew a bit of dawing (...do a bit of drawing), as evidenced by the floofee on this page. + +If all that sounds like an interesting bunch of words... + +- feel free to email me: `hi` at this domain!! +- or add me on Discord---the nickname's *rikimoe*. + +I like to think I'm pretty amicable in person but I'm uh, also really socially awkward...!\ +~Please excuse any social awkwardness that may ensue from you contacting me.\ +Or me contacting you.~ diff --git a/content/index.tree b/content/index.tree index 7f437f2..5e7245f 100644 --- a/content/index.tree +++ b/content/index.tree @@ -73,10 +73,6 @@ visibility = "Private" - I'd like to make some new friends! if you wanna meet me, email `hi` at this domain. -% id = "about" - content.link = "about" -+ ## [``{=html}][page:kuroneko]{.secret}me - % id = "programming" content.link = "programming" + ## ``{=html}programming diff --git a/src/config.rs b/src/config.rs index 48fe361..c55cba2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -152,6 +152,8 @@ impl Config { } pub fn page_url(&self, page: &str) -> String { + // We don't want .dj appearing in URLs, though it exists as a disambiguator in [page:] links. + let page = page.strip_suffix(".dj").unwrap_or(page); format!("{}/{}", self.site, page) } diff --git a/src/generate.rs b/src/generate.rs index 7ea0bce..3caa73e 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1,5 +1,6 @@ mod atom; mod dir_helper; +mod doc; mod include_static_helper; mod simple_template; mod tree; @@ -7,6 +8,7 @@ mod tree; use std::{ops::ControlFlow, sync::Arc}; use atom::FeedDir; +use chrono::{DateTime, Utc}; use dir_helper::DirHelper; use handlebars::{handlebars_helper, Handlebars}; use include_static_helper::IncludeStaticHelper; @@ -18,13 +20,14 @@ use crate::{ dirs::Dirs, fun::seasons::Season, generate::{ + doc::DocDir, simple_template::SimpleTemplateDir, tree::{DirIndex, TreehouseDir}, }, sources::Sources, vfs::{ - self, layered_dir, AnchoredAtExt, Cd, Content, ContentCache, Dir, DynDir, Entries, - HtmlCanonicalize, MemDir, Overlay, ToDynDir, VPath, VPathBuf, + self, layered_dir, AnchoredAtExt, Cd, Content, ContentCache, Dir, DynDir, HtmlCanonicalize, + MemDir, ToDynDir, VPath, VPathBuf, }, }; @@ -54,8 +57,10 @@ fn create_handlebars(site: &str, static_: DynDir) -> Handlebars<'static> { let mut handlebars = Handlebars::new(); handlebars_helper!(cat: |a: String, b: String| a + &b); + handlebars_helper!(iso_date: |d: DateTime| d.format("%F").to_string()); handlebars.register_helper("cat", Box::new(cat)); + handlebars.register_helper("iso_date", Box::new(iso_date)); handlebars.register_helper("asset", Box::new(DirHelper::new(site, static_.clone()))); handlebars.register_helper( "include_static", @@ -103,7 +108,13 @@ pub fn target(dirs: Arc, sources: Arc) -> DynDir { let dir_index = DirIndex::new(sources.treehouse.files_by_tree_path.keys().map(|x| &**x)); let treehouse_dir = layered_dir(&[ - TreehouseDir::new(dirs, sources.clone(), handlebars.clone(), dir_index).to_dyn(), + TreehouseDir::new(dirs.clone(), sources.clone(), handlebars.clone(), dir_index).to_dyn(), + DocDir { + sources: sources.clone(), + dirs, + handlebars: handlebars.clone(), + } + .to_dyn(), SimpleTemplateDir::new(sources.clone(), handlebars.clone()).to_dyn(), ]); diff --git a/src/generate/atom.rs b/src/generate/atom.rs index f41bb9b..1c042eb 100644 --- a/src/generate/atom.rs +++ b/src/generate/atom.rs @@ -11,7 +11,7 @@ use crate::{ html::djot::{self, resolve_link}, sources::Sources, state::FileId, - tree::SemaBranchId, + tree::{feed, SemaBranchId}, vfs::{self, Content, Dir, Entries, VPath, VPathBuf}, }; @@ -155,7 +155,7 @@ fn extract_entries(sources: &Sources, dirs: &Dirs, file_id: FileId) -> Vec Vec, - link: Option, -} - -fn parse_entry( - sources: &Sources, - dirs: &Dirs, - file_id: FileId, - parser: jotdown::Parser, -) -> ParsedEntry { - let mut parser = parser.into_offset_iter(); - while let Some((event, span)) = parser.next() { - if let jotdown::Event::Start(jotdown::Container::Heading { .. }, _attrs) = &event { - let mut events = vec![(event, span)]; - for (event, span) in parser.by_ref() { - // To my knowledge headings cannot nest, so it's okay not keeping a stack here. - let is_heading = matches!( - event, - jotdown::Event::End(jotdown::Container::Heading { .. }) - ); - events.push((event, span)); - if is_heading { - break; - } - } - - let title_events: Vec<_> = events - .iter() - .filter(|(event, _)| { - !matches!( - event, - // A little repetitive, but I don't mind. - // The point of this is not to include extra

and in the link text, - // but preserve other formatting such as bold, italic, code, etc. - jotdown::Event::Start( - jotdown::Container::Link(_, _) | jotdown::Container::Heading { .. }, - _ - ) | jotdown::Event::End( - jotdown::Container::Link(_, _) | jotdown::Container::Heading { .. } - ) - ) - }) - .cloned() - .collect(); - let mut title = String::new(); - let _render_diagnostics = djot::Renderer { - config: &sources.config, - dirs, - treehouse: &sources.treehouse, - file_id, - - // How. Just, stop. - page_id: "liquidex-you-reeeeeal-dummy".into(), - } - .render(&title_events, &mut title); - - let link = events.iter().find_map(|(event, _)| { - if let jotdown::Event::Start(jotdown::Container::Link(link, link_type), _) = event { - Some(link_url(sources, dirs, link, *link_type)) - } else { - None - } - }); - - return ParsedEntry { - title: (!title.is_empty()).then_some(title), - link, - }; - } - } - - ParsedEntry { - title: None, - link: None, - } -} - -fn link_url(sources: &Sources, dirs: &Dirs, url: &str, link_type: jotdown::LinkType) -> String { - if let jotdown::LinkType::Span(jotdown::SpanLinkType::Unresolved) = link_type { - if let Some(url) = resolve_link(&sources.config, &sources.treehouse, dirs, url) { - return url; - } - } - url.to_owned() -} - /// Extremely simple HTML renderer without the treehouse's fancy branch folding and linking features. fn branches_to_html_simple( s: &mut String, diff --git a/src/generate/doc.rs b/src/generate/doc.rs new file mode 100644 index 0000000..eda596f --- /dev/null +++ b/src/generate/doc.rs @@ -0,0 +1,242 @@ +use std::{ + fmt::{self}, + sync::Arc, +}; + +use anyhow::Context; +use chrono::{DateTime, Utc}; +use handlebars::Handlebars; +use serde::{Deserialize, Serialize}; +use tracing::{error, instrument}; + +use crate::{ + dirs::Dirs, + generate::BaseTemplateData, + html::djot, + sources::Sources, + state::{report_diagnostics, toml_error_to_diagnostic, FileId, TomlError}, + tree::{attributes::Picture, feed}, + vfs::{Content, Dir, Query, VPath}, +}; + +#[derive(Default, Deserialize)] +struct Attributes { + /// Template to use for generating the page. + /// Defaults to `_tree.hbs`. + #[serde(default)] + template: Option, + + /// Title of the page. + /// The only necessary field. + /// Unlike tree pages, doc pages always have titles. + title: String, + + /// ID of picture attached to the page, to be used as a thumbnail. + #[serde(default)] + thumbnail: Option, + + /// Additional scripts to load into to the page. + /// These are relative to the /static/js directory. + #[serde(default)] + scripts: Vec, + + /// Additional styles to load into to the page. + /// These are relative to the /static/css directory. + #[serde(default)] + styles: Vec, + + /// If not `None`, the page will get an additional 'feed' field in template data, containing + /// updates from the news feed of the specified name. + #[serde(default)] + include_feed: Option, +} + +#[derive(Deserialize)] +struct IncludeFeed { + /// The name of the feed (within the treehouse database.) + name: String, + + /// The title of the feed shown on the page. + title: String, +} + +#[derive(Serialize)] +struct Page { + title: String, + thumbnail: Option, + scripts: Vec, + styles: Vec, + tree_path: String, + doc: String, + feed: Option, +} + +#[derive(Serialize)] +struct Thumbnail { + url: String, + alt: Option, +} + +#[derive(Serialize)] +struct Feed { + title: String, + entries: Vec, +} + +#[derive(Serialize)] +struct Entry { + title: String, + url: String, + updated: DateTime, + categories: Vec, +} + +#[derive(Serialize)] +struct PageTemplateData<'a> { + #[serde(flatten)] + base: &'a BaseTemplateData<'a>, + page: Page, +} + +pub struct DocDir { + pub sources: Arc, + pub dirs: Arc, + + pub handlebars: Arc>, +} + +impl DocDir { + #[instrument("DocDir::content", skip(self))] + pub fn content(&self, path: &VPath) -> Option { + if let Some(file_id) = self + .sources + .treehouse + .files_by_doc_path + .get(&path.with_extension("dj")) + { + let source = self.sources.treehouse.source(*file_id).input(); + return Some(Content::new( + "text/html", + self.generate(*file_id, path, source).into_bytes(), + )); + } + + None + } + + fn generate(&self, file_id: FileId, path: &VPath, source: &str) -> String { + let (front_matter, text) = source.split_once("+++").unwrap_or(("", source)); + let attributes: Attributes = + toml_edit::de::from_str(front_matter).unwrap_or_else(|error| { + _ = report_diagnostics( + &self.sources.treehouse, + &[toml_error_to_diagnostic(TomlError { + message: error.message().to_owned(), + span: error.span(), + file_id, + input_range: 0..front_matter.len(), + })], + ); + Attributes::default() + }); + + let events: Vec<_> = jotdown::Parser::new(text).into_offset_iter().collect(); + let mut rendered_markup = String::new(); + let render_diagnostics = djot::Renderer { + config: &self.sources.config, + dirs: &self.dirs, + treehouse: &self.sources.treehouse, + file_id, + page_id: path.to_string(), + } + .render(&events, &mut rendered_markup); + + let template_name = attributes.template.as_deref().unwrap_or("_doc.hbs"); + + let render_result = self + .handlebars + .render( + template_name, + &PageTemplateData { + base: &BaseTemplateData::new(&self.sources), + page: Page { + title: attributes.title, + thumbnail: attributes.thumbnail.map(|pic| Thumbnail { + url: self.sources.config.pic_url(&*self.dirs.pic, &pic.id), + alt: pic.alt, + }), + scripts: attributes.scripts, + styles: attributes.styles, + tree_path: path.to_string(), + doc: rendered_markup, + feed: attributes.include_feed.and_then(|feed| { + Some(Feed { + title: feed.title, + entries: self + .generate_feed(&feed.name) + .inspect_err(|e| { + error!("generating feed for {path} failed: {e}") + }) + .ok()?, + }) + }), + }, + }, + ) + .context("template rendering failed"); + match render_result { + Ok(rendered) => rendered, + Err(error) => format!("{error:#?}"), + } + } + + fn generate_feed(&self, name: &str) -> anyhow::Result> { + let file_id = *self + .sources + .treehouse + .feeds_by_name + .get(name) + .context("no feed with the given name")?; + let roots = &self.sources.treehouse.roots[&file_id]; + + Ok(roots + .branches + .iter() + .flat_map(|&branch_id| { + let branch = self.sources.treehouse.tree.branch(branch_id); + + let text = &self.sources.treehouse.source(file_id).input()[branch.content.clone()]; + let parsed = feed::parse_entry( + &self.sources, + &self.dirs, + file_id, + jotdown::Parser::new(text), + ); + + let updated = branch + .attributes + .timestamp() + .unwrap_or(DateTime::UNIX_EPOCH); // if you see the Unix epoch... oops + + parsed.link.map(|url| Entry { + updated, + url, + title: parsed.title.unwrap_or_else(|| "untitled".into()), + categories: branch.attributes.tags.clone(), + }) + }) + .collect()) + } +} + +impl Dir for DocDir { + fn query(&self, path: &VPath, query: &mut Query) { + query.try_provide(|| self.content(path)); + } +} + +impl fmt::Debug for DocDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("DocDir") + } +} diff --git a/src/html/djot.rs b/src/html/djot.rs index 582aae4..8ce3b11 100644 --- a/src/html/djot.rs +++ b/src/html/djot.rs @@ -106,21 +106,6 @@ impl<'a> Writer<'a> { range: Range, out: &mut String, ) -> std::fmt::Result { - if let Event::Start(Container::Footnote { label: _ }, ..) = e { - self.diagnostics.push(Diagnostic { - severity: Severity::Error, - code: Some("djot".into()), - message: "Djot footnotes are not supported".into(), - labels: vec![Label { - style: LabelStyle::Primary, - file_id: self.renderer.file_id, - range: range.clone(), - message: "".into(), - }], - notes: vec![], - }) - } - if matches!(&e, Event::Start(Container::LinkDefinition { .. }, ..)) { self.ignore_next_event = true; return Ok(()); @@ -163,7 +148,7 @@ impl<'a> Writer<'a> { } => { out.push_str(" 1 { - write!(out, r#" start="{}""#, start)?; + write!(out, r#" start="{start}""#)?; } if let Some(ty) = match numbering { Decimal => None, @@ -172,7 +157,7 @@ impl<'a> Writer<'a> { RomanLower => Some('i'), RomanUpper => Some('I'), } { - write!(out, r#" type="{}""#, ty)?; + write!(out, r#" type="{ty}""#)?; } } } @@ -182,7 +167,7 @@ impl<'a> Writer<'a> { } Container::DescriptionList => out.push_str(" out.push_str(" unreachable!(), + Container::Footnote { label } => out.push_str(label), Container::Table => out.push_str(" out.push_str(" {} @@ -193,7 +178,7 @@ impl<'a> Writer<'a> { } out.push_str(" write!(out, " write!(out, " out.push_str(" out.push_str(" out.push_str(" Writer<'a> { .into_iter() .filter(|(a, _)| !(*a == "class" || a.starts_with(':'))) { - write!(out, r#" {}=""#, key)?; + write!(out, r#" {key}=""#)?; value.parts().for_each(|part| write_attr(part, out)); out.push('"'); } @@ -338,7 +323,7 @@ impl<'a> Writer<'a> { Alignment::Center => "center", Alignment::Right => "right", }; - write!(out, r#" style="text-align: {};">"#, a)?; + write!(out, r#" style="text-align: {a};">"#)?; } Container::CodeBlock { language } => { if language.is_empty() { @@ -444,7 +429,7 @@ impl<'a> Writer<'a> { } Container::DescriptionList => out.push_str(""), Container::DescriptionDetails => out.push_str(""), - Container::Footnote { .. } => unreachable!(), + Container::Footnote { label } => out.push_str(label), Container::Table => out.push_str(""), Container::TableRow { .. } => out.push_str(""), Container::Section { .. } => {} @@ -455,7 +440,7 @@ impl<'a> Writer<'a> { } out.push_str("

"); } - Container::Heading { level, .. } => write!(out, "", level)?, + Container::Heading { level, .. } => write!(out, "")?, Container::TableCell { head: false, .. } => out.push_str(""), Container::TableCell { head: true, .. } => out.push_str(""), Container::Caption => out.push_str(""), @@ -537,19 +522,8 @@ impl<'a> Writer<'a> { Raw::Html => out.push_str(s), Raw::Other => {} }, - Event::FootnoteReference(_label) => { - self.diagnostics.push(Diagnostic { - severity: Severity::Error, - code: Some("djot".into()), - message: "Djot footnotes are unsupported".into(), - labels: vec![Label { - style: LabelStyle::Primary, - file_id: self.renderer.file_id, - range, - message: "".into(), - }], - notes: vec![], - }); + Event::FootnoteReference(label) => { + out.push_str(label); } Event::Symbol(sym) => { if let Some(vpath) = self.renderer.config.emoji.get(sym.as_ref()) { @@ -624,7 +598,7 @@ impl<'a> Writer<'a> { } out.push_str(" Some("""), _ => None, } - .map_or(false, |s| { + .is_some_and(|s| { ent = s; true }) diff --git a/src/sources.rs b/src/sources.rs index 56feac4..9e0938a 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, ops::ControlFlow}; use anyhow::{anyhow, Context}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; -use tracing::{info_span, instrument}; +use tracing::{error, info_span, instrument}; use crate::{ config::Config, @@ -66,14 +66,19 @@ fn load_trees(config: &Config, dirs: &Dirs) -> anyhow::Result { let mut parsed_trees = HashMap::new(); let mut paths = vec![]; + let mut doc_paths = vec![]; vfs::walk_dir_rec(&*dirs.content, VPath::ROOT, &mut |path| { - if path.extension() == Some("tree") { - paths.push(path.to_owned()); + match path.extension() { + Some("tree") => paths.push(path.to_owned()), + Some("dj") => doc_paths.push(path.to_owned()), + _ => (), } ControlFlow::Continue(()) }); + // Trees + // NOTE: Sources are filled in later; they can be left out until a call to report_diagnostics. let file_ids: Vec<_> = paths .iter() @@ -132,5 +137,18 @@ fn load_trees(config: &Config, dirs: &Dirs) -> anyhow::Result { report_diagnostics(&treehouse, &diagnostics)?; + // Docs + + for path in doc_paths { + if let Some(input) = + vfs::query::(&dirs.content, &path).and_then(|c| c.string().ok()) + { + let file_id = treehouse.add_file(path.clone(), Source::Other(input)); + treehouse.files_by_doc_path.insert(path, file_id); + } else { + error!("doc {path} does not exist in content directory even though it was enumerated via walk_dir_rec"); + } + } + Ok(treehouse) } diff --git a/src/state.rs b/src/state.rs index 9a592b9..ab98670 100644 --- a/src/state.rs +++ b/src/state.rs @@ -66,7 +66,8 @@ pub struct FileId(usize); /// Treehouse compilation context. pub struct Treehouse { pub files: Vec, - pub files_by_tree_path: HashMap, + pub files_by_tree_path: HashMap, // trees only + pub files_by_doc_path: HashMap, // docs only pub feeds_by_name: HashMap, pub tree: SemaTree, @@ -83,6 +84,7 @@ impl Treehouse { Self { files: vec![], files_by_tree_path: HashMap::new(), + files_by_doc_path: HashMap::new(), feeds_by_name: HashMap::new(), tree: SemaTree::default(), diff --git a/src/tree.rs b/src/tree.rs index b922e32..c5a62e1 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -1,5 +1,6 @@ pub mod ast; pub mod attributes; +pub mod feed; pub mod mini_template; pub mod pull; diff --git a/src/tree/attributes.rs b/src/tree/attributes.rs index 60fed8b..c18fa3e 100644 --- a/src/tree/attributes.rs +++ b/src/tree/attributes.rs @@ -23,10 +23,6 @@ pub struct RootAttributes { #[serde(default = "default_icon")] pub icon: String, - /// Summary of the generated .html page. - #[serde(default)] - pub description: Option, - /// ID of picture attached to the page, to be used as a thumbnail. #[serde(default)] pub thumbnail: Option, @@ -50,7 +46,7 @@ pub struct RootAttributes { #[serde(default)] pub timestamps: Option, - /// When specified, this page will have a corresponding Atom feed under `rss/{feed}.xml`. + /// When specified, this page will have a corresponding Atom feed under `feed/{feed}.atom`. /// /// In feeds, top-level branches are expected to have a single heading containing the post title. /// Their children are turned into the post description diff --git a/src/tree/feed.rs b/src/tree/feed.rs new file mode 100644 index 0000000..b8c0d77 --- /dev/null +++ b/src/tree/feed.rs @@ -0,0 +1,94 @@ +use crate::{ + dirs::Dirs, + html::djot::{self, resolve_link}, + sources::Sources, + state::FileId, +}; + +#[derive(Debug, Clone)] +pub struct ParsedEntry { + pub title: Option, + pub link: Option, +} + +pub fn parse_entry( + sources: &Sources, + dirs: &Dirs, + file_id: FileId, + parser: jotdown::Parser, +) -> ParsedEntry { + let mut parser = parser.into_offset_iter(); + while let Some((event, span)) = parser.next() { + if let jotdown::Event::Start(jotdown::Container::Heading { .. }, _attrs) = &event { + let mut events = vec![(event, span)]; + for (event, span) in parser.by_ref() { + // To my knowledge headings cannot nest, so it's okay not keeping a stack here. + let is_heading = matches!( + event, + jotdown::Event::End(jotdown::Container::Heading { .. }) + ); + events.push((event, span)); + if is_heading { + break; + } + } + + let title_events: Vec<_> = events + .iter() + .filter(|(event, _)| { + !matches!( + event, + // A little repetitive, but I don't mind. + // The point of this is not to include extra

and in the link text, + // but preserve other formatting such as bold, italic, code, etc. + jotdown::Event::Start( + jotdown::Container::Link(_, _) | jotdown::Container::Heading { .. }, + _ + ) | jotdown::Event::End( + jotdown::Container::Link(_, _) | jotdown::Container::Heading { .. } + ) + ) + }) + .cloned() + .collect(); + let mut title = String::new(); + let _render_diagnostics = djot::Renderer { + config: &sources.config, + dirs, + treehouse: &sources.treehouse, + file_id, + + // How. Just, stop. + page_id: "liquidex-you-reeeeeal-dummy".into(), + } + .render(&title_events, &mut title); + + let link = events.iter().find_map(|(event, _)| { + if let jotdown::Event::Start(jotdown::Container::Link(link, link_type), _) = event { + Some(link_url(sources, dirs, link, *link_type)) + } else { + None + } + }); + + return ParsedEntry { + title: (!title.is_empty()).then_some(title), + link, + }; + } + } + + ParsedEntry { + title: None, + link: None, + } +} + +fn link_url(sources: &Sources, dirs: &Dirs, url: &str, link_type: jotdown::LinkType) -> String { + if let jotdown::LinkType::Span(jotdown::SpanLinkType::Unresolved) = link_type { + if let Some(url) = resolve_link(&sources.config, &sources.treehouse, dirs, url) { + return url; + } + } + url.to_owned() +} diff --git a/static/character/riki/sitting.png b/static/character/riki/sitting.png new file mode 100644 index 0000000000000000000000000000000000000000..316229e5bb5fe1a0d60e13de28583b8a9c90f948 GIT binary patch literal 62407 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 and not the scrolled-to element... */ scroll-padding-top: 10vh; } -body { - min-height: calc(100% / var(--virtual-space-ratio)); +.sidebar-sticky { + grid-column: right; + grid-row: main; } -.noscript { - grid-row: top; - grid-column: center; -} +aside.sidebar { + position: sticky; + top: 0px; -#nav-logo { - grid-row: title; - grid-column: left; + max-width: 50rem; + height: 100vh; + padding: 0.8rem; - align-self: center; - justify-self: end; -} - -section.page-header { - grid-row: title; - grid-column: center; - - align-self: center; + display: flex; } main { + grid-column: left; grid-row: main; - grid-column: center / center; - - margin-right: 0.8rem; + min-width: 0; } footer { @@ -75,27 +48,25 @@ footer { grid-column: center / center; } -@media (max-width: 1200px) { - main { - grid-column: left / -1; - } +/* Narrower layout: sidebar is pushed to the top */ - footer { - grid-column: 1 / -1; - } -} - -@media (max-width: 450px) { +@media (max-width: 1280px) { body { - --top-min-spacing: 0px; + --main-min-size: 0; } - section.page-header { - grid-column: 1 / -1; + .sidebar-sticky { + grid-column: left; + grid-row: nav; + + display: flex; + justify-content: center; } - nav#nav-logo { - display: none; + aside.sidebar { + position: relative; + height: auto; + padding: 0; } } @@ -119,16 +90,6 @@ body { /* Set up typography */ -@font-face { - font-family: "RecVar"; - /* NOTE: I put the hash in here manually instead of adding the complexity of piping CSS through - Handlebars because I don't really think it's worth it for this single asset. - Other assets are referenced rarely enough that caching probably isn't gonna make too much of - an impact. - It's unlikely I'll ever update the font anyways, so eh, whatever. */ - src: url("../font/Recursive_VF_1.085.woff2?v=b3-41236e2f"); -} - body, pre, code, @@ -192,7 +153,7 @@ input { h1 { --recursive-wght: 900; - font-size: 5.6rem; + font-size: 4.8rem; font-feature-settings: var(--recursive-simplified-r) 0; } @@ -246,6 +207,12 @@ h6 { text-wrap: balance; } +/* Other classes for controlling typography */ + +.nowrap { + white-space: nowrap; +} + /* Lay out elements a bit more compactly */ p, @@ -320,21 +287,21 @@ th-literate-program { overflow: auto; } -/* Also don't let images get out of hand */ +/* Images */ img { + /* Prevent images from causing horizontal scrolling */ max-width: 100%; + height: auto; } -/* Also regarding images - make them look a bit more pretty by default */ - img.pic { border-radius: 0.6rem; margin: 0.8rem 0; } -/* Image hints for tweaking rendering */ img { + /* Hints for tweaking rendering */ &[src*="+pixel"] { image-rendering: pixelated; border-radius: 0; @@ -378,8 +345,6 @@ a:visited { color: var(--link-color-visited); } -/* Allow for some secret links */ - a.secret { color: var(--text-color); text-decoration: none; @@ -460,32 +425,224 @@ hr { color: #6c2380; } -/* Navigation button */ +/* Feeds */ -#nav-logo { - width: min-content; - height: min-content; +section.feed { + display: flex; + flex-direction: column; + + /* Titles */ + + & a, + & a:visited { + color: var(--text-color); + } + + & a:visited { + color: color-mix( + in srgb, + var(--background-color), + var(--text-color) 60% + ); + } + + & h1 { + --recursive-wght: 800; + font-size: 125%; + padding-top: 1.2rem; + padding-bottom: 1.2rem; + } + + & h2 { + --recursive-wght: 600; + font-size: 100%; + padding: 0; + } + + /* Articles */ + + & article { + display: flex; + flex-direction: column; + + padding-bottom: 1.2rem; + + line-height: 1.4; + + & .info { + display: flex; + flex-direction: row; + flex-wrap: wrap; + font-size: 87.5%; + + & > *:not(:first-child)::before { + content: "·"; + padding: 0 0.4rem; + } + } + + & .categories { + display: flex; + flex-direction: row; + flex-wrap: wrap; + + list-style: none; + margin: 0; + padding: 0; + + & > *::before { + content: "#"; + } + + & > *:not(:first-child)::before { + padding-left: 0.4rem; + } + } + } } -#nav-logo .logo { - /* NOTE: Measurements in px for pixel perfection */ - width: 120px; - height: 120px; +/* Page sidebar */ - display: block; - opacity: 100%; - color: var(--text-color); +aside.sidebar { + overflow: clip; + + & > a { + display: block; + height: min-content; + margin-top: auto; + } +} + +header.floof { + margin-top: auto; + + position: relative; + + & > img { + display: block; + min-width: 0; + object-fit: cover; + object-position: 33% 0; + } + + & > h1 { + position: absolute; + top: 3rem; + left: 3rem; + + display: flex; + flex-direction: column; + line-height: 1; + width: min-content; + + --recursive-wght: 900; + font-size: 5.6rem; + text-align: right; + + transform: skew(-5deg, -5deg); + + & .rikis { + width: max-content; + background-color: var(--text-color); + color: var(--background-color); + padding: 0.1em; + + --shadow-color: var(--accent-pink); + box-shadow: + 0.5px 0.5px 0 var(--shadow-color), + 1px 1px 0 var(--shadow-color), + 1.5px 1.5px 0 var(--shadow-color), + 2px 2px 0 var(--shadow-color), + 2.5px 2.5px 0 var(--shadow-color), + 3px 3px 0 var(--shadow-color), + 3.5px 3.5px 0 var(--shadow-color), + 4px 4px 0 var(--shadow-color); + + /* + import math + + print("box-shadow:") + x = 0 + max_x = 16 + while x < max_x: + print(f"{x}px {x}px {math.pow(x / max_x, 2) * 16}px rgba(from var(--shadow-color) r g b / {math.pow(1 - x / max_x, 3)}),") + x += 0.5 + */ + /* prettier-ignore */ + box-shadow: + 0px 0px 0.0px rgba(from var(--shadow-color) r g b / 1.0), + 0.5px 0.5px 0.015625px rgba(from var(--shadow-color) r g b / 0.909149169921875), + 1.0px 1.0px 0.0625px rgba(from var(--shadow-color) r g b / 0.823974609375), + 1.5px 1.5px 0.140625px rgba(from var(--shadow-color) r g b / 0.744293212890625), + 2.0px 2.0px 0.25px rgba(from var(--shadow-color) r g b / 0.669921875), + 2.5px 2.5px 0.390625px rgba(from var(--shadow-color) r g b / 0.600677490234375), + 3.0px 3.0px 0.5625px rgba(from var(--shadow-color) r g b / 0.536376953125), + 3.5px 3.5px 0.765625px rgba(from var(--shadow-color) r g b / 0.476837158203125), + 4.0px 4.0px 1.0px rgba(from var(--shadow-color) r g b / 0.421875), + 4.5px 4.5px 1.265625px rgba(from var(--shadow-color) r g b / 0.371307373046875), + 5.0px 5.0px 1.5625px rgba(from var(--shadow-color) r g b / 0.324951171875), + 5.5px 5.5px 1.890625px rgba(from var(--shadow-color) r g b / 0.282623291015625), + 6.0px 6.0px 2.25px rgba(from var(--shadow-color) r g b / 0.244140625), + 6.5px 6.5px 2.640625px rgba(from var(--shadow-color) r g b / 0.209320068359375), + 7.0px 7.0px 3.0625px rgba(from var(--shadow-color) r g b / 0.177978515625), + 7.5px 7.5px 3.515625px rgba(from var(--shadow-color) r g b / 0.149932861328125), + 8.0px 8.0px 4.0px rgba(from var(--shadow-color) r g b / 0.125), + 8.5px 8.5px 4.515625px rgba(from var(--shadow-color) r g b / 0.102996826171875), + 9.0px 9.0px 5.0625px rgba(from var(--shadow-color) r g b / 0.083740234375), + 9.5px 9.5px 5.640625px rgba(from var(--shadow-color) r g b / 0.067047119140625), + 10.0px 10.0px 6.25px rgba(from var(--shadow-color) r g b / 0.052734375), + 10.5px 10.5px 6.890625px rgba(from var(--shadow-color) r g b / 0.040618896484375), + 11.0px 11.0px 7.5625px rgba(from var(--shadow-color) r g b / 0.030517578125), + 11.5px 11.5px 8.265625px rgba(from var(--shadow-color) r g b / 0.022247314453125), + 12.0px 12.0px 9.0px rgba(from var(--shadow-color) r g b / 0.015625), + 12.5px 12.5px 9.765625px rgba(from var(--shadow-color) r g b / 0.010467529296875), + 13.0px 13.0px 10.5625px rgba(from var(--shadow-color) r g b / 0.006591796875), + 13.5px 13.5px 11.390625px rgba(from var(--shadow-color) r g b / 0.003814697265625), + 14.0px 14.0px 12.25px rgba(from var(--shadow-color) r g b / 0.001953125), + 14.5px 14.5px 13.140625px rgba(from var(--shadow-color) r g b / 0.000823974609375), + 15.0px 15.0px 14.0625px rgba(from var(--shadow-color) r g b / 0.000244140625), + 15.5px 15.5px 15.015625px rgba(from var(--shadow-color) r g b / 3.0517578125e-05) + ; + } + + & .fluffy-little-house { + display: flex; + flex-direction: column; + + background-color: var(--background-color); + width: min-content; + align-self: end; + + padding-left: 0.8rem; + padding-right: 0.8rem; + padding-top: 0.4rem; + + z-index: -1; + + color: var(--text-color); + + & .adjectives { + --recursive-wght: 800; + font-size: 1.6rem; + padding-top: 0.6rem; + } + + & .house { + margin-top: -0.2em; + font-size: 3.6rem; + } + } + } } /* Navigation header (contains page title & breadcrumbs) */ h1.page-title { - --recursive-wght: 850; + --recursive-wght: 900; - margin-top: 0.32rem; - margin-bottom: 0.32rem; - margin-left: 3.6rem; - font-size: 4rem; + line-height: 1.2; + padding-top: 3lh; + padding-bottom: 0.5lh; & a { color: var(--text-color); @@ -506,7 +663,18 @@ h1.page-title { } } -/* Style badges */ +@media (max-width: 1280px) { + h1.page-title { + padding-top: 0.25lh; + } +} + +@media (max-width: 700px) { + h1.page-title { + font-size: 4rem; + } +} + span.badge { --recursive-wght: 800; --recursive-mono: 1; @@ -533,11 +701,9 @@ span.badge { /* Style the footer */ footer { - padding-left: 1.6rem; - padding-right: 1.6rem; - - margin-top: 6.4rem; - padding-bottom: 6.4rem; + width: 100%; + max-width: 90ch; + padding: 1.6rem 0.8rem; display: flex; flex-direction: row; @@ -610,9 +776,9 @@ dialog[open] { /* Style emojis to be readable */ img[data-cast~="emoji"] { - max-width: 1.5em; - max-height: 1.5em; - vertical-align: bottom; + max-width: 1.3125em; + max-height: 1.3125em; + vertical-align: text-bottom; object-fit: contain; } diff --git a/static/css/page/index.css b/static/css/page/index.css index ac95a5a..a6efd39 100644 --- a/static/css/page/index.css +++ b/static/css/page/index.css @@ -1,98 +1,3 @@ -h1.page-title { - --recursive-wght: 900; - font-size: 5.6rem; - - display: flex; - flex-direction: column; - line-height: 1; - width: min-content; - - transform: skew(-5deg, -5deg); - - & .rikis { - width: max-content; - background-color: var(--text-color); - color: var(--background-color); - padding: 0.1em; - - --shadow-color: var(--accent-pink); - box-shadow: - 0.5px 0.5px 0 var(--shadow-color), - 1px 1px 0 var(--shadow-color), - 1.5px 1.5px 0 var(--shadow-color), - 2px 2px 0 var(--shadow-color), - 2.5px 2.5px 0 var(--shadow-color), - 3px 3px 0 var(--shadow-color), - 3.5px 3.5px 0 var(--shadow-color), - 4px 4px 0 var(--shadow-color); - - /* - import math - - print("box-shadow:") - x = 0 - max_x = 16 - while x < max_x: - print(f"{x}px {x}px {math.pow(x / max_x, 2) * 16}px rgba(from var(--shadow-color) r g b / {math.pow(1 - x / max_x, 3)}),") - x += 0.5 - */ - /* prettier-ignore */ - box-shadow: - 0px 0px 0.0px rgba(from var(--shadow-color) r g b / 1.0), - 0.5px 0.5px 0.015625px rgba(from var(--shadow-color) r g b / 0.909149169921875), - 1.0px 1.0px 0.0625px rgba(from var(--shadow-color) r g b / 0.823974609375), - 1.5px 1.5px 0.140625px rgba(from var(--shadow-color) r g b / 0.744293212890625), - 2.0px 2.0px 0.25px rgba(from var(--shadow-color) r g b / 0.669921875), - 2.5px 2.5px 0.390625px rgba(from var(--shadow-color) r g b / 0.600677490234375), - 3.0px 3.0px 0.5625px rgba(from var(--shadow-color) r g b / 0.536376953125), - 3.5px 3.5px 0.765625px rgba(from var(--shadow-color) r g b / 0.476837158203125), - 4.0px 4.0px 1.0px rgba(from var(--shadow-color) r g b / 0.421875), - 4.5px 4.5px 1.265625px rgba(from var(--shadow-color) r g b / 0.371307373046875), - 5.0px 5.0px 1.5625px rgba(from var(--shadow-color) r g b / 0.324951171875), - 5.5px 5.5px 1.890625px rgba(from var(--shadow-color) r g b / 0.282623291015625), - 6.0px 6.0px 2.25px rgba(from var(--shadow-color) r g b / 0.244140625), - 6.5px 6.5px 2.640625px rgba(from var(--shadow-color) r g b / 0.209320068359375), - 7.0px 7.0px 3.0625px rgba(from var(--shadow-color) r g b / 0.177978515625), - 7.5px 7.5px 3.515625px rgba(from var(--shadow-color) r g b / 0.149932861328125), - 8.0px 8.0px 4.0px rgba(from var(--shadow-color) r g b / 0.125), - 8.5px 8.5px 4.515625px rgba(from var(--shadow-color) r g b / 0.102996826171875), - 9.0px 9.0px 5.0625px rgba(from var(--shadow-color) r g b / 0.083740234375), - 9.5px 9.5px 5.640625px rgba(from var(--shadow-color) r g b / 0.067047119140625), - 10.0px 10.0px 6.25px rgba(from var(--shadow-color) r g b / 0.052734375), - 10.5px 10.5px 6.890625px rgba(from var(--shadow-color) r g b / 0.040618896484375), - 11.0px 11.0px 7.5625px rgba(from var(--shadow-color) r g b / 0.030517578125), - 11.5px 11.5px 8.265625px rgba(from var(--shadow-color) r g b / 0.022247314453125), - 12.0px 12.0px 9.0px rgba(from var(--shadow-color) r g b / 0.015625), - 12.5px 12.5px 9.765625px rgba(from var(--shadow-color) r g b / 0.010467529296875), - 13.0px 13.0px 10.5625px rgba(from var(--shadow-color) r g b / 0.006591796875), - 13.5px 13.5px 11.390625px rgba(from var(--shadow-color) r g b / 0.003814697265625), - 14.0px 14.0px 12.25px rgba(from var(--shadow-color) r g b / 0.001953125), - 14.5px 14.5px 13.140625px rgba(from var(--shadow-color) r g b / 0.000823974609375), - 15.0px 15.0px 14.0625px rgba(from var(--shadow-color) r g b / 0.000244140625), - 15.5px 15.5px 15.015625px rgba(from var(--shadow-color) r g b / 3.0517578125e-05) - ; - } - - & .adjectives { - --recursive-wght: 800; - font-size: 2rem; - vertical-align: 50%; - } - - & .house { - width: max-content; - font-size: 4rem; - padding-left: 1em; - padding-top: 0.1em; - } -} - -@media (hover: none) { - h1.page-title a { - text-decoration: none; - } -} - @media (max-width: 450px) { body { --top-min-spacing: 40px; diff --git a/static/css/tree.css b/static/css/tree.css index 6aaed5e..ae73ba2 100644 --- a/static/css/tree.css +++ b/static/css/tree.css @@ -2,7 +2,6 @@ :root { --tree-indent-width: 3.2rem; - --transition-duration: 0.15s; --button-bar-icon-size: 2.8rem; } @@ -82,6 +81,21 @@ .tree { --tree-indent-guide-dim: transparent; --tree-indent-guide-highlighted: transparent; + + display: flex; + flex-direction: column; + + align-self: start; + align-items: center; + + & > article { + padding: 1.6rem; + width: 100%; + } + + & > footer { + max-width: none; + } } .tree:has(.branch-container:hover) { @@ -116,8 +130,9 @@ } /* Top level should not have an indent or a border. */ -.tree > ul { +.tree article > ul { padding-left: 0; + margin-left: 0; border-left: none; } diff --git a/template/_doc.hbs b/template/_doc.hbs index 132c397..2c68496 100644 --- a/template/_doc.hbs +++ b/template/_doc.hbs @@ -4,28 +4,25 @@ {{> components/_head.hbs }} + + + + {{#each page.styles}} + + {{/each}} + + - - - {{> components/_noscript.hbs }} + {{> components/_sidebar.hbs }} - {{> components/_nav.hbs }} - {{> components/_header.hbs }} - - {{!-- - NOTE: ~ because components/_tree.hbs must not include any extra indentation, because it may - contain pre elements which shouldn't be indented. - --}} - {{~> components/_tree.hbs }} - - {{!-- For all pages except the one linked from the footer, include the footer icon. --}} - {{#if (ne page.tree_path "treehouse")}} - {{> components/_footer.hbs }} - {{/if}} + {{~> components/_doc.hbs }} @@ -33,3 +30,4 @@ +{{~> components/_jar.hbs }} diff --git a/template/_tree.hbs b/template/_tree.hbs index f395225..233f511 100644 --- a/template/_tree.hbs +++ b/template/_tree.hbs @@ -4,31 +4,30 @@ {{> components/_head.hbs }} + + + + {{#each page.styles}} + + {{/each}} + + - - - {{> components/_noscript.hbs }} + {{> components/_sidebar.hbs }} - {{> components/_nav.hbs }} - {{> components/_header.hbs }} - - {{!-- - NOTE: ~ because components/_tree.hbs must not include any extra indentation, because it may - contain pre elements which shouldn't be indented. - --}} {{~> components/_tree.hbs }} - {{!-- For all pages except the one linked from the footer, include the footer icon. --}} - {{#if (ne page.tree_path "treehouse")}} - {{> components/_footer.hbs }} - {{/if}} - + +{{~> components/_jar.hbs }} diff --git a/template/components/_doc.hbs b/template/components/_doc.hbs new file mode 100644 index 0000000..0afa630 --- /dev/null +++ b/template/components/_doc.hbs @@ -0,0 +1,17 @@ + diff --git a/template/components/_feed.hbs b/template/components/_feed.hbs new file mode 100644 index 0000000..780ef75 --- /dev/null +++ b/template/components/_feed.hbs @@ -0,0 +1,20 @@ +{{#if page.feed}} +
+

{{ page.feed.title }}

+ + {{#each page.feed.entries}} +
+

{{{ title }}}

+
+ +
    + {{#each categories as |category|}} +
  • {{ category }}
  • + {{/each}} +
+
+
+ {{/each}} +
+{{/if}} + diff --git a/template/components/_footer.hbs b/template/components/_footer.hbs index e216374..064b07e 100644 --- a/template/components/_footer.hbs +++ b/template/components/_footer.hbs @@ -9,6 +9,8 @@ + {{!-- For all pages except the one linked from the footer, include the footer icon. --}} + {{#if (ne page.tree_path "treehouse")}}
@@ -58,4 +60,5 @@
+ {{/if}} diff --git a/template/components/_head.hbs b/template/components/_head.hbs index cf19b2e..bbed9c8 100644 --- a/template/components/_head.hbs +++ b/template/components/_head.hbs @@ -4,12 +4,15 @@ - + - {{!-- Import maps currently don't support the src="" attribute. Unless we come up with something @@ -38,14 +41,14 @@ clever to do while browser vendors figure that out, we'll just have to do a cach }; diff --git a/template/components/_jar.hbs b/template/components/_jar.hbs new file mode 100644 index 0000000..48941e1 --- /dev/null +++ b/template/components/_jar.hbs @@ -0,0 +1,16 @@ + + diff --git a/template/components/_nav.hbs b/template/components/_nav.hbs deleted file mode 100644 index 7ce797d..0000000 --- a/template/components/_nav.hbs +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/template/components/_sidebar.hbs b/template/components/_sidebar.hbs new file mode 100644 index 0000000..ae1dff5 --- /dev/null +++ b/template/components/_sidebar.hbs @@ -0,0 +1,17 @@ + + diff --git a/template/components/_tree.hbs b/template/components/_tree.hbs index cf3379d..c9368f1 100644 --- a/template/components/_tree.hbs +++ b/template/components/_tree.hbs @@ -1,18 +1,13 @@
- {{!-- Append page styles and scripts into the main content, such that they can be inlined - into linked branches when those are loaded in. Putting them in the page's head would make - extracting them way more painful than it needs to be. --}} +
+ {{#if (ne page.tree_path 'index')}} +
+

{{ page.title }}

+
+ {{/if}} - {{#each page.styles}} - - {{/each}} + {{{~ page.tree }}} +
- - - {{{ page.tree }}} + {{> components/_footer.hbs }}
diff --git a/treehouse.toml b/treehouse.toml index 34ec8f3..8881c23 100644 --- a/treehouse.toml +++ b/treehouse.toml @@ -12,7 +12,7 @@ commit_base_url = "https://src.liquidev.net/liquidex/treehouse/src/commit" [user] title = "riki's house" author = "riki" -description = "a fluffy ragdoll's fluffy house = —w— =" +description = "a pink ragdoll's fluffy house = —w— =" canonical_url = "https://riki.house" # URI prefix to use for entry IDs in feeds. @@ -26,6 +26,7 @@ feed_id_prefix = "https://liquidex.house" "social/github" = "https://github.com/liquidev" "social/soundcloud" = "https://soundcloud.com/daknus" "social/listenbrainz" = "https://listenbrainz.org/user/liquidev/" +"social/bandcamp" = "https://bandcamp.com/rikimoe" # treehouse management facilities "treehouse/issues" = "https://src.liquidev.net/riki/treehouse/issues"
+
+ + + {{> components/_footer.hbs }} +
+ + {{> components/_feed.hbs }} +