Implement video interaction features in web interface. Add a checkbox for enabling/disabling keyboard and mouse input to the remote, along with corresponding UI updates and styles. Enhance user feedback with dynamic hints based on interaction state, improving overall usability.

This commit is contained in:
2026-08-29 18:27:11 +03:00
parent c788ccc825
commit 421c83afd2
2 changed files with 35 additions and 3 deletions
+21 -1
View File
@@ -19,6 +19,8 @@
const videoMeta = document.getElementById("video-meta"); const videoMeta = document.getElementById("video-meta");
const videoStart = document.getElementById("video-start"); const videoStart = document.getElementById("video-start");
const videoStop = document.getElementById("video-stop"); const videoStop = document.getElementById("video-stop");
const videoInteract = document.getElementById("video-interact");
const videoKeyHint = document.getElementById("video-key-hint");
let objectUrls = []; let objectUrls = [];
let videoRunning = false; let videoRunning = false;
@@ -331,6 +333,7 @@
} }
async function sendClick(event, button) { async function sendClick(event, button) {
if (!videoInteract.checked) return;
if (!lastMonitor.width || !lastMonitor.height) return; if (!lastMonitor.width || !lastMonitor.height) return;
const rect = videoCanvas.getBoundingClientRect(); const rect = videoCanvas.getBoundingClientRect();
if (!rect.width || !rect.height) return; if (!rect.width || !rect.height) return;
@@ -393,7 +396,7 @@
} }
function onVideoKeydown(event) { function onVideoKeydown(event) {
if (!videoTabActive) return; if (!videoTabActive || !videoInteract.checked) return;
if (isTypingTarget(event.target)) return; if (isTypingTarget(event.target)) return;
if (MODIFIER_CODES.has(event.code)) return; if (MODIFIER_CODES.has(event.code)) return;
const key = keyFromEvent(event); const key = keyFromEvent(event);
@@ -403,6 +406,20 @@
sendRemoteKey(key, "tap", transientModifiers(event)).catch((err) => showError(err.message)); sendRemoteKey(key, "tap", transientModifiers(event)).catch((err) => showError(err.message));
} }
function syncInteractable() {
const on = videoInteract.checked;
videoCanvas.classList.toggle("view-only", !on);
videoKeyHint.textContent = on
? "Keyboard sends to remote while this tab is active"
: "View only — enable Interactable to send mouse and keyboard";
document.querySelectorAll(".mod-btn").forEach((button) => {
button.disabled = !on;
});
if (!on) {
releaseRemoteModifiers().catch((err) => showError(err.message));
}
}
function keyFromEvent(event) { function keyFromEvent(event) {
if (CODE_KEYS[event.code]) return CODE_KEYS[event.code]; if (CODE_KEYS[event.code]) return CODE_KEYS[event.code];
if (event.key && event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) { if (event.key && event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) {
@@ -580,8 +597,10 @@
event.preventDefault(); event.preventDefault();
sendClick(event, "right").catch((err) => showError(err.message)); sendClick(event, "right").catch((err) => showError(err.message));
}); });
videoInteract.addEventListener("change", () => syncInteractable());
document.querySelectorAll(".mod-btn").forEach((button) => { document.querySelectorAll(".mod-btn").forEach((button) => {
button.addEventListener("click", () => { button.addEventListener("click", () => {
if (!videoInteract.checked) return;
const name = button.dataset.mod; const name = button.dataset.mod;
if (!name) return; if (!name) return;
modState[name] = !modState[name]; modState[name] = !modState[name];
@@ -593,6 +612,7 @@
}); });
}); });
}); });
syncInteractable();
document.getElementById("exec-run").addEventListener("click", () => { document.getElementById("exec-run").addEventListener("click", () => {
runCommand().catch((err) => showError(err.message)); runCommand().catch((err) => showError(err.message));
}); });
+14 -2
View File
@@ -133,6 +133,14 @@
cursor: crosshair; cursor: crosshair;
background: #111; background: #111;
} }
#video-canvas.view-only { cursor: default; }
label.check {
display: inline-flex;
align-items: center;
gap: 0.4rem;
user-select: none;
}
label.check input { margin: 0; }
.error { .error {
display: none; display: none;
max-width: 1100px; max-width: 1100px;
@@ -268,11 +276,15 @@
<label>Monitor <label>Monitor
<input id="video-monitor" type="number" min="0" value="0" style="width:4.5rem"> <input id="video-monitor" type="number" min="0" value="0" style="width:4.5rem">
</label> </label>
<label class="check">
<input id="video-interact" type="checkbox">
Interactable
</label>
</div> </div>
<canvas id="video-canvas" width="1280" height="720"></canvas> <canvas id="video-canvas" class="view-only" width="1280" height="720"></canvas>
<p id="video-meta" class="meta"></p> <p id="video-meta" class="meta"></p>
<div class="row" style="margin-top:0.75rem"> <div class="row" style="margin-top:0.75rem">
<div id="video-key-hint">Keyboard sends to remote while this tab is active</div> <div id="video-key-hint">View only — enable Interactable to send mouse and keyboard</div>
</div> </div>
<div class="row"> <div class="row">
<button type="button" class="mod-btn" data-mod="ctrl" aria-pressed="false">Ctrl</button> <button type="button" class="mod-btn" data-mod="ctrl" aria-pressed="false">Ctrl</button>