lazyBoy/apps/web/vnc.html

147 lines
5.1 KiB
HTML
Raw Normal View History

2026-09-03 12:46:14 +00:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>LazyBoy desktop</title>
<style>
html, body, #screen {
margin: 0;
height: 100%;
width: 100%;
background: #0f172a;
overflow: hidden;
}
#status {
position: absolute;
left: 8px;
top: 8px;
z-index: 2;
padding: 4px 8px;
border-radius: 6px;
background: rgba(0, 0, 0, 0.45);
color: #fff;
font: 12px/1.3 ui-sans-serif, system-ui, sans-serif;
pointer-events: none;
}
#screen canvas { cursor: default; }
</style>
<script type="module">
import RFB from "./core/rfb.js";
function query(name) {
const match = `${document.location.href}${window.location.hash}`.match(
new RegExp(`[?&#]${name}=([^&#]*)`)
);
return match ? decodeURIComponent(match[1]) : null;
}
function flag(name, fallback = false) {
const raw = query(name);
if (raw == null) return fallback;
const value = String(raw).toLowerCase();
return value === "1" || value === "true" || value === "yes";
}
const statusEl = document.getElementById("status");
const setStatus = (text) => {
statusEl.textContent = text;
};
const prefix = window.location.pathname.replace(/[^/]+$/, "");
const protocol = window.location.protocol === "https:" ? "wss" : "ws";
const path = (query("path") || "websockify").replace(/^\//, "");
const url = path.includes("/")
? `${protocol}://${window.location.host}/${path}`
: `${protocol}://${window.location.host}${prefix}${path}`;
let rfb = null;
let reconnectTimer = null;
2026-09-03 23:43:37 +00:00
const parentOrigin = window.location.origin;
2026-09-03 12:46:14 +00:00
2026-09-03 16:16:34 +00:00
function pasteIntoDesktop(text) {
if (!rfb || rfb.viewOnly || !text) return;
try { rfb.clipboardPasteFrom(text); } catch (_) {}
// x11vnc applies ClientCutText asynchronously. Give X11 a moment before
// sending Ctrl+V so pastes are reliable for Chromium and terminals.
setTimeout(() => {
try {
rfb.sendKey(0xffe3, "ControlLeft", true);
rfb.sendKey(0x0076, "KeyV", true);
rfb.sendKey(0x0076, "KeyV", false);
rfb.sendKey(0xffe3, "ControlLeft", false);
} catch (_) {}
}, 100);
}
2026-09-03 12:46:14 +00:00
function pinTaskbar() {
// noVNC centers the scaled canvas (margin:auto), leaving a dead
// strip under the panel. Stick the canvas to the bottom.
const inner = document.querySelector("#screen > div");
const canvas = document.querySelector("#screen canvas");
if (inner) {
inner.style.alignItems = "flex-end";
inner.style.justifyContent = "center";
}
if (canvas) {
canvas.style.margin = "0 auto";
}
}
function connect() {
setStatus("Connecting to desktop…");
statusEl.style.display = "block";
rfb = new RFB(document.getElementById("screen"), url);
rfb.viewOnly = flag("view_only", false);
rfb.scaleViewport = true;
rfb.clipViewport = false;
rfb.background = "#0f172a";
pinTaskbar();
rfb.addEventListener("connect", () => {
pinTaskbar();
setStatus(rfb.viewOnly ? "View only — click to take control" : "Click the desktop");
try { rfb.focus(); } catch (_) {}
setTimeout(() => { statusEl.style.display = "none"; }, 2500);
});
rfb.addEventListener("disconnect", (event) => {
statusEl.style.display = "block";
const clean = event && event.detail && event.detail.clean;
setStatus(clean ? "Disconnected — retrying" : "Desktop connection lost — retrying");
if (reconnectTimer) clearTimeout(reconnectTimer);
reconnectTimer = setTimeout(connect, 1500);
});
2026-09-03 16:16:34 +00:00
rfb.addEventListener("clipboard", (event) => {
const text = event && event.detail ? event.detail.text : "";
if (typeof text === "string") {
2026-09-03 23:43:37 +00:00
window.parent.postMessage({ type: "lazyboy-desktop-clipboard", text }, parentOrigin);
2026-09-03 16:16:34 +00:00
}
});
2026-09-03 12:46:14 +00:00
}
connect();
window.addEventListener("resize", pinTaskbar);
window.addEventListener("pointerdown", () => {
try { if (rfb) rfb.focus(); } catch (_) {}
if (rfb && rfb.viewOnly) {
2026-09-03 23:43:37 +00:00
window.parent.postMessage({ type: "lazyboy-request-control" }, parentOrigin);
2026-09-03 12:46:14 +00:00
}
});
window.addEventListener("paste", (event) => {
if (!rfb || rfb.viewOnly) return;
const text = event.clipboardData ? event.clipboardData.getData("text") : "";
if (!text) return;
event.preventDefault();
2026-09-03 16:16:34 +00:00
pasteIntoDesktop(text);
});
window.addEventListener("message", (event) => {
2026-09-03 23:43:37 +00:00
if (event.origin !== parentOrigin) return;
2026-09-03 16:16:34 +00:00
if (!event.data || event.data.type !== "lazyboy-host-clipboard") return;
pasteIntoDesktop(String(event.data.text || ""));
2026-09-03 12:46:14 +00:00
});
</script>
</head>
<body>
<div id="status">Loading desktop…</div>
<div id="screen"></div>
</body>
</html>