mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-03 04:07:03 +00:00
* perf(studio): keep lint off the preview open path The bundler linted every build only to print a StaticGuard warning; a preview bundle now skips it through a staticGuard option, since Studio lints on its own route. That route now lints in the CLI child-process worker the render path already uses, so seconds of parsing no longer stall the preview and every other Studio request on the server event loop. * perf(parsers): parse identical GSAP source once Studio re-requests the parsed animations of unchanged source on every open, and several lint rules parse the same script in one run. A small memo keyed by the script text serves repeats; each caller gets its own copy. * perf(studio): build each preview document once per content version The preview route rebuilt the document for every browser without a cached copy. It now serves one build per ETag from memory, and the CLI keeps the last build on disk salted with its own file, so a restarted server serves the previous document without stale code. * perf(studio): build the opening film's preview while the browser starts The preview server built the document only when the player asked for it, after Studio had booted, so a first open waited about a second of idle server time. The CLI now requests the opening project's preview right after listening, and requests that arrive while a build runs share it instead of starting another. * perf(studio): request the preview document at page load Studio asked for the preview only once the player mounted, about 640 ms into a first open. It now requests it the moment the page loads, and again when the hash names another project, so the build or cache read overlaps Studio's boot. * feat(studio): show the cached frame 0 while the live preview boots Studio covers the live preview with frame 0 as the thumbnail route last rendered it, sized from the image so the stage does not shift, and removes it when the live frame is ready to show. The request is cache-only, so a miss never starts a render on the open path; the missing poster is rendered after the live frame shows, for the next open. * perf(cli): start Studio before the startup lint finishes hyperframes preview linted the whole project before it listened, 4 to 6 s on a large film, and did so in --json mode where the result is discarded. The lint now runs in the CLI worker while Studio starts and prints its summary when done; --json skips it. * perf(studio): render thumbnails from one loaded page, after the preview boots Every thumbnail opened a page and loaded the whole composition to shoot one frame, and the Compositions card asked for one while the live preview was still booting. The server now keeps one loaded page per preview document and seeks it per frame, reloading only when the project changes; card thumbnails wait for the project first ready preview. * perf(studio): parse GSAP animations after the preview boots Studio asked the server to parse the composition animations while the preview was opening; on a fresh server that cold parse ran on the event loop ahead of the preview build. The request now waits for the live preview to boot, or to fail loading, since only keyframes need it. * perf(cli): find orphaned browsers with one process-table read preview ran pgrep three times and then ps once per matching browser, all synchronously before listening: about half a second on a machine running other headless Chrome. One ps listing now finds every orphan in ~20 ms. * perf(studio): keep lint off the boot Opening a project started two lint processes while Studio booted: the terminal's startup lint and Studio's background lint, ~4 s of CPU next to the boot. Studio's background lint now waits until the live preview has booted, and lint processes run at the lowest CPU priority so they only take idle CPU. * perf(studio): open the editing session after the preview boots Opening a project read the composition source and parsed it for the editing session (linkedom plus hf-id assignment) on the main thread while the live preview was booting on the same thread: ~300 ms on a large film. The session now opens once the preview has booted; nothing the first frame shows needs it. * perf(studio): park the editor overlays until the preview boots The overlay frame loop polled preview geometry and off-canvas indicators from the first frame, forcing layout on a preview document that was still loading, on the thread its boot runs on: ~200 ms before the first frame on a large film. The loop now skips its subscribers until the preview has booted, then wakes. * perf(studio): treat the preview as booted once it can play Work deferred until the boot (editing session, lint, overlays, GSAP parse, card thumbnails) started at timeline-ready, which fires before the asset gate, so the editing-session parse ran inside the wait for play. The Player now latches the boot when the preview can show and play, or fails to load. * fix(studio): keep a left project's deferred work out of the one now open Deferred open-time work (source parse, background lint, editing session) waited on one global boot flag, so leaving a project before its preview booted let the next project's boot run the old project's work and write its results. The wait is now per project and resolves false once another project opens. A preview stuck without an error stops deferring that work after 5 s. * perf(cli): let the play page revalidate instead of refetching hyperframes play served the composition, its assets, the player and the runtime with no validator, so every reload refetched everything, the mp4 in full three times. They now carry an ETag and stay no-cache, so unchanged bytes cost a 304 and an edit is still seen. * fix(cli): serve an edited text asset even when its size and mtime match The play page tagged every asset by mtime and size, so a same-size rewrite within one filesystem tick kept the old tag and the browser kept stale bytes. Text assets now get a content-hash tag; media keeps the mtime and size tag Studio already uses. * fix(studio): close idle thumbnail pages so an idle Studio runs no composition Kept-loaded thumbnail pages stayed open for the life of the server, each running a live composition, so an idle Studio cost server CPU and memory that main does not. A page now closes after 10 s unused, and a page is closed only after the frame still running on it finishes. * fix(studio): stop a first open from reloading the preview once per scene file Studio stamped hf-ids into a composition the first time that composition was served, so opening a film of external scenes wrote to index.html and every scene file mid-session. Each write reached Studio as an outside edit and reloaded the preview, which failed for a large film. The server now stamps every composition once before it starts watching the project. * fix(studio): ignore play until the preview can play, so it is never undone Space (and any play() caller) could start playback before the preview frame finished loading. Its load step then paused and rewound to the restore point, so the play was silently lost. play() now waits for timelineReady, the same gate the Play button already uses. * fix(studio): hold play on a composition switch until the new preview loads Readiness was reset only when the project changed. Drilling into another composition kept the old ready flag while the new preview loaded, so Play or Space could start a play that its load step then undid. * refactor(studio): split background lint and thumbnail page reuse into single-purpose steps Keeps the boot wait in the background lint only, moves page lookup and idle close out of withPage, and drops a suppression the startup worker no longer needs. Behaviour unchanged. * chore(cli): declare the render-setup worker as a fallow entry It is spawned by path from cancellableProcess.ts, so it has no import-graph referrer; the entry list is where the repo records that, replacing the per-file ignore removed in the previous commit. * fix(cli): close a thumbnail page whose load fails The page never leaves the promise that loads it, so when the load throws (a WebGPU composition with no adapter, or the navigation timeout on a big film) nothing could close it. * fix(cli): match text assets by exact type and keep Range on them The pattern anchored only its first alternative, and text assets skipped buildRangeResponse, so a ranged request for a stylesheet or SVG got a full 200. * test: open the next project as Studio does, and read the stamped file once The absent-read guard test switched project without beginning its timeline session, so the per-project boot wait held the read. The stamp test checked then read the same file. * test(cli): rewrite the scene file without opening it first Opening the file by path counted as a check before the later read of the same path. * perf(cli): take a thumbnail's version from the cached project signature Each thumbnail walked the project and hashed every text file for its page version; the adapter already caches that signature and clears it from the watcher. An aborted request now also stops before its screenshot. * fix(studio): keep Play off until the preview's load step has run Play could enable before the preview document's load event, from the runtime's timeline message or from the blank page's load step settling on the preview's first message. The load step then paused and rewound the preview, so a Play pressed in between did nothing; a film with large images loads late enough for this to hit a cold open. Readiness now waits for the document's load step, whichever path asks for it. * refactor(studio): move the preview poster's state into usePreviewPoster NLEPreview.tsx passed the 600-line limit once main grew it; the poster's state and callbacks belong beside PreviewPoster. No behaviour change. * fix(cli): take an earlier thumbnail on a fresh page A composition need not draw the same frame when seeked back, so a kept page that had shown 20 s drew a different 10 s thumbnail than a fresh load. A kept page now only seeks forward, as a render does; an earlier time loads a fresh page. * perf(cli): close a kept thumbnail page a second after its last frame Held for 10 s, the page went on running its composition after the open's thumbnail burst, about 5 % of a core in the thumbnail browser while Studio sat idle. A burst's requests arrive back to back, so it still loads the page once. * fix(studio): count the preview as booted at its first frame The boot waited for every video and audio in the preview to finish buffering, which on a project with media took until the 5 s fallback. The thumbnails, editing session and lint that wait for the boot then started seconds late and kept an idle Studio busy. They now start once the first frame is up; media finishes buffering alongside. * perf(studio): render one thumbnail per composition for its card and clip A composition's sidebar card asked for its frame at 3.00 s and its timeline clip for the clip's midpoint, so opening a project rendered every scene twice. The clip now asks for the card's frame (the midpoint still, for compositions under 3 s), and the thumbnail cache serves both from one render: 20 scene renders drop to 12 on a 12-scene project. * chore: keep each changed file's comment share at or under main's The comment ratchet counts the comment lines this branch added. Each is cut, or kept as one line where it states an invariant (the GSAP parse cache returns copies; the boot starts waiting work at the first frame). The orphan-cleanup docblock is condensed and now names the process-table read it uses instead of the pgrep scan it replaced. No code line changed. * fix(studio): keep a 3 s clip off its end frame and give card and clip one encoded URL A composition exactly 3 s long asked for its thumbnail at 3.00 s, its end instant, which after a fade-out is an empty frame; compositions of 3 s or less now use their midpoint. The card built its thumbnail URL from the raw path while the clip encoded it, so names with & + [ or % asked for two different URLs, and a % in the name made the card's request fail on the server; both now encode. The readiness comment says the boot latches at the first frame, as it has since the boot moved. * fix(studio): encode the card's live preview URL too, and document the bundler's static guard The card's hover preview built its URL from the raw composition path, so a % in the file name made the preview route fail to decode it. The card now builds its composition URL in one place, encoded, for both its thumbnail and its hover preview. The static-guard bundle option keeps a one-line doc (its default and what it warns about); a neighbouring docblock is rewrapped to keep the comment share. * test(cli): mock studio-server once so the signature spy and the history hook both apply The main merge brought a second vi.mock of @hyperframes/studio-server into studioServer.test.ts; the later one replaced the first, so createProjectSignature was no longer a spy and the cached signature test read .mock of undefined. One mock now carries both overrides.
23 lines
924 B
TypeScript
23 lines
924 B
TypeScript
import { constants } from "node:os";
|
|
import { afterEach, expect, it, vi } from "vitest";
|
|
|
|
const os = vi.hoisted(() => ({ setPriority: vi.fn() }));
|
|
vi.mock("node:os", async (importOriginal) => ({
|
|
...(await importOriginal<typeof import("node:os")>()),
|
|
setPriority: os.setPriority,
|
|
}));
|
|
vi.mock("./utils/lintProject.js", () => ({ lintProject: vi.fn(async () => ({ ok: true })) }));
|
|
|
|
afterEach(() => vi.restoreAllMocks());
|
|
|
|
it("lints at the lowest CPU priority so it never competes with a Studio boot", async () => {
|
|
process.argv[2] = "lint";
|
|
process.env.HYPERFRAMES_RENDER_SETUP_INPUT = JSON.stringify({ projectDir: "/project" });
|
|
const write = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
|
|
await import("./renderSetupWorker.js");
|
|
|
|
expect(os.setPriority).toHaveBeenCalledWith(constants.priority.PRIORITY_LOW);
|
|
expect(write).toHaveBeenCalledWith(expect.stringContaining('{"ok":true}'));
|
|
});
|