Skip to content

Commit 28d49f9

Browse files
authored
fix: Better electron handling (#695)
### TL;DR Refactored the Electron main process code to improve organization and error handling. ### What changed? - Split monolithic `index.ts` into smaller, focused modules: - `window.ts` - Window creation and management - `menu.ts` - Application menu building - `deep-links.ts` - Deep link handling - Improved error handling for uncaught exceptions and unhandled rejections - When the main window is closed, we now shut the application down entirely
1 parent fad1f91 commit 28d49f9

5 files changed

Lines changed: 394 additions & 365 deletions

File tree

apps/twig/src/main/bootstrap.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,29 @@
66
* instantiation (which calls app.getPath('userData') in their constructors).
77
*
88
*/
9-
import { existsSync, renameSync } from "node:fs";
9+
10+
import dns from "node:dns";
1011
import path from "node:path";
1112
import { app } from "electron";
13+
import { fixPath } from "./lib/fixPath.js";
1214

1315
const isDev = !app.isPackaged;
1416

15-
// Set different app names for separate single-instance locks
16-
const legacyAppName = isDev ? "array-dev" : "Array";
17+
// Set app name for single-instance lock, crashReporter, etc
1718
const appName = isDev ? "twig-dev" : "Twig";
1819
app.setName(isDev ? "Twig (Development)" : "Twig");
1920

20-
// Migrate userData from legacy location if needed
21+
// Set userData path for @posthog/twig
2122
const appDataPath = app.getPath("appData");
22-
const legacyUserDataPath = path.join(appDataPath, "@posthog", legacyAppName);
2323
const userDataPath = path.join(appDataPath, "@posthog", appName);
24+
app.setPath("userData", userDataPath);
2425

25-
if (existsSync(legacyUserDataPath) && !existsSync(userDataPath)) {
26-
try {
27-
renameSync(legacyUserDataPath, userDataPath);
28-
} catch {
29-
// If migration fails, continue with new path
30-
}
31-
}
26+
// Force IPv4 resolution when "localhost" is used so the agent hits 127.0.0.1
27+
// instead of ::1. This matches how the renderer already reaches the PostHog API.
28+
dns.setDefaultResultOrder("ipv4first");
3229

33-
app.setPath("userData", userDataPath);
30+
// Call fixPath early to ensure PATH is correct for any child processes
31+
fixPath();
3432

3533
// Now dynamically import the rest of the application
3634
// Dynamic import ensures the path is set BEFORE index.js is evaluated

apps/twig/src/main/deep-links.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { app } from "electron";
2+
import { container } from "./di/container.js";
3+
import { MAIN_TOKENS } from "./di/tokens.js";
4+
import type { DeepLinkService } from "./services/deep-link/service.js";
5+
import { focusMainWindow } from "./window.js";
6+
7+
let pendingDeepLinkUrl: string | null = null;
8+
9+
function getDeepLinkService(): DeepLinkService {
10+
return container.get<DeepLinkService>(MAIN_TOKENS.DeepLinkService);
11+
}
12+
13+
/**
14+
* Register app-level deep link event handlers.
15+
* Must be called before app.whenReady() so macOS open-url events are captured.
16+
*/
17+
export function registerDeepLinkHandlers(): void {
18+
// Handle deep link URLs on macOS
19+
app.on("open-url", (event, url) => {
20+
event.preventDefault();
21+
22+
if (!app.isReady()) {
23+
pendingDeepLinkUrl = url;
24+
return;
25+
}
26+
27+
getDeepLinkService().handleUrl(url);
28+
focusMainWindow();
29+
});
30+
31+
// Handle deep link URLs on Windows/Linux (second instance sends URL via command line)
32+
app.on("second-instance", (_event, commandLine) => {
33+
const url = commandLine.find(
34+
(arg) => arg.startsWith("twig://") || arg.startsWith("array://"),
35+
);
36+
if (url) {
37+
getDeepLinkService().handleUrl(url);
38+
}
39+
40+
focusMainWindow();
41+
});
42+
}
43+
44+
/**
45+
* Register the deep link protocol and process any URLs that arrived before
46+
* the app was ready.
47+
* Must be called after app.whenReady().
48+
*/
49+
export function initializeDeepLinks(): void {
50+
getDeepLinkService().registerProtocol();
51+
52+
if (process.platform === "darwin") {
53+
if (pendingDeepLinkUrl) {
54+
getDeepLinkService().handleUrl(pendingDeepLinkUrl);
55+
pendingDeepLinkUrl = null;
56+
}
57+
} else {
58+
const deepLinkUrl = process.argv.find(
59+
(arg) => arg.startsWith("twig://") || arg.startsWith("array://"),
60+
);
61+
if (deepLinkUrl) {
62+
getDeepLinkService().handleUrl(deepLinkUrl);
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)