From 955026278bda8ce2bc4b0432c797c0942c7fa775 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Wed, 12 Aug 2026 18:00:58 +0800 Subject: [PATCH] docs(release): trim the v2.1.0 notes and correct Relationships everywhere The v2.1.0 notes ran 400 lines, most of it cause analysis a reader of release notes did not ask for: why a dyld dependency cycle emptied the protocol list, which csops status word separates two injection target classes, how many event loops a losing MCP instance kept resident, and a Build & Tooling section about dependency pins and branching docs. Fixes now state what was broken in a line each, features state what you can do with them, and the whole file is 169 lines. The upgrade note about the helper reinstall moved up to the header, since it is the one thing in here that asks the reader to do something. Also carries the Relationships correction back into the beta.2 and RC.1 notes, where the inbound/outbound reference wording originated before being copied forward. --- Changelogs/v2.1.0-RC.1.md | 9 +- Changelogs/v2.1.0-beta.2.md | 10 +- Changelogs/v2.1.0.md | 451 +++++++++--------------------------- 3 files changed, 120 insertions(+), 350 deletions(-) diff --git a/Changelogs/v2.1.0-RC.1.md b/Changelogs/v2.1.0-RC.1.md index bbd71453..7a5c6f09 100644 --- a/Changelogs/v2.1.0-RC.1.md +++ b/Changelogs/v2.1.0-RC.1.md @@ -14,7 +14,7 @@ the stable channel with no reinstall: this build supersedes the last beta. - **Multi-tab content navigation** with a window tab bar and per-tab history - **Generic type specialization** — inspect Swift generics with concrete arguments filled in - **Background indexing** — the sidebar is ready when you click into it, not seconds later -- **Inspector → Relationships** — see who references a type, and what a type depends on +- **Inspector → Relationships** — a class's subclasses, a protocol's conforming types - **Batch export** interfaces for multiple images in one pass - **Theme settings** with a data-driven color model and a built-in Xcode preset - **Jump to Definition** for Swift type references @@ -42,9 +42,10 @@ interface alongside the original unspecialized form. silently producing garbage. #### Inspector → Relationships -A new **Relationships** tab sits between *Hierarchy* and *Specialization*. It -lists inbound references (who uses this type) and outbound references (what -this type depends on), drawn across every binary the document has loaded. +A new **Relationships** tab sits between *Hierarchy* and *Specialization*. +Select a class to list its direct subclasses; select a protocol to list the +types conforming to it. Results are gathered across every indexed image, not +just the one defining the type. #### Jump to Definition for Swift types Command-click a Swift type reference in the content view to navigate to its diff --git a/Changelogs/v2.1.0-beta.2.md b/Changelogs/v2.1.0-beta.2.md index 0cbb7644..35db79a4 100644 --- a/Changelogs/v2.1.0-beta.2.md +++ b/Changelogs/v2.1.0-beta.2.md @@ -14,12 +14,12 @@ This is a `beta` build — only clients that opted in via ### Inspector → Relationships A new **Relationships** tab sits between *Hierarchy* and *Specialization* and -shows where a type is referenced across the binaries you have loaded. +shows what else in the loaded binaries builds on a type. -- Surfaces inbound references (who uses this type) and outbound references - (what this type depends on), drawn from a dedicated relationships engine. -- Works across binaries: if the indexed images include the consumers of a - type, they show up regardless of which dylib defines it. +- Select a class to list its direct subclasses; select a protocol to list the + types conforming to it. +- Works across binaries: matches show up regardless of which dylib defines + them, as long as their image has been indexed. - The list lays out cleanly even when a type has no relationships — an empty state explains why instead of showing a blank tab. diff --git a/Changelogs/v2.1.0.md b/Changelogs/v2.1.0.md index 5fdf21ac..f14d68aa 100644 --- a/Changelogs/v2.1.0.md +++ b/Changelogs/v2.1.0.md @@ -1,17 +1,11 @@ # v2.1.0 -The first stable release on the **2.1** line, built up over nine public betas -and three release candidates and consolidated here. If you have been running -the stable channel since v2.0.1, everything below is new to you. +The first stable release on the **2.1** line, built up over nine betas and +three release candidates. If you have been on the stable channel since v2.0.1, +everything below is new to you. -Headline work spans runtime inspection, background indexing, multi-tab -navigation, appearance, and code injection — plus a late pass over what the app -looks like *while* it is working, which removed the flashes and grey blinks that -appeared whenever you clicked from one type to another. - -One upgrade step needs your attention: the privileged helper daemon reinstalls -itself on first launch. See **Upgrade notes** at the bottom — this applies to -release-candidate users too, not only to those coming from 2.0.x. +**On first launch the privileged helper reinstalls itself** — one admin prompt, +then a restart. This applies to RC users too; see *Upgrade notes*. --- @@ -19,13 +13,13 @@ release-candidate users too, not only to those coming from 2.0.x. - **Multi-tab content navigation** with a window tab bar and per-tab history - **Generic type specialization** — inspect Swift generics with concrete arguments filled in -- **Background indexing** — the sidebar is ready when you click into it, not seconds later -- **Inspector → Relationships** — a class's subclasses and a protocol's conforming types, gathered across every indexed binary +- **Background indexing** — the sidebar is ready when you click into it +- **Inspector → Relationships** — a class's subclasses, a protocol's conforming types - **Batch export** interfaces for multiple images in one pass -- **Theme settings** with a data-driven color model and a built-in Xcode preset +- **Theme settings** with a built-in Xcode preset - **Jump to Definition** for Swift type references - **Line numbers** in the content view -- **Injection into strict-seatbelt daemons** via `mach_vm_remap`, and into Apple applications via the same path +- **Injection into strict-seatbelt daemons and Apple applications** - **iOS Simulator runtime installer** built in --- @@ -34,342 +28,125 @@ release-candidate users too, not only to those coming from 2.0.x. ### Runtime inspection -#### Generic Type Specialization -Pick any Swift generic from the sidebar and a new **Specialize…** sheet lets -you fill in its type parameters. The Inspector renders the fully specialized -interface alongside the original unspecialized form. - -- Nested generics work — if an argument is itself generic, you can drill in - and fill those parameters in the same sheet. -- Type definitions can come from a different binary than the generic itself, - so you can specialize types in framework *A* with a model from framework - *B* loaded in the same document. -- Invalid combinations are caught up front with a clear error instead of - silently producing garbage. - -#### Inspector → Relationships -A new **Relationships** tab sits between *Hierarchy* and *Specialization*, -answering the question a single interface listing cannot: what else in the -loaded binaries builds on this type. - -- Select a **class** and it lists that class's direct subclasses. -- Select a **protocol** and it lists the types that conform to it. - -Both are unioned across every indexed image rather than looked up in the image -that defines the target, so a subclass or conformer living in a different -binary still shows up. Images that are loaded but not yet indexed contribute -nothing, so the list fills in as background indexing catches up. - -Objective-C and Swift classes and protocols are covered, including classes -bridged between the two. Other kinds — structs, enums, categories — have no -relationships to show. - -#### Jump to Definition for Swift types -Command-click a Swift type reference in the content view to navigate to its -definition, the same way you already could for Objective-C. - -#### Enum layout fixed-bit masks -The Swift enum-layout view now surfaces fixed-bit masks for each case, giving -you the exact bit pattern the runtime uses to discriminate cases without -having to read it out of the raw metadata. +**Generic Type Specialization** — pick a Swift generic from the sidebar and the +new **Specialize…** sheet lets you fill in its type parameters; the Inspector +then renders the fully specialized interface alongside the original. Nested +generics can be drilled into in the same sheet, and arguments may come from a +different binary than the generic itself. ---- +**Inspector → Relationships** — a new tab between *Hierarchy* and +*Specialization*. Select a class to list its direct subclasses; select a +protocol to list the types conforming to it. Results are gathered across every +indexed image, not just the one defining the type, so matches in other binaries +show up too — and the list fills in as background indexing catches up. Covers +Objective-C and Swift classes and protocols. -### Indexing & batch operations +**Jump to Definition for Swift types** — Command-click a Swift type reference +in the content view to navigate to its definition, as you already could for +Objective-C. -#### Background Indexing -Once a document opens, RuntimeViewer walks the dylib dependency graph and -starts indexing related binaries in the background, so by the time you click -into them they are ready to inspect. - -- A new toolbar progress popover shows what is currently indexing, lets you - cancel any individual batch, and keeps a history of recent runs. -- Selecting a binary that is still in the queue immediately bumps it to the - front. -- Background work runs at low priority and never makes the main UI stutter. -- A new **Settings → Indexing** page controls how aggressively background - indexing runs (or turns it off entirely). - -#### Always Index list + master switch -The Indexing settings page grew a master switch plus two independently -toggleable sub-modes: - -- **Heuristic Discovery** — main-executable BFS at document open and engine - swap, with configurable depth (1–5). -- **Always Index** — a user-editable list where each row pins a single image - (full path or file-name shorthand). Toggle **Follow Dependencies** on a row - to also walk its dependency closure. - -The batches popover groups its active and history sections by reason -(*Heuristic Discovery* / *Always Index* / *Manual*) under collapsible headers. - -#### Batch Export -Export the interfaces of multiple images in one pass without opening each -document individually. - -- New **File → Export Multiple Images…** entry walks you through image - selection, format selection, and destination directory in a single sheet. -- Per-language format choices (Objective-C `.h`, Swift surface, etc.) are - remembered across runs. -- The completion summary uses stat cards to show how many files succeeded, - failed, or were skipped, and lists each failure individually so you can - re-run only what broke. -- Long Swift-mangled names no longer produce APFS `NAME_MAX` errors mid-batch. - -#### iOS Simulator Runtime Installer -A new built-in installer downloads and installs missing iOS Simulator runtime -images directly from the app — no more chasing them down through Xcode. +**Enum layout fixed-bit masks** — the Swift enum-layout view now shows the exact +bit pattern the runtime uses to discriminate each case. ---- +### Indexing & batch operations -### Navigation & windowing +**Background Indexing** — RuntimeViewer walks the dylib dependency graph after a +document opens and indexes related binaries in the background, so they are ready +by the time you click into them. A toolbar popover shows what is indexing, lets +you cancel individual batches, and keeps a history; selecting a queued binary +bumps it to the front. -#### Multi-Tab Content Navigation -The main window now hosts multiple content tabs, so several runtime objects -can be open side by side instead of replacing one another in a single pane. - -- **Window tab bar** drives content navigation directly from the title bar. -- **Open in New Tab** is available both from content-view type links and from - sidebar rows, with a matching menu icon. -- Each tab keeps its own navigation timeline. -- Tabs are keyed by identity, and **Close Tab / Close Window** with **⌘W** - re-bind against the current key window automatically. - -#### Selection History & Navigation Menu -The toolbar drives back / forward navigation off a true selection-history -cursor, the way Xcode and Finder do. - -- Picking a sidebar item, drilling into a type, and switching between - Hierarchy / Relationships / Specialization tabs all push onto the same - stack. -- A **Navigation History** menu exposes the full per-tab timeline. -- The buttons enable / disable based on what's actually in the history, so - you never end up clicking back into nothing. -- The active inspector tab is preserved across selections. - -#### Smarter Sidebar Expansion -- Single-clicking a non-leaf row now expands it (matching the rest of macOS). -- A new setting caps how deep a double-click expands an entire subtree, so - you can preview a framework without exploding hundreds of children. - -#### Sidebar Grouping & Filter Scope -- Runtime objects are grouped into kind sections (classes, protocols, - categories…) instead of a flat list. -- A new **Filter Scope** popover in the sidebar lets you prefilter by kind - and property before typing, so the search corpus stays focused. - -#### Quit After Closing Last Window -**Settings → General → Windows** gains a *Quit After Closing Last Window* -toggle. It is off by default, which is the existing behaviour: the app stays -resident with no window open and a Dock icon click brings a window back. Turn -it on to have RuntimeViewer behave like a single-purpose utility that exits -with its last window. +**Settings → Indexing** — a new page with a master switch and two independent +modes: *Heuristic Discovery* (dependency BFS from the main executable, +configurable depth 1–5) and *Always Index* (a list of pinned images, each able +to follow its own dependencies). ---- +**Batch Export** — **File → Export Multiple Images…** exports the interfaces of +many images in one pass. Format choices are remembered across runs, and the +completion summary lists each failure individually so you can re-run only what +broke. -### Appearance & editing +**iOS Simulator Runtime Installer** — download and install missing Simulator +runtime images from within the app. -#### Theme Settings -A new **Theme** settings panel with a data-driven color model. Pick from -built-in presets (including an Xcode-matching one) or edit colors directly. -Themes drive the content view, inspector, and all syntax-highlighted -surfaces from a single source, and changes apply live. +### Navigation & windowing -#### Line Numbers in the Content View -A line-number gutter runs down the left edge of the interface text view, -handy for cross-referencing decompiled or exported output. +**Multi-Tab Content Navigation** — the main window hosts multiple content tabs, +so several runtime objects stay open side by side. **Open in New Tab** works +from both content-view type links and sidebar rows, and each tab keeps its own +navigation timeline. -#### Content-shaped loading placeholders -Panes that fetch their content asynchronously now stand in a placeholder shaped -like the content it replaces, instead of leaving the previous type's data on -screen until the new one arrives. +**Selection History** — back / forward run off a real history cursor, the way +Xcode and Finder do, with a **Navigation History** menu exposing the full +per-tab timeline. The active inspector tab is preserved across selections. -The placeholder is deliberately hard to catch sight of. Engine calls that hit a -warm cache return in single-digit milliseconds, so anything drawn the moment -work starts would appear and vanish inside one frame — a flash in its own right. -Work that finishes quickly now shows no placeholder at all, and a placeholder -that does appear stays long enough to read, measured from when it appeared, so -genuinely slow work is never delayed further. +**Sidebar** — runtime objects are grouped into kind sections instead of a flat +list; a **Filter Scope** popover prefilters by kind and property before you +type; single-clicking a non-leaf row expands it; and a setting caps how deep a +double-click expands a subtree. ---- +**Quit After Closing Last Window** — a new **Settings → General → Windows** +toggle, off by default. On, the app exits with its last window instead of +staying resident. -### Code injection +### Appearance -#### Injection into strict-seatbelt daemons and Apple applications -System daemons that opt into the strict seatbelt (e.g. `sharingd`, `rapportd`) -can now be injected via a `mach_vm_remap`-based path, which previously refused -to load the runtime dylib. Apple applications, which enforce AMFI library -validation, take the same path — see the corresponding bug fix below. -Everyday injection targets continue to work unchanged; the new path only kicks -in when the standard route is blocked. +**Theme Settings** — a new panel with built-in presets (including an +Xcode-matching one) and directly editable colors. One source drives the content +view, inspector, and every syntax-highlighted surface, and changes apply live. ---- +**Line Numbers** — a gutter down the left edge of the interface text view. -## Improvements +**Loading placeholders** — panes that fetch content asynchronously now show a +placeholder shaped like the content it replaces, instead of leaving the previous +type's data on screen. Work that finishes quickly shows no placeholder at all. -- Popovers (generation options, MCP status, background-indexing, type - picker) now size themselves to their content instead of defaulting to a - fixed 500×500 box. -- The Specialization type picker no longer blocks the UI when a generic - parameter has a wide constraint (10k+ candidates) — the list is built - off the main thread with a loading indicator, and rows are constructed - lazily as you scroll. -- Sidebar filtering skips the debounce when the query is empty, so - clearing the field feels instantaneous. -- The Mirror menu icon was refreshed and the toolbar no longer shows an - empty overflow chevron on macOS 15 when its items are hidden. +### Code injection + +**Strict-seatbelt daemons and Apple applications** — targets that refused the +standard injection path (`sharingd`, `rapportd`, and every Apple application, +which enforces library validation) now go through a `mach_vm_remap`-based path. +Everyday targets are unchanged; the new path only engages where the standard +route cannot work. --- ## Bug fixes -### Objective-C protocols reappear for UIKitCore, Foundation, and friends - -Opening UIKitCore showed an empty protocol list. Foundation showed no -`NSCoding`, `NSCopying`, or `NSFastEnumeration`. Classes, categories, and C -structs were listed normally, so the image itself parsed fine — only protocols -went missing, and they went missing entirely rather than partially. - -The cause was a protocol-ownership heuristic. Because the compiler emits a full -`protocol_t` into every image that saw a `@protocol` declaration at compile -time, `__objc_protolist` carries plenty of protocols an image merely imported. -The heuristic dropped a protocol whenever some other carrier of it sat in the -image's transitive dependency closure — which relied on an image never -appearing in its own closure. dyld's upward dependencies -(`LC_LOAD_UPWARD_DYLIB`) exist precisely to express cycles, so -UIKitCore ↔ PrintKitUI/ShareSheet and Foundation ↔ CoreFoundation each put an -image inside its own closure. Every protocol it carried was then classified as -imported, and the listing came back empty. - -Nothing in the Mach-O format records a defining image for a protocol, so -attribution can only ever be a heuristic — and this one failed silently, with -no error to hint that anything had been dropped. The filter is gone; -`__objc_protolist` is now listed verbatim. Some imported protocols reappear as -noise you can see and judge for yourself, which is the better failure mode: a -protocol that is silently absent is not. - -### Code injection picks its path from the whole rule - -Attaching to Music failed silently — nothing loaded on the target side, and no -error was reported. Two causes stacked. Routing between the two injection paths -consulted only the target's sandbox, which catches strict-seatbelt daemons but -says nothing about AMFI library validation; and Music, like every Apple -application, enforces library validation, which refuses a developer-signed -payload regardless of what the sandbox allows. The `dlopen` it was routed onto -could never have loaded anything, while the injector's shellcode reported -success anyway. - -The decision now lives in the helper daemon, which consults both the seatbelt -predicate and the code-signing flag, so Apple applications are routed onto the -`mach_vm_remap` path instead of one that cannot work. The investigation — the -`csops` status words separating the two target classes, the controlled -experiment isolating library validation from the payload's signature — is -written up in `Documentations/ResolvedIssues/`. - -### The Inspector no longer blanks when you switch types - -Clicking from one runtime object to another emptied the Inspector's list and -refilled it once the query returned. Relationships unions its results across -every indexed image, so that gap was long enough to read as a flash rather than -a redraw. - -The Inspector had been reusing its view controllers, but it still built a fresh -ViewModel per object and rebound it, which tore down the table's data binding -and installed a fresh one starting from an empty list. The tab ViewModels are -now bound once and handed the new object instead. Re-entering the same object — -closing a tab, moving the history cursor — short-circuits entirely, so it -neither refetches nor flashes a placeholder over content that is already -correct. - -This also fixed a real defect hiding behind the visual one: the old load had no -cancellation, so on rapid selection an earlier query could return late and -overwrite a newer object's rows. - -### Sidebar selection no longer blinks grey - -Two separate causes, both of which repainted the sidebar's selected row in the -inactive grey for a frame or two: - -- **Clicking a related type stole keyboard focus.** A row highlight is drawn - unemphasized whenever its table is not the first responder, so the Inspector - taking focus greyed out the sidebar row the user was actually looking at — for - focus the app handed straight back some 50ms later. The Relationships and - Specializations lists exist only to be clicked through, so they now refuse - first responder and paint no highlight of their own. Clicking, selecting and - navigating are unaffected; only arrow-key traversal inside those two lists is - gone, and every arrow press there navigated away anyway. -- **Jumping to a type selected the row before scrolling to it.** An outline view - materializes a row only as it scrolls into view and applies the emphasized - selection style after that, so the target row was drawn once in grey and - flipped to the accent colour on the next frame. Scrolling first — and forcing - the layout the scroll would otherwise defer — puts both in the same frame. - -### The tab strip no longer flashes when the tab set changes - -Changing the inspected kind rebuilds which Inspector tabs exist — selecting a -protocol after a class drops the Hierarchy tab. The strip used to remove every -tab and add the new set back, which swapped the visible view several times -inside one runloop pass. Tabs that survive the change now keep their view -throughout, and only tabs that genuinely appear or disappear cost anything. - -### Clicking the Dock icon reopens a window - -AppKit's built-in reopen handling only creates a window when it sees no visible -window at all, and it counts every window the app owns. With Settings or a -Sparkle update alert still on screen, clicking the Dock icon after closing every -document window therefore appeared to do nothing. RuntimeViewer now decides on -document windows alone, restoring a minimized or ordered-out one before falling -back to a new untitled document. - -### Attached daemons show their real icon - -Attaching to a daemon such as `launchservicesd` left the source menu showing an -App Store placeholder symbol. The icon cache is populated only from Launch -Services registered GUI applications, so a daemon never landed in it and every -lookup fell through to the placeholder. Daemons now fall back to the generic -executable icon — the same one the attach picker already shows for them. - -### A second instance no longer reports a phantom MCP server - -Binding the MCP bridge port fails whenever another RuntimeViewer instance -already holds it. That failure used to stop at a log line: the losing instance -still wrote the port file, still reported itself as running, and kept its fully -allocated network transport — 28 event loops, 56 threads, roughly 21 MiB — -resident for the lifetime of the process. It now tears the transport down, -truthfully reports itself stopped, and leaves the winning instance's port file -alone. A regression test covers the contention scenario end to end. - -### Smaller fixes - -- **Bonjour crash on batched receives.** Inspecting an iOS / visionOS / - Apple TV target over Bonjour, or pushing a batch-export through a - Bonjour connection, no longer crashes when a single network read - carries many small frames. -- **Attached-app and Mac Catalyst inspection.** The Inspector no longer - fails with *"An unknown error occurred"* when the runtime engine is - reached through a proxy hop; loading progress now streams across the - hop instead of getting stuck at *"Preparing…"*. -- **Nested sidebar specializations** keep their identity across sidebar - rebuilds instead of collapsing or re-shuffling. -- **Text view re-entry** rebuilds a fresh content view when returning - from another scene, fixing a case where the pane appeared blank. +- **Objective-C protocols reappear for UIKitCore, Foundation, and friends.** + Images caught in a dyld dependency cycle listed no protocols at all. +- **Injecting into Apple applications works.** Attaching to Music and its peers + failed silently — routed onto a path that could never load the payload, with + no error reported. +- **The Inspector no longer blanks when you switch types.** It also no longer + lets a slow query for an earlier type overwrite a newer one's results. +- **Sidebar selection no longer blinks grey** when you click a related type or + jump to a definition. +- **The tab strip no longer flashes** when switching to a kind with a different + set of Inspector tabs. +- **Clicking the Dock icon reopens a window** even when Settings or an update + alert is still on screen. +- **Attached daemons show their real icon** instead of an App Store placeholder. +- **A second instance no longer reports a phantom MCP server**, and no longer + keeps ~21 MiB of network transport resident after losing the port. +- **Bonjour no longer crashes on batched receives** when inspecting an iOS / + visionOS / Apple TV target, or exporting through a Bonjour connection. +- **Attached-app and Mac Catalyst inspection** no longer fails with *"An unknown + error occurred"*, and loading progress streams across the proxy hop instead of + sticking at *"Preparing…"*. +- **Nested sidebar specializations** keep their identity across sidebar rebuilds. +- **Returning from another scene** no longer leaves the content pane blank. - **Export module metadata resolution** fixed (#72). - **The Xcode theme preset** gained a light-mode selection color. -- **The Specialization type picker popover** no longer animates out of place. -- Numerous smaller stability fixes across the sidebar, inspector, and - document lifecycle. ---- - -## Build & Tooling +## Improvements -- Both package manifests agree on the same `swift-helper-service` floor, so the - two halves of the app can no longer resolve different helper protocols. -- The Core package pins every remote dependency it parses Mach-O with, so a - fresh checkout reproduces a known-good set instead of resolving freely. -- The release process, the documentation index, and the branching model are - written down under `Documentations/`. -- GitHub Releases are titled with the bare version tag. +- Popovers size themselves to their content instead of a fixed 500×500 box. +- The Specialization type picker no longer blocks the UI when a generic + parameter has 10k+ candidates, and its popover no longer animates out of place. +- Clearing the sidebar filter is instantaneous — no debounce on an empty query. +- The Mirror menu icon was refreshed, and the toolbar no longer shows an empty + overflow chevron on macOS 15. --- @@ -379,22 +156,14 @@ alone. A regression test covers the contention scenario end to end. - **MCP features**: macOS 15+ - **iOS variant**: iOS 18+ ---- - ## Upgrade notes -- **Helper Service reinstalls itself** — the privileged helper daemon moves to - protocol version `1.5.0`. On first launch after upgrade you will be prompted - for admin credentials once and then asked to restart the app. - - This applies **even if you were running `v2.1.0-RC.3`**. The daemon-side - change that routes Apple applications onto the `mach_vm_remap` injection path - shipped in RC.3, but the protocol version did not move with it, so an already - installed helper was never replaced and RC.3 users have been running the old - daemon. Bumping it here is what actually delivers that fix. - -- **Beta and RC channel users** — this build supersedes `v2.1.0-RC.3`; you can - switch back to the stable channel with no reinstall of the app itself. - -- **From v2.0.x** — no manual migration steps. Bookmarks, settings, and - connections carry over as-is. +- **The helper daemon reinstalls itself** on first launch — one admin prompt, + then a restart. This happens **even if you were running `v2.1.0-RC.3`**: the + daemon-side fix that routes Apple applications onto the working injection path + shipped in RC.3, but the version the app checks against did not move with it, + so installed helpers were never replaced. This release delivers it. +- **From a beta or RC** — this build supersedes `v2.1.0-RC.3`; switching back to + the stable channel needs no reinstall of the app itself. +- **From v2.0.x** — no migration steps. Bookmarks, settings, and connections + carry over as-is.