1export interface RecordingInputOptions {
2 readonly showKeyPresses: boolean;
3 readonly showMousePresses: boolean;
4}
5
6export const DEFAULT_RECORDING_INPUT_OPTIONS: RecordingInputOptions = {
7 showKeyPresses: false,
8 showMousePresses: false,
9};
10
11export interface RecordingKeyPress {
12 readonly key: string;
13 readonly metaKey: boolean;
14 readonly ctrlKey: boolean;
15 readonly altKey: boolean;
16 readonly shiftKey: boolean;
17}
18
19export function recordingKeyLabel(input: RecordingKeyPress, isMac: boolean): string | null {
20 if (["Dead", "Process", "Unidentified", ""].includes(input.key)) return null;
21 const modifiers = [
22 input.ctrlKey || input.key === "Control" ? (isMac ? "⌃" : "Ctrl") : null,
23 input.altKey || input.key === "Alt" ? (isMac ? "⌥" : "Alt") : null,
24 input.shiftKey || input.key === "Shift" ? (isMac ? "⇧" : "Shift") : null,
25 input.metaKey || input.key === "Meta" ? (isMac ? "⌘" : "Win") : null,
26 ].filter((value) => value !== null);
27 const labels: Record<string, string> = {
28 Enter: "↵",
29 Tab: "⇥",
30 Backspace: "⌫",
31 Delete: "⌦",
32 Escape: "Esc",
33 ArrowUp: "↑",
34 ArrowDown: "↓",
35 ArrowLeft: "←",
36 ArrowRight: "→",
37 " ": "Space",
38 Space: "Space",
39 };
40 if (!["Control", "Alt", "Shift", "Meta"].includes(input.key)) {
41 modifiers.push(
42 labels[input.key] ?? (input.key.length === 1 ? input.key.toUpperCase() : input.key),
43 );
44 }
45 return modifiers.join(isMac ? "" : " + ");
46}
47
48/** Unknown iframe or closed-shadow focus is excluded because its field type cannot be checked. */
49export function recordingKeysAreSensitive(document: Document): boolean {
50 let element = document.activeElement;
51 while (element?.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;
52 return (
53 element?.tagName === "IFRAME" ||
54 element?.tagName.includes("-") === true ||
55 element?.getAttribute("type")?.toLowerCase() === "password"
56 );
57}