Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5300127
chore(deps): bump RuntimeViewerCore Package.resolved
Mx-Iris May 27, 2026
0e0cd5e
feat(batch-export): export multiple images from File menu
Mx-Iris May 27, 2026
d6aec90
chore(deps): bump RuntimeViewerCore Package.resolved
Mx-Iris May 28, 2026
629c6b2
refactor(view-controllers): drop AppKitViewController, add contentInsets
Mx-Iris May 28, 2026
39d9070
refactor(specialization): swap refusesFirstResponder for focusRingType
Mx-Iris May 28, 2026
6e3080d
refactor(batch-export): always offer ObjC/Swift format pickers
Mx-Iris May 28, 2026
105c9f7
chore(deps): force-enable local UIFoundation and refresh Package.reso…
Mx-Iris May 28, 2026
95263d2
refactor(core): unify engine command dispatch via RuntimeEngineRequest
Mx-Iris May 28, 2026
2dde378
refactor(ui): suppress row selection via highlightStyle instead of de…
Mx-Iris May 28, 2026
bb0d250
refactor(batch-export): redesign completion summary with stat cards
Mx-Iris May 28, 2026
673824c
docs(claude): document LayerBackedView conventions and borderPosition…
Mx-Iris May 28, 2026
5755e01
feat(exporting): persist per-language export format selections
Mx-Iris May 29, 2026
7d388d9
refactor(ui): animate in-progress icons with rotate symbol effect
Mx-Iris May 29, 2026
410da5a
refactor(batch-export): back completion rows with LayerBackedTableCel…
Mx-Iris May 29, 2026
6029e79
refactor(batch-export): collapse image selection driver chain
Mx-Iris May 29, 2026
537a333
chore(deps): refresh Package.resolved for forks and new packages
Mx-Iris May 31, 2026
7f95827
fix(core): decode runtimeObjectsInImage as ObjectsInImageRequest
Mx-Iris May 31, 2026
aef275e
fix(core): clamp export file names below APFS NAME_MAX
Mx-Iris May 31, 2026
298be7e
feat(batch-export): surface per-object export failures
Mx-Iris May 31, 2026
3056edf
feat(navigation): drive toolbar navigation off selection history cursor
Mx-Iris Jun 1, 2026
192c3ba
refactor(cell-view): hoist RuntimeObjectCellView into Base group
Mx-Iris Jun 1, 2026
85b79da
refactor(ui): use stack view's semantic alignment names
Mx-Iris Jun 1, 2026
4ab21e8
chore(deps): refresh RuntimeViewerCore Package.resolved
Mx-Iris Jun 1, 2026
f326e7a
refactor(deps): default LocalSearchPath isEnabled to usingLocalDepend…
Mx-Iris Jun 2, 2026
9901581
refactor(sidebar): adopt rx.nodes(options:) for reorderable outline v…
Mx-Iris Jun 2, 2026
ba4eb0f
chore(release): prepare v2.1.0-beta.2
Mx-Iris Jun 2, 2026
5335d1a
fix(application): gate macOS-only selectionRouter usage for iOS build
Mx-Iris Jun 2, 2026
6ac542f
refactor(application): make selection routing and Settings cross-plat…
Mx-Iris Jun 2, 2026
95baeab
refactor(application): restore SelectionRouter Router conformance wit…
Mx-Iris Jun 2, 2026
8bf28ef
fix(uikit): use .center alignment on VStackView
Mx-Iris Jun 2, 2026
5bfeaa5
fix(batch-export): preserve checkbox state across scroll via reactive…
Mx-Iris Jun 2, 2026
d29954a
feat(sidebar): enable type-select keyboard navigation in outline
Mx-Iris Jun 3, 2026
92c0fc9
refactor(packages): unify local-dependency switch across Core/Package…
Mx-Iris Jun 1, 2026
bec9f4b
fix(communication): prevent stack overflow on batched receives
Mx-Iris Jun 3, 2026
6a795d7
chore(release): prepare v2.1.0-beta.3
Mx-Iris Jun 3, 2026
a6824a7
chore(release): bump MachOSwiftSection to 0.12.0-beta.3
Mx-Iris Jun 3, 2026
18210c6
chore(deps): bump remote pins to latest tags
Mx-Iris Jun 3, 2026
1731255
fix(release): force-checkout when restoring appcast on detached HEAD
Mx-Iris Jun 3, 2026
97f3a26
chore(deps): drop unused remote pins from Package.resolved
Mx-Iris Jun 4, 2026
453644c
feat(indexing): add user-configured always-index image list
Mx-Iris Jun 4, 2026
6849d78
refactor(attach-process): reuse a single AttachToProcessViewControlle…
Mx-Iris Jun 4, 2026
28fd4c4
fix(sidebar): debounce type-select navigation to skip intermediate loads
Mx-Iris Jun 4, 2026
0fdca62
fix(communication): revert @Mutex macro back to manual Mutex<T>
Mx-Iris Jun 4, 2026
8d8e5ed
chore(release): prepare v2.1.0-beta.4
Mx-Iris Jun 4, 2026
f7e5fdf
feat: Always indexing
Mx-Iris Jun 4, 2026
aa8ba43
revert(communication): roll back bec9f4b stack-overflow guards
Mx-Iris Jun 4, 2026
e3077fd
docs(changelog): note beta.4 known-issue on remote-Server background …
Mx-Iris Jun 4, 2026
1d9becf
fix(communication): per-round-trip nonce + swallow decode failures
Mx-Iris Jun 5, 2026
b276703
fix(indexing): unblock remote-engine background indexing
Mx-Iris Jun 5, 2026
786df06
feat(indexing): split Background Indexing into master + heuristic + c…
Mx-Iris Jun 6, 2026
8b14323
refactor(engine): expose imageNodes as nonisolated read-only property
Mx-Iris Jun 6, 2026
5b97082
chore: silence unused result + drop stale OpenUXKit local-path switches
Mx-Iris Jun 6, 2026
fe2edd0
chore(deps): bump UIFoundation 0.10.0→0.10.2, RxAppKit 0.4.0→0.5.0
Mx-Iris Jun 6, 2026
a766ec0
chore(deps): bump swift-dependencies 1.12.0→1.13.0
Mx-Iris Jun 6, 2026
4132fb8
chore(release): prepare v2.1.0-beta.5
Mx-Iris Jun 6, 2026
a921e4c
chore: update appcast for v2.1.0-beta.5
Mx-Iris Jun 6, 2026
4f7a98d
Revert "chore: update appcast for v2.1.0-beta.5"
Mx-Iris Jun 6, 2026
17bdb6f
refactor: hoist batch-export row models onto shared CellViewModel base
Mx-Iris Jun 7, 2026
8a81824
chore(ios): declare encryption status and local network usage in Info…
Mx-Iris Jun 7, 2026
dc16cca
chore(deps): bump MachOObjCSection 0.7.102→0.7.103
Mx-Iris Jun 8, 2026
d437c22
refactor(engine): keep transport details out of the engine
Mx-Iris Jun 8, 2026
b53fa7c
style(indexing): normalize trailing commas and brace placement
Mx-Iris Jun 8, 2026
9a3716b
Fix export module metadata resolution (#72)
Kyle-Ye Jun 9, 2026
ea9feaa
Preserve runtime specialization tree identity
Kyle-Ye Jun 9, 2026
2365853
Preserve nested sidebar specializations
Kyle-Ye Jun 9, 2026
27f8a20
refactor(comparable): keep comparableDefinition on legacy some Compar…
Mx-Iris Jun 10, 2026
039cd21
refactor(sidebar): move specialization identity from RuntimeObject to…
Mx-Iris Jun 10, 2026
9b955da
fix(swift-section): register derived nested specialization under dedi…
Mx-Iris Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion ArchiveScript.sh
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,12 @@ if $COMMIT_PUSH; then
cp docs/appcast.xml "$tmp_appcast"
run git fetch origin main
APPCAST_BRANCH="appcast-for-$VERSION_TAG"
run git checkout -B "$APPCAST_BRANCH" origin/main
# Archive runs `xcodebuild -resolvePackageDependencies`, which mutates
# the workspace's Package.resolved. Those changes do NOT belong in the
# appcast PR and would otherwise block `git checkout` with a "local
# changes would be overwritten" abort. docs/appcast.xml is safely in
# $tmp_appcast and gets re-applied below, so a forced checkout is fine.
run git checkout -f -B "$APPCAST_BRANCH" origin/main
cp "$tmp_appcast" docs/appcast.xml
rm -f "$tmp_appcast"
if git diff --quiet docs/appcast.xml; then
Expand Down
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,20 @@ override func setupBindings(for viewModel: MyViewModel) {
| `VStackView(alignment:spacing:) { ... }` | `NSStackView(orientation: .vertical)` |
| `HStackView(spacing:) { ... }` | `NSStackView(orientation: .horizontal)` |
| `ScrollView()` | `NSScrollView()` |
| `final class XxxView: LayerBackedView` | `final class XxxView: NSView` (when it needs `cornerRadius` / border / `backgroundColor` / shadow) |

**Layer-backed views** — any custom AppKit view that needs layer-level visuals (rounded corners, border, background color, shadow) MUST inherit `UIFoundationAppKit.LayerBackedView`, never raw `NSView` with hand-rolled `wantsLayer = true` + `layer?.cornerRadius / layer?.borderColor / layer?.backgroundColor = ....cgColor`. The base class already sets `wantsLayer + layerContentsRedrawPolicy = .onSetNeedsDisplay` and centralizes everything in `updateLayer()`, so:

- Assign the exposed `NSColor?` properties directly — `backgroundColor = NSColor(light:dark:)`, `borderColor = ...` — **without** `.cgColor`. Dynamic colors re-resolve on appearance change automatically; you do NOT need to override `viewDidChangeEffectiveAppearance`.
- One-time setup goes in `override func setup()` (called by `commonInit`); first-layout work goes in `override func firstLayout()`. Don't repeat `wantsLayer = true` in `init`.
- Available properties: `cornerRadius`, `borderWidth`, `borderColor`, `borderPositions`, `borderLocation`, `borderInsets`, `backgroundColor`, `shadowColor`, `shadowOpacity`, `shadowOffset`, `shadowRadius`, `shadowPath`.
- **Gotcha — `borderPositions` defaults to `[]`, so the border won't render** even with non-zero `borderWidth` + non-nil `borderColor`. To draw a full rounded border you MUST set `borderPositions = .all`:
```swift
cornerRadius = 8
borderWidth = 1
borderColor = NSColor(light: ..., dark: ...)
borderPositions = .all // ← otherwise the previous 3 lines are ignored
```

**View initialization** — `.then {}` returns the configured object (for assignment):
```swift
Expand Down
112 changes: 112 additions & 0 deletions Changelogs/v2.1.0-beta.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# v2.1.0-beta.2

Second public preview of the **2.1** line. Headline additions: a new
**Relationships** tab in the Inspector, a **Batch Export** flow from the File
menu, and **selection history** in the toolbar. Plus a stack of performance
and stability fixes carried over from beta.1.

This is a `beta` build — only clients that opted in via
**Settings → Updates → Include pre-release versions** receive it.

---

## New Features

### Inspector → Relationships
A new **Relationships** tab sits between *Hierarchy* and *Specialization* and
shows where a type is referenced across the binaries you have loaded.

- 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.
- The list lays out cleanly even when a type has no relationships — an empty
state explains why instead of showing a blank tab.

### 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, so the next export starts where you left off.
- The completion summary now 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.
- Export file names are clamped below the APFS `NAME_MAX` limit, so long
Swift-mangled names no longer produce errors mid-batch.

### Selection History in the Toolbar
The toolbar now 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.
- 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 when you move between selections, so
jumping back into a type returns you to the panel you were last reading.

### Sidebar: smarter expansion
- Single-clicking a non-leaf row now expands it (matching how the rest of
macOS treats outline rows).
- A new setting caps how deep a double-click expands an entire subtree, so
you can preview a framework without exploding hundreds of children.

---

## Performance

- **Type-picker prep moved off the main thread.** When you open the
Specialization sheet for a generic with a wide constraint, the candidate
list (10k+ entries in common cases) is now built on a background queue
with a `LoadingButton` placeholder — the UI no longer hangs while the
list is being assembled.
- **Lazy cell view models for the type picker.** A new
`DifferentiableBox` wrapper defers per-row view-model construction until
the row is actually rendered, cutting time-to-first-frame on large
candidate lists by roughly an order of magnitude.
- **macOS 26 navbar push cost cut.** Pushing the inspector when the user
selects a new sidebar row reuses the existing view controllers instead of
rebuilding them, removing a visible flash that appeared on macOS 26.

---

## Bug Fixes

- The Inspector no longer races against `reloadData()` when you click
through the sidebar rapidly — in-flight reloads are now cancelled before
the next one starts.
- Indexing skips weak-load dylibs that are shadowed by the dyld shared
cache, so they no longer show up as "missing" entries.
- Injected servers now resolve their main binary path through the dyld
registry, which fixes the inspector failing to load for some injection
targets.
- XPC peer activation runs after the modifier installs its handlers,
closing a small race window that could drop the first request on a fresh
connection.
- Inspector specialization rows can no longer be selected by accident — the
parameter table now refuses row selection the way the rest of the
inspector does.
- Decoding the `runtimeObjectsInImage` response now uses the right request
type, so the inspector populates correctly for some injection scenarios
that previously returned an empty list.

---

## Under the Hood

- The RuntimeViewer ↔ helper transport was rebuilt on top of the upstream
`swift-helper-service` library — XPC connection, peer lifecycle, and
daemon entry all delegate to the library now, leaving this repo with a
thin set of Runtime-specific adapters.
- `RuntimeViewerCore` was split: parsing lives in dedicated indexers, the
relationships engine has its own resolver, and `RuntimeSwiftSection` was
carved into focused extensions instead of one growing file.
- Document navigation is now state-driven from `DocumentState.selectionStack`
rather than ad-hoc coordinator calls, which is what unlocks the toolbar
history behaviour above.
- The communication layer was tightened (sealed `VoidResponse`, prefixed
shared transport types with `Runtime*`, dropped `@unchecked Sendable`).
79 changes: 79 additions & 0 deletions Changelogs/v2.1.0-beta.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# v2.1.0-beta.3

Third public preview of the **2.1** line. Stability focus on top of
[beta.2](v2.1.0-beta.2.md): one critical crash fix in the Bonjour-based
network connection, plus a MachOSwiftSection bump that picks up an
SwiftUI-related opaque-type parsing fix. Everything else carries over
from beta.2 unchanged.

This is a `beta` build — only clients that opted in via
**Settings → Updates → Include pre-release versions** receive it.

---

## Bug Fixes

### Crash on Bonjour-routed peers under batched receives
Inspecting an iOS / visionOS / Apple TV target over Bonjour, or pushing a
batch-export through a Bonjour connection, could crash with
`EXC_BAD_ACCESS` once a single `NWConnection.receive` carried more than a
few dozen small frames (heartbeats, acks, or export chunks). The crash hit
the connection's dispatch-queue worker thread with a 13_000+ frame stack
overflow.

Two compounding factors fed the overflow on this code path:

1. `RuntimeMessageChannel`'s message-dispatch loop accessed two
`@Mutex`-decorated properties (`receivedDataContinuation`,
`onMessageReceived`) on every iteration. The macro-generated `_modify`
coroutine accessor pinned ~280 bytes of coroutine context on the caller's
frame per access and the frames did not unwind between iterations.

2. `RuntimeNetworkConnection.observeIncomingMessages` spawned a fresh
`Task { await handleReceivedMessage(data) }` for each message inside that
same loop, adding another ~10 frames per iteration on top of (1).

The fix:

- The dispatch loop now snapshots both properties into locals before the hot
loop, so each iteration only touches local variables and the `_modify`
coroutine frames no longer accumulate.
- `RuntimeNetworkConnection` now matches `RuntimeLocalSocketConnection` and
`RuntimeDirectTCPConnection`: the callback only does a non-blocking
`yield` into an unbounded `AsyncStream<Data>`, and one long-lived consumer
task drains it. Early messages yielded before the consumer task reaches
`for await` are buffered, preserving FIFO across the entire receive burst.

Regression tests cover a 10k-message burst delivered from a dispatch queue
plus a disabled reproducer that replays the pre-fix per-message-Task shape
for manual verification.

### Cross-image opaque-type parsing crash (via MachOSwiftSection 0.12.0-beta.3)
Bumps MachOSwiftSection to **0.12.0-beta.3**, which fixes a crash when an
indexed image references a Swift opaque-return type whose underlying
descriptor lives in a sibling loaded image — e.g. SwiftUI body code that
resolves `View.searchFieldStyle(_:)` against `DVTUserInterfaceKit`. Before
the bump, the `MachOContext<MachOImage>` decoder treated the symbolic
reference offset as in-image and walked off into `__PAGEZERO`. The decoder
now mirrors the Swift runtime and resolves the descriptor through the
in-process pointer scheme, making cross-image references transparent.

The same MachOSwiftSection bump also brings:

- **SymbolIndexStore self-cleanup** — short-lived indexers now release their
cache entry on `deinit` instead of leaking it for the full process
lifetime.
- **`SharedCache` management API** — `contains` / `remove` / `removeAll`
let holders explicitly tear down cache entries on disposal.
- Internal refactors that fold `GenericContext` initialisers onto the
unified `ReadingContext` path, fixing a latent negative-size bug in the
legacy in-process decoder.

---

## Carried Over From beta.2

All features from [v2.1.0-beta.2](v2.1.0-beta.2.md) ship unchanged: the
Inspector **Relationships** tab, the **Batch Export** flow under *File →
Export Multiple Images…*, selection history in the toolbar, and the
underlying communication-layer hardening.
90 changes: 90 additions & 0 deletions Changelogs/v2.1.0-beta.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# v2.1.0-beta.4

Fourth public preview of the **2.1** line. Re-rolls the Bonjour crash fix from
[beta.3](v2.1.0-beta.3.md): beta.3's release binary still overflowed the
dispatch-queue worker stack inside `AsyncStream.yield`. Everything else
carries over from beta.3 unchanged.

This is a `beta` build — only clients that opted in via
**Settings → Updates → Include pre-release versions** receive it.

---

## Bug Fixes

### Bonjour-connection crash regression vs. beta.3
beta.3 shipped two coordinated fixes for the original v2.1.0-beta.2
`EXC_BAD_ACCESS` on a Bonjour batched receive:

- snapshot `RuntimeMessageChannel`'s `@Mutex`-backed properties into
locals before the hot loop, and
- bridge `RuntimeNetworkConnection.onMessageReceived` through an
`AsyncStream<Data>` drained by one long-lived consumer task.

In debug builds the combination was enough. In release, the binary still
crashed in `AsyncStream._Storage.yield` with a stack-guard fault — the
crash backtrace showed two `RuntimeViewer` offsets (`0xe88a0` / `0x6c33c`)
recursing tightly inside a single `connection.receive` callback on the
`com.RuntimeViewer.RuntimeViewerCommunication.RuntimeNetworkConnection`
queue. The `@Mutex` macro's `_modify` coroutine accessor still pinned
coroutine context on the caller's frame across iterations even with the
snapshot in place, just less of it.

This build drops the `@Mutex` macro from `RuntimeMessageChannel` entirely
and goes back to the hand-rolled `Mutex<T>` + `withLock` form that shipped
in v2.1.0-beta.1 (which never crashed on this path). `withLock` is a plain
synchronous function — no coroutine, no caller-frame pinning, the stack
unwinds cleanly between iterations.

The buffered-stream bridge from beta.3 stays in place: callback only does
a non-blocking `yield` and the long-lived consumer task drains
`handleReceivedMessage` off the dispatch-queue stack.

---

## Known Issues

### Remote-Server background indexing stalls image loading

When the remote engine acting as the Server side (iOS Simulator, iPad /
iPhone over Bonjour, etc.) has **background indexing** enabled, image
loading on the connecting client stalls — the sidebar shows neither image
contents nor a loading progress indicator.

`LoadImageForBackgroundIndexingRequest` on the Server side ends up calling
`dlopen` on the peer's own main-executable path. On the iOS Simulator dyld
rewrites that through the simruntime root prefix and the call fails with
`(no such file)`; on any platform `dlopen` of an already-loaded main exec
fails by definition. The resulting `DyldOpenError` is echoed back as a
`RuntimeNetworkRequestError`, but that payload carries no `identifier`
field, so the peer can't decode it as a `RuntimeRequestData` envelope —
its own catch arm echoes yet another error, and the two sides ping-pong
on the shared `sendSemaphore`, starving real traffic (image-list pushes,
indexing progress, on-demand object requests).

**Workaround**: turn off background indexing on the remote-Server side
until the next build.

**Status**: two-part fix planned for the next build — guard
`_loadImageForBackgroundIndexing` against already-loaded images on the
indexer side, and harden each connection's envelope-decode-failure arm to
swallow rather than echo, so a single peer-handler failure can never
amplify into a ping-pong again.

---

## Carried Over From beta.3

All changes from [v2.1.0-beta.3](v2.1.0-beta.3.md) ship unchanged:

- MachOSwiftSection 0.12.0-beta.3 bump (opaque-type symbolic-ref fix +
SymbolIndexStore self-cleanup + SharedCache management API).
- Remote-pin refresh across `RuntimeViewerCore` / `RuntimeViewerPackages`
to the current latest tag of each non-pinned dependency.
- ArchiveScript: force-checkout in the detached-HEAD branch so the
appcast PR step no longer aborts on archive-time `Package.resolved`
drift.

And from [v2.1.0-beta.2](v2.1.0-beta.2.md) the Inspector **Relationships**
tab, **Batch Export**, selection history, and the underlying
communication-layer hardening.
Loading