naisys
Version:
NAISYS - Autonomous AI agent runner with built-in context management and cost tracking
206 lines • 5.71 kB
JavaScript
// How long each chord in a press is held down. Matches typical human keypress
// duration (~100ms) and, crucially, spans enough frames (~6 @ 60Hz) for games
// and emulators to reliably sample the input. Agent loops think and screenshot
// between presses, so the throughput cost is irrelevant here — reliability wins.
export const PRESS_KEY_HOLD_MS = 100;
const MODIFIER_ALIASES = new Map([
["ctrl", "ctrl"],
["control", "ctrl"],
["alt", "alt"],
["option", "alt"],
["shift", "shift"],
["meta", "meta"],
["super", "meta"],
["win", "meta"],
["windows", "meta"],
["cmd", "meta"],
["command", "meta"],
["fn", "fn"],
]);
const KEY_ALIASES = new Map([
["enter", "enter"],
["return", "enter"],
["space", "space"],
["spacebar", "space"],
["tab", "tab"],
["esc", "escape"],
["escape", "escape"],
["backspace", "backspace"],
["delete", "delete"],
["del", "delete"],
["forwarddelete", "delete"],
["fwddelete", "delete"],
["home", "home"],
["end", "end"],
["pageup", "pageup"],
["pgup", "pageup"],
["pagedown", "pagedown"],
["pgdn", "pagedown"],
["up", "up"],
["arrowup", "up"],
["down", "down"],
["arrowdown", "down"],
["left", "left"],
["arrowleft", "left"],
["right", "right"],
["arrowright", "right"],
]);
function sanitizeKeyToken(token) {
return token.trim().toLowerCase().replace(/[_-]+/g, "");
}
function normalizeChordPart(part) {
const raw = part.trim();
if (!raw)
return null;
const sanitized = sanitizeKeyToken(raw);
const modifier = MODIFIER_ALIASES.get(sanitized);
if (modifier) {
return { kind: "modifier", value: modifier };
}
const key = KEY_ALIASES.get(sanitized);
if (key) {
return { kind: "key", value: key };
}
if (/^f\d{1,2}$/i.test(sanitized)) {
return { kind: "key", value: sanitized.toLowerCase() };
}
if (raw.length === 1) {
return { kind: "key", value: raw.toLowerCase() };
}
return { kind: "key", value: raw };
}
export function normalizeKeyCombo(keyCombo) {
return keyCombo
.trim()
.split(/\s+/)
.filter(Boolean)
.map((rawChord) => {
const modifiers = [];
const keys = [];
for (const rawPart of rawChord.split("+")) {
const part = normalizeChordPart(rawPart);
if (!part)
continue;
if (part.kind === "modifier") {
if (!modifiers.includes(part.value)) {
modifiers.push(part.value);
}
continue;
}
keys.push(part.value);
}
return { modifiers, keys };
})
.filter((chord) => chord.modifiers.length > 0 || chord.keys.length > 0);
}
export function canonicalizeKeyCombo(keyCombo) {
return normalizeKeyCombo(keyCombo)
.map((chord) => [...chord.modifiers, ...chord.keys].join("+"))
.join(" ");
}
/** Map a normalized key combo to Playwright's case-sensitive key syntax. */
export function toPlaywrightKeyCombo(keyCombo) {
return normalizeKeyCombo(keyCombo)
.map((chord) => {
const parts = [];
for (const mod of chord.modifiers) {
parts.push(toPlaywrightModifier(mod));
}
for (const key of chord.keys) {
parts.push(toPlaywrightKey(key));
}
return parts.join("+");
})
.join(" ");
}
function toPlaywrightModifier(mod) {
switch (mod) {
case "ctrl":
return "Control";
case "alt":
return "Alt";
case "shift":
return "Shift";
case "meta":
return "Meta";
case "fn":
return "Fn";
}
}
function toPlaywrightKey(key) {
switch (key) {
case "enter":
return "Enter";
case "tab":
return "Tab";
case "escape":
return "Escape";
case "backspace":
return "Backspace";
case "delete":
return "Delete";
case "space":
return "Space";
case "up":
return "ArrowUp";
case "down":
return "ArrowDown";
case "left":
return "ArrowLeft";
case "right":
return "ArrowRight";
case "home":
return "Home";
case "end":
return "End";
case "pageup":
return "PageUp";
case "pagedown":
return "PageDown";
default:
if (/^f\d{1,2}$/i.test(key)) {
return key.toUpperCase();
}
return key;
}
}
export function toLinuxKeyToken(key) {
switch (key) {
case "enter":
return "Return";
case "tab":
return "Tab";
case "escape":
return "Escape";
case "backspace":
return "BackSpace";
case "delete":
return "Delete";
case "space":
return "space";
case "up":
return "Up";
case "down":
return "Down";
case "left":
return "Left";
case "right":
return "Right";
case "home":
return "Home";
case "end":
return "End";
case "pageup":
return "Page_Up";
case "pagedown":
return "Page_Down";
case "meta":
return "super";
default:
if (/^f\d{1,2}$/i.test(key)) {
return key.toUpperCase();
}
return key;
}
}
//# sourceMappingURL=keyCombo.js.map