mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-21 17:09:37 +01:00
Decouple Tree from client app's hotkey system by adding getSelectedItems() to TreeHandle and having callers register hotkeys externally. Extract shared action callbacks to eliminate duplication between hotkey and context menu handlers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
import type {
|
|
ActionInvocation,
|
|
ActionMetadata,
|
|
} from "@yaakapp-internal/proxy-lib";
|
|
import { rpc } from "./rpc";
|
|
|
|
type ActionBinding = {
|
|
invocation: ActionInvocation;
|
|
meta: ActionMetadata;
|
|
keys: { key: string; ctrl: boolean; shift: boolean; alt: boolean; meta: boolean };
|
|
};
|
|
|
|
/** Parse a hotkey string like "Ctrl+Shift+P" into its parts. */
|
|
function parseHotkey(hotkey: string): ActionBinding["keys"] {
|
|
const parts = hotkey.split("+").map((p) => p.trim().toLowerCase());
|
|
return {
|
|
ctrl: parts.includes("ctrl") || parts.includes("control"),
|
|
shift: parts.includes("shift"),
|
|
alt: parts.includes("alt"),
|
|
meta: parts.includes("meta") || parts.includes("cmd") || parts.includes("command"),
|
|
key: parts.filter(
|
|
(p) => !["ctrl", "control", "shift", "alt", "meta", "cmd", "command"].includes(p),
|
|
)[0] ?? "",
|
|
};
|
|
}
|
|
|
|
function matchesEvent(binding: ActionBinding["keys"], e: KeyboardEvent): boolean {
|
|
return (
|
|
e.ctrlKey === binding.ctrl &&
|
|
e.shiftKey === binding.shift &&
|
|
e.altKey === binding.alt &&
|
|
e.metaKey === binding.meta &&
|
|
e.key.toLowerCase() === binding.key
|
|
);
|
|
}
|
|
|
|
/** Fetch all actions from Rust and register a global keydown listener. */
|
|
export async function initHotkeys(): Promise<() => void> {
|
|
const { actions } = await rpc("list_actions", {});
|
|
|
|
const bindings: ActionBinding[] = actions
|
|
.filter(
|
|
(entry): entry is [ActionInvocation, ActionMetadata & { defaultHotkey: string }] =>
|
|
entry[1].defaultHotkey != null,
|
|
)
|
|
.map(([invocation, meta]) => ({
|
|
invocation,
|
|
meta,
|
|
keys: parseHotkey(meta.defaultHotkey),
|
|
}));
|
|
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
for (const binding of bindings) {
|
|
if (matchesEvent(binding.keys, e)) {
|
|
e.preventDefault();
|
|
rpc("execute_action", binding.invocation);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener("keydown", onKeyDown);
|
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
}
|