mac: hidden panes must not fight for the composer's focus (#930)
Switching spaces left the previous pane's composerFocused stale at true — the old @FocusState dropped itself when focus moved; the representable's plain @State cannot — so the hidden pane's field kept async-stealing first responder from the one the user switched to, reading as a dead input for a few seconds. Focus claims now follow isActive: a pane releases its claim when it goes inactive and only the active pane's mount-time asserts (onAppear / settled) may grab it.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 file changed,
+11
−3
ios/LChatMac/NativeFeedView.swift+11 −3
diff --git a/ios/LChatMac/NativeFeedView.swift b/ios/LChatMac/NativeFeedView.swift| index 10941af..a704007 100644 |
| --- a/ios/LChatMac/NativeFeedView.swift |
| +++ b/ios/LChatMac/NativeFeedView.swift |
| @@ -572,7 +572,14 @@ struct NativeFeedView: View { |
| installSwipeMonitor() |
| registerForDrops() |
| } |
| - .onChange(of: isActive) { _, active in frames.isActive = active } |
| + .onChange(of: isActive) { _, active in |
| + frames.isActive = active |
| + // Release or claim the composer with the pane: a hidden pane's |
| + // stale focused=true would keep async-stealing first responder |
| + // from the visible pane's field (the old @FocusState dropped |
| + // itself automatically; the representable's @State cannot). |
| + composerFocused = active |
| + } |
| .onDisappear { |
| // Deliberately NOT model.stop(): parked models keep their socket |
| // (the FeedWebCache rule); the cache's eviction stops them. |
| @@ -1337,13 +1344,14 @@ struct NativeFeedView: View { |
| // feed reveals: a FocusState write in onAppear races the |
| // freshly-inserted (still invisible) field and loses. |
| .onAppear { |
| + guard isActive else { return } |
| composerFocused = true |
| DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { |
| - composerFocused = true |
| + if isActive { composerFocused = true } |
| } |
| } |
| .onChange(of: settled) { _, isSettled in |
| - if isSettled { composerFocused = true } |
| + if isSettled, isActive { composerFocused = true } |
| } |
| Button(action: sendDraft) { |
| if sendQueued { |