History

chore(deps): bump Electron to 44.1.0

Electron 44 turns clipboard.writeText into a Promise and removes clipboard.writeImage in favour of clipboard.write with ClipboardItem, so the shell and preview manager follow. node-abi is pinned to a release that knows the new Electron ABI so prebuilt native modules resolve during packaging, and the release-age policy excludes this exact version.

Julius Marminge committed 2 months ago

Commit 82bc5b61 parent 0d345797 files · +93 −27Browse files

Unified · Split

7 files
  1. apps/desktop/package.json +1 −1
  2. apps/desktop/src/electron/ElectronShell.test.ts +22 −0
  3. apps/desktop/src/electron/ElectronShell.ts +1 −3
  4. apps/desktop/src/preview/Manager.test.ts +36 −7
  5. apps/desktop/src/preview/Manager.ts +17 −3
  6. pnpm-lock.yaml +14 −13
  7. pnpm-workspace.yaml +2 −0
apps/desktop/package.json+1−1
@@ -22,7 +22,7 @@ · 21 unmodified lines
2222 "@t3tools/ssh": "workspace:*",
2323 "@t3tools/tailscale": "workspace:*",
2424 "effect": "catalog:",
25− "electron": "43.4.1",
25+ "electron": "44.1.0",
2626 "electron-store": "^8.2.0",
2727 "electron-updater": "^6.6.2",
2828 "playwright-core": "1.60.0",
apps/desktop/src/electron/ElectronShell.test.ts+22−0
@@ -36,6 +36,28 @@ describe("ElectronShell", () => { · 35 unmodified lines
3636 }).pipe(Effect.provide(ElectronShell.layer)),
3737 );
3838
39+ it.effect("copies text to the system clipboard", () =>
40+ Effect.gen(function* () {
41+ writeTextMock.mockResolvedValue(undefined);
42+
43+ const electronShell = yield* ElectronShell.ElectronShell;
44+ yield* electronShell.copyText("https://example.com/path");
45+
46+ assert.deepEqual(writeTextMock.mock.calls, [["https://example.com/path"]]);
47+ }).pipe(Effect.provide(ElectronShell.layer)),
48+ );
49+
50+ it.effect("does not fail when the clipboard write rejects", () =>
51+ Effect.gen(function* () {
52+ writeTextMock.mockRejectedValue(new Error("write failed"));
53+
54+ const electronShell = yield* ElectronShell.ElectronShell;
55+ yield* electronShell.copyText("https://example.com/path");
56+
57+ assert.deepEqual(writeTextMock.mock.calls, [["https://example.com/path"]]);
58+ }).pipe(Effect.provide(ElectronShell.layer)),
59+ );
60+
3961 it.effect("opens the Full Disk Access settings anchor", () =>
4062 Effect.gen(function* () {
4163 openExternalMock.mockResolvedValue(undefined);
apps/desktop/src/electron/ElectronShell.ts+1−3
@@ -87,9 +87,7 @@ export const make = ElectronShell.of({ · 86 unmodified lines
8787 ),
8888 ),
8989 copyText: (text) =>
90− Effect.sync(() => {
91− Electron.clipboard.writeText(text);
92− }),
90+ Effect.promise(() => Electron.clipboard.writeText(text).catch(() => undefined)),
9391 });
9492
9593 export const layer = Layer.succeed(ElectronShell, make);
apps/desktop/src/preview/Manager.test.ts+36−7
@@ -113,6 +113,7 @@ describe("previewWindowOpenAction", () => { · 112 unmodified lines
113113
114114 const {
115115 browserWindowConstructor,
116+ clipboardItemConstructor,
116117 createFromPath,
117118 fromId,
118119 getFocusedWebContents,
@@ -120,23 +121,32 @@ const { · 1 unmodified line
120121 showItemInFolder,
121122 webviewSend,
122123 writeFile,
123− writeImage,
124+ writeClipboard,
124125 } = vi.hoisted(() => ({
125126 browserWindowConstructor: vi.fn(),
126− createFromPath: vi.fn((): { readonly isEmpty: () => boolean } => ({ isEmpty: () => false })),
127+ clipboardItemConstructor: vi.fn(),
128+ createFromPath: vi.fn((): { readonly isEmpty: () => boolean; readonly toPNG: () => Buffer } => ({
129+ isEmpty: () => false,
130+ toPNG: () => Buffer.from("png"),
131+ })),
127132 fromId: vi.fn<(_id?: number) => Electron.WebContents | null>((_id?: number) => null),
128133 getFocusedWebContents: vi.fn(() => null),
129134 mkdir: vi.fn((_path: string) => undefined),
130135 showItemInFolder: vi.fn(),
131136 webviewSend: vi.fn(),
132137 writeFile: vi.fn((_path: string, _data: Uint8Array) => undefined),
133− writeImage: vi.fn(),
138+ writeClipboard: vi.fn(async () => undefined),
134139 }));
135140
136141 vi.mock("electron", () => ({
137142 BrowserWindow: browserWindowConstructor,
143+ ClipboardItem: class {
144+ constructor(data: Record<string, unknown>) {
145+ clipboardItemConstructor(data);
146+ }
147+ },
138148 clipboard: {
139− writeImage,
149+ write: writeClipboard,
140150 },
141151 nativeImage: {
142152 createFromPath,
@@ -471,7 +481,8 @@ describe("PreviewManager", () => { · 328 unmodified lines
471481 mkdir.mockClear();
472482 writeFile.mockClear();
473483 showItemInFolder.mockClear();
474− writeImage.mockClear();
484+ clipboardItemConstructor.mockClear();
485+ writeClipboard.mockClear();
475486 createFromPath.mockClear();
476487 webviewSend.mockClear();
477488 });
@@ -3671,7 +3682,10 @@ describe("PreviewManager", () => { · 3193 unmodified lines
36713682 yield* manager.copyArtifactToClipboard(artifactPath);
36723683
36733684 expect(createFromPath).toHaveBeenCalledWith(artifactPath);
3674− expect(writeImage).toHaveBeenCalledOnce();
3685+ expect(clipboardItemConstructor).toHaveBeenCalledWith({
3686+ "image/png": expect.any(Blob),
3687+ });
3688+ expect(writeClipboard).toHaveBeenCalledOnce();
36753689 const exit = yield* Effect.exit(
36763690 manager.copyArtifactToClipboard("/tmp/t3/dev/settings.json"),
36773691 );
@@ -3685,7 +3699,10 @@ describe("PreviewManager", () => { · 7 unmodified lines
36853699 });
36863700 expect("cause" in error).toBe(false);
36873701
3688− createFromPath.mockReturnValueOnce({ isEmpty: () => true });
3702+ createFromPath.mockReturnValueOnce({
3703+ isEmpty: () => true,
3704+ toPNG: () => Buffer.from("invalid"),
3705+ });
36893706 const invalidImageExit = yield* Effect.exit(manager.copyArtifactToClipboard(artifactPath));
36903707 expect(Exit.isFailure(invalidImageExit)).toBe(true);
36913708 if (Exit.isSuccess(invalidImageExit)) return;
@@ -3693,6 +3710,18 @@ describe("PreviewManager", () => { · 1 unmodified line
36933710 _tag: "PreviewArtifactImageLoadError",
36943711 artifactPath,
36953712 });
3713+
3714+ const writeCause = new Error("clipboard write failed");
3715+ writeClipboard.mockRejectedValueOnce(writeCause);
3716+ const writeExit = yield* Effect.exit(manager.copyArtifactToClipboard(artifactPath));
3717+ expect(Exit.isFailure(writeExit)).toBe(true);
3718+ if (Exit.isSuccess(writeExit)) return;
3719+ expect(Option.getOrThrow(Cause.findErrorOption(writeExit.cause))).toMatchObject({
3720+ _tag: "PreviewOperationError",
3721+ operation: "copyArtifactToClipboard.write",
3722+ artifactPath,
3723+ cause: writeCause,
3724+ });
36963725 }),
36973726 ),
36983727 );
apps/desktop/src/preview/Manager.ts+17−3
@@ -32,7 +32,15 @@ import type { · 31 unmodified lines
3232 } from "@t3tools/contracts";
3333 import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
3434 import { normalizePreviewUrl } from "@t3tools/shared/preview";
35−import { BrowserWindow, type Session, clipboard, nativeImage, shell, webContents } from "electron";
35+import {
36+ BrowserWindow,
37+ ClipboardItem,
38+ type Session,
39+ clipboard,
40+ nativeImage,
41+ shell,
42+ webContents,
43+} from "electron";
3644 import * as Cause from "effect/Cause";
3745 import * as Clock from "effect/Clock";
3846 import * as Context from "effect/Context";
@@ -4107,8 +4115,14 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function · 4068 unmodified lines
41074115 if (image.isEmpty()) {
41084116 return yield* new PreviewArtifactImageLoadError({ artifactPath: resolvedPath });
41094117 }
4110− yield* attempt({ operation: "copyArtifactToClipboard.write", artifactPath: resolvedPath }, () =>
4111− clipboard.writeImage(image),
4118+ yield* attemptPromise(
4119+ { operation: "copyArtifactToClipboard.write", artifactPath: resolvedPath },
4120+ () =>
4121+ clipboard.write([
4122+ new ClipboardItem({
4123+ "image/png": new Blob([Uint8Array.from(image.toPNG())], { type: "image/png" }),
4124+ }),
4125+ ]),
41124126 );
41134127 });
41144128
pnpm-lock.yaml+14−13
@@ -79,6 +79,7 @@ overrides: · 78 unmodified lines
7979 expo-router: 57.0.17
8080 expo-sharing>@expo/config-plugins: 57.0.9
8181 expo-sharing>@expo/config-types: 57.0.2
82+ node-abi: 4.33.0
8283 lightningcss: 1.33.0
8384 tailwindcss: 4.3.3
8485 vite: npm:@voidzero-dev/vite-plus-core@0.3.0
@@ -129,7 +130,7 @@ importers: · 44 unmodified lines
129130 dependencies:
130131 '@clerk/electron':
131132 specifier: 0.0.37
132− version: 0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@43.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
133+ version: 0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
133134 '@clerk/electron-passkeys':
134135 specifier: 0.0.3
135136 version: 0.0.3
@@ -158,8 +159,8 @@ importers: · 22 unmodified lines
158159 specifier: 4.0.0-beta.103
159160 version: 4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6)
160161 electron:
161− specifier: 43.4.1
162− version: 43.4.1
162+ specifier: 44.1.0
163+ version: 44.1.0
163164 electron-store:
164165 specifier: ^8.2.0
165166 version: 8.2.0
@@ -556,7 +557,7 @@ importers: · 390 unmodified lines
556557 version: 1.5.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
557558 '@clerk/electron':
558559 specifier: 0.0.37
559− version: 0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@43.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
560+ version: 0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
560561 '@clerk/react':
561562 specifier: 6.14.7
562563 version: 6.14.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -6688,8 +6689,8 @@ packages: · 6125 unmodified lines
66886689 resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==}
66896690 engines: {node: '>=8.0.0'}
66906691
6691− electron@43.4.1:
6692− resolution: {integrity: sha512-5b+EuiwkgG5iRcsEL34rimgRpkYp15SsfZOa0pC5kXs0Tb82TH4n95rpQzTZa7yRCbA7tm0WoEbuBL6NaAhAcA==}
6692+ electron@44.1.0:
6693+ resolution: {integrity: sha512-kmLg8axOg22DC3fXx5NCwBYW8fx0rK2zzJ3Tf67GjNCkxfoxEcd+yo0QfDjlBtHJs3xeA8fTg64b6iVzFTVErg==}
66936694 engines: {node: '>= 22.12.0'}
66946695 hasBin: true
66956696
@@ -8573,8 +8574,8 @@ packages: · 1877 unmodified lines
85738574 nlcst-to-string@4.0.0:
85748575 resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==}
85758576
8576− node-abi@4.31.0:
8577− resolution: {integrity: sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==}
8577+ node-abi@4.33.0:
8578+ resolution: {integrity: sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==}
85788579 engines: {node: '>=22.12.0'}
85798580
85808581 node-addon-api@7.1.1:
@@ -11846,12 +11847,12 @@ snapshots: · 3265 unmodified lines
1184611847 '@clerk/electron-passkeys-win32-arm64-msvc': 0.0.3
1184711848 '@clerk/electron-passkeys-win32-x64-msvc': 0.0.3
1184811849
11849− '@clerk/electron@0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@43.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
11850+ '@clerk/electron@0.0.37(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
1185011851 dependencies:
1185111852 '@clerk/clerk-js': 6.30.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1185211853 '@clerk/react': 6.14.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
1185311854 '@clerk/shared': 4.30.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
11854− electron: 43.4.1
11855+ electron: 44.1.0
1185511856 react: 19.2.6
1185611857 tslib: 2.8.1
1185711858 optionalDependencies:
@@ -12228,7 +12229,7 @@ snapshots: · 370 unmodified lines
1222812229 dependencies:
1222912230 '@malept/cross-spawn-promise': 2.0.0
1223012231 debug: 4.4.3
12231− node-abi: 4.31.0
12232+ node-abi: 4.33.0
1223212233 node-api-version: 0.2.1
1223312234 node-gyp: 12.3.0
1223412235 read-binary-file-arch: 1.0.6
@@ -16741,7 +16742,7 @@ snapshots: · 4506 unmodified lines
1674116742 transitivePeerDependencies:
1674216743 - supports-color
1674316744
16744− electron@43.4.1:
16745+ electron@44.1.0:
1674516746 dependencies:
1674616747 '@electron-internal/extract-zip': 1.0.5
1674716748 '@electron/get': 5.1.0
@@ -19350,7 +19351,7 @@ snapshots: · 2602 unmodified lines
1935019351 dependencies:
1935119352 '@types/nlcst': 2.0.3
1935219353
19353− node-abi@4.31.0:
19354+ node-abi@4.33.0:
1935419355 dependencies:
1935519356 semver: 7.8.5
1935619357
pnpm-workspace.yaml+2−0
@@ -90,6 +90,7 @@ minimumReleaseAgeExclude: · 89 unmodified lines
9090 - expo-updates@57.0.19
9191 - expo@57.0.18
9292 - expo-widgets@57.0.15
93+ - electron@44.1.0
9394 - "@anthropic-ai/claude-agent-sdk@0.3.260"
9495
9596 overrides:
@@ -137,6 +138,7 @@ overrides: · 41 unmodified lines
137138 expo-router: 57.0.17
138139 "expo-sharing>@expo/config-plugins": 57.0.9
139140 "expo-sharing>@expo/config-types": 57.0.2
141+ node-abi: 4.33.0
140142 lightningcss: "catalog:"
141143 tailwindcss: "catalog:"
142144 vite: "catalog:"

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t