diff --git a/content/links/rasjonell-dashbrew.md b/content/links/rasjonell-dashbrew.md
new file mode 100644
index 0000000..ea8a52f
--- /dev/null
+++ b/content/links/rasjonell-dashbrew.md
@@ -0,0 +1,8 @@
++++
+title = "dashbrew: TUI dashboard builder"
+authors = [ "Gurgen (rasjonell)" ]
+date = "2026-05-03T14:30:08-03:00"
+[extra]
+url = "https://github.com/rasjonell/dashbrew"
++++
+
diff --git a/content/notes/feed-and-links-updates.md b/content/notes/feed-and-links-updates.md
index c7e2094..c11e736 100644
--- a/content/notes/feed-and-links-updates.md
+++ b/content/notes/feed-and-links-updates.md
@@ -1,6 +1,9 @@
+++
title = "[Meta] Feed & link updates"
date = "2026-04-11T14:04:07-03:00"
+
+[taxonomies]
+tags = [ "meta" ]
+++
I've changed where the links RSS feed points its main URL to, linking directly to what I'm sharing instead of a page with nothing where you just click the actual link.
diff --git a/content/notes/shells-timeline.md b/content/notes/shells-timeline.md
index 6b18aba..6a21566 100644
--- a/content/notes/shells-timeline.md
+++ b/content/notes/shells-timeline.md
@@ -1,6 +1,9 @@
+++
title = "A timeline of Unix shells"
date = 2025-02-24T14:34:25-03:00
+
+[taxonomies]
+tags = [ "unix", "shell" ]
+++
For some reason, I really like timeline visualizations, meaning a graph with events listed along a temporal axis with the dates in which they happened. I remember using tape to glue several pieces of paper on the wall as a kid to make a long timeline of world history events.
diff --git a/content/notes/tags.md b/content/notes/tags.md
new file mode 100644
index 0000000..143415b
--- /dev/null
+++ b/content/notes/tags.md
@@ -0,0 +1,13 @@
++++
+title = "[Meta] Tags, RSS explosion and Octothorpe Protocol"
+date = "2026-05-02T17:53:18-03:00"
+
+[taxonomies]
+tags = [ "meta", "rss", "octothorpe" ]
++++
+
+On more updates to this blog, we now have tags to browse specific topics and dedicated RSS feeds. This means this blog now serves an obscene amount of RSS feeds which is really appropriate considering RSS is adorable.
+
+To complement the tags, I've added support for the [Octothorpe Protocol](https://octothorp.es/), which is a way to aggregate hashtags and other relations through an indexing server that allows you to discover and curate related content.
+
+I've also added webmention metadata to be able to receive it, but haven't implemented any way to display it yet.
diff --git a/content/posts/notice-on-rss-feeds.md b/content/posts/notice-on-rss-feeds.md
index 75cb0b6..eb13c5d 100644
--- a/content/posts/notice-on-rss-feeds.md
+++ b/content/posts/notice-on-rss-feeds.md
@@ -1,6 +1,9 @@
+++
title = "[Meta] Notice on RSS feeds"
date = 2024-09-01
+
+[taxonomies]
+tags = [ "meta" ]
+++
For those subscribing to this blog's RSS feeds:
diff --git a/content/posts/notice-on-rss-feeds.pt.md b/content/posts/notice-on-rss-feeds.pt.md
index 0e3e351..faeb206 100644
--- a/content/posts/notice-on-rss-feeds.pt.md
+++ b/content/posts/notice-on-rss-feeds.pt.md
@@ -1,6 +1,9 @@
+++
title = "[Meta] Aviso sobre os feeds RSS"
date = 2024-09-01
+
+[taxonomies]
+tags = [ "meta" ]
+++
Para quem está assinando o feed RSS desse blog:
diff --git a/scripts/analyze-css.sh b/scripts/analyze-css.sh
new file mode 100755
index 0000000..fc5d392
--- /dev/null
+++ b/scripts/analyze-css.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env sh
+
+exclusions='^(index-user-controls|subpage-user-controls|title|taxon-title)$'
+
+set -eu
+git_root=$(git rev-parse --show-toplevel)
+style="$git_root/static/assets/css/style.css"
+
+diff_classes() {
+ template_classes=$(mktemp -u)
+ mkfifo "$template_classes"
+
+ # TODO support multiple classes in the same class= key
+ grep -Eoh 'class="[a-zA-Z0-9-]+"' "$git_root/templates/"* |
+ sed -E 's/class=|"//g' |
+ grep -Ev "${exclusions:-f481b6cd58e}" |
+ sort | uniq \
+ > "$template_classes" &
+
+ css_classes=$(mktemp -u)
+ mkfifo "$css_classes"
+
+ grep -Eo '\.[a-zA-Z-]+[0-9-]*' "$style" |
+ sed 's/\.//g' |
+ grep -v '^ttf$' |
+ grep -Ev "${exclusions:-f481b6cd58e}" |
+ sort | uniq \
+ > "$css_classes" &
+
+ echo "Classes diff:"
+ if ! diff "$template_classes" "$css_classes" > /dev/null; then
+ echo '< html | css >'
+ diff --color=auto "$template_classes" "$css_classes"
+ fi
+ [ -z "$exclusions" ] || echo "Exclusions: $exclusions"
+
+ rm -f "$template_classes"
+ rm -f "$css_classes"
+
+}
+
+purge_css() {
+
+ purged_css=$(
+ purgecss \
+ --content "$git_root/templates/*.html" \
+ --css "$style" \
+ --rejected |
+ jq '.[0].rejected'
+ )
+
+ purged_css_count=$(printf '%s' "$purged_css" | jq '. | length')
+
+ if [ "$purged_css_count" -gt 0 ]; then
+ printf '\n%s\n' purgecss:
+ printf '%s' "$purged_css" | jq '.'
+ exit 1
+ fi
+}
+
+diff_classes
+
+printf '\n%s\n' csskit:
+csskit check "$git_root/scripts/lint.cks" "$style"
+
+purge_css
diff --git a/scripts/indexed.cache b/scripts/indexed.cache
new file mode 100644
index 0000000..cc7ab6b
--- /dev/null
+++ b/scripts/indexed.cache
@@ -0,0 +1,13 @@
+https://blog.jutty.dev/posts/half-an-year-on-alpine/
+https://blog.jutty.dev/posts/self-hosting-patch/
+https://blog.jutty.dev/posts/notice-on-rss-feeds/
+https://blog.jutty.dev/posts/unwinding/
+https://blog.jutty.dev/posts/introducing-tori/
+https://blog.jutty.dev/posts/void-on-zfs/
+https://blog.jutty.dev/posts/meeting-the-bsd-family/
+https://blog.jutty.dev/notes/tags/
+https://blog.jutty.dev/notes/feed-and-links-updates/
+https://blog.jutty.dev/notes/waypipe/
+https://blog.jutty.dev/notes/shells-timeline/
+https://blog.jutty.dev/notes/enjoying-alpine/
+https://blog.jutty.dev/notes/notes/
diff --git a/scripts/lint.cks b/scripts/lint.cks
new file mode 100644
index 0000000..2612135
--- /dev/null
+++ b/scripts/lint.cks
@@ -0,0 +1,31 @@
+:prefixed {
+ Level: error;
+ diagnostic: "Avoid prefixed properties";
+}
+
+style-value[name="color"]:not(:computed) {
+ level: error;
+ diagnostic: "Avoid color literals";
+}
+
+declaration:important {
+ level: error;
+ diagnostic: "Avoid !important";
+}
+
+declaration:unknown {
+ level: error;
+ diagnostic: "Unknown property";
+}
+
+declaration:empty {
+ level: error;
+ diagnostic: "Empty ruleset";
+}
+
+@stat --total-size { type: bytes; }
+:root { collect: --total-size; }
+
+/*
+ vi: ft=css
+*/
diff --git a/scripts/octothorpe-index.sh b/scripts/octothorpe-index.sh
new file mode 100755
index 0000000..3486b6c
--- /dev/null
+++ b/scripts/octothorpe-index.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env sh
+
+set -eu
+
+root=https://blog.jutty.dev
+cache=indexed.cache
+request_pause=7 # 1 second above rate limit of 10/minute
+
+touch "$cache"
+zola -r .. build
+feed=$(cat ../public/atom.xml)
+
+filter() {
+ kind="$1"
+ printf '%s' "$feed" | grep "$root/$kind/.*" |
+ grep -v '/drafts/' | sed 's|\s*\(.*\)|\1|'
+}
+
+fetch() {
+ url="$1"
+ curl -sSLf "https://octothorp.es/get/pages/posted?s=$url"
+ sleep "$request_pause"
+}
+
+index() {
+ urls="$1"
+ for url in $urls; do
+ if grep -qF "$url" "$cache"; then
+ echo Cached: "$url"
+ else
+ response=$(fetch "$url")
+ length=$(printf '%s' "$response" | jq '.[] | length')
+
+ if [ "$length" -gt 0 ]; then
+ echo Already indexed: "$url"
+ printf '%s\n' "$url" >> "$cache"
+ else
+ echo Indexing: "$url"
+ response=$(curl -sSLf -X POST https://octothorp.es/index \
+ -H "Origin: https://blog.jutty.dev" \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -d "uri=$url"
+ )
+ if [ "$(printf '%s' "$response" | jq -r .status)" = success ]; then
+ echo Indexed: "$url"
+ printf '%s\n' "$url" >> "$cache"
+ else
+ echo Error: "$response"
+ fi
+ fi
+ fi
+ done
+}
+
+index "$(filter posts)"
+index "$(filter notes)"
+#links=$(filter links)
diff --git a/static/assets/css/style.css b/static/assets/css/style.css
index 3fc5cea..5cc2761 100644
--- a/static/assets/css/style.css
+++ b/static/assets/css/style.css
@@ -5,7 +5,7 @@
@font-face {
font-family: 'Mononoki';
- src: url('../fonts/Mononoki.ttf'), format('truetype');
+ src: url('../fonts/Mononoki.ttf') format('truetype');
}
html {
@@ -79,7 +79,7 @@ a.nav-tags-link {
a.nav-tags-link:hover {
color: light-dark(#0aa, #0ff);
background: light-dark(#ddd, #002);
- text-decoration-color: none;
+ text-decoration: none;
}
#language-selector {
@@ -117,7 +117,7 @@ ul {
list-style: none;
}
-div.contet-metadata {
+div.content-metadata {
display: inline;
}
@@ -132,11 +132,6 @@ div.content-tags {
margin-left: 15px;
}
-.link-page-metadata div.content-tags {
- margin-left: 10px;
- margin-top: 0px;
-}
-
.content-tags span {
color: light-dark(#063, #6db);
}
@@ -231,37 +226,31 @@ main {
border: 2px dotted #888;
border-radius: 12px;
margin: 0 20px;
+ padding: 20px 5px;
}
.link-title {
+ margin-top: 0;
margin-bottom: 5px;
}
-.post-date, .link-date {
+.content-date {
color: #777;
font-size: 14px;
margin-top: 0px;
text-decoration-thickness: 0.1px;
}
-.post-date {
+div.content-date {
display: inline;
}
-.link-data {
- margin-top: 0
-}
-
-.link-data .link-author {
- margin-right: 20px;
-}
-
.content-metadata .link-author {
margin-left: 30px;
}
-article .post-date {
- margin-left: 30px;
+article .content-date {
+ margin-left: 15px;
}
p {
@@ -269,7 +258,7 @@ p {
line-height: 1.7em;
}
-.article p {
+article p {
margin: 25px 0px;
}
@@ -385,7 +374,7 @@ footer {
/* narrow */
@media (max-width: 650px) {
- .article {
+ article {
margin: 0 20px;
}
@@ -409,8 +398,8 @@ footer {
margin-left: 20px;
}
- .post-date { display: block; }
- .link-date { display: block; }
+ .content-date { display: block; }
+ .content-date { display: block; }
.link-author { display: block; }
ul#posts li:before, ul#links li:before {
@@ -431,15 +420,25 @@ footer {
padding-top: 0px;
}
- .link-box .link-date, .link-box .link-author {
+ .link-box .content-date, .link-box .link-author {
display: block;
margin-bottom: 10px;
}
- .link-box .link-date, .link-box div.content-tags {
+ .link-box .content-date, .link-box div.content-tags {
margin-left: 30px;
}
+ div.content-tags {
+ display: inline-block;
+ margin-left: 30px;
+ }
+
+ div.content-date {
+ display: inline-block;
+ margin-bottom: 10px;
+ }
+
}
/* tablet */
@@ -468,7 +467,7 @@ footer {
margin-left: 50px;
}
- .article {
+ article {
margin: 0 45px;
}
}
@@ -578,7 +577,7 @@ footer {
color: #bbb;
}
- .post-date {
+ .content-date {
color: #888;
}
diff --git a/templates/base.html b/templates/base.html
index 4bcca43..0db1912 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -13,70 +13,69 @@
{%- endif %}
-
-
+
+
- {%- set title_tail = " ~ " ~ config.title %}
+ {%- set title_tail = " ~ " ~ config.title %}
- {%- if page.title %}
- {%- set title = page.title ~ title_tail %}
- {%- elif section.title %}
- {%- set title = section.title ~ title_tail %}
- {%- else %}
- {%- set title = config.title %}
- {%- endif %}
+ {%- if page.title %}
+ {%- set title = page.title ~ title_tail %}
+ {%- elif section.title %}
+ {%- set title = section.title ~ title_tail %}
+ {%- else %}
+ {%- set title = config.title %}
+ {%- endif %}
- {{ title }}
+ {{ title }}
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
- {%- set description = page.content | default(value=config.description) | striptags | regex_replace(pattern="\n", rep=" ") | truncate(length=120) %}
+ {%- set description = page.content | default(value=config.description) | striptags | regex_replace(pattern="\n", rep=" ") | truncate(length=120) %}
-
-
-
-
-
-
+
+
+
+
+
+
-
- {% if page.taxonomies -%}
- {%- for taxonomy, terms in page.taxonomies -%}
- {%- if taxonomy == "tags" -%}
- {%- for tag in terms -%}
-
- {% endfor -%}
- {%- endif -%}
- {% endfor -%}
- {%- endif -%}
+
+ {%- block octo_head -%}{%- endblock %}
+ {% if page.taxonomies -%}
+ {%- for taxonomy, terms in page.taxonomies -%}
+ {%- if taxonomy == "tags" -%}
+ {%- for tag in terms -%}
+
+ {% endfor -%}
+ {%- endif -%}
+ {% endfor -%}
+ {%- endif %}
- {%- block extra_head %}{% endblock %}
+ {%- block rss -%}
+ {%- if lang == "pt" %}
+ {%- set locale_all_content = "Todo o conteúdo" %}
+ {%- set locale_language_name = "Português" %}
+ {% else %}
+ {%- set locale_all_content = "All content" %}
+ {%- set locale_language_name = "English" %}
+ {%- endif %}
+
+
+
+
+ {%- endblock %}
- {%- block rss -%}
- {%- if lang == "pt" %}
- {%- set locale_all_content = "Todo o conteúdo" %}
- {%- set locale_language_name = "Português" %}
- {% else %}
- {%- set locale_all_content = "All content" %}
- {%- set locale_language_name = "English" %}
- {%- endif -%}
-
-
-
-
- {%- endblock %}
-
-
-
+
+
diff --git a/templates/content.html b/templates/content.html
index 6b5a97b..5894efe 100644
--- a/templates/content.html
+++ b/templates/content.html
@@ -1,15 +1,17 @@
{% extends "base.html" %}
-{%- block extra_head %}
+{%- block octo_head %}
{%- endblock -%}
{% block content %}
-
- {{ page.title }}
+
+ {{ page.title }}
-
+
+
+
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
tags:
diff --git a/templates/index.html b/templates/index.html
index 2def7cb..7aa6e25 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -32,7 +32,7 @@
{% for note in notes.pages %}
-
- {{ note.date | date(format="%Y-%m-%d") }}
+ {{ note.date | date(format="%Y-%m-%d") }}
{{ note.title }}
{% if loop.index == 5 %}{% break %}{% endif %}
@@ -56,7 +56,7 @@
{% for link in links.pages %}
-
- {{ link.date | date(format="%Y-%m-%d") }}
+ {{ link.date | date(format="%Y-%m-%d") }}
{% for post in posts.pages %}
-
- {{ post.date | date(format="%Y-%m-%d") }}
+ {{ post.date | date(format="%Y-%m-%d") }}
{{ post.title }}
{% if loop.index == 5 %}{% break %}{% endif %}
diff --git a/templates/link-page.html b/templates/link-page.html
index 06dcfc2..a2d43ac 100644
--- a/templates/link-page.html
+++ b/templates/link-page.html
@@ -5,9 +5,9 @@
-
+
{% if lang == "pt" %}Por{% else %}By{% endif %} {{ page.authors | join(sep=", ") }}
-
+
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
tags:
@@ -20,7 +20,8 @@
{%- endif -%}
{% endfor -%}
+
- {%- if page.extra.comment %}
{{ page.extra.comment }}
{% endif %}
- {{- page.content | safe }}
+{%- if page.extra.comment %}
{{ page.extra.comment }}
{% endif %}
+{{- page.content | safe }}
{%- endblock content %}
diff --git a/templates/links.html b/templates/links.html
index a44a683..ffe538a 100644
--- a/templates/links.html
+++ b/templates/links.html
@@ -8,7 +8,7 @@
-
{%- if page.content -%}
{%- set portuguese_page = get_page(path="links/" ~ page.slug ~ ".pt.md") %}
-
{%- else %}
-
+
{%- endif %}
{{ page.title }}
by {{ page.authors | join(sep=", ") }}
diff --git a/templates/notes.html b/templates/notes.html
index a17558a..7221e29 100644
--- a/templates/notes.html
+++ b/templates/notes.html
@@ -5,7 +5,7 @@
{%- for page in section.pages %}
-
-
+
{{ page.title }}
{%- endfor %}
diff --git a/templates/posts.html b/templates/posts.html
index a17558a..7221e29 100644
--- a/templates/posts.html
+++ b/templates/posts.html
@@ -5,7 +5,7 @@
{%- for page in section.pages %}
-
-
+
{{ page.title }}
{%- endfor %}