<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>notes from /dev/null - macos</title><link href="http://yummymelon.com/devnull/" rel="alternate"/><link href="http://yummymelon.com/devnull/feeds/tags/macos.atom.xml" rel="self"/><id>http://yummymelon.com/devnull/</id><updated>2025-12-30T15:00:00-08:00</updated><entry><title>F16 F17 F18</title><link href="http://yummymelon.com/devnull/f16-f17-f18.html" rel="alternate"/><published>2025-12-30T15:00:00-08:00</published><updated>2025-12-30T15:00:00-08:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2025-12-30:/devnull/f16-f17-f18.html</id><summary type="html">&lt;p&gt;Binding the function keys above the number pad to window management has been a total win for me. Perhaps it might be the same for you.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A significant user interface improvement I made to my desktop experience on macOS this past year has been to globally bind the function keys above the number pad on a full-sized keyboard to window management commands, in particular to move a window either to the center, left, or right of a large monitor. On reflection, this seemingly modest change has surprisingly transformed how I manage windows now. If this seems interesting, please read on.&lt;/p&gt;
&lt;p&gt;My usual work environment is setup like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Laptop in clamshell mode, connected to a single monitor, keyboard, and mouse.&lt;/li&gt;
&lt;li&gt;I have the setting “Use F1, F2, etc. keys as standard function keys” turned on.&lt;/li&gt;
&lt;li&gt;For my keyboard, I use a stock Apple USB full sized keyboard (A1243) that has the row of function keys F16-F19 on the top row of the number pad as shown in the image below.&lt;/li&gt;
&lt;/ul&gt;
&lt;p align='center'&gt;
&lt;img src='http://yummymelon.com/devnull/images/f16f17f18/IMG_7614_resize_x425.jpeg' alt='Photo of number pad of Apple A1243 keyboard.'  /&gt;
&lt;/p&gt;

&lt;p&gt;The following table specifies the bindings I use for these number pad function keys.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left;"&gt;Key&lt;/th&gt;
&lt;th style="text-align: left;"&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;F16&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Move to top left, without resizing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;F17&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Center&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left;"&gt;F18&lt;/td&gt;
&lt;td style="text-align: left;"&gt;Move to top right, without resizing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These bindings can be implemented using a 3rd party app like &lt;a href="https://apps.apple.com/us/app/bettersnaptool/id417375580?mt=12"&gt;‎BetterSnapTool&lt;/a&gt; or by configuring a keyboard shortcut via the macOS Settings app. As a happy BetterSnapTool user, I’ve configured it as shown below:&lt;/p&gt;
&lt;p align='center'&gt;
&lt;img src='http://yummymelon.com/devnull/images/f16f17f18/bettersnaptool-config.png' alt='Screenshot of BetterSnapTool Shortcuts panel.' /&gt;
&lt;/p&gt;

&lt;p&gt;Users wanting to use what is built into macOS (Sequoia 15 or greater) can go to &lt;code&gt;System Settings… › Keyboard › Keyboard Shortcuts… › Windows&lt;/code&gt; to customize the bindings to their preference.&lt;/p&gt;
&lt;p align='center'&gt;
&lt;img src='http://yummymelon.com/devnull/images/f16f17f18/macos-settings-windows.png' alt='Screenshot of Windows Keyboard shortcut panel in macOS Settings app.' /&gt;
&lt;/p&gt;

&lt;h1&gt;Closing Thoughts&lt;/h1&gt;
&lt;p&gt;If you’ve followed my work on &lt;a href="https://kickingvegas.github.io/casual/"&gt;Casual&lt;/a&gt; you'll know that I’m not a fan of long key bindings that take up three or more keys to be pressed. Dedicating function keys to do basic window management has been far simpler to adopt in my interactions with a computer. It seems obvious with 20/20 hindsight, but simply centering a window with a single key press has done wonders for my ergonomics.&lt;/p&gt;
&lt;p&gt;I acknowledge that this post is bespoke to me. If you don’t have a lot of function keys to spare, you'll likely find little of value here. But if you do have three spare function keys, I highly recommend binding them to window management commands. Give it a try - I think you’ll be pleasantly surprised.&lt;/p&gt;</content><category term="misc"/><category term="macos"/><category term="ux"/></entry><entry><title>Capturing an Org note via macOS Shortcuts</title><link href="http://yummymelon.com/devnull/capturing-an-org-note-via-macos-shortcuts.html" rel="alternate"/><published>2025-07-02T14:30:00-07:00</published><updated>2025-07-02T14:30:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2025-07-02:/devnull/capturing-an-org-note-via-macos-shortcuts.html</id><summary type="html">&lt;p&gt;Fun with macOS Shortcuts and Org protocol capture.&lt;/p&gt;</summary><content type="html">&lt;p&gt;With a new update of &lt;a href="https://github.com/kickingvegas/scrim/releases/tag/scrim_1.1.0.23"&gt;Scrim v1.1.0&lt;/a&gt; on the &lt;a href="https://apps.apple.com/us/app/scrim/id6744040981"&gt;App Store&lt;/a&gt;, it seems an opportune time to show a nice macOS Shortcut integration with it.&lt;/p&gt;
&lt;p&gt;The use case is this: You're on your Mac, working on something else &lt;em&gt;other&lt;/em&gt; than Emacs (it happens!) and you want to make a quick note in an Org file without having to context switch to Emacs.&lt;/p&gt;
&lt;p&gt;You can accomplish this with a &lt;a href="https://support.apple.com/guide/shortcuts-mac/intro-to-shortcuts-apdf22b0444c/mac"&gt;macOS Shortcut&lt;/a&gt;. Shortcuts is an Apple tool that lets you orchestrate different apps to achieve a custom workflow.&lt;/p&gt;
&lt;p&gt;Shown below is the shortcut named “Make Note Entry” to accomplish the above use case. It will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prompt the user to enter text.&lt;/li&gt;
&lt;li&gt;Run a Python script on that text and construct an Org protocol capture request with it using the template named "note".&lt;/li&gt;
&lt;li&gt;Issue (open) the Org protocol request.&lt;/li&gt;
&lt;/ol&gt;
&lt;p align="center"&gt;
&lt;img src="http://yummymelon.com/devnull/images/capture-org-note-shortcuts/org-note-shortcut_resize_1280x.png" width="60%"/&gt;
&lt;/p&gt;

&lt;p&gt;If the shortcut is configured so that “Pin in Menu Bar” is turned on, you will see some variant of its entry in the Shortcuts bar icon menu.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="http://yummymelon.com/devnull/images/capture-org-note-shortcuts/org-note-shortcut-menu_resize_320x.png" /&gt;
&lt;/p&gt;

&lt;p&gt;Running the shortcut “Make Note Entry” will prompt you with a dialog where you can enter your note:&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="http://yummymelon.com/devnull/images/capture-org-note-shortcuts/org-note-shortcut-window_resize_500x.png" /&gt;
&lt;/p&gt;

&lt;p&gt;Pressing the “Done” button will result in the entry being inserted at the top of the file “~/org/notes.org” as shown below. Note however that Emacs is not raised with this shortcut. The screenshot below is only shown to illustrate that the entry is there.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="http://yummymelon.com/devnull/images/capture-org-note-shortcuts/org-note-shortcut-emacs_resize_1280x.png" width="60%" /&gt;
&lt;/p&gt;

&lt;h1&gt;Installation&lt;/h1&gt;
&lt;p&gt;Want to try this out? Here’s what to do:&lt;/p&gt;
&lt;h2&gt;1. Understand Org protocol&lt;/h2&gt;
&lt;p&gt;If you don’t already then read up about it at &lt;a href="https://orgmode.org/worg/org-contrib/org-protocol.html"&gt;org-protocol.el – Trigger actions in Emacs via a custom URL scheme&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;2. Have Scrim installed and configured&lt;/h2&gt;
&lt;p&gt;Learn more about Scrim at &lt;a href="http://yummymelon.com/scrim"&gt;http://yummymelon.com/scrim&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;3. Add the capture template “note”&lt;/h2&gt;
&lt;p&gt;On the Emacs side, add the following template entry to &lt;code&gt;org-capture-templates&lt;/code&gt;. This template named “note” defines an entry with a heading that has an inactive Org timestamp (“%U”) and the body containing the entered text (“%i”). The destination of this note entry is in the file “~/org/notes.org”&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;note&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Note (Org Protocol)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;entry&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;~/org/notes.org&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string-join&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;* %U&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%i&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;:prepend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;:immediate-finish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;:empty-lines-after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Observe that this template uses the keys &lt;code&gt;:immediate-finish&lt;/code&gt; to not make the entry interactive and &lt;code&gt;:prepend&lt;/code&gt; to insert the entry at the top of the file.&lt;/p&gt;
&lt;h2&gt;4. Add the Shortcut “Make Note Entry”&lt;/h2&gt;
&lt;p&gt;Download the shortcut file &lt;a href="http://yummymelon.com/devnull/images/capture-org-note-shortcuts/Make Note Entry.shortcut"&gt;Make Note Entry.shortcut&lt;/a&gt; and double-click on it to install. It should now be in your Shortcuts collection.&lt;/p&gt;
&lt;h2&gt;5. Run “Make Note Entry”&lt;/h2&gt;
&lt;p&gt;On the first run of this shortcut, you will be prompted twice to give it permissions: 1) to run the shortcut and 2) to open Scrim. Inspect that its contents correspond with its screenshot above and grant them.&lt;/p&gt;
&lt;h1&gt;Closing Thoughts&lt;/h1&gt;
&lt;p&gt;Readers are invited to take these ideas and customize them for their own purposes.&lt;/p&gt;</content><category term="misc"/><category term="emacs"/><category term="org mode"/><category term="macos"/></entry><entry><title>Migrating MacPorts in 2024</title><link href="http://yummymelon.com/devnull/migrating-macports-in-2024.html" rel="alternate"/><published>2024-09-17T15:30:00-07:00</published><updated>2024-09-17T15:30:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2024-09-17:/devnull/migrating-macports-in-2024.html</id><summary type="html">&lt;p&gt;MacPorts now supports built-in migration. Finally.&lt;/p&gt;</summary><content type="html">&lt;p&gt;At long last, MacPorts has &lt;a href="https://trac.macports.org/wiki/Migration"&gt;built-in migration support&lt;/a&gt; for whenever there’s a major macOS upgrade. I learned of this today thanks to &lt;a href="https://infosec.exchange/@harrysintonen/113152168770957511"&gt;Harry Sintonen’s Mastodon post&lt;/a&gt;. You’ll need to be running a version of &lt;code&gt;port&lt;/code&gt; that is ≥ 2.10.0.&lt;/p&gt;
&lt;p&gt;Migration now (at least on paper) is much simpler. After updating your version of the &lt;strong&gt;Xcode&lt;/strong&gt; command line tools, you just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; $ sudo port migrate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before 2.10.0, migration required a &lt;strong&gt;lot more&lt;/strong&gt; hand-holding. It was easily one of my least favorite sys-admin things to do.&lt;/p&gt;
&lt;p&gt;Staying true to my general policy of migrating, I’m waiting until the release of macOS Sequoia 15.1 before migrating MacPorts. I've elaborated more about this in an &lt;a href="http://yummymelon.com/devnull/administering-macports.html"&gt;earlier post&lt;/a&gt; which I highly recommend for those new to MacPorts.&lt;/p&gt;
&lt;p&gt;My most heartfelt thanks to all who are shaking things out to get the MacPorts ecosystem stable until then.&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://yummymelon.com/devnull/administering-macports.html"&gt;Administering MacPorts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/><category term="#macos"/></entry><entry><title>An accidental lock-in feature of the Apple ecosystem</title><link href="http://yummymelon.com/devnull/an-accidental-lock-in-feature-of-the-apple-ecosystem.html" rel="alternate"/><published>2024-06-12T01:25:00-07:00</published><updated>2024-06-12T01:25:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2024-06-12:/devnull/an-accidental-lock-in-feature-of-the-apple-ecosystem.html</id><summary type="html">&lt;p&gt;All the flowers to the developers who keep Emacs keybindings alive in Apple products.&lt;/p&gt;</summary><content type="html">&lt;p&gt;It’s &lt;a href="https://developer.apple.com/wwdc24/"&gt;WWDC&lt;/a&gt; week, when Apple shines their spotlight on its developer ecosystem every June. Given the moment, I’d like take this opportunity to give all the flowers to the unsung developers who have built and maintained a feature in Apple products that I hold quite dear: the Emacs keybindings in Apple UI frameworks.&lt;/p&gt;
&lt;p&gt;If you don’t know, when using a macOS native app like Notes, OmniGraffle, or Safari, you can use the following keybindings in any text field:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C-a&lt;/code&gt; - move cursor to beginning of line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-e&lt;/code&gt; - move cursor to end of line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-f&lt;/code&gt; - move cursor forward one character&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-b&lt;/code&gt; - move cursor backward one character&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-k&lt;/code&gt; - kill text to end of line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-y&lt;/code&gt; - yank killed text (mostly, more on this later)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It can not be overstated how extraordinarily convenient this feature is to Emacs users.  Even more astonishing is how this set of bindings has continued to be maintained (since 2001!) across the different OS variants Apple ships (macOS, iOS, iPadOS, tvOS).&lt;/p&gt;
&lt;h1&gt;Backstory&lt;/h1&gt;
&lt;p&gt;My anecdotal understanding of this feature’s history is that it started with the NextStep OS back in 1988. The developers of NextStep used Emacs and so whenever they typed in the text widgets of the NextStep UI, they naturally wanted to use Emacs bindings. It was unlikely that this was ever an exec-level requirement. But the developers wanted it, &lt;em&gt;so they made it happen&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Fast forward in time, &lt;a href="http://yummymelon.com/devnull/next-is-the-reason-why-you-have-your-iphone.html"&gt;NeXT&lt;/a&gt; effectively acquires Apple and in doing so repackages the NextStep frameworks to ship Mac OS X (now called macOS). This decision fatefully keeps NextStep’s text system and its conventions intact in the UI framework called AppKit as part of family of frameworks called Cocoa. Fast-forward to 2007, the iPhone becomes a thing. Apple makes another fateful decision to build iPhone OS (now called iOS) off of Cocoa but create a new touch-based UI framework called UIKit. UIKit’s text system is heavily influenced by Cocoa where it too carries over the Emacs keybindings. Variants of iOS in the form of iPadOS and tvOS come to be, further propagating these keybindings. In 2019, a declarative UI framework called SwiftUI is created where its text system too adopts the Emacs keybindings.&lt;/p&gt;
&lt;h1&gt;Configuring Keybindings in macOS&lt;/h1&gt;
&lt;p&gt;Wonderfully enough, you can still configure the keybindings in AppKit-based apps on macOS using the NextStep style configuration as shown in the &lt;a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/EventOverview/TextDefaultsBindings/TextDefaultsBindings.html#//apple_ref/doc/uid/20000468-611005"&gt;Text System Defaults and Key Bindings&lt;/a&gt; document. There it provides guidance on adding more Emacs-style keybindings.&lt;/p&gt;
&lt;p&gt;Of note is that macOS has always supported an alternate clipboard to emulate the Emacs kill-ring. This works independent of the system clipboard, and requires a bit of configuration to get working. There are two steps to this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redefine the keybinding &lt;code&gt;C-y&lt;/code&gt; to &lt;code&gt;yankAndSelect:&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the file &lt;code&gt;$HOME/Library/KeyBindings/DefaultKeyBinding.dict&lt;/code&gt; (for details see the link above), add the following line in the body between the braces.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="s2"&gt;&amp;quot;^y&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;yankAndSelect:&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Use the command line utility &lt;code&gt;defaults&lt;/code&gt; to set the variable &lt;code&gt;NSTextKillRingSize&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This variable controls the size of the kill ring. Here we will set it to 5.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;defaults&lt;span class="w"&gt; &lt;/span&gt;write&lt;span class="w"&gt; &lt;/span&gt;NSGlobalDomain&lt;span class="w"&gt; &lt;/span&gt;NSTextKillRingSize&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can confirm the value in &lt;code&gt;NSTextKillRingSize&lt;/code&gt; as follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;defaults&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;NSTextKillRingSize
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Refer to &lt;code&gt;man defaults&lt;/code&gt; for more information on it.&lt;/p&gt;
&lt;p&gt;At this point, you will need to log out and back in for the new keybindings to take effect.&lt;/p&gt;
&lt;h1&gt;Caveats&lt;/h1&gt;
&lt;p&gt;You can’t have everything though as not all apps are built with AppKit. Focusing on only the default Emacs bindings, here’s a survey of the different levels of support for them from the different UI frameworks.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Binding&lt;/th&gt;
&lt;th style="text-align: center;"&gt;AppKit/SwiftUI (macOS)&lt;/th&gt;
&lt;th style="text-align: center;"&gt;UIKit/SwiftUI (iOS, iPadOS)&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Mac Catalyst&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-a&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-e&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-f&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-b&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-k&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;C-y&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Y&lt;/td&gt;
&lt;td style="text-align: center;"&gt;N&lt;/td&gt;
&lt;td style="text-align: center;"&gt;N&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Unsurprisingly, UIKit chose to not support a kill ring, so effectively &lt;code&gt;C-k&lt;/code&gt; is a delete function and yanking is not supported. The column for Mac Catalyst is a category of apps built to work for both iPadOS and macOS based largely on UIKit. Such apps include Messages and Stocks.&lt;/p&gt;
&lt;h1&gt;Closing Thoughts&lt;/h1&gt;
&lt;p&gt;I’ll never take for granted the repeated decision over the years by the Apple text system developers to keep supporting the Emacs keybindings. While perhaps not intended, this feature quite seriously is an ecosystem &lt;a href="https://en.wikipedia.org/wiki/Vendor_lock-in"&gt;lock-in&lt;/a&gt; factor for me. It is a gift and to some extent, a secret handshake, that I’ll always be grateful for. To whoever was, is, or will be involved with keeping this feature alive, thank you.&lt;/p&gt;</content><category term="misc"/><category term="emacs"/><category term="ios"/><category term="computer"/><category term="macos"/></entry><entry><title>Administering MacPorts</title><link href="http://yummymelon.com/devnull/administering-macports.html" rel="alternate"/><published>2024-01-31T16:00:00-08:00</published><updated>2024-01-31T16:00:00-08:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2024-01-31:/devnull/administering-macports.html</id><summary type="html">&lt;p&gt;Some learnings I've made using MacPorts for the past two decades.&lt;/p&gt;</summary><content type="html">&lt;p&gt;When it comes to package managers for macOS, I’ve started with&lt;sup&gt;&lt;a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink"&gt;1&lt;/a&gt;&lt;/sup&gt; and continue to use &lt;a href="https://www.macports.org/"&gt;MacPorts&lt;/a&gt; whenever I can. The reasons for this are largely in line with what Saagar Jha captured in his &lt;a href="https://saagarjha.com/blog/2019/04/26/thoughts-on-macos-package-managers/"&gt;blog post from 2019&lt;/a&gt; and little since then has changed to convince me otherwise. Over the past two decades (!) I’ve come to some learnings about administering MacPorts, which I’ve captured in this post.&lt;/p&gt;
&lt;h1&gt;It is not enough to have Xcode installed. You should also install the separate command line tools before installing MacPorts.&lt;/h1&gt;
&lt;p&gt;In particular, Apple puts certain headers and libraries required by some open-source software into the command line tools package, not in &lt;code&gt;Xcode*.xip&lt;/code&gt;. More often than not you’ll want to install software that requires said headers and libraries. Save yourself the guesswork and install the command line tools anyways.&lt;/p&gt;
&lt;p&gt;You can do this by running &lt;code&gt;xcode-select&lt;/code&gt; in the Terminal.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;xcode-select&lt;span class="w"&gt; &lt;/span&gt;--install
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;At its core, MacPorts is a 90’s-style solution to package management.&lt;/h1&gt;
&lt;p&gt;The design of MacPorts is heavily influenced by the &lt;a href="https://en.wikipedia.org/wiki/FreeBSD_Ports"&gt;Ports&lt;/a&gt; system from FreeBSD. Ports was designed to solve a 90’s-style problem in package management: How do you distribute for many different Unix variants either from different vendors or different OS versions? Ports took this tack: Download the source, configure for the local environment, and build. That said, since 2011 MacPorts can ship &lt;a href="https://trac.macports.org/wiki/FAQ#fromsource"&gt;binaries&lt;/a&gt; to speed things up but only under certain conditions, particularly with regards to CPU architecture, OS version, and licensing.&lt;/p&gt;
&lt;p&gt;Probably the biggest benefit of local building from source is the higher likelihood that you will be running an executable that is &lt;em&gt;tuned&lt;/em&gt; to your system (CPU architecture and OS version). On older versions of macOS, MacPorts will likely let you build and run the latest version of an open-source tool. For years I had a 2010 iMac with macOS 10.13.6 High Sierra that could still run the latest versions of open-source tools, extending its useful lifetime as a development system.&lt;/p&gt;
&lt;p&gt;Probably the biggest frustration of building from source is dealing with package dependencies. Installing a package with many dependencies (looking at you &lt;code&gt;ffmpeg&lt;/code&gt;) can feel like MacPorts is building all the things, because it is.&lt;/p&gt;
&lt;h1&gt;OS upgrading is a deep operation in MacPorts.&lt;/h1&gt;
&lt;p&gt;Because MacPorts tries to tune itself to a particular CPU and OS version, whenever there is a macOS upgrade, the recommended guidance is to &lt;a href="https://trac.macports.org/wiki/Migration"&gt;migrate&lt;/a&gt; your install of MacPorts. Effectively this means &lt;em&gt;reinstalling&lt;/em&gt; your packages for every OS upgrade.&lt;/p&gt;
&lt;p&gt;Pragmatically there are a couple of gotchas with this.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At the time of the macOS release, not necessarily all the software packages have been updated to support it. More often than not, rebuilding the package will break.&lt;/li&gt;
&lt;li&gt;You will need to upgrade Xcode and the command line tools that go along with the OS upgrade before MacPorts migration.&lt;/li&gt;
&lt;li&gt;There are a lot of steps involved with MacPorts migration. For users (particularly those used to primarily GUI driven flows) this can be both cumbersome and error-prone. (Why this step has not yet been automated still bewilders me.)&lt;/li&gt;
&lt;li&gt;If you use a shell provided by MacPorts (configured using &lt;code&gt;chsh&lt;/code&gt;) then you must remember to revert back to the default macOS installed shell (typically &lt;code&gt;/bin/zsh&lt;/code&gt;) before MacPorts migration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Being burned multiple times over the years with the above has led me to this guidance: &lt;em&gt;Only upgrade the OS and migrate MacPorts when the .1 OS release comes out.&lt;/em&gt; While by no means bulletproof, this strategy gives you a higher likelihood of success in migrating MacPorts to the upgraded OS. Admittedly there are years when you &lt;em&gt;must&lt;/em&gt; upgrade the OS and Xcode before the .1, particularly if you are an iOS developer. Under such circumstances, you can defer the migration until the .1 OS release as everything in &lt;code&gt;/opt/local/bin&lt;/code&gt; should still work.&lt;/p&gt;
&lt;h2&gt;Addendum: 17 September 2024&lt;/h2&gt;
&lt;p&gt;MacPorts finally has &lt;a href="http://yummymelon.com/devnull/migrating-macports-in-2024.html"&gt;built-in migration support&lt;/a&gt;!&lt;/p&gt;
&lt;h1&gt;Reporting Broken Ports&lt;/h1&gt;
&lt;p&gt;In the event you find a port to be broken, by all means &lt;a href="https://guide.macports.org/#project"&gt;report&lt;/a&gt; it. Over the years I’ve found MacPorts maintainers to be attentive and I am grateful for it.&lt;/p&gt;
&lt;h1&gt;MacPorts was meant to be run by a system administrator.&lt;/h1&gt;
&lt;p&gt;In the before-time, most deployed Unix systems were multi-user with the responsibilities of system administration given to a separate individual or organization. That system administrator could dedicate the time and resources to manage all of the above, leaving users the benefit of just using the tools installed. With individual ownership of macOS systems being predominant, such users who use MacPorts are forced to wear a sysadmin hat.&lt;/p&gt;
&lt;p&gt;I’d observe that among macOS users, almost no one wants, much less cares to be a Unix sysadmin. That includes those who use Macs for Apple (iOS, macOS) development.&lt;/p&gt;
&lt;p&gt;If you're going to use MacPorts, expect to be a sysadmin.&lt;/p&gt;
&lt;h1&gt;Why not use Homebrew?&lt;/h1&gt;
&lt;p&gt;Some readers might say, with all of the friction described above, why not use Homebrew? I would argue that Homebrew tends to sweep the details of OS upgrading and package migration under the rug. Homebrew tries too much to give you the illusion that installing open source tools is akin to installing a mobile app. But when things go invariably sideways, you are at the mercy of the policies of their package maintainers.&lt;/p&gt;
&lt;p&gt;As a general rule, you trade-off control and optimization for convenience with Homebrew. With MacPorts, it’s the opposite. MacPorts doesn’t try to hide the responsibility of system administration from you; Homebrew does. For example, this blog post describes how Homebrew’s update policies created &lt;a href="https://justinmayer.com/posts/homebrew-python-is-not-for-you/"&gt;unpleasant surprise&lt;/a&gt; to the user when upgrading Python.&lt;/p&gt;
&lt;p&gt;In the end, I prefer to have more control, despite the extra work it entails. MacPorts gives me this.&lt;/p&gt;
&lt;h1&gt;Closing Thoughts&lt;/h1&gt;
&lt;p&gt;My deepest thanks to the maintainers and contributors to MacPorts; this is software that I’ve been using for the past two decades strong. Hopefully my learnings will be of use to new MacPorts users and perhaps provide clarification for existing users as well.&lt;/p&gt;
&lt;h1&gt;Footnotes&lt;/h1&gt;
&lt;p&gt;&lt;sup&gt;&lt;a id="fn.1" href="#fnr.1"&gt;1&lt;/a&gt;&lt;/sup&gt; My first install of MacPorts goes all the way back to a 12” Powerbook G4 in 2003.&lt;/p&gt;</content><category term="misc"/><category term="macOS"/></entry><entry><title>Using Emacs to make phone calls and lookup map places on macOS</title><link href="http://yummymelon.com/devnull/using-emacs-to-make-phone-calls-and-lookup-map-places-on-macos.html" rel="alternate"/><published>2023-06-20T11:13:00-07:00</published><updated>2023-06-20T11:13:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2023-06-20:/devnull/using-emacs-to-make-phone-calls-and-lookup-map-places-on-macos.html</id><summary type="html">&lt;p&gt;Selecting text to make a phone call or map a location in a native macOS app is common. You can do this with Emacs too.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I admit, I do take a perverse delight in making phone calls from my computer. On macOS, this can be achieved via the &lt;a href="https://support.apple.com/en-us/HT209456"&gt;iPhone Cellular Calls&lt;/a&gt; feature, which
proxies your computer to your iPhone&amp;rsquo;s calling capability. Tied to this feature is the ability to select a phone number in a native macOS app text field and use it to dial that number direct. An example is shown below from the &lt;strong&gt;Safari&lt;/strong&gt; app.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://yummymelon.com/devnull/images/phone-call-macos-example.png" style='border-style: groove;' width='30%' alt='screenshot of selected phone number in Safari with phone call context menu' /&gt;&lt;/p&gt;
&lt;p&gt;To dial a number, the app translates the human-formatted phone number into a &lt;code&gt;tel:&lt;/code&gt; &lt;a href="https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/PhoneLinks/PhoneLinks.html#/apple_ref/doc/uid/TP40007899-CH6-SW1"&gt;URL scheme&lt;/a&gt; that is then used to open the &lt;strong&gt;FaceTime&lt;/strong&gt; app to make the call.&lt;/p&gt;
&lt;p&gt;We can emulate this behavior in &lt;strong&gt;Emacs&lt;/strong&gt;. This post will show you how.&lt;/p&gt;
&lt;h1&gt;Making a Phone Call&lt;/h1&gt;
&lt;p&gt;First off is parsing the human-formatted phone number. We can do this by writing some regexps. Since I live in North America, I&amp;rsquo;ve chosen to use regexps that conform to the &lt;a href="https://en.wikipedia.org/wiki/North_American_Numbering_Plan"&gt;North American Numbering Plan&lt;/a&gt; (NANP). NANP  formats phone numbers into the form &lt;code&gt;+1 (xxx) xxx-xxxx&lt;/code&gt;. I've also chosen to make regexps that also accepts spaces and &amp;ldquo;.&amp;rdquo; as separators.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defvar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp-international&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;^+1 \&lt;/span&gt;
&lt;span class="s"&gt;[(]*\\([0-9]\\{3\\}\\)[)]*\&lt;/span&gt;
&lt;span class="s"&gt;[\\. -]\\([0-9]\\{3\\}\\)[\\. -]\\([0-9]\\{4\\}\\)$&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Regexp for North American Numbering Plan phone number including +1.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defvar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;^[(]*\\([0-9]\\{3\\}\\)[)]*[\\. -]\&lt;/span&gt;
&lt;span class="s"&gt;\\([0-9]\\{3\\}\\)[\\. -]\\([0-9]\\{4\\}\\)$&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Regexp for North American Numbering Plan phone number without +1.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the above I&amp;rsquo;ve defined two separate regexps with capture groups, one to handle numbers starting with &lt;code&gt;+1&lt;/code&gt; and the other for those without it. &lt;/p&gt;
&lt;p&gt;From these regexps we can write the following function to convert a phone number to a &lt;code&gt;tel:&lt;/code&gt; URL.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/nanp-phone-number-to-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Convert PHONE number string to url \&amp;quot;tel:\&amp;quot;.&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;cond&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;string-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp-international&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;replace-regexp-in-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp-international&lt;/span&gt;
&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tel:+1-\\1-\\2-\\3&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;string-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;replace-regexp-in-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/pat-nanp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tel:+1-\\1-\\2-\\3&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is then straightforward to write an interactive function that takes a phone number as a selected region, converts it to a &lt;code&gt;tel:&lt;/code&gt; URL, and opens it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/call-nanp-phone-number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;optional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Phone call the selected number (region) bounded between START and END&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interactive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;phone-buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-substring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;browse-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cc/nanp-phone-number-to-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;phone-buf&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Putting the above code to work, we can show the following example of selecting the phone number &lt;code&gt;+1 (415) 867-5309&lt;/code&gt; and dialing it from &lt;strong&gt;Emacs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://yummymelon.com/devnull/images/emacs-phone-example.gif" alt="gif movie of making a phone call from Emacs"/&gt;&lt;/p&gt;
&lt;h1&gt;Looking a place up in Apple Maps&lt;/h1&gt;
&lt;p&gt;A similar exercise can be done with searching for a place in the &lt;strong&gt;Maps&lt;/strong&gt; app, which is accessible via the &lt;code&gt;maps:&lt;/code&gt; &lt;a href="https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html#/apple_ref/doc/uid/TP40007899-CH5-SW1"&gt;URL scheme&lt;/a&gt;. Unlike a phone number, we will forego trying to interpret the selected region beforehand and just pass the region as the query to the &lt;code&gt;maps:&lt;/code&gt; URL.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/open-region-in-apple-maps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;optional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Open region from START to END in Apple Maps&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interactive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;query-buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-substring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mapURL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;maps://?q=&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-encode-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;query-buf&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Searching for %s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;query-buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;browse-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mapURL&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The following function lets you interactively enter an arbitrary place to search in Apple Maps.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/search-apple-maps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Open SEARCH query in Apple Maps&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interactive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;MMap Search: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;mapURL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;maps://?q=&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-encode-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;search&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Searching for %s&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;browse-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mapURL&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Putting the above code to work, we can show the following example of finding "Harlan Records, SF" in the &lt;strong&gt;Maps&lt;/strong&gt; app from &lt;strong&gt;Emacs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://yummymelon.com/devnull/images/emacs-maps-example.gif" alt="gif movie of finding a place in Apple Maps from Emacs"/&gt;&lt;/p&gt;
&lt;p&gt;Modifying the above to work with Google Maps is left as an exercise to reader.&lt;/p&gt;
&lt;h1&gt;Context Menus&lt;/h1&gt;
&lt;p&gt;Note that all of the above examples are keyboard driven. They are not necessarily so. In fact, driving much of the above work was the desire to be able to perform these actions via a &lt;em&gt;context menu&lt;/em&gt;. And it is very much doable, however I'll elaborate on how I've done this in a forthcoming post. For now, here's some demo screenshots of my context menu implementation that supports making a phone call or searching a location. &lt;/p&gt;
&lt;p&gt;&lt;img src="http://yummymelon.com/devnull/images/emacs-context-menu-phone-r.png" alt="Emacs context menu - search maps example"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://yummymelon.com/devnull/images/emacs-context-menu-maps-r.png" alt="Emacs context menu - search maps example"/&gt;&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/North_American_Numbering_Plan"&gt;North American Numbering Plan - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc3966"&gt;RFC 3966 - The tel URI for Telephone Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html#/apple_ref/doc/uid/TP40007899-CH1-SW1"&gt;About Apple URL Schemes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/HT209456"&gt;Make and receive phone calls on Mac or iPad - Apple Support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/><category term="emacs"/><category term="elisp"/><category term="macos"/></entry><entry><title>Sometimes you just want the dang thing to pass butter. Introducing launchutil.</title><link href="http://yummymelon.com/devnull/sometimes-you-just-want-the-dang-thing-to-pass-butter-introducing-launchutil.html" rel="alternate"/><published>2023-06-15T18:28:00-07:00</published><updated>2023-06-15T18:28:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2023-06-15:/devnull/sometimes-you-just-want-the-dang-thing-to-pass-butter-introducing-launchutil.html</id><summary type="html">&lt;p&gt;Introducing &lt;a href="https://github.com/kickingvegas/launchutil"&gt;launchutil&lt;/a&gt;, a helper utility to support creating and running a simple macOS &lt;code&gt;launchd&lt;/code&gt; service.&lt;/p&gt;</summary><content type="html">&lt;p&gt;There&amp;rsquo;s an old yet useful feature in macOS where you can get the time &lt;a href="https://support.apple.com/guide/mac-help/change-control-center-settings-mchlad96d366/13.0/mac/13.0"&gt;announced periodically&lt;/a&gt;, typically at the top of the hour.&lt;/p&gt;
&lt;p&gt;One thing though: Turning this feature on means &lt;em&gt;manually&lt;/em&gt; turning it on and having it repeatedly announce until you &lt;em&gt;manually&lt;/em&gt; turn it off. This begs for automation, especially if you want the time announced only during working hours (say 9am to 5pm). Since macOS is my daily driver, this is where we talk about &lt;a href="https://support.apple.com/guide/terminal/script-management-with-launchd-apdc6c1077b-5d5d-4d35-9c19-60f2397b2369/mac"&gt;launchd&lt;/a&gt;, which since Mac OS X 10.4 has been the system process that manages daemons and agents. There&amp;rsquo;s plenty of posts describing it and how &lt;code&gt;launchd&lt;/code&gt; works (see references below) so I won&amp;rsquo;t go over that here. What I will say though is that working with &lt;code&gt;launchd&lt;/code&gt; via the &lt;code&gt;launchctl&lt;/code&gt; command line utility is a PITA.&lt;/p&gt;
&lt;p&gt;By and large my pain points in using &lt;code&gt;launchctl&lt;/code&gt; are this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Creating a launch script means you need to write it in XML, following a schema that I find impossible to remember.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Installing the launch script means you need to work with two different directories: 1) where you create the launch script and 2) where you need to install it (typically &lt;code&gt;$HOME/Library/LaunchAgents&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Managing the launch script (that is starting, stopping, getting its status) using &lt;code&gt;launchctl&lt;/code&gt; requires different references to the launch script/service which means different command line arguments to access the same thing. This makes the ergonomics of using &lt;code&gt;launchctl&lt;/code&gt; &lt;em&gt;punishing&lt;/em&gt;, especially when debugging the launch script.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having worked with macOS for well over a decade, I&amp;rsquo;ve felt the woe of writing a number of launch scripts and encountering all of the pain points above. So I&amp;rsquo;ve decided to do something about it.&lt;/p&gt;
&lt;p&gt;Introducing &lt;a href="https://github.com/kickingvegas/launchutil"&gt;launchutil&lt;/a&gt;, a helper utility to support creating and running a simple macOS &lt;code&gt;launchd&lt;/code&gt; service. It is written in Python and is expressly designed to have the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy creation of a working XML file for daily scheduling that you can edit to taste.&lt;/li&gt;
&lt;li&gt;Uses the launch script name as the reference to the job/service you want to run. (You can still use the service name though.)&lt;/li&gt;
&lt;li&gt;Easy installation and removal of the launch script.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s see &lt;code&gt;launchutil&lt;/code&gt; at work: Imagine that you want to create a job that invokes the &lt;code&gt;say&lt;/code&gt; command to say &amp;ldquo;hello there&amp;rdquo; at 14:00 (2pm) and 15:15 (3:15 pm) everyday. The command invocation would look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;--program&lt;span class="w"&gt; &lt;/span&gt;/usr/bin/say&lt;span class="w"&gt; &lt;/span&gt;--program-arguments&lt;span class="w"&gt; &lt;/span&gt;hello&lt;span class="w"&gt; &lt;/span&gt;there&lt;span class="w"&gt; &lt;/span&gt;--daily&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;:00&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;:15&lt;span class="w"&gt; &lt;/span&gt;--execute&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternately, you can use short arguments to achieve the same result.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/usr/bin/say&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;hello&lt;span class="w"&gt; &lt;/span&gt;there&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;:00&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;:15&lt;span class="w"&gt; &lt;/span&gt;-x&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Running the above generates the launch script named &lt;code&gt;com.yummymelon.sayhello.plist&lt;/code&gt; whose contents are shown below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;quot;-//Apple//DTD PLIST 1.0//EN&amp;quot; &amp;quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.yummymelon.sayhello&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/bin/say&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;ProgramArguments&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/usr/bin/say&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;hello&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;there&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartCalendarInterval&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;14&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Hour&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;15&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Minute&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;15&lt;span class="nt"&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that the launch script file name is built off the service name. &lt;code&gt;launchutil&lt;/code&gt; relies on this convention to support the command-line ergonomics to &lt;em&gt;infer&lt;/em&gt; the service name from the launch script file name and vice-versa.&lt;/p&gt;
&lt;p&gt;Installing the launch script into the default directory &lt;code&gt;$HOME/Library/LaunchAgents&lt;/code&gt; is achieved with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello.plist&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To start the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello.plist&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To get the status of the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;status&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello.plist&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To stop the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello.plist&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To uninstall the service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;launchutil&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;com.yummymelon.sayhello.plist&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that in all the above commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No navigation or references to the installed directory.&lt;/li&gt;
&lt;li&gt;Only reference the launch script file name (or service name).&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Say Time at the Top of the Hour&lt;/h1&gt;
&lt;p&gt;So as promised at the start of this post, a &lt;code&gt;launchutil&lt;/code&gt; example that will announce the time daily from 9am to 5pm can be found at &lt;a href="https://github.com/kickingvegas/launchutil/tree/main/examples"&gt;https://github.com/kickingvegas/launchutil/tree/main/examples&lt;/a&gt;  &lt;/p&gt;
&lt;h1&gt;Getting launchutil&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;ve made all the way here and are still interested, you can get &lt;code&gt;launchutil&lt;/code&gt; at &lt;a href="https://github.com/kickingvegas/launchutil"&gt;https://github.com/kickingvegas/launchutil&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For the sake of simplicity, there is no &lt;a href="https://pypi.org/project/pip/"&gt;pip · PyPI&lt;/a&gt; packaging; just install the single file Python 3 script into your &lt;code&gt;bin&lt;/code&gt; directory. It has no dependencies to any packages outside of the basic macOS install.&lt;/p&gt;
&lt;p&gt;If you have any feedback, please let me know at the GitHub &lt;a href="https://github.com/kickingvegas/launchutil/discussions"&gt;discussion&lt;/a&gt; board for &lt;code&gt;launchutil&lt;/code&gt;. &lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://eclecticlight.co/2021/09/13/running-software-automatically-using-launchd/"&gt;Running software automatically using launchd – The Eclectic Light Company&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://babodee.wordpress.com/2016/04/09/launchctl-2-0-syntax/"&gt;Launchctl 2.0 Syntax | Babo D&amp;rsquo;s Corner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eclecticlight.co/2023/03/11/whos-managing-my-apps/"&gt;Who’s managing my apps? – The Eclectic Light Company&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leancrew.com/all-this/2009/08/launchd-as-a-replacement-for-at/"&gt;Launchd as a replacement for at - All this&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/><category term="python"/><category term="macos"/><category term="automation"/></entry><entry><title>Automatically adjusting Emacs to macOS switches in appearance</title><link href="http://yummymelon.com/devnull/automatically-adjusting-emacs-to-macos-switches-in-appearance.html" rel="alternate"/><published>2023-06-04T11:36:00-07:00</published><updated>2023-06-04T11:36:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2023-06-04:/devnull/automatically-adjusting-emacs-to-macos-switches-in-appearance.html</id><summary type="html">&lt;p&gt;How to support macOS appearance changes automatically.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Count me among those who likes using the &lt;em&gt;Auto&lt;/em&gt; setting when adjusting the appearance in macOS to either light or dark. Native macOS apps that are coded accordingly can automatically adjust as well, and it turns out that the Yamamoto Mitsuharu fork of &lt;a href="https://bitbucket.org/mituharu/emacs-mac/src/master/"&gt;Emacs&lt;/a&gt; (aka emacs-mac-app) can do this too. But with emacs-mac-app, just because it can detect the appearance change doesn&amp;rsquo;t mean that all the themes or faces will adjust accordingly too. As such it is typically up to the user to write a pair of custom light and dark mode functions to invoke when the appearance change happens.&lt;/p&gt;
&lt;p&gt;For quite some time I&amp;rsquo;ve been calling these appearance mode functions manually &lt;em&gt;like an animal&lt;/em&gt;. Then I recently learned that &lt;code&gt;mac-effective-appearance-change-hook&lt;/code&gt; is a thing.&lt;/p&gt;
&lt;p&gt;This hook gets invoked upon a macOS appearance change, so it becomes straightforward to write a hook function that tests for the &lt;code&gt;:appearance&lt;/code&gt; property returned by&lt;br/&gt; &lt;code&gt;(mac-application-state)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cc/reconfigure-nsappearance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;appearance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;plist-get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mac-application-state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;:appearance&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;string-equal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;appearance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NSAppearanceNameDarkAqua&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cc/dark-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cc/light-mode&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-hook&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;mac-effective-appearance-change-hook&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;cc/reconfigure-nsappearance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A caveat though: the above code will &lt;em&gt;only&lt;/em&gt; work with the Yamamoto fork of Emacs for macOS.  As of now there seems to be no consensus on how to handle OS-level appearance changes by Emacs core. That said, if the above works for you, then no more manual appearance change calls!&lt;/p&gt;
&lt;h1&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kickingvegas/cclisp/blob/5ace8e5e5759bba01dffad6da899374f3214eac7/cc-appearance.el#L76"&gt;Elisp code for cc/reconfigure-nsappearance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Where to get emacs-mac-app:&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ports.macports.org/port/emacs-mac-app/"&gt;MacPorts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/railwaycat/homebrew-emacsmacport"&gt;Homebrew&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content><category term="misc"/><category term="emacs"/><category term="dev"/><category term="elisp"/><category term="software"/><category term="macos"/></entry><entry><title>Captee available on the Mac App Store</title><link href="http://yummymelon.com/devnull/captee-available-on-the-mac-app-store.html" rel="alternate"/><published>2023-04-10T15:04:00-07:00</published><updated>2023-04-10T15:04:00-07:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2023-04-10:/devnull/captee-available-on-the-mac-app-store.html</id><summary type="html">&lt;p&gt;Captee available on the Mac App Store. Go get it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;At long last, &lt;strong&gt;Captee&lt;/strong&gt; is available on the &lt;a href="https://apps.apple.com/us/app/captee/id6446053750" title="Buy Captee on the App Store"&gt;Mac App Store&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Learn more about it at &lt;a href="http://yummymelon.com/captee"&gt;http://yummymelon.com/captee&lt;/a&gt;&lt;/p&gt;</content><category term="misc"/><category term="Captee"/><category term="software"/><category term="macOS"/><category term="org mode"/><category term="markdown"/><category term="emacs"/></entry><entry><title>Introducing Captee alpha, looking for testers</title><link href="http://yummymelon.com/devnull/introducing-captee-alpha-looking-for-testers.html" rel="alternate"/><published>2023-03-10T14:31:00-08:00</published><updated>2023-03-10T14:31:00-08:00</updated><author><name>Charles Choi</name></author><id>tag:yummymelon.com,2023-03-10:/devnull/introducing-captee-alpha-looking-for-testers.html</id><summary type="html">&lt;p&gt;Introducing Captee, a macOS utility app to share links and content via the macOS Share menu. It's in alpha now, but I'm looking for testers.&lt;/p&gt;</summary><content type="html">&lt;p&gt;This past February, I started building a &lt;strong&gt;macOS&lt;/strong&gt; app to help me work with &lt;a href="https://orgmode.org/"&gt;Org mode for Emacs&lt;/a&gt;  It lets you share links and content from another app (typically &lt;strong&gt;Safari&lt;/strong&gt;) via the &lt;strong&gt;macOS&lt;/strong&gt; &lt;em&gt;Share&lt;/em&gt; menu and format it in &lt;strong&gt;Org&lt;/strong&gt; markup. It can also support &lt;a href="https://daringfireball.net/projects/markdown/syntax#link"&gt;Markdown links&lt;/a&gt; so you can work with other editors too. The app is called &lt;strong&gt;Captee&lt;/strong&gt; and here&amp;rsquo;s a video that demonstrates it at work:&lt;/p&gt;
&lt;div class="video-container"&gt;&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/OTtfaxxqnWY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;strong&gt;Captee&lt;/strong&gt; is very much in an alpha/proof-of-concept/bleeding-edge state of polish right now. You&amp;rsquo;ll get the best results for &lt;strong&gt;Captee&lt;/strong&gt; if you use the macOS Yamamoto Mitsuharu port of &lt;strong&gt;Emacs&lt;/strong&gt; (&lt;a href="https://ports.macports.org/port/emacs-mac-app/"&gt;MacPorts&lt;/a&gt;, &lt;a href="https://github.com/railwaycat/homebrew-emacsmacport"&gt;Homebrew&lt;/a&gt;). It supports &lt;a href="https://orgmode.org/manual/Protocols.html"&gt;Org Protocol&lt;/a&gt; out of the box, otherwise you will have to use the system clipboard to share content.&lt;/p&gt;
&lt;p&gt;To capture selected text via &lt;code&gt;org-protocol&lt;/code&gt; you&amp;rsquo;ll need to define a capture template on the &lt;strong&gt;Emacs&lt;/strong&gt; side &lt;em&gt;first&lt;/em&gt; before you use &lt;strong&gt;Captee&lt;/strong&gt;. Here&amp;rsquo;s an example capture template that uses the placeholders described in the Org &lt;a href="https://orgmode.org/manual/The-capture-protocol.html"&gt;capture protocol&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;setq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;org-capture-templates&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Captee Capture&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;entry&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file+headline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;~/org/captee.org&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Captee Captures&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;* %:description\\n%:annotation\\n%i\n%?&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;:empty-lines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Okay with all that said, I&amp;rsquo;m looking for testers. If you&amp;rsquo;re interested, you can get an alpha build of &lt;strong&gt;Captee&lt;/strong&gt; via this &lt;a href="https://testflight.apple.com/join/Jq0GuEv8"&gt;TestFlight link&lt;/a&gt;. Please provide feedback via the TestFlight app. Also, I highly recommend you do a deep-read of the release notes before installing or using &lt;strong&gt;Captee&lt;/strong&gt;.&lt;/p&gt;</content><category term="misc"/><category term="emacs"/><category term="dev"/><category term="software"/><category term="macOS"/><category term="org mode"/><category term="captee"/></entry></feed>