From 874fd9d2b85f2fb1e1b7e8d0ec0edc54ba9095a7 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 19 Feb 2024 08:24:36 -0600 Subject: [PATCH 01/36] update post with minor tweaks --- .../index.md | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/content/posts/dynamic-opengraph-images-with-hugo/index.md b/content/posts/dynamic-opengraph-images-with-hugo/index.md index 0819d41..d19a483 100644 --- a/content/posts/dynamic-opengraph-images-with-hugo/index.md +++ b/content/posts/dynamic-opengraph-images-with-hugo/index.md @@ -13,23 +13,25 @@ tags: - meta - selfhosting --- -I've lately seen some folks on [social.lol](https://social.lol) posting about their various strategies for automatically generating [Open Graph images](https://ogp.me/) for their [Eleventy](https://11ty.dev) sites. So this weekend I started exploring ways to do that for my [Hugo](https://gohugo.io) site. +I've lately seen some folks on [social.lol](https://social.lol) posting about their various strategies for automatically generating [Open Graph images](https://ogp.me/) for their [Eleventy](https://11ty.dev) sites. So this weekend I started exploring how I could do that for my [Hugo](https://gohugo.io) site[^site]. -During my search, I came across a few different approaches using external services or additional scripts to run at build time, but I was hoping for a way to do this with Hugo's built-in tooling. I eventually came across a tremendously helpful post from Aaro titled [Generating OpenGraph images with Hugo](https://aarol.dev/posts/hugo-og-image/). This solution was exactly what I was after, as it uses Hugo's [image functions](https://gohugo.io/functions/images/filter/) to dynamically create a share image for each page. +[^site]: You're looking at it. + +During my search, I came across a few different approaches using external services or additional scripts to run at build time, but I was hoping for a way to do this with Hugo's built-in tooling. I eventually came across a tremendously helpful post from [Aaro](https://aarol.dev/about/) titled [Generating OpenGraph images with Hugo](https://aarol.dev/posts/hugo-og-image/). This solution was exactly what I was after, as it uses Hugo's [image functions](https://gohugo.io/functions/images/filter/) to dynamically create a share image for each page. I ended up borrowing heavily from Aaro's approach while adding a few small variations for my OpenGraph images. - When sharing the home page, the image includes the site description. - When sharing a post, the image includes the post title. -- ... but if the post has a thumbnail[^thumbnail] listed in the front matter, that gets overlaid in the corner. +- ... and if the post has a thumbnail[^thumbnail] listed in the front matter, that gets overlaid in the corner. [^thumbnail]: My current theme doesn't make use of the thumbnails, but a previous theme did so I've got a bunch of posts with thumbnails still assigned. And now I've got a use for them again! Here's how I did it. ### New resources -Based on Aaro's suggestions, I used [GIMP](https://www.gimp.org/) to create a 1200x600 image for the base. I'm not a graphic designer[^web] so I kept it simple while trying to match the theme, font, and colors used on the site. +Based on Aaro's suggestions, I used [GIMP](https://www.gimp.org/) to create a 1200x600 image for the base. I'm not a graphic designer[^web] so I kept it simple while trying to match the site's theme. -I had to install the Fira Mono font [Fira Mono `.ttf`](https://github.com/mozilla/Fira/blob/master/ttf/FiraMono-Regular.ttf) to my `~/.fonts/` folder so I could use it in GIMP. +I had to install the Fira Mono font [Fira Mono `.ttf`](https://github.com/mozilla/Fira/blob/master/ttf/FiraMono-Regular.ttf) to my `~/.fonts/` folder so I could use it in GIMP, and I wound up with a decent recreation of the little "logo" at the top of the page. ![Red background with a command prompt displaying "[runtimeterror.dev] $" in white and red font.](og_base.png) @@ -81,8 +83,10 @@ which is in turn loaded by `layouts/_defaults/baseof.html`: ``` +So now the customized OpenGraph content will be loaded for each page. + ### Aaro's OG image generation -[Aaro's code](https://aarol.dev/posts/hugo-og-image/) provided the base functionality for what I need: +[Aaro's code](https://aarol.dev/posts/hugo-og-image/) provided the base functionality for what I needed: ```jinja-html {{/* Generate opengraph image */}} @@ -121,16 +125,16 @@ which is in turn loaded by `layouts/_defaults/baseof.html`: The [`resources.Get`](https://gohugo.io/functions/resources/get/) bits import the image and font resources to make them available to the [`images.Text`](https://gohugo.io/functions/images/text/) functions, which add the site and page title texts to the image using the designated color, size, placement, and font. -The `resources.Copy` line moves the generated OG image into the post bundle directory and gives it a clean `og.png` name rather than the very-long randomly-generated name it would have by default. +The `resources.Copy` line moves the generated OG image alongside the post itself and gives it a clean `og.png` name rather than the very-long randomly-generated name it would have by default. -And then the `` lines insert the generated image into the page's `` block so it can be rendered when the link is shared on sites which support OpenGraph. +And then the `` lines insert the generated image into the page's `` block so it can be rendered when the link is shared on sites which support OpenGraph. -This is a great starting point for what I wanted to accomplish, but made some changes in my `opengraph.html` partial to tailor it to my needs. +This is a great starting point for what I wanted to accomplish, but I made some changes to my `opengraph.html` partial to tailor it to my needs. ### My tweaks As I mentioned earlier, I wanted to have three slightly-different recipes for baking my OG images: one for the homepage, one for standard posts, and one for posts with an associated thumbnail. They all use the same basic code, though, so I wanted to be sure that my setup didn't repeat itself too much. -My code starts with fetching my resources up front, and initializing an empty `$text` variable to hold the description or title: +My code starts with fetching my resources up front, and initializing an empty `$text` variable to hold either the site description *or* post title: ```jinja-html {{ $img := resources.Get "og_base.png" }} @@ -165,11 +169,13 @@ If the page has a `thumbnail` parameter defined in the front matter, Hugo will The [`resources.Get` function](https://gohugo.io/functions/resources/get/) (little r) I used earlier works on *global* resources, like the image and font stored in the site's `assets/` directory. On the other hand, the [`Resources.Get` method](https://gohugo.io/methods/page/resources/) (big R) is used for loading *page* resources, like the file indicated by the page's `thumbnail` parameter. {{% /notice %}} -And since I'm calling this method from inside a `with` branch I have to put a `$` in front of the method. Otherwise, the leading `.` would refer directly to the `thumbnail` parameter (which isn't a page and so doesn't have the method available[^scope]). +Since I'm calling this method from inside a `with` block I use a `$` in front of the method name to get the [parent context](https://gohugo.io/functions/go-template/with/#understanding-context). Otherwise, the leading `.` would refer directly to the `thumbnail` parameter (which isn't a page and so doesn't have the method available[^scope]). [^scope]: Hugo scoping is kind of wild. -Anyhoo, after the thumbnail is loaded, I use the [`Fit` image processing](https://gohugo.io/content-management/image-processing/#fit) to scale down the thumbnail and then call the [`images.Overlay` function](https://gohugo.io/functions/images/overlay/) to *overlay* it near the top right corner of the `og_base.png` image. +Anyhoo, after the thumbnail is loaded, I use the [`Fit` image processing method](https://gohugo.io/content-management/image-processing/#fit) to scale down the thumbnail. It is then passed to the [`images.Overlay` function](https://gohugo.io/functions/images/overlay/) to *overlay* it near the top right corner of the `og_base.png` image[^placement]. + +[^placement]: The overlay is placed using absolute X and Y coordinates. There's probably a way to tell it "offset the top-right corner of the overlay 20x20 from the top right of the base image" but I ran out of caffeine to figure that out at this time. Let me know if you know a trick! ```jinja-html {{ with $thumbnail }} From b3f1f9d7fcd473b07be7d6e3324c971249ec7d79 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 20 Feb 2024 15:21:04 -0600 Subject: [PATCH 02/36] update contact links --- content/about.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/about.md b/content/about.md index 70c58db..b6505ce 100644 --- a/content/about.md +++ b/content/about.md @@ -31,9 +31,9 @@ See what I've been up to on: Connect with me via: - [SimpleX Chat](/simplex/) -- [Session](https://p.runtimeterror.dev/session-id) +- [Signal](https://signal.me/#eu/lyHZbMnlM16O0w48j3rshYBofO0K-iXOt9LGwln7TS-fNKEHCrxH3La325q8IjRU) - [Matrix](https://matrix.to/#/@jbowdre:omg.lol) -- [XMPP](xmpp://john@chat.vpota.to) +- [XMPP](https://conversations.im/i/jbowdre@omg.lol?omemo-sid-1374125881=a620f3c57733601a6646f6f13a71c86fc9be8dd4126fd158ef3e0a26beb0b434) - [Electronic Mail](mailto:jbowdre@omg.lol) - [PGP: 613F B70C 4FA7 A077](https://l.runtimeterror.dev/pgp) From 090b12abd2b6046442dc31138a4ac75384e135b0 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Fri, 23 Feb 2024 16:00:41 -0600 Subject: [PATCH 03/36] fix typo in partly cloudy icon name --- content/posts/display-tempest-weather-static-site/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/posts/display-tempest-weather-static-site/index.md b/content/posts/display-tempest-weather-static-site/index.md index 1a49f1d..5c0e1fa 100644 --- a/content/posts/display-tempest-weather-static-site/index.md +++ b/content/posts/display-tempest-weather-static-site/index.md @@ -1,7 +1,7 @@ --- title: "Displaying Data from a Tempest Weather Station on a Static Site" date: "2024-02-11T20:48:49Z" -# lastmod: 2024-02-10 +lastmod: "2024-02-23T22:00:22Z" description: "Using a GitHub Actions workflow to retrieve data from an authenticated API, posting results to a publicly-accessible pastebin, and displaying them on a static web site." featured: false thumbnail: "finished-product.png" @@ -674,8 +674,8 @@ const CLASS_MAP_WX = { 'clear-night': 'fa-solid fa-moon', 'cloudy': 'fa-solid fa-cloud', 'foggy': 'fa-solid fa-cloud-showers-smog', - 'partly-cloudy-day': 'fa-solid fa-clouds-sun', - 'partly-cloudy-night': 'fa-solid fa-clouds-moon', + 'partly-cloudy-day': 'fa-solid fa-cloud-sun', + 'partly-cloudy-night': 'fa-solid fa-cloud-moon', 'possibly-rainy-day': 'fa-solid fa-cloud-sun-rain', 'possibly-rainy-night': 'fa-solid fa-cloud-moon-rain', 'possibly-sleet-day': 'fa-solid fa-cloud-meatball', From 373d0945fb2fc64b3f39f12540b41efdad75a8df Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Sun, 25 Feb 2024 17:06:53 -0600 Subject: [PATCH 04/36] add script to create gemini capsule --- .gitignore | 3 +- process_capsule.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 11 ++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 process_capsule.py create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index d5f97c8..3215f1d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /public/ /resources/ /.env* - +/capsule/ +/.certificates/ diff --git a/process_capsule.py b/process_capsule.py new file mode 100644 index 0000000..73003fa --- /dev/null +++ b/process_capsule.py @@ -0,0 +1,85 @@ +# Adapted from Will Webberley's work: +# https://wilw.dev/blog/2023/06/01/automatic-gemini-publishing/ +# https://git.wilw.dev/wilw/wilw.dev/src/branch/main/gemini/process_capsule.py + +import os, datetime, re +import frontmatter + +capsule_url = 'gemini://gmi.runtimeterror.dev' +web_url = 'https://runtimeterror.dev' +content_dir = 'content' +email = 'blog@runtimeterror.dev' +feed_url = 'https://runtimeterror.dev/feed.xml' +site_name = '[runtimeterror]' + +# Gemini-ify hyperlinks +def transform_links(content): + lines = content.splitlines() + pattern = re.compile(r'\[([^][]+)\]\(([^()]+)\)') + current_link_ref = 1 + new_lines = [] + for line_index, line in enumerate(lines): + if line.startswith('!['): continue + links_to_add = [] + if not line.startswith('=>'): + for link_index, match in enumerate(pattern.finditer(line)): + description, url = match.groups() + line = line.replace(f'({url})', f' [{current_link_ref}]').replace(f'[{description}]', description) + # if URL self-references website, handle differently: + if not url.startswith('http') or url.startswith(web_url): + if web_url in url: url = url.replace(web_url, '') + url = capsule_url + '/' + url.replace('/', ' ').strip().replace(' ', '-') + '.gmi' + links_to_add.append(f'=> {url} {current_link_ref}') + current_link_ref += 1 + new_lines.append(line) + for link in links_to_add: + new_lines.append(link) + return '\n'.join(new_lines) + +# Create new directories +needed_directories = ['capsule'] +for dir in needed_directories: + if not os.path.exists(dir): + os.makedirs(dir) + +# Generate blog posts +logs = [] +for dirpath, dirnames, filenames in os.walk(content_dir): + if 'index.md' in filenames: + post = frontmatter.load(os.path.join(dirpath, 'index.md')) + date = post['date'] if isinstance(post['date'], datetime.date) else datetime.datetime.strptime(post['date'].split('T')[0], '%Y-%m-%d') + content = transform_links(post.content) + new_content = ''' +=> {} 🏑 Home + +# {} +## Posted on {} + +{} + +=> mailto:{} Reply via email +=> {} Back to home + '''.format(capsule_url, post['title'], date.strftime('%Y-%m-%d'), content, email, capsule_url) + new_file_name = f'{os.path.basename(dirpath)}.gmi' + logs.append({'file': new_file_name, 'title': post['title'], 'date': date}) + with open(f'capsule/{new_file_name}', 'w') as gem_file: + gem_file.write(new_content) + +# Generate blog index +logs.sort(key=lambda entry: entry['date'].strftime('%Y-%m-%d'), reverse=True) +log_index_content = ''' +=> {} 🏑 Home + +# {} + +=> {} πŸ“² Subscribe via RSS + +{} + +=> {} 🏑 Home +'''.format(capsule_url, site_name, feed_url, + '\n'.join(['=> {} {} - {}'.format(l['file'], l['date'].strftime('%Y-%m-%d'), l['title']) for l in logs]), + capsule_url) +gem_index_file = open(f'capsule/index.gmi', 'w') +gem_index_file.write(log_index_content) + diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..3140b63 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +let + pkgs = import {}; +in pkgs.mkShell { + packages = with pkgs; [ + agate + hugo + (python3.withPackages (python-pkgs: [ + python-pkgs.python-frontmatter + ])) + ]; +} From 39f2827249511e0d89ff4e5b5ec689ef2deb346d Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Sun, 25 Feb 2024 18:31:08 -0600 Subject: [PATCH 05/36] Revert "add script to create gemini capsule" As cool as gemini seems... I'm not sure I have the energy/free time to target it right now. Maybe I'll revisit later. This reverts commit 373d0945fb2fc64b3f39f12540b41efdad75a8df. --- .gitignore | 3 +- process_capsule.py | 85 ---------------------------------------------- shell.nix | 11 ------ 3 files changed, 1 insertion(+), 98 deletions(-) delete mode 100644 process_capsule.py delete mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index 3215f1d..d5f97c8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ /public/ /resources/ /.env* -/capsule/ -/.certificates/ + diff --git a/process_capsule.py b/process_capsule.py deleted file mode 100644 index 73003fa..0000000 --- a/process_capsule.py +++ /dev/null @@ -1,85 +0,0 @@ -# Adapted from Will Webberley's work: -# https://wilw.dev/blog/2023/06/01/automatic-gemini-publishing/ -# https://git.wilw.dev/wilw/wilw.dev/src/branch/main/gemini/process_capsule.py - -import os, datetime, re -import frontmatter - -capsule_url = 'gemini://gmi.runtimeterror.dev' -web_url = 'https://runtimeterror.dev' -content_dir = 'content' -email = 'blog@runtimeterror.dev' -feed_url = 'https://runtimeterror.dev/feed.xml' -site_name = '[runtimeterror]' - -# Gemini-ify hyperlinks -def transform_links(content): - lines = content.splitlines() - pattern = re.compile(r'\[([^][]+)\]\(([^()]+)\)') - current_link_ref = 1 - new_lines = [] - for line_index, line in enumerate(lines): - if line.startswith('!['): continue - links_to_add = [] - if not line.startswith('=>'): - for link_index, match in enumerate(pattern.finditer(line)): - description, url = match.groups() - line = line.replace(f'({url})', f' [{current_link_ref}]').replace(f'[{description}]', description) - # if URL self-references website, handle differently: - if not url.startswith('http') or url.startswith(web_url): - if web_url in url: url = url.replace(web_url, '') - url = capsule_url + '/' + url.replace('/', ' ').strip().replace(' ', '-') + '.gmi' - links_to_add.append(f'=> {url} {current_link_ref}') - current_link_ref += 1 - new_lines.append(line) - for link in links_to_add: - new_lines.append(link) - return '\n'.join(new_lines) - -# Create new directories -needed_directories = ['capsule'] -for dir in needed_directories: - if not os.path.exists(dir): - os.makedirs(dir) - -# Generate blog posts -logs = [] -for dirpath, dirnames, filenames in os.walk(content_dir): - if 'index.md' in filenames: - post = frontmatter.load(os.path.join(dirpath, 'index.md')) - date = post['date'] if isinstance(post['date'], datetime.date) else datetime.datetime.strptime(post['date'].split('T')[0], '%Y-%m-%d') - content = transform_links(post.content) - new_content = ''' -=> {} 🏑 Home - -# {} -## Posted on {} - -{} - -=> mailto:{} Reply via email -=> {} Back to home - '''.format(capsule_url, post['title'], date.strftime('%Y-%m-%d'), content, email, capsule_url) - new_file_name = f'{os.path.basename(dirpath)}.gmi' - logs.append({'file': new_file_name, 'title': post['title'], 'date': date}) - with open(f'capsule/{new_file_name}', 'w') as gem_file: - gem_file.write(new_content) - -# Generate blog index -logs.sort(key=lambda entry: entry['date'].strftime('%Y-%m-%d'), reverse=True) -log_index_content = ''' -=> {} 🏑 Home - -# {} - -=> {} πŸ“² Subscribe via RSS - -{} - -=> {} 🏑 Home -'''.format(capsule_url, site_name, feed_url, - '\n'.join(['=> {} {} - {}'.format(l['file'], l['date'].strftime('%Y-%m-%d'), l['title']) for l in logs]), - capsule_url) -gem_index_file = open(f'capsule/index.gmi', 'w') -gem_index_file.write(log_index_content) - diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 3140b63..0000000 --- a/shell.nix +++ /dev/null @@ -1,11 +0,0 @@ -let - pkgs = import {}; -in pkgs.mkShell { - packages = with pkgs; [ - agate - hugo - (python3.withPackages (python-pkgs: [ - python-pkgs.python-frontmatter - ])) - ]; -} From 57031c5c96f4ac06a6e66d7bce84524b8588e9da Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 26 Feb 2024 16:30:40 -0600 Subject: [PATCH 06/36] update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d5f97c8..b6408e1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ /package.json /public/ /resources/ -/.env* +/.env +/.direnv/ From 217e4cb2b0b571f0f0584b360fca417d9418bdb0 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 26 Feb 2024 16:54:11 -0600 Subject: [PATCH 07/36] add nix flake for nix develop shell with direnv --- .envrc | 2 ++ flake.lock | 27 +++++++++++++++++++++++++++ flake.nix | 21 +++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c06c8fd --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +#!/usr/bin/env direnv +use flake . diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..391d4c2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1708807242, + "narHash": "sha256-sRTRkhMD4delO/hPxxi+XwLqPn8BuUq6nnj4JqLwOu0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "73de017ef2d18a04ac4bfd0c02650007ccb31c2a", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..2ebfeec --- /dev/null +++ b/flake.nix @@ -0,0 +1,21 @@ +{ + description = "runtimeterror build environment"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in + { + devShells.x86_64-linux.default = pkgs.mkShell { + packages = with pkgs; [ + go + hugo + nodePackages.npm + ]; + }; + }; +} \ No newline at end of file From fe29a0bf1ae317da75383569cb960fa647614612 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 26 Feb 2024 22:57:24 -0600 Subject: [PATCH 08/36] initial rework on building a gemini capsule --- .gitignore | 2 +- config/_default/hugo.toml | 16 +++++++++++++-- content/404.md | 1 + content/about.md | 5 ++--- flake.nix | 1 + layouts/_default/index.gmi | 7 +++++++ layouts/_default/single.gmi | 41 +++++++++++++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 layouts/_default/index.gmi create mode 100644 layouts/_default/single.gmi diff --git a/.gitignore b/.gitignore index b6408e1..5d739f1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ /resources/ /.env /.direnv/ - +/.certificates/ diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index f7f30ab..68b8f4a 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -7,20 +7,32 @@ languageCode = "en" DefaultContentLanguage = "en" enableInlineShortcodes = true +# define gemini media type +[mediaTypes] + [mediaTypes.'text/gemini'] + suffixes = ["gmi"] + # Automatically add content sections to main menu # sectionPagesMenu = "main" [outputs] - home = ['html', 'rss'] + home = ['html', 'rss', 'gemini'] section = ['html'] - taxonomy = ['html',] + taxonomy = ['html'] term = ['html', 'rss'] + page = ['html', 'rss', 'gemini'] # rename rss output from index.xml to feed.xml [outputFormats] [outputFormats.rss] mediatype = "application/rss" baseName = "feed" + [outputFormats.gemini] + mediatype = "text/gemini" + isPlainText = true + isHTML = false + protocol = "gemini://" + permalinkable = true [permalinks] posts = ":filename" diff --git a/content/404.md b/content/404.md index 03637e1..2b42a62 100644 --- a/content/404.md +++ b/content/404.md @@ -7,6 +7,7 @@ kudos = false +++ We're not sure what you were looking for but it's not here. + ![Animated GIF from the movie "The Naked Gun". A man in the foreground proclaims "Please disperse. Nothing to see here." while a building explodes in the background.](/images/nothing-to-see-here.gif) Maybe head back [home](/)? diff --git a/content/about.md b/content/about.md index b6505ce..76f2acb 100644 --- a/content/about.md +++ b/content/about.md @@ -5,6 +5,7 @@ timeless = true comments = false aliases = ["tldr", "bio"] +++ + ![Me, +/- a few decades](/images/john.jpg) You've (somehow) managed to stumble upon my dark corner of the internet[^1]. @@ -36,7 +37,5 @@ Connect with me via: - [XMPP](https://conversations.im/i/jbowdre@omg.lol?omemo-sid-1374125881=a620f3c57733601a6646f6f13a71c86fc9be8dd4126fd158ef3e0a26beb0b434) - [Electronic Mail](mailto:jbowdre@omg.lol) - [PGP: 613F B70C 4FA7 A077](https://l.runtimeterror.dev/pgp) - - [^1]: Congrats? And also, *thank you.* -[^2]: A bit. I'm still in the "fake it until you make" it phase of adulthood. \ No newline at end of file +[^2]: A bit. I'm still in the "fake it until you make" it phase of adulthood. diff --git a/flake.nix b/flake.nix index 2ebfeec..2f3f05d 100644 --- a/flake.nix +++ b/flake.nix @@ -12,6 +12,7 @@ { devShells.x86_64-linux.default = pkgs.mkShell { packages = with pkgs; [ + agate go hugo nodePackages.npm diff --git a/layouts/_default/index.gmi b/layouts/_default/index.gmi new file mode 100644 index 0000000..a3bea3d --- /dev/null +++ b/layouts/_default/index.gmi @@ -0,0 +1,7 @@ +## [runtimeterror $] +{{ $pages := .Pages -}} +{{ $pages = where site.RegularPages "Type" "in" site.Params.mainSections -}} + +{{ range $pages }} +=> {{ .RelPermalink }} [{{ .Date.Format "2006-01-02" }}] {{ .Title }} +{{- end }} diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi new file mode 100644 index 0000000..698f39f --- /dev/null +++ b/layouts/_default/single.gmi @@ -0,0 +1,41 @@ +=> / 🏑 [runtimeterror $] + +# {{ .Title }}{{ $scratch := newScratch }} +{{ $content := .RawContent -}} +{{ $content := $content | replaceRE `#### ` "### " -}} +{{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}} +{{ $content := $content | replaceRE `\n(\d+). (.+?)` "\n* $2" -}} +{{ $content := $content | replaceRE `\[\^(.+?)\]:.*\n?\n` "" -}} +{{ $content := $content | replaceRE `\[\^(.+?)\]` "" -}} +{{ $content := $content | replaceRE `
` "\n" -}} +{{ $content := $content | replaceRE `\{\{%\s\/?notice.*%\}\}` "***" -}} +{{ $content := $content | replaceRE `((\/\/)|#)\s*torchlight!.*` "" -}} +{{ $content := $content | replaceRE `` "" -}} +{{ $content := $content | replaceRE `\/\/\s*\[tl!.*\]` "" -}} +{{ $content := $content | replaceRE `#\s*\[tl!.*\]` "" -}} +{{ $content := $content | replaceRE `(.+?)` "[$2]($1)" -}} +{{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$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 John Bowdre {{ if .Params.Date }} on {{ .Lastmod.Format "2006-01-02" }}.{{ end}} + +## Links +{{ $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 }}{{ end }}{{ end }} +--- + +=> / Back to the Index +=> https://runtimeterror.dev{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} View this article on the WWW \ No newline at end of file From 8cf8ba7f7852e850f3f5c24c887e0ca50c6ed150 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 26 Feb 2024 23:10:30 -0600 Subject: [PATCH 09/36] tweak for trailing footnote removal in gemini --- layouts/_default/single.gmi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 698f39f..74c7257 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -5,7 +5,7 @@ {{ $content := $content | replaceRE `#### ` "### " -}} {{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}} {{ $content := $content | replaceRE `\n(\d+). (.+?)` "\n* $2" -}} -{{ $content := $content | replaceRE `\[\^(.+?)\]:.*\n?\n` "" -}} +{{ $content := $content | replaceRE `\[\^(.+?)\]:.*\n?\n?` "" -}} {{ $content := $content | replaceRE `\[\^(.+?)\]` "" -}} {{ $content := $content | replaceRE `
` "\n" -}} {{ $content := $content | replaceRE `\{\{%\s\/?notice.*%\}\}` "***" -}} From d6ff5027f917cc1619d27356276f20a026f66ae7 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Mon, 26 Feb 2024 23:10:45 -0600 Subject: [PATCH 10/36] add reply by email on gemini --- layouts/_default/single.gmi | 1 + 1 file changed, 1 insertion(+) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 74c7257..5566226 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -25,6 +25,7 @@ --- Written by John Bowdre {{ if .Params.Date }} on {{ .Lastmod.Format "2006-01-02" }}.{{ end}} +=> mailto:blog@runtimeterror.dev πŸ“§ Reply via email ## Links {{ $scratch.Set "ref" (add (len $links) 1) }}{{ range $refs }}{{ $ref := $scratch.Get "ref" }}{{ $url := (printf "%s #%d" . $ref) }} From 96eb0c3052940f48677130d0a1a0233e5ce64319 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 10:31:35 -0600 Subject: [PATCH 11/36] add more styling for gemini capsule --- layouts/_default/index.gmi | 11 ++++++++++- layouts/_default/single.gmi | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/layouts/_default/index.gmi b/layouts/_default/index.gmi index a3bea3d..9a7f276 100644 --- a/layouts/_default/index.gmi +++ b/layouts/_default/index.gmi @@ -1,4 +1,13 @@ -## [runtimeterror $] +``` + ___ _ _ _ _ ___ +| _| | | (_) | | | |_ | +| | _ __ _ _ _ __ | |_ _ _ __ ___ ___| |_ ___ _ __ _ __ ___ _ __ / __)| | +| || '__| | | | '_ \| __| | '_ ` _ \ / _ \ __/ _ \ '__| '__/ _ \| '__| \__ \| | +| || | | |_| | | | | |_| | | | | | | __/ || __/ | | | | (_) | | ( /| | +| ||_| \__,_|_| |_|\__|_|_| |_| |_|\___|\__\___|_| |_| \___/|_| |_|_| | +|___| |___| +``` + {{ $pages := .Pages -}} {{ $pages = where site.RegularPages "Type" "in" site.Params.mainSections -}} diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 5566226..49d76de 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -1,4 +1,4 @@ -=> / 🏑 [runtimeterror $] +=> / πŸ’» [runtimeterror $] # {{ .Title }}{{ $scratch := newScratch }} {{ $content := .RawContent -}} From 78155c8c45d9694933ede4fc3fdac7d919cde309 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:09:21 -0600 Subject: [PATCH 12/36] =?UTF-8?q?update=20action=20to=20deploy=20to=20gemi?= =?UTF-8?q?ni=20=F0=9F=A4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...oy-to-neocities.yml => deploy-to-prod.yml} | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) rename .github/workflows/{deploy-to-neocities.yml => deploy-to-prod.yml} (61%) diff --git a/.github/workflows/deploy-to-neocities.yml b/.github/workflows/deploy-to-prod.yml similarity index 61% rename from .github/workflows/deploy-to-neocities.yml rename to .github/workflows/deploy-to-prod.yml index 2b8428f..6e307cd 100644 --- a/.github/workflows/deploy-to-neocities.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -1,4 +1,4 @@ -name: Deploy to Neocities +name: Deploy to Production # only run on changes to main on: @@ -9,7 +9,7 @@ on: - main concurrency: # prevent concurrent deploys doing strange things - group: deploy-to-neocities + group: deploy-to-prod cancel-in-progress: true # Default to bash @@ -40,9 +40,20 @@ jobs: run: | npm i @torchlight-api/torchlight-cli npx torchlight - - name: Deploy to Neocities + - name: Deploy HTML to Neocities uses: bcomnes/deploy-to-neocities@v1 with: api_token: ${{ secrets.NEOCITIES_API_TOKEN }} cleanup: true - dist_dir: public \ No newline at end of file + dist_dir: public + - name: Connect to Tailscale + uses: tailscale/github-action@v2 + with: + oauth-client-id: ${{ secrets.TS_API_CLIENT_ID }} + oauth-secret: ${{ secrets.TS_API_CLIENT_SECRET }} + tags: ${{ secrets.TS_TAG }} + - name: Deploy GMI to Agate + run: | + ssh-keyscan -H ${{ secrets.GMI_HOST }} >> ~/.ssh/known_hosts + rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + From 8a9edae411dd01266f0c3d161fa08d250aaaf1f9 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:11:43 -0600 Subject: [PATCH 13/36] ensure .ssh dir exists --- .github/workflows/deploy-to-prod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index 6e307cd..001f046 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -54,6 +54,7 @@ jobs: tags: ${{ secrets.TS_TAG }} - name: Deploy GMI to Agate run: | + mkdir -p ~/.ssh ssh-keyscan -H ${{ secrets.GMI_HOST }} >> ~/.ssh/known_hosts rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From cff3bee4722663938f57ea90e410e298dde012af Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:18:10 -0600 Subject: [PATCH 14/36] specify rsync user --- .github/workflows/deploy-to-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index 001f046..db76ece 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -56,5 +56,5 @@ jobs: run: | mkdir -p ~/.ssh ssh-keyscan -H ${{ secrets.GMI_HOST }} >> ~/.ssh/known_hosts - rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER}}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From e1aa40dd887bf51a564326635ea112244e356a7b Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:21:46 -0600 Subject: [PATCH 15/36] separate ssh-keyscan and rsync tasks --- .github/workflows/deploy-to-prod.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index db76ece..cf8d707 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -52,9 +52,12 @@ jobs: oauth-client-id: ${{ secrets.TS_API_CLIENT_ID }} oauth-secret: ${{ secrets.TS_API_CLIENT_SECRET }} tags: ${{ secrets.TS_TAG }} - - name: Deploy GMI to Agate + - name: Cache SSH known hosts run: | mkdir -p ~/.ssh ssh-keyscan -H ${{ secrets.GMI_HOST }} >> ~/.ssh/known_hosts - rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER}}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + - name: Deploy GMI to Agate + run: | + which rsync + rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER }}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From 8d1f21d1f2a27ee70a4f4c8d6987892b243ace5e Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:42:48 -0600 Subject: [PATCH 16/36] install SSH key --- .github/workflows/deploy-to-prod.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index cf8d707..550af70 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -52,10 +52,12 @@ jobs: oauth-client-id: ${{ secrets.TS_API_CLIENT_ID }} oauth-secret: ${{ secrets.TS_API_CLIENT_SECRET }} tags: ${{ secrets.TS_TAG }} - - name: Cache SSH known hosts - run: | - mkdir -p ~/.ssh - ssh-keyscan -H ${{ secrets.GMI_HOST }} >> ~/.ssh/known_hosts + - name: Install SSH key + uses: shimataro/ssh-key-action@v2 + with: + key: ${{ secrets.SSH_KEY }} + name: id_rsa + known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - name: Deploy GMI to Agate run: | which rsync From 2f7f4a3a26e9c710a4d780d3b07a9633d8e00267 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:54:23 -0600 Subject: [PATCH 17/36] make less verbose --- .github/workflows/deploy-to-prod.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index 550af70..ab191cb 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -60,6 +60,5 @@ jobs: known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - name: Deploy GMI to Agate run: | - which rsync - rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER }}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + rsync -az --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER }}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From 92c0dfbedb887186389e5d3052879ed43d3ccb9c Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:56:54 -0600 Subject: [PATCH 18/36] name-and-shame deploy user for less ******* in logs --- .github/workflows/deploy-to-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index ab191cb..4af5046 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -60,5 +60,5 @@ jobs: known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - name: Deploy GMI to Agate run: | - rsync -az --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ ${{ secrets.GMI_USER }}@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + rsync -az --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ deploy@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From fd92a338a70b8c773476bf120c03d587290174eb Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 19:59:12 -0600 Subject: [PATCH 19/36] re-add -v to rsync to list files --- .github/workflows/deploy-to-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index 4af5046..f509f2f 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -60,5 +60,5 @@ jobs: known_hosts: ${{ secrets.SSH_KNOWN_HOSTS }} - name: Deploy GMI to Agate run: | - rsync -az --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ deploy@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} + rsync -avz --delete --exclude='*.html' --exclude='*.css' --exclude='*.js' -e ssh public/ deploy@${{ secrets.GMI_HOST }}:${{ secrets.GMI_CONTENT_PATH }} From 39a4fd100aa28503335aa6b74ea93668aab2c88d Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 20:12:09 -0600 Subject: [PATCH 20/36] comments --- config/_default/hugo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/_default/hugo.toml b/config/_default/hugo.toml index 68b8f4a..b864e69 100644 --- a/config/_default/hugo.toml +++ b/config/_default/hugo.toml @@ -27,6 +27,7 @@ enableInlineShortcodes = true [outputFormats.rss] mediatype = "application/rss" baseName = "feed" + # gemini output [outputFormats.gemini] mediatype = "text/gemini" isPlainText = true From 09fcc982451379aa3be248ff7c0069f27e6b7c0f Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 20:18:16 -0600 Subject: [PATCH 21/36] tweak gemini index layout --- layouts/_default/index.gmi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/layouts/_default/index.gmi b/layouts/_default/index.gmi index 9a7f276..31120ba 100644 --- a/layouts/_default/index.gmi +++ b/layouts/_default/index.gmi @@ -8,9 +8,15 @@ |___| |___| ``` +Adventures in self-hosting and other technological frustrations. + {{ $pages := .Pages -}} {{ $pages = where site.RegularPages "Type" "in" site.Params.mainSections -}} {{ range $pages }} => {{ .RelPermalink }} [{{ .Date.Format "2006-01-02" }}] {{ .Title }} {{- end }} + +--- +=> /about About +=> https://runtimeterror.dev View this site on the WWW \ No newline at end of file From 337885c000ecdfc60708a2b7e17780d050de69c1 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 22:32:05 -0600 Subject: [PATCH 22/36] add handy script for quick local gemini builds --- gem-build.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 gem-build.sh diff --git a/gem-build.sh b/gem-build.sh new file mode 100755 index 0000000..bd1c6c1 --- /dev/null +++ b/gem-build.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# Quick script to serve gemini locally +hugo --environment local -D +agate --content public --hostname localhost \ No newline at end of file From a4107cf8affb6e17b18dee050b6489665289ace0 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Tue, 27 Feb 2024 22:32:12 -0600 Subject: [PATCH 23/36] minor gemini layout tweaks --- layouts/_default/index.gmi | 20 +++++--------------- layouts/_default/single.gmi | 13 +++++++++---- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/layouts/_default/index.gmi b/layouts/_default/index.gmi index 31120ba..67226c6 100644 --- a/layouts/_default/index.gmi +++ b/layouts/_default/index.gmi @@ -1,22 +1,12 @@ -``` - ___ _ _ _ _ ___ -| _| | | (_) | | | |_ | -| | _ __ _ _ _ __ | |_ _ _ __ ___ ___| |_ ___ _ __ _ __ ___ _ __ / __)| | -| || '__| | | | '_ \| __| | '_ ` _ \ / _ \ __/ _ \ '__| '__/ _ \| '__| \__ \| | -| || | | |_| | | | | |_| | | | | | | __/ || __/ | | | | (_) | | ( /| | -| ||_| \__,_|_| |_|\__|_|_| |_| |_|\___|\__\___|_| |_| \___/|_| |_|_| | -|___| |___| -``` - -Adventures in self-hosting and other technological frustrations. - +# [runtimeterror $] +=> /about Adventures in self-hosting and other technological frustrations. {{ $pages := .Pages -}} {{ $pages = where site.RegularPages "Type" "in" site.Params.mainSections -}} +### Posts {{ range $pages }} -=> {{ .RelPermalink }} [{{ .Date.Format "2006-01-02" }}] {{ .Title }} +=> {{ .RelPermalink }} {{ .Date.Format "2006-01-02" }} {{ .Title }} {{- end }} --- -=> /about About -=> https://runtimeterror.dev View this site on the WWW \ No newline at end of file +=> https://runtimeterror.dev This site on the big web \ No newline at end of file diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 49d76de..4c2436d 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -1,6 +1,12 @@ => / πŸ’» [runtimeterror $] # {{ .Title }}{{ $scratch := newScratch }} +{{- if .Params.Date }} + {{- $postDate := .Date.Format "2006-01-02" }} + {{- $updateDate := .Lastmod.Format "2006-01-02" }} + {{ $postDate }}{{ if ne $postDate $updateDate }} ~ {{ $updateDate }}{{ end }} +{{- end }} +{{/* The bulk of this regex magic is derived from https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/ */}} {{ $content := .RawContent -}} {{ $content := $content | replaceRE `#### ` "### " -}} {{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}} @@ -24,10 +30,9 @@ {{ $content | safeHTML }} --- -Written by John Bowdre {{ if .Params.Date }} on {{ .Lastmod.Format "2006-01-02" }}.{{ end}} => mailto:blog@runtimeterror.dev πŸ“§ Reply via email -## Links +## Bibliography {{ $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}} @@ -38,5 +43,5 @@ Written by John Bowdre {{ if .Params.Date }} on {{ .Lastmod.Format "2006-01-02" => {{ replace .RelPermalink "/gemini" "" 1}} {{ .Title }}{{ end }}{{ end }} --- -=> / Back to the Index -=> https://runtimeterror.dev{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} View this article on the WWW \ No newline at end of file +=> / Home +=> https://runtimeterror.dev{{ replace (replace .RelPermalink "/gemini" "" 1) "index.gmi" "" }} This article on the big web \ No newline at end of file From b7404377a701ff456475b23e6023913bceb26e63 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Wed, 28 Feb 2024 11:38:16 -0600 Subject: [PATCH 24/36] improve gemini display of li links (esp on about page) --- layouts/_default/single.gmi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 4c2436d..62b50d7 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -21,9 +21,10 @@ {{ $content := $content | replaceRE `#\s*\[tl!.*\]` "" -}} {{ $content := $content | replaceRE `(.+?)` "[$2]($1)" -}} {{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$1)" -}} +{{ $content := $content | replaceRE `\n\*\s+\[(.+?)\]\((.+?)\)` "\n=> $2 $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" -}} +{{ $content := $content | replaceRE `\n?\n!\[.*\]\((.+?) \"(.+?)\"\)` "\n=> $1 Image: $2" -}} +{{ $content := $content | replaceRE `\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]" -}} From 6e2f94b54a635af670a0481a168894c6856e79f3 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Wed, 28 Feb 2024 11:42:56 -0600 Subject: [PATCH 25/36] minor gemini regex tweaks --- layouts/_default/single.gmi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 62b50d7..b63727d 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -23,8 +23,8 @@ {{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$1)" -}} {{ $content := $content | replaceRE `\n\*\s+\[(.+?)\]\((.+?)\)` "\n=> $2 $1" -}} {{/* $content := $content | replaceRE "([^`])<.*?>([^`])" "$1$2" -*/}} -{{ $content := $content | replaceRE `\n?\n!\[.*\]\((.+?) \"(.+?)\"\)` "\n=> $1 Image: $2" -}} -{{ $content := $content | replaceRE `\n?\n!\[.*\]\((.+?)\)` "\n=> $1 Embedded Image: $1" -}} +{{ $content := $content | replaceRE `\n+!\[.*\]\((.+?) \"(.+?)\"\)` "\n\n=> $1 Image: $2" -}} +{{ $content := $content | replaceRE `\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]" -}} From 9f70775a130e22c1f5d486d0734ef0797cbc2c93 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Fri, 1 Mar 2024 21:43:32 -0600 Subject: [PATCH 26/36] gemini improvements --- content/about.md | 8 +-- .../index.md | 7 +++ content/simplex.md | 20 +++---- layouts/_default/single.gmi | 60 +++++++++++-------- 4 files changed, 55 insertions(+), 40 deletions(-) diff --git a/content/about.md b/content/about.md index 76f2acb..b9c5bd5 100644 --- a/content/about.md +++ b/content/about.md @@ -5,7 +5,6 @@ timeless = true comments = false aliases = ["tldr", "bio"] +++ - ![Me, +/- a few decades](/images/john.jpg) You've (somehow) managed to stumble upon my dark corner of the internet[^1]. @@ -22,20 +21,21 @@ On weekends, I race my daily-driven 2014 Subaru BRZ in local [autocross events]( And in the free time I have left, I game on my Steam Deck. -See what I've been up to on: +### See what I've been up to on: - [GitHub](https://github.com/jbowdre) -- [Scribbles 'n Bits](https://scribbes.jbowdre.lol) +- [Scribbles 'n Bits](https://scribbles.jbowdre.lol) - [status.lol](https://status.jbowdre.lol) - [social.lol](https://social.lol/@jbowdre) - [CounterSocial](https://counter.social/@john_b) - [/now](https://now.jbowdre.lol) -Connect with me via: +### Connect with me via: - [SimpleX Chat](/simplex/) - [Signal](https://signal.me/#eu/lyHZbMnlM16O0w48j3rshYBofO0K-iXOt9LGwln7TS-fNKEHCrxH3La325q8IjRU) - [Matrix](https://matrix.to/#/@jbowdre:omg.lol) - [XMPP](https://conversations.im/i/jbowdre@omg.lol?omemo-sid-1374125881=a620f3c57733601a6646f6f13a71c86fc9be8dd4126fd158ef3e0a26beb0b434) - [Electronic Mail](mailto:jbowdre@omg.lol) - [PGP: 613F B70C 4FA7 A077](https://l.runtimeterror.dev/pgp) + [^1]: Congrats? And also, *thank you.* [^2]: A bit. I'm still in the "fake it until you make" it phase of adulthood. diff --git a/content/posts/automating-camera-notifications-home-assistant-ntfy/index.md b/content/posts/automating-camera-notifications-home-assistant-ntfy/index.md index 0e7073f..3184794 100644 --- a/content/posts/automating-camera-notifications-home-assistant-ntfy/index.md +++ b/content/posts/automating-camera-notifications-home-assistant-ntfy/index.md @@ -134,6 +134,7 @@ homeassistant: ``` I'm using the [Home Assistant Operating System virtual appliance](https://www.home-assistant.io/installation/alternative#install-home-assistant-operating-system), so `/media` is already symlinked to `/root/media` inside the Home Assistant installation directory. So I'll just log into that shell and create the `snaps` subdirectory: + ```shell mkdir -p /media/snaps # [tl! .cmd_root] ``` @@ -149,6 +150,7 @@ Now that I've captured the snap, I need to figure out how to attach it to the no I can't use the handy `!secret` expansion inside of the shell command, though, so I'll need a workaround to avoid sticking sensitive details directly in my `configuration.yaml`. I can use a dummy sensor to hold the value, and then use the `{{ states('sensor.$sensor_name') }}` template to retrieve it. So here we go: + ```yaml # configuration.yaml [tl! focus:start] @@ -180,6 +182,7 @@ shell_command: # [tl! focus:9 highlight:6,1] ``` Now I just need to replace the service call in the automation with the new `shell_command.ntfy_put` one: + ```yaml # torchlight! {"lineNumbers": true} # exterior_motion.yaml # [tl! focus] @@ -230,6 +233,7 @@ Well that guy seems sus - but hey, it worked! Of course, I'll also continue to get notified about that creeper in the backyard about every 15-20 seconds or so. That's not quite what I want. The _easy_ way to prevent an automation from firing constantly would be to [insert a `delay`](https://www.home-assistant.io/docs/scripts/#wait-for-time-to-pass-delay) action, but that would be a global delay rather than per-camera. I don't necessarily need to know every time the weirdo in the backyard moves, but I would like to know if he moves around to the side yard or driveway. So I needed something more flexible than an automation-wide delay. Instead, I'll create a 5-minute [`timer`](https://www.home-assistant.io/integrations/timer/) for each camera by simply adding this to my `configuration.yaml`: + ```yaml # configuration.yaml timer: @@ -310,6 +314,7 @@ That pretty much takes care of my needs for exterior motion alerts, and should k ### Managing interior alerts I've got a few interior cameras which I'd like to monitor too, so I'll start by just copying the exterior automation and updating the entity IDs: + ```yaml # torchlight! {"lineNumbers": true} # interior_motion.yaml @@ -361,6 +366,7 @@ But I don't typically want to get alerted by these cameras if my wife or I are h ![calendar](schedule.png) So then I'll just add another condition so that the automation will only fire during those calendar events: + ```yaml # torchlight! {"lineNumbers": true} # interior_motion.yaml [tl! focus] @@ -517,6 +523,7 @@ icon: mdi:alarm-snooze I can then add that script to the camera dashboard in Home Assistant or pin it to the home controls on my Android phone for easy access. I'll also create another script for manually toggling interior alerts for when we're home at an odd time: + ```yaml # torchlight! {"lineNumbers": true} # toggle_interior_alerts.yaml diff --git a/content/simplex.md b/content/simplex.md index 4937533..c27283b 100644 --- a/content/simplex.md +++ b/content/simplex.md @@ -8,7 +8,7 @@ showReadTime = false timeless = true title = "SimpleX Chat" +++ -*You can **[contact me on SimpleX Chat](https://l.runtimeterror.dev/simplex-chat-invite)** by clicking that link or scanning the QR code below.* +> You can [contact me on SimpleX Chat](https://l.runtimeterror.dev/simplex-chat-invite) by clicking that link or scanning the QR code below. ![QR code](/images/simplex-invite.png) @@ -28,9 +28,9 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server `smp://kYx5LmVD9FMM8hJN4BQqL4WmeUNZn8ipXsX2UkBoiHE=@smp.vpota.to` -| | | -| --- | --- | -| [![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp-vpota-to-5223) | [[netdata](https://l.runtimeterror.dev/smp_status)] | +[![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp-vpota-to-5223) + +[netdata](https://l.runtimeterror.dev/smp_status) --- @@ -38,9 +38,9 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server `smp://TbUrGydawdVKID0Lvix14UkaN-WarFgqXx4kaEG8Trw=@smp1.vpota.to` -| | | -| --- | --- | -| [![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp1-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp1-vpota-to-5223) | [[netdata](https://l.runtimeterror.dev/smp1_status)] | +[![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp1-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp1-vpota-to-5223) + +[netdata](https://l.runtimeterror.dev/smp1_status) --- @@ -48,6 +48,6 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server `smp://tNfQisxTQ9MhKpFDTbx9RnjgWigtxF1a26jroy5-rR4=@smp2.vpota.to` -| | | -| --- | --- | -| [![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp2-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp2-vpota-to-5223) | [[netdata](https://l.runtimeterror.dev/smp2_status)] | +[![Uptime](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fjbowdre%2Fupptime%2Fmaster%2Fapi%2Fsmp2-vpota-to-5223%2Fuptime.json)](https://status.runtimeterror.dev/history/smp2-vpota-to-5223) + +[netdata](https://l.runtimeterror.dev/smp2_status) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index b63727d..9462f0b 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -1,5 +1,4 @@ => / πŸ’» [runtimeterror $] - # {{ .Title }}{{ $scratch := newScratch }} {{- if .Params.Date }} {{- $postDate := .Date.Format "2006-01-02" }} @@ -8,35 +7,44 @@ {{- end }} {{/* The bulk of this regex magic is derived from https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/ */}} {{ $content := .RawContent -}} -{{ $content := $content | replaceRE `#### ` "### " -}} -{{ $content := $content | replaceRE `\n- (.+?)` "\n* $1" -}} -{{ $content := $content | replaceRE `\n(\d+). (.+?)` "\n* $2" -}} -{{ $content := $content | replaceRE `\[\^(.+?)\]:.*\n?\n?` "" -}} -{{ $content := $content | replaceRE `\[\^(.+?)\]` "" -}} -{{ $content := $content | replaceRE `
` "\n" -}} -{{ $content := $content | replaceRE `\{\{%\s\/?notice.*%\}\}` "***" -}} -{{ $content := $content | replaceRE `((\/\/)|#)\s*torchlight!.*` "" -}} -{{ $content := $content | replaceRE `` "" -}} -{{ $content := $content | replaceRE `\/\/\s*\[tl!.*\]` "" -}} -{{ $content := $content | replaceRE `#\s*\[tl!.*\]` "" -}} -{{ $content := $content | replaceRE `(.+?)` "[$2]($1)" -}} -{{ $content := $content | replaceRE `\sgemini://(\S*)` " [gemini://$1](gemini://$1)" -}} -{{ $content := $content | replaceRE `\n\*\s+\[(.+?)\]\((.+?)\)` "\n=> $2 $1" -}} -{{/* $content := $content | replaceRE "([^`])<.*?>([^`])" "$1$2" -*/}} -{{ $content := $content | replaceRE `\n+!\[.*\]\((.+?) \"(.+?)\"\)` "\n\n=> $1 Image: $2" -}} -{{ $content := $content | replaceRE `\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 }} - +{{- $content := $content | replaceRE `#{4,} ` "### " -}}{{/* reduce headings to a max of 3 levels */}} +{{- $content := $content | replaceRE `(?m:^- (.+?)$)` "\n* $1" -}}{{/* convert unordered lists */}} +{{- $content := $content | replaceRE `(?m:^(?:\d+). (.+?)$)` "* $1" -}}{{/* convert ordered lists */}} +{{- $content := $content | replaceRE `\n\[\^(.+?)\]:\s.*` "" -}}{{/* remove footnote definitions */}} +{{- $content := $content | replaceRE `\[\^(.+?)\]` "" -}}{{/* remove footnote anchors */}} +{{- $content := $content | replaceRE "(?m:^`([^`]*)`$)" "```\n$1\n```\n" -}}{{/* convert single-line inline code to blocks */}} +{{- $content := $content | replaceRE `\{\{%\snotice.*%\}\}` "<-- note -->" -}}{{/* convert hugo notices */}} +{{- $content := $content | replaceRE `\{\{%\s/notice.*%\}\}` "<-- /note -->" -}} +{{- $content := $content | replaceRE `((\/\/)|#)\s*torchlight!.*\n` "" -}}{{/* remove torchlight markup */}} +{{- $content := $content | replaceRE `(?:(?:" -}}{{/* convert hugo notices */}} {{- $content := $content | replaceRE `\{\{%\s/notice.*%\}\}` "<-- /note -->" -}} From 8034385a976f1628051f9016e61352cca63ea450 Mon Sep 17 00:00:00 2001 From: John Bowdre Date: Sun, 3 Mar 2024 14:44:08 -0600 Subject: [PATCH 30/36] gemini: display links below paragraphs --- layouts/_default/single.gmi | 83 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/layouts/_default/single.gmi b/layouts/_default/single.gmi index 1944a50..8992c36 100644 --- a/layouts/_default/single.gmi +++ b/layouts/_default/single.gmi @@ -1,52 +1,51 @@ +{{- $scratch := newScratch -}}{{- $scratch.Set "ref" 1 -}} => / πŸ’» [runtimeterror $] -# {{ .Title }}{{ $scratch := newScratch }} -{{- if .Params.Date }} +{{ if .Params.Date }} {{- $postDate := .Date.Format "2006-01-02" }} {{- $updateDate := .Lastmod.Format "2006-01-02" }} - {{ $postDate }}{{ if ne $postDate $updateDate }} ~ {{ $updateDate }}{{ end }} + {{- $postDate }}{{ if ne $postDate $updateDate }} ~ {{ $updateDate }}{{ end }} {{- end }} -{{/* The bulk of this regex magic is derived from https://brainbaking.com/post/2021/04/using-hugo-to-launch-a-gemini-capsule/ */}} -{{ $content := .RawContent -}} -{{- $content := $content | replaceRE `#{4,} ` "### " -}}{{/* reduce headings to a max of 3 levels */}} -{{- $content := $content | replaceRE `(?m:^- (.+?)$)` "\n* $1" -}}{{/* convert unordered lists */}} -{{- $content := $content | replaceRE `(?m:^(?:\d+). (.+?)$)` "* $1" -}}{{/* convert ordered lists */}} -{{- $content := $content | replaceRE `\n\[\^(.+?)\]:\s.*` "" -}}{{/* remove footnote definitions */}} -{{- $content := $content | replaceRE `\[\^(.+?)\]` "" -}}{{/* remove footnote anchors */}} -{{- $content := $content | replaceRE `((?m:^(?:\|.*\|)+\n)+)` "```\n$1\n```\n" -}}{{/* convert markdown tables to plaintext ascii */}} -{{- $content := $content | replaceRE "(?m:^`([^`]*)`$)" "```\n$1\n```\n" -}}{{/* convert single-line inline code to blocks */}} -{{- $content := $content | replaceRE `\{\{%\snotice.*%\}\}` "<-- note -->" -}}{{/* convert hugo notices */}} -{{- $content := $content | replaceRE `\{\{%\s/notice.*%\}\}` "<-- /note -->" -}} -{{- $content := $content | replaceRE `((\/\/)|#)\s*torchlight!.*\n` "" -}}{{/* remove torchlight markup */}} -{{- $content := $content | replaceRE `(?:(?:" -}}{{/* convert hugo notices */}} + {{- $content := $content | replaceRE `\{\{%\s/notice.*%\}\}` "<-- /note -->" -}} + {{- $content := $content | replaceRE `((\/\/)|#)\s*torchlight!.*\n` "" -}}{{/* remove torchlight markup */}} + {{- $content := $content | replaceRE `(?:(?: