Skip to content

Bind Cmd+[ / Cmd+] to back/forward in the desktop app#1228

Merged
RhysSullivan merged 1 commit into
mainfrom
fix/desktop-back-forward-accelerators
Jun 30, 2026
Merged

Bind Cmd+[ / Cmd+] to back/forward in the desktop app#1228
RhysSullivan merged 1 commit into
mainfrom
fix/desktop-back-forward-accelerators

Conversation

@RhysSullivan

Copy link
Copy Markdown
Owner

Problem

In the desktop app, the standard macOS back/forward shortcuts (Cmd+[ and Cmd+]) do nothing. The window loads the web shell, which navigates with browser history (TanStack Router pushState). On the web the browser chrome handles those keys, but inside the Electron window there is no chrome, and the application menu is built entirely from Electron roles (editMenu, viewMenu, windowMenu), none of which carry Back or Forward. Electron binds none of these shortcuts by default, so the keys were dead.

Fix

Add a History menu with Back (Cmd+[) and Forward (Cmd+]), Ctrl+[ / Ctrl+] on Windows and Linux, that drives the focused window's webContents.navigationHistory. The canGoBack / canGoForward guards make the keys no-ops at the ends of the history stack, matching how they behave in a browser. This follows the existing pattern in the same file where an explicit File menu was added purely to bind the Cmd+W close accelerator. It also places these shortcuts where Safari and Chrome put them.

Verification

  • bun run typecheck (apps/desktop), oxlint with --deny-warnings, and oxfmt --check all pass.
  • This is a native menu accelerator, so it is not exercised by the web e2e harness. Manual check: open the app, navigate between a few views, then press Cmd+[ to go back and Cmd+] to go forward.

The desktop window loads the web shell, which navigates with browser
history (TanStack Router pushState). On the web the browser chrome handles
the standard back/forward keys, but in the Electron window there is no
chrome, and the app menu is built entirely from roles, none of which carry
Back/Forward. Electron binds none of these shortcuts by default, so Cmd+[
and Cmd+] were dead keys.

Add a History menu with Back (Cmd+[) and Forward (Cmd+]), Ctrl+[ / Ctrl+]
on Windows/Linux, driving the focused window's navigation history. The
canGoBack/canGoForward guards make the keys no-ops at the ends of the
stack, matching how they behave in a browser.
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a History menu to the Electron application menu, binding Cmd+[ / Cmd+] (macOS) and Ctrl+[ / Ctrl+] (Windows/Linux) to back/forward navigation. Because the desktop window has no browser chrome, these shortcuts were previously dead keys despite the web shell using TanStack Router's pushState for navigation.

  • Adds a navigateHistory helper that retrieves BrowserWindow.getFocusedWindow()?.webContents.navigationHistory at click time and calls goBack()/goForward() guarded by canGoBack()/canGoForward().
  • Inserts a historyMenu between viewMenu and windowMenu, matching the History menu position in Safari and Chrome.

Confidence Score: 5/5

Safe to merge — a small, self-contained menu addition with no shared state and graceful null-checks at every step.

The change touches only the application menu construction function and introduces no new async logic, IPC channels, or shared state. The navigationHistory API is stable in Electron 41 (introduced in Electron 30). The canGo* guards prevent calls at history boundaries, and the optional-chaining on getFocusedWindow() prevents crashes when no window is focused. The accelerators don't conflict with any existing role-based shortcuts.

No files require special attention.

Important Files Changed

Filename Overview
apps/desktop/src/main/index.ts Adds History menu with Back/Forward accelerators using the Electron 30+ webContents.navigationHistory API, which is available in the project's Electron 41. Logic and guards are correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant NativeMenu as Native Menu (macOS/Win/Linux)
    participant ClickHandler as click: navigateHistory()
    participant BW as BrowserWindow.getFocusedWindow()
    participant NavHistory as webContents.navigationHistory

    User->>NativeMenu: Press Cmd+[ or Cmd+]
    NativeMenu->>ClickHandler: trigger accelerator
    ClickHandler->>BW: getFocusedWindow()
    BW-->>ClickHandler: "window | null"
    alt no focused window
        ClickHandler-->>NativeMenu: return (no-op)
    else window focused
        ClickHandler->>NavHistory: canGoBack() / canGoForward()
        NavHistory-->>ClickHandler: "true | false"
        alt can navigate
            ClickHandler->>NavHistory: goBack() / goForward()
            NavHistory-->>User: browser history navigated
        else at history boundary
            ClickHandler-->>NativeMenu: return (no-op)
        end
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant NativeMenu as Native Menu (macOS/Win/Linux)
    participant ClickHandler as click: navigateHistory()
    participant BW as BrowserWindow.getFocusedWindow()
    participant NavHistory as webContents.navigationHistory

    User->>NativeMenu: Press Cmd+[ or Cmd+]
    NativeMenu->>ClickHandler: trigger accelerator
    ClickHandler->>BW: getFocusedWindow()
    BW-->>ClickHandler: "window | null"
    alt no focused window
        ClickHandler-->>NativeMenu: return (no-op)
    else window focused
        ClickHandler->>NavHistory: canGoBack() / canGoForward()
        NavHistory-->>ClickHandler: "true | false"
        alt can navigate
            ClickHandler->>NavHistory: goBack() / goForward()
            NavHistory-->>User: browser history navigated
        else at history boundary
            ClickHandler-->>NativeMenu: return (no-op)
        end
    end
Loading

Reviews (1): Last reviewed commit: "Bind Cmd+[ / Cmd+] to back/forward in th..." | Re-trigger Greptile

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 67fde7c Commit Preview URL

Branch Preview URL
Jun 30 2026, 06:31 PM

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 67fde7c Jun 30 2026, 06:33 PM

@RhysSullivan RhysSullivan merged commit 2f9091a into main Jun 30, 2026
15 checks passed
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.

1 participant