Skip to content

Commit

Permalink
impr: monkey now types with the same hand as the user (@ShaneBerhoff) (
Browse files Browse the repository at this point in the history
…#5930)

This PR also refactors some layout conversion code to reduce repetition.
  • Loading branch information
ShaneBerhoff authored Oct 14, 2024
1 parent ebf98f1 commit ac63934
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 182 deletions.
13 changes: 8 additions & 5 deletions frontend/src/ts/controllers/input-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { IgnoredKeys } from "../constants/ignored-keys";
import { ModifierKeys } from "../constants/modifier-keys";
import { navigate } from "./route-controller";
import * as Loader from "../elements/loader";
import * as KeyConverter from "../utils/key-converter";

let dontInsertSpace = false;
let correctShiftUsed = true;
Expand Down Expand Up @@ -1047,8 +1048,6 @@ $(document).on("keydown", async (event) => {
}
}

Monkey.type();

if (event.key === "Backspace" && TestInput.input.current.length === 0) {
backspaceToPrevious();
if (TestInput.input.current) {
Expand Down Expand Up @@ -1123,14 +1122,16 @@ $(document).on("keydown", async (event) => {

return;
}
const keycode = ShiftTracker.layoutKeyToKeycode(event.key, keymapLayout);
const keycode = KeyConverter.layoutKeyToKeycode(event.key, keymapLayout);

correctShiftUsed =
keycode === undefined
? true
: ShiftTracker.isUsingOppositeShift(keycode);
} else {
correctShiftUsed = ShiftTracker.isUsingOppositeShift(event.code);
correctShiftUsed = ShiftTracker.isUsingOppositeShift(
event.code as KeyConverter.Keycode
);
}
}

Expand Down Expand Up @@ -1182,6 +1183,7 @@ $("#wordsInput").on("keydown", (event) => {
return;
}

Monkey.type(event);
// console.debug("Event: keydown", event);

if (event.code === "NumpadEnter" && Config.funbox.includes("58008")) {
Expand Down Expand Up @@ -1235,10 +1237,11 @@ $("#wordsInput").on("keyup", (event) => {
return;
}

Monkey.stop(event);

if (IgnoredKeys.includes(event.key)) return;

if (TestUI.resultVisible) return;
Monkey.stop();
});

$("#wordsInput").on("beforeinput", (event) => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/ts/test/layout-emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ let isAltGrPressed = false;
const isPunctuationPattern = /\p{P}/u;

export async function getCharFromEvent(
event: JQuery.KeyDownEvent
event: JQuery.KeyDownEvent | JQuery.KeyUpEvent
): Promise<string | null> {
function emulatedLayoutGetVariant(
event: JQuery.KeyDownEvent,
event: JQuery.KeyDownEvent | JQuery.KeyUpEvent,
keyVariants: string
): string | undefined {
let isCapitalized = event.shiftKey;
Expand Down
71 changes: 57 additions & 14 deletions frontend/src/ts/test/monkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { mapRange } from "@monkeytype/util/numbers";
import Config from "../config";
import * as ConfigEvent from "../observables/config-event";
import * as TestState from "../test/test-state";
import * as KeyConverter from "../utils/key-converter";

ConfigEvent.subscribe((eventKey) => {
if (eventKey === "monkey" && TestState.isActive) {
Expand All @@ -15,6 +16,7 @@ ConfigEvent.subscribe((eventKey) => {

let left = false;
let right = false;
const middleKeysState = { left: false, right: false, last: "right" };

// 0 hand up
// 1 hand down
Expand All @@ -38,8 +40,6 @@ const elementsFast = {
"11": document.querySelector("#monkey .fast .both"),
};

let last = "right";

function toBit(b: boolean): "1" | "0" {
return b ? "1" : "0";
}
Expand Down Expand Up @@ -70,25 +70,68 @@ export function updateFastOpacity(num: number): void {
$("#monkey").css({ animationDuration: animDuration + "s" });
}

export function type(): void {
export function type(event: JQuery.KeyDownEvent): void {
if (!Config.monkey) return;
if (!left && last === "right") {
left = true;
last = "left";
} else if (!right) {
right = true;
last = "right";

const { leftSide, rightSide } = KeyConverter.keycodeToKeyboardSide(
event.code as KeyConverter.Keycode
);
if (leftSide && rightSide) {
// if its a middle key handle special case
if (middleKeysState.last === "left") {
if (!right) {
right = true;
middleKeysState.last = "right";
middleKeysState.right = true;
} else if (!left) {
left = true;
middleKeysState.last = "left";
middleKeysState.left = true;
}
} else {
if (!left) {
left = true;
middleKeysState.last = "left";
middleKeysState.left = true;
} else if (!right) {
right = true;
middleKeysState.last = "right";
middleKeysState.right = true;
}
}
} else {
// normal key set hand
left = left || leftSide;
right = right || rightSide;
}

update();
}

export function stop(): void {
export function stop(event: JQuery.KeyUpEvent): void {
if (!Config.monkey) return;
if (left) {
left = false;
} else if (right) {
right = false;

const { leftSide, rightSide } = KeyConverter.keycodeToKeyboardSide(
event.code as KeyConverter.Keycode
);
if (leftSide && rightSide) {
// if middle key handle special case
if (middleKeysState.left && middleKeysState.last === "left") {
left = false;
middleKeysState.left = false;
} else if (middleKeysState.right && middleKeysState.last === "right") {
right = false;
middleKeysState.right = false;
} else {
left = left && !middleKeysState.left;
right = right && !middleKeysState.right;
}
} else {
// normal key unset hand
left = left && !leftSide;
right = right && !rightSide;
}

update();
}

Expand Down
167 changes: 6 additions & 161 deletions frontend/src/ts/test/shift-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,13 @@ import Config from "../config";
import * as JSONData from "../utils/json-data";
import { capsState } from "./caps-warning";
import * as Notifications from "../elements/notifications";
import * as KeyConverter from "../utils/key-converter";

export let leftState = false;
export let rightState = false;

type KeymapLegendStates = [letters: boolean, symbols: boolean];

const qwertyKeycodeKeymap = [
[
"Backquote",
"Digit1",
"Digit2",
"Digit3",
"Digit4",
"Digit5",
"Digit6",
"Digit7",
"Digit8",
"Digit9",
"Digit0",
"Minus",
"Equal",
],
[
"KeyQ",
"KeyW",
"KeyE",
"KeyR",
"KeyT",
"KeyY",
"KeyU",
"KeyI",
"KeyO",
"KeyP",
"BracketLeft",
"BracketRight",
"Backslash",
],
[
"KeyA",
"KeyS",
"KeyD",
"KeyF",
"KeyG",
"KeyH",
"KeyJ",
"KeyK",
"KeyL",
"Semicolon",
"Quote",
],
[
"KeyZ",
"KeyX",
"KeyC",
"KeyV",
"KeyB",
"KeyN",
"KeyM",
"Comma",
"Period",
"Slash",
],
["Space"],
];

const symbolsPattern = /^[^\p{L}\p{N}]{1}$/u;

const isMacLike = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
Expand Down Expand Up @@ -149,11 +91,10 @@ async function updateKeymapLegendCasing(): Promise<void> {
(character) => symbolsPattern.test(character ?? "")
);

const keycode = layoutKeyToKeycode(lowerCaseCharacter, layout);
const keycode = KeyConverter.layoutKeyToKeycode(lowerCaseCharacter, layout);
if (keycode === undefined) {
return;
}

const oppositeShift = isUsingOppositeShift(keycode);

const state = keyIsSymbol ? symbolsState : lettersState;
Expand Down Expand Up @@ -195,71 +136,7 @@ export function reset(): void {
rightState = false;
}

const leftSideKeys = [
"KeyQ",
"KeyW",
"KeyE",
"KeyR",
"KeyT",
"KeyY",

"KeyA",
"KeyS",
"KeyD",
"KeyF",
"KeyG",

"IntlBackslash",
"KeyZ",
"KeyX",
"KeyC",
"KeyV",
"KeyB",

"Backquote",
"Digit1",
"Digit2",
"Digit3",
"Digit4",
"Digit5",
"Digit6",
];

const rightSideKeys = [
"KeyY",
"KeyU",
"KeyI",
"KeyO",
"KeyP",

"KeyH",
"KeyJ",
"KeyK",
"KeyL",

"KeyB",
"KeyN",
"KeyM",

"Digit6",
"Digit7",
"Digit8",
"Digit9",
"Digit0",
"Minus",
"Equal",

"Backslash",
"BracketLeft",
"BracketRight",
"Semicolon",
"Quote",
"Comma",
"Period",
"Slash",
];

export function isUsingOppositeShift(keycode: string): boolean {
export function isUsingOppositeShift(keycode: KeyConverter.Keycode): boolean {
if (!leftState && !rightState) {
return true;
}
Expand All @@ -268,46 +145,14 @@ export function isUsingOppositeShift(keycode: string): boolean {
return true;
}

const isRight = rightSideKeys.includes(keycode);
const isLeft = leftSideKeys.includes(keycode);
if (!isRight && !isLeft) {
const { leftSide, rightSide } = KeyConverter.keycodeToKeyboardSide(keycode);
if (!leftSide && !rightSide) {
return true;
}

if ((leftState && isRight) || (rightState && isLeft)) {
if ((leftState && rightSide) || (rightState && leftSide)) {
return true;
}

return false;
}

export function layoutKeyToKeycode(
key: string,
layout: JSONData.Layout
): string | undefined {
const rows: string[][] = Object.values(layout.keys);

const rowIndex = rows.findIndex((row) => row.find((k) => k.includes(key)));
const row = rows[rowIndex];
if (row === undefined) {
return;
}

const keyIndex = row.findIndex((k) => k.includes(key));
if (keyIndex === -1) {
return;
}

let keycode = qwertyKeycodeKeymap[rowIndex]?.[keyIndex];
if (layout.type === "iso") {
if (rowIndex === 2 && keyIndex === 11) {
keycode = "Backslash";
} else if (rowIndex === 3 && keyIndex === 0) {
keycode = "IntlBackslash";
} else if (rowIndex === 3) {
keycode = qwertyKeycodeKeymap[3]?.[keyIndex - 1];
}
}

return keycode;
}
Loading

0 comments on commit ac63934

Please sign in to comment.