Skip to content

Gate minimal-mode chrome and sidebar on focusPaneOnFirstClick - #3857

Closed
rursache wants to merge 1 commit into
manaflow-ai:mainfrom
rursache:fix/focus-pane-first-click-chrome
Closed

rursache wants to merge 1 commit into
manaflow-ai:mainfrom
rursache:fix/focus-pane-first-click-chrome

Conversation

@rursache

@rursache rursache commented May 11, 2026 •

Copy link
Copy Markdown

Closes #3856.

Summary

With app.focusPaneOnFirstClick: false, clicks on cmux chrome while the window was inactive still fired actions on first click. The setting was already honoured for pane content (terminal, web view, markdown observer) but bypassed for chrome and sidebar via five separate code paths that I traced with a temporary instrumented build:

  • TitlebarControlButton SwiftUI Buttons in the titlebar (bell, sidebar toggle, +)
  • The NSEvent.addLocalMonitor in WindowDecorationsController that resolves minimal-mode chrome clicks via the hit-region registry and calls performMinimalModeSidebarControlAction directly (this one bypasses every view-level check, including any acceptsFirstMouse override)
  • The per-window send-event hook in the same controller
  • The sidebar workspace row's .onTapGesture, which switched workspaces on first click — the most surprising symptom

Approach

Adds PaneFirstClickGate next to PaneFirstClickFocusSettings in WorkspaceRuntimeSettings.swift. It observes NSApplication.didBecomeActiveNotification and exposes shouldSwallowFirstClick(now:), which returns true for 200 ms after activation when the setting is disabled. The four action sites consult the gate before firing.

The grace-window approach is necessary because by the time these SwiftUI actions and event-monitor callbacks run, NSApp.isActive is already true — there's no other reliable per-click signal. 200 ms is short enough that a deliberate second click is well past the window and long enough that the activation click reliably falls inside it.

Test plan

  • xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux-unit -configuration Debug -destination 'platform=macOS' -only-testing:cmuxTests/InactivePaneFirstClickFocusTests test — all 9 tests pass (6 existing + 3 new gate-behaviour cases)
  • Manual on a tagged Debug build with focusPaneOnFirstClick: false and minimalMode: true, cmux unfocused:
    • Bell / sidebar toggle / + button: first click only activates cmux, second click fires the action
    • Click workspace 2 from workspace 1: first click only activates cmux, second click switches workspace
    • With focusPaneOnFirstClick: true: all four still fire on first click as before

Summary by CodeRabbit

  • New Features
    • First-click protection: the app now ignores the first click for a short grace period (~0.2s) after the app becomes active to prevent accidental actions. This applies to sidebar rows, titlebar controls, and minimal-mode sidebar chrome, and can be toggled in settings.
  • Tests
    • Added automated tests validating the timing and behavior of the first-click gating.

Review Change Stack

@vercel

vercel Bot commented May 11, 2026

Copy link
Copy Markdown

@rursache is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 11, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5fe5f6e1-c9b8-4662-8b69-b25e9e4aee10

📥 Commits

Reviewing files that changed from the base of the PR and between cc85a73 and 31f7a71.

📒 Files selected for processing (6)
  • Sources/App/WorkspaceRuntimeSettings.swift
  • Sources/AppDelegate.swift
  • Sources/ContentView.swift
  • Sources/Update/UpdateTitlebarAccessory.swift
  • Sources/WindowDecorationsController.swift
  • cmuxTests/InactivePaneFirstClickFocusTests.swift

📝 Walkthrough

Walkthrough

Adds PaneFirstClickGate (installed at launch) that records app activation time and, when the pane-first-click setting is disabled, swallows the first click for a 0.2s grace window; UI handlers consult the gate before performing actions.

Changes

Pane First-Click Gate

Layer / File(s) Summary
Gate definition and state
Sources/App/WorkspaceRuntimeSettings.swift
Add AppKit import. Introduce PaneFirstClickGate enum with @MainActor state for last activation time, guarded NSApplication.didBecomeActiveNotification observer, shouldSwallowFirstClick() method returning true only when setting is disabled and elapsed time is within a 0.2s grace window, and markActivatedForTesting() helper for tests.
Install gate at startup
Sources/AppDelegate.swift
Call PaneFirstClickGate.install() in applicationDidFinishLaunching immediately after AppIconLaunchState.markDidFinishLaunching() to activate the observer before the first background-to-foreground transition.
Consult gate in UI entry points
Sources/ContentView.swift, Sources/Update/UpdateTitlebarAccessory.swift, Sources/WindowDecorationsController.swift
ContentView sidebar workspace row tap, TitlebarControlButton action wrapper, and WindowDecorationsController minimal-mode chrome mouse-down handlers each check PaneFirstClickGate.shouldSwallowFirstClick() and return early (without calling updateSelection, performing action, or updating hover state) when the gate returns true.
Tests
cmuxTests/InactivePaneFirstClickFocusTests.swift
Add three PaneFirstClickGate tests: swallow within grace window when setting disabled; allow after grace window; allow regardless of timing when setting enabled.

Sequence Diagram(s)

sequenceDiagram
  participant AppDelegate
  participant NSApp as NSApplication
  participant Gate as PaneFirstClickGate
  participant Settings as PaneFirstClickFocusSettings
  participant UI as UIHandler

  AppDelegate->>Gate: install()
  Note over Gate: register didBecomeActiveNotification observer
  NSApp->>Gate: didBecomeActiveNotification (record ProcessInfo.processInfo.systemUptime)
  UI->>Gate: shouldSwallowFirstClick(now)
  Gate->>Settings: isEnabled()
  alt setting enabled
    Gate-->>UI: return false (allow click)
  else setting disabled
    Gate->>Gate: check elapsed time since lastBecameActiveAt
    alt within 0.2s
      Gate-->>UI: return true (swallow)
    else after 0.2s
      Gate-->>UI: return false (allow)
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰
I watched the app wake, soft and slow,
Two-tenths of hush before a click may go.
A gentle gate keeps accidental taps at bay,
Then lets your intent through on the next day.
Hooray for tiny pauses 🥕

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Gate minimal-mode chrome and sidebar on focusPaneOnFirstClick' is specific and directly summarizes the main change: gating chrome and sidebar interactions with the focusPaneOnFirstClick setting.
Description check ✅ Passed The description covers all key sections: Summary explains the problem and why the setting was ignored; approach describes the PaneFirstClickGate solution; test plan documents xcodebuild tests and manual verification.
Linked Issues check ✅ Passed The PR fully addresses #3856 by gating titlebar controls, workspace sidebar, and minimal-mode chrome via PaneFirstClickGate; all coding requirements are met with the 200ms grace window approach.
Out of Scope Changes check ✅ Passed All changes directly support the PR objectives: PaneFirstClickGate implementation, integration points (AppDelegate, ContentView, TitlebarControlButton, WindowDecorationsController), and related unit tests are in scope.
Cmux Swift Actor Isolation ✅ Passed PaneFirstClickGate @MainActor-isolated with correct mutable state handling. All access sites properly isolated: SwiftUI views direct, NSEvent callbacks via MainActor.assumeIsolated.
Cmux Swift Blocking Runtime ✅ Passed No blocking synchronization introduced. Gate uses non-blocking timing checks with observer-based state. All integrations early return only.
Cmux No Hacky Sleeps ✅ Passed Check does not apply. All changes are in Swift files. The rule explicitly covers only TypeScript, JavaScript, shell, and build scripts; Swift timing is covered by swift-blocking-runtime.md.
Cmux Swift Concurrency ✅ Passed PaneFirstClickGate uses @MainActor annotation, NotificationCenter (AppKit boundary), and MainActor.assumeIsolated. No DispatchQueue, Combine, fire-and-forget Tasks introduced.
Cmux Swift @Concurrent ✅ Passed PaneFirstClickGate is correctly @MainActor with synchronous methods. Notification observer properly uses MainActor.assumeIsolated. All call sites are @MainActor-isolated or properly wrapped.
Cmux Swift File And Package Boundaries ✅ Passed 69-line focused feature. Largest file addition: 32 lines. No oversized violations. Single coherent responsibility (first-click gating). Appropriate app-target code with clean public interface.
Cmux Swift Logging ✅ Passed PR introduces no logging violations. The PaneFirstClickGate implementation and all integration points contain no print, debugPrint, dump, or NSLog statements that violate swift-logging.md rules.
Cmux Swiftui State Layout ✅ Passed Compliant with SwiftUI state layout rules. Pure wrapper closures without new state patterns. No @Observable/@Published/@StateObject introduced. No GeometryReader, store refs, or render mutations.
Cmux Architecture Rethink ✅ Passed PaneFirstClickGate uses required NotificationCenter observer pattern. No timing repairs or delays. Single implementation. Clear 200ms grace window invariant. Matches existing architecture.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds only behavioral gating logic to existing UI elements. No new user-visible NSWindow, NSPanel, NSWindowController, or SwiftUI Window/WindowGroup code is introduced. Check does not apply.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 11, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR gates minimal-mode chrome and sidebar actions on a new PaneFirstClickGate to honour focusPaneOnFirstClick: false at five previously bypassed call sites — the TitlebarControlButton SwiftUI action, two AppKit event-monitor paths in WindowDecorationsController, and the sidebar workspace .onTapGesture.

  • PaneFirstClickGate (WorkspaceRuntimeSettings.swift): A @MainActor enum singleton that records the systemUptime when didBecomeActiveNotification fires and suppresses actions for a fixed 200 ms afterwards. The approach is timing-based rather than event-based, leaving two silent failure modes: deliberate clicks within 200 ms of activation (e.g., Dock-activated then immediate button press) are swallowed, and late notification delivery on a loaded machine can let the activation click slip through.
  • WindowDecorationsController.swift: Both new gate checks call MainActor.assumeIsolated inside a non-@MainActor class; this is safe while AppKit event monitors deliver on the main thread, but the isolation contract is runtime-only and not compiler-enforced.

Confidence Score: 3/5

The core gate logic is correct for the common case, but the 200 ms timing constant introduces silent edge-case failures and the actor-isolation contract in WindowDecorationsController is verified only at runtime.

The core gate logic is correct for the common case, but the 200 ms timing constant introduces silent edge-case failures and the actor-isolation contract in WindowDecorationsController is verified only at runtime.

Sources/App/WorkspaceRuntimeSettings.swift (timing-gate design) and Sources/WindowDecorationsController.swift (unverified actor isolation).

Important Files Changed

Filename Overview
Sources/App/WorkspaceRuntimeSettings.swift Adds PaneFirstClickGate — a @MainActor-isolated enum singleton with a 200 ms timing gate triggered by didBecomeActiveNotification. The approach is timing-based rather than event-based and introduces false-swallow and false-pass edge cases.
Sources/WindowDecorationsController.swift Adds two PaneFirstClickGate checks behind MainActor.assumeIsolated in a non-@MainActor class; the runtime assumption is safe today but is not compiler-verified.
Sources/ContentView.swift Adds a PaneFirstClickGate.shouldSwallowFirstClick() guard to the .onTapGesture in TabItemView; straightforward gate insertion with no isolation issues in a SwiftUI context.
Sources/Update/UpdateTitlebarAccessory.swift Wraps TitlebarControlButton action in a gated closure that checks PaneFirstClickGate; clean pattern, no isolation issues given SwiftUI body context.
Sources/AppDelegate.swift Installs PaneFirstClickGate once at app launch via applicationDidFinishLaunching; one-line addition with no issues.
cmuxTests/InactivePaneFirstClickFocusTests.swift Adds 3 unit tests covering within-grace, after-grace, and setting-enabled cases for PaneFirstClickGate; tests are correct and isolated via markActivatedForTesting.

Sequence Diagram

sequenceDiagram
    participant OS as macOS
    participant NSApp as NSApplication
    participant Gate as PaneFirstClickGate
    participant Chrome as TitlebarControlButton / WindowDecorationsController
    participant Action as Action Handler

    OS->>NSApp: App becomes active (user clicks window)
    NSApp->>Gate: didBecomeActiveNotification
    Gate->>Gate: "lastBecameActiveAt = systemUptime"

    Note over OS,Action: First click (within 200ms grace window)
    OS->>Chrome: leftMouseDown / SwiftUI tap
    Chrome->>Gate: shouldSwallowFirstClick(now:)
    Gate-->>Chrome: "true (elapsed < 0.2s)"
    Chrome-->>OS: swallow / return nil

    Note over OS,Action: Second click (after grace window)
    OS->>Chrome: leftMouseDown / SwiftUI tap
    Chrome->>Gate: shouldSwallowFirstClick(now:)
    Gate-->>Chrome: "false (elapsed >= 0.2s)"
    Chrome->>Action: perform action
Loading

Reviews (3): Last reviewed commit: "Gate minimal-mode chrome and sidebar row..." | Re-trigger Greptile

@rursache rursache changed the title Gate chrome acceptsFirstMouse on focusPaneOnFirstClick Gate minimal-mode chrome and sidebar on focusPaneOnFirstClick May 11, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/App/WorkspaceRuntimeSettings.swift`:
- Around line 96-123: The static state in PaneFirstClickGate
(lastBecameActiveAt, installed) is accessed from multiple functions (install(),
shouldSwallowFirstClick(now:), markActivatedForTesting(at:)) without guaranteed
main-thread isolation; annotate the enum with `@MainActor` (i.e., make the entire
PaneFirstClickGate `@MainActor`) so all reads/writes are main-thread isolated and
Swift concurrency checks pass, ensuring the notification handler,
shouldSwallowFirstClick(now:), and markActivatedForTesting(at:) are all
MainActor-bound.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aba31b09-8958-47b5-9f1e-8c79af0442f6

📥 Commits

Reviewing files that changed from the base of the PR and between 02d8930 and 5adac6e.

📒 Files selected for processing (6)
  • Sources/App/WorkspaceRuntimeSettings.swift
  • Sources/AppDelegate.swift
  • Sources/ContentView.swift
  • Sources/Update/UpdateTitlebarAccessory.swift
  • Sources/WindowDecorationsController.swift
  • cmuxTests/InactivePaneFirstClickFocusTests.swift

Comment thread Sources/App/WorkspaceRuntimeSettings.swift
Comment on lines +96 to +123
enum PaneFirstClickGate {
private static let graceInterval: TimeInterval = 0.2
private static var lastBecameActiveAt: TimeInterval = 0
private static var installed = false

@MainActor
static func install() {
guard !installed else { return }
installed = true
NotificationCenter.default.addObserver(
forName: NSApplication.didBecomeActiveNotification,
object: nil,
queue: .main
) { _ in
lastBecameActiveAt = ProcessInfo.processInfo.systemUptime
}
}

static func shouldSwallowFirstClick(now: TimeInterval = ProcessInfo.processInfo.systemUptime) -> Bool {
if PaneFirstClickFocusSettings.isEnabled() { return false }
let elapsed = now - lastBecameActiveAt
return elapsed >= 0 && elapsed < graceInterval
}

static func markActivatedForTesting(at time: TimeInterval = ProcessInfo.processInfo.systemUptime) {
lastBecameActiveAt = time
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Actor isolation gap on PaneFirstClickGate static vars

lastBecameActiveAt and installed are plain static var properties with no isolation annotation. The notification callback that writes lastBecameActiveAt runs on queue: .main (i.e., @MainActor), but shouldSwallowFirstClick() and markActivatedForTesting() are nonisolated, so Swift 6 strict concurrency cannot verify the reads are safe — they are a static data race from the compiler's perspective. All callers (onTapGesture, SwiftUI Button body, AppKit event handlers) are on the main actor in practice, but without the annotation the compiler cannot check this. Adding @MainActor to the two static vars (or to shouldSwallowFirstClick and markActivatedForTesting) makes the intent explicit and lets Swift 6 enforce it.

Rule Used: Flag new or materially worsened Swift 6 actor isol... (source)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
Sources/App/WorkspaceRuntimeSettings.swift (1)

96-123: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Isolate the full gate on MainActor to avoid static-state races

install() is main-actor isolated, but shouldSwallowFirstClick and markActivatedForTesting can still touch shared static state off-main. On Line 114 and Line 120, this keeps a concurrency hole open under strict Swift concurrency checks.

Suggested fix
+@MainActor
 enum PaneFirstClickGate {
     private static let graceInterval: TimeInterval = 0.2
     private static var lastBecameActiveAt: TimeInterval = 0
     private static var installed = false
 
-    `@MainActor`
     static func install() {
         guard !installed else { return }
         installed = true
         NotificationCenter.default.addObserver(

You can verify call-site isolation and current annotations with:

#!/bin/bash
set -euo pipefail

echo "Definitions and actor annotations:"
rg -n -C2 'enum PaneFirstClickGate|@MainActor|static func (install|shouldSwallowFirstClick|markActivatedForTesting)' Sources/App/WorkspaceRuntimeSettings.swift

echo
echo "Call sites of shouldSwallowFirstClick:"
rg -n -C2 'PaneFirstClickGate\.shouldSwallowFirstClick\s*\(' Sources cmuxTests

echo
echo "Call sites of markActivatedForTesting:"
rg -n -C2 'PaneFirstClickGate\.markActivatedForTesting\s*\(' Sources cmuxTests
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/App/WorkspaceRuntimeSettings.swift` around lines 96 - 123, The static
shared state in PaneFirstClickGate is not fully MainActor-isolated (install is
but shouldSwallowFirstClick and markActivatedForTesting are not), so annotate
the whole enum with `@MainActor` (add `@MainActor` before enum PaneFirstClickGate)
to ensure lastBecameActiveAt and all static methods are actor-isolated;
alternatively annotate both static methods and the stored properties with
`@MainActor` if you prefer finer-grained changes, and update any tests/call sites
to call these APIs from the main actor if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmuxTests/InactivePaneFirstClickFocusTests.swift`:
- Line 78: The test currently asserts
PaneFirstClickGate.shouldSwallowFirstClick(now: now + 0.05); update it to assert
behavior closer to the 200ms grace boundary to catch edge cases—replace or add
an assertion using PaneFirstClickGate.shouldSwallowFirstClick(now: now + 0.15)
or now + 0.19 (e.g., now + 0.19) so the test validates the gate behavior near
the 200ms threshold; keep the original assertion or add an additional test case
if you want both near-start and near-boundary coverage.

---

Duplicate comments:
In `@Sources/App/WorkspaceRuntimeSettings.swift`:
- Around line 96-123: The static shared state in PaneFirstClickGate is not fully
MainActor-isolated (install is but shouldSwallowFirstClick and
markActivatedForTesting are not), so annotate the whole enum with `@MainActor`
(add `@MainActor` before enum PaneFirstClickGate) to ensure lastBecameActiveAt and
all static methods are actor-isolated; alternatively annotate both static
methods and the stored properties with `@MainActor` if you prefer finer-grained
changes, and update any tests/call sites to call these APIs from the main actor
if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: db4a390d-35f6-4401-ac4e-b69744f587d0

📥 Commits

Reviewing files that changed from the base of the PR and between 5adac6e and cc85a73.

📒 Files selected for processing (6)
  • Sources/App/WorkspaceRuntimeSettings.swift
  • Sources/AppDelegate.swift
  • Sources/ContentView.swift
  • Sources/Update/UpdateTitlebarAccessory.swift
  • Sources/WindowDecorationsController.swift
  • cmuxTests/InactivePaneFirstClickFocusTests.swift

Comment thread cmuxTests/InactivePaneFirstClickFocusTests.swift
@rursache

Copy link
Copy Markdown
Author

Addressed CodeRabbit's actor-isolation note in the latest force-push: annotated PaneFirstClickGate with @MainActor (so all static state and members are main-actor isolated per SE-0316), used MainActor.assumeIsolated { ... } inside the NotificationCenter observer body and at the two WindowDecorationsController call sites that live in non-isolated closures (NSEvent.addLocalMonitorForEvents handler and the non-@MainActor handleMinimalModeSidebarChromeMouseDown helper). The other call sites (AppDelegate, SwiftUI Button action, .onTapGesture, XCTest @MainActor class) are already main-actor isolated so they call straight through. Unit tests still green.

When `focusPaneOnFirstClick` is disabled, the setting was honoured for
pane content (terminal, web view, markdown observer) but five chrome
paths still fired their actions on the first click of an inactive
window:

- `TitlebarControlButton` SwiftUI `Button`s in the titlebar (bell,
  sidebar toggle, `+`), whose action fires regardless of activation.
- The `NSEvent.addLocalMonitor` in `WindowDecorationsController` that
  resolves minimal-mode chrome clicks via the hit-region registry and
  calls `performMinimalModeSidebarControlAction` directly, bypassing
  every view-level check.
- The per-window send-event hook in the same controller.
- The sidebar workspace row's `.onTapGesture`, which switched
  workspaces on first click (the most surprising symptom).

Adds `PaneFirstClickGate` next to `PaneFirstClickFocusSettings`. It
observes `NSApplication.didBecomeActiveNotification` and exposes
`shouldSwallowFirstClick(now:)`, true within a 200 ms grace after
activation when the setting is disabled. By the time these SwiftUI
actions and event-monitor callbacks run, `NSApp.isActive` is already
true, so an activation-timestamp gate is the only reliable per-click
signal. The gate is installed once from `applicationDidFinishLaunching`
and queried at each of the four chrome/sidebar action sites.

Extends `InactivePaneFirstClickFocusTests` with grace-window,
after-grace, and setting-enabled coverage for the gate.
@rursache

Copy link
Copy Markdown
Author

Added a near-boundary assertion (now + 0.19) alongside the existing now + 0.05 case in testPaneFirstClickGateSwallowsWithinGraceWhenSettingDisabled, per CodeRabbit's suggestion. Both points are inside the 200 ms grace and should swallow; the existing testPaneFirstClickGateAllowsAfterGrace test (using now + 1.0) already covers the after-grace side. The earlier @MainActor isolation note is addressed in the previous push and the pre-merge "Cmux Swift Concurrency" check now passes.

Comment thread Sources/App/WorkspaceRuntimeSettings.swift
Comment thread Sources/WindowDecorationsController.swift
@rursache

Copy link
Copy Markdown
Author

closing because it's fixed in #3881

@rursache rursache closed this May 12, 2026
@rursache
rursache deleted the fix/focus-pane-first-click-chrome branch May 12, 2026 07:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

focusPaneOnFirstClick is ignored by minimal-mode toolbar and the workspace sidebar

1 participant