335 lines
16 KiB
Plaintext
335 lines
16 KiB
Plaintext
[1]skip to main content
|
||
[2] [logo]
|
||
|
||
• [3]Archives
|
||
• [4]Works
|
||
• [5]About
|
||
• [6]More...
|
||
|
||
Using Hugo to Launch a Gemini Capsule
|
||
|
||
3 April 2021
|
||
|
||
As you can read in the “[7]exploring the AlterNet” article, I’ve had my eye on
|
||
Gemini for a few weeks now. Ever since discovering the new protocol thanks to a
|
||
couple of weird Mastodon toots, I’ve been thinking about how to set up a
|
||
“capsule” (they’re not called sites) for myself. I like the appeal of a
|
||
text-focused, no-whizzbang protocol where the focus is on contents, not
|
||
aesthetics, especially for blogs such as this one.
|
||
|
||
A few questions needed to be answered before switching to action modus and
|
||
letting the static site generator Hugo do our dirty Gemini work for us.
|
||
|
||
How to host a Gemini capsule?
|
||
|
||
There are many pieces of [8]Gemini software available to us, but they’re all
|
||
quite new, as the protocol itself is from 2019. I was keen on trying out a
|
||
simple Go server, but both go-gemini-server, shavit, and go-gemini required me
|
||
to build it myself and contained very little documentation. Furthermore, some
|
||
packages weren’t updated in more than a year… In the end, I decided to go with
|
||
[9]Agate, a simple Gemini server written in Rust that can serve static files.
|
||
It has binaries for every platform, was updated six days ago, and its README.md
|
||
it extensive.
|
||
|
||
Agate even generates the needed TLS certificates if none are provided. This
|
||
allowed me to quickly set up a localhost server using the command agate
|
||
--content docs/gemini --addr 0.0.0.0:1965 --hostname localhost --lang en-US.
|
||
Fun fact about the port number:
|
||
|
||
When Gemini is served over TCP/IP, servers should listen on port 1965 (the
|
||
first manned Gemini mission, Gemini 3, flew in March ‘65).
|
||
|
||
Running locally before pushing to a server was important to me as I wanted to
|
||
fiddle with the .gmi files first to see how they look like in my Gemini browser
|
||
/client, [10]Lagrange. Gotta double-check the ASCII art!
|
||
|
||
What to publish on Gemini?
|
||
|
||
This is very personal. There are a few options. People like [11]Drew DevVault
|
||
and [12]Sylvain Durand mirror their HTTP(S) blog on Gemini, meaning all blog
|
||
entries are consultable both over the web and over Gemini. Then there are more
|
||
personal articles, published solely on Gemini to accompany the usually more
|
||
technical HTTP blogs, such as [13]gemini://space.eli.li/. He claims to use it
|
||
to whine like we did on MySpace yesteryear. I’ve also seen hybrids popping up:
|
||
articles that are ported, but some exclusive content is also available through
|
||
Gemini. I like that. My method at least makes this possible.
|
||
|
||
I wanted to blog in Dutch, my mother language, for a while now, and I’ve tried
|
||
it a few years back on Brain Baking. It didn’t work out. The entries were
|
||
misplaced somehow and I wasn’t satisfied, even though I did not expect to
|
||
actually have readers. I hoped to use a new domain, wouter.gr, for a Dutch
|
||
Gemini capsule to do some personal whining. That sounded like a good plan.
|
||
|
||
The plan fell through. Instead, I decided to mirror Brain Baking. Why?
|
||
|
||
• I already whine in Dutch in my personal diaries using a fountain pen. I do
|
||
not want to give that up.
|
||
• I already have a (nice?) blog, and I’d like to expand the Gemini
|
||
space-i-verse by adding my existing articles to it. I already write in
|
||
Markdown, so a conversion would be not too difficult.
|
||
• I don’t think I can keep up with posting on yet another blog, since I also
|
||
occasionally write about retro games on [14]jefklakscodex.com.
|
||
|
||
How to publish on Gemini?
|
||
|
||
Right. Porting articles turns out to be ridiculously easy with the help of my
|
||
good old friend, Hugo. [15]Sylvain’s method for declaring Gemini as a custom
|
||
Hugo output format turned out to work flawlessly. All credits go to him.
|
||
However, I did make a few significant changes to the link replacement system.
|
||
First, something important to consider: I do not get rid of special emphasis
|
||
symbols such as underscores or stars, that are Markdown-specific. I still think
|
||
they add something when reading plain text and they’re the next best thing to
|
||
have without any markup at all. So I removed those regex-es.
|
||
|
||
Gemini pages cannot have inline links, so I had to strip out Markdown-style []
|
||
() links and place them on a separate paragraph using => link title. A simple
|
||
find-and-replace, like in Sylvain’s method, is quite ugly if you use inline
|
||
links extensively like I do. It breaks up the text and the result is a
|
||
difficult to read Gemlog (that’s a Gemini blog!). In my approach, I collect all
|
||
links, replace them with a reference number like in academic papers ([1]), and
|
||
add a section called “References” on the bottom of the article to list them
|
||
all. This is what it looks like:
|
||
|
||
[16] [gemini] My Gemini AlterNet article in Lagrange.
|
||
|
||
I’m quite pleased with the result, although the code itself is far from pretty,
|
||
as Gemini is very newline-sensitive, and I had to jam a bunch of Hugo-specific
|
||
regex functions together. Source code available at GitHub: index.gmi source,
|
||
single.gmi source (see below). Next to the link change, I also replaced all -
|
||
and 1. (number) lines, that are enumerators in Markdown, with * ones, which is
|
||
the only supported enumerator in Gemfiles.
|
||
|
||
I tried to design the index and single layout files as similar as possible to
|
||
their html variants, while focusing in simplicity. Related articles are also
|
||
visible at the end of an article, and the index file simply contains a short
|
||
bio followed by an overview of all posts, groupbed by year and month, just like
|
||
in my [17]html /post overview. After defining [outputFormats.GEMINI] in my Hugo
|
||
config.toml, all that was left is to use rsync to copy over the gemini
|
||
subfolder to an appropriate location that gets picked up by Agate. Job done!
|
||
|
||
Well, not entirely. My Markdown files are littered with surprisingly
|
||
Hugo-specific junk:
|
||
|
||
• Shortcodes, such as YouTube, embedded video or audio.
|
||
• Four hashes - h4 - which isn’t supported by the Gemini protocol.
|
||
• <span/> tags in my quotes that help with HTML markup.
|
||
• Links to aliases that are redirects, which don’t work for the Gemini output
|
||
format.
|
||
|
||
Also, after trying out a second Gemini client, the terminal-friendly [18]Amfora
|
||
, I noticed the reference numbers do not align with Amfora’s shortcut keys that
|
||
allow you to quickly navigate to a link. Reference 1 would match to key 2. Why?
|
||
Because an image is also converted to a link (=> url), wich is placed
|
||
in-between text, while the actual references are at the bottom. Hence, pressing
|
||
number one would let us download the image - except Amfora can’t handle that
|
||
(yet). I solved this by starting at a specific index, based on the number of
|
||
times the arrow notation is present in the .gmi file, before processing inline
|
||
links. These are all things to take into account when writing future posts.
|
||
|
||
Now, the the most important question, “why publish on Gemini” could be answered
|
||
with “because it’s easy!”. I’m not yet sure if that answer is very
|
||
satisfactory, but at least Brain Baking got launched into Space today 🚀! All
|
||
that is left is to submit it to the GUS Gemini Universal Search engine…
|
||
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
|
||
Edit 21 June 2021: After a few months of fiddling with Gemini, I came to the
|
||
conclusion that it’s simply too early. There’s almost nothing there, and it
|
||
only increases the complexity of my website codebase. Therefore, I pulled yet
|
||
another plug. Sorry!
|
||
|
||
For future reference, the following files have been added to enable Gemini
|
||
functionality:
|
||
|
||
layouts/_default/index.atom.xml:
|
||
|
||
{{- $allowedRssSections := (slice "post") -}}
|
||
{{- $baseurl := .Site.BaseURL -}}
|
||
{{- $pctx := . -}}
|
||
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
|
||
{{- $pages := slice -}}
|
||
{{- if or $.IsHome $.IsSection -}}
|
||
{{- $pages = $pctx.RegularPages -}}
|
||
{{- else -}}
|
||
{{- $pages = $pctx.Pages -}}
|
||
{{- end -}}
|
||
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
||
{{- if ge $limit 1 -}}
|
||
{{- $pages = $pages | first $limit -}}
|
||
{{- end -}}
|
||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
|
||
<title>{{ .Site.Title }}</title>
|
||
{{- $perm := replace .Permalink "/gemini" "" 1 -}}
|
||
{{- $alt := .Site.BaseURL | replaceRE `https?://(.+?)` "gemini://$1" -}}
|
||
{{ printf "<link rel=\"self\" type=\"application/atom+xml\" href=\"%s\"/>" $perm | safeHTML }}
|
||
{{ printf "<link rel=\"alternate\" type=\"text/html\" href=\"%s\"/>" $alt | safeHTML }}
|
||
<updated>{{ .Date.Format "2006-01-02T15:04:05-0700" | safeHTML }}</updated>
|
||
<author>
|
||
<name>{{ .Site.Author.name }}</name>
|
||
<uri>{{ .Site.BaseURL | replaceRE `https?://(.+?)` "gemini://$1" }}</uri>
|
||
</author>
|
||
<id>{{ $perm }}</id>
|
||
{{ range $pages }}
|
||
{{ if in $allowedRssSections .Section }}
|
||
<entry>
|
||
<title>{{ .Title }}</title>
|
||
{{- $entryperm := .Permalink | replaceRE `https?://(.+?)` "gemini://$1" -}}
|
||
{{ printf "<link rel=\"alternate\" href=\"%s\"/>" $entryperm | safeHTML }}
|
||
<id>{{ $entryperm }}</id>
|
||
<published>{{ .Date.Format "2006-01-02T15:04:05-0700" | safeHTML }}</published>
|
||
<updated>{{ .Lastmod.Format "2006-01-02T15:04:05-0700" | safeHTML }}</updated>
|
||
<summary>{{ if isset .Params "subtitle" }}{{ .Params.subtitle }}{{ else }}{{ .Summary | html }}{{ end }}</summary>
|
||
</entry>
|
||
{{ end }}
|
||
{{ end }}
|
||
</feed>
|
||
|
||
layouts/index.gmi:
|
||
|
||
# Brain Baking in Space
|
||
|
||
> Brain Baking: transforming personal thoughts about thoughts into well-digestible material. The reflective aroma of burnt nervous tissue. Includes a crispy crust of relations between technology, philosophy and the world.
|
||
|
||
## About The Head Brain Baker
|
||
|
||
Hey! Yadda yadda
|
||
|
||
=> https://ko-fi.com/woutergroeneveld Ko-fi Donations
|
||
=> mailto:{{ .Site.Author.email }} E-mail
|
||
|
||
## Freshly Baked Thoughts: The Gemlog
|
||
|
||
=> /atom.xml Gemini Atom Feed
|
||
{{ range (where (where (where .Site.Pages "Section" "in" (slice "post")) ".Params.type" "ne" "archive") ".Params.concept" "ne" "true").GroupByDate "2006" "desc" }}{{ $year := .Key -}}
|
||
{{ range .Pages.GroupByDate "January" }}
|
||
### {{ .Key }} {{ $year }}
|
||
{{ range .Pages.ByDate.Reverse }}
|
||
=> {{ replace .RelPermalink "/gemini" "" 1}} {{ .Date.Format ("02") }} - {{ .Title }}
|
||
{{ .Params.Subtitle }}{{ end }}
|
||
{{ end }}
|
||
{{ end }}
|
||
|
||
# That's All Folks.
|
||
|
||
=> https://brainbaking.com Brain Baking on the WWW
|
||
|
||
And lastly, layouts/_default/single.gmi: (Note the space between {{ < that
|
||
should be removed)
|
||
|
||
# {{ .Title }}{{ $scratch := newScratch }}
|
||
{{ $content := .RawContent -}}
|
||
{{ $content := $content | replaceRE `#### ` "### " -}}
|
||
{{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}}
|
||
{{ $content := $content | replaceRE `\n(\d+). (.+?)` "\n* $2" -}}
|
||
{{ $content := $content | replaceRE `\[\^(.+?)\]:?` "" -}}
|
||
{{ $content := $content | replaceRE `<br/??>` "\n" -}}
|
||
{{ $content := $content | replaceRE `<a .*href="(.+?)".*>(.+?)</a>` "[$2]($1)" -}}
|
||
{{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$1)" -}}
|
||
{{ $content := $content | replaceRE `{{ < audio "(.+?)" >}}` "=> https://brainbaking.com/$1 Embedded Audio link - $1" -}}
|
||
{{ $content := $content | replaceRE `{{ < video "(.+?)" >}}` "=> https://brainbaking.com/$1 Embedded Video link - $1" -}}
|
||
{{ $content := $content | replaceRE `{{ < youtube (.+?) >}}` "=> https://www.youtube.com/watch?v=$1 YouTube Video link to $1" -}}
|
||
{{ $content := $content | replaceRE `{{ < vimeo (.+?) >}}` "=> https://vimeo.com/$1 Vimeo Video link to $1" -}}
|
||
{{ $content := $content | replaceRE "([^`])<.*?>([^`])" "$1$2" -}}
|
||
{{ $content := $content | replaceRE `\n\n!\[.*\]\((.+?) \"(.+?)\"\)` "\n\n=> $1 Image: $2" -}}
|
||
{{ $content := $content | replaceRE `\n\n!\[.*]\((.+?)\)` "\n\n=> $1 Embedded Image: $1" -}}
|
||
{{ $links := findRE `\n=> ` $content }}{{ $scratch.Set "ref" (add (len $links) 1) }}
|
||
{{ $refs := findRE `\[.+?\]\(.+?\)` $content }}
|
||
{{ $scratch.Set "content" $content }}{{ range $refs }}{{ $ref := $scratch.Get "ref" }}{{ $contentInLoop := $scratch.Get "content" }}{{ $url := (printf "%s #%d" . $ref) }}{{ $contentInLoop := replace $contentInLoop . $url -}}{{ $scratch.Set "content" $contentInLoop }}{{ $scratch.Set "ref" (add $ref 1) }}{{ end }}{{ $content := $scratch.Get "content" | replaceRE `\[(.+?)\]\((.+?)\) #(\d+)` "$1 [$3]" -}}
|
||
{{ $content | safeHTML }}
|
||
|
||
---
|
||
Written by Wouter Groeneveld on {{ .Lastmod.Format (.Site.Params.dateFormat | default "2 January 2006") }}.
|
||
|
||
## References
|
||
{{ $scratch.Set "ref" (add (len $links) 1) }}{{ range $refs }}{{ $ref := $scratch.Get "ref" }}{{ $url := (printf "%s #%d" . $ref) }}
|
||
=> {{ $url | replaceRE `\[(.+?)\]\((.+?)\) #(\d+)` "$2 [$3] $1 ($2)" -}}
|
||
{{ $scratch.Set "ref" (add $ref 1) }}{{ end}}
|
||
{{ $related := first 3 (where (where .Site.RegularPages.ByDate.Reverse ".Params.tags" "intersect" .Params.tags) "Permalink" "!=" .Permalink) }}
|
||
{{ if $related }}
|
||
## Related articles
|
||
{{ range $related }}
|
||
=> {{ replace .RelPermalink "/gemini" "" 1}} {{ .Title }}: {{ .Params.Subtitle }}{{ end }}{{ end }}
|
||
---
|
||
|
||
=> / Back to the Index
|
||
=> https://brainbaking.com{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} View this article on the WWW
|
||
|
||
For more information, feel free to contact me or to [19]plod around in the Git
|
||
repo history tab.
|
||
|
||
[20]webdesign [21]gemini [22]hugo [23]accessibility
|
||
|
||
You Might Also Like...
|
||
|
||
• [24]Why I Retired My Webmention Server 08 May 2023
|
||
• [25]Cool Things People Do With Their Blogs 27 Apr 2022
|
||
• [26]Reducing Workflow Load Facilitates Writing 03 Jul 2021
|
||
• [27]Exploring the AlterNet 24 Mar 2021
|
||
• [28]Finding Related Images in Hugo 08 Oct 2024
|
||
• [29]Visualizing Blog Post Links With Obsidian 10 Jun 2024
|
||
• [30]Displaying Series of Posts in Hugo 04 Jan 2024
|
||
|
||
Bio and Support
|
||
|
||
[avatar2024]
|
||
|
||
I'm [31]Wouter Groeneveld, a Brain Baker, and I love the smell of freshly baked
|
||
thoughts (and bread) in the morning. I sometimes convince others to bake their
|
||
brain (and bread) too.
|
||
|
||
If you found this article amusing and/or helpful, you can support me via [32]
|
||
PayPal or [33]Ko-Fi. I also like to hear your feedback via [34]Mastodon or
|
||
email. Thanks!
|
||
|
||
JavaScript is disabled. I use it to obfuscate my e-mail, keeping spambots at
|
||
bay.
|
||
Reach me using: [firstname] at [this domain].
|
||
|
||
↑ [35]Top | [36]Archives | [37]RSS Feed | [38]bv | [39]© CC BY 4.0 License.
|
||
[40] [brainbakin]
|
||
|
||
|
||
References:
|
||
|
||
[1] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/#top
|
||
[2] https://brainbaking.com/
|
||
[3] https://brainbaking.com/archives/
|
||
[4] https://brainbaking.com/works/
|
||
[5] https://brainbaking.com/about
|
||
[6] https://brainbaking.com/more
|
||
[7] https://brainbaking.com/post/2021/03/exploring-the-alternet/
|
||
[8] https://gemini.circumlunar.space/software/
|
||
[9] https://github.com/mbrubeck/agate
|
||
[10] https://gmi.skyjake.fi/lagrange/
|
||
[11] gemini://drewdevault.com/
|
||
[12] gemini://sylvaindurand.org
|
||
[13] gemini://space.eli.li/
|
||
[14] https://jefklakscodex.com/
|
||
[15] https://sylvaindurand.org/gemini-and-hugo/
|
||
[16] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/gemini.jpg
|
||
[17] https://brainbaking.com/post
|
||
[18] https://github.com/makeworld-the-better-one/amfora
|
||
[19] https://git.brainbaking.com/wgroeneveld/brainbaking/
|
||
[20] https://brainbaking.com/categories/webdesign
|
||
[21] https://brainbaking.com/tags/gemini
|
||
[22] https://brainbaking.com/tags/hugo
|
||
[23] https://brainbaking.com/tags/accessibility
|
||
[24] https://brainbaking.com/post/2023/05/why-i-retired-my-webmention-server/
|
||
[25] https://brainbaking.com/post/2022/04/cool-things-people-do-with-their-blogs/
|
||
[26] https://brainbaking.com/post/2021/07/reducing-workflow-load-facilitates-writing/
|
||
[27] https://brainbaking.com/post/2021/03/exploring-the-alternet/
|
||
[28] https://brainbaking.com/post/2024/10/finding-related-images-in-hugo/
|
||
[29] https://brainbaking.com/post/2024/06/visualizing-blog-post-links-with-obsidian/
|
||
[30] https://brainbaking.com/post/2024/01/displaying-series-of-posts-in-hugo/
|
||
[31] https://brainbaking.com/about
|
||
[32] https://www.paypal.com/donate/?hosted_button_id=R2WTKY7G9V2KQ
|
||
[33] https://ko-fi.com/woutergroeneveld
|
||
[34] https://dosgame.club/@jefklak
|
||
[35] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/#top
|
||
[36] https://brainbaking.com/archives
|
||
[37] https://brainbaking.com/index.xml
|
||
[38] https://brainbaking.com/bv
|
||
[39] https://brainbaking.com/copyright-and-tracking-policy
|
||
[40] https://brainbaking.com/links
|