diff --git a/dist/cpinstaller.js b/dist/cpinstaller.js
index 906275b..326460b 100644
--- a/dist/cpinstaller.js
+++ b/dist/cpinstaller.js
@@ -110,8 +110,16 @@ export class CPInstallButton extends InstallButton {
}
async connectedCallback() {
- // Required
- this.boardIds = this.getAttribute("boardid").split(",");
+ // Load the Board Definitions before the button is ever clicked
+ const response = await fetch(BOARD_DEFS);
+ this.boardDefs = await response.json();
+
+ let boardIds = this.getAttribute("boardid")
+ if (!boardIds || boardIds.trim().length === 0) {
+ this.boardIds = Object.keys(this.boardDefs);
+ } else {
+ this.boardIds = boardIds.split(",");
+ }
// If there is only one board id, then select it by default
if (this.boardIds.length === 1) {
@@ -123,10 +131,6 @@ export class CPInstallButton extends InstallButton {
this.releaseVersion = this.getAttribute("version");
}
- // Load the Board Definitions before the button is ever clicked
- const response = await fetch(BOARD_DEFS);
- this.boardDefs = await response.json();
-
super.connectedCallback();
}
@@ -439,6 +443,18 @@ export class CPInstallButton extends InstallButton {
for (let boardId of this.boardIds) {
options.push({id: boardId, name: this.getBoardName(boardId)});
}
+
+ options.sort((a, b) => {
+ let boardA = a.name.trim().toLowerCase();
+ let boardB = b.name.trim().toLowerCase();
+ if (boardA < boardB) {
+ return -1;
+ }
+ if (boardA > boardB) {
+ return 1;
+ }
+ return 0;
+ });
return options;
}
diff --git a/dist/cpinstaller.min.js b/dist/cpinstaller.min.js
index 10d29a1..ca9eb75 100644
--- a/dist/cpinstaller.min.js
+++ b/dist/cpinstaller.min.js
@@ -1,4 +1,4 @@
-"use strict";import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"https://cdn.jsdelivr.net/npm/lit-html/directives/map/+esm";import*as toml from"https://cdn.jsdelivr.net/npm/iarna-toml-esm@3.0.5/+esm";import*as zip from"https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.65/+esm";import*as esptoolPackage from"https://cdn.jsdelivr.net/npm/esp-web-flasher@5.1.2/dist/web/index.js/+esm";import{REPL}from"https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js/repl.js";import{InstallButton,ESP_ROM_BAUD}from"./base_installer.min.js";const PREFERRED_BAUDRATE=921600,COPY_CHUNK_SIZE=65536,DEFAULT_RELEASE_LATEST=!1,BOARD_DEFS="https://adafruit-circuit-python.s3.amazonaws.com/esp32_boards.json",CSS_DIALOG_CLASS="cp-installer-dialog",FAMILY_TO_CHIP_MAP={esp32s2:esptoolPackage.CHIP_FAMILY_ESP32S2,esp32s3:esptoolPackage.CHIP_FAMILY_ESP32S3,esp32c3:esptoolPackage.CHIP_FAMILY_ESP32C3,esp32:esptoolPackage.CHIP_FAMILY_ESP32},attrMap={bootloader:"bootloaderUrl",uf2file:"uf2FileUrl",binfile:"binFileUrl"};class CPInstallButton extends InstallButton{constructor(){super(),this.releaseVersion="[version]",this.boardName="ESP32-based device",this.boardIds=null,this.selectedBoardId=null,this.bootloaderUrl=null,this.boardDefs=null,this.uf2FileUrl=null,this.binFileUrl=null,this.releaseVersion=0,this.chipFamily=null,this.dialogCssClass=CSS_DIALOG_CLASS,this.dialogs={...this.dialogs,...this.cpDialogs},this.bootDriveHandle=null,this.circuitpyDriveHandle=null,this._bootDriveName=null,this._serialPortName=null,this.replSerialDevice=null,this.repl=null,this.fileCache=[],this.reader=null,this.writer=null,this.tomlSettings=null,this.init()}static get observedAttributes(){return Object.keys(attrMap)}parseVersion(e){var t={},e=e.match(/(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?/);return e&&4<=e.length&&(t.major=e[1],t.minor=e[2],t.patch=e[3],e[4]&&e[5]?(t.suffix=e[4],t.suffixVersion=e[5]):(t.suffix="stable",t.suffixVersion=0)),t}sortReleases(e){const r=["major","minor","patch","suffix","suffixVersion"];return e.sort((e,t)=>{var i,s=this.parseVersion(e.version),a=this.parseVersion(t.version);for(i of r){if(s[i]a[i])return 1}return 0}),e}async connectedCallback(){this.boardIds=this.getAttribute("boardid").split(","),1===this.boardIds.length&&(this.selectedBoardId=this.boardIds[0]),this.getAttribute("version")&&(this.releaseVersion=this.getAttribute("version"));var e=await fetch(BOARD_DEFS);this.boardDefs=await e.json(),super.connectedCallback()}async loadBoard(e){let t=null;if(Object.keys(this.boardDefs).includes(e)){e=this.boardDefs[e],e=(this.chipFamily=e.chipfamily,e.name&&(this.boardName=e.name),e.bootloader&&(this.bootloaderUrl=this.updateBinaryUrl(e.bootloader)),this.sortReleases(e.releases));if(this.releaseVersion)for(var i of e)if(i.version==this.releaseVersion){t=i;break}t||(t=DEFAULT_RELEASE_LATEST?e[e.length-1]:e[0],this.releaseVersion=t.version),t.uf2file&&(this.uf2FileUrl=this.updateBinaryUrl(t.uf2file)),t.binfile&&(this.binFileUrl=this.updateBinaryUrl(t.binfile))}this.getAttribute("chipfamily")&&(this.chipFamily=this.getAttribute("chipfamily")),this.getAttribute("boardname")&&(this.boardName=this.getAttribute("boardname")),this.menuTitle="CircuitPython Installer for "+this.boardName}attributeChangedCallback(e,t,i){this[attrMap[e]]=i?this.updateBinaryUrl(i):null}updateBinaryUrl(e){return e=e&&e.replace("https://downloads.circuitpython.org/","https://adafruit-circuit-python.s3.amazonaws.com/")}flows={uf2FullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl&&!!this.uf2FileUrl},binFullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()&&!!this.binFileUrl},uf2Only:{label:"Upgrade/Install CircuitPython [version] UF2 Only",steps:[this.stepWelcome,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.uf2FileUrl},binOnly:{label:"Upgrade CircuitPython [version] Bin Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSuccess],isEnabled:async()=>!!this.binFileUrl},bootloaderOnly:{label:"Install Bootloader Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl},credentialsOnlyRepl:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()},credentialsOnlyDrive:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()}};cpDialogs={boardSelect:{closeable:!0,template:i=>html`
+"use strict";import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"https://cdn.jsdelivr.net/npm/lit-html/directives/map/+esm";import*as toml from"https://cdn.jsdelivr.net/npm/iarna-toml-esm@3.0.5/+esm";import*as zip from"https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.6.65/+esm";import*as esptoolPackage from"https://cdn.jsdelivr.net/npm/esp-web-flasher@5.1.2/dist/web/index.js/+esm";import{REPL}from"https://cdn.jsdelivr.net/gh/adafruit/circuitpython-repl-js/repl.js";import{InstallButton,ESP_ROM_BAUD}from"./base_installer.min.js";const PREFERRED_BAUDRATE=921600,COPY_CHUNK_SIZE=65536,DEFAULT_RELEASE_LATEST=!1,BOARD_DEFS="https://adafruit-circuit-python.s3.amazonaws.com/esp32_boards.json",CSS_DIALOG_CLASS="cp-installer-dialog",FAMILY_TO_CHIP_MAP={esp32s2:esptoolPackage.CHIP_FAMILY_ESP32S2,esp32s3:esptoolPackage.CHIP_FAMILY_ESP32S3,esp32c3:esptoolPackage.CHIP_FAMILY_ESP32C3,esp32:esptoolPackage.CHIP_FAMILY_ESP32},attrMap={bootloader:"bootloaderUrl",uf2file:"uf2FileUrl",binfile:"binFileUrl"};class CPInstallButton extends InstallButton{constructor(){super(),this.releaseVersion="[version]",this.boardName="ESP32-based device",this.boardIds=null,this.selectedBoardId=null,this.bootloaderUrl=null,this.boardDefs=null,this.uf2FileUrl=null,this.binFileUrl=null,this.releaseVersion=0,this.chipFamily=null,this.dialogCssClass=CSS_DIALOG_CLASS,this.dialogs={...this.dialogs,...this.cpDialogs},this.bootDriveHandle=null,this.circuitpyDriveHandle=null,this._bootDriveName=null,this._serialPortName=null,this.replSerialDevice=null,this.repl=null,this.fileCache=[],this.reader=null,this.writer=null,this.tomlSettings=null,this.init()}static get observedAttributes(){return Object.keys(attrMap)}parseVersion(e){var t={},e=e.match(/(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?/);return e&&4<=e.length&&(t.major=e[1],t.minor=e[2],t.patch=e[3],e[4]&&e[5]?(t.suffix=e[4],t.suffixVersion=e[5]):(t.suffix="stable",t.suffixVersion=0)),t}sortReleases(e){const r=["major","minor","patch","suffix","suffixVersion"];return e.sort((e,t)=>{var i,s=this.parseVersion(e.version),a=this.parseVersion(t.version);for(i of r){if(s[i]a[i])return 1}return 0}),e}async connectedCallback(){var e=await fetch(BOARD_DEFS),e=(this.boardDefs=await e.json(),this.getAttribute("boardid"));e&&0!==e.trim().length?this.boardIds=e.split(","):this.boardIds=Object.keys(this.boardDefs),1===this.boardIds.length&&(this.selectedBoardId=this.boardIds[0]),this.getAttribute("version")&&(this.releaseVersion=this.getAttribute("version")),super.connectedCallback()}async loadBoard(e){let t=null;if(Object.keys(this.boardDefs).includes(e)){e=this.boardDefs[e],e=(this.chipFamily=e.chipfamily,e.name&&(this.boardName=e.name),e.bootloader&&(this.bootloaderUrl=this.updateBinaryUrl(e.bootloader)),this.sortReleases(e.releases));if(this.releaseVersion)for(var i of e)if(i.version==this.releaseVersion){t=i;break}t||(t=DEFAULT_RELEASE_LATEST?e[e.length-1]:e[0],this.releaseVersion=t.version),t.uf2file&&(this.uf2FileUrl=this.updateBinaryUrl(t.uf2file)),t.binfile&&(this.binFileUrl=this.updateBinaryUrl(t.binfile))}this.getAttribute("chipfamily")&&(this.chipFamily=this.getAttribute("chipfamily")),this.getAttribute("boardname")&&(this.boardName=this.getAttribute("boardname")),this.menuTitle="CircuitPython Installer for "+this.boardName}attributeChangedCallback(e,t,i){this[attrMap[e]]=i?this.updateBinaryUrl(i):null}updateBinaryUrl(e){return e=e&&e.replace("https://downloads.circuitpython.org/","https://adafruit-circuit-python.s3.amazonaws.com/")}flows={uf2FullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl&&!!this.uf2FileUrl},binFullProgram:{label:"Full CircuitPython [version] Install",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()&&!!this.binFileUrl},uf2Only:{label:"Upgrade/Install CircuitPython [version] UF2 Only",steps:[this.stepWelcome,this.stepSelectBootDrive,this.stepCopyUf2,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.uf2FileUrl},binOnly:{label:"Upgrade CircuitPython [version] Bin Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepFlashBin,this.stepSuccess],isEnabled:async()=>!!this.binFileUrl},bootloaderOnly:{label:"Install Bootloader Only",steps:[this.stepWelcome,this.stepSerialConnect,this.stepConfirm,this.stepEraseAll,this.stepBootloader,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()&&!!this.bootloaderUrl},credentialsOnlyRepl:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSetupRepl,this.stepCredentials,this.stepSuccess],isEnabled:async()=>!this.hasNativeUsb()},credentialsOnlyDrive:{label:"Update WiFi credentials",steps:[this.stepWelcome,this.stepSelectCpyDrive,this.stepCredentials,this.stepSuccess],isEnabled:async()=>this.hasNativeUsb()}};cpDialogs={boardSelect:{closeable:!0,template:i=>html`
There are multiple boards are available. Select the board you have:
Installation Error: ${e.message}
- `,buttons:[this.closeButton]}};getBoardName(e){return Object.keys(this.boardDefs).includes(e)?this.boardDefs[e].name:null}getBoardOptions(){var e,t=[];for(e of this.boardIds)t.push({id:e,name:this.getBoardName(e)});return t}async stepWelcome(){this.showDialog(this.dialogs.welcome,{boardName:this.boardName})}async stepSerialConnect(){this.showDialog(this.dialogs.espSerialConnect)}async stepConfirm(){this.showDialog(this.dialogs.confirm,{boardName:this.boardName})}async stepEraseAll(){this.showDialog(this.dialogs.actionWaiting,{action:"Erasing Flash"});try{await this.espStub.eraseFlash()}catch(e){this.errorMsg("Unable to finish erasing Flash memory. Please try again.")}await this.nextStep()}async stepFlashBin(){this.binFileUrl?(await this.downloadAndInstall(this.binFileUrl),await this.espHardReset(),await this.nextStep()):this.errorMsg("Missing bin file URL. Please make sure the installer button has this specified.")}async stepBootloader(){this.bootloaderUrl?(await this.downloadAndInstall(this.bootloaderUrl,"combined.bin",!0),await this.nextStep()):this.errorMsg("Missing bootloader file URL. Please make sure the installer button has this specified.")}async stepSelectBootDrive(){var e=await this.getBootDriveName();e&&this.logMsg("Waiting for user to select a bootloader volume named "+e),this.showDialog(this.dialogs.bootDriveSelect,{drivename:e||"Bootloader"})}async stepSelectCpyDrive(){this.logMsg("Waiting for user to select CIRCUITPY drive"),this.showDialog(this.dialogs.circuitpyDriveSelect)}async stepCopyUf2(){this.bootDriveHandle?(this.showDialog(this.dialogs.actionProgress,{action:"Copying "+this.uf2FileUrl}),await this.downloadAndCopy(this.uf2FileUrl),await this.nextStep()):this.errorMsg("No boot drive selected. stepSelectBootDrive should preceed this step.")}async stepSetupRepl(){var e=await this.getSerialPortName();let t=e?`There may be several devices listed, but look for one called something like ${e}.`:"There may be several devices listed. If you aren't sure which to choose, look for one that includes the name of your microcontroller.";this.showDialog(this.dialogs.cpSerial,{serialPortInstructions:t})}async stepCredentials(){this.tomlSettings=await this.getCurrentSettings(),console.log(this.tomlSettings);var e={wifi_ssid:this.getSetting("CIRCUITPY_WIFI_SSID"),wifi_password:this.getSetting("CIRCUITPY_WIFI_PASSWORD"),api_password:this.getSetting("CIRCUITPY_WEB_API_PASSWORD","passw0rd"),api_port:this.getSetting("CIRCUITPY_WEB_API_PORT",80)};this.hasNativeUsb(),this.showDialog(this.dialogs.credentials,e)}async stepSuccess(){let e={};this.repl&&(await this.repl.waitForPrompt(),this.currentFlow||this.currentFlow.steps.includes(this.stepCredentials))&&(e=await this.getDeviceHostInfo()),this.showDialog(this.dialogs.success,e)}async stepClose(){this.closeDialog()}async bootDriveSelectHandler(e){var t=await this.getBootDriveName();let i;try{i=await window.showDirectoryPicker({mode:"readwrite"})}catch(e){return}t&&t!=i.name?alert(`The selected drive named ${i.name} does not match the expected name of ${t}. Please select the correct drive.`):await this._verifyPermission(i)?(this.bootDriveHandle=i,await this.nextStep()):alert("Unable to write to the selected folder")}async circuitpyDriveSelectHandler(e){let t;try{t=await window.showDirectoryPicker({mode:"readwrite"})}catch(e){return}await this.getBootOut(t)?await this._verifyPermission(t)?(this.circuitpyDriveHandle=t,await this.nextStep()):alert("Unable to write to the selected folder"):alert("Expecting a folder with boot_out.txt. Please select the root folder of your CIRCUITPY drive.")}async espToolConnectHandler(e){await this.onReplDisconnected(e),await this.espDisconnect();let t;try{t=await this.espConnect({log:(...e)=>this.logMsg(...e),debug:()=>{},error:(...e)=>this.errorMsg(...e)})}catch(e){return void this.errorMsg("Unable to open Serial connection to board. Make sure the port is not already in use by another application or in another browser tab.")}try{this.updateEspConnected(this.connectionStates.CONNECTING),await t.initialize(),this.updateEspConnected(this.connectionStates.CONNECTED)}catch(e){return await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),void this.errorMsg("Unable to connect to the board. Make sure it is in bootloader mode by holding the boot0 button when powering on and try again.")}try{this.logMsg("Connected to "+t.chipName),this.logMsg("MAC Address: "+this.formatMacAddr(t.macAddr())),FAMILY_TO_CHIP_MAP[this.chipFamily]==t.chipFamily?(this.logMsg("This chip checks out"),this.espStub=await t.runStub(),this.espStub.addEventListener("disconnect",()=>{this.updateEspConnected(this.connectionStates.DISCONNECTED),this.espStub=null}),await this.setBaudRateIfChipSupports(t.chipFamily,PREFERRED_BAUDRATE),await this.nextStep()):(this.errorMsg("Oops, this is the wrong firmware for your board."),await this.espDisconnect())}catch(e){await t.disconnect(),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.errorMsg("Oops, we lost connection to your board before completing the install. Please check your USB connection and click Connect again. Refresh the browser if it becomes unresponsive.")}}async onSerialReceive(e){await this.repl.onSerialReceive(e)}async cpSerialConnectHandler(e){await this.espDisconnect(),await this.onReplDisconnected(e);try{this.replSerialDevice=await navigator.serial.requestPort()}catch(e){return}try{await this.replSerialDevice.open({baudRate:ESP_ROM_BAUD})}catch(e){console.error("Error. Unable to open Serial Port. Make sure it isn't already in use in another tab or application.")}await this.setupRepl(),this.nextStep()}async setupRepl(){this.replSerialDevice&&(this.repl=new REPL,this.repl.serialTransmit=this.serialTransmit.bind(this),this.replSerialDevice.addEventListener("message",this.onSerialReceive.bind(this)),this._readLoopPromise=this._readSerialLoop().catch(async function(e){await this.onReplDisconnected()}.bind(this)),this.replSerialDevice.writable)&&(this.writer=this.replSerialDevice.writable.getWriter(),await this.writer.ready)}async onReplDisconnected(e){if(this.reader){try{await this.reader.cancel()}catch(e){}this.reader=null}if(this.writer&&(await this.writer.releaseLock(),this.writer=null),this.replSerialDevice){try{await this.replSerialDevice.close()}catch(e){}this.replSerialDevice=null}}async buttonClickHandler(e,t=!1){!(1