mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
* 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.
133 lines
4.3 KiB
TypeScript
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);
|
|
}
|
|
}
|