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,3 +1,58 @@
|
|
|
1
|
+
const ANSI_256: string[] = (() => {
|
|
2
|
+
const t: string[] = [];
|
|
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
|
+
|
|
43
|
+
// Maps 256-color indices 0–7 to the named CSS class tokens so that
|
|
44
|
+
// \x1b[38;5;1m renders the same red as \x1b[31m.
|
|
45
|
+
const ANSI_NAMED: (string | null)[] = [
|
|
46
|
+
"black",
|
|
47
|
+
"red",
|
|
48
|
+
"green",
|
|
49
|
+
"yellow",
|
|
50
|
+
"blue",
|
|
51
|
+
"magenta",
|
|
52
|
+
"cyan",
|
|
53
|
+
"white",
|
|
54
|
+
];
|
|
55
|
+
|
|
1
56
|
interface ConsoleState {
|
|
2
57
|
bold: boolean;
|
|
3
58
|
italic: boolean;
|
|
@@ -5,8 +60,15 @@ interface ConsoleState {
|
|
|
5
60
|
strikethrough: boolean;
|
|
6
61
|
foregroundColor: string | null;
|
|
7
62
|
backgroundColor: string | null;
|
|
63
|
+
fgRgb: string | null;
|
|
64
|
+
bgRgb: string | null;
|
|
65
|
+
dim: boolean;
|
|
66
|
+
reverse: boolean;
|
|
8
67
|
carriageReturn: boolean;
|
|
68
|
+
lines: string[];
|
|
9
69
|
secret: boolean;
|
|
70
|
+
blink: boolean;
|
|
71
|
+
rapidBlink: boolean;
|
|
10
72
|
}
|
|
11
73
|
|
|
12
74
|
export class ColoredConsole {
|
|
@@ -17,54 +79,82 @@ export class ColoredConsole {
|
|
|
17
79
|
strikethrough: false,
|
|
18
80
|
foregroundColor: null,
|
|
19
81
|
backgroundColor: null,
|
|
82
|
+
fgRgb: null,
|
|
83
|
+
bgRgb: null,
|
|
84
|
+
dim: false,
|
|
85
|
+
reverse: false,
|
|
20
86
|
carriageReturn: false,
|
|
87
|
+
lines: [],
|
|
21
88
|
secret: false,
|
|
89
|
+
blink: false,
|
|
90
|
+
rapidBlink: false,
|
|
22
91
|
};
|
|
23
92
|
|
|
24
93
|
constructor(public targetElement: HTMLElement) {}
|
|
25
94
|
|
|
26
95
|
logs(): string {
|
|
96
|
+
if (this.state.lines.length > 0) {
|
|
97
|
+
this.processLines();
|
|
98
|
+
}
|
|
27
99
|
return this.targetElement.innerText;
|
|
28
100
|
}
|
|
29
101
|
|
|
30
|
-
|
|
102
|
+
processLine(line: string): Element {
|
|
31
103
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
32
104
|
// eslint-disable-next-line no-control-regex
|
|
33
|
-
const re = /(?:\x1B|\\x1B)(?:\[(.*?)[@-~]|\].*?(?:\x07|\x1B\\))/g;
|
|
105
|
+
const re = /(?:\x1B|\\x1B)(?:\[(.*?)([@-~])|\].*?(?:\x07|\x1B\\))/g;
|
|
34
106
|
let i = 0;
|
|
35
107
|
|
|
36
|
-
if (this.state.carriageReturn) {
|
|
37
|
-
if (line !== "\n") {
|
|
38
|
-
// don't remove if \r\n
|
|
39
|
-
if (this.targetElement.lastChild) {
|
|
40
|
-
this.targetElement.removeChild(this.targetElement.lastChild);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
this.state.carriageReturn = false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const hasBareCR = line.endsWith("\r") && !line.endsWith("\r\n");
|
|
47
|
-
if (hasBareCR) {
|
|
48
|
-
this.state.carriageReturn = true;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
108
|
const lineSpan = document.createElement("span");
|
|
52
109
|
lineSpan.classList.add("line");
|
|
53
|
-
this.targetElement.appendChild(lineSpan);
|
|
54
110
|
|
|
55
111
|
const addSpan = (content: string) => {
|
|
56
112
|
if (content === "") return;
|
|
57
113
|
|
|
58
114
|
const span = document.createElement("span");
|
|
59
115
|
if (this.state.bold) span.classList.add("log-bold");
|
|
116
|
+
if (this.state.dim) span.classList.add("log-dim");
|
|
60
117
|
if (this.state.italic) span.classList.add("log-italic");
|
|
61
118
|
if (this.state.underline) span.classList.add("log-underline");
|
|
62
119
|
if (this.state.strikethrough) span.classList.add("log-strikethrough");
|
|
63
120
|
if (this.state.secret) span.classList.add("log-secret");
|
|
64
|
-
if (this.state.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
121
|
+
if (this.state.blink) span.classList.add("log-blink");
|
|
122
|
+
if (this.state.rapidBlink) span.classList.add("log-rapid-blink");
|
|
123
|
+
|
|
124
|
+
// Resolve colors with reverse-video support
|
|
125
|
+
let fgRgb = this.state.fgRgb;
|
|
126
|
+
let bgRgb = this.state.bgRgb;
|
|
127
|
+
let fg = this.state.foregroundColor;
|
|
128
|
+
let bg = this.state.backgroundColor;
|
|
129
|
+
|
|
130
|
+
if (this.state.reverse) {
|
|
131
|
+
fgRgb = this.state.bgRgb;
|
|
132
|
+
bgRgb = this.state.fgRgb;
|
|
133
|
+
fg = this.state.backgroundColor;
|
|
134
|
+
bg = this.state.foregroundColor;
|
|
135
|
+
// When one side is unset, fill in the terminal defaults so the
|
|
136
|
+
// swap is always visible (fg default=#ddd, bg default=#1c1c1c).
|
|
137
|
+
if (!fgRgb && !fg && !bgRgb && !bg) {
|
|
138
|
+
span.classList.add("log-reverse");
|
|
139
|
+
} else {
|
|
140
|
+
if (!fgRgb && !fg) fgRgb = "rgb(28,28,28)";
|
|
141
|
+
if (!bgRgb && !bg) bgRgb = "rgb(221,221,221)";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Inline rgb() style takes priority over CSS class
|
|
146
|
+
if (fgRgb) {
|
|
147
|
+
span.style.color = fgRgb;
|
|
148
|
+
} else if (fg !== null) {
|
|
149
|
+
span.classList.add(`log-fg-${fg}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (bgRgb) {
|
|
153
|
+
span.style.backgroundColor = bgRgb;
|
|
154
|
+
} else if (bg !== null) {
|
|
155
|
+
span.classList.add(`log-bg-${bg}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
68
158
|
span.appendChild(document.createTextNode(content));
|
|
69
159
|
lineSpan.appendChild(span);
|
|
70
160
|
|
|
@@ -84,23 +174,49 @@ export class ColoredConsole {
|
|
|
84
174
|
addSpan(line.substring(i, j));
|
|
85
175
|
i = j + match[0].length;
|
|
86
176
|
|
|
87
|
-
|
|
177
|
+
// Only process SGR sequences (final byte 'm'); skip cursor, erase, etc.
|
|
178
|
+
if (match[1] === undefined || match[2] !== "m") continue;
|
|
179
|
+
|
|
180
|
+
const rawCodes = match[1] === "" ? [""] : match[1].split(";");
|
|
181
|
+
const codes = [];
|
|
182
|
+
let invalidSgr = false;
|
|
183
|
+
for (const rawCode of rawCodes) {
|
|
184
|
+
if (rawCode === "") {
|
|
185
|
+
codes.push(0);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (!/^\d+$/.test(rawCode)) {
|
|
189
|
+
invalidSgr = true;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
codes.push(Number(rawCode));
|
|
193
|
+
}
|
|
194
|
+
if (invalidSgr) continue;
|
|
88
195
|
|
|
89
|
-
for (
|
|
90
|
-
|
|
196
|
+
for (let ci = 0; ci < codes.length; ci++) {
|
|
197
|
+
const code = codes[ci];
|
|
198
|
+
switch (code) {
|
|
91
199
|
case 0:
|
|
92
|
-
// reset
|
|
93
200
|
this.state.bold = false;
|
|
201
|
+
this.state.dim = false;
|
|
94
202
|
this.state.italic = false;
|
|
95
203
|
this.state.underline = false;
|
|
96
204
|
this.state.strikethrough = false;
|
|
97
205
|
this.state.foregroundColor = null;
|
|
98
206
|
this.state.backgroundColor = null;
|
|
207
|
+
this.state.fgRgb = null;
|
|
208
|
+
this.state.bgRgb = null;
|
|
209
|
+
this.state.reverse = false;
|
|
99
210
|
this.state.secret = false;
|
|
211
|
+
this.state.blink = false;
|
|
212
|
+
this.state.rapidBlink = false;
|
|
100
213
|
break;
|
|
101
214
|
case 1:
|
|
102
215
|
this.state.bold = true;
|
|
103
216
|
break;
|
|
217
|
+
case 2:
|
|
218
|
+
this.state.dim = true;
|
|
219
|
+
break;
|
|
104
220
|
case 3:
|
|
105
221
|
this.state.italic = true;
|
|
106
222
|
break;
|
|
@@ -108,16 +224,25 @@ export class ColoredConsole {
|
|
|
108
224
|
this.state.underline = true;
|
|
109
225
|
break;
|
|
110
226
|
case 5:
|
|
111
|
-
this.state.
|
|
227
|
+
this.state.blink = true;
|
|
228
|
+
this.state.rapidBlink = false;
|
|
112
229
|
break;
|
|
113
230
|
case 6:
|
|
114
|
-
this.state.
|
|
231
|
+
this.state.rapidBlink = true;
|
|
232
|
+
this.state.blink = false;
|
|
233
|
+
break;
|
|
234
|
+
case 7:
|
|
235
|
+
this.state.reverse = true;
|
|
236
|
+
break;
|
|
237
|
+
case 8:
|
|
238
|
+
this.state.secret = true;
|
|
115
239
|
break;
|
|
116
240
|
case 9:
|
|
117
241
|
this.state.strikethrough = true;
|
|
118
242
|
break;
|
|
119
243
|
case 22:
|
|
120
244
|
this.state.bold = false;
|
|
245
|
+
this.state.dim = false;
|
|
121
246
|
break;
|
|
122
247
|
case 23:
|
|
123
248
|
this.state.italic = false;
|
|
@@ -125,77 +250,275 @@ export class ColoredConsole {
|
|
|
125
250
|
case 24:
|
|
126
251
|
this.state.underline = false;
|
|
127
252
|
break;
|
|
253
|
+
case 25:
|
|
254
|
+
this.state.blink = false;
|
|
255
|
+
this.state.rapidBlink = false;
|
|
256
|
+
break;
|
|
257
|
+
case 27:
|
|
258
|
+
this.state.reverse = false;
|
|
259
|
+
break;
|
|
260
|
+
case 28:
|
|
261
|
+
this.state.secret = false;
|
|
262
|
+
break;
|
|
128
263
|
case 29:
|
|
129
264
|
this.state.strikethrough = false;
|
|
130
265
|
break;
|
|
131
266
|
case 30:
|
|
132
267
|
this.state.foregroundColor = "black";
|
|
268
|
+
this.state.fgRgb = null;
|
|
133
269
|
break;
|
|
134
270
|
case 31:
|
|
135
271
|
this.state.foregroundColor = "red";
|
|
272
|
+
this.state.fgRgb = null;
|
|
136
273
|
break;
|
|
137
274
|
case 32:
|
|
138
275
|
this.state.foregroundColor = "green";
|
|
276
|
+
this.state.fgRgb = null;
|
|
139
277
|
break;
|
|
140
278
|
case 33:
|
|
141
279
|
this.state.foregroundColor = "yellow";
|
|
280
|
+
this.state.fgRgb = null;
|
|
142
281
|
break;
|
|
143
282
|
case 34:
|
|
144
283
|
this.state.foregroundColor = "blue";
|
|
284
|
+
this.state.fgRgb = null;
|
|
145
285
|
break;
|
|
146
286
|
case 35:
|
|
147
287
|
this.state.foregroundColor = "magenta";
|
|
288
|
+
this.state.fgRgb = null;
|
|
148
289
|
break;
|
|
149
290
|
case 36:
|
|
150
291
|
this.state.foregroundColor = "cyan";
|
|
292
|
+
this.state.fgRgb = null;
|
|
151
293
|
break;
|
|
152
294
|
case 37:
|
|
153
295
|
this.state.foregroundColor = "white";
|
|
296
|
+
this.state.fgRgb = null;
|
|
297
|
+
break;
|
|
298
|
+
case 38:
|
|
299
|
+
// Extended foreground: 38;5;n (256-color) or 38;2;r;g;b (true-color)
|
|
300
|
+
if (ci + 1 < codes.length) {
|
|
301
|
+
if (codes[ci + 1] === 5) {
|
|
302
|
+
if (ci + 2 < codes.length) {
|
|
303
|
+
const idx = codes[ci + 2];
|
|
304
|
+
if (idx >= 0 && idx <= 7 && ANSI_NAMED[idx]) {
|
|
305
|
+
this.state.foregroundColor = ANSI_NAMED[idx];
|
|
306
|
+
this.state.fgRgb = null;
|
|
307
|
+
} else if (idx >= 0 && idx <= 255 && ANSI_256[idx]) {
|
|
308
|
+
this.state.foregroundColor = null;
|
|
309
|
+
this.state.fgRgb = ANSI_256[idx];
|
|
310
|
+
}
|
|
311
|
+
ci += 2;
|
|
312
|
+
} else {
|
|
313
|
+
ci += 1;
|
|
314
|
+
}
|
|
315
|
+
} else if (codes[ci + 1] === 2) {
|
|
316
|
+
if (ci + 4 < codes.length) {
|
|
317
|
+
this.state.foregroundColor = null;
|
|
318
|
+
const r = Math.max(0, Math.min(255, codes[ci + 2]));
|
|
319
|
+
const g = Math.max(0, Math.min(255, codes[ci + 3]));
|
|
320
|
+
const b = Math.max(0, Math.min(255, codes[ci + 4]));
|
|
321
|
+
this.state.fgRgb = "rgb(" + r + "," + g + "," + b + ")";
|
|
322
|
+
ci += 4;
|
|
323
|
+
} else {
|
|
324
|
+
ci = codes.length - 1;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
154
328
|
break;
|
|
155
329
|
case 39:
|
|
156
330
|
this.state.foregroundColor = null;
|
|
331
|
+
this.state.fgRgb = null;
|
|
332
|
+
break;
|
|
333
|
+
case 40:
|
|
334
|
+
this.state.backgroundColor = "black";
|
|
335
|
+
this.state.bgRgb = null;
|
|
157
336
|
break;
|
|
158
337
|
case 41:
|
|
159
338
|
this.state.backgroundColor = "red";
|
|
339
|
+
this.state.bgRgb = null;
|
|
160
340
|
break;
|
|
161
341
|
case 42:
|
|
162
342
|
this.state.backgroundColor = "green";
|
|
343
|
+
this.state.bgRgb = null;
|
|
163
344
|
break;
|
|
164
345
|
case 43:
|
|
165
346
|
this.state.backgroundColor = "yellow";
|
|
347
|
+
this.state.bgRgb = null;
|
|
166
348
|
break;
|
|
167
349
|
case 44:
|
|
168
350
|
this.state.backgroundColor = "blue";
|
|
351
|
+
this.state.bgRgb = null;
|
|
169
352
|
break;
|
|
170
353
|
case 45:
|
|
171
354
|
this.state.backgroundColor = "magenta";
|
|
355
|
+
this.state.bgRgb = null;
|
|
172
356
|
break;
|
|
173
357
|
case 46:
|
|
174
358
|
this.state.backgroundColor = "cyan";
|
|
359
|
+
this.state.bgRgb = null;
|
|
175
360
|
break;
|
|
176
361
|
case 47:
|
|
177
362
|
this.state.backgroundColor = "white";
|
|
363
|
+
this.state.bgRgb = null;
|
|
178
364
|
break;
|
|
179
|
-
case
|
|
180
|
-
|
|
365
|
+
case 48:
|
|
366
|
+
// Extended background: 48;5;n (256-color) or 48;2;r;g;b (true-color)
|
|
367
|
+
if (ci + 1 < codes.length) {
|
|
368
|
+
if (codes[ci + 1] === 5) {
|
|
369
|
+
if (ci + 2 < codes.length) {
|
|
370
|
+
const idx = codes[ci + 2];
|
|
371
|
+
if (idx >= 0 && idx <= 7 && ANSI_NAMED[idx]) {
|
|
372
|
+
this.state.backgroundColor = ANSI_NAMED[idx];
|
|
373
|
+
this.state.bgRgb = null;
|
|
374
|
+
} else if (idx >= 0 && idx <= 255 && ANSI_256[idx]) {
|
|
375
|
+
this.state.backgroundColor = null;
|
|
376
|
+
this.state.bgRgb = ANSI_256[idx];
|
|
377
|
+
}
|
|
378
|
+
ci += 2;
|
|
379
|
+
} else {
|
|
380
|
+
ci += 1;
|
|
381
|
+
}
|
|
382
|
+
} else if (codes[ci + 1] === 2) {
|
|
383
|
+
if (ci + 4 < codes.length) {
|
|
384
|
+
this.state.backgroundColor = null;
|
|
385
|
+
const r = Math.max(0, Math.min(255, codes[ci + 2]));
|
|
386
|
+
const g = Math.max(0, Math.min(255, codes[ci + 3]));
|
|
387
|
+
const b = Math.max(0, Math.min(255, codes[ci + 4]));
|
|
388
|
+
this.state.bgRgb = "rgb(" + r + "," + g + "," + b + ")";
|
|
389
|
+
ci += 4;
|
|
390
|
+
} else {
|
|
391
|
+
ci = codes.length - 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
181
395
|
break;
|
|
182
396
|
case 49:
|
|
183
397
|
this.state.backgroundColor = null;
|
|
398
|
+
this.state.bgRgb = null;
|
|
399
|
+
break;
|
|
400
|
+
// Bright foreground colors
|
|
401
|
+
case 90:
|
|
402
|
+
this.state.foregroundColor = null;
|
|
403
|
+
this.state.fgRgb = ANSI_256[8];
|
|
404
|
+
break;
|
|
405
|
+
case 91:
|
|
406
|
+
this.state.foregroundColor = null;
|
|
407
|
+
this.state.fgRgb = ANSI_256[9];
|
|
408
|
+
break;
|
|
409
|
+
case 92:
|
|
410
|
+
this.state.foregroundColor = null;
|
|
411
|
+
this.state.fgRgb = ANSI_256[10];
|
|
412
|
+
break;
|
|
413
|
+
case 93:
|
|
414
|
+
this.state.foregroundColor = null;
|
|
415
|
+
this.state.fgRgb = ANSI_256[11];
|
|
416
|
+
break;
|
|
417
|
+
case 94:
|
|
418
|
+
this.state.foregroundColor = null;
|
|
419
|
+
this.state.fgRgb = ANSI_256[12];
|
|
420
|
+
break;
|
|
421
|
+
case 95:
|
|
422
|
+
this.state.foregroundColor = null;
|
|
423
|
+
this.state.fgRgb = ANSI_256[13];
|
|
424
|
+
break;
|
|
425
|
+
case 96:
|
|
426
|
+
this.state.foregroundColor = null;
|
|
427
|
+
this.state.fgRgb = ANSI_256[14];
|
|
428
|
+
break;
|
|
429
|
+
case 97:
|
|
430
|
+
this.state.foregroundColor = null;
|
|
431
|
+
this.state.fgRgb = ANSI_256[15];
|
|
432
|
+
break;
|
|
433
|
+
// Bright background colors
|
|
434
|
+
case 100:
|
|
435
|
+
this.state.backgroundColor = null;
|
|
436
|
+
this.state.bgRgb = ANSI_256[8];
|
|
437
|
+
break;
|
|
438
|
+
case 101:
|
|
439
|
+
this.state.backgroundColor = null;
|
|
440
|
+
this.state.bgRgb = ANSI_256[9];
|
|
441
|
+
break;
|
|
442
|
+
case 102:
|
|
443
|
+
this.state.backgroundColor = null;
|
|
444
|
+
this.state.bgRgb = ANSI_256[10];
|
|
445
|
+
break;
|
|
446
|
+
case 103:
|
|
447
|
+
this.state.backgroundColor = null;
|
|
448
|
+
this.state.bgRgb = ANSI_256[11];
|
|
449
|
+
break;
|
|
450
|
+
case 104:
|
|
451
|
+
this.state.backgroundColor = null;
|
|
452
|
+
this.state.bgRgb = ANSI_256[12];
|
|
453
|
+
break;
|
|
454
|
+
case 105:
|
|
455
|
+
this.state.backgroundColor = null;
|
|
456
|
+
this.state.bgRgb = ANSI_256[13];
|
|
457
|
+
break;
|
|
458
|
+
case 106:
|
|
459
|
+
this.state.backgroundColor = null;
|
|
460
|
+
this.state.bgRgb = ANSI_256[14];
|
|
461
|
+
break;
|
|
462
|
+
case 107:
|
|
463
|
+
this.state.backgroundColor = null;
|
|
464
|
+
this.state.bgRgb = ANSI_256[15];
|
|
184
465
|
break;
|
|
185
466
|
}
|
|
186
467
|
}
|
|
187
468
|
}
|
|
469
|
+
addSpan(line.substring(i));
|
|
470
|
+
return lineSpan;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
processLines() {
|
|
188
474
|
const atBottom =
|
|
189
475
|
this.targetElement.scrollTop >
|
|
190
476
|
this.targetElement.scrollHeight - this.targetElement.offsetHeight - 50;
|
|
477
|
+
const prevCarriageReturn = this.state.carriageReturn;
|
|
478
|
+
const fragment = document.createDocumentFragment();
|
|
191
479
|
|
|
192
|
-
|
|
480
|
+
if (this.state.lines.length === 0) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
for (const line of this.state.lines) {
|
|
485
|
+
if (line === "\r") {
|
|
486
|
+
this.state.carriageReturn = true;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (this.state.carriageReturn && line !== "\n") {
|
|
490
|
+
if (fragment.childElementCount) {
|
|
491
|
+
fragment.removeChild(fragment.lastChild!);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const hadCarriageReturn = line.endsWith("\r");
|
|
495
|
+
fragment.appendChild(this.processLine(line.replace(/\r/g, "")));
|
|
496
|
+
this.state.carriageReturn = hadCarriageReturn;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (
|
|
500
|
+
prevCarriageReturn &&
|
|
501
|
+
fragment.childElementCount > 0 &&
|
|
502
|
+
this.targetElement.lastChild
|
|
503
|
+
) {
|
|
504
|
+
this.targetElement.replaceChild(fragment, this.targetElement.lastChild!);
|
|
505
|
+
} else {
|
|
506
|
+
this.targetElement.appendChild(fragment);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this.state.lines = [];
|
|
193
510
|
|
|
194
|
-
// Keep scroll at bottom
|
|
195
511
|
if (atBottom) {
|
|
196
512
|
this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
|
197
513
|
}
|
|
198
514
|
}
|
|
515
|
+
|
|
516
|
+
addLine(line: string) {
|
|
517
|
+
if (this.state.lines.length === 0) {
|
|
518
|
+
setTimeout(() => this.processLines(), 0);
|
|
519
|
+
}
|
|
520
|
+
this.state.lines.push(line);
|
|
521
|
+
}
|
|
199
522
|
}
|
|
200
523
|
|
|
201
524
|
export const coloredConsoleStyles = `
|
|
@@ -217,6 +540,9 @@ export const coloredConsoleStyles = `
|
|
|
217
540
|
.log-bold {
|
|
218
541
|
font-weight: bold;
|
|
219
542
|
}
|
|
543
|
+
.log-dim {
|
|
544
|
+
opacity: 0.5;
|
|
545
|
+
}
|
|
220
546
|
.log-italic {
|
|
221
547
|
font-style: italic;
|
|
222
548
|
}
|
|
@@ -229,6 +555,17 @@ export const coloredConsoleStyles = `
|
|
|
229
555
|
.log-underline.log-strikethrough {
|
|
230
556
|
text-decoration: underline line-through;
|
|
231
557
|
}
|
|
558
|
+
.log-blink {
|
|
559
|
+
animation: blink 1s step-end infinite;
|
|
560
|
+
}
|
|
561
|
+
.log-rapid-blink {
|
|
562
|
+
animation: blink 0.4s step-end infinite;
|
|
563
|
+
}
|
|
564
|
+
@keyframes blink {
|
|
565
|
+
50% {
|
|
566
|
+
opacity: 0;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
232
569
|
.log-secret {
|
|
233
570
|
-webkit-user-select: none;
|
|
234
571
|
-moz-user-select: none;
|
|
@@ -240,6 +577,10 @@ export const coloredConsoleStyles = `
|
|
|
240
577
|
width: 1px;
|
|
241
578
|
font-size: 1px;
|
|
242
579
|
}
|
|
580
|
+
.log-reverse {
|
|
581
|
+
background: #ddd;
|
|
582
|
+
color: #1c1c1c;
|
|
583
|
+
}
|
|
243
584
|
.log-fg-black {
|
|
244
585
|
color: rgb(128, 128, 128);
|
|
245
586
|
}
|
|
@@ -7,10 +7,24 @@ export class LineBreakTransformer implements Transformer<string, string> {
|
|
|
7
7
|
) {
|
|
8
8
|
// Append new chunks to existing chunks.
|
|
9
9
|
this.chunks += chunk;
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// Split on \r\n, lone \r, or lone \n — capturing the separator so we can
|
|
11
|
+
// distinguish a lone \r (overwrite intent) from a normal newline.
|
|
12
|
+
const re = /\r\n|\r|\n/g;
|
|
13
|
+
let lastIndex = 0;
|
|
14
|
+
let match: RegExpExecArray | null;
|
|
15
|
+
while ((match = re.exec(this.chunks)) !== null) {
|
|
16
|
+
// If this is a lone \r at the very end of the buffer, leave it so it can
|
|
17
|
+
// be combined with a possible following \n in the next chunk.
|
|
18
|
+
if (match[0] === "\r" && match.index === this.chunks.length - 1) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
const line = this.chunks.substring(lastIndex, match.index);
|
|
22
|
+
// Emit with \r suffix only for lone \r (overwrite), \n for everything else.
|
|
23
|
+
const suffix = match[0] === "\r" ? "\r" : "\n";
|
|
24
|
+
controller.enqueue(line + suffix);
|
|
25
|
+
lastIndex = re.lastIndex;
|
|
26
|
+
}
|
|
27
|
+
this.chunks = this.chunks.substring(lastIndex);
|
|
14
28
|
}
|
|
15
29
|
|
|
16
30
|
flush(controller: TransformStreamDefaultController<string>) {
|
|
@@ -0,0 +1,45 @@
|
|
|
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 =
|
|
10
|
+
/^\s*(?:\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\]|(?:\d{2}:){2}\d{2}\.\d)/;
|
|
11
|
+
|
|
12
|
+
export class TimestampTransformer implements Transformer<string, string> {
|
|
13
|
+
private deviceHasTimestamps = false;
|
|
14
|
+
|
|
15
|
+
transform(
|
|
16
|
+
chunk: string,
|
|
17
|
+
controller: TransformStreamDefaultController<string>,
|
|
18
|
+
) {
|
|
19
|
+
// Pass through pure newline / empty sentinel unchanged so that
|
|
20
|
+
// carriage-return overwrite logic in console-color.ts still works.
|
|
21
|
+
if (chunk === "" || chunk === "\n" || chunk === "\r") {
|
|
22
|
+
controller.enqueue(chunk);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!this.deviceHasTimestamps && DEVICE_TIMESTAMP_RE.test(chunk)) {
|
|
27
|
+
this.deviceHasTimestamps = true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.deviceHasTimestamps) {
|
|
31
|
+
controller.enqueue(chunk);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const date = new Date();
|
|
36
|
+
const h = date.getHours().toString().padStart(2, "0");
|
|
37
|
+
const m = date.getMinutes().toString().padStart(2, "0");
|
|
38
|
+
const s = date.getSeconds().toString().padStart(2, "0");
|
|
39
|
+
controller.enqueue(`[${h}:${m}:${s}] ${chunk}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
reset() {
|
|
43
|
+
this.deviceHasTimestamps = false;
|
|
44
|
+
}
|
|
45
|
+
}
|
package/sw.js
CHANGED