diff --git a/Cargo.toml b/Cargo.toml
index e178095..6efb188 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,11 @@
[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 8a9584c..d083a2d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,21 @@
# webdog
-Source for webdog, the static site generator fit for a dog. See our website at https://webdog.zyl.gay for more details.
+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"
+```
diff --git a/site/pages/index.md b/site/pages/index.md
index 78e9937..0c4de07 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 --git https://github.com/zyllian/webdog
+cargo install webdog
```
then you can make your first webdog site!
diff --git a/site/sass/index.scss b/site/sass/index.scss
index 4ebf304..13c1808 100644
--- a/site/sass/index.scss
+++ b/site/sass/index.scss
@@ -183,6 +183,7 @@ abbr {
.wd-codeblock {
position: relative;
+ tab-size: 2;
.copy {
display: none;
@@ -206,6 +207,6 @@ abbr {
& > pre {
padding: 8px;
- overflow: scroll;
+ overflow: auto;
}
}
diff --git a/src/builder.rs b/src/builder.rs
index 2222afe..d5da847 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -4,7 +4,6 @@ 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};
@@ -261,10 +260,28 @@ 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, mut new_href)) = href.split_once('$') {
- #[allow(clippy::single_match)]
+ if let Some((command, new_href)) = href.split_once('$') {
+ let mut new_href = new_href.to_string();
match command {
"me" => {
el.set_attribute(
@@ -273,11 +290,14 @@ impl SiteBuilder {
+ " me"),
)?;
}
+ "cdn" => {
+ new_href = self.site.config.cdn_url(&new_href)?.to_string();
+ }
_ => {
- new_href = &href;
+ new_href = href;
}
}
- href = new_href.to_string();
+ href = new_href;
el.set_attribute("href", &href)?;
}
if let Ok(url) = Url::parse(&href) {
@@ -294,47 +314,6 @@ 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(())
}),
],
@@ -419,49 +398,7 @@ impl SiteBuilder {
.with_context(|| format!("Failed to read page at {}", page_path.display()))?;
let page = crate::frontmatter::FrontMatter::parse(input)?;
- 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 page_html = util::render_markdown(self, &page.content)?;
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 239ad7e..06ef3d6 100644
--- a/src/embedded/default_site/sass/index.scss
+++ b/src/embedded/default_site/sass/index.scss
@@ -5,6 +5,7 @@
.wd-codeblock {
position: relative;
+ tab-size: 2;
.copy {
display: none;
@@ -28,6 +29,6 @@
& > pre {
padding: 8px;
- overflow: scroll;
+ overflow: auto;
}
}
diff --git a/src/resource.rs b/src/resource.rs
index d4d5bf1..433584c 100644
--- a/src/resource.rs
+++ b/src/resource.rs
@@ -5,14 +5,13 @@ 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::format_timestamp, PageMetadata,
+ util::{self, format_timestamp}, PageMetadata,
};
/// Metadata for resources.
@@ -182,10 +181,7 @@ impl ResourceBuilder {
let mut page = FrontMatterRequired::::parse(input)
.wrap_err_with(|| eyre::eyre!("Failed to parse resource front matter"))?;
- 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;
+ *page.content_mut() = util::render_markdown(builder, &page.content)?;
let data = page.data_mut();
if let Some(cdn_file) = &data.cdn_file {
diff --git a/src/util.rs b/src/util.rs
index d8ebf18..0f42680 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -2,8 +2,11 @@
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()? {
@@ -24,3 +27,52 @@ 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)
+}