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.
@@ -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
- addLine(line: string) {
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.foregroundColor !== null)
65
- span.classList.add(`log-fg-${this.state.foregroundColor}`);
66
- if (this.state.backgroundColor !== null)
67
- span.classList.add(`log-bg-${this.state.backgroundColor}`);
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
- if (match[1] === undefined) continue;
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 (const colorCode of match[1].split(";")) {
90
- switch (parseInt(colorCode)) {
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.secret = true;
227
+ this.state.blink = true;
228
+ this.state.rapidBlink = false;
112
229
  break;
113
230
  case 6:
114
- this.state.secret = false;
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 40:
180
- this.state.backgroundColor = "black";
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
- addSpan(line.substring(i));
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
- // For each line breaks in chunks, send the parsed lines out.
11
- const lines = this.chunks.split("\r\n");
12
- this.chunks = lines.pop()!;
13
- lines.forEach((line) => controller.enqueue(line + "\r\n"));
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
@@ -1,5 +1,5 @@
1
1
  // Service Worker for ESP32Tool PWA
2
- const CACHE_NAME = 'esp32tool-v1.6.4';
2
+ const CACHE_NAME = 'esp32tool-v1.6.6';
3
3
  const RUNTIME_CACHE = 'esp32tool-runtime';
4
4
 
5
5
  // Core files to cache on install (relative paths work for any deployment path)
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "js/util",
5
+ "declaration": false,
6
+ "removeComments": false
7
+ },
8
+ "include": ["src/util/**/*.ts"]
9
+ }