esp32tool 1.2.0 → 1.3.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/apple-touch-icon.png +0 -0
- package/css/style.css +38 -8
- package/dist/console.d.ts +15 -0
- package/dist/console.js +237 -0
- package/dist/const.d.ts +99 -0
- package/dist/const.js +129 -8
- package/dist/esp_loader.d.ts +119 -3
- package/dist/esp_loader.js +797 -48
- package/dist/util/console-color.d.ts +19 -0
- package/dist/util/console-color.js +272 -0
- package/dist/util/line-break-transformer.d.ts +5 -0
- package/dist/util/line-break-transformer.js +17 -0
- package/dist/web/index.js +1 -1
- package/icons/icon-128.png +0 -0
- package/icons/icon-144.png +0 -0
- package/icons/icon-152.png +0 -0
- package/icons/icon-192.png +0 -0
- package/icons/icon-384.png +0 -0
- package/icons/icon-512.png +0 -0
- package/icons/icon-72.png +0 -0
- package/icons/icon-96.png +0 -0
- package/index.html +62 -22
- package/js/console.js +269 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +586 -16
- package/js/util/console-color.js +282 -0
- package/js/util/line-break-transformer.js +19 -0
- package/package.cli.json +1 -1
- package/package.json +9 -8
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/console.ts +278 -0
- package/src/const.ts +165 -8
- package/src/esp_loader.ts +971 -50
- package/src/util/console-color.ts +290 -0
- package/src/util/line-break-transformer.ts +20 -0
package/index.html
CHANGED
|
@@ -83,6 +83,20 @@
|
|
|
83
83
|
<label for="baudRate">Baud</label>
|
|
84
84
|
<select id="baudRate"></select>
|
|
85
85
|
|
|
86
|
+
<label for="advanced">Advanced</label>
|
|
87
|
+
<div class="onoffswitch">
|
|
88
|
+
<input
|
|
89
|
+
type="checkbox"
|
|
90
|
+
name="advanced"
|
|
91
|
+
class="onoffswitch-checkbox"
|
|
92
|
+
id="advanced"
|
|
93
|
+
/>
|
|
94
|
+
<label class="onoffswitch-label" for="advanced">
|
|
95
|
+
<span class="onoffswitch-inner"></span>
|
|
96
|
+
<span class="onoffswitch-switch"></span>
|
|
97
|
+
</label>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
86
100
|
<label for="showlog">Show Log</label>
|
|
87
101
|
<div class="onoffswitch">
|
|
88
102
|
<input
|
|
@@ -98,47 +112,72 @@
|
|
|
98
112
|
</div>
|
|
99
113
|
|
|
100
114
|
<span class="log-controls">
|
|
101
|
-
<label for="autoscroll">Autoscroll</label>
|
|
102
|
-
<div class="onoffswitch">
|
|
103
|
-
<input
|
|
104
|
-
type="checkbox"
|
|
105
|
-
name="autoscroll"
|
|
106
|
-
class="onoffswitch-checkbox"
|
|
107
|
-
id="autoscroll"
|
|
108
|
-
/>
|
|
109
|
-
<label class="onoffswitch-label" for="autoscroll">
|
|
110
|
-
<span class="onoffswitch-inner"></span>
|
|
111
|
-
<span class="onoffswitch-switch"></span>
|
|
112
|
-
</label>
|
|
113
|
-
</div>
|
|
114
115
|
<button id="butClear" type="button" class="small-btn">
|
|
115
116
|
Clear
|
|
116
117
|
</button>
|
|
117
118
|
</span>
|
|
118
119
|
|
|
119
|
-
<label for="
|
|
120
|
+
<label for="darkmode">Dark</label>
|
|
120
121
|
<div class="onoffswitch">
|
|
121
122
|
<input
|
|
122
123
|
type="checkbox"
|
|
123
|
-
name="
|
|
124
|
+
name="darkmode"
|
|
124
125
|
class="onoffswitch-checkbox"
|
|
125
|
-
id="
|
|
126
|
+
id="darkmode"
|
|
126
127
|
/>
|
|
127
|
-
<label class="onoffswitch-label" for="
|
|
128
|
+
<label class="onoffswitch-label" for="darkmode">
|
|
128
129
|
<span class="onoffswitch-inner"></span>
|
|
129
130
|
<span class="onoffswitch-switch"></span>
|
|
130
131
|
</label>
|
|
131
132
|
</div>
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="header-content advanced-row" style="display: none;">
|
|
136
|
+
<label for="chunkSize">Chunk</label>
|
|
137
|
+
<select id="chunkSize"></select>
|
|
138
|
+
<label for="blockSize">Block</label>
|
|
139
|
+
<select id="blockSize"></select>
|
|
140
|
+
<label for="maxInFlight">MaxInFlight</label>
|
|
141
|
+
<select id="maxInFlight"></select>
|
|
142
|
+
|
|
143
|
+
<label for="autoscroll">Autoscroll</label>
|
|
134
144
|
<div class="onoffswitch">
|
|
135
145
|
<input
|
|
136
146
|
type="checkbox"
|
|
137
|
-
name="
|
|
147
|
+
name="autoscroll"
|
|
138
148
|
class="onoffswitch-checkbox"
|
|
139
|
-
id="
|
|
149
|
+
id="autoscroll"
|
|
150
|
+
checked
|
|
140
151
|
/>
|
|
141
|
-
<label class="onoffswitch-label" for="
|
|
152
|
+
<label class="onoffswitch-label" for="autoscroll">
|
|
153
|
+
<span class="onoffswitch-inner"></span>
|
|
154
|
+
<span class="onoffswitch-switch"></span>
|
|
155
|
+
</label>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<label for="console">Console</label>
|
|
159
|
+
<div class="onoffswitch">
|
|
160
|
+
<input
|
|
161
|
+
type="checkbox"
|
|
162
|
+
name="console"
|
|
163
|
+
class="onoffswitch-checkbox"
|
|
164
|
+
id="console"
|
|
165
|
+
/>
|
|
166
|
+
<label class="onoffswitch-label" for="console">
|
|
167
|
+
<span class="onoffswitch-inner"></span>
|
|
168
|
+
<span class="onoffswitch-switch"></span>
|
|
169
|
+
</label>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<label for="debugmode">Debug</label>
|
|
173
|
+
<div class="onoffswitch">
|
|
174
|
+
<input
|
|
175
|
+
type="checkbox"
|
|
176
|
+
name="debugmode"
|
|
177
|
+
class="onoffswitch-checkbox"
|
|
178
|
+
id="debugmode"
|
|
179
|
+
/>
|
|
180
|
+
<label class="onoffswitch-label" for="debugmode">
|
|
142
181
|
<span class="onoffswitch-inner"></span>
|
|
143
182
|
<span class="onoffswitch-switch"></span>
|
|
144
183
|
</label>
|
|
@@ -151,6 +190,7 @@
|
|
|
151
190
|
Make sure you're running Chrome 89 or later (Desktop) or Chrome 61+ (Android with USB OTG).
|
|
152
191
|
</div>
|
|
153
192
|
<div id="app">
|
|
193
|
+
<div id="console-container" class="console-container hidden"></div>
|
|
154
194
|
<div id="commands">
|
|
155
195
|
<div class="upload">
|
|
156
196
|
<label
|
package/js/console.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { ColoredConsole, coloredConsoleStyles } from "./util/console-color.js";
|
|
2
|
+
import { LineBreakTransformer } from "./util/line-break-transformer.js";
|
|
3
|
+
|
|
4
|
+
export class ESP32ToolConsole {
|
|
5
|
+
constructor(port, containerElement, allowInput = true) {
|
|
6
|
+
this.port = port;
|
|
7
|
+
this.containerElement = containerElement;
|
|
8
|
+
this.allowInput = allowInput;
|
|
9
|
+
this.console = null;
|
|
10
|
+
this.cancelConnection = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
logs() {
|
|
14
|
+
return this.console?.logs() || "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async init() {
|
|
18
|
+
// Create console HTML
|
|
19
|
+
this.containerElement.innerHTML = `
|
|
20
|
+
<style>
|
|
21
|
+
.esp32tool-console-wrapper {
|
|
22
|
+
background-color: #1c1c1c;
|
|
23
|
+
color: #ddd;
|
|
24
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier,
|
|
25
|
+
monospace;
|
|
26
|
+
line-height: 1.45;
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
height: 100%;
|
|
30
|
+
border: 1px solid #333;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
}
|
|
33
|
+
.esp32tool-console-header {
|
|
34
|
+
display: flex;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
align-items: center;
|
|
37
|
+
padding: 8px 12px;
|
|
38
|
+
background-color: #2a2a2a;
|
|
39
|
+
border-bottom: 1px solid #333;
|
|
40
|
+
}
|
|
41
|
+
.esp32tool-console-header h3 {
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
order: 2;
|
|
46
|
+
}
|
|
47
|
+
.esp32tool-console-controls {
|
|
48
|
+
display: flex;
|
|
49
|
+
gap: 8px;
|
|
50
|
+
order: 1;
|
|
51
|
+
}
|
|
52
|
+
.esp32tool-console-controls button {
|
|
53
|
+
padding: 4px 12px;
|
|
54
|
+
font-size: 12px;
|
|
55
|
+
background-color: #444;
|
|
56
|
+
color: #ddd;
|
|
57
|
+
border: 1px solid #555;
|
|
58
|
+
border-radius: 3px;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
}
|
|
61
|
+
.esp32tool-console-controls button:hover {
|
|
62
|
+
background-color: #555;
|
|
63
|
+
}
|
|
64
|
+
.esp32tool-console-form {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
padding: 0 8px 0 16px;
|
|
68
|
+
background-color: #1c1c1c;
|
|
69
|
+
border-top: 1px solid #333;
|
|
70
|
+
}
|
|
71
|
+
.esp32tool-console-input {
|
|
72
|
+
flex: 1;
|
|
73
|
+
padding: 8px;
|
|
74
|
+
margin: 4px 8px;
|
|
75
|
+
border: 0;
|
|
76
|
+
outline: none;
|
|
77
|
+
background-color: #1c1c1c;
|
|
78
|
+
color: #ddd;
|
|
79
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier,
|
|
80
|
+
monospace;
|
|
81
|
+
font-size: 12px;
|
|
82
|
+
}
|
|
83
|
+
${coloredConsoleStyles}
|
|
84
|
+
.esp32tool-console-wrapper .log {
|
|
85
|
+
flex: 1;
|
|
86
|
+
margin: 0;
|
|
87
|
+
border-radius: 0;
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
90
|
+
<div class="esp32tool-console-wrapper">
|
|
91
|
+
<div class="esp32tool-console-header">
|
|
92
|
+
<div class="esp32tool-console-controls">
|
|
93
|
+
<button id="console-clear-btn">Clear</button>
|
|
94
|
+
<button id="console-reset-btn">Reset Device</button>
|
|
95
|
+
<button id="console-close-btn">Close Console</button>
|
|
96
|
+
</div>
|
|
97
|
+
<h3>ESP Console</h3>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="log"></div>
|
|
100
|
+
${
|
|
101
|
+
this.allowInput
|
|
102
|
+
? `<form class="esp32tool-console-form">
|
|
103
|
+
<input class="esp32tool-console-input" autofocus placeholder="Type command and press Enter...">
|
|
104
|
+
</form>`
|
|
105
|
+
: ""
|
|
106
|
+
}
|
|
107
|
+
</div>
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
this.console = new ColoredConsole(
|
|
111
|
+
this.containerElement.querySelector(".log")
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Setup event listeners
|
|
115
|
+
const clearBtn = this.containerElement.querySelector("#console-clear-btn");
|
|
116
|
+
if (clearBtn) {
|
|
117
|
+
clearBtn.addEventListener("click", () => this.clear());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const resetBtn = this.containerElement.querySelector("#console-reset-btn");
|
|
121
|
+
if (resetBtn) {
|
|
122
|
+
resetBtn.addEventListener("click", () => this.reset());
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const closeBtn = this.containerElement.querySelector("#console-close-btn");
|
|
126
|
+
if (closeBtn) {
|
|
127
|
+
closeBtn.addEventListener("click", () => {
|
|
128
|
+
// Dispatch close event to parent
|
|
129
|
+
this.containerElement.dispatchEvent(
|
|
130
|
+
new CustomEvent("console-close", { bubbles: true })
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.allowInput) {
|
|
136
|
+
const input = this.containerElement.querySelector(".esp32tool-console-input");
|
|
137
|
+
|
|
138
|
+
this.containerElement.addEventListener("click", () => {
|
|
139
|
+
// Only focus input if user didn't select some text
|
|
140
|
+
if (getSelection()?.toString() === "") {
|
|
141
|
+
input.focus();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const form = this.containerElement.querySelector("form");
|
|
146
|
+
if (form) {
|
|
147
|
+
form.addEventListener("submit", (ev) => {
|
|
148
|
+
ev.preventDefault();
|
|
149
|
+
ev.stopPropagation();
|
|
150
|
+
this._sendCommand();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
input.addEventListener("keydown", (ev) => {
|
|
155
|
+
if (ev.key === "Enter") {
|
|
156
|
+
ev.preventDefault();
|
|
157
|
+
ev.stopPropagation();
|
|
158
|
+
this._sendCommand();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Start connection
|
|
164
|
+
const abortController = new AbortController();
|
|
165
|
+
const connection = this._connect(abortController.signal);
|
|
166
|
+
this.cancelConnection = () => {
|
|
167
|
+
abortController.abort();
|
|
168
|
+
return connection;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async _connect(abortSignal) {
|
|
173
|
+
console.log("Starting console read loop");
|
|
174
|
+
|
|
175
|
+
// Check if port.readable is available
|
|
176
|
+
if (!this.port.readable) {
|
|
177
|
+
this.console.addLine("");
|
|
178
|
+
this.console.addLine("");
|
|
179
|
+
this.console.addLine(
|
|
180
|
+
`Terminal disconnected: Port readable stream not available`
|
|
181
|
+
);
|
|
182
|
+
console.error(
|
|
183
|
+
"Port readable stream not available - port may need to be reopened at correct baudrate"
|
|
184
|
+
);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await this.port.readable
|
|
190
|
+
.pipeThrough(new TextDecoderStream(), {
|
|
191
|
+
signal: abortSignal,
|
|
192
|
+
})
|
|
193
|
+
.pipeThrough(new TransformStream(new LineBreakTransformer()))
|
|
194
|
+
.pipeTo(
|
|
195
|
+
new WritableStream({
|
|
196
|
+
write: (chunk) => {
|
|
197
|
+
const cleaned = chunk.replace(/\r\n$/, "\n");
|
|
198
|
+
this.console.addLine(cleaned);
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
);
|
|
202
|
+
if (!abortSignal.aborted) {
|
|
203
|
+
this.console.addLine("");
|
|
204
|
+
this.console.addLine("");
|
|
205
|
+
this.console.addLine("Terminal disconnected");
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
// Only log disconnect errors if the abort was NOT intentional
|
|
209
|
+
if (!abortSignal.aborted && !(e instanceof DOMException && e.name === 'AbortError')) {
|
|
210
|
+
this.console.addLine("");
|
|
211
|
+
this.console.addLine("");
|
|
212
|
+
this.console.addLine(`Terminal disconnected: ${e}`);
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
216
|
+
console.log("Finished console read loop");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async _sendCommand() {
|
|
221
|
+
const input = this.containerElement.querySelector(".esp32tool-console-input");
|
|
222
|
+
const command = input.value;
|
|
223
|
+
if (!this.port.writable) {
|
|
224
|
+
this.console.addLine("Terminal disconnected: port not writable");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const encoder = new TextEncoder();
|
|
228
|
+
let writer;
|
|
229
|
+
try {
|
|
230
|
+
writer = this.port.writable.getWriter();
|
|
231
|
+
await writer.write(encoder.encode(command + "\r\n"));
|
|
232
|
+
this.console.addLine(`> ${command}`);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
this.console.addLine(`Write failed: ${err}`);
|
|
235
|
+
} finally {
|
|
236
|
+
if (writer) {
|
|
237
|
+
try {
|
|
238
|
+
writer.releaseLock();
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error("Ignoring release lock error", err);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
input.value = "";
|
|
245
|
+
input.focus();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
clear() {
|
|
249
|
+
const logElement = this.containerElement.querySelector(".log");
|
|
250
|
+
if (logElement) {
|
|
251
|
+
logElement.innerHTML = "";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async reset() {
|
|
256
|
+
console.log("Reset device requested from console");
|
|
257
|
+
// Don't use addLine here as stream might already be closed
|
|
258
|
+
// This will be called from script.js with proper reset logic
|
|
259
|
+
const event = new CustomEvent("console-reset");
|
|
260
|
+
this.containerElement.dispatchEvent(event);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async disconnect() {
|
|
264
|
+
if (this.cancelConnection) {
|
|
265
|
+
await this.cancelConnection();
|
|
266
|
+
this.cancelConnection = null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|