Skip to content

Add "Open System Settings" button to denied notifications banner (WAR-3575) - #14932

Draft
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
warp1/war-3575-allow-user-to-open-up-system-preferences-notifications-from
Draft

Add "Open System Settings" button to denied notifications banner (WAR-3575)#14932
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
warp1/war-3575-allow-user-to-open-up-system-preferences-notifications-from

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes WAR-3575: when the user has denied macOS's notification permission request, Warp's notification banners previously offered no direct path back to the OS setting.

Two banners are affected, and both are covered:

  • The notifications discovery banner (shown when the user clicks "Enable" and then denies the macOS permission prompt) — this is the exact moment the issue describes.
  • The notifications error banner (shown later, if a notification send fails because permission is still denied).

Both now show an "Open System Settings" button, offered only when the active state is PermissionsDenied and only on macOS (re-invoking the permission request is a no-op once macOS has denied it once — the system won't show the prompt again). The button deep-links via x-apple.systempreferences: directly into System Settings → Notifications, using the correct pane identifier for the running macOS version:

  • macOS 13 (Ventura) and later: com.apple.Notifications-Settings.extension
  • Older supported macOS (back to the project's 10.14 minimum): the legacy com.apple.preference.notifications

detected at runtime via NSProcessInfo.isOperatingSystemAtLeastVersion. The URL is also scoped with Warp's own bundle identifier (ChannelState::app_id()) via the ?id= query parameter, so it works correctly across every channel (Stable/Preview/Dev/Local/Oss) without hardcoding.

The change is compile-time gated behind #[cfg(target_os = "macos")] (enum variants, mouse-state fields, button rendering, and action handling), so non-mac platforms are unaffected and still compile.

  • The linked issue is labeled ready-to-spec or ready-to-implement. (Issue predates this label; work was assigned directly.)
  • Where appropriate, screenshots or a short video of the implementation are included below.

Changes

  • crates/warp_core/src/macos.rs (+ Cargo.toml): add is_system_settings_era(), detecting whether the running macOS is Ventura (13.0)+ via NSProcessInfo.
  • app/src/terminal/view.rs: mac_notification_settings_url_for(is_system_settings_era, app_id) (pure, testable) + mac_notification_settings_url() (production wrapper); wire the new OpenSystemSettings action into both banners' action handlers.
  • app/src/terminal/view/inline_banner/notifications_error.rs: OpenSystemSettings action/button when PermissionsDenied; button-list building extracted into a directly-testable function.
  • app/src/terminal/view/inline_banner/notifications_discovery.rs: same CTA added to the PermissionsDenied outcome branch; title/button building extracted into a directly-testable function.
  • New test files notifications_error_tests.rs / notifications_discovery_tests.rs, plus tests in view_tests.rs.

Testing

  • I have manually tested my changes locally with ./script/run (via a macOS verification runner — see below; I don't have a macOS environment directly in my own sandbox)

Linux sandbox (confirms the mac-gated code doesn't regress other platforms):

  • cargo check -p warp --bin warp-oss (+ --tests), cargo clippy -p warp --all-targets --tests -- -D warnings, ./script/format --check all pass.

Real macOS verification (macOS 26.3.1, Apple Silicon), against this exact branch/commit:

  • All new/updated unit and integration tests pass for real on macOS: cargo test -p warp --lib notification48 passed, 0 failed, including:
    • Pure URL-format tests for both the System-Settings and legacy System-Preferences identifiers.
    • Regression tests that drive each banner into a real PermissionsDenied state via production code paths, dispatch the actual CTA action handler, and assert on the exact URL captured via ctx.set_before_open_url.
    • Per-state button-presence tests for both banners (would fail if a CTA were missing, wrongly wired, or shown/hidden in the wrong state).
  • Real end-to-end capture: using a temporary, uncommitted debug trigger (reverted before finishing — verified git status clean afterward) to reach the live PermissionsDenied banner in a running ./script/run build, then:
    1. Triggered the debug command via the Command Palette.
    2. Confirmed the banner rendered with "Open System Settings", "Troubleshoot", and a close button, with the correct denial message.
    3. Clicked "Open System Settings" and confirmed System Settings actually opened to the Notifications area.
    4. Independently compared the new vs. legacy pane identifiers directly via open — both correctly launch System Settings on this macOS version.

Known limitation (environment, not code): on this sandbox, "Warp Local" was never registered with the OS via a real UNUserNotificationCenter permission request (no code-signed, long-lived install), so x-apple.systempreferences:...?id=dev.warp.Warp-Local resolves to the generic top-level Notifications pane rather than an app-specific sub-page — this is expected per Apple's behavior and is a property of the test environment, not the PR's code. The full click chain (button → action → URL → OS navigation) was verified end-to-end regardless.

Known caveat (macOS behavior, not specific to this change): if System Settings is already open to a different pane in the background, macOS brings the existing window forward without navigating to the requested pane — a pre-existing macOS quirk with the x-apple.systempreferences: URL scheme.

Screenshots / Videos

The denied-notifications banner, showing the "Open System Settings" button:

Denied notifications banner

System Settings after clicking "Open System Settings":

System Settings Notifications pane

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Conversation: https://staging.warp.dev/conversation/0c7f30f9-65c1-4e51-9af7-692edf842b38
Run: https://oz.staging.warp.dev/runs/019ff0ac-37a2-781e-b08b-40c868e3cfaa

This PR was generated with Oz.

…-3575)

When macOS denies desktop notification permissions, the notifications
error banner only offered "Troubleshoot" (a docs link) and "Close",
since re-requesting permission is a no-op once macOS has denied it.

Add an "Open System Settings" button, shown only in that denied state
on macOS, that deep-links directly into System Settings > Notifications
(scoped to Warp's own bundle identifier via the app_id-based ?id= query
param) so users can fix the permission without hunting through System
Settings themselves.

Co-Authored-By: Warp Agent <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Aug 11, 2026
1. Use the macOS 13+ (Ventura) System Settings extension bundle ID
   (com.apple.Notifications-Settings.extension) for the Notifications
   deep link, falling back to the legacy System Preferences pane ID
   (com.apple.preference.notifications) on older macOS, detected at
   runtime via NSProcessInfo.isOperatingSystemAtLeastVersion. Split the
   URL builder into a pure, version-agnostic function plus a thin
   production wrapper so both pane-ID branches can be tested
   deterministically.

2. Add the same "Open System Settings" CTA to the notifications
   discovery banner's PermissionsDenied branch -- the banner shown
   immediately after the user denies the macOS permission prompt from
   "Enable", which is the exact moment WAR-3575 describes. The
   error-banner CTA (for a later failed-send while already denied)
   remains from the previous commit.

3. Extract the button-list-building logic in both banners into
   dedicated, directly-testable functions, and add regression tests:
   - Unit tests asserting exactly which actions are offered per error
     state / permission outcome for both banners.
   - Integration tests that drive each banner into a real
     PermissionsDenied state via production code paths, dispatch the
     CTA's action through the real handler, and capture the URL that
     reaches the platform's open-url hook via ctx.set_before_open_url.
   - A pure test validating both the System Settings and legacy System
     Preferences URL formats.

Co-Authored-By: Warp Agent <agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant