Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/spicy-bats-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"runed": patch
---

perf(pressed-keys): improve internal performance
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createSubscriber } from "svelte/reactivity";
import { watch } from "$lib/utilities/watch/index.js";
import { defaultWindow, type ConfigurableWindow } from "$lib/internal/configurable-globals.js";

const modifierKeys = ["meta", "control", "alt", "shift"];
const modifierKeys = new Set(["meta", "control", "alt", "shift"]);

export type PressedKeysOptions = ConfigurableWindow;
/**
Expand All @@ -12,7 +12,7 @@ export type PressedKeysOptions = ConfigurableWindow;
* @see {@link https://runed.dev/docs/utilities/pressed-keys}
*/
export class PressedKeys {
#pressedKeys = $state<string[]>([]);
#pressedKeys = new Set<string>();
readonly #subscribe?: () => void;

constructor(options: PressedKeysOptions = {}) {
Expand All @@ -24,9 +24,7 @@ export class PressedKeys {
this.#subscribe = createSubscriber((update) => {
const keydown = on(window, "keydown", (e) => {
const key = e.key.toLowerCase();
if (!this.#pressedKeys.includes(key)) {
this.#pressedKeys.push(key);
}
this.#pressedKeys.add(key);
update();
});

Expand All @@ -36,30 +34,30 @@ export class PressedKeys {
// Special handling for modifier keys (meta, control, alt, shift)
// This addresses issues with OS/browser intercepting certain key combinations
// where non-modifier keyup events might not fire properly
if (modifierKeys.includes(key)) {
if (modifierKeys.has(key)) {
// When a modifier key is released, clear all non-modifier keys
// but keep other modifier keys that might still be pressed
// This prevents keys from getting "stuck" in the pressed state
this.#pressedKeys = this.#pressedKeys.filter((k) => modifierKeys.includes(k));
this.#pressedKeys = this.#pressedKeys.intersection(modifierKeys);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set.prototype.intersection is only baseline since 2024/06, tell me if you want me to add a fallback for that

}

// Regular key removal
this.#pressedKeys = this.#pressedKeys.filter((k) => k !== key);
this.#pressedKeys.delete(key);
update();
});

// Handle window blur events (switching applications, clicking outside browser)
// Reset all keys when user shifts focus away from the window
const blur = on(window, "blur", () => {
this.#pressedKeys = [];
this.#pressedKeys.clear();
update();
});

// Handle tab visibility changes (switching browser tabs)
// This catches cases where the window doesn't lose focus but the tab is hidden
const visibilityChange = on(document, "visibilitychange", () => {
if (document.visibilityState === "hidden") {
this.#pressedKeys = [];
this.#pressedKeys.clear();
update();
}
});
Expand All @@ -75,13 +73,12 @@ export class PressedKeys {

has(...keys: string[]): boolean {
this.#subscribe?.();
const normalizedKeys = keys.map((key) => key.toLowerCase());
return normalizedKeys.every((key) => this.#pressedKeys.includes(key));
return keys.every((key) => this.#pressedKeys.has(key.toLowerCase()));
}

get all(): string[] {
this.#subscribe?.();
return this.#pressedKeys;
return [...this.#pressedKeys];
}

/**
Expand Down
10 changes: 6 additions & 4 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ packages:
- sites/*

catalog:
"@sveltejs/kit": ^2.42.0
"@sveltejs/vite-plugin-svelte": ^6.2.0
"@types/node": ^20.19.16
'@sveltejs/kit': ^2.42.0
'@sveltejs/vite-plugin-svelte': ^6.2.0
'@types/node': ^20.19.16
svelte: ^5.39.3
svelte-check: ^4.3.1
typescript: ^5.9.2
vite: ^7.1.5
vitest: ^3.2.4

onlyBuiltDependencies:
- "@tailwindcss/oxide"
- '@tailwindcss/oxide'
- esbuild
- msw
- sharp
- workerd