@sensebox/flash-tool 0.1.0 → 1.0.0
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/dist/basic-OBQD77DM.js +6 -0
- package/dist/basic-OBQD77DM.js.map +1 -0
- package/dist/basic-PDQS3NQS.cjs +6 -0
- package/dist/basic-PDQS3NQS.cjs.map +1 -0
- package/dist/index.cjs +514 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +574 -112
- package/dist/index.js.map +1 -1
- package/dist/style.css +288 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/react/FlashTool.jsx
|
|
2
|
-
import
|
|
2
|
+
import React2, { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
-
import
|
|
4
|
+
import PropTypes2 from "prop-types";
|
|
5
5
|
|
|
6
6
|
// src/react/useFlashDevice.js
|
|
7
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -190,12 +190,17 @@ var DEFAULT_FLASH_OPTIONS = {
|
|
|
190
190
|
eraseAll: false,
|
|
191
191
|
usingUsbOtg: true
|
|
192
192
|
};
|
|
193
|
+
var KNOWN_USB_DEVICES = {
|
|
194
|
+
"303a:81b8": "senseBox MCU-S2 (ESP32-S2)",
|
|
195
|
+
"303a:2": "senseBox MCU-S2 (ESP32-S2 bootloader)"
|
|
196
|
+
};
|
|
193
197
|
function labelForPort(port) {
|
|
194
198
|
var _a;
|
|
195
199
|
const info = ((_a = port.getInfo) == null ? void 0 : _a.call(port)) ?? {};
|
|
196
200
|
const vendor = info.usbVendorId ? info.usbVendorId.toString(16).padStart(4, "0") : "????";
|
|
197
201
|
const product = info.usbProductId ? info.usbProductId.toString(16).padStart(4, "0") : "????";
|
|
198
|
-
|
|
202
|
+
const known = info.usbVendorId !== void 0 && info.usbProductId !== void 0 ? KNOWN_USB_DEVICES[`${info.usbVendorId.toString(16)}:${info.usbProductId.toString(16)}`] : void 0;
|
|
203
|
+
return known || `USB device (VID 0x${vendor} / PID 0x${product})`;
|
|
199
204
|
}
|
|
200
205
|
function useFlashDevice({
|
|
201
206
|
usbVendorId = DEFAULT_USB_VENDOR_ID,
|
|
@@ -222,10 +227,17 @@ function useFlashDevice({
|
|
|
222
227
|
filters: [{ usbVendorId }]
|
|
223
228
|
});
|
|
224
229
|
setPort(selectedPort);
|
|
225
|
-
setBootloaderPort(null);
|
|
226
230
|
setNeedsBootloaderPermission(false);
|
|
227
231
|
const label = labelForPort(selectedPort);
|
|
228
232
|
setDeviceLabel(label);
|
|
233
|
+
if (isRomBootloaderPort(selectedPort, usbVendorId)) {
|
|
234
|
+
setBootloaderPort(selectedPort);
|
|
235
|
+
appendLog(
|
|
236
|
+
"Board is already in bootloader mode - skipping reset.\n"
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
setBootloaderPort(null);
|
|
240
|
+
}
|
|
229
241
|
return selectedPort;
|
|
230
242
|
} catch (err) {
|
|
231
243
|
if ((err == null ? void 0 : err.name) !== "NotFoundError") {
|
|
@@ -233,7 +245,7 @@ function useFlashDevice({
|
|
|
233
245
|
}
|
|
234
246
|
return null;
|
|
235
247
|
}
|
|
236
|
-
}, [usbVendorId]);
|
|
248
|
+
}, [usbVendorId, appendLog]);
|
|
237
249
|
const disconnect = useCallback(() => {
|
|
238
250
|
setPort(null);
|
|
239
251
|
setBootloaderPort(null);
|
|
@@ -391,7 +403,7 @@ function useFlashDevice({
|
|
|
391
403
|
var LOADERS = {
|
|
392
404
|
circuitpy: () => import("./circuitpy-UZR4K6GK.js"),
|
|
393
405
|
ota: () => import("./ota-NY6U6TKC.js"),
|
|
394
|
-
basic: () => import("./basic-
|
|
406
|
+
basic: () => import("./basic-OBQD77DM.js"),
|
|
395
407
|
standard: () => import("./standard-BZVXWJHX.js")
|
|
396
408
|
};
|
|
397
409
|
var BUILTIN_FIRMWARE_TYPES = Object.keys(LOADERS);
|
|
@@ -410,15 +422,263 @@ async function loadBuiltinFirmware(type) {
|
|
|
410
422
|
return base64ToBytes(module.default);
|
|
411
423
|
}
|
|
412
424
|
|
|
425
|
+
// src/react/Dropdown.jsx
|
|
426
|
+
import React, { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
427
|
+
import PropTypes from "prop-types";
|
|
428
|
+
function Dropdown({
|
|
429
|
+
options,
|
|
430
|
+
value,
|
|
431
|
+
onChange,
|
|
432
|
+
disabled = false,
|
|
433
|
+
placeholder,
|
|
434
|
+
className,
|
|
435
|
+
"aria-label": ariaLabel
|
|
436
|
+
}) {
|
|
437
|
+
const [open, setOpen] = useState2(false);
|
|
438
|
+
const [activeIndex, setActiveIndex] = useState2(-1);
|
|
439
|
+
const rootRef = useRef2(null);
|
|
440
|
+
const triggerRef = useRef2(null);
|
|
441
|
+
const optionRefs = useRef2([]);
|
|
442
|
+
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
443
|
+
const selected = selectedIndex >= 0 ? options[selectedIndex] : null;
|
|
444
|
+
useEffect2(() => {
|
|
445
|
+
if (disabled && open) setOpen(false);
|
|
446
|
+
}, [disabled, open]);
|
|
447
|
+
useEffect2(() => {
|
|
448
|
+
if (!open) return;
|
|
449
|
+
const onDocPointerDown = (event) => {
|
|
450
|
+
if (rootRef.current && !rootRef.current.contains(event.target)) {
|
|
451
|
+
setOpen(false);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
document.addEventListener("pointerdown", onDocPointerDown);
|
|
455
|
+
return () => document.removeEventListener("pointerdown", onDocPointerDown);
|
|
456
|
+
}, [open]);
|
|
457
|
+
useEffect2(() => {
|
|
458
|
+
if (!open) return;
|
|
459
|
+
setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);
|
|
460
|
+
}, [open, selectedIndex]);
|
|
461
|
+
useEffect2(() => {
|
|
462
|
+
var _a;
|
|
463
|
+
if (!open || activeIndex < 0) return;
|
|
464
|
+
(_a = optionRefs.current[activeIndex]) == null ? void 0 : _a.focus();
|
|
465
|
+
}, [open, activeIndex]);
|
|
466
|
+
const commit = (index) => {
|
|
467
|
+
var _a;
|
|
468
|
+
const option = options[index];
|
|
469
|
+
if (!option || option.disabled) return;
|
|
470
|
+
onChange == null ? void 0 : onChange(option.value);
|
|
471
|
+
setOpen(false);
|
|
472
|
+
(_a = triggerRef.current) == null ? void 0 : _a.focus();
|
|
473
|
+
};
|
|
474
|
+
const handleTriggerKeyDown = (event) => {
|
|
475
|
+
if (disabled) return;
|
|
476
|
+
if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
477
|
+
event.preventDefault();
|
|
478
|
+
setOpen(true);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
const moveActive = (delta) => {
|
|
482
|
+
setActiveIndex((current) => {
|
|
483
|
+
const count = options.length;
|
|
484
|
+
let next = current;
|
|
485
|
+
for (let i = 0; i < count; i++) {
|
|
486
|
+
next = (next + delta + count) % count;
|
|
487
|
+
if (!options[next].disabled) return next;
|
|
488
|
+
}
|
|
489
|
+
return current;
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
const handleListKeyDown = (event) => {
|
|
493
|
+
var _a;
|
|
494
|
+
switch (event.key) {
|
|
495
|
+
case "ArrowDown":
|
|
496
|
+
event.preventDefault();
|
|
497
|
+
moveActive(1);
|
|
498
|
+
break;
|
|
499
|
+
case "ArrowUp":
|
|
500
|
+
event.preventDefault();
|
|
501
|
+
moveActive(-1);
|
|
502
|
+
break;
|
|
503
|
+
case "Home":
|
|
504
|
+
event.preventDefault();
|
|
505
|
+
setActiveIndex(0);
|
|
506
|
+
break;
|
|
507
|
+
case "End":
|
|
508
|
+
event.preventDefault();
|
|
509
|
+
setActiveIndex(options.length - 1);
|
|
510
|
+
break;
|
|
511
|
+
case "Enter":
|
|
512
|
+
case " ":
|
|
513
|
+
event.preventDefault();
|
|
514
|
+
commit(activeIndex);
|
|
515
|
+
break;
|
|
516
|
+
case "Escape":
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
setOpen(false);
|
|
519
|
+
(_a = triggerRef.current) == null ? void 0 : _a.focus();
|
|
520
|
+
break;
|
|
521
|
+
case "Tab":
|
|
522
|
+
setOpen(false);
|
|
523
|
+
break;
|
|
524
|
+
default:
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
return /* @__PURE__ */ React.createElement(
|
|
529
|
+
"div",
|
|
530
|
+
{
|
|
531
|
+
ref: rootRef,
|
|
532
|
+
className: `sbdropdown${open ? " sbdropdown--open" : ""}${disabled ? " sbdropdown--disabled" : ""}${className ? ` ${className}` : ""}`
|
|
533
|
+
},
|
|
534
|
+
/* @__PURE__ */ React.createElement(
|
|
535
|
+
"button",
|
|
536
|
+
{
|
|
537
|
+
ref: triggerRef,
|
|
538
|
+
type: "button",
|
|
539
|
+
className: "sbdropdown__trigger",
|
|
540
|
+
"aria-haspopup": "listbox",
|
|
541
|
+
"aria-expanded": open,
|
|
542
|
+
"aria-label": ariaLabel,
|
|
543
|
+
disabled,
|
|
544
|
+
onClick: () => setOpen((v) => !v),
|
|
545
|
+
onKeyDown: handleTriggerKeyDown
|
|
546
|
+
},
|
|
547
|
+
/* @__PURE__ */ React.createElement("span", { className: "sbdropdown__icon-slot" }, selected == null ? void 0 : selected.icon),
|
|
548
|
+
/* @__PURE__ */ React.createElement("span", { className: "sbdropdown__trigger-label" }, (selected == null ? void 0 : selected.label) || placeholder),
|
|
549
|
+
/* @__PURE__ */ React.createElement(IconChevron, { className: "sbdropdown__chevron" })
|
|
550
|
+
),
|
|
551
|
+
/* @__PURE__ */ React.createElement(
|
|
552
|
+
"ul",
|
|
553
|
+
{
|
|
554
|
+
className: "sbdropdown__menu",
|
|
555
|
+
role: "listbox",
|
|
556
|
+
"aria-label": ariaLabel,
|
|
557
|
+
onKeyDown: handleListKeyDown
|
|
558
|
+
},
|
|
559
|
+
options.map((option, index) => /* @__PURE__ */ React.createElement("li", { key: option.value, role: "presentation" }, /* @__PURE__ */ React.createElement(
|
|
560
|
+
"button",
|
|
561
|
+
{
|
|
562
|
+
ref: (el) => {
|
|
563
|
+
optionRefs.current[index] = el;
|
|
564
|
+
},
|
|
565
|
+
type: "button",
|
|
566
|
+
role: "option",
|
|
567
|
+
"aria-selected": option.value === value,
|
|
568
|
+
tabIndex: -1,
|
|
569
|
+
className: `sbdropdown__option${option.value === value ? " sbdropdown__option--selected" : ""}${index === activeIndex ? " sbdropdown__option--active" : ""}`,
|
|
570
|
+
disabled: option.disabled,
|
|
571
|
+
onClick: () => commit(index),
|
|
572
|
+
onMouseEnter: () => setActiveIndex(index)
|
|
573
|
+
},
|
|
574
|
+
/* @__PURE__ */ React.createElement("span", { className: "sbdropdown__icon-slot" }, option.icon),
|
|
575
|
+
/* @__PURE__ */ React.createElement("span", { className: "sbdropdown__option-label" }, option.label),
|
|
576
|
+
option.value === value && /* @__PURE__ */ React.createElement(IconCheck, { className: "sbdropdown__option-check" })
|
|
577
|
+
)))
|
|
578
|
+
)
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
function IconChevron({ className }) {
|
|
582
|
+
return /* @__PURE__ */ React.createElement(
|
|
583
|
+
"svg",
|
|
584
|
+
{
|
|
585
|
+
className,
|
|
586
|
+
viewBox: "0 0 10 6",
|
|
587
|
+
width: "10",
|
|
588
|
+
height: "6",
|
|
589
|
+
fill: "none",
|
|
590
|
+
stroke: "currentColor",
|
|
591
|
+
strokeWidth: "1.5",
|
|
592
|
+
strokeLinecap: "round",
|
|
593
|
+
strokeLinejoin: "round",
|
|
594
|
+
"aria-hidden": "true"
|
|
595
|
+
},
|
|
596
|
+
/* @__PURE__ */ React.createElement("path", { d: "M1 1l4 4 4-4" })
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
function IconCheck({ className }) {
|
|
600
|
+
return /* @__PURE__ */ React.createElement(
|
|
601
|
+
"svg",
|
|
602
|
+
{
|
|
603
|
+
className,
|
|
604
|
+
viewBox: "0 0 16 16",
|
|
605
|
+
width: "12",
|
|
606
|
+
height: "12",
|
|
607
|
+
fill: "none",
|
|
608
|
+
stroke: "currentColor",
|
|
609
|
+
strokeWidth: "2",
|
|
610
|
+
strokeLinecap: "round",
|
|
611
|
+
strokeLinejoin: "round",
|
|
612
|
+
"aria-hidden": "true"
|
|
613
|
+
},
|
|
614
|
+
/* @__PURE__ */ React.createElement("path", { d: "M3 8.5 6.5 12 13 4.5" })
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
Dropdown.propTypes = {
|
|
618
|
+
options: PropTypes.arrayOf(
|
|
619
|
+
PropTypes.shape({
|
|
620
|
+
value: PropTypes.string.isRequired,
|
|
621
|
+
label: PropTypes.node.isRequired,
|
|
622
|
+
icon: PropTypes.node,
|
|
623
|
+
disabled: PropTypes.bool
|
|
624
|
+
})
|
|
625
|
+
).isRequired,
|
|
626
|
+
value: PropTypes.string,
|
|
627
|
+
onChange: PropTypes.func,
|
|
628
|
+
disabled: PropTypes.bool,
|
|
629
|
+
placeholder: PropTypes.string,
|
|
630
|
+
className: PropTypes.string,
|
|
631
|
+
"aria-label": PropTypes.string
|
|
632
|
+
};
|
|
633
|
+
|
|
413
634
|
// src/react/FlashTool.jsx
|
|
414
635
|
var builtinFirmwareCache = /* @__PURE__ */ new Map();
|
|
415
636
|
var FIRMWARE_TYPES = ["circuitpy", "ota", "basic", "standard"];
|
|
637
|
+
var FIRMWARE_ALIASES = {
|
|
638
|
+
circuitpython: "circuitpy"
|
|
639
|
+
};
|
|
640
|
+
function resolveFirmwareType(value) {
|
|
641
|
+
if (!value) return void 0;
|
|
642
|
+
return FIRMWARE_ALIASES[value] || value;
|
|
643
|
+
}
|
|
416
644
|
var BUILTIN_FIRMWARE_FLASH_OVERRIDES = {
|
|
417
645
|
circuitpy: { address: 0, eraseAll: true },
|
|
418
646
|
ota: { address: 0, eraseAll: true },
|
|
419
647
|
basic: { address: 0, eraseAll: true },
|
|
420
648
|
standard: { address: 0, eraseAll: true }
|
|
421
649
|
};
|
|
650
|
+
var DEFAULT_FIRMWARE_DESCRIPTIONS = {
|
|
651
|
+
circuitpy: {
|
|
652
|
+
text: "Install CircuitPython on your senseBox now! For a more detailed tutorial, check out our",
|
|
653
|
+
linkText: "docs",
|
|
654
|
+
linkUrl: "https://docs.sensebox.de/docs/editors/circuitpython/circuitpython_esp32/?board=edus2"
|
|
655
|
+
},
|
|
656
|
+
ota: {
|
|
657
|
+
text: "Flashes the senseBox OTA firmware, which lets you update your senseBox wirelessly afterwards."
|
|
658
|
+
},
|
|
659
|
+
basic: {
|
|
660
|
+
text: "A minimal starter sketch - a good starting point for your own senseBox projects."
|
|
661
|
+
},
|
|
662
|
+
standard: {
|
|
663
|
+
text: "The TinyUF2 bootloader, ready to read out and upload data from your connected sensors."
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
var DEFAULT_FIRMWARE_DESCRIPTIONS_DE = {
|
|
667
|
+
circuitpy: {
|
|
668
|
+
text: "Installiere jetzt CircuitPython auf deiner senseBox! Ein ausf\xFChrliches Tutorial findest du in unserer",
|
|
669
|
+
linkText: "Dokumentation",
|
|
670
|
+
linkUrl: "https://docs.sensebox.de/docs/editors/circuitpython/circuitpython_esp32/?board=edus2"
|
|
671
|
+
},
|
|
672
|
+
ota: {
|
|
673
|
+
text: "Spielt die senseBox-OTA-Firmware auf, mit der du deine senseBox anschlie\xDFend drahtlos aktualisieren kannst."
|
|
674
|
+
},
|
|
675
|
+
basic: {
|
|
676
|
+
text: "Ein minimales Starter-Sketch - ein guter Ausgangspunkt f\xFCr eigene senseBox-Projekte."
|
|
677
|
+
},
|
|
678
|
+
standard: {
|
|
679
|
+
text: "Das Standard-senseBox-Sketch, bereit zum Auslesen und Hochladen von Daten deiner angeschlossenen Sensoren."
|
|
680
|
+
}
|
|
681
|
+
};
|
|
422
682
|
var DEFAULT_STRINGS = {
|
|
423
683
|
unsupported: "Web Serial is not supported in this browser.",
|
|
424
684
|
connect: "Connect board",
|
|
@@ -432,16 +692,26 @@ var DEFAULT_STRINGS = {
|
|
|
432
692
|
flash: "Upload",
|
|
433
693
|
flashing: "Uploading...",
|
|
434
694
|
done: "Upload complete",
|
|
695
|
+
newUpload: "Start new upload",
|
|
435
696
|
error: "Upload failed",
|
|
436
697
|
retry: "Retry",
|
|
437
698
|
disconnect: "Disconnect",
|
|
438
699
|
showLog: "Show log",
|
|
439
700
|
hideLog: "Hide log",
|
|
701
|
+
logPlaceholder: 'Click "Connect board" to see the log here.',
|
|
440
702
|
sketchLabel: "Sketch",
|
|
703
|
+
step1Title: "Connect your board",
|
|
704
|
+
step1Hint: "Plug in your senseBox, then pick it from the browser's device list.",
|
|
705
|
+
step2Title: "Enter bootloader mode",
|
|
706
|
+
step2Hint: "Put the board into upload mode.",
|
|
707
|
+
step2HintPermission: "Select the bootloader port the browser is asking for.",
|
|
708
|
+
step2Done: "Bootloader ready",
|
|
709
|
+
step3Title: "Upload firmware",
|
|
710
|
+
step3Hint: "Waiting for the previous steps to finish.",
|
|
441
711
|
firmwareCircuitpy: "CircuitPython",
|
|
442
712
|
firmwareOta: "senseBox OTA",
|
|
443
|
-
firmwareBasic: "
|
|
444
|
-
firmwareStandard: "
|
|
713
|
+
firmwareBasic: "senseBox:basic",
|
|
714
|
+
firmwareStandard: "TinyUF2",
|
|
445
715
|
help: "Help",
|
|
446
716
|
helpTitle: "How it works",
|
|
447
717
|
helpStep1: "Connect your senseBox",
|
|
@@ -450,15 +720,60 @@ var DEFAULT_STRINGS = {
|
|
|
450
720
|
helpStep4: "Start the upload",
|
|
451
721
|
helpClose: "Close"
|
|
452
722
|
};
|
|
723
|
+
var DEFAULT_STRINGS_DE = {
|
|
724
|
+
unsupported: "Web Serial wird von diesem Browser nicht unterst\xFCtzt.",
|
|
725
|
+
connect: "Board verbinden",
|
|
726
|
+
connecting: "Verbinde...",
|
|
727
|
+
connected: "senseBox verbunden",
|
|
728
|
+
prepare: "Upload vorbereiten",
|
|
729
|
+
preparing: "Bereite vor...",
|
|
730
|
+
grantPermission: "Bootloader-Port ausw\xE4hlen",
|
|
731
|
+
waitingForBinary: "Warte auf Firmware...",
|
|
732
|
+
loadingFirmware: "Firmware wird geladen...",
|
|
733
|
+
flash: "Hochladen",
|
|
734
|
+
flashing: "Wird hochgeladen...",
|
|
735
|
+
done: "Upload abgeschlossen",
|
|
736
|
+
newUpload: "Neuen Upload starten",
|
|
737
|
+
error: "Upload fehlgeschlagen",
|
|
738
|
+
retry: "Erneut versuchen",
|
|
739
|
+
disconnect: "Trennen",
|
|
740
|
+
showLog: "Protokoll anzeigen",
|
|
741
|
+
hideLog: "Protokoll ausblenden",
|
|
742
|
+
logPlaceholder: 'Klicke auf "Board verbinden", um das Protokoll hier zu sehen.',
|
|
743
|
+
sketchLabel: "Sketch",
|
|
744
|
+
step1Title: "Board verbinden",
|
|
745
|
+
step1Hint: "Schlie\xDFe deine senseBox an und w\xE4hle sie aus der Ger\xE4teliste des Browsers aus.",
|
|
746
|
+
step2Title: "Bootloader-Modus aktivieren",
|
|
747
|
+
step2Hint: "Versetze das Board in den Upload-Modus.",
|
|
748
|
+
step2HintPermission: "W\xE4hle den Bootloader-Port aus, nach dem der Browser fragt.",
|
|
749
|
+
step2Done: "Bootloader bereit",
|
|
750
|
+
step3Title: "Firmware hochladen",
|
|
751
|
+
step3Hint: "Warte auf die vorherigen Schritte.",
|
|
752
|
+
firmwareCircuitpy: "CircuitPython",
|
|
753
|
+
firmwareOta: "senseBox OTA",
|
|
754
|
+
firmwareBasic: "senseBox:basic",
|
|
755
|
+
firmwareStandard: "TinyUF2",
|
|
756
|
+
help: "Hilfe",
|
|
757
|
+
helpTitle: "So funktioniert's",
|
|
758
|
+
helpStep1: "Verbinde deine senseBox",
|
|
759
|
+
helpStep2: "W\xE4hle einen Sketch aus",
|
|
760
|
+
helpStep3: "Erteile deinem Browser die Berechtigung",
|
|
761
|
+
helpStep4: "Starte den Upload",
|
|
762
|
+
helpClose: "Schlie\xDFen"
|
|
763
|
+
};
|
|
453
764
|
function FlashTool({
|
|
454
765
|
binary,
|
|
455
766
|
firmwares,
|
|
767
|
+
firmware,
|
|
456
768
|
firmwareType,
|
|
457
769
|
defaultFirmwareType,
|
|
458
770
|
firmwareFlashOptions,
|
|
771
|
+
firmwareDescriptions,
|
|
459
772
|
usbVendorId,
|
|
460
773
|
flashOptions,
|
|
461
774
|
autoFlash = true,
|
|
775
|
+
expand = false,
|
|
776
|
+
language = "en",
|
|
462
777
|
strings,
|
|
463
778
|
className,
|
|
464
779
|
onConnected,
|
|
@@ -467,19 +782,26 @@ function FlashTool({
|
|
|
467
782
|
onError,
|
|
468
783
|
onFirmwareTypeChange
|
|
469
784
|
}) {
|
|
470
|
-
const merged = {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const [
|
|
475
|
-
|
|
785
|
+
const merged = {
|
|
786
|
+
...language === "de" ? DEFAULT_STRINGS_DE : DEFAULT_STRINGS,
|
|
787
|
+
...strings
|
|
788
|
+
};
|
|
789
|
+
const [logOpen, setLogOpen] = React2.useState(expand);
|
|
790
|
+
const [helpOpen, setHelpOpen] = React2.useState(false);
|
|
791
|
+
const [busy, setBusy] = React2.useState(null);
|
|
792
|
+
const lockedFirmwareType = resolveFirmwareType(firmwareType || firmware);
|
|
793
|
+
const [selectedType, setSelectedType] = React2.useState(
|
|
794
|
+
lockedFirmwareType || defaultFirmwareType || FIRMWARE_TYPES[0]
|
|
476
795
|
);
|
|
477
|
-
const firmwareLocked = Boolean(
|
|
796
|
+
const firmwareLocked = Boolean(lockedFirmwareType);
|
|
478
797
|
const showFirmwarePicker = Boolean(firmwares) || !binary || firmwareLocked;
|
|
479
|
-
|
|
480
|
-
if (
|
|
481
|
-
}, [
|
|
482
|
-
|
|
798
|
+
useEffect3(() => {
|
|
799
|
+
if (lockedFirmwareType) setSelectedType(lockedFirmwareType);
|
|
800
|
+
}, [lockedFirmwareType]);
|
|
801
|
+
useEffect3(() => {
|
|
802
|
+
if (expand) setLogOpen(true);
|
|
803
|
+
}, [expand]);
|
|
804
|
+
useEffect3(() => {
|
|
483
805
|
if (!helpOpen) return;
|
|
484
806
|
const onKeyDown = (event) => {
|
|
485
807
|
if (event.key === "Escape") setHelpOpen(false);
|
|
@@ -505,11 +827,11 @@ function FlashTool({
|
|
|
505
827
|
resetFlashState
|
|
506
828
|
} = useFlashDevice({ usbVendorId, flashOptions });
|
|
507
829
|
const explicitBinary = firmwares ? firmwares[selectedType] : binary;
|
|
508
|
-
const [builtinBinary, setBuiltinBinary] =
|
|
509
|
-
const [builtinLoading, setBuiltinLoading] =
|
|
510
|
-
const [builtinError, setBuiltinError] =
|
|
511
|
-
const [builtinRetryToken, setBuiltinRetryToken] =
|
|
512
|
-
|
|
830
|
+
const [builtinBinary, setBuiltinBinary] = React2.useState(null);
|
|
831
|
+
const [builtinLoading, setBuiltinLoading] = React2.useState(false);
|
|
832
|
+
const [builtinError, setBuiltinError] = React2.useState(null);
|
|
833
|
+
const [builtinRetryToken, setBuiltinRetryToken] = React2.useState(0);
|
|
834
|
+
useEffect3(() => {
|
|
513
835
|
if (!supported || explicitBinary) return;
|
|
514
836
|
const cached = builtinFirmwareCache.get(selectedType);
|
|
515
837
|
if (cached) {
|
|
@@ -542,37 +864,51 @@ function FlashTool({
|
|
|
542
864
|
};
|
|
543
865
|
}, [supported, explicitBinary, selectedType, onError, builtinRetryToken]);
|
|
544
866
|
const resolvedBinary = explicitBinary || builtinBinary || void 0;
|
|
545
|
-
const flashOverrides =
|
|
867
|
+
const flashOverrides = React2.useMemo(
|
|
546
868
|
() => showFirmwarePicker ? {
|
|
547
869
|
...BUILTIN_FIRMWARE_FLASH_OVERRIDES[selectedType],
|
|
548
870
|
...firmwareFlashOptions == null ? void 0 : firmwareFlashOptions[selectedType]
|
|
549
871
|
} : {},
|
|
550
872
|
[showFirmwarePicker, firmwareFlashOptions, selectedType]
|
|
551
873
|
);
|
|
552
|
-
const latestLogLine =
|
|
874
|
+
const latestLogLine = React2.useMemo(() => {
|
|
553
875
|
if (!log) return "";
|
|
554
876
|
const lines = log.split("\n").filter((line) => line.trim().length > 0);
|
|
555
|
-
|
|
877
|
+
const lastLine = lines[lines.length - 1] || "";
|
|
878
|
+
return lastLine.replace(/\s*\(\d+ ?%\)\s*$/, "");
|
|
556
879
|
}, [log]);
|
|
557
|
-
const
|
|
558
|
-
|
|
880
|
+
const activeFirmwareDescription = showFirmwarePicker ? {
|
|
881
|
+
...language === "de" ? DEFAULT_FIRMWARE_DESCRIPTIONS_DE[selectedType] : DEFAULT_FIRMWARE_DESCRIPTIONS[selectedType],
|
|
882
|
+
...firmwareDescriptions == null ? void 0 : firmwareDescriptions[selectedType]
|
|
883
|
+
} : null;
|
|
884
|
+
const handleFirmwareChange = (value) => {
|
|
559
885
|
setSelectedType(value);
|
|
560
886
|
onFirmwareTypeChange == null ? void 0 : onFirmwareTypeChange(value);
|
|
561
887
|
};
|
|
888
|
+
const firmwareOptions = FIRMWARE_TYPES.map((type) => ({
|
|
889
|
+
value: type,
|
|
890
|
+
label: merged[`firmware${type.charAt(0).toUpperCase()}${type.slice(1)}`],
|
|
891
|
+
icon: FIRMWARE_ICONS[type]
|
|
892
|
+
}));
|
|
562
893
|
const handleRetryFirmwareLoad = () => {
|
|
563
894
|
setBuiltinRetryToken((token) => token + 1);
|
|
564
895
|
};
|
|
565
|
-
const
|
|
566
|
-
|
|
896
|
+
const logRef = useRef3(null);
|
|
897
|
+
useEffect3(() => {
|
|
898
|
+
if (!logOpen || !logRef.current) return;
|
|
899
|
+
logRef.current.scrollTop = logRef.current.scrollHeight;
|
|
900
|
+
}, [log, logOpen]);
|
|
901
|
+
const prevConnected = useRef3(connected);
|
|
902
|
+
useEffect3(() => {
|
|
567
903
|
if (connected && !prevConnected.current) onConnected == null ? void 0 : onConnected(deviceLabel);
|
|
568
904
|
if (!connected && prevConnected.current) onDisconnected == null ? void 0 : onDisconnected();
|
|
569
905
|
prevConnected.current = connected;
|
|
570
906
|
}, [connected, deviceLabel, onConnected, onDisconnected]);
|
|
571
|
-
|
|
907
|
+
useEffect3(() => {
|
|
572
908
|
if (status === "done") onFlashed == null ? void 0 : onFlashed();
|
|
573
909
|
if (status === "error" && error) onError == null ? void 0 : onError(new Error(error));
|
|
574
910
|
}, [status, error, onFlashed, onError]);
|
|
575
|
-
|
|
911
|
+
useEffect3(() => {
|
|
576
912
|
if (autoFlash && resolvedBinary && connected && !bootloaderReady && status === "idle" && !needsBootloaderPermission) {
|
|
577
913
|
flash(resolvedBinary, flashOverrides);
|
|
578
914
|
}
|
|
@@ -587,7 +923,7 @@ function FlashTool({
|
|
|
587
923
|
flash
|
|
588
924
|
]);
|
|
589
925
|
if (!supported) {
|
|
590
|
-
return /* @__PURE__ */
|
|
926
|
+
return /* @__PURE__ */ React2.createElement("div", { className: `sbflash sbflash--unsupported ${className || ""}` }, merged.unsupported);
|
|
591
927
|
}
|
|
592
928
|
const handleConnect = async () => {
|
|
593
929
|
setBusy("connect");
|
|
@@ -621,30 +957,26 @@ function FlashTool({
|
|
|
621
957
|
resetFlashState();
|
|
622
958
|
if (resolvedBinary) flash(resolvedBinary, flashOverrides);
|
|
623
959
|
};
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
className: "sbflash__label",
|
|
633
|
-
title: connected ? deviceLabel : void 0
|
|
634
|
-
},
|
|
635
|
-
connected ? merged.connected : merged.connect
|
|
636
|
-
)), /* @__PURE__ */ React.createElement("div", { className: "sbflash__field" }, /* @__PURE__ */ React.createElement("div", { className: "sbflash__field-head" }, /* @__PURE__ */ React.createElement("span", { className: "sbflash__field-label" }, merged.sketchLabel), /* @__PURE__ */ React.createElement("div", { className: "sbflash__icon-row" }, log && /* @__PURE__ */ React.createElement(
|
|
960
|
+
const flashComplete = status === "done";
|
|
961
|
+
const step1Status = flashComplete ? "done" : connected ? "done" : "active";
|
|
962
|
+
const step2Status = flashComplete ? "done" : !connected ? "pending" : bootloaderReady ? "done" : "active";
|
|
963
|
+
const step3Status = flashComplete ? "done" : !bootloaderReady ? "pending" : status === "error" ? "error" : "active";
|
|
964
|
+
const handleStartNew = () => {
|
|
965
|
+
resetFlashState();
|
|
966
|
+
};
|
|
967
|
+
return /* @__PURE__ */ React2.createElement("div", { className: `sbflash ${className || ""}` }, /* @__PURE__ */ React2.createElement("div", { className: "sbflash__field" }, /* @__PURE__ */ React2.createElement("div", { className: "sbflash__field-head" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__field-label" }, merged.sketchLabel), /* @__PURE__ */ React2.createElement("div", { className: "sbflash__icon-row" }, /* @__PURE__ */ React2.createElement(
|
|
637
968
|
"button",
|
|
638
969
|
{
|
|
639
970
|
type: "button",
|
|
640
971
|
className: `sbflash__icon-button${logOpen ? " sbflash__icon-button--active" : ""}`,
|
|
641
|
-
onClick: () => setLogOpen((v) => !v),
|
|
972
|
+
onClick: () => !expand && setLogOpen((v) => !v),
|
|
973
|
+
disabled: expand,
|
|
642
974
|
"aria-pressed": logOpen,
|
|
643
975
|
"aria-label": logOpen ? merged.hideLog : merged.showLog,
|
|
644
976
|
title: logOpen ? merged.hideLog : merged.showLog
|
|
645
977
|
},
|
|
646
|
-
/* @__PURE__ */
|
|
647
|
-
), connected && /* @__PURE__ */
|
|
978
|
+
/* @__PURE__ */ React2.createElement(IconTerminal, null)
|
|
979
|
+
), connected && /* @__PURE__ */ React2.createElement(
|
|
648
980
|
"button",
|
|
649
981
|
{
|
|
650
982
|
type: "button",
|
|
@@ -653,8 +985,8 @@ function FlashTool({
|
|
|
653
985
|
"aria-label": merged.disconnect,
|
|
654
986
|
title: merged.disconnect
|
|
655
987
|
},
|
|
656
|
-
/* @__PURE__ */
|
|
657
|
-
), /* @__PURE__ */
|
|
988
|
+
/* @__PURE__ */ React2.createElement(IconDisconnect, null)
|
|
989
|
+
), /* @__PURE__ */ React2.createElement(
|
|
658
990
|
"button",
|
|
659
991
|
{
|
|
660
992
|
type: "button",
|
|
@@ -663,17 +995,17 @@ function FlashTool({
|
|
|
663
995
|
"aria-label": merged.help,
|
|
664
996
|
title: merged.help
|
|
665
997
|
},
|
|
666
|
-
/* @__PURE__ */
|
|
667
|
-
))), showFirmwarePicker && /* @__PURE__ */
|
|
668
|
-
|
|
998
|
+
/* @__PURE__ */ React2.createElement(IconHelp, null)
|
|
999
|
+
))), showFirmwarePicker && /* @__PURE__ */ React2.createElement(
|
|
1000
|
+
Dropdown,
|
|
669
1001
|
{
|
|
670
|
-
|
|
1002
|
+
options: firmwareOptions,
|
|
671
1003
|
value: selectedType,
|
|
672
1004
|
onChange: handleFirmwareChange,
|
|
673
|
-
disabled: firmwareLocked || status === "flashing"
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
)
|
|
1005
|
+
disabled: firmwareLocked || status === "flashing",
|
|
1006
|
+
"aria-label": merged.sketchLabel
|
|
1007
|
+
}
|
|
1008
|
+
), builtinError && /* @__PURE__ */ React2.createElement("div", { className: "sbflash__field-error-row" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__field-error" }, builtinError), /* @__PURE__ */ React2.createElement(
|
|
677
1009
|
"button",
|
|
678
1010
|
{
|
|
679
1011
|
type: "button",
|
|
@@ -681,14 +1013,22 @@ function FlashTool({
|
|
|
681
1013
|
onClick: handleRetryFirmwareLoad
|
|
682
1014
|
},
|
|
683
1015
|
merged.retry
|
|
684
|
-
))),
|
|
685
|
-
|
|
1016
|
+
)), (activeFirmwareDescription == null ? void 0 : activeFirmwareDescription.text) && /* @__PURE__ */ React2.createElement("p", { className: "sbflash__field-description" }, activeFirmwareDescription.text, activeFirmwareDescription.linkUrl && /* @__PURE__ */ React2.createElement(React2.Fragment, null, " ", /* @__PURE__ */ React2.createElement(
|
|
1017
|
+
"a",
|
|
1018
|
+
{
|
|
1019
|
+
href: activeFirmwareDescription.linkUrl,
|
|
1020
|
+
target: "_blank",
|
|
1021
|
+
rel: "noopener noreferrer"
|
|
1022
|
+
},
|
|
1023
|
+
activeFirmwareDescription.linkText || activeFirmwareDescription.linkUrl
|
|
1024
|
+
)))), helpOpen && createPortal(
|
|
1025
|
+
/* @__PURE__ */ React2.createElement(
|
|
686
1026
|
"div",
|
|
687
1027
|
{
|
|
688
1028
|
className: "sbflash__modal-overlay",
|
|
689
1029
|
onClick: () => setHelpOpen(false)
|
|
690
1030
|
},
|
|
691
|
-
/* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ React2.createElement(
|
|
692
1032
|
"div",
|
|
693
1033
|
{
|
|
694
1034
|
className: "sbflash__modal",
|
|
@@ -697,7 +1037,7 @@ function FlashTool({
|
|
|
697
1037
|
"aria-label": merged.helpTitle,
|
|
698
1038
|
onClick: (event) => event.stopPropagation()
|
|
699
1039
|
},
|
|
700
|
-
/* @__PURE__ */
|
|
1040
|
+
/* @__PURE__ */ React2.createElement("div", { className: "sbflash__modal-head" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__modal-title" }, merged.helpTitle), /* @__PURE__ */ React2.createElement(
|
|
701
1041
|
"button",
|
|
702
1042
|
{
|
|
703
1043
|
type: "button",
|
|
@@ -707,16 +1047,21 @@ function FlashTool({
|
|
|
707
1047
|
},
|
|
708
1048
|
"\xD7"
|
|
709
1049
|
)),
|
|
710
|
-
/* @__PURE__ */
|
|
1050
|
+
/* @__PURE__ */ React2.createElement("ol", { className: "sbflash__modal-steps" }, [
|
|
711
1051
|
merged.helpStep1,
|
|
712
1052
|
merged.helpStep2,
|
|
713
1053
|
merged.helpStep3,
|
|
714
1054
|
merged.helpStep4
|
|
715
|
-
].map((step, index) => /* @__PURE__ */
|
|
1055
|
+
].map((step, index) => /* @__PURE__ */ React2.createElement("li", { key: index, className: "sbflash__modal-step" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__modal-step-index" }, index + 1), /* @__PURE__ */ React2.createElement("span", null, step))))
|
|
716
1056
|
)
|
|
717
1057
|
),
|
|
718
1058
|
document.body
|
|
719
|
-
),
|
|
1059
|
+
), /* @__PURE__ */ React2.createElement("ol", { className: "sbflash__stepper" }, /* @__PURE__ */ React2.createElement("li", { className: `sbflash__step sbflash__step--${step1Status}` }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-marker" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-index" }, step1Status === "done" ? /* @__PURE__ */ React2.createElement(IconCheck2, null) : 1), /* @__PURE__ */ React2.createElement(
|
|
1060
|
+
"span",
|
|
1061
|
+
{
|
|
1062
|
+
className: `sbflash__step-line${step1Status === "done" ? " sbflash__step-line--done" : ""}`
|
|
1063
|
+
}
|
|
1064
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "sbflash__step-body" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-title" }, merged.step1Title), !connected && !flashComplete ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-hint" }, merged.step1Hint), /* @__PURE__ */ React2.createElement(
|
|
720
1065
|
"button",
|
|
721
1066
|
{
|
|
722
1067
|
type: "button",
|
|
@@ -725,7 +1070,12 @@ function FlashTool({
|
|
|
725
1070
|
disabled: busy === "connect"
|
|
726
1071
|
},
|
|
727
1072
|
busy === "connect" ? merged.connecting : merged.connect
|
|
728
|
-
), connected
|
|
1073
|
+
)) : /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-detail", title: deviceLabel }, deviceLabel || merged.connected))), /* @__PURE__ */ React2.createElement("li", { className: `sbflash__step sbflash__step--${step2Status}` }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-marker" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-index" }, step2Status === "done" ? /* @__PURE__ */ React2.createElement(IconCheck2, null) : 2), /* @__PURE__ */ React2.createElement(
|
|
1074
|
+
"span",
|
|
1075
|
+
{
|
|
1076
|
+
className: `sbflash__step-line${step2Status === "done" ? " sbflash__step-line--done" : ""}`
|
|
1077
|
+
}
|
|
1078
|
+
)), /* @__PURE__ */ React2.createElement("div", { className: "sbflash__step-body" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-title" }, merged.step2Title), step2Status === "active" && needsBootloaderPermission && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-hint" }, merged.step2HintPermission), /* @__PURE__ */ React2.createElement(
|
|
729
1079
|
"button",
|
|
730
1080
|
{
|
|
731
1081
|
type: "button",
|
|
@@ -734,16 +1084,7 @@ function FlashTool({
|
|
|
734
1084
|
disabled: busy === "grant"
|
|
735
1085
|
},
|
|
736
1086
|
merged.grantPermission
|
|
737
|
-
),
|
|
738
|
-
"button",
|
|
739
|
-
{
|
|
740
|
-
type: "button",
|
|
741
|
-
className: "sbflash__button sbflash__button--secondary",
|
|
742
|
-
onClick: handlePrepare,
|
|
743
|
-
disabled: busy === "prepare" || status === "flashing"
|
|
744
|
-
},
|
|
745
|
-
busy === "prepare" ? merged.preparing : merged.prepare
|
|
746
|
-
), connected && bootloaderReady && status !== "flashing" && status !== "error" && /* @__PURE__ */ React.createElement(
|
|
1087
|
+
)), step2Status === "active" && !needsBootloaderPermission && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-hint" }, merged.step2Hint)), step2Status === "done" && /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-detail" }, merged.step2Done), step2Status === "pending" && /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-hint" }, merged.step2Hint))), /* @__PURE__ */ React2.createElement("li", { className: `sbflash__step sbflash__step--${step3Status}` }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-marker" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-index" }, step3Status === "done" ? /* @__PURE__ */ React2.createElement(IconCheck2, null) : 3)), /* @__PURE__ */ React2.createElement("div", { className: "sbflash__step-body" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-title" }, merged.step3Title), step3Status === "pending" && /* @__PURE__ */ React2.createElement("span", { className: "sbflash__step-hint" }, merged.step3Hint), step3Status === "active" && status !== "flashing" && /* @__PURE__ */ React2.createElement(
|
|
747
1088
|
"button",
|
|
748
1089
|
{
|
|
749
1090
|
type: "button",
|
|
@@ -752,13 +1093,21 @@ function FlashTool({
|
|
|
752
1093
|
disabled: !resolvedBinary
|
|
753
1094
|
},
|
|
754
1095
|
resolvedBinary ? merged.flash : builtinLoading ? merged.loadingFirmware : merged.waitingForBinary
|
|
755
|
-
), status === "flashing" && (progress > 0 ? /* @__PURE__ */
|
|
1096
|
+
), status === "flashing" && (progress > 0 ? /* @__PURE__ */ React2.createElement("div", { className: "sbflash__progress-wrap" }, /* @__PURE__ */ React2.createElement("div", { className: "sbflash__progress" }, /* @__PURE__ */ React2.createElement("div", { className: "sbflash__progress-track" }, /* @__PURE__ */ React2.createElement(
|
|
756
1097
|
"div",
|
|
757
1098
|
{
|
|
758
1099
|
className: "sbflash__progress-fill",
|
|
759
1100
|
style: { width: `${Math.min(100, progress)}%` }
|
|
760
1101
|
}
|
|
761
|
-
)), /* @__PURE__ */
|
|
1102
|
+
)), /* @__PURE__ */ React2.createElement("span", { className: "sbflash__progress-value" }, progress, "%")), latestLogLine && /* @__PURE__ */ React2.createElement("span", { className: "sbflash__progress-detail" }, latestLogLine)) : /* @__PURE__ */ React2.createElement("div", { className: "sbflash__task" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__task-spinner" }), /* @__PURE__ */ React2.createElement("span", { className: "sbflash__task-label" }, latestLogLine || merged.flashing))), status === "done" && /* @__PURE__ */ React2.createElement("div", { className: "sbflash__done-banner", role: "status" }, /* @__PURE__ */ React2.createElement("span", { className: "sbflash__done-icon" }, /* @__PURE__ */ React2.createElement(IconCheck2, null)), /* @__PURE__ */ React2.createElement("span", { className: "sbflash__done-text" }, merged.done), /* @__PURE__ */ React2.createElement(
|
|
1103
|
+
"button",
|
|
1104
|
+
{
|
|
1105
|
+
type: "button",
|
|
1106
|
+
className: "sbflash__button sbflash__button--secondary",
|
|
1107
|
+
onClick: handleStartNew
|
|
1108
|
+
},
|
|
1109
|
+
merged.newUpload
|
|
1110
|
+
)), status === "error" && /* @__PURE__ */ React2.createElement("div", { className: "sbflash__status sbflash__status--error" }, merged.error, /* @__PURE__ */ React2.createElement(
|
|
762
1111
|
"button",
|
|
763
1112
|
{
|
|
764
1113
|
type: "button",
|
|
@@ -766,10 +1115,10 @@ function FlashTool({
|
|
|
766
1115
|
onClick: handleRetry
|
|
767
1116
|
},
|
|
768
1117
|
merged.retry
|
|
769
|
-
)),
|
|
1118
|
+
))))), logOpen && /* @__PURE__ */ React2.createElement("pre", { className: "sbflash__log", ref: logRef }, log || merged.logPlaceholder));
|
|
770
1119
|
}
|
|
771
1120
|
function IconTerminal() {
|
|
772
|
-
return /* @__PURE__ */
|
|
1121
|
+
return /* @__PURE__ */ React2.createElement(
|
|
773
1122
|
"svg",
|
|
774
1123
|
{
|
|
775
1124
|
viewBox: "0 0 16 16",
|
|
@@ -782,13 +1131,30 @@ function IconTerminal() {
|
|
|
782
1131
|
strokeLinejoin: "round",
|
|
783
1132
|
"aria-hidden": "true"
|
|
784
1133
|
},
|
|
785
|
-
/* @__PURE__ */
|
|
786
|
-
/* @__PURE__ */
|
|
787
|
-
/* @__PURE__ */
|
|
1134
|
+
/* @__PURE__ */ React2.createElement("rect", { x: "1.5", y: "2.5", width: "13", height: "11", rx: "2" }),
|
|
1135
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M4.25 6.25 6.75 8.5l-2.5 2.25" }),
|
|
1136
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M8.5 11h3" })
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
function IconCheck2() {
|
|
1140
|
+
return /* @__PURE__ */ React2.createElement(
|
|
1141
|
+
"svg",
|
|
1142
|
+
{
|
|
1143
|
+
viewBox: "0 0 16 16",
|
|
1144
|
+
width: "11",
|
|
1145
|
+
height: "11",
|
|
1146
|
+
fill: "none",
|
|
1147
|
+
stroke: "currentColor",
|
|
1148
|
+
strokeWidth: "2",
|
|
1149
|
+
strokeLinecap: "round",
|
|
1150
|
+
strokeLinejoin: "round",
|
|
1151
|
+
"aria-hidden": "true"
|
|
1152
|
+
},
|
|
1153
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M3 8.5 6.5 12 13 4.5" })
|
|
788
1154
|
);
|
|
789
1155
|
}
|
|
790
1156
|
function IconDisconnect() {
|
|
791
|
-
return /* @__PURE__ */
|
|
1157
|
+
return /* @__PURE__ */ React2.createElement(
|
|
792
1158
|
"svg",
|
|
793
1159
|
{
|
|
794
1160
|
viewBox: "0 0 16 16",
|
|
@@ -801,12 +1167,12 @@ function IconDisconnect() {
|
|
|
801
1167
|
strokeLinejoin: "round",
|
|
802
1168
|
"aria-hidden": "true"
|
|
803
1169
|
},
|
|
804
|
-
/* @__PURE__ */
|
|
805
|
-
/* @__PURE__ */
|
|
1170
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M8 1.5v4.25" }),
|
|
1171
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M4.5 5.5a5 5 0 1 0 7 0" })
|
|
806
1172
|
);
|
|
807
1173
|
}
|
|
808
1174
|
function IconHelp() {
|
|
809
|
-
return /* @__PURE__ */
|
|
1175
|
+
return /* @__PURE__ */ React2.createElement(
|
|
810
1176
|
"svg",
|
|
811
1177
|
{
|
|
812
1178
|
viewBox: "0 0 16 16",
|
|
@@ -819,42 +1185,138 @@ function IconHelp() {
|
|
|
819
1185
|
strokeLinejoin: "round",
|
|
820
1186
|
"aria-hidden": "true"
|
|
821
1187
|
},
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
824
|
-
/* @__PURE__ */
|
|
1188
|
+
/* @__PURE__ */ React2.createElement("circle", { cx: "8", cy: "8", r: "6.5" }),
|
|
1189
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M6 6.25a2 2 0 1 1 2.75 1.85c-.6.25-.95.7-.95 1.4v.25" }),
|
|
1190
|
+
/* @__PURE__ */ React2.createElement("circle", { cx: "8", cy: "11.5", r: "0.9", fill: "currentColor", stroke: "none" })
|
|
825
1191
|
);
|
|
826
1192
|
}
|
|
1193
|
+
function IconChip() {
|
|
1194
|
+
return /* @__PURE__ */ React2.createElement(
|
|
1195
|
+
"svg",
|
|
1196
|
+
{
|
|
1197
|
+
viewBox: "0 0 16 16",
|
|
1198
|
+
width: "14",
|
|
1199
|
+
height: "14",
|
|
1200
|
+
fill: "none",
|
|
1201
|
+
stroke: "currentColor",
|
|
1202
|
+
strokeWidth: "1.4",
|
|
1203
|
+
strokeLinecap: "round",
|
|
1204
|
+
strokeLinejoin: "round",
|
|
1205
|
+
"aria-hidden": "true"
|
|
1206
|
+
},
|
|
1207
|
+
/* @__PURE__ */ React2.createElement("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.2" }),
|
|
1208
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M6 1.5v2M10 1.5v2M6 12.5v2M10 12.5v2M1.5 6v-.01M1.5 10v-.01M14.5 6v-.01M14.5 10v-.01M1.5 6h2M1.5 10h2M12.5 6h2M12.5 10h2" })
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
function IconCloud() {
|
|
1212
|
+
return /* @__PURE__ */ React2.createElement(
|
|
1213
|
+
"svg",
|
|
1214
|
+
{
|
|
1215
|
+
viewBox: "0 0 16 16",
|
|
1216
|
+
width: "14",
|
|
1217
|
+
height: "14",
|
|
1218
|
+
fill: "none",
|
|
1219
|
+
stroke: "currentColor",
|
|
1220
|
+
strokeWidth: "1.4",
|
|
1221
|
+
strokeLinecap: "round",
|
|
1222
|
+
strokeLinejoin: "round",
|
|
1223
|
+
"aria-hidden": "true"
|
|
1224
|
+
},
|
|
1225
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M4.5 12.5A3 3 0 0 1 4 6.53 3.5 3.5 0 0 1 11 5.5a2.75 2.75 0 0 1 .5 5.46" }),
|
|
1226
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M6.5 12.5h5" }),
|
|
1227
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M8 9.5v5M6.25 12.25 8 14.5l1.75-2.25" })
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
function IconBolt() {
|
|
1231
|
+
return /* @__PURE__ */ React2.createElement(
|
|
1232
|
+
"svg",
|
|
1233
|
+
{
|
|
1234
|
+
viewBox: "0 0 16 16",
|
|
1235
|
+
width: "14",
|
|
1236
|
+
height: "14",
|
|
1237
|
+
fill: "none",
|
|
1238
|
+
stroke: "currentColor",
|
|
1239
|
+
strokeWidth: "1.4",
|
|
1240
|
+
strokeLinecap: "round",
|
|
1241
|
+
strokeLinejoin: "round",
|
|
1242
|
+
"aria-hidden": "true"
|
|
1243
|
+
},
|
|
1244
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M8.5 1.5 3 9h4l-1 5.5L13 7H9l-0.5-5.5Z" })
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
function IconLayers() {
|
|
1248
|
+
return /* @__PURE__ */ React2.createElement(
|
|
1249
|
+
"svg",
|
|
1250
|
+
{
|
|
1251
|
+
viewBox: "0 0 16 16",
|
|
1252
|
+
width: "14",
|
|
1253
|
+
height: "14",
|
|
1254
|
+
fill: "none",
|
|
1255
|
+
stroke: "currentColor",
|
|
1256
|
+
strokeWidth: "1.4",
|
|
1257
|
+
strokeLinecap: "round",
|
|
1258
|
+
strokeLinejoin: "round",
|
|
1259
|
+
"aria-hidden": "true"
|
|
1260
|
+
},
|
|
1261
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M8 1.75 14 5 8 8.25 2 5Z" }),
|
|
1262
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M2 8 8 11.25 14 8" }),
|
|
1263
|
+
/* @__PURE__ */ React2.createElement("path", { d: "M2 11 8 14.25 14 11" })
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
var FIRMWARE_ICONS = {
|
|
1267
|
+
circuitpy: /* @__PURE__ */ React2.createElement(IconChip, null),
|
|
1268
|
+
ota: /* @__PURE__ */ React2.createElement(IconCloud, null),
|
|
1269
|
+
basic: /* @__PURE__ */ React2.createElement(IconBolt, null),
|
|
1270
|
+
standard: /* @__PURE__ */ React2.createElement(IconLayers, null)
|
|
1271
|
+
};
|
|
827
1272
|
FlashTool.propTypes = {
|
|
828
1273
|
/** Already-compiled firmware image to flash. Ignored if `firmwares` is set. */
|
|
829
|
-
binary:
|
|
1274
|
+
binary: PropTypes2.instanceOf(Uint8Array),
|
|
830
1275
|
/** Map of sketch options to their compiled firmware image: { circuitpy, ota, basic, standard }. */
|
|
831
|
-
firmwares:
|
|
832
|
-
circuitpy:
|
|
833
|
-
ota:
|
|
834
|
-
basic:
|
|
835
|
-
standard:
|
|
1276
|
+
firmwares: PropTypes2.shape({
|
|
1277
|
+
circuitpy: PropTypes2.instanceOf(Uint8Array),
|
|
1278
|
+
ota: PropTypes2.instanceOf(Uint8Array),
|
|
1279
|
+
basic: PropTypes2.instanceOf(Uint8Array),
|
|
1280
|
+
standard: PropTypes2.instanceOf(Uint8Array)
|
|
836
1281
|
}),
|
|
1282
|
+
/** Same as `firmwareType`, but also accepts friendlier aliases (e.g. "circuitpython" for "circuitpy"). */
|
|
1283
|
+
firmware: PropTypes2.oneOf([
|
|
1284
|
+
...FIRMWARE_TYPES,
|
|
1285
|
+
...Object.keys(FIRMWARE_ALIASES)
|
|
1286
|
+
]),
|
|
837
1287
|
/** Locks the sketch dropdown to this value and disables it. One of "circuitpy" | "ota" | "basic" | "standard". */
|
|
838
|
-
firmwareType:
|
|
1288
|
+
firmwareType: PropTypes2.oneOf(FIRMWARE_TYPES),
|
|
839
1289
|
/** Initial dropdown selection when `firmwareType` isn't set (dropdown stays interactive). */
|
|
840
|
-
defaultFirmwareType:
|
|
1290
|
+
defaultFirmwareType: PropTypes2.oneOf(FIRMWARE_TYPES),
|
|
841
1291
|
/** Per-sketch overrides for the esptool flash step, keyed by firmware type. Merges over (and can override) the built-in CircuitPython defaults. */
|
|
842
|
-
firmwareFlashOptions:
|
|
1292
|
+
firmwareFlashOptions: PropTypes2.objectOf(PropTypes2.object),
|
|
1293
|
+
/** Per-sketch description shown under the sketch picker, keyed by firmware type: { text, linkText, linkUrl }. Merges over (and can override) the built-in defaults. */
|
|
1294
|
+
firmwareDescriptions: PropTypes2.objectOf(
|
|
1295
|
+
PropTypes2.shape({
|
|
1296
|
+
text: PropTypes2.string,
|
|
1297
|
+
linkText: PropTypes2.string,
|
|
1298
|
+
linkUrl: PropTypes2.string
|
|
1299
|
+
})
|
|
1300
|
+
),
|
|
843
1301
|
/** USB vendor id used to filter the device picker. Defaults to senseBox (0x303a). */
|
|
844
|
-
usbVendorId:
|
|
1302
|
+
usbVendorId: PropTypes2.number,
|
|
845
1303
|
/** Overrides for the esptool flash step (address, baudrate, flashMode, flashFreq, flashSize, eraseAll, usingUsbOtg). */
|
|
846
|
-
flashOptions:
|
|
1304
|
+
flashOptions: PropTypes2.object,
|
|
847
1305
|
/** Start flashing automatically once the selected firmware and a connected device are both ready. */
|
|
848
|
-
autoFlash:
|
|
849
|
-
/**
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1306
|
+
autoFlash: PropTypes2.bool,
|
|
1307
|
+
/** Keep the log/terminal view expanded by default and disable the button to collapse it. */
|
|
1308
|
+
expand: PropTypes2.bool,
|
|
1309
|
+
/** UI language for the built-in strings. "de" uses the built-in German translation. */
|
|
1310
|
+
language: PropTypes2.oneOf(["en", "de"]),
|
|
1311
|
+
/** Overrides for the built-in UI strings (applied on top of `language`). */
|
|
1312
|
+
strings: PropTypes2.object,
|
|
1313
|
+
className: PropTypes2.string,
|
|
1314
|
+
onConnected: PropTypes2.func,
|
|
1315
|
+
onDisconnected: PropTypes2.func,
|
|
1316
|
+
onFlashed: PropTypes2.func,
|
|
1317
|
+
onError: PropTypes2.func,
|
|
856
1318
|
/** Called with the newly selected sketch type when the dropdown changes. */
|
|
857
|
-
onFirmwareTypeChange:
|
|
1319
|
+
onFirmwareTypeChange: PropTypes2.func
|
|
858
1320
|
};
|
|
859
1321
|
export {
|
|
860
1322
|
DEFAULT_USB_VENDOR_ID,
|