Compare commits

..

10 commits

Author SHA1 Message Date
jultty
1f718887ca Refactor and cleanup CSS classes 2026-05-04 09:25:56 -03:00
jultty
01607ff10f Add build command to Octothorpe indexing script 2026-05-03 14:48:30 -03:00
jultty
b9046d7c55 New link: rasjonell/dashbrew 2026-05-03 14:31:28 -03:00
jultty
8a8ab76e50 Add Octothorpe indexing script 2026-05-02 22:37:34 -03:00
jultty
a8310bef46 Remove Octothorpes preload in favor of POST requests 2026-05-02 18:09:03 -03:00
jultty
c8b2d0ffbe Add retroactive meta tags 2026-05-02 17:58:37 -03:00
jultty
17e2f6908c New note: Tags, RSS explosion and Octothorpe Protocol 2026-05-02 17:55:16 -03:00
jultty
c7255b3d6e Add tags to timeline of unix shells 2026-05-02 16:59:38 -03:00
jultty
d867fad40c Add Octothorpe indexing links 2026-05-02 16:58:00 -03:00
jultty
911fa6f78e Fix tags display on mobile viewports 2026-05-02 16:45:08 -03:00
18 changed files with 310 additions and 109 deletions

View file

@ -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"
+++

View file

@ -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.

View file

@ -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.

13
content/notes/tags.md Normal file
View file

@ -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.

View file

@ -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:

View file

@ -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:

66
scripts/analyze-css.sh Executable file
View file

@ -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

13
scripts/indexed.cache Normal file
View file

@ -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/

31
scripts/lint.cks Normal file
View file

@ -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
*/

57
scripts/octothorpe-index.sh Executable file
View file

@ -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 "<id>$root/$kind/.*</id>" |
grep -v '/drafts/' | sed 's|\s*<id>\(.*\)</id>|\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)

View file

@ -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;
}

View file

@ -13,70 +13,69 @@
{%- endif %}
<head prefix="og: https://ogp.me/ns/website#">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<meta name="viewport" content="width=device-width, initial-scale=1">
{%- 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 }}</title>
<title>{{ title }}</title>
<link href="{{ get_url(path="assets/css/style.css", trailing_slash=false) }}" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="{{ get_url(path="assets/css/syntax-dark.css", trailing_slash=false) }}" media="(prefers-color-scheme: dark)" />
<link rel="stylesheet" type="text/css" href="{{ get_url(path="assets/css/syntax-light.css", trailing_slash=false) }}" media="(prefers-color-scheme: light)" />
<link href="{{ get_url(path="assets/css/style.css", trailing_slash=false) }}" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="{{ get_url(path="assets/css/syntax-dark.css", trailing_slash=false) }}" media="(prefers-color-scheme: dark)" />
<link rel="stylesheet" type="text/css" href="{{ get_url(path="assets/css/syntax-light.css", trailing_slash=false) }}" media="(prefers-color-scheme: light)" />
<link rel="icon" type="image/png" sizes="36x36" href="{{ get_url(path="assets/img/icons/kitty/kitty_32.png", trailing_slash=false) }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ get_url(path="assets/img/icons/kitty/kitty_16.png", trailing_slash=false) }}">
<link rel="icon" type="image/png" sizes="128x128" href="{{ get_url(path="assets/img/icons/kitty/kitty_circle_128.png", trailing_slash=false) }}">
<link rel="icon" type="image/png" sizes="36x36" href="{{ get_url(path="assets/img/icons/kitty/kitty_32.png", trailing_slash=false) }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ get_url(path="assets/img/icons/kitty/kitty_16.png", trailing_slash=false) }}">
<link rel="icon" type="image/png" sizes="128x128" href="{{ get_url(path="assets/img/icons/kitty/kitty_circle_128.png", trailing_slash=false) }}">
<link rel="me" href="https://tilde.zone/@jutty">
<link rel="webmention" href="https://webmention.io/jutty.dev/webmention"/>
<link rel="me" href="https://tilde.zone/@jutty">
<link rel="webmention" href="https://webmention.io/jutty.dev/webmention"/>
{%- 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) %}
<meta property="og:title" content="{{ title }}" />
<meta property="og:description" content="{{ description }}" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ current_url | default(value= get_url(path="/")) | safe }}" />
<meta property="og:image" content="{{ get_url(path="assets/img/icons/kitty/kitty_circle_192.png") }}" />
<meta name="fediverse:creator" content="@jutty@tilde.zone">
<meta property="og:title" content="{{ title }}" />
<meta property="og:description" content="{{ description }}" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ current_url | default(value= get_url(path="/")) | safe }}" />
<meta property="og:image" content="{{ get_url(path="assets/img/icons/kitty/kitty_circle_192.png") }}" />
<meta name="fediverse:creator" content="@jutty@tilde.zone">
<meta name="octo-policy" content="index">
{% if page.taxonomies -%}
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
{%- for tag in terms -%}
<link rel="octo:octothorpes" href="{{ tag }}">
{% endfor -%}
{%- endif -%}
{% endfor -%}
{%- endif -%}
<meta name="octo-policy" content="index">
{%- block octo_head -%}{%- endblock %}
{% if page.taxonomies -%}
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
{%- for tag in terms -%}
<link rel="octo:octothorpes" href="{{ tag }}">
{% 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 %}
<link rel="alternate" type="application/rss+xml" title="{{ locale_all_content }} ({{ locale_language_name }})" href="{{ get_url(path="atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="Posts ({{ locale_language_name }})" href="{{ get_url(path="posts/atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="{% if lang == "pt" %}Notas{% else %}Notes{% endif %} ({{ locale_language_name }})" href="{{ get_url(path="notes/atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="Links" href="{{ get_url(path="links/atom.xml", trailing_slash=false) }}">
{%- 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 -%}
<link rel="alternate" type="application/rss+xml" title="{{ locale_all_content }} ({{ locale_language_name }})" href="{{ get_url(path="atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="Posts ({{ locale_language_name }})" href="{{ get_url(path="posts/atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="{% if lang == "pt" %}Notas{% else %}Notes{% endif %} ({{ locale_language_name }})" href="{{ get_url(path="notes/atom.xml", lang=lang, trailing_slash=false) }}">
<link rel="alternate" type="application/rss+xml" title="Links" href="{{ get_url(path="links/atom.xml", trailing_slash=false) }}">
{%- endblock %}
<script defer src="https://stats.jutty.dev/script.js" data-website-id="f402fd3a-d5db-4121-ac20-3ca2eaea338a"></script>
<noscript><img src="https://stats.jutty.dev/p/rYMIdIgEV"></noscript>
<script defer src="https://stats.jutty.dev/script.js" data-website-id="f402fd3a-d5db-4121-ac20-3ca2eaea338a"></script>
<noscript><img src="https://stats.jutty.dev/p/rYMIdIgEV"></noscript>
</head>
<body>

View file

@ -1,15 +1,17 @@
{% extends "base.html" %}
{%- block extra_head %}
{%- block octo_head %}
{%- endblock -%}
{% block content %}
<article class="blog-post">
<h1 id="post-title">{{ page.title }}</h1>
<article>
<h1>{{ page.title }}</h1>
<div class="content-metadata">
<time class="post-date">
{{- page.date | date(format="%Y-%m-%d %H:%M:%S") -}}
</time>
<div class="content-date">
<time class="content-date">
{{- page.date | date(format="%Y-%m-%d %H:%M:%S") -}}
</time>
</div>
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
<div class="content-tags"><span>tags:</span><ul>

View file

@ -32,7 +32,7 @@
<ul id="posts">
{% for note in notes.pages %}
<li>
<span class="post-date">{{ note.date | date(format="%Y-%m-%d") }}</span>
<span class="content-date">{{ note.date | date(format="%Y-%m-%d") }}</span>
<a href="{{ note.permalink | safe }}">{{ note.title }}</a>
</li>
{% if loop.index == 5 %}{% break %}{% endif %}
@ -56,7 +56,7 @@
<ul id="posts">
{% for link in links.pages %}
<li>
<span class="post-date">{{ link.date | date(format="%Y-%m-%d") }}</span>
<span class="content-date">{{ link.date | date(format="%Y-%m-%d") }}</span>
<a href="
{%- if link.content -%}
{{ link.permalink | safe }}
@ -86,7 +86,7 @@
<ul id="posts">
{% for post in posts.pages %}
<li>
<span class="post-date">{{ post.date | date(format="%Y-%m-%d") }}</span>
<span class="content-date">{{ post.date | date(format="%Y-%m-%d") }}</span>
<a href="{{ post.permalink | safe }}">{{ post.title }}</a>
</li>
{% if loop.index == 5 %}{% break %}{% endif %}

View file

@ -5,9 +5,9 @@
<h1 class="link-title">
<a href="{{ page.extra.url }}" rel="octo:bookmarks">{{ page.title }}</a>
</h1>
<div class="content-metadata link-page-metadata">
<div class="content-metadata">
<span class="link-author">{% if lang == "pt" %}Por{% else %}By{% endif %} {{ page.authors | join(sep=", ") }}</span>
<time class="link-date"><strong>{% if lang == "pt" %}Linkado{% else %}Linked{% endif %} {{ page.date | date(format="%Y-%m-%d %H:%M:%S") }}</strong></time>
<time class="content-date"><strong>{% if lang == "pt" %}Linkado{% else %}Linked{% endif %} {{ page.date | date(format="%Y-%m-%d %H:%M:%S") }}</strong></time>
{%- for taxonomy, terms in page.taxonomies -%}
{%- if taxonomy == "tags" -%}
<div class="content-tags"><span>tags:</span><ul>
@ -20,7 +20,8 @@
{%- endif -%}
{% endfor -%}
</div>
</div>
{%- if page.extra.comment %}<p>{{ page.extra.comment }}</p>{% endif %}
{{- page.content | safe }}
{%- if page.extra.comment %}<p>{{ page.extra.comment }}</p>{% endif %}
{{- page.content | safe }}
{%- endblock content %}

View file

@ -8,7 +8,7 @@
<li>
{%- if page.content -%}
{%- set portuguese_page = get_page(path="links/" ~ page.slug ~ ".pt.md") %}
<time class="link-date"><a href="
<time class="content-date"><a href="
{%- if lang == "pt" -%}
{{ portuguese_page.permalink -}}
{%- else -%}
@ -16,7 +16,7 @@
{%- endif -%}
">{{ page.date | date(format="%Y-%m-%d") -}}</a></time>
{%- else %}
<time class="link-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
<time class="content-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
{%- endif %}
<a href="{{ page.extra.url | safe }}">{{ page.title }}</a>
<small class="link-author">by {{ page.authors | join(sep=", ") }}</small>

View file

@ -5,7 +5,7 @@
<ul id="posts">
{%- for page in section.pages %}
<li>
<time class="post-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
<time class="content-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
</li>
{%- endfor %}

View file

@ -5,7 +5,7 @@
<ul id="posts">
{%- for page in section.pages %}
<li>
<time class="post-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
<time class="content-date">{{ page.date | date(format="%Y-%m-%d") }}</time>
<a href="{{ page.permalink | safe }}">{{ page.title }}</a>
</li>
{%- endfor %}