Mastodon

Blog de Alkahan

Une capsule gemini avec Hugo

  26/04/2026 [gemini hugo] - 1100 mots


Dans la continuité de remettre en route ce blog, j’ai envie de proposer une version accessible via une capsule gemini. Je me suis inspiré de l’article Managing this site and my gemini capsule with Hugo . Voici les étapes à suivre:

Configuration de Hugo

Les modifications sont à apporter dans le fichier de configuration de Hugo. Dans mon cas j’utilise le format toml, je modifie donc le fichier config.toml

Ajout du type de média text/gemini

On définit le type de média pour text/gemini:

[mediaTypes]
  [mediaTypes."text/gemini"]
    suffixes = ["gmi"]

Ajout du nouveau format de sortie

Ensuite ajouter un nouveau format de sortie pour hugo:

[outputFormats]
  [outputFormats.GEMINI]
    name         = "GEMINI"
    isPlainText  = true
    isHTML       = false
    mediaType    = "text/gemini"
    protocol     = "gemini://"
    permalinkable = true
    path         = ""

Le nom est en majuscule ici alors que toutes les autres références sont en minuscules, cela semble normal. Lors de mes premiers tests, je mettais tout en minuscule, mais ça ne fonctionnait pas.

Le paramètre path, définit le répertoire dans lequel les fichiers gmi seront créés. Ici c’est "" on trouvera donc les fichiers générés dans public directement. J’ai choisi ce mode de fonctionnement, car je le trouve plus simple, les serveurs web et gemini pointent tous les deux vers le meme répertoire, mais j’aurais pu décider de placer ma capsule dans un sous répertoire.

public
├── 404.html
├── css
├── index.gmi
├── index.html
├── index.xml
├── js
├── page
├── post
├── sitemap.xml
└── tags

Il est possible de ne pas définir ce paramètre ou alors de le laisser vide mais dans ce cas les fichiers gmi et html seront mélangés au même endroit. Cela va dépendre du serveur utilisé pour publier votre capsule.

ajouter gemini comme format de sortie pour les types de pages

[outputs]
  home = ['html', 'rss', 'gemini']
  section = ['html', 'rss', 'gemini']
  page = ['html', 'gemini']
  taxonomy = ['html']
  term = ['html']

Création des templates

J’ai créé tous les fichiers de template dans le répertoire de mon theme. Dans de nombreux exemples que j’ai pu lire, les liens sont générés avec Permalink, mais dans ce cas les auteurs étaient obligés d’utiliser la fonction replace pour changer le protocole des liens générés. En utilisant RelPermalink, les liens sont relatifs on n’a donc pas à se soucier de ces problèmes.

La page d’accueil

layouts/index.gmi

## Sections

{{ range .Pages }}
=> {{ .RelPermalink }} {{ .Title }}
{{- end }}

## Derniers articles

{{ range where .Site.RegularPages "Type" "post"  }}
=> {{ .RelPermalink }} {{ .Title }}
{{- end }}

## Posts groupés par année

{{ range .Site.RegularPages.GroupByDate "2006" }}
### {{ .Key }}
{{ range .Pages.ByDate.Reverse }}
=> {{ .RelPermalink }} {{ .Title }}
{{- end }}
{{ end }}

Liste d’article

Ce template est utilisé pour générer les listes d’articles pour un type donné ou pour les tags

layouts/_default/list.gmi

# {{ .Title }}

{{ range .Pages }}
=> {{ .RelPermalink }} {{ .Title }} — {{ .Date.Format "2006-01-02" }}
{{ end }}

Article

J’écris mon contenu dans le format Markdown qu’il faut convertir dans le format gemini (gemtext specification ).

Pour traiter ce problème, certains utilisent un script python qui utilise la librairie md2gemini . Elle permet plus de choix dans le traitement des liens, mais le projet est archivé depuis aout 2023 et cela oblige à faire un post traitement des fichiers gmi générés par Hugo. Je préfère réaliser la conversion dans le template.

On a besoin de réaliser les opérations suivantes:

On remplace donc dans un premier temps les liens pour les images et ensuite les liens vers les URL.

layouts/_default/single.gmi

# {{ .Title }}

{{- $content := .RawContent -}}
{{ $matches := $content | findRESubmatch `\[(.+?)\]\((.+?)\)` }}
{{- /* Remplace les listing markdown et les applati */ -}}
{{- $content = $content | replaceRE `(?m)^(\s*)-(.*?)$` "* $2" }}
{{- /* Remplace les images */ -}}
{{- $content = $content | replaceRE `\!\[(.+?)\]\((.+?)\)` "=> $2 Image: $1" }}

{{- /* Remplace les liens vers des URL en fin de paragraphe */ -}}
{{- range findRE `\[.+?\]\(.+?\)` $content }}
    {{- $content = $content | replaceRE `\[(.+?)\]\((.+?)\)(.+)` "$1$3\n\n=> $2 $1 " }}
{{- end }}

{{ $content }}

## Liens
{{- range $matches }}
=> {{ index . 2 }} {{ index . 1 }} 
{{- end }}

{{ if .Next }}=> {{ .Next.RelPermalink }} ← Newer: {{ .Next.Title }}{{ end }}
{{ if .Prev -}}=> {{ .Prev.RelPermalink }} → Older: {{ .Prev.Title }}{{- end }}

Publication

Mon site web est publié par static-web-server et pour ma capsule j’ai choisi agate . Je n’ai rien de particulier dans ma configuration, j’ai seulement eu besoin de lui préciser l’emplacement de mes fichiers et le domaine à utiliser qui est le meme que pour la version web.

Avec cette méthode de configuration, ma procédure de rédaction n’a pas changé. Je rédige mes articles dans emacs, je lance hugo en mode dev et je vérifie le rendu dans mon navigateur.

./hugo server -wD -t blog

hugo ne gérant pas nativement le protocole gemini, il me suffit de lancer une instance d’agate qui pointe sur le répertoire public.

agate --hostname localhost --content public/

Je peux alors vérifier le rendu de ma capsule en consultant l’URL gemini://localhost

Lorsque je corrige ou met à jour mon article, hugo met à jour le répertoire public et je peux visualiser le résultat dans mon navigateur web pour la version web et dans lagrange pour la version gemini.

rendu du site web
rendu de la capsule gemini