I honestly don't know whether the past few days of tinkering with Emacs evil-mode, Doom, etc. was fun or a complete, confusing waste of time. Right now I'm thinking the latter.
Sunday, April 06, 2025
I honestly don't know whether the past few days of tinkering with Emacs evil-mode, Doom, etc. was fun or a complete, confusing waste of time. Right now I'm thinking the latter.
I don’t mean to be rude, but I don’t care much what you think. OK, that’s not exactly true, I care deeply what you think. Maybe it’s more accurate to say that I don’t worry about what you think. Are you mad that I keep switching platforms? Sorry, not sorry. Are you annoyed that I use words like “just” and “maybe” and “really” too often? Yeah, me too. We’ll get over it. Does it bother you that I don’t do enough throat-clearing before mentioning something that has become problematic? You’ll be fine. Would you prefer that I only write about Emacs? Not happening.
There, I feel a little better now, because when I got up this morning I wanted to apologize for being alive and just (see, there’s that word again) shut everything down. I don’t think I’ll do that, though, because regardless of what you think…I think…and it needs to come out somehow.
I should apologize for my mood this morning...Sorry about my mood this morning.
Yesterday, I decided to bring back my Doom Emacs config. I've been missing evil-mode and using Space as leader key. Sometimes hitting Control-this Control-that constantly becomes tedious, ya know? After a couple of hours, I bailed on the idea. Doom offers a ton of quality-of-life features, but it also contains a lot of magic. As much as I appreciate the magic, I too often feel like it's working against me. Back to my own config, which works against me too, but at least it's my fault this way.
I need to find a way for my brain to relax. I spend entire days with a dozen apps open, each with a dozen tabs open. I click rapidly between them looking for something to focus on. I never find anything.
Part of my problem might be that I've surrounded myself with too much infrastructure. There are dependencies everywhere. daily.baty.net for example. The idea is that I have a separate blog that works well for short, daily writing. Except that now I have two blogs. And I'm (for the moment) using Kirby for that one. This means an entirely different workflow and setup. This is great for when I'm feeling bored with Hugo. It keeps me from moving this blog back and forth. However, it's also more stuff in my brain. Do I need more stuff in my brain? Right now, I don't think so. Hence, I'm writing today's journal post here.
I was feeling nostalgic for Evil mode today, so, instead of doing the smart thing and installing and configuring Evil-mode, I brought back my Doom Emacs config and got it up to date. Doom is pretty great. It usually takes a couple weeks before I get twitchy about Emacs not feeling like "it's mine" but we'll see.
Kirby CMS uses plain .txt files for content. Since the bare .txt files should not be accessible with a browser, one normally uses a path matcher in Caddy and then denies requests based on a path, like so...
path *.txt /content/* /site/* /kirby/* /.*
But what if I want a /robots.txt file? Turned out to be a simple answer, but it took me a while to find it. I'm writing it down here in case anyone else might need it. Here's the whole block from my site's Caddyfile:
(kirby) {
php_fastcgi unix//run/php/php8.3-fpm.sock
@blocked {
path *.txt *.md /content/* /site/* /kirby/* /.*
not path /robots.txt # <----- Here's the important part
}
respond @blocked "Not Found" 404 {
close
}
}
With that one extra line, when you add an import kirby line to a server block, /robots.txt will remain accessible.
I saw the Irreal post about Journelly, but mostly ignored it because I wasn't looking for a new iOS journaling app. He did mention that Journelly is by Álvaro Ramírez, author of Plain Org, lmno.lol, and others, so that made things more interesting.
What intrigued me most, though, was learning that Journelly is backed by plain-text Org Mode files. Bonus! Now it had my attention.
Álvaro was kind enough to let me into the TestFlight, and I'm putting it through its paces this morning.
Here's what mine looks like so far...

Simple and easy to use, so that's good. As Álvaro desribed Journelly, it's like a personal, private Twitter/Instagram/Mastodon account.
Speaking of simple, the entire journal is kept as a single journelly.org file. I chose to sync mine via iCloud, which meant that I could view and edit it locally on my Mac. The problem with iCloud Drive's default storage locations is that they're in a stupid place, e.g. /Users/jbaty/Library/Mobile Documents/iCloud~com~xenodium~Journelly/Documents/Journelly.org. Don't make me create symlinks, iCloud! 😄
Digging into the settings, I noticed that in addition to the default iCloud Drive folder, I could configure Journelly to use any folder in iCloud, including my ever-popular ~/Documents/Notes folder. Much better.
Since I use Emacs for most everything, including my journals, I've never done much journaling on my Phone. Journelly could change this equation.
It would be cool if I could use Journelly from my Mac as well. To that end, I created an Org Mode capture template to make it easy to add entries while in Emacs on my Mac.
("j" "Journelly Entry" entry
(file "~/Documents/Notes/Journelly/Journelly.org")
"* %U @ -\n%?" :prepend t)
I can now hit C-c c j and I'm in a capture buffer for the Journelly file. The Journelly app can include weather and location to new entries. Currently, my capture template isn't smart enough to do that, but it could be, with just a bit of work.
It's only been a couple of hours with Journelly, so this isn't meant to be a review, but first impressions are that it's handy, simple, and could work well as an adjunct to my existing Org mode based note-taking setup.
For a recent review of Journelly, see Monday App Finder: Journelly, a Twitter-Like Journal for iOS - The Mac Observer.
I've migrated daily.baty.net to Kirby CMS. It has been a static site managed by Tinderbox for a long time. I love Tinderbox, but now that baty.net is back on Hugo, I wanted a playground for learning more about Kirby, without moving the main blog back and forth between Hugo and Kirby. So, here we are.
Your RSS feed is probably complaining right now. Sorry about that.
I ran across something neat while reading the Mu4e release notes
A (experimental) “transient” menu has been added for mu4e. You can use it e.g., with something like:
(require 'mu4e-transient)
(global-set-key (kbd "C-c m") #'mu4e-transient-menu)
It's very handy!
One of the things I'm finding useful about LLMs is having them summarize my own writing. For example, I threw this month's Org journal at ChatGPT and asked for a summary:
Jack Baty's journal entries from early March 2025 reflect his daily life, thoughts on photography, and personal observations. He struggles with sleep due to being woken early, enjoys organizing his journals and photos, and contemplates selling his cameras, particularly the Leica SL2 or Nikon Zf. He describes his experiences shopping with friends and family, expresses concerns about aging, and shares his mixed feelings about current events, especially regarding political figures. His reflections reveal a sense of nostalgia, motivation issues, and a desire for connection, particularly with his partner, Gail. He also documents mundane tasks, like grocery shopping and website building, while dealing with the ups and downs of daily life, including caring for his dog, Alice.
It's as if my journaling is raw material for a new set of documents summarizing my life. It's readable, accurate[1], and like seeing it this way.
🎶 "Absolute Elsewhere" by Blood Incantation.
When are we going to get past this period of Metal sounding like call-and-respond between Backstreet Boys and Cookie Monster with the flu? It's been self-parody for a decade.
Well OK, my feelings about current events and "political figures" are not at all "mixed". ↩︎
I thought it would be neat to include the date and weather on the images I use for my journal entry covers here on the blog.
It turned out to be neat, but not fun. I spent nearly 3 hours on all sorts of failed approaches. I figured it might be useful to write down where I ended up.
I leveraged two of my existing bash scripts that deal with the weather, and wrote a new one that uses ImageMagick to put things together.
Basically, I pass an image name to the script and it overlays the date at the bottom left and the weather at the bottom right. Like so...
~/bin/annotate-image.sh MyImage.jpg
Here is the result:

#!/bin/bash
# Check if an image name is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <image_file>"
exit 1
fi
IMAGE_FILE="$1"
OUTPUT_FILE="cover_$(basename "$IMAGE_FILE")"
WEATHER_URL="wttr.in/Grand+Rapids_0tqp0.png"
WEATHER_ICON=$(~/bin/getwcond)
WEATHER_ICON="/Users/jbaty/Sync/graphics/weather/64x64/day/$WEATHER_ICON"
CONDITIONS=$(~/bin/getweather | cut -f2 -d"|")
# Get current date in format "Friday, March 28, 2025"
CURRENT_DATE=$(date "+%A, %B %d, %Y")
# Add the weather info to the bottom right and the date to the bottom left
/opt/homebrew/bin/magick \
"$IMAGE_FILE" \
-fill white \
-undercolor '#6F737880' \
-font "Helvetica" \
-pointsize 40 \
-gravity SouthWest \
-annotate +20+20 "$CURRENT_DATE" \
-gravity SouthEast \
-pointsize 30 \
-annotate +20+20 "$CONDITIONS" \
"$OUTPUT_FILE"
# Now overlay the weather from wttr onto the output from the previous command
/opt/homebrew/bin/magick composite \
-gravity SouthEast \
-geometry +20+60 \
"$WEATHER_ICON" \
"$OUTPUT_FILE" \
"$OUTPUT_FILE"
echo "Weather added to image: $OUTPUT_FILE"
That script uses two other scripts that I wrote years ago for grabbing weather conditions getweather
#!/bin/sh
# Grab and parse weather info using WeatherAPI.com
jq=/opt/homebrew/bin/jq
weatherfile=`mktemp`
curl -s "https://api.weatherapi.com/v1/forecast.json?key=MYAPIKEY&q=MYZIP&days=1&aqi=no&alerts=no" > $weatherfile
now=`${jq} -r .current.condition.text ${weatherfile}`
temp=`${jq} -r .current.temp_f ${weatherfile}`
condition=`${jq} -r .forecast.forecastday[0].day.condition.text ${weatherfile}`
high=`${jq} -r .forecast.forecastday[0].day.maxtemp_f ${weatherfile}`
low=`${jq} -r .forecast.forecastday[0].day.mintemp_f ${weatherfile}`
echo "${now} ${temp} | Low ${low}, High ${high}"
Returns something like "Light rain 50.4 | Low 27.9, High 69.1". I use cut to grab just the high/low temperature part.
Then there's getwcond which is mostly a copy/paste job from the previous script. It returns the name of an image file for the current conditions (e.g. 234.png). I use this as the icon to overlay onto the image.
#!/bin/sh
# Jack Baty, 2023 (https://baty.net)
# Grab and parse weather info using WeatherAPI.com
jq=/opt/homebrew/bin/jq
weatherfile=`mktemp`
conditionfile=/Users/jbaty/Sync/graphics/weather/conditions.json
curl -s "https://api.weatherapi.com/v1/forecast.json?key=MYAPIKEY&q=MYZIP&days=1&aqi=no&alerts=no" > $weatherfile
code=`${jq} -r .forecast.forecastday[0].day.condition.code ${weatherfile}`
iconcond=`${jq} -r .current.condition.icon ${weatherfile}`
icon=$(${jq} '.[] | select(.code=='${code}').icon' ${conditionfile})
iconfile=$(basename $iconcond)
echo $iconfile
This all took me way too long. I could've pulled out the LLM crutch, but I really wanted to figure it out on my own. Next time I should just take the extra 2 minutes and use Photoshop instead :).
I spent hours this morning trying to find a good way of adding some metadata to the cover images on the blog. I wanted the temparature, at least. I tried shoehorning it into my Retrobatch script, but that was a dead end. Whenever I'm lost in image manipulation, I turn to ImageMagick. Boy did that take me down a rabbit hole. Long story short, I figured it out. But now I don't like it. ¯_(ツ)_/¯.
So, the so-called DOGE is planning to rewrite the SSA codebase in months. This sounds to me like a way to stop paying people, but being able to blame it on software.
I maintain a list of shell commands for updating Make/Model/Lens information in film scans. I've always run this via babel in a code block in an Org mode file. Something like this:
#+begin_src sh
cd ~/Pictures/_Scans
exiftool '-m' '-Make=Leica' '-Model=Leica MP' -overwrite_original .
exiftool '-m' '-LensModel=Summilux-M 1:1.4/50 ASPH' '-FocalLength=50mm' -overwrite_original .
#+End_src
Easy enough, I just copy and paste from a list of commands, depending on the lens and camera. However, it occurred to me that I'd prefer to simply have Emacs prompt me for the information, and then take care of the shell commands for me. Also, instead of hard-coding the path, I wanted to use marked files in a Dired buffer.
I had no idea how to approach this, so I tucked my tail between my legs and asked ChatGPT.
Write an emacs lisp function that, when run in a dired buffer, prompts me for a camera model (from a predefined list of models) and then uses exiftool to set the Make and Model fields of the marked files using my selection.
It worked, first try, but I'd forgotten about the lens options, so I followed up with:
Can you modify it so that I also select a lens and so that the lens information includes focal length?
In 20 seconds, with only a couple of small tweaks, I had a working solution. The only thing for me to do was put my options into camera-list and lens-list.
(defun my/dired-set-exif-camera-lens ()
"Set the Make, Model, Lens, and Focal Length of marked files in Dired using exiftool."
"Written (mostly) by ChatGPT"
(interactive)
(let* ((camera-list '(("Leica MP" . "Leica")
("Leica M3" . "Leica")
("Nikon FM2n" . "Nikon")
("Hasselblad 500C/M" . "Hasselblad")
("Olympus OM2n" . "Olympus")))
(lens-list '(("Summilux-M 1:1.4/50 ASPH" . "50mm")
("Summilux-M 1:1.4/35 ASPH FLE" . "35mm")
("Zeiss Planar f/2.8 80mm" . "80mm")
("Nikkor f/2.5 105mm" . "105mm)))
(camera-choice (completing-read "Select Camera Model: " (mapcar #'car camera-list)))
(lens-choice (completing-read "Select Lens: " (mapcar #'car lens-list)))
(make (cdr (assoc camera-choice camera-list)))
(focal-length (cdr (assoc lens-choice lens-list)))
(files (dired-get-marked-files)))
(dolist (file files)
(shell-command (format "exiftool -Make=\"%s\" -Model=\"%s\" -LensModel=\"%s\" -FocalLength=\"%s\" \"%s\" -overwrite_original"
make camera-choice lens-choice focal-length file)))
(revert-buffer))) ;; Refresh Dired buffer to reflect changes
I'm as skeptical as the next person about LLM use, but it's hard to argue with its efficacy for small tasks like this. I'm sure an expert in lisp could do better, but I'm not, and I didn't have to be one. This is a vast improvement over the way I used to do it and getting here saved me hours of fumbling around.
I knew it would happen, didn't you? I really don't want to talk about changing blog platforms, though. It's embarrassing. I'll just say that, while I don't love Hugo, it's the thing I'm least uncomfortable with overall. Let's move on, shall we?
Going slowly, making lots of little mistakes, and figuring out how things work is how you learn to solve tons of problems. AI platforms are creating a world where the solution to any problem is “more AI” and prompting.
Paul Ford, "Bad Vibes Coding"
LinkedIn is not the right place for me to find interesting work.
I was feeling envious of the Obsidian Web Clipper, which is quite fancy, so I thought I'd try leveraging it for use with Denote.
My first run at this involves a couple of steps:
Here's the Web Clipper template configuration I ended up with:

It was important to set the "Tags" property type to "Text" rather than the default "Multitext" so that Denote does the right thing with it when renaming the file later.
In the Web Clipper's advanced settings, I set the behavior to "Save file..." rather than "Add to Obsidian".

OK, so now after using the Web Clipper, I get a Markdown file[1]with a (mostly) Denote-compatible file name and front matter in my ~/Downloads folder. Here's what clipping this post looks like:

To get the file into my denote-directory, I use a rule in Hazel. Hazel watches my Downloads folder for any new file whose name contains the string "__clipping", and automatically moves it into a "clippings" folder in my Denote folder.
The only manual step remaining is to finish renaming the files using Denote. I don't yet know how to have the Web Clipper "slugify" the file name, so I have Denote do it. This can be done in batch using Dired, so it's not a huge burden.
If there's a simpler way to get a nicely-formatted Org mode file from a web page directly to my Denote folder, I'm all ears, but for now...
Take that, Obsidian! 😄
Denote handles Markdown files natively, so this is fine. ↩︎
I upgraded PHP to v8.3 (from 8.2) today on the server running baty.net. I don't pretend to be an Ubuntu sysadmin, so I'm writing it down, just in case.
sudo apt update
sudo apt install php8.3 php8.3-cli php8.3-{bz2,curl,mbstring,intl,gd,xml}
sudo apt install php8.3-fpm
sudo a2enconf php8.3-fpm # enable it
sudo vi /etc/caddy/Caddyfile # replace socket path with 8.3
sudo systemctl reload caddy
sudo sudo a2disconf php8.2-fpm # disable 8.2
sudo apt purge php8.2* # in fact, just delete 8.2
The site runs on Caddy, so I needed to change the path to the fpm socket. Here's the Kirby section of the Caddyfile...
(kirby) {
php_fastcgi unix//run/php/php8.3-fpm.sock
@blocked {
path *.txt *.md /content/* /site/* /kirby/* /.*
}
redir @blocked /
}
I've not been shooting much film recently. Yesterday, I was bored and in a mood, so I grabbed the Hasselblad and fired off a roll using Alice as my model. Only one frame was good enough to share. I really like it, so it was worth sacrificing the other 11.

For a while, I tried maintaining a combined RSS feed that included posts from all my sundry blogs. I kept it at /everything.rss. It was managed as part of my WordPress blog, and since I've stopped using WordPress, I've been redirecting /everything.rss to the feed for baty.net, which is either /feed or /index.xml, depending on my blog platform of the day.
I think that instead of that, I'll piggyback off my @batybot account on Mastodon.social. I crosspost most stuff from my sites to @batybot via EchoFeed already, and Mastodon offers an rss feed for each account. Mine is https://mastodon.social/@batybot.rss.
Soon, I'll redirect requests from baty.net/everything.rss to https://mastodon.social/@batybot.rss. This doesn't offer full posts, but rather it's more like a firehose of links to everything I post. If you'd like to avoid that kind of noise, delete your subscription to /everything.rss. If you are a glutton for punishment, feel free to subscribe.
I bought the Nikon Z f thinking it would replace my Leica SL2, since the Nikon is lighter, newer, cheaper, and faster. While it is all of those things, it's still not better.
Here's what I like about the Z f:
Here's what I don't love about it:
If I sell the camera now, I'll lose my ass. But if I don't learn to like it more, the money might be better spent elsewhere, even at a loss.
Also, I haven't sold the SL2, yet.
Scrolling with the Logitech MX Master in Safari sucks out of the box. The following from this Reddit thread helped, even though it required installing 2 additional packages.
Here’s the useful part of that thread:
Logi Options+ (Plus): Customization App for Logitech Devices
What a gross bit of software, but it’s necessary here. I’ve read that once this process is done, I can delete the app completely, but I haven’t tried that yet.
brew install mos
(Note that launching Mos required xattr -d com.apple.quarantine Mos.app)
Once I’d done this, scrolling in the MX Master has been like butter.