<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Screenshot Ultra changelog</title>
    <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/</link>
    <description>Releases of Screenshot Ultra. Latest: v0.12.0.</description>
    <language>en</language>
    <lastBuildDate>Tue, 12 May 2026 15:38:55 GMT</lastBuildDate>
    <item>
      <title>v0.12.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.12.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.12.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>Quick Tray redesign</strong> release. The post-capture action bar is no
longer a fixed slab pinned to the bottom-right of the screen — it&#39;s a
borderless, screenshot-first floating card that anchors to where you
just finished the cut.</p>
<h3 id="changed-quick-tray"><a class="anchor" href="#changed-quick-tray" aria-label="Anchor">#</a>Changed — Quick Tray</h3>
<ul>
<li><strong><code>src/quick_tray.rs</code></strong> — replaced the titled 540×110 NSWindow with
a borderless, rounded NSVisualEffectView (HUDWindow material) panel.
The captured screenshot is now rendered large on top (sized to its
own aspect ratio, 260–380 × 150–260pt, with rounded corners and the
same drag-out behavior as before). The action row sits directly
beneath as a compact strip of SF Symbol icon buttons (Copy, Text,
Edit, Folder, Reveal, Pin, Discard) with hover borders and tooltips.</li>
<li><strong>Anchor position</strong> is now the cursor location at the moment
<code>screencapture</code> returns — effectively &quot;just below the region you
finished dragging&quot; for region cuts. Multi-display aware via
<code>NSScreen.visibleFrame</code>; flips above the cursor near the bottom of
the screen; falls back to bottom-right for fullscreen / clipboard
paths where there&#39;s no meaningful cursor.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.11.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.11.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.11.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>auto-update</strong> release. v0.10 added a passive update check
that nudged the user toward the Releases page; v0.11 closes the
loop with an in-place installer that downloads, verifies, and
relaunches without ever leaving the app.</p>
<h3 id="added-in-place-auto-update-from-github-releases"><a class="anchor" href="#added-in-place-auto-update-from-github-releases" aria-label="Anchor">#</a>Added — in-place auto-update from GitHub Releases</h3>
<ul>
<li><strong><code>src/installer.rs</code></strong> — full Rust pipeline that pulls the
release <code>.zip</code> + matching <code>.sha256</code> from
<code>github.com/MPJHorner/ScreenshotUltra/releases</code>, verifies the
checksum via <code>shasum -c</code>, unpacks with <code>ditto -xk</code>, then spawns
a detached bash helper that waits for our PID to exit, atomically
swaps <code>/Applications/Screenshot Ultra.app</code>, clears the
Gatekeeper <code>com.apple.quarantine</code> xattr, and relaunches via <code>open</code>.</li>
<li><strong><code>Check for Updates…</code></strong> tray-menu item now offers an
<code>Install Now / Later / Skip This Version</code> NSAlert on hit. Skipped
versions write a marker into <code>~/.config/ScreenshotUltra/</code> so the
user isn&#39;t re-prompted for the same release.</li>
<li>Background update check (<code>[general].check_for_updates = true</code>)
still just notifies — it never steals focus from a typing user.
The modal is only shown on an explicit menu click.</li>
<li>New NDJSON events: <code>update_installing</code>, <code>update_install_failed</code>,
<code>update_available_skipped</code>, <code>update_skipped</code>.</li>
</ul>
<h3 id="safety"><a class="anchor" href="#safety" aria-label="Anchor">#</a>Safety</h3>
<ul>
<li>We refuse to auto-install if the app isn&#39;t in <code>/Applications/</code>
(e.g. running from <code>~/Downloads</code>, <code>cargo run</code>, etc.) — the
manual-update notification fires instead.</li>
<li>We refuse to auto-install if <code>/Applications/Screenshot Ultra.app</code>
isn&#39;t writable by the current user — surfaces a notification that
explains the situation and links to the Releases page.</li>
<li>The helper script backs up the existing bundle before moving the
new one into place, and restores the backup if the swap fails.</li>
<li>SHA-256 verification is mandatory; a checksum mismatch aborts
before anything is touched in <code>/Applications/</code>.</li>
</ul>
<h3 id="internals"><a class="anchor" href="#internals" aria-label="Anchor">#</a>Internals</h3>
<ul>
<li><code>_dispatch_main_q</code> is referenced directly via <code>extern &quot;C&quot;</code>
because <code>dispatch_get_main_queue()</code> is a libdispatch static-inline
with no exported symbol. <code>block2::RcBlock</code> carries the install
closure to the main run loop.</li>
<li>Once M6 (signing + notarisation) lands the only change here will
be an extra signature-verify step before the swap.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.10.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.10.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.10.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>native recorder</strong> release. <code>screencapture -v</code> was a great
bootstrap, but it tops out at ~30 fps and gives us no quality knobs.
v0.10 ships a real ScreenCaptureKit + AVAssetWriter pipeline.</p>
<h3 id="added-screencapturekit-recording-backend"><a class="anchor" href="#added-screencapturekit-recording-backend" aria-label="Anchor">#</a>Added — ScreenCaptureKit recording backend</h3>
<ul>
<li><strong><code>mac/STURecorder.swift</code></strong> — a ~190-line Swift CLI built on
<code>SCStream</code> + <code>AVAssetWriter</code>. Captures the main display at H.264
1080p+ (full Retina pixel dimensions) at <strong>60 fps</strong> by default,
with a sensible bitrate (≈ <code>width × height × fps / 8</code> bps), 2-second
GOP, High Profile. Cleanly handles SIGTERM/SIGINT to finalise the
<code>.mov</code> (writes the <code>moov</code> atom before exit).</li>
<li><strong><code>scripts/build-recorder.sh</code></strong> compiles a <strong>universal binary</strong>
(arm64 + x86_64 on supported SDKs, arm64-only otherwise) via
<code>swiftc</code> and drops it at <code>target/recorder/STURecorder</code>. Graceful
no-op when <code>swiftc</code> isn&#39;t on PATH.</li>
<li><strong><code>make app</code></strong> bundles the binary into
<code>Contents/Resources/STURecorder</code> so the <code>.app</code> is fully self-
contained.</li>
<li><code>src/recording.rs</code> now tries the bundled <code>STURecorder</code> first and
<strong>falls back to <code>screencapture -v</code> automatically</strong> if (a) the
bundle doesn&#39;t have one (dev build without <code>swiftc</code>), (b) spawning
fails, or (c) the user sets <code>[recording].use_screen_capture_kit = false</code>.</li>
<li>Per-backend signal handling: SIGTERM for STURecorder (matches its
graceful shutdown), SIGINT for <code>screencapture -v</code> (what it expects).</li>
<li><code>recording_start</code> NDJSON event now records the <code>backend</code> field
(<code>screencapturekit</code> or <code>screencapture</code>).</li>
<li>New setting <code>[recording].use_screen_capture_kit</code> (default <code>true</code>).</li>
</ul>
<h3 id="why-a-swift-bridge-not-pure-rust-objc2"><a class="anchor" href="#why-a-swift-bridge-not-pure-rust-objc2" aria-label="Anchor">#</a>Why a Swift bridge (not pure-Rust objc2)?</h3>
<ul>
<li><code>SCStreamOutput</code> is a Cocoa delegate with async sample-buffer
callbacks. <code>AVAssetWriter</code> has its own threading model around
<code>expectsMediaDataInRealTime</code> and <code>startSession(atSourceTime:)</code>.</li>
<li>Swift expresses this in ~190 lines with idiomatic <code>async</code> /
<code>await</code>. The same in <code>objc2</code> would be 600+ lines of
<code>RcBlock</code>-wrapped delegate methods + manual <code>CMSampleBuffer</code>
juggling. The Swift binary is 250 KB and lives inside the .app
bundle — no runtime hit, no extra deps.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.9.1</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.9.1</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.9.1</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-per-mode-shell-sink-overrides"><a class="anchor" href="#added-per-mode-shell-sink-overrides" aria-label="Anchor">#</a>Added — per-mode shell sink overrides</h3>
<ul>
<li>New <code>[sinks].shell_region</code>, <code>shell_window</code>, <code>shell_fullscreen</code>,
<code>shell_video</code>, <code>shell_gif</code> fields. If set, override the global
<code>[sinks].shell</code> for that capture mode — so region captures can land
on S3 while videos go to your team&#39;s Slack while GIFs go to a
personal web server. Each override is independently optional;
whatever&#39;s empty falls back to the global <code>shell</code>.</li>
<li><code>Sinks::shell_for(mode)</code> is the single lookup used by every
capture path (still capture, timed fullscreen, and recording).
Timed-fullscreen captures use the <code>fullscreen</code> override.</li>
<li>5 new tests for the lookup logic.</li>
</ul>
<p>Example:</p>
<div class="code-block"><span class="code-lang">toml</span><button class="copy-btn" type="button" aria-label="Copy code">copy</button><pre><code class="language-toml">[sinks]
shell        = &quot;rclone copy $1 cloud:misc/&quot;
shell_video  = &quot;scp $1 home.example.com:/var/www/recordings/&quot;
shell_gif    = &quot;/usr/local/bin/upload-shot $1&quot;</code></pre></div>
]]></description>
    </item>
    <item>
      <title>v0.9.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.9.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.9.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="changed-m6-signnotarise-parked"><a class="anchor" href="#changed-m6-signnotarise-parked" aria-label="Anchor">#</a>Changed — M6 (sign/notarise) parked</h3>
<ul>
<li>M6 (signed <code>.dmg</code> + notarisation + Homebrew cask) is parked until a
Developer ID Application certificate is available to test against.
Until then the existing tag-driven pipeline continues to ship
unsigned universal <code>.zip</code>s. plan.md, milestone README, and
<code>docs/milestones/M6-ship.md</code> all flagged 🔒 parked.</li>
</ul>
<h3 id="added-opt-in-update-check"><a class="anchor" href="#added-opt-in-update-check" aria-label="Anchor">#</a>Added — opt-in update check</h3>
<ul>
<li>New setting <strong><code>[general].check_for_updates = false</code></strong> (off by default
per plan §11 &quot;no telemetry / no phone-home unless the user opts in&quot;).
When on: at startup (after a 30 s settling delay) and every 24 hours
we poll the GitHub Releases API for a newer <code>tag_name</code>. If one
exists, fire a notification banner and emit an <code>update_available</code>
NDJSON event.</li>
<li><strong>Tray menu</strong> gains a <code>Check for Updates…</code> entry that runs the
check on demand regardless of the setting and notifies whether
you&#39;re up to date. Useful even with the auto-poll off.</li>
<li>Uses <code>/usr/bin/curl</code> rather than pulling in <code>reqwest</code> for a single
GET — keeps the binary small.</li>
<li>7 new unit tests for the SemVer-ish comparator.</li>
<li>We never auto-install (signing pipeline isn&#39;t in place; see M6
parked status).</li>
</ul>
<h3 id="added-drag-out-from-pin-windows"><a class="anchor" href="#added-drag-out-from-pin-windows" aria-label="Anchor">#</a>Added — drag-out from Pin windows</h3>
<ul>
<li>The Pin window&#39;s image is now a drag source too. Drag a pinned
screenshot into Slack / Mail / Finder / any drop target that
accepts a file URL and the file lands there.</li>
<li>The <code>STUPinControl</code> overlay view was folded back into the image
view itself (new <code>STUDraggablePin</code>, NSImageView subclass) so one
class handles drawing, scroll-dim, keyboard zoom + close, <em>and</em>
drag-out. Multi-pin support via a <code>PIN_PATHS: HashMap&lt;view_ptr, PathBuf&gt;</code> keyed by stable view pointer.</li>
<li>New NDJSON event: <code>pin_action.action = &quot;drag_out&quot;</code>.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.8.3</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.3</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.3</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-first-run-welcome-window"><a class="anchor" href="#added-first-run-welcome-window" aria-label="Anchor">#</a>Added — first-run welcome window</h3>
<ul>
<li>On first launch the app opens a styled <code>NSWindow</code> walking the user
through the three things they need to know: (1) the Screen Recording
permission, (2) the default hotkeys, (3) the menu bar icon.</li>
<li>Two deep-link buttons jump straight to the relevant System Settings
panes:<ul>
<li><strong>Grant Screen Recording…</strong> →
<code>x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture</code></li>
<li><strong>Grant Accessibility (optional)…</strong> →
<code>x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility</code>
(The Accessibility one is only needed for the keystroke overlay.)</li>
</ul>
</li>
<li>Hotkey summary inside the welcome window is pulled from the <strong>live</strong>
<code>Settings</code>, so rebound bindings show up if the user has imported a
config from another machine.</li>
<li>Gated by a versioned marker file at
<code>~/Library/Application Support/ScreenshotUltra/.welcomed-v1</code> — users
upgrading from an earlier version don&#39;t get re-welcomed.</li>
<li>New NDJSON event: <code>welcomed</code>.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.8.2</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.2</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.2</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-drag-out-from-the-quick-tray"><a class="anchor" href="#added-drag-out-from-the-quick-tray" aria-label="Anchor">#</a>Added — drag-out from the Quick Tray</h3>
<ul>
<li>The Quick Tray&#39;s thumbnail is now a real drag source. Hold the mouse
button on the thumb, drag, drop onto Slack / Mail / a Finder window /
iMessage / your team&#39;s CRM — anywhere that accepts a file URL — and
the screenshot lands there.</li>
<li>Mechanically: <code>STUDraggableThumb</code> is an <code>NSImageView</code> subclass with
<code>mouseDown:</code> / <code>mouseDragged:</code> handlers + the required
<code>draggingSession:sourceOperationMaskForDraggingContext:</code> method
(returning <code>Copy</code>). On drag, we build an <code>NSPasteboardItem</code> with
<code>NSPasteboardTypeFileURL</code> pointing at the captured file, wrap it in
an <code>NSDraggingItem</code> whose drag-image is the live thumbnail, and call
<code>beginDraggingSessionWithItems:event:source:</code>.</li>
<li>New NDJSON event: <code>tray_action.action = &quot;drag_out&quot;</code>.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.8.1</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.1</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.1</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-keystroke-overlay-for-screencasts"><a class="anchor" href="#added-keystroke-overlay-for-screencasts" aria-label="Anchor">#</a>Added — Keystroke overlay for screencasts</h3>
<ul>
<li>Setting <strong><code>[recording].keystroke_overlay = true</code></strong> (default off) shows
a floating dark-glass pill at the bottom-centre of the main screen
whenever a recording is active. Every key (and modifier combo) you
type briefly flashes into the pill — perfect for screencasts where
you want viewers to see which shortcut you just hit without
narrating.</li>
<li>Translates raw NSEvent characters into the macOS glyphs you&#39;d expect:
<code>⌘C</code>, <code>↩</code>, <code>⌫</code>, <code>Tab</code>, <code>Space</code>, arrow keys, <code>F1</code>–<code>F12</code>, etc.</li>
<li>New module <code>src/keystroke_overlay.rs</code> (~290 LOC). NSEvent global
monitor + transparent borderless click-through NSWindow + custom
<code>STUKeystrokePill</code> NSView that paints the rounded pill. Keys fade
after 2 s; up to 8 chunks shown at once. Requires Accessibility
permission (macOS prompts on first use).</li>
<li><code>recording::start</code> enables the overlay if the setting&#39;s on;
<code>recording::stop</code> always tears it down.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.8.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.8.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>history-and-OCR</strong> release. Two new in-app windows plus a custom
hand-rolled docs site that matches the rest of the Ultra family
exactly.</p>
<h3 id="added-history-window-h"><a class="anchor" href="#added-history-window-h" aria-label="Anchor">#</a>Added — History window (<code>⌃⌥⌘H</code>)</h3>
<ul>
<li>Native <code>NSWindow</code> browser of the per-folder NDJSON history index
(<code>&lt;save_folder&gt;/.screenshot-ultra/index.ndjson</code>). One row per
capture, most recent first, fixed-width columns for timestamp /
mode / size / filename. ⌘F searches the view.</li>
<li>Friendly &quot;No history yet&quot; empty state for first-run.</li>
<li>New <code>Action::History</code>, tray menu &quot;History…&quot; entry.</li>
</ul>
<h3 id="added-editor-ocr-button"><a class="anchor" href="#added-editor-ocr-button" aria-label="Anchor">#</a>Added — Editor &quot;OCR&quot; button</h3>
<ul>
<li>New action button in the annotation editor&#39;s bottom row that runs
Apple Vision against the source image and copies the recognised
text onto the clipboard via <code>pbcopy</code>. Same notification flow as the
Quick Tray&#39;s Text button.</li>
<li>12 default hotkeys now register at startup (added <code>history</code>).</li>
</ul>
<h3 id="changed-custom-docs-site-replacing-mkdocs-material"><a class="anchor" href="#changed-custom-docs-site-replacing-mkdocs-material" aria-label="Anchor">#</a>Changed — custom docs site replacing mkdocs-material</h3>
<ul>
<li>The whole <code>site/</code> directory is now a hand-rolled static site
matching MailBox Ultra / Postbin Ultra exactly:<ul>
<li>Node 20 builder in <code>site/build.mjs</code> (~370 LOC, deps: gray-matter +
marked).</li>
<li><code>site/templates/{layout,home}.html</code> + <code>partials/{nav,footer}.html</code>
with the aperture-iris SVG logo inline.</li>
<li><code>site/static/style.css</code> carried over from MailboxUltra with the
palette swapped to brand red (<code>#FF3D54</code> / <code>#B91D36</code>), light-mode
accent <code>#DC2542</code>.</li>
<li><code>site/content/*.md</code> — 10 frontmatter-equipped pages including a
full home hero (badge, gradient headline, hero meta strip, feature
grid, four-step tour, &quot;Part of the Ultra family&quot; cross-link grid).</li>
<li><code>.github/workflows/pages.yml</code> swapped from <code>pip install mkdocs-material</code>
to <code>setup-node@v4</code> + <code>npm ci</code> + <code>node build.mjs</code>.</li>
<li>The build pulls version from <code>Cargo.toml</code> and parses
<code>CHANGELOG.md</code> release sections at build time so the docs site
can never drift from shipping code.</li>
</ul>
</li>
<li>Site lives at the same URL: <a href="https://mpjhorner.github.io/ScreenshotUltra/" rel="noopener noreferrer">https://mpjhorner.github.io/ScreenshotUltra/</a>.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.7.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.7.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.7.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>M5 v0</strong> release. On-device OCR via Apple Vision lands as a Quick
Tray button.</p>
<h3 id="added-ocr-apple-vision-on-device"><a class="anchor" href="#added-ocr-apple-vision-on-device" aria-label="Anchor">#</a>Added — OCR (Apple Vision, on-device)</h3>
<ul>
<li><strong>&quot;Text&quot; button on the Quick Tray</strong> — pulls every recognised string
out of the capture via <code>VNRecognizeTextRequest</code> and dumps it onto the
clipboard. Result count + notification banner so you know how much
text came out.</li>
<li>Recognition level pinned to <code>Accurate</code>, language correction on. Pure
on-device — no network calls, no API keys, no per-character pricing.</li>
<li>New <code>src/ocr.rs</code> (<code>extract_text(path) -&gt; Option&lt;String&gt;</code>) — same
boundary as the rest of our native helpers.</li>
<li>New NDJSON event: <code>ocr</code> with <code>path</code> and <code>chars</code> fields.</li>
</ul>
<h3 id="added-better-video-quick-tray-poster-frames"><a class="anchor" href="#added-better-video-quick-tray-poster-frames" aria-label="Anchor">#</a>Added — better video Quick Tray (poster frames)</h3>
<ul>
<li><code>.mov</code> / <code>.mp4</code> / <code>.m4v</code> captures now show a <strong>poster-frame
thumbnail</strong> in the Quick Tray, generated by macOS&#39;s own <code>qlmanage -t</code>
Quick Look engine. Cached under <code>$TMPDIR/screenshot-ultra-posters/</code>
so repeated shows of the same recording skip the spawn.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.6.2</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.2</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.2</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-cheat-sheet-window"><a class="anchor" href="#added-cheat-sheet-window" aria-label="Anchor">#</a>Added — Cheat Sheet window (<code>⌃⌥⌘/</code>)</h3>
<ul>
<li>New in-app reference card listing every global hotkey + every editor
tool / colour / width / action / pin gesture / Quick-Tray button.</li>
<li>Rendered into a read-only monospaced <code>NSTextView</code> so users can scroll,
<code>⌘F</code> search, and copy lines. Window is resizable + searchable.</li>
<li>Hotkey bindings come from the <strong>live</strong> <code>Settings</code> — if you&#39;ve
rebound <code>region</code> to <code>⌃⌥⌘5</code>, the cheat sheet shows <code>⌃⌥⌘5</code>. Unbound
slots render as <code>—</code>.</li>
<li>New tray menu item &quot;Cheat Sheet&quot; with the <code>⌃⌥⌘/</code> accelerator shown
inline.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.6.1</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.1</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.1</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>Recording UX polish on top of v0.6.0&#39;s screen-recording v0.</p>
<h3 id="added-recording-feedback"><a class="anchor" href="#added-recording-feedback" aria-label="Anchor">#</a>Added — recording feedback</h3>
<ul>
<li><strong>Menu-bar indicator</strong> — the aperture-iris glyph swaps to a filled
solid disc whenever a recording is in progress, and the tooltip
reads &quot;Screenshot Ultra — recording&quot;. Removes the uncertainty about
&quot;did I start it / stop it&quot; that v0.6.0 had.</li>
<li><strong>Notification banner</strong> on recording stop — top-right macOS banner
reads <code>&lt;filename&gt; · &lt;size&gt; · &lt;duration&gt;</code> so you can see your
recording landed even if you missed the Quick Tray (which can&#39;t
render a <code>.mov</code> thumbnail).</li>
<li>New <code>sinks::notify(title, body)</code> helper (shells out to
<code>osascript display notification</code>, so no UNUserNotification
permission dance).</li>
<li>New <code>tray::register(TrayIcon)</code> + <code>tray::set_recording_indicator(bool)</code>
— the tray module now owns the constructed <code>TrayIcon</code> so anywhere in
the app can toggle the indicator without threading it through.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.6.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.6.0</guid>
      <pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>M3 begins</strong> release. Video + GIF recording lands, plus a third row
in the tray menu and two new global hotkeys.</p>
<h3 id="added-m3-v0-recording"><a class="anchor" href="#added-m3-v0-recording" aria-label="Anchor">#</a>Added — M3 v0 recording</h3>
<ul>
<li><strong>Toggle video recording</strong> (default <code>⌃⌥⌘V</code>) — press to start, press
again to stop. Saves as <code>&lt;save_folder&gt;/...mov</code> with the same
<code>filename_template</code> as still captures. Mode token = <code>video</code>.</li>
<li><strong>Toggle GIF recording</strong> (default <code>⌃⌥⌘G</code>) — captures as <code>.mov</code>, then
post-processes through <code>ffmpeg</code> (12 fps + generated palette for crisp
small files) into a <code>.gif</code>. Without <code>ffmpeg</code>, falls back to keeping
the <code>.mov</code> with a log line.</li>
<li><strong>Mouse-click highlight</strong> in recordings via <code>screencapture -k</code> —
enable / disable via <code>[recording].show_clicks</code> (default on).</li>
<li><strong>Microphone capture</strong> via <code>screencapture -g</code> —
<code>[recording].record_microphone</code> (default off).</li>
<li>Tray menu adds <strong>Record Video / Stop</strong> and <strong>Record GIF / Stop</strong>
items. Quick Tray fires for the resulting file once recording stops.</li>
<li>New NDJSON events: <code>recording_start</code>, <code>recording_stop</code>.</li>
<li>New module <code>src/recording.rs</code> (~270 LOC). The <code>start()</code> / <code>stop()</code> /
<code>toggle()</code> boundary is stable so the planned ScreenCaptureKit +
AVAssetWriter swap-in is contained.</li>
</ul>
<h3 id="notes"><a class="anchor" href="#notes" aria-label="Anchor">#</a>Notes</h3>
<ul>
<li>M3 v0 piggybacks on macOS&#39;s <code>screencapture -v</code> binary so we can ship
start/stop video recording today. A native ScreenCaptureKit pipeline
lands in v0.7 alongside system-audio capture and the keystroke
overlay (which <code>screencapture</code> can&#39;t do).</li>
<li>The full-screen Quick Tray &quot;Edit&quot; button still routes through the
annotation editor for stills only; videos open in the user&#39;s default
player.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.5.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.5.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.5.0</guid>
      <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The <strong>M2 complete</strong> release. The Preferences GUI lands, finishing the
last material item on the M2 milestone.</p>
<h3 id="added-preferences-window"><a class="anchor" href="#added-preferences-window" aria-label="Anchor">#</a>Added — Preferences window</h3>
<ul>
<li><strong>In-app Preferences</strong> (default <code>⌃⌥⌘,</code>, plus tray-menu &quot;Preferences…&quot;):
a real <code>NSWindow</code> with a scrollable <code>NSTextView</code> showing the current
<code>settings.toml</code>. Edit in place; the <strong>Apply</strong> button validates the TOML
by parsing it through <code>Settings</code>&#39;s serde derives, then writes back to
disk so the hot-reload watcher picks it up — same path as external
editors.</li>
<li><strong>Reset to Defaults</strong> button reloads the canonical defaults into the
editor (doesn&#39;t save until you click Apply).</li>
<li><strong>Cancel</strong> (<code>Esc</code>) or <code>⌘W</code> closes without saving.</li>
<li>Apply errors land in a native <code>NSAlert</code> — typos won&#39;t silently revert
your bindings.</li>
<li>Logs <code>preferences_apply</code> on successful save.</li>
</ul>
<h3 id="fixed-upgrade-friendly-settings-parsing"><a class="anchor" href="#fixed-upgrade-friendly-settings-parsing" aria-label="Anchor">#</a>Fixed — upgrade-friendly settings parsing</h3>
<ul>
<li>Every hotkey slot now has an explicit per-field serde default function
(<code>default_window</code>, <code>default_pin_last</code>, …). Previously
<code>#[serde(default)]</code> fell back to <code>String::default()</code> → empty, so
anyone upgrading from an older settings.toml silently lost the new
bindings (window, pin_last, repeat_last, open_clipboard_image,
color_picker, preferences).</li>
<li><code>Settings::load_or_default</code> now rewrites the file when round-tripping
reveals missing fields, so the next <code>Edit settings.toml…</code> shows every
current option.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.4.1</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.4.1</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.4.1</guid>
      <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-pin-to-screen-interaction-polish"><a class="anchor" href="#added-pin-to-screen-interaction-polish" aria-label="Anchor">#</a>Added — pin-to-screen interaction polish</h3>
<ul>
<li><strong>Scroll-wheel opacity</strong> — hover any pinned window and scroll to dim
it (alpha 1.0 → 0.3). Lets you keep a pin visible while reading
through it.</li>
<li><strong>⌘+ / ⌘- zoom</strong> — scales the pin in place around its centre.</li>
<li><strong>⌫ / ⌫-forward / Esc closes the pin</strong> — quick dismiss without
reaching for the title-bar close button.</li>
<li><strong>⌘0 resets alpha to 1.0</strong> for when you&#39;ve dimmed too far.</li>
<li>Implemented as a transparent <code>PinControl</code> <code>NSView</code> overlay on top of
the image view, made first responder so it gets scroll + key events.</li>
</ul>
<h3 id="added-eyedropper-tooltips-aperture-menu-bar-icon"><a class="anchor" href="#added-eyedropper-tooltips-aperture-menu-bar-icon" aria-label="Anchor">#</a>Added — eyedropper, tooltips, aperture menu-bar icon</h3>
<ul>
<li><strong>Eyedropper colour picker</strong> (default <code>⌃⌥⌘P</code>) — shows macOS&#39;s
<code>NSColorSampler</code> magnifier; clicking any on-screen pixel copies its
sRGB hex (<code>#rrggbb</code>) onto the clipboard and emits an <code>eyedropper_pick</code>
NDJSON event. New <code>src/eyedropper.rs</code>. Uses <code>block2::RcBlock</code> to bridge
the Cocoa selection-handler block.</li>
<li><strong>Tooltips on every editor button</strong> — hover any tool / colour / width
/ action button to see a one-line hint with the shortcut.</li>
<li><strong>Aperture menu-bar icon</strong> — replaced the programmatic camera glyph
with a six-blade aperture iris that matches the .app icon (which is
itself an aperture). Still procedurally drawn at 22×22; still a
template image so macOS tints it for the menu bar&#39;s light/dark mode.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.4.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.4.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.4.0</guid>
      <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The &quot;annotate everything, then some&quot; release. The annotation editor now
ships with <strong>eleven tools</strong> (Pen / Line / Arrow / Rect / Ellipse /
Highlighter / Redact / Counter / Text / Blur / Crop), visual indicators
for the active tool / colour / width, and a real app icon. README and
docs site restyled to match the <strong>Ultra</strong> family.</p>
<h3 id="added-app-icon"><a class="anchor" href="#added-app-icon" aria-label="Anchor">#</a>Added — app icon</h3>
<ul>
<li>Hand-tuned SVG aperture icon (<code>icon/icon.svg</code>) generated to match
the macOS squircle + sheen treatment of the sister projects, with
the brand camera-shutter red (<code>#FF3D54</code>) gradient and a six-bladed
iris around a glossy lens.</li>
<li><code>scripts/render-icon.sh</code> builds the full <code>.icns</code> from the SVG using
Swift&#39;s <code>NSImage</code> + <code>iconutil</code>. Pure macOS tooling — no <code>brew install</code>
required. Hooked into <code>make app</code> as a dependency of the bundle.</li>
<li><code>Info.plist</code> declares <code>CFBundleIconFile = AppIcon</code> so the app shows
the icon in <code>/Applications</code>, Spotlight, the App Switcher, and the
Quick Tray&#39;s Reveal-in-Finder result.</li>
</ul>
<h3 id="changed-ultra-family-styling"><a class="anchor" href="#changed-ultra-family-styling" aria-label="Anchor">#</a>Changed — Ultra-family styling</h3>
<ul>
<li>README rewritten to match the <strong>MailBox Ultra</strong> / <strong>Postbin Ultra</strong>
layout: badges row, blockquote of doc links, <strong>Why</strong>, install one-liner
in a 🚀 blockquote, Quick start, What it does, Hotkeys + Editor
shortcut tables, Configuration, Sinks &amp; shell, Development, Documentation,
Sister projects, Contributing, License.</li>
<li>Docs site <code>index.md</code> follows the same three-promises structure as the
sister sites.</li>
<li>mkdocs accent colour set to <code>pink</code> (closest Material accent to the
brand <code>#FF3D54</code>).</li>
</ul>
<h3 id="added-active-selection-indicators"><a class="anchor" href="#added-active-selection-indicators" aria-label="Anchor">#</a>Added — active-selection indicators</h3>
<ul>
<li>Tool, colour, and stroke-width buttons now visually reflect the
current selection via <code>NSButton.state</code> (Cocoa renders the &quot;on&quot; state
with a pressed-in highlight). No more guessing which tool is active.</li>
<li><code>set_tool</code> / <code>set_color</code> / <code>set_width</code> refresh button state in lockstep.</li>
<li>Initial state seeded from <code>open()</code> so the default <code>Pen / Red / Thin</code>
shows highlighted on first paint.</li>
</ul>
<h3 id="added-crop-tool"><a class="anchor" href="#added-crop-tool" aria-label="Anchor">#</a>Added — Crop tool</h3>
<ul>
<li><strong>Crop tool</strong> (<code>C</code>) — drag a yellow guide rectangle, release to crop
the image to that region. The window resizes to the new aspect ratio,
annotations are cleared, and a <code>editor_crop</code> NDJSON event is emitted.
Crop is destructive (not undoable via ⌘Z) by design; ⌘Z still affects
annotations.</li>
</ul>
<h3 id="added-blur-tool"><a class="anchor" href="#added-blur-tool" aria-label="Anchor">#</a>Added — Blur tool</h3>
<ul>
<li><strong>Blur tool</strong> (<code>B</code>) — pixelate-style privacy filter. Drag to define a
rectangle; the tool reads the corresponding sub-rect from the source
<code>NSImage</code>, downsamples it to <code>dst_size / (width × 3)</code> via
<code>NSBitmapImageRep</code>, then redraws it across the destination rect with
<code>NSImageInterpolation::None</code> to produce a chunky mosaic. Works in both
on-screen painting (view coords) and save rendering (pixel coords)
via a single <code>pixelate_blur</code> helper that picks coordinate space from
context bounds.</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.3.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.3.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.3.0</guid>
      <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<p>The &quot;annotate everything&quot; release. Nine editor tools, full-colour palette,
stroke-width picker, undo/redo, Apple Preview-quality save back to PNG.</p>
<h3 id="added-editor-counter-text-docs-page"><a class="anchor" href="#added-editor-counter-text-docs-page" aria-label="Anchor">#</a>Added — editor: Counter + Text + docs page</h3>
<ul>
<li><strong>Counter tool</strong> (<code>N</code>) — click drops an auto-incrementing numbered
circle in the current colour with a white outline + bold white digit.
Numbers reset to 1 each time you open the editor.</li>
<li><strong>Text tool</strong> (<code>T</code>) — click prompts for a string (native <code>NSAlert</code> +
<code>NSTextField</code>) and places it at the click point. Uses the current
colour; size derives from the stroke-width picker.</li>
<li>New <strong>Annotation editor</strong> docs page on the site (linked from the top
nav).</li>
<li>Editor file-header comment refreshed to list the full tool roster.</li>
</ul>
<h3 id="added-editor-expansion"><a class="anchor" href="#added-editor-expansion" aria-label="Anchor">#</a>Added — editor expansion</h3>
<ul>
<li><strong>Three more editor tools</strong>: Line (no arrowhead), Highlighter
(translucent thick yellow), Redact (filled black rectangle). Total now
seven: Pen / Line / Arrow / Rect / Ellipse / Highlighter / Redact.</li>
<li><strong>Five-colour palette</strong> (Red / Yellow / Green / Blue / Black) — each
annotation captures its colour at draw time so later palette changes
don&#39;t repaint prior strokes.</li>
<li><strong>Stroke-width picker</strong> (Thin 3 px / Med 6 px / Thick 12 px) with
number-key shortcuts <code>1</code> / <code>2</code> / <code>3</code>.</li>
<li>New tool shortcuts: <code>P</code> Pen, <code>L</code> Line, <code>A</code> Arrow, <code>R</code> Rect, <code>E</code>
Ellipse, <code>H</code> Highlighter, <code>X</code> Redact.</li>
<li>Editor toolbar grew a second row above the canvas: tools on row 1,
colours + width on row 2. Bottom row keeps the actions (Save/Copy/
Undo/Redo/Clear/Done).</li>
</ul>
]]></description>
    </item>
    <item>
      <title>v0.2.0</title>
      <link>https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.2.0</link>
      <guid isPermaLink="true">https://mpjhorner.github.io/ScreenshotUltra/changelog/#v0.2.0</guid>
      <pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[<h3 id="added-m2-annotation-editor-polish"><a class="anchor" href="#added-m2-annotation-editor-polish" aria-label="Anchor">#</a>Added — M2 (annotation editor + polish)</h3>
<ul>
<li><strong>Native annotation editor</strong>, now with <strong>multiple tools</strong>: Pen / Arrow /
Rectangle / Ellipse. Tool-picker buttons sit above the canvas;
shortcuts <code>P</code> / <code>A</code> / <code>R</code> / <code>E</code> switch tools. ⌘S save (overwrites the
original), ⌘C copy annotated PNG, ⌘Z undo, <strong>⌘⇧Z redo</strong>, ⌘W close.
Bottom toolbar mirrors actions plus a Clear button.</li>
<li>Arrow tool draws a calculated arrowhead at the tip; Rect/Ellipse use
drag-to-define-bounds; Pen is freehand. All four scale correctly when
saved back into the original image&#39;s pixel dimensions.</li>
<li>The Quick Tray&#39;s <strong>Edit</strong> button now opens the native editor (was
bouncing through Apple Preview).</li>
<li><strong>Timed fullscreen captures</strong> — Tray menu offers <code>Fullscreen in 3 / 5 / 10 s</code>.
Uses <code>screencapture -T</code> so the OS-level countdown runs.</li>
<li><strong>About window</strong> — <code>NSAlert</code> with version, description, and an
&quot;Open GitHub&quot; button.</li>
<li><strong>Panic crash handler</strong> writes a report to
<code>~/Library/Logs/ScreenshotUltra/crashes/&lt;timestamp&gt;.txt</code> and emits a
<code>panic</code> NDJSON event. No auto-send, ever (per plan §11).</li>
</ul>
<h3 id="fixed"><a class="anchor" href="#fixed" aria-label="Anchor">#</a>Fixed</h3>
<ul>
<li><code>scripts/install.sh</code> downloads the universal <code>.zip</code> from the release (was
expecting a <code>.dmg</code>, which we don&#39;t publish yet) and verifies the SHA-256
sidecar before installing.</li>
</ul>
<h3 id="added-m2-earlier-this-milestone"><a class="anchor" href="#added-m2-earlier-this-milestone" aria-label="Anchor">#</a>Added — M2 (earlier this milestone)</h3>
<ul>
<li><strong>Edit button</strong> on the Quick Tray — opens the capture in Apple
Preview for annotation (markup tools, signatures, redaction). A
placeholder for the native editor planned later in M2.</li>
<li><strong><code>[capture]</code> settings</strong>:<ul>
<li><code>include_cursor</code> — pass <code>-C</code> to <code>screencapture</code> so the mouse
cursor is baked into the image.</li>
<li><code>fullscreen_scope = &quot;main&quot;</code> (default) or <code>&quot;all&quot;</code> — main display
only vs. every connected display.</li>
</ul>
</li>
<li><strong>Documentation site</strong> under <code>site/</code> (mkdocs-material) — Install,
Quick start, Hotkeys, Capture modes, Sinks, Configuration, Logging,
Changelog. Deployed via <code>.github/workflows/pages.yml</code>.</li>
<li><strong>Settings hot-reload</strong> — <code>settings.toml</code> is watched in a background thread
and changes are applied within ~1 s. Invalid hotkeys keep the previous
binding (with a log message) instead of crashing.</li>
<li><strong>Open clipboard image</strong> (default <code>⌃⌥⌘E</code>) — pulls a PNG off the macOS
clipboard, saves it to the normal save folder, and runs it through the
Quick Tray.</li>
<li><strong>Release workflow</strong> — <code>.github/workflows/release.yml</code> builds a universal
(arm64 + x86_64) <code>.app</code>, zips it via <code>ditto</code>, and uploads to a tagged
GitHub release with a SHA-256 sidecar. Unsigned for now; signing in M6.</li>
<li><strong>Manual test plan</strong> at <code>tests/MANUAL.md</code> for release sign-off.</li>
<li><strong>Shell sink</strong> — <code>sinks.shell = &quot;scp $1 user@host:/path/&quot;</code> runs an
arbitrary shell command with the capture&#39;s file path as <code>$1</code>. Spawned
detached so a slow uploader can&#39;t stall capture.</li>
<li><strong>History NDJSON index</strong> at <code>&lt;save_folder&gt;/.screenshot-ultra/index.ndjson</code>,
one JSON line per capture. Searchable with <code>jq</code>/<code>fzf</code>/<code>grep</code>.</li>
<li><strong>Shutter sound</strong> plays <code>/System/Library/Sounds/Grab.aiff</code> when
<code>[general].play_shutter_sound = true</code> (the default).</li>
<li><strong>CLI flags</strong>: <code>--version</code>, <code>--help</code>, <code>--settings-path</code>,
<code>--print-defaults</code>. Useful for shell pipelines and onboarding.</li>
<li><strong>Window capture mode</strong> (default <code>⌃⌥⌘2</code>) — interactive window selection
via <code>screencapture -W</code>, with the window&#39;s drop shadow trimmed off.</li>
<li><strong>Pin-to-screen</strong> (default <code>⌃⌥⌘.</code>) — floating always-on-top window holding
the most recent capture. Multiple pins supported; each cascades 24 px
from the previous. Pin button added to the Quick Tray.</li>
<li><strong>Repeat last capture</strong> (default <code>⌃⌥⌘R</code>) — re-runs whatever mode you used
last with the same tray-or-silent behaviour.</li>
<li>Tray menu: <code>Edit settings.toml…</code>, <code>Reveal Log File</code>.</li>
<li>Tray actions for every capture mode plus pin/repeat, mirroring hotkeys.</li>
<li>22 unit tests across hotkey parsing, settings serialisation, capture
templating, and pin-window sizing.</li>
</ul>
<h3 id="added-m2-earlier"><a class="anchor" href="#added-m2-earlier" aria-label="Anchor">#</a>Added — M2 (earlier)</h3>
<ul>
<li>Quick Tray: a floating bottom-right window that appears after a capture
with Copy / Folder / Reveal / Pin / Discard buttons and auto-dismisses
after <code>quick_tray_timeout_ms</code> (default 6 s). Native <code>NSWindow</code> via
<code>objc2</code>; promotes the app to <code>.Accessory</code> activation policy on demand so
the window actually draws for <code>LSUIElement</code> background apps.</li>
<li>Silent capture variants (<code>silent_region</code>, <code>silent_fullscreen</code>,
<code>silent_window</code>) — separate hotkey slots that skip the tray and just
save+copy. Unbound by default; set them in <code>settings.toml</code> to enable.</li>
</ul>
<h3 id="added-m1-press-the-key"><a class="anchor" href="#added-m1-press-the-key" aria-label="Anchor">#</a>Added — M1 &quot;Press the key&quot;</h3>
<ul>
<li>Menu-bar agent (<code>LSUIElement = true</code>) with a status-bar icon and basic menu.</li>
<li>Global hotkey listener with rebindable accelerators in <code>settings.toml</code>.</li>
<li>Region capture (default <code>⌃⌥⌘1</code>) — interactive marquee, Esc cancels.</li>
<li>Fullscreen capture (default <code>⌃⌥⌘3</code>) — main display, single file.</li>
<li>Disk sink: writes to <code>~/Pictures/ScreenshotUltra/</code> with templated filenames.</li>
<li>Clipboard sink: PNG/JPG goes straight onto the macOS clipboard.</li>
<li>NDJSON event log at <code>~/Library/Logs/ScreenshotUltra/log.ndjson</code>.</li>
<li><code>settings.toml</code> with first-run defaults at
<code>~/Library/Application Support/ScreenshotUltra/settings.toml</code>.</li>
<li><code>make app</code> builds a <code>.app</code> bundle in <code>dist/</code> from <code>mac/Info.plist</code>.</li>
<li><code>scripts/install.sh</code> one-liner (release download with source-build fallback).</li>
<li>GitHub Actions CI (fmt + clippy + tests + release build on macOS 14).</li>
</ul>
<h3 id="notes"><a class="anchor" href="#notes" aria-label="Anchor">#</a>Notes</h3>
<ul>
<li>The capture backend in M1 shells out to <code>/usr/sbin/screencapture</code>. A native
ScreenCaptureKit backend is planned in a later milestone.</li>
<li>Clipboard copy uses <code>osascript</code> for reliable image transfer; this will move
to a native path alongside the ScreenCaptureKit backend.</li>
</ul>
]]></description>
    </item>
  </channel>
</rss>
