Dialog wizard groundwork mostly working

This commit is contained in:
Melissa LeBlanc-Williams 2023-01-10 18:36:51 -08:00
parent 79de9d5aa2
commit c0c01ec808
10 changed files with 1526 additions and 113 deletions

View file

@ -112,7 +112,7 @@
<a class="download-button {% if version.stable %}stable{% else %}unstable{% endif %} {{ extension }}" href="{{ board_url }}/en_US/adafruit-circuitpython-{{ board_id }}-en_US-{{ version.version }}.{{ extension }}">DOWNLOAD .{{ extension | upcase }} NOW <i class="fas fa-download" aria-hidden="true"></i></a> <a class="download-button {% if version.stable %}stable{% else %}unstable{% endif %} {{ extension }}" href="{{ board_url }}/en_US/adafruit-circuitpython-{{ board_id }}-en_US-{{ version.version }}.{{ extension }}">DOWNLOAD .{{ extension | upcase }} NOW <i class="fas fa-download" aria-hidden="true"></i></a>
{% endfor %} {% endfor %}
{% if page.family == 'esp32s2' or page.family == 'esp32c3' or page.family == 'esp32s3' or page.family == 'esp32' %} {% if page.family == 'esp32s2' or page.family == 'esp32c3' or page.family == 'esp32s3' or page.family == 'esp32' %}
<a class="download-button {% if version.stable %}stable{% else %}unstable{% endif %} bin" id="esp-installer" href="#">OPEN INSTALLER <i class="fas fa-magic" aria-hidden="true"></i></a> <button is="cp-install-button" class="installer-button" boardname="{{ page.name }}" boardid="{{ board_id }}" firmware="{{ board_url }}/en_US/adafruit-circuitpython-{{ board_id }}-en_US-{{ version.version }}.{{ extension }}">OPEN INSTALLER <i class="fas fa-magic" aria-hidden="true"></i></button>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -135,7 +135,7 @@
<h3>Absolute Newest</h3> <h3>Absolute Newest</h3>
<p> <p>
Every time we commit new code to CircuitPython we automatically Every time we commit new code to CircuitPython we automatically
build binaries for each board and language. The binaries are build binaries for each board and language. The binaries are
stored on Amazon S3, organized by board, and then by stored on Amazon S3, organized by board, and then by
language. Try them if you want the absolute latest and are language. Try them if you want the absolute latest and are
feeling daring or want to see if a problem has been fixed. feeling daring or want to see if a problem has been fixed.
@ -149,10 +149,10 @@
<h3>Past Releases</h3> <h3>Past Releases</h3>
<p> <p>
All previous releases are listed on GitHub, with release notes, All previous releases are listed on GitHub, with release notes,
and are available for download from Amazon S3. They are handy for and are available for download from Amazon S3. They are handy for
testing, but otherwise we recommend using the latest stable testing, but otherwise we recommend using the latest stable
release. Some older GitHub release pages include the same release. Some older GitHub release pages include the same
binaries for downloading. But we have discontinued including binaries for downloading. But we have discontinued including
binaries as assets on newer release pages because of the large binaries as assets on newer release pages because of the large
number of files for each release. number of files for each release.
</p> </p>

View file

@ -28,7 +28,6 @@ layout: default
</div> </div>
{% if page.family == 'esp32' or page.family == 'esp32s2' or page.family == 'esp32c3' or page.family == 'esp32s3' %} {% if page.family == 'esp32' or page.family == 'esp32s2' or page.family == 'esp32c3' or page.family == 'esp32s3' %}
{% include downloads/esp_installer.html %} <script src="/cpinstaller/src/cpinstaller.js" type="module"></script>
<script src="/assets/javascript/espinstaller.js" type="module"></script>
{% endif %} {% endif %}
<script src="/assets/javascript/download.js"></script> <script src="/assets/javascript/download.js"></script>

View file

@ -21,3 +21,4 @@
@import 'pages/stats'; @import 'pages/stats';
@import 'pages/libraries'; @import 'pages/libraries';
@import 'pages/contributing'; @import 'pages/contributing';
@import 'pages/espinstaller';

View file

@ -44,13 +44,14 @@
} }
.download { .download {
a.download-button, a.download-button-unrecommended { a.download-button, a.download-button-unrecommended, button[is="cp-install-button"] {
display: inline-block; display: inline-block;
width: auto; width: auto;
padding: 15px 30px 15px 30px; padding: 15px 30px 15px 30px;
color: #fff; color: #fff;
border-radius: 5px; border-radius: 5px;
border: none; border: none;
cursor: pointer;
i { i {
padding-left: 10px; padding-left: 10px;
@ -143,8 +144,14 @@
} }
} }
button[is="cp-install-button"] {
color: $purple;
background-color: transparent;
border: 2px solid $purple;
}
.download-buttons { .download-buttons {
a { a, button {
width: 100%; width: 100%;
height: 49px; height: 49px;
padding: 0; padding: 0;

View file

@ -0,0 +1,66 @@
.cp-installer-dialog {
min-width: 300px;
min-height: 100px;
max-width: 600px;
padding-bottom: 50px;
border-radius: 15px;
&::backdrop {
backdrop-filter: blur(2px);
}
.close-button {
position: absolute;
right: 10px;
top: 10px;
width: 24px;
height: 24px;
opacity: 0.3;
background: transparent;
border: none;
&:hover {
opacity: 1;
}
&:before, &:after {
position: absolute;
left: 12px;
top: 0;
content: ' ';
height: 25px;
width: 2px;
background-color: #333;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
.dialog-body {
margin-top: 24px;
progress {
width: 100%;
}
}
.dialog-navigation {
display: flex;
justify-content: space-between;
position: absolute;
bottom: 20px;
padding-left: inherit;
padding-right: inherit;
left: 0;
right: 0;
button {
margin: 10px 20px;
}
}
}

View file

@ -1,4 +1,54 @@
<div id="espinstaller"> <!--
// Wizard screens
// - Menu (installerMenu)
// - Verify user wants to install (installerVerify)
// - erase flash (installerErase)
// - if esp32 or c3 flash bin (installerFlash)
// - if s2 or s3, flash bootloader (installerFlash)
// - if s2 or s3, copy uf2 (May need to use File System Access API)
// - request wifi credentials (skip, connect buttons) and AP password
// - generate and program settings.toml via REPL
// - install complete
-->
<dialog class="installer" id="installerMenu">
</dialog>
<dialog class="installer" id="installerVerify">
</dialog>
<dialog class="installer" id="installerErase">
</dialog>
<!-- Used for flashing either bin or uf2 -->
<dialog class="installer" id="installerFlash">
</dialog>
<dialog class="installer" id="installerCopyUf2">
</dialog>
<dialog class="installer" id="installerCredentials">
</dialog>
<dialog class="installer" id="installerGenerateSettings">
</dialog>
<dialog class="installer" id="installerSuccess">
</dialog>
<dialog class="installer" id="installerError">
</dialog>
<dialog id="installerDialog">
<main class="main"> <main class="main">
<div id="notSupported" class="notSupported"> <div id="notSupported" class="notSupported">
Sorry, <b>Web Serial</b> is not supported on your browser at this time. Browsers we expect to work: Sorry, <b>Web Serial</b> is not supported on your browser at this time. Browsers we expect to work:
@ -94,4 +144,4 @@
<div id="log" class="hidden console-item"></div> <div id="log" class="hidden console-item"></div>
</main> </main>
</div> </dialog>

File diff suppressed because it is too large Load diff

View file

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#fff;opacity:0;}.cls-2{fill:#231f20;}</style></defs><title>close</title><g id="Layer_2" data-name="Layer 2"><g id="close"><g id="close-2" data-name="close"><rect class="cls-1" width="24" height="24" transform="translate(24 24) rotate(180)"/><path class="cls-2" d="M13.41,12l4.3-4.29a1,1,0,1,0-1.42-1.42L12,10.59,7.71,6.29A1,1,0,0,0,6.29,7.71L10.59,12l-4.3,4.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0L12,13.41l4.29,4.3a1,1,0,0,0,1.42,0,1,1,0,0,0,0-1.42Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 553 B

View file

@ -48,28 +48,50 @@ const full_bin_program = [stage_erase_all, stage_flash_cpbin, stage_program_sett
const full_uf2_program = [stage_erase_all, stage_flash_bootloader, stage_copy_uf2, stage_program_settings]; const full_uf2_program = [stage_erase_all, stage_flash_bootloader, stage_copy_uf2, stage_program_settings];
const factory_reset_program = [stage_erase_all, stage_flash_bootloader]; const factory_reset_program = [stage_erase_all, stage_flash_bootloader];
// Wizard screens
// - Menu
// - Verify user wants to install
// - erase flash
// - if esp32 or c3 flash bin
// - if s2 or s3, flash bootloader
// - if s2 or s3, copy uf2 (May need to use File System Access API)
// - request wifi credentials (skip, connect buttons) and AP password
// - generate and program settings.toml via REPL
// - install complete
// So we will have a couple of wizard flows and we'll need to associate the flow with the board
// We should add the info to the board's page in the boards folder
// Changes to make:
// Hide the log and make it accessible via the menu
// Generate dialogs on the fly
// Make a drop-in component
// Keep in mind it will be used for LEARN too
// May need to deal with CORS issues
// May need to deal with the fact that the ESPTool uses Web Serial and CircuitPython REPL uses Web Serial
const maxLogLength = 100; const maxLogLength = 100;
const log = document.getElementById("log"); const installerDialog = document.getElementById('installerDialog');
const semverLabel = document.getElementById("semver"); const butInstallers = document.getElementsByClassName("installer-button");
const butShowConsole = document.getElementById("butShowConsole");
const consoleItems = document.getElementsByClassName("console-item"); const log = installerDialog.querySelector("#log");
const butConnect = document.getElementById("butConnect"); const semverLabel = installerDialog.querySelector("#semver");
const binSelector = document.getElementById("binSelector"); //const butShowConsole = installerDialog.querySelector("#butShowConsole");
const baudRate = document.getElementById("baudRate"); const consoleItems = installerDialog.getElementsByClassName("console-item");
const butClear = document.getElementById("butClear"); const butConnect = installerDialog.querySelector("#butConnect");
const butProgram = document.getElementById("butProgram"); const binSelector = installerDialog.querySelector("#binSelector");
const butProgramBootloader = document.getElementById("butProgramBootloader"); const baudRate = installerDialog.querySelector("#baudRate");
const autoscroll = document.getElementById("autoscroll"); const butClear = installerDialog.querySelector("#butClear");
const lightSS = document.getElementById("light"); const butProgram = installerDialog.querySelector("#butProgram");
const darkSS = document.getElementById("dark"); const butProgramBootloader = installerDialog.querySelector("#butProgramBootloader");
const darkMode = document.getElementById("darkmode"); const autoscroll = installerDialog.querySelector("#autoscroll");
// TODO: This should grab the stuff for settings.toml // TODO: This should grab the stuff for settings.toml
const partitionData = document.querySelectorAll(".field input.partition-data"); const partitionData = installerDialog.querySelectorAll(".field input.partition-data");
const progress = installerDialog.querySelector("#progressBar");
const progress = document.getElementById("progressBar"); const stepname = installerDialog.querySelector("#stepname");
const stepname = document.getElementById("stepname"); const appDiv = installerDialog.querySelector("#app");
const appDiv = document.getElementById("app");
const disableWhileBusy = [partitionData, butProgram, butProgramBootloader, baudRate]; const disableWhileBusy = [partitionData, butProgram, butProgramBootloader, baudRate];
@ -79,7 +101,6 @@ let debug;
// querystring options // querystring options
const QUERYSTRING_BOARD_KEY = 'board' const QUERYSTRING_BOARD_KEY = 'board'
const QUERYSTRING_DEBUG_KEY = 'debug' const QUERYSTRING_DEBUG_KEY = 'debug'
const QUERYSTRING_STAGING_KEY = 'staging'
function getFromQuerystring(key) { function getFromQuerystring(key) {
const location = new URL(document.location) const location = new URL(document.location)
@ -87,6 +108,15 @@ function getFromQuerystring(key) {
return params.get(key) return params.get(key)
} }
for (let installer of butInstallers) {
installer.addEventListener("click", (e) => {
let installerName = e.target.id;
installerDialog.showModal();
e.preventDefault();
e.stopImmediatePropagation();
});
}
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// detect debug setting from querystring // detect debug setting from querystring
debug = getFromQuerystring(QUERYSTRING_DEBUG_KEY); debug = getFromQuerystring(QUERYSTRING_DEBUG_KEY);
@ -101,11 +131,11 @@ document.addEventListener("DOMContentLoaded", () => {
debug = getArgs["debug"] == "1" || getArgs["debug"].toLowerCase() == "true"; debug = getArgs["debug"] == "1" || getArgs["debug"].toLowerCase() == "true";
} }
butShowConsole.addEventListener("click", () => { /*butShowConsole.addEventListener("click", () => {
showConsole = !showConsole showConsole = !showConsole
saveSetting("showConsole", showConsole) saveSetting("showConsole", showConsole)
toggleConsole(showConsole) toggleConsole(showConsole)
}) })*/
// register dom event listeners // register dom event listeners
butConnect.addEventListener("click", () => { butConnect.addEventListener("click", () => {
@ -127,17 +157,16 @@ document.addEventListener("DOMContentLoaded", () => {
toggleUIConnected(false); toggleUIConnected(false);
}); });
}); });
butClear.addEventListener("click", clickClear); //butClear.addEventListener("click", clickClear);
butProgram.addEventListener("click", clickProgram); butProgram.addEventListener("click", clickProgram);
butProgramNvm.addEventListener("click", clickProgramNvm); butProgramBootloader.addEventListener("click", clickProgramNvm);
for (let i = 0; i < partitionData.length; i++) { for (let i = 0; i < partitionData.length; i++) {
partitionData[i].addEventListener("change", checkProgrammable); partitionData[i].addEventListener("change", checkProgrammable);
partitionData[i].addEventListener("keydown", checkProgrammable); partitionData[i].addEventListener("keydown", checkProgrammable);
partitionData[i].addEventListener("input", checkProgrammable); partitionData[i].addEventListener("input", checkProgrammable);
} }
autoscroll.addEventListener("click", clickAutoscroll); //autoscroll.addEventListener("click", clickAutoscroll);
baudRate.addEventListener("change", changeBaudRate); //baudRate.addEventListener("change", changeBaudRate);
darkMode.addEventListener("click", clickDarkMode);
// handle runaway errors // handle runaway errors
window.addEventListener("error", event => { window.addEventListener("error", event => {
@ -155,10 +184,9 @@ document.addEventListener("DOMContentLoaded", () => {
notSupported.classList.add("hidden"); notSupported.classList.add("hidden");
} }
initBinSelector(); //initBinSelector();
initBaudRate(); //initBaudRate();
loadAllSettings(); loadAllSettings();
updateTheme();
logMsg("CircuitPython ESP32 Installer loaded."); logMsg("CircuitPython ESP32 Installer loaded.");
checkProgrammable(); checkProgrammable();
}); });
@ -171,7 +199,7 @@ function createOption(value, text) {
} }
let latestFirmwares = [] let latestFirmwares = []
async function initBinSelector() { /*async function initBinSelector() {
// fetch firmware index from io-rails, a list of available littlefs // fetch firmware index from io-rails, a list of available littlefs
// firmware items, like the example above // firmware items, like the example above
const response = await fetch(`${FIRMWARE_API}/wipper_releases`) const response = await fetch(`${FIRMWARE_API}/wipper_releases`)
@ -212,7 +240,7 @@ function populateBinSelector(title, filter=() => true) {
}) })
return any return any
} }*/
function returnToStepOne() { function returnToStepOne() {
showStep(1, { hideHigherSteps: false }); showStep(1, { hideHigherSteps: false });
@ -301,7 +329,7 @@ function toggleConsole(show) {
consoleItems.item(idx).classList[consoleItemsMethod]("hidden") consoleItems.item(idx).classList[consoleItemsMethod]("hidden")
} }
// toggle the button // toggle the button
butShowConsole.checked = show //butShowConsole.checked = show
// tell the app if it's sharing space with the console // tell the app if it's sharing space with the console
const appDivMethod = show ? "add" : "remove" const appDivMethod = show ? "add" : "remove"
appDiv.classList[appDivMethod]("with-console") appDiv.classList[appDivMethod]("with-console")
@ -376,9 +404,9 @@ function logMsg(text) {
log.innerHTML = logLines.splice(-maxLogLength).join("<br>\n"); log.innerHTML = logLines.splice(-maxLogLength).join("<br>\n");
} }
if (autoscroll.checked) { /*if (autoscroll.checked) {
log.scrollTop = log.scrollHeight; log.scrollTop = log.scrollHeight;
} }*/
} }
function debugMsg(...args) { function debugMsg(...args) {
@ -454,27 +482,6 @@ function formatMacAddr(macAddr) {
return macAddr.map((value) => value.toString(16).toUpperCase().padStart(2, "0")).join(":"); return macAddr.map((value) => value.toString(16).toUpperCase().padStart(2, "0")).join(":");
} }
/**
* @name updateTheme
* Sets the theme to Adafruit (dark) mode. Can be refactored later for more themes
*/
function updateTheme() {
// Disable all themes
document.querySelectorAll("link[rel=stylesheet].alternate").forEach((styleSheet) => {
enableStyleSheet(styleSheet, false);
});
if (darkMode.checked) {
enableStyleSheet(darkSS, true);
} else {
enableStyleSheet(lightSS, true);
}
}
function enableStyleSheet(node, enabled) {
node.disabled = !enabled;
}
/** /**
* @name reset * @name reset
* Reset the Panels, Log, and associated data * Reset the Panels, Log, and associated data
@ -618,15 +625,6 @@ async function clickAutoscroll() {
saveSetting("autoscroll", autoscroll.checked); saveSetting("autoscroll", autoscroll.checked);
} }
/**
* @name clickDarkMode
* Change handler for the Dark Mode checkbox.
*/
async function clickDarkMode() {
updateTheme();
saveSetting("darkmode", darkMode.checked);
}
/** /**
* @name clickProgram * @name clickProgram
* Click handler for the program button. * Click handler for the program button.
@ -643,10 +641,6 @@ async function clickProgramNvm() {
await programScript(factory_reset_program); await programScript(factory_reset_program);
} }
function stagingFlagSet() {
return getFromQuerystring(QUERYSTRING_STAGING_KEY)
}
async function populateSecretsFile(path) { async function populateSecretsFile(path) {
let response = await fetch(path); let response = await fetch(path);
let contents = await response.json(); let contents = await response.json();
@ -662,10 +656,6 @@ async function populateSecretsFile(path) {
} }
} }
// add "io_url" property to json root with the staging url override
if(stagingFlagSet()) {
updateObject(contents, 'io_url', 'io.adafruit.us')
}
// Convert the data to text and return // Convert the data to text and return
return JSON.stringify(contents, null, 4); return JSON.stringify(contents, null, 4);
} }
@ -831,8 +821,11 @@ async function programScript(stages) {
} }
} else if (stages[i] == stage_program_settings) { } else if (stages[i] == stage_program_settings) {
// TODO: This needs to be rewritten to talk with circuitpython // TODO: This needs to be rewritten to talk with circuitpython
// and run python code via the repl to write a settings.toml file
// See https://learn.adafruit.com/circuitpython-with-esp32-quick-start/setting-up-web-workflow
// and https://github.com/circuitpython/web-editor/pull/46
steps.push({ steps.push({
name: "Generating and Flashing LittleFS Partition", name: "Generating and Writing the WiFi Settings",
func: async function (params) { func: async function (params) {
let fileSystemImage = await generate(params.flashParams); let fileSystemImage = await generate(params.flashParams);
@ -914,11 +907,10 @@ async function programScript(stages) {
function getValidFields() { function getValidFields() {
// Validate user inputs // Validate user inputs
const validFields = []; const validFields = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 3; i++) {
const { id, value } = partitionData[i] const { id, value } = partitionData[i]
// password & brightness can be blank, the rest must have some value // password & brightness can be blank, the rest must have some value
if (id === "network_type_wifi.network_password" || if (id === "network_type_wifi.network_password" ||
id === "status_pixel_brightness" ||
value.length > 0) { value.length > 0) {
validFields.push(i); validFields.push(i);
} }
@ -938,29 +930,6 @@ async function checkProgrammable() {
} }
} }
/**
* @name checkFirmware
* Handler for firmware upload changes
*/
async function checkFirmware(event) {
let filename = event.target.value.split("\\").pop();
let label = event.target.parentNode.querySelector("span");
let icon = event.target.parentNode.querySelector("svg");
if (filename != "") {
if (filename.length > 17) {
label.innerHTML = filename.substring(0, 14) + "&hellip;";
} else {
label.innerHTML = filename;
}
icon.classList.add("hidden");
} else {
label.innerHTML = "Choose a file&hellip;";
icon.classList.remove("hidden");
}
await checkProgrammable();
}
/** /**
* @name clickClear * @name clickClear
* Click handler for the clear button. * Click handler for the clear button.
@ -997,9 +966,8 @@ function toggleUIConnected(connected) {
function loadAllSettings() { function loadAllSettings() {
// Load all saved settings or defaults // Load all saved settings or defaults
autoscroll.checked = loadSetting("autoscroll", true); //autoscroll.checked = loadSetting("autoscroll", true);
baudRate.value = loadSetting("baudrate", baudRates[0]); //baudRate.value = loadSetting("baudrate", baudRates[0]);
darkMode.checked = loadSetting("darkmode", false);
showConsole = loadSetting('showConsole', false); showConsole = loadSetting('showConsole', false);
toggleConsole(showConsole); toggleConsole(showConsole);
} }