mirror of
https://github.com/zyllian/webdog.git
synced 2025-08-02 04:18:37 -07:00
Compare commits
10 commits
e555f437c7
...
9125738498
Author | SHA1 | Date | |
---|---|---|---|
9125738498 | |||
230e22dbf8 | |||
abd591c8fa | |||
2330648b69 | |||
05d66c2143 | |||
0dbe284d57 | |||
f51af1ad74 | |||
e05f040c38 | |||
3daa80f8af | |||
b4088fb397 |
7 changed files with 532 additions and 564 deletions
986
Cargo.lock
generated
986
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
43
Cargo.toml
43
Cargo.toml
|
@ -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"]
|
||||||
|
|
|
@ -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)`
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,10 +110,12 @@ fn resource_list_outside(
|
||||||
)
|
)
|
||||||
.map(|ts| (id, v, ts))
|
.map(|ts| (id, v, ts))
|
||||||
})
|
})
|
||||||
.map_ok(|(id, v, ts)| ResourceTemplateData {
|
.map(|v| {
|
||||||
resource: v,
|
v.map(|(id, v, ts)| ResourceTemplateData {
|
||||||
id: id.clone(),
|
resource: v,
|
||||||
readable_timestamp: ts,
|
id: id.clone(),
|
||||||
|
readable_timestamp: ts,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.collect::<eyre::Result<Vec<_>>>()?,
|
.collect::<eyre::Result<Vec<_>>>()?,
|
||||||
})?,
|
})?,
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue