Files
hyperframes/packages/player/src/shader-loader-state.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

133 lines
4.3 KiB
TypeScript

/**
* Runtime state controller for the shader-transition loading overlay.
*
* Manages show/hide transitions (with a CSS fade-out delay) and updates
* the progress bar, phrase text, and detail rows from `ShaderTransitionState`
* messages received from the iframe.
*
* Holds direct references to the DOM nodes created by `createShaderLoader`
* so state updates never touch the shadow-DOM query API at runtime.
*/
import { SHADER_LOADING_PHRASES, type ShaderTransitionState } from "./shader-options.js";
import type { ShaderLoaderElements } from "./shader-loader-element.js";
const HIDE_TRANSITION_MS = 420;
export class ShaderLoaderState {
private readonly _el: ShaderLoaderElements;
private _hideTimeout: ReturnType<typeof setTimeout> | null = null;
constructor(elements: ShaderLoaderElements) {
this._el = elements;
}
show(): void {
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
this._el.root.classList.remove("hfp-hiding");
this._el.root.classList.add("hfp-visible");
}
hide(): void {
if (this._el.root.classList.contains("hfp-hiding")) {
if (!this._hideTimeout) this._scheduleCleanup();
return;
}
if (!this._el.root.classList.contains("hfp-visible")) return;
this._el.root.classList.add("hfp-hiding");
this._el.root.classList.remove("hfp-visible");
this._scheduleCleanup();
}
reset(): void {
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
this._el.root.classList.remove("hfp-visible", "hfp-hiding");
this._el.fill.style.transform = "scaleX(0)";
this._el.transitionValue.textContent = "";
this._el.frameValue.textContent = "";
this._el.frameRow.style.visibility = "hidden";
}
update(status: ShaderTransitionState, loadingMode: string): void {
if (loadingMode !== "player") {
this.reset();
return;
}
if (status.ready || !status.loading) {
this.hide();
return;
}
const progress =
typeof status.progress === "number" && Number.isFinite(status.progress) ? status.progress : 0;
const total =
typeof status.total === "number" && Number.isFinite(status.total) ? status.total : 0;
const ratio = total > 0 ? Math.min(1, Math.max(0, progress / total)) : 0;
const phraseIndex = Math.min(
SHADER_LOADING_PHRASES.length - 1,
Math.floor(ratio * SHADER_LOADING_PHRASES.length),
);
this._el.title.textContent =
SHADER_LOADING_PHRASES[phraseIndex] || "Preparing scene transitions";
this._el.detail.textContent =
status.phase === "cached"
? "Loading cached transition frames before playback."
: status.phase === "finalizing"
? "Uploading transition textures for smooth playback."
: "Rendering animated scene samples for shader transitions.";
this._el.fill.style.transform = `scaleX(${ratio})`;
this._el.transitionValue.textContent =
status.currentTransition !== undefined && status.transitionTotal !== undefined
? `${status.currentTransition}/${status.transitionTotal}`
: total > 0
? `${progress}/${total}`
: "";
const frameValue =
status.transitionFrame !== undefined && status.transitionFrames !== undefined
? `${status.transitionFrame}/${status.transitionFrames}`
: "";
this._el.frameLabel.textContent =
status.phase === "cached"
? "cached transition frames"
: status.phase === "finalizing"
? "finalizing transition frames"
: "rendering transition frames";
this._el.frameValue.textContent = frameValue;
this._el.frameRow.style.visibility = frameValue ? "visible" : "hidden";
this._el.root.setAttribute("aria-valuenow", String(Math.round(ratio * 100)));
this.show();
}
get hideTimeout(): ReturnType<typeof setTimeout> | null {
return this._hideTimeout;
}
destroy(): void {
if (this._hideTimeout) {
clearTimeout(this._hideTimeout);
this._hideTimeout = null;
}
}
private _scheduleCleanup(): void {
if (this._hideTimeout) clearTimeout(this._hideTimeout);
this._hideTimeout = setTimeout(() => {
this._el.root.classList.remove("hfp-hiding");
this._hideTimeout = null;
}, HIDE_TRANSITION_MS);
}
}