Skip to content

fix(macos): bind Safari navigation to press-time process - #1082

Open
foleykyle01 wants to merge 14 commits into
AprilNEA:masterfrom
foleykyle01:fix/736-safari-navigation
Open

fix(macos): bind Safari navigation to press-time process#1082
foleykyle01 wants to merge 14 commits into
AprilNEA:masterfrom
foleykyle01:fix/736-safari-navigation

Conversation

@foleykyle01

@foleykyle01 foleykyle01 commented Aug 27, 2026

Copy link
Copy Markdown

Summary

Harden Safari Back and Forward dispatch on macOS by binding each action to the Safari process that was active when the physical input was pressed.

#1225 fixed the primary routing problem by making explicit Browser Back and Browser Forward bindings enter the dispatch path. This PR now covers the remaining Safari-specific focus-safety work: queued input cannot fall through to an application that became active after the press.

Changes

  • Capture Safari's process ID at press time and carry that typed target through OS-hook, HID++, pulse, gesture, held-key, and queued one-shot paths.
  • Maintain the press-time Safari snapshot from the existing NSWorkspace activation observer, keeping AppKit reads off the freeze-sensitive event-tap callback.
  • Validate that the captured Safari process is still frontmost before AXPress; do not send a keyboard fallback after the target becomes stale.
  • Release the navigation debounce reservation when Accessibility dispatch fails, while preserving keyboard shortcuts for non-Safari applications.
  • Cover target capture, stale focus, button lifecycles, queued dispatch, debounce cleanup, and Safari snapshot filtering with regression tests.

Testing

  • cargo fmt --all -- --check
  • RUSTFLAGS='-D warnings' cargo clippy --workspace --all-targets -- -D warnings
  • RUSTFLAGS='-D warnings' cargo test --workspace — all runnable tests passed except openlogi-hook::tap_callback_suppresses_normally_and_passes_through_panics; the same unchanged test fails on origin/master because this host session cannot create a private CGEventSource.
  • RUSTDOCFLAGS='-D warnings' cargo doc --workspace --no-deps --document-private-items --exclude openlogi-ui --exclude openlogi-desktop --exclude openlogi-overlay --exclude openlogi-agent
  • RUSTFLAGS='-D warnings' cargo xtask ci clippy-windows
  • Linux-musl cross-target Clippy for the affected non-macOS crates.
  • Hardware-tested physical Back and Forward with an MX Master 4 in both Safari and Chrome using the signed development bundle built from 058d9a99. Current head e9678b92 is an empty CI-retry commit with the identical source tree.
  • Upstream CI run 34380533484: four Ubuntu jobs stopped in apt-get update before compilation because dl.google.com/linux/chrome-stable returned a package-index hash mismatch. The retry workflows require upstream maintainer approval.

Related

@foleykyle01
foleykyle01 requested a review from AprilNEA as a code owner August 27, 2026 16:27
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; the outstanding synchronous AppKit event-tap issue is fixed and no new changes or rule violations remain to review.

Summary

  • Captures Safari’s process ID from an asynchronously maintained frontmost-application snapshot.
  • Carries the press-time target through hook, HID++, gesture, held-key, pulse, and queued-action paths.
  • Validates that the captured Safari process remains frontmost before performing AXPress.
  • Suppresses unsafe keyboard fallback for stale Safari targets and releases failed debounce reservations.
  • Adds regression coverage for target propagation, stale focus, debounce cleanup, and button lifecycles.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Physical Back or Forward press] --> B[Read atomic frontmost Safari PID snapshot]
    B -->|Safari PID| C[Carry SafariProcess PID through queue]
    B -->|Not Safari| D[Carry Keyboard target through queue]
    C --> E[Reserve navigation debounce slot]
    D --> E
    E --> F{Target type}
    F -->|SafariProcess| G{Captured Safari PID still frontmost?}
    G -->|Yes| H[AXPress Safari toolbar button]
    G -->|No| I[Suppress keyboard fallback]
    H -->|Failure| J[Release debounce reservation]
    I --> J
    F -->|Keyboard| K[Send browser keyboard shortcut]
Loading

Comment thread crates/openlogi-agent-core/src/runtime.rs Outdated
@foleykyle01
foleykyle01 force-pushed the fix/736-safari-navigation branch from 1ea4782 to 3b12248 Compare August 27, 2026 17:02
Comment thread crates/openlogi-agent-core/src/runtime.rs Outdated
@davidbudnick davidbudnick added type: bug Something is broken or behaves incorrectly platform: macos macOS-specific issue labels Aug 27, 2026
@foleykyle01

foleykyle01 commented Aug 27, 2026

Copy link
Copy Markdown
Author

Safari took a brief detour through the Linux and Windows build lanes lol. It’s back in its macOS lane now—cross-target Clippy is green locally.

@nehal3m

nehal3m commented Aug 28, 2026

Copy link
Copy Markdown

Would absolutely love a version with this fix in it. Reversing the buttons is a workaround, but muscle memory is driving me insane.

Love the app by the way, so glad there's finally a good replacement.

@foleykyle01
foleykyle01 force-pushed the fix/736-safari-navigation branch from c5c60eb to bc73714 Compare August 28, 2026 18:55
@foleykyle01 foleykyle01 changed the title fix(macos): restore back and forward buttons in Safari fix(macos): restore Safari back and forward navigation Aug 28, 2026
Comment thread crates/openlogi-agent-core/src/runtime.rs
@foleykyle01

Copy link
Copy Markdown
Author

Greptile's latest reservation warning assumes the two mouse capture paths dispatch concurrently. They do not: OS-hook and HID++ Back/Forward events both enter the same ButtonRuntimeOwner worker, and Safari AX dispatch is synchronous in that worker. If AX fails, browser_nav_debounce_cancel runs before the worker can dequeue the duplicate event, so that second path is admitted. The failed_captured_navigation_releases_its_debounce_reservation regression covers this sequence. The separate hook action worker handles configured function-key actions, not duplicate mouse capture, so I do not think a code change is needed for this finding.

@foleykyle01
foleykyle01 force-pushed the fix/736-safari-navigation branch 2 times, most recently from 87d898e to 1054ad8 Compare August 30, 2026 16:34
@eyvind

eyvind commented Sep 6, 2026

Copy link
Copy Markdown

Have you looked at the swipe-based approach used by e.g https://github.com/weiyou/sensible-side-buttons-rust? It seems like a better option since it doesn't require any browser-specific workarounds.

@foleykyle01

Copy link
Copy Markdown
Author

Thanks for the suggestion. This PR is intentionally scoped to fixing Back/Forward in Safari on macOS. Switching to synthesized swipe events would be a broader architectural decision for the maintainers, so that is outside the scope of this fix.

@foleykyle01
foleykyle01 force-pushed the fix/736-safari-navigation branch from 1054ad8 to b6ff420 Compare September 9, 2026 14:20
Comment on lines +39 to +42
impl ActionDispatchTarget {
fn capture() -> Self {
openlogi_inject::frontmost_safari_pid().map_or(Self::Keyboard, Self::SafariProcess)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Synchronous Input-Tap AppKit Call

ActionDispatchTarget::capture() calls NSWorkspace.frontmostApplication() synchronously from the macOS global event-tap callback. This capture runs for every candidate button press, bound key, and thumb-wheel action, not only browser navigation. If the AppKit call stalls, shorter stalls delay the system-wide input tap, while a stall beyond the 200 ms watchdog budget terminates the agent. Capture the application identity without doing potentially blocking AppKit work on the tap thread, for example by maintaining an asynchronous frontmost-application snapshot.

Fix in Codex Fix in Claude Code

@foleykyle01
foleykyle01 force-pushed the fix/736-safari-navigation branch from b6ff420 to 058d9a9 Compare September 9, 2026 17:03
@foleykyle01 foleykyle01 changed the title fix(macos): restore Safari back and forward navigation fix(macos): bind Safari navigation to press-time process Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: macos macOS-specific issue type: bug Something is broken or behaves incorrectly

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants