Skip to content

Commit fe3ee04

Browse files
authored
Merge branch 'main' into fix/remote-protocol-relative-host
2 parents 7ea8f97 + fdca154 commit fe3ee04

87 files changed

Lines changed: 5999 additions & 423 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/desktop/src/ipc/DesktopIpcHandlers.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
getAppBranding,
3636
getLocalEnvironmentBootstraps,
3737
getLocalEnvironmentBearerToken,
38+
getWindowFullscreenState,
3839
openExternal,
3940
pickFolder,
4041
setTheme,
@@ -48,6 +49,7 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers"
4849
yield* PreviewIpc.installPreviewEventForwarding();
4950

5051
yield* ipc.handleSync(getAppBranding);
52+
yield* ipc.handleSync(getWindowFullscreenState);
5153
yield* ipc.handleSync(getLocalEnvironmentBootstraps);
5254
yield* ipc.handle(getLocalEnvironmentBearerToken);
5355

‎apps/desktop/src/ipc/channels.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export const SET_THEME_CHANNEL = "desktop:set-theme";
44
export const CONTEXT_MENU_CHANNEL = "desktop:context-menu";
55
export const OPEN_EXTERNAL_CHANNEL = "desktop:open-external";
66
export const MENU_ACTION_CHANNEL = "desktop:menu-action";
7+
export const GET_WINDOW_FULLSCREEN_STATE_CHANNEL = "desktop:get-window-fullscreen-state";
8+
export const WINDOW_FULLSCREEN_STATE_CHANNEL = "desktop:window-fullscreen-state";
79
export const UPDATE_STATE_CHANNEL = "desktop:update-state";
810
export const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state";
911
export const UPDATE_SET_CHANNEL_CHANNEL = "desktop:update-set-channel";

‎apps/desktop/src/ipc/methods/window.test.ts‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { assert, describe, it } from "@effect/vitest";
22
import * as Effect from "effect/Effect";
3+
import * as Layer from "effect/Layer";
34
import * as Option from "effect/Option";
45

6+
import type * as Electron from "electron";
7+
58
import * as DesktopBackendManager from "../../backend/DesktopBackendManager.ts";
69
import * as DesktopBackendPool from "../../backend/DesktopBackendPool.ts";
7-
import { getLocalEnvironmentBootstraps } from "./window.ts";
10+
import * as ElectronWindow from "../../electron/ElectronWindow.ts";
11+
import { getLocalEnvironmentBootstraps, getWindowFullscreenState } from "./window.ts";
812

913
const readyWslConfig: DesktopBackendManager.DesktopBackendStartConfig = {
1014
executablePath: "wsl.exe",
@@ -126,3 +130,19 @@ describe("getLocalEnvironmentBootstraps", () => {
126130
}).pipe(Effect.provide(DesktopBackendPool.layerTest([stoppedInstance])));
127131
});
128132
});
133+
134+
describe("getWindowFullscreenState", () => {
135+
it.effect("reads the current native window state", () => {
136+
const window = { isFullScreen: () => true } as Electron.BrowserWindow;
137+
138+
return Effect.gen(function* () {
139+
assert.isTrue(yield* getWindowFullscreenState.handler());
140+
}).pipe(
141+
Effect.provide(
142+
Layer.mock(ElectronWindow.ElectronWindow)({
143+
currentMainOrFirst: Effect.succeed(Option.some(window)),
144+
}),
145+
),
146+
);
147+
});
148+
});

‎apps/desktop/src/ipc/methods/window.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ export const getAppBranding = DesktopIpc.makeSyncIpcMethod({
5555
}),
5656
});
5757

58+
export const getWindowFullscreenState = DesktopIpc.makeSyncIpcMethod({
59+
channel: IpcChannels.GET_WINDOW_FULLSCREEN_STATE_CHANNEL,
60+
result: Schema.Boolean,
61+
handler: Effect.fn("desktop.ipc.window.getWindowFullscreenState")(function* () {
62+
const electronWindow = yield* ElectronWindow.ElectronWindow;
63+
const window = yield* electronWindow.currentMainOrFirst;
64+
return Option.isSome(window) && window.value.isFullScreen();
65+
}),
66+
});
67+
5868
export const getLocalEnvironmentBootstraps = DesktopIpc.makeSyncIpcMethod({
5969
channel: IpcChannels.GET_LOCAL_ENVIRONMENT_BOOTSTRAPS_CHANNEL,
6070
result: Schema.Array(DesktopEnvironmentBootstrapSchema),

‎apps/desktop/src/preload.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ contextBridge.exposeInMainWorld("desktopBridge", {
116116
ipcRenderer.removeListener(IpcChannels.MENU_ACTION_CHANNEL, wrappedListener);
117117
};
118118
},
119+
getWindowFullscreenState: () =>
120+
ipcRenderer.sendSync(IpcChannels.GET_WINDOW_FULLSCREEN_STATE_CHANNEL) === true,
121+
onWindowFullscreenStateChange: (listener) => {
122+
const wrappedListener = (_event: Electron.IpcRendererEvent, fullscreen: unknown) => {
123+
if (typeof fullscreen !== "boolean") return;
124+
listener(fullscreen);
125+
};
126+
127+
ipcRenderer.on(IpcChannels.WINDOW_FULLSCREEN_STATE_CHANNEL, wrappedListener);
128+
return () => {
129+
ipcRenderer.removeListener(IpcChannels.WINDOW_FULLSCREEN_STATE_CHANNEL, wrappedListener);
130+
};
131+
},
119132
getUpdateState: () => ipcRenderer.invoke(IpcChannels.UPDATE_GET_STATE_CHANNEL),
120133
setUpdateChannel: (channel) =>
121134
ipcRenderer.invoke(IpcChannels.UPDATE_SET_CHANNEL_CHANNEL, channel),

‎apps/desktop/src/window/DesktopWindow.test.ts‎

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import * as ElectronMenu from "../electron/ElectronMenu.ts";
2828
import * as ElectronShell from "../electron/ElectronShell.ts";
2929
import * as ElectronTheme from "../electron/ElectronTheme.ts";
3030
import * as ElectronWindow from "../electron/ElectronWindow.ts";
31-
import { MENU_ACTION_CHANNEL } from "../ipc/channels.ts";
31+
import { MENU_ACTION_CHANNEL, WINDOW_FULLSCREEN_STATE_CHANNEL } from "../ipc/channels.ts";
3232
import * as DesktopServerExposure from "../backend/DesktopServerExposure.ts";
3333
import * as DesktopWindow from "./DesktopWindow.ts";
3434
import * as PreviewManager from "../preview/Manager.ts";
@@ -46,6 +46,7 @@ const environmentInput = {
4646
} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput;
4747

4848
function makeFakeBrowserWindow() {
49+
const windowListeners = new Map<string, (...args: readonly unknown[]) => void>();
4950
const webContentsListeners = new Map<string, (...args: readonly unknown[]) => void>();
5051
const webContents = {
5152
copyImageAt: vi.fn(),
@@ -66,10 +67,13 @@ function makeFakeBrowserWindow() {
6667
close: vi.fn(),
6768
focus: vi.fn(),
6869
isDestroyed: vi.fn(() => false),
70+
isFullScreen: vi.fn(() => false),
6971
isMinimized: vi.fn(() => false),
7072
isVisible: vi.fn(() => true),
7173
loadURL: vi.fn(() => Promise.resolve()),
72-
on: vi.fn(),
74+
on: vi.fn((eventName: string, listener: (...args: readonly unknown[]) => void) => {
75+
windowListeners.set(eventName, listener);
76+
}),
7377
once: vi.fn(),
7478
restore: vi.fn(),
7579
setBackgroundColor: vi.fn(),
@@ -88,6 +92,7 @@ function makeFakeBrowserWindow() {
8892
send: webContents.send,
8993
setAutoHideCursor: window.setAutoHideCursor,
9094
webContentsListeners,
95+
windowListeners,
9196
};
9297
}
9398

@@ -338,6 +343,37 @@ describe("DesktopWindow", () => {
338343
}),
339344
);
340345

346+
it.effect("publishes native macOS fullscreen changes to the renderer", () =>
347+
Effect.gen(function* () {
348+
const fakeWindow = makeFakeBrowserWindow();
349+
const createCount = yield* Ref.make(0);
350+
const mainWindow = yield* Ref.make<Option.Option<Electron.BrowserWindow>>(Option.none());
351+
const layer = makeTestLayer({
352+
window: fakeWindow.window,
353+
createCount,
354+
mainWindow,
355+
});
356+
357+
yield* Effect.gen(function* () {
358+
const desktopWindow = yield* DesktopWindow.DesktopWindow;
359+
yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773"));
360+
361+
const enterFullscreen = fakeWindow.windowListeners.get("enter-full-screen");
362+
const leaveFullscreen = fakeWindow.windowListeners.get("leave-full-screen");
363+
if (!enterFullscreen || !leaveFullscreen) {
364+
return yield* Effect.die("fullscreen listeners were not registered");
365+
}
366+
367+
enterFullscreen();
368+
leaveFullscreen();
369+
assert.deepEqual(fakeWindow.send.mock.calls, [
370+
[WINDOW_FULLSCREEN_STATE_CHANNEL, true],
371+
[WINDOW_FULLSCREEN_STATE_CHANNEL, false],
372+
]);
373+
}).pipe(Effect.provide(layer));
374+
}),
375+
);
376+
341377
it.effect("recovers when the development renderer is temporarily unreachable", () =>
342378
Effect.gen(function* () {
343379
const fakeWindow = makeFakeBrowserWindow();

‎apps/desktop/src/window/DesktopWindow.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { getDesktopUrl } from "../electron/ElectronProtocol.ts";
1515
import * as ElectronShell from "../electron/ElectronShell.ts";
1616
import * as ElectronTheme from "../electron/ElectronTheme.ts";
1717
import * as ElectronWindow from "../electron/ElectronWindow.ts";
18-
import { MENU_ACTION_CHANNEL } from "../ipc/channels.ts";
18+
import { MENU_ACTION_CHANNEL, WINDOW_FULLSCREEN_STATE_CHANNEL } from "../ipc/channels.ts";
1919
import * as PreviewManager from "../preview/Manager.ts";
2020

2121
const TITLEBAR_HEIGHT = 40;
@@ -365,6 +365,15 @@ export const make = Effect.gen(function* () {
365365
window.setTitle(environment.displayName);
366366
});
367367

368+
if (environment.platform === "darwin") {
369+
window.on("enter-full-screen", () => {
370+
window.webContents.send(WINDOW_FULLSCREEN_STATE_CHANNEL, true);
371+
});
372+
window.on("leave-full-screen", () => {
373+
window.webContents.send(WINDOW_FULLSCREEN_STATE_CHANNEL, false);
374+
});
375+
}
376+
368377
let developmentLoadRetryIndex = 0;
369378
let developmentLoadRetryFiber: Fiber.Fiber<void, never> | undefined;
370379
const clearDevelopmentLoadRetry = () => {

0 commit comments

Comments
 (0)