Fix terminal focus retry after tiny responder handoff
Summary
- add regression coverage for the hidden/tiny first-responder focus handoff
- schedule the existing automatic first-responder reconciliation when
GhosttyNSView.becomeFirstResponder()suppresses focus because geometry is hidden or tiny - keep the retry on the existing coalesced focus-apply path so it reasserts Ghostty focus only after the pane is visible, active, key-window, and still the current focus target
Root cause
Code inspection confirmed a reachable AppKit-focused/Ghostty-unfocused divergence: becomeFirstResponder() sets the view-level desiredFocus before geometry gating, then the hidden/tiny branch logs and returns without calling ghostty_surface_set_focus or scheduling the normal focus reconciliation path. If AppKit makes the Ghostty view first responder during transient bonsplit/window portal layout churn, subsequent plain key input can route to the view while the runtime surface still has desiredFocusState == false.
The branch dates to February 2026 (5070b137a4), so this looks like a latent race exposed by later timing changes rather than code introduced directly by the June refactors. The June candidates mostly changed surrounding timing: #6147 moved portal installation through window-chrome overlay resolution, #6278 moved terminal surface lifecycle into the macOS package and added restore/input-demand paths, and #6227/#6267 did not show a direct focus-state change in this path.
Tests
- Added deterministic DEBUG app-target coverage for first-responder while the inner Ghostty view is zero-sized, then geometry settling before the deferred apply drains.
- Per issue instructions, I did not run local tests or local builds; CI is the validation path for this branch.
Closes #6342
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.
Summary by cubic
Retries terminal focus after AppKit hands first responder to a hidden or tiny terminal, deferring apply until the portal and inner surface are usable. Forced reparent focus uses the same geometry guard and queues a retry on layout/visibility; dock retries only run when the dock owns first responder (fixes #6342).
-
Bug Fixes
- Defer suppressed first-responder focus; reapply only when the window is key, pane and surface are visible, portal >1x1, and (for suppressed handoffs) inner surface >1x1. Automatic apply and Find-restore keep this deferral.
- Gate forced reparent reassert by geometry; if hidden/tiny, queue a retry from layout/visibility and clear pending state on success or foreign focus. Skip redundant scans when an apply is queued. In the right-sidebar dock, require current first-responder ownership and readiness so visibility alone never steals focus.
-
Tests
- Reapply after geometry settles; automatic apply doesn’t bypass deferral; Find “Restore Terminal” preserves it.
- Forced reparent reassert queues while tiny and recovers once usable; already-first-responder reparent clear waits for surface geometry. Right-sidebar dock: deferred reapply after geometry and no focus theft on visibility apply.
Summary by CodeRabbit
-
Bug Fixes
- Improved terminal focus recovery when the focused surface is hidden or shrinks to a tiny size. Suppressed first-responder focus is now deferred and conditionally retried once the view is active/visible and the surface geometry is usable. Pending retries are reliably cleared after unrelated focus handoffs to prevent stale reapplication.
-
Tests
- Added a DEBUG-only regression test that verifies deferred suppressed focus is re-applied after resizing the surface back to a usable size.