Add key input handling functionality to the agent. Implement API endpoint for sending key presses with support for action types (tap, down, up) and modifiers. Update web interface to allow users to send keys and toggle modifier states through a new interactive element.
This commit is contained in:
+73
-20
@@ -273,6 +273,7 @@
|
||||
videoRunning = false;
|
||||
videoStart.disabled = false;
|
||||
videoStop.disabled = true;
|
||||
releaseRemoteModifiers().catch((err) => showError(err.message));
|
||||
}
|
||||
|
||||
async function pullVideoFrame() {
|
||||
@@ -315,17 +316,62 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function sendVideoText() {
|
||||
const field = document.getElementById("video-text");
|
||||
const text = field.value;
|
||||
if (!text) return;
|
||||
const delayMs = clamp(document.getElementById("video-key-delay").value, 0, 200);
|
||||
await api("/api/v1/input/text", {
|
||||
const MODIFIER_CODES = new Set([
|
||||
"ControlLeft", "ControlRight", "ShiftLeft", "ShiftRight",
|
||||
"AltLeft", "AltRight", "MetaLeft", "MetaRight", "OSLeft", "OSRight",
|
||||
]);
|
||||
|
||||
const CODE_KEYS = {
|
||||
Enter: "enter", NumpadEnter: "enter", Backspace: "backspace", Tab: "tab",
|
||||
Escape: "escape", Space: "space", Delete: "delete", Insert: "insert",
|
||||
Home: "home", End: "end", PageUp: "pageup", PageDown: "pagedown",
|
||||
ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right",
|
||||
Semicolon: ";", Equal: "=", Comma: ",", Minus: "-", Period: ".", Slash: "/",
|
||||
Backquote: "`", BracketLeft: "[", Backslash: "\\", BracketRight: "]", Quote: "'",
|
||||
};
|
||||
for (let i = 0; i <= 9; i += 1) CODE_KEYS[`Digit${i}`] = String(i);
|
||||
for (let i = 0; i < 26; i += 1) {
|
||||
const letter = String.fromCharCode(65 + i);
|
||||
CODE_KEYS[`Key${letter}`] = letter.toLowerCase();
|
||||
}
|
||||
for (let i = 1; i <= 12; i += 1) CODE_KEYS[`F${i}`] = `f${i}`;
|
||||
|
||||
const modState = { ctrl: false, alt: false, shift: false, win: false };
|
||||
|
||||
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)) {
|
||||
return event.key.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function transientModifiers(event) {
|
||||
const mods = [];
|
||||
if (event.shiftKey && !modState.shift) mods.push("shift");
|
||||
if (event.ctrlKey && !modState.ctrl) mods.push("ctrl");
|
||||
if (event.altKey && !modState.alt) mods.push("alt");
|
||||
if (event.metaKey && !modState.win) mods.push("win");
|
||||
return mods;
|
||||
}
|
||||
|
||||
async function sendRemoteKey(key, action = "tap", modifiers = []) {
|
||||
await api("/api/v1/input/key", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ text, delay_ms: delayMs }),
|
||||
body: JSON.stringify({ key, action, modifiers }),
|
||||
});
|
||||
field.value = "";
|
||||
}
|
||||
|
||||
async function releaseRemoteModifiers() {
|
||||
const jobs = [];
|
||||
for (const [name, active] of Object.entries(modState)) {
|
||||
if (!active) continue;
|
||||
modState[name] = false;
|
||||
document.querySelector(`.mod-btn[data-mod="${name}"]`)?.classList.remove("active");
|
||||
jobs.push(sendRemoteKey(name, "up"));
|
||||
}
|
||||
await Promise.all(jobs);
|
||||
}
|
||||
|
||||
async function listKeylogs() {
|
||||
@@ -427,19 +473,26 @@
|
||||
event.preventDefault();
|
||||
sendClick(event, "right").catch((err) => showError(err.message));
|
||||
});
|
||||
document.getElementById("video-send").addEventListener("click", () => {
|
||||
sendVideoText().catch((err) => showError(err.message));
|
||||
const videoKeys = document.getElementById("video-keys");
|
||||
videoKeys.addEventListener("keydown", (event) => {
|
||||
if (MODIFIER_CODES.has(event.code)) return;
|
||||
const key = keyFromEvent(event);
|
||||
if (!key) return;
|
||||
event.preventDefault();
|
||||
sendRemoteKey(key, "tap", transientModifiers(event)).catch((err) => showError(err.message));
|
||||
});
|
||||
const videoKeyDelay = document.getElementById("video-key-delay");
|
||||
const videoKeyDelayVal = document.getElementById("video-key-delay-val");
|
||||
videoKeyDelay.addEventListener("input", () => {
|
||||
videoKeyDelayVal.textContent = videoKeyDelay.value;
|
||||
});
|
||||
document.getElementById("video-text").addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
sendVideoText().catch((err) => showError(err.message));
|
||||
}
|
||||
document.querySelectorAll(".mod-btn").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const name = button.dataset.mod;
|
||||
if (!name) return;
|
||||
modState[name] = !modState[name];
|
||||
button.classList.toggle("active", modState[name]);
|
||||
sendRemoteKey(name, modState[name] ? "down" : "up").catch((err) => {
|
||||
modState[name] = !modState[name];
|
||||
button.classList.toggle("active", modState[name]);
|
||||
showError(err.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
document.getElementById("exec-run").addEventListener("click", () => {
|
||||
runCommand().catch((err) => showError(err.message));
|
||||
|
||||
Reference in New Issue
Block a user