-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Improve Simulator discoverability on macOS and iOS #9888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
azooz2003-bit
wants to merge
26
commits into
main
Choose a base branch
from
task-improve-simulator-discoverability
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
24a3c08
Improve Simulator discoverability
azooz2003-bit 445870a
Merge remote-tracking branch 'origin/main' into task-improve-simulato…
azooz2003-bit 901915e
Fix Simulator button accessibility label
azooz2003-bit 290955b
Address simulator discoverability review findings
azooz2003-bit 2cec093
Fix simulator picker getter return
azooz2003-bit 7a84690
Unify simulator stream state ownership
azooz2003-bit 9e77288
Stabilize simulator stream environment lifetime
azooz2003-bit 0efe264
Own mobile terminal runtime before SwiftUI mounting
azooz2003-bit db6f334
Expose borrowed mobile session view
azooz2003-bit 62fc0aa
Allow process-owned terminal runtime retry
azooz2003-bit 4968ed8
Import terminal theme in renderer recovery view
azooz2003-bit 282d6aa
Load iOS Ghostty config from owned path
azooz2003-bit e356c7f
Resolve renderer recovery strings from package
azooz2003-bit 9b65abd
Add renderer recovery preview fixture
azooz2003-bit 24c437f
Stabilize Simulator discoverability UI tests
azooz2003-bit 7144595
Instrument Simulator selection lifecycle
azooz2003-bit ba45abe
Expose Simulator lifecycle diagnostic timeline
azooz2003-bit c2fc815
Fix Simulator discoverability UI assertions
azooz2003-bit 3a60ad2
Avoid quadratic simulator picker snapshots
azooz2003-bit a4fb70f
Test macOS tab bar omits Simulator button
azooz2003-bit 48c2c7c
Remove Simulator from macOS tab defaults
azooz2003-bit 3d83481
Test crowded tab bar keeps default actions visible
azooz2003-bit f7097c9
Remove unrelated crowded tab bar test
azooz2003-bit ee2df25
Test Simulator toolbar returns from active panel
azooz2003-bit 08cc2f7
Add Simulator tab navigation on iOS
azooz2003-bit 9eedc37
Merge remote-tracking branch 'origin/main' into task-improve-simulato…
azooz2003-bit File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
102 changes: 102 additions & 0 deletions
102
...S/CmuxMobileShell/Sources/CmuxMobileShell/MobileSimulatorStreamSelectionCoordinator.swift
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| import Foundation | ||
|
|
||
| /// Serializes workspace Simulator selection intents so stream RPCs never overlap. | ||
| @MainActor | ||
| final class MobileSimulatorStreamSelectionCoordinator { | ||
| enum Operation: Equatable { | ||
| case start(panelID: String, workspaceID: String) | ||
| case stop(panelID: String, workspaceID: String) | ||
| } | ||
|
|
||
| private struct Selection: Equatable { | ||
| let panelID: String | ||
| let workspaceID: String | ||
| } | ||
|
|
||
| private struct Intent { | ||
| let target: Selection? | ||
| } | ||
|
|
||
| typealias PerformOperation = @MainActor (Operation) async -> Void | ||
|
|
||
| private let performOperation: PerformOperation | ||
| private var pendingIntent: Intent? | ||
| private var activeSelection: Selection? | ||
| private(set) var transitionTask: Task<Void, Never>? | ||
|
|
||
| init(performOperation: @escaping PerformOperation) { | ||
| self.performOperation = performOperation | ||
| } | ||
|
|
||
| func requestTransition( | ||
| from previousPanelID: String?, | ||
| to targetPanelID: String?, | ||
| workspaceID: String | ||
| ) { | ||
| let previous = previousPanelID.map { Selection(panelID: $0, workspaceID: workspaceID) } | ||
| let target = targetPanelID.map { Selection(panelID: $0, workspaceID: workspaceID) } | ||
| if activeSelection == nil { | ||
| activeSelection = previous | ||
| } | ||
| pendingIntent = Intent(target: target) | ||
| startDrainIfNeeded() | ||
| } | ||
|
|
||
| func cancel() { | ||
| pendingIntent = nil | ||
| transitionTask?.cancel() | ||
| } | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| func waitForIdle() async { | ||
| while let task = transitionTask { | ||
| await task.value | ||
| } | ||
| } | ||
|
|
||
| private func startDrainIfNeeded() { | ||
| guard transitionTask == nil else { return } | ||
| transitionTask = Task { @MainActor [weak self] in | ||
| await self?.drainPendingTransitions() | ||
| } | ||
| } | ||
|
|
||
| private func drainPendingTransitions() async { | ||
| while !Task.isCancelled, let intent = pendingIntent { | ||
| pendingIntent = nil | ||
| let previous = activeSelection | ||
|
|
||
| if let previous, previous != intent.target { | ||
| await performOperation( | ||
| .stop(panelID: previous.panelID, workspaceID: previous.workspaceID) | ||
| ) | ||
| activeSelection = nil | ||
| guard !Task.isCancelled else { break } | ||
| } | ||
|
|
||
| // A newer intent that arrived while the stop was in flight owns | ||
| // the next start. Skipping this target avoids transient stale RPCs. | ||
| guard pendingIntent == nil else { continue } | ||
| guard let target = intent.target, target != activeSelection else { continue } | ||
|
|
||
| await performOperation( | ||
| .start(panelID: target.panelID, workspaceID: target.workspaceID) | ||
| ) | ||
| activeSelection = target | ||
|
|
||
| if Task.isCancelled { | ||
| // Cancellation can race an already-sent start RPC. Stop the | ||
| // accepted target before releasing coordinator ownership. | ||
| await performOperation( | ||
| .stop(panelID: target.panelID, workspaceID: target.workspaceID) | ||
| ) | ||
| activeSelection = nil | ||
| break | ||
| } | ||
| } | ||
|
|
||
| transitionTask = nil | ||
| if pendingIntent != nil { | ||
| startDrainIfNeeded() | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coordinator breaks multi-workspace streams
High Severity
MobileSimulatorStreamSelectionCoordinatortracks one globalactiveSelection, while the store andrestartActiveMobileSimulatorStreamskeep per-workspace actives. After a selection in another workspace—or a clear whosefromdoes not match the coordinator—the drain can stop the wrong panel.cancelalso leavesactiveSelectionstale across disconnect/reconnect, so later UI actions diverge from the store.Additional Locations (2)
Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SimulatorStream.swift#L60-L88Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite+SimulatorStream.swift#L233-L235Reviewed by Cursor Bugbot for commit 9eedc37. Configure here.