From aa652d8bb0e04fc158191ab45c22d917fc509064 Mon Sep 17 00:00:00 2001 From: Vinit Neogi <20491952+vneogi199@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:01:49 +0530 Subject: [PATCH 1/3] fix(desktop): allow reopening the macOS window from File menu --- .../src/window/DesktopApplicationMenu.ts | 8 +++- apps/desktop/src/window/DesktopWindow.test.ts | 44 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/window/DesktopApplicationMenu.ts b/apps/desktop/src/window/DesktopApplicationMenu.ts index 07fd8654af6b..58d34a4b1186 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.ts @@ -116,6 +116,7 @@ const handleCheckForUpdatesMenuClick = Effect.gen(function* () { export const make = Effect.gen(function* () { const electronMenu = yield* ElectronMenu.ElectronMenu; const environment = yield* DesktopEnvironment.DesktopEnvironment; + const desktopWindow = yield* DesktopWindow.DesktopWindow; const context = yield* Effect.context(); const runPromise = Effect.runPromiseWith(context); @@ -191,7 +192,12 @@ export const make = Effect.gen(function* () { label: "File", submenu: [ ...(environment.platform === "darwin" - ? [] + ? [ + { + label: "Open Window", + click: () => runMenuEffect("open-window", desktopWindow.activate), + }, + ] : [ { label: "Settings...", diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index 16d345f97e4b..3c22c99ccdbc 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -8,6 +8,7 @@ import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Logger from "effect/Logger"; import * as Option from "effect/Option"; +import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; @@ -40,6 +41,9 @@ import * as DesktopState from "../app/DesktopState.ts"; import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts"; import * as DesktopClientSettings from "../settings/DesktopClientSettings.ts"; import * as ElectronApp from "../electron/ElectronApp.ts"; +import * as ElectronDialog from "../electron/ElectronDialog.ts"; +import * as DesktopApplicationMenu from "./DesktopApplicationMenu.ts"; +import * as DesktopUpdates from "../updates/DesktopUpdates.ts"; import * as DesktopRendererHistory from "../telemetry/DesktopRendererHistory.ts"; import * as ElectronMenu from "../electron/ElectronMenu.ts"; import * as ElectronShell from "../electron/ElectronShell.ts"; @@ -518,11 +522,14 @@ describe("DesktopWindow", () => { const fakeWindow = makeFakeBrowserWindow(); const createCount = yield* Ref.make(0); const mainWindow = yield* Ref.make>(Option.none()); + const createdWindows = yield* Queue.unbounded(); + const menuTemplate = yield* Deferred.make(); const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow, createdWindowOptions: [], + onWindowCreate: Queue.offer(createdWindows, undefined).pipe(Effect.asVoid), desktopSettings: { ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, localEnvironmentEnabled: false, @@ -538,7 +545,42 @@ describe("DesktopWindow", () => { yield* Ref.set(mainWindow, Option.none()); yield* desktopWindow.dispatchMenuAction("new-thread"); assert.equal(yield* Ref.get(createCount), 3); - }).pipe(Effect.provide(layer)); + + yield* Queue.takeN(createdWindows, 3); + yield* Ref.set(mainWindow, Option.none()); + const menu = yield* DesktopApplicationMenu.DesktopApplicationMenu; + yield* menu.configure; + const fileMenu = (yield* Deferred.await(menuTemplate)).find( + (item) => item.label === "File", + ); + if (!Array.isArray(fileMenu?.submenu)) { + throw new Error("Expected File menu submenu to be an array."); + } + const openWindow = fileMenu.submenu.find((item) => item.label === "Open Window"); + if (typeof openWindow?.click !== "function") { + throw new Error("Expected Open Window menu item to have a click handler."); + } + openWindow.click({} as Electron.MenuItem, undefined, {} as KeyboardEvent); + yield* Queue.take(createdWindows); + assert.equal(yield* Ref.get(createCount), 4); + }).pipe( + Effect.provide( + DesktopApplicationMenu.layer.pipe( + Layer.provideMerge(layer), + Layer.provide(layerDesktopEnvironment), + Layer.provide( + Layer.mergeAll( + Layer.mock(DesktopUpdates.DesktopUpdates)({}), + Layer.mock(ElectronDialog.ElectronDialog)({}), + Layer.mock(ElectronMenu.ElectronMenu)({ + setApplicationMenu: (template) => + Deferred.succeed(menuTemplate, template).pipe(Effect.asVoid), + }), + ), + ), + ), + ), + ); }), ); From 9744acdbe3448fdd11508dc14741033bf79181a6 Mon Sep 17 00:00:00 2001 From: Vinit Neogi <20491952+vneogi199@users.noreply.github.com> Date: Sat, 31 Oct 2026 14:20:05 +0530 Subject: [PATCH 2/3] test(desktop): close the window through its registered listener --- apps/desktop/src/window/DesktopWindow.test.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index f487769e9c65..b6676b808136 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -234,6 +234,7 @@ function layerTest(input: { readonly setPreviewMainWindow?: (window: Electron.BrowserWindow) => Effect.Effect; readonly destroyAll?: Effect.Effect; readonly onWindowCreate?: Effect.Effect; + readonly onMainClear?: Effect.Effect; readonly openedExternalUrls?: unknown[]; readonly copiedTexts?: string[]; readonly onPopupTemplate?: (input: ElectronMenu.ElectronMenuTemplateInput) => Effect.Effect; @@ -292,7 +293,10 @@ function layerTest(input: { ? Ref.get(input.mainWindow) : Effect.succeedSome(input.focusedWindow), setMain: (window) => Ref.set(input.mainWindow, Option.some(window)), - clearMain: () => Ref.set(input.mainWindow, Option.none()), + clearMain: () => + Ref.set(input.mainWindow, Option.none()).pipe( + Effect.andThen(input.onMainClear ?? Effect.void), + ), prepareReveal: () => Effect.succeed(false), reveal: (window) => Effect.sync(() => input.onReveal?.(window)), sendAll: () => Effect.void, @@ -530,6 +534,7 @@ describe("DesktopWindow", () => { const createCount = yield* Ref.make(0); const mainWindow = yield* Ref.make>(Option.none()); const createdWindows = yield* Queue.unbounded(); + const mainCleared = yield* Deferred.make(); const menuTemplate = yield* Deferred.make(); const layer = layerTest({ window: fakeWindow.window, @@ -537,6 +542,7 @@ describe("DesktopWindow", () => { mainWindow, createdWindowOptions: [], onWindowCreate: Queue.offer(createdWindows, undefined).pipe(Effect.asVoid), + onMainClear: Deferred.succeed(mainCleared, undefined).pipe(Effect.asVoid), desktopSettings: { ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, localEnvironmentEnabled: false, @@ -554,7 +560,11 @@ describe("DesktopWindow", () => { assert.equal(yield* Ref.get(createCount), 3); yield* Queue.takeN(createdWindows, 3); - yield* Ref.set(mainWindow, Option.none()); + const closed = fakeWindow.windowListeners.get("closed"); + assert.isDefined(closed); + closed(); + yield* Deferred.await(mainCleared); + assert.isTrue(Option.isNone(yield* Ref.get(mainWindow))); const menu = yield* DesktopApplicationMenu.DesktopApplicationMenu; yield* menu.configure; const fileMenu = (yield* Deferred.await(menuTemplate)).find( From f4bc6285f0eb370487ad3d44ae332999fc5b524b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 21:04:36 +0000 Subject: [PATCH 3/3] refactor(desktop): test Open Window in the menu tests Resolve the window service when the menu item runs, like the other menu actions, and replace the integration-style DesktopWindow test with a focused DesktopApplicationMenu case. AI-assisted; verified with the tests and tools listed above. Co-Authored-By: Claude Opus 5.5 --- .../src/window/DesktopApplicationMenu.test.ts | 26 ++++++++- .../src/window/DesktopApplicationMenu.ts | 8 ++- apps/desktop/src/window/DesktopWindow.test.ts | 56 +------------------ 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/apps/desktop/src/window/DesktopApplicationMenu.test.ts b/apps/desktop/src/window/DesktopApplicationMenu.test.ts index 612ec441ba99..9d4e26f64d51 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.test.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.test.ts @@ -83,7 +83,7 @@ const layerDesktopWindow = (selectedAction: Deferred.Deferred) => createMain: Effect.die("unexpected createMain"), ensureMain: Effect.die("unexpected ensureMain"), revealOrCreateMain: Effect.die("unexpected revealOrCreateMain"), - activate: Effect.void, + activate: Deferred.succeed(selectedAction, "activate").pipe(Effect.asVoid), createMainIfBackendReady: Effect.void, showConnectingSplash: Effect.void, handleBackendReady: () => Effect.void, @@ -205,6 +205,30 @@ describe("DesktopApplicationMenu", () => { }), ); + it.effect("reopens the main window from File > Open Window on macOS", () => + Effect.gen(function* () { + const selectedAction = yield* Deferred.make(); + const applicationMenuTemplate = + yield* Deferred.make(); + + yield* configureMenu(selectedAction, applicationMenuTemplate, { platform: "darwin" }); + + const fileMenu = (yield* Deferred.await(applicationMenuTemplate)).find( + (item) => item.label === "File", + ); + if (!Array.isArray(fileMenu?.submenu)) { + throw new Error("Expected File menu submenu to be an array."); + } + const openWindow = fileMenu.submenu.find((item) => item.label === "Open Window"); + if (typeof openWindow?.click !== "function") { + throw new Error("Expected Open Window menu item to have a click handler."); + } + + openWindow.click({} as Electron.MenuItem, undefined, {} as KeyboardEvent); + assert.equal(yield* Deferred.await(selectedAction), "activate"); + }), + ); + it.effect("never binds Cmd/Ctrl+W to closing the window", () => Effect.gen(function* () { for (const platform of ["darwin", "linux", "win32"] as const) { diff --git a/apps/desktop/src/window/DesktopApplicationMenu.ts b/apps/desktop/src/window/DesktopApplicationMenu.ts index 87daef100c6b..f2a6b6307e5a 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.ts @@ -64,6 +64,11 @@ const runMainContentsCommand = Effect.fn("desktop.menu.runMainContentsCommand")( yield* desktopWindow.runMainContentsCommand(command); }); +const activateMainWindow = Effect.gen(function* () { + const desktopWindow = yield* DesktopWindow.DesktopWindow; + yield* desktopWindow.activate; +}).pipe(Effect.withSpan("desktop.menu.activateMainWindow")); + const checkForUpdatesFromMenu = Effect.gen(function* () { const updates = yield* DesktopUpdates.DesktopUpdates; const electronDialog = yield* ElectronDialog.ElectronDialog; @@ -116,7 +121,6 @@ const handleCheckForUpdatesMenuClick = Effect.gen(function* () { export const make = Effect.gen(function* () { const electronMenu = yield* ElectronMenu.ElectronMenu; const environment = yield* DesktopEnvironment.DesktopEnvironment; - const desktopWindow = yield* DesktopWindow.DesktopWindow; const context = yield* Effect.context(); const runPromise = Effect.runPromiseWith(context); @@ -195,7 +199,7 @@ export const make = Effect.gen(function* () { ? [ { label: "Open Window", - click: () => runMenuEffect("open-window", desktopWindow.activate), + click: () => runMenuEffect("open-window", activateMainWindow), }, ] : [ diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index 4193eb936302..497b315ef522 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -8,7 +8,6 @@ import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Logger from "effect/Logger"; import * as Option from "effect/Option"; -import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; @@ -42,9 +41,6 @@ import * as DesktopState from "../app/DesktopState.ts"; import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts"; import * as DesktopClientSettings from "../settings/DesktopClientSettings.ts"; import * as ElectronApp from "../electron/ElectronApp.ts"; -import * as ElectronDialog from "../electron/ElectronDialog.ts"; -import * as DesktopApplicationMenu from "./DesktopApplicationMenu.ts"; -import * as DesktopUpdates from "../updates/DesktopUpdates.ts"; import * as DesktopRendererHistory from "../telemetry/DesktopRendererHistory.ts"; import * as ElectronMenu from "../electron/ElectronMenu.ts"; import * as ElectronShell from "../electron/ElectronShell.ts"; @@ -241,7 +237,6 @@ function layerTest(input: { readonly setPreviewMainWindow?: (window: Electron.BrowserWindow) => Effect.Effect; readonly destroyAll?: Effect.Effect; readonly onWindowCreate?: Effect.Effect; - readonly onMainClear?: Effect.Effect; readonly openedExternalUrls?: unknown[]; readonly onCopyText?: (text: string) => Effect.Effect; readonly onPopupTemplate?: (input: ElectronMenu.ElectronMenuTemplateInput) => Effect.Effect; @@ -302,10 +297,7 @@ function layerTest(input: { ? Ref.get(input.mainWindow) : Effect.succeedSome(input.focusedWindow), setMain: (window) => Ref.set(input.mainWindow, Option.some(window)), - clearMain: () => - Ref.set(input.mainWindow, Option.none()).pipe( - Effect.andThen(input.onMainClear ?? Effect.void), - ), + clearMain: () => Ref.set(input.mainWindow, Option.none()), prepareReveal: () => Effect.succeed(false), reveal: (window) => Effect.sync(() => input.onReveal?.(window)), sendAll: () => Effect.void, @@ -567,16 +559,11 @@ describe("DesktopWindow", () => { const fakeWindow = makeFakeBrowserWindow(); const createCount = yield* Ref.make(0); const mainWindow = yield* Ref.make>(Option.none()); - const createdWindows = yield* Queue.unbounded(); - const mainCleared = yield* Deferred.make(); - const menuTemplate = yield* Deferred.make(); const layer = layerTest({ window: fakeWindow.window, createCount, mainWindow, createdWindowOptions: [], - onWindowCreate: Queue.offer(createdWindows, undefined).pipe(Effect.asVoid), - onMainClear: Deferred.succeed(mainCleared, undefined).pipe(Effect.asVoid), desktopSettings: { ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, localEnvironmentEnabled: false, @@ -592,46 +579,7 @@ describe("DesktopWindow", () => { yield* Ref.set(mainWindow, Option.none()); yield* desktopWindow.dispatchMenuAction("new-thread"); assert.equal(yield* Ref.get(createCount), 3); - - yield* Queue.takeN(createdWindows, 3); - const closed = fakeWindow.windowListeners.get("closed"); - assert.isDefined(closed); - closed(); - yield* Deferred.await(mainCleared); - assert.isTrue(Option.isNone(yield* Ref.get(mainWindow))); - const menu = yield* DesktopApplicationMenu.DesktopApplicationMenu; - yield* menu.configure; - const fileMenu = (yield* Deferred.await(menuTemplate)).find( - (item) => item.label === "File", - ); - if (!Array.isArray(fileMenu?.submenu)) { - throw new Error("Expected File menu submenu to be an array."); - } - const openWindow = fileMenu.submenu.find((item) => item.label === "Open Window"); - if (typeof openWindow?.click !== "function") { - throw new Error("Expected Open Window menu item to have a click handler."); - } - openWindow.click({} as Electron.MenuItem, undefined, {} as KeyboardEvent); - yield* Queue.take(createdWindows); - assert.equal(yield* Ref.get(createCount), 4); - }).pipe( - Effect.provide( - DesktopApplicationMenu.layer.pipe( - Layer.provideMerge(layer), - Layer.provide(layerDesktopEnvironment), - Layer.provide( - Layer.mergeAll( - Layer.mock(DesktopUpdates.DesktopUpdates)({}), - Layer.mock(ElectronDialog.ElectronDialog)({}), - Layer.mock(ElectronMenu.ElectronMenu)({ - setApplicationMenu: (template) => - Deferred.succeed(menuTemplate, template).pipe(Effect.asVoid), - }), - ), - ), - ), - ), - ); + }).pipe(Effect.provide(layer)); }), );