This commit is contained in:
Shadowfacts 2025-01-12 19:04:58 -05:00
parent de9291cd50
commit 1e772a97e2
9 changed files with 145 additions and 60 deletions

View File

@ -31,10 +31,6 @@ a:visited {
color: var(--link-color); color: var(--link-color);
text-decoration-thickness: 2px; text-decoration-thickness: 2px;
text-underline-offset: 2px; text-underline-offset: 2px;
/* color: white;
text-decoration: none;
background-color: black;
padding: 0 4px; */
} }
a:hover { a:hover {
text-decoration-thickness: 3px; text-decoration-thickness: 3px;
@ -159,7 +155,7 @@ header {
} }
} }
.article-title { .headline {
// balance the number of words per line // balance the number of words per line
text-wrap: balance; text-wrap: balance;
@ -174,12 +170,16 @@ header {
text-wrap: pretty; text-wrap: pretty;
} }
.article-content { .body-content {
font-size: 1.25rem; font-size: 1.25rem;
// Chrome only, but minimizes orphan words // Chrome only, but minimizes orphan words
text-wrap: pretty; text-wrap: pretty;
} }
.read-more {
font-style: italic;
}
.header-anchor { .header-anchor {
text-decoration: none; text-decoration: none;
font-size: 1rem; font-size: 1rem;
@ -314,7 +314,7 @@ aside.inline {
footer { footer {
margin-bottom: var(--page-vertical-margin); margin-bottom: var(--page-vertical-margin);
font-style: italic; font-style: italic;
font-size: 1.5rem; font-size: 1.25rem;
ul { ul {
padding: 0; padding: 0;
list-style: none; list-style: none;
@ -326,3 +326,23 @@ footer {
font-size: 1rem; font-size: 1rem;
} }
} }
.webring {
background: linear-gradient(
90deg,
#855988,
#6b4984,
#483475,
#2b2f77,
#141852
);
background-clip: text;
font-variant: small-caps;
font-weight: bold;
a {
color: transparent;
&::after {
display: none;
}
}
}

View File

@ -1,9 +1,68 @@
{% extends "default" %} {% extends "default" %}
{% block footer_vars %}
{% set footer_links = false %}
{% endblock %}
{% block content -%} {% block content -%}
<a href="{{ latest_post_permalink }}"> <h2 class="headline">About Me</h2>
{{ latest_post.metadata.title }} <div class="body-content">
</a> <p class="about">
Hi.
My day job is building software for people who pay me to build software.
In the evenings, I build software for people who don&rsquo;t pay me to build software (myself included).
I mostly write about building software on here.
That probably doesn&rsquo;t come as a shock.
</p>
</div>
<h2 class="headline">
Latest Post:
<a href="{{ latest_post_permalink }}">
{{ latest_post.metadata.title }}
</a>
</h2>
<p class="article-meta">
Published on
<time itemprop="datePublished" datetime="{{ latest_post.metadata.date | iso_datetime }}">
{{ latest_post.metadata.date | pretty_date }},
</time>
in
{% for tag in latest_post.metadata.tags %}
<span itemprop="articleSection">
<a href="/{{ tag.slug }}/">{{ tag.name }}</a>{% if loop.last %}.{% else %},{% endif %}
</span>
{% endfor %}
{{ latest_post.word_count | reading_time }} minute read.
</p>
<div class="body-content">
{% if latest_post.excerpt %}
{{ latest_post.excerpt }}
<p>
<a href="{{ latest_post_permalink }}" class="read-more">Read more…</a>
</p>
{% else %}
{{ latest_post_content }}
{% endif %}
</div>
<h2 class="headline">Other Things</h2>
{%- endblock %} {%- endblock %}
{% block footer_links %}
{% set additional_links = [
"Book Log", "/books/",
"TV Commentary", "/tv/",
"Modding Tutorials", "/tutorials/",
] %}
{% endblock %}
{% block after_footer_links %}
<p class="webring">
<a href="https://metro.bieszczady.pl">Metro Bieszczady Webring</a>
<a title="previous page in webring" href="https://metro.bieszczady.pl/cgi-bin/webring?action=previous&amp;from=shadowfacts"></a>
<a title="next page in webring" href="https://metro.bieszczady.pl/cgi-bin/webring?action=next&amp;from=shadowfacts"></a>
</p>
{% endblock %}

View File

@ -31,7 +31,7 @@
<article itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting"> <article itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
<meta itemprop="mainEntityOfPage" content="https://{{ _domain }}{{ _permalink }}"> <meta itemprop="mainEntityOfPage" content="https://{{ _domain }}{{ _permalink }}">
<h1 class="article-title" itemprop="name headline"> <h1 class="headline" itemprop="name headline">
{% if metadata.html_title %} {% if metadata.html_title %}
{{ metadata.html_title }} {{ metadata.html_title }}
{% else %} {% else %}
@ -51,7 +51,7 @@
{% endfor %} {% endfor %}
{{ word_count | reading_time }} minute read. {{ word_count | reading_time }} minute read.
</p> </p>
<div class="article-content" itemprop="articleBody"> <div class="body-content" itemprop="articleBody">
{{ content }} {{ content }}
</div> </div>
</article> </article>

View File

@ -49,11 +49,22 @@
<footer> <footer>
<div class="container"> <div class="container">
<ul> <ul>
<li><a href="/archive/">Archive</a></li> {% set footer_links = [
<li><a href="/elsewhere/">Contact</a></li> "Archive", "/archive/",
<!-- TODO: webring --> "Colophon", "/colophon/",
<li>Generated on {{ _generated_at | pretty_date }}, by <a href="https://git.shadowfacts.net/shadowfacts/v7">v7</a>.</li> "Contact", "/elsewhere/"
] %}
{% block footer_links %}
{% set additional_links = [] %}
{% endblock %}
{% set sorted_links = footer_links | concat(with=additional_links) | zip | sort(attribute="0") %}
{% for link in sorted_links %}
<li><a href="{{ link.1 }}">{{ link.0 }}</a></li>
{% endfor %}
</ul> </ul>
{% block after_footer_links %}
{% endblock %}
<p>Generated on {{ _generated_at | pretty_date }}, by <a href="https://git.shadowfacts.net/shadowfacts/v7">v7</a>.</p>
</div> </div>
</footer> </footer>

View File

@ -14,6 +14,8 @@ use compute_graph::{
}; };
use content::{AnyContent, HtmlContent, Post, PostContent}; use content::{AnyContent, HtmlContent, Post, PostContent};
use log::error; use log::error;
use once_cell::sync::Lazy;
use regex::Regex;
use tera::Context; use tera::Context;
use super::{ use super::{
@ -192,10 +194,22 @@ struct ConvertToHTML(Input<ReadPostOutput>);
impl Rule for ConvertToHTML { impl Rule for ConvertToHTML {
type Output = Option<Post<HtmlContent>>; type Output = Option<Post<HtmlContent>>;
fn evaluate(&mut self) -> Self::Output { fn evaluate(&mut self) -> Self::Output {
self.input_0().clone().map(|post| post.to_html()) self.input_0().clone().map(|post| {
let html_post = post.to_html();
let excerpt = find_excerpt(&html_post);
html_post.with_excerpt(excerpt)
})
} }
} }
static EXCERPT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("<!--\\s*excerpt-end\\s*-->").unwrap());
fn find_excerpt(post: &Post<HtmlContent>) -> Option<String> {
EXCERPT_REGEX
.find(&post.content.html())
.map(|m| post.content.html()[0..m.start()].trim().to_owned())
}
// #[derive(InputVisitable)] // #[derive(InputVisitable)]
// struct MakeExtractMetadatas { // struct MakeExtractMetadatas {
// posts: DynamicInput<ReadPostOutput>, // posts: DynamicInput<ReadPostOutput>,

View File

@ -66,6 +66,10 @@ impl<C: PostContent> Post<C> {
self.map_content(|c| c.to_html()) self.map_content(|c| c.to_html())
} }
pub fn with_excerpt(self, excerpt: Option<String>) -> Self {
Post { excerpt, ..self }
}
pub fn permalink(&self) -> String { pub fn permalink(&self) -> String {
format!("/{}/{}/", self.metadata.date.year(), self.slug) format!("/{}/{}/", self.metadata.date.year(), self.slug)
} }

View File

@ -28,6 +28,7 @@ pub fn make_graph(
empty_templates.register_filter("pretty_date", filters::pretty_date); empty_templates.register_filter("pretty_date", filters::pretty_date);
empty_templates.register_filter("pretty_datetime", filters::pretty_datetime); empty_templates.register_filter("pretty_datetime", filters::pretty_datetime);
empty_templates.register_filter("reading_time", filters::reading_time); empty_templates.register_filter("reading_time", filters::reading_time);
empty_templates.register_filter("zip", filters::zip);
let empty_templates = builder.add_value(empty_templates); let empty_templates = builder.add_value(empty_templates);
let default_path = content_path("layout/default.html"); let default_path = content_path("layout/default.html");
@ -214,21 +215,7 @@ pub mod filters {
use chrono::{DateTime, Datelike, Local}; use chrono::{DateTime, Datelike, Local};
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use tera::{Number, Result, Value}; use tera::{Error, Number, Result, Value};
// pub fn iso_date(date: &NaiveDate) -> String {
// Utc::from_utc_datetime(&Utc, &date.and_hms_opt(12, 0, 0).unwrap())
// .format("%+:0")
// .to_string()
// }
// pub fn iso_datetime<Tz>(datetime: &DateTime<Tz>) -> String
// where
// Tz: TimeZone,
// Tz::Offset: Display,
// {
// datetime.format("%+:0").to_string()
// }
pub fn iso_datetime(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> { pub fn iso_datetime(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> {
let date = DateTime::<Local>::deserialize(value)?; let date = DateTime::<Local>::deserialize(value)?;
@ -256,9 +243,7 @@ pub mod filters {
} }
pub fn pretty_datetime(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> { pub fn pretty_datetime(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> {
dbg!(value);
let datetime = DateTime::<Local>::deserialize(value)?; let datetime = DateTime::<Local>::deserialize(value)?;
dbg!(&datetime);
let s = format!( let s = format!(
"{} {}", "{} {}",
datetime.format("%-I:%M:%S %p"), datetime.format("%-I:%M:%S %p"),
@ -273,4 +258,22 @@ pub mod filters {
let minutes = (words as f64 / wpm).max(1.0).round(); let minutes = (words as f64 / wpm).max(1.0).round();
Ok(Value::Number(Number::from_f64(minutes).unwrap())) Ok(Value::Number(Number::from_f64(minutes).unwrap()))
} }
pub fn zip(value: &Value, _args: &HashMap<String, Value>) -> Result<Value> {
let values = value
.as_array()
.ok_or_else(|| Error::msg("zip filter requires an array"))?;
if values.len() % 2 == 1 {
return Err(Error::msg("zipped array must have even length"));
}
let len = values.len() / 2;
let zipped = (0..len)
.map(|i| {
let first = values[i * 2].clone();
let second = values[i * 2 + 1].clone();
Value::Array(vec![first, second])
})
.collect();
Ok(Value::Array(zipped))
}
} }

View File

@ -2,7 +2,6 @@ pub mod file_watcher;
pub mod highlight; pub mod highlight;
pub mod one_more; pub mod one_more;
pub mod slugify; pub mod slugify;
pub mod templates;
pub mod word_count; pub mod word_count;
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};

View File

@ -1,25 +0,0 @@
// static DOMAIN: Lazy<String> =
// Lazy::new(|| std::env::var("DOMAIN").unwrap_or("shadowfacts.net".to_owned()));
// static CB: Lazy<u64> = Lazy::new(|| {
// SystemTime::now()
// .duration_since(SystemTime::UNIX_EPOCH)
// .unwrap()
// .as_secs()
// });
// static GENERATED_AT: Lazy<DateTime<Local>> = Lazy::new(|| Local::now());
// pub trait TemplateCommon {
// fn domain() -> String {
// DOMAIN.to_owned()
// }
// fn stylesheet_cache_buster() -> u64 {
// *CB
// }
// fn generated_at() -> &'static DateTime<Local> {
// &*GENERATED_AT
// }
// }