e555561a2d
- Remove useSpecialKeys hook state, export pure utility functions instead - MobileTerminalWrapper now owns activeModifier state - SpecialKeysStrip and SpecialKeysPanel receive modifier via props - TerminalComponent applies modifier to virtual keyboard input via activeModifier prop - Modifier now works with both special keys AND virtual keyboard input - Modifier clears after any key press (special or virtual keyboard)
141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
|
|
|
|
export type SpecialKey =
|
|
| "escape"
|
|
| "tab"
|
|
| "ctrl"
|
|
| "alt"
|
|
| "up"
|
|
| "down"
|
|
| "left"
|
|
| "right"
|
|
| "home"
|
|
| "end"
|
|
| "pageup"
|
|
| "pagedown"
|
|
| "ctrlc"
|
|
| "ctrld"
|
|
| "ctrlz"
|
|
| "f1"
|
|
| "f2"
|
|
| "f3"
|
|
| "f4"
|
|
| "f5"
|
|
| "f6"
|
|
| "f7"
|
|
| "f8"
|
|
| "f9"
|
|
| "f10"
|
|
| "f11"
|
|
| "f12";
|
|
|
|
export type ModifierKey = "ctrl" | "alt";
|
|
|
|
const KEY_SEQUENCES: Record<SpecialKey, string> = {
|
|
escape: "\x1B",
|
|
tab: "\t",
|
|
ctrl: "",
|
|
alt: "",
|
|
up: "\x1B[A",
|
|
down: "\x1B[B",
|
|
right: "\x1B[C",
|
|
left: "\x1B[D",
|
|
home: "\x1B[H",
|
|
end: "\x1B[F",
|
|
pageup: "\x1B[5~",
|
|
pagedown: "\x1B[6~",
|
|
ctrlc: "\x03",
|
|
ctrld: "\x04",
|
|
ctrlz: "\x1A",
|
|
f1: "\x1BOP",
|
|
f2: "\x1BOQ",
|
|
f3: "\x1BOR",
|
|
f4: "\x1BOS",
|
|
f5: "\x1B[15~",
|
|
f6: "\x1B[17~",
|
|
f7: "\x1B[18~",
|
|
f8: "\x1B[19~",
|
|
f9: "\x1B[20~",
|
|
f10: "\x1B[21~",
|
|
f11: "\x1B[23~",
|
|
f12: "\x1B[24~",
|
|
};
|
|
|
|
// Single character sequences with modifier prefixes
|
|
const MODIFIER_PREFIXES: Record<string, { ctrl: string; alt: string; ctrlAlt: string }> = {
|
|
// Letters
|
|
a: { ctrl: "\x01", alt: "\x1Ba", ctrlAlt: "\x1B\x01" },
|
|
b: { ctrl: "\x02", alt: "\x1Bb", ctrlAlt: "\x1B\x02" },
|
|
c: { ctrl: "\x03", alt: "\x1Bc", ctrlAlt: "\x1B\x03" },
|
|
d: { ctrl: "\x04", alt: "\x1Bd", ctrlAlt: "\x1B\x04" },
|
|
e: { ctrl: "\x05", alt: "\x1Be", ctrlAlt: "\x1B\x05" },
|
|
f: { ctrl: "\x06", alt: "\x1Bf", ctrlAlt: "\x1B\x06" },
|
|
g: { ctrl: "\x07", alt: "\x1Bg", ctrlAlt: "\x1B\x07" },
|
|
h: { ctrl: "\x08", alt: "\x1Bh", ctrlAlt: "\x1B\x08" },
|
|
i: { ctrl: "\x09", alt: "\x1Bi", ctrlAlt: "\x1B\x09" },
|
|
j: { ctrl: "\x0A", alt: "\x1Bj", ctrlAlt: "\x1B\x0A" },
|
|
k: { ctrl: "\x0B", alt: "\x1Bk", ctrlAlt: "\x1B\x0B" },
|
|
l: { ctrl: "\x0C", alt: "\x1Bl", ctrlAlt: "\x1B\x0C" },
|
|
m: { ctrl: "\x0D", alt: "\x1Bm", ctrlAlt: "\x1B\x0D" },
|
|
n: { ctrl: "\x0E", alt: "\x1Bn", ctrlAlt: "\x1B\x0E" },
|
|
o: { ctrl: "\x0F", alt: "\x1Bo", ctrlAlt: "\x1B\x0F" },
|
|
p: { ctrl: "\x10", alt: "\x1Bp", ctrlAlt: "\x1B\x10" },
|
|
q: { ctrl: "\x11", alt: "\x1Bq", ctrlAlt: "\x1B\x11" },
|
|
r: { ctrl: "\x12", alt: "\x1Br", ctrlAlt: "\x1B\x12" },
|
|
s: { ctrl: "\x13", alt: "\x1Bs", ctrlAlt: "\x1B\x13" },
|
|
t: { ctrl: "\x14", alt: "\x1Bt", ctrlAlt: "\x1B\x14" },
|
|
u: { ctrl: "\x15", alt: "\x1Bu", ctrlAlt: "\x1B\x15" },
|
|
v: { ctrl: "\x16", alt: "\x1Bv", ctrlAlt: "\x1B\x16" },
|
|
w: { ctrl: "\x17", alt: "\x1Bw", ctrlAlt: "\x1B\x17" },
|
|
x: { ctrl: "\x18", alt: "\x1Bx", ctrlAlt: "\x1B\x18" },
|
|
y: { ctrl: "\x19", alt: "\x1By", ctrlAlt: "\x1B\x19" },
|
|
z: { ctrl: "\x1A", alt: "\x1Bz", ctrlAlt: "\x1B\x1A" },
|
|
// Numbers
|
|
"0": { ctrl: "0", alt: "\x1B0", ctrlAlt: "\x1B0" },
|
|
"1": { ctrl: "1", alt: "\x1B1", ctrlAlt: "\x1B1" },
|
|
"2": { ctrl: "\x00", alt: "\x1B2", ctrlAlt: "\x1B\x00" },
|
|
"3": { ctrl: "\x1B", alt: "\x1B3", ctrlAlt: "\x1B\x1B" },
|
|
"4": { ctrl: "\x1C", alt: "\x1B4", ctrlAlt: "\x1B\x1C" },
|
|
"5": { ctrl: "\x1D", alt: "\x1B5", ctrlAlt: "\x1B\x1D" },
|
|
"6": { ctrl: "\x1E", alt: "\x1B6", ctrlAlt: "\x1B\x1E" },
|
|
"7": { ctrl: "\x1F", alt: "\x1B7", ctrlAlt: "\x1B\x1F" },
|
|
"8": { ctrl: "\x7F", alt: "\x1B8", ctrlAlt: "\x1B\x7F" },
|
|
"9": { ctrl: "9", alt: "\x1B9", ctrlAlt: "\x1B9" },
|
|
};
|
|
|
|
export function getSequenceWithModifier(
|
|
key: SpecialKey,
|
|
activeModifier: ModifierKey | null
|
|
): { sequence: string; clearModifier: boolean } | null {
|
|
// Handle modifier keys (one-shot)
|
|
if (key === "ctrl" || key === "alt") {
|
|
return null; // Modifiers don't send anything themselves
|
|
}
|
|
|
|
const sequence = KEY_SEQUENCES[key];
|
|
if (!sequence) return null;
|
|
|
|
// Check if we have an active modifier and the key is a single character
|
|
if (activeModifier && sequence.length === 1) {
|
|
const char = sequence;
|
|
const mapping = MODIFIER_PREFIXES[char.toLowerCase()];
|
|
if (mapping) {
|
|
return { sequence: mapping[activeModifier], clearModifier: true };
|
|
}
|
|
}
|
|
|
|
return { sequence, clearModifier: !!activeModifier };
|
|
}
|
|
|
|
export function applyModifierToChar(
|
|
char: string,
|
|
modifier: ModifierKey
|
|
): string | null {
|
|
if (char.length !== 1) return null;
|
|
const mapping = MODIFIER_PREFIXES[char.toLowerCase()];
|
|
if (!mapping) return null;
|
|
return mapping[modifier];
|
|
}
|
|
|
|
export { KEY_SEQUENCES };
|