Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
85e884c
test: cover parking unmounted terminal portal layers
austinywang Sep 14, 2026
2d59e3c
fix: park unmounted terminal portal layers
austinywang Sep 14, 2026
75d689d
fix: batch portal parking across workspace unmounts
austinywang Sep 14, 2026
d7d00b8
fix: type batched portal parking ids
austinywang Sep 14, 2026
ed7e320
test: keep portal parking lifecycle coverage in app host suite
austinywang Sep 14, 2026
304a54a
test: exercise workspace unmount through the existing portal API
austinywang Sep 14, 2026
3a45cf3
fix: retire portal layers at the existing hide boundary
austinywang Sep 14, 2026
ab34560
fix: remove obsolete batch parking bridge
austinywang Sep 14, 2026
236deba
chore: restore portal lifecycle source formatting
austinywang Sep 14, 2026
54bb4a7
Merge origin/main and retain portal parking fix
austinywang Sep 16, 2026
7376cba
fix: minimize portal parking and repair upstream test IDs
austinywang Sep 16, 2026
04f7436
fix: restore dangling upstream test build references
austinywang Sep 16, 2026
8f469e9
fix: use existing auth identity stream in legacy host
austinywang Sep 16, 2026
3c1448c
fix: restore legacy runtime companions omitted upstream
austinywang Sep 16, 2026
d4148cf
test: cover workspace retirement through the portal registry
austinywang Sep 16, 2026
6361785
fix: preserve admitted RPC idle policy at renamed API
austinywang Sep 16, 2026
a77ae64
fix: complete upstream admission call and pairing translations
austinywang Sep 16, 2026
7677ca4
fix: reconcile pairing state and version-aware lane tests
austinywang Sep 16, 2026
6686b2c
fix: reconcile host queue API and Dock routing guard
austinywang Sep 16, 2026
76561a1
test: replace timing sleeps with causal synchronization
austinywang Sep 16, 2026
92dedbc
Merge origin/main and resolve CI test conflicts
austinywang Sep 16, 2026
e5014f8
test: make client config concurrency checks causal
austinywang Sep 16, 2026
f5ee1be
fix: update legacy transport timeout API
austinywang Sep 17, 2026
5114310
test: await blocked response before releasing shared fetch
austinywang Sep 17, 2026
96f6b10
Merge origin/main into issue-12586-windowserver-gestures
austinywang Sep 20, 2026
8c3b9a3
Merge origin/main into issue-12586-windowserver-gestures
austinywang Sep 21, 2026
ee7c93c
Resolve main conflicts and retire dead legacy runtime files
austinywang Sep 21, 2026
3a57387
Prune merge leftovers and keep portal regression focused
austinywang Sep 21, 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
5 changes: 4 additions & 1 deletion Sources/TerminalWindowPortal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,7 @@ final class WindowTerminalPortal: NSObject {
}
}

/// Hide a portal entry for permanent workspace unmounts without detaching it.
/// Removes an inactive terminal's view tree from the window while retaining its binding and PTY.
func hideEntry(forHostedId hostedId: ObjectIdentifier) {
guard var entry = entriesByHostedId[hostedId] else {
clearPresentationNotificationState(for: hostedId)
Expand All @@ -1500,6 +1500,9 @@ final class WindowTerminalPortal: NSObject {
entriesByHostedId[hostedId] = entry
clearPresentationNotificationState(for: hostedId)
entry.hostedView?.isHidden = true
if let hostedView = entry.hostedView, hostedView.superview === hostView {
hostedView.removeFromSuperview()
}
#if DEBUG
cmuxDebugLog("portal.hideEntry hosted=\(portalDebugToken(entry.hostedView)) reason=workspaceUnmount")
#endif
Expand Down
55 changes: 55 additions & 0 deletions cmuxTests/TerminalWindowPortalLifecycleHiddenRefreshTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,61 @@ import CmuxTerminal

extension TerminalWindowPortalLifecycleTests {

@MainActor
func testWorkspaceUnmountDetachesTerminalAndRebindsOnReveal() async throws {
let window = makeTestWindow(
contentRect: NSRect(x: 0, y: 0, width: 520, height: 340)
)
defer {
NotificationCenter.default.post(name: NSWindow.willCloseNotification, object: window)
window.orderOut(nil)
}
layoutResizeTestWindow(window)
guard let contentView = window.contentView else {
XCTFail("Expected content view")
return
}

let anchor = NSView(frame: NSRect(x: 8, y: 8, width: 240, height: 160))
contentView.addSubview(anchor)
let surface = makeTrackedTerminalSurface()
TerminalWindowPortalRegistry.bind(hostedView: surface.hostedView, to: anchor, visibleInUI: true)
let initialGeometrySettled = await waitForSettledPortalGeometry(surface, anchor: anchor)
XCTAssertTrue(initialGeometrySettled)

let originalHost = try XCTUnwrap(surface.hostedView.superview)
let originalRuntime = try XCTUnwrap(surface.surface)
surface.hostedView.setVisibleInUI(false)
TerminalWindowPortalRegistry.hideHostedViews(forWorkspaceID: surface.tabId)

XCTAssertNil(
surface.hostedView.superview,
"An unmounted workspace must remove its terminal layer tree from the WindowServer hierarchy"
)
XCTAssertTrue(surface.hostedView.isHidden)
XCTAssertEqual(surface.surface, originalRuntime, "Unmounting must preserve the live PTY")
XCTAssertTrue(
TerminalWindowPortalRegistry.hasEntry(for: surface.hostedView, boundTo: anchor),
"Parking must retain the logical portal binding so the workspace can reattach on reveal"
)
XCTAssertTrue(
TerminalWindowPortalRegistry.updateEntryVisibility(
for: surface.hostedView,
visibleInUI: true
),
"A parked entry must request a reattach when its workspace becomes visible"
)
surface.hostedView.setVisibleInUI(true)
TerminalWindowPortalRegistry.bind(hostedView: surface.hostedView, to: anchor, visibleInUI: true)
let revealedGeometrySettled = await waitForSettledPortalGeometry(surface, anchor: anchor)
XCTAssertTrue(revealedGeometrySettled)
XCTAssertTrue(surface.hostedView.superview === originalHost)
XCTAssertTrue(surface.hostedView.window === window)
XCTAssertFalse(surface.hostedView.isHidden)
XCTAssertEqual(surface.surface, originalRuntime, "Reveal must reuse the terminal process")
withExtendedLifetime(surface) {}
}

@MainActor
func testPortalSkipsSynchronousRefreshForHiddenSurfaces() throws {
let window = makeTestWindow(
Expand Down
Loading