esp32tool 1.0.0

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.
Files changed (115) hide show
  1. package/README.md +31 -0
  2. package/css/dark.css +156 -0
  3. package/css/light.css +156 -0
  4. package/css/style.css +870 -0
  5. package/dist/const.d.ts +277 -0
  6. package/dist/const.js +511 -0
  7. package/dist/esp_loader.d.ts +222 -0
  8. package/dist/esp_loader.js +1466 -0
  9. package/dist/index.d.ts +10 -0
  10. package/dist/index.js +15 -0
  11. package/dist/lib/spiffs/index.d.ts +15 -0
  12. package/dist/lib/spiffs/index.js +16 -0
  13. package/dist/lib/spiffs/spiffs.d.ts +26 -0
  14. package/dist/lib/spiffs/spiffs.js +132 -0
  15. package/dist/lib/spiffs/spiffsBlock.d.ts +36 -0
  16. package/dist/lib/spiffs/spiffsBlock.js +140 -0
  17. package/dist/lib/spiffs/spiffsConfig.d.ts +63 -0
  18. package/dist/lib/spiffs/spiffsConfig.js +79 -0
  19. package/dist/lib/spiffs/spiffsPage.d.ts +45 -0
  20. package/dist/lib/spiffs/spiffsPage.js +260 -0
  21. package/dist/lib/spiffs/spiffsReader.d.ts +19 -0
  22. package/dist/lib/spiffs/spiffsReader.js +192 -0
  23. package/dist/partition.d.ts +26 -0
  24. package/dist/partition.js +129 -0
  25. package/dist/struct.d.ts +2 -0
  26. package/dist/struct.js +91 -0
  27. package/dist/stubs/esp32.json +8 -0
  28. package/dist/stubs/esp32c2.json +8 -0
  29. package/dist/stubs/esp32c3.json +8 -0
  30. package/dist/stubs/esp32c5.json +8 -0
  31. package/dist/stubs/esp32c6.json +8 -0
  32. package/dist/stubs/esp32c61.json +8 -0
  33. package/dist/stubs/esp32h2.json +8 -0
  34. package/dist/stubs/esp32p4.json +8 -0
  35. package/dist/stubs/esp32p4r3.json +8 -0
  36. package/dist/stubs/esp32s2.json +8 -0
  37. package/dist/stubs/esp32s3.json +8 -0
  38. package/dist/stubs/esp8266.json +8 -0
  39. package/dist/stubs/index.d.ts +10 -0
  40. package/dist/stubs/index.js +56 -0
  41. package/dist/util.d.ts +14 -0
  42. package/dist/util.js +46 -0
  43. package/dist/wasm/filesystems.d.ts +33 -0
  44. package/dist/wasm/filesystems.js +114 -0
  45. package/dist/web/esp32-D955RjN9.js +16 -0
  46. package/dist/web/esp32c2-CJkxHDQi.js +16 -0
  47. package/dist/web/esp32c3-BhUHzH0o.js +16 -0
  48. package/dist/web/esp32c5-Chs0HtmA.js +16 -0
  49. package/dist/web/esp32c6-D6mPN6ut.js +16 -0
  50. package/dist/web/esp32c61-CQiYCWAs.js +16 -0
  51. package/dist/web/esp32h2-LsKJE9AS.js +16 -0
  52. package/dist/web/esp32p4-7nWC-HiD.js +16 -0
  53. package/dist/web/esp32p4r3-CwiPecZW.js +16 -0
  54. package/dist/web/esp32s2-CtqVheSJ.js +16 -0
  55. package/dist/web/esp32s3-CRbtB0QR.js +16 -0
  56. package/dist/web/esp8266-nEkNAo8K.js +16 -0
  57. package/dist/web/index.js +7265 -0
  58. package/electron/main.js +333 -0
  59. package/electron/preload.js +37 -0
  60. package/eslint.config.js +22 -0
  61. package/index.html +408 -0
  62. package/js/modules/esp32-D955RjN9.js +16 -0
  63. package/js/modules/esp32c2-CJkxHDQi.js +16 -0
  64. package/js/modules/esp32c3-BhUHzH0o.js +16 -0
  65. package/js/modules/esp32c5-Chs0HtmA.js +16 -0
  66. package/js/modules/esp32c6-D6mPN6ut.js +16 -0
  67. package/js/modules/esp32c61-CQiYCWAs.js +16 -0
  68. package/js/modules/esp32h2-LsKJE9AS.js +16 -0
  69. package/js/modules/esp32p4-7nWC-HiD.js +16 -0
  70. package/js/modules/esp32p4r3-CwiPecZW.js +16 -0
  71. package/js/modules/esp32s2-CtqVheSJ.js +16 -0
  72. package/js/modules/esp32s3-CRbtB0QR.js +16 -0
  73. package/js/modules/esp8266-nEkNAo8K.js +16 -0
  74. package/js/modules/esptool.js +7265 -0
  75. package/js/script.js +2237 -0
  76. package/js/utilities.js +182 -0
  77. package/license.md +11 -0
  78. package/package.json +61 -0
  79. package/script/build +12 -0
  80. package/script/develop +17 -0
  81. package/src/const.ts +599 -0
  82. package/src/esp_loader.ts +1907 -0
  83. package/src/index.ts +63 -0
  84. package/src/lib/spiffs/index.ts +22 -0
  85. package/src/lib/spiffs/spiffs.ts +175 -0
  86. package/src/lib/spiffs/spiffsBlock.ts +204 -0
  87. package/src/lib/spiffs/spiffsConfig.ts +140 -0
  88. package/src/lib/spiffs/spiffsPage.ts +357 -0
  89. package/src/lib/spiffs/spiffsReader.ts +280 -0
  90. package/src/partition.ts +155 -0
  91. package/src/struct.ts +108 -0
  92. package/src/stubs/README.md +3 -0
  93. package/src/stubs/esp32.json +8 -0
  94. package/src/stubs/esp32c2.json +8 -0
  95. package/src/stubs/esp32c3.json +8 -0
  96. package/src/stubs/esp32c5.json +8 -0
  97. package/src/stubs/esp32c6.json +8 -0
  98. package/src/stubs/esp32c61.json +8 -0
  99. package/src/stubs/esp32h2.json +8 -0
  100. package/src/stubs/esp32p4.json +8 -0
  101. package/src/stubs/esp32p4r3.json +8 -0
  102. package/src/stubs/esp32s2.json +8 -0
  103. package/src/stubs/esp32s3.json +8 -0
  104. package/src/stubs/esp8266.json +8 -0
  105. package/src/stubs/index.ts +86 -0
  106. package/src/util.ts +49 -0
  107. package/src/wasm/fatfs/fatfs.wasm +0 -0
  108. package/src/wasm/fatfs/index.d.ts +26 -0
  109. package/src/wasm/fatfs/index.js +343 -0
  110. package/src/wasm/filesystems.ts +156 -0
  111. package/src/wasm/littlefs/index.d.ts +83 -0
  112. package/src/wasm/littlefs/index.js +529 -0
  113. package/src/wasm/littlefs/littlefs.js +2 -0
  114. package/src/wasm/littlefs/littlefs.wasm +0 -0
  115. package/src/wasm/shared/types.ts +13 -0
@@ -0,0 +1,333 @@
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('cp210') ||
74
+ name.includes('ch910') ||
75
+ name.includes('ch340') ||
76
+ name.includes('ch341') ||
77
+ name.includes('ch343') ||
78
+ name.includes('ftdi') ||
79
+ name.includes('usb') ||
80
+ name.includes('uart') ||
81
+ name.includes('silicon labs') ||
82
+ name.includes('esp');
83
+ });
84
+
85
+ // Select ESP-compatible port or first available
86
+ const selectedPort = espPort || portList[0];
87
+ console.log('Selected port:', selectedPort.portId, selectedPort.displayName || selectedPort.portName);
88
+ lastSelectedPort = selectedPort;
89
+
90
+ callback(selectedPort.portId);
91
+ } else {
92
+ console.log('No serial ports available - queuing selection');
93
+ // No ports available yet - queue this callback for when a port appears
94
+ portSelectionQueue.push(callback);
95
+ }
96
+ });
97
+
98
+ // Track port additions - handle ESP32-S2 reconnect
99
+ ses.on('serial-port-added', (event, port) => {
100
+ console.log('Serial port added:', port);
101
+
102
+ // If we have queued port selections, handle them now
103
+ if (portSelectionQueue.length > 0) {
104
+ console.log('Processing queued port selection');
105
+ const callback = portSelectionQueue.shift();
106
+ callback(port.portId);
107
+ lastSelectedPort = port;
108
+ }
109
+
110
+ // Check if this looks like an ESP32-S2 CDC port appearing after ROM port disappeared
111
+ if (lastSelectedPort && port.portName !== lastSelectedPort.portName) {
112
+ const name = (port.displayName || port.portName || '').toLowerCase();
113
+ if (name.includes('esp') || name.includes('usb') || name.includes('uart')) {
114
+ console.log('ESP32-S2 reconnect detected - new CDC port available');
115
+ esp32s2ReconnectPending = true;
116
+ }
117
+ }
118
+ });
119
+
120
+ // Track port removals - detect ESP32-S2 disconnect
121
+ ses.on('serial-port-removed', (event, port) => {
122
+ console.log('Serial port removed:', port);
123
+
124
+ // If the last selected port was removed, prepare for reconnect
125
+ if (lastSelectedPort && port.portId === lastSelectedPort.portId) {
126
+ console.log('Last selected port removed - may be ESP32-S2 mode switch');
127
+ // Don't clear lastSelectedPort yet, we might need it for comparison
128
+ }
129
+ });
130
+
131
+ // Grant permission for serial port access checks
132
+ ses.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
133
+ if (permission === 'serial') {
134
+ return true;
135
+ }
136
+ return true;
137
+ });
138
+
139
+ // Handle device permission requests
140
+ ses.setDevicePermissionHandler((details) => {
141
+ if (details.deviceType === 'serial') {
142
+ if (details.device) {
143
+ grantedDevices.set(details.device.deviceId, details.device);
144
+ }
145
+ return true;
146
+ }
147
+ return true;
148
+ });
149
+ }
150
+
151
+ // Create application menu
152
+ function createMenu() {
153
+ const template = [
154
+ {
155
+ label: 'File',
156
+ submenu: [
157
+ { role: 'quit' }
158
+ ]
159
+ },
160
+ {
161
+ label: 'Edit',
162
+ submenu: [
163
+ { role: 'undo' },
164
+ { role: 'redo' },
165
+ { type: 'separator' },
166
+ { role: 'cut' },
167
+ { role: 'copy' },
168
+ { role: 'paste' },
169
+ { role: 'selectAll' }
170
+ ]
171
+ },
172
+ {
173
+ label: 'View',
174
+ submenu: [
175
+ { role: 'reload' },
176
+ { role: 'forceReload' },
177
+ { role: 'toggleDevTools' },
178
+ { type: 'separator' },
179
+ { role: 'resetZoom' },
180
+ { role: 'zoomIn' },
181
+ { role: 'zoomOut' },
182
+ { type: 'separator' },
183
+ { role: 'togglefullscreen' }
184
+ ]
185
+ },
186
+ {
187
+ label: 'Help',
188
+ submenu: [
189
+ {
190
+ label: 'About ESP32Tool',
191
+ click: async () => {
192
+ const { shell } = require('electron');
193
+ await shell.openExternal('https://github.com/Jason2866/esp32tool');
194
+ }
195
+ },
196
+ {
197
+ label: 'ESP32 Documentation',
198
+ click: async () => {
199
+ const { shell } = require('electron');
200
+ await shell.openExternal('https://docs.espressif.com/');
201
+ }
202
+ }
203
+ ]
204
+ }
205
+ ];
206
+
207
+ // macOS specific menu adjustments
208
+ if (process.platform === 'darwin') {
209
+ template.unshift({
210
+ label: app.getName(),
211
+ submenu: [
212
+ { role: 'about' },
213
+ { type: 'separator' },
214
+ { role: 'services' },
215
+ { type: 'separator' },
216
+ { role: 'hide' },
217
+ { role: 'hideOthers' },
218
+ { role: 'unhide' },
219
+ { type: 'separator' },
220
+ { role: 'quit' }
221
+ ]
222
+ });
223
+ }
224
+
225
+ const menu = Menu.buildFromTemplate(template);
226
+ Menu.setApplicationMenu(menu);
227
+ }
228
+
229
+ // This method will be called when Electron has finished initialization
230
+ app.whenReady().then(() => {
231
+ createMenu();
232
+ createWindow();
233
+
234
+ app.on('activate', () => {
235
+ // On macOS re-create a window when dock icon is clicked and no windows are open
236
+ if (BrowserWindow.getAllWindows().length === 0) {
237
+ createWindow();
238
+ }
239
+ });
240
+ });
241
+
242
+ // Quit when all windows are closed, except on macOS
243
+ app.on('window-all-closed', () => {
244
+ if (process.platform !== 'darwin') {
245
+ app.quit();
246
+ }
247
+ });
248
+
249
+ // ============================================
250
+ // IPC Handlers for File Operations
251
+ // ============================================
252
+
253
+ // Save file dialog and write data
254
+ ipcMain.handle('save-file', async (event, { data, defaultFilename, filters }) => {
255
+ const result = await dialog.showSaveDialog(mainWindow, {
256
+ defaultPath: defaultFilename,
257
+ filters: filters || [
258
+ { name: 'Binary Files', extensions: ['bin'] },
259
+ { name: 'All Files', extensions: ['*'] }
260
+ ]
261
+ });
262
+
263
+ if (result.canceled || !result.filePath) {
264
+ return { success: false, canceled: true };
265
+ }
266
+
267
+ try {
268
+ // Convert data to Buffer if it's a Uint8Array
269
+ const buffer = Buffer.from(data);
270
+ fs.writeFileSync(result.filePath, buffer);
271
+ return { success: true, filePath: result.filePath };
272
+ } catch (error) {
273
+ return { success: false, error: error.message };
274
+ }
275
+ });
276
+
277
+ // Open file dialog and read data
278
+ ipcMain.handle('open-file', async (event, { filters }) => {
279
+ const result = await dialog.showOpenDialog(mainWindow, {
280
+ properties: ['openFile'],
281
+ filters: filters || [
282
+ { name: 'Binary Files', extensions: ['bin'] },
283
+ { name: 'All Files', extensions: ['*'] }
284
+ ]
285
+ });
286
+
287
+ if (result.canceled || result.filePaths.length === 0) {
288
+ return { success: false, canceled: true };
289
+ }
290
+
291
+ try {
292
+ const filePath = result.filePaths[0];
293
+ const data = fs.readFileSync(filePath);
294
+ const filename = path.basename(filePath);
295
+ return {
296
+ success: true,
297
+ filePath,
298
+ filename,
299
+ data: Array.from(data) // Convert Buffer to array for IPC transfer
300
+ };
301
+ } catch (error) {
302
+ return { success: false, error: error.message };
303
+ }
304
+ });
305
+
306
+ // Show input dialog (replacement for prompt())
307
+ ipcMain.handle('show-prompt', async (event, { message, defaultValue }) => {
308
+ // Use a simple approach - return the default value
309
+ // In Electron, we use the save dialog instead of prompt
310
+ return defaultValue;
311
+ });
312
+
313
+ // Show message box
314
+ ipcMain.handle('show-message', async (event, { type, title, message, buttons }) => {
315
+ const result = await dialog.showMessageBox(mainWindow, {
316
+ type: type || 'info',
317
+ title: title || 'ESP32Tool',
318
+ message: message,
319
+ buttons: buttons || ['OK']
320
+ });
321
+ return result.response;
322
+ });
323
+
324
+ // Show confirm dialog
325
+ ipcMain.handle('show-confirm', async (event, { message }) => {
326
+ const result = await dialog.showMessageBox(mainWindow, {
327
+ type: 'question',
328
+ title: 'Confirm',
329
+ message: message,
330
+ buttons: ['OK', 'Cancel']
331
+ });
332
+ return result.response === 0; // true if OK clicked
333
+ });
@@ -0,0 +1,37 @@
1
+ // Preload script for Electron
2
+ // This runs in a sandboxed environment with access to some Node.js APIs
3
+
4
+ const { contextBridge, ipcRenderer } = require('electron');
5
+
6
+ // Expose platform info and file APIs to renderer
7
+ contextBridge.exposeInMainWorld('electronAPI', {
8
+ platform: process.platform,
9
+ isElectron: true,
10
+ versions: {
11
+ node: process.versions.node,
12
+ chrome: process.versions.chrome,
13
+ electron: process.versions.electron,
14
+ },
15
+
16
+ // File operations
17
+ saveFile: (data, defaultFilename, filters) =>
18
+ ipcRenderer.invoke('save-file', { data, defaultFilename, filters }),
19
+
20
+ openFile: (filters) =>
21
+ ipcRenderer.invoke('open-file', { filters }),
22
+
23
+ // Dialog operations
24
+ showPrompt: (message, defaultValue) =>
25
+ ipcRenderer.invoke('show-prompt', { message, defaultValue }),
26
+
27
+ showMessage: (type, title, message, buttons) =>
28
+ ipcRenderer.invoke('show-message', { type, title, message, buttons }),
29
+
30
+ showConfirm: (message) =>
31
+ ipcRenderer.invoke('show-confirm', { message })
32
+ });
33
+
34
+ // Log when preload script runs
35
+ console.log('Electron preload script loaded');
36
+ console.log('Platform:', process.platform);
37
+ console.log('Electron version:', process.versions.electron);
@@ -0,0 +1,22 @@
1
+ import eslint from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+ import eslintConfigPrettier from "eslint-config-prettier";
4
+ import eslintPluginPrettier from "eslint-plugin-prettier";
5
+
6
+ export default tseslint.config(
7
+ eslint.configs.recommended,
8
+ ...tseslint.configs.recommended,
9
+ eslintConfigPrettier,
10
+ {
11
+ plugins: {
12
+ prettier: eslintPluginPrettier,
13
+ },
14
+ rules: {
15
+ "no-console": "warn",
16
+ "prettier/prettier": "error",
17
+ },
18
+ },
19
+ {
20
+ ignores: ["dist/", "node_modules/", "js/", "src/wasm/"],
21
+ },
22
+ );