lazyBoy/apps/web/vnc.html

155 lines
5.7 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 {
display: none;
2026-09-03 12:46:14 +00:00
}
#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;
window.parent.postMessage({type:"lazyboy-paste-text", text}, parentOrigin);
2026-09-03 16:16:34 +00:00
}
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 applyViewOnly(value) {
if (!rfb) return;
rfb.viewOnly = Boolean(value);
}
2026-09-03 12:46:14 +00:00
function connect() {
setStatus("Connecting to desktop…");
window.parent.postMessage({ type: "lazyboy-desktop-lost" }, parentOrigin);
2026-09-03 12:46:14 +00:00
rfb = new RFB(document.getElementById("screen"), url);
rfb.viewOnly = flag("view_only", true);
2026-09-03 12:46:14 +00:00
rfb.scaleViewport = true;
rfb.clipViewport = false;
rfb.background = "#0f172a";
pinTaskbar();
rfb.addEventListener("connect", () => {
pinTaskbar();
try { rfb.focus(); } catch (_) {}
window.parent.postMessage({ type: "lazyboy-desktop-ready" }, parentOrigin);
2026-09-03 12:46:14 +00:00
});
rfb.addEventListener("disconnect", (event) => {
window.parent.postMessage({ type: "lazyboy-desktop-lost" }, parentOrigin);
2026-09-03 12:46:14 +00:00
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
}
// Capture before noVNC forwards the shortcut; otherwise stale remote
// clipboard contents can be pasted once before the local text arrives.
window.addEventListener("keydown", async (event) => {
if (rfb && !rfb.viewOnly && event.metaKey && event.code === "KeyC") {
event.preventDefault(); event.stopImmediatePropagation();
window.parent.postMessage({type:"lazyboy-copy-request"},parentOrigin); return;
}
if (!rfb || rfb.viewOnly || !(event.ctrlKey || event.metaKey) || event.altKey || event.code !== "KeyV") return;
event.preventDefault();
event.stopImmediatePropagation();
const target = rfb;
try {
const text = await navigator.clipboard.readText();
if (rfb === target && !target.viewOnly) pasteIntoDesktop(text);
} catch (_) {
window.parent.postMessage({ type: "lazyboy-paste-request" }, parentOrigin);
}
}, true);
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) => {
if (event.origin !== parentOrigin || event.source !== window.parent) return;
if (!event.data) return;
if (event.data.type === "lazyboy-view-only") {
applyViewOnly(event.data.viewOnly);
return;
}
if (event.data.type !== "lazyboy-host-clipboard") return;
2026-09-03 16:16:34 +00:00
pasteIntoDesktop(String(event.data.text || ""));
2026-09-03 12:46:14 +00:00
});
</script>
</head>
<body>
<div id="status" hidden>Loading desktop…</div>
2026-09-03 12:46:14 +00:00
<div id="screen"></div>
</body>
</html>