davideisinger.com

My personal website
Log | Files | Refs | README

brainbaking-com-ro5lug.txt (16592B)


      1 [1]skip to main content
      2 [2] [logo]
      3 
      4   • [3]Archives
      5   • [4]Works
      6   • [5]About
      7   • [6]More...
      8 
      9 Using Hugo to Launch a Gemini Capsule
     10 
     11 3 April 2021
     12 
     13 As you can read in the “[7]exploring the AlterNet” article, I’ve had my eye on
     14 Gemini for a few weeks now. Ever since discovering the new protocol thanks to a
     15 couple of weird Mastodon toots, I’ve been thinking about how to set up a
     16 “capsule” (they’re not called sites) for myself. I like the appeal of a
     17 text-focused, no-whizzbang protocol where the focus is on contents, not
     18 aesthetics, especially for blogs such as this one.
     19 
     20 A few questions needed to be answered before switching to action modus and
     21 letting the static site generator Hugo do our dirty Gemini work for us.
     22 
     23 How to host a Gemini capsule?
     24 
     25 There are many pieces of [8]Gemini software available to us, but they’re all
     26 quite new, as the protocol itself is from 2019. I was keen on trying out a
     27 simple Go server, but both go-gemini-server, shavit, and go-gemini required me
     28 to build it myself and contained very little documentation. Furthermore, some
     29 packages weren’t updated in more than a year… In the end, I decided to go with 
     30 [9]Agate, a simple Gemini server written in Rust that can serve static files.
     31 It has binaries for every platform, was updated six days ago, and its README.md
     32 it extensive.
     33 
     34 Agate even generates the needed TLS certificates if none are provided. This
     35 allowed me to quickly set up a localhost server using the command agate
     36 --content docs/gemini --addr 0.0.0.0:1965 --hostname localhost --lang en-US.
     37 Fun fact about the port number:
     38 
     39     When Gemini is served over TCP/IP, servers should listen on port 1965 (the
     40     first manned Gemini mission, Gemini 3, flew in March ‘65).
     41 
     42 Running locally before pushing to a server was important to me as I wanted to
     43 fiddle with the .gmi files first to see how they look like in my Gemini browser
     44 /client, [10]Lagrange. Gotta double-check the ASCII art!
     45 
     46 What to publish on Gemini?
     47 
     48 This is very personal. There are a few options. People like [11]Drew DevVault
     49 and [12]Sylvain Durand mirror their HTTP(S) blog on Gemini, meaning all blog
     50 entries are consultable both over the web and over Gemini. Then there are more
     51 personal articles, published solely on Gemini to accompany the usually more
     52 technical HTTP blogs, such as [13]gemini://space.eli.li/. He claims to use it
     53 to whine like we did on MySpace yesteryear. I’ve also seen hybrids popping up:
     54 articles that are ported, but some exclusive content is also available through
     55 Gemini. I like that. My method at least makes this possible.
     56 
     57 I wanted to blog in Dutch, my mother language, for a while now, and I’ve tried
     58 it a few years back on Brain Baking. It didn’t work out. The entries were
     59 misplaced somehow and I wasn’t satisfied, even though I did not expect to
     60 actually have readers. I hoped to use a new domain, wouter.gr, for a Dutch
     61 Gemini capsule to do some personal whining. That sounded like a good plan.
     62 
     63 The plan fell through. Instead, I decided to mirror Brain Baking. Why?
     64 
     65   • I already whine in Dutch in my personal diaries using a fountain pen. I do
     66     not want to give that up.
     67   • I already have a (nice?) blog, and I’d like to expand the Gemini
     68     space-i-verse by adding my existing articles to it. I already write in
     69     Markdown, so a conversion would be not too difficult.
     70   • I don’t think I can keep up with posting on yet another blog, since I also
     71     occasionally write about retro games on [14]jefklakscodex.com.
     72 
     73 How to publish on Gemini?
     74 
     75 Right. Porting articles turns out to be ridiculously easy with the help of my
     76 good old friend, Hugo. [15]Sylvain’s method for declaring Gemini as a custom
     77 Hugo output format turned out to work flawlessly. All credits go to him.
     78 However, I did make a few significant changes to the link replacement system.
     79 First, something important to consider: I do not get rid of special emphasis
     80 symbols such as underscores or stars, that are Markdown-specific. I still think
     81 they add something when reading plain text and they’re the next best thing to
     82 have without any markup at all. So I removed those regex-es.
     83 
     84 Gemini pages cannot have inline links, so I had to strip out Markdown-style []
     85 () links and place them on a separate paragraph using => link title. A simple
     86 find-and-replace, like in Sylvain’s method, is quite ugly if you use inline
     87 links extensively like I do. It breaks up the text and the result is a
     88 difficult to read Gemlog (that’s a Gemini blog!). In my approach, I collect all
     89 links, replace them with a reference number like in academic papers ([1]), and
     90 add a section called “References” on the bottom of the article to list them
     91 all. This is what it looks like:
     92 
     93 [16] [gemini] My Gemini AlterNet article in Lagrange.
     94 
     95 I’m quite pleased with the result, although the code itself is far from pretty,
     96 as Gemini is very newline-sensitive, and I had to jam a bunch of Hugo-specific
     97 regex functions together. Source code available at GitHub: index.gmi source,
     98 single.gmi source (see below). Next to the link change, I also replaced all -
     99 and 1. (number) lines, that are enumerators in Markdown, with * ones, which is
    100 the only supported enumerator in Gemfiles.
    101 
    102 I tried to design the index and single layout files as similar as possible to
    103 their html variants, while focusing in simplicity. Related articles are also
    104 visible at the end of an article, and the index file simply contains a short
    105 bio followed by an overview of all posts, groupbed by year and month, just like
    106 in my [17]html /post overview. After defining [outputFormats.GEMINI] in my Hugo
    107 config.toml, all that was left is to use rsync to copy over the gemini
    108 subfolder to an appropriate location that gets picked up by Agate. Job done!
    109 
    110 Well, not entirely. My Markdown files are littered with surprisingly
    111 Hugo-specific junk:
    112 
    113   • Shortcodes, such as YouTube, embedded video or audio.
    114   • Four hashes - h4 - which isn’t supported by the Gemini protocol.
    115   • <span/> tags in my quotes that help with HTML markup.
    116   • Links to aliases that are redirects, which don’t work for the Gemini output
    117     format.
    118 
    119 Also, after trying out a second Gemini client, the terminal-friendly [18]Amfora
    120 , I noticed the reference numbers do not align with Amfora’s shortcut keys that
    121 allow you to quickly navigate to a link. Reference 1 would match to key 2. Why?
    122 Because an image is also converted to a link (=> url), wich is placed
    123 in-between text, while the actual references are at the bottom. Hence, pressing
    124 number one would let us download the image - except Amfora can’t handle that
    125 (yet). I solved this by starting at a specific index, based on the number of
    126 times the arrow notation is present in the .gmi file, before processing inline
    127 links. These are all things to take into account when writing future posts.
    128 
    129 Now, the the most important question, “why publish on Gemini” could be answered
    130 with “because it’s easy!”. I’m not yet sure if that answer is very
    131 satisfactory, but at least Brain Baking got launched into Space today 🚀! All
    132 that is left is to submit it to the GUS Gemini Universal Search engine…
    133 
    134 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    135 
    136 Edit 21 June 2021: After a few months of fiddling with Gemini, I came to the
    137 conclusion that it’s simply too early. There’s almost nothing there, and it
    138 only increases the complexity of my website codebase. Therefore, I pulled yet
    139 another plug. Sorry!
    140 
    141 For future reference, the following files have been added to enable Gemini
    142 functionality:
    143 
    144 layouts/_default/index.atom.xml:
    145 
    146 {{- $allowedRssSections := (slice "post") -}}
    147 {{- $baseurl := .Site.BaseURL -}}
    148 {{- $pctx := . -}}
    149 {{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
    150 {{- $pages := slice -}}
    151 {{- if or $.IsHome $.IsSection -}}
    152 {{- $pages = $pctx.RegularPages -}}
    153 {{- else -}}
    154 {{- $pages = $pctx.Pages -}}
    155 {{- end -}}
    156 {{- $limit := .Site.Config.Services.RSS.Limit -}}
    157 {{- if ge $limit 1 -}}
    158 {{- $pages = $pages | first $limit -}}
    159 {{- end -}}
    160 {{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
    161 <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    162   <title>{{ .Site.Title }}</title>
    163   {{- $perm := replace .Permalink "/gemini" "" 1 -}}
    164   {{- $alt := .Site.BaseURL | replaceRE `https?://(.+?)` "gemini://$1" -}}
    165   {{ printf "<link rel=\"self\" type=\"application/atom+xml\" href=\"%s\"/>" $perm | safeHTML }}
    166   {{ printf "<link rel=\"alternate\" type=\"text/html\" href=\"%s\"/>" $alt | safeHTML }}
    167   <updated>{{ .Date.Format "2006-01-02T15:04:05-0700" | safeHTML }}</updated>
    168   <author>
    169     <name>{{ .Site.Author.name }}</name>
    170     <uri>{{ .Site.BaseURL | replaceRE `https?://(.+?)` "gemini://$1" }}</uri>
    171   </author>
    172   <id>{{ $perm }}</id>
    173   {{ range $pages }}
    174   {{ if in $allowedRssSections .Section }}
    175   <entry>
    176     <title>{{ .Title }}</title>
    177     {{- $entryperm := .Permalink | replaceRE `https?://(.+?)` "gemini://$1" -}}
    178     {{ printf "<link rel=\"alternate\" href=\"%s\"/>" $entryperm | safeHTML }}
    179     <id>{{ $entryperm }}</id>
    180     <published>{{ .Date.Format "2006-01-02T15:04:05-0700" | safeHTML }}</published>
    181     <updated>{{ .Lastmod.Format "2006-01-02T15:04:05-0700" | safeHTML }}</updated>
    182     <summary>{{ if isset .Params "subtitle" }}{{ .Params.subtitle }}{{ else }}{{ .Summary | html }}{{ end }}</summary>
    183   </entry>
    184   {{ end }}
    185   {{ end }}
    186 </feed>
    187 
    188 layouts/index.gmi:
    189 
    190 # Brain Baking in Space
    191 
    192 > 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.
    193 
    194 ## About The Head Brain Baker
    195 
    196 Hey! Yadda yadda
    197 
    198 => https://ko-fi.com/woutergroeneveld Ko-fi Donations
    199 => mailto:{{ .Site.Author.email }} E-mail
    200 
    201 ## Freshly Baked Thoughts: The Gemlog
    202 
    203 => /atom.xml Gemini Atom Feed
    204 {{ range (where (where (where .Site.Pages "Section" "in" (slice "post")) ".Params.type" "ne" "archive") ".Params.concept" "ne" "true").GroupByDate "2006" "desc" }}{{ $year := .Key -}}
    205 {{ range .Pages.GroupByDate "January" }}
    206 ### {{ .Key }} {{ $year }}
    207 {{ range .Pages.ByDate.Reverse }}
    208 => {{ replace .RelPermalink "/gemini" "" 1}} {{ .Date.Format ("02") }} - {{ .Title }}
    209 {{ .Params.Subtitle }}{{ end }}
    210 {{ end }}
    211 {{ end }}
    212 
    213 # That's All Folks.
    214 
    215 => https://brainbaking.com Brain Baking on the WWW
    216 
    217 And lastly, layouts/_default/single.gmi: (Note the space between {{ < that
    218 should be removed)
    219 
    220 # {{ .Title }}{{ $scratch := newScratch }}
    221 {{ $content := .RawContent -}}
    222 {{ $content := $content | replaceRE `#### ` "### " -}}
    223 {{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}}
    224 {{ $content := $content | replaceRE `\n(\d+). (.+?)` "\n* $2" -}}
    225 {{ $content := $content | replaceRE `\[\^(.+?)\]:?` "" -}}
    226 {{ $content := $content | replaceRE `<br/??>` "\n" -}}
    227 {{ $content := $content | replaceRE `<a .*href="(.+?)".*>(.+?)</a>` "[$2]($1)" -}}
    228 {{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$1)" -}}
    229 {{ $content := $content | replaceRE `{{ < audio "(.+?)" >}}` "=> https://brainbaking.com/$1 Embedded Audio link - $1" -}}
    230 {{ $content := $content | replaceRE `{{ < video "(.+?)" >}}` "=> https://brainbaking.com/$1 Embedded Video link - $1" -}}
    231 {{ $content := $content | replaceRE `{{ < youtube (.+?) >}}` "=> https://www.youtube.com/watch?v=$1 YouTube Video link to $1" -}}
    232 {{ $content := $content | replaceRE `{{ < vimeo (.+?) >}}` "=> https://vimeo.com/$1 Vimeo Video link to $1" -}}
    233 {{ $content := $content | replaceRE "([^`])<.*?>([^`])" "$1$2" -}}
    234 {{ $content := $content | replaceRE `\n\n!\[.*\]\((.+?) \"(.+?)\"\)` "\n\n=> $1 Image: $2" -}}
    235 {{ $content := $content | replaceRE `\n\n!\[.*]\((.+?)\)` "\n\n=> $1 Embedded Image: $1" -}}
    236 {{ $links := findRE `\n=> ` $content }}{{ $scratch.Set "ref" (add (len $links) 1) }}
    237 {{ $refs := findRE `\[.+?\]\(.+?\)` $content }}
    238 {{ $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]" -}}
    239 {{ $content | safeHTML }}
    240 
    241 ---
    242 Written by Wouter Groeneveld on {{ .Lastmod.Format (.Site.Params.dateFormat | default "2 January 2006") }}.
    243 
    244 ## References
    245 {{ $scratch.Set "ref" (add (len $links) 1) }}{{ range $refs }}{{ $ref := $scratch.Get "ref" }}{{ $url := (printf "%s #%d" . $ref) }}
    246 => {{ $url | replaceRE `\[(.+?)\]\((.+?)\) #(\d+)` "$2 [$3] $1 ($2)" -}}
    247 {{ $scratch.Set "ref" (add $ref 1) }}{{ end}}
    248 {{ $related := first 3 (where (where .Site.RegularPages.ByDate.Reverse ".Params.tags" "intersect" .Params.tags) "Permalink" "!=" .Permalink) }}
    249 {{ if $related }}
    250 ## Related articles
    251 {{ range $related }}
    252 => {{ replace .RelPermalink "/gemini" "" 1}} {{ .Title }}: {{ .Params.Subtitle }}{{ end }}{{ end }}
    253 ---
    254 
    255 => / Back to the Index
    256 => https://brainbaking.com{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} View this article on the WWW
    257 
    258 For more information, feel free to contact me or to [19]plod around in the Git
    259 repo history tab.
    260 
    261 [20]webdesign   [21]gemini  [22]hugo  [23]accessibility 
    262 
    263 You Might Also Like...
    264 
    265   • [24]Why I Retired My Webmention Server 08 May 2023
    266   • [25]Cool Things People Do With Their Blogs 27 Apr 2022
    267   • [26]Reducing Workflow Load Facilitates Writing 03 Jul 2021
    268   • [27]Exploring the AlterNet 24 Mar 2021
    269   • [28]Finding Related Images in Hugo 08 Oct 2024
    270   • [29]Visualizing Blog Post Links With Obsidian 10 Jun 2024
    271   • [30]Displaying Series of Posts in Hugo 04 Jan 2024
    272 
    273 Bio and Support
    274 
    275 [avatar2024]
    276 
    277 I'm [31]Wouter Groeneveld, a Brain Baker, and I love the smell of freshly baked
    278 thoughts (and bread) in the morning. I sometimes convince others to bake their
    279 brain (and bread) too.
    280 
    281 If you found this article amusing and/or helpful, you can support me via [32]
    282 PayPal or [33]Ko-Fi. I also like to hear your feedback via [34]Mastodon or 
    283 email. Thanks!
    284 
    285 JavaScript is disabled. I use it to obfuscate my e-mail, keeping spambots at
    286 bay.
    287 Reach me using: [firstname] at [this domain].
    288 
    289 ↑ [35]Top | [36]Archives | [37]RSS Feed | [38]bv | [39]© CC BY 4.0 License. 
    290 [40] [brainbakin]
    291 
    292 
    293 References:
    294 
    295 [1] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/#top
    296 [2] https://brainbaking.com/
    297 [3] https://brainbaking.com/archives/
    298 [4] https://brainbaking.com/works/
    299 [5] https://brainbaking.com/about
    300 [6] https://brainbaking.com/more
    301 [7] https://brainbaking.com/post/2021/03/exploring-the-alternet/
    302 [8] https://gemini.circumlunar.space/software/
    303 [9] https://github.com/mbrubeck/agate
    304 [10] https://gmi.skyjake.fi/lagrange/
    305 [11] gemini://drewdevault.com/
    306 [12] gemini://sylvaindurand.org
    307 [13] gemini://space.eli.li/
    308 [14] https://jefklakscodex.com/
    309 [15] https://sylvaindurand.org/gemini-and-hugo/
    310 [16] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/gemini.jpg
    311 [17] https://brainbaking.com/post
    312 [18] https://github.com/makeworld-the-better-one/amfora
    313 [19] https://git.brainbaking.com/wgroeneveld/brainbaking/
    314 [20] https://brainbaking.com/categories/webdesign
    315 [21] https://brainbaking.com/tags/gemini
    316 [22] https://brainbaking.com/tags/hugo
    317 [23] https://brainbaking.com/tags/accessibility
    318 [24] https://brainbaking.com/post/2023/05/why-i-retired-my-webmention-server/
    319 [25] https://brainbaking.com/post/2022/04/cool-things-people-do-with-their-blogs/
    320 [26] https://brainbaking.com/post/2021/07/reducing-workflow-load-facilitates-writing/
    321 [27] https://brainbaking.com/post/2021/03/exploring-the-alternet/
    322 [28] https://brainbaking.com/post/2024/10/finding-related-images-in-hugo/
    323 [29] https://brainbaking.com/post/2024/06/visualizing-blog-post-links-with-obsidian/
    324 [30] https://brainbaking.com/post/2024/01/displaying-series-of-posts-in-hugo/
    325 [31] https://brainbaking.com/about
    326 [32] https://www.paypal.com/donate/?hosted_button_id=R2WTKY7G9V2KQ
    327 [33] https://ko-fi.com/woutergroeneveld
    328 [34] https://dosgame.club/@jefklak
    329 [35] https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/#top
    330 [36] https://brainbaking.com/archives
    331 [37] https://brainbaking.com/index.xml
    332 [38] https://brainbaking.com/bv
    333 [39] https://brainbaking.com/copyright-and-tracking-policy
    334 [40] https://brainbaking.com/links