Compare commits

...

10 commits

Author SHA1 Message Date
zyl
9125738498
remove itertools dependency
Some checks failed
Build Site / build (push) Has been cancelled
Build Site / deploy (push) Has been cancelled
2025-06-05 14:26:17 -07:00
zyl
230e22dbf8
remove unused fs_extra dependency 2025-06-05 14:16:55 -07:00
zyl
abd591c8fa
remove unused tokio macros feature 2025-06-05 14:13:43 -07:00
zyl
2330648b69
bump version
Some checks failed
Build Site / build (push) Has been cancelled
Build Site / deploy (push) Has been cancelled
2025-06-04 23:52:30 -07:00
zyl
05d66c2143
fix webdog script insertion with custom paths 2025-06-04 23:52:10 -07:00
zyl
0dbe284d57
bump version 2025-06-04 23:20:56 -07:00
zyl
f51af1ad74
upgrade rust edition 2025-06-04 23:20:29 -07:00
zyl
e05f040c38
add documentation for the new webdog_path config feature 2025-06-04 23:19:49 -07:00
zyl
3daa80f8af
add feature to set a custom path for static webdog resources 2025-06-04 23:17:58 -07:00
zyl
b4088fb397
update dependencies 2025-06-04 23:17:34 -07:00
7 changed files with 532 additions and 564 deletions

986
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,47 +1,42 @@
[package] [package]
description = "static site generator fit for a dog" description = "static site generator fit for a dog"
edition = "2021" edition = "2024"
homepage = "https://webdog.zyl.gay" homepage = "https://webdog.zyl.gay"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
name = "webdog" name = "webdog"
readme = "README.md" readme = "README.md"
repository = "https://github.com/zyllian/webdog" repository = "https://github.com/zyllian/webdog"
version = "0.1.1" version = "0.1.3"
[dependencies] [dependencies]
clap = {version = "4", features = ["derive"]} clap = { version = "4", features = ["derive"] }
color-eyre = {version = "0.6", optional = true} color-eyre = { version = "0.6", optional = true }
extract-frontmatter = "4" extract-frontmatter = "4"
eyre = "0.6" eyre = "0.6"
fs_extra = "1.2" futures = { version = "0.3", optional = true }
futures = {version = "0.3", optional = true} grass = { version = "0.13", default-features = false }
grass = {version = "0.13", default-features = false} hotwatch = { version = "0.5", optional = true }
hotwatch = {version = "0.5", optional = true} html5ever = "0.31"
html5ever = "0.29"
include_dir = "0.7" include_dir = "0.7"
itertools = "0.14" kuchikiki = "0.8.8-speedreader"
kuchikiki = "0.8.6-speedreader"
lol_html = "2" lol_html = "2"
minifier = {version = "0.3", features = ["html"]} minifier = { version = "0.3", features = ["html"] }
percent-encoding = {version = "2", optional = true} percent-encoding = { version = "2", optional = true }
pulldown-cmark = {version = "0.12", default-features = false, features = [ pulldown-cmark = { version = "0.13", default-features = false, features = [
"simd", "simd",
"html", "html",
]} ] }
rayon = "1" rayon = "1"
rss = {version = "2", features = ["validation"]} rss = { version = "2", features = ["validation"] }
serde = {version = "1", features = ["derive"]} serde = { version = "1", features = ["derive"] }
serde_yaml_ng = "0.10" serde_yaml_ng = "0.10"
syntect = "5" syntect = "5"
tera = "1" tera = "1"
time = {version = "0.3", features = ["serde-human-readable"]} time = { version = "0.3", features = ["serde-human-readable"] }
tokio = {version = "1", features = [ tokio = { version = "1", features = ["rt-multi-thread"], optional = true }
"macros", url = { version = "2", features = ["serde"] }
"rt-multi-thread",
], optional = true}
url = {version = "2", features = ["serde"]}
walkdir = "2" walkdir = "2"
warp = {version = "0.3", optional = true} warp = { version = "0.3", optional = true }
[features] [features]
default = ["serve", "color-eyre"] default = ["serve", "color-eyre"]

View file

@ -37,6 +37,10 @@ list of sass/scss stylesheets in the site's `sass` directory to treat as root st
base url for the various cdn url transformation features of webdog. base url for the various cdn url transformation features of webdog.
## `webdog_path`
optional custom path for webdog's static resources.
## `code_theme` ## `code_theme`
the theme to use for code blocks. valid options: `base16-ocean.dark`, `base16-eighties.dark`, `base16-mocha.dark`, `base16-ocean.light`, `InspiredGitHub`, `Solarized (dark)`, and `Solarized (light)` the theme to use for code blocks. valid options: `base16-ocean.dark`, `base16-eighties.dark`, `base16-mocha.dark`, `base16-ocean.light`, `InspiredGitHub`, `Solarized (dark)`, and `Solarized (light)`

View file

@ -2,18 +2,18 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use eyre::{eyre, Context, OptionExt}; use eyre::{Context, OptionExt, eyre};
use lol_html::{element, html_content::ContentType, HtmlRewriter, Settings}; use lol_html::{HtmlRewriter, Settings, element, html_content::ContentType};
use rayon::prelude::*; use rayon::prelude::*;
use serde::Serialize; use serde::Serialize;
use syntect::{highlighting::ThemeSet, parsing::SyntaxSet}; use syntect::{highlighting::ThemeSet, parsing::SyntaxSet};
use tera::Tera; use tera::Tera;
use url::Url; use url::Url;
use crate::{resource::ResourceBuilder, util, PageMetadata, Site, ROOT_PATH, SASS_PATH}; use crate::{PageMetadata, ROOT_PATH, SASS_PATH, Site, resource::ResourceBuilder, util};
/// Path for static webdog resources included with the site build. /// Default path for static webdog resources included with the site build.
const WEBDOG_PATH: &str = "webdog"; const WEBDOG_DEFAULT_PATH: &str = "webdog";
/// Struct containing data to be sent to templates when rendering them. /// Struct containing data to be sent to templates when rendering them.
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -96,7 +96,13 @@ impl SiteBuilder {
std::fs::create_dir(&self.build_path).wrap_err("Failed to create build directory")?; std::fs::create_dir(&self.build_path).wrap_err("Failed to create build directory")?;
} }
let webdog_path = self.build_path.join(WEBDOG_PATH); let webdog_path = self.build_path.join(
self.site
.config
.webdog_path
.clone()
.unwrap_or_else(|| WEBDOG_DEFAULT_PATH.to_string()),
);
std::fs::create_dir(&webdog_path)?; std::fs::create_dir(&webdog_path)?;
std::fs::write( std::fs::write(
webdog_path.join("webdog.js"), webdog_path.join("webdog.js"),
@ -155,6 +161,7 @@ impl SiteBuilder {
} }
/// Function to rewrite HTML wow. /// Function to rewrite HTML wow.
#[allow(clippy::too_many_arguments)]
pub fn rewrite_html( pub fn rewrite_html(
&self, &self,
html: String, html: String,
@ -163,6 +170,7 @@ impl SiteBuilder {
scripts: &[String], scripts: &[String],
styles: &[String], styles: &[String],
is_partial: bool, is_partial: bool,
webdog_path: &str,
) -> eyre::Result<String> { ) -> eyre::Result<String> {
use kuchikiki::traits::*; use kuchikiki::traits::*;
@ -251,7 +259,9 @@ impl SiteBuilder {
); );
} }
el.append( el.append(
r#"<script type="text/javascript" src="/webdog/webdog.js" defer></script>"#, &format!(
r#"<script type="text/javascript" src="/{webdog_path}/webdog.js" defer></script>"#
),
ContentType::Html, ContentType::Html,
); );
if self.serving { if self.serving {
@ -377,6 +387,12 @@ impl SiteBuilder {
&page_metadata.scripts, &page_metadata.scripts,
&page_metadata.styles, &page_metadata.styles,
page_metadata.is_partial, page_metadata.is_partial,
&self
.site
.config
.webdog_path
.clone()
.unwrap_or_else(|| WEBDOG_DEFAULT_PATH.to_string()),
)?; )?;
if let Some(data) = extra { if let Some(data) = extra {

View file

@ -1,5 +1,4 @@
use itertools::Itertools; use lol_html::{RewriteStrSettings, element};
use lol_html::{element, RewriteStrSettings};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{builder::SiteBuilder, resource::ResourceTemplateData}; use crate::{builder::SiteBuilder, resource::ResourceTemplateData};
@ -111,11 +110,13 @@ fn resource_list_outside(
) )
.map(|ts| (id, v, ts)) .map(|ts| (id, v, ts))
}) })
.map_ok(|(id, v, ts)| ResourceTemplateData { .map(|v| {
v.map(|(id, v, ts)| ResourceTemplateData {
resource: v, resource: v,
id: id.clone(), id: id.clone(),
readable_timestamp: ts, readable_timestamp: ts,
}) })
})
.collect::<eyre::Result<Vec<_>>>()?, .collect::<eyre::Result<Vec<_>>>()?,
})?, })?,
)?; )?;

View file

@ -49,6 +49,8 @@ pub struct SiteConfig {
pub sass_styles: Vec<PathBuf>, pub sass_styles: Vec<PathBuf>,
/// URL to the CDN used for the site's images. /// URL to the CDN used for the site's images.
pub cdn_url: Url, pub cdn_url: Url,
/// The path to output webdog static resources to. Defaults to "webdog"
pub webdog_path: Option<String>,
/// The theme to use for the site's code blocks. /// The theme to use for the site's code blocks.
/// TODO: dark/light themes /// TODO: dark/light themes
/// TODO: export themes as CSS instead of styling HTML directly /// TODO: export themes as CSS instead of styling HTML directly
@ -73,6 +75,7 @@ impl SiteConfig {
build: None, build: None,
sass_styles: vec!["index.scss".into()], sass_styles: vec!["index.scss".into()],
cdn_url, cdn_url,
webdog_path: None,
code_theme: "base16-ocean.dark".to_string(), code_theme: "base16-ocean.dark".to_string(),
resources: Default::default(), resources: Default::default(),
} }

View file

@ -4,17 +4,16 @@ use std::{
}; };
use eyre::Context; use eyre::Context;
use itertools::Itertools; use rss::{ChannelBuilder, ItemBuilder, validation::Validate};
use rss::{validation::Validate, ChannelBuilder, ItemBuilder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::{format_description::well_known::Rfc2822, OffsetDateTime}; use time::{OffsetDateTime, format_description::well_known::Rfc2822};
use crate::{ use crate::{
PageMetadata,
builder::SiteBuilder, builder::SiteBuilder,
frontmatter::FrontMatterRequired, frontmatter::FrontMatterRequired,
link_list::Link, link_list::Link,
util::{self, format_timestamp}, util::{self, format_timestamp},
PageMetadata,
}; };
/// Metadata for resources. /// Metadata for resources.
@ -319,7 +318,7 @@ impl ResourceBuilder {
let page_max = list.len() / items_per_page + (list.len() % items_per_page).min(1); let page_max = list.len() / items_per_page + (list.len() % items_per_page).min(1);
let mut previous = None; let mut previous = None;
let mut next; let mut next;
for (page, iter) in list.iter().chunks(items_per_page).into_iter().enumerate() { for (page, iter) in list.chunks(items_per_page).enumerate() {
next = (page + 1 != page_max).then_some(page + 2); next = (page + 1 != page_max).then_some(page + 2);
let out = builder.build_page_raw( let out = builder.build_page_raw(
PageMetadata { PageMetadata {
@ -329,7 +328,7 @@ impl ResourceBuilder {
}, },
"", "",
ResourceListTemplateData { ResourceListTemplateData {
resources: iter.copied().collect(), resources: iter.to_vec(),
tag, tag,
rss_enabled: config.rss.is_some(), rss_enabled: config.rss.is_some(),
page: page + 1, page: page + 1,
@ -372,7 +371,7 @@ impl ResourceBuilder {
// Build list of tags // Build list of tags
{ {
let links = tags let mut links: Vec<_> = tags
.iter() .iter()
.map(|(tag, data)| { .map(|(tag, data)| {
let count = data.len(); let count = data.len();
@ -384,9 +383,9 @@ impl ResourceBuilder {
count, count,
) )
}) })
.sorted_by(|(_, a), (_, b)| b.cmp(a))
.map(|(l, _)| l)
.collect(); .collect();
links.sort_by(|(_, a), (_, b)| b.cmp(a));
let links = links.into_iter().map(|(l, _)| l).collect();
let out = crate::link_list::render_basic_link_list( let out = crate::link_list::render_basic_link_list(
builder, builder,
&self.config.tag_list_template, &self.config.tag_list_template,