Files
hyperframes/packages/studio/src/player/lib/playbackShortcuts.ts
T
Miguel Ángel 91bdffffe6 fix(ci): scope LOC check to studio, split useTimelinePlayer + hyperframes-player under 500 LOC (#750)
* refactor: split useTimelinePlayer.ts and hyperframes-player.ts into focused modules (<500 LOC each)

* fix(ci): scope 500 LOC check to packages/studio, add allowlist for grandfathered files

* feat(cli): Linux ARM64 support — auto-install Chromium on DGX Spark / GB10 / Jetson

Chrome Headless Shell has no Linux ARM64 binary. On arm64 Linux:
- Detects the platform automatically
- Tries to auto-install system Chromium via apt-get (works on Ubuntu/Debian ARM)
- Falls back to clear manual instructions with exact commands
- 'hyperframes browser ensure' guides through the setup interactively
- After setup, all render commands work without any flags

* fix(ci): disable Windows Defender real-time monitoring to prevent EPERM builds

Path exclusions are insufficient — Defender re-scans new files created
during bun install before the exclusion takes effect. Disable real-time
monitoring for the entire job duration instead (standard CI practice).

* refactor(studio): split all files >500 LOC + extract useToast, delete allowlist

All 11 large files split into focused modules under 500 LOC.
App.tsx extracted toast logic into useToast hook (493 LOC now).
.filesize-allowlist deleted — no longer needed.

* fix: remove unused imports from split files, extract useToast from App.tsx

App.tsx: 504 → 493 lines (toast logic extracted to useToast hook)
timelineDOM.ts: remove unused imports from re-export pattern
MotionPanel.tsx: remove unused clampStudioCustomEasePoints import
studioMotionOps.ts: remove unused StudioGsapMotionDirection import

* fix(ci): use Set-MpPreference to fully disable Windows Defender (both jobs)

* fix(producer): use node --experimental-strip-types instead of tsx for build:fonts

Eliminates the tsx binary dependency that Windows Defender locks during
bun install, causing EPERM errors. Node 22.6+ strips TypeScript types
natively with no external binary.

* chore: remove .filesize-allowlist — App.tsx is now 493 lines (<500)

* fix(ci): disable Windows Defender before checkout to prevent all EPERM races

* fix(producer): skip build:fonts if fontData.generated.ts already exists

The generated file is tracked in git, so CI doesn't need to regenerate
it. This avoids @fontsource/inter node_modules access on Windows which
triggers EPERM from Defender scanning during bun install.
2026-05-13 01:48:12 +02:00

69 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Keyboard shortcut filtering logic for playback controls.
*
* Determines whether a keydown event should be handled as a playback shortcut
* or ignored (e.g. when focus is in an input field, or when caption edit mode
* is active and the user is navigating caption segments).
*/
const PLAYBACK_FRAME_STEP_CODES = new Set(["ArrowLeft", "ArrowRight"]);
const PLAYBACK_SHORTCUT_IGNORED_SELECTOR = [
"input",
"textarea",
"select",
"button",
"a[href]",
"[contenteditable='true']",
"[role='button']",
"[role='checkbox']",
"[role='combobox']",
"[role='menuitem']",
"[role='radio']",
"[role='slider']",
"[role='spinbutton']",
"[role='switch']",
"[role='textbox']",
].join(",");
export function shouldIgnorePlaybackShortcutTarget(target: EventTarget | null): boolean {
if (!target || typeof target !== "object") return false;
const candidate = target as { closest?: unknown };
if (typeof candidate.closest !== "function") return false;
return (
(candidate.closest as (selector: string) => Element | null).call(
target,
PLAYBACK_SHORTCUT_IGNORED_SELECTOR,
) !== null
);
}
interface PlaybackShortcutCaptionState {
isCaptionEditMode: boolean;
selectedCaptionSegmentCount: number;
}
type PlaybackShortcutEvent = Pick<
KeyboardEvent,
"altKey" | "ctrlKey" | "metaKey" | "code" | "target"
>;
export function shouldIgnorePlaybackShortcutEvent(
event: PlaybackShortcutEvent,
captionState: PlaybackShortcutCaptionState = {
isCaptionEditMode: false,
selectedCaptionSegmentCount: 0,
},
): boolean {
if (event.metaKey || event.ctrlKey || event.altKey) return true;
if (shouldIgnorePlaybackShortcutTarget(event.target)) return true;
return (
PLAYBACK_FRAME_STEP_CODES.has(event.code) &&
captionState.isCaptionEditMode &&
captionState.selectedCaptionSegmentCount > 0
);
}
/** JKL shuttle speeds (×1, ×2, ×4). */
export const SHUTTLE_SPEEDS = [1, 2, 4] as const;