ui.html•6.66 kB
<!doctype html>
<html>
<head>
<style>
body {
font-family: Inter, sans-serif;
font-size: 11px;
padding: 8px;
margin: 0;
background: #2c2c2c;
color: #fff;
}
.status {
padding: 4px 8px;
border-radius: 4px;
}
.connected {
background: #1e4620;
}
.disconnected {
background: #4a1e1e;
}
.connecting {
background: #4a3a1e;
}
.status-row {
display: flex;
align-items: center;
gap: 8px;
}
#reconnect-btn {
display: none;
padding: 4px 8px;
border-radius: 4px;
border: none;
background: #0d99ff;
color: #fff;
font-size: 11px;
cursor: pointer;
}
#reconnect-btn:hover {
background: #0b85e0;
}
#reconnect-btn.visible {
display: block;
}
.port-row {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
}
.port-label {
color: #999;
}
#port-input {
width: 60px;
padding: 4px 6px;
border-radius: 4px;
border: 1px solid #444;
background: #1e1e1e;
color: #fff;
font-size: 11px;
}
#port-input:focus {
outline: none;
border-color: #0d99ff;
}
</style>
</head>
<body>
<div class="status-row">
<div id="status" class="status disconnected">Disconnected</div>
<button id="reconnect-btn" onclick="manualReconnect()">Reconnect</button>
</div>
<div class="port-row">
<span class="port-label">Port:</span>
<input
type="number"
id="port-input"
value="3055"
min="1024"
max="65535"
onchange="changePort()"
/>
</div>
<script>
const CONFIG = {
defaultPort: 3055,
reconnectMaxAttempts: 10,
reconnectBaseDelay: 1000,
reconnectMaxDelay: 30000,
};
let socket = null;
let reconnectAttempts = 0;
let reconnectTimer = null;
const statusEl = document.getElementById("status");
const reconnectBtn = document.getElementById("reconnect-btn");
const portInput = document.getElementById("port-input");
function getServerUrl() {
const port = parseInt(portInput.value, 10) || CONFIG.defaultPort;
return "ws://localhost:" + port;
}
function changePort() {
// Disconnect from current server and connect to new port
if (socket) {
socket.close(1000, "Port changed");
socket = null;
}
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
reconnectAttempts = 0;
connect();
}
function setStatus(state, text) {
statusEl.textContent = text;
statusEl.className = "status " + state;
// Show reconnect button only when disconnected or failed
if (state === "disconnected") {
reconnectBtn.classList.add("visible");
} else {
reconnectBtn.classList.remove("visible");
}
}
function manualReconnect() {
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
reconnectAttempts = 0;
connect();
}
function connect() {
if (socket && socket.readyState === WebSocket.OPEN) return;
setStatus("connecting", "Connecting...");
try {
socket = new WebSocket(getServerUrl());
socket.onopen = () => {
console.log("[UI] Connected to port " + portInput.value);
setStatus("connected", "Connected: " + portInput.value);
reconnectAttempts = 0;
// Ask main thread for handshake info
parent.postMessage(
{ pluginMessage: { type: "get_handshake_info" } },
"*",
);
};
socket.onmessage = (event) => {
let message;
try {
message = JSON.parse(event.data);
} catch (e) {
console.error("[UI] Parse error:", e);
return;
}
if (message.type === "handshake_ack") {
console.log("[UI] Handshake acknowledged");
return;
}
if (message.type === "ping") {
socket.send(
JSON.stringify({ type: "pong", timestamp: message.timestamp }),
);
return;
}
// Forward commands to main thread
if (message.requestId && message.command) {
parent.postMessage(
{
pluginMessage: {
type: "command",
requestId: message.requestId,
command: message.command,
payload: message.payload || {},
},
},
"*",
);
}
};
socket.onclose = (event) => {
console.log("[UI] Closed:", event.code);
setStatus("disconnected", "Disconnected");
socket = null;
if (event.code !== 1000) scheduleReconnect();
};
socket.onerror = (e) => console.error("[UI] Error:", e);
} catch (error) {
console.error("[UI] Failed:", error);
scheduleReconnect();
}
}
function scheduleReconnect() {
if (reconnectAttempts >= CONFIG.reconnectMaxAttempts) {
setStatus("disconnected", "Failed");
return;
}
const delay = Math.min(
CONFIG.reconnectBaseDelay * Math.pow(2, reconnectAttempts),
CONFIG.reconnectMaxDelay,
);
reconnectAttempts++;
setStatus("connecting", "Retry in " + delay / 1000 + "s");
reconnectTimer = setTimeout(connect, delay);
}
function sendToServer(data) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(data));
}
}
// Messages from main thread
window.onmessage = (event) => {
const msg = event.data.pluginMessage;
if (!msg) return;
if (msg.type === "handshake_info") {
sendToServer({ type: "handshake", payload: msg.payload });
} else if (msg.type === "command_response") {
sendToServer({ responseTo: msg.requestId, payload: msg.payload });
}
};
connect();
</script>
</body>
</html>