diff --git a/Cargo.toml b/Cargo.toml index 6efb188..e178095 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,6 @@ [package] -description = "static site generator fit for a dog" edition = "2021" -homepage = "https://webdog.zyl.gay" -license = "AGPL-3.0-or-later" name = "webdog" -readme = "README.md" -repository = "https://github.com/zyllian/webdog" version = "0.1.0" [dependencies] diff --git a/README.md b/README.md index d083a2d..8a9584c 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,3 @@ # webdog -webdog, the static site generator fit for a dog :3 - -```sh -cargo install webdog -``` - -after installing, you can create your first site: - -```sh -webdog create https://example.com "My First Site!" --site my-site -cd my-site -webdog serve # your site is now running at http://127.0.0.1:8080 🥳 -``` - -from there, you can start editing your site and adding pages or [more advanced things](https://webdog.zyl.gay/docs/) - -```sh -webdog page new my-first-page "My First Page" -``` +Source for webdog, the static site generator fit for a dog. See our website at https://webdog.zyl.gay for more details. diff --git a/site/pages/index.md b/site/pages/index.md index 0c4de07..78e9937 100644 --- a/site/pages/index.md +++ b/site/pages/index.md @@ -5,7 +5,7 @@ welcome to webdog, the static site generator fit for a dog :3 if you have [rust](https://rust-lang.org) installed, all you need to do to install webdog is run the following command: ```sh -cargo install webdog +cargo install webdog --git https://github.com/zyllian/webdog ``` then you can make your first webdog site! diff --git a/site/sass/index.scss b/site/sass/index.scss index 13c1808..4ebf304 100644 --- a/site/sass/index.scss +++ b/site/sass/index.scss @@ -183,7 +183,6 @@ abbr { .wd-codeblock { position: relative; - tab-size: 2; .copy { display: none; @@ -207,6 +206,6 @@ abbr { & > pre { padding: 8px; - overflow: auto; + overflow: scroll; } } diff --git a/src/builder.rs b/src/builder.rs index d5da847..2222afe 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -4,6 +4,7 @@ use std::{collections::HashMap, path::PathBuf}; use eyre::{eyre, Context, OptionExt}; use lol_html::{element, html_content::ContentType, HtmlRewriter, Settings}; +use pulldown_cmark::{Options, Parser}; use rayon::prelude::*; use serde::Serialize; use syntect::{highlighting::ThemeSet, parsing::SyntaxSet}; @@ -260,28 +261,10 @@ impl SiteBuilder { Ok(()) }), - element!("img", |el| { - if let Some(mut src) = el.get_attribute("src") { - if let Some((command, new_src)) = src.split_once('$') { - let mut new_src = new_src.to_string(); - #[allow(clippy::single_match)] - match command { - "cdn" => { - new_src = self.site.config.cdn_url(&new_src)?.to_string(); - } - _ => new_src = src, - } - src = new_src; - el.set_attribute("src", &src)?; - } - } - - Ok(()) - }), element!("a", |el| { if let Some(mut href) = el.get_attribute("href") { - if let Some((command, new_href)) = href.split_once('$') { - let mut new_href = new_href.to_string(); + if let Some((command, mut new_href)) = href.split_once('$') { + #[allow(clippy::single_match)] match command { "me" => { el.set_attribute( @@ -290,14 +273,11 @@ impl SiteBuilder { + " me"), )?; } - "cdn" => { - new_href = self.site.config.cdn_url(&new_href)?.to_string(); - } _ => { - new_href = href; + new_href = &href; } } - href = new_href; + href = new_href.to_string(); el.set_attribute("href", &href)?; } if let Ok(url) = Url::parse(&href) { @@ -314,6 +294,47 @@ impl SiteBuilder { } } + Ok(()) + }), + element!("md", |el| { + el.remove(); + let class = el.get_attribute("class"); + + let md_type = el + .get_attribute("type") + .ok_or_eyre("missing type attribute on markdown tag")?; + + if md_type == "blog-image" { + let mut src = el + .get_attribute("src") + .ok_or_eyre("missing src attribute")?; + + if src.starts_with("cdn$") { + src = self.site.config.cdn_url(&src[4..])?.to_string(); + } + + let class = format!("image {}", class.unwrap_or_default()); + let content = el + .get_attribute("content") + .ok_or_eyre("missing content attribute")?; + + el.replace( + &format!( + r#" +
+ + + + {content} +
+ "# + ), + ContentType::Html, + ); + } else { + return Err(eyre!("unknown markdown tag type: {md_type}").into()); + } + Ok(()) }), ], @@ -398,7 +419,49 @@ impl SiteBuilder { .with_context(|| format!("Failed to read page at {}", page_path.display()))?; let page = crate::frontmatter::FrontMatter::parse(input)?; - let page_html = util::render_markdown(self, &page.content)?; + let mut language = None; + let parser = Parser::new_ext(&page.content, Options::all()).filter_map(|event| { + // syntax highlighting for code blocks + match event { + pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock( + pulldown_cmark::CodeBlockKind::Fenced(name), + )) => { + language = Some(name); + None + } + pulldown_cmark::Event::Text(code) => { + if let Some(language) = language.take() { + let syntax_reference = self + .syntax_set + .find_syntax_by_token(&language) + .unwrap_or_else(|| self.syntax_set.find_syntax_plain_text()); + let html = format!( + r#"
+ + {} +
"#, + syntect::html::highlighted_html_for_string( + &code, + &self.syntax_set, + syntax_reference, + self.theme_set + .themes + .get(&self.site.config.code_theme) + .as_ref() + .expect("should never fail"), + ) + .expect("failed to highlight syntax") + ); + Some(pulldown_cmark::Event::Html(html.into())) + } else { + Some(pulldown_cmark::Event::Text(code)) + } + } + _ => Some(event), + } + }); + let mut page_html = String::new(); + pulldown_cmark::html::push_html(&mut page_html, parser); let out = self.build_page_raw(page.data.unwrap_or_default(), &page_html, ())?; diff --git a/src/embedded/default_site/sass/index.scss b/src/embedded/default_site/sass/index.scss index 06ef3d6..239ad7e 100644 --- a/src/embedded/default_site/sass/index.scss +++ b/src/embedded/default_site/sass/index.scss @@ -5,7 +5,6 @@ .wd-codeblock { position: relative; - tab-size: 2; .copy { display: none; @@ -29,6 +28,6 @@ & > pre { padding: 8px; - overflow: auto; + overflow: scroll; } } diff --git a/src/resource.rs b/src/resource.rs index 433584c..d4d5bf1 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -5,13 +5,14 @@ use std::{ use eyre::Context; use itertools::Itertools; +use pulldown_cmark::{Options, Parser}; use rss::{validation::Validate, ChannelBuilder, ItemBuilder}; use serde::{Deserialize, Serialize}; use time::{format_description::well_known::Rfc2822, OffsetDateTime}; use crate::{ builder::SiteBuilder, frontmatter::FrontMatterRequired, link_list::Link, - util::{self, format_timestamp}, PageMetadata, + util::format_timestamp, PageMetadata, }; /// Metadata for resources. @@ -181,7 +182,10 @@ impl ResourceBuilder { let mut page = FrontMatterRequired::::parse(input) .wrap_err_with(|| eyre::eyre!("Failed to parse resource front matter"))?; - *page.content_mut() = util::render_markdown(builder, &page.content)?; + let parser = Parser::new_ext(&page.content, Options::all()); + let mut html = String::new(); + pulldown_cmark::html::push_html(&mut html, parser); + *page.content_mut() = html; let data = page.data_mut(); if let Some(cdn_file) = &data.cdn_file { diff --git a/src/util.rs b/src/util.rs index 0f42680..d8ebf18 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,11 +2,8 @@ use std::path::Path; -use pulldown_cmark::{Options, Parser}; use time::OffsetDateTime; -use crate::builder::SiteBuilder; - /// Simple helper to remove the contents of a directory without removing the directory itself. pub fn remove_dir_contents(path: &Path) -> eyre::Result<()> { for entry in path.read_dir()? { @@ -27,52 +24,3 @@ pub fn format_timestamp(ts: OffsetDateTime, format: &str) -> eyre::Result(format)?; Ok(ts.format(&fmt)?) } - -/// Helper to render markdown. -pub fn render_markdown(builder: &SiteBuilder, input: &str) -> eyre::Result { - let mut language = None; - let parser = Parser::new_ext(input, Options::all()).filter_map(|event| { - // syntax highlighting for code blocks - match event { - pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock( - pulldown_cmark::CodeBlockKind::Fenced(name), - )) => { - language = Some(name); - None - } - pulldown_cmark::Event::Text(code) => { - if let Some(language) = language.take() { - let syntax_reference = builder - .syntax_set - .find_syntax_by_token(&language) - .unwrap_or_else(|| builder.syntax_set.find_syntax_plain_text()); - let html = format!( - r#"
- - {} -
"#, - syntect::html::highlighted_html_for_string( - &code, - &builder.syntax_set, - syntax_reference, - builder.theme_set - .themes - .get(&builder.site.config.code_theme) - .as_ref() - .expect("should never fail"), - ) - .expect("failed to highlight syntax") - ); - Some(pulldown_cmark::Event::Html(html.into())) - } else { - Some(pulldown_cmark::Event::Text(code)) - } - } - _ => Some(event), - } - }); - let mut page_html = String::new(); - pulldown_cmark::html::push_html(&mut page_html, parser); - - Ok(page_html) -}