From a27ca789ddb8648b4f0f9701e9867343d48e0e7c Mon Sep 17 00:00:00 2001 From: rahul-muthu Date: Tue, 18 Aug 2026 17:12:41 +0400 Subject: [PATCH] Make hotkeys work in pop-out window --- src/platform/Hotkeys.ts | 69 ++++++++++++++++++++++++++++++++++++++--- src/ui/LiveSplit.tsx | 9 +++--- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/platform/Hotkeys.ts b/src/platform/Hotkeys.ts index 8bb5c43b..1fe06464 100644 --- a/src/platform/Hotkeys.ts +++ b/src/platform/Hotkeys.ts @@ -1,17 +1,72 @@ -import { type CommandSinkRef, HotkeyConfig, HotkeySystem, type Language } from "../livesplit-core"; +import { + type CommandSinkRef, + HotkeyConfig, + HotkeySystem, + type Language, +} from "../livesplit-core"; import { expect } from "../util/OptionUtil"; export interface HotkeyImplementation { - ptr?: number; config(lang: Language | undefined): Promise | HotkeyConfig; setConfig(config: HotkeyConfig): void; activate(): void; deactivate(): void; resolve(keyCode: string): Promise | string; + addWindow(window: Window): void; +} + +class LocalHotkeys implements HotkeyImplementation { + constructor(private hotkeySystem: HotkeySystem) {} + + public config(_lang: Language | undefined): HotkeyConfig { + return this.hotkeySystem.config(); + } + + public setConfig(config: HotkeyConfig): void { + this.hotkeySystem.setConfig(config); + } + + public activate(): void { + this.hotkeySystem.activate(); + } + + public deactivate(): void { + this.hotkeySystem.deactivate(); + } + + public resolve(keyCode: string): string { + return this.hotkeySystem.resolve(keyCode); + } + + public addWindow(childWindow: Window): void { + childWindow.addEventListener("keydown", (event) => { + // Each browser window has its own JavaScript realm. Forwarding a + // newly-created event into the main window lets the existing + // hotkey listener process popup input without passing a child + // realm's Window or KeyboardEvent through the WASM boundary. + const forwardedEvent = new KeyboardEvent(event.type, { + key: event.key, + code: event.code, + location: event.location, + ctrlKey: event.ctrlKey, + shiftKey: event.shiftKey, + altKey: event.altKey, + metaKey: event.metaKey, + repeat: event.repeat, + isComposing: event.isComposing, + bubbles: true, + cancelable: true, + }); + + if (!window.dispatchEvent(forwardedEvent)) { + event.preventDefault(); + } + }); + } } class GlobalHotkeys implements HotkeyImplementation { - constructor(private hotkeySystem?: HotkeySystem) { } + constructor(private hotkeySystem?: HotkeySystem) {} public async config(lang: Language | undefined): Promise { return expect( @@ -63,6 +118,12 @@ class GlobalHotkeys implements HotkeyImplementation { public resolve(keyCode: string): Promise { return window.__TAURI__!.core.invoke("resolve_hotkey", { keyCode }); } + + public addWindow(_window: Window): void { + // Tauri's hotkeys are global and already receive input regardless of + // which application window has focus. Adding a local listener as well + // would make a popup key press trigger the same command twice. + } } export function createHotkeys( @@ -100,6 +161,6 @@ export function createHotkeys( } return globalHotkeys; } else { - return hotkeySystem!; + return new LocalHotkeys(hotkeySystem!); } } diff --git a/src/ui/LiveSplit.tsx b/src/ui/LiveSplit.tsx index 0442c4e3..c9d417d6 100644 --- a/src/ui/LiveSplit.tsx +++ b/src/ui/LiveSplit.tsx @@ -47,7 +47,6 @@ import { ToastContainer, toast } from "react-toastify"; import * as Storage from "../storage"; import { UrlCache } from "../util/UrlCache"; import { - HotkeySystem_add_window, ServerProtocol, TheRunClient, WebRenderer, @@ -1318,6 +1317,10 @@ async function popOut( childDoc.title = "LiveSplit One"; + // Register the window before doing any asynchronous popup setup so its + // shortcuts work immediately, even while fonts and rendering are loading. + hotkeySystem?.addWindow(childWindow); + const link = childDoc.createElement("link"); link.rel = "icon"; link.type = "image/svg+xml"; @@ -1353,10 +1356,6 @@ async function popOut( element.style.width = "100%"; element.style.height = "100%"; - if (hotkeySystem?.ptr) { - HotkeySystem_add_window(hotkeySystem.ptr, childWindow); - } - createRoot(childDoc.body).render( {