tasmota-esp-web-tools 10.0.0 → 10.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ewt-littlefs-manager.d.ts +1 -0
- package/dist/components/ewt-littlefs-manager.js +40 -8
- package/dist/install-dialog.js +11 -0
- package/dist/web/install-button.js +1 -1
- package/dist/web/{install-dialog-CVebVk1R.js → install-dialog-Bt_kBJJW.js} +6 -4
- package/dist/web/wasm/littlefs/index.d.ts +19 -1
- package/dist/web/wasm/littlefs/index.js +0 -10
- package/js/modules/install-button.js +1 -1
- package/js/modules/{install-dialog-CVebVk1R.js → install-dialog-Bt_kBJJW.js} +6 -4
- package/js/modules/wasm/littlefs/index.d.ts +19 -1
- package/js/modules/wasm/littlefs/index.js +0 -10
- package/package.json +1 -1
|
@@ -17,6 +17,7 @@ export declare class EwtLittleFSManager extends LitElement {
|
|
|
17
17
|
private _selectedFile;
|
|
18
18
|
private _flashProgress;
|
|
19
19
|
private _isFlashing;
|
|
20
|
+
private _flashOperation;
|
|
20
21
|
connectedCallback(): Promise<void>;
|
|
21
22
|
disconnectedCallback(): void;
|
|
22
23
|
private _openFilesystem;
|
|
@@ -50,11 +50,13 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
50
50
|
this._diskVersion = "";
|
|
51
51
|
this._busy = false;
|
|
52
52
|
this._selectedFile = null;
|
|
53
|
-
this._flashProgress = 0; // 0-100 for flash progress
|
|
53
|
+
this._flashProgress = 0; // 0-100 for flash progress
|
|
54
54
|
this._isFlashing = false;
|
|
55
|
+
this._flashOperation = null; // Track operation type
|
|
55
56
|
}
|
|
56
57
|
async connectedCallback() {
|
|
57
58
|
super.connectedCallback();
|
|
59
|
+
this.logger.log("LittleFS Manager: connectedCallback called");
|
|
58
60
|
await this._openFilesystem();
|
|
59
61
|
}
|
|
60
62
|
disconnectedCallback() {
|
|
@@ -64,9 +66,21 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
64
66
|
async _openFilesystem() {
|
|
65
67
|
try {
|
|
66
68
|
this._busy = true;
|
|
69
|
+
this._isFlashing = true;
|
|
70
|
+
this._flashProgress = 0;
|
|
71
|
+
this._flashOperation = "reading";
|
|
67
72
|
this.logger.log(`Reading LittleFS partition "${this.partition.name}" (${this._formatSize(this.partition.size)})...`);
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
if (!this.espStub.IS_STUB) {
|
|
74
|
+
throw new Error("ESP stub loader is not running. Cannot read flash.");
|
|
75
|
+
}
|
|
76
|
+
// Read entire partition with progress callback
|
|
77
|
+
const data = await this.espStub.readFlash(this.partition.offset, this.partition.size, (_packet, progress, totalSize) => {
|
|
78
|
+
const progressPercent = Math.floor((progress / totalSize) * 100);
|
|
79
|
+
this._flashProgress = progressPercent;
|
|
80
|
+
});
|
|
81
|
+
if (data.length === 0) {
|
|
82
|
+
throw new Error("Read 0 bytes from partition");
|
|
83
|
+
}
|
|
70
84
|
this.logger.log("Mounting LittleFS filesystem...");
|
|
71
85
|
// Load LittleFS module dynamically
|
|
72
86
|
const { createLittleFSFromImage, formatDiskVersion } = await loadLittleFS();
|
|
@@ -105,10 +119,15 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
105
119
|
// Get disk version
|
|
106
120
|
try {
|
|
107
121
|
const diskVer = fs.getDiskVersion();
|
|
108
|
-
|
|
122
|
+
if (diskVer && diskVer !== 0) {
|
|
123
|
+
this._diskVersion = formatDiskVersion(diskVer);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this._diskVersion = "Unknown";
|
|
127
|
+
}
|
|
109
128
|
}
|
|
110
129
|
catch (e) {
|
|
111
|
-
this._diskVersion = "";
|
|
130
|
+
this._diskVersion = "Unknown";
|
|
112
131
|
}
|
|
113
132
|
this._refreshFiles();
|
|
114
133
|
this.logger.log("LittleFS filesystem opened successfully");
|
|
@@ -121,11 +140,15 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
121
140
|
}
|
|
122
141
|
finally {
|
|
123
142
|
this._busy = false;
|
|
143
|
+
this._isFlashing = false;
|
|
144
|
+
this._flashProgress = 0;
|
|
145
|
+
this._flashOperation = null;
|
|
124
146
|
}
|
|
125
147
|
}
|
|
126
148
|
_refreshFiles() {
|
|
127
|
-
if (!this._fs)
|
|
149
|
+
if (!this._fs) {
|
|
128
150
|
return;
|
|
151
|
+
}
|
|
129
152
|
try {
|
|
130
153
|
// Calculate usage
|
|
131
154
|
const allFiles = this._fs.list("/");
|
|
@@ -150,6 +173,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
150
173
|
}
|
|
151
174
|
catch (e) {
|
|
152
175
|
this.logger.error(`Failed to refresh file list: ${e.message || e}`);
|
|
176
|
+
this._files = [];
|
|
153
177
|
}
|
|
154
178
|
}
|
|
155
179
|
_estimateUsage(entries) {
|
|
@@ -343,6 +367,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
343
367
|
this._busy = true;
|
|
344
368
|
this._isFlashing = true;
|
|
345
369
|
this._flashProgress = 0;
|
|
370
|
+
this._flashOperation = "writing"; // Set operation type
|
|
346
371
|
this.logger.log("Creating LittleFS image...");
|
|
347
372
|
const image = this._fs.toImage();
|
|
348
373
|
this.logger.log(`Image created: ${this._formatSize(image.length)}`);
|
|
@@ -357,7 +382,6 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
357
382
|
await this.espStub.flashData(imageBuffer, (bytesWritten, totalBytes) => {
|
|
358
383
|
const percent = Math.floor((bytesWritten / totalBytes) * 100);
|
|
359
384
|
this._flashProgress = percent;
|
|
360
|
-
this.logger.log(`Writing: ${percent}%`);
|
|
361
385
|
}, this.partition.offset);
|
|
362
386
|
this.logger.log(`✓ LittleFS successfully written to flash!`);
|
|
363
387
|
this.logger.log(`To use the new filesystem, reset your device.`);
|
|
@@ -369,6 +393,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
369
393
|
this._busy = false;
|
|
370
394
|
this._isFlashing = false;
|
|
371
395
|
this._flashProgress = 0;
|
|
396
|
+
this._flashOperation = null;
|
|
372
397
|
}
|
|
373
398
|
}
|
|
374
399
|
_cleanup() {
|
|
@@ -412,7 +437,11 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
412
437
|
<div class="usage-text">
|
|
413
438
|
${this._isFlashing
|
|
414
439
|
? html `<span class="flash-status">
|
|
415
|
-
⚡
|
|
440
|
+
⚡
|
|
441
|
+
${this._flashOperation === "reading"
|
|
442
|
+
? "Reading from"
|
|
443
|
+
: "Writing to"}
|
|
444
|
+
flash: ${this._flashProgress}%
|
|
416
445
|
</span>`
|
|
417
446
|
: html `<span
|
|
418
447
|
>Used: ${this._formatSize(this._usage.usedBytes)} /
|
|
@@ -813,6 +842,9 @@ __decorate([
|
|
|
813
842
|
__decorate([
|
|
814
843
|
state()
|
|
815
844
|
], EwtLittleFSManager.prototype, "_isFlashing", void 0);
|
|
845
|
+
__decorate([
|
|
846
|
+
state()
|
|
847
|
+
], EwtLittleFSManager.prototype, "_flashOperation", void 0);
|
|
816
848
|
EwtLittleFSManager = __decorate([
|
|
817
849
|
customElement("ewt-littlefs-manager")
|
|
818
850
|
], EwtLittleFSManager);
|
package/dist/install-dialog.js
CHANGED
|
@@ -867,6 +867,17 @@ export class EwtInstallDialog extends LitElement {
|
|
|
867
867
|
this.logger.log("Running stub...");
|
|
868
868
|
const espStub = await esploader.runStub();
|
|
869
869
|
this._espStub = espStub;
|
|
870
|
+
// Set baudrate for reading flash (use user-selected baudrate if available)
|
|
871
|
+
if (this.baudRate) {
|
|
872
|
+
this.logger.log(`Setting baudrate to ${this.baudRate} for flash reading...`);
|
|
873
|
+
try {
|
|
874
|
+
await espStub.setBaudrate(this.baudRate);
|
|
875
|
+
this.logger.log(`Baudrate set to ${this.baudRate}`);
|
|
876
|
+
}
|
|
877
|
+
catch (baudErr) {
|
|
878
|
+
this.logger.log(`Failed to set baudrate: ${baudErr.message}, continuing with default`);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
870
881
|
// Add a small delay after stub is running
|
|
871
882
|
await sleep(500);
|
|
872
883
|
this.logger.log("Reading flash data...");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=async t=>{let n;import("./install-dialog-
|
|
1
|
+
const e=async t=>{let n;import("./install-dialog-Bt_kBJJW.js");try{n=await navigator.serial.requestPort()}catch(n){return"NotFoundError"===n.name?void import("./index-t2Vsxqjj.js").then(n=>n.openNoPortPickedDialog(()=>e(t))):void alert(`Error: ${n.message}`)}if(!n)return;try{await n.open({baudRate:115200})}catch(e){return void alert(e.message)}const o=document.createElement("ewt-install-dialog");o.port=n,o.manifestPath=t.manifest||t.getAttribute("manifest"),o.overrides=t.overrides,o.firmwareFile=t.firmwareFile;const r=t.getAttribute("baud-rate");if(r){const e=parseInt(r,10);isNaN(e)||(o.baudRate=e)}else void 0!==t.baudRate&&(o.baudRate=t.baudRate);o.addEventListener("closed",()=>{n.close()},{once:!0}),document.body.appendChild(o)};class t extends HTMLElement{connectedCallback(){if(this.renderRoot)return;if(this.renderRoot=this.attachShadow({mode:"open"}),!t.isSupported||!t.isAllowed)return this.toggleAttribute("install-unsupported",!0),void(this.renderRoot.innerHTML=t.isAllowed?"<slot name='unsupported'>Your browser does not support installing things on ESP devices. Use Google Chrome or Microsoft Edge.</slot>":"<slot name='not-allowed'>You can only install ESP devices on HTTPS websites or on the localhost.</slot>");this.toggleAttribute("install-supported",!0);const n=document.createElement("slot");n.addEventListener("click",async t=>{t.preventDefault(),e(this)}),n.name="activate";const o=document.createElement("button");if(o.innerText="CONNECT",n.append(o),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype){const e=new CSSStyleSheet;e.replaceSync(t.style),this.renderRoot.adoptedStyleSheets=[e]}else{const e=document.createElement("style");e.innerText=t.style,this.renderRoot.append(e)}this.renderRoot.append(n)}}t.isSupported="serial"in navigator,t.isAllowed=window.isSecureContext,t.style='\n button {\n position: relative;\n cursor: pointer;\n font-size: 14px;\n padding: 8px 28px;\n color: var(--esp-tools-button-text-color, #fff);\n background-color: var(--esp-tools-button-color, #03a9f4);\n border: none;\n border-radius: 4px;\n box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.12), 0 1px 5px 0 rgba(0,0,0,.2);\n }\n button::before {\n content: " ";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n opacity: 0.2;\n border-radius: 4px;\n }\n button:hover {\n box-shadow: 0 4px 8px 0 rgba(0,0,0,.14), 0 1px 7px 0 rgba(0,0,0,.12), 0 3px 1px -1px rgba(0,0,0,.2);\n }\n button:hover::before {\n background-color: rgba(255,255,255,.8);\n }\n button:focus {\n outline: none;\n }\n button:focus::before {\n background-color: white;\n }\n button:active::before {\n background-color: grey;\n }\n :host([active]) button {\n color: rgba(0, 0, 0, 0.38);\n background-color: rgba(0, 0, 0, 0.12);\n box-shadow: none;\n cursor: unset;\n pointer-events: none;\n }\n improv-wifi-launch-button {\n display: block;\n margin-top: 16px;\n }\n .hidden {\n display: none;\n }',customElements.define("esp-web-install-button",t);
|
|
@@ -301,7 +301,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
301
301
|
.mdc-floating-label {
|
|
302
302
|
line-height: 1.15em;
|
|
303
303
|
}
|
|
304
|
-
`],customElements.define("ewt-select",li);class ci extends Te{}ci.styles=[Ae],customElements.define("ewt-list-item",ci);let hi=null,mi=null;let pi=class extends x{constructor(){super(...arguments),this.logger=console,this._currentPath="/",this._files=[],this._fs=null,this._blockSize=4096,this._usage={capacityBytes:0,usedBytes:0,freeBytes:0},this._diskVersion="",this._busy=!1,this._selectedFile=null,this._flashProgress=0,this._isFlashing=!1}async connectedCallback(){super.connectedCallback(),await this._openFilesystem()}disconnectedCallback(){super.disconnectedCallback(),this._cleanup()}async _openFilesystem(){try{this._busy=!0,this.logger.log(`Reading LittleFS partition "${this.partition.name}" (${this._formatSize(this.partition.size)})...`);const e=await this.espStub.readFlash(this.partition.offset,this.partition.size);this.logger.log("Mounting LittleFS filesystem...");const{createLittleFSFromImage:t,formatDiskVersion:i}=await async function(){if(mi)return mi;if(!hi){const e=new URL(import.meta.url),t=e.href.substring(0,e.href.lastIndexOf("/")+1);hi=t+"wasm/littlefs/"}try{const e=hi+"index.js";return console.log("[LittleFS] Loading module from:",e),mi=await import(e),mi}catch(e){console.error("[LittleFS] Failed to load from calculated path:",hi,e);try{return mi=await import("./wasm/littlefs/index.js"),mi}catch(t){throw console.error("[LittleFS] Fallback import also failed:",t),new Error(`Failed to load LittleFS module: ${e}`)}}}(),n=[4096,2048,1024,512];let o=null,r=0;for(const i of n)try{const n={blockSize:i,blockCount:Math.floor(this.partition.size/i)};hi&&(n.wasmURL=new URL("littlefs.wasm",hi).href),o=await t(e,n),o.list("/"),r=i,this.logger.log(`Successfully mounted LittleFS with block size ${i}`);break}catch(e){o=null}if(!o)throw new Error("Failed to mount LittleFS with any block size");this._fs=o,this._blockSize=r;try{const e=o.getDiskVersion();this._diskVersion=i(e)}catch(e){this._diskVersion=""}this._refreshFiles(),this.logger.log("LittleFS filesystem opened successfully")}catch(e){this.logger.error(`Failed to open LittleFS: ${e.message||e}`),this.onClose&&this.onClose()}finally{this._busy=!1}}_refreshFiles(){if(this._fs)try{const e=this._fs.list("/"),t=this._estimateUsage(e),i=this.partition.size;this._usage={capacityBytes:i,usedBytes:t,freeBytes:i-t};const n=this._fs.list(this._currentPath);n.sort((e,t)=>"dir"===e.type&&"dir"!==t.type?-1:"dir"!==e.type&&"dir"===t.type?1:e.path.localeCompare(t.path)),this._files=n}catch(e){this.logger.error(`Failed to refresh file list: ${e.message||e}`)}}_estimateUsage(e){const t=this._blockSize||4096;let i=2*t;for(const n of e||[])if("dir"===n.type)i+=t;else{i+=Math.max(1,Math.ceil((n.size||0)/t))*t+t}return i}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}_navigateUp(){if("/"===this._currentPath||!this._currentPath)return;const e=this._currentPath.split("/").filter(Boolean);e.pop(),this._currentPath="/"+e.join("/"),"/"===this._currentPath||this._currentPath.endsWith("/")||(this._currentPath+="/"),this._refreshFiles()}_navigateTo(e){this._currentPath=e,this._refreshFiles()}async _uploadFile(){if(this._fs&&this._selectedFile)try{this._busy=!0,this.logger.log(`Uploading file "${this._selectedFile.name}"...`);const e=await this._selectedFile.arrayBuffer(),t=new Uint8Array(e);let i=this._currentPath;i.endsWith("/")||(i+="/"),i+=this._selectedFile.name;const n=i.split("/").filter(Boolean);if(n.length>1){let e="";for(let t=0;t<n.length-1;t++){e+=`/${n[t]}`;try{this._fs.mkdir(e)}catch(e){}}}"function"==typeof this._fs.writeFile?this._fs.writeFile(i,t):"function"==typeof this._fs.addFile&&this._fs.addFile(i,t);const o=this._fs.readFile(i);this.logger.log(`✓ File written: ${o.length} bytes at ${i}`);const r=this._selectedFile.name;this._selectedFile=null,this._refreshFiles(),this.logger.log(`File "${r}" uploaded successfully`)}catch(e){this.logger.error(`Failed to upload file: ${e.message||e}`)}finally{this._busy=!1}}_createFolder(){if(!this._fs)return;const e=prompt("Enter directory name:");if(e&&e.trim())try{let t=this._currentPath;t.endsWith("/")||(t+="/"),t+=e.trim(),this._fs.mkdir(t),this._refreshFiles(),this.logger.log(`Directory "${e}" created successfully`)}catch(e){this.logger.error(`Failed to create directory: ${e.message||e}`)}}async _downloadFile(e){if(this._fs)try{this.logger.log(`Downloading file "${e}"...`);const t=this._fs.readFile(e),i=e.split("/").filter(Boolean).pop()||"file.bin",n=new Blob([t],{type:"application/octet-stream"}),o=URL.createObjectURL(n),r=document.createElement("a");r.href=o,r.download=i,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o),this.logger.log(`File "${i}" downloaded successfully`)}catch(e){this.logger.error(`Failed to download file: ${e.message||e}`)}}_deleteFile(e,t){if(!this._fs)return;const i=e.split("/").filter(Boolean).pop()||e;if(confirm(`Delete ${t} "${i}"?`))try{"dir"===t?this._fs.delete(e,{recursive:!0}):this._fs.deleteFile(e),this._refreshFiles(),this.logger.log(`${"dir"===t?"Directory":"File"} "${i}" deleted successfully`)}catch(e){this.logger.error(`Failed to delete ${t}: ${e.message||e}`)}}async _backupImage(){if(this._fs)try{this.logger.log("Creating LittleFS backup image...");const e=this._fs.toImage(),t=`${this.partition.name}_littlefs_backup.bin`,i=new Blob([e],{type:"application/octet-stream"}),n=URL.createObjectURL(i),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n),this.logger.log(`LittleFS backup saved as "${t}"`)}catch(e){this.logger.error(`Failed to backup LittleFS: ${e.message||e}`)}}async _writeToFlash(){if(!this._fs)return;if(confirm(`Write modified LittleFS to flash?\n\nPartition: ${this.partition.name}\nOffset: 0x${this.partition.offset.toString(16)}\nSize: ${this._formatSize(this.partition.size)}\n\nThis will overwrite the current filesystem on the device!`))try{this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this.logger.log("Creating LittleFS image...");const e=this._fs.toImage();if(this.logger.log(`Image created: ${this._formatSize(e.length)}`),e.length>this.partition.size)return void this.logger.error(`Image size (${this._formatSize(e.length)}) exceeds partition size (${this._formatSize(this.partition.size)})`);this.logger.log(`Writing ${this._formatSize(e.length)} to partition "${this.partition.name}" at 0x${this.partition.offset.toString(16)}...`);const t=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);await this.espStub.flashData(t,(e,t)=>{const i=Math.floor(e/t*100);this._flashProgress=i
|
|
304
|
+
`],customElements.define("ewt-select",li);class ci extends Te{}ci.styles=[Ae],customElements.define("ewt-list-item",ci);let hi=null,mi=null;let pi=class extends x{constructor(){super(...arguments),this.logger=console,this._currentPath="/",this._files=[],this._fs=null,this._blockSize=4096,this._usage={capacityBytes:0,usedBytes:0,freeBytes:0},this._diskVersion="",this._busy=!1,this._selectedFile=null,this._flashProgress=0,this._isFlashing=!1,this._flashOperation=null}async connectedCallback(){super.connectedCallback(),this.logger.log("LittleFS Manager: connectedCallback called"),await this._openFilesystem()}disconnectedCallback(){super.disconnectedCallback(),this._cleanup()}async _openFilesystem(){try{if(this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this._flashOperation="reading",this.logger.log(`Reading LittleFS partition "${this.partition.name}" (${this._formatSize(this.partition.size)})...`),!this.espStub.IS_STUB)throw new Error("ESP stub loader is not running. Cannot read flash.");const e=await this.espStub.readFlash(this.partition.offset,this.partition.size,(e,t,i)=>{const n=Math.floor(t/i*100);this._flashProgress=n});if(0===e.length)throw new Error("Read 0 bytes from partition");this.logger.log("Mounting LittleFS filesystem...");const{createLittleFSFromImage:t,formatDiskVersion:i}=await async function(){if(mi)return mi;if(!hi){const e=new URL(import.meta.url),t=e.href.substring(0,e.href.lastIndexOf("/")+1);hi=t+"wasm/littlefs/"}try{const e=hi+"index.js";return console.log("[LittleFS] Loading module from:",e),mi=await import(e),mi}catch(e){console.error("[LittleFS] Failed to load from calculated path:",hi,e);try{return mi=await import("./wasm/littlefs/index.js"),mi}catch(t){throw console.error("[LittleFS] Fallback import also failed:",t),new Error(`Failed to load LittleFS module: ${e}`)}}}(),n=[4096,2048,1024,512];let o=null,r=0;for(const i of n)try{const n={blockSize:i,blockCount:Math.floor(this.partition.size/i)};hi&&(n.wasmURL=new URL("littlefs.wasm",hi).href),o=await t(e,n),o.list("/"),r=i,this.logger.log(`Successfully mounted LittleFS with block size ${i}`);break}catch(e){o=null}if(!o)throw new Error("Failed to mount LittleFS with any block size");this._fs=o,this._blockSize=r;try{const e=o.getDiskVersion();this._diskVersion=e&&0!==e?i(e):"Unknown"}catch(e){this._diskVersion="Unknown"}this._refreshFiles(),this.logger.log("LittleFS filesystem opened successfully")}catch(e){this.logger.error(`Failed to open LittleFS: ${e.message||e}`),this.onClose&&this.onClose()}finally{this._busy=!1,this._isFlashing=!1,this._flashProgress=0,this._flashOperation=null}}_refreshFiles(){if(this._fs)try{const e=this._fs.list("/"),t=this._estimateUsage(e),i=this.partition.size;this._usage={capacityBytes:i,usedBytes:t,freeBytes:i-t};const n=this._fs.list(this._currentPath);n.sort((e,t)=>"dir"===e.type&&"dir"!==t.type?-1:"dir"!==e.type&&"dir"===t.type?1:e.path.localeCompare(t.path)),this._files=n}catch(e){this.logger.error(`Failed to refresh file list: ${e.message||e}`),this._files=[]}}_estimateUsage(e){const t=this._blockSize||4096;let i=2*t;for(const n of e||[])if("dir"===n.type)i+=t;else{i+=Math.max(1,Math.ceil((n.size||0)/t))*t+t}return i}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}_navigateUp(){if("/"===this._currentPath||!this._currentPath)return;const e=this._currentPath.split("/").filter(Boolean);e.pop(),this._currentPath="/"+e.join("/"),"/"===this._currentPath||this._currentPath.endsWith("/")||(this._currentPath+="/"),this._refreshFiles()}_navigateTo(e){this._currentPath=e,this._refreshFiles()}async _uploadFile(){if(this._fs&&this._selectedFile)try{this._busy=!0,this.logger.log(`Uploading file "${this._selectedFile.name}"...`);const e=await this._selectedFile.arrayBuffer(),t=new Uint8Array(e);let i=this._currentPath;i.endsWith("/")||(i+="/"),i+=this._selectedFile.name;const n=i.split("/").filter(Boolean);if(n.length>1){let e="";for(let t=0;t<n.length-1;t++){e+=`/${n[t]}`;try{this._fs.mkdir(e)}catch(e){}}}"function"==typeof this._fs.writeFile?this._fs.writeFile(i,t):"function"==typeof this._fs.addFile&&this._fs.addFile(i,t);const o=this._fs.readFile(i);this.logger.log(`✓ File written: ${o.length} bytes at ${i}`);const r=this._selectedFile.name;this._selectedFile=null,this._refreshFiles(),this.logger.log(`File "${r}" uploaded successfully`)}catch(e){this.logger.error(`Failed to upload file: ${e.message||e}`)}finally{this._busy=!1}}_createFolder(){if(!this._fs)return;const e=prompt("Enter directory name:");if(e&&e.trim())try{let t=this._currentPath;t.endsWith("/")||(t+="/"),t+=e.trim(),this._fs.mkdir(t),this._refreshFiles(),this.logger.log(`Directory "${e}" created successfully`)}catch(e){this.logger.error(`Failed to create directory: ${e.message||e}`)}}async _downloadFile(e){if(this._fs)try{this.logger.log(`Downloading file "${e}"...`);const t=this._fs.readFile(e),i=e.split("/").filter(Boolean).pop()||"file.bin",n=new Blob([t],{type:"application/octet-stream"}),o=URL.createObjectURL(n),r=document.createElement("a");r.href=o,r.download=i,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o),this.logger.log(`File "${i}" downloaded successfully`)}catch(e){this.logger.error(`Failed to download file: ${e.message||e}`)}}_deleteFile(e,t){if(!this._fs)return;const i=e.split("/").filter(Boolean).pop()||e;if(confirm(`Delete ${t} "${i}"?`))try{"dir"===t?this._fs.delete(e,{recursive:!0}):this._fs.deleteFile(e),this._refreshFiles(),this.logger.log(`${"dir"===t?"Directory":"File"} "${i}" deleted successfully`)}catch(e){this.logger.error(`Failed to delete ${t}: ${e.message||e}`)}}async _backupImage(){if(this._fs)try{this.logger.log("Creating LittleFS backup image...");const e=this._fs.toImage(),t=`${this.partition.name}_littlefs_backup.bin`,i=new Blob([e],{type:"application/octet-stream"}),n=URL.createObjectURL(i),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n),this.logger.log(`LittleFS backup saved as "${t}"`)}catch(e){this.logger.error(`Failed to backup LittleFS: ${e.message||e}`)}}async _writeToFlash(){if(!this._fs)return;if(confirm(`Write modified LittleFS to flash?\n\nPartition: ${this.partition.name}\nOffset: 0x${this.partition.offset.toString(16)}\nSize: ${this._formatSize(this.partition.size)}\n\nThis will overwrite the current filesystem on the device!`))try{this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this._flashOperation="writing",this.logger.log("Creating LittleFS image...");const e=this._fs.toImage();if(this.logger.log(`Image created: ${this._formatSize(e.length)}`),e.length>this.partition.size)return void this.logger.error(`Image size (${this._formatSize(e.length)}) exceeds partition size (${this._formatSize(this.partition.size)})`);this.logger.log(`Writing ${this._formatSize(e.length)} to partition "${this.partition.name}" at 0x${this.partition.offset.toString(16)}...`);const t=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);await this.espStub.flashData(t,(e,t)=>{const i=Math.floor(e/t*100);this._flashProgress=i},this.partition.offset),this.logger.log("✓ LittleFS successfully written to flash!"),this.logger.log("To use the new filesystem, reset your device.")}catch(e){this.logger.error(`Failed to write LittleFS to flash: ${e.message||e}`)}finally{this._busy=!1,this._isFlashing=!1,this._flashProgress=0,this._flashOperation=null}}_cleanup(){this._fs&&(this._fs=null)}_handleFileSelect(e){var t;const i=e.target;this._selectedFile=(null===(t=i.files)||void 0===t?void 0:t[0])||null}render(){const e=Math.round(this._usage.usedBytes/this._usage.capacityBytes*100);return h`
|
|
305
305
|
<div class="littlefs-manager">
|
|
306
306
|
<h3>LittleFS Filesystem Manager</h3>
|
|
307
307
|
|
|
@@ -321,7 +321,9 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
321
321
|
</div>
|
|
322
322
|
<div class="usage-text">
|
|
323
323
|
${this._isFlashing?h`<span class="flash-status">
|
|
324
|
-
⚡
|
|
324
|
+
⚡
|
|
325
|
+
${"reading"===this._flashOperation?"Reading from":"Writing to"}
|
|
326
|
+
flash: ${this._flashProgress}%
|
|
325
327
|
</span>`:h`<span
|
|
326
328
|
>Used: ${this._formatSize(this._usage.usedBytes)} /
|
|
327
329
|
${this._formatSize(this._usage.capacityBytes)}
|
|
@@ -658,7 +660,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
658
660
|
.danger {
|
|
659
661
|
--mdc-theme-primary: var(--improv-danger-color, #db4437);
|
|
660
662
|
}
|
|
661
|
-
`,i([n({type:Object})],pi.prototype,"partition",void 0),i([n({type:Object})],pi.prototype,"espStub",void 0),i([n({type:Function})],pi.prototype,"logger",void 0),i([n({type:Function})],pi.prototype,"onClose",void 0),i([s()],pi.prototype,"_currentPath",void 0),i([s()],pi.prototype,"_files",void 0),i([s()],pi.prototype,"_fs",void 0),i([s()],pi.prototype,"_blockSize",void 0),i([s()],pi.prototype,"_usage",void 0),i([s()],pi.prototype,"_diskVersion",void 0),i([s()],pi.prototype,"_busy",void 0),i([s()],pi.prototype,"_selectedFile",void 0),i([s()],pi.prototype,"_flashProgress",void 0),i([s()],pi.prototype,"_isFlashing",void 0),pi=i([y("ewt-littlefs-manager")],pi);class ui extends x{constructor(){super(...arguments),this.indeterminate=!1,this.progress=0,this.density=0,this.closed=!1}open(){this.closed=!1}close(){this.closed=!0}render(){const e={"mdc-circular-progress--closed":this.closed,"mdc-circular-progress--indeterminate":this.indeterminate},t=48+4*this.density,i={width:`${t}px`,height:`${t}px`};return h`
|
|
663
|
+
`,i([n({type:Object})],pi.prototype,"partition",void 0),i([n({type:Object})],pi.prototype,"espStub",void 0),i([n({type:Function})],pi.prototype,"logger",void 0),i([n({type:Function})],pi.prototype,"onClose",void 0),i([s()],pi.prototype,"_currentPath",void 0),i([s()],pi.prototype,"_files",void 0),i([s()],pi.prototype,"_fs",void 0),i([s()],pi.prototype,"_blockSize",void 0),i([s()],pi.prototype,"_usage",void 0),i([s()],pi.prototype,"_diskVersion",void 0),i([s()],pi.prototype,"_busy",void 0),i([s()],pi.prototype,"_selectedFile",void 0),i([s()],pi.prototype,"_flashProgress",void 0),i([s()],pi.prototype,"_isFlashing",void 0),i([s()],pi.prototype,"_flashOperation",void 0),pi=i([y("ewt-littlefs-manager")],pi);class ui extends x{constructor(){super(...arguments),this.indeterminate=!1,this.progress=0,this.density=0,this.closed=!1}open(){this.closed=!1}close(){this.closed=!0}render(){const e={"mdc-circular-progress--closed":this.closed,"mdc-circular-progress--indeterminate":this.indeterminate},t=48+4*this.density,i={width:`${t}px`,height:`${t}px`};return h`
|
|
662
664
|
<div
|
|
663
665
|
class="mdc-circular-progress ${m(e)}"
|
|
664
666
|
style="${F(i)}"
|
|
@@ -1168,7 +1170,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
1168
1170
|
label="Cancel"
|
|
1169
1171
|
@click=${()=>{this._state="DASHBOARD"}}
|
|
1170
1172
|
></ewt-button>
|
|
1171
|
-
`,!1]}async _handleESP32S2ReconnectClick(){try{this._busy=!0,this.logger.log("Requesting new port selection...");const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.logger.log("New port selected, updating..."),this.port=e,this._state="PARTITIONS",await this._readPartitionTable()}catch(e){"NotFoundError"===e.name?(this.logger.log("Port selection cancelled"),this._state="DASHBOARD"):(this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR")}finally{this._busy=!1}}async _readPartitionTable(){this._busy=!0,this._partitions=void 0;try{this.logger.log("Reading partition table from 0x8000...");const{ESPLoader:e}=await Promise.resolve().then(function(){return Sr});let t=this.port,i=new e(t,{log:(e,...t)=>this.logger.log(e,...t),debug:(e,...t)=>{var i,n;return null===(n=(i=this.logger).debug)||void 0===n?void 0:n.call(i,e,...t)},error:(e,...t)=>this.logger.error(e,...t)});const n=async e=>{this.logger.log("ESP32-S2 USB reconnect event:",e.detail.message),await Tr(t),this.logger.log("Please select the new ESP32-S2 USB CDC port")};i.addEventListener("esp32s2-usb-reconnect",n,{once:!0}),this.logger.log("Initializing ESP loader...");try{await i.initialize(),this.logger.log("ESP loader initialized successfully")}catch(e){if(Cr(t)&&Rr(e))return this.logger.log("ESP32-S2 USB port changed - user needs to select new port"),await Tr(t),this._busy=!1,void(this._state="ESP32S2_RECONNECT");throw e}this.logger.log("Running stub...");const o=await i.runStub();this._espStub=o,await V(500),this.logger.log("Reading flash data...");const r=function(e){const t=[];for(let i=0;i<e.length;i+=32){const n=zr(e.slice(i,i+32));if(null===n)break;t.push(n)}return t}(await o.readFlash(32768,4096));0===r.length?(this.logger.error("No valid partition table found"),this._partitions=[]):(this.logger.log(`Found ${r.length} partition(s)`),this._partitions=r)}catch(e){this.logger.error(`Failed to read partition table: ${e.message||e}`),"Port selection cancelled"===e.message?this._error="Port selection cancelled":this._error=`Failed to read partition table: ${e.message||e}. Try resetting your device.`,this._state="ERROR"}finally{this._busy=!1}}async _openFilesystem(e){try{if(this._busy=!0,this.logger.log(`Detecting filesystem type for partition "${e.name}"...`),!this._espStub)throw new Error("ESP stub not available. Please reconnect.");const t=await async function(e,t,i,n=console){try{const o=Math.min(8192,i),r=await e.readFlash(t,o);if(r.length<32)return n.log("Partition too small, assuming SPIFFS"),"spiffs";if(new TextDecoder("ascii",{fatal:!1}).decode(r).includes("littlefs"))return n.log('✓ LittleFS detected: Found "littlefs" signature'),"littlefs";const a=new DataView(r.buffer,r.byteOffset,r.byteLength),s=[4096,2048,1024,512];for(const e of s)if(r.length>=2*e)try{for(let t=0;t<Math.min(e,r.length-4);t+=4){const e=a.getUint32(t,!0),i=1023&e;if((e>>20&4095)<=2047&&i>0&&i<=1022&&t+i+4<=r.length)return n.log("✓ LittleFS detected: Found valid metadata structure"),"littlefs"}}catch(e){}for(let e=0;e<Math.min(4096,r.length-4);e+=4){const t=a.getUint32(e,!0);if(538182953===t||538314025===t)return n.log("✓ SPIFFS detected: Found SPIFFS magic number"),"spiffs"}return n.log("⚠ No clear filesystem signature found, assuming SPIFFS"),"spiffs"}catch(e){return n.error(`Failed to detect filesystem type: ${e.message||e}`),"spiffs"}}(this._espStub,e.offset,e.size,this.logger);this.logger.log(`Detected filesystem: ${t}`),"littlefs"===t?(this._selectedPartition=e,this._state="LITTLEFS"):"spiffs"===t?(this.logger.error("SPIFFS support not yet implemented. Use LittleFS partitions."),this._error="SPIFFS support not yet implemented",this._state="ERROR"):(this.logger.error("Unknown filesystem type. Cannot open partition."),this._error="Unknown filesystem type",this._state="ERROR")}catch(e){this.logger.error(`Failed to open filesystem: ${e.message||e}`),this._error=`Failed to open filesystem: ${e.message||e}`,this._state="ERROR"}finally{this._busy=!1}}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}willUpdate(e){e.has("_state")&&("ERROR"!==this._state&&(this._error=void 0),"PROVISION"===this._state?(this._ssids=void 0,this._busy=!0,this._client.scan().then(e=>{this._busy=!1,this._ssids=e,this._selectedSsid=e.length?0:-1},()=>{this._busy=!1,this._ssids=null,this._selectedSsid=-1})):this._provisionForce=!1,"INSTALL"===this._state&&(this._installConfirmed=!1,this._installState=void 0))}firstUpdated(e){super.firstUpdated(e),this._initialize()}updated(e){super.updated(e),e.has("_state")&&this.setAttribute("state",this._state),"PROVISION"===this._state&&(e.has("_selectedSsid")&&-1===this._selectedSsid?this._focusFormElement("ewt-textfield[name=ssid]"):e.has("_ssids")&&this._focusFormElement())}_focusFormElement(e="ewt-textfield, ewt-select"){const t=this.shadowRoot.querySelector(e);t&&t.updateComplete.then(()=>setTimeout(()=>t.focus(),100))}async _initialize(e=!1){if(null===this.port.readable||null===this.port.writable)return this._state="ERROR",void(this._error="Serial port is not readable/writable. Close any other application using it and try again.");try{this._manifest=JSON.parse(this.manifestPath)}catch{try{this._manifest=await(async e=>{const t=new URL(e,location.toString()).toString(),i=await fetch(t),n=await i.json();return"new_install_skip_erase"in n&&(console.warn('Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.'),n.new_install_skip_erase&&(n.new_install_prompt_erase=!0)),n})(this.manifestPath)}catch(e){return this._state="ERROR",void(this._error="Failed to download manifest")}}if(0===this._manifest.new_install_improv_wait_time)return void(this._client=null);const t=new Ri(this.port,this.logger);t.addEventListener("state-changed",()=>{this.requestUpdate()}),t.addEventListener("error-changed",()=>this.requestUpdate());try{const i=e?void 0!==this._manifest.new_install_improv_wait_time?1e3*this._manifest.new_install_improv_wait_time:1e4:1e3;this._info=await t.initialize(i),this._client=t,t.addEventListener("disconnect",this._handleDisconnect)}catch(e){this._info=void 0,e instanceof Si?(this._state="ERROR",this._error="Serial port is not ready. Close any other application using it and try again."):(this._client=null,this.logger.error("Improv initialization failed.",e))}}_startInstall(e){this._state="INSTALL",this._installErase=e,this._installConfirmed=!1,this._esp32s2ReconnectInProgress=!1}async _handleESP32S2Reconnect(){if(this._esp32s2ReconnectInProgress)return this.logger.log("ESP32-S2 reconnect already in progress, ignoring"),this._error="Reconnection failed. Please try again manually.",void(this._state="ERROR");this._esp32s2ReconnectInProgress=!0;try{try{await this.port.close()}catch{}try{await this.port.forget()}catch{}const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.port=e,this._installState=void 0,this._installConfirmed=!1,this._confirmInstall()}catch(e){if(this._esp32s2ReconnectInProgress=!1,"NotFoundError"===e.name)return void this.logger.log("User cancelled port selection");this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR"}}async _confirmInstall(){this._installConfirmed=!0,this._installState=void 0,this._esp32s2ReconnectInProgress=!1,this._client&&await this._closeClientWithoutEvents(this._client),this._client=void 0,null!=this.firmwareFile?new Blob([this.firmwareFile]).arrayBuffer().then(e=>this._flashFilebuffer(new Uint8Array(e))):Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,new Uint8Array(0),this.baudRate)}async _flashFilebuffer(e){Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,e,this.baudRate)}async _doProvision(){this._busy=!0,this._wasProvisioned=this._client.state===Ei.PROVISIONED;const e=-1===this._selectedSsid?this.shadowRoot.querySelector("ewt-textfield[name=ssid]").value:this._ssids[this._selectedSsid].name,t=this.shadowRoot.querySelector("ewt-textfield[name=password]").value;try{await this._client.provision(e,t)}catch(e){return}finally{this._busy=!1,this._provisionForce=!1}}async _handleClose(){this._client&&await this._closeClientWithoutEvents(this._client),((e,t,i,n)=>{n=n||{};const o=new CustomEvent(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed,detail:i});e.dispatchEvent(o)})(this,"closed"),this.parentNode.removeChild(this)}get _isSameFirmware(){var e;return!!this._info&&((null===(e=this.overrides)||void 0===e?void 0:e.checkSameFirmware)?this.overrides.checkSameFirmware(this._manifest,this._info):this._info.firmware===this._manifest.name)}get _isSameVersion(){return this._isSameFirmware&&this._info.version===this._manifest.version}async _closeClientWithoutEvents(e){e.removeEventListener("disconnect",this._handleDisconnect),await e.close()}}Dr.styles=[L,u`
|
|
1173
|
+
`,!1]}async _handleESP32S2ReconnectClick(){try{this._busy=!0,this.logger.log("Requesting new port selection...");const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.logger.log("New port selected, updating..."),this.port=e,this._state="PARTITIONS",await this._readPartitionTable()}catch(e){"NotFoundError"===e.name?(this.logger.log("Port selection cancelled"),this._state="DASHBOARD"):(this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR")}finally{this._busy=!1}}async _readPartitionTable(){this._busy=!0,this._partitions=void 0;try{this.logger.log("Reading partition table from 0x8000...");const{ESPLoader:e}=await Promise.resolve().then(function(){return Sr});let t=this.port,i=new e(t,{log:(e,...t)=>this.logger.log(e,...t),debug:(e,...t)=>{var i,n;return null===(n=(i=this.logger).debug)||void 0===n?void 0:n.call(i,e,...t)},error:(e,...t)=>this.logger.error(e,...t)});const n=async e=>{this.logger.log("ESP32-S2 USB reconnect event:",e.detail.message),await Tr(t),this.logger.log("Please select the new ESP32-S2 USB CDC port")};i.addEventListener("esp32s2-usb-reconnect",n,{once:!0}),this.logger.log("Initializing ESP loader...");try{await i.initialize(),this.logger.log("ESP loader initialized successfully")}catch(e){if(Cr(t)&&Rr(e))return this.logger.log("ESP32-S2 USB port changed - user needs to select new port"),await Tr(t),this._busy=!1,void(this._state="ESP32S2_RECONNECT");throw e}this.logger.log("Running stub...");const o=await i.runStub();if(this._espStub=o,this.baudRate){this.logger.log(`Setting baudrate to ${this.baudRate} for flash reading...`);try{await o.setBaudrate(this.baudRate),this.logger.log(`Baudrate set to ${this.baudRate}`)}catch(e){this.logger.log(`Failed to set baudrate: ${e.message}, continuing with default`)}}await V(500),this.logger.log("Reading flash data...");const r=function(e){const t=[];for(let i=0;i<e.length;i+=32){const n=zr(e.slice(i,i+32));if(null===n)break;t.push(n)}return t}(await o.readFlash(32768,4096));0===r.length?(this.logger.error("No valid partition table found"),this._partitions=[]):(this.logger.log(`Found ${r.length} partition(s)`),this._partitions=r)}catch(e){this.logger.error(`Failed to read partition table: ${e.message||e}`),"Port selection cancelled"===e.message?this._error="Port selection cancelled":this._error=`Failed to read partition table: ${e.message||e}. Try resetting your device.`,this._state="ERROR"}finally{this._busy=!1}}async _openFilesystem(e){try{if(this._busy=!0,this.logger.log(`Detecting filesystem type for partition "${e.name}"...`),!this._espStub)throw new Error("ESP stub not available. Please reconnect.");const t=await async function(e,t,i,n=console){try{const o=Math.min(8192,i),r=await e.readFlash(t,o);if(r.length<32)return n.log("Partition too small, assuming SPIFFS"),"spiffs";if(new TextDecoder("ascii",{fatal:!1}).decode(r).includes("littlefs"))return n.log('✓ LittleFS detected: Found "littlefs" signature'),"littlefs";const a=new DataView(r.buffer,r.byteOffset,r.byteLength),s=[4096,2048,1024,512];for(const e of s)if(r.length>=2*e)try{for(let t=0;t<Math.min(e,r.length-4);t+=4){const e=a.getUint32(t,!0),i=1023&e;if((e>>20&4095)<=2047&&i>0&&i<=1022&&t+i+4<=r.length)return n.log("✓ LittleFS detected: Found valid metadata structure"),"littlefs"}}catch(e){}for(let e=0;e<Math.min(4096,r.length-4);e+=4){const t=a.getUint32(e,!0);if(538182953===t||538314025===t)return n.log("✓ SPIFFS detected: Found SPIFFS magic number"),"spiffs"}return n.log("⚠ No clear filesystem signature found, assuming SPIFFS"),"spiffs"}catch(e){return n.error(`Failed to detect filesystem type: ${e.message||e}`),"spiffs"}}(this._espStub,e.offset,e.size,this.logger);this.logger.log(`Detected filesystem: ${t}`),"littlefs"===t?(this._selectedPartition=e,this._state="LITTLEFS"):"spiffs"===t?(this.logger.error("SPIFFS support not yet implemented. Use LittleFS partitions."),this._error="SPIFFS support not yet implemented",this._state="ERROR"):(this.logger.error("Unknown filesystem type. Cannot open partition."),this._error="Unknown filesystem type",this._state="ERROR")}catch(e){this.logger.error(`Failed to open filesystem: ${e.message||e}`),this._error=`Failed to open filesystem: ${e.message||e}`,this._state="ERROR"}finally{this._busy=!1}}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}willUpdate(e){e.has("_state")&&("ERROR"!==this._state&&(this._error=void 0),"PROVISION"===this._state?(this._ssids=void 0,this._busy=!0,this._client.scan().then(e=>{this._busy=!1,this._ssids=e,this._selectedSsid=e.length?0:-1},()=>{this._busy=!1,this._ssids=null,this._selectedSsid=-1})):this._provisionForce=!1,"INSTALL"===this._state&&(this._installConfirmed=!1,this._installState=void 0))}firstUpdated(e){super.firstUpdated(e),this._initialize()}updated(e){super.updated(e),e.has("_state")&&this.setAttribute("state",this._state),"PROVISION"===this._state&&(e.has("_selectedSsid")&&-1===this._selectedSsid?this._focusFormElement("ewt-textfield[name=ssid]"):e.has("_ssids")&&this._focusFormElement())}_focusFormElement(e="ewt-textfield, ewt-select"){const t=this.shadowRoot.querySelector(e);t&&t.updateComplete.then(()=>setTimeout(()=>t.focus(),100))}async _initialize(e=!1){if(null===this.port.readable||null===this.port.writable)return this._state="ERROR",void(this._error="Serial port is not readable/writable. Close any other application using it and try again.");try{this._manifest=JSON.parse(this.manifestPath)}catch{try{this._manifest=await(async e=>{const t=new URL(e,location.toString()).toString(),i=await fetch(t),n=await i.json();return"new_install_skip_erase"in n&&(console.warn('Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.'),n.new_install_skip_erase&&(n.new_install_prompt_erase=!0)),n})(this.manifestPath)}catch(e){return this._state="ERROR",void(this._error="Failed to download manifest")}}if(0===this._manifest.new_install_improv_wait_time)return void(this._client=null);const t=new Ri(this.port,this.logger);t.addEventListener("state-changed",()=>{this.requestUpdate()}),t.addEventListener("error-changed",()=>this.requestUpdate());try{const i=e?void 0!==this._manifest.new_install_improv_wait_time?1e3*this._manifest.new_install_improv_wait_time:1e4:1e3;this._info=await t.initialize(i),this._client=t,t.addEventListener("disconnect",this._handleDisconnect)}catch(e){this._info=void 0,e instanceof Si?(this._state="ERROR",this._error="Serial port is not ready. Close any other application using it and try again."):(this._client=null,this.logger.error("Improv initialization failed.",e))}}_startInstall(e){this._state="INSTALL",this._installErase=e,this._installConfirmed=!1,this._esp32s2ReconnectInProgress=!1}async _handleESP32S2Reconnect(){if(this._esp32s2ReconnectInProgress)return this.logger.log("ESP32-S2 reconnect already in progress, ignoring"),this._error="Reconnection failed. Please try again manually.",void(this._state="ERROR");this._esp32s2ReconnectInProgress=!0;try{try{await this.port.close()}catch{}try{await this.port.forget()}catch{}const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.port=e,this._installState=void 0,this._installConfirmed=!1,this._confirmInstall()}catch(e){if(this._esp32s2ReconnectInProgress=!1,"NotFoundError"===e.name)return void this.logger.log("User cancelled port selection");this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR"}}async _confirmInstall(){this._installConfirmed=!0,this._installState=void 0,this._esp32s2ReconnectInProgress=!1,this._client&&await this._closeClientWithoutEvents(this._client),this._client=void 0,null!=this.firmwareFile?new Blob([this.firmwareFile]).arrayBuffer().then(e=>this._flashFilebuffer(new Uint8Array(e))):Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,new Uint8Array(0),this.baudRate)}async _flashFilebuffer(e){Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,e,this.baudRate)}async _doProvision(){this._busy=!0,this._wasProvisioned=this._client.state===Ei.PROVISIONED;const e=-1===this._selectedSsid?this.shadowRoot.querySelector("ewt-textfield[name=ssid]").value:this._ssids[this._selectedSsid].name,t=this.shadowRoot.querySelector("ewt-textfield[name=password]").value;try{await this._client.provision(e,t)}catch(e){return}finally{this._busy=!1,this._provisionForce=!1}}async _handleClose(){this._client&&await this._closeClientWithoutEvents(this._client),((e,t,i,n)=>{n=n||{};const o=new CustomEvent(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed,detail:i});e.dispatchEvent(o)})(this,"closed"),this.parentNode.removeChild(this)}get _isSameFirmware(){var e;return!!this._info&&((null===(e=this.overrides)||void 0===e?void 0:e.checkSameFirmware)?this.overrides.checkSameFirmware(this._manifest,this._info):this._info.firmware===this._manifest.name)}get _isSameVersion(){return this._isSameFirmware&&this._info.version===this._manifest.version}async _closeClientWithoutEvents(e){e.removeEventListener("disconnect",this._handleDisconnect),await e.close()}}Dr.styles=[L,u`
|
|
1172
1174
|
:host {
|
|
1173
1175
|
--mdc-dialog-max-width: 390px;
|
|
1174
1176
|
}
|
|
@@ -68,13 +68,31 @@ export interface LittleFS {
|
|
|
68
68
|
readFile(path: string): Uint8Array;
|
|
69
69
|
/**
|
|
70
70
|
* Get the disk version of the mounted filesystem.
|
|
71
|
-
* @returns Version as 32-bit number (e.g., 0x00020000 for v2.0)
|
|
71
|
+
* @returns Version as 32-bit number (e.g., 0x00020000 for v2.0, 0x00020001 for v2.1)
|
|
72
72
|
*/
|
|
73
73
|
getDiskVersion(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Set the disk version for new filesystems.
|
|
76
|
+
* Must be called before formatting.
|
|
77
|
+
* @param version - Version as 32-bit number (use DISK_VERSION_2_0 or DISK_VERSION_2_1)
|
|
78
|
+
*/
|
|
79
|
+
setDiskVersion(version: number): void;
|
|
74
80
|
/**
|
|
75
81
|
* Get filesystem usage statistics.
|
|
76
82
|
*/
|
|
77
83
|
getUsage(): { capacityBytes: number; usedBytes: number; freeBytes: number };
|
|
84
|
+
/**
|
|
85
|
+
* Check if a file of given size can fit in the filesystem.
|
|
86
|
+
* @param path - File path (currently unused, reserved for future use)
|
|
87
|
+
* @param size - Size in bytes
|
|
88
|
+
* @returns true if the file can fit, false otherwise
|
|
89
|
+
*/
|
|
90
|
+
canFit(path: string, size: number): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Cleanup and unmount the filesystem.
|
|
93
|
+
* Should be called when done using the filesystem to free resources.
|
|
94
|
+
*/
|
|
95
|
+
cleanup(): void;
|
|
78
96
|
}
|
|
79
97
|
|
|
80
98
|
export declare class LittleFSError extends Error {
|
|
@@ -599,13 +599,3 @@ function createClient(Module, blockSize, blockCount) {
|
|
|
599
599
|
|
|
600
600
|
return client;
|
|
601
601
|
}
|
|
602
|
-
|
|
603
|
-
// Default export for CommonJS compatibility
|
|
604
|
-
export default {
|
|
605
|
-
createLittleFS,
|
|
606
|
-
createLittleFSFromImage,
|
|
607
|
-
DISK_VERSION_2_0,
|
|
608
|
-
DISK_VERSION_2_1,
|
|
609
|
-
LFS_NAME_MAX,
|
|
610
|
-
formatDiskVersion,
|
|
611
|
-
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=async t=>{let n;import("./install-dialog-
|
|
1
|
+
const e=async t=>{let n;import("./install-dialog-Bt_kBJJW.js");try{n=await navigator.serial.requestPort()}catch(n){return"NotFoundError"===n.name?void import("./index-t2Vsxqjj.js").then(n=>n.openNoPortPickedDialog(()=>e(t))):void alert(`Error: ${n.message}`)}if(!n)return;try{await n.open({baudRate:115200})}catch(e){return void alert(e.message)}const o=document.createElement("ewt-install-dialog");o.port=n,o.manifestPath=t.manifest||t.getAttribute("manifest"),o.overrides=t.overrides,o.firmwareFile=t.firmwareFile;const r=t.getAttribute("baud-rate");if(r){const e=parseInt(r,10);isNaN(e)||(o.baudRate=e)}else void 0!==t.baudRate&&(o.baudRate=t.baudRate);o.addEventListener("closed",()=>{n.close()},{once:!0}),document.body.appendChild(o)};class t extends HTMLElement{connectedCallback(){if(this.renderRoot)return;if(this.renderRoot=this.attachShadow({mode:"open"}),!t.isSupported||!t.isAllowed)return this.toggleAttribute("install-unsupported",!0),void(this.renderRoot.innerHTML=t.isAllowed?"<slot name='unsupported'>Your browser does not support installing things on ESP devices. Use Google Chrome or Microsoft Edge.</slot>":"<slot name='not-allowed'>You can only install ESP devices on HTTPS websites or on the localhost.</slot>");this.toggleAttribute("install-supported",!0);const n=document.createElement("slot");n.addEventListener("click",async t=>{t.preventDefault(),e(this)}),n.name="activate";const o=document.createElement("button");if(o.innerText="CONNECT",n.append(o),"adoptedStyleSheets"in Document.prototype&&"replaceSync"in CSSStyleSheet.prototype){const e=new CSSStyleSheet;e.replaceSync(t.style),this.renderRoot.adoptedStyleSheets=[e]}else{const e=document.createElement("style");e.innerText=t.style,this.renderRoot.append(e)}this.renderRoot.append(n)}}t.isSupported="serial"in navigator,t.isAllowed=window.isSecureContext,t.style='\n button {\n position: relative;\n cursor: pointer;\n font-size: 14px;\n padding: 8px 28px;\n color: var(--esp-tools-button-text-color, #fff);\n background-color: var(--esp-tools-button-color, #03a9f4);\n border: none;\n border-radius: 4px;\n box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.12), 0 1px 5px 0 rgba(0,0,0,.2);\n }\n button::before {\n content: " ";\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n opacity: 0.2;\n border-radius: 4px;\n }\n button:hover {\n box-shadow: 0 4px 8px 0 rgba(0,0,0,.14), 0 1px 7px 0 rgba(0,0,0,.12), 0 3px 1px -1px rgba(0,0,0,.2);\n }\n button:hover::before {\n background-color: rgba(255,255,255,.8);\n }\n button:focus {\n outline: none;\n }\n button:focus::before {\n background-color: white;\n }\n button:active::before {\n background-color: grey;\n }\n :host([active]) button {\n color: rgba(0, 0, 0, 0.38);\n background-color: rgba(0, 0, 0, 0.12);\n box-shadow: none;\n cursor: unset;\n pointer-events: none;\n }\n improv-wifi-launch-button {\n display: block;\n margin-top: 16px;\n }\n .hidden {\n display: none;\n }',customElements.define("esp-web-install-button",t);
|
|
@@ -301,7 +301,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
301
301
|
.mdc-floating-label {
|
|
302
302
|
line-height: 1.15em;
|
|
303
303
|
}
|
|
304
|
-
`],customElements.define("ewt-select",li);class ci extends Te{}ci.styles=[Ae],customElements.define("ewt-list-item",ci);let hi=null,mi=null;let pi=class extends x{constructor(){super(...arguments),this.logger=console,this._currentPath="/",this._files=[],this._fs=null,this._blockSize=4096,this._usage={capacityBytes:0,usedBytes:0,freeBytes:0},this._diskVersion="",this._busy=!1,this._selectedFile=null,this._flashProgress=0,this._isFlashing=!1}async connectedCallback(){super.connectedCallback(),await this._openFilesystem()}disconnectedCallback(){super.disconnectedCallback(),this._cleanup()}async _openFilesystem(){try{this._busy=!0,this.logger.log(`Reading LittleFS partition "${this.partition.name}" (${this._formatSize(this.partition.size)})...`);const e=await this.espStub.readFlash(this.partition.offset,this.partition.size);this.logger.log("Mounting LittleFS filesystem...");const{createLittleFSFromImage:t,formatDiskVersion:i}=await async function(){if(mi)return mi;if(!hi){const e=new URL(import.meta.url),t=e.href.substring(0,e.href.lastIndexOf("/")+1);hi=t+"wasm/littlefs/"}try{const e=hi+"index.js";return console.log("[LittleFS] Loading module from:",e),mi=await import(e),mi}catch(e){console.error("[LittleFS] Failed to load from calculated path:",hi,e);try{return mi=await import("./wasm/littlefs/index.js"),mi}catch(t){throw console.error("[LittleFS] Fallback import also failed:",t),new Error(`Failed to load LittleFS module: ${e}`)}}}(),n=[4096,2048,1024,512];let o=null,r=0;for(const i of n)try{const n={blockSize:i,blockCount:Math.floor(this.partition.size/i)};hi&&(n.wasmURL=new URL("littlefs.wasm",hi).href),o=await t(e,n),o.list("/"),r=i,this.logger.log(`Successfully mounted LittleFS with block size ${i}`);break}catch(e){o=null}if(!o)throw new Error("Failed to mount LittleFS with any block size");this._fs=o,this._blockSize=r;try{const e=o.getDiskVersion();this._diskVersion=i(e)}catch(e){this._diskVersion=""}this._refreshFiles(),this.logger.log("LittleFS filesystem opened successfully")}catch(e){this.logger.error(`Failed to open LittleFS: ${e.message||e}`),this.onClose&&this.onClose()}finally{this._busy=!1}}_refreshFiles(){if(this._fs)try{const e=this._fs.list("/"),t=this._estimateUsage(e),i=this.partition.size;this._usage={capacityBytes:i,usedBytes:t,freeBytes:i-t};const n=this._fs.list(this._currentPath);n.sort((e,t)=>"dir"===e.type&&"dir"!==t.type?-1:"dir"!==e.type&&"dir"===t.type?1:e.path.localeCompare(t.path)),this._files=n}catch(e){this.logger.error(`Failed to refresh file list: ${e.message||e}`)}}_estimateUsage(e){const t=this._blockSize||4096;let i=2*t;for(const n of e||[])if("dir"===n.type)i+=t;else{i+=Math.max(1,Math.ceil((n.size||0)/t))*t+t}return i}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}_navigateUp(){if("/"===this._currentPath||!this._currentPath)return;const e=this._currentPath.split("/").filter(Boolean);e.pop(),this._currentPath="/"+e.join("/"),"/"===this._currentPath||this._currentPath.endsWith("/")||(this._currentPath+="/"),this._refreshFiles()}_navigateTo(e){this._currentPath=e,this._refreshFiles()}async _uploadFile(){if(this._fs&&this._selectedFile)try{this._busy=!0,this.logger.log(`Uploading file "${this._selectedFile.name}"...`);const e=await this._selectedFile.arrayBuffer(),t=new Uint8Array(e);let i=this._currentPath;i.endsWith("/")||(i+="/"),i+=this._selectedFile.name;const n=i.split("/").filter(Boolean);if(n.length>1){let e="";for(let t=0;t<n.length-1;t++){e+=`/${n[t]}`;try{this._fs.mkdir(e)}catch(e){}}}"function"==typeof this._fs.writeFile?this._fs.writeFile(i,t):"function"==typeof this._fs.addFile&&this._fs.addFile(i,t);const o=this._fs.readFile(i);this.logger.log(`✓ File written: ${o.length} bytes at ${i}`);const r=this._selectedFile.name;this._selectedFile=null,this._refreshFiles(),this.logger.log(`File "${r}" uploaded successfully`)}catch(e){this.logger.error(`Failed to upload file: ${e.message||e}`)}finally{this._busy=!1}}_createFolder(){if(!this._fs)return;const e=prompt("Enter directory name:");if(e&&e.trim())try{let t=this._currentPath;t.endsWith("/")||(t+="/"),t+=e.trim(),this._fs.mkdir(t),this._refreshFiles(),this.logger.log(`Directory "${e}" created successfully`)}catch(e){this.logger.error(`Failed to create directory: ${e.message||e}`)}}async _downloadFile(e){if(this._fs)try{this.logger.log(`Downloading file "${e}"...`);const t=this._fs.readFile(e),i=e.split("/").filter(Boolean).pop()||"file.bin",n=new Blob([t],{type:"application/octet-stream"}),o=URL.createObjectURL(n),r=document.createElement("a");r.href=o,r.download=i,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o),this.logger.log(`File "${i}" downloaded successfully`)}catch(e){this.logger.error(`Failed to download file: ${e.message||e}`)}}_deleteFile(e,t){if(!this._fs)return;const i=e.split("/").filter(Boolean).pop()||e;if(confirm(`Delete ${t} "${i}"?`))try{"dir"===t?this._fs.delete(e,{recursive:!0}):this._fs.deleteFile(e),this._refreshFiles(),this.logger.log(`${"dir"===t?"Directory":"File"} "${i}" deleted successfully`)}catch(e){this.logger.error(`Failed to delete ${t}: ${e.message||e}`)}}async _backupImage(){if(this._fs)try{this.logger.log("Creating LittleFS backup image...");const e=this._fs.toImage(),t=`${this.partition.name}_littlefs_backup.bin`,i=new Blob([e],{type:"application/octet-stream"}),n=URL.createObjectURL(i),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n),this.logger.log(`LittleFS backup saved as "${t}"`)}catch(e){this.logger.error(`Failed to backup LittleFS: ${e.message||e}`)}}async _writeToFlash(){if(!this._fs)return;if(confirm(`Write modified LittleFS to flash?\n\nPartition: ${this.partition.name}\nOffset: 0x${this.partition.offset.toString(16)}\nSize: ${this._formatSize(this.partition.size)}\n\nThis will overwrite the current filesystem on the device!`))try{this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this.logger.log("Creating LittleFS image...");const e=this._fs.toImage();if(this.logger.log(`Image created: ${this._formatSize(e.length)}`),e.length>this.partition.size)return void this.logger.error(`Image size (${this._formatSize(e.length)}) exceeds partition size (${this._formatSize(this.partition.size)})`);this.logger.log(`Writing ${this._formatSize(e.length)} to partition "${this.partition.name}" at 0x${this.partition.offset.toString(16)}...`);const t=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);await this.espStub.flashData(t,(e,t)=>{const i=Math.floor(e/t*100);this._flashProgress=i
|
|
304
|
+
`],customElements.define("ewt-select",li);class ci extends Te{}ci.styles=[Ae],customElements.define("ewt-list-item",ci);let hi=null,mi=null;let pi=class extends x{constructor(){super(...arguments),this.logger=console,this._currentPath="/",this._files=[],this._fs=null,this._blockSize=4096,this._usage={capacityBytes:0,usedBytes:0,freeBytes:0},this._diskVersion="",this._busy=!1,this._selectedFile=null,this._flashProgress=0,this._isFlashing=!1,this._flashOperation=null}async connectedCallback(){super.connectedCallback(),this.logger.log("LittleFS Manager: connectedCallback called"),await this._openFilesystem()}disconnectedCallback(){super.disconnectedCallback(),this._cleanup()}async _openFilesystem(){try{if(this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this._flashOperation="reading",this.logger.log(`Reading LittleFS partition "${this.partition.name}" (${this._formatSize(this.partition.size)})...`),!this.espStub.IS_STUB)throw new Error("ESP stub loader is not running. Cannot read flash.");const e=await this.espStub.readFlash(this.partition.offset,this.partition.size,(e,t,i)=>{const n=Math.floor(t/i*100);this._flashProgress=n});if(0===e.length)throw new Error("Read 0 bytes from partition");this.logger.log("Mounting LittleFS filesystem...");const{createLittleFSFromImage:t,formatDiskVersion:i}=await async function(){if(mi)return mi;if(!hi){const e=new URL(import.meta.url),t=e.href.substring(0,e.href.lastIndexOf("/")+1);hi=t+"wasm/littlefs/"}try{const e=hi+"index.js";return console.log("[LittleFS] Loading module from:",e),mi=await import(e),mi}catch(e){console.error("[LittleFS] Failed to load from calculated path:",hi,e);try{return mi=await import("./wasm/littlefs/index.js"),mi}catch(t){throw console.error("[LittleFS] Fallback import also failed:",t),new Error(`Failed to load LittleFS module: ${e}`)}}}(),n=[4096,2048,1024,512];let o=null,r=0;for(const i of n)try{const n={blockSize:i,blockCount:Math.floor(this.partition.size/i)};hi&&(n.wasmURL=new URL("littlefs.wasm",hi).href),o=await t(e,n),o.list("/"),r=i,this.logger.log(`Successfully mounted LittleFS with block size ${i}`);break}catch(e){o=null}if(!o)throw new Error("Failed to mount LittleFS with any block size");this._fs=o,this._blockSize=r;try{const e=o.getDiskVersion();this._diskVersion=e&&0!==e?i(e):"Unknown"}catch(e){this._diskVersion="Unknown"}this._refreshFiles(),this.logger.log("LittleFS filesystem opened successfully")}catch(e){this.logger.error(`Failed to open LittleFS: ${e.message||e}`),this.onClose&&this.onClose()}finally{this._busy=!1,this._isFlashing=!1,this._flashProgress=0,this._flashOperation=null}}_refreshFiles(){if(this._fs)try{const e=this._fs.list("/"),t=this._estimateUsage(e),i=this.partition.size;this._usage={capacityBytes:i,usedBytes:t,freeBytes:i-t};const n=this._fs.list(this._currentPath);n.sort((e,t)=>"dir"===e.type&&"dir"!==t.type?-1:"dir"!==e.type&&"dir"===t.type?1:e.path.localeCompare(t.path)),this._files=n}catch(e){this.logger.error(`Failed to refresh file list: ${e.message||e}`),this._files=[]}}_estimateUsage(e){const t=this._blockSize||4096;let i=2*t;for(const n of e||[])if("dir"===n.type)i+=t;else{i+=Math.max(1,Math.ceil((n.size||0)/t))*t+t}return i}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}_navigateUp(){if("/"===this._currentPath||!this._currentPath)return;const e=this._currentPath.split("/").filter(Boolean);e.pop(),this._currentPath="/"+e.join("/"),"/"===this._currentPath||this._currentPath.endsWith("/")||(this._currentPath+="/"),this._refreshFiles()}_navigateTo(e){this._currentPath=e,this._refreshFiles()}async _uploadFile(){if(this._fs&&this._selectedFile)try{this._busy=!0,this.logger.log(`Uploading file "${this._selectedFile.name}"...`);const e=await this._selectedFile.arrayBuffer(),t=new Uint8Array(e);let i=this._currentPath;i.endsWith("/")||(i+="/"),i+=this._selectedFile.name;const n=i.split("/").filter(Boolean);if(n.length>1){let e="";for(let t=0;t<n.length-1;t++){e+=`/${n[t]}`;try{this._fs.mkdir(e)}catch(e){}}}"function"==typeof this._fs.writeFile?this._fs.writeFile(i,t):"function"==typeof this._fs.addFile&&this._fs.addFile(i,t);const o=this._fs.readFile(i);this.logger.log(`✓ File written: ${o.length} bytes at ${i}`);const r=this._selectedFile.name;this._selectedFile=null,this._refreshFiles(),this.logger.log(`File "${r}" uploaded successfully`)}catch(e){this.logger.error(`Failed to upload file: ${e.message||e}`)}finally{this._busy=!1}}_createFolder(){if(!this._fs)return;const e=prompt("Enter directory name:");if(e&&e.trim())try{let t=this._currentPath;t.endsWith("/")||(t+="/"),t+=e.trim(),this._fs.mkdir(t),this._refreshFiles(),this.logger.log(`Directory "${e}" created successfully`)}catch(e){this.logger.error(`Failed to create directory: ${e.message||e}`)}}async _downloadFile(e){if(this._fs)try{this.logger.log(`Downloading file "${e}"...`);const t=this._fs.readFile(e),i=e.split("/").filter(Boolean).pop()||"file.bin",n=new Blob([t],{type:"application/octet-stream"}),o=URL.createObjectURL(n),r=document.createElement("a");r.href=o,r.download=i,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(o),this.logger.log(`File "${i}" downloaded successfully`)}catch(e){this.logger.error(`Failed to download file: ${e.message||e}`)}}_deleteFile(e,t){if(!this._fs)return;const i=e.split("/").filter(Boolean).pop()||e;if(confirm(`Delete ${t} "${i}"?`))try{"dir"===t?this._fs.delete(e,{recursive:!0}):this._fs.deleteFile(e),this._refreshFiles(),this.logger.log(`${"dir"===t?"Directory":"File"} "${i}" deleted successfully`)}catch(e){this.logger.error(`Failed to delete ${t}: ${e.message||e}`)}}async _backupImage(){if(this._fs)try{this.logger.log("Creating LittleFS backup image...");const e=this._fs.toImage(),t=`${this.partition.name}_littlefs_backup.bin`,i=new Blob([e],{type:"application/octet-stream"}),n=URL.createObjectURL(i),o=document.createElement("a");o.href=n,o.download=t,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n),this.logger.log(`LittleFS backup saved as "${t}"`)}catch(e){this.logger.error(`Failed to backup LittleFS: ${e.message||e}`)}}async _writeToFlash(){if(!this._fs)return;if(confirm(`Write modified LittleFS to flash?\n\nPartition: ${this.partition.name}\nOffset: 0x${this.partition.offset.toString(16)}\nSize: ${this._formatSize(this.partition.size)}\n\nThis will overwrite the current filesystem on the device!`))try{this._busy=!0,this._isFlashing=!0,this._flashProgress=0,this._flashOperation="writing",this.logger.log("Creating LittleFS image...");const e=this._fs.toImage();if(this.logger.log(`Image created: ${this._formatSize(e.length)}`),e.length>this.partition.size)return void this.logger.error(`Image size (${this._formatSize(e.length)}) exceeds partition size (${this._formatSize(this.partition.size)})`);this.logger.log(`Writing ${this._formatSize(e.length)} to partition "${this.partition.name}" at 0x${this.partition.offset.toString(16)}...`);const t=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);await this.espStub.flashData(t,(e,t)=>{const i=Math.floor(e/t*100);this._flashProgress=i},this.partition.offset),this.logger.log("✓ LittleFS successfully written to flash!"),this.logger.log("To use the new filesystem, reset your device.")}catch(e){this.logger.error(`Failed to write LittleFS to flash: ${e.message||e}`)}finally{this._busy=!1,this._isFlashing=!1,this._flashProgress=0,this._flashOperation=null}}_cleanup(){this._fs&&(this._fs=null)}_handleFileSelect(e){var t;const i=e.target;this._selectedFile=(null===(t=i.files)||void 0===t?void 0:t[0])||null}render(){const e=Math.round(this._usage.usedBytes/this._usage.capacityBytes*100);return h`
|
|
305
305
|
<div class="littlefs-manager">
|
|
306
306
|
<h3>LittleFS Filesystem Manager</h3>
|
|
307
307
|
|
|
@@ -321,7 +321,9 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
321
321
|
</div>
|
|
322
322
|
<div class="usage-text">
|
|
323
323
|
${this._isFlashing?h`<span class="flash-status">
|
|
324
|
-
⚡
|
|
324
|
+
⚡
|
|
325
|
+
${"reading"===this._flashOperation?"Reading from":"Writing to"}
|
|
326
|
+
flash: ${this._flashProgress}%
|
|
325
327
|
</span>`:h`<span
|
|
326
328
|
>Used: ${this._formatSize(this._usage.usedBytes)} /
|
|
327
329
|
${this._formatSize(this._usage.capacityBytes)}
|
|
@@ -658,7 +660,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
658
660
|
.danger {
|
|
659
661
|
--mdc-theme-primary: var(--improv-danger-color, #db4437);
|
|
660
662
|
}
|
|
661
|
-
`,i([n({type:Object})],pi.prototype,"partition",void 0),i([n({type:Object})],pi.prototype,"espStub",void 0),i([n({type:Function})],pi.prototype,"logger",void 0),i([n({type:Function})],pi.prototype,"onClose",void 0),i([s()],pi.prototype,"_currentPath",void 0),i([s()],pi.prototype,"_files",void 0),i([s()],pi.prototype,"_fs",void 0),i([s()],pi.prototype,"_blockSize",void 0),i([s()],pi.prototype,"_usage",void 0),i([s()],pi.prototype,"_diskVersion",void 0),i([s()],pi.prototype,"_busy",void 0),i([s()],pi.prototype,"_selectedFile",void 0),i([s()],pi.prototype,"_flashProgress",void 0),i([s()],pi.prototype,"_isFlashing",void 0),pi=i([y("ewt-littlefs-manager")],pi);class ui extends x{constructor(){super(...arguments),this.indeterminate=!1,this.progress=0,this.density=0,this.closed=!1}open(){this.closed=!1}close(){this.closed=!0}render(){const e={"mdc-circular-progress--closed":this.closed,"mdc-circular-progress--indeterminate":this.indeterminate},t=48+4*this.density,i={width:`${t}px`,height:`${t}px`};return h`
|
|
663
|
+
`,i([n({type:Object})],pi.prototype,"partition",void 0),i([n({type:Object})],pi.prototype,"espStub",void 0),i([n({type:Function})],pi.prototype,"logger",void 0),i([n({type:Function})],pi.prototype,"onClose",void 0),i([s()],pi.prototype,"_currentPath",void 0),i([s()],pi.prototype,"_files",void 0),i([s()],pi.prototype,"_fs",void 0),i([s()],pi.prototype,"_blockSize",void 0),i([s()],pi.prototype,"_usage",void 0),i([s()],pi.prototype,"_diskVersion",void 0),i([s()],pi.prototype,"_busy",void 0),i([s()],pi.prototype,"_selectedFile",void 0),i([s()],pi.prototype,"_flashProgress",void 0),i([s()],pi.prototype,"_isFlashing",void 0),i([s()],pi.prototype,"_flashOperation",void 0),pi=i([y("ewt-littlefs-manager")],pi);class ui extends x{constructor(){super(...arguments),this.indeterminate=!1,this.progress=0,this.density=0,this.closed=!1}open(){this.closed=!1}close(){this.closed=!0}render(){const e={"mdc-circular-progress--closed":this.closed,"mdc-circular-progress--indeterminate":this.indeterminate},t=48+4*this.density,i={width:`${t}px`,height:`${t}px`};return h`
|
|
662
664
|
<div
|
|
663
665
|
class="mdc-circular-progress ${m(e)}"
|
|
664
666
|
style="${F(i)}"
|
|
@@ -1168,7 +1170,7 @@ import{l as e,o as t,_ as i,n,B as o,a as r,c as a,t as s,f as d,g as l,R as c,x
|
|
|
1168
1170
|
label="Cancel"
|
|
1169
1171
|
@click=${()=>{this._state="DASHBOARD"}}
|
|
1170
1172
|
></ewt-button>
|
|
1171
|
-
`,!1]}async _handleESP32S2ReconnectClick(){try{this._busy=!0,this.logger.log("Requesting new port selection...");const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.logger.log("New port selected, updating..."),this.port=e,this._state="PARTITIONS",await this._readPartitionTable()}catch(e){"NotFoundError"===e.name?(this.logger.log("Port selection cancelled"),this._state="DASHBOARD"):(this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR")}finally{this._busy=!1}}async _readPartitionTable(){this._busy=!0,this._partitions=void 0;try{this.logger.log("Reading partition table from 0x8000...");const{ESPLoader:e}=await Promise.resolve().then(function(){return Sr});let t=this.port,i=new e(t,{log:(e,...t)=>this.logger.log(e,...t),debug:(e,...t)=>{var i,n;return null===(n=(i=this.logger).debug)||void 0===n?void 0:n.call(i,e,...t)},error:(e,...t)=>this.logger.error(e,...t)});const n=async e=>{this.logger.log("ESP32-S2 USB reconnect event:",e.detail.message),await Tr(t),this.logger.log("Please select the new ESP32-S2 USB CDC port")};i.addEventListener("esp32s2-usb-reconnect",n,{once:!0}),this.logger.log("Initializing ESP loader...");try{await i.initialize(),this.logger.log("ESP loader initialized successfully")}catch(e){if(Cr(t)&&Rr(e))return this.logger.log("ESP32-S2 USB port changed - user needs to select new port"),await Tr(t),this._busy=!1,void(this._state="ESP32S2_RECONNECT");throw e}this.logger.log("Running stub...");const o=await i.runStub();this._espStub=o,await V(500),this.logger.log("Reading flash data...");const r=function(e){const t=[];for(let i=0;i<e.length;i+=32){const n=zr(e.slice(i,i+32));if(null===n)break;t.push(n)}return t}(await o.readFlash(32768,4096));0===r.length?(this.logger.error("No valid partition table found"),this._partitions=[]):(this.logger.log(`Found ${r.length} partition(s)`),this._partitions=r)}catch(e){this.logger.error(`Failed to read partition table: ${e.message||e}`),"Port selection cancelled"===e.message?this._error="Port selection cancelled":this._error=`Failed to read partition table: ${e.message||e}. Try resetting your device.`,this._state="ERROR"}finally{this._busy=!1}}async _openFilesystem(e){try{if(this._busy=!0,this.logger.log(`Detecting filesystem type for partition "${e.name}"...`),!this._espStub)throw new Error("ESP stub not available. Please reconnect.");const t=await async function(e,t,i,n=console){try{const o=Math.min(8192,i),r=await e.readFlash(t,o);if(r.length<32)return n.log("Partition too small, assuming SPIFFS"),"spiffs";if(new TextDecoder("ascii",{fatal:!1}).decode(r).includes("littlefs"))return n.log('✓ LittleFS detected: Found "littlefs" signature'),"littlefs";const a=new DataView(r.buffer,r.byteOffset,r.byteLength),s=[4096,2048,1024,512];for(const e of s)if(r.length>=2*e)try{for(let t=0;t<Math.min(e,r.length-4);t+=4){const e=a.getUint32(t,!0),i=1023&e;if((e>>20&4095)<=2047&&i>0&&i<=1022&&t+i+4<=r.length)return n.log("✓ LittleFS detected: Found valid metadata structure"),"littlefs"}}catch(e){}for(let e=0;e<Math.min(4096,r.length-4);e+=4){const t=a.getUint32(e,!0);if(538182953===t||538314025===t)return n.log("✓ SPIFFS detected: Found SPIFFS magic number"),"spiffs"}return n.log("⚠ No clear filesystem signature found, assuming SPIFFS"),"spiffs"}catch(e){return n.error(`Failed to detect filesystem type: ${e.message||e}`),"spiffs"}}(this._espStub,e.offset,e.size,this.logger);this.logger.log(`Detected filesystem: ${t}`),"littlefs"===t?(this._selectedPartition=e,this._state="LITTLEFS"):"spiffs"===t?(this.logger.error("SPIFFS support not yet implemented. Use LittleFS partitions."),this._error="SPIFFS support not yet implemented",this._state="ERROR"):(this.logger.error("Unknown filesystem type. Cannot open partition."),this._error="Unknown filesystem type",this._state="ERROR")}catch(e){this.logger.error(`Failed to open filesystem: ${e.message||e}`),this._error=`Failed to open filesystem: ${e.message||e}`,this._state="ERROR"}finally{this._busy=!1}}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}willUpdate(e){e.has("_state")&&("ERROR"!==this._state&&(this._error=void 0),"PROVISION"===this._state?(this._ssids=void 0,this._busy=!0,this._client.scan().then(e=>{this._busy=!1,this._ssids=e,this._selectedSsid=e.length?0:-1},()=>{this._busy=!1,this._ssids=null,this._selectedSsid=-1})):this._provisionForce=!1,"INSTALL"===this._state&&(this._installConfirmed=!1,this._installState=void 0))}firstUpdated(e){super.firstUpdated(e),this._initialize()}updated(e){super.updated(e),e.has("_state")&&this.setAttribute("state",this._state),"PROVISION"===this._state&&(e.has("_selectedSsid")&&-1===this._selectedSsid?this._focusFormElement("ewt-textfield[name=ssid]"):e.has("_ssids")&&this._focusFormElement())}_focusFormElement(e="ewt-textfield, ewt-select"){const t=this.shadowRoot.querySelector(e);t&&t.updateComplete.then(()=>setTimeout(()=>t.focus(),100))}async _initialize(e=!1){if(null===this.port.readable||null===this.port.writable)return this._state="ERROR",void(this._error="Serial port is not readable/writable. Close any other application using it and try again.");try{this._manifest=JSON.parse(this.manifestPath)}catch{try{this._manifest=await(async e=>{const t=new URL(e,location.toString()).toString(),i=await fetch(t),n=await i.json();return"new_install_skip_erase"in n&&(console.warn('Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.'),n.new_install_skip_erase&&(n.new_install_prompt_erase=!0)),n})(this.manifestPath)}catch(e){return this._state="ERROR",void(this._error="Failed to download manifest")}}if(0===this._manifest.new_install_improv_wait_time)return void(this._client=null);const t=new Ri(this.port,this.logger);t.addEventListener("state-changed",()=>{this.requestUpdate()}),t.addEventListener("error-changed",()=>this.requestUpdate());try{const i=e?void 0!==this._manifest.new_install_improv_wait_time?1e3*this._manifest.new_install_improv_wait_time:1e4:1e3;this._info=await t.initialize(i),this._client=t,t.addEventListener("disconnect",this._handleDisconnect)}catch(e){this._info=void 0,e instanceof Si?(this._state="ERROR",this._error="Serial port is not ready. Close any other application using it and try again."):(this._client=null,this.logger.error("Improv initialization failed.",e))}}_startInstall(e){this._state="INSTALL",this._installErase=e,this._installConfirmed=!1,this._esp32s2ReconnectInProgress=!1}async _handleESP32S2Reconnect(){if(this._esp32s2ReconnectInProgress)return this.logger.log("ESP32-S2 reconnect already in progress, ignoring"),this._error="Reconnection failed. Please try again manually.",void(this._state="ERROR");this._esp32s2ReconnectInProgress=!0;try{try{await this.port.close()}catch{}try{await this.port.forget()}catch{}const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.port=e,this._installState=void 0,this._installConfirmed=!1,this._confirmInstall()}catch(e){if(this._esp32s2ReconnectInProgress=!1,"NotFoundError"===e.name)return void this.logger.log("User cancelled port selection");this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR"}}async _confirmInstall(){this._installConfirmed=!0,this._installState=void 0,this._esp32s2ReconnectInProgress=!1,this._client&&await this._closeClientWithoutEvents(this._client),this._client=void 0,null!=this.firmwareFile?new Blob([this.firmwareFile]).arrayBuffer().then(e=>this._flashFilebuffer(new Uint8Array(e))):Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,new Uint8Array(0),this.baudRate)}async _flashFilebuffer(e){Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,e,this.baudRate)}async _doProvision(){this._busy=!0,this._wasProvisioned=this._client.state===Ei.PROVISIONED;const e=-1===this._selectedSsid?this.shadowRoot.querySelector("ewt-textfield[name=ssid]").value:this._ssids[this._selectedSsid].name,t=this.shadowRoot.querySelector("ewt-textfield[name=password]").value;try{await this._client.provision(e,t)}catch(e){return}finally{this._busy=!1,this._provisionForce=!1}}async _handleClose(){this._client&&await this._closeClientWithoutEvents(this._client),((e,t,i,n)=>{n=n||{};const o=new CustomEvent(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed,detail:i});e.dispatchEvent(o)})(this,"closed"),this.parentNode.removeChild(this)}get _isSameFirmware(){var e;return!!this._info&&((null===(e=this.overrides)||void 0===e?void 0:e.checkSameFirmware)?this.overrides.checkSameFirmware(this._manifest,this._info):this._info.firmware===this._manifest.name)}get _isSameVersion(){return this._isSameFirmware&&this._info.version===this._manifest.version}async _closeClientWithoutEvents(e){e.removeEventListener("disconnect",this._handleDisconnect),await e.close()}}Dr.styles=[L,u`
|
|
1173
|
+
`,!1]}async _handleESP32S2ReconnectClick(){try{this._busy=!0,this.logger.log("Requesting new port selection...");const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.logger.log("New port selected, updating..."),this.port=e,this._state="PARTITIONS",await this._readPartitionTable()}catch(e){"NotFoundError"===e.name?(this.logger.log("Port selection cancelled"),this._state="DASHBOARD"):(this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR")}finally{this._busy=!1}}async _readPartitionTable(){this._busy=!0,this._partitions=void 0;try{this.logger.log("Reading partition table from 0x8000...");const{ESPLoader:e}=await Promise.resolve().then(function(){return Sr});let t=this.port,i=new e(t,{log:(e,...t)=>this.logger.log(e,...t),debug:(e,...t)=>{var i,n;return null===(n=(i=this.logger).debug)||void 0===n?void 0:n.call(i,e,...t)},error:(e,...t)=>this.logger.error(e,...t)});const n=async e=>{this.logger.log("ESP32-S2 USB reconnect event:",e.detail.message),await Tr(t),this.logger.log("Please select the new ESP32-S2 USB CDC port")};i.addEventListener("esp32s2-usb-reconnect",n,{once:!0}),this.logger.log("Initializing ESP loader...");try{await i.initialize(),this.logger.log("ESP loader initialized successfully")}catch(e){if(Cr(t)&&Rr(e))return this.logger.log("ESP32-S2 USB port changed - user needs to select new port"),await Tr(t),this._busy=!1,void(this._state="ESP32S2_RECONNECT");throw e}this.logger.log("Running stub...");const o=await i.runStub();if(this._espStub=o,this.baudRate){this.logger.log(`Setting baudrate to ${this.baudRate} for flash reading...`);try{await o.setBaudrate(this.baudRate),this.logger.log(`Baudrate set to ${this.baudRate}`)}catch(e){this.logger.log(`Failed to set baudrate: ${e.message}, continuing with default`)}}await V(500),this.logger.log("Reading flash data...");const r=function(e){const t=[];for(let i=0;i<e.length;i+=32){const n=zr(e.slice(i,i+32));if(null===n)break;t.push(n)}return t}(await o.readFlash(32768,4096));0===r.length?(this.logger.error("No valid partition table found"),this._partitions=[]):(this.logger.log(`Found ${r.length} partition(s)`),this._partitions=r)}catch(e){this.logger.error(`Failed to read partition table: ${e.message||e}`),"Port selection cancelled"===e.message?this._error="Port selection cancelled":this._error=`Failed to read partition table: ${e.message||e}. Try resetting your device.`,this._state="ERROR"}finally{this._busy=!1}}async _openFilesystem(e){try{if(this._busy=!0,this.logger.log(`Detecting filesystem type for partition "${e.name}"...`),!this._espStub)throw new Error("ESP stub not available. Please reconnect.");const t=await async function(e,t,i,n=console){try{const o=Math.min(8192,i),r=await e.readFlash(t,o);if(r.length<32)return n.log("Partition too small, assuming SPIFFS"),"spiffs";if(new TextDecoder("ascii",{fatal:!1}).decode(r).includes("littlefs"))return n.log('✓ LittleFS detected: Found "littlefs" signature'),"littlefs";const a=new DataView(r.buffer,r.byteOffset,r.byteLength),s=[4096,2048,1024,512];for(const e of s)if(r.length>=2*e)try{for(let t=0;t<Math.min(e,r.length-4);t+=4){const e=a.getUint32(t,!0),i=1023&e;if((e>>20&4095)<=2047&&i>0&&i<=1022&&t+i+4<=r.length)return n.log("✓ LittleFS detected: Found valid metadata structure"),"littlefs"}}catch(e){}for(let e=0;e<Math.min(4096,r.length-4);e+=4){const t=a.getUint32(e,!0);if(538182953===t||538314025===t)return n.log("✓ SPIFFS detected: Found SPIFFS magic number"),"spiffs"}return n.log("⚠ No clear filesystem signature found, assuming SPIFFS"),"spiffs"}catch(e){return n.error(`Failed to detect filesystem type: ${e.message||e}`),"spiffs"}}(this._espStub,e.offset,e.size,this.logger);this.logger.log(`Detected filesystem: ${t}`),"littlefs"===t?(this._selectedPartition=e,this._state="LITTLEFS"):"spiffs"===t?(this.logger.error("SPIFFS support not yet implemented. Use LittleFS partitions."),this._error="SPIFFS support not yet implemented",this._state="ERROR"):(this.logger.error("Unknown filesystem type. Cannot open partition."),this._error="Unknown filesystem type",this._state="ERROR")}catch(e){this.logger.error(`Failed to open filesystem: ${e.message||e}`),this._error=`Failed to open filesystem: ${e.message||e}`,this._state="ERROR"}finally{this._busy=!1}}_formatSize(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(2)} KB`:`${(e/1048576).toFixed(2)} MB`}willUpdate(e){e.has("_state")&&("ERROR"!==this._state&&(this._error=void 0),"PROVISION"===this._state?(this._ssids=void 0,this._busy=!0,this._client.scan().then(e=>{this._busy=!1,this._ssids=e,this._selectedSsid=e.length?0:-1},()=>{this._busy=!1,this._ssids=null,this._selectedSsid=-1})):this._provisionForce=!1,"INSTALL"===this._state&&(this._installConfirmed=!1,this._installState=void 0))}firstUpdated(e){super.firstUpdated(e),this._initialize()}updated(e){super.updated(e),e.has("_state")&&this.setAttribute("state",this._state),"PROVISION"===this._state&&(e.has("_selectedSsid")&&-1===this._selectedSsid?this._focusFormElement("ewt-textfield[name=ssid]"):e.has("_ssids")&&this._focusFormElement())}_focusFormElement(e="ewt-textfield, ewt-select"){const t=this.shadowRoot.querySelector(e);t&&t.updateComplete.then(()=>setTimeout(()=>t.focus(),100))}async _initialize(e=!1){if(null===this.port.readable||null===this.port.writable)return this._state="ERROR",void(this._error="Serial port is not readable/writable. Close any other application using it and try again.");try{this._manifest=JSON.parse(this.manifestPath)}catch{try{this._manifest=await(async e=>{const t=new URL(e,location.toString()).toString(),i=await fetch(t),n=await i.json();return"new_install_skip_erase"in n&&(console.warn('Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.'),n.new_install_skip_erase&&(n.new_install_prompt_erase=!0)),n})(this.manifestPath)}catch(e){return this._state="ERROR",void(this._error="Failed to download manifest")}}if(0===this._manifest.new_install_improv_wait_time)return void(this._client=null);const t=new Ri(this.port,this.logger);t.addEventListener("state-changed",()=>{this.requestUpdate()}),t.addEventListener("error-changed",()=>this.requestUpdate());try{const i=e?void 0!==this._manifest.new_install_improv_wait_time?1e3*this._manifest.new_install_improv_wait_time:1e4:1e3;this._info=await t.initialize(i),this._client=t,t.addEventListener("disconnect",this._handleDisconnect)}catch(e){this._info=void 0,e instanceof Si?(this._state="ERROR",this._error="Serial port is not ready. Close any other application using it and try again."):(this._client=null,this.logger.error("Improv initialization failed.",e))}}_startInstall(e){this._state="INSTALL",this._installErase=e,this._installConfirmed=!1,this._esp32s2ReconnectInProgress=!1}async _handleESP32S2Reconnect(){if(this._esp32s2ReconnectInProgress)return this.logger.log("ESP32-S2 reconnect already in progress, ignoring"),this._error="Reconnection failed. Please try again manually.",void(this._state="ERROR");this._esp32s2ReconnectInProgress=!0;try{try{await this.port.close()}catch{}try{await this.port.forget()}catch{}const e=await navigator.serial.requestPort();await e.open({baudRate:115200}),this.port=e,this._installState=void 0,this._installConfirmed=!1,this._confirmInstall()}catch(e){if(this._esp32s2ReconnectInProgress=!1,"NotFoundError"===e.name)return void this.logger.log("User cancelled port selection");this._error=`Failed to reconnect: ${e.message}`,this._state="ERROR"}}async _confirmInstall(){this._installConfirmed=!0,this._installState=void 0,this._esp32s2ReconnectInProgress=!1,this._client&&await this._closeClientWithoutEvents(this._client),this._client=void 0,null!=this.firmwareFile?new Blob([this.firmwareFile]).arrayBuffer().then(e=>this._flashFilebuffer(new Uint8Array(e))):Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,new Uint8Array(0),this.baudRate)}async _flashFilebuffer(e){Ar(e=>{this._installState=e,"finished"===e.state&&V(100).then(()=>this._initialize(!0)).then(()=>this.requestUpdate())},this.port,this.logger,this.manifestPath,this._installErase,e,this.baudRate)}async _doProvision(){this._busy=!0,this._wasProvisioned=this._client.state===Ei.PROVISIONED;const e=-1===this._selectedSsid?this.shadowRoot.querySelector("ewt-textfield[name=ssid]").value:this._ssids[this._selectedSsid].name,t=this.shadowRoot.querySelector("ewt-textfield[name=password]").value;try{await this._client.provision(e,t)}catch(e){return}finally{this._busy=!1,this._provisionForce=!1}}async _handleClose(){this._client&&await this._closeClientWithoutEvents(this._client),((e,t,i,n)=>{n=n||{};const o=new CustomEvent(t,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed,detail:i});e.dispatchEvent(o)})(this,"closed"),this.parentNode.removeChild(this)}get _isSameFirmware(){var e;return!!this._info&&((null===(e=this.overrides)||void 0===e?void 0:e.checkSameFirmware)?this.overrides.checkSameFirmware(this._manifest,this._info):this._info.firmware===this._manifest.name)}get _isSameVersion(){return this._isSameFirmware&&this._info.version===this._manifest.version}async _closeClientWithoutEvents(e){e.removeEventListener("disconnect",this._handleDisconnect),await e.close()}}Dr.styles=[L,u`
|
|
1172
1174
|
:host {
|
|
1173
1175
|
--mdc-dialog-max-width: 390px;
|
|
1174
1176
|
}
|
|
@@ -68,13 +68,31 @@ export interface LittleFS {
|
|
|
68
68
|
readFile(path: string): Uint8Array;
|
|
69
69
|
/**
|
|
70
70
|
* Get the disk version of the mounted filesystem.
|
|
71
|
-
* @returns Version as 32-bit number (e.g., 0x00020000 for v2.0)
|
|
71
|
+
* @returns Version as 32-bit number (e.g., 0x00020000 for v2.0, 0x00020001 for v2.1)
|
|
72
72
|
*/
|
|
73
73
|
getDiskVersion(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Set the disk version for new filesystems.
|
|
76
|
+
* Must be called before formatting.
|
|
77
|
+
* @param version - Version as 32-bit number (use DISK_VERSION_2_0 or DISK_VERSION_2_1)
|
|
78
|
+
*/
|
|
79
|
+
setDiskVersion(version: number): void;
|
|
74
80
|
/**
|
|
75
81
|
* Get filesystem usage statistics.
|
|
76
82
|
*/
|
|
77
83
|
getUsage(): { capacityBytes: number; usedBytes: number; freeBytes: number };
|
|
84
|
+
/**
|
|
85
|
+
* Check if a file of given size can fit in the filesystem.
|
|
86
|
+
* @param path - File path (currently unused, reserved for future use)
|
|
87
|
+
* @param size - Size in bytes
|
|
88
|
+
* @returns true if the file can fit, false otherwise
|
|
89
|
+
*/
|
|
90
|
+
canFit(path: string, size: number): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Cleanup and unmount the filesystem.
|
|
93
|
+
* Should be called when done using the filesystem to free resources.
|
|
94
|
+
*/
|
|
95
|
+
cleanup(): void;
|
|
78
96
|
}
|
|
79
97
|
|
|
80
98
|
export declare class LittleFSError extends Error {
|
|
@@ -599,13 +599,3 @@ function createClient(Module, blockSize, blockCount) {
|
|
|
599
599
|
|
|
600
600
|
return client;
|
|
601
601
|
}
|
|
602
|
-
|
|
603
|
-
// Default export for CommonJS compatibility
|
|
604
|
-
export default {
|
|
605
|
-
createLittleFS,
|
|
606
|
-
createLittleFSFromImage,
|
|
607
|
-
DISK_VERSION_2_0,
|
|
608
|
-
DISK_VERSION_2_1,
|
|
609
|
-
LFS_NAME_MAX,
|
|
610
|
-
formatDiskVersion,
|
|
611
|
-
};
|