diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2026-08-03 13:22:07 +0300 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2026-08-03 13:22:07 +0300 |
| commit | 23821211e03cd69cf87b2f264b48e0125fcac29d (patch) | |
| tree | 42bd1a9fd7a8922a5351aed16309331d431acf7a | |
| parent | 72a42e0449a0cbbbc455b36af9f53fc293c73fdf (diff) | |
Add gemini protocol
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | Makefile | 26 | ||||
| -rwxr-xr-x | convert_gmi_html.py | 60 | ||||
| -rw-r--r-- | templates_gmi/atom.xml (renamed from templates/atom.xml) | 0 | ||||
| -rw-r--r-- | templates_gmi/base.html | 11 | ||||
| -rw-r--r-- | templates_gmi/index.html | 51 | ||||
| -rw-r--r-- | templates_gmi/macros.html | 21 | ||||
| -rw-r--r-- | templates_gmi/now.html | 10 | ||||
| -rw-r--r-- | templates_gmi/page.html | 11 | ||||
| -rw-r--r-- | templates_gmi/page_linklog.html | 7 | ||||
| -rw-r--r-- | templates_gmi/section_posts.html | 11 | ||||
| -rw-r--r-- | templates_gmi/section_projects.html (renamed from templates/section_projects.html) | 0 | ||||
| -rw-r--r-- | templates_gmi/shortcodes/fig.html | 1 | ||||
| -rw-r--r-- | templates_gmi/shortcodes/linklog.html (renamed from templates/shortcodes/linklog.html) | 0 | ||||
| -rw-r--r-- | templates_gmi/shortcodes/toc.html (renamed from templates/shortcodes/toc.html) | 0 | ||||
| -rw-r--r-- | templates_html/atom.xml | 68 | ||||
| -rw-r--r-- | templates_html/base.html (renamed from templates/base.html) | 0 | ||||
| -rw-r--r-- | templates_html/index.html (renamed from templates/index.html) | 0 | ||||
| -rw-r--r-- | templates_html/macros.html (renamed from templates/macros.html) | 0 | ||||
| -rw-r--r-- | templates_html/now.html (renamed from templates/now.html) | 0 | ||||
| -rw-r--r-- | templates_html/page.html (renamed from templates/page.html) | 0 | ||||
| -rw-r--r-- | templates_html/page_linklog.html (renamed from templates/page_linklog.html) | 0 | ||||
| -rw-r--r-- | templates_html/section_posts.html (renamed from templates/section_posts.html) | 0 | ||||
| -rw-r--r-- | templates_html/section_projects.html | 5 | ||||
| -rw-r--r-- | templates_html/shortcodes/fig.html (renamed from templates/shortcodes/fig.html) | 0 | ||||
| -rw-r--r-- | templates_html/shortcodes/linklog.html | 1 | ||||
| -rw-r--r-- | templates_html/shortcodes/toc.html | 1 |
27 files changed, 282 insertions, 6 deletions
@@ -1,6 +1,8 @@ -public/ +public* linklog.json .envrc static/giallo.css .obsidian/ .stfolder/ +.DS_Store +.certificates/ @@ -1,21 +1,37 @@ .PHONY: all -all: +all: clean @echo "Building project..." ./update-linklog-json.sh - zola build + + # Build html + ln -s templates_html templates + zola build -o public_html + unlink templates || true + + # Build gemini + ln -s templates_gmi templates + zola build -o public_gmi + find public_gmi -type f -name '*.html' -exec sh -c \ + 'for html do mv "$$html" "$${html%.html}.gmi"; done' sh {} + + # Convert HTML blocks to gemtext + ./convert_gmi_html.py public_gmi + unlink templates || true .PHONY: dev dev: @echo "Starting development server..." ./update-linklog-json.sh - zola serve + ln -s templates_html templates + zola serve --drafts .PHONY: deploy deploy: @echo "Deploying project..." - rsync -rvzP --delete --chown 80:80 public/* $(RSYNC_TARGET) + rsync -rvzP --delete --chown 80:80 public_html/* $(RSYNC_TARGET_HTML) + rsync -rvzP --delete --chown 80:80 public_gmi/* $(RSYNC_TARGET_GMI) .PHONY: clean clean: @echo "Cleaning project..." - rm -rf public + unlink templates || true + rm -rf public* diff --git a/convert_gmi_html.py b/convert_gmi_html.py new file mode 100755 index 0000000..abfbe4a --- /dev/null +++ b/convert_gmi_html.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Convert HTML blocks in .gmi files to gemtext using html2gmi.""" + +import html +import subprocess +import sys +from pathlib import Path + +START_MARKER = "<!-- START_GEMINI_CONTENT -->" +END_MARKER = "<!-- END_GEMINI_CONTENT -->" +HTML2GMI = Path.home() / "go/bin/html2gmi" + + +def convert_file(path: Path) -> None: + content = path.read_text() + result = [] + pos = 0 + + while True: + start = content.find(START_MARKER, pos) + if start == -1: + result.append(content[pos:]) + break + + end = content.find(END_MARKER, start) + if end == -1: + result.append(content[pos:]) + break + + # Add content before the marker + result.append(content[pos:start]) + + # Convert HTML block to gemtext + html_content = content[start + len(START_MARKER):end] + converted = subprocess.run( + [HTML2GMI], + input=html_content, + capture_output=True, + text=True, + ) + result.append(converted.stdout) + + pos = end + len(END_MARKER) + + # Unescape HTML entities in the entire file + path.write_text(html.unescape("".join(result))) + + +def main() -> None: + if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} <directory>", file=sys.stderr) + sys.exit(1) + + directory = Path(sys.argv[1]) + for path in directory.rglob("*.gmi"): + convert_file(path) + + +if __name__ == "__main__": + main() diff --git a/templates/atom.xml b/templates_gmi/atom.xml index b1721ce..b1721ce 100644 --- a/templates/atom.xml +++ b/templates_gmi/atom.xml diff --git a/templates_gmi/base.html b/templates_gmi/base.html new file mode 100644 index 0000000..3c9447f --- /dev/null +++ b/templates_gmi/base.html @@ -0,0 +1,11 @@ +# Jan Tuomi + +=> /posts posts +=> /linklog linklog +=> /now now +=> /projects projects +{% block content %}{% endblock %} + +> Jan Tuomi. Minimalist, digital gardener, senior software engineer. Finland. + +=> https://github.com/jantuomi GitHub diff --git a/templates_gmi/index.html b/templates_gmi/index.html new file mode 100644 index 0000000..b9bca7e --- /dev/null +++ b/templates_gmi/index.html @@ -0,0 +1,51 @@ +{% extends "base.html" %} +{% block content %} + +Hi! My name is Jan. I am a senior software engineer based in Finland. Welcome to my personal blog and digital garden¹. + +=> /files/Jan_Tuomi_headshot_220.jpg My portrait + +I like keeping things simple and pragmatic. I'm a life-long learner, but I don't go chasing trends. +I enjoy spending time with my fiancée and our dog, tinkering with computer hardware and software, games of all kinds but mostly video-, tabletop and role-playing, electronics, music, language learning and linguistics. +I created this site² as a creative outlet for expressing myself and sharing my experiences. + +{% set posts = get_section(path="posts/_index.md") %} + +## Latest posts + +{% for page in posts.pages | slice(end=3) %} +=> {{ page.path }} {{ page.title }} ({{ page.date | date(format="%Y-%m-%d") }}) +{% endfor %} + +The posts page is a listing of my posts. I post sporadically about various subjects and with varying levels of effort. +The linklog page contains a curated aggregation of bookmarks, saved blog posts and essays, books, series, papers or anything else that I feel like sharing or recommending, stored in Linkhut. +On the *now* page I write what's currently going on in my life. Stuff that some would write onto their social media page. +The *projects* page contains info about my hobby projects. + +=> https://ln.ht/~jant My Linkhut page + +## Contact + +Email (jan *at* jantuomi *dot* fi) +=> https://fi.linkedin.com/in/jantuomi LinkedIn (jantuomi) +=> https://dice.camp/@kal_jan Mastodon (dice.camp/@kal_jan) +=> https://kal-jan.itch.io/ itch.io (kal_jan) +=> https://github.com/jantuomi Github (jantuomi) + +## Webrings + +=> https://xn--sr8hvo.ws/previous IndieWeb Webring (← previous) +=> https://xn--sr8hvo.ws/next IndieWeb Webring (→ next) +=> https://fediring.net/previous?host=jan.systems Fediring (← previous) +=> https://fediring.net/next?host=jan.systems Fediring (→ next) + +[1] The phrase "digital garden" is a metaphor for thinking about writing and creating that focuses less on the resulting "showpiece" and more on the process, care, and craft it takes to get there. — Joel Hooks + +=> https://joelhooks.com/digital-garden/ Joel Hooks + +[2] This site is written using the Zola static site generator. At first, the site was automatically deployed after every Markdown edit, but nowadays I use a more traditional manual deploy mechanism. + +=> https://www.getzola.org Zola +=> /posts/deploying-the-garden Original Markdown edit based deployment + +{% endblock %} diff --git a/templates_gmi/macros.html b/templates_gmi/macros.html new file mode 100644 index 0000000..d3d5562 --- /dev/null +++ b/templates_gmi/macros.html @@ -0,0 +1,21 @@ +{% macro toc(page) %}{% endmacro %} + +{% macro linklog(page) %} +{% set lh_data = load_data(path="linklog.json") %} +{% for lh_post in lh_data.posts %} +=> {{ lh_post.href }} {{ lh_post.description }} ({{ lh_post.time | date(format='%Y-%m-%d') }}) +{% endfor %} +{% endmacro %} + +{% macro page_h1(page) %} +# {{ page.title }} + +{% if page.date %}({{ page.date }}){% endif %}{% if page.extra.kind %} [{{ page.extra.kind }}]{% endif %} +{% endmacro %} + +{% macro page_content(page) %} +{{ self::page_h1(page=page) }} +<!-- START_GEMINI_CONTENT --> +{{ page.content | replace(from="<!-- toc -->", to=macros::toc(page=page)) | safe }} +<!-- END_GEMINI_CONTENT --> +{% endmacro %} diff --git a/templates_gmi/now.html b/templates_gmi/now.html new file mode 100644 index 0000000..3062054 --- /dev/null +++ b/templates_gmi/now.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block content %} +{% set posts = get_section(path="posts/_index.md") %} +{% set nows = posts.pages | filter(attribute="extra.kind", value="now") %} +{% set latest_now = nows | sort(attribute="date") | last %} + +{{ macros::page_content(page=latest_now) }} + +{% endblock %} diff --git a/templates_gmi/page.html b/templates_gmi/page.html new file mode 100644 index 0000000..2676372 --- /dev/null +++ b/templates_gmi/page.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block content %} +{{ macros::page_content(page=page) }} + +{% if page.higher %} +=> {{ page.higher.path }} ← {{ page.higher.title }} +{% endif %}{% if page.lower %} +=> {{ page.lower.path }} {{ page.lower.title }} → +{% endif %} +{% endblock content %} diff --git a/templates_gmi/page_linklog.html b/templates_gmi/page_linklog.html new file mode 100644 index 0000000..14602d6 --- /dev/null +++ b/templates_gmi/page_linklog.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block content %} +<!-- START_GEMINI_CONTENT --> +{{ page.content | replace(from="<!-- linklog -->", to=macros::linklog(page=page)) }} +<!-- END_GEMINI_CONTENT --> +{% endblock %} diff --git a/templates_gmi/section_posts.html b/templates_gmi/section_posts.html new file mode 100644 index 0000000..4d2cb55 --- /dev/null +++ b/templates_gmi/section_posts.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} +{% block content %} +{% set posts = get_section(path="posts/_index.md") %} +## {{ section.title }} + +=> /atom.xml Follow via Atom/RSS +=> https://aboutfeeds.com/ Huh? + +{% for page in posts.pages %} +=> {{ page.path }} {{ page.title }} ({{ page.date | date(format="%Y-%m-%d") }}) +{% endfor %} {% endblock %} diff --git a/templates/section_projects.html b/templates_gmi/section_projects.html index be0db07..be0db07 100644 --- a/templates/section_projects.html +++ b/templates_gmi/section_projects.html diff --git a/templates_gmi/shortcodes/fig.html b/templates_gmi/shortcodes/fig.html new file mode 100644 index 0000000..9988107 --- /dev/null +++ b/templates_gmi/shortcodes/fig.html @@ -0,0 +1 @@ +=> {{ src }} {{ alt }} diff --git a/templates/shortcodes/linklog.html b/templates_gmi/shortcodes/linklog.html index 6f46a2b..6f46a2b 100644 --- a/templates/shortcodes/linklog.html +++ b/templates_gmi/shortcodes/linklog.html diff --git a/templates/shortcodes/toc.html b/templates_gmi/shortcodes/toc.html index 479abec..479abec 100644 --- a/templates/shortcodes/toc.html +++ b/templates_gmi/shortcodes/toc.html diff --git a/templates_html/atom.xml b/templates_html/atom.xml new file mode 100644 index 0000000..b1721ce --- /dev/null +++ b/templates_html/atom.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}"> + <title>{{ config.title }} + {%- if term %} - {{ term.name }} + {%- elif section.title %} - {{ section.title }} + {%- endif -%} + </title> + {%- if config.description %} + <subtitle>{{ config.description }}</subtitle> + {%- endif %} + <link rel="self" type="application/atom+xml" href="{{ feed_url | safe }}" /> + <link + rel="alternate" + type="text/html" + href=" + {%- if section -%} + {{ section.permalink | escape_xml | safe }} + {%- else -%} + {{ config.base_url | escape_xml | safe }} + {%- endif -%} + " + /> + <generator uri="https://www.getzola.org/">Zola</generator> + {% if last_updated is defined %}<updated + >{{ last_updated | date(format="%+") }}</updated>{% endif %} + <id>{{ feed_url | safe }}</id> + {%- for page in pages %} + <entry xml:lang="{{ page.lang }}"> + <title>{{ page.title }}</title> + <published>{{ page.date | date(format="%+") }}</published> + <updated + >{{ page.updated | default(value=page.date) | date(format="%+") }}</updated> + {% for author in page.authors %} + <author> + <name> + {{ author }} + </name> + </author> + {% else %} + <author> + <name> + {%- if config.author -%} + {{ config.author }} + {%- else -%} + Unknown + {%- endif -%} + </name> + </author> + {% endfor %} + <link + rel="alternate" + type="text/html" + href="{{ page.permalink | safe }}" + /> + <id>{{ page.permalink | safe }}</id> + {% if page.summary %} + <summary type="html">{{ page.summary }}</summary> + {% else %} + <content + type="html" + xml:base="{{ page.permalink | escape_xml | safe }}" + > + {{ page.content }} + </content> + {% endif %} + </entry> + {%- endfor %} +</feed> diff --git a/templates/base.html b/templates_html/base.html index 82f185e..82f185e 100644 --- a/templates/base.html +++ b/templates_html/base.html diff --git a/templates/index.html b/templates_html/index.html index 31d0a90..31d0a90 100644 --- a/templates/index.html +++ b/templates_html/index.html diff --git a/templates/macros.html b/templates_html/macros.html index 3922659..3922659 100644 --- a/templates/macros.html +++ b/templates_html/macros.html diff --git a/templates/now.html b/templates_html/now.html index f05b6aa..f05b6aa 100644 --- a/templates/now.html +++ b/templates_html/now.html diff --git a/templates/page.html b/templates_html/page.html index 61db175..61db175 100644 --- a/templates/page.html +++ b/templates_html/page.html diff --git a/templates/page_linklog.html b/templates_html/page_linklog.html index e4fc945..e4fc945 100644 --- a/templates/page_linklog.html +++ b/templates_html/page_linklog.html diff --git a/templates/section_posts.html b/templates_html/section_posts.html index f44321f..f44321f 100644 --- a/templates/section_posts.html +++ b/templates_html/section_posts.html diff --git a/templates_html/section_projects.html b/templates_html/section_projects.html new file mode 100644 index 0000000..be0db07 --- /dev/null +++ b/templates_html/section_projects.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block content %} +{{ section.content | safe }} +{% endblock %} diff --git a/templates/shortcodes/fig.html b/templates_html/shortcodes/fig.html index 4ac99b4..4ac99b4 100644 --- a/templates/shortcodes/fig.html +++ b/templates_html/shortcodes/fig.html diff --git a/templates_html/shortcodes/linklog.html b/templates_html/shortcodes/linklog.html new file mode 100644 index 0000000..6f46a2b --- /dev/null +++ b/templates_html/shortcodes/linklog.html @@ -0,0 +1 @@ +<!-- linklog --> diff --git a/templates_html/shortcodes/toc.html b/templates_html/shortcodes/toc.html new file mode 100644 index 0000000..479abec --- /dev/null +++ b/templates_html/shortcodes/toc.html @@ -0,0 +1 @@ +<!-- toc --> |
