esp32tool 1.6.6 → 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.
Binary file
package/css/style.css CHANGED
@@ -69,7 +69,8 @@
69
69
  }
70
70
 
71
71
  @keyframes pulse {
72
- 0%, 100% {
72
+ 0%,
73
+ 100% {
73
74
  transform: scale(1);
74
75
  box-shadow: 0 0 0 0 rgba(255, 107, 0, 0.7);
75
76
  }
@@ -90,7 +91,9 @@ button,
90
91
  }
91
92
 
92
93
  body {
93
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Arial, sans-serif;
94
+ font-family:
95
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu,
96
+ "Helvetica Neue", Arial, sans-serif;
94
97
  font-style: normal;
95
98
  font-weight: 400;
96
99
  margin: 0;
@@ -261,7 +264,9 @@ div.clear {
261
264
 
262
265
  #log {
263
266
  max-width: 100%;
264
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
267
+ font-family:
268
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
269
+ monospace;
265
270
  font-style: normal;
266
271
  font-weight: 400;
267
272
  font-size: 16px;
@@ -343,7 +348,9 @@ div.clear {
343
348
  line-height: 25px;
344
349
  font-size: 14px;
345
350
  color: white;
346
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Arial, sans-serif;
351
+ font-family:
352
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu,
353
+ "Helvetica Neue", Arial, sans-serif;
347
354
  font-style: normal;
348
355
  font-weight: 600;
349
356
  box-sizing: border-box;
@@ -391,8 +398,6 @@ div.clear {
391
398
  border-color: #67ac38;
392
399
  }
393
400
 
394
-
395
-
396
401
  #commands {
397
402
  min-width: 600px;
398
403
  justify-content: center;
@@ -410,7 +415,7 @@ div.clear {
410
415
  }
411
416
 
412
417
  /* Hide upload rows 2-8 by default, show dynamically via JavaScript */
413
- #commands .upload:nth-child(n+2):nth-child(-n+8) {
418
+ #commands .upload:nth-child(n + 2):nth-child(-n + 8) {
414
419
  display: none;
415
420
  }
416
421
 
@@ -486,8 +491,12 @@ div.clear {
486
491
  }
487
492
 
488
493
  @keyframes indeterminate-stripes {
489
- 0% { background-position: 0 0; }
490
- 100% { background-position: 28px 0; }
494
+ 0% {
495
+ background-position: 0 0;
496
+ }
497
+ 100% {
498
+ background-position: 28px 0;
499
+ }
491
500
  }
492
501
 
493
502
  #eraseProgress {
@@ -625,7 +634,9 @@ div.clear {
625
634
  border-style: solid;
626
635
  cursor: pointer;
627
636
  background-color: transparent;
628
- transition: background-color 0.2s, color 0.2s;
637
+ transition:
638
+ background-color 0.2s,
639
+ color 0.2s;
629
640
  }
630
641
 
631
642
  .partition-download-btn:hover {
@@ -976,7 +987,10 @@ div.clear {
976
987
  border-radius: 25px;
977
988
  cursor: pointer;
978
989
  margin-top: 20px;
979
- transition: background-color 0.2s, color 0.2s, border-color 0.2s;
990
+ transition:
991
+ background-color 0.2s,
992
+ color 0.2s,
993
+ border-color 0.2s;
980
994
  display: inline-flex;
981
995
  align-items: center;
982
996
  justify-content: center;
@@ -1089,7 +1103,9 @@ div.clear {
1089
1103
 
1090
1104
  .file-viewer-body pre {
1091
1105
  margin: 0;
1092
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
1106
+ font-family:
1107
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
1108
+ monospace;
1093
1109
  font-size: 13px;
1094
1110
  line-height: 1.5;
1095
1111
  white-space: pre;
@@ -1100,7 +1116,9 @@ div.clear {
1100
1116
  }
1101
1117
 
1102
1118
  .file-viewer-body .hex-view {
1103
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
1119
+ font-family:
1120
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
1121
+ monospace;
1104
1122
  font-size: 12px;
1105
1123
  line-height: 1.6;
1106
1124
  }
@@ -1134,6 +1152,262 @@ div.clear {
1134
1152
  gap: 10px;
1135
1153
  }
1136
1154
 
1155
+ /* NVS Editor Dialog Styles */
1156
+ .nvs-dialog-overlay {
1157
+ position: fixed;
1158
+ top: 0;
1159
+ left: 0;
1160
+ width: 100%;
1161
+ height: 100%;
1162
+ background-color: rgba(0, 0, 0, 0.7);
1163
+ display: flex;
1164
+ align-items: center;
1165
+ justify-content: center;
1166
+ z-index: 10000;
1167
+ }
1168
+
1169
+ .nvs-dialog {
1170
+ background-color: #fff;
1171
+ border-radius: 10px;
1172
+ box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3);
1173
+ max-width: 600px;
1174
+ max-height: 80vh;
1175
+ width: 90%;
1176
+ overflow: hidden;
1177
+ display: flex;
1178
+ flex-direction: column;
1179
+ }
1180
+
1181
+ .nvs-dialog-header {
1182
+ display: flex;
1183
+ justify-content: space-between;
1184
+ align-items: center;
1185
+ padding: 20px 25px;
1186
+ border-bottom: 2px solid #e0e0e0;
1187
+ background-color: #f8f8f8;
1188
+ }
1189
+
1190
+ .nvs-dialog-header h3 {
1191
+ margin: 0;
1192
+ font-size: 18px;
1193
+ color: #333;
1194
+ }
1195
+
1196
+ .nvs-dialog-close {
1197
+ background: none;
1198
+ border: none;
1199
+ font-size: 24px;
1200
+ color: #999;
1201
+ cursor: pointer;
1202
+ padding: 0;
1203
+ width: 30px;
1204
+ height: 30px;
1205
+ display: flex;
1206
+ align-items: center;
1207
+ justify-content: center;
1208
+ border-radius: 50%;
1209
+ transition: all 0.2s;
1210
+ }
1211
+
1212
+ .nvs-dialog-close:hover {
1213
+ background-color: #f0f0f0;
1214
+ color: #333;
1215
+ }
1216
+
1217
+ .nvs-dialog-close:focus,
1218
+ .nvs-dialog-close:focus-visible {
1219
+ outline: 2px solid #0078d4;
1220
+ outline-offset: 2px;
1221
+ background-color: #f0f0f0;
1222
+ color: #333;
1223
+ }
1224
+
1225
+ .nvs-dialog-body {
1226
+ padding: 25px;
1227
+ overflow-y: auto;
1228
+ flex: 1;
1229
+ color: #333;
1230
+ }
1231
+
1232
+ .nvs-dialog-body h4 {
1233
+ margin: 0 0 12px 0;
1234
+ font-size: 15px;
1235
+ color: #333;
1236
+ text-transform: uppercase;
1237
+ font-weight: 700;
1238
+ letter-spacing: 0.5px;
1239
+ }
1240
+
1241
+ .nvs-dialog-body pre {
1242
+ background-color: #f5f5f5;
1243
+ padding: 12px 15px;
1244
+ border-radius: 6px;
1245
+ margin: 0 0 18px 0;
1246
+ font-family:
1247
+ "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
1248
+ monospace;
1249
+ font-size: 14px;
1250
+ line-height: 1.6;
1251
+ white-space: pre-wrap;
1252
+ overflow-wrap: break-word;
1253
+ border: 1px solid #e0e0e0;
1254
+ }
1255
+
1256
+ .nvs-ok {
1257
+ color: #71ae1e;
1258
+ font-weight: 600;
1259
+ }
1260
+
1261
+ .nvs-warn {
1262
+ color: #ff6b00;
1263
+ font-weight: 600;
1264
+ }
1265
+
1266
+ .nvs-error {
1267
+ color: #c64141;
1268
+ font-weight: 600;
1269
+ }
1270
+
1271
+ .nvs-blob-item {
1272
+ background-color: #f8f8f8;
1273
+ border: 1px solid #e0e0e0;
1274
+ border-radius: 5px;
1275
+ padding: 15px;
1276
+ margin-bottom: 10px;
1277
+ }
1278
+
1279
+ .nvs-blob-item div {
1280
+ margin: 5px 0;
1281
+ font-size: 13px;
1282
+ }
1283
+
1284
+ .nvs-blob-item button {
1285
+ margin-right: 10px;
1286
+ margin-top: 10px;
1287
+ padding: 5px 12px;
1288
+ font-size: 12px;
1289
+ border-style: solid;
1290
+ cursor: pointer;
1291
+ border-radius: 12px;
1292
+ }
1293
+
1294
+ .nvs-blob-item button:focus,
1295
+ .nvs-blob-item button:focus-visible {
1296
+ outline: 2px solid #0078d4;
1297
+ outline-offset: 2px;
1298
+ }
1299
+
1300
+ .nvs-issue-group {
1301
+ margin: 6px 0 14px 0;
1302
+ padding: 8px 10px;
1303
+ background-color: #fff8f3;
1304
+ border: 1px solid #ffd9b8;
1305
+ border-radius: 6px;
1306
+ }
1307
+
1308
+ .nvs-issue-title {
1309
+ font-weight: 700;
1310
+ font-size: 13px;
1311
+ margin-bottom: 6px;
1312
+ }
1313
+
1314
+ pre.nvs-issue {
1315
+ background-color: #fff;
1316
+ border: 1px solid #f0d3b8;
1317
+ border-radius: 4px;
1318
+ padding: 6px 8px;
1319
+ margin: 4px 0;
1320
+ font-family:
1321
+ "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
1322
+ monospace;
1323
+ font-size: 12px;
1324
+ line-height: 1.5;
1325
+ white-space: pre-wrap;
1326
+ overflow-wrap: break-word;
1327
+ color: #333;
1328
+ flex: 1;
1329
+ }
1330
+
1331
+ .nvs-issue-row {
1332
+ display: flex;
1333
+ align-items: center;
1334
+ gap: 8px;
1335
+ flex-wrap: wrap;
1336
+ margin: 4px 0;
1337
+ }
1338
+
1339
+ .nvs-issue-actions {
1340
+ display: inline-flex;
1341
+ gap: 4px;
1342
+ flex-shrink: 0;
1343
+ }
1344
+
1345
+ .nvs-issue-btn {
1346
+ padding: 4px 8px;
1347
+ font-size: 12px;
1348
+ border: 1px solid #c0c0c0;
1349
+ border-radius: 4px;
1350
+ background: #fafafa;
1351
+ color: #333;
1352
+ cursor: pointer;
1353
+ white-space: nowrap;
1354
+ }
1355
+
1356
+ .nvs-issue-btn:hover {
1357
+ background: #f0f0f0;
1358
+ border-color: #888;
1359
+ }
1360
+
1361
+ .nvs-issue-btn:focus,
1362
+ .nvs-issue-btn:focus-visible {
1363
+ outline: 2px solid #0078d4;
1364
+ outline-offset: 2px;
1365
+ background: #f0f0f0;
1366
+ border-color: #888;
1367
+ }
1368
+
1369
+ .nvs-issue-delete:hover {
1370
+ background: #ffe0e0;
1371
+ border-color: #c64141;
1372
+ color: #c64141;
1373
+ }
1374
+
1375
+ .nvs-issue-delete:focus,
1376
+ .nvs-issue-delete:focus-visible {
1377
+ outline: 2px solid #c64141;
1378
+ outline-offset: 2px;
1379
+ background: #ffe0e0;
1380
+ border-color: #c64141;
1381
+ color: #c64141;
1382
+ }
1383
+
1384
+ /* Highlight effect when jumping to entry/page */
1385
+ .nvs-row-highlight {
1386
+ animation: nvs-flash-row 2.2s ease-out;
1387
+ }
1388
+
1389
+ .nvs-page-highlight {
1390
+ animation: nvs-flash-page 2.2s ease-out;
1391
+ }
1392
+
1393
+ @keyframes nvs-flash-row {
1394
+ 0% {
1395
+ background-color: #fff3a8;
1396
+ }
1397
+ 100% {
1398
+ background-color: transparent;
1399
+ }
1400
+ }
1401
+
1402
+ @keyframes nvs-flash-page {
1403
+ 0% {
1404
+ box-shadow: 0 0 0 3px #ffb74d;
1405
+ }
1406
+ 100% {
1407
+ box-shadow: 0 0 0 0 transparent;
1408
+ }
1409
+ }
1410
+
1137
1411
  /* Mobile Optimizations */
1138
1412
  @media (max-width: 768px) {
1139
1413
  /* Larger touch targets and better readability */
@@ -2313,7 +2587,8 @@ body.console-active .main {
2313
2587
  background-color: #1c1c1c;
2314
2588
  display: flex;
2315
2589
  flex-direction: column;
2316
- font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
2590
+ font-family:
2591
+ SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
2317
2592
  color: #ddd;
2318
2593
  }
2319
2594
 
@@ -2638,8 +2913,11 @@ body.hexeditor-active .main {
2638
2913
  /* Progress overlay for loading */
2639
2914
  .hexeditor-progress-overlay {
2640
2915
  position: absolute;
2641
- top: 0; left: 0; right: 0; bottom: 0;
2642
- background-color: rgba(0,0,0,0.85);
2916
+ top: 0;
2917
+ left: 0;
2918
+ right: 0;
2919
+ bottom: 0;
2920
+ background-color: rgba(0, 0, 0, 0.85);
2643
2921
  display: flex;
2644
2922
  flex-direction: column;
2645
2923
  align-items: center;
@@ -2776,7 +3054,9 @@ body.hexeditor-active .main {
2776
3054
  background-color: #1c1c1c;
2777
3055
  display: flex;
2778
3056
  flex-direction: column;
2779
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
3057
+ font-family:
3058
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
3059
+ sans-serif;
2780
3060
  color: #ddd;
2781
3061
  }
2782
3062
 
@@ -2952,10 +3232,18 @@ body.nvseditor-active .main {
2952
3232
  border-bottom: 1px solid #3a3a3a;
2953
3233
  }
2954
3234
 
2955
- .nvs-page-header.state-active { border-left: 4px solid #43a047; }
2956
- .nvs-page-header.state-full { border-left: 4px solid #ffa726; }
2957
- .nvs-page-header.state-freeing { border-left: 4px solid #ef5350; }
2958
- .nvs-page-header.state-other { border-left: 4px solid #666; }
3235
+ .nvs-page-header.state-active {
3236
+ border-left: 4px solid #43a047;
3237
+ }
3238
+ .nvs-page-header.state-full {
3239
+ border-left: 4px solid #ffa726;
3240
+ }
3241
+ .nvs-page-header.state-freeing {
3242
+ border-left: 4px solid #ef5350;
3243
+ }
3244
+ .nvs-page-header.state-other {
3245
+ border-left: 4px solid #666;
3246
+ }
2959
3247
 
2960
3248
  .nvs-page-state {
2961
3249
  font-weight: bold;
@@ -2965,10 +3253,22 @@ body.nvseditor-active .main {
2965
3253
  text-transform: uppercase;
2966
3254
  }
2967
3255
 
2968
- .state-active .nvs-page-state { background: #1b5e20; color: #a5d6a7; }
2969
- .state-full .nvs-page-state { background: #e65100; color: #ffcc80; }
2970
- .state-freeing .nvs-page-state { background: #b71c1c; color: #ef9a9a; }
2971
- .state-other .nvs-page-state { background: #424242; color: #999; }
3256
+ .state-active .nvs-page-state {
3257
+ background: #1b5e20;
3258
+ color: #a5d6a7;
3259
+ }
3260
+ .state-full .nvs-page-state {
3261
+ background: #e65100;
3262
+ color: #ffcc80;
3263
+ }
3264
+ .state-freeing .nvs-page-state {
3265
+ background: #b71c1c;
3266
+ color: #ef9a9a;
3267
+ }
3268
+ .state-other .nvs-page-state {
3269
+ background: #424242;
3270
+ color: #999;
3271
+ }
2972
3272
 
2973
3273
  /* NVS Namespace */
2974
3274
  .nvs-namespace {
@@ -86,8 +86,6 @@ export class ColoredConsole {
86
86
  const lineSpan = document.createElement("span");
87
87
  lineSpan.classList.add("line");
88
88
  const addSpan = (content) => {
89
- if (content === "")
90
- return;
91
89
  const span = document.createElement("span");
92
90
  if (this.state.bold)
93
91
  span.classList.add("log-bold");
@@ -175,7 +173,8 @@ export class ColoredConsole {
175
173
  }
176
174
  if (invalidSgr)
177
175
  continue;
178
- for (let ci = 0; ci < codes.length; ci++) {
176
+ let ci = 0;
177
+ while (ci < codes.length) {
179
178
  const code = codes[ci];
180
179
  switch (code) {
181
180
  case 0:
@@ -454,6 +453,7 @@ export class ColoredConsole {
454
453
  this.state.bgRgb = ANSI_256[15];
455
454
  break;
456
455
  }
456
+ ci++;
457
457
  }
458
458
  }
459
459
  addSpan(line.substring(i));
@@ -7,6 +7,10 @@
7
7
  // [HH:MM:SS.mmm] wall-clock bracket with millis
8
8
  // HH:MM:SS.mmm plain wall-clock
9
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)+/;
10
14
  export class TimestampTransformer {
11
15
  constructor() {
12
16
  this.deviceHasTimestamps = false;
@@ -25,11 +29,30 @@ export class TimestampTransformer {
25
29
  controller.enqueue(chunk);
26
30
  return;
27
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;
28
38
  const date = new Date();
29
39
  const h = date.getHours().toString().padStart(2, "0");
30
40
  const m = date.getMinutes().toString().padStart(2, "0");
31
41
  const s = date.getSeconds().toString().padStart(2, "0");
32
- controller.enqueue(`[${h}:${m}:${s}] ${chunk}`);
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);
33
56
  }
34
57
  reset() {
35
58
  this.deviceHasTimestamps = false;
@@ -3,13 +3,13 @@
3
3
  * This provides a truly standalone CLI without requiring Node.js installation
4
4
  */
5
5
 
6
- const { app } = require('electron');
7
- const path = require('path');
6
+ const { app } = require("electron");
7
+ const path = require("path");
8
8
 
9
9
  // Prevent Electron from showing any windows
10
10
  app.dock?.hide(); // macOS
11
- app.commandLine.appendSwitch('disable-gpu');
12
- app.commandLine.appendSwitch('no-sandbox');
11
+ app.commandLine.appendSwitch("disable-gpu");
12
+ app.commandLine.appendSwitch("no-sandbox");
13
13
 
14
14
  // Get CLI arguments (skip electron executable and script path)
15
15
  const cliArgs = process.argv.slice(app.isPackaged ? 1 : 2);
@@ -19,27 +19,27 @@ app.whenReady().then(async () => {
19
19
  try {
20
20
  // Dynamically import the CLI module
21
21
  const cliPath = app.isPackaged
22
- ? path.join(process.resourcesPath, 'app.asar', 'dist', 'cli.js')
23
- : path.join(__dirname, '..', 'dist', 'cli.js');
24
-
22
+ ? path.join(process.resourcesPath, "app.asar", "dist", "cli.js")
23
+ : path.join(__dirname, "..", "dist", "cli.js");
24
+
25
25
  // Set process.argv to match CLI expectations
26
26
  // In packaged app: argv[0] is the executable path
27
27
  // We want: ['node', 'esp32tool', ...actualArgs]
28
28
  const actualArgs = cliArgs;
29
- process.argv = ['node', 'esp32tool', ...actualArgs];
30
-
29
+ process.argv = ["node", "esp32tool", ...actualArgs];
30
+
31
31
  // Import CLI module
32
- const cliModule = await import('file://' + cliPath);
33
-
32
+ const cliModule = await import("file://" + cliPath);
33
+
34
34
  // Run the CLI
35
35
  await cliModule.runCLI();
36
-
36
+
37
37
  // Give a small delay for cleanup
38
38
  setTimeout(() => {
39
39
  app.exit(0);
40
40
  }, 100);
41
41
  } catch (error) {
42
- console.error('CLI Error:', error.message);
42
+ console.error("CLI Error:", error.message);
43
43
  if (process.env.DEBUG) {
44
44
  console.error(error.stack);
45
45
  }
@@ -50,25 +50,25 @@ app.whenReady().then(async () => {
50
50
  });
51
51
 
52
52
  // Handle app activation (macOS)
53
- app.on('activate', () => {
53
+ app.on("activate", () => {
54
54
  // Do nothing - we're a CLI app
55
55
  });
56
56
 
57
57
  // Quit when all windows are closed
58
- app.on('window-all-closed', () => {
58
+ app.on("window-all-closed", () => {
59
59
  // Do nothing - we don't have windows
60
60
  });
61
61
 
62
62
  // Handle errors
63
- process.on('uncaughtException', (error) => {
64
- console.error('Uncaught Exception:', error.message);
63
+ process.on("uncaughtException", (error) => {
64
+ console.error("Uncaught Exception:", error.message);
65
65
  if (process.env.DEBUG) {
66
66
  console.error(error.stack);
67
67
  }
68
68
  app.exit(1);
69
69
  });
70
70
 
71
- process.on('unhandledRejection', (reason) => {
72
- console.error('Unhandled Rejection:', reason);
71
+ process.on("unhandledRejection", (reason) => {
72
+ console.error("Unhandled Rejection:", reason);
73
73
  app.exit(1);
74
74
  });