esp32tool 1.6.5 → 1.6.7
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/css/style.css +325 -25
- package/dist/util/console-color.d.ts +5 -1
- package/dist/util/console-color.js +293 -15
- package/dist/util/timestamp-transformer.js +29 -8
- package/electron/cli-main.cjs +19 -19
- package/electron/main.cjs +167 -148
- package/electron/preload.js +16 -18
- 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 +21 -12
- package/js/hex-editor.js +216 -163
- package/js/improv.js +59 -21
- package/js/nvs-editor.js +1189 -182
- package/js/script.js +1048 -845
- package/js/util/console-color.js +293 -15
- package/js/util/timestamp-transformer.js +29 -8
- package/js/webusb-serial.js +1075 -950
- package/package.cli.json +2 -2
- package/package.json +15 -16
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/util/console-color.ts +290 -15
- package/src/util/timestamp-transformer.ts +32 -8
- package/sw.js +1 -1
package/js/util/console-color.js
CHANGED
|
@@ -1,3 +1,56 @@
|
|
|
1
|
+
const ANSI_256 = (() => {
|
|
2
|
+
const t = [];
|
|
3
|
+
// Standard colors 0-7
|
|
4
|
+
t[0] = "rgb(0,0,0)";
|
|
5
|
+
t[1] = "rgb(128,0,0)";
|
|
6
|
+
t[2] = "rgb(0,128,0)";
|
|
7
|
+
t[3] = "rgb(128,128,0)";
|
|
8
|
+
t[4] = "rgb(0,0,128)";
|
|
9
|
+
t[5] = "rgb(128,0,128)";
|
|
10
|
+
t[6] = "rgb(0,128,128)";
|
|
11
|
+
t[7] = "rgb(192,192,192)";
|
|
12
|
+
// Bright colors 8-15
|
|
13
|
+
t[8] = "rgb(128,128,128)";
|
|
14
|
+
t[9] = "rgb(255,0,0)";
|
|
15
|
+
t[10] = "rgb(0,255,0)";
|
|
16
|
+
t[11] = "rgb(255,255,0)";
|
|
17
|
+
t[12] = "rgb(99,153,255)";
|
|
18
|
+
t[13] = "rgb(255,0,255)";
|
|
19
|
+
t[14] = "rgb(0,255,255)";
|
|
20
|
+
t[15] = "rgb(255,255,255)";
|
|
21
|
+
// 6x6x6 color cube 16-231
|
|
22
|
+
for (let i = 0; i < 216; i++) {
|
|
23
|
+
const r = Math.floor(i / 36);
|
|
24
|
+
const g = Math.floor((i % 36) / 6);
|
|
25
|
+
const b = i % 6;
|
|
26
|
+
t[16 + i] =
|
|
27
|
+
"rgb(" +
|
|
28
|
+
(r ? r * 40 + 55 : 0) +
|
|
29
|
+
"," +
|
|
30
|
+
(g ? g * 40 + 55 : 0) +
|
|
31
|
+
"," +
|
|
32
|
+
(b ? b * 40 + 55 : 0) +
|
|
33
|
+
")";
|
|
34
|
+
}
|
|
35
|
+
// Grayscale ramp 232-255
|
|
36
|
+
for (let i = 0; i < 24; i++) {
|
|
37
|
+
const v = i * 10 + 8;
|
|
38
|
+
t[232 + i] = "rgb(" + v + "," + v + "," + v + ")";
|
|
39
|
+
}
|
|
40
|
+
return t;
|
|
41
|
+
})();
|
|
42
|
+
// Maps 256-color indices 0–7 to the named CSS class tokens so that
|
|
43
|
+
// \x1b[38;5;1m renders the same red as \x1b[31m.
|
|
44
|
+
const ANSI_NAMED = [
|
|
45
|
+
"black",
|
|
46
|
+
"red",
|
|
47
|
+
"green",
|
|
48
|
+
"yellow",
|
|
49
|
+
"blue",
|
|
50
|
+
"magenta",
|
|
51
|
+
"cyan",
|
|
52
|
+
"white",
|
|
53
|
+
];
|
|
1
54
|
export class ColoredConsole {
|
|
2
55
|
constructor(targetElement) {
|
|
3
56
|
this.targetElement = targetElement;
|
|
@@ -8,6 +61,10 @@ export class ColoredConsole {
|
|
|
8
61
|
strikethrough: false,
|
|
9
62
|
foregroundColor: null,
|
|
10
63
|
backgroundColor: null,
|
|
64
|
+
fgRgb: null,
|
|
65
|
+
bgRgb: null,
|
|
66
|
+
dim: false,
|
|
67
|
+
reverse: false,
|
|
11
68
|
carriageReturn: false,
|
|
12
69
|
lines: [],
|
|
13
70
|
secret: false,
|
|
@@ -24,16 +81,16 @@ export class ColoredConsole {
|
|
|
24
81
|
processLine(line) {
|
|
25
82
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
26
83
|
// eslint-disable-next-line no-control-regex
|
|
27
|
-
const re = /(?:\x1B|\\x1B)(?:\[(.*?)[@-~]|\].*?(?:\x07|\x1B\\))/g;
|
|
84
|
+
const re = /(?:\x1B|\\x1B)(?:\[(.*?)([@-~])|\].*?(?:\x07|\x1B\\))/g;
|
|
28
85
|
let i = 0;
|
|
29
86
|
const lineSpan = document.createElement("span");
|
|
30
87
|
lineSpan.classList.add("line");
|
|
31
88
|
const addSpan = (content) => {
|
|
32
|
-
if (content === "")
|
|
33
|
-
return;
|
|
34
89
|
const span = document.createElement("span");
|
|
35
90
|
if (this.state.bold)
|
|
36
91
|
span.classList.add("log-bold");
|
|
92
|
+
if (this.state.dim)
|
|
93
|
+
span.classList.add("log-dim");
|
|
37
94
|
if (this.state.italic)
|
|
38
95
|
span.classList.add("log-italic");
|
|
39
96
|
if (this.state.underline)
|
|
@@ -46,10 +103,41 @@ export class ColoredConsole {
|
|
|
46
103
|
span.classList.add("log-blink");
|
|
47
104
|
if (this.state.rapidBlink)
|
|
48
105
|
span.classList.add("log-rapid-blink");
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
106
|
+
// Resolve colors with reverse-video support
|
|
107
|
+
let fgRgb = this.state.fgRgb;
|
|
108
|
+
let bgRgb = this.state.bgRgb;
|
|
109
|
+
let fg = this.state.foregroundColor;
|
|
110
|
+
let bg = this.state.backgroundColor;
|
|
111
|
+
if (this.state.reverse) {
|
|
112
|
+
fgRgb = this.state.bgRgb;
|
|
113
|
+
bgRgb = this.state.fgRgb;
|
|
114
|
+
fg = this.state.backgroundColor;
|
|
115
|
+
bg = this.state.foregroundColor;
|
|
116
|
+
// When one side is unset, fill in the terminal defaults so the
|
|
117
|
+
// swap is always visible (fg default=#ddd, bg default=#1c1c1c).
|
|
118
|
+
if (!fgRgb && !fg && !bgRgb && !bg) {
|
|
119
|
+
span.classList.add("log-reverse");
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (!fgRgb && !fg)
|
|
123
|
+
fgRgb = "rgb(28,28,28)";
|
|
124
|
+
if (!bgRgb && !bg)
|
|
125
|
+
bgRgb = "rgb(221,221,221)";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Inline rgb() style takes priority over CSS class
|
|
129
|
+
if (fgRgb) {
|
|
130
|
+
span.style.color = fgRgb;
|
|
131
|
+
}
|
|
132
|
+
else if (fg !== null) {
|
|
133
|
+
span.classList.add(`log-fg-${fg}`);
|
|
134
|
+
}
|
|
135
|
+
if (bgRgb) {
|
|
136
|
+
span.style.backgroundColor = bgRgb;
|
|
137
|
+
}
|
|
138
|
+
else if (bg !== null) {
|
|
139
|
+
span.classList.add(`log-bg-${bg}`);
|
|
140
|
+
}
|
|
53
141
|
span.appendChild(document.createTextNode(content));
|
|
54
142
|
lineSpan.appendChild(span);
|
|
55
143
|
if (this.state.secret) {
|
|
@@ -66,18 +154,40 @@ export class ColoredConsole {
|
|
|
66
154
|
const j = match.index;
|
|
67
155
|
addSpan(line.substring(i, j));
|
|
68
156
|
i = j + match[0].length;
|
|
69
|
-
|
|
157
|
+
// Only process SGR sequences (final byte 'm'); skip cursor, erase, etc.
|
|
158
|
+
if (match[1] === undefined || match[2] !== "m")
|
|
159
|
+
continue;
|
|
160
|
+
const rawCodes = match[1] === "" ? [""] : match[1].split(";");
|
|
161
|
+
const codes = [];
|
|
162
|
+
let invalidSgr = false;
|
|
163
|
+
for (const rawCode of rawCodes) {
|
|
164
|
+
if (rawCode === "") {
|
|
165
|
+
codes.push(0);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!/^\d+$/.test(rawCode)) {
|
|
169
|
+
invalidSgr = true;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
codes.push(Number(rawCode));
|
|
173
|
+
}
|
|
174
|
+
if (invalidSgr)
|
|
70
175
|
continue;
|
|
71
|
-
|
|
72
|
-
|
|
176
|
+
let ci = 0;
|
|
177
|
+
while (ci < codes.length) {
|
|
178
|
+
const code = codes[ci];
|
|
179
|
+
switch (code) {
|
|
73
180
|
case 0:
|
|
74
|
-
// reset
|
|
75
181
|
this.state.bold = false;
|
|
182
|
+
this.state.dim = false;
|
|
76
183
|
this.state.italic = false;
|
|
77
184
|
this.state.underline = false;
|
|
78
185
|
this.state.strikethrough = false;
|
|
79
186
|
this.state.foregroundColor = null;
|
|
80
187
|
this.state.backgroundColor = null;
|
|
188
|
+
this.state.fgRgb = null;
|
|
189
|
+
this.state.bgRgb = null;
|
|
190
|
+
this.state.reverse = false;
|
|
81
191
|
this.state.secret = false;
|
|
82
192
|
this.state.blink = false;
|
|
83
193
|
this.state.rapidBlink = false;
|
|
@@ -85,6 +195,9 @@ export class ColoredConsole {
|
|
|
85
195
|
case 1:
|
|
86
196
|
this.state.bold = true;
|
|
87
197
|
break;
|
|
198
|
+
case 2:
|
|
199
|
+
this.state.dim = true;
|
|
200
|
+
break;
|
|
88
201
|
case 3:
|
|
89
202
|
this.state.italic = true;
|
|
90
203
|
break;
|
|
@@ -99,6 +212,9 @@ export class ColoredConsole {
|
|
|
99
212
|
this.state.rapidBlink = true;
|
|
100
213
|
this.state.blink = false;
|
|
101
214
|
break;
|
|
215
|
+
case 7:
|
|
216
|
+
this.state.reverse = true;
|
|
217
|
+
break;
|
|
102
218
|
case 8:
|
|
103
219
|
this.state.secret = true;
|
|
104
220
|
break;
|
|
@@ -107,6 +223,7 @@ export class ColoredConsole {
|
|
|
107
223
|
break;
|
|
108
224
|
case 22:
|
|
109
225
|
this.state.bold = false;
|
|
226
|
+
this.state.dim = false;
|
|
110
227
|
break;
|
|
111
228
|
case 23:
|
|
112
229
|
this.state.italic = false;
|
|
@@ -118,6 +235,9 @@ export class ColoredConsole {
|
|
|
118
235
|
this.state.blink = false;
|
|
119
236
|
this.state.rapidBlink = false;
|
|
120
237
|
break;
|
|
238
|
+
case 27:
|
|
239
|
+
this.state.reverse = false;
|
|
240
|
+
break;
|
|
121
241
|
case 28:
|
|
122
242
|
this.state.secret = false;
|
|
123
243
|
break;
|
|
@@ -126,59 +246,214 @@ export class ColoredConsole {
|
|
|
126
246
|
break;
|
|
127
247
|
case 30:
|
|
128
248
|
this.state.foregroundColor = "black";
|
|
249
|
+
this.state.fgRgb = null;
|
|
129
250
|
break;
|
|
130
251
|
case 31:
|
|
131
252
|
this.state.foregroundColor = "red";
|
|
253
|
+
this.state.fgRgb = null;
|
|
132
254
|
break;
|
|
133
255
|
case 32:
|
|
134
256
|
this.state.foregroundColor = "green";
|
|
257
|
+
this.state.fgRgb = null;
|
|
135
258
|
break;
|
|
136
259
|
case 33:
|
|
137
260
|
this.state.foregroundColor = "yellow";
|
|
261
|
+
this.state.fgRgb = null;
|
|
138
262
|
break;
|
|
139
263
|
case 34:
|
|
140
264
|
this.state.foregroundColor = "blue";
|
|
265
|
+
this.state.fgRgb = null;
|
|
141
266
|
break;
|
|
142
267
|
case 35:
|
|
143
268
|
this.state.foregroundColor = "magenta";
|
|
269
|
+
this.state.fgRgb = null;
|
|
144
270
|
break;
|
|
145
271
|
case 36:
|
|
146
272
|
this.state.foregroundColor = "cyan";
|
|
273
|
+
this.state.fgRgb = null;
|
|
147
274
|
break;
|
|
148
275
|
case 37:
|
|
149
276
|
this.state.foregroundColor = "white";
|
|
277
|
+
this.state.fgRgb = null;
|
|
278
|
+
break;
|
|
279
|
+
case 38:
|
|
280
|
+
// Extended foreground: 38;5;n (256-color) or 38;2;r;g;b (true-color)
|
|
281
|
+
if (ci + 1 < codes.length) {
|
|
282
|
+
if (codes[ci + 1] === 5) {
|
|
283
|
+
if (ci + 2 < codes.length) {
|
|
284
|
+
const idx = codes[ci + 2];
|
|
285
|
+
if (idx >= 0 && idx <= 7 && ANSI_NAMED[idx]) {
|
|
286
|
+
this.state.foregroundColor = ANSI_NAMED[idx];
|
|
287
|
+
this.state.fgRgb = null;
|
|
288
|
+
}
|
|
289
|
+
else if (idx >= 0 && idx <= 255 && ANSI_256[idx]) {
|
|
290
|
+
this.state.foregroundColor = null;
|
|
291
|
+
this.state.fgRgb = ANSI_256[idx];
|
|
292
|
+
}
|
|
293
|
+
ci += 2;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
ci += 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (codes[ci + 1] === 2) {
|
|
300
|
+
if (ci + 4 < codes.length) {
|
|
301
|
+
this.state.foregroundColor = null;
|
|
302
|
+
const r = Math.max(0, Math.min(255, codes[ci + 2]));
|
|
303
|
+
const g = Math.max(0, Math.min(255, codes[ci + 3]));
|
|
304
|
+
const b = Math.max(0, Math.min(255, codes[ci + 4]));
|
|
305
|
+
this.state.fgRgb = "rgb(" + r + "," + g + "," + b + ")";
|
|
306
|
+
ci += 4;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
ci = codes.length - 1;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
150
313
|
break;
|
|
151
314
|
case 39:
|
|
152
315
|
this.state.foregroundColor = null;
|
|
316
|
+
this.state.fgRgb = null;
|
|
153
317
|
break;
|
|
154
318
|
case 40:
|
|
155
319
|
this.state.backgroundColor = "black";
|
|
320
|
+
this.state.bgRgb = null;
|
|
156
321
|
break;
|
|
157
322
|
case 41:
|
|
158
323
|
this.state.backgroundColor = "red";
|
|
324
|
+
this.state.bgRgb = null;
|
|
159
325
|
break;
|
|
160
326
|
case 42:
|
|
161
327
|
this.state.backgroundColor = "green";
|
|
328
|
+
this.state.bgRgb = null;
|
|
162
329
|
break;
|
|
163
330
|
case 43:
|
|
164
331
|
this.state.backgroundColor = "yellow";
|
|
332
|
+
this.state.bgRgb = null;
|
|
165
333
|
break;
|
|
166
334
|
case 44:
|
|
167
335
|
this.state.backgroundColor = "blue";
|
|
336
|
+
this.state.bgRgb = null;
|
|
168
337
|
break;
|
|
169
338
|
case 45:
|
|
170
339
|
this.state.backgroundColor = "magenta";
|
|
340
|
+
this.state.bgRgb = null;
|
|
171
341
|
break;
|
|
172
342
|
case 46:
|
|
173
343
|
this.state.backgroundColor = "cyan";
|
|
344
|
+
this.state.bgRgb = null;
|
|
174
345
|
break;
|
|
175
346
|
case 47:
|
|
176
347
|
this.state.backgroundColor = "white";
|
|
348
|
+
this.state.bgRgb = null;
|
|
349
|
+
break;
|
|
350
|
+
case 48:
|
|
351
|
+
// Extended background: 48;5;n (256-color) or 48;2;r;g;b (true-color)
|
|
352
|
+
if (ci + 1 < codes.length) {
|
|
353
|
+
if (codes[ci + 1] === 5) {
|
|
354
|
+
if (ci + 2 < codes.length) {
|
|
355
|
+
const idx = codes[ci + 2];
|
|
356
|
+
if (idx >= 0 && idx <= 7 && ANSI_NAMED[idx]) {
|
|
357
|
+
this.state.backgroundColor = ANSI_NAMED[idx];
|
|
358
|
+
this.state.bgRgb = null;
|
|
359
|
+
}
|
|
360
|
+
else if (idx >= 0 && idx <= 255 && ANSI_256[idx]) {
|
|
361
|
+
this.state.backgroundColor = null;
|
|
362
|
+
this.state.bgRgb = ANSI_256[idx];
|
|
363
|
+
}
|
|
364
|
+
ci += 2;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
ci += 1;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else if (codes[ci + 1] === 2) {
|
|
371
|
+
if (ci + 4 < codes.length) {
|
|
372
|
+
this.state.backgroundColor = null;
|
|
373
|
+
const r = Math.max(0, Math.min(255, codes[ci + 2]));
|
|
374
|
+
const g = Math.max(0, Math.min(255, codes[ci + 3]));
|
|
375
|
+
const b = Math.max(0, Math.min(255, codes[ci + 4]));
|
|
376
|
+
this.state.bgRgb = "rgb(" + r + "," + g + "," + b + ")";
|
|
377
|
+
ci += 4;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
ci = codes.length - 1;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
177
384
|
break;
|
|
178
385
|
case 49:
|
|
179
386
|
this.state.backgroundColor = null;
|
|
387
|
+
this.state.bgRgb = null;
|
|
388
|
+
break;
|
|
389
|
+
// Bright foreground colors
|
|
390
|
+
case 90:
|
|
391
|
+
this.state.foregroundColor = null;
|
|
392
|
+
this.state.fgRgb = ANSI_256[8];
|
|
393
|
+
break;
|
|
394
|
+
case 91:
|
|
395
|
+
this.state.foregroundColor = null;
|
|
396
|
+
this.state.fgRgb = ANSI_256[9];
|
|
397
|
+
break;
|
|
398
|
+
case 92:
|
|
399
|
+
this.state.foregroundColor = null;
|
|
400
|
+
this.state.fgRgb = ANSI_256[10];
|
|
401
|
+
break;
|
|
402
|
+
case 93:
|
|
403
|
+
this.state.foregroundColor = null;
|
|
404
|
+
this.state.fgRgb = ANSI_256[11];
|
|
405
|
+
break;
|
|
406
|
+
case 94:
|
|
407
|
+
this.state.foregroundColor = null;
|
|
408
|
+
this.state.fgRgb = ANSI_256[12];
|
|
409
|
+
break;
|
|
410
|
+
case 95:
|
|
411
|
+
this.state.foregroundColor = null;
|
|
412
|
+
this.state.fgRgb = ANSI_256[13];
|
|
413
|
+
break;
|
|
414
|
+
case 96:
|
|
415
|
+
this.state.foregroundColor = null;
|
|
416
|
+
this.state.fgRgb = ANSI_256[14];
|
|
417
|
+
break;
|
|
418
|
+
case 97:
|
|
419
|
+
this.state.foregroundColor = null;
|
|
420
|
+
this.state.fgRgb = ANSI_256[15];
|
|
421
|
+
break;
|
|
422
|
+
// Bright background colors
|
|
423
|
+
case 100:
|
|
424
|
+
this.state.backgroundColor = null;
|
|
425
|
+
this.state.bgRgb = ANSI_256[8];
|
|
426
|
+
break;
|
|
427
|
+
case 101:
|
|
428
|
+
this.state.backgroundColor = null;
|
|
429
|
+
this.state.bgRgb = ANSI_256[9];
|
|
430
|
+
break;
|
|
431
|
+
case 102:
|
|
432
|
+
this.state.backgroundColor = null;
|
|
433
|
+
this.state.bgRgb = ANSI_256[10];
|
|
434
|
+
break;
|
|
435
|
+
case 103:
|
|
436
|
+
this.state.backgroundColor = null;
|
|
437
|
+
this.state.bgRgb = ANSI_256[11];
|
|
438
|
+
break;
|
|
439
|
+
case 104:
|
|
440
|
+
this.state.backgroundColor = null;
|
|
441
|
+
this.state.bgRgb = ANSI_256[12];
|
|
442
|
+
break;
|
|
443
|
+
case 105:
|
|
444
|
+
this.state.backgroundColor = null;
|
|
445
|
+
this.state.bgRgb = ANSI_256[13];
|
|
446
|
+
break;
|
|
447
|
+
case 106:
|
|
448
|
+
this.state.backgroundColor = null;
|
|
449
|
+
this.state.bgRgb = ANSI_256[14];
|
|
450
|
+
break;
|
|
451
|
+
case 107:
|
|
452
|
+
this.state.backgroundColor = null;
|
|
453
|
+
this.state.bgRgb = ANSI_256[15];
|
|
180
454
|
break;
|
|
181
455
|
}
|
|
456
|
+
ci++;
|
|
182
457
|
}
|
|
183
458
|
}
|
|
184
459
|
addSpan(line.substring(i));
|
|
@@ -193,8 +468,6 @@ export class ColoredConsole {
|
|
|
193
468
|
return;
|
|
194
469
|
}
|
|
195
470
|
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
471
|
if (line === "\r") {
|
|
199
472
|
this.state.carriageReturn = true;
|
|
200
473
|
continue;
|
|
@@ -217,13 +490,11 @@ export class ColoredConsole {
|
|
|
217
490
|
this.targetElement.appendChild(fragment);
|
|
218
491
|
}
|
|
219
492
|
this.state.lines = [];
|
|
220
|
-
// Keep scroll at bottom
|
|
221
493
|
if (atBottom) {
|
|
222
494
|
this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
|
223
495
|
}
|
|
224
496
|
}
|
|
225
497
|
addLine(line) {
|
|
226
|
-
// Processing of lines is deferred for performance reasons
|
|
227
498
|
if (this.state.lines.length === 0) {
|
|
228
499
|
setTimeout(() => this.processLines(), 0);
|
|
229
500
|
}
|
|
@@ -249,6 +520,9 @@ export const coloredConsoleStyles = `
|
|
|
249
520
|
.log-bold {
|
|
250
521
|
font-weight: bold;
|
|
251
522
|
}
|
|
523
|
+
.log-dim {
|
|
524
|
+
opacity: 0.5;
|
|
525
|
+
}
|
|
252
526
|
.log-italic {
|
|
253
527
|
font-style: italic;
|
|
254
528
|
}
|
|
@@ -283,6 +557,10 @@ export const coloredConsoleStyles = `
|
|
|
283
557
|
width: 1px;
|
|
284
558
|
font-size: 1px;
|
|
285
559
|
}
|
|
560
|
+
.log-reverse {
|
|
561
|
+
background: #ddd;
|
|
562
|
+
color: #1c1c1c;
|
|
563
|
+
}
|
|
286
564
|
.log-fg-black {
|
|
287
565
|
color: rgb(128, 128, 128);
|
|
288
566
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
// Matches lines that already carry a wall-clock
|
|
2
|
-
//
|
|
3
|
-
// like
|
|
4
|
-
//
|
|
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
5
|
// Covered formats:
|
|
6
|
-
// (123456) FreeRTOS ms-tick e.g. "(12345) "
|
|
7
6
|
// [HH:MM:SS] wall-clock bracket
|
|
8
7
|
// [HH:MM:SS.mmm] wall-clock bracket with millis
|
|
9
|
-
// I (1234) tag: ESP-IDF log level + tick e.g. "I (1234) wifi: ..."
|
|
10
8
|
// HH:MM:SS.mmm plain wall-clock
|
|
11
|
-
const DEVICE_TIMESTAMP_RE = /^\s*(?:\
|
|
9
|
+
const DEVICE_TIMESTAMP_RE = /^\s*(?:\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\]|(?:\d{2}:){2}\d{2}\.\d)/;
|
|
10
|
+
// Matches leading ANSI SGR (color/style) codes at the start of a string
|
|
11
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
12
|
+
// eslint-disable-next-line no-control-regex
|
|
13
|
+
const LEADING_ANSI_RE = /^(\x1b\[(?:\d+;)*\d*m)+/;
|
|
12
14
|
export class TimestampTransformer {
|
|
13
15
|
constructor() {
|
|
14
16
|
this.deviceHasTimestamps = false;
|
|
@@ -27,11 +29,30 @@ export class TimestampTransformer {
|
|
|
27
29
|
controller.enqueue(chunk);
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
32
|
+
// Extract leading ANSI codes to preserve them across line splits
|
|
33
|
+
const ansiMatch = chunk.match(LEADING_ANSI_RE);
|
|
34
|
+
const leadingAnsi = ansiMatch ? ansiMatch[0] : "";
|
|
35
|
+
const contentWithoutAnsi = leadingAnsi
|
|
36
|
+
? chunk.slice(leadingAnsi.length)
|
|
37
|
+
: chunk;
|
|
30
38
|
const date = new Date();
|
|
31
39
|
const h = date.getHours().toString().padStart(2, "0");
|
|
32
40
|
const m = date.getMinutes().toString().padStart(2, "0");
|
|
33
41
|
const s = date.getSeconds().toString().padStart(2, "0");
|
|
34
|
-
|
|
42
|
+
const timestamp = `[${h}:${m}:${s}]`;
|
|
43
|
+
// For multi-line chunks, we need to preserve ANSI codes on each line
|
|
44
|
+
// Split on newlines, but keep the newline characters
|
|
45
|
+
const lines = contentWithoutAnsi.split(/(\r?\n)/);
|
|
46
|
+
let result = "";
|
|
47
|
+
for (const part of lines) {
|
|
48
|
+
if (part === "\n" || part === "\r\n") {
|
|
49
|
+
result += part;
|
|
50
|
+
}
|
|
51
|
+
else if (part !== "") {
|
|
52
|
+
result += leadingAnsi + timestamp + " " + part;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
controller.enqueue(result);
|
|
35
56
|
}
|
|
36
57
|
reset() {
|
|
37
58
|
this.deviceHasTimestamps = false;
|