Files
hyperframes/packages/player/src/controls-setup.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

73 lines
2.3 KiB
TypeScript

/**
* Helpers for wiring the player's optional UI elements: the playback controls
* bar, the poster image overlay, and input-filtering utilities.
*
* Extracted from the web component so the setup logic doesn't inflate the
* class body. These are pure imperative DOM operations; they carry no
* persistent state beyond what the caller tracks.
*/
import { createControls, type ControlsCallbacks, type ControlsOptions } from "./controls.js";
/**
* Create the playback controls overlay and attach it to `parent`.
* Returns the controls API object. A no-op guard (returns the existing API)
* must be enforced by the caller — this function always constructs.
*/
export function setupControls(
parent: ShadowRoot,
muted: boolean,
volume: number,
speedPresetsAttr: string | null,
callbacks: ControlsCallbacks,
): ReturnType<typeof createControls> {
const speedPresets = speedPresetsAttr
? speedPresetsAttr
.split(",")
.map(Number)
.filter((n) => !isNaN(n) && n > 0)
: undefined;
const options: ControlsOptions = speedPresets ? { speedPresets } : {};
const api = createControls(parent, callbacks, options);
api.updateMuted(muted);
api.updateVolume(volume);
return api;
}
/**
* Set up or remove the poster image element in `parent`.
*
* - When `posterUrl` is non-empty, creates the `<img>` if needed and sets src.
* - When `posterUrl` is null/empty, removes the existing element (if any).
*
* Returns the current poster element (possibly newly created) or `null`.
*/
export function setupPoster(
parent: ShadowRoot,
posterUrl: string | null,
existing: HTMLImageElement | null,
): HTMLImageElement | null {
if (!posterUrl) {
existing?.remove();
return null;
}
if (!existing) {
existing = document.createElement("img");
existing.className = "hfp-poster";
parent.appendChild(existing);
}
existing.src = posterUrl;
return existing;
}
/**
* Returns `true` when `event` originated inside an `hfp-controls` element.
* Used to prevent the bare-player-surface click handler from double-firing
* when the user clicks an overlay control button.
*/
export function isControlsClick(event: Event): boolean {
return event
.composedPath()
.some((t) => t instanceof HTMLElement && t.classList.contains("hfp-controls"));
}