1
0
Fork 0
forked from 2sys/phonograph
phonograph/components/src/entrypoints/dev-reloader.ts

119 lines
2.7 KiB
TypeScript
Raw Normal View History

2025-07-08 14:37:03 -07:00
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js";
@customElement("dev-reloader")
export class DevReloader extends LitElement {
@property({ attribute: true })
ws = "";
@property({ attribute: true, type: Boolean, reflect: true })
auto = false;
@state()
private _connected = false;
private _armed = false;
private _socket?: WebSocket;
static override styles = css`
button {
appearance: none;
background: none;
border: none;
padding: none;
}
.widget {
position: fixed;
z-index: 999;
bottom: 1rem;
left: 1rem;
border: solid 1px #0002;
padding: 1rem;
border-radius: 9999px;
cursor: pointer;
display: flex;
align-items: center;
box-shadow: 0 0.5rem 1rem #0002;
opacity: 0.5;
&:hover {
opacity: 1;
}
}
.indicator {
width: 8px;
height: 8px;
border-radius: 9999px;
background: #f60;
&.connected {
background: #06f;
}
}
.label {
margin-left: 1rem;
}
`;
override connectedCallback() {
super.connectedCallback();
this._connected = true;
this._handleDisconnect();
}
private _handleDisconnect() {
if (this._connected) {
console.log("dev-reloader: disconnected");
this._connected = false;
this._socket = undefined;
const intvl = setInterval(() => {
if (!this._socket || this._socket.readyState === WebSocket.CLOSED) {
try {
this._socket = new WebSocket(this.ws);
this._socket.addEventListener("open", () => {
if (this.auto && this._armed) {
globalThis.location.reload();
}
console.log("dev-reloader: connected");
this._connected = true;
this._armed = true;
clearInterval(intvl);
});
this._socket.addEventListener(
"close",
this._handleDisconnect.bind(this),
);
this._socket.addEventListener(
"error",
this._handleDisconnect.bind(this),
);
} catch { /* no-op */ }
}
}, 500);
}
}
private _toggleAuto() {
this.auto = !this.auto;
}
protected override render() {
return html`
<button type="button" class="widget" @click="${this._toggleAuto}">
<div class="${classMap({
indicator: true,
connected: this._connected,
})}"></div>
<div class="label">
${this.auto ? "Disable" : "Enable"} auto-reload
</div>
</button>
`;
}
}