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>
{% endfor %}
{% 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 %}
</div>
</div>

View file

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

View file

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

View file

@ -44,13 +44,14 @@
}
.download {
a.download-button, a.download-button-unrecommended {
a.download-button, a.download-button-unrecommended, button[is="cp-install-button"] {
display: inline-block;
width: auto;
padding: 15px 30px 15px 30px;
color: #fff;
border-radius: 5px;
border: none;
cursor: pointer;
i {
padding-left: 10px;
@ -143,8 +144,14 @@
}
}
button[is="cp-install-button"] {
color: $purple;
background-color: transparent;
border: 2px solid $purple;
}
.download-buttons {
a {
a, button {
width: 100%;
height: 49px;
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">
<div id="notSupported" class="notSupported">
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>
</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 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 log = document.getElementById("log");
const semverLabel = document.getElementById("semver");
const butShowConsole = document.getElementById("butShowConsole");
const consoleItems = document.getElementsByClassName("console-item");
const butConnect = document.getElementById("butConnect");
const binSelector = document.getElementById("binSelector");
const baudRate = document.getElementById("baudRate");
const butClear = document.getElementById("butClear");
const butProgram = document.getElementById("butProgram");
const butProgramBootloader = document.getElementById("butProgramBootloader");
const autoscroll = document.getElementById("autoscroll");
const lightSS = document.getElementById("light");
const darkSS = document.getElementById("dark");
const darkMode = document.getElementById("darkmode");
const installerDialog = document.getElementById('installerDialog');
const butInstallers = document.getElementsByClassName("installer-button");
const log = installerDialog.querySelector("#log");
const semverLabel = installerDialog.querySelector("#semver");
//const butShowConsole = installerDialog.querySelector("#butShowConsole");
const consoleItems = installerDialog.getElementsByClassName("console-item");
const butConnect = installerDialog.querySelector("#butConnect");
const binSelector = installerDialog.querySelector("#binSelector");
const baudRate = installerDialog.querySelector("#baudRate");
const butClear = installerDialog.querySelector("#butClear");
const butProgram = installerDialog.querySelector("#butProgram");
const butProgramBootloader = installerDialog.querySelector("#butProgramBootloader");
const autoscroll = installerDialog.querySelector("#autoscroll");
// TODO: This should grab the stuff for settings.toml
const partitionData = document.querySelectorAll(".field input.partition-data");
const progress = document.getElementById("progressBar");
const stepname = document.getElementById("stepname");
const appDiv = document.getElementById("app");
const partitionData = installerDialog.querySelectorAll(".field input.partition-data");
const progress = installerDialog.querySelector("#progressBar");
const stepname = installerDialog.querySelector("#stepname");
const appDiv = installerDialog.querySelector("#app");
const disableWhileBusy = [partitionData, butProgram, butProgramBootloader, baudRate];
@ -79,7 +101,6 @@ let debug;
// querystring options
const QUERYSTRING_BOARD_KEY = 'board'
const QUERYSTRING_DEBUG_KEY = 'debug'
const QUERYSTRING_STAGING_KEY = 'staging'
function getFromQuerystring(key) {
const location = new URL(document.location)
@ -87,6 +108,15 @@ function getFromQuerystring(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", () => {
// detect debug setting from querystring
debug = getFromQuerystring(QUERYSTRING_DEBUG_KEY);
@ -101,11 +131,11 @@ document.addEventListener("DOMContentLoaded", () => {
debug = getArgs["debug"] == "1" || getArgs["debug"].toLowerCase() == "true";
}
butShowConsole.addEventListener("click", () => {
/*butShowConsole.addEventListener("click", () => {
showConsole = !showConsole
saveSetting("showConsole", showConsole)
toggleConsole(showConsole)
})
})*/
// register dom event listeners
butConnect.addEventListener("click", () => {
@ -127,17 +157,16 @@ document.addEventListener("DOMContentLoaded", () => {
toggleUIConnected(false);
});
});
butClear.addEventListener("click", clickClear);
//butClear.addEventListener("click", clickClear);
butProgram.addEventListener("click", clickProgram);
butProgramNvm.addEventListener("click", clickProgramNvm);
butProgramBootloader.addEventListener("click", clickProgramNvm);
for (let i = 0; i < partitionData.length; i++) {
partitionData[i].addEventListener("change", checkProgrammable);
partitionData[i].addEventListener("keydown", checkProgrammable);
partitionData[i].addEventListener("input", checkProgrammable);
}
autoscroll.addEventListener("click", clickAutoscroll);
baudRate.addEventListener("change", changeBaudRate);
darkMode.addEventListener("click", clickDarkMode);
//autoscroll.addEventListener("click", clickAutoscroll);
//baudRate.addEventListener("change", changeBaudRate);
// handle runaway errors
window.addEventListener("error", event => {
@ -155,10 +184,9 @@ document.addEventListener("DOMContentLoaded", () => {
notSupported.classList.add("hidden");
}
initBinSelector();
initBaudRate();
//initBinSelector();
//initBaudRate();
loadAllSettings();
updateTheme();
logMsg("CircuitPython ESP32 Installer loaded.");
checkProgrammable();
});
@ -171,7 +199,7 @@ function createOption(value, text) {
}
let latestFirmwares = []
async function initBinSelector() {
/*async function initBinSelector() {
// fetch firmware index from io-rails, a list of available littlefs
// firmware items, like the example above
const response = await fetch(`${FIRMWARE_API}/wipper_releases`)
@ -212,7 +240,7 @@ function populateBinSelector(title, filter=() => true) {
})
return any
}
}*/
function returnToStepOne() {
showStep(1, { hideHigherSteps: false });
@ -301,7 +329,7 @@ function toggleConsole(show) {
consoleItems.item(idx).classList[consoleItemsMethod]("hidden")
}
// toggle the button
butShowConsole.checked = show
//butShowConsole.checked = show
// tell the app if it's sharing space with the console
const appDivMethod = show ? "add" : "remove"
appDiv.classList[appDivMethod]("with-console")
@ -376,9 +404,9 @@ function logMsg(text) {
log.innerHTML = logLines.splice(-maxLogLength).join("<br>\n");
}
if (autoscroll.checked) {
/*if (autoscroll.checked) {
log.scrollTop = log.scrollHeight;
}
}*/
}
function debugMsg(...args) {
@ -454,27 +482,6 @@ function formatMacAddr(macAddr) {
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
* Reset the Panels, Log, and associated data
@ -618,15 +625,6 @@ async function clickAutoscroll() {
saveSetting("autoscroll", autoscroll.checked);
}
/**
* @name clickDarkMode
* Change handler for the Dark Mode checkbox.
*/
async function clickDarkMode() {
updateTheme();
saveSetting("darkmode", darkMode.checked);
}
/**
* @name clickProgram
* Click handler for the program button.
@ -643,10 +641,6 @@ async function clickProgramNvm() {
await programScript(factory_reset_program);
}
function stagingFlagSet() {
return getFromQuerystring(QUERYSTRING_STAGING_KEY)
}
async function populateSecretsFile(path) {
let response = await fetch(path);
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
return JSON.stringify(contents, null, 4);
}
@ -831,8 +821,11 @@ async function programScript(stages) {
}
} else if (stages[i] == stage_program_settings) {
// 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({
name: "Generating and Flashing LittleFS Partition",
name: "Generating and Writing the WiFi Settings",
func: async function (params) {
let fileSystemImage = await generate(params.flashParams);
@ -914,11 +907,10 @@ async function programScript(stages) {
function getValidFields() {
// Validate user inputs
const validFields = [];
for (let i = 0; i < 5; i++) {
for (let i = 0; i < 3; i++) {
const { id, value } = partitionData[i]
// password & brightness can be blank, the rest must have some value
if (id === "network_type_wifi.network_password" ||
id === "status_pixel_brightness" ||
value.length > 0) {
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
* Click handler for the clear button.
@ -997,9 +966,8 @@ function toggleUIConnected(connected) {
function loadAllSettings() {
// Load all saved settings or defaults
autoscroll.checked = loadSetting("autoscroll", true);
baudRate.value = loadSetting("baudrate", baudRates[0]);
darkMode.checked = loadSetting("darkmode", false);
//autoscroll.checked = loadSetting("autoscroll", true);
//baudRate.value = loadSetting("baudrate", baudRates[0]);
showConsole = loadSetting('showConsole', false);
toggleConsole(showConsole);
}