mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 10:21:34 +00:00
fix: forward Android hardware keyboard keys (#1114)
This commit is contained in:
@@ -47,6 +47,7 @@ import { globalShortcutHandler } from "@/lib/global-shortcut-handler";
|
||||
import { useCommandTracker } from "@/features/terminal/command-history/useCommandTracker.ts";
|
||||
import { highlightTerminalOutput } from "@/lib/terminal-syntax-highlighter.ts";
|
||||
import { useCommandHistory } from "@/features/terminal/command-history/CommandHistoryContext.tsx";
|
||||
import { getAndroidHardwareKeySequence } from "@/features/terminal/android-hardware-keyboard.ts";
|
||||
import { CommandAutocomplete } from "./command-history/CommandAutocomplete.tsx";
|
||||
import { SimpleLoader } from "@/lib/SimpleLoader.tsx";
|
||||
import { useConfirmation } from "@/hooks/use-confirmation.ts";
|
||||
@@ -2479,6 +2480,24 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.userAgent.includes("Android")) {
|
||||
const sequence = getAndroidHardwareKeySequence(
|
||||
e,
|
||||
terminal.modes.applicationCursorKeysMode,
|
||||
hostConfig.terminalConfig?.backspaceMode,
|
||||
);
|
||||
if (sequence) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
|
||||
webSocketRef.current.send(
|
||||
JSON.stringify({ type: "input", data: sequence }),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward global app shortcuts to AppShell directly — xterm swallows
|
||||
// all keydown events and synthetic re-dispatch is unreliable.
|
||||
// stopPropagation prevents the same event from also firing the window listener.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { HostBackspaceMode } from "@/sidebar/HostEditorData";
|
||||
|
||||
const CURSOR_SEQUENCES: Record<string, [normal: string, application: string]> =
|
||||
{
|
||||
ArrowUp: ["\x1b[A", "\x1bOA"],
|
||||
ArrowDown: ["\x1b[B", "\x1bOB"],
|
||||
ArrowRight: ["\x1b[C", "\x1bOC"],
|
||||
ArrowLeft: ["\x1b[D", "\x1bOD"],
|
||||
};
|
||||
|
||||
export function getAndroidHardwareKeySequence(
|
||||
event: Pick<
|
||||
KeyboardEvent,
|
||||
"key" | "ctrlKey" | "altKey" | "metaKey" | "shiftKey"
|
||||
>,
|
||||
applicationCursorKeys: boolean,
|
||||
backspaceMode: HostBackspaceMode | undefined,
|
||||
): string | null {
|
||||
if (event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cursor = CURSOR_SEQUENCES[event.key];
|
||||
if (cursor) return cursor[applicationCursorKeys ? 1 : 0];
|
||||
if (event.key === "Delete") return "\x1b[3~";
|
||||
if (event.key === "Backspace" && backspaceMode !== "control-h") {
|
||||
return "\x7f";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getAndroidHardwareKeySequence } from "@/features/terminal/android-hardware-keyboard";
|
||||
|
||||
const key = (
|
||||
value: string,
|
||||
modifiers: Partial<KeyboardEvent> = {},
|
||||
): Pick<
|
||||
KeyboardEvent,
|
||||
"key" | "ctrlKey" | "altKey" | "metaKey" | "shiftKey"
|
||||
> => ({
|
||||
key: value,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
...modifiers,
|
||||
});
|
||||
|
||||
describe("getAndroidHardwareKeySequence", () => {
|
||||
it("maps cursor keys in normal and application modes", () => {
|
||||
expect(
|
||||
getAndroidHardwareKeySequence(key("ArrowUp"), false, undefined),
|
||||
).toBe("\x1b[A");
|
||||
expect(getAndroidHardwareKeySequence(key("ArrowUp"), true, undefined)).toBe(
|
||||
"\x1bOA",
|
||||
);
|
||||
});
|
||||
|
||||
it("maps Delete and the default Backspace mode", () => {
|
||||
expect(getAndroidHardwareKeySequence(key("Delete"), false, undefined)).toBe(
|
||||
"\x1b[3~",
|
||||
);
|
||||
expect(
|
||||
getAndroidHardwareKeySequence(key("Backspace"), false, undefined),
|
||||
).toBe("\x7f");
|
||||
});
|
||||
|
||||
it("leaves control-h Backspace and modified keys to existing handlers", () => {
|
||||
expect(
|
||||
getAndroidHardwareKeySequence(key("Backspace"), false, "control-h"),
|
||||
).toBeNull();
|
||||
expect(
|
||||
getAndroidHardwareKeySequence(
|
||||
key("ArrowLeft", { ctrlKey: true }),
|
||||
false,
|
||||
undefined,
|
||||
),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user