From 367df08b530bb489738dc3d2308595500d2da6e1 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 d234bd13a8954bb11e261d529e8fef03a50d1999 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 bb2c884613e8bc3477e308dad9e8724246e6e3f7 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 1669e0ed0cca02aed9f02dea1f59b918e3d369f4 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 8e26f1a9b21d3b218daa414a415e868dcf7c4681 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 1669e0ed0cca02aed9f02dea1f59b918e3d369f4. --- .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 789c40f98cabb187b87e576950e0dea1df71099d 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 c7fd136217ad03f6be46d0730cff40fd9b3ba112 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 da1e11e2c53b8d2267056cd06cbf709b8bea7965 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 4d9a7d01d4e96e9f56bca07e73067fde08f98359 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 5db9726557d6ae6d87fc754232e6b62f1acb883c 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 4a68ba22c36df1187184b110a15f473ca2463dc1 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 34c957d72a92e01397f0d53da034236ce2cd8138 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 6fedef77947585a2072b0644c67ad7e24245edef 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 fe82db5f9b026a75a7ffd0a55af0f0595ac225ee 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 6fda9fb38f1191581ac12cb82267419fac97025f 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 74e3ce9663a858d32d60239bcabfdeb3e69fb6cb 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 219c842e45ecc7858cb39378a2140e5b1d1275a0 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 648de7a562a5fc0b5aad4d8eb1693bd6a56ab94a 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 50d3c0f91bc2a25f90e27c6f43ba15947dd5d6f2 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 df7bbc7b5317060bd506ef7a0f341b3240f1d8db 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 0c1874ef571f0d923c2c35a35d7a41bc26d7e1d1 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 4cf694fc47dd756e9c485042e801e08cc3bcf99c 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 9bb7cf108d01ebefacd5cc17733c5e6a0e824727 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 bfa55701d8a82e08968b00ec9b58cce922fa972b 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 95902d57d439cfa74da21a6cb2453fad50d8ac7b 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 b23af4eb4605fa55349f2210a578fe37bf91ce39 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 60d916261e9e4ef9b3bc8990b9c5c79432d844eb 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 `(?:(?: