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