Files
PodTui/src/hooks/useMultimediaKeys.ts

71 lines
1.9 KiB
TypeScript

/**
* Global multimedia key handler hook.
*
* Captures media-related key events (play/pause, volume, speed)
* regardless of which component is focused. Uses the event bus to
* decouple key detection from audio control logic.
*
* Volume and speed are app-level settings — adjustable with or without
* an episode loaded (they apply to the next playback and persist). Seek
* is NOT handled here: it lives on the yazi keybind router (`<` / `>` =
* shift+, / shift+.), so the arrow keys stay free for navigation.
*/
import { useKeyboard } from "@opentui/solid";
import { emit } from "../utils/event-bus";
export type MediaKeyAction =
| "media.toggle"
| "media.volumeUp"
| "media.volumeDown"
| "media.speedCycle";
export interface MultimediaKeysOptions {
/** When true, skip handling (Player.tsx handles keys locally) */
playerFocused?: () => boolean;
/** When true, skip handling (text input has focus) */
inputFocused?: () => boolean;
}
/**
* Registers a global keyboard listener that emits media events on the
* event bus. Call once at the app level (e.g. in App.tsx).
*/
export function useMultimediaKeys(options: MultimediaKeysOptions = {}) {
useKeyboard((key) => {
// Don't intercept when a text input owns the keyboard
if (options.inputFocused?.()) return;
// Don't intercept when Player component handles its own keys
if (options.playerFocused?.()) return;
// Ctrl/Meta combos are app-level shortcuts, not media keys
if (key.ctrl || key.meta) return;
switch (key.name) {
case "space":
// Toggle play/pause — always valid (may start a loaded episode)
emit("media.toggle", {});
break;
case "up":
emit("media.volumeUp", {});
break;
case "down":
emit("media.volumeDown", {});
break;
case "s":
// Speed is shift+s (S) so plain `s` stays free for search.
if (!key.shift) return;
emit("media.speedCycle", {});
break;
default:
// Not a media key — do nothing
break;
}
});
}