react-native-electron-platform 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1157 -27
- package/electron-builder.json +3 -2
- package/index.mjs +83 -0
- package/package.json +2 -2
- package/src/modules/autoUpdater.js +72 -0
- package/src/modules/deepLinking.js +21 -0
- package/src/modules/ipcHandlers/clipboard.js +11 -0
- package/src/modules/ipcHandlers/dialog.js +11 -0
- package/src/modules/ipcHandlers/fileOps.js +33 -0
- package/src/modules/ipcHandlers/index.js +33 -0
- package/src/modules/ipcHandlers/network.js +18 -0
- package/src/modules/ipcHandlers/pdf.js +135 -0
- package/src/modules/ipcHandlers/updater.js +17 -0
- package/src/modules/ipcHandlers/utils.js +15 -0
- package/src/modules/networkService.js +36 -0
- package/src/modules/pdfHelper.js +46 -0
- package/src/modules/safeMode.js +32 -0
- package/src/modules/windowManager.js +142 -0
- package/src/webpackConfigHelper.mjs +1 -1
- package/test/package.json +43 -0
- package/src/main.mjs +0 -662
- package/test/electron/electron-builder.json +0 -94
package/src/main.mjs
DELETED
|
@@ -1,662 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
app,
|
|
3
|
-
BrowserWindow,
|
|
4
|
-
session,
|
|
5
|
-
screen,
|
|
6
|
-
ipcMain,
|
|
7
|
-
dialog,
|
|
8
|
-
clipboard,
|
|
9
|
-
shell
|
|
10
|
-
} from "electron";
|
|
11
|
-
import electronUpdater from "electron-updater";
|
|
12
|
-
const { autoUpdater } = electronUpdater;
|
|
13
|
-
import path from "path";
|
|
14
|
-
import fs from "fs";
|
|
15
|
-
import { Readable } from "stream";
|
|
16
|
-
import os from "os";
|
|
17
|
-
import { fileURLToPath } from 'url';
|
|
18
|
-
import { dirname } from 'path';
|
|
19
|
-
import packageJson from "../../../package.json" with { type: 'json' };
|
|
20
|
-
|
|
21
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
|
|
23
|
-
// ======================================================
|
|
24
|
-
// AUTO SAFE MODE DETECTION
|
|
25
|
-
// ======================================================
|
|
26
|
-
|
|
27
|
-
function isRunningFromNetwork() {
|
|
28
|
-
try {
|
|
29
|
-
const exePath = app.getPath("exe");
|
|
30
|
-
return exePath.startsWith("\\\\"); // UNC path
|
|
31
|
-
} catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function shouldUseSafeMode() {
|
|
37
|
-
const network = isRunningFromNetwork();
|
|
38
|
-
const hostname = os.hostname().toLowerCase();
|
|
39
|
-
const vmHint =
|
|
40
|
-
hostname.includes("vm") ||
|
|
41
|
-
hostname.includes("virtual") ||
|
|
42
|
-
hostname.includes("vbox") ||
|
|
43
|
-
hostname.includes("hyper");
|
|
44
|
-
|
|
45
|
-
return network || vmHint;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Apply BEFORE Electron ready
|
|
49
|
-
if (shouldUseSafeMode()) {
|
|
50
|
-
console.log("⚠ SAFE MODE ENABLED");
|
|
51
|
-
app.disableHardwareAcceleration();
|
|
52
|
-
app.commandLine.appendSwitch("disable-gpu");
|
|
53
|
-
app.commandLine.appendSwitch("disable-software-rasterizer");
|
|
54
|
-
app.commandLine.appendSwitch("no-sandbox");
|
|
55
|
-
app.commandLine.appendSwitch("disable-dev-shm-usage");
|
|
56
|
-
} else {
|
|
57
|
-
console.log("✅ NORMAL MODE");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let mainWindow;
|
|
61
|
-
|
|
62
|
-
// ======================================================
|
|
63
|
-
// DEEP LINKING / URL HANDLING
|
|
64
|
-
// ======================================================
|
|
65
|
-
|
|
66
|
-
const appOpenURLTargets = new WeakSet();
|
|
67
|
-
|
|
68
|
-
function sendOpenURL(url) {
|
|
69
|
-
for (const window of BrowserWindow.getAllWindows()) {
|
|
70
|
-
if (appOpenURLTargets.has(window.webContents)) {
|
|
71
|
-
window.webContents.send('react-native-app-open-url', url);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ======================================================
|
|
77
|
-
// REGISTER ALL IPC HANDLERS
|
|
78
|
-
// ======================================================
|
|
79
|
-
|
|
80
|
-
function registerIpcHandlers() {
|
|
81
|
-
console.log("📝 Registering IPC handlers...");
|
|
82
|
-
|
|
83
|
-
// Deep Linking Handlers
|
|
84
|
-
ipcMain.handle('react-native-add-app-open-url', (event) => {
|
|
85
|
-
appOpenURLTargets.add(event.sender);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
ipcMain.handle('react-native-get-initial-url', () => {
|
|
89
|
-
return Promise.resolve(process.argv[1]);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
ipcMain.handle('react-native-open-url', async (_event, url) => {
|
|
93
|
-
await shell.openExternal(url);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Clipboard Handlers
|
|
97
|
-
ipcMain.handle('react-native-get-clipboard-text', async () => {
|
|
98
|
-
return await clipboard.readText();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
ipcMain.handle('react-native-set-clipboard-text', async (_event, text) => {
|
|
102
|
-
await clipboard.writeText(text);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Dialog Handlers
|
|
106
|
-
ipcMain.handle('react-native-show-alert', async (event, options) => {
|
|
107
|
-
const window = BrowserWindow.fromWebContents(event.sender);
|
|
108
|
-
if (window != null) {
|
|
109
|
-
const { response } = await dialog.showMessageBox(window, options);
|
|
110
|
-
return response;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Internal support check
|
|
115
|
-
ipcMain.on('react-native-supported', (event) => {
|
|
116
|
-
event.returnValue = true;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Auto-updater Handlers
|
|
120
|
-
ipcMain.handle("check-for-updates", async () => {
|
|
121
|
-
if (app.isPackaged) {
|
|
122
|
-
autoUpdater.checkForUpdates();
|
|
123
|
-
return { status: "checking" };
|
|
124
|
-
}
|
|
125
|
-
return { status: "disabled", message: "Auto-update disabled in development" };
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
ipcMain.handle("get-app-version", () => {
|
|
129
|
-
return app.getVersion();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Network Service Handlers
|
|
133
|
-
ipcMain.handle("network-call", async (event, payload) => {
|
|
134
|
-
try {
|
|
135
|
-
const { method, url, params, headers } = payload;
|
|
136
|
-
console.log("IPC network-call:", { method, url });
|
|
137
|
-
return await NetworkServiceCall(method, url, params, headers);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
console.error("IPC network-call error:", err);
|
|
140
|
-
return {
|
|
141
|
-
httpstatus: 500,
|
|
142
|
-
data: { title: "ERROR", message: err.message },
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// PDF Operations Handlers
|
|
148
|
-
ipcMain.handle("save-pdf", async (event, html) => {
|
|
149
|
-
try {
|
|
150
|
-
console.log("IPC save-pdf called");
|
|
151
|
-
const tempWin = new BrowserWindow({
|
|
152
|
-
show: false,
|
|
153
|
-
webPreferences: { offscreen: true },
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
await tempWin.loadURL(
|
|
157
|
-
`data:text/html;charset=utf-8,${encodeURIComponent(html)}`
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const pdfBuffer = await tempWin.webContents.printToPDF({});
|
|
161
|
-
tempWin.destroy();
|
|
162
|
-
|
|
163
|
-
const { filePath } = await dialog.showSaveDialog({
|
|
164
|
-
title: "Save PDF",
|
|
165
|
-
defaultPath: "document.pdf",
|
|
166
|
-
filters: [{ name: "PDF", extensions: ["pdf"] }],
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
if (filePath) {
|
|
170
|
-
fs.writeFileSync(filePath, pdfBuffer);
|
|
171
|
-
console.log("PDF saved:", filePath);
|
|
172
|
-
return { status: "saved", path: filePath };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return { status: "cancelled" };
|
|
176
|
-
} catch (err) {
|
|
177
|
-
console.error("IPC save-pdf error:", err);
|
|
178
|
-
return { status: "error", message: err.message };
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
ipcMain.handle("post-pdf-preview", async (event, payload) => {
|
|
183
|
-
try {
|
|
184
|
-
const { url, data, headers = {} } = payload;
|
|
185
|
-
console.log("IPC post-pdf-preview:", { url });
|
|
186
|
-
|
|
187
|
-
const res = await fetch(url, {
|
|
188
|
-
method: "POST",
|
|
189
|
-
headers: {
|
|
190
|
-
"Content-Type": "application/json",
|
|
191
|
-
Accept: "application/pdf",
|
|
192
|
-
...headers,
|
|
193
|
-
},
|
|
194
|
-
body: data,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
if (!res.ok) {
|
|
198
|
-
throw new Error(`HTTP ${res.status}`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const fileName = `Report_${Date.now()}.pdf`;
|
|
202
|
-
const tempPath = path.join(app.getPath("temp"), fileName);
|
|
203
|
-
|
|
204
|
-
const nodeStream = Readable.fromWeb(res.body);
|
|
205
|
-
await new Promise((resolve, reject) => {
|
|
206
|
-
const fileStream = fs.createWriteStream(tempPath);
|
|
207
|
-
nodeStream.pipe(fileStream);
|
|
208
|
-
nodeStream.on("error", reject);
|
|
209
|
-
fileStream.on("finish", resolve);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
status: "ok",
|
|
214
|
-
path: `file://${tempPath}`,
|
|
215
|
-
};
|
|
216
|
-
} catch (err) {
|
|
217
|
-
console.error("post-pdf-preview error:", err);
|
|
218
|
-
return {
|
|
219
|
-
status: "error",
|
|
220
|
-
message: err.message,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
ipcMain.handle("open-pdf-preview", async (_, pdfUrl) => {
|
|
226
|
-
try {
|
|
227
|
-
const res = await fetch(pdfUrl);
|
|
228
|
-
const buffer = Buffer.from(await res.arrayBuffer());
|
|
229
|
-
|
|
230
|
-
const tempPath = path.join(app.getPath("temp"), `preview_${Date.now()}.pdf`);
|
|
231
|
-
fs.writeFileSync(tempPath, buffer);
|
|
232
|
-
|
|
233
|
-
return `file://${tempPath}`;
|
|
234
|
-
} catch (err) {
|
|
235
|
-
console.error("open-pdf-preview error:", err);
|
|
236
|
-
throw err;
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// File Operations Handlers
|
|
241
|
-
ipcMain.handle("select-file", async () => {
|
|
242
|
-
try {
|
|
243
|
-
const result = await dialog.showOpenDialog({
|
|
244
|
-
title: "Select a file",
|
|
245
|
-
properties: ["openFile"],
|
|
246
|
-
filters: [
|
|
247
|
-
{ name: "All Files", extensions: ["*"] },
|
|
248
|
-
],
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
if (result.canceled) {
|
|
252
|
-
return {
|
|
253
|
-
status: "cancelled",
|
|
254
|
-
filePath: null,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
status: "selected",
|
|
260
|
-
filePath: result.filePaths[0],
|
|
261
|
-
};
|
|
262
|
-
} catch (err) {
|
|
263
|
-
console.error("select-file error:", err);
|
|
264
|
-
return {
|
|
265
|
-
status: "error",
|
|
266
|
-
message: err.message,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// HTML Preview Handlers
|
|
272
|
-
ipcMain.handle("preview-html", async (event, htmlContent) => {
|
|
273
|
-
try {
|
|
274
|
-
const previewWin = new BrowserWindow({
|
|
275
|
-
width: 800,
|
|
276
|
-
height: 600,
|
|
277
|
-
show: false,
|
|
278
|
-
webPreferences: {
|
|
279
|
-
contextIsolation: true,
|
|
280
|
-
sandbox: false,
|
|
281
|
-
nodeIntegration: false,
|
|
282
|
-
},
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
await previewWin.loadURL(
|
|
286
|
-
`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
previewWin.show();
|
|
290
|
-
return { status: "ok" };
|
|
291
|
-
} catch (err) {
|
|
292
|
-
console.error("preview-html error:", err);
|
|
293
|
-
return { status: "error", message: err.message };
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
ipcMain.handle("html-to-pdf-preview", async (event, htmlContent) => {
|
|
298
|
-
try {
|
|
299
|
-
const pdfPath = await convertHtmlToPdfPreview(htmlContent);
|
|
300
|
-
return { status: "ok", path: pdfPath };
|
|
301
|
-
} catch (err) {
|
|
302
|
-
console.error("html-to-pdf-preview error:", err);
|
|
303
|
-
return { status: "error", message: err.message };
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
// Utility Handlers
|
|
308
|
-
ipcMain.handle("get-platform", () => {
|
|
309
|
-
return process.platform;
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
ipcMain.handle("get-app-path", () => {
|
|
313
|
-
return app.getAppPath();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
ipcMain.handle("get-user-data-path", () => {
|
|
317
|
-
return app.getPath("userData");
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
console.log("✅ All IPC handlers registered");
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// ======================================================
|
|
324
|
-
// NETWORK SERVICE
|
|
325
|
-
// ======================================================
|
|
326
|
-
|
|
327
|
-
async function NetworkServiceCall(method, url, params = {}, headers = {}) {
|
|
328
|
-
try {
|
|
329
|
-
const upperMethod = method.toUpperCase();
|
|
330
|
-
const options = {
|
|
331
|
-
method: upperMethod,
|
|
332
|
-
headers: {
|
|
333
|
-
"Content-Type": "application/json",
|
|
334
|
-
...headers,
|
|
335
|
-
},
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
if (upperMethod !== "GET") {
|
|
339
|
-
options.body = JSON.stringify(params);
|
|
340
|
-
} else if (params && Object.keys(params).length > 0) {
|
|
341
|
-
const query = new URLSearchParams(params).toString();
|
|
342
|
-
url += `?${query}`;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const response = await fetch(url, options);
|
|
346
|
-
const data = await response.json().catch(() => ({}));
|
|
347
|
-
|
|
348
|
-
if (response.ok || data?.httpstatus === 200) {
|
|
349
|
-
return { httpstatus: 200, data: data?.data || data };
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return {
|
|
353
|
-
httpstatus: response.status,
|
|
354
|
-
data: { title: "ERROR", message: data?.message || "Network Error" },
|
|
355
|
-
};
|
|
356
|
-
} catch (err) {
|
|
357
|
-
return {
|
|
358
|
-
httpstatus: 404,
|
|
359
|
-
data: { title: "ERROR", message: err.message },
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ======================================================
|
|
365
|
-
// PDF CONVERSION HELPER
|
|
366
|
-
// ======================================================
|
|
367
|
-
|
|
368
|
-
async function convertHtmlToPdfPreview(htmlContent, options = {}) {
|
|
369
|
-
try {
|
|
370
|
-
const tempWin = new BrowserWindow({
|
|
371
|
-
show: false,
|
|
372
|
-
webPreferences: {
|
|
373
|
-
offscreen: true,
|
|
374
|
-
contextIsolation: true,
|
|
375
|
-
nodeIntegration: false,
|
|
376
|
-
},
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
await tempWin.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`);
|
|
380
|
-
|
|
381
|
-
const pdfBuffer = await tempWin.webContents.printToPDF({
|
|
382
|
-
printBackground: true,
|
|
383
|
-
...options,
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
tempWin.destroy();
|
|
387
|
-
|
|
388
|
-
const pdfFileName = `Preview_${Date.now()}.pdf`;
|
|
389
|
-
const pdfPath = path.join(app.getPath("temp"), pdfFileName);
|
|
390
|
-
fs.writeFileSync(pdfPath, pdfBuffer);
|
|
391
|
-
|
|
392
|
-
const previewWin = new BrowserWindow({
|
|
393
|
-
width: 900,
|
|
394
|
-
height: 700,
|
|
395
|
-
show: true,
|
|
396
|
-
webPreferences: {
|
|
397
|
-
contextIsolation: true,
|
|
398
|
-
nodeIntegration: false,
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
await previewWin.loadURL(`file://${pdfPath}`);
|
|
403
|
-
return pdfPath;
|
|
404
|
-
} catch (err) {
|
|
405
|
-
console.error("convertHtmlToPdfPreview error:", err);
|
|
406
|
-
throw err;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// ======================================================
|
|
411
|
-
// AUTO UPDATER SETUP
|
|
412
|
-
// ======================================================
|
|
413
|
-
|
|
414
|
-
function setupAutoUpdater() {
|
|
415
|
-
console.log("Current app version:", app.getVersion());
|
|
416
|
-
|
|
417
|
-
if (!app.isPackaged) {
|
|
418
|
-
console.log("Auto-update disabled in development.");
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
autoUpdater.autoDownload = true;
|
|
423
|
-
autoUpdater.autoInstallOnAppQuit = true;
|
|
424
|
-
|
|
425
|
-
const sendStatus = (data) => {
|
|
426
|
-
if (mainWindow) {
|
|
427
|
-
mainWindow.webContents.send("update-status", data);
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
autoUpdater.on("checking-for-update", () => {
|
|
432
|
-
console.log("Checking for updates...");
|
|
433
|
-
sendStatus({ status: "checking" });
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
autoUpdater.on("update-available", (info) => {
|
|
437
|
-
console.log("Update available:", info.version);
|
|
438
|
-
sendStatus({ status: "available", version: info.version });
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
autoUpdater.on("update-not-available", () => {
|
|
442
|
-
console.log("No updates available");
|
|
443
|
-
sendStatus({ status: "not-available" });
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
autoUpdater.on("download-progress", (progress) => {
|
|
447
|
-
console.log(`Download progress: ${Math.round(progress.percent)}%`);
|
|
448
|
-
sendStatus({
|
|
449
|
-
status: "downloading",
|
|
450
|
-
percent: Math.round(progress.percent),
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
autoUpdater.on("update-downloaded", () => {
|
|
455
|
-
console.log("Update downloaded");
|
|
456
|
-
sendStatus({ status: "downloaded" });
|
|
457
|
-
|
|
458
|
-
dialog
|
|
459
|
-
.showMessageBox(mainWindow, {
|
|
460
|
-
type: "info",
|
|
461
|
-
title: "Update Ready",
|
|
462
|
-
message: "A new version has been downloaded. Restart now?",
|
|
463
|
-
buttons: ["Restart", "Later"],
|
|
464
|
-
})
|
|
465
|
-
.then((result) => {
|
|
466
|
-
if (result.response === 0) {
|
|
467
|
-
autoUpdater.quitAndInstall();
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
autoUpdater.on("error", (err) => {
|
|
473
|
-
console.error("Auto-updater error:", err);
|
|
474
|
-
sendStatus({ status: "error", message: err.message });
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
// Check for updates after a short delay to ensure window is ready
|
|
478
|
-
setTimeout(() => {
|
|
479
|
-
autoUpdater.checkForUpdates();
|
|
480
|
-
}, 3000);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// ======================================================
|
|
484
|
-
// WINDOW CREATION
|
|
485
|
-
// ======================================================
|
|
486
|
-
|
|
487
|
-
function createWindow() {
|
|
488
|
-
const primaryDisplay = screen.getPrimaryDisplay();
|
|
489
|
-
const { x, y, width, height } = primaryDisplay.bounds;
|
|
490
|
-
|
|
491
|
-
const iconPath = path.join(app.getAppPath(), "electron/icon.ico");
|
|
492
|
-
const preloadPath = path.join(__dirname, "preload.mjs");
|
|
493
|
-
|
|
494
|
-
mainWindow = new BrowserWindow({
|
|
495
|
-
x,
|
|
496
|
-
y,
|
|
497
|
-
width,
|
|
498
|
-
height,
|
|
499
|
-
show: false,
|
|
500
|
-
icon: iconPath,
|
|
501
|
-
frame: true,
|
|
502
|
-
webPreferences: {
|
|
503
|
-
preload: preloadPath,
|
|
504
|
-
nodeIntegration: false,
|
|
505
|
-
contextIsolation: true,
|
|
506
|
-
sandbox: false,
|
|
507
|
-
webSecurity: false,
|
|
508
|
-
disableBlinkFeatures: "AutoLoadIconsForPage",
|
|
509
|
-
nativeWindowOpen: true,
|
|
510
|
-
spellcheck: true,
|
|
511
|
-
},
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
mainWindow.removeMenu();
|
|
515
|
-
mainWindow.maximize();
|
|
516
|
-
mainWindow.show();
|
|
517
|
-
|
|
518
|
-
mainWindow.on("resize", () => {
|
|
519
|
-
const bounds = mainWindow.getBounds();
|
|
520
|
-
if (bounds?.width && bounds?.height) {
|
|
521
|
-
console.log(`Window resized: ${bounds.width}x${bounds.height}`);
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
// CORS handling
|
|
526
|
-
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
|
527
|
-
const responseHeaders = { ...details.responseHeaders };
|
|
528
|
-
|
|
529
|
-
delete responseHeaders["access-control-allow-origin"];
|
|
530
|
-
delete responseHeaders["access-control-allow-headers"];
|
|
531
|
-
delete responseHeaders["access-control-allow-methods"];
|
|
532
|
-
delete responseHeaders["Access-Control-Allow-Origin"];
|
|
533
|
-
delete responseHeaders["Access-Control-Allow-Headers"];
|
|
534
|
-
delete responseHeaders["Access-Control-Allow-Methods"];
|
|
535
|
-
|
|
536
|
-
callback({
|
|
537
|
-
responseHeaders: {
|
|
538
|
-
...responseHeaders,
|
|
539
|
-
"Access-Control-Allow-Origin": ["*"],
|
|
540
|
-
"Access-Control-Allow-Headers": ["*"],
|
|
541
|
-
"Access-Control-Allow-Methods": ["GET, POST, PUT, DELETE, OPTIONS"],
|
|
542
|
-
},
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
const isDev =
|
|
547
|
-
process.argv.includes("--enable-remote-module") ||
|
|
548
|
-
process.env.NODE_ENV === "development";
|
|
549
|
-
|
|
550
|
-
if (isDev) {
|
|
551
|
-
mainWindow.loadURL("http://localhost:5001");
|
|
552
|
-
console.log("DEV MODE: http://localhost:5001");
|
|
553
|
-
|
|
554
|
-
// Dev tools shortcuts
|
|
555
|
-
mainWindow.webContents.on("before-input-event", (event, input) => {
|
|
556
|
-
if (input.control && input.shift && input.key.toLowerCase() === "i") {
|
|
557
|
-
mainWindow.webContents.toggleDevTools();
|
|
558
|
-
event.preventDefault();
|
|
559
|
-
} else if (input.key === "F12") {
|
|
560
|
-
mainWindow.webContents.toggleDevTools();
|
|
561
|
-
event.preventDefault();
|
|
562
|
-
} else if (
|
|
563
|
-
input.key === "F5" ||
|
|
564
|
-
(input.control && input.key.toLowerCase() === "r")
|
|
565
|
-
) {
|
|
566
|
-
mainWindow.webContents.reload();
|
|
567
|
-
event.preventDefault();
|
|
568
|
-
}
|
|
569
|
-
});
|
|
570
|
-
} else {
|
|
571
|
-
const possiblePaths = [
|
|
572
|
-
path.join(__dirname, "web-build/index.html"),
|
|
573
|
-
path.join(__dirname, "../web-build/index.html"),
|
|
574
|
-
path.join(__dirname, "../../web-build/index.html"),
|
|
575
|
-
path.join(app.getAppPath(), "web-build/index.html"),
|
|
576
|
-
];
|
|
577
|
-
|
|
578
|
-
let indexPath = null;
|
|
579
|
-
for (const p of possiblePaths) {
|
|
580
|
-
if (fs.existsSync(p)) {
|
|
581
|
-
indexPath = p;
|
|
582
|
-
break;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
console.log("Loading:", indexPath);
|
|
587
|
-
|
|
588
|
-
if (!indexPath) {
|
|
589
|
-
dialog.showErrorBox(
|
|
590
|
-
"Application Error",
|
|
591
|
-
`web-build/index.html not found. Tried: ${possiblePaths.join(", ")}`
|
|
592
|
-
);
|
|
593
|
-
app.quit();
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
mainWindow.loadFile(indexPath);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
mainWindow.webContents.on("did-finish-load", () => {
|
|
601
|
-
console.log("Page loaded successfully");
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// ======================================================
|
|
606
|
-
// REGISTER IPC HANDLERS BEFORE APP READY
|
|
607
|
-
// ======================================================
|
|
608
|
-
|
|
609
|
-
registerIpcHandlers();
|
|
610
|
-
|
|
611
|
-
// ======================================================
|
|
612
|
-
// APP LIFECYCLE
|
|
613
|
-
// ======================================================
|
|
614
|
-
|
|
615
|
-
app.whenReady().then(() => {
|
|
616
|
-
if (process.platform === "win32") {
|
|
617
|
-
app.setAppUserModelId(`com.${packageJson.name}.desktop`);
|
|
618
|
-
}
|
|
619
|
-
createWindow();
|
|
620
|
-
setupAutoUpdater();
|
|
621
|
-
|
|
622
|
-
// Log all registered IPC handlers for debugging
|
|
623
|
-
console.log("📊 IPC Handlers registered:",
|
|
624
|
-
ipcMain.eventNames().filter(name =>
|
|
625
|
-
typeof name === 'string' &&
|
|
626
|
-
!name.startsWith('ELECTRON_')
|
|
627
|
-
)
|
|
628
|
-
);
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
app.on("window-all-closed", () => {
|
|
632
|
-
if (process.platform !== "darwin") {
|
|
633
|
-
app.quit();
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
app.on("activate", () => {
|
|
638
|
-
if (BrowserWindow.getAllWindows().length === 0) {
|
|
639
|
-
createWindow();
|
|
640
|
-
}
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
// Handle second instance (for single instance apps)
|
|
644
|
-
const gotTheLock = app.requestSingleInstanceLock();
|
|
645
|
-
|
|
646
|
-
if (!gotTheLock) {
|
|
647
|
-
app.quit();
|
|
648
|
-
} else {
|
|
649
|
-
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
|
650
|
-
// Someone tried to run a second instance, focus our window instead
|
|
651
|
-
if (mainWindow) {
|
|
652
|
-
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
653
|
-
mainWindow.focus();
|
|
654
|
-
|
|
655
|
-
// Handle deep link from second instance
|
|
656
|
-
const url = commandLine.pop();
|
|
657
|
-
if (url && (url.startsWith('myapp://') || url.startsWith('http'))) {
|
|
658
|
-
sendOpenURL(url);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
}
|