diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index 8b6ab7c7d8a5e..eb306ece80c32 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -78,6 +78,12 @@ function makeFakeBrowserWindow() { zoomLevel = level; }), isLoadingMainFrame: vi.fn(() => false), + navigationHistory: { + canGoBack: vi.fn(() => true), + canGoForward: vi.fn(() => true), + goBack: vi.fn(), + goForward: vi.fn(), + }, on: vi.fn((eventName: string, listener: (...args: readonly unknown[]) => void) => { webContentsListeners.set(eventName, listener); }), @@ -136,6 +142,7 @@ function makeFakeBrowserWindow() { reload: webContents.reload, reloadIgnoringCache: webContents.reloadIgnoringCache, toggleDevTools: webContents.toggleDevTools, + navigationHistory: webContents.navigationHistory, send: webContents.send, setZoomLevel: webContents.setZoomLevel, setWindowButtonPosition: window.setWindowButtonPosition, @@ -895,6 +902,39 @@ describe("DesktopWindow", () => { }), ); + it.effect("maps macOS horizontal swipes to available history navigation", () => + Effect.gen(function* () { + const fakeWindow = makeFakeBrowserWindow(); + const createCount = yield* Ref.make(0); + const mainWindow = yield* Ref.make>(Option.none()); + const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow }); + + yield* Effect.gen(function* () { + const desktopWindow = yield* DesktopWindow.DesktopWindow; + yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773")); + const swipe = fakeWindow.windowListeners.get("swipe"); + assert.isDefined(swipe); + if (!swipe) return; + const history = fakeWindow.navigationHistory; + + swipe({}, "left"); + assert.equal(history.goBack.mock.calls.length, 1); + assert.equal(history.goForward.mock.calls.length, 0); + swipe({}, "right"); + assert.equal(history.goForward.mock.calls.length, 1); + + swipe({}, "up"); + swipe({}, "down"); + history.canGoBack.mockReturnValue(false); + history.canGoForward.mockReturnValue(false); + swipe({}, "left"); + swipe({}, "right"); + assert.equal(history.goBack.mock.calls.length, 1); + assert.equal(history.goForward.mock.calls.length, 1); + }).pipe(Effect.provide(layer)); + }), + ); + it.effect("persists the current main window bounds before the window closes", () => Effect.gen(function* () { const fakeWindow = makeFakeBrowserWindow(); diff --git a/apps/desktop/src/window/DesktopWindow.ts b/apps/desktop/src/window/DesktopWindow.ts index 6270f80a4a7e6..5d28c88335355 100644 --- a/apps/desktop/src/window/DesktopWindow.ts +++ b/apps/desktop/src/window/DesktopWindow.ts @@ -673,6 +673,12 @@ export const make = Effect.gen(function* () { }); if (environment.platform === "darwin") { + // Mouse drivers can deliver Back/Forward as macOS swipe gestures instead of buttons. + window.on("swipe", (_event, direction) => { + const history = window.webContents.navigationHistory; + if (direction === "left" && history.canGoBack()) history.goBack(); + else if (direction === "right" && history.canGoForward()) history.goForward(); + }); window.on("enter-full-screen", () => { window.webContents.send(WINDOW_FULLSCREEN_STATE_CHANNEL, true); });