esp32tool 1.6.4 → 1.6.6
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/console.d.ts +4 -0
- package/dist/console.js +54 -2
- package/dist/util/console-color.d.ts +10 -1
- package/dist/util/console-color.js +369 -30
- package/dist/util/line-break-transformer.js +18 -4
- package/dist/util/timestamp-transformer.d.ts +5 -0
- package/dist/util/timestamp-transformer.js +37 -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/js/console.js +52 -3
- package/js/util/console-color.js +513 -211
- package/js/util/line-break-transformer.js +29 -17
- package/js/util/timestamp-transformer.js +37 -0
- package/package.cli.json +1 -1
- package/package.json +13 -12
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/console.ts +57 -2
- package/src/util/console-color.ts +373 -32
- package/src/util/line-break-transformer.ts +18 -4
- package/src/util/timestamp-transformer.ts +45 -0
- package/sw.js +1 -1
- package/tsconfig.util.json +9 -0
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
export class LineBreakTransformer {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
constructor() {
|
|
3
|
+
this.chunks = "";
|
|
4
|
+
}
|
|
5
|
+
transform(chunk, controller) {
|
|
6
|
+
// Append new chunks to existing chunks.
|
|
7
|
+
this.chunks += chunk;
|
|
8
|
+
// Split on \r\n, lone \r, or lone \n — capturing the separator so we can
|
|
9
|
+
// distinguish a lone \r (overwrite intent) from a normal newline.
|
|
10
|
+
const re = /\r\n|\r|\n/g;
|
|
11
|
+
let lastIndex = 0;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = re.exec(this.chunks)) !== null) {
|
|
14
|
+
// If this is a lone \r at the very end of the buffer, leave it so it can
|
|
15
|
+
// be combined with a possible following \n in the next chunk.
|
|
16
|
+
if (match[0] === "\r" && match.index === this.chunks.length - 1) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const line = this.chunks.substring(lastIndex, match.index);
|
|
20
|
+
// Emit with \r suffix only for lone \r (overwrite), \n for everything else.
|
|
21
|
+
const suffix = match[0] === "\r" ? "\r" : "\n";
|
|
22
|
+
controller.enqueue(line + suffix);
|
|
23
|
+
lastIndex = re.lastIndex;
|
|
24
|
+
}
|
|
25
|
+
this.chunks = this.chunks.substring(lastIndex);
|
|
26
|
+
}
|
|
27
|
+
flush(controller) {
|
|
28
|
+
// When the stream is closed, flush any remaining chunks out.
|
|
29
|
+
controller.enqueue(this.chunks);
|
|
30
|
+
}
|
|
19
31
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Matches lines that already carry a wall-clock timestamp so we don't add a
|
|
2
|
+
// redundant one. Only real wall-clock formats are matched — tick-based
|
|
3
|
+
// formats like FreeRTOS "(12345)" or ESP-IDF "I (15) boot:" are NOT matched
|
|
4
|
+
// because they don't carry time-of-day information
|
|
5
|
+
// Covered formats:
|
|
6
|
+
// [HH:MM:SS] wall-clock bracket
|
|
7
|
+
// [HH:MM:SS.mmm] wall-clock bracket with millis
|
|
8
|
+
// HH:MM:SS.mmm plain wall-clock
|
|
9
|
+
const DEVICE_TIMESTAMP_RE = /^\s*(?:\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\]|(?:\d{2}:){2}\d{2}\.\d)/;
|
|
10
|
+
export class TimestampTransformer {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.deviceHasTimestamps = false;
|
|
13
|
+
}
|
|
14
|
+
transform(chunk, controller) {
|
|
15
|
+
// Pass through pure newline / empty sentinel unchanged so that
|
|
16
|
+
// carriage-return overwrite logic in console-color.ts still works.
|
|
17
|
+
if (chunk === "" || chunk === "\n" || chunk === "\r") {
|
|
18
|
+
controller.enqueue(chunk);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!this.deviceHasTimestamps && DEVICE_TIMESTAMP_RE.test(chunk)) {
|
|
22
|
+
this.deviceHasTimestamps = true;
|
|
23
|
+
}
|
|
24
|
+
if (this.deviceHasTimestamps) {
|
|
25
|
+
controller.enqueue(chunk);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const date = new Date();
|
|
29
|
+
const h = date.getHours().toString().padStart(2, "0");
|
|
30
|
+
const m = date.getMinutes().toString().padStart(2, "0");
|
|
31
|
+
const s = date.getSeconds().toString().padStart(2, "0");
|
|
32
|
+
controller.enqueue(`[${h}:${m}:${s}] ${chunk}`);
|
|
33
|
+
}
|
|
34
|
+
reset() {
|
|
35
|
+
this.deviceHasTimestamps = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
package/package.cli.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esp32tool",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.6",
|
|
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",
|
|
@@ -20,14 +20,15 @@
|
|
|
20
20
|
"node": ">=25.4.0"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"prebuild": "node -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('js/modules',{recursive:true,force:true}); fs.mkdirSync('js/modules',{recursive:true});\"",
|
|
24
|
-
"build": "npm run prebuild && node update-sw-version.cjs && tsc --skipLibCheck && rollup -c && node -e \"const fs=require('fs'); fs.readdirSync('dist/web').filter(f=>f.endsWith('.js')).forEach(f=>fs.copyFileSync('dist/web/'+f,'js/modules/'+f)); fs.renameSync('js/modules/index.js','js/modules/esptool.js');\" && node fix-cli-imports.cjs",
|
|
23
|
+
"prebuild": "node -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('js/modules',{recursive:true,force:true}); fs.rmSync('js/util',{recursive:true,force:true}); fs.mkdirSync('js/modules',{recursive:true}); fs.mkdirSync('js/util',{recursive:true});\"",
|
|
24
|
+
"build": "npm run prebuild && node update-sw-version.cjs && tsc --skipLibCheck && tsc -p tsconfig.util.json && rollup -c && node -e \"const fs=require('fs'); fs.readdirSync('dist/web').filter(f=>f.endsWith('.js')).forEach(f=>fs.copyFileSync('dist/web/'+f,'js/modules/'+f)); fs.renameSync('js/modules/index.js','js/modules/esptool.js');\" && node fix-cli-imports.cjs",
|
|
25
25
|
"build:binary": "node build-single-binary.cjs",
|
|
26
26
|
"build:cli-electron": "node build-electron-cli.cjs",
|
|
27
27
|
"format": "npm exec -- prettier --write src",
|
|
28
28
|
"dev:clean": "node -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true});\"",
|
|
29
29
|
"dev:tsc-once": "tsc",
|
|
30
30
|
"dev:tsc": "tsc --watch",
|
|
31
|
+
"dev:tsc-util": "tsc -p tsconfig.util.json --watch",
|
|
31
32
|
"dev:rollup": "rollup -c --watch",
|
|
32
33
|
"dev:serve": "serve -p 5004",
|
|
33
34
|
"develop": "npm run dev:clean && npm run dev:tsc-once && npm-run-all --parallel dev:serve dev:tsc dev:rollup",
|
|
@@ -49,27 +50,27 @@
|
|
|
49
50
|
"@electron-forge/maker-zip": "^7.11.1",
|
|
50
51
|
"@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
|
|
51
52
|
"@electron/fuses": "^2.1.1",
|
|
52
|
-
"@eslint/js": "^9.39.
|
|
53
|
+
"@eslint/js": "^9.39.4",
|
|
53
54
|
"@rollup/plugin-json": "^6.1.0",
|
|
54
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
55
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
55
56
|
"@rollup/plugin-terser": "^1.0.0",
|
|
56
57
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
57
|
-
"@types/node": "^25.
|
|
58
|
+
"@types/node": "^25.5.2",
|
|
58
59
|
"@types/pako": "^2.0.4",
|
|
59
60
|
"@types/serialport": "^10.2.0",
|
|
60
|
-
"@types/w3c-web-serial": "^1.0.
|
|
61
|
+
"@types/w3c-web-serial": "^1.0.8",
|
|
61
62
|
"archiver": "^7.0.1",
|
|
62
|
-
"electron": "^41.
|
|
63
|
+
"electron": "^41.3.0",
|
|
63
64
|
"electron-squirrel-startup": "^1.0.1",
|
|
64
|
-
"eslint": "^10.
|
|
65
|
+
"eslint": "^10.2.1",
|
|
65
66
|
"eslint-config-prettier": "^10.1.8",
|
|
66
67
|
"eslint-plugin-prettier": "^5.5.5",
|
|
67
68
|
"npm-run-all": "^4.1.5",
|
|
68
|
-
"prettier": "^3.8.
|
|
69
|
-
"rollup": "^4.60.
|
|
69
|
+
"prettier": "^3.8.3",
|
|
70
|
+
"rollup": "^4.60.2",
|
|
70
71
|
"serve": "^14.2.6",
|
|
71
72
|
"typescript": "^5.9.3",
|
|
72
|
-
"typescript-eslint": "^8.
|
|
73
|
+
"typescript-eslint": "^8.59.0"
|
|
73
74
|
},
|
|
74
75
|
"dependencies": {
|
|
75
76
|
"pako": "^2.1.0",
|
package/screenshots/desktop.png
CHANGED
|
Binary file
|
package/screenshots/mobile.png
CHANGED
|
Binary file
|
package/src/console.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ColoredConsole, coloredConsoleStyles } from "./util/console-color.js";
|
|
2
2
|
import { LineBreakTransformer } from "./util/line-break-transformer.js";
|
|
3
|
+
import { TimestampTransformer } from "./util/timestamp-transformer.js";
|
|
3
4
|
|
|
4
5
|
export class ESP32ToolConsole {
|
|
5
6
|
private port: SerialPort;
|
|
@@ -8,6 +9,11 @@ export class ESP32ToolConsole {
|
|
|
8
9
|
private containerElement: HTMLElement;
|
|
9
10
|
private allowInput: boolean;
|
|
10
11
|
|
|
12
|
+
// Command history buffer
|
|
13
|
+
private commandHistory: string[] = [];
|
|
14
|
+
private historyIndex: number = -1;
|
|
15
|
+
private currentInput: string = "";
|
|
16
|
+
|
|
11
17
|
constructor(
|
|
12
18
|
port: SerialPort,
|
|
13
19
|
containerElement: HTMLElement,
|
|
@@ -163,6 +169,17 @@ export class ESP32ToolConsole {
|
|
|
163
169
|
ev.preventDefault();
|
|
164
170
|
ev.stopPropagation();
|
|
165
171
|
this._sendCommand();
|
|
172
|
+
} else if (ev.key === "ArrowUp") {
|
|
173
|
+
ev.preventDefault();
|
|
174
|
+
this._navigateHistory(1, input);
|
|
175
|
+
} else if (ev.key === "ArrowDown") {
|
|
176
|
+
ev.preventDefault();
|
|
177
|
+
this._navigateHistory(-1, input);
|
|
178
|
+
} else {
|
|
179
|
+
// User is editing — reset history navigation to live input
|
|
180
|
+
if (this.historyIndex !== -1) {
|
|
181
|
+
this.historyIndex = -1;
|
|
182
|
+
}
|
|
166
183
|
}
|
|
167
184
|
});
|
|
168
185
|
}
|
|
@@ -218,11 +235,11 @@ export class ESP32ToolConsole {
|
|
|
218
235
|
},
|
|
219
236
|
)
|
|
220
237
|
.pipeThrough(new TransformStream(new LineBreakTransformer()))
|
|
238
|
+
.pipeThrough(new TransformStream(new TimestampTransformer()))
|
|
221
239
|
.pipeTo(
|
|
222
240
|
new WritableStream({
|
|
223
241
|
write: (chunk) => {
|
|
224
|
-
|
|
225
|
-
this.console!.addLine(cleaned);
|
|
242
|
+
this.console!.addLine(chunk);
|
|
226
243
|
},
|
|
227
244
|
}),
|
|
228
245
|
);
|
|
@@ -241,11 +258,49 @@ export class ESP32ToolConsole {
|
|
|
241
258
|
}
|
|
242
259
|
}
|
|
243
260
|
|
|
261
|
+
private _navigateHistory(direction: 1 | -1, input: HTMLInputElement) {
|
|
262
|
+
if (this.commandHistory.length === 0) return;
|
|
263
|
+
|
|
264
|
+
// Save current unsent input before navigating away
|
|
265
|
+
if (this.historyIndex === -1) {
|
|
266
|
+
this.currentInput = input.value;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const nextIndex = this.historyIndex + direction;
|
|
270
|
+
|
|
271
|
+
if (nextIndex < 0) {
|
|
272
|
+
// Back to unsent draft
|
|
273
|
+
this.historyIndex = -1;
|
|
274
|
+
input.value = this.currentInput;
|
|
275
|
+
} else if (nextIndex < this.commandHistory.length) {
|
|
276
|
+
this.historyIndex = nextIndex;
|
|
277
|
+
input.value = this.commandHistory[this.historyIndex];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Move cursor to end
|
|
281
|
+
const len = input.value.length;
|
|
282
|
+
input.setSelectionRange(len, len);
|
|
283
|
+
}
|
|
284
|
+
|
|
244
285
|
private async _sendCommand() {
|
|
245
286
|
const input = this.containerElement.querySelector<HTMLInputElement>(
|
|
246
287
|
".esp32tool-console-input",
|
|
247
288
|
)!;
|
|
248
289
|
const command = input.value;
|
|
290
|
+
|
|
291
|
+
if (command.trim() !== "") {
|
|
292
|
+
// Avoid consecutive duplicates, cap at 100
|
|
293
|
+
if (this.commandHistory[0] !== command) {
|
|
294
|
+
this.commandHistory.unshift(command);
|
|
295
|
+
if (this.commandHistory.length > 100) {
|
|
296
|
+
this.commandHistory.pop();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Reset history navigation state
|
|
301
|
+
this.historyIndex = -1;
|
|
302
|
+
this.currentInput = "";
|
|
303
|
+
|
|
249
304
|
if (!this.port.writable) {
|
|
250
305
|
this.console!.addLine("Terminal disconnected: port not writable");
|
|
251
306
|
return;
|