esp32tool 1.1.8 ā 1.2.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 +225 -35
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +458 -0
- package/dist/esp_loader.d.ts +129 -21
- package/dist/esp_loader.js +1227 -222
- 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/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 +94 -64
- package/install-android.html +411 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +165 -160
- 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 +31 -21
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/cli.ts +618 -0
- package/src/esp_loader.ts +1438 -254
- package/src/index.ts +69 -3
- package/src/node-usb-adapter.ts +924 -0
- package/src/stubs/index.ts +4 -1
- package/sw.js +155 -0
package/.nojekyll
ADDED
|
File without changes
|
package/README.md
CHANGED
|
@@ -2,9 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
# š ESP32Tool ā The Ultimate ESP Filesystem Powerhouse
|
|
4
4
|
|
|
5
|
-
**Flash. Manage. Dominate.
|
|
5
|
+
**Flash. Manage. Dominate. In your browser, on desktop, mobile, or command line.**
|
|
6
6
|
|
|
7
|
-
Meet **ESP32Tool** ā your all-in-one, next-gen solution for ESP device management. Experience seamless firmware flashing, backup, and now, full filesystem control with just a few clicks. No drivers, no command line, no limits!
|
|
7
|
+
Meet **ESP32Tool** ā your all-in-one, next-gen solution for ESP device management. Experience seamless firmware flashing, backup, and now, full filesystem control with just a few clicks. No drivers, no command line hassle, no limits!
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## š¦ Available Versions
|
|
12
|
+
|
|
13
|
+
**Choose the version that fits your workflow:**
|
|
14
|
+
|
|
15
|
+
### š Web App (Browser)
|
|
16
|
+
- **Zero installation** ā runs directly in your browser
|
|
17
|
+
- Works on any modern desktop or mobile browser
|
|
18
|
+
- Perfect for quick tasks and on-the-go management
|
|
19
|
+
- **Try it now:** [jason2866.github.io/esp32tool](https://jason2866.github.io/esp32tool)
|
|
20
|
+
|
|
21
|
+
### š± Mobile App (Android PWA)
|
|
22
|
+
- **Install as native app** on Android devices
|
|
23
|
+
- Works with USB OTG adapters via WebUSB
|
|
24
|
+
- Full ESP management on your phone or tablet
|
|
25
|
+
- **Install:** Visit [jason2866.github.io/esp32tool](https://jason2866.github.io/esp32tool) in Chrome ā "Add to Home screen"
|
|
26
|
+
- **Details:** See [Android Installation](#-android-installation-pwa) below
|
|
27
|
+
|
|
28
|
+
### š» Desktop App (Electron GUI)
|
|
29
|
+
- **Full-featured GUI** with all web capabilities
|
|
30
|
+
- Offline-ready, no internet required
|
|
31
|
+
- Native desktop integration
|
|
32
|
+
- **Download:** [Latest Release](https://github.com/Jason2866/esp32tool/releases)
|
|
33
|
+
- Available for: macOS (Intel & Apple Silicon), Windows, Linux
|
|
34
|
+
|
|
35
|
+
### āØļø Command Line Interface (CLI)
|
|
36
|
+
- **Standalone executable** ā no Node.js required!
|
|
37
|
+
- Perfect for automation and scripting
|
|
38
|
+
- esptool.py-compatible commands
|
|
39
|
+
- **Download:** [Latest Release](https://github.com/Jason2866/esp32tool/releases)
|
|
40
|
+
- **Documentation:** [CLI-README.md](CLI-README.md)
|
|
41
|
+
- Available for: macOS (Intel & Apple Silicon), Windows, Linux
|
|
8
42
|
|
|
9
43
|
---
|
|
10
44
|
|
|
@@ -18,7 +52,9 @@ ESP32Tool is designed first and foremost for the entire ESP32 family ā deliver
|
|
|
18
52
|
- **Lightning Fast:** Custom, high-performance flash access ā up to 10x faster than esptool.py!
|
|
19
53
|
- **Rock-Solid Reliability:** Automatic resume on read errors. No more broken operations, ever.
|
|
20
54
|
- **Plug & Play:** Manage your ESP directly in the browser. No software installation needed.
|
|
21
|
-
- **
|
|
55
|
+
- **Multi-Platform:** Available as web app, desktop app (Electron), and standalone CLI
|
|
56
|
+
- **Offline Ready:** Desktop and CLI versions work completely offline
|
|
57
|
+
- **š± Android Ready:** Install as Progressive Web App (PWA) on Android! Works with USB OTG adapters via WebUSB.
|
|
22
58
|
|
|
23
59
|
- **Bonus:** ESP8266 support is now included! Manage filesystems on ESP8266 devices with the same ease.
|
|
24
60
|
|
|
@@ -26,6 +62,60 @@ ESP32Tool is designed first and foremost for the entire ESP32 family ā deliver
|
|
|
26
62
|
|
|
27
63
|
---
|
|
28
64
|
|
|
65
|
+
## š¦ Desktop & CLI Installation
|
|
66
|
+
|
|
67
|
+
### Desktop App (GUI)
|
|
68
|
+
|
|
69
|
+
**Download from [GitHub Releases](https://github.com/Jason2866/esp32tool/releases)**
|
|
70
|
+
|
|
71
|
+
- **macOS:** Download `ESP32Tool-*.dmg`, open and drag to Applications
|
|
72
|
+
- **Windows:** Download and run `ESP32Tool-Setup-*.exe` installer
|
|
73
|
+
- **Linux:** Download `.deb` (Debian/Ubuntu) or `.rpm` (Fedora/RHEL) package
|
|
74
|
+
|
|
75
|
+
### Command Line Interface
|
|
76
|
+
|
|
77
|
+
**Download from [GitHub Releases](https://github.com/Jason2866/esp32tool/releases)**
|
|
78
|
+
|
|
79
|
+
Look for `ESP32Tool-CLI-*` files. No Node.js installation required!
|
|
80
|
+
|
|
81
|
+
**Quick Start:**
|
|
82
|
+
```bash
|
|
83
|
+
# macOS
|
|
84
|
+
/Applications/ESP32Tool.app/Contents/MacOS/esp32tool list-ports
|
|
85
|
+
/Applications/ESP32Tool.app/Contents/MacOS/esp32tool --port /dev/ttyUSB0 chip-id
|
|
86
|
+
|
|
87
|
+
# Linux
|
|
88
|
+
./esp32tool list-ports
|
|
89
|
+
./esp32tool --port /dev/ttyUSB0 chip-id
|
|
90
|
+
|
|
91
|
+
# Windows
|
|
92
|
+
esp32tool.exe list-ports
|
|
93
|
+
esp32tool.exe --port COM3 chip-id
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Full CLI documentation:** [CLI-README.md](CLI-README.md)
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## š± Android Installation (PWA)
|
|
101
|
+
|
|
102
|
+
ESP32Tool works natively on Android devices with USB OTG support!
|
|
103
|
+
|
|
104
|
+
**Requirements:**
|
|
105
|
+
- Android 5.0+ (Lollipop or higher)
|
|
106
|
+
- Chrome for Android 61+ (recommended: latest version)
|
|
107
|
+
- USB OTG adapter/cable
|
|
108
|
+
- ESP32/ESP8266 device
|
|
109
|
+
|
|
110
|
+
**Installation:**
|
|
111
|
+
1. Open [jason2866.github.io/esp32tool](https://jason2866.github.io/esp32tool) in Chrome
|
|
112
|
+
2. Tap the menu (ā®) ā "Add to Home screen" or "Install app"
|
|
113
|
+
3. Connect your ESP device via USB OTG adapter
|
|
114
|
+
4. Grant USB permissions when prompted
|
|
115
|
+
5. Flash and manage your ESP devices on the go!
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
29
119
|
## š ļø Developer Quickstart
|
|
30
120
|
|
|
31
121
|
1. Clone this repository
|
|
@@ -35,11 +125,15 @@ ESP32Tool is designed first and foremost for the entire ESP32 family ā deliver
|
|
|
35
125
|
|
|
36
126
|
---
|
|
37
127
|
|
|
38
|
-
## š
|
|
128
|
+
## š The Story
|
|
129
|
+
|
|
130
|
+
Born from the minds of [Melissa LeBlanc-Williams](https://github.com/makermelissa), Adafruit, Nabu Casa and now supercharged by Jason2866, ESP32Tool has evolved into the most advanced, browser-based ESP management suite. With every update, the boundaries are pushed of whatās possible for your ESP devices.
|
|
131
|
+
|
|
132
|
+
**Latest updates:**
|
|
39
133
|
|
|
40
|
-
|
|
134
|
+
December 2025 ā Now with full LittleFS, SPIFFS, and FATFS support, plus file add/delete magic! Available as web app and desktop GUI.
|
|
41
135
|
|
|
42
|
-
|
|
136
|
+
January 2026 ā Added Android mobile devices support, standalone CLI.
|
|
43
137
|
|
|
44
138
|
---
|
|
45
139
|
|
|
Binary file
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build standalone Electron-based CLI binaries
|
|
5
|
+
* Creates truly standalone executables that don't require Node.js installation
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This script temporarily swaps package.json with package.cli.json
|
|
8
|
+
* during the build process, then restores the original package.json.
|
|
9
|
+
*
|
|
10
|
+
* When releasing a new version:
|
|
11
|
+
* 1. Update version in package.json
|
|
12
|
+
* 2. Update version in package.cli.json (keep in sync!)
|
|
13
|
+
* 3. Run this script to build CLI binaries
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
console.log('Building Electron-based CLI binaries...\n');
|
|
21
|
+
|
|
22
|
+
// Ensure dist is built
|
|
23
|
+
if (!fs.existsSync('dist/cli.js')) {
|
|
24
|
+
console.log('Building TypeScript first...');
|
|
25
|
+
execSync('npm run build', { stdio: 'inherit' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Read package files
|
|
29
|
+
const originalPackage = fs.readFileSync('package.json', 'utf8');
|
|
30
|
+
const cliPackageContent = fs.readFileSync('package.cli.json', 'utf8');
|
|
31
|
+
|
|
32
|
+
// Parse and sync versions
|
|
33
|
+
const originalPkg = JSON.parse(originalPackage);
|
|
34
|
+
const cliPkg = JSON.parse(cliPackageContent);
|
|
35
|
+
|
|
36
|
+
if (originalPkg.version !== cliPkg.version) {
|
|
37
|
+
console.log(`ā ļø Version mismatch detected!`);
|
|
38
|
+
console.log(` package.json: ${originalPkg.version}`);
|
|
39
|
+
console.log(` package.cli.json: ${cliPkg.version}`);
|
|
40
|
+
console.log(` Syncing package.cli.json to ${originalPkg.version}...\n`);
|
|
41
|
+
|
|
42
|
+
cliPkg.version = originalPkg.version;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sync dependencies from main package.json to CLI package.json
|
|
46
|
+
if (originalPkg.dependencies) {
|
|
47
|
+
if (JSON.stringify(originalPkg.dependencies) !== JSON.stringify(cliPkg.dependencies)) {
|
|
48
|
+
console.log(`ā ļø Dependencies mismatch detected!`);
|
|
49
|
+
console.log(` Syncing dependencies from package.json to package.cli.json...\n`);
|
|
50
|
+
cliPkg.dependencies = originalPkg.dependencies;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Save synced package.cli.json if changes were made
|
|
55
|
+
if (originalPkg.version !== JSON.parse(cliPackageContent).version ||
|
|
56
|
+
JSON.stringify(originalPkg.dependencies) !== JSON.stringify(JSON.parse(cliPackageContent).dependencies)) {
|
|
57
|
+
fs.writeFileSync('package.cli.json', JSON.stringify(cliPkg, null, 2) + '\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cliPackage = JSON.stringify(cliPkg, null, 2);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Backup original package.json
|
|
64
|
+
fs.writeFileSync('package.json.backup', originalPackage);
|
|
65
|
+
|
|
66
|
+
// Use CLI package.json
|
|
67
|
+
fs.writeFileSync('package.json', cliPackage);
|
|
68
|
+
|
|
69
|
+
// Temporarily swap forge configs
|
|
70
|
+
if (fs.existsSync('forge.config.cjs')) {
|
|
71
|
+
fs.renameSync('forge.config.cjs', 'forge.config.cjs.gui-backup');
|
|
72
|
+
}
|
|
73
|
+
if (fs.existsSync('forge.config.cli.cjs')) {
|
|
74
|
+
fs.renameSync('forge.config.cli.cjs', 'forge.config.cjs');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Build for current platform
|
|
78
|
+
const platform = process.platform;
|
|
79
|
+
const arch = process.arch;
|
|
80
|
+
|
|
81
|
+
console.log(`\nBuilding CLI for ${platform}-${arch}...`);
|
|
82
|
+
|
|
83
|
+
// Backup GUI builds if they exist
|
|
84
|
+
let guiBackupPath = null;
|
|
85
|
+
if (fs.existsSync('out')) {
|
|
86
|
+
guiBackupPath = 'out-gui-backup-' + Date.now();
|
|
87
|
+
console.log(`ā ļø Backing up existing GUI builds to ${guiBackupPath}...`);
|
|
88
|
+
fs.renameSync('out', guiBackupPath);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
execSync(`npx electron-forge package --platform=${platform} --arch=${arch}`, {
|
|
93
|
+
stdio: 'inherit'
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
execSync(`npx electron-forge make --platform=${platform} --arch=${arch}`, {
|
|
97
|
+
stdio: 'inherit'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Move CLI output to out-cli directory
|
|
101
|
+
if (fs.existsSync('out')) {
|
|
102
|
+
if (fs.existsSync('out-cli')) {
|
|
103
|
+
fs.rmSync('out-cli', { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
fs.renameSync('out', 'out-cli');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Restore GUI builds
|
|
109
|
+
if (guiBackupPath && fs.existsSync(guiBackupPath)) {
|
|
110
|
+
console.log('ā Restoring GUI builds...');
|
|
111
|
+
fs.renameSync(guiBackupPath, 'out');
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Restore GUI builds even on error
|
|
115
|
+
if (guiBackupPath && fs.existsSync(guiBackupPath)) {
|
|
116
|
+
console.log('ā ļø Restoring GUI builds after error...');
|
|
117
|
+
if (fs.existsSync('out')) {
|
|
118
|
+
fs.rmSync('out', { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
fs.renameSync(guiBackupPath, 'out');
|
|
121
|
+
}
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
// Rename release files to add -CLI suffix
|
|
127
|
+
const makeDir = path.join('out-cli', 'make');
|
|
128
|
+
if (fs.existsSync(makeDir)) {
|
|
129
|
+
const renameInDir = (dir) => {
|
|
130
|
+
if (!fs.existsSync(dir)) return;
|
|
131
|
+
const files = fs.readdirSync(dir, { recursive: true, withFileTypes: true });
|
|
132
|
+
files.forEach(file => {
|
|
133
|
+
if (file.isFile()) {
|
|
134
|
+
const oldPath = path.join(file.path || file.parentPath, file.name);
|
|
135
|
+
let newName = file.name;
|
|
136
|
+
|
|
137
|
+
// Handle different file types
|
|
138
|
+
if (file.name.endsWith('.zip') || file.name.endsWith('.exe')) {
|
|
139
|
+
// Add -CLI before the platform identifier
|
|
140
|
+
newName = file.name.replace(/(ESP32Tool)(-darwin|-linux|-win32)/, '$1-CLI$2');
|
|
141
|
+
} else if (file.name.endsWith('.deb')) {
|
|
142
|
+
// esp32tool_1.2.0_amd64.deb -> esp32tool-cli_1.2.0_amd64.deb
|
|
143
|
+
newName = file.name.replace(/^esp32tool_/, 'esp32tool-cli_');
|
|
144
|
+
} else if (file.name.endsWith('.rpm')) {
|
|
145
|
+
// esp32tool-1.2.0-1.x86_64.rpm -> esp32tool-cli-1.2.0-1.x86_64.rpm
|
|
146
|
+
newName = file.name.replace(/^esp32tool-/, 'esp32tool-cli-');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const newPath = path.join(file.path || file.parentPath, newName);
|
|
150
|
+
if (oldPath !== newPath) {
|
|
151
|
+
fs.renameSync(oldPath, newPath);
|
|
152
|
+
console.log(`Renamed: ${file.name} -> ${newName}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
renameInDir(makeDir);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log('\nā CLI binaries built successfully!');
|
|
161
|
+
console.log('Output: out-cli/make/');
|
|
162
|
+
|
|
163
|
+
} finally {
|
|
164
|
+
// Restore forge configs
|
|
165
|
+
if (fs.existsSync('forge.config.cjs')) {
|
|
166
|
+
fs.renameSync('forge.config.cjs', 'forge.config.cli.cjs');
|
|
167
|
+
}
|
|
168
|
+
if (fs.existsSync('forge.config.cjs.gui-backup')) {
|
|
169
|
+
fs.renameSync('forge.config.cjs.gui-backup', 'forge.config.cjs');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Restore original package.json
|
|
173
|
+
fs.writeFileSync('package.json', originalPackage);
|
|
174
|
+
if (fs.existsSync('package.json.backup')) {
|
|
175
|
+
fs.unlinkSync('package.json.backup');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Build single executable binaries for all platforms
|
|
4
|
+
* Creates self-contained executables that include Node.js + app + dependencies
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const archiver = require('archiver');
|
|
11
|
+
|
|
12
|
+
const packageJson = require('./package.json');
|
|
13
|
+
const VERSION = packageJson.version;
|
|
14
|
+
const NODE_VERSION = packageJson.engines.node.replace(/[^\d.]/g, '').split('.')[0]; // Extract major version
|
|
15
|
+
|
|
16
|
+
console.log(`Building ESP32Tool CLI v${VERSION} single executables...\n`);
|
|
17
|
+
|
|
18
|
+
// Create binaries directory
|
|
19
|
+
const binariesDir = path.join(__dirname, 'binaries');
|
|
20
|
+
if (!fs.existsSync(binariesDir)) {
|
|
21
|
+
fs.mkdirSync(binariesDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Build the app first
|
|
25
|
+
console.log('1. Building application...');
|
|
26
|
+
execSync('npm run build', { stdio: 'inherit' });
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get all dependencies recursively from package.json
|
|
30
|
+
*/
|
|
31
|
+
function getAllDependencies() {
|
|
32
|
+
const dependencies = new Set();
|
|
33
|
+
|
|
34
|
+
// Add direct dependencies from package.json
|
|
35
|
+
if (packageJson.dependencies) {
|
|
36
|
+
Object.keys(packageJson.dependencies).forEach(dep => dependencies.add(dep));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add scoped packages that are dependencies of our dependencies
|
|
40
|
+
const nodeModulesDir = path.join(__dirname, 'node_modules');
|
|
41
|
+
|
|
42
|
+
// Helper to recursively find dependencies
|
|
43
|
+
function addDepsRecursively(moduleName) {
|
|
44
|
+
const modulePath = path.join(nodeModulesDir, moduleName);
|
|
45
|
+
if (!fs.existsSync(modulePath)) return;
|
|
46
|
+
|
|
47
|
+
const modulePackageJson = path.join(modulePath, 'package.json');
|
|
48
|
+
if (fs.existsSync(modulePackageJson)) {
|
|
49
|
+
try {
|
|
50
|
+
const pkg = JSON.parse(fs.readFileSync(modulePackageJson, 'utf8'));
|
|
51
|
+
if (pkg.dependencies) {
|
|
52
|
+
Object.keys(pkg.dependencies).forEach(dep => {
|
|
53
|
+
if (!dependencies.has(dep)) {
|
|
54
|
+
dependencies.add(dep);
|
|
55
|
+
addDepsRecursively(dep);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
// Ignore invalid package.json
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Recursively add dependencies
|
|
66
|
+
Array.from(dependencies).forEach(dep => addDepsRecursively(dep));
|
|
67
|
+
|
|
68
|
+
return Array.from(dependencies);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create bundle directory with all necessary files
|
|
73
|
+
*/
|
|
74
|
+
function createBundle() {
|
|
75
|
+
const bundleDir = path.join(__dirname, '.bundle-temp');
|
|
76
|
+
|
|
77
|
+
// Clean up old bundle
|
|
78
|
+
if (fs.existsSync(bundleDir)) {
|
|
79
|
+
fs.rmSync(bundleDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
83
|
+
|
|
84
|
+
console.log('2. Creating bundle...');
|
|
85
|
+
|
|
86
|
+
// Copy dist
|
|
87
|
+
fs.cpSync(path.join(__dirname, 'dist'), path.join(bundleDir, 'dist'), { recursive: true });
|
|
88
|
+
|
|
89
|
+
// Get all dependencies from package.json
|
|
90
|
+
const modules = getAllDependencies();
|
|
91
|
+
|
|
92
|
+
console.log(` Bundling ${modules.length} dependencies...`);
|
|
93
|
+
|
|
94
|
+
const nodeModulesDir = path.join(bundleDir, 'node_modules');
|
|
95
|
+
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
|
96
|
+
|
|
97
|
+
modules.forEach(mod => {
|
|
98
|
+
const src = path.join(__dirname, 'node_modules', mod);
|
|
99
|
+
const dest = path.join(nodeModulesDir, mod);
|
|
100
|
+
if (fs.existsSync(src)) {
|
|
101
|
+
// Ensure parent directory exists (for scoped packages like `@scope/pkg`)
|
|
102
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
103
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Copy package.json
|
|
108
|
+
fs.copyFileSync(
|
|
109
|
+
path.join(__dirname, 'package.json'),
|
|
110
|
+
path.join(bundleDir, 'package.json')
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return bundleDir;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create launcher script for Unix (macOS/Linux)
|
|
118
|
+
*/
|
|
119
|
+
function createUnixLauncher(bundleDir) {
|
|
120
|
+
const launcher = `#!/bin/bash
|
|
121
|
+
# ESP32Tool CLI - Self-contained launcher
|
|
122
|
+
|
|
123
|
+
# Get the directory where this script is located
|
|
124
|
+
DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
125
|
+
|
|
126
|
+
# Check for Node.js
|
|
127
|
+
if ! command -v node &> /dev/null; then
|
|
128
|
+
echo "Error: Node.js not found"
|
|
129
|
+
echo "Please install Node.js ${NODE_VERSION}+ from https://nodejs.org/"
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Check Node.js version
|
|
134
|
+
CURRENT_NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
|
135
|
+
if [ "$CURRENT_NODE_VERSION" -lt ${NODE_VERSION} ]; then
|
|
136
|
+
echo "Error: Node.js ${NODE_VERSION}+ required (found: $(node -v))"
|
|
137
|
+
echo "Please upgrade Node.js from https://nodejs.org/"
|
|
138
|
+
exit 1
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Run CLI
|
|
142
|
+
cd "$DIR"
|
|
143
|
+
exec node dist/cli-fixed.js "$@"
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
fs.writeFileSync(path.join(bundleDir, 'esp32tool'), launcher);
|
|
147
|
+
fs.chmodSync(path.join(bundleDir, 'esp32tool'), 0o755);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Create launcher script for Windows
|
|
152
|
+
*/
|
|
153
|
+
function createWindowsLauncher(bundleDir) {
|
|
154
|
+
const launcher = `@echo off
|
|
155
|
+
REM ESP32Tool CLI - Self-contained launcher
|
|
156
|
+
|
|
157
|
+
REM Get the directory where this script is located
|
|
158
|
+
set DIR=%~dp0
|
|
159
|
+
|
|
160
|
+
REM Check for Node.js
|
|
161
|
+
where node >nul 2>nul
|
|
162
|
+
if %ERRORLEVEL% NEQ 0 (
|
|
163
|
+
echo Error: Node.js not found
|
|
164
|
+
echo Please install Node.js ${NODE_VERSION}+ from https://nodejs.org/
|
|
165
|
+
exit /b 1
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
REM Check Node.js version
|
|
169
|
+
for /f "tokens=2 delims=v." %%a in ('node -v') do (
|
|
170
|
+
set NODE_MAJOR=%%a
|
|
171
|
+
goto :check_version
|
|
172
|
+
)
|
|
173
|
+
:check_version
|
|
174
|
+
if %NODE_MAJOR% LSS ${NODE_VERSION} (
|
|
175
|
+
echo Error: Node.js ${NODE_VERSION}+ required
|
|
176
|
+
echo Please upgrade Node.js from https://nodejs.org/
|
|
177
|
+
exit /b 1
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
REM Run CLI
|
|
181
|
+
cd /d "%DIR%"
|
|
182
|
+
node dist\\cli-fixed.js %*
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(path.join(bundleDir, 'esp32tool.bat'), launcher);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Create self-extracting archive for Unix using makeself
|
|
190
|
+
*/
|
|
191
|
+
function createUnixBinary(bundleDir, platform) {
|
|
192
|
+
console.log(`3. Creating ${platform} binary...`);
|
|
193
|
+
|
|
194
|
+
const outputFile = path.join(binariesDir, `esp32tool-${platform}`);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
// Check if makeself is available
|
|
198
|
+
try {
|
|
199
|
+
execSync('which makeself', { stdio: 'pipe' });
|
|
200
|
+
} catch {
|
|
201
|
+
console.error(` ā Skipped: makeself not installed (install with: brew install makeself / apt install makeself)`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Create makeself archive with .run extension
|
|
206
|
+
const runFile = `${outputFile}.run`;
|
|
207
|
+
execSync(
|
|
208
|
+
`makeself --notemp "${bundleDir}" "${runFile}" "ESP32Tool CLI v${VERSION}" ./esp32tool`,
|
|
209
|
+
{ stdio: 'inherit' }
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Create wrapper script that adds -- automatically
|
|
213
|
+
const runFileName = path.basename(runFile);
|
|
214
|
+
const wrapper = `#!/bin/bash
|
|
215
|
+
# ESP32Tool CLI Wrapper
|
|
216
|
+
|
|
217
|
+
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
218
|
+
exec "$SCRIPT_DIR/${runFileName}" -- "$@"
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
fs.writeFileSync(outputFile, wrapper);
|
|
222
|
+
fs.chmodSync(outputFile, 0o755);
|
|
223
|
+
|
|
224
|
+
console.log(` ā Created: ${outputFile}`);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error(` ā Failed: ${err.message}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Create ZIP archive for Windows
|
|
232
|
+
*/
|
|
233
|
+
function createWindowsBinary(bundleDir) {
|
|
234
|
+
console.log('3. Creating Windows binary...');
|
|
235
|
+
|
|
236
|
+
const outputFile = path.join(binariesDir, `esp32tool-win-x64.zip`);
|
|
237
|
+
|
|
238
|
+
return new Promise((resolve, reject) => {
|
|
239
|
+
const output = fs.createWriteStream(outputFile);
|
|
240
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
241
|
+
|
|
242
|
+
output.on('close', () => {
|
|
243
|
+
console.log(` ā Created: ${outputFile}`);
|
|
244
|
+
resolve();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
output.on('error', reject);
|
|
248
|
+
archive.on('error', reject);
|
|
249
|
+
archive.pipe(output);
|
|
250
|
+
archive.directory(bundleDir, false);
|
|
251
|
+
archive.finalize();
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Main build process
|
|
257
|
+
*/
|
|
258
|
+
async function main() {
|
|
259
|
+
try {
|
|
260
|
+
// Create bundle
|
|
261
|
+
const bundleDir = createBundle();
|
|
262
|
+
|
|
263
|
+
// Create launchers
|
|
264
|
+
createUnixLauncher(bundleDir);
|
|
265
|
+
createWindowsLauncher(bundleDir);
|
|
266
|
+
|
|
267
|
+
// Create binaries for each platform
|
|
268
|
+
createUnixBinary(bundleDir, 'macos');
|
|
269
|
+
createUnixBinary(bundleDir, 'linux-x64');
|
|
270
|
+
await createWindowsBinary(bundleDir);
|
|
271
|
+
|
|
272
|
+
// Clean up
|
|
273
|
+
fs.rmSync(bundleDir, { recursive: true });
|
|
274
|
+
|
|
275
|
+
console.log('\nā All binaries created!\n');
|
|
276
|
+
console.log('Files in ./binaries/:');
|
|
277
|
+
fs.readdirSync(binariesDir).forEach(file => {
|
|
278
|
+
const stats = fs.statSync(path.join(binariesDir, file));
|
|
279
|
+
const sizeMB = (stats.size / 1024 / 1024).toFixed(2);
|
|
280
|
+
console.log(` - ${file} (${sizeMB} MB)`);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
console.log('\nUsage:');
|
|
284
|
+
console.log(' macOS: ./binaries/esp32tool-macos list-ports');
|
|
285
|
+
console.log(' Linux: ./binaries/esp32tool-linux-x64 list-ports');
|
|
286
|
+
console.log(' Windows: Extract zip, then: esp32tool.bat list-ports');
|
|
287
|
+
console.log(`\nNote: Requires Node.js ${NODE_VERSION}+ on target system`);
|
|
288
|
+
|
|
289
|
+
} catch (error) {
|
|
290
|
+
console.error('\nError:', error.message);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
main();
|
package/css/light.css
CHANGED
|
@@ -154,3 +154,14 @@ button {
|
|
|
154
154
|
.littlefs-fs-button:hover {
|
|
155
155
|
background-color: #71ae1e;
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
/* ESP8266 Info Box - Light Mode */
|
|
159
|
+
.esp8266-info .info-box {
|
|
160
|
+
background-color: #f8f9fa;
|
|
161
|
+
border-color: #00a7e9;
|
|
162
|
+
color: #333;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.esp8266-info .info-box strong {
|
|
166
|
+
color: #00a7e9;
|
|
167
|
+
}
|