tasmota-esp-web-tools 12.1.2 → 12.2.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/README.md +62 -0
- package/dist/components/ew-littlefs-manager.js +3 -0
- package/dist/connect.js +1 -0
- package/dist/const.d.ts +9 -0
- package/dist/flash.js +105 -4
- package/dist/install-dialog.d.ts +2 -0
- package/dist/install-dialog.js +77 -10
- package/dist/util/line-break-transformer.js +3 -1
- package/dist/util/manifest.js +1 -0
- 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-C1yqGLP_.js → dist/web/install-dialog-C3uI77Wb.js} +113 -103
- package/{js/modules/styles-D69gtq6_.js → dist/web/styles-D4zXLz1s.js} +6 -9
- package/js/modules/{index-DUmRg0Cu.js → index-B5owDZF7.js} +1 -1
- package/js/modules/install-button.js +1 -1
- package/{dist/web/install-dialog-C1yqGLP_.js → js/modules/install-dialog-C3uI77Wb.js} +113 -103
- package/{dist/web/styles-D69gtq6_.js → js/modules/styles-D4zXLz1s.js} +6 -9
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -108,6 +108,68 @@ 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
|
+
|
|
141
|
+
## USB Interface Support (UART vs CDC)
|
|
142
|
+
|
|
143
|
+
For chips that can be connected either through a native USB interface (USB-JTAG/USB-OTG, e.g. ESP32-S2/S3/C3/C6/...) or through an external USB-to-Serial bridge (CP210x, FTDI, CH340, ...), you can ship dedicated firmware variants by specifying `usbInterface`:
|
|
144
|
+
|
|
145
|
+
- `"CDC"` – firmware built for native USB (CDC) console / programming
|
|
146
|
+
- `"UART"` – firmware built for the regular UART console via an external USB-to-Serial chip
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"name": "My Firmware",
|
|
151
|
+
"builds": [
|
|
152
|
+
{
|
|
153
|
+
"chipFamily": "ESP32-S3",
|
|
154
|
+
"usbInterface": "CDC",
|
|
155
|
+
"parts": [{ "path": "s3-cdc.bin", "offset": 0 }]
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"chipFamily": "ESP32-S3",
|
|
159
|
+
"usbInterface": "UART",
|
|
160
|
+
"parts": [{ "path": "s3-uart.bin", "offset": 0 }]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
ESP Web Tools automatically detects how the device is connected:
|
|
167
|
+
|
|
168
|
+
- If the device is reached via native USB (USB-JTAG/USB-OTG) the build with `usbInterface: "CDC"` is selected.
|
|
169
|
+
- Otherwise (external USB-to-Serial bridge) the build with `usbInterface: "UART"` is selected.
|
|
170
|
+
|
|
171
|
+
The `usbInterface` field is **optional**. If omitted, the build will match any USB interface and is used as a fallback. It can also be combined freely with `chipVariant` and `flashSizeMB`.
|
|
172
|
+
|
|
111
173
|
## Performance
|
|
112
174
|
|
|
113
175
|
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
|
}
|
package/dist/connect.js
CHANGED
package/dist/const.d.ts
CHANGED
|
@@ -6,6 +6,14 @@ 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;
|
|
10
|
+
/**
|
|
11
|
+
* Optional USB interface qualifier.
|
|
12
|
+
* - "CDC": native USB CDC (USB-JTAG/USB-OTG, e.g. direct USB on S2/S3/C3/C6/...)
|
|
13
|
+
* - "UART": external USB-to-Serial bridge (CP210x, FTDI, CH340, ...)
|
|
14
|
+
* If omitted, the build will match any USB interface.
|
|
15
|
+
*/
|
|
16
|
+
usbInterface?: "UART" | "CDC";
|
|
9
17
|
parts: {
|
|
10
18
|
path: string;
|
|
11
19
|
offset: number;
|
|
@@ -29,6 +37,7 @@ export interface BaseFlashState {
|
|
|
29
37
|
build?: Build;
|
|
30
38
|
chipFamily?: Build["chipFamily"] | "Unknown Chip";
|
|
31
39
|
chipVariant?: string | null;
|
|
40
|
+
flashSize?: string;
|
|
32
41
|
}
|
|
33
42
|
export interface InitializingState extends BaseFlashState {
|
|
34
43
|
state: FlashStateType.INITIALIZING;
|
package/dist/flash.js
CHANGED
|
@@ -1,18 +1,87 @@
|
|
|
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 usbInterface are strongly preferred
|
|
22
|
+
* - Builds with matching flashSizeMB are preferred
|
|
23
|
+
* - Among builds with same specificity, first one wins
|
|
24
|
+
* - Builds without these qualifiers are fallback options
|
|
25
|
+
*/
|
|
26
|
+
function selectBestBuild(builds, detectedFlashSizeMB, detectedUsbInterface) {
|
|
27
|
+
if (builds.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
// Score builds: higher score = more specific match
|
|
30
|
+
let bestBuild;
|
|
31
|
+
let bestScore = -Infinity;
|
|
32
|
+
for (const build of builds) {
|
|
33
|
+
let score = 0;
|
|
34
|
+
// USB interface match - if specified, must match
|
|
35
|
+
if (build.usbInterface !== undefined) {
|
|
36
|
+
if (detectedUsbInterface !== undefined &&
|
|
37
|
+
build.usbInterface === detectedUsbInterface) {
|
|
38
|
+
score += 1000; // Strong preference for explicit usbInterface match
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Mismatched usbInterface - disqualify this build
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Builds without usbInterface stay neutral (compatible with any)
|
|
46
|
+
// Flash size match gives second priority
|
|
47
|
+
if (build.flashSizeMB !== undefined && detectedFlashSizeMB !== undefined) {
|
|
48
|
+
if (build.flashSizeMB === detectedFlashSizeMB) {
|
|
49
|
+
score += 100; // Exact flash size match
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
score -= 1; // Penalize non-matching specific builds
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (build.flashSizeMB !== undefined) {
|
|
56
|
+
// flashSizeMB is defined but detectedFlashSizeMB is undefined
|
|
57
|
+
score -= 1; // Penalize non-matching specific builds
|
|
58
|
+
}
|
|
59
|
+
// Generic builds (flashSizeMB undefined) stay at score 0
|
|
60
|
+
// Prefer this build if it has higher score
|
|
61
|
+
// If same score, keep the first one (stable selection)
|
|
62
|
+
if (score > bestScore) {
|
|
63
|
+
bestScore = score;
|
|
64
|
+
bestBuild = build;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return bestScore >= 0 ? bestBuild : undefined;
|
|
68
|
+
}
|
|
4
69
|
export const flash = async (onEvent, esploader, // ESPLoader instance from tasmota-webserial-esptool
|
|
5
70
|
logger, manifestPath, eraseFirst, firmwareBuffer, _baudRate) => {
|
|
6
71
|
let manifest;
|
|
7
72
|
let build;
|
|
73
|
+
// eslint-disable-next-line prefer-const
|
|
8
74
|
let chipFamily;
|
|
9
75
|
let chipVariant = null;
|
|
76
|
+
// eslint-disable-next-line prefer-const
|
|
77
|
+
let flashSize;
|
|
10
78
|
const fireStateEvent = (stateUpdate) => onEvent({
|
|
11
79
|
...stateUpdate,
|
|
12
80
|
manifest,
|
|
13
81
|
build,
|
|
14
82
|
chipFamily,
|
|
15
83
|
chipVariant,
|
|
84
|
+
flashSize,
|
|
16
85
|
});
|
|
17
86
|
let manifestProm = null;
|
|
18
87
|
let manifestURL = "";
|
|
@@ -51,9 +120,34 @@ logger, manifestPath, eraseFirst, firmwareBuffer, _baudRate) => {
|
|
|
51
120
|
}
|
|
52
121
|
chipFamily = getChipFamilyName(esploader);
|
|
53
122
|
chipVariant = esploader.chipVariant;
|
|
123
|
+
// Detect flash size if not already detected
|
|
124
|
+
if (!esploader.flashSize && esploader.detectFlashSize) {
|
|
125
|
+
try {
|
|
126
|
+
await esploader.detectFlashSize();
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
logger.debug("Failed to detect flash size:", err);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
flashSize = esploader.flashSize; // e.g., "4MB", "8MB"
|
|
133
|
+
const flashSizeMB = flashSize ? parseFlashSizeToMB(flashSize) : undefined;
|
|
134
|
+
// Detect USB connection type to pick CDC vs UART firmware variants
|
|
135
|
+
// - true: native USB (USB-JTAG/USB-OTG) -> CDC
|
|
136
|
+
// - false: external USB-to-Serial bridge -> UART
|
|
137
|
+
let detectedUsbInterface;
|
|
138
|
+
if (typeof esploader.detectUsbConnectionType === "function") {
|
|
139
|
+
try {
|
|
140
|
+
const isUsbJtagOrOtg = await esploader.detectUsbConnectionType();
|
|
141
|
+
detectedUsbInterface = isUsbJtagOrOtg ? "CDC" : "UART";
|
|
142
|
+
logger.debug(`Detected USB interface: ${detectedUsbInterface}`);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
logger.debug("Failed to detect USB connection type:", err);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
54
148
|
fireStateEvent({
|
|
55
149
|
state: "initializing" /* FlashStateType.INITIALIZING */,
|
|
56
|
-
message: `Initialized. Found ${chipFamily}${chipVariant ? ` (${chipVariant})` : ""}`,
|
|
150
|
+
message: `Initialized. Found ${chipFamily}${chipVariant ? ` (${chipVariant})` : ""}${flashSize ? `, ${flashSize}` : ""}`,
|
|
57
151
|
details: { done: true },
|
|
58
152
|
});
|
|
59
153
|
fireStateEvent({
|
|
@@ -73,17 +167,24 @@ logger, manifestPath, eraseFirst, firmwareBuffer, _baudRate) => {
|
|
|
73
167
|
await esploader.disconnect();
|
|
74
168
|
return;
|
|
75
169
|
}
|
|
76
|
-
|
|
77
|
-
|
|
170
|
+
// Filter builds by chipFamily and chipVariant
|
|
171
|
+
const compatibleBuilds = manifest.builds.filter((b) => {
|
|
78
172
|
if (b.chipFamily !== chipFamily) {
|
|
79
173
|
return false;
|
|
80
174
|
}
|
|
81
|
-
// If build specifies chipVariant, it must match
|
|
82
175
|
if (b.chipVariant && b.chipVariant !== chipVariant) {
|
|
83
176
|
return false;
|
|
84
177
|
}
|
|
85
178
|
return true;
|
|
86
179
|
});
|
|
180
|
+
// Select the best build using most-specific-matching algorithm
|
|
181
|
+
// Prefer builds with more matching qualifiers (flashSizeMB)
|
|
182
|
+
const exactVariantBuilds = compatibleBuilds.filter((b) => b.chipVariant !== undefined && b.chipVariant === chipVariant);
|
|
183
|
+
const variantAgnosticBuilds = compatibleBuilds.filter((b) => b.chipVariant === undefined);
|
|
184
|
+
build = selectBestBuild(exactVariantBuilds, flashSizeMB, detectedUsbInterface);
|
|
185
|
+
if (!build) {
|
|
186
|
+
build = selectBestBuild(variantAgnosticBuilds, flashSizeMB, detectedUsbInterface);
|
|
187
|
+
}
|
|
87
188
|
if (!build) {
|
|
88
189
|
fireStateEvent({
|
|
89
190
|
state: "error" /* FlashStateType.ERROR */,
|
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
|
-
let content;
|
|
508
524
|
const hideActions = true;
|
|
509
525
|
const allowClosing = true;
|
|
510
|
-
content = html `
|
|
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
|
-
let content;
|
|
831
848
|
const hideActions = true;
|
|
832
849
|
const allowClosing = true;
|
|
833
|
-
|
|
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}
|
|
@@ -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 */) {
|
|
@@ -1355,9 +1401,8 @@ export class EwtInstallDialog extends LitElement {
|
|
|
1355
1401
|
}
|
|
1356
1402
|
_renderLogs() {
|
|
1357
1403
|
const heading = `Logs`;
|
|
1358
|
-
let content;
|
|
1359
1404
|
const hideActions = false;
|
|
1360
|
-
content = html `
|
|
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>
|
|
@@ -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;
|
|
@@ -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);
|
|
@@ -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;
|
|
@@ -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;
|