tasmota-esp-web-tools 12.1.1 → 12.2.0
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/README.md +30 -0
- package/dist/components/ew-littlefs-manager.js +7 -9
- package/dist/connect.js +2 -1
- package/dist/const.d.ts +2 -0
- package/dist/flash.d.ts +1 -1
- package/dist/flash.js +82 -8
- package/dist/install-dialog.d.ts +2 -0
- package/dist/install-dialog.js +87 -20
- package/dist/util/cors-proxy.js +3 -3
- package/dist/util/line-break-transformer.js +3 -1
- package/dist/util/manifest.js +1 -0
- package/dist/util/partition.js +1 -1
- package/dist/util/timestamp-transformer.js +23 -1
- package/dist/web/{index-DUmRg0Cu.js → index-B5owDZF7.js} +1 -1
- package/dist/web/install-button.js +1 -1
- package/{js/modules/install-dialog-HRJeEoXW.js → dist/web/install-dialog-B3AVqM93.js} +114 -104
- package/{js/modules/styles-D69gtq6_.js → dist/web/styles-D4zXLz1s.js} +6 -9
- package/eslint.config.js +15 -0
- package/js/modules/{index-DUmRg0Cu.js → index-B5owDZF7.js} +1 -1
- package/js/modules/install-button.js +1 -1
- package/{dist/web/install-dialog-HRJeEoXW.js → js/modules/install-dialog-B3AVqM93.js} +114 -104
- package/{dist/web/styles-D69gtq6_.js → js/modules/styles-D4zXLz1s.js} +6 -9
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -108,6 +108,36 @@ The `chipVariant` field is optional. If omitted, the build will match any varian
|
|
|
108
108
|
|
|
109
109
|
See [manifest-example-p4-variants.json](manifest-example-p4-variants.json) for a complete example.
|
|
110
110
|
|
|
111
|
+
## Flash Size Support
|
|
112
|
+
|
|
113
|
+
For chips with different flash sizes, you can specify `flashSizeMB` to target specific hardware configurations. This is useful for ESP32-S3 modules that come with various flash sizes.
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"name": "My Firmware",
|
|
118
|
+
"builds": [
|
|
119
|
+
{
|
|
120
|
+
"chipFamily": "ESP32-S3",
|
|
121
|
+
"flashSizeMB": 16,
|
|
122
|
+
"parts": [{ "path": "s3-16mb.bin", "offset": 0 }]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"chipFamily": "ESP32-S3",
|
|
126
|
+
"flashSizeMB": 4,
|
|
127
|
+
"parts": [{ "path": "s3-4mb.bin", "offset": 0 }]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"chipFamily": "ESP32-S3",
|
|
131
|
+
"parts": [{ "path": "s3-generic.bin", "offset": 0 }]
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
A device with ESP32-S3 and 16MB flash gets the first build, 4MB gets the second, and any other ESP32-S3 falls back to the third.
|
|
138
|
+
|
|
139
|
+
The `flashSizeMB` field is **optional**. If omitted, the build will match any flash size. Builds with matching `flashSizeMB` are preferred over builds without it (most-specific-matching algorithm).
|
|
140
|
+
|
|
111
141
|
## Performance
|
|
112
142
|
|
|
113
143
|
ESP Web Tools supports configurable baud rates for flashing. By default, it uses 115200 baud for maximum compatibility. You can increase the baud rate for significantly faster flashing speeds.
|
|
@@ -20,11 +20,13 @@ async function loadLittleFS() {
|
|
|
20
20
|
try {
|
|
21
21
|
// Try to import from the calculated path
|
|
22
22
|
const indexUrl = _wasmBasePath + "index.js";
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
23
24
|
console.log("[LittleFS] Loading module from:", indexUrl);
|
|
24
25
|
_littleFSModule = await import(/* @vite-ignore */ indexUrl);
|
|
25
26
|
return _littleFSModule;
|
|
26
27
|
}
|
|
27
28
|
catch (err) {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
28
30
|
console.error("[LittleFS] Failed to load from calculated path:", _wasmBasePath, err);
|
|
29
31
|
// Fallback to relative import (for local development)
|
|
30
32
|
try {
|
|
@@ -32,6 +34,7 @@ async function loadLittleFS() {
|
|
|
32
34
|
return _littleFSModule;
|
|
33
35
|
}
|
|
34
36
|
catch (fallbackErr) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
35
38
|
console.error("[LittleFS] Fallback import also failed:", fallbackErr);
|
|
36
39
|
throw new Error(`Failed to load LittleFS module: ${err}`);
|
|
37
40
|
}
|
|
@@ -105,7 +108,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
105
108
|
this.logger.log(`Successfully mounted LittleFS with block size ${bs}`);
|
|
106
109
|
break;
|
|
107
110
|
}
|
|
108
|
-
catch (
|
|
111
|
+
catch (_err) {
|
|
109
112
|
// Try next block size
|
|
110
113
|
fs = null;
|
|
111
114
|
}
|
|
@@ -125,7 +128,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
125
128
|
this._diskVersion = "Unknown";
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
|
-
catch (
|
|
131
|
+
catch (_e) {
|
|
129
132
|
this._diskVersion = "Unknown";
|
|
130
133
|
}
|
|
131
134
|
this._refreshFiles();
|
|
@@ -238,7 +241,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
238
241
|
try {
|
|
239
242
|
this._fs.mkdir(built);
|
|
240
243
|
}
|
|
241
|
-
catch (
|
|
244
|
+
catch (_e) {
|
|
242
245
|
// Ignore if directory already exists
|
|
243
246
|
}
|
|
244
247
|
}
|
|
@@ -397,12 +400,7 @@ let EwtLittleFSManager = class EwtLittleFSManager extends LitElement {
|
|
|
397
400
|
}
|
|
398
401
|
_cleanup() {
|
|
399
402
|
if (this._fs) {
|
|
400
|
-
|
|
401
|
-
// Don't call destroy() - just let garbage collection handle it
|
|
402
|
-
}
|
|
403
|
-
catch (e) {
|
|
404
|
-
console.error("Error cleaning up LittleFS:", e);
|
|
405
|
-
}
|
|
403
|
+
// Don't call destroy() - just let garbage collection handle it
|
|
406
404
|
this._fs = null;
|
|
407
405
|
}
|
|
408
406
|
}
|
package/dist/connect.js
CHANGED
|
@@ -50,6 +50,7 @@ export const connect = async (button) => {
|
|
|
50
50
|
esploader = await esptoolConnect({
|
|
51
51
|
log: () => { }, // Silent logger for connection
|
|
52
52
|
debug: () => { },
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
53
54
|
error: (msg) => console.error(msg),
|
|
54
55
|
});
|
|
55
56
|
}
|
|
@@ -85,7 +86,7 @@ export const connect = async (button) => {
|
|
|
85
86
|
try {
|
|
86
87
|
await esploader.disconnect();
|
|
87
88
|
}
|
|
88
|
-
catch (
|
|
89
|
+
catch (_err) {
|
|
89
90
|
// Ignore disconnect errors
|
|
90
91
|
}
|
|
91
92
|
}, { once: true });
|
package/dist/const.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface Logger {
|
|
|
6
6
|
export interface Build {
|
|
7
7
|
chipFamily: "ESP32" | "ESP8266" | "ESP32-S2" | "ESP32-S3" | "ESP32-C2" | "ESP32-C3" | "ESP32-C5" | "ESP32-C6" | "ESP32-C61" | "ESP32-H2" | "ESP32-P4";
|
|
8
8
|
chipVariant?: string;
|
|
9
|
+
flashSizeMB?: number;
|
|
9
10
|
parts: {
|
|
10
11
|
path: string;
|
|
11
12
|
offset: number;
|
|
@@ -29,6 +30,7 @@ export interface BaseFlashState {
|
|
|
29
30
|
build?: Build;
|
|
30
31
|
chipFamily?: Build["chipFamily"] | "Unknown Chip";
|
|
31
32
|
chipVariant?: string | null;
|
|
33
|
+
flashSize?: string;
|
|
32
34
|
}
|
|
33
35
|
export interface InitializingState extends BaseFlashState {
|
|
34
36
|
state: FlashStateType.INITIALIZING;
|
package/dist/flash.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Logger } from "tasmota-webserial-esptool";
|
|
2
2
|
import { FlashState } from "./const";
|
|
3
3
|
export declare const flash: (onEvent: (state: FlashState) => void, esploader: any, // ESPLoader instance from tasmota-webserial-esptool
|
|
4
|
-
logger: Logger, manifestPath: string, eraseFirst: boolean, firmwareBuffer: Uint8Array,
|
|
4
|
+
logger: Logger, manifestPath: string, eraseFirst: boolean, firmwareBuffer: Uint8Array, _baudRate?: number) => Promise<void>;
|
package/dist/flash.js
CHANGED
|
@@ -1,21 +1,77 @@
|
|
|
1
1
|
import { getChipFamilyName } from "./util/chip-family-name";
|
|
2
2
|
import { sleep } from "./util/sleep";
|
|
3
3
|
import { corsProxyFetch } from "./util/cors-proxy";
|
|
4
|
+
/**
|
|
5
|
+
* Parse flash size string (e.g., "4MB", "8MB", "16MB") to megabytes number
|
|
6
|
+
*/
|
|
7
|
+
function parseFlashSizeToMB(flashSize) {
|
|
8
|
+
if (!flashSize)
|
|
9
|
+
return undefined;
|
|
10
|
+
const match = flashSize.match(/^(\d+)(MB|GB)$/);
|
|
11
|
+
if (!match)
|
|
12
|
+
return undefined;
|
|
13
|
+
const size = parseInt(match[1], 10);
|
|
14
|
+
const unit = match[2];
|
|
15
|
+
if (unit === "GB")
|
|
16
|
+
return size * 1024;
|
|
17
|
+
return size;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Select the best build using most-specific-matching algorithm
|
|
21
|
+
* - Builds with matching flashSizeMB are preferred
|
|
22
|
+
* - Among builds with same specificity, first one wins
|
|
23
|
+
* - Builds without flashSizeMB are fallback options
|
|
24
|
+
*/
|
|
25
|
+
function selectBestBuild(builds, detectedFlashSizeMB) {
|
|
26
|
+
if (builds.length === 0)
|
|
27
|
+
return undefined;
|
|
28
|
+
// Score builds: higher score = more specific match
|
|
29
|
+
let bestBuild;
|
|
30
|
+
let bestScore = -Infinity;
|
|
31
|
+
for (const build of builds) {
|
|
32
|
+
let score = 0;
|
|
33
|
+
// Flash size match gives highest priority
|
|
34
|
+
if (build.flashSizeMB !== undefined && detectedFlashSizeMB !== undefined) {
|
|
35
|
+
if (build.flashSizeMB === detectedFlashSizeMB) {
|
|
36
|
+
score += 100; // Exact flash size match
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
score -= 1; // Penalize non-matching specific builds
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (build.flashSizeMB !== undefined) {
|
|
43
|
+
// flashSizeMB is defined but detectedFlashSizeMB is undefined
|
|
44
|
+
score -= 1; // Penalize non-matching specific builds
|
|
45
|
+
}
|
|
46
|
+
// Generic builds (flashSizeMB undefined) stay at score 0
|
|
47
|
+
// Prefer this build if it has higher score
|
|
48
|
+
// If same score, keep the first one (stable selection)
|
|
49
|
+
if (score > bestScore) {
|
|
50
|
+
bestScore = score;
|
|
51
|
+
bestBuild = build;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return bestScore >= 0 ? bestBuild : undefined;
|
|
55
|
+
}
|
|
4
56
|
export const flash = async (onEvent, esploader, // ESPLoader instance from tasmota-webserial-esptool
|
|
5
|
-
logger, manifestPath, eraseFirst, firmwareBuffer,
|
|
57
|
+
logger, manifestPath, eraseFirst, firmwareBuffer, _baudRate) => {
|
|
6
58
|
let manifest;
|
|
7
59
|
let build;
|
|
60
|
+
// eslint-disable-next-line prefer-const
|
|
8
61
|
let chipFamily;
|
|
9
62
|
let chipVariant = null;
|
|
63
|
+
// eslint-disable-next-line prefer-const
|
|
64
|
+
let flashSize;
|
|
10
65
|
const fireStateEvent = (stateUpdate) => onEvent({
|
|
11
66
|
...stateUpdate,
|
|
12
67
|
manifest,
|
|
13
68
|
build,
|
|
14
69
|
chipFamily,
|
|
15
70
|
chipVariant,
|
|
71
|
+
flashSize,
|
|
16
72
|
});
|
|
17
|
-
|
|
18
|
-
|
|
73
|
+
let manifestProm = null;
|
|
74
|
+
let manifestURL = "";
|
|
19
75
|
try {
|
|
20
76
|
manifestProm = JSON.parse(manifestPath);
|
|
21
77
|
}
|
|
@@ -51,9 +107,20 @@ logger, manifestPath, eraseFirst, firmwareBuffer, baudRate) => {
|
|
|
51
107
|
}
|
|
52
108
|
chipFamily = getChipFamilyName(esploader);
|
|
53
109
|
chipVariant = esploader.chipVariant;
|
|
110
|
+
// Detect flash size if not already detected
|
|
111
|
+
if (!esploader.flashSize && esploader.detectFlashSize) {
|
|
112
|
+
try {
|
|
113
|
+
await esploader.detectFlashSize();
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
logger.debug("Failed to detect flash size:", err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
flashSize = esploader.flashSize; // e.g., "4MB", "8MB"
|
|
120
|
+
const flashSizeMB = flashSize ? parseFlashSizeToMB(flashSize) : undefined;
|
|
54
121
|
fireStateEvent({
|
|
55
122
|
state: "initializing" /* FlashStateType.INITIALIZING */,
|
|
56
|
-
message: `Initialized. Found ${chipFamily}${chipVariant ? ` (${chipVariant})` : ""}`,
|
|
123
|
+
message: `Initialized. Found ${chipFamily}${chipVariant ? ` (${chipVariant})` : ""}${flashSize ? `, ${flashSize}` : ""}`,
|
|
57
124
|
details: { done: true },
|
|
58
125
|
});
|
|
59
126
|
fireStateEvent({
|
|
@@ -73,17 +140,24 @@ logger, manifestPath, eraseFirst, firmwareBuffer, baudRate) => {
|
|
|
73
140
|
await esploader.disconnect();
|
|
74
141
|
return;
|
|
75
142
|
}
|
|
76
|
-
|
|
77
|
-
|
|
143
|
+
// Filter builds by chipFamily and chipVariant
|
|
144
|
+
const compatibleBuilds = manifest.builds.filter((b) => {
|
|
78
145
|
if (b.chipFamily !== chipFamily) {
|
|
79
146
|
return false;
|
|
80
147
|
}
|
|
81
|
-
// If build specifies chipVariant, it must match
|
|
82
148
|
if (b.chipVariant && b.chipVariant !== chipVariant) {
|
|
83
149
|
return false;
|
|
84
150
|
}
|
|
85
151
|
return true;
|
|
86
152
|
});
|
|
153
|
+
// Select the best build using most-specific-matching algorithm
|
|
154
|
+
// Prefer builds with more matching qualifiers (flashSizeMB)
|
|
155
|
+
const exactVariantBuilds = compatibleBuilds.filter((b) => b.chipVariant !== undefined && b.chipVariant === chipVariant);
|
|
156
|
+
const variantAgnosticBuilds = compatibleBuilds.filter((b) => b.chipVariant === undefined);
|
|
157
|
+
build = selectBestBuild(exactVariantBuilds, flashSizeMB);
|
|
158
|
+
if (!build) {
|
|
159
|
+
build = selectBestBuild(variantAgnosticBuilds, flashSizeMB);
|
|
160
|
+
}
|
|
87
161
|
if (!build) {
|
|
88
162
|
fireStateEvent({
|
|
89
163
|
state: "error" /* FlashStateType.ERROR */,
|
|
@@ -198,7 +272,7 @@ logger, manifestPath, eraseFirst, firmwareBuffer, baudRate) => {
|
|
|
198
272
|
for (let i = 0; i < build.parts.length; i++) {
|
|
199
273
|
const part = build.parts[i];
|
|
200
274
|
const data = files[i];
|
|
201
|
-
await espStub.flashData(data, (bytesWritten,
|
|
275
|
+
await espStub.flashData(data, (bytesWritten, _bytesTotal) => {
|
|
202
276
|
const newPct = Math.floor(((totalBytesWritten + bytesWritten) / totalSize) * 100);
|
|
203
277
|
if (newPct === lastPct) {
|
|
204
278
|
return;
|
package/dist/install-dialog.d.ts
CHANGED
|
@@ -45,12 +45,14 @@ export declare class EwtInstallDialog extends LitElement {
|
|
|
45
45
|
private _consoleInitialized;
|
|
46
46
|
private _improvSupported;
|
|
47
47
|
private _isUsbJtagOrOtgDevice;
|
|
48
|
+
private _flashSize?;
|
|
48
49
|
private _openConsoleAfterReconnect;
|
|
49
50
|
private _visitDeviceAfterReconnect;
|
|
50
51
|
private _addToHAAfterReconnect;
|
|
51
52
|
private _changeWiFiAfterReconnect;
|
|
52
53
|
private _ensureStub;
|
|
53
54
|
private get _port();
|
|
55
|
+
private _probeFlashSize;
|
|
54
56
|
private _isUsbJtagOrOtg;
|
|
55
57
|
private _isWebUsbWithExternalSerial;
|
|
56
58
|
private _releaseReaderWriter;
|
package/dist/install-dialog.js
CHANGED
|
@@ -57,6 +57,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
57
57
|
this._handleDisconnect = () => {
|
|
58
58
|
this._state = "ERROR";
|
|
59
59
|
this._error = "Disconnected";
|
|
60
|
+
// Reset flash size when device is actually disconnected
|
|
61
|
+
this._flashSize = undefined;
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
// Ensure stub is initialized (called before any operation that needs it)
|
|
@@ -116,6 +118,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
116
118
|
const espStub = await this.esploader.runStub();
|
|
117
119
|
this.logger.log(`Stub created: IS_STUB=${espStub.IS_STUB}, chipFamily=${getChipFamilyName(espStub)}`);
|
|
118
120
|
this._espStub = espStub;
|
|
121
|
+
// Detect flash size AFTER stub creation (flash size is only available after stub)
|
|
122
|
+
await this._probeFlashSize(espStub);
|
|
119
123
|
// Set baudrate BEFORE any operations (use user-selected baudrate if available)
|
|
120
124
|
if (this.baudRate && this.baudRate > 115200) {
|
|
121
125
|
this.logger.log(`Setting baudrate to ${this.baudRate}...`);
|
|
@@ -138,6 +142,19 @@ export class EwtInstallDialog extends LitElement {
|
|
|
138
142
|
get _port() {
|
|
139
143
|
return this.esploader.port;
|
|
140
144
|
}
|
|
145
|
+
// Helper to probe flash size from a running stub (only available after stub)
|
|
146
|
+
async _probeFlashSize(espStub) {
|
|
147
|
+
if (espStub.detectFlashSize && !this._flashSize) {
|
|
148
|
+
try {
|
|
149
|
+
await espStub.detectFlashSize();
|
|
150
|
+
this._flashSize = espStub.flashSize;
|
|
151
|
+
this.logger.log(`Flash size detected: ${this._flashSize}`);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
this.logger.debug("Failed to detect flash size:", err);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
141
158
|
// Helper to check if device is using USB-JTAG or USB-OTG (not external serial chip)
|
|
142
159
|
async _isUsbJtagOrOtg() {
|
|
143
160
|
// Use detectUsbConnectionType from tasmota-webserial-esptool
|
|
@@ -504,16 +521,17 @@ export class EwtInstallDialog extends LitElement {
|
|
|
504
521
|
}
|
|
505
522
|
_renderDashboard() {
|
|
506
523
|
const heading = this._info.name;
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
content = html `
|
|
524
|
+
const hideActions = true;
|
|
525
|
+
const allowClosing = true;
|
|
526
|
+
const content = html `
|
|
511
527
|
<ew-list>
|
|
512
528
|
<ew-list-item>
|
|
513
529
|
<div slot="headline">Connected to ${this._info.name}</div>
|
|
514
530
|
<div slot="supporting-text">
|
|
515
531
|
${this._info.firmware} ${this._info.version}
|
|
516
|
-
(${this._info.chipFamily}
|
|
532
|
+
(${this._info.chipFamily}${this._flashSize
|
|
533
|
+
? `, ${this._flashSize}`
|
|
534
|
+
: ""})
|
|
517
535
|
</div>
|
|
518
536
|
</ew-list-item>
|
|
519
537
|
${!this._isSameVersion
|
|
@@ -827,11 +845,25 @@ export class EwtInstallDialog extends LitElement {
|
|
|
827
845
|
}
|
|
828
846
|
_renderDashboardNoImprov() {
|
|
829
847
|
const heading = "Device Dashboard";
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
848
|
+
const hideActions = true;
|
|
849
|
+
const allowClosing = true;
|
|
850
|
+
// Build device info string if available
|
|
851
|
+
const chipFamily = this.esploader.chipFamily
|
|
852
|
+
? getChipFamilyName(this.esploader)
|
|
853
|
+
: null;
|
|
854
|
+
const deviceInfo = chipFamily
|
|
855
|
+
? `(${chipFamily}${this._flashSize ? `, ${this._flashSize}` : ""})`
|
|
856
|
+
: null;
|
|
857
|
+
const content = html `
|
|
834
858
|
<ew-list>
|
|
859
|
+
${deviceInfo
|
|
860
|
+
? html `
|
|
861
|
+
<ew-list-item>
|
|
862
|
+
<div slot="headline">${chipFamily}</div>
|
|
863
|
+
<div slot="supporting-text">${deviceInfo}</div>
|
|
864
|
+
</ew-list-item>
|
|
865
|
+
`
|
|
866
|
+
: ""}
|
|
835
867
|
<ew-list-item
|
|
836
868
|
type="button"
|
|
837
869
|
?disabled=${this._busy}
|
|
@@ -1099,8 +1131,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1099
1131
|
case 3 /* ImprovSerialErrorState.UNABLE_TO_CONNECT */:
|
|
1100
1132
|
error = "Unable to connect";
|
|
1101
1133
|
break;
|
|
1134
|
+
// UNKNOWN_RPC_COMMAND happens when list SSIDs not supported.
|
|
1102
1135
|
case 0 /* ImprovSerialErrorState.NO_ERROR */:
|
|
1103
|
-
// Happens when list SSIDs not supported.
|
|
1104
1136
|
case 2 /* ImprovSerialErrorState.UNKNOWN_RPC_COMMAND */:
|
|
1105
1137
|
break;
|
|
1106
1138
|
default:
|
|
@@ -1219,6 +1251,7 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1219
1251
|
return [heading, content];
|
|
1220
1252
|
}
|
|
1221
1253
|
_renderInstall() {
|
|
1254
|
+
var _a, _b, _c;
|
|
1222
1255
|
let heading;
|
|
1223
1256
|
let content;
|
|
1224
1257
|
let hideActions = false;
|
|
@@ -1247,11 +1280,20 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1247
1280
|
else if (!this._installConfirmed) {
|
|
1248
1281
|
heading = "Confirm Installation";
|
|
1249
1282
|
const action = isUpdate ? "update to" : "install";
|
|
1283
|
+
// Build device info with flash size if available
|
|
1284
|
+
const deviceInfo = this._flashSize
|
|
1285
|
+
? html ` (${((_a = this._info) === null || _a === void 0 ? void 0 : _a.chipFamily) || ""}${((_b = this._info) === null || _b === void 0 ? void 0 : _b.chipFamily)
|
|
1286
|
+
? `, ${this._flashSize}`
|
|
1287
|
+
: this._flashSize})`
|
|
1288
|
+
: "";
|
|
1250
1289
|
content = html `
|
|
1251
1290
|
${isUpdate
|
|
1252
1291
|
? html `Your device is running
|
|
1253
|
-
${this._info.firmware} ${this._info
|
|
1254
|
-
|
|
1292
|
+
${this._info.firmware} ${this._info
|
|
1293
|
+
.version}${deviceInfo}.<br /><br />`
|
|
1294
|
+
: deviceInfo
|
|
1295
|
+
? html `Device detected: ${deviceInfo}<br /><br />`
|
|
1296
|
+
: ""}
|
|
1255
1297
|
Do you want to ${action}
|
|
1256
1298
|
${this._manifest.name} ${this._manifest.version}?
|
|
1257
1299
|
${this._installErase
|
|
@@ -1274,7 +1316,11 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1274
1316
|
this._installState.state === "manifest" /* FlashStateType.MANIFEST */ ||
|
|
1275
1317
|
this._installState.state === "preparing" /* FlashStateType.PREPARING */) {
|
|
1276
1318
|
heading = "Installing";
|
|
1277
|
-
|
|
1319
|
+
// Show flash size in preparing message if available
|
|
1320
|
+
const preparingMsg = ((_c = this._installState) === null || _c === void 0 ? void 0 : _c.flashSize)
|
|
1321
|
+
? `Preparing installation (${this._installState.flashSize})`
|
|
1322
|
+
: "Preparing installation";
|
|
1323
|
+
content = this._renderProgress(preparingMsg);
|
|
1278
1324
|
hideActions = true;
|
|
1279
1325
|
}
|
|
1280
1326
|
else if (this._installState.state === "erasing" /* FlashStateType.ERASING */) {
|
|
@@ -1354,10 +1400,9 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1354
1400
|
return [heading, content, hideActions, allowClosing];
|
|
1355
1401
|
}
|
|
1356
1402
|
_renderLogs() {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
content = html `
|
|
1403
|
+
const heading = `Logs`;
|
|
1404
|
+
const hideActions = false;
|
|
1405
|
+
const content = html `
|
|
1361
1406
|
<ew-console
|
|
1362
1407
|
.port=${this._port}
|
|
1363
1408
|
.logger=${this.logger}
|
|
@@ -1422,7 +1467,22 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1422
1467
|
`;
|
|
1423
1468
|
}
|
|
1424
1469
|
else {
|
|
1470
|
+
// Build device info with flash size for partition view
|
|
1471
|
+
const chipFamily = this.esploader.chipFamily
|
|
1472
|
+
? getChipFamilyName(this.esploader)
|
|
1473
|
+
: null;
|
|
1474
|
+
const deviceInfo = chipFamily
|
|
1475
|
+
? `${chipFamily}${this._flashSize ? `, ${this._flashSize}` : ""}`
|
|
1476
|
+
: null;
|
|
1425
1477
|
content = html `
|
|
1478
|
+
${deviceInfo
|
|
1479
|
+
? html `<div
|
|
1480
|
+
class="device-info"
|
|
1481
|
+
style="margin-bottom: 16px; font-size: 14px; color: var(--md-sys-color-on-surface-variant, #666);"
|
|
1482
|
+
>
|
|
1483
|
+
Device: ${deviceInfo}
|
|
1484
|
+
</div>`
|
|
1485
|
+
: ""}
|
|
1426
1486
|
<div class="partition-list">
|
|
1427
1487
|
<table class="partition-table">
|
|
1428
1488
|
<thead>
|
|
@@ -1632,7 +1692,7 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1632
1692
|
try {
|
|
1633
1693
|
ssids = await this._client.scan();
|
|
1634
1694
|
}
|
|
1635
|
-
catch (
|
|
1695
|
+
catch (_err) {
|
|
1636
1696
|
// When we fail while loading, pick "Join other"
|
|
1637
1697
|
if (this._ssids === undefined) {
|
|
1638
1698
|
this._ssids = null;
|
|
@@ -1720,7 +1780,7 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1720
1780
|
try {
|
|
1721
1781
|
this._manifest = await downloadManifest(this.manifestPath);
|
|
1722
1782
|
}
|
|
1723
|
-
catch (
|
|
1783
|
+
catch (_err) {
|
|
1724
1784
|
this._state = "ERROR";
|
|
1725
1785
|
this._error = "Failed to download manifest";
|
|
1726
1786
|
this._busy = false;
|
|
@@ -1786,6 +1846,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1786
1846
|
this._espStub = await this.esploader.runStub();
|
|
1787
1847
|
this.logger.log(`Stub created: IS_STUB=${this._espStub.IS_STUB}`);
|
|
1788
1848
|
}
|
|
1849
|
+
// Detect flash size after stub is running
|
|
1850
|
+
await this._probeFlashSize(this._espStub);
|
|
1789
1851
|
// CRITICAL: Save parent loader
|
|
1790
1852
|
const loaderToSave = this._espStub._parent || this._espStub;
|
|
1791
1853
|
this._savedLoaderBeforeConsole = loaderToSave;
|
|
@@ -2094,7 +2156,7 @@ export class EwtInstallDialog extends LitElement {
|
|
|
2094
2156
|
try {
|
|
2095
2157
|
await this._client.provision(ssid, password);
|
|
2096
2158
|
}
|
|
2097
|
-
catch (
|
|
2159
|
+
catch (_err) {
|
|
2098
2160
|
return;
|
|
2099
2161
|
}
|
|
2100
2162
|
finally {
|
|
@@ -2500,6 +2562,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
2500
2562
|
await this._closeClientWithoutEvents(this._client);
|
|
2501
2563
|
}
|
|
2502
2564
|
document.body.style.overflow = (_a = this._bodyOverflow) !== null && _a !== void 0 ? _a : "";
|
|
2565
|
+
// Reset flash size when dialog is closed
|
|
2566
|
+
this._flashSize = undefined;
|
|
2503
2567
|
fireEvent(this, "closed");
|
|
2504
2568
|
this.parentNode.removeChild(this);
|
|
2505
2569
|
}
|
|
@@ -2689,4 +2753,7 @@ __decorate([
|
|
|
2689
2753
|
__decorate([
|
|
2690
2754
|
state()
|
|
2691
2755
|
], EwtInstallDialog.prototype, "_isUsbJtagOrOtgDevice", void 0);
|
|
2756
|
+
__decorate([
|
|
2757
|
+
state()
|
|
2758
|
+
], EwtInstallDialog.prototype, "_flashSize", void 0);
|
|
2692
2759
|
customElements.define("ewt-install-dialog", EwtInstallDialog);
|
package/dist/util/cors-proxy.js
CHANGED
|
@@ -32,7 +32,7 @@ export const corsProxyFetch = async (url, options) => {
|
|
|
32
32
|
// GitHub releases don't support CORS, use proxy directly
|
|
33
33
|
if (url.includes("github.com") && url.includes("/releases/download/")) {
|
|
34
34
|
const proxiedUrl = `${CORS_PROXY}/?url=${encodeURIComponent(url)}`;
|
|
35
|
-
const { headers, credentials, ...safeOptions } = options !== null && options !== void 0 ? options : {};
|
|
35
|
+
const { headers: _headers, credentials: _credentials, ...safeOptions } = options !== null && options !== void 0 ? options : {};
|
|
36
36
|
return fetch(proxiedUrl, safeOptions);
|
|
37
37
|
}
|
|
38
38
|
// Try direct fetch first for other cross-origin requests
|
|
@@ -44,10 +44,10 @@ export const corsProxyFetch = async (url, options) => {
|
|
|
44
44
|
// Direct fetch failed, try proxy
|
|
45
45
|
try {
|
|
46
46
|
const proxiedUrl = `${CORS_PROXY}/?url=${encodeURIComponent(url)}`;
|
|
47
|
-
const { headers, credentials, ...safeOptions } = options !== null && options !== void 0 ? options : {};
|
|
47
|
+
const { headers: _headers, credentials: _credentials, ...safeOptions } = options !== null && options !== void 0 ? options : {};
|
|
48
48
|
return await fetch(proxiedUrl, safeOptions);
|
|
49
49
|
}
|
|
50
|
-
catch (
|
|
50
|
+
catch (_proxyError) {
|
|
51
51
|
// Both failed, throw the original error
|
|
52
52
|
throw directError;
|
|
53
53
|
}
|
|
@@ -11,7 +11,9 @@ export class LineBreakTransformer {
|
|
|
11
11
|
let lastIndex = 0;
|
|
12
12
|
let match;
|
|
13
13
|
while ((match = re.exec(this.chunks)) !== null) {
|
|
14
|
-
|
|
14
|
+
// If this is a lone \r at the very end of the buffer, leave it so it can
|
|
15
|
+
// be combined with a possible following \n in the next chunk.
|
|
16
|
+
if (match[0] === "\r" && match.index === this.chunks.length - 1) {
|
|
15
17
|
break;
|
|
16
18
|
}
|
|
17
19
|
const line = this.chunks.substring(lastIndex, match.index);
|
package/dist/util/manifest.js
CHANGED
|
@@ -7,6 +7,7 @@ export const downloadManifest = async (manifestPath) => {
|
|
|
7
7
|
}
|
|
8
8
|
const manifest = await resp.json();
|
|
9
9
|
if ("new_install_skip_erase" in manifest) {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
10
11
|
console.warn('Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.');
|
|
11
12
|
if (manifest.new_install_skip_erase) {
|
|
12
13
|
manifest.new_install_prompt_erase = true;
|
package/dist/util/partition.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
// [HH:MM:SS.mmm] wall-clock bracket with millis
|
|
8
8
|
// HH:MM:SS.mmm plain wall-clock
|
|
9
9
|
const DEVICE_TIMESTAMP_RE = /^\s*(?:\[\d{2}:\d{2}:\d{2}(?:\.\d+)?\]|(?:\d{2}:){2}\d{2}\.\d)/;
|
|
10
|
+
// Matches leading ANSI SGR (color/style) codes at the start of a string
|
|
11
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences
|
|
12
|
+
const LEADING_ANSI_RE = /^(\x1b\[(?:\d+;)*\d*m)+/;
|
|
10
13
|
export class TimestampTransformer {
|
|
11
14
|
constructor() {
|
|
12
15
|
this.deviceHasTimestamps = false;
|
|
@@ -25,11 +28,30 @@ export class TimestampTransformer {
|
|
|
25
28
|
controller.enqueue(chunk);
|
|
26
29
|
return;
|
|
27
30
|
}
|
|
31
|
+
// Extract leading ANSI codes to preserve them across line splits
|
|
32
|
+
const ansiMatch = chunk.match(LEADING_ANSI_RE);
|
|
33
|
+
const leadingAnsi = ansiMatch ? ansiMatch[0] : "";
|
|
34
|
+
const contentWithoutAnsi = leadingAnsi
|
|
35
|
+
? chunk.slice(leadingAnsi.length)
|
|
36
|
+
: chunk;
|
|
28
37
|
const date = new Date();
|
|
29
38
|
const h = date.getHours().toString().padStart(2, "0");
|
|
30
39
|
const m = date.getMinutes().toString().padStart(2, "0");
|
|
31
40
|
const s = date.getSeconds().toString().padStart(2, "0");
|
|
32
|
-
|
|
41
|
+
const timestamp = `[${h}:${m}:${s}]`;
|
|
42
|
+
// For multi-line chunks, we need to preserve ANSI codes on each line
|
|
43
|
+
// Split on newlines, but keep the newline characters
|
|
44
|
+
const lines = contentWithoutAnsi.split(/(\r?\n)/);
|
|
45
|
+
let result = "";
|
|
46
|
+
for (const part of lines) {
|
|
47
|
+
if (part === "\n" || part === "\r\n") {
|
|
48
|
+
result += part;
|
|
49
|
+
}
|
|
50
|
+
else if (part !== "") {
|
|
51
|
+
result += leadingAnsi + timestamp + " " + part;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
controller.enqueue(result);
|
|
33
55
|
}
|
|
34
56
|
reset() {
|
|
35
57
|
this.deviceHasTimestamps = false;
|