Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
becb40f
perf
Miodec Nov 26, 2025
b201281
fix: ellipsis not working correctly
Miodec Nov 26, 2025
3baf5e9
use func
Miodec Nov 26, 2025
19c3515
Merge branch 'master' into perf
Miodec Nov 26, 2025
a57724e
raf keymap
Miodec Nov 27, 2025
aa49735
optimize monkey
Miodec Nov 27, 2025
78ad855
comment
Miodec Nov 27, 2025
96d6be0
spark position
Miodec Nov 27, 2025
a3fd0d4
move power to ui
Miodec Nov 27, 2025
c199d5c
more guards
Miodec Nov 27, 2025
f6e73e2
guard
Miodec Nov 27, 2025
dac89fc
disable timer debug
Miodec Nov 27, 2025
e524a51
big brain solution
Miodec Nov 27, 2025
c9794b5
disable expensive jump check when timer is slow
Miodec Nov 27, 2025
8bb9bb7
remove debug code
Miodec Nov 28, 2025
a2813d8
unnecessary sleep
Miodec Nov 28, 2025
b4b247c
comment
Miodec Nov 28, 2025
c2ad456
hide replay stuff when updating result page, not starting test
Miodec Nov 28, 2025
abd679e
allow strings
Miodec Nov 28, 2025
613efd9
restructure result showing
Miodec Nov 28, 2025
c1e703d
update keys
Miodec Nov 28, 2025
f3f804f
cache words and wordsWrapper
Miodec Nov 28, 2025
6fa100e
move animation
Miodec Nov 28, 2025
06007ee
rework loader
Miodec Nov 28, 2025
b9cad38
dont use raf if word is needed immediately
Miodec Nov 28, 2025
6d1b36e
use vanilla
Miodec Nov 28, 2025
0b0621b
use cache
Miodec Nov 28, 2025
48e8ac9
Merge branch 'master' into perf
Miodec Nov 28, 2025
a77d79d
Merge branch 'master' into perf
Miodec Nov 28, 2025
f391d87
Merge branch 'master' into perf
Miodec Dec 4, 2025
1aa4d0a
move common code
Miodec Dec 4, 2025
3110c8a
Merge branch 'master' into perf
Miodec Dec 4, 2025
f0a3535
Merge branch 'master' into perf
Miodec Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frontend/src/html/pages/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@
<div id="premidTestMode" class="hidden"></div>
<div id="premidSecondsLeft" class="hidden"></div>
</div>
<div class="loading hidden">
<i class="fas fa-circle-notch fa-spin"></i>
</div>
<div id="result" class="content-grid full-width hidden" tabindex="-1">
<div class="wrapper">
<div class="stats">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<load src="html/warnings.html" />
<div id="fpsCounter" class="hidden"></div>
<div class="customBackground"></div>
<div id="backgroundLoader" style="display: none"></div>
<div id="backgroundLoader" class="hidden"></div>
<div id="bannerCenter" class="focus"></div>
<div id="notificationCenter">
<div class="clearAll button invisible" style="display: none">
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/styles/animations.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
}
}

@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

@keyframes caretFlashSmooth {
0%,
100% {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/styles/core.scss
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ body {
background: var(--main-color);
animation: loader 2s cubic-bezier(0.38, 0.16, 0.57, 0.82) infinite;
z-index: 9999;
opacity: 0;
}

key {
Expand Down
18 changes: 17 additions & 1 deletion frontend/src/styles/test.scss
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@
}

#wordsInput {
width: 1ch;
width: 0;
font-size: 1em;
height: 1em;
opacity: 0;
Expand Down Expand Up @@ -631,6 +631,22 @@
}
}

.pageTest > .loading {
text-align: center;
& > i {
font-size: 2rem;
color: var(--main-color);
}
opacity: 0;
&:not(.hidden) {
animation-name: fadeIn;
animation-duration: 0.125s;
animation-delay: 0.5s;
animation-fill-mode: forwards;
animation-timing-function: ease;
}
}

#result {
&:focus-visible {
outline: none;
Expand Down
92 changes: 49 additions & 43 deletions frontend/src/ts/elements/keymap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getActiveFunboxNames } from "../test/funbox/list";
import { areSortedArraysEqual } from "../utils/arrays";
import { LayoutObject } from "@monkeytype/schemas/layouts";
import { animate } from "animejs";
import { requestDebouncedAnimationFrame } from "../utils/debounced-animation-frame";

export const keyDataDelimiter = "\uE000";

Expand Down Expand Up @@ -72,62 +73,67 @@ function findKeyElements(char: string): JQuery {

function highlightKey(currentKey: string): void {
if (Config.mode === "zen") return;
if (currentKey === "") currentKey = " ";
try {
$(".activeKey").removeClass("activeKey");
requestDebouncedAnimationFrame("keymap.highlightKey", async () => {
if (currentKey === "") currentKey = " ";
try {
document
.querySelectorAll(".activeKey")
.forEach((el) => el.classList.remove("activeKey"));

if (Config.language.startsWith("korean")) {
currentKey = Hangul.disassemble(currentKey)[0] ?? currentKey;
}
if (Config.language.startsWith("korean")) {
currentKey = Hangul.disassemble(currentKey)[0] ?? currentKey;
}

const $target = findKeyElements(currentKey);
$target.addClass("activeKey");
} catch (e) {
if (e instanceof Error) {
console.log("could not update highlighted keymap key: " + e.message);
const $target = findKeyElements(currentKey);
$target.addClass("activeKey");
} catch (e) {
if (e instanceof Error) {
console.log("could not update highlighted keymap key: " + e.message);
}
}
}
});
}

async function flashKey(key: string, correct?: boolean): Promise<void> {
if (key === undefined) return;
requestDebouncedAnimationFrame(`keymap.flashKey.${key}`, async () => {
const $target = findKeyElements(key);

const $target = findKeyElements(key);

const elements = $target.toArray();
if (elements.length === 0) return;
const elements = $target.toArray();
if (elements.length === 0) return;

const themecolors = await ThemeColors.getAll();
const themecolors = await ThemeColors.getAll();

try {
let startingStyle = {
color: themecolors.bg,
backgroundColor: themecolors.sub,
borderColor: themecolors.sub,
};

if (correct || Config.blindMode) {
startingStyle = {
color: themecolors.bg,
backgroundColor: themecolors.main,
borderColor: themecolors.main,
};
} else {
startingStyle = {
try {
let startingStyle = {
color: themecolors.bg,
backgroundColor: themecolors.error,
borderColor: themecolors.error,
backgroundColor: themecolors.sub,
borderColor: themecolors.sub,
};
}

animate(elements, {
color: [startingStyle.color, themecolors.sub],
backgroundColor: [startingStyle.backgroundColor, themecolors.subAlt],
borderColor: [startingStyle.borderColor, themecolors.sub],
duration: 250,
easing: "out(5)",
});
} catch (e) {}
if (correct || Config.blindMode) {
startingStyle = {
color: themecolors.bg,
backgroundColor: themecolors.main,
borderColor: themecolors.main,
};
} else {
startingStyle = {
color: themecolors.bg,
backgroundColor: themecolors.error,
borderColor: themecolors.error,
};
}

animate(elements, {
color: [startingStyle.color, themecolors.sub],
backgroundColor: [startingStyle.backgroundColor, themecolors.subAlt],
borderColor: [startingStyle.borderColor, themecolors.sub],
duration: 250,
easing: "out(5)",
});
} catch (e) {}
});
}

export function hide(): void {
Expand Down
48 changes: 24 additions & 24 deletions frontend/src/ts/elements/loader.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
const element = $("#backgroundLoader");
let timeout: NodeJS.Timeout | null = null;
let visible = false;
import { animate, JSAnimation } from "animejs";
import { requestDebouncedAnimationFrame } from "../utils/debounced-animation-frame";

function clearTimeout(): void {
if (timeout !== null) {
window.clearTimeout(timeout);
timeout = null;
}
}
const element = document.querySelector("#backgroundLoader") as HTMLElement;
let showAnim: JSAnimation | null = null;

export function show(instant = false): void {
if (visible) return;

if (instant) {
element.stop(true, true).show();
visible = true;
} else {
timeout = setTimeout(() => {
element.stop(true, true).show();
}, 125);
visible = true;
}
requestDebouncedAnimationFrame("loader.show", () => {
showAnim = animate(element, {
opacity: 1,
duration: 125,
delay: instant ? 0 : 125,
onBegin: () => {
element.classList.remove("hidden");
},
});
});
}

export function hide(): void {
if (!visible) return;
clearTimeout();
element.stop(true, true).fadeOut(125);
visible = false;
requestDebouncedAnimationFrame("loader.hide", () => {
showAnim?.pause();
animate(element, {
opacity: 0,
duration: 125,
onComplete: () => {
element.classList.add("hidden");
},
});
});
}
102 changes: 52 additions & 50 deletions frontend/src/ts/elements/monkey-power.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import * as ThemeColors from "./theme-colors";
import * as SlowTimer from "../states/slow-timer";
import Config from "../config";
import { isSafeNumber } from "@monkeytype/util/numbers";
import { requestDebouncedAnimationFrame } from "../utils/debounced-animation-frame";

const html = document.querySelector("html") as HTMLElement;
const body = document.body;

type Particle = {
x: number;
Expand All @@ -14,7 +18,7 @@ type Particle = {

type CTX = {
particles: Particle[];
caret?: JQuery;
caret?: HTMLElement;
canvas?: HTMLCanvasElement;
context2d?: CanvasRenderingContext2D;
rendering: boolean;
Expand Down Expand Up @@ -118,7 +122,7 @@ function updateParticle(particle: Particle): void {
}

export function init(): void {
ctx.caret = $("#caret");
ctx.caret = document.querySelector("#caret") as HTMLElement;
ctx.canvas = createCanvas();
ctx.context2d = ctx.canvas.getContext("2d") as CanvasRenderingContext2D;
}
Expand Down Expand Up @@ -155,7 +159,7 @@ function render(): void {
}
ctx.particles = keep;

if (ctx.particles.length && !SlowTimer.get()) {
if (ctx.particles.length) {
requestAnimationFrame(render);
} else {
ctx.context2d.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
Expand All @@ -168,14 +172,13 @@ export function reset(immediate = false): void {
delete ctx.resetTimeOut;

clearTimeout(ctx.resetTimeOut);
const body = $(document.body);
body.css("transition", "all .25s, transform 0.8s");
body.css("transform", `translate(0,0)`);
body.style.transition = "all .25s, transform 0.8s";
body.style.transform = `translate(0,0)`;
setTimeout(
() => {
body.css("transition", "all .25s, transform .05s");
$("html").css("overflow", "inherit");
$("html").css("overflow-y", "scroll");
body.style.transition = "all .25s, transform .05s";
html.style.overflow = "inherit";
html.style.overflowY = "scroll";
},
immediate ? 0 : 1000,
);
Expand All @@ -201,47 +204,46 @@ function randomColor(): string {
export async function addPower(good = true, extra = false): Promise<void> {
if (Config.monkeyPowerLevel === "off" || SlowTimer.get()) return;

if (Config.blindMode) good = true;

// Shake
if (["3", "4"].includes(Config.monkeyPowerLevel)) {
$("html").css("overflow", "hidden");
const shake = [
Math.round(shakeAmount - Math.random() * shakeAmount),
Math.round(shakeAmount - Math.random() * shakeAmount),
requestDebouncedAnimationFrame("monkey-power.addPower", async () => {
if (Config.blindMode) good = true;

// Shake
if (["3", "4"].includes(Config.monkeyPowerLevel)) {
html.style.overflow = "hidden";
const shake = [
Math.round(shakeAmount - Math.random() * shakeAmount),
Math.round(shakeAmount - Math.random() * shakeAmount),
];
body.style.transform = `translate(${shake[0]}px, ${shake[1]}px)`;
if (isSafeNumber(ctx.resetTimeOut)) clearTimeout(ctx.resetTimeOut);
ctx.resetTimeOut = setTimeout(reset, 2000) as unknown as number;
}

// Sparks
const offset = ctx.caret?.getBoundingClientRect();
const coords = [
offset?.left ?? 0,
(offset?.top ?? 0) + (ctx.caret?.offsetHeight ?? 0) / 2,
];
$(document.body).css(
"transform",
`translate(${shake[0]}px, ${shake[1]}px)`,
);
if (isSafeNumber(ctx.resetTimeOut)) clearTimeout(ctx.resetTimeOut);
ctx.resetTimeOut = setTimeout(reset, 2000) as unknown as number;
}

// Sparks
const offset = ctx.caret?.offset();
const coords = [
offset?.left ?? 0,
(offset?.top ?? 0) + (ctx.caret?.height() ?? 0),
];

for (
let i = Math.round(
(particleCreateCount[0] + Math.random() * particleCreateCount[1]) *
(extra ? 2 : 1),
);
i > 0;
i--
) {
const color = ["2", "4"].includes(Config.monkeyPowerLevel)
? randomColor()
: good
? await ThemeColors.get("caret")
: await ThemeColors.get("error");
ctx.particles.push(
createParticle(...(coords as [x: number, y: number]), color),
);
}

startRender();
for (
let i = Math.round(
(particleCreateCount[0] + Math.random() * particleCreateCount[1]) *
(extra ? 2 : 1),
);
i > 0;
i--
) {
const color = ["2", "4"].includes(Config.monkeyPowerLevel)
? randomColor()
: good
? await ThemeColors.get("caret")
: await ThemeColors.get("error");
ctx.particles.push(
createParticle(...(coords as [x: number, y: number]), color),
);
}

startRender();
});
}
Loading