authorgravatar for morgan@skyslope.comMorgana Allen <morgan@skyslope.com> 2026-09-28 11:10:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-28 18:10:54+00:00
loged57bed8e71b2e89a9350c94bda0dacc71cd4817
tree33daa79de5ce784122142172ac1d56d35a08d6fe
parentd15210cd3da79f9a1a495a6309d912d76362a046
signature Signed by PGP key B5690EEEBB952194

fix(web): skip unchanged shortcut modifier updates - 195x typing latency improvement (#13884)

Co-authored-by: GPT-6 Astra <noreply@openai.com>

2 files changed, 78 insertions(+), 8 deletions(-)

apps/web/src/shortcutModifierState.test.ts+67-1
......@@ -1,8 +1,13 @@
1import { describe, expect, it } from "vite-plus/test";
1// @vitest-environment jsdom
2
3import { act, createElement } from "react";
4import { createRoot, type Root } from "react-dom/client";
5import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
26
37import {
48 areShortcutModifierStatesEqual,
59 shortcutModifierStateAfterKeyboardEvent,
10 useShortcutModifierState,
611 type ShortcutModifierState,
712} from "./shortcutModifierState";
813
......@@ -13,6 +18,67 @@ const emptyState = (): ShortcutModifierState => ({
1318 shiftKey: false,
1419});
1520
21describe("useShortcutModifierState", () => {
22 let root: Root;
23 let container: HTMLDivElement;
24
25 beforeEach(() => {
26 vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
27 container = document.createElement("div");
28 document.body.append(container);
29 root = createRoot(container);
30 });
31
32 afterEach(async () => {
33 await act(async () => root.unmount());
34 container.remove();
35 vi.unstubAllGlobals();
36 });
37
38 it.each(["keyup", "paste", "blur"] as const)(
39 "does not render for unchanged modifiers after %s resets the state",
40 async (reset) => {
41 const render = vi.fn();
42 function Consumer() {
43 render(useShortcutModifierState());
44 return null;
45 }
46 await act(async () => {
47 root.render(createElement(Consumer));
48 });
49 const press = async (type: "keydown" | "keyup", key: string, shiftKey = false) => {
50 await act(async () => {
51 window.dispatchEvent(new KeyboardEvent(type, { key, shiftKey }));
52 });
53 };
54
55 await press("keydown", "Shift", true);
56 expect(render).toHaveBeenLastCalledWith({ ...emptyState(), shiftKey: true });
57 if (reset === "keyup") {
58 await press("keyup", "Shift");
59 } else {
60 await act(async () => {
61 window.dispatchEvent(new Event(reset));
62 });
63 }
64 expect(render).toHaveBeenLastCalledWith(emptyState());
65 render.mockClear();
66
67 for (const key of "typing") {
68 await press("keydown", key);
69 await press("keyup", key);
70 }
71 await act(async () => {
72 window.dispatchEvent(new Event("paste"));
73 window.dispatchEvent(new Event("blur"));
74 });
75 expect(render).not.toHaveBeenCalled();
76 await press("keydown", "Shift", true);
77 expect(render).toHaveBeenCalledExactlyOnceWith({ ...emptyState(), shiftKey: true });
78 },
79 );
80});
81
1682function keyboardEventLike(type: "keydown" | "keyup", init: Partial<KeyboardEvent>): KeyboardEvent {
1783 return {
1884 type,
apps/web/src/shortcutModifierState.ts+11-7
......@@ -1,4 +1,4 @@
1import { useEffect, useState } from "react";
1import { useEffect, useRef, useState } from "react";
22
33export interface ShortcutModifierState {
44 metaKey: boolean;
......@@ -28,10 +28,18 @@ export function areShortcutModifierStatesEqual(
2828
2929export function useShortcutModifierState(): ShortcutModifierState {
3030 const [state, setState] = useState(EMPTY_SHORTCUT_MODIFIER_STATE);
31 const stateRef = useRef(EMPTY_SHORTCUT_MODIFIER_STATE);
3132
3233 useEffect(() => {
34 const updateState = (next: ShortcutModifierState) => {
35 // Even a no-op state dispatch can cost work in the sidebar's large tree.
36 // Ordinary typing must return before dispatching a React update.
37 if (areShortcutModifierStatesEqual(stateRef.current, next)) return;
38 stateRef.current = next;
39 setState(next);
40 };
3341 const onKeyboardEvent = (event: KeyboardEvent) => {
34 setState((current) => shortcutModifierStateAfterKeyboardEvent(current, event));
42 updateState(shortcutModifierStateAfterKeyboardEvent(stateRef.current, event));
3543 };
3644 // Dictation tools (Wispr Flow) paste with a synthetic ⌘V whose Meta keyup
3745 // never reaches the page, so the tracked state stays "⌘ held" forever and
......@@ -39,11 +47,7 @@ export function useShortcutModifierState(): ShortcutModifierState {
3947 // treat it like a blur and reset. A physically held modifier re-registers
4048 // on the next real key event.
4149 const onResetEvent = () => {
42 setState((current) =>
43 areShortcutModifierStatesEqual(current, EMPTY_SHORTCUT_MODIFIER_STATE)
44 ? current
45 : EMPTY_SHORTCUT_MODIFIER_STATE,
46 );
50 updateState(EMPTY_SHORTCUT_MODIFIER_STATE);
4751 };
4852
4953 window.addEventListener("keydown", onKeyboardEvent, true);