pi-remote-control/extensions/remote-control/tmux/input.ts

90 lines
1.9 KiB
TypeScript

/**
* tmux send-keys input translation.
*
* Translates IC-1 key names (and literal text) into tmux send-keys arguments.
* Used by the input route (T-1.5) to deliver keystrokes to a pane.
*
* Owner: T-1.1
*/
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
/** Named keys from IC-1 ClientToServer `{ type: "key"; name: string }`. */
const KEY_MAP: Record<string, string> = {
escape: "Escape",
tab: "Tab",
up: "Up",
down: "Down",
left: "Left",
right: "Right",
enter: "Enter",
"shift-enter": "S-Enter",
backspace: "BSpace",
"ctrl-c": "C-c",
"ctrl-d": "C-d",
"ctrl-z": "C-z",
};
/**
* Send a single named key to a tmux pane.
* Pane defaults to the first pane of the session (session:0.0).
*/
export async function sendKey(
session: string,
name: string,
pane = "0.0",
): Promise<void> {
const tmuxKey = KEY_MAP[name.toLowerCase()];
if (!tmuxKey) {
throw new Error(
`Unknown key name: "${name}". Supported: ${Object.keys(KEY_MAP).join(", ")}`,
);
}
await execFileAsync("tmux", [
"send-keys",
"-t",
`${session}:${pane}`,
tmuxKey,
]);
}
/**
* Send literal text to a tmux pane (IC-1 `{ type: "keys"; data: string }`).
* Uses send-keys -l which sends each character literally.
*/
export async function sendKeys(
session: string,
data: string,
pane = "0.0",
): Promise<void> {
await execFileAsync("tmux", [
"send-keys",
"-t",
`${session}:${pane}`,
"-l",
data,
]);
}
/**
* Send bracketed-paste to a tmux pane (IC-1 `{ type: "paste"; data: string }`).
* Wraps the data in bracketed-paste sequences then sends literally.
*/
export async function sendPaste(
session: string,
data: string,
pane = "0.0",
): Promise<void> {
const wrapped = `\x1b[200~${data}\x1b[201~`;
await execFileAsync("tmux", [
"send-keys",
"-t",
`${session}:${pane}`,
"-l",
wrapped,
]);
}