esp32tool 1.6.3 → 1.6.5
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/README.md +2 -0
- package/apple-touch-icon.png +0 -0
- package/css/style.css +23 -0
- package/dist/cli.js +13 -2
- package/dist/console.d.ts +4 -0
- package/dist/console.js +54 -2
- package/dist/esp_loader.js +12 -0
- package/dist/util/console-color.d.ts +6 -1
- package/dist/util/console-color.js +82 -21
- package/dist/util/line-break-transformer.js +18 -4
- package/dist/util/timestamp-transformer.d.ts +5 -0
- package/dist/util/timestamp-transformer.js +39 -0
- package/dist/web/index.js +1 -1
- 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 +1 -0
- package/js/console.js +52 -3
- package/js/modules/esptool.js +1 -1
- package/js/script.js +3 -0
- package/js/util/console-color.js +236 -185
- package/js/util/line-break-transformer.js +29 -17
- package/js/util/timestamp-transformer.js +39 -0
- package/package.cli.json +2 -2
- package/package.json +19 -14
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/cli.ts +13 -2
- package/src/console.ts +57 -2
- package/src/esp_loader.ts +24 -0
- package/src/util/console-color.ts +89 -23
- package/src/util/line-break-transformer.ts +18 -4
- package/src/util/timestamp-transformer.ts +47 -0
- package/sw.js +1 -1
- package/tsconfig.util.json +9 -0
package/js/script.js
CHANGED
|
@@ -199,6 +199,7 @@ const butReadFlash = document.getElementById("butReadFlash");
|
|
|
199
199
|
const readOffset = document.getElementById("readOffset");
|
|
200
200
|
const readSize = document.getElementById("readSize");
|
|
201
201
|
const readProgress = document.getElementById("readProgress");
|
|
202
|
+
const eraseProgress = document.getElementById("eraseProgress");
|
|
202
203
|
const butReadPartitions = document.getElementById("butReadPartitions");
|
|
203
204
|
const butDetectFS = document.getElementById("butDetectFS");
|
|
204
205
|
const butOpenFSManager = document.getElementById("butOpenFSManager");
|
|
@@ -1601,6 +1602,7 @@ async function clickErase() {
|
|
|
1601
1602
|
baudRateSelect.disabled = true;
|
|
1602
1603
|
butErase.disabled = true;
|
|
1603
1604
|
butProgram.disabled = true;
|
|
1605
|
+
eraseProgress.classList.remove("hidden");
|
|
1604
1606
|
try {
|
|
1605
1607
|
logMsg("Erasing flash memory. Please wait...");
|
|
1606
1608
|
let stamp = Date.now();
|
|
@@ -1609,6 +1611,7 @@ async function clickErase() {
|
|
|
1609
1611
|
} catch (e) {
|
|
1610
1612
|
errorMsg(e);
|
|
1611
1613
|
} finally {
|
|
1614
|
+
eraseProgress.classList.add("hidden");
|
|
1612
1615
|
butErase.disabled = false;
|
|
1613
1616
|
baudRateSelect.disabled = false;
|
|
1614
1617
|
butProgram.disabled = getValidFiles().length == 0;
|
package/js/util/console-color.js
CHANGED
|
@@ -1,194 +1,235 @@
|
|
|
1
1
|
export class ColoredConsole {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
22
|
-
const re = /(?:\x1B|\\x1B)(?:\[(.*?)[@-~]|\].*?(?:\x07|\x1B\\))/g;
|
|
23
|
-
let i = 0;
|
|
24
|
-
|
|
25
|
-
if (this.state.carriageReturn) {
|
|
26
|
-
if (line !== "\n") {
|
|
27
|
-
// don't remove if \r\n
|
|
28
|
-
if (this.targetElement.lastChild) {
|
|
29
|
-
this.targetElement.removeChild(this.targetElement.lastChild);
|
|
2
|
+
constructor(targetElement) {
|
|
3
|
+
this.targetElement = targetElement;
|
|
4
|
+
this.state = {
|
|
5
|
+
bold: false,
|
|
6
|
+
italic: false,
|
|
7
|
+
underline: false,
|
|
8
|
+
strikethrough: false,
|
|
9
|
+
foregroundColor: null,
|
|
10
|
+
backgroundColor: null,
|
|
11
|
+
carriageReturn: false,
|
|
12
|
+
lines: [],
|
|
13
|
+
secret: false,
|
|
14
|
+
blink: false,
|
|
15
|
+
rapidBlink: false,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
logs() {
|
|
19
|
+
if (this.state.lines.length > 0) {
|
|
20
|
+
this.processLines();
|
|
30
21
|
}
|
|
31
|
-
|
|
32
|
-
this.state.carriageReturn = false;
|
|
22
|
+
return this.targetElement.innerText;
|
|
33
23
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
processLine(line) {
|
|
25
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
26
|
+
// eslint-disable-next-line no-control-regex
|
|
27
|
+
const re = /(?:\x1B|\\x1B)(?:\[(.*?)[@-~]|\].*?(?:\x07|\x1B\\))/g;
|
|
28
|
+
let i = 0;
|
|
29
|
+
const lineSpan = document.createElement("span");
|
|
30
|
+
lineSpan.classList.add("line");
|
|
31
|
+
const addSpan = (content) => {
|
|
32
|
+
if (content === "")
|
|
33
|
+
return;
|
|
34
|
+
const span = document.createElement("span");
|
|
35
|
+
if (this.state.bold)
|
|
36
|
+
span.classList.add("log-bold");
|
|
37
|
+
if (this.state.italic)
|
|
38
|
+
span.classList.add("log-italic");
|
|
39
|
+
if (this.state.underline)
|
|
40
|
+
span.classList.add("log-underline");
|
|
41
|
+
if (this.state.strikethrough)
|
|
42
|
+
span.classList.add("log-strikethrough");
|
|
43
|
+
if (this.state.secret)
|
|
44
|
+
span.classList.add("log-secret");
|
|
45
|
+
if (this.state.blink)
|
|
46
|
+
span.classList.add("log-blink");
|
|
47
|
+
if (this.state.rapidBlink)
|
|
48
|
+
span.classList.add("log-rapid-blink");
|
|
49
|
+
if (this.state.foregroundColor !== null)
|
|
50
|
+
span.classList.add(`log-fg-${this.state.foregroundColor}`);
|
|
51
|
+
if (this.state.backgroundColor !== null)
|
|
52
|
+
span.classList.add(`log-bg-${this.state.backgroundColor}`);
|
|
53
|
+
span.appendChild(document.createTextNode(content));
|
|
54
|
+
lineSpan.appendChild(span);
|
|
55
|
+
if (this.state.secret) {
|
|
56
|
+
const redacted = document.createElement("span");
|
|
57
|
+
redacted.classList.add("log-secret-redacted");
|
|
58
|
+
redacted.appendChild(document.createTextNode("[redacted]"));
|
|
59
|
+
lineSpan.appendChild(redacted);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
while (true) {
|
|
63
|
+
const match = re.exec(line);
|
|
64
|
+
if (match === null)
|
|
65
|
+
break;
|
|
66
|
+
const j = match.index;
|
|
67
|
+
addSpan(line.substring(i, j));
|
|
68
|
+
i = j + match[0].length;
|
|
69
|
+
if (match[1] === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
for (const colorCode of match[1].split(";")) {
|
|
72
|
+
switch (parseInt(colorCode)) {
|
|
73
|
+
case 0:
|
|
74
|
+
// reset
|
|
75
|
+
this.state.bold = false;
|
|
76
|
+
this.state.italic = false;
|
|
77
|
+
this.state.underline = false;
|
|
78
|
+
this.state.strikethrough = false;
|
|
79
|
+
this.state.foregroundColor = null;
|
|
80
|
+
this.state.backgroundColor = null;
|
|
81
|
+
this.state.secret = false;
|
|
82
|
+
this.state.blink = false;
|
|
83
|
+
this.state.rapidBlink = false;
|
|
84
|
+
break;
|
|
85
|
+
case 1:
|
|
86
|
+
this.state.bold = true;
|
|
87
|
+
break;
|
|
88
|
+
case 3:
|
|
89
|
+
this.state.italic = true;
|
|
90
|
+
break;
|
|
91
|
+
case 4:
|
|
92
|
+
this.state.underline = true;
|
|
93
|
+
break;
|
|
94
|
+
case 5:
|
|
95
|
+
this.state.blink = true;
|
|
96
|
+
this.state.rapidBlink = false;
|
|
97
|
+
break;
|
|
98
|
+
case 6:
|
|
99
|
+
this.state.rapidBlink = true;
|
|
100
|
+
this.state.blink = false;
|
|
101
|
+
break;
|
|
102
|
+
case 8:
|
|
103
|
+
this.state.secret = true;
|
|
104
|
+
break;
|
|
105
|
+
case 9:
|
|
106
|
+
this.state.strikethrough = true;
|
|
107
|
+
break;
|
|
108
|
+
case 22:
|
|
109
|
+
this.state.bold = false;
|
|
110
|
+
break;
|
|
111
|
+
case 23:
|
|
112
|
+
this.state.italic = false;
|
|
113
|
+
break;
|
|
114
|
+
case 24:
|
|
115
|
+
this.state.underline = false;
|
|
116
|
+
break;
|
|
117
|
+
case 25:
|
|
118
|
+
this.state.blink = false;
|
|
119
|
+
this.state.rapidBlink = false;
|
|
120
|
+
break;
|
|
121
|
+
case 28:
|
|
122
|
+
this.state.secret = false;
|
|
123
|
+
break;
|
|
124
|
+
case 29:
|
|
125
|
+
this.state.strikethrough = false;
|
|
126
|
+
break;
|
|
127
|
+
case 30:
|
|
128
|
+
this.state.foregroundColor = "black";
|
|
129
|
+
break;
|
|
130
|
+
case 31:
|
|
131
|
+
this.state.foregroundColor = "red";
|
|
132
|
+
break;
|
|
133
|
+
case 32:
|
|
134
|
+
this.state.foregroundColor = "green";
|
|
135
|
+
break;
|
|
136
|
+
case 33:
|
|
137
|
+
this.state.foregroundColor = "yellow";
|
|
138
|
+
break;
|
|
139
|
+
case 34:
|
|
140
|
+
this.state.foregroundColor = "blue";
|
|
141
|
+
break;
|
|
142
|
+
case 35:
|
|
143
|
+
this.state.foregroundColor = "magenta";
|
|
144
|
+
break;
|
|
145
|
+
case 36:
|
|
146
|
+
this.state.foregroundColor = "cyan";
|
|
147
|
+
break;
|
|
148
|
+
case 37:
|
|
149
|
+
this.state.foregroundColor = "white";
|
|
150
|
+
break;
|
|
151
|
+
case 39:
|
|
152
|
+
this.state.foregroundColor = null;
|
|
153
|
+
break;
|
|
154
|
+
case 40:
|
|
155
|
+
this.state.backgroundColor = "black";
|
|
156
|
+
break;
|
|
157
|
+
case 41:
|
|
158
|
+
this.state.backgroundColor = "red";
|
|
159
|
+
break;
|
|
160
|
+
case 42:
|
|
161
|
+
this.state.backgroundColor = "green";
|
|
162
|
+
break;
|
|
163
|
+
case 43:
|
|
164
|
+
this.state.backgroundColor = "yellow";
|
|
165
|
+
break;
|
|
166
|
+
case 44:
|
|
167
|
+
this.state.backgroundColor = "blue";
|
|
168
|
+
break;
|
|
169
|
+
case 45:
|
|
170
|
+
this.state.backgroundColor = "magenta";
|
|
171
|
+
break;
|
|
172
|
+
case 46:
|
|
173
|
+
this.state.backgroundColor = "cyan";
|
|
174
|
+
break;
|
|
175
|
+
case 47:
|
|
176
|
+
this.state.backgroundColor = "white";
|
|
177
|
+
break;
|
|
178
|
+
case 49:
|
|
179
|
+
this.state.backgroundColor = null;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
addSpan(line.substring(i));
|
|
185
|
+
return lineSpan;
|
|
37
186
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (match[1] === undefined) continue;
|
|
76
|
-
|
|
77
|
-
for (const colorCode of match[1].split(";")) {
|
|
78
|
-
switch (parseInt(colorCode)) {
|
|
79
|
-
case 0:
|
|
80
|
-
// reset
|
|
81
|
-
this.state.bold = false;
|
|
82
|
-
this.state.italic = false;
|
|
83
|
-
this.state.underline = false;
|
|
84
|
-
this.state.strikethrough = false;
|
|
85
|
-
this.state.foregroundColor = null;
|
|
86
|
-
this.state.backgroundColor = null;
|
|
87
|
-
this.state.secret = false;
|
|
88
|
-
break;
|
|
89
|
-
case 1:
|
|
90
|
-
this.state.bold = true;
|
|
91
|
-
break;
|
|
92
|
-
case 3:
|
|
93
|
-
this.state.italic = true;
|
|
94
|
-
break;
|
|
95
|
-
case 4:
|
|
96
|
-
this.state.underline = true;
|
|
97
|
-
break;
|
|
98
|
-
case 5:
|
|
99
|
-
this.state.secret = true;
|
|
100
|
-
break;
|
|
101
|
-
case 6:
|
|
102
|
-
this.state.secret = false;
|
|
103
|
-
break;
|
|
104
|
-
case 9:
|
|
105
|
-
this.state.strikethrough = true;
|
|
106
|
-
break;
|
|
107
|
-
case 22:
|
|
108
|
-
this.state.bold = false;
|
|
109
|
-
break;
|
|
110
|
-
case 23:
|
|
111
|
-
this.state.italic = false;
|
|
112
|
-
break;
|
|
113
|
-
case 24:
|
|
114
|
-
this.state.underline = false;
|
|
115
|
-
break;
|
|
116
|
-
case 29:
|
|
117
|
-
this.state.strikethrough = false;
|
|
118
|
-
break;
|
|
119
|
-
case 30:
|
|
120
|
-
this.state.foregroundColor = "black";
|
|
121
|
-
break;
|
|
122
|
-
case 31:
|
|
123
|
-
this.state.foregroundColor = "red";
|
|
124
|
-
break;
|
|
125
|
-
case 32:
|
|
126
|
-
this.state.foregroundColor = "green";
|
|
127
|
-
break;
|
|
128
|
-
case 33:
|
|
129
|
-
this.state.foregroundColor = "yellow";
|
|
130
|
-
break;
|
|
131
|
-
case 34:
|
|
132
|
-
this.state.foregroundColor = "blue";
|
|
133
|
-
break;
|
|
134
|
-
case 35:
|
|
135
|
-
this.state.foregroundColor = "magenta";
|
|
136
|
-
break;
|
|
137
|
-
case 36:
|
|
138
|
-
this.state.foregroundColor = "cyan";
|
|
139
|
-
break;
|
|
140
|
-
case 37:
|
|
141
|
-
this.state.foregroundColor = "white";
|
|
142
|
-
break;
|
|
143
|
-
case 39:
|
|
144
|
-
this.state.foregroundColor = null;
|
|
145
|
-
break;
|
|
146
|
-
case 41:
|
|
147
|
-
this.state.backgroundColor = "red";
|
|
148
|
-
break;
|
|
149
|
-
case 42:
|
|
150
|
-
this.state.backgroundColor = "green";
|
|
151
|
-
break;
|
|
152
|
-
case 43:
|
|
153
|
-
this.state.backgroundColor = "yellow";
|
|
154
|
-
break;
|
|
155
|
-
case 44:
|
|
156
|
-
this.state.backgroundColor = "blue";
|
|
157
|
-
break;
|
|
158
|
-
case 45:
|
|
159
|
-
this.state.backgroundColor = "magenta";
|
|
160
|
-
break;
|
|
161
|
-
case 46:
|
|
162
|
-
this.state.backgroundColor = "cyan";
|
|
163
|
-
break;
|
|
164
|
-
case 47:
|
|
165
|
-
this.state.backgroundColor = "white";
|
|
166
|
-
break;
|
|
167
|
-
case 40:
|
|
168
|
-
this.state.backgroundColor = "black";
|
|
169
|
-
break;
|
|
170
|
-
case 49:
|
|
171
|
-
this.state.backgroundColor = null;
|
|
172
|
-
break;
|
|
187
|
+
processLines() {
|
|
188
|
+
const atBottom = this.targetElement.scrollTop >
|
|
189
|
+
this.targetElement.scrollHeight - this.targetElement.offsetHeight - 50;
|
|
190
|
+
const prevCarriageReturn = this.state.carriageReturn;
|
|
191
|
+
const fragment = document.createDocumentFragment();
|
|
192
|
+
if (this.state.lines.length === 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
for (const line of this.state.lines) {
|
|
196
|
+
// A lone \r is a pure carriage-return signal — update state but don't
|
|
197
|
+
// create a DOM node for it (it has no renderable content).
|
|
198
|
+
if (line === "\r") {
|
|
199
|
+
this.state.carriageReturn = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (this.state.carriageReturn && line !== "\n") {
|
|
203
|
+
if (fragment.childElementCount) {
|
|
204
|
+
fragment.removeChild(fragment.lastChild);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const hadCarriageReturn = line.endsWith("\r");
|
|
208
|
+
fragment.appendChild(this.processLine(line.replace(/\r/g, "")));
|
|
209
|
+
this.state.carriageReturn = hadCarriageReturn;
|
|
210
|
+
}
|
|
211
|
+
if (prevCarriageReturn &&
|
|
212
|
+
fragment.childElementCount > 0 &&
|
|
213
|
+
this.targetElement.lastChild) {
|
|
214
|
+
this.targetElement.replaceChild(fragment, this.targetElement.lastChild);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this.targetElement.appendChild(fragment);
|
|
218
|
+
}
|
|
219
|
+
this.state.lines = [];
|
|
220
|
+
// Keep scroll at bottom
|
|
221
|
+
if (atBottom) {
|
|
222
|
+
this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
|
173
223
|
}
|
|
174
|
-
}
|
|
175
224
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
addSpan(line.substring(i));
|
|
184
|
-
|
|
185
|
-
// Keep scroll at bottom
|
|
186
|
-
if (atBottom) {
|
|
187
|
-
this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
|
225
|
+
addLine(line) {
|
|
226
|
+
// Processing of lines is deferred for performance reasons
|
|
227
|
+
if (this.state.lines.length === 0) {
|
|
228
|
+
setTimeout(() => this.processLines(), 0);
|
|
229
|
+
}
|
|
230
|
+
this.state.lines.push(line);
|
|
188
231
|
}
|
|
189
|
-
}
|
|
190
232
|
}
|
|
191
|
-
|
|
192
233
|
export const coloredConsoleStyles = `
|
|
193
234
|
.log {
|
|
194
235
|
flex: 1;
|
|
@@ -199,11 +240,10 @@ export const coloredConsoleStyles = `
|
|
|
199
240
|
padding: 16px;
|
|
200
241
|
overflow: auto;
|
|
201
242
|
line-height: 1.45;
|
|
202
|
-
border-radius:
|
|
243
|
+
border-radius: 3px;
|
|
203
244
|
white-space: pre-wrap;
|
|
204
245
|
overflow-wrap: break-word;
|
|
205
246
|
color: #ddd;
|
|
206
|
-
min-height: 0;
|
|
207
247
|
}
|
|
208
248
|
|
|
209
249
|
.log-bold {
|
|
@@ -221,6 +261,17 @@ export const coloredConsoleStyles = `
|
|
|
221
261
|
.log-underline.log-strikethrough {
|
|
222
262
|
text-decoration: underline line-through;
|
|
223
263
|
}
|
|
264
|
+
.log-blink {
|
|
265
|
+
animation: blink 1s step-end infinite;
|
|
266
|
+
}
|
|
267
|
+
.log-rapid-blink {
|
|
268
|
+
animation: blink 0.4s step-end infinite;
|
|
269
|
+
}
|
|
270
|
+
@keyframes blink {
|
|
271
|
+
50% {
|
|
272
|
+
opacity: 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
224
275
|
.log-secret {
|
|
225
276
|
-webkit-user-select: none;
|
|
226
277
|
-moz-user-select: none;
|
|
@@ -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,39 @@
|
|
|
1
|
+
// Matches lines that already carry a wall-clock or tick timestamp so we don't
|
|
2
|
+
// add a redundant one. Intentionally does NOT match bare log-level prefixes
|
|
3
|
+
// like ESPHome's [I][tag:line]: — those have no time information.
|
|
4
|
+
//
|
|
5
|
+
// Covered formats:
|
|
6
|
+
// (123456) FreeRTOS ms-tick e.g. "(12345) "
|
|
7
|
+
// [HH:MM:SS] wall-clock bracket
|
|
8
|
+
// [HH:MM:SS.mmm] wall-clock bracket with millis
|
|
9
|
+
// I (1234) tag: ESP-IDF log level + tick e.g. "I (1234) wifi: ..."
|
|
10
|
+
// HH:MM:SS.mmm plain wall-clock
|
|
11
|
+
const DEVICE_TIMESTAMP_RE = /^\s*(?:\(\d+\)\s|\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\]|[DIWEACV] \(\d+\) \w|(?:\d{2}:){2}\d{2}\.\d)/;
|
|
12
|
+
export class TimestampTransformer {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.deviceHasTimestamps = false;
|
|
15
|
+
}
|
|
16
|
+
transform(chunk, controller) {
|
|
17
|
+
// Pass through pure newline / empty sentinel unchanged so that
|
|
18
|
+
// carriage-return overwrite logic in console-color.ts still works.
|
|
19
|
+
if (chunk === "" || chunk === "\n" || chunk === "\r") {
|
|
20
|
+
controller.enqueue(chunk);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!this.deviceHasTimestamps && DEVICE_TIMESTAMP_RE.test(chunk)) {
|
|
24
|
+
this.deviceHasTimestamps = true;
|
|
25
|
+
}
|
|
26
|
+
if (this.deviceHasTimestamps) {
|
|
27
|
+
controller.enqueue(chunk);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const date = new Date();
|
|
31
|
+
const h = date.getHours().toString().padStart(2, "0");
|
|
32
|
+
const m = date.getMinutes().toString().padStart(2, "0");
|
|
33
|
+
const s = date.getSeconds().toString().padStart(2, "0");
|
|
34
|
+
controller.enqueue(`[${h}:${m}:${s}] ${chunk}`);
|
|
35
|
+
}
|
|
36
|
+
reset() {
|
|
37
|
+
this.deviceHasTimestamps = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.cli.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esp32tool",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "ESP32Tool - Standalone command-line tool (build-time config only)",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"pako": "^2.1.0",
|
|
22
22
|
"tslib": "^2.8.1",
|
|
23
|
-
"usb": "^2.
|
|
23
|
+
"usb": "^2.17.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"electron": "^39.2.5"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esp32tool",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
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",
|
|
@@ -17,17 +17,18 @@
|
|
|
17
17
|
"author": "Johann Obermeier",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
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",
|
|
@@ -48,28 +49,28 @@
|
|
|
48
49
|
"@electron-forge/maker-squirrel": "^7.11.1",
|
|
49
50
|
"@electron-forge/maker-zip": "^7.11.1",
|
|
50
51
|
"@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
|
|
51
|
-
"@electron/fuses": "^2.
|
|
52
|
+
"@electron/fuses": "^2.1.1",
|
|
52
53
|
"@eslint/js": "^9.39.3",
|
|
53
54
|
"@rollup/plugin-json": "^6.1.0",
|
|
54
55
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
55
|
-
"@rollup/plugin-terser": "^0.
|
|
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
61
|
"@types/w3c-web-serial": "^1.0.7",
|
|
61
62
|
"archiver": "^7.0.1",
|
|
62
|
-
"electron": "^
|
|
63
|
+
"electron": "^41.1.0",
|
|
63
64
|
"electron-squirrel-startup": "^1.0.1",
|
|
64
|
-
"eslint": "^10.0
|
|
65
|
+
"eslint": "^10.2.0",
|
|
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
69
|
"prettier": "^3.8.1",
|
|
69
|
-
"rollup": "^4.
|
|
70
|
-
"serve": "^14.2.
|
|
71
|
-
"typescript": "^5.
|
|
72
|
-
"typescript-eslint": "^8.
|
|
70
|
+
"rollup": "^4.60.1",
|
|
71
|
+
"serve": "^14.2.6",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"typescript-eslint": "^8.58.0"
|
|
73
74
|
},
|
|
74
75
|
"dependencies": {
|
|
75
76
|
"pako": "^2.1.0",
|
|
@@ -83,6 +84,10 @@
|
|
|
83
84
|
"serve": {
|
|
84
85
|
"ajv": "^8.18.0"
|
|
85
86
|
},
|
|
86
|
-
"@electron/asar": "^4.0.1"
|
|
87
|
+
"@electron/asar": "^4.0.1",
|
|
88
|
+
"serialize-javascript": "^7.0.3",
|
|
89
|
+
"@tootallnate/once": "^3.0.1",
|
|
90
|
+
"http-proxy-agent": "^7.0.2",
|
|
91
|
+
"make-fetch-happen": "^13.0.1"
|
|
87
92
|
}
|
|
88
93
|
}
|
package/screenshots/desktop.png
CHANGED
|
Binary file
|
package/screenshots/mobile.png
CHANGED
|
Binary file
|