<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>Sal&#39;s</title>
  <subtitle>A typical blog about blog stuff.</subtitle>
  <link href="https://sals.place/feed/feed.xml" rel="self" />
  <link href="https://sals.place/" />
  <updated>2026-07-11T00:00:00Z</updated>
  <id>https://sals.place/</id>
  <author>
    <name>Sal</name>
  </author>
  <entry>
    <title>24 hours with Emacs (a hasty rant)</title>
    <link href="https://sals.place/blog/24-hours-with-emacs/" />
    <updated>2026-07-11T00:00:00Z</updated>
    <id>https://sals.place/blog/24-hours-with-emacs/</id>
    <content type="html">&lt;p&gt;The process I outlined at the top of my
&lt;a href=&quot;https://sals.place/blog/mixed-feels-on-modal-editing/&quot;&gt;recent post on modal editing&lt;/a&gt;
missed some steps, which are italicized below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pick Neovim back up after some number of months or years.&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Wonder: Should I use Emacs again??&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;[30% chance] Try Emacs again.&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;← just finished&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So yeah, I did that.
Per the cycle,
I was once again confidently moving away from modal editing, but I still wanted an editor I could run in the terminal.
Yadda yadda, and I spent the last day feverishly re-learning Emacs.&lt;/p&gt;
&lt;p&gt;But minutes ago I decided to bail on it, and here are some hasty thoughts in support of that decision.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: I’m about to complain a bunch about Emacs because I’m in a pissy mood about it.
So I’ll say at the outset that I think Emacs is highly cool.
Amazing, really.
That’s why I come back to it every so often.
If you’re an Emacs person, I respect and admire and am sorta jealous of you.
That said…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;My purpose for trying Emacs was to have a terminal-based editor that’s vaguely similar to a native macOS text editor.
As you might know, macOS has many baked-in Emacs movements.
See
&lt;a href=&quot;https://jblevins.org/log/kbd&quot;&gt;this post from Jason Blevins&lt;/a&gt;
for some good details on this.&lt;/p&gt;
&lt;p&gt;But the similarities between Emacs and macOS are vanishingly thin.
Emacs goes so much deeper so quickly that, within an hour or so of getting reacquainted with it, the thought of using it “because it feels like macOS” was laughable.&lt;/p&gt;
&lt;p&gt;Instead, I found myself studying all sorts of commands, patterns, philosophies, etc. that aren’t relevant to any other editor.
(Kudos to &lt;em&gt;Mastering Emacs&lt;/em&gt; for being a wonderful guide, btw.)
Plus, while you certainly &lt;em&gt;can&lt;/em&gt; run Emacs in the terminal and many do, most Emacsers seem to recommend running the GUI app for the best and fullest experience, so I started doing that.&lt;/p&gt;
&lt;p&gt;And thus, my two justifications for trying Emacs — similarity to macOS and having a go-to terminal editor — went up in smoke out of the gate.&lt;/p&gt;
&lt;p&gt;What other justifications are there?
Fun.
I think that’s the only indisputable one.
There’s an upvoted Reddit comment somewhere that says that, but I can’t find it now.&lt;/p&gt;
&lt;p&gt;And it is fun.
&lt;em&gt;Too much&lt;/em&gt; fun.
That’s the problem.
I got up at 5:30 this morning to study and practice Emacs.
Two hours later, when my family woke up, I was annoyed that I had to stop.
I remember this feeling from the last time I dove into Emacs years ago.
It becomes all I can think about, creating a sort of persistent and irritable obsession.
My wife notices and reminds me to be present with the kids.
This is not good for me.&lt;/p&gt;
&lt;p&gt;Emacs not more efficient or faster.
No way.
Not when VSCode or Zed or just about any “popular” editor pops up and lets you get right to work with minimal fuss.&lt;/p&gt;
&lt;p&gt;I’ve said it before, but it’s relevant: I’m pretty damn fast at macOS text editing.
I’ve been at it for over 20 years.
This is why Vim feels kludgy, too, even once I’m starting to get fast at it.&lt;/p&gt;
&lt;p&gt;Poke around Reddit and you’ll see comments like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You learn Emacs once, and you have an editor for life. Who knows how long — what’s it called? Zed? — will be around? Emacs stands the test of time. Jesus was catching up on his RSS feeds in Emacs before the last supper, you know.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sure fine okay.
But once you’re fast at the macOS defaults, you’re pretty much fast at any editor that comes along.
Zed gets blocked at work, and I switch to VSCode.
I want a zero-distraction editor, and I hop into iA Writer.
I need to write an email, and I do it in Gmail or Fastmail.
They’re all different but feel similar enough to me.
The basic cursor movements and edits work the same across all of them.
There’s no huge learning curve or switching cost.
Certainly nothing like the cost of learning Emacs.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;
And that’s awesome.
That’s freedom.&lt;/p&gt;
&lt;p&gt;I know where Emacs and Vim mastery lead:
wanting to do damn-near everything in your editor (or whatever you call Emacs 😉),
and feeling some level of discomfort when you can’t.
This is especially true for Emacs because it can actually &lt;em&gt;do&lt;/em&gt; damn-near everything.
That’s not a place I want to go, honestly.
I see that as a constraint rather than a feature.
See above re: freedom.&lt;/p&gt;
&lt;p&gt;And good &lt;em&gt;lord&lt;/em&gt;, the mental overhead of trying to learn Emacs at a deep level.
It’s immense.
“Just use the basics,” some say.
But I can’t, not knowing there’s this ocean of nerdy tools and tricks just underneath.
It’s like telling a hungry dog to ignore a t-bone steak.&lt;/p&gt;
&lt;p&gt;Hot take: For my basic-bitch needs, VSCode’s git UI is just as good if not better than Magit.&lt;/p&gt;
&lt;p&gt;Hot take: TRAMP sucks.
Again, for my needs, and specifically compared to VSCode or Zed’s remote SSH features.
I’m sure it was innovative and amazing at one point.
But from my perch, TRAMP is slow, doesn’t remember remote projects, causes LSPs and such struggle, and doesn’t handle port forwarding.
Sure, you can optimize it and customize it endlessly.
It’s Emacs, after all!
But out of the box, it doesn’t hold a candle.&lt;/p&gt;
&lt;p&gt;Going back to terminals, I’m plenty happy with the integrated terminals in VSCode and Zed.
They feel mostly like a “real” terminal pane in my emulator of choice.
I like that I can toggle them so easily with &lt;code&gt;Ctrl-&amp;lt;backtick&amp;gt;&lt;/code&gt;.
Emacs’s terminal options work, but they’re kinda wonky by comparison.
That’s probably why there are several different options — shell, eshell, vterm, etc.
I’m sure I’d get used to one of them, but again, it feels like a regression.&lt;/p&gt;
&lt;p&gt;(For a good time, run a TUI like &lt;code&gt;lazygit&lt;/code&gt; inside &lt;code&gt;M-x shell&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;The Emacs chords for navigating buffers, tabs, etc. are slower and clunkier than the equivalents across macOS.
In my opinion.
Again, I’m biased because the macOS stuff is in my bones at this point.&lt;/p&gt;
&lt;p&gt;Emacs server and &lt;code&gt;emacsclient&lt;/code&gt; are cool but confusing.
I installed Emacs via the generally recommended
&lt;a href=&quot;https://github.com/d12frosted/homebrew-emacs-plus&quot;&gt;homebrew-emacs-plus&lt;/a&gt; package.
Sometimes Emacs.app would just … do nothing when I opened it.
I assume a preexisting server process was stuck somewhere.
And when I’d launch &lt;code&gt;emacsclient&lt;/code&gt;, it would load its own frame (aka “window”) even when I instructed it not to, and that frame didn’t seem to have any of my &lt;code&gt;init.el&lt;/code&gt; settings loaded.&lt;/p&gt;
&lt;p&gt;This is about the point where I said, fuck this, it’s not worth it.&lt;/p&gt;
&lt;p&gt;When I end up torn about a decision like this, and I &lt;em&gt;often&lt;/em&gt; do, I try ask myself these two similar questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What’s the simpler choice?&lt;/li&gt;
&lt;li&gt;What’s path of less resistance?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It became hotly evident that Emacs is not the simpler or less-resistant path in the TextEditorverse.
Meanwhile, from this angle, VSCode&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; is &lt;em&gt;really&lt;/em&gt; hard to argue with.&lt;/p&gt;
&lt;p&gt;Case in point: I just banged out this blog post very quickly with what felt like zero friction in VSCode.
I wrote it at the “speed of thought,” as the Vim folk like to say in praise of their editor.
I’ll now do the git stuff in a matter of seconds.
If this is already so comfortable and fast for me, what else am I trying to find?&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I’ll close by repeating: Emacs is amazing.
In many ways I love it what it stands for.
But it’s not healthy for me, not at this stage of life anyway, so I have to get mad at it in order to distance myself.&lt;/p&gt;
&lt;p&gt;If you’re curious about Emacs or Neovim and trying make heads or tails of my ramblings, here’s my advice:&lt;/p&gt;
&lt;p&gt;If you’re just wondering if one or the other will make you more productive in a general sense, back away slowly, then turn and run and don’t stop running until you’ve reached the Land of Whatever You Already Know.
These editors are epic time-sucks, and you’ll be much more holistically productive just using what you’re already familiar with.&lt;/p&gt;
&lt;p&gt;If, on the other hand, you have a specific, unfulfilled need, like editing code on remote servers, or you just want to learn and experiment and have a geeky adventure, by all means, &lt;em&gt;go for it&lt;/em&gt;.
They’re both extremely cool and so much fun when they’re not messing up your life.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Maybe that’s why so many Emacs folk are hung up on never having to learn another editor; they still have PTSD from learning Emacs. 😆 &lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I’d have said Zed instead, but it’s quasi-blocked at work, so it’s now a path of significant resistance. &lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Oh right, &lt;code&gt;M-x enlightenment-mode&lt;/code&gt;. &lt;a href=&quot;https://sals.place/blog/24-hours-with-emacs/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Making VSCode play better with Markdown over SSH</title>
    <link href="https://sals.place/blog/vscode-and-markdown-over-ssh/" />
    <updated>2026-07-09T00:00:00Z</updated>
    <id>https://sals.place/blog/vscode-and-markdown-over-ssh/</id>
    <content type="html">&lt;p&gt;For reasons I hope to get around to writing about soon,
I’m once again toying with VSCode to edit my notes and blog over SSH over a proxy host.
🕺
In fact, I’m writing &lt;em&gt;this very blog post&lt;/em&gt; that way.
Isn’t that amazing??&lt;/p&gt;
&lt;p&gt;See
&lt;a href=&quot;https://sals.place/blog/my-remote-blogging-workflow/&quot;&gt;this post&lt;/a&gt;
for some earlier thoughts on this sort of thing.&lt;/p&gt;
&lt;p&gt;Anyway, I noticed that VSCode felt quite sluggish compared to Zed with the same SSH target.
I noticed it especially with backspaces.
I’d hit backspace several times and then type a letter, and some of the backspaces were so slow to process that they’d end up deleting the letter I typed after them.
As you might imagine, that was annoying.&lt;/p&gt;
&lt;p&gt;Zed, meanwhile, was perfectly snappy.&lt;/p&gt;
&lt;p&gt;I tried various things to improve this, including tweaking &lt;code&gt;~/.ssh/config&lt;/code&gt;, but the fix ended up being a simple tweak to the very popular
&lt;a href=&quot;https://github.com/yzhang-gh/vscode-markdown&quot;&gt;Markdown All in One&lt;/a&gt;
extension,
which I’ve had installed since the dawn of time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yzhang-gh/vscode-markdown/issues/1372&quot;&gt;This GitHub issue&lt;/a&gt; was a direct hit.
I disabled these two keyboard shortcuts:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;markdown.extension.onBackspace
markdown.extension.onEnterKey&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now things seem fine!&lt;/p&gt;
&lt;p&gt;Note that those are names of keyboard-shortcut settings, not the app-wide JSON settings.
(I hadn’t yet noticed trouble with the Enter key, but figured I’d get ahead of it.)&lt;/p&gt;
&lt;p&gt;Looking at
&lt;a href=&quot;https://github.com/yzhang-gh/vscode-markdown/issues/423&quot;&gt;the issue referenced from the one above&lt;/a&gt;,
I believe disabling these shortcuts will break the extension’s list-continuation feature.
That’s fine for this blog.
I don’t make many lists here.&lt;/p&gt;
&lt;p&gt;Now that VSCode is working fine, I have &lt;code&gt;n+1&lt;/code&gt; options to choose from in my text-editing indecision.
Yippee. 😆&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Mixed feels on modal editing</title>
    <link href="https://sals.place/blog/mixed-feels-on-modal-editing/" />
    <updated>2026-07-09T00:00:00Z</updated>
    <id>https://sals.place/blog/mixed-feels-on-modal-editing/</id>
    <content type="html">&lt;p&gt;I’m at a familiar stage of my cycle with Neovim.
It goes something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick Neovim back up after some number of months or years.&lt;/li&gt;
&lt;li&gt;It’s so fun! Feel the power, tweak the configs, install the packages, learn some more tricks. It’s nearly all I can think about.&lt;/li&gt;
&lt;li&gt;Some weeks later, the honeymoon period begins to fade, and it starts to feel normal instead of new and shiny. Inevitably, something in my customized config isn’t working right, and debugging it feels more like a chore now than an exciting side quest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;[current status]&lt;/em&gt; Start to wonder, is modal editing &lt;em&gt;really&lt;/em&gt; any better for my use cases?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Switch back to non-modal editing, embracing the Emacs movements baked into macOS. Feel content.&lt;/li&gt;
&lt;li&gt;Eventually, get itchy feet and return to (1).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I write a lot, but not a lot of code.
Most of my writing is note-taking, chat messages, emails, documentation, blog posts, etc.&lt;/p&gt;
&lt;p&gt;I know it’s annoying to say “here’s what an LLM said” in blog posts, but … here’s what Claude said.
I found it useful because it’s &lt;em&gt;really&lt;/em&gt; flippin’ hard to find an unbiased opinion on modal vs. non-modal editing from humans on the internet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mode-switching overhead is genuinely worse for bursty, prose-heavy, or exploratory editing (writing docs, README updates, chat messages) than for surgical code edits. If a lot of your work is prose or config files rather than repetitive code transforms, the ROI on modal editing is just lower — that’s not a skill issue.
&lt;br&gt;&lt;br&gt;
Insert mode in Vim is worse than a normal text editor at insert-mode things (multi-cursor, smart autocomplete UX) unless you layer on plugins that basically reimplement what other editors give you for free.
&lt;br&gt;&lt;br&gt;
If you’re constantly context-switching between Neovim and a non-modal editor (which you are, with code-server as primary), you pay a tax every switch — your fingers do the wrong thing in the wrong app for the first few edits.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There’s no objectively correct answer to the question of modal vs. non-modal editing, but those thoughts from Claude vibe with what I’m feeling.
Constantly hopping between modes starts to feel clunky after a while, like I’m tripping over shoelaces.
And when I drop modal editing and just go with the macOS default movements I know so well, it feels like I’m being let off a leash.
If I were spending all day in code, I’d probably feel differently.&lt;/p&gt;
&lt;p&gt;Admittedly, a factor here is that I use &lt;code&gt;Ctrl-[&lt;/code&gt; to escape from insert mode.
I do that because I already have Caps mapped to Control.
But those extra keystrokes add up.
I should try the Karabiner-powered option of having Caps be Escape when it’s tapped and Control when it’s held.
Or maybe mapping right-Command to Escape.
Or something more efficient.
But that’s more third-party software to install and manage.&lt;/p&gt;
&lt;p&gt;Which gets me back to: is it really worth it?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>VSCode Remote-SSH setup hangs when using fish shell</title>
    <link href="https://sals.place/blog/vscode-ssh-and-fish/" />
    <updated>2026-07-08T00:00:00Z</updated>
    <id>https://sals.place/blog/vscode-ssh-and-fish/</id>
    <content type="html">&lt;p&gt;I
&lt;a href=&quot;https://sals.place/blog/reworking-my-remote-notes-and-blogging-rig/&quot;&gt;previously mentioned&lt;/a&gt;
that
&lt;a href=&quot;https://code.visualstudio.com/docs/remote/ssh&quot;&gt;VSCode’s Remote-SSH feature&lt;/a&gt;
wasn’t working with my jump-host-proxied server.
Well, I think I figured out why.
It wasn’t the jump host that was the problem.
Rather, VSCode’s SSH plumbing doesn’t play well with my beloved fish shell.
See &lt;a href=&quot;https://github.com/microsoft/vscode-remote-release/issues/2509&quot;&gt;this thread&lt;/a&gt;, for example.&lt;/p&gt;
&lt;p&gt;I’ve spent a fairly comical amount of time coming up with alternative workflows, including some big ones I haven’t had the chance to write about yet, when all I needed to do to fix this was add the following to my &lt;code&gt;~/.profile&lt;/code&gt; and &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; $- &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
  *i*&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; fish &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;esac&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’ll switch to fish in an interactive shell and otherwise stick with bash, letting VSCode do its thing.&lt;/p&gt;
&lt;p&gt;Luckily I had fun and learned stuff while building out the alternatives.
Otherwise I’d be slightly horrifed atm.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Hello from my Supernote Manta</title>
    <link href="https://sals.place/blog/hello-from-manta/" />
    <updated>2026-07-08T00:00:00Z</updated>
    <id>https://sals.place/blog/hello-from-manta/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://sals.place/blog/hello-from-manta/WppoEn_VQP-1600.webp&quot; alt=&quot;picture of handwritten blog post&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1600&quot; height=&quot;2133&quot;&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>I think my little VPS is OOMing</title>
    <link href="https://sals.place/blog/vps-ooming/" />
    <updated>2026-07-07T00:00:00Z</updated>
    <id>https://sals.place/blog/vps-ooming/</id>
    <content type="html">&lt;p&gt;My
&lt;a href=&quot;https://sals.place/blog/reworking-my-remote-notes-and-blogging-rig/&quot;&gt;IONOS VPS&lt;/a&gt;
has been suddenly going unresponsive lately.
I told Gemini I have this VPS for editing files in Neovim, and it suggested I might be running out of memory, especially if I’m running a TypeScript LSP, which is known for hogging lots of resources.
It seems to have happened at least a couple times when I’m editing a .ts file, so that’s a good theory.&lt;/p&gt;
&lt;h2 id=&quot;add-a-swap-file&quot;&gt;Add a swap file&lt;/h2&gt;
&lt;p&gt;The first thing I did was add a swap file so memory can spill over onto disk if necessary.
Gemini said to do this with &lt;code&gt;fallocate&lt;/code&gt; or &lt;code&gt;dd&lt;/code&gt; depending on your filesystem, but after looking into it, I decided to just use &lt;code&gt;dd&lt;/code&gt; to be safe.
See &lt;a href=&quot;https://askubuntu.com/questions/1017309/fallocate-vs-dd-for-swapfile&quot;&gt;this Ask Ubuntu thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what I did:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# create the swap file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dd&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/dev/zero &lt;span class=&quot;token assign-left variable&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/var/cache/swapfile &lt;span class=&quot;token assign-left variable&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;1K &lt;span class=&quot;token assign-left variable&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;4M
&lt;span class=&quot;token comment&quot;&gt;# only root should be allowed to read/write the file, since it can hold sensitive data&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt; /var/cache/swapfile
&lt;span class=&quot;token comment&quot;&gt;# format the file as swap&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkswap&lt;/span&gt; /var/cache/swapfile
&lt;span class=&quot;token comment&quot;&gt;# activate the swap file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;swapon&lt;/span&gt; /var/cache/swapfile
&lt;span class=&quot;token comment&quot;&gt;# check the swap status&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;swapon&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--show&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;free&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-h&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apparently the swap file won’t automatically get re-mounted after system restart, so to fix that, I edited &lt;code&gt;/etc/fstab&lt;/code&gt; and added this line to the bottom:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;/var/cache/swapfile none swap sw 0 0&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/swapfile&lt;/code&gt; is the swap file path.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt; is the mount point (swap doesn’t get a standard folder mount point).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;swap&lt;/code&gt; is the filesystem type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sw&lt;/code&gt; is the mount options, telling the system this is for swap.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 0&lt;/code&gt; are the Dump and Pass options, used for filesystem backups and checks; swap doesn’t need them, so they are set to zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you see any issues with any of the above, please email and educate me!&lt;/p&gt;
&lt;p&gt;For some smarter people covering these topics, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/1017309/fallocate-vs-dd-for-swapfile&quot;&gt;swap - fallocate vs dd for swapfile? - Ask Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/114566/what-is-swap-and-how-large-a-swap-partition-should-i-create&quot;&gt;What is swap and how large a swap partition should I create? - Ask Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.redhat.com/en/blog/etc-fstab&quot;&gt;An introduction to the Linux /etc/fstab file - Red Hat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/126018/adding-a-new-swap-file-how-to-edit-fstab-to-enable-swap-after-reboot&quot;&gt;Adding a new swap file. How to edit fstab to enable swap after reboot? - Ask Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I believe this will fix the OOM problem, but it’s got me thinking about alternatives to my current rig.
More to come on that soon.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Two-sentence post: Mosh is cool</title>
    <link href="https://sals.place/blog/tsp-mosh-is-cool/" />
    <updated>2026-07-03T00:00:00Z</updated>
    <id>https://sals.place/blog/tsp-mosh-is-cool/</id>
    <content type="html">&lt;p&gt;My dumb-guy summary of
&lt;a href=&quot;https://mosh.org/&quot;&gt;Mosh&lt;/a&gt;
is that it’s like SSH but feels &lt;em&gt;almost&lt;/em&gt; like you’re typing locally and keeps your connection alive through internet blips, computers naps, etc.
It’s fitting pretty nicely into my
&lt;a href=&quot;https://sals.place/blog/reworking-my-remote-notes-and-blogging-rig/&quot;&gt;remote-dev&lt;/a&gt;
rig.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Apparently I&#39;ve used Todoist before</title>
    <link href="https://sals.place/blog/apparently-i-ve-used-todoist-before/" />
    <updated>2026-07-01T00:00:00Z</updated>
    <id>https://sals.place/blog/apparently-i-ve-used-todoist-before/</id>
    <content type="html">&lt;p&gt;As
&lt;a href=&quot;https://sals.place/blog/two-sentence-post-todoist-on-the-brain/&quot;&gt;I mentioned&lt;/a&gt;,
I signed back into Todoist this week to kick the tires.
The next day, I got this email:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://sals.place/blog/apparently-i-ve-used-todoist-before/23p8c1gl83-585.avif 585w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://sals.place/blog/apparently-i-ve-used-todoist-before/23p8c1gl83-585.webp 585w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://sals.place/blog/apparently-i-ve-used-todoist-before/23p8c1gl83-585.png&quot; alt=&quot;screenshot of Todoist email&quot; width=&quot;585&quot; height=&quot;800&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I knew I’d dabbled on and off over the years, but &lt;em&gt;19 years ago&lt;/em&gt;?
Is that even possible??&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Todoist&quot;&gt;Wikipedia says&lt;/a&gt;
Todoist was created in 2007 as a hobby project.
I guess I was an early adopter.
And that I’m old.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Fish shell</title>
    <link href="https://sals.place/blog/fish-shell/" />
    <updated>2026-06-30T00:00:00Z</updated>
    <id>https://sals.place/blog/fish-shell/</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://fishshell.com/&quot;&gt;Fish shell&lt;/a&gt;,
how do I love thee?
Let me count the ways.&lt;/p&gt;
&lt;p&gt;You give me the glory of
&lt;a href=&quot;https://fishshell.com/docs/current/interactive.html#autosuggestions&quot;&gt;autosuggestions&lt;/a&gt;
with nary a line of config.&lt;/p&gt;
&lt;p&gt;Your default prompt
&lt;a href=&quot;https://jvns.ca/blog/2024/09/12/reasons-i--still--love-fish/#5-nice-default-prompt-including-git-integration&quot;&gt;has all my favorite things&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Your
&lt;a href=&quot;https://fishshell.com/docs/current/cmds/history.html&quot;&gt;command history&lt;/a&gt;
stretches to the beginning of time.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/fish-shell/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;
I considered &lt;a href=&quot;https://atuin.sh/&quot;&gt;Atuin&lt;/a&gt;, but why bother when I have you?&lt;/p&gt;
&lt;p&gt;Your
&lt;a href=&quot;https://fishshell.com/docs/current/interactive.html#tab-completion&quot;&gt;tab completion&lt;/a&gt;
is my tireless assistant, searching and suggesting without distracting.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/fish-shell/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Your
&lt;a href=&quot;https://fishshell.com/docs/current/language.html&quot;&gt;scripting language&lt;/a&gt;
is a pleasant escape from murk of bash.
POSIX be damned!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://sals.place/blog/fish-shell/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Your
&lt;a href=&quot;https://fishshell.com/docs/current/tutorial.html#syntax-highlighting&quot;&gt;builtin syntax highlighting&lt;/a&gt;
forestalls my otherwise steady stream of user errors.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fishshell.com/docs/current/cmds/abbr.html&quot;&gt;Abbreviations&lt;/a&gt;,
your improvement on bash aliases, expand when you type them, letting me know exactly what I’m running &lt;em&gt;and&lt;/em&gt; popping the command into your history for posterity.
And you have
&lt;a href=&quot;https://fishshell.com/docs/current/cmds/alias.html&quot;&gt;more bash-like aliases&lt;/a&gt;
for those that want them.&lt;/p&gt;
&lt;p&gt;You are
&lt;a href=&quot;https://sals.place/blog/reworking-my-remote-notes-and-blogging-rig/&quot;&gt;the first thing I install&lt;/a&gt;
on a new machine.
Let’s grow old together.&lt;/p&gt;
&lt;p&gt;For more praise from smarter peeps, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Julia’s post, &lt;a href=&quot;https://jvns.ca/blog/2017/04/23/the-fish-shell-is-awesome/&quot;&gt;The fish shell is awesome&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Her followup, &lt;a href=&quot;https://jvns.ca/blog/2024/09/12/reasons-i--still--love-fish/&quot;&gt;Reasons I still love the fish shell&lt;/a&gt;.
&lt;ul&gt;
&lt;li&gt;See also the &lt;a href=&quot;https://news.ycombinator.com/item?id=41525447&quot;&gt;HN discussion&lt;/a&gt;. Top comment: “I adore fish and can’t imagine ever switching back to zsh or bash.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Paul’s post, &lt;a href=&quot;https://rmpr.xyz/the-fish-shell-is-amazing/&quot;&gt;The fish shell is amazing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The HN thread, &lt;a href=&quot;https://news.ycombinator.com/item?id=27992073&quot;&gt;Command line tools for productive programmers&lt;/a&gt;. Top comment: “The single best improvement to my command-line workflow I’ve done in the last few years has been switching shells [to fish].”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(Email me more good stuff to add here and I’ll do it!)&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;As long as time contains 256,000 or fewer commands. &lt;a href=&quot;https://sals.place/blog/fish-shell/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;I just learned while writing this post that you can hit &lt;code&gt;Ctrl-s&lt;/code&gt; to search the completion list! &lt;code&gt;&amp;lt;3&lt;/code&gt; &lt;a href=&quot;https://sals.place/blog/fish-shell/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;jk but also kind of. Though I think fish has gotten a &lt;em&gt;little&lt;/em&gt; more POSIX-friendly over the years. &lt;a href=&quot;https://sals.place/blog/fish-shell/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Two-sentence post: Todoist on the brain</title>
    <link href="https://sals.place/blog/two-sentence-post-todoist-on-the-brain/" />
    <updated>2026-06-29T00:00:00Z</updated>
    <id>https://sals.place/blog/two-sentence-post-todoist-on-the-brain/</id>
    <content type="html">&lt;p&gt;It’s helpful when apps have a web version so I can access them from my work laptop, which I keep mostly clean from personal apps and data.
And thusly, despite being a long-time Things fan, I find myself back on app.todoist.com this morning.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Making a custom Neovim filetype picker with mini.pick</title>
    <link href="https://sals.place/blog/custom-neovim-filetype-picker/" />
    <updated>2026-06-29T00:00:00Z</updated>
    <id>https://sals.place/blog/custom-neovim-filetype-picker/</id>
    <content type="html">&lt;p&gt;It took me a minute to find a good example, so I’ll post this in case it helps anyone else.&lt;/p&gt;
&lt;p&gt;For you Vimmers out there,
here’s an example of how to make your own custom picker using Neovim and
&lt;a href=&quot;https://github.com/nvim-mini/mini.pick&quot;&gt;mini.pick&lt;/a&gt;, part of the very popular
&lt;a href=&quot;https://github.com/nvim-mini/mini.nvim&quot;&gt;mini.nvim plugin&lt;/a&gt;.
In this case, I’m adding a picker to set the active buffer’s filetype.&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pick_filetype&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- Grab the current buffer before firing the picker&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- so we can update it later when we&#39;re inside&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- the picker&#39;s buffer.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; current_buf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nvim_get_current_buf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- Get the choices for the picker. In this case,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- all available filetypes.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; filetypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getcompletion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;filetype&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;-- Launch the picker with our custom choices and&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- handler.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mini.pick&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Filetypes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; filetypes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      choose &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;-- Handle the user&#39;s choice.&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bo&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;current_buf&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filetype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; item
        &lt;span class=&quot;token comment&quot;&gt;-- (Optional) Pop a notification to help us&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;-- know it&#39;s working.&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Set filetype to &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Add a keymap to run it&lt;/span&gt;
vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keymap&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;leader&gt;bt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pick_filetype&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; desc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Pick filetype&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I created this file with my code: &lt;code&gt;~/.config/nvim/lua/custom/filetype_picker.lua&lt;/code&gt;.
And then I wrapped the code in a module.
Unnecessary but possibly more idiomatic, and leaves room to add configuration parameters later.
(If this is bad form, please email and educate me!)&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; M &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; M&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;-- (insert all the code from the block above)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; M&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then from &lt;code&gt;init.lua&lt;/code&gt; or the like, invoke it like so:&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;custom.filetype_picker&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not too shabby.
Cheers to
&lt;a href=&quot;https://github.com/echasnovski&quot;&gt;echasnovski&lt;/a&gt;
and all the other contributors for making such a great library!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Added a light/dark theme toggle</title>
    <link href="https://sals.place/blog/added-a-light-dark-theme-toggle/" />
    <updated>2026-06-28T00:00:00Z</updated>
    <id>https://sals.place/blog/added-a-light-dark-theme-toggle/</id>
    <content type="html">&lt;p&gt;My
&lt;a href=&quot;https://sals.place/blog/high-contrast-vs-muted-color-themes/&quot;&gt;last post on color themes&lt;/a&gt;
and subsequent dark-mode refresh made me want to add an easier way to toggle light and dark mode on this site.&lt;/p&gt;
&lt;p&gt;In my new nav section, you’ll now see a theme switcher in the middle:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://sals.place/blog/added-a-light-dark-theme-toggle/YHX7lqUBUw-930.avif 930w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://sals.place/blog/added-a-light-dark-theme-toggle/YHX7lqUBUw-930.webp 930w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://sals.place/blog/added-a-light-dark-theme-toggle/YHX7lqUBUw-930.png&quot; alt=&quot;nav with theme toggle&quot; width=&quot;930&quot; height=&quot;220&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;css-vs-javascript&quot;&gt;CSS vs. JavaScript?&lt;/h2&gt;
&lt;p&gt;I was originally hoping to implement this entirely with CSS, since I know a number of folks out there keep JavaScript disabled when they peruse.
I got that working, but the downside is that it doesn’t stick.
As soon as a new page loads — for example, when you click any link on my site — the theme reverts to whatever’s dictated by your system.&lt;/p&gt;
&lt;p&gt;And so it came to pass that this site grew its first few lines of JavaScript.&lt;/p&gt;
&lt;h2 id=&quot;revealing-the-switcher&quot;&gt;Revealing the switcher&lt;/h2&gt;
&lt;p&gt;I don’t want to show the toggle if it won’t work, so I default to hiding it and then revealing it via JS.
This way, if the user doesn’t have JS enabled, they won’t see the toggle.&lt;/p&gt;
&lt;p&gt;My first implementation of this resulted in a brief flicker on page load as the JS unhid the button.
That really bugged me, so I asked Gemini to help me come up with a better approach.
Now, I put a &lt;code&gt;.no-js&lt;/code&gt; tag on the entire html body:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no-js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And I hide everything related to the theme toggler in CSS:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.no-js .theme-toggle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section, I replace &lt;code&gt;.no-js&lt;/code&gt; with &lt;code&gt;.js&lt;/code&gt; via one line of JavaScript:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voila! If JS is enabled, the theme switcher is enabled before the browser starts painting anything in &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;.
No more flicker.
At least, that’s how Gemini explained it to me.
If I’m misunderstanding, or if you know of a better way to do this, &lt;em&gt;please&lt;/em&gt; email me.
I’d love to learn from you.&lt;/p&gt;
&lt;h2 id=&quot;toggling-the-theme&quot;&gt;Toggling the theme&lt;/h2&gt;
&lt;p&gt;The JavaScript toggle logic does two things:
(1) sets a data attribute on the document that CSS can key off of, and
(2) saves the setting in localStorage to persist it across page loads and visits.&lt;/p&gt;
&lt;p&gt;Here’s the entire script at this moment:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; toggleAttr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data-theme&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; storageKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;theme&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; light &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dark &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;applyTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;toggleAttr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;storageKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Edit: These two lines might create a bug (defaulting to light mode&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// even if the OS is in dark mode). Need to revisit.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; savedTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;storageKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; light&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;applyTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;savedTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupListeners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; toggleBtn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;theme-toggle&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  toggleBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;toggleAttr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentTheme &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; dark &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; light &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;applyTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; osModeQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matchMedia &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;(prefers-color-scheme: dark)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;osModeQuery&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    osModeQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;change&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; dark &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; light&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;applyTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;setupListeners&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;setTimeout&lt;/code&gt; at the end is because JS can’t see the toggle element when the script first runs.
I assume that’s because the page hasn’t been rendered yet?
I need to look into that.&lt;/p&gt;
&lt;p&gt;Meanwhile, in the Sass (I switched to Sass from vanilla CSS during this project because mixins felt like a cleaner way to handle theming):&lt;/p&gt;
&lt;pre class=&quot;language-scss&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;@mixin&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;light-mode &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// define light-mode variables&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--bg-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-bg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--bg-color-alt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--light-bg-alt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;@mixin&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;dark-mode &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// define dark-mode variables&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; gruvbox-dark&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--bg-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--dark-bg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;:root &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// default to light mode&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; light-mode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// handle OS-level setting&lt;/span&gt;
&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;:root &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; dark-mode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// handle theme-toggle set to dark mode&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;:root[data-theme=&quot;dark&quot;] &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; dark-mode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// handle theme-toggle set to light mode&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;:root[data-theme=&quot;light&quot;] &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@include&lt;/span&gt; light-mode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Use the variables defined in light-mode and dark-mode throughout.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// For example:&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;html,
body &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--text-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--bg-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyway, I’ve got more testing and cleanup to do, but I think it’s working.
If you spot a problem, please let me know!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>High contrast vs. muted color themes</title>
    <link href="https://sals.place/blog/high-contrast-vs-muted-color-themes/" />
    <updated>2026-06-26T00:00:00Z</updated>
    <id>https://sals.place/blog/high-contrast-vs-muted-color-themes/</id>
    <content type="html">&lt;p&gt;Are high-contrast themes like
&lt;a href=&quot;https://protesilaos.com/emacs/modus-themes&quot;&gt;Modus&lt;/a&gt;
“better” than the more muted vibes like
&lt;a href=&quot;https://catppuccin.com/&quot;&gt;Catppuccin&lt;/a&gt;,
&lt;a href=&quot;https://github.com/folke/tokyonight.nvim&quot;&gt;Tokyo Night&lt;/a&gt;,
or my personal favorite,
&lt;a href=&quot;https://github.com/morhetz/gruvbox&quot;&gt;Gruvbox&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;I assumed there was a correct answer to this question, but of course reality is more complicated.
The benefits of high-contrast themes are perhaps obvious.
You can more easily see the letter shapes, especially when there’s a lot of ambient light or glare.
That’s when I tend to reach for something like Modus.&lt;/p&gt;
&lt;p&gt;On the other hand, high-contrast themes can be fatiguing because you’re staring at brighter lights on the screen.
They can also cause &lt;em&gt;halation&lt;/em&gt;, where bright text looks like it’s glowing or bleeding into the surrounding background.
Apparently this is even more common for people with astigmatism.&lt;/p&gt;
&lt;p&gt;Muted themes address those issues by reducing glare and brightness-driven fatigue.
But if the contrast drops too low, or there’s too much ambient light bouncing off the screen, that’s a different sort of strain.&lt;/p&gt;
&lt;p&gt;I suppose the question comes down to what I’ll call the “net contrast” of the theme once you factor in your environment’s lighting.
(Gemini referred to this as &lt;em&gt;contrast disparity&lt;/em&gt;, but I’m not sure that’s correct.)&lt;/p&gt;
&lt;h2 id=&quot;my-takeaways&quot;&gt;My takeaways&lt;/h2&gt;
&lt;p&gt;Given the variables, it seems smart to be able to switch themes depending on the conditions.
In my case, I want to mostly live in a more muted theme like Gruvbox and then switch to a higher-contrast theme when it’s bright around me.&lt;/p&gt;
&lt;h3 id=&quot;gui-text-editors&quot;&gt;GUI text editors&lt;/h3&gt;
&lt;p&gt;Switching themes is trivial in editors like VSCode and Zed.
They have shortcuts like &lt;code&gt;Cmd-K Cmd-T&lt;/code&gt; to pop up a quick switcher, and the change is saved in your config.
Boom.
Done.&lt;/p&gt;
&lt;h3 id=&quot;neovim&quot;&gt;Neovim&lt;/h3&gt;
&lt;p&gt;You can quick-switch themes in Neovim with the command:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;:colorscheme $name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can tab-complete the names. Pretty good.&lt;/p&gt;
&lt;p&gt;Neovim will also figure out whether your system is light or dark mode and follow suit.
It doesn’t actually ask your OS, but rather your terminal emulator via an ANSI escape sequence (&lt;code&gt;OSC 11&lt;/code&gt;, apparently).
Modern terminals like Ghostty, which I use, know how to respond to Neovim’s “question” with an RGB value, which Neovim then uses to determine whether it should set the background to light or dark.
And since Ghostty restyles itself because on the OS setting, I can use my macOS-level light/dark mode to control the OS, my terminal, &lt;em&gt;and&lt;/em&gt; Neovim.
Pretty cool!&lt;/p&gt;
&lt;p&gt;You can also force light or dark palettes in Neovim with &lt;code&gt;:set bg=light&lt;/code&gt; and &lt;code&gt;:set bg=dark&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You do need to install the themes first.
In a simple case, this can look like:&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;-- ...&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/folke/tokyonight.nvim&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cmd&lt;span class=&quot;token string&quot;&gt;[[colorscheme tokyonight]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re getting wonky coloring, Neovim might not be detecting 24-bit color properly.
You could try enabling it explicitly in your config to see if it helps:&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;termguicolors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See &lt;code&gt;:h &#39;termguicolors&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ghostty-terminal-emulator&quot;&gt;Ghostty (terminal emulator)&lt;/h3&gt;
&lt;p&gt;Ghostty will on-the-fly switch between light and dark mode as long as you have them configured properly.
My config currently looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token key attr-name&quot;&gt;theme&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;dark:Gruvbox Material,light:Gruvbox Material Light&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ghostty provides this handy command which pops up a searchable picker of its pantload of themes for perusal:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;ghostty +list-themes&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See the &lt;a href=&quot;https://ghostty.org/docs/features/theme&quot;&gt;Ghostty docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;a-note-on-tmux&quot;&gt;A note on tmux&lt;/h3&gt;
&lt;p&gt;I noticed that my Neovim colors were subtly different when inside tmux.
I fixed it by adding these lines to &lt;code&gt;~/.tmux.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Tell tmux to use 24-bit true color mappings&lt;/span&gt;
set -g default-terminal &quot;tmux-256color&quot;
set -as terminal-features &quot;,xterm-256color:RGB&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then restarting tmux with &lt;code&gt;tmux kill-server&lt;/code&gt;.
Apparently merely reloading the config file isn’t enough.&lt;/p&gt;
&lt;h3 id=&quot;thoughts-on-my-blog-s-color-theme&quot;&gt;Thoughts on my blog’s color theme&lt;/h3&gt;
&lt;p&gt;While writing this post, I began appreciating the muted color schemes more deliberately and decided my blog’s dark-mode styling was too high contrast for my taste.
I gave it a makeover with the Gruvbox palette.
I’m not a designer;
If you hate it or see anything broken, please email me!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Neovim rabbit hole</title>
    <link href="https://sals.place/blog/neovim-rabbit-hole/" />
    <updated>2026-06-25T00:00:00Z</updated>
    <id>https://sals.place/blog/neovim-rabbit-hole/</id>
    <content type="html">&lt;p&gt;I’m in the rabbit hole.
I’m not sure if I’m falling or burrowing.
Both, I think!&lt;/p&gt;
&lt;p&gt;Current statuses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trying to write some blog posts. I have three or four new drafts. But I keep getting sucked into config reworking.&lt;/li&gt;
&lt;li&gt;Doing most of my writing and config editing on my remote VM now. It’s going pretty well!&lt;/li&gt;
&lt;li&gt;Pulling in configs from Mini.nvim’s
&lt;a href=&quot;https://github.com/nvim-mini/MiniMax&quot;&gt;MiniMax&lt;/a&gt;.
There’s so much to explore.
It’s fun and overwhelming.
&lt;ul&gt;
&lt;li&gt;Following MiniMax’s lead by breaking out sections of my config into the &lt;code&gt;plugin/&lt;/code&gt; subdir,
&lt;a href=&quot;https://github.com/nvim-mini/MiniMax/tree/main/configs/nvim-0.12/plugin&quot;&gt;like this&lt;/a&gt;.
Files in there get auto-loaded, apparently.&lt;/li&gt;
&lt;li&gt;Feeling torn between starting simple and adding one thing at a time versus diving into the deep end and then backing out the features I don’t want.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Faffed about with color schemes, landed on Gruvbox for dark mode (my old flame) and Nightfox’s Dawnfox for light mode.&lt;/li&gt;
&lt;li&gt;Wondering if I should add a focus/zen-mode plugin for writing or just keep resizing my terminal window.&lt;/li&gt;
&lt;li&gt;Learning how to dig around better in &lt;code&gt;:help&lt;/code&gt;.
For example, use &lt;code&gt;Ctrl-]&lt;/code&gt; to follow a link, not &lt;code&gt;gd&lt;/code&gt; as you might use to jump to a declaration in code.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Botsitting</title>
    <link href="https://sals.place/blog/botsitting/" />
    <updated>2026-06-22T00:00:00Z</updated>
    <id>https://sals.place/blog/botsitting/</id>
    <content type="html">&lt;p&gt;After yet another push from senior leadership, I’m finally getting around to trying to do something more interesting with our internal AI resources than just chatting with Gemini.
Specifically, I’m using Antigravity and writing agent skills to query various databases, read my meeting notes, read the news, and smash it all together in the hopes of surfacing something interesting that I may otherwise have missed.&lt;/p&gt;
&lt;p&gt;The results so far are encouraging; &lt;em&gt;and&lt;/em&gt;, the effort is making me appreciate all the commentary around “botsitting,” which refers to all the work required to get the agent to do actually the thing and, critically, do it well enough to actually be better than whatever you were doing before.
When you venture beyond the typical chatbot and code-generation use cases, the botsitting factor gets real.&lt;/p&gt;
&lt;p&gt;The back-and-forth looks something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Me: &lt;code&gt;/news&lt;/code&gt; [this invokes me skill to read all the stuff and tell me what’s changed lately]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent: [Spins for seven-ish minutes.] Okay, I made that report for you! [blah blah blah]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Me: The report loads but doesn’t have any data in it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent: [Spins for a bit.] I fixed the problem!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Me: The report has rows now but only some of them show timeseries data in the chart when I click on them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent: [Spins] …&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You get the idea.&lt;/p&gt;
&lt;p&gt;The interesting and disconcerting thing compared to writing actual code is that I never get the sense that I’ve fixed a problem.
When I fix a bug in code, it’s fixed, and I can move on.
I might break it again later of course, but I have confidence that the bug isn’t going to magically reappear the very next time I run the software.&lt;/p&gt;
&lt;p&gt;With agent skills, the bugs seem to come and go as they please.
I figured I could just get more and more verbose and explicit in my skill instructions as the bugs arise, instructing the agent how to avoid each one next time, but it’s not that simple.
Something that was working fine last time, like the aforementioned report, will suddenly stop working without any changes to the relevant instructions.
It feels like I’m Mickey Mouse waving a magic wand each time I run one:
I can sense the power at my disposal, but I’m not sure what’s going to come from it.&lt;/p&gt;
&lt;p&gt;I’m glad that these skills are just a side quest for me.
I feel for peeps whose jobs have become full-time botsitting.&lt;/p&gt;
</content>
  </entry>
</feed>