Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
97b0926
portal: invalidate the split-divider hit-test cache on nested subview…
ejc3 Jul 21, 2026
dfb0bad
portal: compare structure snapshots without intermediate arrays
ejc3 Jul 25, 2026
988f507
portal: cover deep-container split insertion and content-root replace…
ejc3 Jul 31, 2026
7346268
portal: validate the divider cache with a root-keyed full-tree split …
ejc3 Jul 31, 2026
679c016
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 4, 2026
93de123
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
ce464fb
portal: prove cache hits skip large hierarchy traversal
austinywang Aug 7, 2026
f9cd05c
portal: invalidate divider caches at hierarchy mutations
austinywang Aug 7, 2026
84884a5
portal: scope hierarchy invalidation to cache roots
austinywang Aug 7, 2026
2dfe036
portal: nest cache invalidation helpers
austinywang Aug 7, 2026
57606a4
portal: give hierarchy mutation routing one owner
austinywang Aug 7, 2026
6c1a17b
portal: hook every hierarchy mutation entrypoint
austinywang Aug 7, 2026
3138477
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
8c0dac3
test: align OMP resume environment expectation
austinywang Aug 7, 2026
42dccd8
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
1fb78d7
test: keep install command fixture inert
austinywang Aug 7, 2026
3534745
test: cover app-host config path aliases
austinywang Aug 7, 2026
a86438f
ci: canonicalize app-host config evidence paths
austinywang Aug 7, 2026
85d13af
fix: resolve Xcode 26.3 Swift warnings
austinywang Aug 7, 2026
92ec9a6
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
438bde3
fix: preserve Xcode 16 delegate compatibility
austinywang Aug 7, 2026
92fdb4c
test: bound portal hierarchy mutation hook work
austinywang Aug 7, 2026
37eb01e
portal: make hierarchy invalidation generation-based
austinywang Aug 7, 2026
585cbd2
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
f57b7d7
test: cover no-op sorts and detached portal roots
austinywang Aug 7, 2026
f99e858
portal: preserve cache validity across sort and reattach
austinywang Aug 7, 2026
aecb63c
test: cover detached nested portal subtree mutation
austinywang Aug 7, 2026
342b48d
test: calibrate portal mutation work against AppKit
austinywang Aug 7, 2026
053695f
portal: distrust detached subtree hierarchy state
austinywang Aug 7, 2026
cab9967
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 7, 2026
f87e5f2
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 8, 2026
9b66708
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 8, 2026
21f7c99
fix: fail closed when portal hierarchy hooks are unavailable
austinywang Aug 9, 2026
332aaec
test: drop stale OMP path override from portal branch
austinywang Aug 9, 2026
947a99e
test: cover split-free subviews replacement
austinywang Aug 9, 2026
7ea0c07
portal: preserve split-free subviews fast path
austinywang Aug 9, 2026
fc3dfe7
test: validate every warmed portal cache
austinywang Aug 9, 2026
c1e4e7d
test: cover arranged split pane mutations
austinywang Aug 9, 2026
02aecd7
portal: track arranged split pane mutations
austinywang Aug 9, 2026
53dae4a
test: cover detached subtree parking path
austinywang Aug 9, 2026
c7b568d
portal: revoke proofs at unindexed hierarchy boundaries
austinywang Aug 9, 2026
aaa4600
test: isolate arranged pane mutation cases
austinywang Aug 9, 2026
c1bcede
Merge remote-tracking branch 'origin/main' into fix/portal-hittest-ca…
austinywang Aug 9, 2026
b267f3c
test: cover wrapped detached portal subtree
austinywang Aug 9, 2026
be1e22b
portal: track detached indexed subtree mutations
austinywang Aug 9, 2026
27427a5
portal: keep mutation snapshots with their owner
austinywang Aug 9, 2026
d0d9126
test: cover inactive portal proof lifetime
austinywang Aug 9, 2026
0edb762
portal: revoke proofs between cache lifetimes
austinywang Aug 9, 2026
6b8fd11
test: avoid arranged-pane teardown KVO trap
austinywang Aug 9, 2026
779aa42
test: cover detached portal reorder mutations
austinywang Aug 9, 2026
e3be8ba
test: restore split arrangement before teardown
austinywang Aug 9, 2026
99968cc
test: document detached cache boundary
austinywang Aug 9, 2026
dfe3fd7
test: use valid arranged-pane lifecycles
austinywang Aug 9, 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
11 changes: 5 additions & 6 deletions Sources/BrowserWindowPortal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ final class WindowBrowserHostView: NSView {
private var cachedSidebarDividerX: CGFloat?
private var sidebarDividerMissCount = 0
private var cachedSplitDividerRegions: [DividerRegion]?
private var cachedSplitDividerRootSubviewIds: [ObjectIdentifier]?
private var cachedSplitDividerStructure: [PortalStructureSnapshot]?
private let splitDividerCacheInvalidator = PortalSplitDividerCacheInvalidator()
private var splitDividerResizeObserver: NSObjectProtocol?
private var trackingArea: NSTrackingArea?
Expand Down Expand Up @@ -1033,12 +1033,11 @@ final class WindowBrowserHostView: NSView {
return (pageFrame, inspectorFrame)
}
private func splitDividerRegions() -> [DividerRegion] {
guard let rootView = dividerSearchRootView() else { cachedSplitDividerRegions = []; cachedSplitDividerRootSubviewIds = nil; return [] }
let rootSubviewIds = rootView.subviews.map { ObjectIdentifier($0) }
if let regions = cachedSplitDividerRegions, cachedSplitDividerRootSubviewIds == rootSubviewIds, PortalSplitDividerRegion.allLive(regions) { return regions }
guard let rootView = dividerSearchRootView() else { cachedSplitDividerRegions = []; cachedSplitDividerStructure = nil; return [] }
if let regions = cachedSplitDividerRegions, let structure = cachedSplitDividerStructure, PortalSplitDividerRegion.structureSnapshotsMatch(structure), PortalSplitDividerRegion.allLive(regions) { return regions }
let collected = PortalSplitDividerRegion.collect(in: rootView, hostView: self)
cachedSplitDividerRegions = collected.regions
cachedSplitDividerRootSubviewIds = rootSubviewIds
cachedSplitDividerStructure = PortalSplitDividerRegion.structureSnapshots(of: collected.structureObservedViews)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
splitDividerCacheInvalidator.observe(
geometryViews: collected.geometryObservedViews,
structureViews: collected.structureObservedViews
Expand All @@ -1052,7 +1051,7 @@ final class WindowBrowserHostView: NSView {

private func invalidateSplitDividerRegionCache() {
cachedSplitDividerRegions = nil
cachedSplitDividerRootSubviewIds = nil
cachedSplitDividerStructure = nil
splitDividerCacheInvalidator.invalidate()
}

Expand Down
29 changes: 29 additions & 0 deletions Sources/PortalSplitDividerRegion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,32 @@ final class PortalSplitDividerRegion {
}
}
}

/// A snapshot of the subview-identity lists of the structure-observed views. Used to
/// validate the split-divider cache without relying on KVO of `NSView.subviews`, which
/// does not reliably fire for `addSubview(_:)` across macOS versions.
struct PortalStructureSnapshot {
fileprivate weak var view: NSView?
fileprivate let subviewIds: [ObjectIdentifier]
}

extension PortalSplitDividerRegion {
static func structureSnapshots(of views: [NSView]) -> [PortalStructureSnapshot] {
views.map { PortalStructureSnapshot(view: $0, subviewIds: $0.subviews.map(ObjectIdentifier.init)) }
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

/// True only if every recorded view is still alive and its direct subview identities are
/// unchanged. A nested insertion changes an observed container's subview list, so this
/// catches it even when the KVO callback never fired.
static func structureSnapshotsMatch(_ snapshots: [PortalStructureSnapshot]) -> Bool {
for snapshot in snapshots {
guard let view = snapshot.view else { return false }
let subviews = view.subviews
guard subviews.count == snapshot.subviewIds.count,
zip(subviews, snapshot.subviewIds).allSatisfy({ ObjectIdentifier($0.0) == $0.1 }) else {
return false
}
}
return true
}
}
11 changes: 5 additions & 6 deletions Sources/TerminalWindowPortal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class WindowTerminalHostView: NSView {
private var cachedSidebarDividerX: CGFloat?
private var sidebarDividerMissCount = 0
private var cachedSplitDividerRegions: [DividerRegion]?
private var cachedSplitDividerRootSubviewIds: [ObjectIdentifier]?
private var cachedSplitDividerStructure: [PortalStructureSnapshot]?
private let splitDividerCacheInvalidator = PortalSplitDividerCacheInvalidator()
private var splitDividerResizeObserver: NSObjectProtocol?
private var trackingArea: NSTrackingArea?
Expand Down Expand Up @@ -371,12 +371,11 @@ final class WindowTerminalHostView: NSView {
}

private func splitDividerRegions() -> [DividerRegion] {
guard let window, let rootView = window.contentView else { cachedSplitDividerRegions = []; cachedSplitDividerRootSubviewIds = nil; return [] }
let rootSubviewIds = rootView.subviews.map { ObjectIdentifier($0) }
if let regions = cachedSplitDividerRegions, cachedSplitDividerRootSubviewIds == rootSubviewIds, PortalSplitDividerRegion.allLive(regions) { return regions }
guard let window, let rootView = window.contentView else { cachedSplitDividerRegions = []; cachedSplitDividerStructure = nil; return [] }
if let regions = cachedSplitDividerRegions, let structure = cachedSplitDividerStructure, PortalSplitDividerRegion.structureSnapshotsMatch(structure), PortalSplitDividerRegion.allLive(regions) { return regions }
let collected = PortalSplitDividerRegion.collect(in: rootView)
cachedSplitDividerRegions = collected.regions
cachedSplitDividerRootSubviewIds = rootSubviewIds
cachedSplitDividerStructure = PortalSplitDividerRegion.structureSnapshots(of: collected.structureObservedViews)
splitDividerCacheInvalidator.observe(
geometryViews: collected.geometryObservedViews,
structureViews: collected.structureObservedViews
Expand All @@ -390,7 +389,7 @@ final class WindowTerminalHostView: NSView {

private func invalidateSplitDividerRegionCache() {
cachedSplitDividerRegions = nil
cachedSplitDividerRootSubviewIds = nil
cachedSplitDividerStructure = nil
splitDividerCacheInvalidator.invalidate()
}

Expand Down