esp32tool 1.6.1 → 1.6.2
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/apple-touch-icon.png +0 -0
- package/dist/esp_loader.js +20 -32
- package/dist/web/index.js +1 -1
- package/electron/main.cjs +78 -83
- package/icons/icon-128.png +0 -0
- package/icons/icon-144.png +0 -0
- package/icons/icon-152.png +0 -0
- package/icons/icon-192.png +0 -0
- package/icons/icon-384.png +0 -0
- package/icons/icon-512.png +0 -0
- package/icons/icon-72.png +0 -0
- package/icons/icon-96.png +0 -0
- package/js/modules/esptool.js +1 -1
- package/package.json +10 -8
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/esp_loader.ts +20 -32
- package/sw.js +1 -1
- package/electron/main.js +0 -338
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esp32tool",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Flash & Read ESP devices using WebSerial, Electron, and also Android mobile via WebUSB",
|
|
6
6
|
"main": "electron/main.cjs",
|
|
@@ -49,27 +49,27 @@
|
|
|
49
49
|
"@electron-forge/maker-zip": "^7.11.1",
|
|
50
50
|
"@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
|
|
51
51
|
"@electron/fuses": "^2.0.0",
|
|
52
|
-
"@eslint/js": "^9.39.
|
|
52
|
+
"@eslint/js": "^9.39.3",
|
|
53
53
|
"@rollup/plugin-json": "^6.1.0",
|
|
54
54
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
55
55
|
"@rollup/plugin-terser": "^0.4.4",
|
|
56
56
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
57
|
-
"@types/node": "^25.
|
|
57
|
+
"@types/node": "^25.3.0",
|
|
58
58
|
"@types/pako": "^2.0.4",
|
|
59
59
|
"@types/serialport": "^10.2.0",
|
|
60
60
|
"@types/w3c-web-serial": "^1.0.7",
|
|
61
61
|
"archiver": "^7.0.1",
|
|
62
|
-
"electron": "^40.
|
|
62
|
+
"electron": "^40.6.0",
|
|
63
63
|
"electron-squirrel-startup": "^1.0.1",
|
|
64
|
-
"eslint": "^
|
|
64
|
+
"eslint": "^10.0.2",
|
|
65
65
|
"eslint-config-prettier": "^10.1.8",
|
|
66
66
|
"eslint-plugin-prettier": "^5.5.5",
|
|
67
67
|
"npm-run-all": "^4.1.5",
|
|
68
68
|
"prettier": "^3.8.1",
|
|
69
|
-
"rollup": "^4.
|
|
69
|
+
"rollup": "^4.59.0",
|
|
70
70
|
"serve": "^14.2.5",
|
|
71
71
|
"typescript": "^5.7.3",
|
|
72
|
-
"typescript-eslint": "^8.56.
|
|
72
|
+
"typescript-eslint": "^8.56.1"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"pako": "^2.1.0",
|
|
@@ -79,8 +79,10 @@
|
|
|
79
79
|
"overrides": {
|
|
80
80
|
"tmp": "^0.2.4",
|
|
81
81
|
"tar": "^7.5.6",
|
|
82
|
-
"ajv": "^8.18.0",
|
|
83
82
|
"minimatch": "^10.2.1",
|
|
83
|
+
"serve": {
|
|
84
|
+
"ajv": "^8.18.0"
|
|
85
|
+
},
|
|
84
86
|
"@electron/asar": "^4.0.1"
|
|
85
87
|
}
|
|
86
88
|
}
|
package/screenshots/desktop.png
CHANGED
|
Binary file
|
package/screenshots/mobile.png
CHANGED
|
Binary file
|
package/src/esp_loader.ts
CHANGED
|
@@ -1161,14 +1161,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1161
1161
|
* Classic reset with shorter delays for WebUSB (Android)
|
|
1162
1162
|
*/
|
|
1163
1163
|
async hardResetClassicShortDelayWebUSB() {
|
|
1164
|
-
await this.
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
await sleep(25);
|
|
1170
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
1171
|
-
await sleep(100);
|
|
1164
|
+
await this.runSignalSequence([
|
|
1165
|
+
{ dtr: false, rts: true, delayMs: 50 },
|
|
1166
|
+
{ dtr: true, rts: false, delayMs: 25 },
|
|
1167
|
+
{ dtr: false, delayMs: 100 },
|
|
1168
|
+
]);
|
|
1172
1169
|
}
|
|
1173
1170
|
|
|
1174
1171
|
/**
|
|
@@ -1176,14 +1173,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1176
1173
|
* Inverted reset sequence for WebUSB (Android) - both signals inverted
|
|
1177
1174
|
*/
|
|
1178
1175
|
async hardResetInvertedWebUSB() {
|
|
1179
|
-
await this.
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
await sleep(50);
|
|
1185
|
-
await this.setDTRWebUSB(true); // IO0=HIGH, done (inverted)
|
|
1186
|
-
await sleep(200);
|
|
1176
|
+
await this.runSignalSequence([
|
|
1177
|
+
{ dtr: true, rts: false, delayMs: 100 },
|
|
1178
|
+
{ dtr: false, rts: true, delayMs: 50 },
|
|
1179
|
+
{ dtr: true, delayMs: 200 },
|
|
1180
|
+
]);
|
|
1187
1181
|
}
|
|
1188
1182
|
|
|
1189
1183
|
/**
|
|
@@ -1191,14 +1185,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1191
1185
|
* Only DTR inverted for WebUSB (Android)
|
|
1192
1186
|
*/
|
|
1193
1187
|
async hardResetInvertedDTRWebUSB() {
|
|
1194
|
-
await this.
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
await sleep(50);
|
|
1200
|
-
await this.setDTRWebUSB(true); // IO0=HIGH, done (DTR inverted)
|
|
1201
|
-
await sleep(200);
|
|
1188
|
+
await this.runSignalSequence([
|
|
1189
|
+
{ dtr: true, rts: true, delayMs: 100 },
|
|
1190
|
+
{ dtr: false, rts: false, delayMs: 50 },
|
|
1191
|
+
{ dtr: true, delayMs: 200 },
|
|
1192
|
+
]);
|
|
1202
1193
|
}
|
|
1203
1194
|
|
|
1204
1195
|
/**
|
|
@@ -1206,14 +1197,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1206
1197
|
* Only RTS inverted for WebUSB (Android)
|
|
1207
1198
|
*/
|
|
1208
1199
|
async hardResetInvertedRTSWebUSB() {
|
|
1209
|
-
await this.
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
await sleep(50);
|
|
1215
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done (DTR normal)
|
|
1216
|
-
await sleep(200);
|
|
1200
|
+
await this.runSignalSequence([
|
|
1201
|
+
{ dtr: false, rts: false, delayMs: 100 },
|
|
1202
|
+
{ dtr: true, rts: true, delayMs: 50 },
|
|
1203
|
+
{ dtr: false, delayMs: 200 },
|
|
1204
|
+
]);
|
|
1217
1205
|
}
|
|
1218
1206
|
|
|
1219
1207
|
/**
|
package/sw.js
CHANGED
package/electron/main.js
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
const { app, BrowserWindow, Menu, session, ipcMain, dialog } = require('electron');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
|
6
|
-
// Only required for Windows Squirrel installer
|
|
7
|
-
if (process.platform === 'win32') {
|
|
8
|
-
try {
|
|
9
|
-
if (require('electron-squirrel-startup')) {
|
|
10
|
-
app.quit();
|
|
11
|
-
}
|
|
12
|
-
} catch (e) {
|
|
13
|
-
// Module not available, ignore
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let mainWindow;
|
|
18
|
-
|
|
19
|
-
// Store granted serial port devices
|
|
20
|
-
const grantedDevices = new Map();
|
|
21
|
-
|
|
22
|
-
function createWindow() {
|
|
23
|
-
mainWindow = new BrowserWindow({
|
|
24
|
-
width: 1200,
|
|
25
|
-
height: 800,
|
|
26
|
-
minWidth: 800,
|
|
27
|
-
minHeight: 600,
|
|
28
|
-
webPreferences: {
|
|
29
|
-
nodeIntegration: false,
|
|
30
|
-
contextIsolation: true,
|
|
31
|
-
preload: path.join(__dirname, 'preload.js'),
|
|
32
|
-
},
|
|
33
|
-
icon: path.join(__dirname, 'icons', 'icon.png'),
|
|
34
|
-
title: 'ESP32Tool',
|
|
35
|
-
autoHideMenuBar: false,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Load the index.html of the app
|
|
39
|
-
mainWindow.loadFile(path.join(__dirname, '..', 'index.html'));
|
|
40
|
-
|
|
41
|
-
// Open DevTools in development
|
|
42
|
-
if (process.env.NODE_ENV === 'development') {
|
|
43
|
-
mainWindow.webContents.openDevTools();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
mainWindow.on('closed', () => {
|
|
47
|
-
mainWindow = null;
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Setup serial port handlers for this window's session
|
|
51
|
-
setupSerialPortHandlers(mainWindow.webContents.session);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function setupSerialPortHandlers(ses) {
|
|
55
|
-
let lastSelectedPort = null;
|
|
56
|
-
let esp32s2ReconnectPending = false;
|
|
57
|
-
let portSelectionQueue = [];
|
|
58
|
-
|
|
59
|
-
// Handle serial port selection - shows when navigator.serial.requestPort() is called
|
|
60
|
-
ses.on('select-serial-port', (event, portList, webContents, callback) => {
|
|
61
|
-
event.preventDefault();
|
|
62
|
-
|
|
63
|
-
console.log('Available serial ports:', portList.map(p => ({
|
|
64
|
-
portId: p.portId,
|
|
65
|
-
portName: p.portName,
|
|
66
|
-
displayName: p.displayName
|
|
67
|
-
})));
|
|
68
|
-
|
|
69
|
-
if (portList && portList.length > 0) {
|
|
70
|
-
// Try to find ESP-compatible port
|
|
71
|
-
const espPort = portList.find(port => {
|
|
72
|
-
const name = (port.displayName || port.portName || '').toLowerCase();
|
|
73
|
-
return name.includes('cp2102') ||
|
|
74
|
-
name.includes('cp2103') ||
|
|
75
|
-
name.includes('cp2104') ||
|
|
76
|
-
name.includes('cp2105') ||
|
|
77
|
-
name.includes('cp2108') ||
|
|
78
|
-
name.includes('ch9102') ||
|
|
79
|
-
name.includes('ch9104') ||
|
|
80
|
-
name.includes('ch340') ||
|
|
81
|
-
name.includes('ch341') ||
|
|
82
|
-
name.includes('ch343') ||
|
|
83
|
-
name.includes('ftdi') ||
|
|
84
|
-
name.includes('usb') ||
|
|
85
|
-
name.includes('uart') ||
|
|
86
|
-
name.includes('silicon labs') ||
|
|
87
|
-
name.includes('esp');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Select ESP-compatible port or first available
|
|
91
|
-
const selectedPort = espPort || portList[0];
|
|
92
|
-
console.log('Selected port:', selectedPort.portId, selectedPort.displayName || selectedPort.portName);
|
|
93
|
-
lastSelectedPort = selectedPort;
|
|
94
|
-
|
|
95
|
-
callback(selectedPort.portId);
|
|
96
|
-
} else {
|
|
97
|
-
console.log('No serial ports available - queuing selection');
|
|
98
|
-
// No ports available yet - queue this callback for when a port appears
|
|
99
|
-
portSelectionQueue.push(callback);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Track port additions - handle ESP32-S2 reconnect
|
|
104
|
-
ses.on('serial-port-added', (event, port) => {
|
|
105
|
-
console.log('Serial port added:', port);
|
|
106
|
-
|
|
107
|
-
// If we have queued port selections, handle them now
|
|
108
|
-
if (portSelectionQueue.length > 0) {
|
|
109
|
-
console.log('Processing queued port selection');
|
|
110
|
-
const callback = portSelectionQueue.shift();
|
|
111
|
-
callback(port.portId);
|
|
112
|
-
lastSelectedPort = port;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Check if this looks like an ESP32-S2 CDC port appearing after ROM port disappeared
|
|
116
|
-
if (lastSelectedPort && port.portName !== lastSelectedPort.portName) {
|
|
117
|
-
const name = (port.displayName || port.portName || '').toLowerCase();
|
|
118
|
-
if (name.includes('esp') || name.includes('usb') || name.includes('uart')) {
|
|
119
|
-
console.log('ESP32-S2 reconnect detected - new CDC port available');
|
|
120
|
-
esp32s2ReconnectPending = true;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Track port removals - detect ESP32-S2 disconnect
|
|
126
|
-
ses.on('serial-port-removed', (event, port) => {
|
|
127
|
-
console.log('Serial port removed:', port);
|
|
128
|
-
|
|
129
|
-
// If the last selected port was removed, prepare for reconnect
|
|
130
|
-
if (lastSelectedPort && port.portId === lastSelectedPort.portId) {
|
|
131
|
-
console.log('Last selected port removed - may be ESP32-S2 mode switch');
|
|
132
|
-
// Don't clear lastSelectedPort yet, we might need it for comparison
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Grant permission for serial port access checks
|
|
137
|
-
ses.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
|
|
138
|
-
if (permission === 'serial') {
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
return true;
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Handle device permission requests
|
|
145
|
-
ses.setDevicePermissionHandler((details) => {
|
|
146
|
-
if (details.deviceType === 'serial') {
|
|
147
|
-
if (details.device) {
|
|
148
|
-
grantedDevices.set(details.device.deviceId, details.device);
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
return true;
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Create application menu
|
|
157
|
-
function createMenu() {
|
|
158
|
-
const template = [
|
|
159
|
-
{
|
|
160
|
-
label: 'File',
|
|
161
|
-
submenu: [
|
|
162
|
-
{ role: 'quit' }
|
|
163
|
-
]
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
label: 'Edit',
|
|
167
|
-
submenu: [
|
|
168
|
-
{ role: 'undo' },
|
|
169
|
-
{ role: 'redo' },
|
|
170
|
-
{ type: 'separator' },
|
|
171
|
-
{ role: 'cut' },
|
|
172
|
-
{ role: 'copy' },
|
|
173
|
-
{ role: 'paste' },
|
|
174
|
-
{ role: 'selectAll' }
|
|
175
|
-
]
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
label: 'View',
|
|
179
|
-
submenu: [
|
|
180
|
-
{ role: 'reload' },
|
|
181
|
-
{ role: 'forceReload' },
|
|
182
|
-
{ role: 'toggleDevTools' },
|
|
183
|
-
{ type: 'separator' },
|
|
184
|
-
{ role: 'resetZoom' },
|
|
185
|
-
{ role: 'zoomIn' },
|
|
186
|
-
{ role: 'zoomOut' },
|
|
187
|
-
{ type: 'separator' },
|
|
188
|
-
{ role: 'togglefullscreen' }
|
|
189
|
-
]
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
label: 'Help',
|
|
193
|
-
submenu: [
|
|
194
|
-
{
|
|
195
|
-
label: 'About ESP32Tool',
|
|
196
|
-
click: async () => {
|
|
197
|
-
const { shell } = require('electron');
|
|
198
|
-
await shell.openExternal('https://github.com/Jason2866/esp32tool');
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
label: 'ESP32 Documentation',
|
|
203
|
-
click: async () => {
|
|
204
|
-
const { shell } = require('electron');
|
|
205
|
-
await shell.openExternal('https://docs.espressif.com/');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
]
|
|
209
|
-
}
|
|
210
|
-
];
|
|
211
|
-
|
|
212
|
-
// macOS specific menu adjustments
|
|
213
|
-
if (process.platform === 'darwin') {
|
|
214
|
-
template.unshift({
|
|
215
|
-
label: app.getName(),
|
|
216
|
-
submenu: [
|
|
217
|
-
{ role: 'about' },
|
|
218
|
-
{ type: 'separator' },
|
|
219
|
-
{ role: 'services' },
|
|
220
|
-
{ type: 'separator' },
|
|
221
|
-
{ role: 'hide' },
|
|
222
|
-
{ role: 'hideOthers' },
|
|
223
|
-
{ role: 'unhide' },
|
|
224
|
-
{ type: 'separator' },
|
|
225
|
-
{ role: 'quit' }
|
|
226
|
-
]
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const menu = Menu.buildFromTemplate(template);
|
|
231
|
-
Menu.setApplicationMenu(menu);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// This method will be called when Electron has finished initialization
|
|
235
|
-
app.whenReady().then(() => {
|
|
236
|
-
createMenu();
|
|
237
|
-
createWindow();
|
|
238
|
-
|
|
239
|
-
app.on('activate', () => {
|
|
240
|
-
// On macOS re-create a window when dock icon is clicked and no windows are open
|
|
241
|
-
if (BrowserWindow.getAllWindows().length === 0) {
|
|
242
|
-
createWindow();
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Quit when all windows are closed, except on macOS
|
|
248
|
-
app.on('window-all-closed', () => {
|
|
249
|
-
if (process.platform !== 'darwin') {
|
|
250
|
-
app.quit();
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// ============================================
|
|
255
|
-
// IPC Handlers for File Operations
|
|
256
|
-
// ============================================
|
|
257
|
-
|
|
258
|
-
// Save file dialog and write data
|
|
259
|
-
ipcMain.handle('save-file', async (event, { data, defaultFilename, filters }) => {
|
|
260
|
-
const result = await dialog.showSaveDialog(mainWindow, {
|
|
261
|
-
defaultPath: defaultFilename,
|
|
262
|
-
filters: filters || [
|
|
263
|
-
{ name: 'Binary Files', extensions: ['bin'] },
|
|
264
|
-
{ name: 'All Files', extensions: ['*'] }
|
|
265
|
-
]
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
if (result.canceled || !result.filePath) {
|
|
269
|
-
return { success: false, canceled: true };
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
// Convert data to Buffer if it's a Uint8Array
|
|
274
|
-
const buffer = Buffer.from(data);
|
|
275
|
-
fs.writeFileSync(result.filePath, buffer);
|
|
276
|
-
return { success: true, filePath: result.filePath };
|
|
277
|
-
} catch (error) {
|
|
278
|
-
return { success: false, error: error.message };
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// Open file dialog and read data
|
|
283
|
-
ipcMain.handle('open-file', async (event, { filters }) => {
|
|
284
|
-
const result = await dialog.showOpenDialog(mainWindow, {
|
|
285
|
-
properties: ['openFile'],
|
|
286
|
-
filters: filters || [
|
|
287
|
-
{ name: 'Binary Files', extensions: ['bin'] },
|
|
288
|
-
{ name: 'All Files', extensions: ['*'] }
|
|
289
|
-
]
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
if (result.canceled || result.filePaths.length === 0) {
|
|
293
|
-
return { success: false, canceled: true };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
const filePath = result.filePaths[0];
|
|
298
|
-
const data = fs.readFileSync(filePath);
|
|
299
|
-
const filename = path.basename(filePath);
|
|
300
|
-
return {
|
|
301
|
-
success: true,
|
|
302
|
-
filePath,
|
|
303
|
-
filename,
|
|
304
|
-
data: Array.from(data) // Convert Buffer to array for IPC transfer
|
|
305
|
-
};
|
|
306
|
-
} catch (error) {
|
|
307
|
-
return { success: false, error: error.message };
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
// Show input dialog (replacement for prompt())
|
|
312
|
-
ipcMain.handle('show-prompt', async (event, { message, defaultValue }) => {
|
|
313
|
-
// Use a simple approach - return the default value
|
|
314
|
-
// In Electron, we use the save dialog instead of prompt
|
|
315
|
-
return defaultValue;
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Show message box
|
|
319
|
-
ipcMain.handle('show-message', async (event, { type, title, message, buttons }) => {
|
|
320
|
-
const result = await dialog.showMessageBox(mainWindow, {
|
|
321
|
-
type: type || 'info',
|
|
322
|
-
title: title || 'ESP32Tool',
|
|
323
|
-
message: message,
|
|
324
|
-
buttons: buttons || ['OK']
|
|
325
|
-
});
|
|
326
|
-
return result.response;
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Show confirm dialog
|
|
330
|
-
ipcMain.handle('show-confirm', async (event, { message }) => {
|
|
331
|
-
const result = await dialog.showMessageBox(mainWindow, {
|
|
332
|
-
type: 'question',
|
|
333
|
-
title: 'Confirm',
|
|
334
|
-
message: message,
|
|
335
|
-
buttons: ['OK', 'Cancel']
|
|
336
|
-
});
|
|
337
|
-
return result.response === 0; // true if OK clicked
|
|
338
|
-
});
|