esp32tool 1.1.9 → 1.3.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.
- package/.nojekyll +0 -0
- package/README.md +100 -6
- package/apple-touch-icon.png +0 -0
- package/build-electron-cli.cjs +177 -0
- package/build-single-binary.cjs +295 -0
- package/css/light.css +11 -0
- package/css/style.css +261 -41
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +458 -0
- package/dist/console.d.ts +15 -0
- package/dist/console.js +237 -0
- package/dist/const.d.ts +99 -0
- package/dist/const.js +129 -8
- package/dist/esp_loader.d.ts +244 -22
- package/dist/esp_loader.js +1960 -251
- package/dist/index.d.ts +2 -1
- package/dist/index.js +37 -4
- package/dist/node-usb-adapter.d.ts +47 -0
- package/dist/node-usb-adapter.js +725 -0
- package/dist/stubs/index.d.ts +1 -2
- package/dist/stubs/index.js +4 -0
- package/dist/util/console-color.d.ts +19 -0
- package/dist/util/console-color.js +272 -0
- package/dist/util/line-break-transformer.d.ts +5 -0
- package/dist/util/line-break-transformer.js +17 -0
- package/dist/web/index.js +1 -1
- package/electron/cli-main.cjs +74 -0
- package/electron/main.cjs +338 -0
- package/electron/main.js +7 -2
- package/favicon.ico +0 -0
- package/fix-cli-imports.cjs +127 -0
- package/generate-icons.sh +89 -0
- 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/index.html +143 -73
- package/install-android.html +411 -0
- package/js/console.js +269 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +750 -175
- package/js/util/console-color.js +282 -0
- package/js/util/line-break-transformer.js +19 -0
- package/js/webusb-serial.js +1017 -0
- package/license.md +1 -1
- package/manifest.json +89 -0
- package/package.cli.json +29 -0
- package/package.json +35 -24
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/cli.ts +618 -0
- package/src/console.ts +278 -0
- package/src/const.ts +165 -8
- package/src/esp_loader.ts +2354 -302
- package/src/index.ts +69 -3
- package/src/node-usb-adapter.ts +924 -0
- package/src/stubs/index.ts +4 -1
- package/src/util/console-color.ts +290 -0
- package/src/util/line-break-transformer.ts +20 -0
- package/sw.js +155 -0
|
@@ -0,0 +1,338 @@
|
|
|
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
|
+
});
|
package/electron/main.js
CHANGED
|
@@ -70,8 +70,13 @@ function setupSerialPortHandlers(ses) {
|
|
|
70
70
|
// Try to find ESP-compatible port
|
|
71
71
|
const espPort = portList.find(port => {
|
|
72
72
|
const name = (port.displayName || port.portName || '').toLowerCase();
|
|
73
|
-
return name.includes('
|
|
74
|
-
name.includes('
|
|
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') ||
|
|
75
80
|
name.includes('ch340') ||
|
|
76
81
|
name.includes('ch341') ||
|
|
77
82
|
name.includes('ch343') ||
|
package/favicon.ico
ADDED
|
Binary file
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
if (!fs.existsSync('dist/cli.js')) {
|
|
5
|
+
console.error('Run npm run build first!');
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function fixImportsInFile(filePath) {
|
|
10
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
11
|
+
|
|
12
|
+
// Determine the relative path depth (how many ../ we need)
|
|
13
|
+
const depth = filePath.split(path.sep).length - 2; // -2 for 'dist' and filename
|
|
14
|
+
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
15
|
+
|
|
16
|
+
// Fix imports with single quotes: add .js extension to relative imports
|
|
17
|
+
content = content.replace(/from '\.\.\/([^']+)'/g, function(match, moduleName) {
|
|
18
|
+
if (moduleName.endsWith('.js') || moduleName.endsWith('.json')) {
|
|
19
|
+
return match;
|
|
20
|
+
}
|
|
21
|
+
if (moduleName === 'stubs') {
|
|
22
|
+
return `from '../stubs/index.js'`;
|
|
23
|
+
}
|
|
24
|
+
return `from '../${moduleName}.js'`;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
content = content.replace(/from '\.\/([^']+)'/g, function(match, moduleName) {
|
|
28
|
+
if (moduleName.endsWith('.js') || moduleName.endsWith('.json')) {
|
|
29
|
+
return match;
|
|
30
|
+
}
|
|
31
|
+
if (moduleName === 'stubs') {
|
|
32
|
+
return `from './stubs/index.js'`;
|
|
33
|
+
}
|
|
34
|
+
return `from './${moduleName}.js'`;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Fix imports with double quotes
|
|
38
|
+
content = content.replace(/from "\.\.\/([^"]+)"/g, function(match, moduleName) {
|
|
39
|
+
if (moduleName.endsWith('.js') || moduleName.endsWith('.json')) {
|
|
40
|
+
return match;
|
|
41
|
+
}
|
|
42
|
+
if (moduleName === 'stubs') {
|
|
43
|
+
return `from "../stubs/index.js"`;
|
|
44
|
+
}
|
|
45
|
+
return `from "../${moduleName}.js"`;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
content = content.replace(/from "\.\/([^"]+)"/g, function(match, moduleName) {
|
|
49
|
+
if (moduleName.endsWith('.js') || moduleName.endsWith('.json')) {
|
|
50
|
+
return match;
|
|
51
|
+
}
|
|
52
|
+
if (moduleName === 'stubs') {
|
|
53
|
+
return `from "./stubs/index.js"`;
|
|
54
|
+
}
|
|
55
|
+
return `from "./${moduleName}.js"`;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Fix dynamic JSON imports and add .default access
|
|
59
|
+
// Pattern: stubcode = await import("./esp32c3.json", { with: { type: "json" } });
|
|
60
|
+
// Replace with: stubcode = (await import("./esp32c3.json", { with: { type: "json" } })).default;
|
|
61
|
+
content = content.replace(
|
|
62
|
+
/(\w+)\s*=\s*await\s+import\("\.\/([^"]+\.json)",\s*\{\s*with:\s*\{\s*type:\s*"json"\s*\}\s*\}\);/g,
|
|
63
|
+
function(match, varName, jsonFile) {
|
|
64
|
+
return `${varName} = (await import("./${jsonFile}", { with: { type: "json" } })).default;`;
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Also handle JSON imports without .default that were already processed
|
|
69
|
+
// Pattern: stubcode = await import("./esp32c3.json", { with: { type: "json" } });
|
|
70
|
+
// This catches cases where the import is already formatted but missing .default
|
|
71
|
+
content = content.replace(
|
|
72
|
+
/(\w+)\s*=\s*await\s+import\(([^)]+\.json[^)]*)\);/g,
|
|
73
|
+
function(match, varName, importPath) {
|
|
74
|
+
// Skip if already has .default
|
|
75
|
+
if (match.includes('.default')) {
|
|
76
|
+
return match;
|
|
77
|
+
}
|
|
78
|
+
return `${varName} = (await import(${importPath})).default;`;
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Also fix dynamic imports for .js files
|
|
83
|
+
content = content.replace(/import\("\.\/([^"]+)"\)/g, function(match, moduleName) {
|
|
84
|
+
if (moduleName.endsWith('.js')) {
|
|
85
|
+
return match;
|
|
86
|
+
}
|
|
87
|
+
if (moduleName.endsWith('.json')) {
|
|
88
|
+
// Add JSON import assertion for Node.js ES modules
|
|
89
|
+
return `import("./${moduleName}", { with: { type: "json" } })`;
|
|
90
|
+
}
|
|
91
|
+
return `import("./${moduleName}.js")`;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
fs.writeFileSync(filePath, content);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function fixAllJsFiles(dir) {
|
|
98
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
99
|
+
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const fullPath = path.join(dir, entry.name);
|
|
102
|
+
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
// Recursively fix subdirectories
|
|
105
|
+
fixAllJsFiles(fullPath);
|
|
106
|
+
} else if (entry.isFile() && entry.name.endsWith('.js') && entry.name !== 'cli-fixed.js') {
|
|
107
|
+
fixImportsInFile(fullPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('Fixing imports in all .js files...');
|
|
113
|
+
fixAllJsFiles('dist');
|
|
114
|
+
|
|
115
|
+
// Now create cli-fixed.js with shebang
|
|
116
|
+
let cliSrc = fs.readFileSync('dist/cli.js', 'utf8');
|
|
117
|
+
|
|
118
|
+
// Remove shebang if present
|
|
119
|
+
if (cliSrc.startsWith('#!')) {
|
|
120
|
+
cliSrc = cliSrc.substring(cliSrc.indexOf('\n') + 1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Write fixed version with shebang
|
|
124
|
+
fs.writeFileSync('dist/cli-fixed.js', '#!/usr/bin/env node\n' + cliSrc);
|
|
125
|
+
fs.chmodSync('dist/cli-fixed.js', 0o755);
|
|
126
|
+
|
|
127
|
+
console.log('CLI built: dist/cli-fixed.js');
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Icon Generator für ESP32Tool PWA
|
|
4
|
+
# Erstellt einfache Placeholder-Icons falls ImageMagick installiert ist
|
|
5
|
+
|
|
6
|
+
echo "🎨 ESP32Tool Icon Generator"
|
|
7
|
+
echo ""
|
|
8
|
+
|
|
9
|
+
# Prüfe ob ImageMagick installiert ist
|
|
10
|
+
if ! command -v convert &> /dev/null; then
|
|
11
|
+
echo "❌ ImageMagick ist nicht installiert!"
|
|
12
|
+
echo ""
|
|
13
|
+
echo "Installation:"
|
|
14
|
+
echo " macOS: brew install imagemagick"
|
|
15
|
+
echo " Linux: sudo apt install imagemagick"
|
|
16
|
+
echo " Windows: https://imagemagick.org/script/download.php"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Alternativ: Nutze ein Online-Tool wie https://realfavicongenerator.net/"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Erstelle icons Ordner
|
|
23
|
+
mkdir -p icons
|
|
24
|
+
mkdir -p screenshots
|
|
25
|
+
|
|
26
|
+
echo "📦 Erstelle Icon-Größen..."
|
|
27
|
+
|
|
28
|
+
# Farben
|
|
29
|
+
BG_COLOR="#1a1a1a"
|
|
30
|
+
TEXT_COLOR="#ffffff"
|
|
31
|
+
ACCENT_COLOR="#4CAF50"
|
|
32
|
+
|
|
33
|
+
# Erstelle Icons mit ESP32 Text
|
|
34
|
+
for size in 72 96 128 144 152 192 384 512; do
|
|
35
|
+
# Berechne Schriftgröße basierend auf Icon-Größe
|
|
36
|
+
fontsize=$((size / 6))
|
|
37
|
+
|
|
38
|
+
convert -size ${size}x${size} xc:${BG_COLOR} \
|
|
39
|
+
-fill ${ACCENT_COLOR} -draw "circle $((size/2)),$((size/2)) $((size/2)),$((size/4))" \
|
|
40
|
+
-fill ${TEXT_COLOR} -pointsize ${fontsize} -font "DejaVu-Sans-Bold" \
|
|
41
|
+
-gravity center -annotate +0-$((size/12)) "ESP32" \
|
|
42
|
+
-pointsize $((fontsize/2)) -annotate +0+$((size/8)) "TOOL" \
|
|
43
|
+
icons/icon-${size}.png
|
|
44
|
+
|
|
45
|
+
echo " ✅ icon-${size}.png"
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# Erstelle Favicon
|
|
49
|
+
convert icons/icon-192.png -resize 32x32 favicon.ico
|
|
50
|
+
echo " ✅ favicon.ico"
|
|
51
|
+
|
|
52
|
+
# Erstelle Apple Touch Icon
|
|
53
|
+
convert icons/icon-192.png -resize 180x180 apple-touch-icon.png
|
|
54
|
+
echo " ✅ apple-touch-icon.png"
|
|
55
|
+
|
|
56
|
+
# Erstelle Placeholder Screenshots
|
|
57
|
+
echo ""
|
|
58
|
+
echo "📸 Erstelle Placeholder Screenshots..."
|
|
59
|
+
|
|
60
|
+
# Desktop Screenshot (1280x720)
|
|
61
|
+
convert -size 1280x720 xc:${BG_COLOR} \
|
|
62
|
+
-fill ${TEXT_COLOR} -pointsize 48 -font "Helvetica-Bold" \
|
|
63
|
+
-gravity center -annotate +0-100 "ESP32Tool" \
|
|
64
|
+
-pointsize 24 -annotate +0+0 "Flash & Read ESP Devices" \
|
|
65
|
+
-pointsize 18 -annotate +0+50 "Desktop View" \
|
|
66
|
+
screenshots/desktop.png
|
|
67
|
+
echo " ✅ desktop.png (1280x720)"
|
|
68
|
+
|
|
69
|
+
# Mobile Screenshot (540x720)
|
|
70
|
+
convert -size 540x720 xc:${BG_COLOR} \
|
|
71
|
+
-fill ${TEXT_COLOR} -pointsize 36 -font "Helvetica-Bold" \
|
|
72
|
+
-gravity center -annotate +0-100 "ESP32Tool" \
|
|
73
|
+
-pointsize 18 -annotate +0+0 "Flash & Read" \
|
|
74
|
+
-pointsize 18 -annotate +0+30 "ESP Devices" \
|
|
75
|
+
-pointsize 14 -annotate +0+80 "Mobile View" \
|
|
76
|
+
screenshots/mobile.png
|
|
77
|
+
echo " ✅ mobile.png (540x720)"
|
|
78
|
+
|
|
79
|
+
echo ""
|
|
80
|
+
echo "✨ Fertig! Icons und Screenshots wurden erstellt."
|
|
81
|
+
echo ""
|
|
82
|
+
echo "📁 Dateien:"
|
|
83
|
+
echo " - icons/icon-*.png (8 Größen)"
|
|
84
|
+
echo " - favicon.ico"
|
|
85
|
+
echo " - apple-touch-icon.png"
|
|
86
|
+
echo " - screenshots/*.png"
|
|
87
|
+
echo ""
|
|
88
|
+
echo "💡 Tipp: Ersetze die Placeholder-Icons mit deinem eigenen Logo!"
|
|
89
|
+
echo " Nutze dazu: convert dein-logo.png -resize 512x512 icons/icon-512.png"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|