esp32tool 1.6.4 → 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/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 +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/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 +236 -212
- package/js/util/line-break-transformer.js +29 -17
- package/js/util/timestamp-transformer.js +39 -0
- package/package.cli.json +1 -1
- package/package.json +7 -6
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/console.ts +57 -2
- 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/util/console-color.js
CHANGED
|
@@ -1,210 +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
|
-
|
|
22
|
-
addLine(line) {
|
|
23
|
-
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
24
|
-
const re = /(?:\x1B|\\x1B)(?:\[(.*?)[@-~]|\].*?(?:\x07|\x1B\\))/g;
|
|
25
|
-
let i = 0;
|
|
26
|
-
|
|
27
|
-
if (this.state.carriageReturn) {
|
|
28
|
-
if (line !== "\n") {
|
|
29
|
-
// don't remove if \r\n
|
|
30
|
-
if (this.targetElement.lastChild) {
|
|
31
|
-
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();
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
this.state.carriageReturn = false;
|
|
22
|
+
return this.targetElement.innerText;
|
|
35
23
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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;
|
|
39
186
|
}
|
|
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
|
-
|
|
76
|
-
addSpan(line.substring(i, j));
|
|
77
|
-
i = j + match[0].length;
|
|
78
|
-
|
|
79
|
-
if (match[1] === undefined) continue;
|
|
80
|
-
|
|
81
|
-
for (const colorCode of match[1].split(";")) {
|
|
82
|
-
switch (parseInt(colorCode)) {
|
|
83
|
-
case 0:
|
|
84
|
-
// reset
|
|
85
|
-
this.state.bold = false;
|
|
86
|
-
this.state.italic = false;
|
|
87
|
-
this.state.underline = false;
|
|
88
|
-
this.state.strikethrough = false;
|
|
89
|
-
this.state.foregroundColor = null;
|
|
90
|
-
this.state.backgroundColor = null;
|
|
91
|
-
this.state.secret = false;
|
|
92
|
-
this.state.blink = false;
|
|
93
|
-
this.state.rapidBlink = false;
|
|
94
|
-
break;
|
|
95
|
-
case 1:
|
|
96
|
-
this.state.bold = true;
|
|
97
|
-
break;
|
|
98
|
-
case 3:
|
|
99
|
-
this.state.italic = true;
|
|
100
|
-
break;
|
|
101
|
-
case 4:
|
|
102
|
-
this.state.underline = true;
|
|
103
|
-
break;
|
|
104
|
-
case 5:
|
|
105
|
-
this.state.blink = true;
|
|
106
|
-
break;
|
|
107
|
-
case 6:
|
|
108
|
-
this.state.rapidBlink = true;
|
|
109
|
-
break;
|
|
110
|
-
case 8:
|
|
111
|
-
this.state.secret = true;
|
|
112
|
-
break;
|
|
113
|
-
case 9:
|
|
114
|
-
this.state.strikethrough = true;
|
|
115
|
-
break;
|
|
116
|
-
case 22:
|
|
117
|
-
this.state.bold = false;
|
|
118
|
-
break;
|
|
119
|
-
case 23:
|
|
120
|
-
this.state.italic = false;
|
|
121
|
-
break;
|
|
122
|
-
case 24:
|
|
123
|
-
this.state.underline = false;
|
|
124
|
-
break;
|
|
125
|
-
case 25:
|
|
126
|
-
this.state.blink = false;
|
|
127
|
-
this.state.rapidBlink = false;
|
|
128
|
-
break;
|
|
129
|
-
case 28:
|
|
130
|
-
this.state.secret = false;
|
|
131
|
-
break;
|
|
132
|
-
case 29:
|
|
133
|
-
this.state.strikethrough = false;
|
|
134
|
-
break;
|
|
135
|
-
case 30:
|
|
136
|
-
this.state.foregroundColor = "black";
|
|
137
|
-
break;
|
|
138
|
-
case 31:
|
|
139
|
-
this.state.foregroundColor = "red";
|
|
140
|
-
break;
|
|
141
|
-
case 32:
|
|
142
|
-
this.state.foregroundColor = "green";
|
|
143
|
-
break;
|
|
144
|
-
case 33:
|
|
145
|
-
this.state.foregroundColor = "yellow";
|
|
146
|
-
break;
|
|
147
|
-
case 34:
|
|
148
|
-
this.state.foregroundColor = "blue";
|
|
149
|
-
break;
|
|
150
|
-
case 35:
|
|
151
|
-
this.state.foregroundColor = "magenta";
|
|
152
|
-
break;
|
|
153
|
-
case 36:
|
|
154
|
-
this.state.foregroundColor = "cyan";
|
|
155
|
-
break;
|
|
156
|
-
case 37:
|
|
157
|
-
this.state.foregroundColor = "white";
|
|
158
|
-
break;
|
|
159
|
-
case 39:
|
|
160
|
-
this.state.foregroundColor = null;
|
|
161
|
-
break;
|
|
162
|
-
case 41:
|
|
163
|
-
this.state.backgroundColor = "red";
|
|
164
|
-
break;
|
|
165
|
-
case 42:
|
|
166
|
-
this.state.backgroundColor = "green";
|
|
167
|
-
break;
|
|
168
|
-
case 43:
|
|
169
|
-
this.state.backgroundColor = "yellow";
|
|
170
|
-
break;
|
|
171
|
-
case 44:
|
|
172
|
-
this.state.backgroundColor = "blue";
|
|
173
|
-
break;
|
|
174
|
-
case 45:
|
|
175
|
-
this.state.backgroundColor = "magenta";
|
|
176
|
-
break;
|
|
177
|
-
case 46:
|
|
178
|
-
this.state.backgroundColor = "cyan";
|
|
179
|
-
break;
|
|
180
|
-
case 47:
|
|
181
|
-
this.state.backgroundColor = "white";
|
|
182
|
-
break;
|
|
183
|
-
case 40:
|
|
184
|
-
this.state.backgroundColor = "black";
|
|
185
|
-
break;
|
|
186
|
-
case 49:
|
|
187
|
-
this.state.backgroundColor = null;
|
|
188
|
-
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;
|
|
189
223
|
}
|
|
190
|
-
}
|
|
191
224
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
addSpan(line.substring(i));
|
|
200
|
-
|
|
201
|
-
// Keep scroll at bottom
|
|
202
|
-
if (atBottom) {
|
|
203
|
-
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);
|
|
204
231
|
}
|
|
205
|
-
}
|
|
206
232
|
}
|
|
207
|
-
|
|
208
233
|
export const coloredConsoleStyles = `
|
|
209
234
|
.log {
|
|
210
235
|
flex: 1;
|
|
@@ -215,11 +240,10 @@ export const coloredConsoleStyles = `
|
|
|
215
240
|
padding: 16px;
|
|
216
241
|
overflow: auto;
|
|
217
242
|
line-height: 1.45;
|
|
218
|
-
border-radius:
|
|
243
|
+
border-radius: 3px;
|
|
219
244
|
white-space: pre-wrap;
|
|
220
245
|
overflow-wrap: break-word;
|
|
221
246
|
color: #ddd;
|
|
222
|
-
min-height: 0;
|
|
223
247
|
}
|
|
224
248
|
|
|
225
249
|
.log-bold {
|
|
@@ -237,6 +261,17 @@ export const coloredConsoleStyles = `
|
|
|
237
261
|
.log-underline.log-strikethrough {
|
|
238
262
|
text-decoration: underline line-through;
|
|
239
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
|
+
}
|
|
240
275
|
.log-secret {
|
|
241
276
|
-webkit-user-select: none;
|
|
242
277
|
-moz-user-select: none;
|
|
@@ -248,17 +283,6 @@ export const coloredConsoleStyles = `
|
|
|
248
283
|
width: 1px;
|
|
249
284
|
font-size: 1px;
|
|
250
285
|
}
|
|
251
|
-
.log-blink {
|
|
252
|
-
animation: blink 1s step-start infinite;
|
|
253
|
-
}
|
|
254
|
-
.log-rapid-blink {
|
|
255
|
-
animation: blink 0.3s step-start infinite;
|
|
256
|
-
}
|
|
257
|
-
@keyframes blink {
|
|
258
|
-
50% {
|
|
259
|
-
opacity: 0;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
286
|
.log-fg-black {
|
|
263
287
|
color: rgb(128, 128, 128);
|
|
264
288
|
}
|
|
@@ -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
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",
|
|
@@ -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",
|
|
@@ -54,14 +55,14 @@
|
|
|
54
55
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
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
61
|
"@types/w3c-web-serial": "^1.0.7",
|
|
61
62
|
"archiver": "^7.0.1",
|
|
62
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",
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
"rollup": "^4.60.1",
|
|
70
71
|
"serve": "^14.2.6",
|
|
71
72
|
"typescript": "^5.9.3",
|
|
72
|
-
"typescript-eslint": "^8.
|
|
73
|
+
"typescript-eslint": "^8.58.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
|