Homepage
This commit is contained in:
parent
de9291cd50
commit
1e772a97e2
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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’t pay me to build software (myself included).
|
||||||
|
I mostly write about building software on here.
|
||||||
|
That probably doesn’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&from=shadowfacts">←</a>
|
||||||
|
<a title="next page in webring" href="https://metro.bieszczady.pl/cgi-bin/webring?action=next&from=shadowfacts">→</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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>,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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};
|
||||||
|
@ -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
|
|
||||||
// }
|
|
||||||
// }
|
|
Loading…
x
Reference in New Issue
Block a user