From 647197884e6922540572cf35ed3d19a3d1f393af Mon Sep 17 00:00:00 2001 From: makermelissa Date: Wed, 30 Jul 2025 18:17:40 +0000 Subject: [PATCH] Github Action: Updated dist files --- dist/cpinstaller.js | 191 +++++++++++++++++++++++++--------------- dist/cpinstaller.min.js | 143 +++++++++++++++++++----------- 2 files changed, 216 insertions(+), 118 deletions(-) diff --git a/dist/cpinstaller.js b/dist/cpinstaller.js index a09d391..cfa993f 100644 --- a/dist/cpinstaller.js +++ b/dist/cpinstaller.js @@ -260,39 +260,61 @@ export class CPInstallButton extends InstallButton { welcome: { closeable: true, template: (data) => html` +

Web Firmware Installer

- Welcome to the CircuitPython Installer. This tool will install CircuitPython on your ${data.boardName}. + Welcome! + This tool will install a UF2 bootloader and/or CircuitPython on your ${data.boardName}.

- This tool is new and experimental. If you experience any issues, feel free to check out + This tool is experimental. + If you experience any issues, feel free to check out https://github.com/adafruit/circuitpython-org/issues - to see if somebody has already submitted the same issue you are experiencing. If not, feel free to open a new issue. If - you do see the same issue and are able to contribute additional information, that would be appreciated. + to see if the issue you are experiencing has already been reported. + If not, feel free to open a new issue. + If you do see the same issue and are able to contribute additional information, + that would be appreciated.

- If you are unable to use this tool, then the manual installation methods should still work. + If you are unable to use this tool, + then the manual installation methods like the + Adafruit WebSerial Tool + and esptool.py should still work.

` }, espSerialConnect: { closeable: true, template: (data) => html` -

- Make sure your board is plugged into this computer via a Serial connection using a USB Cable. -

- -

- - Click this button to open the Web Serial connection menu. -

- -

There may be many devices listed, such as your remembered Bluetooth peripherals, anything else plugged into USB, etc.

- -

- If you aren't sure which to choose, look for words like "USB", "UART", "JTAG", and "Bridge Controller". There may be more than one right option depending on your system configuration. Experiment if needed. -

`, buttons: [this.previousButton, { label: "Next", @@ -303,7 +325,8 @@ export class CPInstallButton extends InstallButton { }, confirm: { template: (data) => html` -

This will overwrite everything on the ${data.boardName}.

+

Erase Flash

+

Now, optionally, erase everything on the ${data.boardName}.

`, buttons: [ this.previousButton, @@ -320,41 +343,53 @@ export class CPInstallButton extends InstallButton { bootDriveSelect: { closeable: true, template: (data) => html` -

- Please select the ${data.drivename} Drive where the UF2 file will be copied. -

-

- If you just installed the bootloader, you may need to reset your board. If you already had the bootloader installed, - you may need to double press the reset button. -

-

- -

+

Select the ${data.drivename} Drive

+
    +
  1. +

    + Reset your board if you just installed the UF2 bootloader, + by pressing the RESET button. + If you already had the UF2 bootloader installed, + you may need to double-click the RESET button to start up the UF2 bootloader. +

    +
  2. +
  3. +

    + + Select the ${data.drivename} drive where the UF2 file will be copied. +

    +
  4. + `, buttons: [], }, circuitpyDriveSelect: { closeable: true, template: (data) => html` -

    - Please select the CIRCUITPY Drive. If you don't see your CIRCUITPY drive, it may be disabled in boot.py or you may have renamed it at some point. -

    -

    - -

    +

    Select the CIRCUITPY Drive

    + `, buttons: [], }, actionWaiting: { template: (data) => html` -

    ${data.action}...

    +

    ${data.action}

    `, buttons: [], }, actionProgress: { template: (data) => html` -

    ${data.action}...

    +

    ${data.action}

    ${data.percentage}% `, buttons: [], @@ -362,14 +397,15 @@ export class CPInstallButton extends InstallButton { cpSerial: { closeable: true, template: (data) => html` -

    - The next step is to write your credentials to settings.toml. Make sure your board is running CircuitPython. If you just installed CircuitPython, you may to reset the board first. +

    Reconnect to serial

    +

    -

    - - Click this button to open the Web Serial connection menu. If it is already connected, pressing again will allow you to select a different port. -

    -

    ${data.serialPortInstructions}

    `, buttons: [this.previousButton, { @@ -383,6 +419,15 @@ export class CPInstallButton extends InstallButton { credentials: { closeable: true, template: (data) => html` +

    Fill in settings.toml

    +

    + This step will write your network credentials to the settings.toml file on CIRCUITPY. + Make sure your board is running CircuitPython. +

    +

    + If you want to skip this step and fill in settings.toml later, + just close this dialog. +

    @@ -401,9 +446,9 @@ export class CPInstallButton extends InstallButton {
    ${data.mass_storage_disabled === true || data.mass_storage_disabled === false ? - html`
    - -
    ` : ''} + html`
    + +
    ` : ''}
    `, buttons: [this.previousButton, { @@ -478,7 +523,7 @@ export class CPInstallButton extends InstallButton { async stepEraseAll() { // Display Erase Dialog this.showDialog(this.dialogs.actionWaiting, { - action: "Erasing Flash", + action: "Erasing Flash ...", }); try { await this.esploader.eraseFlash(); @@ -538,7 +583,7 @@ export class CPInstallButton extends InstallButton { } // Display Progress Dialog this.showDialog(this.dialogs.actionProgress, { - action: `Copying ${this.uf2FileUrl}`, + action: `Copying ${this.uf2FileUrl} ...`, }); // Do a copy and update progress along the way @@ -671,7 +716,7 @@ export class CPInstallButton extends InstallButton { } catch (err) { // It's possible the dialog was also canceled here this.updateEspConnected(this.connectionStates.DISCONNECTED); - 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."); + 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. If installing the bootloader, make sure you are in ROM bootloader mode."); return; } @@ -848,7 +893,8 @@ export class CPInstallButton extends InstallButton { } // Download the bootloader zip file - let [filename, fileBlob] = await this.downloadAndExtract(this.bootloaderUrl, 'tinyuf2.bin'); + let [filename, extracted_filename, fileBlob] = + await this.downloadAndExtract(this.bootloaderUrl, 'tinyuf2.bin'); const fileContents = await fileBlob.text(); const bootDriveRegex = /B\x00B\x00([A-Z0-9\x00]{11})FAT16/; @@ -981,7 +1027,7 @@ export class CPInstallButton extends InstallButton { if (!fileBlob) { this.showDialog(this.dialogs.actionProgress, { - action: `Downloading ${filename}` + action: `Downloading ${filename} ...` }); const progressElement = this.currentDialogElement.querySelector("#stepProgress"); @@ -995,11 +1041,12 @@ export class CPInstallButton extends InstallButton { } // If the file is a zip file, unzip and find the file to extract + let extracted_filename = null; if (filename.endsWith(".zip") && fileToExtract) { let foundFile; // Update the Progress dialog this.showDialog(this.dialogs.actionProgress, { - action: `Extracting ${fileToExtract}` + action: html`

    Downloaded ${filename}

    Extracting ${fileToExtract} ...

    ` }); // Set that to the current file to flash @@ -1008,14 +1055,14 @@ export class CPInstallButton extends InstallButton { this.errorMsg(`Unable to find ${fileToExtract} in ${filename}`); return; } - filename = foundFile; + extracted_filename = foundFile; } - return [filename, fileBlob]; + return [filename, extracted_filename, fileBlob]; } async downloadAndInstall(url, fileToExtract = null, cacheFile = false) { - let [filename, fileBlob] = await this.downloadAndExtract(url, fileToExtract, cacheFile); + let [filename, extracted_filename, fileBlob] = await this.downloadAndExtract(url, fileToExtract, cacheFile); const fileArray = []; const readBlobAsBinaryString = (inputFile) => { @@ -1024,7 +1071,7 @@ export class CPInstallButton extends InstallButton { return new Promise((resolve, reject) => { reader.onerror = () => { reader.abort(); - reject(new DOMException("Problem parsing input file.")); + reject(new DOMException("Problem parsing input file")); }; reader.onload = () => { @@ -1040,7 +1087,9 @@ export class CPInstallButton extends InstallButton { let lastPercent = 0; this.showDialog(this.dialogs.actionProgress, { - action: `Flashing ${filename}` + action: fileToExtract + ? html`

    Downloaded ${filename}

    Extracted ${fileToExtract}

    Flashing (be patient; you will see pauses) ...

    ` + : html`

    Downloaded ${filename}

    Flashing (be patient; you will see pauses) ...

    ` }); const progressElement = this.currentDialogElement.querySelector("#stepProgress"); @@ -1056,7 +1105,7 @@ export class CPInstallButton extends InstallButton { let percentage = Math.round((written / total) * 100); if (percentage > lastPercent) { progressElement.value = percentage; - this.logMsg(`${percentage}% (${written}/${total})...`); + this.logMsg(`${percentage}% (${written}/${total}) ...`); lastPercent = percentage; } }, @@ -1064,7 +1113,7 @@ export class CPInstallButton extends InstallButton { }; await this.esploader.writeFlash(flashOptions); } catch (err) { - this.errorMsg(`Unable to flash file: ${filename}. Error Message: ${err}`); + this.errorMsg(`Unable to flash file: ${fileToExtract}. Error Message: ${err}`); } } } @@ -1078,10 +1127,14 @@ export class CPInstallButton extends InstallButton { return; } + let [filename, extracted_filename, fileBlob] = await this.downloadAndExtract(url); + this.showDialog(this.dialogs.actionProgress, { + action: html`

    Downloaded: ${filename}

    Flashing ...

    ` + }); + const progressElement = this.currentDialogElement.querySelector("#stepProgress"); progressElement.value = 0; - let [filename, fileBlob] = await this.downloadAndExtract(url); const fileHandle = await dirHandle.getFileHandle(filename, {create: true}); const writableStream = await fileHandle.createWritable(); const totalSize = fileBlob.size; @@ -1093,7 +1146,7 @@ export class CPInstallButton extends InstallButton { bytesWritten += chunk.size; progressElement.value = Math.round(bytesWritten / totalSize * 100); - this.logMsg(`${Math.round(bytesWritten / totalSize * 100)}% (${bytesWritten} / ${totalSize}) written...`); + this.logMsg(`${Math.round(bytesWritten / totalSize * 100)}% (${bytesWritten} / ${totalSize}) written ...`); } this.logMsg("File successfully written"); try { @@ -1101,7 +1154,7 @@ export class CPInstallButton extends InstallButton { await writableStream.close(); this.logMsg("File successfully closed"); } catch (err) { - this.logMsg("Error closing file, probably due to board reset. Continuing..."); + this.logMsg("Error closing file, probably due to board reset. Continuing ..."); } } @@ -1163,7 +1216,7 @@ export class CPInstallButton extends InstallButton { if (fileContents) { return toml.parse(fileContents); } - this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing..."); + this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing ..."); return {}; } @@ -1230,7 +1283,7 @@ export class CPInstallButton extends InstallButton { // Perform a soft restart to avoid losing the connection and get an IP address this.showDialog(this.dialogs.actionWaiting, { - action: "Waiting for IP Address...", + action: "Waiting for IP Address ...", }); await this.repl.softRestart(); try { @@ -1270,7 +1323,7 @@ export class CPInstallButton extends InstallButton { if (fileContents) { return toml.parse(fileContents); } - this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing..."); + this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing ..."); return {}; } @@ -1353,4 +1406,4 @@ export class CPInstallButton extends InstallButton { } } -customElements.define('cp-install-button', CPInstallButton, {extends: "button"}); \ No newline at end of file +customElements.define('cp-install-button', CPInstallButton, {extends: "button"}); diff --git a/dist/cpinstaller.min.js b/dist/cpinstaller.min.js index 153b120..ceba732 100644 --- a/dist/cpinstaller.min.js +++ b/dist/cpinstaller.min.js @@ -9,72 +9,117 @@ import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"htt

    `,buttons:[{label:"Select Board",onClick:this.selectBoardHandler,isEnabled:async()=>"0"!=this.currentDialogElement.querySelector("#availableBoards").value}]},welcome:{closeable:!0,template:e=>html` +

    Web Firmware Installer

    - Welcome to the CircuitPython Installer. This tool will install CircuitPython on your ${e.boardName}. + Welcome! + This tool will install a UF2 bootloader and/or CircuitPython on your ${e.boardName}.

    - This tool is new and experimental. If you experience any issues, feel free to check out + This tool is experimental. + If you experience any issues, feel free to check out https://github.com/adafruit/circuitpython-org/issues - to see if somebody has already submitted the same issue you are experiencing. If not, feel free to open a new issue. If - you do see the same issue and are able to contribute additional information, that would be appreciated. + to see if the issue you are experiencing has already been reported. + If not, feel free to open a new issue. + If you do see the same issue and are able to contribute additional information, + that would be appreciated.

    - If you are unable to use this tool, then the manual installation methods should still work. + If you are unable to use this tool, + then the manual installation methods like the + Adafruit WebSerial Tool + and esptool.py should still work.

    `},espSerialConnect:{closeable:!0,template:e=>html` -

    - Make sure your board is plugged into this computer via a Serial connection using a USB Cable. -

    - -

    - - Click this button to open the Web Serial connection menu. -

    - -

    There may be many devices listed, such as your remembered Bluetooth peripherals, anything else plugged into USB, etc.

    - -

    - If you aren't sure which to choose, look for words like "USB", "UART", "JTAG", and "Bridge Controller". There may be more than one right option depending on your system configuration. Experiment if needed. -

    `,buttons:[this.previousButton,{label:"Next",onClick:this.nextStep,isEnabled:async()=>this.currentStep{this.currentDialogElement.querySelector("#butConnect").innerText=this.connected}}]},confirm:{template:e=>html` -

    This will overwrite everything on the ${e.boardName}.

    +

    Erase Flash

    +

    Now, optionally, erase everything on the ${e.boardName}.

    `,buttons:[this.previousButton,{label:"Skip Erase",onClick:async e=>{confirm("Skipping the erase step may cause issues and is not recommended. Continue?")&&await this.advanceSteps(2)}},{label:"Continue",onClick:this.nextStep}]},bootDriveSelect:{closeable:!0,template:e=>html` -

    - Please select the ${e.drivename} Drive where the UF2 file will be copied. -

    -

    - If you just installed the bootloader, you may need to reset your board. If you already had the bootloader installed, - you may need to double press the reset button. -

    -

    - -

    +

    Select the ${e.drivename} Drive

    +
      +
    1. +

      + Reset your board if you just installed the UF2 bootloader, + by pressing the RESET button. + If you already had the UF2 bootloader installed, + you may need to double-click the RESET button to start up the UF2 bootloader. +

      +
    2. +
    3. +

      + + Select the ${e.drivename} drive where the UF2 file will be copied. +

      +
    4. + `,buttons:[]},circuitpyDriveSelect:{closeable:!0,template:e=>html` -

      - Please select the CIRCUITPY Drive. If you don't see your CIRCUITPY drive, it may be disabled in boot.py or you may have renamed it at some point. -

      -

      - -

      +

      Select the CIRCUITPY Drive

      +
        +
      • +

        + + Select the CIRCUITPY Drive. + You may need to wait a few seconds for it to appear. + If you don't see your CIRCUITPY drive, it may be disabled in boot.py or you may have previously renamed it. +

        +
      • +
      `,buttons:[]},actionWaiting:{template:e=>html` -

      ${e.action}...

      +

      ${e.action}

      `,buttons:[]},actionProgress:{template:e=>html` -

      ${e.action}...

      +

      ${e.action}

      ${e.percentage}% `,buttons:[]},cpSerial:{closeable:!0,template:e=>html` -

      - The next step is to write your credentials to settings.toml. Make sure your board is running CircuitPython. If you just installed CircuitPython, you may to reset the board first. +

      Reconnect to serial

      +
        +
      • + + Click this button to open the Web Serial connection menu. + If it is already connected, you can press it again if you need to select a different port. +
      • +

      -

      - - Click this button to open the Web Serial connection menu. If it is already connected, pressing again will allow you to select a different port. -

      -

      ${e.serialPortInstructions}

      `,buttons:[this.previousButton,{label:"Next",onClick:this.nextStep,isEnabled:async()=>this.currentStep{this.currentDialogElement.querySelector("#butConnect").innerText=this.replSerialDevice?"Connected":"Connect"}}]},credentials:{closeable:!0,template:e=>html` +

      Fill in settings.toml

      +

      + This step will write your network credentials to the settings.toml file on CIRCUITPY. + Make sure your board is running CircuitPython. +

      +

      + If you want to skip this step and fill in settings.toml later, + just close this dialog. +

      @@ -93,8 +138,8 @@ import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"htt
      ${!0===e.mass_storage_disabled||!1===e.mass_storage_disabled?html`
      - -
      `:""} + + `:""}
      `,buttons:[this.previousButton,{label:"Next",onClick:this.saveCredentials}]},success:{closeable:!0,template:e=>html`

      Successfully Completed

      @@ -103,4 +148,4 @@ import{html}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{map}from"htt

      `:""} `,buttons:[this.closeButton]},error:{closeable:!0,template:e=>html`

      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.sort((e,t)=>{e=e.name.trim().toLowerCase(),t=t.name.trim().toLowerCase();return ethis.logMsg(...e),debug:()=>{},error:(...e)=>this.errorMsg(...e)}),this.updateEspConnected(this.connectionStates.CONNECTED)}catch(e){return this.updateEspConnected(this.connectionStates.DISCONNECTED),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.logMsg("Connected to "+this.esploader.chip.CHIP_NAME),this.chipFamily==(""+this.esploader.chip.CHIP_NAME).toLowerCase().replaceAll("-","")?(this.logMsg("This chip checks out"),await this.nextStep()):(this.errorMsg("Oops, this is the wrong firmware for your board."),await this.espDisconnect())}catch(e){this.transport&&await this.transport.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{let s=new FileReader;return new Promise((e,t)=>{s.onerror=()=>{s.abort(),t(new DOMException("Problem parsing input file."))},s.onload=()=>{e(s.result)},s.readAsBinaryString(i)})})(e),address:0});let a=0,r=(this.showDialog(this.dialogs.actionProgress,{action:"Flashing "+t}),this.currentDialogElement.querySelector("#stepProgress"));r.value=0;try{var s={fileArray:i,flashSize:"keep",eraseAll:!1,compress:!0,reportProgress:(e,t,i)=>{var s=Math.round(t/i*100);s>a&&(r.value=s,this.logMsg(s+`% (${t}/${i})...`),a=s)},calculateMD5Hash:e=>CryptoJS.MD5(CryptoJS.enc.Latin1.parse(e))};await this.esploader.writeFlash(s)}catch(e){this.errorMsg(`Unable to flash file: ${t}. Error Message: `+e)}}}async downloadAndCopy(t,i=null){if(i=i||this.bootDriveHandle){var s,a=this.currentDialogElement.querySelector("#stepProgress"),[t,r]=(a.value=0,await this.downloadAndExtract(t)),o=await(await i.getFileHandle(t,{create:!0})).createWritable(),l=r.size;let e=0;for(;e{let e={};for(;0==Object.entries(e).length||null===e.ip;)e=await this.getDeviceHostInfo(),await this.sleep(300)},1e4)}catch(e){return console.warn("Unable to get IP Address. Network Credentials may be incorrect"),null}}else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),null;e=toml.stringify(e);await this.writeFile("settings.toml",e)}}async getCurrentSettings(){let e;if(this.repl)e=await this.runCode(["f = open('settings.toml', 'r')","print(f.read())","f.close()"]);else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),{};e=await this.readFile("settings.toml")}return e?toml.parse(e):(this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing..."),{})}async serialTransmit(e){var t=new TextEncoder;this.writer&&(t=t.encode(e),await this.writer.ready.catch(e=>{this.errorMsg("Ready error: "+e)}),await this.writer.write(t).catch(e=>{this.errorMsg("Chunk error: "+e)}),await this.writer.ready)}async _readSerialLoop(){if(this.replSerialDevice){var e=new Event("message"),t=new TextDecoder;if(this.replSerialDevice.readable)for(this.reader=this.replSerialDevice.readable.getReader();;){var{value:i,done:s}=await this.reader.read();if(i&&(e.data=t.decode(i),this.replSerialDevice.dispatchEvent(e)),s){this.reader.releaseLock(),await this.onReplDisconnected();break}}this.logMsg("Read Loop Stopped. Closing Serial Port.")}}async getDeviceHostInfo(){return this.repl?{ip:this.repl.getIpAddress(),version:this.repl.getVersion()}:{}}hasNativeUsb(){return!(!this.chipFamily||"esp32c3".includes(this.chipFamily))}sleep(t){return new Promise(e=>setTimeout(e,t))}timeout(e,t){return Promise.race([e(),this.sleep(t).then(()=>{throw Error("Timed Out")})])}}customElements.define("cp-install-button",CPInstallButton,{extends:"button"});export{CPInstallButton}; \ No newline at end of file + `,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.sort((e,t)=>{e=e.name.trim().toLowerCase(),t=t.name.trim().toLowerCase();return ethis.logMsg(...e),debug:()=>{},error:(...e)=>this.errorMsg(...e)}),this.updateEspConnected(this.connectionStates.CONNECTED)}catch(e){return this.updateEspConnected(this.connectionStates.DISCONNECTED),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. If installing the bootloader, make sure you are in ROM bootloader mode.")}try{this.logMsg("Connected to "+this.esploader.chip.CHIP_NAME),this.chipFamily==(""+this.esploader.chip.CHIP_NAME).toLowerCase().replaceAll("-","")?(this.logMsg("This chip checks out"),await this.nextStep()):(this.errorMsg("Oops, this is the wrong firmware for your board."),await this.espDisconnect())}catch(e){this.transport&&await this.transport.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){!(1Downloaded ${a}

      Extracting ${t} ...

      `}),[e,r]=await this.findAndExtractFromZip(r,t),!r)return void this.errorMsg(`Unable to find ${t} in `+a);o=e}return[a,o,r]}async downloadAndInstall(e,t=null,i=!1){var[e,,i]=await this.downloadAndExtract(e,t,i),s=[];if(i){s.push({data:await(i=>{let s=new FileReader;return new Promise((e,t)=>{s.onerror=()=>{s.abort(),t(new DOMException("Problem parsing input file"))},s.onload=()=>{e(s.result)},s.readAsBinaryString(i)})})(i),address:0});let a=0,r=(this.showDialog(this.dialogs.actionProgress,{action:t?html`

      Downloaded ${e}

      Extracted ${t}

      Flashing (be patient; you will see pauses) ...

      `:html`

      Downloaded ${e}

      Flashing (be patient; you will see pauses) ...

      `}),this.currentDialogElement.querySelector("#stepProgress"));r.value=0;try{var o={fileArray:s,flashSize:"keep",eraseAll:!1,compress:!0,reportProgress:(e,t,i)=>{var s=Math.round(t/i*100);s>a&&(r.value=s,this.logMsg(s+`% (${t}/${i}) ...`),a=s)},calculateMD5Hash:e=>CryptoJS.MD5(CryptoJS.enc.Latin1.parse(e))};await this.esploader.writeFlash(o)}catch(e){this.errorMsg(`Unable to flash file: ${t}. Error Message: `+e)}}}async downloadAndCopy(t,i=null){if(i=i||this.bootDriveHandle){var[t,,s]=await this.downloadAndExtract(t),a=(this.showDialog(this.dialogs.actionProgress,{action:html`

      Downloaded: ${t}

      Flashing ...

      `}),this.currentDialogElement.querySelector("#stepProgress"));a.value=0;var r,o=await(await i.getFileHandle(t,{create:!0})).createWritable(),l=s.size;let e=0;for(;e{let e={};for(;0==Object.entries(e).length||null===e.ip;)e=await this.getDeviceHostInfo(),await this.sleep(300)},1e4)}catch(e){return console.warn("Unable to get IP Address. Network Credentials may be incorrect"),null}}else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),null;e=toml.stringify(e);await this.writeFile("settings.toml",e)}}async getCurrentSettings(){let e;if(this.repl)e=await this.runCode(["f = open('settings.toml', 'r')","print(f.read())","f.close()"]);else{if(!this.circuitpyDriveHandle)return this.errorMsg("Connect to the CIRCUITPY drive or the REPL first"),{};e=await this.readFile("settings.toml")}return e?toml.parse(e):(this.logMsg("Unable to read settings.toml from CircuitPython. It may not exist. Continuing ..."),{})}async serialTransmit(e){var t=new TextEncoder;this.writer&&(t=t.encode(e),await this.writer.ready.catch(e=>{this.errorMsg("Ready error: "+e)}),await this.writer.write(t).catch(e=>{this.errorMsg("Chunk error: "+e)}),await this.writer.ready)}async _readSerialLoop(){if(this.replSerialDevice){var e=new Event("message"),t=new TextDecoder;if(this.replSerialDevice.readable)for(this.reader=this.replSerialDevice.readable.getReader();;){var{value:i,done:s}=await this.reader.read();if(i&&(e.data=t.decode(i),this.replSerialDevice.dispatchEvent(e)),s){this.reader.releaseLock(),await this.onReplDisconnected();break}}this.logMsg("Read Loop Stopped. Closing Serial Port.")}}async getDeviceHostInfo(){return this.repl?{ip:this.repl.getIpAddress(),version:this.repl.getVersion()}:{}}hasNativeUsb(){return!(!this.chipFamily||"esp32c3".includes(this.chipFamily))}sleep(t){return new Promise(e=>setTimeout(e,t))}timeout(e,t){return Promise.race([e(),this.sleep(t).then(()=>{throw Error("Timed Out")})])}}customElements.define("cp-install-button",CPInstallButton,{extends:"button"});export{CPInstallButton}; \ No newline at end of file