From e61e6d702da490f29e9f798c5110b2267dd166a2 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 25 Jul 2024 11:27:23 -0700 Subject: [PATCH 01/42] Improved reliability on freshly booted board. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe08554..de4ff71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.0.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.0", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", @@ -37,7 +37,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "2.0.1", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#7e245f35bfa8f8110298b37aaf953ebc65cdb1b2", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#2bc32e7958e1c5bc5885d9af891c9025e8733f08", "license": "MIT" }, "node_modules/@codemirror/autocomplete": { diff --git a/package.json b/package.json index 3be800f..40135c3 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.0.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.0", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From 883b7a2478e4fd02efd6ef046584753001cf2877 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 31 Jul 2024 13:03:38 -0700 Subject: [PATCH 02/42] Implement multiple file selection dialog --- js/common/file_dialog.js | 309 +++++++++++++++++++++++++++++---------- sass/layout/_layout.scss | 1 + 2 files changed, 236 insertions(+), 74 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index 1bc3ec4..b7d4d0a 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -13,6 +13,9 @@ const FA_STYLE_REGULAR = "fa-regular"; const FA_STYLE_SOLID = "fa-solid"; const FA_STYLE_BRANDS = "fa-brands"; +const MODIFIER_SHIFT = "shift"; +const MODIFIER_CTRL = "ctrl"; + // Hide any file or folder matching these exact names const HIDDEN_FILES = [".Trashes", ".metadata_never_index", ".fseventsd"]; @@ -91,6 +94,7 @@ class FileDialog extends GenericModal { this._fileHelper = null; this._readOnlyMode = false; this._progressDialog = null; + this._lastSelectedNode = null; } _removeAllChildNodes(parent) { @@ -110,13 +114,14 @@ class FileDialog extends GenericModal { return "bin"; } - async open(fileHelper, type, hidePaths = null) { + async open(fileHelper, type, hidePaths = null, allowMultiple = true) { if (![FILE_DIALOG_OPEN, FILE_DIALOG_SAVE, FILE_DIALOG_MOVE, FILE_DIALOG_COPY].includes(type)) { return; } this._fileHelper = fileHelper; this._readOnlyMode = await this._showBusy(this._fileHelper.readOnly()); this._hidePaths = hidePaths ? hidePaths : new Set(); + this._allowMultiple = allowMultiple; let p = super.open(); const cancelButton = this._currentModal.querySelector("button.cancel-button"); @@ -176,6 +181,7 @@ class FileDialog extends GenericModal { async _openFolder(path) { const fileList = this._getElement('fileList'); this._removeAllChildNodes(fileList); + this._lastSelectedNode = null; if (path !== undefined) { this._currentPath = path; } @@ -213,28 +219,102 @@ class FileDialog extends GenericModal { if (this._hidePaths.has(this._currentPath)) { return false; } + if (this._multipleItemsSelected()) { + return false; + } return true; } - _handleFileClick(clickedItem) { + _handleFileClick(clickedItem, event) { + // Get a list of nodes that have the data-selected attribute and store them in an array + let listItem; + let previouslySelectedNodes = []; for (let listItem of this._getElement('fileList').childNodes) { - listItem.setAttribute("data-selected", listItem.isEqualNode(clickedItem)); - if (listItem.isEqualNode(clickedItem)) { - listItem.classList.add("selected"); + if (this._isSelected(listItem)) { + previouslySelectedNodes.push(listItem); + } + } + + // Get a list of modifier keys that are currently pressed if event was passed in + let modifierKeys = []; + if (this._allowMultiple && event.shiftKey && this._lastSelectedNode !== null) { + modifierKeys.push(MODIFIER_SHIFT); + } + + // Command for macs, Control for Windows + if (this._allowMultiple && (event.metaKey || event.ctrlKey)) { + modifierKeys.push(MODIFIER_CTRL); + } + + // Go through and add which files should be selected. This will be the key for updating the UI for the + // files that should be selected + let selectedFiles = []; + + // If control is held down, we should start by populating the list with everything currently selected + if (modifierKeys.includes(MODIFIER_CTRL)) { + selectedFiles = previouslySelectedNodes; + } + + // If shift is held down, we should add all the files between the last selected file and the current file + if (modifierKeys.includes(MODIFIER_SHIFT)) { + let lastSelectedIndex = Array.from(this._getElement('fileList').childNodes).indexOf(this._lastSelectedNode); + let currentSelectedIndex = Array.from(this._getElement('fileList').childNodes).indexOf(clickedItem); + let startIndex = Math.min(lastSelectedIndex, currentSelectedIndex); + let endIndex = Math.max(lastSelectedIndex, currentSelectedIndex); + for (let i = startIndex; i <= endIndex; i++) { + selectedFiles.push(this._getElement('fileList').childNodes[i]); + } + } else if (modifierKeys.includes(MODIFIER_CTRL)) { + if (selectedFiles.includes(clickedItem)) { + selectedFiles.splice(selectedFiles.indexOf(clickedItem), 1); } else { - listItem.classList.remove("selected"); + selectedFiles.push(clickedItem); } + } else { + selectedFiles.push(clickedItem); } + + // Go through and update the UI for all of the files that should be selected or delselected + for (listItem of this._getElement('fileList').childNodes) { + // If Control key is pressed, toggle selection + this._selectItem(listItem, selectedFiles.includes(listItem)); + } + if (clickedItem.getAttribute("data-type") != "folder") { this._getElement('fileNameField').value = clickedItem.querySelector("span").innerHTML; } - this._setElementEnabled('okButton', clickedItem.getAttribute("data-type") != "bin"); + + this._lastSelectedNode = clickedItem; + this._setElementEnabled('okButton', !this._multipleItemsSelected() && clickedItem.getAttribute("data-type") != "bin"); this._updateToolbar(); } + _selectItem(listItem, value) { + listItem.setAttribute("data-selected", value); + if (value) { + listItem.classList.add("selected"); + } else { + listItem.classList.remove("selected"); + } + } + + _isSelected(listItem) { + return (/true/i).test(listItem.getAttribute("data-selected")); + } + + _multipleItemsSelected() { + let selectedItems = 0; + for (let listItem of this._getElement('fileList').childNodes) { + if (this._isSelected(listItem)) { + selectedItems++; + } + } + return selectedItems > 1; + } + _updateToolbar() { this._setElementEnabled('delButton', this._canPerformWritableFileOperation()); - this._setElementEnabled('renameButton', this._canPerformWritableFileOperation()); + this._setElementEnabled('renameButton', !this._multipleItemsSelected() && this._canPerformWritableFileOperation()); this._setElementEnabled('moveButton', this._canPerformWritableFileOperation()); this._setElementEnabled('downloadButton', this._canDownload()); } @@ -299,28 +379,38 @@ class FileDialog extends GenericModal { if (this._readOnlyMode) { return false; } - let selectedItem = this._getSelectedFile(); - if (!selectedItem) { + + let selectedItems = this._getSelectedFilesInfo(); + + if (selectedItems.length < 1) { return false; } - let filename = selectedItem.querySelector("span").innerHTML; - if (!this._validName(filename)) { - return false; + + for (let item of selectedItems) { + if (!this._validName(item.filename)) { + return false; + } } - if (!includeFolder && selectedItem.getAttribute("data-type") == "folder") { - return false; + + if (!includeFolder) { + for (let item of selectedItems) { + if (item.filetype == "folder") { + return false; + } + } } + return true; } _canDownload() { - let selectedItem = this._getSelectedFile(); - if (!selectedItem) { - return true; - } - if (!this._validName(selectedItem.querySelector("span").innerHTML)) { - return false; + let selectedItems = this._getSelectedFilesInfo(); + for (let item of selectedItems) { + if (!this._validName(item.filename)) { + return false; + } } + return true; } @@ -331,15 +421,26 @@ class FileDialog extends GenericModal { async _handleDelButton() { if (!this._canPerformWritableFileOperation()) return; - let filename = this._getSelectedFilename(); - filename = this._currentPath + filename; + let filenames = this._getSelectedFilenames(); + let displayFilename = ''; + if (filenames.length == 0) return; + + if (filenames.length > 1) { + displayFilename = `${filenames.length} items`; + } else { + displayFilename = this._currentPath + filenames[0]; + } - if (!confirm(`Are you sure you want to delete ${filename}?`)) { + if (!confirm(`Are you sure you want to delete ${displayFilename}?`)) { return; // If cancelled, do nothing } - // Delete the item - await this._showBusy(this._fileHelper.delete(filename)); + for (let filename of filenames) { + await this._showBusy(this._fileHelper.delete(filename)); + // Delete the item + await this._showBusy(this._fileHelper.delete(this._currentPath + filename)); + } + // Refresh the file list await this._openFolder(); }; @@ -442,65 +543,94 @@ class FileDialog extends GenericModal { // Currently only files are downloadable, but it would be nice to eventually download zipped folders async _handleDownloadButton() { - await this._download(this._getSelectedFilename()); + // TODO: Implement a way to download multiple files at once into a zip file + + await this._download(this._getSelectedFilesInfo()); } - async _download(filename) { + async _download(files) { if (!this._canDownload()) return; - let type, folder, blob; + let folder, blob, filename; + + // If we only have 1 item and it is a file, we can download it directly + // Otherwise, we need to zip the files and download the zip keeping the structure intact + // Function to read the file contents as a blob let getBlob = async (path) => { return await this._fileHelper.readFile(path, true); }; - if (filename) { - type = this._getSelectedFileType(); - } + let getParentFolderName = () => { + if (this._currentPath == "/") { + return "CIRCUITPY"; + } else { + return this._currentPath.split("/").slice(-2).join(""); + } + }; - if (type == "folder" || !filename) { + if (files.length == 1 && files[0].filetype != "folder") { + filename = files[0].filename; + blob = await this._showBusy(getBlob(this._currentPath + filename)); + } else { + // We either have more than 1 item selected or we have a folder selected or we have no file selected and want to download the current folder + // If we have nothing selected, we will download the current folder folder = this._currentPath; - if (filename) { + if (files.length == 0) { + files.push({filename: getParentFolderName(), filetype: "folder", path: this._currentPath}); + } + + if (files.length == 1) { + filename = files[0].filename; folder += filename + "/"; filename = `${filename}.zip`; } else { - if (folder == "/") { - filename = "CIRCUITPY.zip"; - } else { - filename = folder.split("/").slice(-2).join("") + ".zip"; - } + filename = `${getParentFolderName()}.zip`; } - let files = await this._fileHelper.findContainedFiles(folder, true); let zip = new JSZip(); - for (let location of files) { - let contents = await this._showBusy(getBlob(folder + location)); - zip.file(location, contents); + for (let item of files) { + if (item.filetype == "folder") { + let containedFiles = await this._fileHelper.findContainedFiles(folder + item + "/", true); + for (let location of containedFiles) { + let contents = await this._showBusy(getBlob(folder + location)); + zip.file(location, contents); + } + } else { + let contents = await this._showBusy(getBlob(folder + item.filename)); + zip.file(item.filename, contents); + } } blob = await zip.generateAsync({type: "blob"}); - } else { - blob = await this._showBusy(getBlob(this._currentPath + filename)); } + saveAs(blob, filename); } async _handleMoveButton() { + // Get the new path const newFolderDialog = new FileDialog("folder-select", this._showBusy); let hidePaths = new Set(); hidePaths.add(this._getSelectedFilePath()); hidePaths.add(this._currentPath); - let newFolder = await newFolderDialog.open(this._fileHelper, FILE_DIALOG_MOVE, hidePaths); - + let newFolder = await newFolderDialog.open(this._fileHelper, FILE_DIALOG_MOVE, hidePaths, false); + let errors = false; if (newFolder) { - const filename = this._getSelectedFilename(); - const filetype = this._getSelectedFileType() == "folder" ? "folder" : "file"; - const oldPath = this._currentPath + filename; - const newPath = newFolder + filename; - if (await this._showBusy(this._fileHelper.fileExists(newPath))) { - this._showMessage(`Error moving ${oldPath}. Another ${filetype} with the same name already exists at ${newPath}.`); - } else if (!(await this._showBusy(this._fileHelper.move(oldPath, newPath)))) { - this._showMessage(`Error moving ${oldPath} to ${newPath}. Make sure the ${filetype} you are moving exists.`); - } else { + const files = this._getSelectedFilesInfo(); + for (let file of files) { + const filename = file.filename; + const filetype = file.filetype == "folder" ? "folder" : "file"; + const oldPath = this._currentPath + filename; + const newPath = newFolder + filename; + if (await this._showBusy(this._fileHelper.fileExists(newPath))) { + this._showMessage(`Error moving ${oldPath}. Another ${filetype} with the same name already exists at ${newPath}.`); + errors = true; + } else if (!(await this._showBusy(this._fileHelper.move(oldPath, newPath)))) { + this._showMessage(`Error moving ${oldPath} to ${newPath}. Make sure the file you are moving exists.`); + errors = true; + } + } + if (!errors) { // Go to the new location await this._openFolder(newFolder); } @@ -510,7 +640,7 @@ class FileDialog extends GenericModal { async _handleRenameButton() { if (!this._canPerformWritableFileOperation()) return; - let oldName = this._getSelectedFilename(); + let oldName = this._getSelectedFilenames(); let newName = prompt("Enter a new folder name", oldName); // If cancelled, do nothing if (!newName) { @@ -563,27 +693,46 @@ class FileDialog extends GenericModal { await this._openFolder(); }; - _getSelectedFile() { + _getSelectedFiles() { + let files = []; + // Loop through items and see if any have data-selected for (let listItem of this._getElement('fileList').childNodes) { if ((/true/i).test(listItem.getAttribute("data-selected"))) { - return listItem; + files.push(listItem); } } - return null; + return files; } - _getSelectedFilename() { - let file = this._getSelectedFile(); - if (file) { - return file.querySelector("span").innerHTML; + _getSelectedFilesInfo() { + let files = []; + let selectedFles = this._getSelectedFiles(); + for (let file of selectedFles) { + let info = { + filename: file.querySelector("span").innerHTML, + filetype: file.getAttribute("data-type"), + path: file.getAttribute("data-type") == "folder" ? this._currentPath : this._currentPath + file.querySelector("span").innerHTML, + }; + files.push(info); } - return null; + + return files; + } + + _getSelectedFilenames() { + let filenames = []; + let files = this._getSelectedFiles(); + for (let file of files) { + filenames.push(file.querySelector("span").innerHTML); + } + + return filenames; } _getSelectedFileType() { - let file = this._getSelectedFile(); + let file = this._getSelectedFiles(); if (file) { return file.getAttribute("data-type"); } @@ -591,20 +740,32 @@ class FileDialog extends GenericModal { } _getSelectedFilePath() { - let filename = this._getSelectedFilename(); - if (!filename) return null; - - if (this._getSelectedFileType() != "folder") { - return this._currentPath; + // Get the paths of all selected files. These will not be valid paths to move to. + paths = []; + let files = this._getSelectedFilesInfo(); + if (files.length < 1) return []; + + for (let file of files) { + if (file.filetype != "folder") { + if (!paths.includes(this._currentPath)) { + paths.push(this._currentPath); + } + } else { + paths.push(this._currentPath + filename); + } } - return this._currentPath + filename; + return paths; } async _openItem(item, forceNavigate = false) { const fileNameField = this._getElement('fileNameField'); let filetype, filename; - let selectedItem = this._getSelectedFile(); + let selectedItem = this._getSelectedFiles(); + if (selectedItem.length != 1) { + return; + } + selectedItem = selectedItem[0]; if (item !== undefined) { filetype = item.getAttribute("data-type"); @@ -686,7 +847,7 @@ class FileDialog extends GenericModal { if (clickedItem.tagName.toLowerCase() != "a") { clickedItem = clickedItem.parentNode; } - this._handleFileClick(clickedItem); + this._handleFileClick(clickedItem, event); }); fileItem.addEventListener("dblclick", async (event) => { let clickedItem = event.target; diff --git a/sass/layout/_layout.scss b/sass/layout/_layout.scss index ec8cbc3..eb71a34 100644 --- a/sass/layout/_layout.scss +++ b/sass/layout/_layout.scss @@ -360,6 +360,7 @@ grid-template-columns: 30px minmax(60px, 1fr) 60px 1fr; grid-gap: 10px; cursor: default; + user-select:none; &.hidden-file { From 4b38457abad96d4b756815ecfb3cd9f99df50060 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 31 Jul 2024 13:39:01 -0700 Subject: [PATCH 03/42] bug fixes --- js/common/file_dialog.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index b7d4d0a..b106b78 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -741,7 +741,7 @@ class FileDialog extends GenericModal { _getSelectedFilePath() { // Get the paths of all selected files. These will not be valid paths to move to. - paths = []; + let paths = []; let files = this._getSelectedFilesInfo(); if (files.length < 1) return []; @@ -762,10 +762,14 @@ class FileDialog extends GenericModal { const fileNameField = this._getElement('fileNameField'); let filetype, filename; let selectedItem = this._getSelectedFiles(); - if (selectedItem.length != 1) { + if (selectedItem.length > 1) { return; } - selectedItem = selectedItem[0]; + if (selectedItem.length == 1) { + selectedItem = selectedItem[0]; + } else { + selectedItem = null; + } if (item !== undefined) { filetype = item.getAttribute("data-type"); From a64c10a241488c1f095b34fcbeea9d450f4146ca Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 31 Jul 2024 13:52:34 -0700 Subject: [PATCH 04/42] more bug fixes --- js/common/file_dialog.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index b106b78..d4421fa 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -436,9 +436,8 @@ class FileDialog extends GenericModal { } for (let filename of filenames) { - await this._showBusy(this._fileHelper.delete(filename)); // Delete the item - await this._showBusy(this._fileHelper.delete(this._currentPath + filename)); + await this._showBusy(this._fileHelper.delete(filename)); } // Refresh the file list @@ -641,6 +640,10 @@ class FileDialog extends GenericModal { if (!this._canPerformWritableFileOperation()) return; let oldName = this._getSelectedFilenames(); + if (oldName.length != 1) { + return; + } + oldName = oldName[0]; let newName = prompt("Enter a new folder name", oldName); // If cancelled, do nothing if (!newName) { @@ -763,13 +766,10 @@ class FileDialog extends GenericModal { let filetype, filename; let selectedItem = this._getSelectedFiles(); if (selectedItem.length > 1) { + // We don't currently support opening multiple items return; } - if (selectedItem.length == 1) { - selectedItem = selectedItem[0]; - } else { - selectedItem = null; - } + selectedItem = selectedItem.length == 1 ? selectedItem[0] : null; if (item !== undefined) { filetype = item.getAttribute("data-type"); From 8a8ce65039c59402f887d0db1021c53e3f4e73c4 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 1 Aug 2024 15:12:46 -0700 Subject: [PATCH 05/42] Add more file types and hide filename when multiple files selected --- js/common/file_dialog.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index d4421fa..8bd7a38 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -30,10 +30,13 @@ const extensionMap = { "gif": {style: FA_STYLE_REGULAR, icon: "file-image", type: "bin"}, "htm": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, "html": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, + "ini": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, + "inf": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, "jpeg": {style: FA_STYLE_REGULAR, icon: "file-image", type: "bin"}, "jpg": {style: FA_STYLE_REGULAR, icon: "file-image", type: "bin"}, "js": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, "json": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, + "md": {style: FA_STYLE_REGULAR, icon: "file-lines", type: "text"}, "mov": {style: FA_STYLE_REGULAR, icon: "file-video", type: "bin"}, "mp3": {style: FA_STYLE_REGULAR, icon: "file-audio", type: "bin"}, "mp4": {style: FA_STYLE_REGULAR, icon: "file-video", type: "bin"}, @@ -50,8 +53,8 @@ const extensionMap = { const FOLDER_ICON = [FA_STYLE_REGULAR, "fa-folder"]; const DEFAULT_FILE_ICON = [FA_STYLE_REGULAR, "fa-file"]; -const FILESIZE_UNITS = ["bytes", "KB", "MB", "GB"]; -const COMPACT_UNITS = ["", "K", "M", "G"]; +const FILESIZE_UNITS = ["bytes", "KB", "MB", "GB", "TB"]; +const COMPACT_UNITS = ["", "K", "M", "G", "T"]; function getFileExtension(filename) { let extension = filename.split('.').pop(); @@ -280,7 +283,9 @@ class FileDialog extends GenericModal { this._selectItem(listItem, selectedFiles.includes(listItem)); } - if (clickedItem.getAttribute("data-type") != "folder") { + if (this._multipleItemsSelected()) { + this._getElement('fileNameField').value = ""; + } else if (clickedItem.getAttribute("data-type") != "folder") { this._getElement('fileNameField').value = clickedItem.querySelector("span").innerHTML; } From 90bdebd4e4a73bcc778a085a8eb26c506017e7bc Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 2 Aug 2024 14:11:43 -0700 Subject: [PATCH 06/42] Show more info for all workflows --- index.html | 55 ++++++ js/common/ble-file-transfer.js | 43 ++++ js/common/dialogs.js | 38 +++- js/common/fsapi-file-transfer.js | 4 +- js/common/repl-file-transfer.js | 4 +- js/workflows/ble.js | 10 +- js/workflows/usb copy.js | 328 ------------------------------- js/workflows/usb.js | 7 +- sass/layout/_layout.scss | 3 +- 9 files changed, 156 insertions(+), 336 deletions(-) create mode 100644 js/common/ble-file-transfer.js delete mode 100644 js/workflows/usb copy.js diff --git a/index.html b/index.html index 84d2111..a8f30c4 100644 --- a/index.html +++ b/index.html @@ -325,6 +325,22 @@

Select USB Host Folder

IP Address: + + Build Date: + + + + MCU Name: + + + + Board ID: + + + + UID: + +

More network devices

@@ -333,6 +349,45 @@

More network devicesClose + diff --git a/js/common/ble-file-transfer.js b/js/common/ble-file-transfer.js new file mode 100644 index 0000000..fd7abf9 --- /dev/null +++ b/js/common/ble-file-transfer.js @@ -0,0 +1,43 @@ +import {FileTransferClient as BLEFileTransferClient} from '@adafruit/ble-file-transfer-js'; + +// Wrapper for BLEFileTransferClient to add additional functionality +class FileTransferClient extends BLEFileTransferClient { + constructor(bleDevice, bufferSize) { + super(bleDevice, bufferSize); + } + + async versionInfo() { + // Possibly open /boot_out.txt and read the version info + let versionInfo = {}; + console.log("Reading version info"); + let bootout = await this.readFile('/boot_out.txt', false); + console.log(bootout); + if (!bootout) { + console.error("Unable to read boot_out.txt"); + return null; + } + bootout += "\n"; + + // Add these items as they are found + const searchItems = { + version: /Adafruit CircuitPython (.*?) on/, + build_date: /on ([0-9]{4}-[0-9]{2}-[0-9]{2});/, + board_name: /; (.*?) with/, + mcu_name: /with (.*?)\r?\n/, + board_id: /Board ID:(.*?)\r?\n/, + uid: /UID:([0-9A-F]{12,16})\r?\n/, + } + + for (const [key, regex] of Object.entries(searchItems)) { + const match = bootout.match(regex); + + if (match) { + versionInfo[key] = match[1]; + } + } + + return versionInfo; + } +} + +export {FileTransferClient}; \ No newline at end of file diff --git a/js/common/dialogs.js b/js/common/dialogs.js index 74d110f..73b87e1 100644 --- a/js/common/dialogs.js +++ b/js/common/dialogs.js @@ -329,6 +329,10 @@ class DiscoveryModal extends GenericModal { let ip = this._currentModal.querySelector("#ip"); ip.href = `http://${deviceInfo.ip + port}/code/`; ip.textContent = deviceInfo.ip; + this._currentModal.querySelector("#builddate").textContent = deviceInfo.build_date; + this._currentModal.querySelector("#mcuname").textContent = deviceInfo.mcu_name; + this._currentModal.querySelector("#boardid").textContent = deviceInfo.board_id; + this._currentModal.querySelector("#uid").textContent = deviceInfo.uid; } async _refreshDevices() { @@ -378,11 +382,43 @@ class DiscoveryModal extends GenericModal { } } +class DeviceInfoModal extends GenericModal { + async _getDeviceInfo() { + const deviceInfo = await this._showBusy(this._fileHelper.versionInfo()); + this._currentModal.querySelector("#version").textContent = deviceInfo.version; + const boardLink = this._currentModal.querySelector("#board"); + boardLink.href = `https://circuitpython.org/board/${deviceInfo.board_id}/`; + boardLink.textContent = deviceInfo.board_name; + this._currentModal.querySelector("#builddate").textContent = deviceInfo.build_date; + this._currentModal.querySelector("#mcuname").textContent = deviceInfo.mcu_name; + this._currentModal.querySelector("#boardid").textContent = deviceInfo.board_id; + this._currentModal.querySelector("#uid").textContent = deviceInfo.uid; + } + + async open(workflow, documentState) { + this._workflow = workflow; + this._fileHelper = workflow.fileHelper; + this._showBusy = workflow.showBusy.bind(workflow); + this._docState = documentState; + + let p = super.open(); + const okButton = this._currentModal.querySelector("button.ok-button"); + this._addDialogElement('okButton', okButton, 'click', this._closeModal); + + const refreshIcon = this._currentModal.querySelector("i.refresh"); + this._addDialogElement('refreshIcon', refreshIcon, 'click', this._refreshDevices); + + await this._getDeviceInfo(); + return p; + } +} + export { GenericModal, MessageModal, ButtonValueDialog, UnsavedDialog, DiscoveryModal, - ProgressDialog + ProgressDialog, + DeviceInfoModal }; \ No newline at end of file diff --git a/js/common/fsapi-file-transfer.js b/js/common/fsapi-file-transfer.js index 645fdd1..d8a0af5 100644 --- a/js/common/fsapi-file-transfer.js +++ b/js/common/fsapi-file-transfer.js @@ -325,8 +325,10 @@ class FileTransferClient { let bootout = await this.readFile('/boot_out.txt', false); console.log(bootout); if (!bootout) { + console.error("Unable to read boot_out.txt"); return null; } + bootout += "\n"; // Add these items as they are found const searchItems = { @@ -335,7 +337,7 @@ class FileTransferClient { board_name: /; (.*?) with/, mcu_name: /with (.*?)\r?\n/, board_id: /Board ID:(.*?)\r?\n/, - uid: /UID:([0-9A-F]{12})\r?\n/, + uid: /UID:([0-9A-F]{12,16})\r?\n/, } for (const [key, regex] of Object.entries(searchItems)) { diff --git a/js/common/repl-file-transfer.js b/js/common/repl-file-transfer.js index c76f9f1..97ae832 100644 --- a/js/common/repl-file-transfer.js +++ b/js/common/repl-file-transfer.js @@ -91,8 +91,10 @@ class FileTransferClient { let bootout = await this.readFile('/boot_out.txt', false); console.log(bootout); if (!bootout) { + console.error("Unable to read boot_out.txt"); return null; } + bootout += "\n"; // Add these items as they are found const searchItems = { @@ -101,7 +103,7 @@ class FileTransferClient { board_name: /; (.*?) with/, mcu_name: /with (.*?)\r?\n/, board_id: /Board ID:(.*?)\r?\n/, - uid: /UID:([0-9A-F]{12})\r?\n/, + uid: /UID:([0-9A-F]{12,16})\r?\n/, } for (const [key, regex] of Object.entries(searchItems)) { diff --git a/js/workflows/ble.js b/js/workflows/ble.js index 2d2e201..fc8fde2 100644 --- a/js/workflows/ble.js +++ b/js/workflows/ble.js @@ -2,11 +2,10 @@ * This class will encapsulate all of the workflow functions specific to BLE */ -import {FileTransferClient} from '@adafruit/ble-file-transfer-js'; - +import {FileTransferClient} from '../common/ble-file-transfer.js'; import {CONNTYPE, CONNSTATE} from '../constants.js'; import {Workflow} from './workflow.js'; -import {GenericModal} from '../common/dialogs.js'; +import {GenericModal, DeviceInfoModal} from '../common/dialogs.js'; import {sleep, getUrlParam} from '../common/utilities.js'; const bleNusServiceUUID = 'adaf0001-4369-7263-7569-74507974686e'; @@ -27,6 +26,7 @@ class BLEWorkflow extends Workflow { this.bleDevice = null; this.decoder = new TextDecoder(); this.connectDialog = new GenericModal("ble-connect"); + this.infoDialog = new DeviceInfoModal("device-info"); this.partialWrites = true; this.type = CONNTYPE.Ble; } @@ -270,6 +270,10 @@ class BLEWorkflow extends Workflow { return true; } + async showInfo(documentState) { + return await this.infoDialog.open(this, documentState); + } + // Handle the different button states for various connection steps connectionStep(step) { const buttonStates = [ diff --git a/js/workflows/usb copy.js b/js/workflows/usb copy.js deleted file mode 100644 index f149137..0000000 --- a/js/workflows/usb copy.js +++ /dev/null @@ -1,328 +0,0 @@ -import {CONNTYPE, CONNSTATE} from '../constants.js'; -import {Workflow} from './workflow.js'; -import {GenericModal} from '../common/dialogs.js'; -import {FileTransferClient} from '../common/repl-file-transfer.js'; - -let btnRequestSerialDevice, btnSelectHostFolder, btnUseHostFolder, lblWorkingfolder; - -class USBWorkflow extends Workflow { - constructor() { - super(); - this._serialDevice = null; - this.titleMode = false; - this.reader = null; - this.writer = null; - this.connectDialog = new GenericModal("usb-connect"); - this._fileContents = null; - this.type = CONNTYPE.Usb; - this._partialToken = null; - this._uid = null; - this._readLoopPromise = null; - } - - async init(params) { - await super.init(params); - } - - // This is called when a user clicks the main disconnect button - async disconnectButtonHandler(e) { - await super.disconnectButtonHandler(e); - if (this.connectionStatus()) { - await this.onDisconnected(null, false); - } - } - - async onConnected(e) { - this.connectDialog.close(); - await this.loadEditor(); - super.onConnected(e); - } - - async onDisconnected(e, reconnect = true) { - if (this.reader) { - await this.reader.cancel(); - this.reader = null; - } - if (this.writer) { - await this.writer.releaseLock(); - this.writer = null; - } - - if (this._serialDevice) { - await this._serialDevice.close(); - this._serialDevice = null; - } - - super.onDisconnected(e, reconnect); - } - - async serialTransmit(msg) { - const encoder = new TextEncoder(); - if (this.writer) { - const encMessage = encoder.encode(msg); - await this.writer.ready.catch((err) => { - console.error(`Ready error: ${err}`); - }); - await this.writer.write(encMessage).catch((err) => { - console.error(`Chunk error: ${err}`); - }); - await this.writer.ready; - } - } - - async connect() { - let result; - if (result = await super.connect() instanceof Error) { - return result; - } - - return await this.connectToDevice(); - } - - async connectToDevice() { - return await this.connectToSerial(); - } - - async connectToSerial() { - // There's no way to reference a specific port, so we just hope the user - // only has a single device stored and connected. However, we can check that - // the device on the stored port is currently connected by checking if the - // readable and writable properties are null. - - let allDevices = await navigator.serial.getPorts(); - let connectedDevices = []; - for (let device of allDevices) { - let devInfo = await device.getInfo(); - if (devInfo.readable && devInfo.writable) { - connectedDevices.push(device); - } - } - let device = null; - - if (connectedDevices.length == 1) { - device = connectedDevices[0]; - console.log(await device.getInfo()); - try { - // Attempt to connect to the saved device. If it's not found, this will fail. - await this._switchToDevice(device); - } catch (e) { - // We should probably remove existing devices if it fails here - await device.forget(); - - console.log("Failed to automatically connect to saved device. Prompting user to select a device."); - device = await navigator.serial.requestPort(); - console.log(device); - } - - // TODO: Make it more obvious to user that something happened for smaller screens - // Perhaps providing checkmarks by adding a css class when a step is complete would be helpful - // This would help with other workflows as well - } else { - console.log('Requesting any serial device...'); - device = await navigator.serial.requestPort(); - } - - // If we didn't automatically use a saved device - if (!this._serialDevice) { - console.log('> Requested ', device); - await this._switchToDevice(device); - } - console.log(this._serialDevice); - if (this._serialDevice != null) { - this._connectionStep(2); - return true; - } - - return false; - } - - async showConnect(documentState) { - let p = this.connectDialog.open(); - let modal = this.connectDialog.getModal(); - - btnRequestSerialDevice = modal.querySelector('#requestSerialDevice'); - btnSelectHostFolder = modal.querySelector('#selectHostFolder'); - btnUseHostFolder = modal.querySelector('#useHostFolder'); - lblWorkingfolder = modal.querySelector('#workingFolder'); - - btnRequestSerialDevice.disabled = true; - btnSelectHostFolder.disabled = true; - - btnRequestSerialDevice.addEventListener('click', async (event) => { - try { - await this.connectToSerial(); - } catch (e) { - //console.log(e); - //alert(e.message); - //alert("Unable to connect to device. Make sure it is not already in use."); - // TODO: I think this also occurs if the user cancels the requestPort dialog - } - }); - - btnSelectHostFolder.addEventListener('click', async (event) => { - await this._selectHostFolder(); - }); - - btnUseHostFolder.addEventListener('click', async (event) => { - await this._useHostFolder(); - }); - - if (!(await this.available() instanceof Error)) { - let stepOne; - if (stepOne = modal.querySelector('.step:first-of-type')) { - stepOne.classList.add("hidden"); - } - this._connectionStep(1); - } else { - modal.querySelectorAll('.step:not(:first-of-type)').forEach((stepItem) => { - stepItem.classList.add("hidden"); - }); - this._connectionStep(0); - } - - // TODO: If this is closed before all steps are completed, we should close the serial connection - // probably by calling onDisconnect() - - return await p; - } - - async available() { - if (!('serial' in navigator)) { - return Error("Web Serial is not enabled in this browser"); - } - return true; - } - - // Workflow specific functions - async _selectHostFolder() { - console.log('Initializing File Transfer Client...'); - const fileClient = this.fileHelper.getFileClient(); - const changed = await fileClient.loadDirHandle(false); - if (changed) { - await this._hostFolderChanged(); - } - } - - async _useHostFolder() { - await this.fileHelper.listDir('/'); - this.onConnected(); - } - - async _hostFolderChanged() { - const fileClient = this.fileHelper.getFileClient(); - const folderName = fileClient.getWorkingDirectoryName(); - console.log("New folder name:", folderName); - if (folderName) { - // Set the working folder label - lblWorkingfolder.innerHTML = folderName; - btnUseHostFolder.classList.remove("hidden"); - btnSelectHostFolder.innerHTML = "Select Different Folder"; - btnSelectHostFolder.classList.add("inverted"); - btnSelectHostFolder.classList.remove("first-item"); - } - } - - async _switchToDevice(device) { - device.addEventListener("message", this.onSerialReceive.bind(this)); - - this._serialDevice = device; - console.log("switch to", this._serialDevice); - await this._serialDevice.open({baudRate: 115200}); // TODO: Will fail if something else is already connected or it isn't found. - - // Start the read loop - this._readLoopPromise = this._readSerialLoop().catch( - async function(error) { - await this.onDisconnected(); - }.bind(this) - ); - - if (this._serialDevice.writable) { - this.writer = this._serialDevice.writable.getWriter(); - await this.writer.ready; - } - - await this.showBusy(this._getDeviceUid()); - - this.updateConnected(CONNSTATE.partial); - - // At this point we should see if we should init the file client and check if have a saved dir handle - this.initFileClient(new FileTransferClient(this.connectionStatus.bind(this), this.repl)); - const fileClient = this.fileHelper.getFileClient(); - const result = await fileClient.loadSavedDirHandle(); - if (result) { - console.log("Successfully loaded directory:", fileClient.getWorkingDirectoryName()); - await this._hostFolderChanged(); - } else { - console.log("Failed to load directory"); - } - } - - async _getDeviceUid() { - // TODO: Make this python code more robust for older devices - // For instance what if there is an import error with binascii - // or uid is not set due to older firmware - // or microcontroller is a list - // It might be better to take a minimal python approach and do most of - // the conversion in the javascript code - - console.log("Getting Device UID..."); - let result = await this.repl.runCode( -`import microcontroller -import binascii -binascii.hexlify(microcontroller.cpu.uid).decode('ascii').upper()` - ); - // Strip out whitespace as well as start and end quotes - if (result) { - this._uid = result.trim().slice(1, -1); - console.log("Device UID: " + this._uid); - this.debugLog("Device UID: " + this._uid) - } else { - console.log("Failed to get Device UID, result was", result); - } - } - - async _readSerialLoop() { - console.log("Read Loop Init"); - if (!this._serialDevice) { - return; - } - - const messageEvent = new Event("message"); - const decoder = new TextDecoder(); - - if (this._serialDevice.readable) { - this.reader = this._serialDevice.readable.getReader(); - console.log("Read Loop Started"); - while (true) { - const {value, done} = await this.reader.read(); - if (value) { - messageEvent.data = decoder.decode(value); - this._serialDevice.dispatchEvent(messageEvent); - } - if (done) { - this.reader.releaseLock(); - break; - } - } - } - - console.log("Read Loop Stopped. Closing Serial Port."); - } - - // Handle the different button states for various connection steps - _connectionStep(step) { - const buttonStates = [ - {request: false, select: false}, - {request: true, select: false}, - {request: true, select: true}, - ]; - - if (step < 0) step = 0; - if (step > buttonStates.length - 1) step = buttonStates.length - 1; - - btnRequestSerialDevice.disabled = !buttonStates[step].request; - btnSelectHostFolder.disabled = !buttonStates[step].select; - } -} - -export {USBWorkflow}; diff --git a/js/workflows/usb.js b/js/workflows/usb.js index ba4ed07..f0da219 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -1,6 +1,6 @@ import {CONNTYPE, CONNSTATE} from '../constants.js'; import {Workflow} from './workflow.js'; -import {GenericModal} from '../common/dialogs.js'; +import {GenericModal, DeviceInfoModal} from '../common/dialogs.js'; import {FileOps} from '@adafruit/circuitpython-repl-js'; // Use this to determine which FileTransferClient to load import {FileTransferClient as ReplFileTransferClient} from '../common/repl-file-transfer.js'; import {FileTransferClient as FSAPIFileTransferClient} from '../common/fsapi-file-transfer.js'; @@ -15,6 +15,7 @@ class USBWorkflow extends Workflow { this.reader = null; this.writer = null; this.connectDialog = new GenericModal("usb-connect"); + this.infoDialog = new DeviceInfoModal("device-info"); this._fileContents = null; this.type = CONNTYPE.Usb; this._partialToken = null; @@ -341,6 +342,10 @@ print(binascii.hexlify(microcontroller.cpu.uid).decode('ascii').upper())` console.log("Read Loop Stopped. Closing Serial Port."); } + async showInfo(documentState) { + return await this.infoDialog.open(this, documentState); + } + // Handle the different button states for various connection steps _connectionStep(step) { const buttonStates = [ diff --git a/sass/layout/_layout.scss b/sass/layout/_layout.scss index eb71a34..a5182f3 100644 --- a/sass/layout/_layout.scss +++ b/sass/layout/_layout.scss @@ -401,7 +401,8 @@ } } - &[data-popup-modal="device-discovery"] { + &[data-popup-modal="device-discovery"], + &[data-popup-modal="device-info"] { .device-info { margin-top: 5px; width: 100%; From 352c2aea5700ad464bf42d69c3cf66ac442373f9 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 2 Aug 2024 14:23:11 -0700 Subject: [PATCH 07/42] Use newer npm version --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4e35b0b..5289dae 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 22 cache: 'npm' - run: npm ci - run: npm run build From 204dc708a31546859a94022f8731f928cc338454 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 2 Aug 2024 14:28:09 -0700 Subject: [PATCH 08/42] Update workflow to remove node_modules --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5289dae..2ad8efd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -14,5 +14,6 @@ jobs: with: node-version: 22 cache: 'npm' + - run: rm -rf node_modules - run: npm ci - run: npm run build From fa624ae727067f4450013824b5844acc0cdeaa98 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 2 Aug 2024 14:29:20 -0700 Subject: [PATCH 09/42] Update workflow to remove package-lock.json --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2ad8efd..9f94b1e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,5 +15,6 @@ jobs: node-version: 22 cache: 'npm' - run: rm -rf node_modules + - run: rm package-lock.json - run: npm ci - run: npm run build From e4853b2fa56308cdf60d50bfbb35b859c89410ff Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 2 Aug 2024 14:30:21 -0700 Subject: [PATCH 10/42] More workflow tinkering --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9f94b1e..403b427 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,5 +16,6 @@ jobs: cache: 'npm' - run: rm -rf node_modules - run: rm package-lock.json + - run: npm install - run: npm ci - run: npm run build From e6b66529c27ad86c03501a8b9bdc9b74aedf33e0 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 5 Aug 2024 14:07:44 -0700 Subject: [PATCH 11/42] Make path label more obvious --- js/common/file_dialog.js | 7 +++++-- js/script.js | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index 8bd7a38..c051903 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -42,7 +42,7 @@ const extensionMap = { "mp4": {style: FA_STYLE_REGULAR, icon: "file-video", type: "bin"}, "mpy": {style: FA_STYLE_REGULAR, icon: "file", type: "bin"}, "pdf": {style: FA_STYLE_REGULAR, icon: "file-pdf", type: "bin"}, - "py": {style: FA_STYLE_REGULAR, icon: "file-lines", type: "text"}, + "py": {style: FA_STYLE_REGULAR, icon: "file-code", type: "text"}, "toml": {style: FA_STYLE_REGULAR, icon: "file-lines", type: "text"}, "txt": {style: FA_STYLE_REGULAR, icon: "file-lines", type: "text"}, "wav": {style: FA_STYLE_REGULAR, icon: "file-audio", type: "bin"}, @@ -57,6 +57,9 @@ const FILESIZE_UNITS = ["bytes", "KB", "MB", "GB", "TB"]; const COMPACT_UNITS = ["", "K", "M", "G", "T"]; function getFileExtension(filename) { + if (filename === null) { + return null; + } let extension = filename.split('.').pop(); if (extension !== null) { return String(extension).toLowerCase(); @@ -189,7 +192,7 @@ class FileDialog extends GenericModal { this._currentPath = path; } const currentPathLabel = this._getElement('currentPathLabel'); - currentPathLabel.innerHTML = this._currentPath; + currentPathLabel.innerHTML = ` ` + this._currentPath; if (this._currentPath != "/") { this._addFile({path: "..", isDir: true}, "fa-folder-open"); diff --git a/js/script.js b/js/script.js index d562a46..83ece82 100644 --- a/js/script.js +++ b/js/script.js @@ -5,6 +5,7 @@ import {indentWithTab} from "@codemirror/commands" import { python } from "@codemirror/lang-python"; import { syntaxHighlighting, indentUnit } from "@codemirror/language"; import { classHighlighter } from "@lezer/highlight"; +import { getFileIcon } from "./common/file_dialog.js"; import { Terminal } from '@xterm/xterm'; import { FitAddon } from '@xterm/addon-fit'; @@ -237,7 +238,13 @@ async function checkReadOnly() { /* Update the filename and update the UI */ function setFilename(path) { + // Use the extension_map to figure out the file icon let filename = path; + + // Prepend an icon to the path + const [style, icon] = getFileIcon(path); + filename = ` ` + filename; + if (path === null) { filename = "[New Document]"; btnSave.forEach((b) => b.style.display = 'none'); From bb274fe8d642164b907ddc7298d0f337ed92215b Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 5 Aug 2024 16:09:48 -0700 Subject: [PATCH 12/42] Add rollup package --- package-lock.json | 227 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 230 insertions(+) diff --git a/package-lock.json b/package-lock.json index bc0a494..7904007 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.0", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", + "@rollup/rollup-linux-x64-gnu": "*", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", @@ -24,6 +25,9 @@ "devDependencies": { "sass": "^1.77.8", "vite": "^5.3.5" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.20.0" } }, "node_modules/@adafruit/ble-file-transfer-js": { @@ -166,6 +170,48 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.18.0", "cpu": [ @@ -178,6 +224,173 @@ "darwin" ] }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@types/estree": { "version": "1.0.5", "dev": true, @@ -571,6 +784,20 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/safe-buffer": { "version": "5.1.2", "license": "MIT" diff --git a/package.json b/package.json index 9f75bbe..2431f32 100644 --- a/package.json +++ b/package.json @@ -25,5 +25,8 @@ "focus-trap": "^7.5.4", "idb-keyval": "^6.2.1", "jszip": "^3.10.1" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.20.0" } } From 306abfb30b36c73301410c08e26b376c2c187439 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 5 Aug 2024 16:13:07 -0700 Subject: [PATCH 13/42] Empty-Commit test From 97dea7d723df79dd7bf4518ac35a1997f2a3a588 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 6 Aug 2024 08:23:47 -0700 Subject: [PATCH 14/42] Undo GitHub Actions changes but use node 18 (same as beta) --- .github/workflows/build.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 403b427..7a09c91 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,10 +12,6 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 18 cache: 'npm' - - run: rm -rf node_modules - - run: rm package-lock.json - - run: npm install - - run: npm ci - run: npm run build From 7953aea5d3e400be61bb82640eab1df400116698 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 6 Aug 2024 08:26:02 -0700 Subject: [PATCH 15/42] Add npm ci back in --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7a09c91..5369a87 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -14,4 +14,5 @@ jobs: with: node-version: 18 cache: 'npm' + - run: npm ci - run: npm run build From 853ae24ab934b0fa125de99ec6658388d2b36646 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Sat, 10 Aug 2024 10:19:31 -0700 Subject: [PATCH 16/42] Update repl js library to bug fixed version --- package-lock.json | 5 ++--- package.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7904007..98326fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,9 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.1", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", - "@rollup/rollup-linux-x64-gnu": "*", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", @@ -38,7 +37,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "2.0.1", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#2bc32e7958e1c5bc5885d9af891c9025e8733f08", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#25c8306c0f65a06a61cec729f8f30aec0ce7508c", "license": "MIT" }, "node_modules/@codemirror/autocomplete": { diff --git a/package.json b/package.json index 2431f32..533e70d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.1", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From 6bd11806f86f9c3e34ca7a79f89d457fda09dba0 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 16 Aug 2024 09:47:47 -0700 Subject: [PATCH 17/42] Updated REPL lib version and fix duplicate event listeners on usb --- js/workflows/usb.js | 10 +++++----- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index f0da219..3ff5af3 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -160,15 +160,15 @@ class USBWorkflow extends Workflow { //alert("Unable to connect to device. Make sure it is not already in use."); // TODO: I think this also occurs if the user cancels the requestPort dialog } - }); + }, {once: true}); btnSelectHostFolder.addEventListener('click', async (event) => { await this._selectHostFolder(); - }); + }, {once: true}); btnUseHostFolder.addEventListener('click', async (event) => { await this._useHostFolder(); - }); + }, {once: true}); // Check if WebSerial is available if (!(await this.available() instanceof Error)) { @@ -236,10 +236,10 @@ class USBWorkflow extends Workflow { // Workflow specific Functions async _switchToDevice(device) { - device.addEventListener("message", this.onSerialReceive.bind(this)); + device.addEventListener("message", this.onSerialReceive.bind(this), {once: true}); device.addEventListener("disconnect", async (e) => { await this.onDisconnected(e, false); - }); + }, {once: true}); this._serialDevice = device; console.log("switch to", this._serialDevice); diff --git a/package-lock.json b/package-lock.json index 98326fb..6f7830f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.1", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.0", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", @@ -37,7 +37,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "2.0.1", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#25c8306c0f65a06a61cec729f8f30aec0ce7508c", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#f8e9b6fd013312e10ac7379775e253bafc1f71b5", "license": "MIT" }, "node_modules/@codemirror/autocomplete": { diff --git a/package.json b/package.json index 533e70d..ccf0d95 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.1.1", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.0", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From 350e99dd29d538cb057f698fce443b06be7f847b Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 26 Aug 2024 14:15:36 -0700 Subject: [PATCH 18/42] Bug fixes for ESP32 C6 --- js/workflows/usb.js | 27 ++++---- package-lock.json | 161 +++++++++++++++++++++----------------------- package.json | 4 +- 3 files changed, 93 insertions(+), 99 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 3ff5af3..e150a76 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -150,8 +150,7 @@ class USBWorkflow extends Workflow { btnRequestSerialDevice.disabled = true; btnSelectHostFolder.disabled = true; - - btnRequestSerialDevice.addEventListener('click', async (event) => { + let serialConnect = async (event) => { try { await this.connectToSerial(); } catch (e) { @@ -160,15 +159,15 @@ class USBWorkflow extends Workflow { //alert("Unable to connect to device. Make sure it is not already in use."); // TODO: I think this also occurs if the user cancels the requestPort dialog } - }, {once: true}); + }; + btnRequestSerialDevice.removeEventListener('click', serialConnect); + btnRequestSerialDevice.addEventListener('click', serialConnect); - btnSelectHostFolder.addEventListener('click', async (event) => { - await this._selectHostFolder(); - }, {once: true}); + btnSelectHostFolder.removeEventListener('click', this._selectHostFolder); + btnSelectHostFolder.addEventListener('click', this._selectHostFolder); - btnUseHostFolder.addEventListener('click', async (event) => { - await this._useHostFolder(); - }, {once: true}); + btnUseHostFolder.removeEventListener('click', this._useHostFolder); + btnUseHostFolder.addEventListener('click', this._useHostFolder); // Check if WebSerial is available if (!(await this.available() instanceof Error)) { @@ -236,10 +235,14 @@ class USBWorkflow extends Workflow { // Workflow specific Functions async _switchToDevice(device) { - device.addEventListener("message", this.onSerialReceive.bind(this), {once: true}); - device.addEventListener("disconnect", async (e) => { + device.removeEventListener("message", this.onSerialReceive.bind(this)); + device.addEventListener("message", this.onSerialReceive.bind(this)); + + let onDisconnect = async (e) => { await this.onDisconnected(e, false); - }, {once: true}); + }; + device.removeEventListener("disconnect", onDisconnect); + device.addEventListener("disconnect", onDisconnect); this._serialDevice = device; console.log("switch to", this._serialDevice); diff --git a/package-lock.json b/package-lock.json index da78ef9..7397f0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.1", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", @@ -23,7 +23,7 @@ }, "devDependencies": { "sass": "^1.77.8", - "vite": "^5.4.1" + "vite": "^5.4.2" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.20.0" @@ -37,8 +37,7 @@ }, "node_modules/@adafruit/circuitpython-repl-js": { "version": "2.0.1", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#25c8306c0f65a06a61cec729f8f30aec0ce7508c", - "integrity": "sha512-ioKZpoZy5jfePlbSOYxDSulgnbUFiyQrJkTePmzBFQRYwYVUYw/dcltVs9RMGnVaIqSEoSEkilSFi9FcDAhQcQ==", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#2defa7c9489c2c84e471e861509027d5418b53ed", "license": "MIT" }, "node_modules/@codemirror/autocomplete": { @@ -171,9 +170,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", "cpu": [ "arm" ], @@ -185,9 +184,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", "cpu": [ "arm64" ], @@ -199,9 +198,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", "cpu": [ "arm64" ], @@ -213,7 +212,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", "cpu": [ "x64" ], @@ -225,9 +226,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", "cpu": [ "arm" ], @@ -239,9 +240,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", "cpu": [ "arm" ], @@ -253,9 +254,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", "cpu": [ "arm64" ], @@ -267,9 +268,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", "cpu": [ "arm64" ], @@ -281,9 +282,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", "cpu": [ "ppc64" ], @@ -295,9 +296,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", "cpu": [ "riscv64" ], @@ -309,9 +310,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", "cpu": [ "s390x" ], @@ -323,9 +324,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.20.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", - "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", "cpu": [ "x64" ], @@ -336,9 +337,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", "cpu": [ "x64" ], @@ -350,9 +351,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", "cpu": [ "arm64" ], @@ -364,9 +365,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", "cpu": [ "ia32" ], @@ -378,9 +379,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", "cpu": [ "x64" ], @@ -393,6 +394,8 @@ }, "node_modules/@types/estree": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true, "license": "MIT" }, @@ -753,7 +756,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", "dev": true, "license": "MIT", "dependencies": { @@ -767,39 +772,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.21.1", + "@rollup/rollup-android-arm64": "4.21.1", + "@rollup/rollup-darwin-arm64": "4.21.1", + "@rollup/rollup-darwin-x64": "4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "4.21.1", + "@rollup/rollup-linux-arm64-gnu": "4.21.1", + "@rollup/rollup-linux-arm64-musl": "4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "4.21.1", + "@rollup/rollup-linux-s390x-gnu": "4.21.1", + "@rollup/rollup-linux-x64-gnu": "4.21.1", + "@rollup/rollup-linux-x64-musl": "4.21.1", + "@rollup/rollup-win32-arm64-msvc": "4.21.1", + "@rollup/rollup-win32-ia32-msvc": "4.21.1", + "@rollup/rollup-win32-x64-msvc": "4.21.1", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/safe-buffer": { "version": "5.1.2", "license": "MIT" @@ -863,15 +854,15 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz", - "integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.41", - "rollup": "^4.13.0" + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index c42aa49..c5f7ab4 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ }, "devDependencies": { "sass": "^1.77.8", - "vite": "^5.4.1" + "vite": "^5.4.2" }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.0", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.1", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From f0da56120002eed60d2ef81af67543a55f78f9d9 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 15:10:51 -0500 Subject: [PATCH 19/42] save callback on this, and await folder functions --- js/workflows/usb.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index e150a76..1c1b1ec 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -163,11 +163,18 @@ class USBWorkflow extends Workflow { btnRequestSerialDevice.removeEventListener('click', serialConnect); btnRequestSerialDevice.addEventListener('click', serialConnect); - btnSelectHostFolder.removeEventListener('click', this._selectHostFolder); - btnSelectHostFolder.addEventListener('click', this._selectHostFolder); + btnSelectHostFolder.removeEventListener('click', this.btnSelectHostFolderCallback) + this.btnSelectHostFolderCallback = async (event) => { + await this._selectHostFolder(); + }; + btnSelectHostFolder.addEventListener('click', this.btnSelectHostFolderCallback); + - btnUseHostFolder.removeEventListener('click', this._useHostFolder); - btnUseHostFolder.addEventListener('click', this._useHostFolder); + btnUseHostFolder.removeEventListener('click', this.btnUseHostFolderCallback); + this.btnUseHostFolderCallback = async (event) => { + await this._useHostFolder(); + } + btnUseHostFolder.addEventListener('click', this.btnUseHostFolderCallback); // Check if WebSerial is available if (!(await this.available() instanceof Error)) { From ae8276e73e945fdc04589050d104c66990f713e8 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 15:33:07 -0500 Subject: [PATCH 20/42] save messageCallback on this, and remove listener on it --- js/workflows/usb.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index e150a76..d532b83 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -235,8 +235,10 @@ class USBWorkflow extends Workflow { // Workflow specific Functions async _switchToDevice(device) { - device.removeEventListener("message", this.onSerialReceive.bind(this)); - device.addEventListener("message", this.onSerialReceive.bind(this)); + + device.removeEventListener("message", this.messageCallback); + this.messageCallback = this.onSerialReceive.bind(this); + device.addEventListener("message", this.messageCallback); let onDisconnect = async (e) => { await this.onDisconnected(e, false); From 8b95e137c75dccd2a17f0c6193a431fb87499214 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 16:33:47 -0500 Subject: [PATCH 21/42] make private field Co-authored-by: Melissa LeBlanc-Williams --- js/workflows/usb.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index d532b83..5d7c113 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -237,8 +237,8 @@ class USBWorkflow extends Workflow { async _switchToDevice(device) { device.removeEventListener("message", this.messageCallback); - this.messageCallback = this.onSerialReceive.bind(this); - device.addEventListener("message", this.messageCallback); + this._messageCallback = this.onSerialReceive.bind(this); + device.addEventListener("message", this._messageCallback); let onDisconnect = async (e) => { await this.onDisconnected(e, false); From f83bb2a56cfb0d296ba90dafa36e2067196924fa Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 16:34:52 -0500 Subject: [PATCH 22/42] add _messageCallback to constructor --- js/workflows/usb.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 5d7c113..8aac72f 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -21,6 +21,7 @@ class USBWorkflow extends Workflow { this._partialToken = null; this._uid = null; this._readLoopPromise = null; + this._messageCallback = null; } async init(params) { From dc21368c2ce3db2a6435084bdfa04fc8e72219c9 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 16:38:11 -0500 Subject: [PATCH 23/42] private field for remove too --- js/workflows/usb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 8aac72f..ff18846 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -237,7 +237,7 @@ class USBWorkflow extends Workflow { // Workflow specific Functions async _switchToDevice(device) { - device.removeEventListener("message", this.messageCallback); + device.removeEventListener("message", this._messageCallback); this._messageCallback = this.onSerialReceive.bind(this); device.addEventListener("message", this._messageCallback); From 66f50e5ca2c8e30e12378bbb0d7231bb9b48c6fe Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 28 Aug 2024 16:41:29 -0500 Subject: [PATCH 24/42] make fields private and add to constructor --- js/workflows/usb.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 1c1b1ec..de14ae4 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -21,6 +21,9 @@ class USBWorkflow extends Workflow { this._partialToken = null; this._uid = null; this._readLoopPromise = null; + this._btnSelectHostFolderCallback = null; + this._btnUseHostFolderCallback = null; + } async init(params) { @@ -163,18 +166,18 @@ class USBWorkflow extends Workflow { btnRequestSerialDevice.removeEventListener('click', serialConnect); btnRequestSerialDevice.addEventListener('click', serialConnect); - btnSelectHostFolder.removeEventListener('click', this.btnSelectHostFolderCallback) - this.btnSelectHostFolderCallback = async (event) => { + btnSelectHostFolder.removeEventListener('click', this._btnSelectHostFolderCallback) + this._btnSelectHostFolderCallback = async (event) => { await this._selectHostFolder(); }; - btnSelectHostFolder.addEventListener('click', this.btnSelectHostFolderCallback); + btnSelectHostFolder.addEventListener('click', this._btnSelectHostFolderCallback); - btnUseHostFolder.removeEventListener('click', this.btnUseHostFolderCallback); - this.btnUseHostFolderCallback = async (event) => { + btnUseHostFolder.removeEventListener('click', this._btnUseHostFolderCallback); + this._btnUseHostFolderCallback = async (event) => { await this._useHostFolder(); } - btnUseHostFolder.addEventListener('click', this.btnUseHostFolderCallback); + btnUseHostFolder.addEventListener('click', this._btnUseHostFolderCallback); // Check if WebSerial is available if (!(await this.available() instanceof Error)) { From 17754a049fd4aacd189862c2f7f8c452f5c86f19 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 11 Sep 2024 10:36:53 -0500 Subject: [PATCH 25/42] serial plotter implementation --- index.html | 13 +++ js/common/plotter.js | 193 +++++++++++++++++++++++++++++++++++++++ js/script.js | 22 +++++ js/workflows/workflow.js | 8 ++ package.json | 1 + sass/layout/_layout.scss | 15 +++ 6 files changed, 252 insertions(+) create mode 100644 js/common/plotter.js diff --git a/index.html b/index.html index a8f30c4..e7f3efa 100644 --- a/index.html +++ b/index.html @@ -97,8 +97,21 @@
+
+
diff --git a/js/common/plotter.js b/js/common/plotter.js new file mode 100644 index 0000000..c224b05 --- /dev/null +++ b/js/common/plotter.js @@ -0,0 +1,193 @@ +import Chart from "chart.js/auto"; + +let textLineBuffer = ""; +let textLine; + +let defaultColors = ['#8888ff', '#ff8888', '#88ff88']; + +/** + * @name LineBreakTransformer + * Helper to parse the incoming string messages into lines. + */ +class LineBreakTransformer { + constructor() { + // A container for holding stream data until a new line. + this.container = ''; + } + + transform(chunk, linesList) { + this.container += chunk; + const lines = this.container.split('\n'); + this.container = lines.pop(); + lines.forEach(line => linesList.push(line)); + } + +} + +let lineTransformer = new LineBreakTransformer() + +export function plotValues(chartObj, serialMessage, bufferSize) { + /* + Given a string serialMessage, parse it into the plottable value(s) that + it contains if any, and plot those values onto the given chartObj. If + the serialMessage doesn't represent a complete textLine it will be stored + into a buffer and combined with subsequent serialMessages until a full + textLine is formed. + */ + let currentLines = [] + lineTransformer.transform(serialMessage, currentLines) + + for (textLine of currentLines) { + + textLine = textLine.replace("\r", "").replace("\n", "") + if (textLine.length === 0) { + continue; + } + + let valuesToPlot; + + // handle possible tuple in textLine + if (textLine.startsWith("(") && textLine.endsWith(")")) { + textLine = "[" + textLine.substring(1, textLine.length - 1) + "]"; + console.log("after tuple conversion: " + textLine); + } + + // handle possible list in textLine + if (textLine.startsWith("[") && textLine.endsWith("]")) { + valuesToPlot = JSON.parse(textLine); + for (let i = 0; i < valuesToPlot.length; i++) { + valuesToPlot[i] = parseFloat(valuesToPlot[i]) + } + + } else { // handle possible CSV in textLine + valuesToPlot = textLine.split(",") + for (let i = 0; i < valuesToPlot.length; i++) { + valuesToPlot[i] = parseFloat(valuesToPlot[i]) + } + } + + if (valuesToPlot === undefined || valuesToPlot.length === 0) { + continue; + } + + try { + while (chartObj.data.labels.length > bufferSize) { + chartObj.data.labels.shift(); + for (let i = 0; i < chartObj.data.datasets.length; i++) { + while (chartObj.data.datasets[i].data.length > bufferSize) { + chartObj.data.datasets[i].data.shift(); + } + } + } + chartObj.data.labels.push(""); + + for (let i = 0; i < valuesToPlot.length; i++) { + if (isNaN(valuesToPlot[i])) { + continue; + } + if (i > chartObj.data.datasets.length - 1) { + let curColor = '#000000'; + if (i < defaultColors.length) { + curColor = defaultColors[i]; + } + chartObj.data.datasets.push({ + label: i.toString(), + data: [], + borderColor: curColor, + backgroundColor: curColor + }); + } + chartObj.data.datasets[i].data.push(valuesToPlot[i]); + } + + updatePlotterScales(chartObj); + chartObj.update(); + } catch (e) { + console.log("JSON parse error"); + // This line isn't a valid data value + } + } +} + +function updatePlotterScales(chartObj) { + /* + Update the scale of the plotter so that maximum and minimum values are sure + to be shown within the plotter instead of going outside the visible range. + */ + let allData = [] + for (let i = 0; i < chartObj.data.datasets.length; i++) { + allData = allData.concat(chartObj.data.datasets[i].data) + } + chartObj.options.scales.y.min = Math.min(...allData) - 10 + chartObj.options.scales.y.max = Math.max(...allData) + 10 +} + +export async function setupPlotterChart(workflow) { + /* + Initialize the plotter chart and configure it. + */ + let initialData = [] + Chart.defaults.backgroundColor = '#444444'; + Chart.defaults.borderColor = '#000000'; + Chart.defaults.color = '#000000'; + Chart.defaults.aspectRatio = 3/2; + workflow.plotterChart = new Chart( + document.getElementById('plotter-canvas'), + { + type: 'line', + options: { + animation: false, + scales: { + y: { + min: -1, + max: 1, + grid:{ + color: "#666" + }, + border: { + color: "#444" + } + }, + x:{ + grid: { + display: true, + color: "#666" + }, + border: { + color: "#444" + } + } + } + }, + data: { + labels: initialData.map(row => row.timestamp), + datasets: [ + { + label: '0', + data: initialData.map(row => row.value) + } + ] + } + } + ); + + // Set up a listener to respond to user changing the grid choice configuration + // dropdown + workflow.plotterGridLines.addEventListener('change', (event) => { + let gridChoice = event.target.value; + if (gridChoice === "x"){ + workflow.plotterChart.options.scales.x.grid.display = true; + workflow.plotterChart.options.scales.y.grid.display = false; + }else if (gridChoice === "y"){ + workflow.plotterChart.options.scales.y.grid.display = true; + workflow.plotterChart.options.scales.x.grid.display = false; + }else if (gridChoice === "both"){ + workflow.plotterChart.options.scales.y.grid.display = true; + workflow.plotterChart.options.scales.x.grid.display = true; + }else if (gridChoice === "none"){ + workflow.plotterChart.options.scales.y.grid.display = false; + workflow.plotterChart.options.scales.x.grid.display = false; + } + workflow.plotterChart.update(); + }); +} diff --git a/js/script.js b/js/script.js index 83ece82..3f0f1ab 100644 --- a/js/script.js +++ b/js/script.js @@ -20,6 +20,7 @@ import { ButtonValueDialog, MessageModal } from './common/dialogs.js'; import { isLocal, switchUrl, getUrlParam } from './common/utilities.js'; import { CONNTYPE } from './constants.js'; import './layout.js'; // load for side effects only +import {setupPlotterChart} from "./common/plotter.js"; import { mainContent, showSerial } from './layout.js'; // Instantiate workflows @@ -33,6 +34,7 @@ let unchanged = 0; let connectionPromise = null; const btnRestart = document.querySelector('.btn-restart'); +const btnPlotter = document.querySelector('.btn-plotter'); const btnClear = document.querySelector('.btn-clear'); const btnConnect = document.querySelectorAll('.btn-connect'); const btnNew = document.querySelectorAll('.btn-new'); @@ -42,6 +44,7 @@ const btnSaveAs = document.querySelectorAll('.btn-save-as'); const btnSaveRun = document.querySelectorAll('.btn-save-run'); const btnInfo = document.querySelector('.btn-info'); const terminalTitle = document.getElementById('terminal-title'); +const serialPlotter = document.getElementById('plotter'); const messageDialog = new MessageModal("message"); const connectionType = new ButtonValueDialog("connection-type"); @@ -130,9 +133,28 @@ btnRestart.addEventListener('click', async function(e) { // Clear Button btnClear.addEventListener('click', async function(e) { + if (workflow.plotterChart){ + workflow.plotterChart.data.datasets.forEach((dataSet, index) => { + workflow.plotterChart.data.datasets[index].data = []; + }); + workflow.plotterChart.data.labels = []; + workflow.plotterChart.options.scales.y.min = -1; + workflow.plotterChart.options.scales.y.max = 1; + workflow.plotterChart.update(); + } state.terminal.clear(); }); +// Plotter Button +btnPlotter.addEventListener('click', async function(e){ + serialPlotter.classList.toggle("hidden"); + if (!workflow.plotterEnabled){ + await setupPlotterChart(workflow); + workflow.plotterEnabled = true; + } + state.fitter.fit(); +}); + btnInfo.addEventListener('click', async function(e) { if (await checkConnected()) { await workflow.showInfo(getDocState()); diff --git a/js/workflows/workflow.js b/js/workflows/workflow.js index 42f5491..0889768 100644 --- a/js/workflows/workflow.js +++ b/js/workflows/workflow.js @@ -4,6 +4,7 @@ import {FileHelper} from '../common/file.js'; import {UnsavedDialog} from '../common/dialogs.js'; import {FileDialog, FILE_DIALOG_OPEN, FILE_DIALOG_SAVE} from '../common/file_dialog.js'; import {CONNTYPE, CONNSTATE} from '../constants.js'; +import {plotValues} from '../common/plotter.js' /* * This class will encapsulate all of the common workflow-related functions @@ -47,6 +48,8 @@ class Workflow { this._unsavedDialog = new UnsavedDialog("unsaved"); this._fileDialog = new FileDialog("files", this.showBusy.bind(this)); this.repl = new REPL(); + this.plotterEnabled = false; + this.plotterChart = false; } async init(params) { @@ -59,6 +62,8 @@ class Workflow { this._loadFileContents = params.loadFileFunc; this._showMessage = params.showMessageFunc; this.loader = document.getElementById("loader"); + this.plotterBufferSize = document.getElementById('buffer-size'); + this.plotterGridLines = document.getElementById('plot-gridlines-select'); if ("terminalTitle" in params) { this.terminalTitle = params.terminalTitle; } @@ -159,6 +164,9 @@ class Workflow { } writeToTerminal(data) { + if (this.plotterEnabled) { + plotValues(this.plotterChart, data, this.plotterBufferSize.value); + } this.terminal.write(data); } diff --git a/package.json b/package.json index c5f7ab4..1cfa427 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", + "chart.js": "^4.4.4", "codemirror": "^6.0.1", "file-saver": "^2.0.5", "focus-trap": "^7.5.4", diff --git a/sass/layout/_layout.scss b/sass/layout/_layout.scss index a5182f3..e2fa129 100644 --- a/sass/layout/_layout.scss +++ b/sass/layout/_layout.scss @@ -72,6 +72,18 @@ } #serial-page { + #plotter { + flex: 2 1 0; + background: #777; + position: relative; + width: 99%; + overflow: hidden; + padding: 10px 20px; + + &.hidden{ + display: none; + } + } #terminal { flex: 1 1 0%; background: #333; @@ -99,6 +111,9 @@ } } } + #buffer-size{ + width: 70px; + } } #ble-instructions, From 39cc946b5ebc46225106bec0cea428046f9331cf Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 11 Sep 2024 10:48:11 -0500 Subject: [PATCH 26/42] commit after npm install --- package-lock.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7397f0a..f668a9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", + "chart.js": "^4.4.4", "codemirror": "^6.0.1", "file-saver": "^2.0.5", "focus-trap": "^7.5.4", @@ -142,6 +143,11 @@ "node": ">=6" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@lezer/common": { "version": "1.2.1", "license": "MIT" @@ -451,6 +457,17 @@ "node": ">=8" } }, + "node_modules/chart.js": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", + "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "dev": true, From abae501a38c719d258d747ac145e765114766865 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 12 Sep 2024 19:28:55 -0700 Subject: [PATCH 27/42] Last panel settings are now saved --- js/common/utilities.js | 21 ++++++- js/layout.js | 139 +++++++++++++++++++++++++++++------------ js/script.js | 2 +- 3 files changed, 121 insertions(+), 41 deletions(-) diff --git a/js/common/utilities.js b/js/common/utilities.js index e83db44..8935ed1 100644 --- a/js/common/utilities.js +++ b/js/common/utilities.js @@ -123,6 +123,23 @@ function readUploadedFileAsArrayBuffer(inputFile) { }); }; +// Load a setting from local storage with a default value if it doesn't exist +function loadSetting(setting, defaultValue) { + let value = JSON.parse(window.localStorage.getItem(setting)); + console.log(`Loading setting ${setting} with value ${value}`); + if (value == null) { + return defaultValue; + } + + return value; +} + +// Save a setting to local storage +function saveSetting(setting, value) { + console.log(`Saving setting ${setting} with value ${value}`); + window.localStorage.setItem(setting, JSON.stringify(value)); +} + export { isTestHost, buildHash, @@ -136,5 +153,7 @@ export { sleep, switchUrl, switchDevice, - readUploadedFileAsArrayBuffer + readUploadedFileAsArrayBuffer, + loadSetting, + saveSetting }; \ No newline at end of file diff --git a/js/layout.js b/js/layout.js index 4927480..4cdfee7 100644 --- a/js/layout.js +++ b/js/layout.js @@ -1,4 +1,5 @@ import state from './state.js' +import { loadSetting, saveSetting } from './common/utilities.js'; const btnModeEditor = document.getElementById('btn-mode-editor'); const btnModeSerial = document.getElementById('btn-mode-serial'); @@ -8,28 +9,57 @@ const editorPage = document.getElementById('editor-page'); const serialPage = document.getElementById('serial-page'); const pageSeparator = document.getElementById('page-separator'); -btnModeEditor.addEventListener('click', async function (e) { - if (btnModeEditor.classList.contains('active') && !btnModeSerial.classList.contains('active')) { - // this would cause both editor & serial pages to disappear - return; +const SETTING_EDITOR_VISIBLE = "editor-visible"; +const SETTING_TERMINAL_VISIBLE = "terminal-visible"; + +const UPDATE_TYPE_EDITOR = 1; +const UPDATE_TYPE_SERIAL = 2; + +function isEditorVisible() { + return editorPage.classList.contains('active'); +} + +function isSerialVisible() { + return serialPage.classList.contains('active'); +} + +async function toggleEditor() { + if (isSerialVisible()) { + editorPage.classList.toggle('active'); + saveSetting(SETTING_EDITOR_VISIBLE, isEditorVisible()); + updatePageLayout(UPDATE_TYPE_EDITOR); } - btnModeEditor.classList.toggle('active'); - editorPage.classList.toggle('active') - updatePageLayout(true, false); -}); +} -btnModeSerial.addEventListener('click', async function (e) { - if (btnModeSerial.classList.contains('active') && !btnModeEditor.classList.contains('active')) { - // this would cause both editor & serial pages to disappear - return; +async function toggleSerial() { + if (isEditorVisible()) { + serialPage.classList.toggle('active'); + saveSetting(SETTING_TERMINAL_VISIBLE, isSerialVisible()); + updatePageLayout(UPDATE_TYPE_SERIAL); } - btnModeSerial.classList.toggle('active'); - serialPage.classList.toggle('active') - updatePageLayout(false, true); -}); +} + +btnModeEditor.removeEventListener('click', toggleEditor); +btnModeEditor.addEventListener('click', toggleEditor); + +btnModeSerial.removeEventListener('click', toggleSerial); +btnModeSerial.addEventListener('click', toggleSerial); -function updatePageLayout(editor = false, serial = false) { - if (editorPage.classList.contains('active') && serialPage.classList.contains('active')) { +// Show the editor panel if hidden +export function showEditor() { + editorPage.classList.add('active'); + updatePageLayout(UPDATE_TYPE_EDITOR); +} + +// Show the serial panel if hidden +export function showSerial() { + serialPage.classList.add('active'); + updatePageLayout(UPDATE_TYPE_SERIAL); +} + +// update type is used to indicate which button was clicked +function updatePageLayout(updateType) { + if (isEditorVisible() && isSerialVisible()) { pageSeparator.classList.add('active'); } else { pageSeparator.classList.remove('active'); @@ -40,39 +70,41 @@ function updatePageLayout(editor = false, serial = false) { return; } + // Mobile layout, so only show one or the other if (mainContent.offsetWidth < 768) { - if (editor) { - btnModeSerial.classList.remove('active'); + if (updateType == UPDATE_TYPE_EDITOR && isEditorVisible()) { serialPage.classList.remove('active'); - } else if (serial) { - btnModeEditor.classList.remove('active'); + } else if (updateType == UPDATE_TYPE_SERIAL && isSerialVisible()) { editorPage.classList.remove('active'); } + + // Make sure the separator is hidden for mobile pageSeparator.classList.remove('active'); } else { let w = mainContent.offsetWidth; let s = pageSeparator.offsetWidth; editorPage.style.width = ((w - s) / 2) + 'px'; - editorPage.style.flex = '0 0 auto'; + editorPage.style.flex = 'none'; serialPage.style.width = ((w - s) / 2) + 'px'; - serialPage.style.flex = '0 0 auto'; + serialPage.style.flex = 'none'; } - if (serial) { - refitTerminal(); + // Match the button state to the panel state to avoid getting out of sync + if (isEditorVisible()) { + btnModeEditor.classList.add('active'); + } else { + btnModeEditor.classList.remove('active'); } -} -export function showEditor() { - btnModeEditor.classList.add('active'); - editorPage.classList.add('active'); - updatePageLayout(true, false); -} + if (isSerialVisible()) { + btnModeSerial.classList.add('active'); + } else { + btnModeSerial.classList.remove('active'); + } -export function showSerial() { - btnModeSerial.classList.add('active'); - serialPage.classList.add('active'); - updatePageLayout(false, true); + if (isSerialVisible()) { + refitTerminal(); + } } function refitTerminal() { @@ -94,12 +126,12 @@ function refitTerminal() { function fixViewportHeight(e) { let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); - updatePageLayout(); + updatePageLayout(UPDATE_TYPE_EDITOR); } -fixViewportHeight(); -window.addEventListener("resize", fixViewportHeight); +// Resize the panes when the separator is moved function resize(e) { + console.log("Resized"); const w = mainContent.offsetWidth; const gap = pageSeparator.offsetWidth; const ratio = e.clientX / w; @@ -124,6 +156,31 @@ function resize(e) { serialPage.style.width = (w - e.clientX - gap / 2) + 'px'; } +// For the moment, we're going to just use this to keep track of the shown and hidden states +// of the terminal and editor (possibly plotter) +function loadPanelSettings() { + // Load all saved settings or defaults + // Update the terminal first + if (loadSetting(SETTING_EDITOR_VISIBLE, true)) { + editorPage.classList.add('active'); + } else { + editorPage.classList.remove('active'); + } + + if (loadSetting(SETTING_TERMINAL_VISIBLE, false)) { + serialPage.classList.add('active'); + } else { + serialPage.classList.remove('active'); + } + + // Make sure at lest one is visible + if (!isEditorVisible() && !isSerialVisible()) { + editorPage.classList.add('active'); + } + + updatePageLayout(UPDATE_TYPE_SERIAL); +} + function stopResize(e) { window.removeEventListener('mousemove', resize, false); window.removeEventListener('mouseup', stopResize, false); @@ -133,3 +190,7 @@ pageSeparator.addEventListener('mousedown', async function (e) { window.addEventListener('mousemove', resize, false); window.addEventListener('mouseup', stopResize, false); }); + +fixViewportHeight(); +window.addEventListener("resize", fixViewportHeight); +loadPanelSettings(); \ No newline at end of file diff --git a/js/script.js b/js/script.js index 3f0f1ab..4154a8b 100644 --- a/js/script.js +++ b/js/script.js @@ -148,7 +148,7 @@ btnClear.addEventListener('click', async function(e) { // Plotter Button btnPlotter.addEventListener('click', async function(e){ serialPlotter.classList.toggle("hidden"); - if (!workflow.plotterEnabled){ + if (workflow && !workflow.plotterEnabled){ await setupPlotterChart(workflow); workflow.plotterEnabled = true; } From d65328702419e5324d0304856bc9c80b8aa5161a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 22 Jan 2025 09:56:19 -0600 Subject: [PATCH 28/42] remove \r from file contents when read via USB repl --- js/common/repl-file-transfer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/common/repl-file-transfer.js b/js/common/repl-file-transfer.js index 97ae832..a6cf4f6 100644 --- a/js/common/repl-file-transfer.js +++ b/js/common/repl-file-transfer.js @@ -35,7 +35,7 @@ class FileTransferClient { if (contents === null) { return raw ? null : ""; } - return contents; + return contents.replaceAll("\r\n", "\n"); } async writeFile(path, offset, contents, modificationTime, raw = false) { From ce8c2d026227d32c57cdfdfb9ed2adf1a66a4823 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Thu, 23 Jan 2025 12:53:29 -0800 Subject: [PATCH 29/42] Initial resize fixes --- js/layout.js | 9 +++++---- package-lock.json | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/js/layout.js b/js/layout.js index 4cdfee7..3d25a0f 100644 --- a/js/layout.js +++ b/js/layout.js @@ -59,6 +59,7 @@ export function showSerial() { // update type is used to indicate which button was clicked function updatePageLayout(updateType) { + // If both are visible, show the separator if (isEditorVisible() && isSerialVisible()) { pageSeparator.classList.add('active'); } else { @@ -72,6 +73,7 @@ function updatePageLayout(updateType) { // Mobile layout, so only show one or the other if (mainContent.offsetWidth < 768) { + // Prioritize based on the update type if (updateType == UPDATE_TYPE_EDITOR && isEditorVisible()) { serialPage.classList.remove('active'); } else if (updateType == UPDATE_TYPE_SERIAL && isSerialVisible()) { @@ -130,8 +132,7 @@ function fixViewportHeight(e) { } // Resize the panes when the separator is moved -function resize(e) { - console.log("Resized"); +function resizePanels(e) { const w = mainContent.offsetWidth; const gap = pageSeparator.offsetWidth; const ratio = e.clientX / w; @@ -182,12 +183,12 @@ function loadPanelSettings() { } function stopResize(e) { - window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mousemove', resizePanels, false); window.removeEventListener('mouseup', stopResize, false); } pageSeparator.addEventListener('mousedown', async function (e) { - window.addEventListener('mousemove', resize, false); + window.addEventListener('mousemove', resizePanels, false); window.addEventListener('mouseup', stopResize, false); }); diff --git a/package-lock.json b/package-lock.json index 10affc2..38d3aaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,9 @@ "devDependencies": { "sass": "^1.78.0", "vite": "^5.4.3" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.20.0" } }, "node_modules/@adafruit/ble-file-transfer-js": { From 6724e800bfb20206cc763d0104d0807e31d535bd Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 31 Jan 2025 13:11:43 -0800 Subject: [PATCH 30/42] Fix file download issues --- js/common/repl-file-transfer.js | 3 + package-lock.json | 221 +++++++++++++++++++++----------- package.json | 2 +- 3 files changed, 148 insertions(+), 78 deletions(-) diff --git a/js/common/repl-file-transfer.js b/js/common/repl-file-transfer.js index a6cf4f6..7231887 100644 --- a/js/common/repl-file-transfer.js +++ b/js/common/repl-file-transfer.js @@ -35,6 +35,9 @@ class FileTransferClient { if (contents === null) { return raw ? null : ""; } + if (raw) { + return contents; + } return contents.replaceAll("\r\n", "\n"); } diff --git a/package-lock.json b/package-lock.json index 10affc2..880d9de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.1", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.2", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", @@ -25,6 +25,9 @@ "devDependencies": { "sass": "^1.78.0", "vite": "^5.4.3" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "^4.20.0" } }, "node_modules/@adafruit/ble-file-transfer-js": { @@ -173,152 +176,205 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", - "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", + "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", - "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", + "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", - "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", + "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", - "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", + "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", + "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", + "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", - "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", + "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", - "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", + "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", - "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", + "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", - "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", + "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", + "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", - "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", + "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", - "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", + "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", - "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", + "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", - "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", + "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", "cpu": [ "x64" ], @@ -329,62 +385,67 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", - "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", + "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", - "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", + "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", - "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", + "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", - "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", + "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@xterm/addon-fit": { "version": "0.10.0", @@ -652,7 +713,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -755,12 +818,13 @@ } }, "node_modules/rollup": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", - "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", + "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -770,22 +834,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.1", - "@rollup/rollup-android-arm64": "4.21.1", - "@rollup/rollup-darwin-arm64": "4.21.1", - "@rollup/rollup-darwin-x64": "4.21.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", - "@rollup/rollup-linux-arm-musleabihf": "4.21.1", - "@rollup/rollup-linux-arm64-gnu": "4.21.1", - "@rollup/rollup-linux-arm64-musl": "4.21.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", - "@rollup/rollup-linux-riscv64-gnu": "4.21.1", - "@rollup/rollup-linux-s390x-gnu": "4.21.1", - "@rollup/rollup-linux-x64-gnu": "4.21.1", - "@rollup/rollup-linux-x64-musl": "4.21.1", - "@rollup/rollup-win32-arm64-msvc": "4.21.1", - "@rollup/rollup-win32-ia32-msvc": "4.21.1", - "@rollup/rollup-win32-x64-msvc": "4.21.1", + "@rollup/rollup-android-arm-eabi": "4.32.1", + "@rollup/rollup-android-arm64": "4.32.1", + "@rollup/rollup-darwin-arm64": "4.32.1", + "@rollup/rollup-darwin-x64": "4.32.1", + "@rollup/rollup-freebsd-arm64": "4.32.1", + "@rollup/rollup-freebsd-x64": "4.32.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", + "@rollup/rollup-linux-arm-musleabihf": "4.32.1", + "@rollup/rollup-linux-arm64-gnu": "4.32.1", + "@rollup/rollup-linux-arm64-musl": "4.32.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", + "@rollup/rollup-linux-riscv64-gnu": "4.32.1", + "@rollup/rollup-linux-s390x-gnu": "4.32.1", + "@rollup/rollup-linux-x64-gnu": "4.32.1", + "@rollup/rollup-linux-x64-musl": "4.32.1", + "@rollup/rollup-win32-arm64-msvc": "4.32.1", + "@rollup/rollup-win32-ia32-msvc": "4.32.1", + "@rollup/rollup-win32-x64-msvc": "4.32.1", "fsevents": "~2.3.2" } }, @@ -854,9 +921,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", - "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 07ca3b0..b128e51 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.1", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.2", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From e94c613f731f37b02321286dc779fe3b48d2db42 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 5 Feb 2025 13:07:12 -0800 Subject: [PATCH 31/42] Fixed Downloading files in all cases --- js/common/file_dialog.js | 47 ++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/js/common/file_dialog.js b/js/common/file_dialog.js index c051903..3875482 100644 --- a/js/common/file_dialog.js +++ b/js/common/file_dialog.js @@ -548,20 +548,14 @@ class FileDialog extends GenericModal { input.click(); } - // Currently only files are downloadable, but it would be nice to eventually download zipped folders async _handleDownloadButton() { - // TODO: Implement a way to download multiple files at once into a zip file - - await this._download(this._getSelectedFilesInfo()); + await this._showBusy(this._download(this._getSelectedFilesInfo())); } async _download(files) { if (!this._canDownload()) return; - let folder, blob, filename; - - // If we only have 1 item and it is a file, we can download it directly - // Otherwise, we need to zip the files and download the zip keeping the structure intact + let blob, filename; // Function to read the file contents as a blob let getBlob = async (path) => { @@ -576,36 +570,43 @@ class FileDialog extends GenericModal { } }; + let addFileContentsToZip = async (zip, folder, location) => { + let contents = await getBlob(folder + location); + // Get the filename only from the path + zip.file(location, contents); + }; + if (files.length == 1 && files[0].filetype != "folder") { + // Single File Selected filename = files[0].filename; - blob = await this._showBusy(getBlob(this._currentPath + filename)); + blob = await getBlob(this._currentPath + filename); } else { // We either have more than 1 item selected or we have a folder selected or we have no file selected and want to download the current folder // If we have nothing selected, we will download the current folder - folder = this._currentPath; + filename = `${getParentFolderName()}.zip`; if (files.length == 0) { - files.push({filename: getParentFolderName(), filetype: "folder", path: this._currentPath}); - } + // No Files Selected, so get everything in current folder + const filesInFolder = await this._fileHelper.listDir(this._currentPath); - if (files.length == 1) { - filename = files[0].filename; - folder += filename + "/"; - filename = `${filename}.zip`; - } else { - filename = `${getParentFolderName()}.zip`; + // Add all files in current folder to files array + for (let fileObj of filesInFolder) { + if (this._hidePaths.has(this._currentPath + fileObj.path)) continue; + files.push({filename: fileObj.path, filetype: fileObj.isDir ? "folder" : "file", path: this._currentPath}); + } + } else if (files.length == 1) { + // Single Folder Selected + filename = `${files[0].filename}.zip`; } let zip = new JSZip(); for (let item of files) { if (item.filetype == "folder") { - let containedFiles = await this._fileHelper.findContainedFiles(folder + item + "/", true); + let containedFiles = await this._fileHelper.findContainedFiles(item.path + item.filename + "/", true); for (let location of containedFiles) { - let contents = await this._showBusy(getBlob(folder + location)); - zip.file(location, contents); + await addFileContentsToZip(zip, item.path, item.filename + "/" + location); } } else { - let contents = await this._showBusy(getBlob(folder + item.filename)); - zip.file(item.filename, contents); + await addFileContentsToZip(zip, item.path, item.filename); } } blob = await zip.generateAsync({type: "blob"}); diff --git a/package.json b/package.json index b128e51..f5b3237 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.2", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.3", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From 59761cfe6886c62fa946466484ee059b6ddbe90a Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 7 Feb 2025 13:26:02 -0800 Subject: [PATCH 32/42] Show busy while user selects port and handle canceling properly --- js/workflows/usb.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index ab052c6..fec12ff 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -125,7 +125,12 @@ class USBWorkflow extends Workflow { // This would help with other workflows as well } else { console.log('Requesting any serial device...'); - device = await navigator.serial.requestPort(); + try { + device = await navigator.serial.requestPort(); + } catch (e) { + console.log(e); + return false; + } } // If we didn't automatically use a saved device @@ -155,7 +160,7 @@ class USBWorkflow extends Workflow { btnSelectHostFolder.disabled = true; let serialConnect = async (event) => { try { - await this.connectToSerial(); + await this.showBusy(this.connectToSerial()); } catch (e) { //console.log(e); //alert(e.message); From d1024b2895ce8bd393fd973beae54a916c5f74d8 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 12 Feb 2025 11:18:35 -0800 Subject: [PATCH 33/42] Bump circuitpython-repl-js from 3.2.3 to 3.2.4 --- package-lock.json | 12 +++++++++--- package.json | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 880d9de..d2929e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.2", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.4", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", @@ -30,6 +30,12 @@ "@rollup/rollup-linux-x64-gnu": "^4.20.0" } }, + "../../../circuitpython-repl-js/repl.js": { + "extraneous": true + }, + "../../circuitpython-repl-js/repl.js": { + "extraneous": true + }, "node_modules/@adafruit/ble-file-transfer-js": { "name": "@adafruit/ble-file-transfer", "version": "1.0.2", @@ -37,8 +43,8 @@ "license": "MIT" }, "node_modules/@adafruit/circuitpython-repl-js": { - "version": "2.0.1", - "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#2defa7c9489c2c84e471e861509027d5418b53ed", + "version": "3.2.4", + "resolved": "git+ssh://git@github.com/adafruit/circuitpython-repl-js.git#1bd2db05082d245afeb17bb1ffb2d66b7833f1ce", "license": "MIT" }, "node_modules/@codemirror/autocomplete": { diff --git a/package.json b/package.json index f5b3237..11641ee 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", - "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.3", + "@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.2.4", "@codemirror/lang-python": "^6.1.6", "@fortawesome/fontawesome-free": "^6.6.0", "@xterm/addon-fit": "^0.10.0", From f863277202d69dd907bdb11776b6459f435cd871 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 12 Feb 2025 14:22:18 -0800 Subject: [PATCH 34/42] Basic shrink fix --- js/layout.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/js/layout.js b/js/layout.js index 3d25a0f..2933252 100644 --- a/js/layout.js +++ b/js/layout.js @@ -116,7 +116,35 @@ function refitTerminal() { window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { if (state.fitter) { + // We need to get the main viewport height and calculate what the size of the terminal pane should be + + // Get the height of the header, footer, and serial bar to determine the height of the terminal + let siteHeader = document.getElementById('site-header'); + let mobileHeader = document.getElementById('mobile-header'); + let headerHeight = siteHeader.offsetHeight; + if (siteHeader.style.display === 'none') { + headerHeight = mobileHeader.offsetHeight; + } + let foorterBarHeight = document.getElementById('footer-bar').offsetHeight; + let serialBarHeight = document.getElementById('serial-bar').offsetHeight; + let viewportHeight = window.innerHeight; + let terminalHeight = viewportHeight - headerHeight - foorterBarHeight - serialBarHeight; + + // Fit the terminal to the new size (works good for growing) state.fitter.fit(); + + // Fix the terminal screen height if it's too big + let screen = document.querySelector('.xterm-screen'); + if (screen && (terminalHeight < screen.offsetHeight)) { + // xterm-screen is 17px per row and 9px per column + let rows = Math.floor(terminalHeight / 17); + if (rows < 0) { + rows = 0; + } + if (rows < state.fitter.proposeDimensions().rows) { + screen.style.height = (rows * 17) + 'px'; + } + } } }); }); From 80cf762c83b8a56c375d17acc7aa580c2d56a064 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Wed, 12 Feb 2025 14:50:08 -0800 Subject: [PATCH 35/42] Replace fitter with custom solution --- js/layout.js | 58 +++++++++++++++++++++++++++------------------------- js/script.js | 4 ---- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/js/layout.js b/js/layout.js index 2933252..3ee4994 100644 --- a/js/layout.js +++ b/js/layout.js @@ -15,6 +15,9 @@ const SETTING_TERMINAL_VISIBLE = "terminal-visible"; const UPDATE_TYPE_EDITOR = 1; const UPDATE_TYPE_SERIAL = 2; +const MINIMUM_COLS = 2; +const MINIMUM_ROWS = 1; + function isEditorVisible() { return editorPage.classList.contains('active'); } @@ -110,41 +113,40 @@ function updatePageLayout(updateType) { } function refitTerminal() { + // Custom function to replace the terminal refit function as it was a bit buggy + // Re-fitting the terminal requires a full re-layout of the DOM which can be tricky to time right. // see https://www.macarthur.me/posts/when-dom-updates-appear-to-be-asynchronous window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { - if (state.fitter) { - // We need to get the main viewport height and calculate what the size of the terminal pane should be - - // Get the height of the header, footer, and serial bar to determine the height of the terminal - let siteHeader = document.getElementById('site-header'); - let mobileHeader = document.getElementById('mobile-header'); - let headerHeight = siteHeader.offsetHeight; - if (siteHeader.style.display === 'none') { - headerHeight = mobileHeader.offsetHeight; + const TERMINAL_ROW_HEIGHT = state.terminal._core._renderService.dimensions.css.cell.height; + const TERMINAL_COL_WIDTH = state.terminal._core._renderService.dimensions.css.cell.width; + + // Get the height of the header, footer, and serial bar to determine the height of the terminal + let siteHeader = document.getElementById('site-header'); + let mobileHeader = document.getElementById('mobile-header'); + let headerHeight = siteHeader.offsetHeight; + if (siteHeader.style.display === 'none') { + headerHeight = mobileHeader.offsetHeight; + } + let foorterBarHeight = document.getElementById('footer-bar').offsetHeight; + let serialBarHeight = document.getElementById('serial-bar').offsetHeight; + let viewportHeight = window.innerHeight; + let terminalHeight = viewportHeight - headerHeight - foorterBarHeight - serialBarHeight; + let terminalWidth = document.getElementById('serial-page').offsetWidth; + let screen = document.querySelector('.xterm-screen'); + if (screen) { + let cols = Math.floor(terminalWidth / TERMINAL_COL_WIDTH); + let rows = Math.floor(terminalHeight / TERMINAL_ROW_HEIGHT); + if (cols < MINIMUM_COLS) { + cols = MINIMUM_COLS; } - let foorterBarHeight = document.getElementById('footer-bar').offsetHeight; - let serialBarHeight = document.getElementById('serial-bar').offsetHeight; - let viewportHeight = window.innerHeight; - let terminalHeight = viewportHeight - headerHeight - foorterBarHeight - serialBarHeight; - - // Fit the terminal to the new size (works good for growing) - state.fitter.fit(); - - // Fix the terminal screen height if it's too big - let screen = document.querySelector('.xterm-screen'); - if (screen && (terminalHeight < screen.offsetHeight)) { - // xterm-screen is 17px per row and 9px per column - let rows = Math.floor(terminalHeight / 17); - if (rows < 0) { - rows = 0; - } - if (rows < state.fitter.proposeDimensions().rows) { - screen.style.height = (rows * 17) + 'px'; - } + if (rows < MINIMUM_ROWS) { + rows = MINIMUM_ROWS; } + screen.style.width = (cols * TERMINAL_COL_WIDTH) + 'px'; + screen.style.height = (rows * TERMINAL_ROW_HEIGHT) + 'px'; } }); }); diff --git a/js/script.js b/js/script.js index 4154a8b..484059f 100644 --- a/js/script.js +++ b/js/script.js @@ -152,7 +152,6 @@ btnPlotter.addEventListener('click', async function(e){ await setupPlotterChart(workflow); workflow.plotterEnabled = true; } - state.fitter.fit(); }); btnInfo.addEventListener('click', async function(e) { @@ -548,9 +547,6 @@ async function setupXterm() { } }); - state.fitter = new FitAddon(); - state.terminal.loadAddon(state.fitter); - state.terminal.loadAddon(new WebLinksAddon()); state.terminal.open(document.getElementById('terminal')); From bba27c37f930caca9801ee43d17e9240d30f3ee0 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Fri, 14 Feb 2025 11:42:02 -0800 Subject: [PATCH 36/42] Remove unused import --- js/script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/js/script.js b/js/script.js index 484059f..f18886d 100644 --- a/js/script.js +++ b/js/script.js @@ -8,7 +8,6 @@ import { classHighlighter } from "@lezer/highlight"; import { getFileIcon } from "./common/file_dialog.js"; import { Terminal } from '@xterm/xterm'; -import { FitAddon } from '@xterm/addon-fit'; import { WebLinksAddon } from '@xterm/addon-web-links'; import state from './state.js' From 4274fc125ef0969da7aca39e52d83adbd7b1c40a Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 17 Feb 2025 12:54:45 -0800 Subject: [PATCH 37/42] Improve connection step updating and add checkmarks --- images/checkmark.svg | 1 + js/workflows/ble.js | 30 +++++++++++++----------------- js/workflows/usb.js | 32 ++++++++++++++------------------ js/workflows/web.js | 2 ++ js/workflows/workflow.js | 29 +++++++++++++++++++++++++++++ sass/layout/_grid.scss | 11 +++++++++++ 6 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 images/checkmark.svg diff --git a/images/checkmark.svg b/images/checkmark.svg new file mode 100644 index 0000000..c560639 --- /dev/null +++ b/images/checkmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/js/workflows/ble.js b/js/workflows/ble.js index fc8fde2..5cecbda 100644 --- a/js/workflows/ble.js +++ b/js/workflows/ble.js @@ -29,6 +29,12 @@ class BLEWorkflow extends Workflow { this.infoDialog = new DeviceInfoModal("device-info"); this.partialWrites = true; this.type = CONNTYPE.Ble; + this.buttonStates = [ + {reconnect: false, request: false, bond: false}, + {reconnect: false, request: true, bond: false}, + {reconnect: true, request: true, bond: false}, + {reconnect: false, request: false, bond: true}, + ]; } // This is called when a user clicks the main disconnect button @@ -50,6 +56,13 @@ class BLEWorkflow extends Workflow { btnBond = modal.querySelector('#promptBond'); btnReconnect = modal.querySelector('#bleReconnect'); + // Map the button states to the buttons + this.connectButtons = { + reconnect: btnReconnect, + request: btnRequestBluetoothDevice, + bond: btnBond + }; + btnRequestBluetoothDevice.addEventListener('click', async (event) => { await this.onRequestBluetoothDeviceButtonClick(event); }); @@ -273,23 +286,6 @@ class BLEWorkflow extends Workflow { async showInfo(documentState) { return await this.infoDialog.open(this, documentState); } - - // Handle the different button states for various connection steps - connectionStep(step) { - const buttonStates = [ - {reconnect: false, request: false, bond: false}, - {reconnect: false, request: true, bond: false}, - {reconnect: true, request: true, bond: false}, - {reconnect: false, request: false, bond: true}, - ]; - - if (step < 0) step = 0; - if (step > buttonStates.length - 1) step = buttonStates.length - 1; - - btnReconnect.disabled = !buttonStates[step].reconnect; - btnRequestBluetoothDevice.disabled = !buttonStates[step].request; - btnBond.disabled = !buttonStates[step].bond; - } } export {BLEWorkflow}; diff --git a/js/workflows/usb.js b/js/workflows/usb.js index fec12ff..82a0408 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -24,6 +24,11 @@ class USBWorkflow extends Workflow { this._messageCallback = null; this._btnSelectHostFolderCallback = null; this._btnUseHostFolderCallback = null; + this.buttonStates = [ + {request: false, select: false}, + {request: true, select: false}, + {request: true, select: true}, + ]; } async init(params) { @@ -140,7 +145,7 @@ class USBWorkflow extends Workflow { } console.log(this._serialDevice); if (this._serialDevice != null) { - this._connectionStep(2); + this.connectionStep(2); return true; } @@ -156,6 +161,12 @@ class USBWorkflow extends Workflow { btnUseHostFolder = modal.querySelector('#useHostFolder'); lblWorkingfolder = modal.querySelector('#workingFolder'); + // Map the button states to the buttons + this.connectButtons = { + request: btnRequestSerialDevice, + select: btnSelectHostFolder, + }; + btnRequestSerialDevice.disabled = true; btnSelectHostFolder.disabled = true; let serialConnect = async (event) => { @@ -191,13 +202,13 @@ class USBWorkflow extends Workflow { if (stepOne = modal.querySelector('.step:first-of-type')) { stepOne.classList.add("hidden"); } - this._connectionStep(1); + this.connectionStep(1); } else { // If not, hide all steps beyond the message modal.querySelectorAll('.step:not(:first-of-type)').forEach((stepItem) => { stepItem.classList.add("hidden"); }); - this._connectionStep(0); + this.connectionStep(0); } // Hide the last step until we determine that we need it @@ -365,21 +376,6 @@ print(binascii.hexlify(microcontroller.cpu.uid).decode('ascii').upper())` async showInfo(documentState) { return await this.infoDialog.open(this, documentState); } - - // Handle the different button states for various connection steps - _connectionStep(step) { - const buttonStates = [ - {request: false, select: false}, - {request: true, select: false}, - {request: true, select: true}, - ]; - - if (step < 0) step = 0; - if (step > buttonStates.length - 1) step = buttonStates.length - 1; - - btnRequestSerialDevice.disabled = !buttonStates[step].request; - btnSelectHostFolder.disabled = !buttonStates[step].select; - } } export {USBWorkflow}; diff --git a/js/workflows/web.js b/js/workflows/web.js index b78ac28..5c26438 100644 --- a/js/workflows/web.js +++ b/js/workflows/web.js @@ -23,6 +23,8 @@ class WebWorkflow extends Workflow { this.deviceDiscoveryDialog = new DiscoveryModal("device-discovery"); this.connIntervalId = null; this.type = CONNTYPE.Web; + this.buttonStates = []; + this.buttons = {}; } // This is called when a user clicks the main disconnect button diff --git a/js/workflows/workflow.js b/js/workflows/workflow.js index 0889768..661acb0 100644 --- a/js/workflows/workflow.js +++ b/js/workflows/workflow.js @@ -50,6 +50,8 @@ class Workflow { this.repl = new REPL(); this.plotterEnabled = false; this.plotterChart = false; + this.buttonStates = []; + this.connectButtons = {}; } async init(params) { @@ -308,6 +310,33 @@ class Workflow { async available() { return Error("This work flow is not available."); } + + // Handle the different button states for various connection steps + connectionStep(step) { + if (step < 0) step = 0; + if (step > this.buttonStates.length - 1) step = this.buttonStates.length - 1; + + for (let button in this.connectButtons) { + this.connectButtons[button].disabled = !this.buttonStates[step][button]; + } + + // Mark all previous steps as completed (hidden or not) + for (let stepNumber = 0; stepNumber < step; stepNumber++) { + this._markStepCompleted(stepNumber); + } + } + + _markStepCompleted(stepNumber) { + let modal = this.connectDialog.getModal(); + let steps = modal.querySelectorAll('.step'); + // For any steps prior to the last step, add a checkmark + for (let i = 0; i < steps.length - 1; i++) { + let step = steps[stepNumber]; + if (!step.classList.contains('completed')) { + step.classList.add('completed'); + } + } + } } export { diff --git a/sass/layout/_grid.scss b/sass/layout/_grid.scss index fc2e3f2..36c9221 100644 --- a/sass/layout/_grid.scss +++ b/sass/layout/_grid.scss @@ -55,6 +55,17 @@ &.hidden { display: none; } + + &.completed .step-number::after { + content: ""; + background: url('/images/checkmark.svg'); + position: relative; + display: block; + top: 20px; + width: 50px; + height: 50px; + filter: drop-shadow(2px 2px 2px #888); + } } } From c08895f3c7db5ca1df161a3cfbf405c9c8d9d18c Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Tue, 18 Feb 2025 15:54:12 -0800 Subject: [PATCH 38/42] Fix spelling error in variable name --- js/layout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/layout.js b/js/layout.js index 3ee4994..216899a 100644 --- a/js/layout.js +++ b/js/layout.js @@ -130,10 +130,10 @@ function refitTerminal() { if (siteHeader.style.display === 'none') { headerHeight = mobileHeader.offsetHeight; } - let foorterBarHeight = document.getElementById('footer-bar').offsetHeight; + let footerBarHeight = document.getElementById('footer-bar').offsetHeight; let serialBarHeight = document.getElementById('serial-bar').offsetHeight; let viewportHeight = window.innerHeight; - let terminalHeight = viewportHeight - headerHeight - foorterBarHeight - serialBarHeight; + let terminalHeight = viewportHeight - headerHeight - footerBarHeight - serialBarHeight; let terminalWidth = document.getElementById('serial-page').offsetWidth; let screen = document.querySelector('.xterm-screen'); if (screen) { From c5ca119e6cdc80b33f6d3c193719eab57a5329bd Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Sun, 2 Mar 2025 12:15:53 -0800 Subject: [PATCH 39/42] Improvements to BLE connectivity --- index.html | 4 +- js/workflows/ble.js | 100 ++++---- package-lock.json | 553 +++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- vite.config.js | 4 +- 5 files changed, 618 insertions(+), 46 deletions(-) diff --git a/index.html b/index.html index e7f3efa..4f10992 100644 --- a/index.html +++ b/index.html @@ -212,7 +212,9 @@

Web Bluetooth not available!

flag. However be careful as it would be risky to browse the web with this flag turned on as it enables many other experimental web platform features. Starting with Chromium version 100, enable the about://flags/#enable-web-bluetooth - safer flag instead.

+ safer flag instead. You can also enable Web Bluetooth Binding by enabling the + about://flags/#enable-web-bluetooth-new-permissions-backend + flag instead of the experimental features if it is available.

diff --git a/js/workflows/ble.js b/js/workflows/ble.js index fc8fde2..61b03e3 100644 --- a/js/workflows/ble.js +++ b/js/workflows/ble.js @@ -3,10 +3,10 @@ */ import {FileTransferClient} from '../common/ble-file-transfer.js'; -import {CONNTYPE, CONNSTATE} from '../constants.js'; +import {CONNTYPE} from '../constants.js'; import {Workflow} from './workflow.js'; import {GenericModal, DeviceInfoModal} from '../common/dialogs.js'; -import {sleep, getUrlParam} from '../common/utilities.js'; +import {sleep} from '../common/utilities.js'; const bleNusServiceUUID = 'adaf0001-4369-7263-7569-74507974686e'; const bleNusCharRXUUID = 'adaf0002-4369-7263-7569-74507974686e'; @@ -50,23 +50,23 @@ class BLEWorkflow extends Workflow { btnBond = modal.querySelector('#promptBond'); btnReconnect = modal.querySelector('#bleReconnect'); - btnRequestBluetoothDevice.addEventListener('click', async (event) => { - await this.onRequestBluetoothDeviceButtonClick(event); - }); - btnBond.addEventListener('click', async (event) => { - await this.onBond(event); - }); - btnReconnect.addEventListener('click', async (event) => { - await this.reconnectButtonHandler(event); - }); + btnRequestBluetoothDevice.addEventListener('click', this.onRequestBluetoothDeviceButtonClick.bind(this)); + //btnBond.addEventListener('click', this.onBond.bind(this)); + btnReconnect.addEventListener('click', this.reconnectButtonHandler.bind(this)); + // Check if Web Bluetooth is available if (!(await this.available() instanceof Error)) { let stepOne; if (stepOne = modal.querySelector('.step:first-of-type')) { stepOne.classList.add("hidden"); } - const devices = await navigator.bluetooth.getDevices(); - this.connectionStep(devices.length > 0 ? 2 : 1); + try { + const devices = await navigator.bluetooth.getDevices(); + console.log(devices); + this.connectionStep(devices.length > 0 ? 2 : 1); + } catch (e) { + console.log("New Permissions backend for Web Bluetooth not enabled. Go to chrome://flags/#enable-web-bluetooth-new-permissions-backend to enable.", e); + } } else { modal.querySelectorAll('.step:not(:first-of-type)').forEach((stepItem) => { stepItem.classList.add("hidden"); @@ -79,7 +79,9 @@ class BLEWorkflow extends Workflow { async onSerialReceive(e) {; // TODO: Make use of super.onSerialReceive() so that title can be extracted - this.writeToTerminal(this.decoder.decode(e.target.value.buffer, {stream: true})); + let output = this.decoder.decode(e.target.value.buffer, {stream: true}); + console.log(output); + this.writeToTerminal(output); } async connectToSerial() { @@ -89,6 +91,8 @@ class BLEWorkflow extends Workflow { this.txCharacteristic = await this.serialService.getCharacteristic(bleNusCharTXUUID); this.rxCharacteristic = await this.serialService.getCharacteristic(bleNusCharRXUUID); + // Remove any existing event listeners to prevent multiple reads + this.txCharacteristic.removeEventListener('characteristicvaluechanged', this.onSerialReceive.bind(this)); this.txCharacteristic.addEventListener('characteristicvaluechanged', this.onSerialReceive.bind(this)); await this.txCharacteristic.startNotifications(); return true; @@ -105,7 +109,7 @@ class BLEWorkflow extends Workflow { console.log('Getting existing permitted Bluetooth devices...'); const devices = await navigator.bluetooth.getDevices(); - console.log('> Got ' + devices.length + ' Bluetooth devices.'); + console.log('> Found ' + devices.length + ' Bluetooth device(s).'); // These devices may not be powered on or in range, so scan for // advertisement packets from them before connecting. for (const device of devices) { @@ -113,34 +117,43 @@ class BLEWorkflow extends Workflow { } } catch (error) { - console.log('Argh! ' + error); + console.error(error); + await this._showMessage(error); } } } + // Bring up a dialog to request a device + async requestDevice() { + return navigator.bluetooth.requestDevice({ + filters: [{services: [0xfebb]},], // <- Prefer filters to save energy & show relevant devices. + optionalServices: [0xfebb, bleNusServiceUUID] + }); + } + async connectToBluetoothDevice(device) { const abortController = new AbortController(); - device.addEventListener('advertisementreceived', async (event) => { + async function onAdvertisementReceived(event) { console.log('> Received advertisement from "' + device.name + '"...'); // Stop watching advertisements to conserve battery life. abortController.abort(); console.log('Connecting to GATT Server from "' + device.name + '"...'); try { - await this.showBusy(device.gatt.connect()); + await device.gatt.connect(); + } catch (error) { + await this._showMessage("Failed to connect to device. Try forgetting device from OS bluetooth devices and try again."); + } + if (device.gatt.connected) { console.log('> Bluetooth device "' + device.name + ' connected.'); await this.switchToDevice(device); + } else { + console.log('Unable to connect to bluetooth device "' + device.name + '.'); } - catch (error) { - console.log('Argh! ' + error); - } - }, {once: true}); + } - //await this.showBusy(device.gatt.connect()); - await navigator.bluetooth.requestDevice({ - filters: [{services: [0xfebb]},], // <- Prefer filters to save energy & show relevant devices. - optionalServices: [0xfebb, bleNusServiceUUID] - }); + device.removeEventListener('advertisementreceived', onAdvertisementReceived.bind(this)); + device.addEventListener('advertisementreceived', onAdvertisementReceived.bind(this)); this.debugLog("connecting to " + device.name); try { @@ -148,49 +161,50 @@ class BLEWorkflow extends Workflow { await device.watchAdvertisements({signal: abortController.signal}); } catch (error) { - console.log('Argh! ' + error); + console.error(error); + await this._showMessage(error); } } // Request Bluetooth Device async onRequestBluetoothDeviceButtonClick(e) { - try { + //try { console.log('Requesting any Bluetooth device...'); this.debugLog("Requesting device. Cancel if empty and try existing"); - let device = await navigator.bluetooth.requestDevice({ - filters: [{services: [0xfebb]},], // <- Prefer filters to save energy & show relevant devices. - optionalServices: [0xfebb, bleNusServiceUUID] - }); + let device = await this.requestDevice(); - await this.showBusy(device.gatt.connect()); console.log('> Requested ' + device.name); + await device.gatt.connect(); await this.switchToDevice(device); - } + /*} catch (error) { - console.log('Argh: ' + error); + console.error(error); + await this._showMessage(error); this.debugLog('No device selected. Try to connect to existing.'); - } + }*/ } async switchToDevice(device) { console.log(device); this.bleDevice = device; + this.bleDevice.removeEventListener("gattserverdisconnected", this.onDisconnected.bind(this)); this.bleDevice.addEventListener("gattserverdisconnected", this.onDisconnected.bind(this)); this.bleServer = this.bleDevice.gatt; console.log("connected", this.bleServer); let services; - try { + console.log(device.gatt.connected); + //try { services = await this.bleServer.getPrimaryServices(); - } catch (e) { + /*} catch (e) { console.log(e, e.stack); - } + }*/ console.log(services); console.log('Initializing File Transfer Client...'); this.initFileClient(new FileTransferClient(this.bleDevice, 65536)); - this.debugLog("connected"); + await this.fileHelper.bond(); await this.connectToSerial(); // Enable/Disable UI buttons @@ -243,6 +257,7 @@ class BLEWorkflow extends Workflow { if (result = await super.connect() instanceof Error) { return result; } + // Is this a new connection? if (!this.bleDevice) { let devices = await navigator.bluetooth.getDevices(); for (const device of devices) { @@ -250,8 +265,9 @@ class BLEWorkflow extends Workflow { } } + // Do we have a connection now but still need to connect serial? if (this.bleDevice && !this.bleServer) { - await await this.showBusy(this.bleDevice.gatt.connect()); + await this.showBusy(this.bleDevice.gatt.connect()); this.switchToDevice(this.bleDevice); } } diff --git a/package-lock.json b/package-lock.json index d2929e0..406247a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,8 @@ }, "devDependencies": { "sass": "^1.78.0", - "vite": "^5.4.3" + "vite": "^5.4.3", + "vite-plugin-mkcert": "^1.17.6" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.20.0" @@ -181,6 +182,173 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", + "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", + "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", + "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", + "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5" + } + }, + "node_modules/@octokit/request": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", + "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^5.0.2", + "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "13.2.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", + "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^23.0.1" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.32.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", @@ -483,6 +651,32 @@ "node": ">= 8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/binary-extensions": { "version": "2.3.0", "dev": true, @@ -505,6 +699,20 @@ "node": ">=8" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/chart.js": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", @@ -552,6 +760,19 @@ "@codemirror/view": "^6.0.0" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "license": "MIT" @@ -560,6 +781,105 @@ "version": "1.0.6", "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.21.5", "dev": true, @@ -619,6 +939,43 @@ "tabbable": "^6.2.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "dev": true, @@ -631,6 +988,55 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "dev": true, @@ -642,6 +1048,61 @@ "node": ">= 6" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/idb-keyval": { "version": "6.2.1", "license": "Apache-2.0" @@ -718,6 +1179,46 @@ "immediate": "~3.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -745,6 +1246,16 @@ "node": ">=0.10.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/pako": { "version": "1.0.11", "license": "(MIT AND Zlib)" @@ -799,6 +1310,13 @@ "version": "2.0.1", "license": "MIT" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/readable-stream": { "version": "2.3.8", "license": "MIT", @@ -922,6 +1440,13 @@ "node": ">=8.0" } }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "dev": true, + "license": "ISC" + }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT" @@ -986,9 +1511,35 @@ } } }, + "node_modules/vite-plugin-mkcert": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/vite-plugin-mkcert/-/vite-plugin-mkcert-1.17.6.tgz", + "integrity": "sha512-4JR1RN0HEg/w17eRQJ/Ve2pSa6KCVQcQO6yKtIaKQCFDyd63zGfXHWpygBkvvRSpqa0GcqNKf0fjUJ0HiJQXVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/rest": "^20.1.1", + "axios": "^1.7.4", + "debug": "^4.3.6", + "picocolors": "^1.0.1" + }, + "engines": { + "node": ">=v16.7.0" + }, + "peerDependencies": { + "vite": ">=3" + } + }, "node_modules/w3c-keyname": { "version": "2.2.8", "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" } } } diff --git a/package.json b/package.json index 11641ee..640924c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ }, "devDependencies": { "sass": "^1.78.0", - "vite": "^5.4.3" + "vite": "^5.4.3", + "vite-plugin-mkcert": "^1.17.6" }, "dependencies": { "@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.2", diff --git a/vite.config.js b/vite.config.js index 3d49619..fb84170 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,5 +1,6 @@ // vite.config.js | https://vitejs.dev/config/ import { defineConfig } from 'vite' +import mkcert from 'vite-plugin-mkcert' export default defineConfig({ build: { @@ -20,5 +21,6 @@ export default defineConfig({ } } } - } + }, + plugins: [ mkcert() ] }) From 35f571e6aa0adb0366a9b4765fbbf4d881d7f9fa Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Sun, 2 Mar 2025 13:20:13 -0800 Subject: [PATCH 40/42] Disable Connect to Device after selected --- js/workflows/usb.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/workflows/usb.js b/js/workflows/usb.js index 82a0408..00b6ca0 100644 --- a/js/workflows/usb.js +++ b/js/workflows/usb.js @@ -27,7 +27,7 @@ class USBWorkflow extends Workflow { this.buttonStates = [ {request: false, select: false}, {request: true, select: false}, - {request: true, select: true}, + {request: false, select: true}, ]; } @@ -261,7 +261,6 @@ class USBWorkflow extends Workflow { // Workflow specific Functions async _switchToDevice(device) { - device.removeEventListener("message", this._messageCallback); this._messageCallback = this.onSerialReceive.bind(this); device.addEventListener("message", this._messageCallback); From d4338ac18638efedea140f5c395527ca787b1e16 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Sun, 2 Mar 2025 13:40:14 -0800 Subject: [PATCH 41/42] Reenable btnBond event listener --- js/workflows/ble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/workflows/ble.js b/js/workflows/ble.js index 61b03e3..bca1948 100644 --- a/js/workflows/ble.js +++ b/js/workflows/ble.js @@ -51,7 +51,7 @@ class BLEWorkflow extends Workflow { btnReconnect = modal.querySelector('#bleReconnect'); btnRequestBluetoothDevice.addEventListener('click', this.onRequestBluetoothDeviceButtonClick.bind(this)); - //btnBond.addEventListener('click', this.onBond.bind(this)); + btnBond.addEventListener('click', this.onBond.bind(this)); btnReconnect.addEventListener('click', this.reconnectButtonHandler.bind(this)); // Check if Web Bluetooth is available From 29f5c086fd30464b77f536abfcf59859e5ad2120 Mon Sep 17 00:00:00 2001 From: Melissa LeBlanc-Williams Date: Mon, 23 Jun 2025 14:05:17 -0700 Subject: [PATCH 42/42] Fix package.json and packagelock.json --- package-lock.json | 610 ++++++++++++++++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 422 insertions(+), 190 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7bd4dab..b95f7eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,19 +24,13 @@ }, "devDependencies": { "sass": "^1.80.6", - "vite": "^5.4.11" + "vite": "^5.4.11", "vite-plugin-mkcert": "^1.17.6" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.20.0" } }, - "../../../circuitpython-repl-js/repl.js": { - "extraneous": true - }, - "../../circuitpython-repl-js/repl.js": { - "extraneous": true - }, "node_modules/@adafruit/ble-file-transfer-js": { "name": "@adafruit/ble-file-transfer", "version": "1.0.2", @@ -152,8 +146,7 @@ }, "node_modules/@kurkle/color": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + "license": "MIT" }, "node_modules/@lezer/common": { "version": "1.2.1", @@ -184,8 +177,6 @@ }, "node_modules/@octokit/auth-token": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, "license": "MIT", "engines": { @@ -194,8 +185,6 @@ }, "node_modules/@octokit/core": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", - "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dev": true, "license": "MIT", "dependencies": { @@ -213,8 +202,6 @@ }, "node_modules/@octokit/endpoint": { "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", - "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "dev": true, "license": "MIT", "dependencies": { @@ -227,8 +214,6 @@ }, "node_modules/@octokit/graphql": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", - "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -242,19 +227,17 @@ }, "node_modules/@octokit/openapi-types": { "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", - "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", "dev": true, "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.1.tgz", - "integrity": "sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==", + "version": "11.4.4-cjs.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.4-cjs.2.tgz", + "integrity": "sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.5.0" + "@octokit/types": "^13.7.0" }, "engines": { "node": ">= 18" @@ -265,8 +248,6 @@ }, "node_modules/@octokit/plugin-request-log": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", - "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", "dev": true, "license": "MIT", "engines": { @@ -277,13 +258,13 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", - "integrity": "sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==", + "version": "13.3.2-cjs.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.2-cjs.1.tgz", + "integrity": "sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==", "dev": true, "license": "MIT", "dependencies": { - "@octokit/types": "^13.5.0" + "@octokit/types": "^13.8.0" }, "engines": { "node": ">= 18" @@ -294,8 +275,6 @@ }, "node_modules/@octokit/request": { "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", - "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "dev": true, "license": "MIT", "dependencies": { @@ -310,8 +289,6 @@ }, "node_modules/@octokit/request-error": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", - "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "dev": true, "license": "MIT", "dependencies": { @@ -324,16 +301,16 @@ } }, "node_modules/@octokit/rest": { - "version": "20.1.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", - "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.2.tgz", + "integrity": "sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==", "dev": true, "license": "MIT", "dependencies": { "@octokit/core": "^5.0.2", - "@octokit/plugin-paginate-rest": "11.3.1", + "@octokit/plugin-paginate-rest": "11.4.4-cjs.2", "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "13.2.2" + "@octokit/plugin-rest-endpoint-methods": "13.3.2-cjs.1" }, "engines": { "node": ">= 18" @@ -341,14 +318,322 @@ }, "node_modules/@octokit/types": { "version": "13.8.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", - "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", "dev": true, "license": "MIT", "dependencies": { "@octokit/openapi-types": "^23.0.1" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.32.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", @@ -393,8 +678,6 @@ }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", - "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", "cpu": [ "x64" ], @@ -546,9 +829,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.32.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", - "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -616,8 +899,6 @@ }, "node_modules/@types/estree": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, "license": "MIT" }, @@ -639,29 +920,15 @@ "version": "5.5.0", "license": "MIT" }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, "license": "MIT" }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "dev": true, "license": "MIT", "dependencies": { @@ -672,25 +939,15 @@ }, "node_modules/before-after-hook": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "dev": true, "license": "Apache-2.0" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "fill-range": "^7.1.1" @@ -701,8 +958,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -715,8 +970,7 @@ }, "node_modules/chart.js": { "version": "4.4.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", - "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", + "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -725,10 +979,11 @@ } }, "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, + "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, @@ -754,8 +1009,6 @@ }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", "dependencies": { @@ -775,8 +1028,6 @@ }, "node_modules/debug": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -793,8 +1044,6 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "license": "MIT", "engines": { @@ -803,15 +1052,25 @@ }, "node_modules/deprecation": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true, "license": "ISC" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { @@ -825,8 +1084,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", "engines": { @@ -835,8 +1092,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", "engines": { @@ -845,8 +1100,6 @@ }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", "dependencies": { @@ -858,8 +1111,6 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { @@ -915,7 +1166,10 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -925,9 +1179,9 @@ } }, "node_modules/focus-trap": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.1.tgz", - "integrity": "sha512-nB8y4nQl8PshahLpGKZOq1sb0xrMVFSn6at7u/qOsBZTlZRzaapISGENcB6mOkoezbClZyiMwEF/dGY8AZ00rA==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", + "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", "license": "MIT", "dependencies": { "tabbable": "^6.2.0" @@ -935,8 +1189,6 @@ }, "node_modules/follow-redirects": { "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -956,8 +1208,6 @@ }, "node_modules/form-data": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, "license": "MIT", "dependencies": { @@ -984,8 +1234,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", "funding": { @@ -994,8 +1242,6 @@ }, "node_modules/get-intrinsic": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", "dependencies": { @@ -1019,8 +1265,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", "dependencies": { @@ -1031,21 +1275,8 @@ "node": ">= 0.4" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", "engines": { @@ -1057,8 +1288,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -1070,8 +1299,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", "dependencies": { @@ -1086,8 +1313,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1106,7 +1331,9 @@ "license": "MIT" }, "node_modules/immutable": { - "version": "4.3.6", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", "dev": true, "license": "MIT" }, @@ -1114,20 +1341,12 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -1135,7 +1354,10 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "is-extglob": "^2.1.1" @@ -1146,7 +1368,10 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.12.0" @@ -1175,18 +1400,29 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { @@ -1195,8 +1431,6 @@ }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { @@ -1208,15 +1442,11 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -1232,22 +1462,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "optional": true - }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", "dependencies": { @@ -1260,13 +1484,15 @@ }, "node_modules/picocolors": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=8.6" @@ -1277,8 +1503,6 @@ }, "node_modules/postcss": { "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", "dev": true, "funding": [ { @@ -1310,8 +1534,6 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true, "license": "MIT" }, @@ -1329,12 +1551,13 @@ } }, "node_modules/readdirp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", - "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 14.16.0" + "node": ">= 14.18.0" }, "funding": { "type": "individual", @@ -1343,8 +1566,6 @@ }, "node_modules/rollup": { "version": "4.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", - "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", "dev": true, "license": "MIT", "dependencies": { @@ -1380,19 +1601,33 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", + "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/safe-buffer": { "version": "5.1.2", "license": "MIT" }, "node_modules/sass": { - "version": "1.80.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", - "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", - "immutable": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -1434,7 +1669,10 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "is-number": "^7.0.0" @@ -1445,8 +1683,6 @@ }, "node_modules/universal-user-agent": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", "dev": true, "license": "ISC" }, @@ -1455,9 +1691,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", - "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", "dependencies": { @@ -1516,8 +1752,6 @@ }, "node_modules/vite-plugin-mkcert": { "version": "1.17.6", - "resolved": "https://registry.npmjs.org/vite-plugin-mkcert/-/vite-plugin-mkcert-1.17.6.tgz", - "integrity": "sha512-4JR1RN0HEg/w17eRQJ/Ve2pSa6KCVQcQO6yKtIaKQCFDyd63zGfXHWpygBkvvRSpqa0GcqNKf0fjUJ0HiJQXVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1539,8 +1773,6 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" } diff --git a/package.json b/package.json index 0d13960..eec4e1a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "sass": "^1.80.6", - "vite": "^5.4.11" + "vite": "^5.4.11", "vite-plugin-mkcert": "^1.17.6" }, "dependencies": {