From 421c83afd2d08c7460e238cfc2b01249cc89b1d7 Mon Sep 17 00:00:00 2001 From: Daniel Legt Date: Sat, 29 Aug 2026 18:27:11 +0300 Subject: [PATCH] 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. --- lib/agent/web/app.js | 22 +++++++++++++++++++++- lib/agent/web/index.html | 16 ++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/agent/web/app.js b/lib/agent/web/app.js index a943a88..c208748 100644 --- a/lib/agent/web/app.js +++ b/lib/agent/web/app.js @@ -19,6 +19,8 @@ const videoMeta = document.getElementById("video-meta"); const videoStart = document.getElementById("video-start"); const videoStop = document.getElementById("video-stop"); + const videoInteract = document.getElementById("video-interact"); + const videoKeyHint = document.getElementById("video-key-hint"); let objectUrls = []; let videoRunning = false; @@ -331,6 +333,7 @@ } async function sendClick(event, button) { + if (!videoInteract.checked) return; if (!lastMonitor.width || !lastMonitor.height) return; const rect = videoCanvas.getBoundingClientRect(); if (!rect.width || !rect.height) return; @@ -393,7 +396,7 @@ } function onVideoKeydown(event) { - if (!videoTabActive) return; + if (!videoTabActive || !videoInteract.checked) return; if (isTypingTarget(event.target)) return; if (MODIFIER_CODES.has(event.code)) return; const key = keyFromEvent(event); @@ -403,6 +406,20 @@ 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) { if (CODE_KEYS[event.code]) return CODE_KEYS[event.code]; if (event.key && event.key.length === 1 && /[a-zA-Z0-9]/.test(event.key)) { @@ -580,8 +597,10 @@ event.preventDefault(); sendClick(event, "right").catch((err) => showError(err.message)); }); + videoInteract.addEventListener("change", () => syncInteractable()); document.querySelectorAll(".mod-btn").forEach((button) => { button.addEventListener("click", () => { + if (!videoInteract.checked) return; const name = button.dataset.mod; if (!name) return; modState[name] = !modState[name]; @@ -593,6 +612,7 @@ }); }); }); + syncInteractable(); document.getElementById("exec-run").addEventListener("click", () => { runCommand().catch((err) => showError(err.message)); }); diff --git a/lib/agent/web/index.html b/lib/agent/web/index.html index 5867051..072298d 100644 --- a/lib/agent/web/index.html +++ b/lib/agent/web/index.html @@ -133,6 +133,14 @@ cursor: crosshair; 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 { display: none; max-width: 1100px; @@ -268,11 +276,15 @@ + - +

-
Keyboard sends to remote while this tab is active
+
View only — enable Interactable to send mouse and keyboard