refactor: unify window portal registries behind one hosted-view base#69
Merged
Conversation
N4/WP3 (nuclear-review): WindowTerminalPortal and WindowBrowserPortal were near line-for-line duplicate anchor-bound hosted-view registries (one for GhosttySurfaceScrollView, one for WKWebView). This extracts the slice of their lifecycle that is provably identical into a new shared base class, HostedViewPortalRegistry, and leaves everything else untouched in each subclass. Unified into the base (byte-identical or identical modulo a per-class debug log token, verified line-by-line before moving): - Stored bookkeeping: window, installedContainerView, installedReferenceView, geometryObservers - removeGeometryObservers() - effectiveAnchorFrameInWindow(for:) - synchronizeHostFrameToReference() (per-class dlog kept verbatim via an overridable logHostFrameUpdate(_:) hook) - debugHostedSubviewCount() - static helpers: rectApproximatelyEqual, pixelSnappedRect, isHiddenOrAncestorHidden, isView(_:above:in:) Kept divergent per subclass, on purpose, after comparing method-by-method: - pruneDeadEntries: terminal prunes non-visible entries with a dead anchor; browser deliberately preserves them so a hidden WKWebView can be rebound without a full reload on workspace switch. - resetTransientRecoveryRetryIfNeeded / scheduleTransientRecoveryRetryIfNeeded: terminal's retry budget is gated behind PROGRAMA_ISSUE_483_PORTAL_RECOVERY and resets only when the budget hits zero; browser's is always enabled and resets whenever the recovery reason changes (tracked per-entry). Different coalescing semantics, left untouched on each side. - installGeometryObservers(for:) / ensureInstalled(): terminal uses Auto Layout constraints and orders itself below the last WindowBrowserHostView; browser is frame-based and orders itself above the last WindowTerminalHostView/reference. This is the actual z-order coordination between the two portal types, not incidental duplication. - bind / detachHostedView<->detachWebView / hideEntry<->hideWebView / updateEntryVisibility / synchronizeHostedView<->synchronizeWebView: browser wraps each WKWebView in a WindowBrowserSlotView container (drop-zone, search-overlay, hosted-inspector-divider, WebKit lifecycle notifications); terminal parents the hosted view directly. Two-level vs one-level containment, not safely generalizable. - tearDown(): terminal additionally deactivates/clears Auto Layout constraints; kept as two small separate implementations rather than adding an abstract-method hook for a 2-line difference. - scheduleDeferredFullSynchronizeAll() / debugEntryCount(): trivial-bodied but reference different backing dictionaries and (for the schedule method) different debug logging; left as near-duplicate one-liners rather than adding indirection for marginal benefit. WindowTerminalHostView, WindowBrowserHostView, and their hitTest() implementations are untouched, as are all WebKit-specific, drop-zone/search-overlay/hosted-inspector-divider, and terminal-specific attach/refresh code paths. Line counts: TerminalWindowPortal.swift 2148 -> 2045 (-103), BrowserWindowPortal.swift 4177 -> 4075 (-102), new HostedViewPortalRegistry.swift +147.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Removes the line-for-line duplication between the two window-level portal registries —
WindowTerminalPortal(hosts terminal surfaces) andWindowBrowserPortal(hosts browser web views) — by pulling out the parts of their lifecycle that are genuinely identical into a new shared base class. This is nuclear-review finding N4/WP3, called out as the highest-risk item in the audit, so the goal here was provable correctness over maximal dedup: only code that is byte-identical (or identical modulo a per-class debug-log line) moved into the shared base; everything with real behavioral differences was left alone, even where it shares a name.Summary
New file
Sources/HostedViewPortalRegistry.swift(@mainactor class, +147 lines) holds:window,installedContainerView,installedReferenceView,geometryObserversremoveGeometryObservers()effectiveAnchorFrameInWindow(for:)synchronizeHostFrameToReference()— per-class dlog preserved verbatim via an overridablelogHostFrameUpdate(_:)hookdebugHostedSubviewCount()rectApproximatelyEqual,pixelSnappedRect,isHiddenOrAncestorHidden,isView(_:above:in:)WindowTerminalPortalandWindowBrowserPortalnow subclassHostedViewPortalRegistryand overridehostViewForGeometryto expose their concrete host view (WindowTerminalHostView/WindowBrowserHostView, both unmodified) to the shared geometry code.Line deltas:
Sources/TerminalWindowPortal.swiftSources/BrowserWindowPortal.swiftSources/HostedViewPortalRegistry.swiftKept-divergent methods (compared method-by-method, left alone deliberately)
pruneDeadEntriesWKWebViewcan be rebound on workspace switch without a full reload. Opposite pruning policy, not incidental.resetTransientRecoveryRetryIfNeeded/scheduleTransientRecoveryRetryIfNeededPROGRAMA_ISSUE_483_PORTAL_RECOVERYand resets only when exhausted. Browser's is always enabled and resets whenever the recovery reason changes (tracked per-entry, extraEntry.transientRecoveryReasonfield). Different coalescing semantics — explicitly called out as must-not-touch.installGeometryObservers(for:)/ensureInstalled()WindowBrowserHostView; browser is frame-based and orders itself above the lastWindowTerminalHostView/reference. This pair is the actual z-order coordination contract between the two portal types.bind/detachHostedView↔detachWebView/hideEntry↔hideWebView/updateEntryVisibility/synchronizeHostedView↔synchronizeWebViewWKWebViewin aWindowBrowserSlotViewcontainer (drop-zone overlay, search overlay, hosted-inspector-divider, WebKit lifecycle notifications); terminal parents the hosted view directly into the host. Two-level vs one-level containment — not safely generalizable without touching WebKit-specific and terminal-specific code.tearDown()scheduleDeferredFullSynchronizeAll()/debugEntryCount()Off-limits, untouched
WindowTerminalHostViewand itshitTest()(typing-latency-sensitive path per repo CLAUDE.md) — not modified at all.WindowBrowserHostView— not modified at all.Xcode project
Registered
Sources/HostedViewPortalRegistry.swiftinproject.pbxprojwith the standard 4 entries (PBXBuildFile, PBXFileReference, group child, Sources build phase), IDsA5FF0008/A5FF0018— verified no collision with existing IDs.Test Plan
xcodebuild -scheme programa -configuration Debug build→** BUILD SUCCEEDED **xcodebuild -scheme programa-unit build-for-testing→** TEST BUILD SUCCEEDED **ui-regressionsandtests-build-and-lagworkflows are the real arbiters here, not the local build — this touches the terminal/browser portal geometry-sync and z-ordering paths on every window resize, split-divider drag, and pane bind/detach. Please wait for both to go green before merging.