mirror of
https://github.com/esphome/esp-web-tools.git
synced 2025-07-28 14:16:41 +00:00
v4.0.0 - Next Gen (#87)
This commit is contained in:
parent
8386598933
commit
74187d9f44
23
README.md
23
README.md
@ -13,10 +13,10 @@ Manifest definition:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "ESPHome",
|
"name": "ESPHome",
|
||||||
|
"version": "2021.10.3",
|
||||||
"builds": [
|
"builds": [
|
||||||
{
|
{
|
||||||
"chipFamily": "ESP32",
|
"chipFamily": "ESP32",
|
||||||
"improv": true,
|
|
||||||
"parts": [
|
"parts": [
|
||||||
{ "path": "bootloader.bin", "offset": 4096 },
|
{ "path": "bootloader.bin", "offset": 4096 },
|
||||||
{ "path": "partitions.bin", "offset": 32768 },
|
{ "path": "partitions.bin", "offset": 32768 },
|
||||||
@ -46,17 +46,6 @@ Manifest definition:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Allows for optionally passing an attribute to trigger an erase before installation.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<esp-web-install-button
|
|
||||||
manifest="firmware_esphome/manifest.json"
|
|
||||||
erase-first
|
|
||||||
></esp-web-install-button>
|
|
||||||
```
|
|
||||||
|
|
||||||
All attributes can also be set via properties (`manifest`, `eraseFirst`)
|
|
||||||
|
|
||||||
## Styling
|
## Styling
|
||||||
|
|
||||||
### Attributes
|
### Attributes
|
||||||
@ -67,14 +56,6 @@ The following attributes are automatically added to `<esp-web-install-button>` a
|
|||||||
| -- | -- |
|
| -- | -- |
|
||||||
| `install-supported` | Added if installing firmware is supported
|
| `install-supported` | Added if installing firmware is supported
|
||||||
| `install-unsupported` | Added if installing firmware is not supported
|
| `install-unsupported` | Added if installing firmware is not supported
|
||||||
| `active` | Added when flashing is active
|
|
||||||
|
|
||||||
You can add the following attributes or properties to change the UI elements:
|
|
||||||
|
|
||||||
| Attribute | Property | Description |
|
|
||||||
| -- | -- | -- |
|
|
||||||
| `show-log` | `showLog` | Show a log style view of the progress instead of a progress bar
|
|
||||||
| `hide-progress` | `hideProgress` | Hides all progress UI elements
|
|
||||||
|
|
||||||
### CSS custom properties (variables)
|
### CSS custom properties (variables)
|
||||||
|
|
||||||
@ -115,4 +96,4 @@ details | An optional extra field that is different [per state](https://github.c
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Run `script/develop`. This starts a server. Open it on http://localhost:5000.
|
Run `script/develop`. This starts a server. Open it on http://localhost:5001.
|
||||||
|
38
index.html
38
index.html
@ -90,13 +90,22 @@
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #58a6ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script module>
|
<script module>
|
||||||
import(
|
import(
|
||||||
// In development we import locally.
|
// In development we import locally.
|
||||||
window.location.hostname === "localhost"
|
window.location.hostname === "localhost"
|
||||||
? "/dist/web/install-button.js"
|
? "/dist/web/install-button.js"
|
||||||
: "https://unpkg.com/esp-web-tools@3.6.0/dist/web/install-button.js?module"
|
: "https://unpkg.com/esp-web-tools@4.0.0/dist/web/install-button.js?module"
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
@ -112,8 +121,8 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
To try it out and install
|
To try it out and install
|
||||||
<a href="https://esphome.io">the ESPHome firmware</a>, connect an ESP to
|
<a href="https://esphome.io">ESPHome</a> on an ESP, connect it to your
|
||||||
your computer and hit the button:
|
computer and hit the button:
|
||||||
</p>
|
</p>
|
||||||
<esp-web-install-button
|
<esp-web-install-button
|
||||||
log-console
|
log-console
|
||||||
@ -209,7 +218,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
<script
|
<script
|
||||||
type="module"
|
type="module"
|
||||||
src="https://unpkg.com/esp-web-tools@3.6.0/dist/web/install-button.js?module"
|
src="https://unpkg.com/esp-web-tools@4.0.0/dist/web/install-button.js?module"
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
<esp-web-install-button
|
<esp-web-install-button
|
||||||
@ -243,17 +252,17 @@
|
|||||||
ESP Web Tools manifest describe the firmware that you want to install.
|
ESP Web Tools manifest describe the firmware that you want to install.
|
||||||
It allows specifying different builds for the different types of ESP
|
It allows specifying different builds for the different types of ESP
|
||||||
devices. Current supported chip families are <code>ESP8266</code>,
|
devices. Current supported chip families are <code>ESP8266</code>,
|
||||||
<code>ESP32</code>, <code>ESP32-C3</code> and <code>ESP32-S2</code>. The
|
<code>ESP32</code>, <code>ESP32C3</code> and <code>ESP32S2</code>. The
|
||||||
correct build will be automatically selected based on the type of the
|
correct build will be automatically selected based on the type of the
|
||||||
ESP device we detect via the serial port.
|
ESP device we detect via the serial port.
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
{
|
{
|
||||||
"name": "ESPHome",
|
"name": "ESPHome",
|
||||||
|
"version": "2021.11.0",
|
||||||
"builds": [
|
"builds": [
|
||||||
{
|
{
|
||||||
"chipFamily": "ESP32",
|
"chipFamily": "ESP32",
|
||||||
"improv": true,
|
|
||||||
"parts": [
|
"parts": [
|
||||||
{ "path": "bootloader.bin", "offset": 4096 },
|
{ "path": "bootloader.bin", "offset": 4096 },
|
||||||
{ "path": "partitions.bin", "offset": 32768 },
|
{ "path": "partitions.bin", "offset": 32768 },
|
||||||
@ -276,7 +285,22 @@
|
|||||||
where it should be installed. Part paths are resolved relative to the
|
where it should be installed. Part paths are resolved relative to the
|
||||||
path of the manifest, but can also be URLs to other hosts.
|
path of the manifest, but can also be URLs to other hosts.
|
||||||
</p>
|
</p>
|
||||||
|
<h3 id="improv">Wi-Fi provisioning</h3>
|
||||||
<p>
|
<p>
|
||||||
|
ESP Web Tools has support for the
|
||||||
|
<a href="https://www.improv-wifi.com/serial"
|
||||||
|
>Improv Wi-Fi serial standard</a
|
||||||
|
>. This is an open standard to allow configuring Wi-Fi via the serial
|
||||||
|
port.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If Improv is supported, a user will be guided to connect the device to
|
||||||
|
the network after installation. It also allows the user to connect
|
||||||
|
already installed devices and re-configure the wireless network
|
||||||
|
settings.
|
||||||
|
</p>
|
||||||
|
<p>TODO EXAMPLE VIDEO</p>
|
||||||
|
<!-- <p>
|
||||||
Each build also allows you to specify if it supports
|
Each build also allows you to specify if it supports
|
||||||
<a href="https://www.improv-wifi.com">the Improv Wi-Fi standard</a>. If
|
<a href="https://www.improv-wifi.com">the Improv Wi-Fi standard</a>. If
|
||||||
it does, the user will be offered to configure the Wi-Fi after
|
it does, the user will be offered to configure the Wi-Fi after
|
||||||
@ -292,7 +316,7 @@
|
|||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<h3 id="customize">Customizing the look and feel</h3>
|
<h3 id="customize">Customizing the look and feel</h3>
|
||||||
<p>
|
<p>
|
||||||
|
742
package-lock.json
generated
742
package-lock.json
generated
@ -1,15 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "esp-web-tools",
|
"name": "esp-web-tools",
|
||||||
"version": "3.6.0",
|
"version": "4.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "3.6.0",
|
"name": "esp-web-tools",
|
||||||
|
"version": "4.0.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"@material/mwc-checkbox": "^0.25.3",
|
||||||
|
"@material/mwc-circular-progress": "^0.25.3",
|
||||||
|
"@material/mwc-dialog": "^0.25.3",
|
||||||
|
"@material/mwc-icon-button": "^0.25.3",
|
||||||
"@material/mwc-linear-progress": "^0.25.1",
|
"@material/mwc-linear-progress": "^0.25.1",
|
||||||
"esp-web-flasher": "^3.2.0",
|
"@material/mwc-textfield": "^0.25.3",
|
||||||
|
"esp-web-flasher": "^4.0.0",
|
||||||
|
"improv-wifi-serial-sdk": "^1.0.0",
|
||||||
"lit": "^2.0.0",
|
"lit": "^2.0.0",
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
},
|
},
|
||||||
@ -72,6 +80,69 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/button": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/button/-/button-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-DB0MAvdIGWKuFwlQ57hjv7ZuHIioT2mnG7RWtL7ZoCWoY45nCrsbJirmX5zZFipm9gIOJ3YnIkIrUyMVSrDX+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/tokens": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/circular-progress": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-Gi6Ika8MEZQOT3Qei2NfTj+sRWxCDFjchPM7szNjIKgL2DyH03bHmodQFVcyBFiPWEcWMc/mqVYgGf/XJXs85w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/progress-indicator": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/density": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/density/-/density-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-zOR5wISqPVr8KS/ERNC1jdRV9O832lzclyS9Ea20rDrWfuOiYsQ9bbIk12xWlxpgsn7r9fxQJyd1O2SURoHdRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/dialog": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-NfQR0fmNS/y2iRAx5YeODLLywBAnSyZI/CL9GUq4NiNj+FeSxe+5bhG1p9NxHeGMjEVrl6fG5L9ql7lqtfQaYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/button": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/icon-button": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/tokens": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/dom": {
|
"node_modules/@material/dom": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/dom/-/dom-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/dom/-/dom-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -81,6 +152,19 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/elevation": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-AqN/tsTGGyBzZ7CtoSMBY9bDYvCuUt98EUfiGjZGXcf4HgoHV3Cn/JSLrhru5Cq8Nx6HF6AmHh3dQCfNCQduew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/feature-targeting": {
|
"node_modules/@material/feature-targeting": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -89,6 +173,49 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/floating-label": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-Cp0/LngkW6/uZWbEDTe3Ox143V4kYtxl9twiM3XLKd6a67JHCzneQWFzC0qSg90b3r5O+1zOkT3ZMF2Pbu2Vwg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/icon-button": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-9P6cjRqKtjE6ML+r5yz0ExU/f2KLdNabHQxmO6RpKd/FnjTyP1NcWqqj8dsvo/DZ7mOtT1MIThgkQDdiMqcYLg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/line-ripple": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-LlyiyxpHNlFt0PZ8Q2tvOPbjNcgm3L7tUebXsM7iGyoKXfj0HwyDI31S0KgtU3Vs5DIK4U4mnRWtoAxtBW6Jfg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/linear-progress": {
|
"node_modules/@material/linear-progress": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -115,6 +242,94 @@
|
|||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/mwc-button": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-button/-/mwc-button-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-usHEKchj9hqetY7n0yebTz1Pk9Z+9W/sNZheFoSaiWQCv9XhtCdKkHH0MXTv8SpwxWuEKUf/XjtyvikGIcIn7w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/mwc-icon": "^0.25.3",
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-checkbox": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-checkbox/-/mwc-checkbox-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-PSh9IAgQK4XiDzBwgclheejkA4cbZ3K9V1JTTl/YVRDD/OLLM+Bh8tbnAg/1kGVlPWOUfDrYCcZ0gg472ca7gw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-circular-progress": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-circular-progress/-/mwc-circular-progress-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-ajgSzfdRfq0/sZg0Z5W/ZpgZwD8Ioj59m5ScCPXXdkRoVHf7+8lsD/2Fh4095GfoYE4PWSkXYVlWsQCx+aJbcA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/circular-progress": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/theme": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-dialog": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-dialog/-/mwc-dialog-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-UpxAYAzKXO1MW4ezpiYfEQgov08p0J8KDVKqKrMwg7lsZRkAtUMk4YJkM6qmWGqGPqd/cN++42PMPHAISJH3yA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/dialog": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"blocking-elements": "^0.1.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1",
|
||||||
|
"wicg-inert": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-floating-label": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-floating-label/-/mwc-floating-label-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-3uFMi8Y680P0nzP5zih4YuOZJLl/C6Ux9G810Unwo44zblG/ckgJlFiM+T+oR+OH5KM8LbfNlV0ypo7FT5zYJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/floating-label": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-icon": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-36076AWZIRSr8qYOLjuDDkxej/HA0XAosrj7TS1ZeLlUBnLUtbDtvc1S7KSa0hqez7ouzOqGaWK24yoNnTa2OA==",
|
||||||
|
"dependencies": {
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-icon-button": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-icon-button/-/mwc-icon-button-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-FexkMpK3ZSHh7NF+PIqvVhvAbBOgFDYPck/lqnxIDC3VGJ0rjD/1MqevDy2fY6IcHGlc8Ai7VuYbdQ6Cvw8WcQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-line-ripple": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-line-ripple/-/mwc-line-ripple-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-ANJzSyumb+shBVTIhqF1+YByPU/EpFXxI9CS26qThFqlUDpYXg5xcoZpkMSmZv3Wv/loF1rs2mJfFWOcC6nFnw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/line-ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/mwc-linear-progress": {
|
"node_modules/@material/mwc-linear-progress": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.3",
|
||||||
"resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
|
||||||
@ -127,6 +342,59 @@
|
|||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/mwc-notched-outline": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-notched-outline/-/mwc-notched-outline-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-8jvU8GD0Pke+pfTQ0PdXpZmkU3XIHhMVY6AHM/2IQrXHkVZmAm9kbwL7ne3Ao+6f5n+DeXDGd+SG9U6ZZjD7gw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/notched-outline": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-ripple": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-ripple/-/mwc-ripple-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-G/gt/csxgME6/sAku3GiuB0O2LLvoPWsRTLq/9iABpaGLJjqaKHvNg/IVzNDdF3YZT7EORgR9cBWWl7umA4i4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/dom": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/mwc-textfield": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-textfield/-/mwc-textfield-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-stpZ8sEyo2Mb9fG2XCoTc1Kom8oRXZiVI5rU88GtfcBU7nH0em8S4grq9X1mVfUG6Cfi1G/T+avCSIhzbYtr0w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/floating-label": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/line-ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-floating-label": "^0.25.3",
|
||||||
|
"@material/mwc-line-ripple": "^0.25.3",
|
||||||
|
"@material/mwc-notched-outline": "^0.25.3",
|
||||||
|
"@material/textfield": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/notched-outline": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-gtn+IKAiX2rbfbX3a9aDlfUoKCEYrlAPOZifKXUaZ4UJYMNLzZuAqy7l5Ds30emtqUE22mySTEWqhzK6dePKsA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/floating-label": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/progress-indicator": {
|
"node_modules/@material/progress-indicator": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -135,6 +403,20 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/ripple": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-3FLCLj8X7KrFfuYBHJg1b7Odb3V/AW7fxk3m1i1zhDnygKmlQ/abVucH1s2qbX3Y+JIiq+5/C5407h9BFtOf+A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/rtl": {
|
"node_modules/@material/rtl": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -144,6 +426,38 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/shape": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/shape/-/shape-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-VjcQltd1uF9ugvLExMy00SMISjy/370o8lsZlb1T+xHyhXHL3UxeuWYLW5Amq6mbx65+c9Df9WmlXXOdebpEkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/textfield": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-KBPgpvvVFBfLx9nc6+wWOS2hJ40JVwh5KBjMoYbiOEFLf0O7SgCAVREHaFAXrPsC8AeTyUipx6TReONIGfMCPQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/floating-label": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/line-ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/notched-outline": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/theme": {
|
"node_modules/@material/theme": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/theme/-/theme-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/theme/-/theme-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -153,6 +467,35 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@material/tokens": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-mgar9gsLv00HTvXIDvNR1vEEXpfKgeWhVTO8a7aWofSNyENNOVc5ImJwBgCAMb5SgLHBi6w8/c1tPzjOewBfCA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/touch-target": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-xA6TTHN7aOTXg/+c6mQJlogzTD+Sp8WPC5TK8RBXbQxEykGXGW15p+H9pG+rX/gzD5iehnHRBrDUFmAGoskhcQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@material/typography": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/typography/-/typography-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-WOCdcNkD5KBRAwICcRqWBRG3cDkyrwK5USTNmG0oxnwnZAN7daOpPTdLppVAhadE7faj8d67ON+V9pH7+T62FQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-json": {
|
"node_modules/@rollup/plugin-json": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
|
||||||
@ -337,6 +680,11 @@
|
|||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/blocking-elements": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/blocking-elements/-/blocking-elements-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-/SLWbEzMoVIMZACCyhD/4Ya2M1PWP1qMKuiymowPcI+PdWDARqeARBjhj73kbUBCxEmTZCUu5TAqxtwUO9C1Ig=="
|
||||||
|
},
|
||||||
"node_modules/boxen": {
|
"node_modules/boxen": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
||||||
@ -639,9 +987,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esp-web-flasher": {
|
"node_modules/esp-web-flasher": {
|
||||||
"version": "3.2.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/esp-web-flasher/-/esp-web-flasher-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/esp-web-flasher/-/esp-web-flasher-4.0.0.tgz",
|
||||||
"integrity": "sha512-jcJtWb5QuENWzeasfGYcJP/MV+XmRQelNRoOVCAKXcBJFh9h9NnfPXJtpoG+RsIMqb7hDdutomz/bBoBUH6urw==",
|
"integrity": "sha512-7d23iEkEjvrYkywLZtvg69GAitRJVE73dN6nmyWNmTvCe55b0UTzndLJtTHANbAiNzpgmJ7/kYnt202A7BD75A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pako": "^2.0.3",
|
"pako": "^2.0.3",
|
||||||
"tslib": "^2.2.0"
|
"tslib": "^2.2.0"
|
||||||
@ -760,6 +1108,19 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/improv-wifi-serial-sdk": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/improv-wifi-serial-sdk/-/improv-wifi-serial-sdk-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-R3NM7Ry9DjTyT5B6iIIZjW5LMia64PwLEJnue5lfYlmqHyJuNMxkWrGomqG7AxQLLCul7CPN1qs52nkJglqYsg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"@material/mwc-circular-progress": "^0.25.3",
|
||||||
|
"@material/mwc-dialog": "^0.25.3",
|
||||||
|
"@material/mwc-textfield": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ini": {
|
"node_modules/ini": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
@ -1469,6 +1830,11 @@
|
|||||||
"which": "bin/which"
|
"which": "bin/which"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wicg-inert": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-PhBaNh8ur9Xm4Ggy4umelwNIP6pPP1bv3EaWaKqfb/QNme2rdLjm7wIInvV4WhxVHhzA4Spgw9qNSqWtB/ca2A=="
|
||||||
|
},
|
||||||
"node_modules/widest-line": {
|
"node_modules/widest-line": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
@ -1586,6 +1952,69 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/button": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/button/-/button-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-DB0MAvdIGWKuFwlQ57hjv7ZuHIioT2mnG7RWtL7ZoCWoY45nCrsbJirmX5zZFipm9gIOJ3YnIkIrUyMVSrDX+g==",
|
||||||
|
"requires": {
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/tokens": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/circular-progress": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-Gi6Ika8MEZQOT3Qei2NfTj+sRWxCDFjchPM7szNjIKgL2DyH03bHmodQFVcyBFiPWEcWMc/mqVYgGf/XJXs85w==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/progress-indicator": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/density": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/density/-/density-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-zOR5wISqPVr8KS/ERNC1jdRV9O832lzclyS9Ea20rDrWfuOiYsQ9bbIk12xWlxpgsn7r9fxQJyd1O2SURoHdRA==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/dialog": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-NfQR0fmNS/y2iRAx5YeODLLywBAnSyZI/CL9GUq4NiNj+FeSxe+5bhG1p9NxHeGMjEVrl6fG5L9ql7lqtfQaYQ==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/button": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/icon-button": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/tokens": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/dom": {
|
"@material/dom": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/dom/-/dom-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/dom/-/dom-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1595,6 +2024,19 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/elevation": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-AqN/tsTGGyBzZ7CtoSMBY9bDYvCuUt98EUfiGjZGXcf4HgoHV3Cn/JSLrhru5Cq8Nx6HF6AmHh3dQCfNCQduew==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/feature-targeting": {
|
"@material/feature-targeting": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1603,6 +2045,49 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/floating-label": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-Cp0/LngkW6/uZWbEDTe3Ox143V4kYtxl9twiM3XLKd6a67JHCzneQWFzC0qSg90b3r5O+1zOkT3ZMF2Pbu2Vwg==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/icon-button": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-9P6cjRqKtjE6ML+r5yz0ExU/f2KLdNabHQxmO6RpKd/FnjTyP1NcWqqj8dsvo/DZ7mOtT1MIThgkQDdiMqcYLg==",
|
||||||
|
"requires": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/touch-target": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/line-ripple": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-LlyiyxpHNlFt0PZ8Q2tvOPbjNcgm3L7tUebXsM7iGyoKXfj0HwyDI31S0KgtU3Vs5DIK4U4mnRWtoAxtBW6Jfg==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/linear-progress": {
|
"@material/linear-progress": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1629,6 +2114,94 @@
|
|||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/mwc-button": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-button/-/mwc-button-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-usHEKchj9hqetY7n0yebTz1Pk9Z+9W/sNZheFoSaiWQCv9XhtCdKkHH0MXTv8SpwxWuEKUf/XjtyvikGIcIn7w==",
|
||||||
|
"requires": {
|
||||||
|
"@material/mwc-icon": "^0.25.3",
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-checkbox": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-checkbox/-/mwc-checkbox-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-PSh9IAgQK4XiDzBwgclheejkA4cbZ3K9V1JTTl/YVRDD/OLLM+Bh8tbnAg/1kGVlPWOUfDrYCcZ0gg472ca7gw==",
|
||||||
|
"requires": {
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-circular-progress": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-circular-progress/-/mwc-circular-progress-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-ajgSzfdRfq0/sZg0Z5W/ZpgZwD8Ioj59m5ScCPXXdkRoVHf7+8lsD/2Fh4095GfoYE4PWSkXYVlWsQCx+aJbcA==",
|
||||||
|
"requires": {
|
||||||
|
"@material/circular-progress": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/theme": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-dialog": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-dialog/-/mwc-dialog-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-UpxAYAzKXO1MW4ezpiYfEQgov08p0J8KDVKqKrMwg7lsZRkAtUMk4YJkM6qmWGqGPqd/cN++42PMPHAISJH3yA==",
|
||||||
|
"requires": {
|
||||||
|
"@material/dialog": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"blocking-elements": "^0.1.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1",
|
||||||
|
"wicg-inert": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-floating-label": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-floating-label/-/mwc-floating-label-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-3uFMi8Y680P0nzP5zih4YuOZJLl/C6Ux9G810Unwo44zblG/ckgJlFiM+T+oR+OH5KM8LbfNlV0ypo7FT5zYJA==",
|
||||||
|
"requires": {
|
||||||
|
"@material/floating-label": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-icon": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-36076AWZIRSr8qYOLjuDDkxej/HA0XAosrj7TS1ZeLlUBnLUtbDtvc1S7KSa0hqez7ouzOqGaWK24yoNnTa2OA==",
|
||||||
|
"requires": {
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-icon-button": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-icon-button/-/mwc-icon-button-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-FexkMpK3ZSHh7NF+PIqvVhvAbBOgFDYPck/lqnxIDC3VGJ0rjD/1MqevDy2fY6IcHGlc8Ai7VuYbdQ6Cvw8WcQ==",
|
||||||
|
"requires": {
|
||||||
|
"@material/mwc-ripple": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-line-ripple": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-line-ripple/-/mwc-line-ripple-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-ANJzSyumb+shBVTIhqF1+YByPU/EpFXxI9CS26qThFqlUDpYXg5xcoZpkMSmZv3Wv/loF1rs2mJfFWOcC6nFnw==",
|
||||||
|
"requires": {
|
||||||
|
"@material/line-ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/mwc-linear-progress": {
|
"@material/mwc-linear-progress": {
|
||||||
"version": "0.25.3",
|
"version": "0.25.3",
|
||||||
"resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
|
||||||
@ -1641,6 +2214,59 @@
|
|||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/mwc-notched-outline": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-notched-outline/-/mwc-notched-outline-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-8jvU8GD0Pke+pfTQ0PdXpZmkU3XIHhMVY6AHM/2IQrXHkVZmAm9kbwL7ne3Ao+6f5n+DeXDGd+SG9U6ZZjD7gw==",
|
||||||
|
"requires": {
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/notched-outline": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-ripple": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-ripple/-/mwc-ripple-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-G/gt/csxgME6/sAku3GiuB0O2LLvoPWsRTLq/9iABpaGLJjqaKHvNg/IVzNDdF3YZT7EORgR9cBWWl7umA4i4Q==",
|
||||||
|
"requires": {
|
||||||
|
"@material/dom": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/mwc-textfield": {
|
||||||
|
"version": "0.25.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/mwc-textfield/-/mwc-textfield-0.25.3.tgz",
|
||||||
|
"integrity": "sha512-stpZ8sEyo2Mb9fG2XCoTc1Kom8oRXZiVI5rU88GtfcBU7nH0em8S4grq9X1mVfUG6Cfi1G/T+avCSIhzbYtr0w==",
|
||||||
|
"requires": {
|
||||||
|
"@material/floating-label": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/line-ripple": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/mwc-base": "^0.25.3",
|
||||||
|
"@material/mwc-floating-label": "^0.25.3",
|
||||||
|
"@material/mwc-line-ripple": "^0.25.3",
|
||||||
|
"@material/mwc-notched-outline": "^0.25.3",
|
||||||
|
"@material/textfield": "=14.0.0-canary.261f2db59.0",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/notched-outline": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-gtn+IKAiX2rbfbX3a9aDlfUoKCEYrlAPOZifKXUaZ4UJYMNLzZuAqy7l5Ds30emtqUE22mySTEWqhzK6dePKsA==",
|
||||||
|
"requires": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/floating-label": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/progress-indicator": {
|
"@material/progress-indicator": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1649,6 +2275,20 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/ripple": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-3FLCLj8X7KrFfuYBHJg1b7Odb3V/AW7fxk3m1i1zhDnygKmlQ/abVucH1s2qbX3Y+JIiq+5/C5407h9BFtOf+A==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/rtl": {
|
"@material/rtl": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1658,6 +2298,38 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/shape": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/shape/-/shape-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-VjcQltd1uF9ugvLExMy00SMISjy/370o8lsZlb1T+xHyhXHL3UxeuWYLW5Amq6mbx65+c9Df9WmlXXOdebpEkw==",
|
||||||
|
"requires": {
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/textfield": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-KBPgpvvVFBfLx9nc6+wWOS2hJ40JVwh5KBjMoYbiOEFLf0O7SgCAVREHaFAXrPsC8AeTyUipx6TReONIGfMCPQ==",
|
||||||
|
"requires": {
|
||||||
|
"@material/animation": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/density": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/dom": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/floating-label": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/line-ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/notched-outline": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/ripple": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/shape": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/typography": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@material/theme": {
|
"@material/theme": {
|
||||||
"version": "14.0.0-canary.261f2db59.0",
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material/theme/-/theme-14.0.0-canary.261f2db59.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material/theme/-/theme-14.0.0-canary.261f2db59.0.tgz",
|
||||||
@ -1667,6 +2339,35 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@material/tokens": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-mgar9gsLv00HTvXIDvNR1vEEXpfKgeWhVTO8a7aWofSNyENNOVc5ImJwBgCAMb5SgLHBi6w8/c1tPzjOewBfCA==",
|
||||||
|
"requires": {
|
||||||
|
"@material/elevation": "14.0.0-canary.261f2db59.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/touch-target": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-xA6TTHN7aOTXg/+c6mQJlogzTD+Sp8WPC5TK8RBXbQxEykGXGW15p+H9pG+rX/gzD5iehnHRBrDUFmAGoskhcQ==",
|
||||||
|
"requires": {
|
||||||
|
"@material/base": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/rtl": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material/typography": {
|
||||||
|
"version": "14.0.0-canary.261f2db59.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material/typography/-/typography-14.0.0-canary.261f2db59.0.tgz",
|
||||||
|
"integrity": "sha512-WOCdcNkD5KBRAwICcRqWBRG3cDkyrwK5USTNmG0oxnwnZAN7daOpPTdLppVAhadE7faj8d67ON+V9pH7+T62FQ==",
|
||||||
|
"requires": {
|
||||||
|
"@material/feature-targeting": "14.0.0-canary.261f2db59.0",
|
||||||
|
"@material/theme": "14.0.0-canary.261f2db59.0",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/plugin-json": {
|
"@rollup/plugin-json": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz",
|
||||||
@ -1824,6 +2525,11 @@
|
|||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"blocking-elements": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/blocking-elements/-/blocking-elements-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-/SLWbEzMoVIMZACCyhD/4Ya2M1PWP1qMKuiymowPcI+PdWDARqeARBjhj73kbUBCxEmTZCUu5TAqxtwUO9C1Ig=="
|
||||||
|
},
|
||||||
"boxen": {
|
"boxen": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
||||||
@ -2061,9 +2767,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"esp-web-flasher": {
|
"esp-web-flasher": {
|
||||||
"version": "3.2.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/esp-web-flasher/-/esp-web-flasher-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/esp-web-flasher/-/esp-web-flasher-4.0.0.tgz",
|
||||||
"integrity": "sha512-jcJtWb5QuENWzeasfGYcJP/MV+XmRQelNRoOVCAKXcBJFh9h9NnfPXJtpoG+RsIMqb7hDdutomz/bBoBUH6urw==",
|
"integrity": "sha512-7d23iEkEjvrYkywLZtvg69GAitRJVE73dN6nmyWNmTvCe55b0UTzndLJtTHANbAiNzpgmJ7/kYnt202A7BD75A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"pako": "^2.0.3",
|
"pako": "^2.0.3",
|
||||||
"tslib": "^2.2.0"
|
"tslib": "^2.2.0"
|
||||||
@ -2163,6 +2869,19 @@
|
|||||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"improv-wifi-serial-sdk": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/improv-wifi-serial-sdk/-/improv-wifi-serial-sdk-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-R3NM7Ry9DjTyT5B6iIIZjW5LMia64PwLEJnue5lfYlmqHyJuNMxkWrGomqG7AxQLLCul7CPN1qs52nkJglqYsg==",
|
||||||
|
"requires": {
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"@material/mwc-circular-progress": "^0.25.3",
|
||||||
|
"@material/mwc-dialog": "^0.25.3",
|
||||||
|
"@material/mwc-textfield": "^0.25.3",
|
||||||
|
"lit": "^2.0.0",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
@ -2747,6 +3466,11 @@
|
|||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wicg-inert": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/wicg-inert/-/wicg-inert-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-PhBaNh8ur9Xm4Ggy4umelwNIP6pPP1bv3EaWaKqfb/QNme2rdLjm7wIInvV4WhxVHhzA4Spgw9qNSqWtB/ca2A=="
|
||||||
|
},
|
||||||
"widest-line": {
|
"widest-line": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
|
11
package.json
11
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "esp-web-tools",
|
"name": "esp-web-tools",
|
||||||
"version": "3.6.0",
|
"version": "4.0.0",
|
||||||
"description": "Web tools for ESP devices",
|
"description": "Web tools for ESP devices",
|
||||||
"main": "dist/install-button.js",
|
"main": "dist/install-button.js",
|
||||||
"repository": "https://github.com/esphome/web",
|
"repository": "https://github.com/esphome/web",
|
||||||
@ -21,8 +21,15 @@
|
|||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@material/mwc-button": "^0.25.3",
|
||||||
|
"@material/mwc-checkbox": "^0.25.3",
|
||||||
|
"@material/mwc-circular-progress": "^0.25.3",
|
||||||
|
"@material/mwc-dialog": "^0.25.3",
|
||||||
|
"@material/mwc-icon-button": "^0.25.3",
|
||||||
"@material/mwc-linear-progress": "^0.25.1",
|
"@material/mwc-linear-progress": "^0.25.1",
|
||||||
"esp-web-flasher": "^3.2.0",
|
"@material/mwc-textfield": "^0.25.3",
|
||||||
|
"esp-web-flasher": "^4.0.0",
|
||||||
|
"improv-wifi-serial-sdk": "^1.0.0",
|
||||||
"lit": "^2.0.0",
|
"lit": "^2.0.0",
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ trap "kill 0" EXIT
|
|||||||
# Run tsc once as rollup expects those files
|
# Run tsc once as rollup expects those files
|
||||||
tsc || true
|
tsc || true
|
||||||
|
|
||||||
npm exec -- serve &
|
npm exec -- serve -p 5001 &
|
||||||
npm exec -- tsc --watch &
|
npm exec -- tsc --watch &
|
||||||
npm exec -- rollup -c --watch &
|
npm exec -- rollup -c --watch &
|
||||||
wait
|
wait
|
||||||
|
14
src/components/ewt-button.ts
Normal file
14
src/components/ewt-button.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { ButtonBase } from "@material/mwc-button/mwc-button-base";
|
||||||
|
import { styles } from "@material/mwc-button/styles.css";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-button": EwtButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EwtButton extends ButtonBase {
|
||||||
|
static override styles = [styles];
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-button", EwtButton);
|
14
src/components/ewt-circular-progress.ts
Normal file
14
src/components/ewt-circular-progress.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { CircularProgressBase } from "@material/mwc-circular-progress/mwc-circular-progress-base";
|
||||||
|
import { styles } from "@material/mwc-circular-progress/mwc-circular-progress.css";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-circular-progress": EwtCircularProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EwtCircularProgress extends CircularProgressBase {
|
||||||
|
static override styles = [styles];
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-circular-progress", EwtCircularProgress);
|
227
src/components/ewt-console.ts
Normal file
227
src/components/ewt-console.ts
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import { ColoredConsole } from "../util/console-color";
|
||||||
|
import { sleep } from "../util/sleep";
|
||||||
|
import { LineBreakTransformer } from "../util/line-break-transformer";
|
||||||
|
import { Logger } from "../const";
|
||||||
|
|
||||||
|
export class EwtConsole extends HTMLElement {
|
||||||
|
public port!: SerialPort;
|
||||||
|
public logger!: Logger;
|
||||||
|
|
||||||
|
private _console?: ColoredConsole;
|
||||||
|
private _cancelConnection?: () => Promise<void>;
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
if (this._console) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const shadowRoot = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
|
shadowRoot.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host, input {
|
||||||
|
background-color: #1c1c1c;
|
||||||
|
color: #ddd;
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier,
|
||||||
|
monospace;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
.log {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: calc(100% - 28px);
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.log-italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.log-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.log-strikethrough {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.log-underline.log-strikethrough {
|
||||||
|
text-decoration: underline line-through;
|
||||||
|
}
|
||||||
|
.log-secret {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.log-secret-redacted {
|
||||||
|
opacity: 0;
|
||||||
|
width: 1px;
|
||||||
|
font-size: 1px;
|
||||||
|
}
|
||||||
|
.log-fg-black {
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
}
|
||||||
|
.log-fg-red {
|
||||||
|
color: rgb(255, 0, 0);
|
||||||
|
}
|
||||||
|
.log-fg-green {
|
||||||
|
color: rgb(0, 255, 0);
|
||||||
|
}
|
||||||
|
.log-fg-yellow {
|
||||||
|
color: rgb(255, 255, 0);
|
||||||
|
}
|
||||||
|
.log-fg-blue {
|
||||||
|
color: rgb(0, 0, 255);
|
||||||
|
}
|
||||||
|
.log-fg-magenta {
|
||||||
|
color: rgb(255, 0, 255);
|
||||||
|
}
|
||||||
|
.log-fg-cyan {
|
||||||
|
color: rgb(0, 255, 255);
|
||||||
|
}
|
||||||
|
.log-fg-white {
|
||||||
|
color: rgb(187, 187, 187);
|
||||||
|
}
|
||||||
|
.log-bg-black {
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
.log-bg-red {
|
||||||
|
background-color: rgb(255, 0, 0);
|
||||||
|
}
|
||||||
|
.log-bg-green {
|
||||||
|
background-color: rgb(0, 255, 0);
|
||||||
|
}
|
||||||
|
.log-bg-yellow {
|
||||||
|
background-color: rgb(255, 255, 0);
|
||||||
|
}
|
||||||
|
.log-bg-blue {
|
||||||
|
background-color: rgb(0, 0, 255);
|
||||||
|
}
|
||||||
|
.log-bg-magenta {
|
||||||
|
background-color: rgb(255, 0, 255);
|
||||||
|
}
|
||||||
|
.log-bg-cyan {
|
||||||
|
background-color: rgb(0, 255, 255);
|
||||||
|
}
|
||||||
|
.log-bg-white {
|
||||||
|
background-color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px 0 16px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 4px;
|
||||||
|
margin: 0 8px;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="log"></div>
|
||||||
|
<form>
|
||||||
|
>
|
||||||
|
<input autofocus>
|
||||||
|
<button type="button">Send</button>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this._console = new ColoredConsole(this.shadowRoot!.querySelector("div")!);
|
||||||
|
const input = this.shadowRoot!.querySelector("input")!;
|
||||||
|
|
||||||
|
this.addEventListener("click", () => input.focus());
|
||||||
|
|
||||||
|
input.addEventListener("keydown", (ev) => {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._sendCommand();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const connection = this._connect(abortController.signal);
|
||||||
|
this._cancelConnection = () => {
|
||||||
|
abortController.abort();
|
||||||
|
return connection;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _connect(abortSignal: AbortSignal) {
|
||||||
|
this.logger.debug("Starting console read loop");
|
||||||
|
try {
|
||||||
|
await this.port
|
||||||
|
.readable!.pipeThrough(new TextDecoderStream(), {
|
||||||
|
signal: abortSignal,
|
||||||
|
})
|
||||||
|
.pipeThrough(new TransformStream(new LineBreakTransformer()))
|
||||||
|
.pipeTo(
|
||||||
|
new WritableStream({
|
||||||
|
write: (chunk) => {
|
||||||
|
this._console!.addLine(chunk);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (!abortSignal.aborted) {
|
||||||
|
this._console!.addLine("");
|
||||||
|
this._console!.addLine("");
|
||||||
|
this._console!.addLine("Terminal disconnected");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this._console!.addLine("");
|
||||||
|
this._console!.addLine("");
|
||||||
|
this._console!.addLine(`Terminal disconnected: ${e}`);
|
||||||
|
} finally {
|
||||||
|
await sleep(100);
|
||||||
|
this.logger.debug("Finished console read loop");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _sendCommand() {
|
||||||
|
const input = this.shadowRoot!.querySelector("input")!;
|
||||||
|
const command = input.value;
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const writer = this.port.writable!.getWriter();
|
||||||
|
await writer.write(encoder.encode(command));
|
||||||
|
this._console!.addLine(`> ${command}\n`);
|
||||||
|
input.value = "";
|
||||||
|
input.focus();
|
||||||
|
try {
|
||||||
|
writer.releaseLock();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Ignoring release lock error", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async disconnect() {
|
||||||
|
if (this._cancelConnection) {
|
||||||
|
await this._cancelConnection();
|
||||||
|
this._cancelConnection = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async reset() {
|
||||||
|
this.logger.debug("Triggering reset.");
|
||||||
|
await this.port.setSignals({
|
||||||
|
dataTerminalReady: false,
|
||||||
|
requestToSend: true,
|
||||||
|
});
|
||||||
|
await this.port.setSignals({
|
||||||
|
dataTerminalReady: false,
|
||||||
|
requestToSend: false,
|
||||||
|
});
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-console", EwtConsole);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-console": EwtConsole;
|
||||||
|
}
|
||||||
|
}
|
22
src/components/ewt-dialog.ts
Normal file
22
src/components/ewt-dialog.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { DialogBase } from "@material/mwc-dialog/mwc-dialog-base";
|
||||||
|
import { styles } from "@material/mwc-dialog/mwc-dialog.css";
|
||||||
|
import { css } from "lit";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-dialog": EwtDialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EwtDialog extends DialogBase {
|
||||||
|
static override styles = [
|
||||||
|
styles,
|
||||||
|
css`
|
||||||
|
.mdc-dialog__title {
|
||||||
|
padding-right: 52px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-dialog", EwtDialog);
|
14
src/components/ewt-icon-button.ts
Normal file
14
src/components/ewt-icon-button.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { IconButtonBase } from "@material/mwc-icon-button/mwc-icon-button-base";
|
||||||
|
import { styles } from "@material/mwc-icon-button/mwc-icon-button.css";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-icon-button": EwtIconButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EwtIconButton extends IconButtonBase {
|
||||||
|
static override styles = [styles];
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-icon-button", EwtIconButton);
|
14
src/components/ewt-textfield.ts
Normal file
14
src/components/ewt-textfield.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { TextFieldBase } from "@material/mwc-textfield/mwc-textfield-base";
|
||||||
|
import { styles } from "@material/mwc-textfield/mwc-textfield.css";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-textfield": EwtTextfield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EwtTextfield extends TextFieldBase {
|
||||||
|
static override styles = [styles];
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-textfield", EwtTextfield);
|
30
src/connect.ts
Normal file
30
src/connect.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import type { InstallButton } from "./install-button.js";
|
||||||
|
import "./install-dialog.js";
|
||||||
|
|
||||||
|
export const connect = async (button: InstallButton) => {
|
||||||
|
let port: SerialPort | undefined;
|
||||||
|
try {
|
||||||
|
port = await navigator.serial.requestPort();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("User cancelled request", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!port) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await port.open({ baudRate: 115200 });
|
||||||
|
|
||||||
|
const el = document.createElement("ewt-install-dialog");
|
||||||
|
el.port = port;
|
||||||
|
el.manifestPath = button.getAttribute("manifest")!;
|
||||||
|
el.addEventListener(
|
||||||
|
"closed",
|
||||||
|
() => {
|
||||||
|
port!.close();
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
document.body.appendChild(el);
|
||||||
|
};
|
26
src/const.ts
26
src/const.ts
@ -1,6 +1,11 @@
|
|||||||
|
export interface Logger {
|
||||||
|
log(msg: string, ...args: any[]): void;
|
||||||
|
error(msg: string, ...args: any[]): void;
|
||||||
|
debug(msg: string, ...args: any[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Build {
|
export interface Build {
|
||||||
chipFamily: "ESP32" | "ESP8266" | "ESP32-S2" | "ESP32-C3";
|
chipFamily: "ESP32" | "ESP8266" | "ESP32-S2" | "ESP32-C3";
|
||||||
improv: boolean;
|
|
||||||
parts: {
|
parts: {
|
||||||
path: string;
|
path: string;
|
||||||
offset: number;
|
offset: number;
|
||||||
@ -9,11 +14,12 @@ export interface Build {
|
|||||||
|
|
||||||
export interface Manifest {
|
export interface Manifest {
|
||||||
name: string;
|
name: string;
|
||||||
|
version: string;
|
||||||
builds: Build[];
|
builds: Build[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseFlashState {
|
export interface BaseFlashState {
|
||||||
state: State;
|
state: FlashStateType;
|
||||||
message: string;
|
message: string;
|
||||||
manifest?: Manifest;
|
manifest?: Manifest;
|
||||||
build?: Build;
|
build?: Build;
|
||||||
@ -21,36 +27,36 @@ export interface BaseFlashState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface InitializingState extends BaseFlashState {
|
export interface InitializingState extends BaseFlashState {
|
||||||
state: State.INITIALIZING;
|
state: FlashStateType.INITIALIZING;
|
||||||
details: { done: boolean };
|
details: { done: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ManifestState extends BaseFlashState {
|
export interface ManifestState extends BaseFlashState {
|
||||||
state: State.MANIFEST;
|
state: FlashStateType.MANIFEST;
|
||||||
details: { done: boolean };
|
details: { done: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreparingState extends BaseFlashState {
|
export interface PreparingState extends BaseFlashState {
|
||||||
state: State.PREPARING;
|
state: FlashStateType.PREPARING;
|
||||||
details: { done: boolean };
|
details: { done: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ErasingState extends BaseFlashState {
|
export interface ErasingState extends BaseFlashState {
|
||||||
state: State.ERASING;
|
state: FlashStateType.ERASING;
|
||||||
details: { done: boolean };
|
details: { done: boolean };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WritingState extends BaseFlashState {
|
export interface WritingState extends BaseFlashState {
|
||||||
state: State.WRITING;
|
state: FlashStateType.WRITING;
|
||||||
details: { bytesTotal: number; bytesWritten: number; percentage: number };
|
details: { bytesTotal: number; bytesWritten: number; percentage: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FinishedState extends BaseFlashState {
|
export interface FinishedState extends BaseFlashState {
|
||||||
state: State.FINISHED;
|
state: FlashStateType.FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ErrorState extends BaseFlashState {
|
export interface ErrorState extends BaseFlashState {
|
||||||
state: State.ERROR;
|
state: FlashStateType.ERROR;
|
||||||
details: { error: FlashError; details: string | Error };
|
details: { error: FlashError; details: string | Error };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +69,7 @@ export type FlashState =
|
|||||||
| FinishedState
|
| FinishedState
|
||||||
| ErrorState;
|
| ErrorState;
|
||||||
|
|
||||||
export const enum State {
|
export const enum FlashStateType {
|
||||||
INITIALIZING = "initializing",
|
INITIALIZING = "initializing",
|
||||||
MANIFEST = "manifest",
|
MANIFEST = "manifest",
|
||||||
PREPARING = "preparing",
|
PREPARING = "preparing",
|
||||||
|
138
src/flash-log.ts
138
src/flash-log.ts
@ -1,138 +0,0 @@
|
|||||||
import { css, html, HTMLTemplateResult, LitElement } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators.js";
|
|
||||||
import { classMap } from "lit/directives/class-map.js";
|
|
||||||
import { FlashState, State } from "./const";
|
|
||||||
|
|
||||||
interface Row {
|
|
||||||
state?: State;
|
|
||||||
message: HTMLTemplateResult | string;
|
|
||||||
error?: boolean;
|
|
||||||
action?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@customElement("esp-web-flash-log")
|
|
||||||
export class FlashLog extends LitElement {
|
|
||||||
@state() private _rows: Row[] = [];
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
return html`${this._rows.map(
|
|
||||||
(row) =>
|
|
||||||
html`<div
|
|
||||||
class=${classMap({
|
|
||||||
error: row.error === true,
|
|
||||||
action: row.action === true,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${row.message}
|
|
||||||
</div>`
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public willUpdate() {
|
|
||||||
this.toggleAttribute("hidden", !this._rows.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public clear() {
|
|
||||||
this._rows = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public processState(state: FlashState) {
|
|
||||||
if (state.state === State.ERROR) {
|
|
||||||
this.addError(state.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.addRow(state);
|
|
||||||
if (state.state === State.FINISHED) {
|
|
||||||
this.addAction(
|
|
||||||
html`<button @click=${this.clear}>Close this log</button>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add or replace a row.
|
|
||||||
*/
|
|
||||||
public addRow(row: Row) {
|
|
||||||
// If last entry has same ID, replace it.
|
|
||||||
if (
|
|
||||||
row.state &&
|
|
||||||
this._rows.length > 0 &&
|
|
||||||
this._rows[this._rows.length - 1].state === row.state
|
|
||||||
) {
|
|
||||||
const newRows = this._rows.slice(0, -1);
|
|
||||||
newRows.push(row);
|
|
||||||
this._rows = newRows;
|
|
||||||
} else {
|
|
||||||
this._rows = [...this._rows, row];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an error row
|
|
||||||
*/
|
|
||||||
public addError(message: Row["message"]) {
|
|
||||||
this.addRow({ message, error: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an action row
|
|
||||||
*/
|
|
||||||
public addAction(message: Row["message"]) {
|
|
||||||
this.addRow({ message, action: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove last row if state matches
|
|
||||||
*/
|
|
||||||
public removeRow(state: string) {
|
|
||||||
if (
|
|
||||||
this._rows.length > 0 &&
|
|
||||||
this._rows[this._rows.length - 1].state === state
|
|
||||||
) {
|
|
||||||
this._rows = this._rows.slice(0, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
margin-top: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
font-family: monospace;
|
|
||||||
background: var(--esp-tools-log-background, black);
|
|
||||||
color: var(--esp-tools-log-text-color, greenyellow);
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([hidden]) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: none;
|
|
||||||
color: inherit;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
font: inherit;
|
|
||||||
text-align: left;
|
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error {
|
|
||||||
color: var(--esp-tools-error-color, #dc3545);
|
|
||||||
}
|
|
||||||
|
|
||||||
.error,
|
|
||||||
.action {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"esp-web-flash-log": FlashLog;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement, state } from "lit/decorators.js";
|
|
||||||
import { FlashState, State } from "./const";
|
|
||||||
import "@material/mwc-linear-progress";
|
|
||||||
import { classMap } from "lit/directives/class-map.js";
|
|
||||||
|
|
||||||
@customElement("esp-web-flash-progress")
|
|
||||||
export class FlashProgress extends LitElement {
|
|
||||||
@state() private _state?: FlashState;
|
|
||||||
|
|
||||||
@state() private _indeterminate = true;
|
|
||||||
|
|
||||||
@state() private _progress = 0;
|
|
||||||
|
|
||||||
public processState(state: FlashState) {
|
|
||||||
this._state = state;
|
|
||||||
if (this._state.state === State.WRITING) {
|
|
||||||
this._indeterminate = false;
|
|
||||||
this._progress = this._state.details.percentage / 100;
|
|
||||||
}
|
|
||||||
if (this._state.state === State.ERROR) {
|
|
||||||
this._indeterminate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public clear() {
|
|
||||||
this._state = undefined;
|
|
||||||
this._progress = 0;
|
|
||||||
this._indeterminate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (!this._state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return html`<h2
|
|
||||||
class=${classMap({
|
|
||||||
error: this._state.state === State.ERROR,
|
|
||||||
done: this._state.state === State.FINISHED,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
${this._state.message}
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
${this._state.manifest
|
|
||||||
? html`${this._state.manifest.name}: ${this._state.chipFamily}`
|
|
||||||
: html` `}
|
|
||||||
</p>
|
|
||||||
<mwc-linear-progress
|
|
||||||
class=${classMap({
|
|
||||||
error: this._state.state === State.ERROR,
|
|
||||||
done: this._state.state === State.FINISHED,
|
|
||||||
})}
|
|
||||||
.indeterminate=${this._indeterminate}
|
|
||||||
.progress=${this._progress}
|
|
||||||
></mwc-linear-progress>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
--mdc-theme-primary: var(--esp-tools-progress-color, #03a9f4);
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: var(--esp-tools-error-color, #dc3545);
|
|
||||||
--mdc-theme-primary: var(--esp-tools-error-color, #dc3545);
|
|
||||||
}
|
|
||||||
.done {
|
|
||||||
color: var(--esp-tools-success-color, #28a745);
|
|
||||||
--mdc-theme-primary: var(--esp-tools-success-color, #28a745);
|
|
||||||
}
|
|
||||||
mwc-linear-progress {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
margin: 16px 0 0;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"esp-web-flash-progress": FlashProgress;
|
|
||||||
}
|
|
||||||
}
|
|
89
src/flash.ts
89
src/flash.ts
@ -1,9 +1,17 @@
|
|||||||
import { connect, ESPLoader, Logger } from "esp-web-flasher";
|
import { ESPLoader, Logger } from "esp-web-flasher";
|
||||||
import { Build, FlashError, FlashState, Manifest, State } from "./const";
|
import {
|
||||||
import { fireEvent, getChipFamilyName, sleep } from "./util";
|
Build,
|
||||||
|
FlashError,
|
||||||
|
FlashState,
|
||||||
|
Manifest,
|
||||||
|
FlashStateType,
|
||||||
|
} from "./const";
|
||||||
|
import { getChipFamilyName } from "./util/chip-family-name";
|
||||||
|
import { sleep } from "./util/sleep";
|
||||||
|
|
||||||
export const flash = async (
|
export const flash = async (
|
||||||
eventTarget: EventTarget,
|
onEvent: (state: FlashState) => void,
|
||||||
|
port: SerialPort,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
manifestPath: string,
|
manifestPath: string,
|
||||||
eraseFirst: boolean
|
eraseFirst: boolean
|
||||||
@ -12,47 +20,39 @@ export const flash = async (
|
|||||||
let build: Build | undefined;
|
let build: Build | undefined;
|
||||||
let chipFamily: ReturnType<typeof getChipFamilyName>;
|
let chipFamily: ReturnType<typeof getChipFamilyName>;
|
||||||
|
|
||||||
const fireStateEvent = (stateUpdate: FlashState) => {
|
const fireStateEvent = (stateUpdate: FlashState) =>
|
||||||
fireEvent(eventTarget, "state-changed", {
|
onEvent({
|
||||||
...stateUpdate,
|
...stateUpdate,
|
||||||
manifest,
|
manifest,
|
||||||
build,
|
build,
|
||||||
chipFamily,
|
chipFamily,
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const manifestURL = new URL(manifestPath, location.toString()).toString();
|
const manifestURL = new URL(manifestPath, location.toString()).toString();
|
||||||
const manifestProm = fetch(manifestURL).then(
|
const manifestProm = fetch(manifestURL).then(
|
||||||
(resp): Promise<Manifest> => resp.json()
|
(resp): Promise<Manifest> => resp.json()
|
||||||
);
|
);
|
||||||
|
|
||||||
let esploader: ESPLoader | undefined;
|
const esploader = new ESPLoader(port, logger);
|
||||||
|
|
||||||
try {
|
|
||||||
esploader = await connect(logger);
|
|
||||||
} catch (err) {
|
|
||||||
// User pressed cancel on web serial
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
(window as any).esploader = esploader;
|
(window as any).esploader = esploader;
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.INITIALIZING,
|
state: FlashStateType.INITIALIZING,
|
||||||
message: "Initializing...",
|
message: "Initializing...",
|
||||||
details: { done: false },
|
details: { done: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await esploader.initialize();
|
await esploader.initialize();
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
if (esploader.connected) {
|
if (esploader.connected) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERROR,
|
state: FlashStateType.ERROR,
|
||||||
message:
|
message:
|
||||||
"Failed to initialize. Try resetting your device or holding the BOOT button while selecting your serial port.",
|
"Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
|
||||||
details: { error: FlashError.FAILED_INITIALIZING, details: err },
|
details: { error: FlashError.FAILED_INITIALIZING, details: err },
|
||||||
});
|
});
|
||||||
await esploader.disconnect();
|
await esploader.disconnect();
|
||||||
@ -63,22 +63,22 @@ export const flash = async (
|
|||||||
chipFamily = getChipFamilyName(esploader);
|
chipFamily = getChipFamilyName(esploader);
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.INITIALIZING,
|
state: FlashStateType.INITIALIZING,
|
||||||
message: `Initialized. Found ${chipFamily}`,
|
message: `Initialized. Found ${chipFamily}`,
|
||||||
details: { done: true },
|
details: { done: true },
|
||||||
});
|
});
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.MANIFEST,
|
state: FlashStateType.MANIFEST,
|
||||||
message: "Fetching manifest...",
|
message: "Fetching manifest...",
|
||||||
details: { done: false },
|
details: { done: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manifest = await manifestProm;
|
manifest = await manifestProm;
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERROR,
|
state: FlashStateType.ERROR,
|
||||||
message: `Unable to fetch manifest: ${err.message}`,
|
message: `Unable to fetch manifest: ${err}`,
|
||||||
details: { error: FlashError.FAILED_MANIFEST_FETCH, details: err },
|
details: { error: FlashError.FAILED_MANIFEST_FETCH, details: err },
|
||||||
});
|
});
|
||||||
await esploader.disconnect();
|
await esploader.disconnect();
|
||||||
@ -88,14 +88,14 @@ export const flash = async (
|
|||||||
build = manifest.builds.find((b) => b.chipFamily === chipFamily);
|
build = manifest.builds.find((b) => b.chipFamily === chipFamily);
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.MANIFEST,
|
state: FlashStateType.MANIFEST,
|
||||||
message: `Found manifest for ${manifest.name}`,
|
message: `Found manifest for ${manifest.name}`,
|
||||||
details: { done: true },
|
details: { done: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!build) {
|
if (!build) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERROR,
|
state: FlashStateType.ERROR,
|
||||||
message: `Your ${chipFamily} board is not supported.`,
|
message: `Your ${chipFamily} board is not supported.`,
|
||||||
details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
|
details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
|
||||||
});
|
});
|
||||||
@ -104,7 +104,7 @@ export const flash = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.PREPARING,
|
state: FlashStateType.PREPARING,
|
||||||
message: "Preparing installation...",
|
message: "Preparing installation...",
|
||||||
details: { done: false },
|
details: { done: false },
|
||||||
});
|
});
|
||||||
@ -131,11 +131,14 @@ export const flash = async (
|
|||||||
const data = await prom;
|
const data = await prom;
|
||||||
files.push(data);
|
files.push(data);
|
||||||
totalSize += data.byteLength;
|
totalSize += data.byteLength;
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERROR,
|
state: FlashStateType.ERROR,
|
||||||
message: err,
|
message: err.message,
|
||||||
details: { error: FlashError.FAILED_FIRMWARE_DOWNLOAD, details: err },
|
details: {
|
||||||
|
error: FlashError.FAILED_FIRMWARE_DOWNLOAD,
|
||||||
|
details: err.message,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
await esploader.disconnect();
|
await esploader.disconnect();
|
||||||
return;
|
return;
|
||||||
@ -143,20 +146,20 @@ export const flash = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.PREPARING,
|
state: FlashStateType.PREPARING,
|
||||||
message: "Installation prepared",
|
message: "Installation prepared",
|
||||||
details: { done: true },
|
details: { done: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (eraseFirst) {
|
if (eraseFirst) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERASING,
|
state: FlashStateType.ERASING,
|
||||||
message: "Erasing device...",
|
message: "Erasing device...",
|
||||||
details: { done: false },
|
details: { done: false },
|
||||||
});
|
});
|
||||||
await espStub.eraseFlash();
|
await espStub.eraseFlash();
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERASING,
|
state: FlashStateType.ERASING,
|
||||||
message: "Device erased",
|
message: "Device erased",
|
||||||
details: { done: true },
|
details: { done: true },
|
||||||
});
|
});
|
||||||
@ -165,7 +168,7 @@ export const flash = async (
|
|||||||
let lastPct = 0;
|
let lastPct = 0;
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.WRITING,
|
state: FlashStateType.WRITING,
|
||||||
message: `Writing progress: ${lastPct}%`,
|
message: `Writing progress: ${lastPct}%`,
|
||||||
details: {
|
details: {
|
||||||
bytesTotal: totalSize,
|
bytesTotal: totalSize,
|
||||||
@ -190,7 +193,7 @@ export const flash = async (
|
|||||||
}
|
}
|
||||||
lastPct = newPct;
|
lastPct = newPct;
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.WRITING,
|
state: FlashStateType.WRITING,
|
||||||
message: `Writing progress: ${newPct}%`,
|
message: `Writing progress: ${newPct}%`,
|
||||||
details: {
|
details: {
|
||||||
bytesTotal: totalSize,
|
bytesTotal: totalSize,
|
||||||
@ -202,10 +205,10 @@ export const flash = async (
|
|||||||
part.offset,
|
part.offset,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.ERROR,
|
state: FlashStateType.ERROR,
|
||||||
message: err,
|
message: err.message,
|
||||||
details: { error: FlashError.WRITE_FAILED, details: err },
|
details: { error: FlashError.WRITE_FAILED, details: err },
|
||||||
});
|
});
|
||||||
await esploader.disconnect();
|
await esploader.disconnect();
|
||||||
@ -215,7 +218,7 @@ export const flash = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.WRITING,
|
state: FlashStateType.WRITING,
|
||||||
message: "Writing complete",
|
message: "Writing complete",
|
||||||
details: {
|
details: {
|
||||||
bytesTotal: totalSize,
|
bytesTotal: totalSize,
|
||||||
@ -225,11 +228,13 @@ export const flash = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
await sleep(100);
|
await sleep(100);
|
||||||
await esploader.hardReset();
|
console.log("DISCONNECT");
|
||||||
await esploader.disconnect();
|
await esploader.disconnect();
|
||||||
|
console.log("HARD RESET");
|
||||||
|
await esploader.hardReset();
|
||||||
|
|
||||||
fireStateEvent({
|
fireStateEvent({
|
||||||
state: State.FINISHED,
|
state: FlashStateType.FINISHED,
|
||||||
message: "All done!",
|
message: "All done!",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -72,7 +72,7 @@ export class InstallButton extends HTMLElement {
|
|||||||
public renderRoot?: ShadowRoot;
|
public renderRoot?: ShadowRoot;
|
||||||
|
|
||||||
public static preload() {
|
public static preload() {
|
||||||
import("./start-flash");
|
import("./connect");
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
@ -98,8 +98,8 @@ export class InstallButton extends HTMLElement {
|
|||||||
|
|
||||||
slot.addEventListener("click", async (ev) => {
|
slot.addEventListener("click", async (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const mod = await import("./start-flash");
|
const mod = await import("./connect");
|
||||||
mod.startFlash(this);
|
mod.connect(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
slot.name = "activate";
|
slot.name = "activate";
|
||||||
|
718
src/install-dialog.ts
Normal file
718
src/install-dialog.ts
Normal file
@ -0,0 +1,718 @@
|
|||||||
|
import { LitElement, html, PropertyValues, css, TemplateResult } from "lit";
|
||||||
|
import { state } from "lit/decorators.js";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
import "./components/ewt-dialog";
|
||||||
|
import "./components/ewt-textfield";
|
||||||
|
import "./components/ewt-button";
|
||||||
|
import "./components/ewt-icon-button";
|
||||||
|
import "./components/ewt-circular-progress";
|
||||||
|
import type { EwtTextfield } from "./components/ewt-textfield";
|
||||||
|
import { Logger, Manifest, FlashStateType, FlashState } from "./const.js";
|
||||||
|
import { ImprovSerial } from "improv-wifi-serial-sdk/dist/serial";
|
||||||
|
import {
|
||||||
|
ImprovSerialCurrentState,
|
||||||
|
ImprovSerialErrorState,
|
||||||
|
PortNotReady,
|
||||||
|
} from "improv-wifi-serial-sdk/dist/const";
|
||||||
|
import { fireEvent } from "./util/fire-event";
|
||||||
|
import { flash } from "./flash";
|
||||||
|
import "./components/ewt-console";
|
||||||
|
import { sleep } from "./util/sleep";
|
||||||
|
|
||||||
|
const ERROR_ICON = "⚠️";
|
||||||
|
const OK_ICON = "🎉";
|
||||||
|
|
||||||
|
const messageTemplate = (icon: string, label: string) => html`
|
||||||
|
<div class="center">
|
||||||
|
<div class="icon">${icon}</div>
|
||||||
|
${label}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
class EwtInstallDialog extends LitElement {
|
||||||
|
public port!: SerialPort;
|
||||||
|
|
||||||
|
public manifestPath!: string;
|
||||||
|
|
||||||
|
public logger: Logger = console;
|
||||||
|
|
||||||
|
private _manifest!: Manifest;
|
||||||
|
|
||||||
|
private _info?: ImprovSerial["info"];
|
||||||
|
|
||||||
|
// null = NOT_SUPPORTED
|
||||||
|
@state() private _client?: ImprovSerial | null;
|
||||||
|
|
||||||
|
@state() private _state:
|
||||||
|
| "ERROR"
|
||||||
|
| "DASHBOARD"
|
||||||
|
| "PROVISION"
|
||||||
|
| "INSTALL"
|
||||||
|
| "LOGS" = "DASHBOARD";
|
||||||
|
|
||||||
|
@state() private _installErase = false;
|
||||||
|
@state() private _installConfirmed = false;
|
||||||
|
@state() private _installState?: FlashState;
|
||||||
|
|
||||||
|
@state() private _provisionForce = false;
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
@state() private _busy = false;
|
||||||
|
|
||||||
|
private _progressFeedback?: {
|
||||||
|
resolve: (_: unknown) => void;
|
||||||
|
reject: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.port) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
let heading: string | undefined;
|
||||||
|
let content: TemplateResult;
|
||||||
|
let hideActions = false;
|
||||||
|
let allowClosing = false;
|
||||||
|
|
||||||
|
// During installation phase we temporarily remove the client
|
||||||
|
if (
|
||||||
|
this._client === undefined &&
|
||||||
|
this._state !== "INSTALL" &&
|
||||||
|
this._state !== "LOGS"
|
||||||
|
) {
|
||||||
|
if (this._error) {
|
||||||
|
content = this._renderMessage(ERROR_ICON, this._error, true);
|
||||||
|
} else {
|
||||||
|
content = this._renderProgress("Connecting");
|
||||||
|
hideActions = true;
|
||||||
|
}
|
||||||
|
} else if (this._state === "INSTALL") {
|
||||||
|
[heading, content, hideActions, allowClosing] = this._renderInstall();
|
||||||
|
} else if (this._state === "ERROR") {
|
||||||
|
heading = "Error";
|
||||||
|
content = this._renderMessage(ERROR_ICON, this._error!, true);
|
||||||
|
} else if (this._state === "DASHBOARD") {
|
||||||
|
[heading, content, hideActions, allowClosing] = this._renderDashboard();
|
||||||
|
} else if (this._state === "PROVISION") {
|
||||||
|
[heading, content, hideActions] = this._renderProvision();
|
||||||
|
} else if (this._state === "LOGS") {
|
||||||
|
[heading, content, hideActions] = this._renderLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ewt-dialog
|
||||||
|
open
|
||||||
|
.heading=${heading!}
|
||||||
|
scrimClickAction
|
||||||
|
@closed=${this._handleClose}
|
||||||
|
.hideActions=${hideActions}
|
||||||
|
>
|
||||||
|
${heading && allowClosing
|
||||||
|
? html`
|
||||||
|
<ewt-icon-button dialogAction="close">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</ewt-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${content!}
|
||||||
|
</ewt-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderProgress(label: string | TemplateResult, progress?: number) {
|
||||||
|
return html`
|
||||||
|
<div class="center">
|
||||||
|
<div>
|
||||||
|
<ewt-circular-progress
|
||||||
|
active
|
||||||
|
?indeterminate=${progress === undefined}
|
||||||
|
.progress=${progress !== undefined ? progress / 100 : undefined}
|
||||||
|
density="8"
|
||||||
|
></ewt-circular-progress>
|
||||||
|
${progress !== undefined
|
||||||
|
? html`<div class="progress-pct">${progress}%</div>`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
${label}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
_renderMessage(icon: string, label: string, showClose: boolean) {
|
||||||
|
return html`
|
||||||
|
${messageTemplate(icon, label)}
|
||||||
|
${showClose &&
|
||||||
|
html`
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
dialogAction="ok"
|
||||||
|
label="Close"
|
||||||
|
></ewt-button>
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderDashboard(): [string, TemplateResult, boolean, boolean] {
|
||||||
|
const heading = this._info!.name;
|
||||||
|
let content: TemplateResult;
|
||||||
|
let hideActions = true;
|
||||||
|
let allowClosing = true;
|
||||||
|
|
||||||
|
const isSameFirmware = this._info!.firmware === this._manifest!.name;
|
||||||
|
const isSameVersion =
|
||||||
|
isSameFirmware && this._info!.version === this._manifest!.version;
|
||||||
|
|
||||||
|
content = html`
|
||||||
|
<div class="device-info">
|
||||||
|
${this._info!.firmware} ${this._info!.version}
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-buttons">
|
||||||
|
${this._client!.nextUrl === undefined
|
||||||
|
? ""
|
||||||
|
: html`
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href=${this._client!.nextUrl}
|
||||||
|
class="has-button"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<ewt-button label="Set up Device"></ewt-button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
<div>
|
||||||
|
<ewt-button
|
||||||
|
.label=${this._client!.state === ImprovSerialCurrentState.READY
|
||||||
|
? "Connect to Wi-Fi"
|
||||||
|
: "Change Wi-Fi"}
|
||||||
|
@click=${() => {
|
||||||
|
this._state = "PROVISION";
|
||||||
|
if (
|
||||||
|
this._client!.state === ImprovSerialCurrentState.PROVISIONED
|
||||||
|
) {
|
||||||
|
this._provisionForce = true;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ewt-button
|
||||||
|
.label=${!isSameFirmware
|
||||||
|
? `Install ${this._manifest!.name}`
|
||||||
|
: isSameVersion
|
||||||
|
? "Up to date"
|
||||||
|
: "Update"}
|
||||||
|
@click=${() => this._startInstall(!isSameFirmware)}
|
||||||
|
.disabled=${isSameVersion}
|
||||||
|
></ewt-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ewt-button
|
||||||
|
label="Logs"
|
||||||
|
@click=${async () => {
|
||||||
|
const client = this._client;
|
||||||
|
if (client) {
|
||||||
|
await this._closeClientWithoutEvents(client);
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
// Also set `null` back to undefined.
|
||||||
|
this._client = undefined;
|
||||||
|
this._state = "LOGS";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return [heading, content, hideActions, allowClosing];
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderProvision(): [string | undefined, TemplateResult, boolean] {
|
||||||
|
let heading: string | undefined = "Configure Wi-Fi";
|
||||||
|
let content: TemplateResult;
|
||||||
|
let hideActions = false;
|
||||||
|
|
||||||
|
if (this._busy) {
|
||||||
|
return [heading, this._renderProgress("Trying to connect"), true];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this._provisionForce &&
|
||||||
|
this._client!.state === ImprovSerialCurrentState.PROVISIONED
|
||||||
|
) {
|
||||||
|
heading = undefined;
|
||||||
|
content = html`
|
||||||
|
${messageTemplate(OK_ICON, "Device connected to the network!")}
|
||||||
|
${
|
||||||
|
// If we went to provision after installing the firmware with a full erase,
|
||||||
|
// there is nothing left for the user, let them go to the device dashboard
|
||||||
|
// if available
|
||||||
|
this._installState?.state === FlashStateType.FINISHED &&
|
||||||
|
this._installErase &&
|
||||||
|
this._client!.nextUrl !== undefined
|
||||||
|
? html`
|
||||||
|
<a
|
||||||
|
slot="primaryAction"
|
||||||
|
href=${this._client!.nextUrl}
|
||||||
|
class="has-button"
|
||||||
|
target="_blank"
|
||||||
|
@click=${() => {
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ewt-button label="Set up Device"></ewt-button>
|
||||||
|
</a>
|
||||||
|
<ewt-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
label="Skip"
|
||||||
|
@click=${() => {
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
this._installState = undefined;
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
label="Continue"
|
||||||
|
@click=${() => {
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
let error: string | undefined;
|
||||||
|
|
||||||
|
switch (this._client!.error) {
|
||||||
|
case ImprovSerialErrorState.UNABLE_TO_CONNECT:
|
||||||
|
error = "Unable to connect";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ImprovSerialErrorState.NO_ERROR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = `Unknown error (${this._client!.error})`;
|
||||||
|
}
|
||||||
|
content = html`
|
||||||
|
<div>
|
||||||
|
Enter the credentials of the Wi-Fi network that you want your device
|
||||||
|
to connect to.
|
||||||
|
</div>
|
||||||
|
${error ? html`<p class="error">${error}</p>` : ""}
|
||||||
|
<ewt-textfield label="Network Name" name="ssid"></ewt-textfield>
|
||||||
|
<ewt-textfield
|
||||||
|
label="Password"
|
||||||
|
name="password"
|
||||||
|
type="password"
|
||||||
|
></ewt-textfield>
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
label="Connect"
|
||||||
|
@click="${this._doProvision}"
|
||||||
|
></ewt-button>
|
||||||
|
<ewt-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
.label=${this._installState && this._installErase ? "Skip" : "Back"}
|
||||||
|
@click=${() => {
|
||||||
|
this._installState = undefined;
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return [heading, content, hideActions];
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderInstall(): [string | undefined, TemplateResult, boolean, boolean] {
|
||||||
|
let heading: string | undefined = `Install ${this._manifest!.name}`;
|
||||||
|
let content: TemplateResult;
|
||||||
|
let hideActions = false;
|
||||||
|
let allowClosing = false;
|
||||||
|
|
||||||
|
const isUpdate = !this._installErase && this._isUpdate;
|
||||||
|
|
||||||
|
if (!this._installConfirmed) {
|
||||||
|
const action = isUpdate ? "update to" : "install";
|
||||||
|
content = html`
|
||||||
|
${isUpdate
|
||||||
|
? html`Your device is running
|
||||||
|
${this._info!.firmware} ${this._info!.version}.<br /><br />`
|
||||||
|
: ""}
|
||||||
|
Do you want to ${action}
|
||||||
|
${this._manifest!.name} ${this._manifest!.version}?
|
||||||
|
${this._installErase
|
||||||
|
? "All existing data will be erased from your device."
|
||||||
|
: ""}
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
label="Install"
|
||||||
|
@click=${this._confirmInstall}
|
||||||
|
></ewt-button>
|
||||||
|
${this._client
|
||||||
|
? html`
|
||||||
|
<ewt-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
label="Back"
|
||||||
|
@click=${() => {
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ewt-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
label="Logs"
|
||||||
|
@click=${async () => {
|
||||||
|
// In case it was null
|
||||||
|
this._client = undefined;
|
||||||
|
this._state = "LOGS";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
allowClosing = !this._client;
|
||||||
|
} else if (
|
||||||
|
!this._installState ||
|
||||||
|
this._installState.state === FlashStateType.INITIALIZING ||
|
||||||
|
this._installState.state === FlashStateType.MANIFEST ||
|
||||||
|
this._installState.state === FlashStateType.PREPARING
|
||||||
|
) {
|
||||||
|
content = this._renderProgress("Preparing installation");
|
||||||
|
hideActions = true;
|
||||||
|
} else if (this._installState.state === FlashStateType.ERASING) {
|
||||||
|
content = this._renderProgress("Erasing");
|
||||||
|
hideActions = true;
|
||||||
|
} else if (this._installState.state === FlashStateType.WRITING) {
|
||||||
|
content = this._renderProgress(
|
||||||
|
html`
|
||||||
|
${this._installState.details.percentage > 3
|
||||||
|
? ""
|
||||||
|
: html`Installing<br />`}
|
||||||
|
<br />
|
||||||
|
This will take
|
||||||
|
${this._installState.chipFamily === "ESP8266"
|
||||||
|
? "a minute"
|
||||||
|
: "2 minutes"}.<br />
|
||||||
|
Keep this page visible to prevent slow down
|
||||||
|
`,
|
||||||
|
// Show as undeterminate under 3% or else we don't show any pixels
|
||||||
|
this._installState.details.percentage > 3
|
||||||
|
? this._installState.details.percentage
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
hideActions = true;
|
||||||
|
} else if (this._installState.state === FlashStateType.FINISHED) {
|
||||||
|
heading = undefined;
|
||||||
|
const supportsImprov = this._client !== null;
|
||||||
|
content = html`
|
||||||
|
${messageTemplate(OK_ICON, "Installation complete!")}
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
.label=${supportsImprov ? "Next" : "Close"}
|
||||||
|
dialogAction=${ifDefined(supportsImprov ? undefined : "close")}
|
||||||
|
@click=${!supportsImprov
|
||||||
|
? undefined
|
||||||
|
: () => {
|
||||||
|
this._state = this._installErase ? "PROVISION" : "DASHBOARD";
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`;
|
||||||
|
} else if (this._installState.state === FlashStateType.ERROR) {
|
||||||
|
content = html`
|
||||||
|
${messageTemplate(ERROR_ICON, this._installState.message)}
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
label="Back"
|
||||||
|
@click=${async () => {
|
||||||
|
this._initialize();
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
this._installState = undefined;
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return [heading, content!, hideActions, allowClosing];
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderLogs(): [string | undefined, TemplateResult, boolean] {
|
||||||
|
let heading: string | undefined = `Logs`;
|
||||||
|
let content: TemplateResult;
|
||||||
|
let hideActions = false;
|
||||||
|
|
||||||
|
content = html`
|
||||||
|
<ewt-console .port=${this.port} .logger=${this.logger}></ewt-console>
|
||||||
|
<ewt-button
|
||||||
|
slot="primaryAction"
|
||||||
|
label="Back"
|
||||||
|
@click=${async () => {
|
||||||
|
await this.shadowRoot!.querySelector("ewt-console")!.disconnect();
|
||||||
|
this._state = "DASHBOARD";
|
||||||
|
this._initialize();
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
<ewt-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
label="Reset Device"
|
||||||
|
@click=${async () => {
|
||||||
|
await this.shadowRoot!.querySelector("ewt-console")!.reset();
|
||||||
|
}}
|
||||||
|
></ewt-button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return [heading, content!, hideActions];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override willUpdate(changedProps: PropertyValues) {
|
||||||
|
if (!changedProps.has("_state")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Clear errors when changing between pages unless we change
|
||||||
|
// to the error page.
|
||||||
|
if (this._state !== "ERROR") {
|
||||||
|
this._error = undefined;
|
||||||
|
}
|
||||||
|
if (this._state !== "PROVISION") {
|
||||||
|
this._provisionForce = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
|
||||||
|
if (!changedProps.has("_state")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setAttribute("state", this._state);
|
||||||
|
|
||||||
|
if (this._state === "PROVISION") {
|
||||||
|
const textfield = this.shadowRoot!.querySelector("ewt-textfield");
|
||||||
|
if (textfield) {
|
||||||
|
textfield.updateComplete.then(() => textfield.focus());
|
||||||
|
}
|
||||||
|
} else if (this._state === "INSTALL") {
|
||||||
|
this._installConfirmed = false;
|
||||||
|
this._installState = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchManifest() {
|
||||||
|
if (this._manifest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifestURL = new URL(
|
||||||
|
this.manifestPath,
|
||||||
|
location.toString()
|
||||||
|
).toString();
|
||||||
|
this._manifest = await fetch(manifestURL).then(
|
||||||
|
(resp): Promise<Manifest> => resp.json()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _initialize() {
|
||||||
|
if (this.port.readable === null || this.port.writable === null) {
|
||||||
|
this._state = "ERROR";
|
||||||
|
this._error =
|
||||||
|
"Serial port is not readable/writable. Close any other application using it and try again.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const manifestProm = this._fetchManifest();
|
||||||
|
|
||||||
|
const client = new ImprovSerial(this.port!, this.logger);
|
||||||
|
client.addEventListener("state-changed", () => {
|
||||||
|
this.requestUpdate();
|
||||||
|
});
|
||||||
|
client.addEventListener("error-changed", () => this.requestUpdate());
|
||||||
|
try {
|
||||||
|
this._info = await client.initialize();
|
||||||
|
this._client = client;
|
||||||
|
client.addEventListener("disconnect", this._handleDisconnect);
|
||||||
|
} catch (err: any) {
|
||||||
|
// Clear old value
|
||||||
|
this._info = undefined;
|
||||||
|
if (err instanceof PortNotReady) {
|
||||||
|
this._state = "ERROR";
|
||||||
|
this._error =
|
||||||
|
"Serial port is not ready. Close any other application using it and try again.";
|
||||||
|
} else {
|
||||||
|
this._client = null; // not supported
|
||||||
|
this.logger.error("Improv initialization failed.", err);
|
||||||
|
// initialize is also called at the end of an installation
|
||||||
|
// When it can't detect improv (ie because install failed)
|
||||||
|
// We shouldn't reset settings but instead show the error
|
||||||
|
if (this._state !== "INSTALL") {
|
||||||
|
this._startInstall(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await manifestProm;
|
||||||
|
} catch (err: any) {
|
||||||
|
this._state = "ERROR";
|
||||||
|
this._error = "Failed to download manifest";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _startInstall(erase: boolean) {
|
||||||
|
this._state = "INSTALL";
|
||||||
|
this._installErase = erase;
|
||||||
|
this._installConfirmed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _confirmInstall() {
|
||||||
|
this._installConfirmed = true;
|
||||||
|
this._installState = undefined;
|
||||||
|
if (this._client) {
|
||||||
|
await this._closeClientWithoutEvents(this._client);
|
||||||
|
}
|
||||||
|
this._client = undefined;
|
||||||
|
|
||||||
|
flash(
|
||||||
|
(state) => {
|
||||||
|
this._installState = state;
|
||||||
|
|
||||||
|
if (state.state === FlashStateType.FINISHED) {
|
||||||
|
this._initialize().then(() => this.requestUpdate());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this.port,
|
||||||
|
this.logger,
|
||||||
|
this.manifestPath,
|
||||||
|
this._installErase
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _doProvision() {
|
||||||
|
this._busy = true;
|
||||||
|
const ssid = (
|
||||||
|
this.shadowRoot!.querySelector("ewt-textfield[name=ssid]") as EwtTextfield
|
||||||
|
).value;
|
||||||
|
const password = (
|
||||||
|
this.shadowRoot!.querySelector(
|
||||||
|
"ewt-textfield[name=password]"
|
||||||
|
) as EwtTextfield
|
||||||
|
).value;
|
||||||
|
try {
|
||||||
|
await this._client!.provision(ssid, password);
|
||||||
|
} catch (err: any) {
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
this._busy = false;
|
||||||
|
this._provisionForce = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleDisconnect = () => {
|
||||||
|
this._state = "ERROR";
|
||||||
|
this._error = "Disconnected";
|
||||||
|
};
|
||||||
|
|
||||||
|
private async _handleClose() {
|
||||||
|
if (this._progressFeedback) {
|
||||||
|
this._progressFeedback.reject();
|
||||||
|
}
|
||||||
|
if (this._client) {
|
||||||
|
await this._closeClientWithoutEvents(this._client);
|
||||||
|
}
|
||||||
|
fireEvent(this, "closed" as any);
|
||||||
|
this.parentNode!.removeChild(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _isUpdate() {
|
||||||
|
return this._info?.firmware === this._manifest!.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _closeClientWithoutEvents(client: ImprovSerial) {
|
||||||
|
client.removeEventListener("disconnect", this._handleDisconnect);
|
||||||
|
await client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--mdc-dialog-max-width: 390px;
|
||||||
|
--mdc-theme-primary: var(--improv-primary-color, #03a9f4);
|
||||||
|
--mdc-theme-on-primary: var(--improv-on-primary-color, #fff);
|
||||||
|
}
|
||||||
|
ewt-icon-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
ewt-textfield {
|
||||||
|
display: block;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.flash {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
background-color: var(--mdc-theme-primary);
|
||||||
|
padding: 8px 4px;
|
||||||
|
color: var(--mdc-theme-on-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.dashboard-buttons {
|
||||||
|
margin: 16px 0 -16px -8px;
|
||||||
|
}
|
||||||
|
.dashboard-buttons div {
|
||||||
|
display: block;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
ewt-circular-progress {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
a.has-button {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
font-size: 50px;
|
||||||
|
line-height: 80px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #db4437;
|
||||||
|
}
|
||||||
|
button.link {
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
:host([state="LOGS"]) ewt-dialog {
|
||||||
|
--mdc-dialog-max-width: 90vw;
|
||||||
|
}
|
||||||
|
ewt-console {
|
||||||
|
display: block;
|
||||||
|
width: calc(80vw - 48px);
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ewt-install-dialog", EwtInstallDialog);
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ewt-install-dialog": EwtInstallDialog;
|
||||||
|
}
|
||||||
|
}
|
@ -1,154 +0,0 @@
|
|||||||
import { flash } from "./flash";
|
|
||||||
import "./flash-log";
|
|
||||||
import "./flash-progress";
|
|
||||||
import type { FlashLog } from "./flash-log";
|
|
||||||
import type { FlashProgress } from "./flash-progress";
|
|
||||||
import type { InstallButton } from "./install-button";
|
|
||||||
import { State } from "./const";
|
|
||||||
|
|
||||||
interface FlashData {
|
|
||||||
stateListenerAdded: boolean;
|
|
||||||
logEl: FlashLog | undefined;
|
|
||||||
progressEl: FlashProgress | undefined;
|
|
||||||
improvEl: HTMLElement | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getData = (button: InstallButton): FlashData => {
|
|
||||||
if (!("_flashData" in button)) {
|
|
||||||
(button as any)._flashData = {
|
|
||||||
stateListenerAdded: false,
|
|
||||||
logEl: undefined,
|
|
||||||
progressEl: undefined,
|
|
||||||
improvEl: undefined,
|
|
||||||
} as FlashData;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (button as any)._flashData as FlashData;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addElement = <T extends HTMLElement>(
|
|
||||||
button: InstallButton,
|
|
||||||
element: T
|
|
||||||
): T => {
|
|
||||||
button.renderRoot!.append(element);
|
|
||||||
return element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const startFlash = async (button: InstallButton) => {
|
|
||||||
if (button.hasAttribute("active")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const manifest = button.manifest || button.getAttribute("manifest");
|
|
||||||
if (!manifest) {
|
|
||||||
alert("No manifest defined!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = getData(button);
|
|
||||||
|
|
||||||
let hasImprov = false;
|
|
||||||
|
|
||||||
if (!data.stateListenerAdded) {
|
|
||||||
data.stateListenerAdded = true;
|
|
||||||
button.addEventListener("state-changed", (ev) => {
|
|
||||||
const state = (button.state = ev.detail);
|
|
||||||
if (state.state === State.INITIALIZING) {
|
|
||||||
button.toggleAttribute("active", true);
|
|
||||||
} else if (state.state === State.MANIFEST && state.build?.improv) {
|
|
||||||
hasImprov = true;
|
|
||||||
// @ts-ignore
|
|
||||||
// preload improv button
|
|
||||||
import("https://www.improv-wifi.com/sdk-js/launch-button.js");
|
|
||||||
} else if (state.state === State.FINISHED) {
|
|
||||||
button.toggleAttribute("active", false);
|
|
||||||
if (hasImprov) {
|
|
||||||
startImprov(button);
|
|
||||||
}
|
|
||||||
} else if (state.state === State.ERROR) {
|
|
||||||
button.toggleAttribute("active", false);
|
|
||||||
}
|
|
||||||
data.progressEl?.processState(ev.detail);
|
|
||||||
data.logEl?.processState(ev.detail);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const logConsole = button.logConsole || button.hasAttribute("log-console");
|
|
||||||
const showLog = button.showLog || button.hasAttribute("show-log");
|
|
||||||
const showProgress =
|
|
||||||
!showLog &&
|
|
||||||
button.hideProgress !== true &&
|
|
||||||
!button.hasAttribute("hide-progress");
|
|
||||||
|
|
||||||
if (showLog && !data.logEl) {
|
|
||||||
data.logEl = addElement<FlashLog>(
|
|
||||||
button,
|
|
||||||
document.createElement("esp-web-flash-log")
|
|
||||||
);
|
|
||||||
} else if (!showLog && data.logEl) {
|
|
||||||
data.logEl.remove();
|
|
||||||
data.logEl = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showProgress && !data.progressEl) {
|
|
||||||
data.progressEl = addElement<FlashProgress>(
|
|
||||||
button,
|
|
||||||
document.createElement("esp-web-flash-progress")
|
|
||||||
);
|
|
||||||
} else if (!showProgress && data.progressEl) {
|
|
||||||
data.progressEl.remove();
|
|
||||||
data.progressEl = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.logEl?.clear();
|
|
||||||
data.progressEl?.clear();
|
|
||||||
data.improvEl?.classList.toggle("hidden", true);
|
|
||||||
|
|
||||||
flash(
|
|
||||||
button,
|
|
||||||
logConsole
|
|
||||||
? console
|
|
||||||
: {
|
|
||||||
log: () => {},
|
|
||||||
error: () => {},
|
|
||||||
debug: () => {},
|
|
||||||
},
|
|
||||||
manifest,
|
|
||||||
button.eraseFirst !== undefined
|
|
||||||
? button.eraseFirst
|
|
||||||
: button.hasAttribute("erase-first")
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const startImprov = async (button: InstallButton) => {
|
|
||||||
// @ts-ignore
|
|
||||||
await import("https://www.improv-wifi.com/sdk-js/launch-button.js");
|
|
||||||
|
|
||||||
const improvButtonConstructor = customElements.get(
|
|
||||||
"improv-wifi-launch-button"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!improvButtonConstructor.isSupported ||
|
|
||||||
!improvButtonConstructor.isAllowed
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = getData(button);
|
|
||||||
|
|
||||||
if (!data.improvEl) {
|
|
||||||
data.improvEl = document.createElement("improv-wifi-launch-button");
|
|
||||||
data.improvEl.addEventListener("state-changed", (ev: any) => {
|
|
||||||
if (ev.detail.state === "PROVISIONED") {
|
|
||||||
data.improvEl!.classList.toggle("hidden", true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const improvButton = document.createElement("button");
|
|
||||||
improvButton.slot = "activate";
|
|
||||||
improvButton.textContent = "CLICK HERE TO FINISH SETTING UP YOUR DEVICE";
|
|
||||||
data.improvEl.appendChild(improvButton);
|
|
||||||
addElement(button, data.improvEl);
|
|
||||||
}
|
|
||||||
data.improvEl.classList.toggle("hidden", false);
|
|
||||||
};
|
|
49
src/util.ts
49
src/util.ts
@ -1,49 +0,0 @@
|
|||||||
import {
|
|
||||||
CHIP_FAMILY_ESP32,
|
|
||||||
CHIP_FAMILY_ESP32S2,
|
|
||||||
CHIP_FAMILY_ESP8266,
|
|
||||||
CHIP_FAMILY_ESP32C3,
|
|
||||||
ESPLoader,
|
|
||||||
} from "esp-web-flasher";
|
|
||||||
import type { BaseFlashState } from "./const";
|
|
||||||
|
|
||||||
export const getChipFamilyName = (
|
|
||||||
esploader: ESPLoader
|
|
||||||
): NonNullable<BaseFlashState["chipFamily"]> => {
|
|
||||||
switch (esploader.chipFamily) {
|
|
||||||
case CHIP_FAMILY_ESP32:
|
|
||||||
return "ESP32";
|
|
||||||
case CHIP_FAMILY_ESP8266:
|
|
||||||
return "ESP8266";
|
|
||||||
case CHIP_FAMILY_ESP32S2:
|
|
||||||
return "ESP32-S2";
|
|
||||||
case CHIP_FAMILY_ESP32C3:
|
|
||||||
return "ESP32-C3";
|
|
||||||
default:
|
|
||||||
return "Unknown Chip";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sleep = (time: number) =>
|
|
||||||
new Promise((resolve) => setTimeout(resolve, time));
|
|
||||||
|
|
||||||
export const fireEvent = <Event extends keyof HTMLElementEventMap>(
|
|
||||||
eventTarget: EventTarget,
|
|
||||||
type: Event,
|
|
||||||
// @ts-ignore
|
|
||||||
detail?: HTMLElementEventMap[Event]["detail"],
|
|
||||||
options?: {
|
|
||||||
bubbles?: boolean;
|
|
||||||
cancelable?: boolean;
|
|
||||||
composed?: boolean;
|
|
||||||
}
|
|
||||||
): void => {
|
|
||||||
options = options || {};
|
|
||||||
const event = new CustomEvent(type, {
|
|
||||||
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
|
||||||
cancelable: Boolean(options.cancelable),
|
|
||||||
composed: options.composed === undefined ? true : options.composed,
|
|
||||||
detail,
|
|
||||||
});
|
|
||||||
eventTarget.dispatchEvent(event);
|
|
||||||
};
|
|
25
src/util/chip-family-name.ts
Normal file
25
src/util/chip-family-name.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {
|
||||||
|
CHIP_FAMILY_ESP32,
|
||||||
|
CHIP_FAMILY_ESP32S2,
|
||||||
|
CHIP_FAMILY_ESP8266,
|
||||||
|
CHIP_FAMILY_ESP32C3,
|
||||||
|
ESPLoader,
|
||||||
|
} from "esp-web-flasher";
|
||||||
|
import type { BaseFlashState } from "../const";
|
||||||
|
|
||||||
|
export const getChipFamilyName = (
|
||||||
|
esploader: ESPLoader
|
||||||
|
): NonNullable<BaseFlashState["chipFamily"]> => {
|
||||||
|
switch (esploader.chipFamily) {
|
||||||
|
case CHIP_FAMILY_ESP32:
|
||||||
|
return "ESP32";
|
||||||
|
case CHIP_FAMILY_ESP8266:
|
||||||
|
return "ESP8266";
|
||||||
|
case CHIP_FAMILY_ESP32S2:
|
||||||
|
return "ESP32-S2";
|
||||||
|
case CHIP_FAMILY_ESP32C3:
|
||||||
|
return "ESP32-C3";
|
||||||
|
default:
|
||||||
|
return "Unknown Chip";
|
||||||
|
}
|
||||||
|
};
|
188
src/util/console-color.ts
Normal file
188
src/util/console-color.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
interface ConsoleState {
|
||||||
|
bold: boolean;
|
||||||
|
italic: boolean;
|
||||||
|
underline: boolean;
|
||||||
|
strikethrough: boolean;
|
||||||
|
foregroundColor: string | null;
|
||||||
|
backgroundColor: string | null;
|
||||||
|
// carriageReturn: boolean;
|
||||||
|
secret: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ColoredConsole {
|
||||||
|
public state: ConsoleState = {
|
||||||
|
bold: false,
|
||||||
|
italic: false,
|
||||||
|
underline: false,
|
||||||
|
strikethrough: false,
|
||||||
|
foregroundColor: null,
|
||||||
|
backgroundColor: null,
|
||||||
|
// carriageReturn: false,
|
||||||
|
secret: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(public targetElement: HTMLElement) {}
|
||||||
|
|
||||||
|
addLine(line: string) {
|
||||||
|
const re = /(?:\033|\\033)(?:\[(.*?)[@-~]|\].*?(?:\007|\033\\))/g;
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
// This doesn't work for some reason
|
||||||
|
// if (this.state.carriageReturn) {
|
||||||
|
// if (line !== "\n") {
|
||||||
|
// // don't remove if \r\n
|
||||||
|
// this.targetElement.removeChild(this.targetElement.lastChild!);
|
||||||
|
// }
|
||||||
|
// this.state.carriageReturn = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (line.includes("\r")) {
|
||||||
|
// this.state.carriageReturn = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
const lineSpan = document.createElement("span");
|
||||||
|
lineSpan.classList.add("line");
|
||||||
|
this.targetElement.appendChild(lineSpan);
|
||||||
|
|
||||||
|
const addSpan = (content: string) => {
|
||||||
|
if (content === "") return;
|
||||||
|
|
||||||
|
const span = document.createElement("span");
|
||||||
|
if (this.state.bold) span.classList.add("log-bold");
|
||||||
|
if (this.state.italic) span.classList.add("log-italic");
|
||||||
|
if (this.state.underline) span.classList.add("log-underline");
|
||||||
|
if (this.state.strikethrough) span.classList.add("log-strikethrough");
|
||||||
|
if (this.state.secret) span.classList.add("log-secret");
|
||||||
|
if (this.state.foregroundColor !== null)
|
||||||
|
span.classList.add(`log-fg-${this.state.foregroundColor}`);
|
||||||
|
if (this.state.backgroundColor !== null)
|
||||||
|
span.classList.add(`log-bg-${this.state.backgroundColor}`);
|
||||||
|
span.appendChild(document.createTextNode(content));
|
||||||
|
lineSpan.appendChild(span);
|
||||||
|
|
||||||
|
if (this.state.secret) {
|
||||||
|
const redacted = document.createElement("span");
|
||||||
|
redacted.classList.add("log-secret-redacted");
|
||||||
|
redacted.appendChild(document.createTextNode("[redacted]"));
|
||||||
|
lineSpan.appendChild(redacted);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const match = re.exec(line);
|
||||||
|
if (match === null) break;
|
||||||
|
|
||||||
|
const j = match.index;
|
||||||
|
addSpan(line.substring(i, j));
|
||||||
|
i = j + match[0].length;
|
||||||
|
|
||||||
|
if (match[1] === undefined) continue;
|
||||||
|
|
||||||
|
for (const colorCode of match[1].split(";")) {
|
||||||
|
switch (parseInt(colorCode)) {
|
||||||
|
case 0:
|
||||||
|
// reset
|
||||||
|
this.state.bold = false;
|
||||||
|
this.state.italic = false;
|
||||||
|
this.state.underline = false;
|
||||||
|
this.state.strikethrough = false;
|
||||||
|
this.state.foregroundColor = null;
|
||||||
|
this.state.backgroundColor = null;
|
||||||
|
this.state.secret = false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
this.state.bold = true;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.state.italic = true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this.state.underline = true;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this.state.secret = true;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
this.state.secret = false;
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
this.state.strikethrough = true;
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
this.state.bold = false;
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
this.state.italic = false;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
this.state.underline = false;
|
||||||
|
break;
|
||||||
|
case 29:
|
||||||
|
this.state.strikethrough = false;
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
this.state.foregroundColor = "black";
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
this.state.foregroundColor = "red";
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
this.state.foregroundColor = "green";
|
||||||
|
break;
|
||||||
|
case 33:
|
||||||
|
this.state.foregroundColor = "yellow";
|
||||||
|
break;
|
||||||
|
case 34:
|
||||||
|
this.state.foregroundColor = "blue";
|
||||||
|
break;
|
||||||
|
case 35:
|
||||||
|
this.state.foregroundColor = "magenta";
|
||||||
|
break;
|
||||||
|
case 36:
|
||||||
|
this.state.foregroundColor = "cyan";
|
||||||
|
break;
|
||||||
|
case 37:
|
||||||
|
this.state.foregroundColor = "white";
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
this.state.foregroundColor = null;
|
||||||
|
break;
|
||||||
|
case 41:
|
||||||
|
this.state.backgroundColor = "red";
|
||||||
|
break;
|
||||||
|
case 42:
|
||||||
|
this.state.backgroundColor = "green";
|
||||||
|
break;
|
||||||
|
case 43:
|
||||||
|
this.state.backgroundColor = "yellow";
|
||||||
|
break;
|
||||||
|
case 44:
|
||||||
|
this.state.backgroundColor = "blue";
|
||||||
|
break;
|
||||||
|
case 45:
|
||||||
|
this.state.backgroundColor = "magenta";
|
||||||
|
break;
|
||||||
|
case 46:
|
||||||
|
this.state.backgroundColor = "cyan";
|
||||||
|
break;
|
||||||
|
case 47:
|
||||||
|
this.state.backgroundColor = "white";
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
case 49:
|
||||||
|
this.state.backgroundColor = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addSpan(line.substring(i));
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.targetElement.scrollTop + 56 >=
|
||||||
|
this.targetElement.scrollHeight - this.targetElement.offsetHeight
|
||||||
|
) {
|
||||||
|
// at bottom
|
||||||
|
this.targetElement.scrollTop = this.targetElement.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/util/fire-event.ts
Normal file
20
src/util/fire-event.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const fireEvent = <Event extends keyof HTMLElementEventMap>(
|
||||||
|
eventTarget: EventTarget,
|
||||||
|
type: Event,
|
||||||
|
// @ts-ignore
|
||||||
|
detail?: HTMLElementEventMap[Event]["detail"],
|
||||||
|
options?: {
|
||||||
|
bubbles?: boolean;
|
||||||
|
cancelable?: boolean;
|
||||||
|
composed?: boolean;
|
||||||
|
}
|
||||||
|
): void => {
|
||||||
|
options = options || {};
|
||||||
|
const event = new CustomEvent(type, {
|
||||||
|
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
||||||
|
cancelable: Boolean(options.cancelable),
|
||||||
|
composed: options.composed === undefined ? true : options.composed,
|
||||||
|
detail,
|
||||||
|
});
|
||||||
|
eventTarget.dispatchEvent(event);
|
||||||
|
};
|
20
src/util/line-break-transformer.ts
Normal file
20
src/util/line-break-transformer.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export class LineBreakTransformer implements Transformer<string, string> {
|
||||||
|
private chunks = "";
|
||||||
|
|
||||||
|
transform(
|
||||||
|
chunk: string,
|
||||||
|
controller: TransformStreamDefaultController<string>
|
||||||
|
) {
|
||||||
|
// Append new chunks to existing chunks.
|
||||||
|
this.chunks += chunk;
|
||||||
|
// For each line breaks in chunks, send the parsed lines out.
|
||||||
|
const lines = this.chunks.split("\r\n");
|
||||||
|
this.chunks = lines.pop()!;
|
||||||
|
lines.forEach((line) => controller.enqueue(line + "\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
flush(controller: TransformStreamDefaultController<string>) {
|
||||||
|
// When the stream is closed, flush any remaining chunks out.
|
||||||
|
controller.enqueue(this.chunks);
|
||||||
|
}
|
||||||
|
}
|
2
src/util/sleep.ts
Normal file
2
src/util/sleep.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const sleep = (time: number) =>
|
||||||
|
new Promise((resolve) => setTimeout(resolve, time));
|
Binary file not shown.
Binary file not shown.
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "ESP Web Tools demo powered by ESPHome",
|
"name": "ESPHome",
|
||||||
|
"version": "2021.11.0-dev",
|
||||||
"builds": [
|
"builds": [
|
||||||
{
|
{
|
||||||
"chipFamily": "ESP32",
|
"chipFamily": "ESP32",
|
||||||
"improv": true,
|
|
||||||
"parts": [
|
"parts": [
|
||||||
{ "path": "bootloader.bin", "offset": 4096 },
|
{ "path": "bootloader.bin", "offset": 4096 },
|
||||||
{ "path": "partitions.bin", "offset": 32768 },
|
{ "path": "partitions.bin", "offset": 32768 },
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user