feat(agent): add screen blackout feature

- Physical displays blanked via overlay
- Remote video and input keep working
- Exclude overlay from screen capture
- Add blackout API and UI toggle
- Wire flag through capture protocol
This commit is contained in:
2026-09-02 13:32:21 +03:00
parent 45f345123d
commit 149fcde379
16 changed files with 557 additions and 28 deletions
+27 -1
View File
@@ -27,6 +27,7 @@
const videoStop = document.getElementById("video-stop");
const videoInteract = document.getElementById("video-interact");
const videoKeyHint = document.getElementById("video-key-hint");
const videoBlackout = document.getElementById("video-blackout");
const webcamCanvas = document.getElementById("webcam-canvas");
const webcamMeta = document.getElementById("webcam-meta");
const webcamDevice = document.getElementById("webcam-device");
@@ -680,7 +681,7 @@
const width = bitmap.width;
const height = bitmap.height;
bitmap.close();
videoMeta.textContent = `${width}×${height} · monitor ${monitor} @ ${lastMonitor.left},${lastMonitor.top}`;
videoMeta.textContent = `${width}×${height} · monitor ${monitor} @ ${lastMonitor.left},${lastMonitor.top}${res.headers.get("X-Screen-Blackout") === "1" ? " · blackout" : ""}`;
}
async function sendClick(event, button) {
@@ -737,6 +738,22 @@
return false;
}
async function loadBlackout() {
const res = await api("/api/v1/blackout");
const data = await res.json();
videoBlackout.checked = Boolean(data.enabled);
}
async function setBlackoutEnabled(enabled) {
const res = await api("/api/v1/blackout", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ enabled }),
});
const data = await res.json();
videoBlackout.checked = Boolean(data.enabled);
}
function setVideoTabActive(active) {
if (videoTabActive === active) return;
videoTabActive = active;
@@ -920,6 +937,9 @@
document.getElementById(button.dataset.tab).classList.add("active");
const onVideo = button.dataset.tab === "video";
setVideoTabActive(onVideo);
if (onVideo) {
loadBlackout().catch((err) => showError(err.message));
}
if (!onVideo) {
stopVideo();
}
@@ -1028,6 +1048,12 @@
sendClick(event, "right").catch((err) => showError(err.message));
});
videoInteract.addEventListener("change", () => syncInteractable());
videoBlackout.addEventListener("change", () => {
setBlackoutEnabled(videoBlackout.checked).catch((err) => {
videoBlackout.checked = !videoBlackout.checked;
showError(err.message);
});
});
document.querySelectorAll(".mod-btn").forEach((button) => {
button.addEventListener("click", () => {
if (!videoInteract.checked) return;