<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Get Info: #poweruser</title>
    <description>Posts tagged “poweruser” — Blog of independent game and app developer Matt Sephton. Featuring vintage Macintosh, game development, digital artwork, Japanese esoterica, video game reviews, hacks and tips, and much more.</description>
    <link>https://blog.gingerbeardman.com/tag/poweruser/</link>
    <atom:link href="https://blog.gingerbeardman.com/tag/poweruser/index.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 06 Jul 2026 14:37:40 +0000</pubDate>
    <lastBuildDate>Mon, 06 Jul 2026 14:37:40 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>

    
      
        <item>
          <title>Transparent compression with Folder Actions in macOS</title>
          <description>&lt;p&gt;I just wired this up for my own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; folder—where my current project is quietly piling up data—and figured it was worth writing down. The same trick works on any big codebase, a pile of assets, or a crowded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications&lt;/code&gt; folder. I generally use it on folders that don’t have frequent churn, but do have large or compressible things added to them often enough.&lt;/p&gt;

&lt;p&gt;It relies on &lt;em&gt;transparent file system compression&lt;/em&gt; (sometimes called AFSC), which has been built into macOS for years. It’s nothing like a zip archive: files aren’t bundled up or locked away—they keep their names, stay right where they are, and every app opens them as normal. macOS simply stores them compressed on disk and unpacks them on the fly as they’re read. The only thing that changes is the &lt;em&gt;size on disk&lt;/em&gt;—often dramatically. Apps can even launch more quickly, which is why all Mac App Store downloads already have this sort of filesystem compression applied.&lt;/p&gt;

&lt;p&gt;The classic tool for this is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;afsctool&lt;/code&gt;, but these days I reach for &lt;a href=&quot;https://github.com/Dr-Emann/applesauce&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce&lt;/code&gt;&lt;/a&gt;, a modern Rust rewrite. It compresses much faster by working on blocks in parallel, uses less memory, and writes safely via atomic temp-file renames so you can’t corrupt a file.&lt;/p&gt;

&lt;p&gt;Rather than run it by hand in the Terminal or from a &lt;a href=&quot;/2024/07/30/taking-command-of-the-context-menu-in-macos/&quot;&gt;context menu&lt;/a&gt; every time, we can wire it into a &lt;em&gt;Folder Action&lt;/em&gt; so files get compressed the moment they land in a folder.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;install-applesauce&quot;&gt;Install applesauce&lt;/h2&gt;

&lt;p&gt;Two easy options:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Homebrew (lives in a custom tap, not the core repo)&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;Dr-Emann/homebrew-tap/applesauce

&lt;span class=&quot;c&quot;&gt;# or the standalone installer, if you&apos;d rather skip brew&lt;/span&gt;
curl &lt;span class=&quot;nt&quot;&gt;--proto&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;=https&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tlsv1&lt;/span&gt;.2 &lt;span class=&quot;nt&quot;&gt;-LsSf&lt;/span&gt; https://github.com/Dr-Emann/applesauce/releases/latest/download/applesauce-cli-installer.sh | sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then find where it landed:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;which applesauce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On Apple silicon &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; installs to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/bin/&lt;/code&gt;; older Intel Macs use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/&lt;/code&gt;. We’ll need that path in a moment.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;create-the-folder-action&quot;&gt;Create the Folder Action&lt;/h2&gt;

&lt;p&gt;Folder Actions is a built-in macOS directory watcher. We’ll build one with Automator:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open &lt;em&gt;Automator&lt;/em&gt;, choose &lt;em&gt;New Document&lt;/em&gt;, pick &lt;em&gt;Folder Action&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;At the top, set &lt;em&gt;receives files and folders added to:&lt;/em&gt; to your target folder.&lt;/li&gt;
  &lt;li&gt;If Automator drops in a &lt;em&gt;Get Selected Finder Items&lt;/em&gt; action, delete it—Folder Actions already pass incoming files along, so keeping it breaks the pipeline.&lt;/li&gt;
  &lt;li&gt;Search the sidebar for &lt;em&gt;Run AppleScript&lt;/em&gt; and drag it into the workflow.&lt;/li&gt;
  &lt;li&gt;Paste this, swapping the path if yours differs:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-applescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;theItem&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;do shell script&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/homebrew/bin/applesauce compress &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;quoted form&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;POSIX&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;theItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd + S&lt;/code&gt;) as something descriptive like “Compress”, drop a few files into the folder, and macOS shrinks them behind the scenes. Pretty easy!&lt;/p&gt;

&lt;p&gt;Just for you, here’s one I made earlier: &lt;a href=&quot;https://cdn.gingerbeardman.com/files/Compress.workflow.zip&quot;&gt;Compress.workflow.zip&lt;/a&gt; (93 KB)&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;no-sudo-required&quot;&gt;No sudo required&lt;/h2&gt;

&lt;p&gt;Changing a file’s compression &lt;em&gt;sounds&lt;/em&gt; like root territory, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce&lt;/code&gt; only writes to your own user-owned folders, so it isn’t. That means no Touch ID or password prompts breaking your flow, and nothing running with privileges it doesn’t need.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;what-triggers-it&quot;&gt;What triggers it&lt;/h2&gt;

&lt;p&gt;Worth knowing before you lean on this: a Folder Action only fires when items are &lt;em&gt;added&lt;/em&gt; to the folder (or removed from it), never when a file already sitting inside it changes. It isn’t recursive either—only the top level of the folder is watched, not the contents of any subfolders. That’s &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/WatchFolders.html&quot;&gt;straight from Apple’s docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Applications&lt;/code&gt; that comes with a catch. When an app updates itself it usually swaps in a whole new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.app&lt;/code&gt; bundle, which is technically a new item—but Folder Actions have always been flaky at noticing changes made by anything other than Finder, and updaters move files into place programmatically. So don’t count on self-updating apps getting re-compressed for you. When you want to be sure, drag the app out and back in, or just re-run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce compress&lt;/code&gt; on it by hand.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;choosing-the-algorithm&quot;&gt;Choosing the algorithm&lt;/h2&gt;

&lt;p&gt;By default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce compress&lt;/code&gt; uses &lt;em&gt;LZFSE&lt;/em&gt;, Apple’s fast and well-balanced algorithm—a good pick for almost everything. Two others are available:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;LZVN&lt;/em&gt; — tuned for 64-bit, very fast to decompress.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;ZLIB&lt;/em&gt; — slower to compress, but often the best ratio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Force one with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-applescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;do shell script&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/homebrew/bin/applesauce compress -c LZVN &quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;quoted form&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;POSIX&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;theItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce info &amp;lt;path&amp;gt;&lt;/code&gt; will report the ratio and algorithm any file ended up with. You beauty!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;managing-setup&quot;&gt;Managing setup&lt;/h2&gt;

&lt;p&gt;Saving from Automator attaches the action to the folder you chose, but you can wire the same “Compress” action to more folders—or check it’s switched on if nothing’s happening—from the &lt;em&gt;Folder Actions Setup&lt;/em&gt; dialog. Right-click any folder in Finder and choose &lt;em&gt;Services → Folder Actions Setup…&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Tick &lt;em&gt;Enable Folder Actions&lt;/em&gt; (bottom-left).&lt;/li&gt;
  &lt;li&gt;Add the folder you want watched to the left column.&lt;/li&gt;
  &lt;li&gt;Pick “Compress” from the actions on the right.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now anything you drop into that folder gets squeezed automatically, no matter how it got there.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/folder-actions.png&quot; alt=&quot;IMG&quot; title=&quot;Compress workflow applied to build folder&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;We can confirm the savings with Finder’s Get Info (wouldn’t that be a great name for a blog?). The &lt;em&gt;Size&lt;/em&gt; line still reports the full, uncompressed size—that’s what apps see—while the smaller figure in brackets is what the folder now actually takes up &lt;em&gt;on disk&lt;/em&gt;. The gap between the two is the saving.&lt;/p&gt;

&lt;p&gt;For finer detail, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;applesauce info &amp;lt;path&amp;gt;&lt;/code&gt; prints the exact ratio and algorithm file by file.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/folder-actions-compression-results.png&quot; alt=&quot;IMG&quot; title=&quot;Check size on disk&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 01 Jul 2026 15:44:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/07/01/transparent-compression-with-folder-actions-in-macos/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/07/01/transparent-compression-with-folder-actions-in-macos/</guid>
        </item>
      
    
      
        <item>
          <title>Passing Cloudflare Turnstile using two fingers</title>
          <description>&lt;p&gt;Here’s a quick tip for passing the infamous Cloudflare Turnstile captcha checkbox pages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tab&lt;/code&gt; key, to focus the checkbox (I use my index finger)&lt;/li&gt;
  &lt;li&gt;press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Space&lt;/code&gt; bar, to select the checkbox (I use my thumb)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;…no mouse movement or clicking needed!&lt;/p&gt;

&lt;p&gt;✌️&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/cloudflare-turnstile-two-fingers.png&quot; alt=&quot;Cloudflare Turnstile checkbox&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 15 Jun 2026 17:33:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/06/15/passing-cloudflare-turnstile-using-two-fingers/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/06/15/passing-cloudflare-turnstile-using-two-fingers/</guid>
        </item>
      
    
      
        <item>
          <title>Fresh Files Extension for Nova editor</title>
          <description>&lt;p&gt;I spotted &lt;a href=&quot;https://github.com/FreHu/vscode-fresh-file-explorer&quot;&gt;Fresh File Explorer&lt;/a&gt;, a VS Code extension, &lt;a href=&quot;https://github.com/FreHu/vscode-fresh-file-explorer&quot;&gt;on Hacker News&lt;/a&gt; and loved the idea—a file sidebar that only shows recently modified files.&lt;/p&gt;

&lt;p&gt;The default file sidebar in an editor shows &lt;em&gt;everything&lt;/em&gt;, which in a large project is mostly noise. I thought it would be fun to have something like this for &lt;a href=&quot;https://nova.app&quot;&gt;Nova&lt;/a&gt;, so I reimplemented the concept from scratch using only the OG repo readme as a reference. No code is shared, it’s a completely new extension built against Nova’s API. That means I can’t do as much as the VS Code extension, such as colour coding the files like a heatmap, but it’s still quite useful as it is.&lt;/p&gt;

&lt;p&gt;The result is a sort of hybrid filesystem/git/bookmarks sidebar which I’m finding quite pleasant to use.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/fresh-files-extension-for-nova.png&quot; alt=&quot;PNG&quot; title=&quot;Fresh Files showing pinned files and recent changes&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;In Git repositories, Fresh Files uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git log&lt;/code&gt; to figure out what’s changed. In non-Git workspaces it falls back to filesystem modification times, so it works in any folder. It shares some functionality with the built-in Git Sidebar but I’ve been careful to not duplicate too much.&lt;/p&gt;

&lt;p&gt;There are two modes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pending Changes&lt;/strong&gt; — shows uncommitted files (or files modified in the last day if there’s no Git)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Historical&lt;/strong&gt; — shows files modified within a configurable time window, from 1 hour to 360 days&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can toggle the time window from the sidebar header, the command palette, or project settings.&lt;/p&gt;

&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;

&lt;p&gt;The sidebar supports both a flat file list and a directory tree view, with sorting by recency or name. Files show relative timestamps like “2h ago” or “3d ago” so you can see at a glance what’s freshest.&lt;/p&gt;

&lt;p&gt;Other things worth mentioning:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pinned files&lt;/strong&gt; — pin files to keep them visible regardless of time window&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;File history&lt;/strong&gt; — right-click any file to see its commit history with diffs&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Diff Search (Pickaxe)&lt;/strong&gt; — find commits where a string was added or removed, file-scoped from the sidebar or repo-wide from the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Line History&lt;/strong&gt; — view git history for the current line or selection&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Exhume&lt;/strong&gt; — view deleted file contents with syntax highlighting&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resurrect&lt;/strong&gt; — restore deleted files to their original location&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Show All Files&lt;/strong&gt; — temporarily show all tracked files, overriding the time window&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Search Fresh Files&lt;/strong&gt; — full-text search across fresh files from the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;New File&lt;/strong&gt; — create a new file from the sidebar context menu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Move to Trash&lt;/strong&gt; — delete files from the sidebar context menu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Quick Open&lt;/strong&gt; — fuzzy-open from just the fresh files via the command palette&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deleted file indicators&lt;/strong&gt; — deleted files show up with a distinct icon&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auto-refresh&lt;/strong&gt; — the sidebar updates after saves, git commits, checkouts, and merges&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;context-menu&quot;&gt;Context menu&lt;/h2&gt;

&lt;p&gt;Right-click gives you New File, Show in Finder, Copy Path, Copy Relative Path, Move to Trash, Pin/Unpin, Show File History, Diff Search, and Exhume/Resurrect for deleted files.&lt;/p&gt;

&lt;h2 id=&quot;get-it-now&quot;&gt;Get it now&lt;/h2&gt;

&lt;p&gt;You can install it at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FreshFiles/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FreshFiles/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 24 Feb 2026 13:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/02/24/fresh-files-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/02/24/fresh-files-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Controlling local web servers using xbar</title>
          <description>&lt;blockquote&gt;
  &lt;p&gt;I’ve released a native macOS app that does all this script can do &lt;em&gt;and much more!&lt;/em&gt;&lt;br /&gt;Read all about it: &lt;a href=&quot;https://www.gingerbeardman.com/apps/localmost/&quot;&gt;gingerbeardman.com/apps/localmost/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes I want to run local web servers for projects I’m working on. Usually more than one at a time, or at least over a short space of time.&lt;/p&gt;

&lt;p&gt;So I thought it would be cool to have a controller for those local servers in my menu bar. Sounded like the perfect job for a little scripting and &lt;a href=&quot;https://github.com/matryer/xbar&quot;&gt;xbar&lt;/a&gt;, which is a great way to prove a menubar app idea quickly.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;The plugin allows you to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;toggle servers on and off&lt;/li&gt;
  &lt;li&gt;open in browser&lt;/li&gt;
  &lt;li&gt;view ports&lt;/li&gt;
  &lt;li&gt;view paths&lt;/li&gt;
  &lt;li&gt;view log sizes&lt;/li&gt;
  &lt;li&gt;clear logs&lt;/li&gt;
  &lt;li&gt;edit config&lt;/li&gt;
  &lt;li&gt;and more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/xbar-localhost-dark.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-config&quot;&gt;Example config&lt;/h2&gt;

&lt;p&gt;In the config file we set the starting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_PORT&lt;/code&gt;, followed by one or more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SERVER_DIR&lt;/code&gt; for as many projects as you might want servers. We can temporarily comment out those server lines to prevent projects from appearing in the menu.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# .xbar_httpd_config&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;8000
&lt;span class=&quot;c&quot;&gt;#SERVER_DIR=~/Projects/starchasers/&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/serenity/
&lt;span class=&quot;nv&quot;&gt;SERVER_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/Projects/point-cloud/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;source-code&quot;&gt;Source code&lt;/h2&gt;

&lt;p&gt;Python source code is available in the following gist:&lt;/p&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/a81df96cd0b4c7a397b04711cafeb287.js&quot;&gt;&lt;/script&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 12 Jan 2026 20:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2026/01/12/xbar-local-web-server-controller/</guid>
        </item>
      
    
      
        <item>
          <title>Remove Comments Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released a new extension for the Nova editor.&lt;/p&gt;

&lt;p&gt;It’s called &lt;em&gt;Remove Comments&lt;/em&gt; and it …removes comments from the current line, or selected lines, in your code. That’s it!&lt;/p&gt;

&lt;p&gt;Oh, I’ve tried to support as many comment/syntax formats as I can think of. Sadly it’s not possible to get the current comment formatting from the Nova API, so I had to roll my own logic and heuristics.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.remove-comments/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JavaScript (C-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// This comment will be removed&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// This trailing comment will be removed&lt;/span&gt;
&lt;span class=&quot;cm&quot;&gt;/* This block comment will be removed */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Python (hash-style comments):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This comment will be removed
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# This trailing comment will be removed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;HTML:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This comment will be removed --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- This trailing comment will be removed --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After running Remove Comments:&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Content&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Mon, 08 Dec 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/12/08/remove-comments-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>How to tame a user interface using a spreadsheet</title>
          <description>&lt;p&gt;Many years ago, while working at Apple and running a lab at WWDC, I met a guy who was using a piece of &lt;a href=&quot;https://en.wikipedia.org/wiki/IAd_Producer&quot;&gt;Apple software designed for creating interactive ads&lt;/a&gt; to design a Car Play user interface for a popular US car manufacturer. I was impressed by his ability to think outside the box and told him so. I mentioned to him how human interface designers at Apple &lt;a href=&quot;https://www.youtube.com/watch?v=DGn7BcFGigc&amp;amp;t=474s&quot;&gt;were using Keynote to rapidly prototype user interfaces and animations&lt;/a&gt;. The discussion then took a strange turn onto spreadsheets.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;spreadsheets&quot;&gt;Spreadsheets?&lt;/h2&gt;

&lt;p&gt;In my opinion, spreadsheets are one of the greatest user interface design tools ever created. I believe that the most effective tool for a job is one that gets out of the way as much as possible. Even better if it’s a tool that you already have access to, or at the very least, can use at no cost.&lt;/p&gt;

&lt;p&gt;Before you have time to load up Figma, Sketch, Photoshop, or any other design software, I’ll have already typed in my content placeholders in a spreadsheet and be most of my way towards a solution. I’ll be merging and moving cells at lightning speed—rationalising layouts, determining the optimal use of space, and creating order out of chaos.&lt;/p&gt;

&lt;p&gt;The purpose of using a tool that may seem ill-suited to the task at hand—designing user interfaces—is to liberate yourself from the clutter or baggage of content and style. This allows you to focus solely on layout, positioning, and hierarchy. You don’t have to worry about type sizes, or get bogged down with software choices. You can quickly adjust the size or structure of the screen by adding/removing rows/columns. With a spreadsheet, it’s just the keyboard between you and the perfect layout.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;lets-take-your-brain-to-another-dimension&quot;&gt;Let’s take your brain to another dimension&lt;/h2&gt;

&lt;p&gt;One frequent criticism of spreadsheets is that they are “only 2D” or that a grid isn’t versatile enough to solve your problem. But if you pay close attention, you’ll realise that spreadsheets can occupy as many dimensions as you require by using multiple sheets. Like the stack of ledger paper that was used before spreadsheets were invented. An episode of The Computer Chronicles called “The Spreadsheet Wars” aired in 1988 and has a great &lt;a href=&quot;https://youtu.be/eAiZBUYNUBA?si=TMiXXLj1gtFwZQkp&amp;amp;t=1154&quot;&gt;demo of spreadsheet software Lotus 1-2-3 doing 3D spreadsheet manipulation&lt;/a&gt; in ways that are still impressive several decades later.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://archive.org/details/GridSystemsInGraphicDesignJosefMullerBrockmann&quot;&gt;Grid systems are well known across many fields of design&lt;/a&gt; for being a huge assist when solving layouts and rationalising problems. The grid enforces structure invisibly during the design process. A spreadsheet is a highly configurable grid system that is ready to be put to work on your problem!&lt;/p&gt;

&lt;p&gt;You might also consider the use of plain paper, graph paper, and a bitmap editor. Which might be best suited for sketching out a quick icon idea? It’s easy to understand that graph paper is more suitable than plain paper for drawing images made up of pixels, and in many situations it may be quicker and more suitable than a bitmap editor. &lt;a href=&quot;https://www.moma.org/collection/works/188382?artist_id=38483&amp;amp;page=1&amp;amp;sov_referrer=artist&quot;&gt;Susan Kare famously used a book of grid/graph paper to draw many early Macintosh icons&lt;/a&gt; because icon creation software had yet to be invented.&lt;/p&gt;

&lt;h2 id=&quot;theres-an-app-for-that&quot;&gt;There’s an app for that&lt;/h2&gt;

&lt;p&gt;I use Apple Numbers to do most of my spreadsheet work, because it’s the one that I have easiest access to most of the time—it’s installed on both my Mac and iPhone. You might want to use Google Sheets, Microsoft Excel, or even a simpler table tool like &lt;a href=&quot;https://smoothcsv.com&quot;&gt;SmoothCSV&lt;/a&gt;. There is no wrong choice.&lt;/p&gt;

&lt;p&gt;Here are some examples of layouts I’ve solved using a spreadsheet.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;yoyozo&quot;&gt;YOYOZO&lt;/h2&gt;

&lt;p&gt;Perhaps my most famous example is the stats screen of &lt;a href=&quot;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&quot;&gt;my hit game &lt;em&gt;YOYOZO&lt;/em&gt;&lt;/a&gt;. With this one I already had a TTF font file that I’d created for the manual, so I went the extra mile and planned a pixel perfect layout in the right colours and everything. A fun fact you might not know: &lt;em&gt;YOYOZO&lt;/em&gt; was featured in Ars Technica’s “Best Games of 2023” list, alongside Mario and Zelda. This spreadsheet thing is great!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-yoyozo.png&quot; alt=&quot;IMG&quot; title=&quot;Pixel perfect precision using the exact font&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;enhanced-sfxr&quot;&gt;Enhanced sfxr&lt;/h2&gt;

&lt;p&gt;This year I’ve made two enhanced versions of sfxr—&lt;a href=&quot;https://gingerbeardman.itch.io/enhanced-sfxr-for-love2d&quot;&gt;one for Love2D&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/search?q=from%3Agingerbeardman%20sfxr&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;one for macOS&lt;/a&gt;—and I can’t remember which one this was done for. I usually get rid of the header rows and columns but in this instance they acted as sidebar and title bar. Regardless, whilst it might not look like much this sort of pre-planning and organisation saved a ton of time when I was working on the app. It involved no code and was done in a minute or two!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-sfxr.png&quot; alt=&quot;IMG&quot; title=&quot;Organising the large components that make up the window&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;spindle&quot;&gt;Spindle&lt;/h2&gt;

&lt;p&gt;Another of my “coming soon” apps is &lt;a href=&quot;https://twitter.com/search?q=from:gingerbeardman%20spindle&amp;amp;src=typed_query&amp;amp;f=live&quot;&gt;an optical media dumper whose USP is its ability to dump from multiple drives at the same time&lt;/a&gt;. I wanted a UI that would stay mostly the same across both the setup and dumping states, and also that was easily repeatable when additional drives duplicate the app UI. The benefit of working with a grid here is that it was 1:1 transferrable to SwiftUI grid system!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-spindle.png&quot; alt=&quot;IMG&quot; title=&quot;Two states of the same interface showing what changes and what remains the same&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;driving-game&quot;&gt;Driving Game&lt;/h2&gt;

&lt;p&gt;This final one is an example of a racing game. Something that you might already be more familiar with and so be able to figure out how you can apply the technique to your own work. Something like Mario Kart, F-Zero, Colin McRae Rally, or WipEout. Let’s go!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/spreadsheet-ui-racer.png&quot; alt=&quot;IMG&quot; title=&quot;A fairly standard driving/racing game head up display&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 11 Oct 2025 00:03:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/10/11/how-to-tame-a-user-interface-using-a-spreadsheet/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/10/11/how-to-tame-a-user-interface-using-a-spreadsheet/</guid>
        </item>
      
    
      
        <item>
          <title>Three.js Completions Extension for Nova editor</title>
          <description>&lt;p&gt;I’ve released an extension to add &lt;a href=&quot;https://threejs.org&quot;&gt;Three.js&lt;/a&gt; completions support to the Nova editor.&lt;/p&gt;

&lt;p&gt;The coolest thing about this extension is that most of the code is automatically generated straight from the Three.js TypeScript files. This means there’s the potential for less errors in the data, and I can easily update the extension whenever a new release of Three.js comes out just by running a single command. Why work &lt;em&gt;harder&lt;/em&gt; when you can work &lt;em&gt;cleverer&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.ThreeJS/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might be quick to deduce that—yes—I’m making a 3D game. 😘&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 04 Oct 2025 15:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/10/04/three-js-completions-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Updates to Extensions for Nova editor</title>
          <description>&lt;p&gt;In late 2024, I spent some time improving my tools by &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;building a set of extensions for Nova editor&lt;/a&gt; to streamline some time-consuming tasks I encounter during blogging and game development.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-extensions.png&quot; alt=&quot;IMG&quot; title=&quot;My current list of extensions for Nova editor&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;This week, I released major updates to many of them with enhancements and new features:&lt;/p&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Adds sorting, the ability to bookmark folders, marking of missing items, and other improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Results in the choice palette are now sorted by default using dates found in filenames.&lt;/li&gt;
  &lt;li&gt;Along with other filtering improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can now unwrap the text surrounding cursor, which removes some friction.&lt;/li&gt;
  &lt;li&gt;In selected text multiple paragraphs will be unwrapped individually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Watched words can be managed through the sidebar, added from selected text, or via typing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;Version 2.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;You can quickly insert or update the creation/modified ISO date in your front matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Plus!&lt;/em&gt; I also released a brand new extension:&lt;/p&gt;

&lt;h2 id=&quot;csv-to-md-table&quot;&gt;CSV to MD table&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.csv2md/&quot;&gt;Version 1.0.0&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Convert tabular data between CSV/TSV and Markdown formats.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 30 Aug 2025 15:03:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/08/30/updates-to-my-extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Intelligent Agent Technology: Open Sesame! (1993)</title>
          <description>&lt;p&gt;After years of unsuccessful attempts to find the name of a long-forgotten app through Google, I tried pasting my query into Gemini:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Back in my teens I saw a software demo for a Mac/Windows app that tracked what you were doing and offered to complete repetitive tasks for you. Like renaming all files in a folder it would interrupt after a few and offer to do the rest. Maybe late 80s or early 90s.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini instantly responded with the app by name and with references. (out of all the services I tried, ChatGPT was the only other one to identify the app correctly.)&lt;/p&gt;

&lt;p&gt;Open Sesame! was “the world’s first intelligent software assistant for the Macintosh. It observes how you work, learns your repetitive patterns, and then offers to do them for you. Automatically.”&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/open-sesame.jpg&quot; alt=&quot;IMG&quot; title=&quot;Open Sesame! software reduces computer-use complexity&amp;lt;br&amp;gt;by relieving users of many routine tasks.&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;one-thousand-and-one-nights&quot;&gt;One Thousand and One Nights&lt;/h2&gt;

&lt;p&gt;I remember seeing a demo of Open Sesame! in the early/mid-1990s. Although the demo failed—the Macintosh was supposed to prompt the user to continue renaming a series of files in sequence—the concept left a lasting impression on me.&lt;/p&gt;

&lt;p&gt;Over the years I’ve tried to find the event I saw the app at, the app itself, or any other information. I asked other classic Macintosh enthusiasts. Time after time I failed to find the answer.&lt;/p&gt;

&lt;p&gt;So I find it delightfully circular that in 2025 I am using the “ai” benefits of machine learning to find out the name of an app built around machine learning in 1993.&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/open-sesame-ad-1993.jpg&quot; alt=&quot;IMG&quot; title=&quot;Advertisement in MacWorld (December 1993)&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://macintoshgarden.org/apps/open-sesame-11&quot;&gt;Open Sesame! 1.1 at Macintosh Garden&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9308_August_1993/page/n26/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: MacBulletin&lt;/a&gt; (August 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9309_September_1993/page/n37/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: News&lt;/a&gt; (September 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9312_December_1993/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: advertisement&lt;/a&gt; (December 1993)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9405_May_1994/page/n85/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: review of version 1.02&lt;/a&gt; (May 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9406_June_1994/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: advertisement&lt;/a&gt; (June 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9412_December_1994/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: price of version 1.1&lt;/a&gt; (December 1994)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.org/details/MacWorld_9508_August_1995/page/n203/mode/2up?q=%22open+sesame%22&quot;&gt;MacWorld: get Open Sesame! free when buying a keyboard&lt;/a&gt; (August 1995)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://spinoff.nasa.gov/spinoff1996/56.html&quot;&gt;NASA Spinoff: Intelligent Agent Technology&lt;/a&gt; (&lt;a href=&quot;https://archive.org/details/1991_20210720_202107/1996/page/n96/mode/1up&quot;&gt;scan&lt;/a&gt;) (1996)&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 31 May 2025 15:33:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/05/31/intelligent-agent-technology-open-sesame-1993/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/05/31/intelligent-agent-technology-open-sesame-1993/</guid>
        </item>
      
    
      
        <item>
          <title>Monokai Pro Vibrant theme for Nova editor</title>
          <description>&lt;p&gt;I neglected to blog about this in October 2024, so here it is now.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&quot;&gt;Monokai Pro Vibrant&lt;/a&gt; is my fork of the &lt;a href=&quot;https://github.com/keisto/Monokai-Pro.novaextension&quot;&gt;unofficial Monokai Pro theme&lt;/a&gt; for &lt;a href=&quot;https://nova.app&quot;&gt;Nova&lt;/a&gt; editor, by Tony Keiser.&lt;/p&gt;

&lt;p&gt;My fork has a few quality of life changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Theme Vibrancy is enabled, making focused and unfocused windows more distinguishable&lt;/li&gt;
  &lt;li&gt;Colour tweaks improve contrast when highlighting found instances of search terms&lt;/li&gt;
  &lt;li&gt;Comments are now &lt;em&gt;italicised&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use the Spectrum variation of the theme with the &lt;a href=&quot;https://github.com/IBM/plex/releases/tag/%40ibm%2Fplex-mono%401.1.0&quot;&gt;IBM Plex Mono&lt;/a&gt; font to great effect.&lt;/p&gt;

&lt;p&gt;Since late-2025 I’m now using font &lt;a href=&quot;https://mass-driver.com/trial-fonts&quot;&gt;MD IO Trial&lt;/a&gt; (free for personal use).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-monokai-pro-vibrant.png&quot; alt=&quot;IMG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;monokai-pro&quot;&gt;Monokai Pro&lt;/h2&gt;

&lt;p&gt;The original &lt;a href=&quot;https://monokai.pro&quot;&gt;Monokai Pro&lt;/a&gt; theme by Wimer Hazenberg is not free, so I would encourage you to buy a licence even if you’re using an unofficial port or modification of the theme. It’s only right to support software developers. You can get a licence to use Monokai Pro from the original creator: &lt;a href=&quot;https://monokai.pro&quot;&gt;monokai.pro&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;p&gt;I filed a &lt;a href=&quot;https://github.com/keisto/Monokai-Pro.novaextension/pull/9&quot;&gt;PR for these changes&lt;/a&gt;, but it has not yet been accepted. I’m not comfortable distributing this on the official extension gallery at this time. So grab it from GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&quot;&gt;github.com/gingerbeardman/Monokai-Pro-Vibrant.novaextension/releases/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Wed, 14 May 2025 19:50:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2025/05/14/monokai-pro-vibrant-theme-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2025/05/14/monokai-pro-vibrant-theme-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Word Counter extension for Nova editor</title>
          <description>&lt;p&gt;So the &lt;a href=&quot;/2024/10/24/macro-extension-for-nova-editor/&quot;&gt;Macro extension&lt;/a&gt; and &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;a bunch of smaller extensions&lt;/a&gt; were supposed to be it, but it’s so much fun to create these that whenever I get an idea for an extension I can’t help making it.&lt;/p&gt;

&lt;p&gt;This time I wanted to keep track of how many times I’m calling certain functions in my code. That’s quite a niche requirement so I made a more general purpose word counter with sidebar, thresholds and coloured blobs!&lt;/p&gt;

&lt;p&gt;You can configure a list of words to count, and the thesholds that will trigger the grey/green/yellow/red coloured blobs, on a per-project basis using the workspace &lt;em&gt;Project Settings…&lt;/em&gt; panel.&lt;/p&gt;

&lt;p&gt;And you can double-click on a word in the sidebar to search the current document for it. This feature uses AppleScript so you’ll need to give Nova permissions, though it should prompt you to do that if it doesn’t have them already.&lt;/p&gt;

&lt;p&gt;Note: the screenshot below is slightly old as I’ve since refined the images in the extension.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-word-counter.png&quot; alt=&quot;PNG&quot; title=&quot;Counting some Playdate SDK specific function names&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;downloads&quot;&gt;Downloads&lt;/h2&gt;

&lt;p&gt;You can download it at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.wordcounter/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the &lt;a href=&quot;https://github.com/gingerbeardman/Word-Counter&quot;&gt;source code is available on GitHub&lt;/a&gt; and PRs are more than welcome!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 27 Oct 2024 17:12:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/27/word-counter-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/27/word-counter-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Macro extension for Nova editor</title>
          <description>&lt;p&gt;After creating &lt;a href=&quot;/2024/10/17/extensions-for-nova-editor/&quot;&gt;a bunch of smaller Nova Extensions quite quickly&lt;/a&gt; I wondered how far I could push things, just as a personal challenge. I had the idea of implementing a Macro text recording and playback system. There was something similar in TextMate editor, and whilst the Nova API doesn’t currently allow as sophisticated a system I thought it was something still worth exploring.&lt;/p&gt;

&lt;p&gt;Design of the system was incremental, with improvements and simplifications that came about through using it. It took a few aborted attempts to land on a good mechanism of recording and playing back “actions”, which currently encompass:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;insertions&lt;/li&gt;
  &lt;li&gt;deletions&lt;/li&gt;
  &lt;li&gt;cursor position changes&lt;/li&gt;
  &lt;li&gt;selection changes&lt;/li&gt;
  &lt;li&gt;selection replacements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I encode these changes with some simple JSON markup. Insertions are represented as text, and replacements as pairs of text, whilst deletions, cursor position changes, and selection changes are represented as numbers where positive means “to the right” and negative means “to the left”. It’s a simple system but should prove to be expandable if the Nova API is expanded to include extra capabilities, such as hooking into the &lt;em&gt;Find&lt;/em&gt;, &lt;em&gt;Files&lt;/em&gt; sidebar, or the &lt;em&gt;Command Palette&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Macro data can end up quite large, as we’re recording single character or position changes, so I added the ability to compress a macro by coalescing actions of the same type into one. A sort of naïve huffman encoding scheme. This makes a huge difference to both size and playback speed.&lt;/p&gt;

&lt;p&gt;You can download the extension at: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Macro/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Macro/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;recording&quot;&gt;Recording&lt;/h2&gt;

&lt;p&gt;Recording is done on-demand, only when you want it. A notification will signal recording has begun. When you’ve finished you click the Stop button in the notification and the macro will be automatically saved to the sidebar with a sequential name. You can rename it afterwards if you’d like.&lt;/p&gt;

&lt;p&gt;You can also start the recording using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; sidebar icon, or using the command via the Editor menu or &lt;em&gt;Command Palette&lt;/em&gt;, but recording is always stopped through the notification. It took a while to arrive at this mechanism, and for a long time the extension was a lot more complicated and confusing to use.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-record.png&quot; alt=&quot;IMG&quot; title=&quot;Macro recording notification&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;example-macro&quot;&gt;Example Macro&lt;/h2&gt;

&lt;p&gt;Let’s type:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nova!&amp;lt;move left 1&amp;gt;&amp;lt;select -3&amp;gt;&amp;lt;replace &quot;ova&quot; → &quot;O&quot;&amp;gt;VA&amp;lt;move left 3&amp;gt;&amp;lt;delete 3&amp;gt;ova&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end result of which is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Nova!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to record this, the results would be…&lt;/p&gt;

&lt;p&gt;Raw macro (17 actions):&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Macro 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;actions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;N&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;old&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;new&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;O&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;V&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;o&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;v&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;isExpanded&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compressed macro (6 actions):&lt;/p&gt;
&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Nova!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REP&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;old&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;new&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;O&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;VA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;POS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;←&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DEL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ova&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compression also helps with readability though I added a function to copy a human-readable version of the macro,which is how the &lt;a href=&quot;#example-macro&quot;&gt;text at the start of this example&lt;/a&gt; was generated.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-anim.gif&quot; alt=&quot;IMG&quot; title=&quot;Playback of the above macro&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;sidebar&quot;&gt;Sidebar&lt;/h2&gt;

&lt;p&gt;Recorded macros live in a bespoke sidebar, and can be expanded or collapsed on demand. This allows us to see each action in the macro and exactly how the compressed version differs. The context menu provides functions to manipulate and manage the macros.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-macro-extension-sidebar.png&quot; alt=&quot;IMG&quot; title=&quot;Macro sidebar with raw and compressed versions, expanded to show all actions&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;context-menu&quot;&gt;Context Menu&lt;/h3&gt;

&lt;p&gt;After a macro is recorded you can manipulate it using the context menu of items in the Macro sidebar:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Replay&lt;/li&gt;
  &lt;li&gt;Compress&lt;/li&gt;
  &lt;li&gt;Duplicate&lt;/li&gt;
  &lt;li&gt;Rename&lt;/li&gt;
  &lt;li&gt;Copy (Compressed)&lt;/li&gt;
  &lt;li&gt;Copy (Raw)&lt;/li&gt;
  &lt;li&gt;Copy (Readable)&lt;/li&gt;
  &lt;li&gt;Delete&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;

&lt;p&gt;Recording selections adds extra weight and slowness to a macro, so whilst the extension defaults to recording text selection changes I added the option to switch it off. If you’re only interested in the smallest size macros you can enable automatic compression of every macro which happens as the macro is saved.&lt;/p&gt;

&lt;p&gt;Playback is very quick but in some situations, such as playing back for a screen recording, you might want the text to appear more slowly, so I added a playback speed option.&lt;/p&gt;

&lt;p&gt;Note: if you want to see the smoothest character-by-character playback, you should make sure compression and record selection changes are both switched off.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;improvements&quot;&gt;Improvements&lt;/h2&gt;

&lt;p&gt;There are still some improvements I’d like to make in the future: to be able to more easily see the cursor during playback, and to highlight the current action in the sidebar so you can keep track of progress. We’ll see if/when I get around to those.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/gingerbeardman/Macro&quot;&gt;source code is available on GitHub&lt;/a&gt; and PRs are more than welcome!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 24 Oct 2024 14:02:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/24/macro-extension-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/24/macro-extension-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Extensions for Nova editor</title>
          <description>&lt;p&gt;I’m a big believer in solving problems yourself if it’s possible rather than waiting for app updates that might never arrive. Making extensions for the &lt;a href=&quot;https://nova.app&quot;&gt;Nova editor&lt;/a&gt; that I do most of my programming and blogging in is so much fun! So, here are some of my own creation:&lt;/p&gt;

&lt;p&gt;View all: &lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;yaml-tag-picker&quot;&gt;YAML Tag Picker&lt;/h2&gt;

&lt;p&gt;Allows you to easily select tags for the front matter in your blog posts. It scans your existing posts for tags and presents them in a Choice Palette, making it easy to maintain consistent tagging across your blog. Nice!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.YAMLTagPicker/&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/yaml-tag-picker.png&quot; alt=&quot;IMG&quot; title=&quot;Searching existing tags for the word “play”&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;markdown-file-linker&quot;&gt;Markdown File Linker&lt;/h2&gt;

&lt;p&gt;Allows you to insert links to local files as Markdown, perfect for linking between articles in your Jekyll blog! Also includes the ability to wrap the current text in a link, and if there’s a URL on the clipboard that’ll be used as the link destination.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.MarkdownFileLinker/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You choose a local file using the file selector, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users/matt/Projects/blog/_posts/2023/2023-11-21-yoyozo-how-i-made-a-playdate-game-in-39kb.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it will be inserted as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve selected some text before invoking the extension, you’ll get:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[YOYOZO](/2023/11/21/yoyozo-how-i-made-a-playdate-game-in-39kb/)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an image you might end up with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;![IMG](∕images/posts/yoyozo-teaser.gif)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;reindent-o-matic&quot;&gt;Reindent-o-matic&lt;/h2&gt;

&lt;p&gt;Allows you to apply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt; indent rules to the current file, or all files matching specific extensions. Important: changes are applied but not saved, giving you the opportunity to review.&lt;/p&gt;

&lt;p&gt;Useful when reusing code from one project that used space indentation to a new one that uses tab indentation. Ideally Nova should do this automatically, and sometimes it does, but not every time for reasons I can’t figure out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Reindent-o-matic/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;toggle-scroll-bars&quot;&gt;Toggle Scroll Bars&lt;/h2&gt;

&lt;p&gt;Allows you to see the scrollbar at all times. This is useful because the scrollbar also contains source control change markers. With scrollbars always visible you can more easily locate changes across the entire length of your document.&lt;/p&gt;

&lt;p&gt;This has already saved me so much time and effort and I’ve only been using the editor this way for a week. So I thought I’d create an extension to make it easier for other people to enjoy this usability improvement.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.scrollbars/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scroll Bars on/off&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-on-minimap-off.png&quot; alt=&quot;on-off&quot; /&gt; &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/scrollbars-off-minimap-off.png&quot; alt=&quot;off-off&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;unwrap-paragraph&quot;&gt;Unwrap Paragraph&lt;/h2&gt;

&lt;p&gt;OK, I got carried away. This one adds the ability to merge multiple lines of selected text into one.&lt;/p&gt;

&lt;p&gt;This is useful for combining multiple lines of data into one, or reflowing a paragraph of text that has had manual line breaks applied such as something pasted from an old email. It also colalesces areas of white space into a single space. So, it’s not just “Join Lines” but something more specific.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;TextMate&lt;/em&gt; editor this command is called Unwrap Paragraph, so I’ve kept the same name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.unwraptext/&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;Text, before:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	What 
exactly does  
this   extension	
do with the
text?  🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Text, after:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;What exactly does this extension do with the text? 🤔
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, before:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Source code, after:&lt;/p&gt;
&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x202&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x451&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x505&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x088&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x050&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;filter-through-command&quot;&gt;Filter Through Command&lt;/h2&gt;

&lt;p&gt;Run terminal commands on selected text. When you need to use a terminal command but you just want the results. The possibilities are endless as command line is your oyster! Comes with a library of useful commands including sort, base64 decode/encode, extract URLs, remove blank lines, count loc, remove HTML tags.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.FilterThroughCommand/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type your own command&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-custom-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pick from a library of commands&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/nova-filter-through-command.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br clear=&quot;both&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;bookmarks&quot;&gt;Bookmarks&lt;/h2&gt;

&lt;p&gt;Bookmark files in the Sidebar for easier management. Ideal if the Files Sidebar is too much, or you want a simpler view of the files you are working on.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.Bookmarks/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;tag-sync&quot;&gt;Tag Sync&lt;/h2&gt;

&lt;p&gt;Automatic synchronisation of closing tag when editing opening tag. Improve your coding efficiency by ensuring tag pairs stay synchronized while editing markup languages!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.tagsync/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;openscad&quot;&gt;OpenSCAD&lt;/h2&gt;

&lt;p&gt;Adds language support for &lt;a href=&quot;https://openscad.org&quot;&gt;OpenSCAD&lt;/a&gt;: &lt;em&gt;The Programmers Solid 3D CAD Modeller&lt;/em&gt;, including both syntaxes (with highlighting) and completions (with alternate forms).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&quot;&gt;extensions.panic.com/extensions/com.gingerbeardman/com.gingerbeardman.openscad/&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;macro&quot;&gt;Macro&lt;/h2&gt;

&lt;p&gt;Text editing recording and playback system. This one &lt;a href=&quot;/2024/10/24/macro-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;word-counter&quot;&gt;Word Counter&lt;/h2&gt;

&lt;p&gt;Count multiple words and have their tally displayed in the sidebar. This one also &lt;a href=&quot;/2024/10/27/word-counter-extension-for-nova-editor/&quot;&gt;has its own blog post&lt;/a&gt;.&lt;/p&gt;

</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Oct 2024 20:47:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/17/extensions-for-nova-editor/</guid>
        </item>
      
    
      
        <item>
          <title>Automating the most annoying aspects of blogging</title>
          <description>&lt;p&gt;Back in 2021 I had a look around and decided to base this incarnation of my blog on an open-source &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt; theme called “&lt;a href=&quot;https://github.com/ahmadajmi/type&quot;&gt;Type&lt;/a&gt;”, though I’ve changed and added so much it’s quite some distance from the original as it stands today. As I added blog posts the performance became much worse. Initially I blamed Jekyll for this, until I took a closer look. What I learned was that the blog theme did some things in sub-optimal ways, so over the course of 2024 I have corrected as many of them as I can. Build time dropped from ~12 seconds to ~1 second.&lt;/p&gt;

&lt;p&gt;My fixes and optimisations included:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Optimised SASS to compile once in plugin rather than header of every page (23 Feb)&lt;/li&gt;
  &lt;li&gt;Optimised all includes and templates to reduce build time (19 Aug)&lt;/li&gt;
  &lt;li&gt;Improved “noun” replacement, which are automatically-emphasised words (3 Sep)&lt;/li&gt;
  &lt;li&gt;Better YouTube embeds using lite-yt-embed (11 Sep)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And my additions along the way:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Multi-carousel support (14 Jul)&lt;/li&gt;
  &lt;li&gt;Automatic transformation of local images urls to CDN urls (19 Aug)&lt;/li&gt;
  &lt;li&gt;Automatic smart quotes in post titles (19 Aug)&lt;/li&gt;
  &lt;li&gt;Added OpenSearch to allow searching blog from address bar (15 Sep)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might ask… what’s left to do? Not much I reckon. So I took a closer look at what is involved in me creating a blog post and made a list of the most repetitive, awkward or error prone tasks:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Entering links, partcularly links to other blog posts&lt;/li&gt;
  &lt;li&gt;Entering tags, going from memory or using search across project&lt;/li&gt;
  &lt;li&gt;Getting images onto my CDN server, currently copy by SFTP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s solve all these annoyances!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;entering-links-and-tags&quot;&gt;Entering links and tags&lt;/h2&gt;

&lt;p&gt;You can read about these two in previous blog posts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Created &lt;a href=&quot;/2024/10/08/markdown-file-linker/&quot;&gt;Markdown File Linker&lt;/a&gt; to make linking easier (8 Oct)&lt;/li&gt;
  &lt;li&gt;Created &lt;a href=&quot;/2024/10/08/yaml-tag-picker/&quot;&gt;YAML Tag Picker&lt;/a&gt; to make tagging easier (8 Oct)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;getting-images-onto-my-cdn-server&quot;&gt;Getting images onto my CDN server&lt;/h2&gt;

&lt;p&gt;I was still having to copy my images to my CDN server manually, which was a pain. But the software stack on the server was intimidating. I kept putting it off for a rainy day, but I knew I would eventually get around to automating it.&lt;/p&gt;

&lt;p&gt;The idea was to not upload the images at all, but rather download them to the server directly. I’d use a GitHub Webhook to trigger a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; of the latest files onto my server. It took a few hours and a few attempts, but I finally arrived at a fairly elegant system I’m happy with:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;GitHub Webhook that runs on push event&lt;/li&gt;
  &lt;li&gt;PHP script in web server docker container receives, validates, and creates a trigger file&lt;/li&gt;
  &lt;li&gt;Service on server outside of docker looks for trigger file and does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: in January 2025, since this post was originally written, I have simplified the following files after migrating away from web servers in Docker containers to &lt;a href=&quot;https://caddyserver.com&quot;&gt;Caddy server&lt;/a&gt;. Highly recommended!&lt;/p&gt;

&lt;h3 id=&quot;github-webhook&quot;&gt;GitHub Webhook&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Payload URL: https://www.example.com/webhook.php&lt;/li&gt;
  &lt;li&gt;Content type: application/json&lt;/li&gt;
  &lt;li&gt;SSL verification: enabled&lt;/li&gt;
  &lt;li&gt;Which events would you like to trigger this webhook: Just the push event&lt;/li&gt;
  &lt;li&gt;Active: on&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;webhook-receiver-php-script&quot;&gt;Webhook receiver (PHP script)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/63e4dc0bce459ad6609c2701963eb61f&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/63e4dc0bce459ad6609c2701963eb61f.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;webhook-git-pull-watcher-shell-script&quot;&gt;Webhook git pull watcher (Shell script)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/e1c513c69b9e9d41aa91155893ae7334&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/e1c513c69b9e9d41aa91155893ae7334.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;trigger-system-service&quot;&gt;Trigger (System service)&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/1ff95ce64a6a255919b8262dd4a21bc7&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/1ff95ce64a6a255919b8262dd4a21bc7.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;access-control&quot;&gt;Access control&lt;/h3&gt;

&lt;noscript&gt;&lt;p&gt;&lt;a href=&quot;https://gist.github.com/gingerbeardman/23e0da9949a8d41ebd190d60b0bd033b&quot;&gt;View the source code as a Gist&lt;/a&gt;&lt;/p&gt;&lt;/noscript&gt;
&lt;script src=&quot;https://gist.github.com/gingerbeardman/23e0da9949a8d41ebd190d60b0bd033b.js&quot;&gt;&lt;/script&gt;

&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;/h3&gt;

&lt;p&gt;You can set up the service using:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl start git-pull-watcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl enable git-pull-watcher&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other commands&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl restart git-pull-watcher&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl status git-pull-watcher&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 11 Oct 2024 16:42:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/10/11/automating-the-most-annoying-aspects-of-blogging/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/10/11/automating-the-most-annoying-aspects-of-blogging/</guid>
        </item>
      
    
      
        <item>
          <title>Search Moby Games using Alfred app</title>
          <description>&lt;p&gt;Earlier this year I made a workflow for &lt;a href=&quot;https://alfred.app&quot;&gt;Alfred app&lt;/a&gt; to allow easy searching of &lt;a href=&quot;https://www.mobygames.com&quot;&gt;Moby Games&lt;/a&gt;. Earlier this week it was released on &lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;Alfred Gallery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/alfred-moby-games.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://alfred.app/workflows/gingerbeardman/moby-games/&quot;&gt;alfred.app/workflows/gingerbeardman/moby-games/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow &lt;a href=&quot;https://www.mobygames.com/info/api/&quot;&gt;requires an API key that you can get instantly&lt;/a&gt; via your Moby Games profile page.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’d like access to the free API (not-for-profit use), create a MobyGames account and then visit your profile page. Click the ‘API’ link under your user name to sign up for an API key. If you wish to use the API key with an existing game launcher or plugin please specify which launcher and plugin you will be using.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enter “Alfred app Moby Games workflow”. Then paste your API into the Workflow configuration.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;open-source&quot;&gt;Open Source&lt;/h2&gt;
&lt;p&gt;Suggestions and PRs are welcome at: &lt;a href=&quot;https://github.com/gingerbeardman/alfred-moby-games/&quot;&gt;github.com/gingerbeardman/alfred-moby-games/&lt;/a&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 04 Aug 2024 12:57:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/08/04/search-moby-games-using-alfred-app/</guid>
        </item>
      
    
      
        <item>
          <title>Taking command of the Context Menu in macOS</title>
          <description>&lt;p&gt;Yesterday on Twitter the inimitable &lt;a href=&quot;https://twitter.com/mortenjust/status/1817991110544744764&quot;&gt;Morten Just posted a preview of a tool he’s created&lt;/a&gt; that wrap ffmpeg to allow movies, such screen recordings but pretty much anything, to be re-encoded to a smaller filesize.&lt;/p&gt;

&lt;p&gt;I responded with a trick I use to do the same on “right-click” context menu using a macOS app called ContextMenu, and others said it was possible to do it using Automator (with some caveats). In this blog post I’ll compare the two.&lt;/p&gt;

&lt;p&gt;But first… let’s talk about how we will make this work.&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;processing-files-through-shell-scripts&quot;&gt;Processing files through shell scripts&lt;/h1&gt;

&lt;p&gt;We’ll be making use of command line tools to do the heavy lifting, but don’t worry I’ll show that this can be as simple as a single-line command to process a single file, or a shell scripts of a few lines to process multiple files.&lt;/p&gt;

&lt;h3 id=&quot;single-files&quot;&gt;Single files&lt;/h3&gt;

&lt;p&gt;The one-line command to convert a video or audio file can be as simple as the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are some assumptions here:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; has been installed using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;my Mac is Apple silicon, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew&lt;/code&gt; installs commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt/homebrew/bin/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;(older Intel Macs will have them installed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;the script is called with parameters passed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;argv&lt;/code&gt; so the file is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$@&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;the new file will have the same name as the original, but with a new .mp4 file extension&lt;/li&gt;
  &lt;li&gt;it only works on single files (read on)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; add an extra parameter to the command line to use hardware encoding (hevc_videotoolbox) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:v hevc_videotoolbox&lt;/code&gt;, which would be something like twice as fast, but this will not reduce the file size. So we stick with ffmpeg default settings.&lt;/p&gt;

&lt;p&gt;You can take the exact same approach with any destination format as it is decided by the file extension. Cool. You could convert any of ffmpeg’s supported file types to any other, such as WAV, OGG, MKV, etc.&lt;/p&gt;

&lt;p&gt;And as we’ll see later we can take this simple command and change it to use other command line tools to compress GIFs, with or without upscaling, and many other timesaving tasks. Powerful stuff!&lt;/p&gt;

&lt;h3 id=&quot;multiple-files&quot;&gt;Multiple files&lt;/h3&gt;

&lt;p&gt;To extend this approach for multiple files you can use the following for loop:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;multiple-files-in-parallel&quot;&gt;Multiple files in parallel&lt;/h3&gt;

&lt;p&gt;And to process those multiple files in parallel (be careful not to overwhelm your computer!) we can background each execution using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; symbol.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;f &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    /opt/homebrew/bin/ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%.*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.mp4&quot;&lt;/span&gt; &amp;amp;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;automator&quot;&gt;Automator&lt;/h2&gt;

&lt;p&gt;This has been a built-in macOS app since 2005. Setup is relatively straightforward:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a new &lt;em&gt;Quick Action&lt;/em&gt; workflow&lt;/li&gt;
  &lt;li&gt;Workflow receives current: &lt;em&gt;movie files&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;in: &lt;em&gt;Finder&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Add a new action of type action &lt;em&gt;Run Shell Script&lt;/em&gt; (search for it in the sidebar)&lt;/li&gt;
  &lt;li&gt;Pass input: &lt;em&gt;as arguments&lt;/em&gt; (this will give us a template command)&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Replace the echo line with your command&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Save&lt;/em&gt;, name it “Duplicate as MP4”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item is now available on the context menu, inside the &lt;em&gt;Quick Actions&lt;/em&gt; submenu. After repeated use I find this submenu too annoying, but you may fare better.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-automator-finder.png&quot; alt=&quot;PNG&quot; title=&quot;Automator Quick Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So, pretty easy! But, there are some caveats or limitations that may, or may not, annoy you. Perhaps we don’t want the menu item to appear for such a broad range of files (all “movie files”), or maybe we want it to appear for multiple types of files (both “audio files” and “movie files”). Sadly this is not so easy with Automator.&lt;/p&gt;

&lt;p&gt;Automator stores these files at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Library/Services/&lt;/code&gt; and they will migrate to a new Mac.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;contextmenu&quot;&gt;ContextMenu&lt;/h2&gt;

&lt;p&gt;Many years ago I found &lt;a href=&quot;https://apps.apple.com/us/app/context-menu/id1236813619?mt=12&quot;&gt;ContextMenu&lt;/a&gt; ($4.99) which solves all of the issues I have with the Automator approach. There’s also a free version, &lt;a href=&quot;https://apps.apple.com/gb/app/context-menu-lite/id1261373706?mt=12&quot;&gt;ContextMenu Lite&lt;/a&gt;, that supports up to 3 actions to give you a taste of the good stuff.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Submenu is optional&lt;/li&gt;
  &lt;li&gt;Apply to multiple types (files or directories; can be as granular as file extension)&lt;/li&gt;
  &lt;li&gt;Show output (sometimes you want to see the results of the command)&lt;/li&gt;
  &lt;li&gt;Confirmation before running (if it’s a potentially dangerous operation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-setup.png&quot; alt=&quot;PNG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The menu item displays in Finder as follows:&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/take-command-context-menu-finder.png&quot; alt=&quot;PNG&quot; title=&quot;ContextMenu Action in Finder Context Menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ContextMenu stores these files in its own group folder, right click an action and choose &lt;em&gt;Show in Finder&lt;/em&gt;, but you can specify your own folder in a location of your choice. Both will migrate to a new Mac, but the later might give you more control.&lt;/p&gt;

&lt;p&gt;Edit: we can limit actions to running on files with specific extensions or, since update 1.4.4 &lt;a href=&quot;https://gist.github.com/RhetTbull/7221ef3cfd9d746f34b2550d4419a8c2&quot;&gt;specific UTI types&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;compressing-a-gif&quot;&gt;Compressing a GIF&lt;/h2&gt;

&lt;p&gt;We can use the &lt;a href=&quot;https://www.lcdf.org/gifsicle/&quot;&gt;gifsicle&lt;/a&gt; command line tool to optimise (compress) an Animated GIF. The a parameters I use here are explained in the &lt;a href=&quot;https://www.lcdf.org/gifsicle/man.html&quot;&gt;gifsicle man page&lt;/a&gt;, and I arrived at them with &lt;a href=&quot;/2016/06/16/post-processing-animated-gifs/&quot;&gt;years of experimentation&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also do the same thing whilst scaling it up by a factor of 2:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/opt/homebrew/bin/gifsicle &lt;span class=&quot;nt&quot;&gt;-O1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-Okeep-empty&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--careful&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--scale&lt;/span&gt; 2 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@%.gif&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.o.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;repository-of-contextmenu-actions&quot;&gt;Repository of ContextMenu Actions&lt;/h2&gt;

&lt;p&gt;Here’s my repo of shared actions. Inside each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.cmaction&lt;/code&gt; file is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.sh&lt;/code&gt; file which contains the command that you can use in Automator if you’d like.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/gingerbeardman/contextmenu-actions&quot;&gt;github.com/gingerbeardman/contextmenu-actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These include many useful commands such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Convert BIN+CUE to ISO&lt;/li&gt;
  &lt;li&gt;Change sample rate of audio&lt;/li&gt;
  &lt;li&gt;Convert image to 1-bit&lt;/li&gt;
  &lt;li&gt;Convert image format&lt;/li&gt;
  &lt;li&gt;Create Clean ZIP&lt;/li&gt;
  &lt;li&gt;Duplicate audio as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate image as alternative format&lt;/li&gt;
  &lt;li&gt;Duplicate movie as alternative format&lt;/li&gt;
  &lt;li&gt;File information&lt;/li&gt;
  &lt;li&gt;Generate images for web&lt;/li&gt;
  &lt;li&gt;Make script executable&lt;/li&gt;
  &lt;li&gt;Move file (includes helper app)&lt;/li&gt;
  &lt;li&gt;New file with clipboard&lt;/li&gt;
  &lt;li&gt;Open file with specific apps&lt;/li&gt;
  &lt;li&gt;Optimize GIF&lt;/li&gt;
  &lt;li&gt;Set GIF to loop infinitely&lt;/li&gt;
  &lt;li&gt;Show GIF info&lt;/li&gt;
  &lt;li&gt;Playdate Colorize IMG&lt;/li&gt;
  &lt;li&gt;Replace Existing App (warning)&lt;/li&gt;
  &lt;li&gt;Scale image to certain percentage&lt;/li&gt;
  &lt;li&gt;Set as MacJapanese encoding&lt;/li&gt;
  &lt;li&gt;Show hashes (includes helper app)&lt;/li&gt;
  &lt;li&gt;Stub out file (warning)&lt;/li&gt;
  &lt;li&gt;Touch file&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 30 Jul 2024 16:14:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/07/30/taking-command-of-the-context-menu-in-macos/</guid>
        </item>
      
    
      
        <item>
          <title>Adding your own multi-channel audio to Music app</title>
          <description>&lt;p&gt;I’m really enjoying Dolby Atmos, “spatial audio”, or “surround sound” as we used to call it. There is so much music in this format on streaming platforms, both new albums and classic albums. I wanted to listen to Björk’s first four albums in “spatial audio”, but they don’t currently exist in that format on streaming platforms. But I was reminded about DVD-Audio versions of these from back in the day, and I could find most of mine, so I figured I’d have a go of converting them myself whilst I’m waiting for Dolby Atmos versions to hit streaming.&lt;/p&gt;

&lt;p&gt;This blog post lists the three main steps needed to add your own multi-channel audio to Music.app (you might know it by its old name: iTunes) and have them be accessible across all of your devices thanks to Apple Music’s Cloud Library (you might know it by its old name: iCloud Music Library).&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;dvd-audiovideo&quot;&gt;DVD Audio/Video&lt;/h2&gt;

&lt;p&gt;Three albums I have were released on DVD and contain multi-channel audio in both DTS and Dolby Digital as DVD-Video, these are Debut, Post, and Homogenic. Vespertine is better mastered and was released as a disc that included DVD-Audio. Regardless, we can take the same approach for these two disc types: use &lt;a href=&quot;https://www.dvdae.com&quot;&gt;DVD Audio Extractor&lt;/a&gt; (the 30-day trial is OK) to extract the tracks to FLAC files:&lt;/p&gt;

&lt;p&gt;Settings:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Sample rate: Same as input&lt;/li&gt;
  &lt;li&gt;Channels: All 6 Channels&lt;/li&gt;
  &lt;li&gt;Bits per sample: 24-bit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll take a minute or two for the files to be generated.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel FLAC files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;aside-cloud-library&quot;&gt;Aside: Cloud Library&lt;/h2&gt;

&lt;p&gt;Music.app (formerly iTunes) doesn’t really go out of its way to support multi-channel audio users might add themselves. We won’t be creating Dolby Atmos tracks, but rather we’ll take advantage of a loop hole in Apple Music’s Cloud Library feature.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;https://www.quadraphonicquad.com/forums/threads/ripping-in-surround-for-apple-tv.33931/post-699285&quot;&gt;a post on the QuadrophonicQuad forum&lt;/a&gt; we know that we need to limit the bitrate to 256kb/s for it to be uploaded unmodified to our Cloud Library and sync to all our devices. Well, it turns out that trick also works with 768kb/s bitrate files, which is the same bitrate as Dolby Atmos releases on Apple Music. That’s good enough for me.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conversionencoding&quot;&gt;Conversion/Encoding&lt;/h2&gt;

&lt;p&gt;There are many GUI tools that you might use to do this but I’ve chose to use ffmpeg on the command line. Feel free to adapt these settings to your tool of choice. The most important property is the 768kb/s bitrate, as mentioned earlier.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do ffmpeg -i &quot;$n&quot; -vn -c:a aac_at -b:a 768k -sample_fmt s16 -ar 48000 &quot;$n.m4a&quot;; done&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Breaking down the command above:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for n in *.flac; do&lt;/code&gt; = loop through all *.flac files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i &quot;$n&quot;&lt;/code&gt; = input, using filename from for loop&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-vn&lt;/code&gt; = remove the video track&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c:a aac_at&lt;/code&gt; = use Apple’s AAC encoder&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-b:a 768k&lt;/code&gt; = target 768kb/s bitrate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-sample_fmt s16&lt;/code&gt; = 16-bit sample format&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ar 48000&lt;/code&gt; = 48KHz sample rate&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;$n.m4a&quot;&lt;/code&gt; = output, using filename from for loop, adding .m4a&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;; done&lt;/code&gt; = end for loop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully that makes sense.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: many multi-channel M4A files&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chapters&quot;&gt;Chapters&lt;/h2&gt;

&lt;p&gt;If the M4A audio file contains chapters they will need to be removed or the file will not be playable on iPhone. We can load it into &lt;a href=&quot;https://subler.org&quot;&gt;Subler&lt;/a&gt;, then select and delete the “Text Track”.&lt;/p&gt;

&lt;p&gt;The process above should not introduce them, but I’m mentioning it as I was repeatedly caught out by this on my journey to this solution. If you fail to remove it the track will either either upload but not be playable, error and not upload, or be matched rather than uploaded.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Result: multi-channel M4A file (modified)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;final-steps&quot;&gt;Final steps&lt;/h2&gt;

&lt;p&gt;Add the final files to Music.app on your desktop. At this point you can add artwork and other meta data, such as tagging them as “multi-channel” or similar.&lt;/p&gt;

&lt;p&gt;When you’re done make sure to choose &lt;em&gt;File &amp;gt; Library &amp;gt; Update Cloud Library&lt;/em&gt;. After they finish uploading, open the Music app on your iPhone and you’ll see the multi-channel files ready to play.&lt;/p&gt;

&lt;p&gt;These multi-channel files work wonderfully with AirPods Pro spatial audio head tracking!&lt;/p&gt;

&lt;hr /&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/music-multi-channel.heic&quot; alt=&quot;WEBP&quot; title=&quot;My own multi-channel audio in Music app on my iPhone&quot; /&gt;&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 09 May 2024 20:19:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2024/05/09/adding-your-own-multi-channel-audio-to-music-app/</guid>
        </item>
      
    
      
        <item>
          <title>Using game controllers and keyboards for custom shortcuts</title>
          <description>&lt;p&gt;Recently I’ve been following a trend in &lt;a href=&quot;https://www.thetechedvocate.org/what-is-a-macro-pad-and-what-do-you-use-it-for/&quot;&gt;macro-pads&lt;/a&gt;, specialised/bespoke keyboards that provide an easy way to trigger keyboard shortcuts. A host of small companies have flooded the market with modified Bluetooth numeric pads that target Procreate, and Figma have teamed up with Work Louder to create a branded keypad with additional jog and rotary dials.&lt;/p&gt;

&lt;p&gt;But none of these seem quite right for me. I have so many controllers and devices already it felt better to make use of what I have to hand.&lt;/p&gt;

&lt;p&gt;I’m encouraged to find that there are lots of options! I’m a macOS user and I also dabble with Windows on occasion, but I have no idea how this can be done well on Linux.&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/8bitdo-micro-shortcuts.jpg&quot; alt=&quot;8Bitdo Micro&quot; title=&quot;&amp;lt;em&amp;gt;8Bitdo Micro&amp;lt;/em&amp;gt; is marketed as a multi-tasking controller&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;controllers&quot;&gt;Controllers&lt;/h2&gt;

&lt;p&gt;An obvious choice for a device with multiple buttons is a game controller. In modern macOS it’s easy to pair Nintendo Switch controllers, and the JoyCon (left or right) is an ideal candidate for a hand-held shortcut device. Xbox and PlayStation controllers can also be paired but they are much larger. Wired or wireless controllers will work.&lt;/p&gt;

&lt;p&gt;You can even use a Wii remote using an adapter like the &lt;a href=&quot;https://www.mayflash.com/product/magic_ns_lite.html&quot;&gt;&lt;em&gt;Mayflash MAGIC-NS Lite&lt;/em&gt;&lt;/a&gt;. Or you might use more esoteric controllers with an adapter from Robert Dale Smith’s &lt;a href=&quot;https://controlleradapter.com&quot;&gt;Controller Adapter&lt;/a&gt; store. In fact, I use one of his adapters to get an old &lt;a href=&quot;https://x.com/gingerbeardman/status/1629936413801062403?s=20&quot;&gt;&lt;em&gt;Sony Jog Controller&lt;/em&gt;&lt;/a&gt; to act like a GameCube controller, which I then map to keyboard shortcuts using the methods below. The sky’s the limit!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;8Bitdo&lt;/em&gt; also have their &lt;a href=&quot;https://www.8bitdo.com/micro/&quot;&gt;&lt;em&gt;Micro&lt;/em&gt; controller&lt;/a&gt; which offers more buttons and slightly better one-handed ergonomics than a Switch JoyCon. This controller is really interesting as it can pose as a Switch Pro Controller, generic controller, or keyboard. This gives us even more options. 8Bitdo are aware of this market and have created this device to fit, including a bespoke mobile app, and they even go so far as to &lt;a href=&quot;https://www.8bitdo.com/micro/#content-1-9&quot;&gt;call out this use case in their marketing material&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;keyboards&quot;&gt;Keyboards&lt;/h2&gt;

&lt;p&gt;These could be cheap bluetooth numeric pads or other small keyboards. I’ve personally tried a &lt;a href=&quot;https://niwanetwork.org/wiki/Nintendo_Wireless_Keyboard&quot;&gt;&lt;em&gt;Nintendo Wireless Keyboard&lt;/em&gt;&lt;/a&gt; that came with Nintendo DS game &lt;em&gt;Pokémon Typing Adventure&lt;/em&gt;. And of course the 8Bitdo controller mentioned above has a keyboard mode. There are many macro-pads listed for sale online, with various numbers of keys and rotary dials. Wired or wireless keyboards will work, so get creative!&lt;/p&gt;

&lt;h2 id=&quot;remapping&quot;&gt;Remapping&lt;/h2&gt;

&lt;p&gt;The 8Bitdo controller has a specific app for iOS/Android that can change its keyboard mappings. That’s cool for those mobile devices, and the iOS version will even run on an M1 Mac. But we can achieve a more versatile solution by doing the remapping in software on the computer.&lt;/p&gt;

&lt;p&gt;The general concept is to use an app that will take an input from your device of choice and map it to a keyboard shortcut or some other action that you specify. There are many apps that do this, so I’ve limited my list below to those that offer one important feature: &lt;em&gt;per-app mapping&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This means you can set different shortcuts for each app you use, and they will change as you move between apps. Of course, it is best if this happens automatically to prevent any friction in the process.&lt;/p&gt;

&lt;p&gt;I might have a button set to “zoom in” or “zoom out” that will trigger slightly different shortcuts in my text editor than in my image editor, or I might have a button set to “primary tool” and it will trigger a tool in a different way depending on the design app I am currently using.&lt;/p&gt;

&lt;p&gt;My personal favourite app is &lt;a href=&quot;https://github.com/qibinc/JoyMapperSilicon&quot;&gt;JoyMapperSilicon&lt;/a&gt;, but we really are spoiled for choice.&lt;/p&gt;

&lt;h2 id=&quot;mouse&quot;&gt;Mouse&lt;/h2&gt;

&lt;p&gt;It’s also possible to use the analog sticks and gyro of a Switch JoyCon as a mouse using QJoyControl. Analog is straightforward and as you’d expect. But using gyro basically gives you an Air Mouse which is pretty amazing.&lt;/p&gt;

&lt;h2 id=&quot;apps&quot;&gt;Apps&lt;/h2&gt;

&lt;p&gt;You can sort the table by headings: name, cost, platform, how the per-app function works, relative ease-of-use, and whether it works with devices keyboard (key) or controller (joy) or both.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Name&lt;/th&gt;
        &lt;th&gt;Cost&lt;/th&gt;
        &lt;th&gt;Platform&lt;/th&gt;
        &lt;th&gt;Per‑app?&lt;/th&gt;
        &lt;th&gt;Easy?&lt;/th&gt;
        &lt;th&gt;Type&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://support.8bitdo.com/ultimate/micro.html&quot;&gt;8BitDo Ultimate Software&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;iOS/Android&lt;/td&gt;
        &lt;td&gt;Manual&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Key&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.autohotkey.com&quot;&gt;AutoHotKey&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;Windows&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://folivora.ai&quot;&gt;BetterTouchTool&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$10&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/gamepad-companion/id428799479?mt=12&quot;&gt;GamePad Companion&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$10&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Manual&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://apps.apple.com/gb/app/joykeymapper/id1511416593?mt=12&quot;&gt;JoyKeyMapper&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/qibinc/JoyMapperSilicon&quot;&gt;JoyMapperSilicon&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://joytokey.net/en/&quot;&gt;JoyToKey&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$7&lt;/td&gt;
        &lt;td&gt;Windows&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/zenangst/KeyboardCowboy&quot;&gt;Keyboard Cowboy&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Key&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.keyboardmaestro.com&quot;&gt;Keyboard Maestro&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$36&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://github.com/erikmwerner/QJoyControl&quot;&gt;QJoyControl&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;Free&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;No&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Joy&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;a href=&quot;https://www.usboverdrive.com&quot;&gt;USB Overdrive&lt;/a&gt;&lt;/td&gt;
        &lt;td&gt;$20&lt;/td&gt;
        &lt;td&gt;macOS&lt;/td&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Yes&lt;/td&gt;
        &lt;td&gt;Both&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

&lt;/div&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 23 Dec 2023 23:06:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/12/23/using-game-controllers-and-keyboards-for-custom-shortcuts/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/12/23/using-game-controllers-and-keyboards-for-custom-shortcuts/</guid>
        </item>
      
    
      
        <item>
          <title>Going back to the old (pre-X) Twitter iOS app</title>
          <description>&lt;p&gt;There are two main ways to do this. As of 2025 I recommend the first one, using a tweaked app, but I’ll leave the second one up for the sake of history as it still works, albeit more limited in use.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-a-tweaked-app&quot;&gt;Using a tweaked app&lt;/h2&gt;

&lt;p&gt;My current recommended way of doing this to sideload a tweaked version of the Twitter/X app.&lt;/p&gt;

&lt;p&gt;You’ll need a tweaked Twitter.ipa and the version I use is &lt;a href=&quot;https://github.com/ghl3m0n/FuckElon&quot;&gt;a version by ghl3m0n&lt;/a&gt; that also replaces all X branding with the bird.&lt;/p&gt;

&lt;p&gt;Tweaked apps have additional plugins and extensions added to them. The most common and useful tweak is BHTwitter, which will block ads and most bots whilst enabling some extra features. The only real downside to tweaked apps is that they don’t support deep linking like the original apps, but you can use the bundled Safari extensions and/or &lt;a href=&quot;https://www.opener.link&quot;&gt;Opener app&lt;/a&gt; to work around that.&lt;/p&gt;

&lt;p&gt;Then you will need to pick a method of installing, using &lt;a href=&quot;https://altstore.io&quot;&gt;AltStore&lt;/a&gt;, &lt;a href=&quot;https://appdb.to&quot;&gt;appdb&lt;/a&gt;, &lt;a href=&quot;https://sidestore.io&quot;&gt;SideStore&lt;/a&gt;, &lt;a href=&quot;https://github.com/khcrysalis/Feather&quot;&gt;Feather&lt;/a&gt;, &lt;a href=&quot;https://sideloadly.io&quot;&gt;Sideloadly&lt;/a&gt;, or similar. Depending which method you choose you may have to reinstall or reactivate the app from time to time.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;using-an-old-version-of-the-app&quot;&gt;Using an old version of the app&lt;/h2&gt;

&lt;p&gt;Here’s how you can downgrade to an older version of Twitter (pre-X changes) and install it in a way that means &lt;em&gt;it will not be automatically updated&lt;/em&gt;.&lt;/p&gt;

&lt;figure class=&quot;img-with-caption&quot;&gt;
&lt;picture&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.avif&quot; type=&quot;image/avif&quot; /&gt;
  &lt;source srcset=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.webp&quot; type=&quot;image/webp&quot; /&gt;
  &lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-installed.png&quot; alt=&quot;&quot; title=&quot;&quot; loading=&quot;lazy&quot; /&gt;
&lt;/picture&gt;
&lt;figcaption class=&quot;caption&quot;&gt;Twitter 9.66 (858339189) running on an iPhone Xs just now&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h3 id=&quot;downloading-the-old-version&quot;&gt;Downloading the old version&lt;/h3&gt;

&lt;p&gt;This is the trickiest part of the process as it requires installing some old software and following a guide. But don’t worry—it’s not that difficult! You’ll need access to an old version of iTunes (so it’s most easily done using Windows) and about 15 minutes to carry out the steps.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/qnblackcat/How-to-Downgrade-apps-on-AppStore-with-iTunes-and-Charles-Proxy&quot;&gt;Follow this great step-by-step tutorial&lt;/a&gt;. It might seem complicated but it is quite easy. A high level summary of what is involved in the guide is as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install old iTunes &amp;amp; Charles Proxy&lt;/li&gt;
  &lt;li&gt;Configure intercepting of the latest app download&lt;/li&gt;
  &lt;li&gt;Change the version of the app to be downloaded&lt;/li&gt;
  &lt;li&gt;Download the old version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;id 858339189 (version 9.66) predates the rebrand to X&lt;/li&gt;
  &lt;li&gt;id 848443565 (version 9.7.2) predates Blue&lt;/li&gt;
  &lt;li&gt;id 840768123 (version 8.56) predates Spaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result of this process is a completely legitimate .ipa file, tied to your Apple ID in exactly the same way that apps normally are. As such, the resulting file can be installed on your device and once installed would be no different to an app you’ve downloaded directly from the App Store. This also means that if you share your .ipa with somebody else they would need to log in to your account using your Apple ID to do so, which is undesirable. Best if they download their own! Send them this blog post.&lt;/p&gt;

&lt;h3 id=&quot;avoiding-updates&quot;&gt;Avoiding updates&lt;/h3&gt;

&lt;p&gt;If you install the .ipa file onto your phone using Finder, iTunes, Apple Configurator it will install just fine. But, because of the metadata that is included the .ipa, it will be checked for updates and automatically updated soon after installation. We need to go deeper.&lt;/p&gt;

&lt;p&gt;There is a little-known method of installing apps on an iOS device which will prevent it from being checked for updates. I discovered this method &lt;a href=&quot;/2016/07/19/how-to-prevent-an-individual-ios-app-from-updating-forever/&quot;&gt;back in 2016&lt;/a&gt; when I used it to downgrade the eBay and Gmail Inbox apps.&lt;/p&gt;

&lt;h3 id=&quot;installing-the-app&quot;&gt;Installing the app&lt;/h3&gt;

&lt;p&gt;Before we begin, you don’t need to remove the current app you’re using. But best to make sure to backup any data you need from it regardless! You may, or may not, need to login once the old version of the app is reinstalled.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copy your backup of .ipa somewhere where you can work on it&lt;/li&gt;
  &lt;li&gt;Open the .ipa with Archive Utility to decompress it&lt;/li&gt;
  &lt;li&gt;Expand the resulting folder until you go into the Payload folder&lt;/li&gt;
  &lt;li&gt;You’ll see Twitter.app (on macOS the icon has a “no entry sign” because it’s an iOS app)&lt;/li&gt;
  &lt;li&gt;Connect your device&lt;/li&gt;
  &lt;li&gt;Open Apple Configurator (you might need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an old version&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Double click on your device (missing this will mean you can’t do step 8)&lt;/li&gt;
  &lt;li&gt;Click Add &amp;gt; App &amp;gt; Choose from my Mac…&lt;/li&gt;
  &lt;li&gt;Confirm you want to overwrite the old app&lt;/li&gt;
  &lt;li&gt;Wait for the app to install on your device&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’re done!&lt;/p&gt;

&lt;p class=&quot;tofigure&quot;&gt;&lt;img src=&quot;https://cdn.gingerbeardman.com/images/posts/twitter-old-payload.png&quot; alt=&quot;PNG&quot; title=&quot;Locating the Twitter .ipa Payload&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;questions&quot;&gt;Questions&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why can’t I add the app to my phone?&lt;/strong&gt;
    Either you missed step 7, or you need to &lt;a href=&quot;https://archive.org/details/apple-configurator&quot;&gt;use an older version of Apple Configurator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does downloading this way work?&lt;/strong&gt;
    iTunes used to be able to download apps this way, so we’re just persuading it to download a particular version. The app is attached to your Apple ID and totally legitimate, no funny business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does installing this way work?&lt;/strong&gt;
    Installing the payload directly means the App Store app has no record of the app being installed so it does not check for updates to it. You can confirm this by going to the App Store page for the app, where it will show it is yet to be downloaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will the old app stop working at some point?&lt;/strong&gt;
    Eventually, yes. Older versions lack support for modern Twitter features but that can be a desirable feature depending on your point of view. But let’s enjoy the old experience for as long as we can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I get help to do this?&lt;/strong&gt;
    No, sorry. You need to do it yourself for various reasons. It will take less than 1 hour to read all the steps, then follow them one-by-one.&lt;/p&gt;

&lt;p&gt;Long live the bird!&lt;/p&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Thu, 17 Aug 2023 18:39:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2023/08/17/going-back-to-the-old-pre-x-twitter-ios-app/</guid>
        </item>
      
    
      
        <item>
          <title>Services for Mac OS X</title>
          <description>&lt;p&gt;I created some Services for Mac OS X to do useful things on selected text.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Calculate &amp;amp; Append&lt;/li&gt;
  &lt;li&gt;Calculate &amp;amp; Replace&lt;/li&gt;
  &lt;li&gt;Calculate &amp;amp; Speak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Edit, January 2026: over the following years I would create many more, most of which are in daily use.&lt;/p&gt;

&lt;hr /&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The Services menu in Mac OS X (from 10.6 onwards) lets you use features of one application while working in another. The Services menu is contextual, so it shows just the services appropriate for the application you’re using or content you’re viewing rather than all available services. You can access services with a right click of your mouse or Control-click of your trackpad. You can configure the menu to show only the services you want, and you can even create your own services using Automator.” —Apple&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;screenshot&quot;&gt;Screenshot&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/services/calculate-services.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/services/&quot;&gt;www.gingerbeardman.com/services/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 28 Aug 2009 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/08/28/services-for-mac-os-x/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/08/28/services-for-mac-os-x/</guid>
        </item>
      
    
      
        <item>
          <title>PNGenie image optimiser</title>
          <description>&lt;p&gt;PNGenie is lossless PNG optimiser for Mac OS X.&lt;/p&gt;

&lt;p&gt;A PNG file contains image related data. This data can be stored in a multitude of different ways and the file size will vary for each storage method—even with the same raw image data. Image files often have an attached resource fork that also artificially increases the size of the file.&lt;/p&gt;

&lt;p&gt;PNGenie optimises any given PNG file—trying out different methods of compression—with a goal of achieving the smallest file size. Smaller files are good for a number of number practical reasons as well as, obviously, saving disk space. Let’s say you want to reduce the size of data files belonging to an iPhone application, or you want to speed up loading of a website that uses PNG images, for example. Resource forks, gamma values and software-specific tags are automatically stripped, too.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/pngenie/processing.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/pngenie/&quot;&gt;www.gingerbeardman.com/pngenie/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Fri, 03 Jul 2009 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2009/07/03/pngenie-image-optimiser/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2009/07/03/pngenie-image-optimiser/</guid>
        </item>
      
    
      
        <item>
          <title>(iPhoto) Exif Cleaner</title>
          <description>&lt;p&gt;Digital cameras attach information to each photo you take. This information - called EXIF data - contains the time-stamp, aperture and shutter time amongst other things.&lt;/p&gt;

&lt;p&gt;However some brands of cameras also attach a lot of extraneous data. This is inconvenient for a number of reasons, but most of all the fact that iPhoto caches all this data in it’s Library file resulting in slower operation after each new import of photos. Affected camera brands include: Casio, Konica Minolta, Kyocera, Nikon and Pentax - amongst others.&lt;/p&gt;

&lt;h2 id=&quot;purpose&quot;&gt;Purpose&lt;/h2&gt;

&lt;p&gt;This application will seamlessly clean your photos of extraneous exif data prior to importing into iPhoto. This utility does not delete all exif data, just the extraneous MakerNote section and any blank sections. All remaining, standard exif data is left intact.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/exifcleaner/screenshot.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;download&quot;&gt;Download&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.gingerbeardman.com/exifcleaner/&quot;&gt;www.gingerbeardman.com/exifcleaner/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20061029083354/http://www.versiontracker.com/dyn/moreinfo/macosx/28734&quot;&gt;Version Tracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sat, 31 Dec 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/12/31/exif-cleaner/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/12/31/exif-cleaner/</guid>
        </item>
      
    
      
        <item>
          <title>Upcoming badge</title>
          <description>&lt;p&gt;Upcoming Badge is a widget for Mac OS X Dashboard.&lt;/p&gt;

&lt;p&gt;Use it to view your Upcoming.org events watch list using Dashboard.&lt;/p&gt;

&lt;p&gt;Simply enter your username on the flip side of the widget.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/upcoming/upcoming.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/upcoming/&quot;&gt;www.gingerbeardman.com/upcoming/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Tue, 22 Nov 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/11/22/upcoming-badge/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/11/22/upcoming-badge/</guid>
        </item>
      
    
      
        <item>
          <title>Ceefax Viewer</title>
          <description>&lt;p&gt;Ceefax Viewer is a widget for Mac OS X Dashboard.&lt;/p&gt;

&lt;p&gt;Ceefax (phonetic for “See Facts”) is the BBC’s teletext information service. Teletext is an information retrieval service provided by television broadcast companies. Teletext pages can be viewed on television sets with suitable decoders. They offer a range of text-based information, usually including national, international and sporting news, weather and TV schedules. Subtitle (or closed caption) information is also transmitted in the teletext signal.&lt;/p&gt;

&lt;p&gt;This widget will let you view Ceefax at any time using Dashboard. You do not need a TV but you will need Mac OS 10.4.3 or better and an internet connection.&lt;/p&gt;

&lt;p&gt;Click on the page numbers to navigate around the service. Use keyboard shortcuts for additional quick navigation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.gingerbeardman.com/ceefaxviewer/ceefaxviewer.png&quot; alt=&quot;Remote&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gingerbeardman.com/ceefaxviewer/&quot;&gt;www.gingerbeardman.com/ceefaxviewer/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
          <author>by Matt Sephton</author>
          <pubDate>Sun, 22 May 2005 12:00:00 +0000</pubDate>
          <link>https://blog.gingerbeardman.com/2005/05/22/ceefax-viewer/</link>
          <guid isPermaLink="true">https://blog.gingerbeardman.com/2005/05/22/ceefax-viewer/</guid>
        </item>
      
    

  </channel>
</rss>
