reframe-video 0.6.18 → 0.6.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/bin.js +92 -31
- package/dist/cli.js +67 -25
- package/dist/compile-api.d.ts +6 -0
- package/dist/compile-api.js +477 -0
- package/dist/compile.js +477 -0
- package/dist/diff.js +67 -25
- package/dist/labels.js +71 -31
- package/dist/renderer-canvas.d.ts +1 -0
- package/dist/renderer-canvas.js +1 -1
- package/dist/types-renderer/index.d.ts +34 -0
- package/package.json +11 -2
package/dist/labels.js
CHANGED
|
@@ -678,42 +678,76 @@ import { dirname, resolve } from "node:path";
|
|
|
678
678
|
import { fileURLToPath } from "node:url";
|
|
679
679
|
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
680
680
|
var CORE_ENTRY = true ? resolve(HERE, "index.js") : resolve(HERE, "..", "..", "core", "src", "index.ts");
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
681
|
+
var SceneLoadError = class extends Error {
|
|
682
|
+
kind;
|
|
683
|
+
constructor(kind, message, options) {
|
|
684
|
+
super(message, options);
|
|
685
|
+
this.name = "SceneLoadError";
|
|
686
|
+
this.kind = kind;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
var clean = (err) => (err instanceof Error ? err.message : String(err)).replace(
|
|
690
|
+
/data:text\/javascript;base64,[A-Za-z0-9+/=]+/g,
|
|
691
|
+
"<scene bundle>"
|
|
692
|
+
);
|
|
693
|
+
var ALIAS = { "@reframe/core": CORE_ENTRY, "reframe-video": CORE_ENTRY };
|
|
694
|
+
async function bundle(input) {
|
|
695
|
+
const common = {
|
|
696
|
+
bundle: true,
|
|
697
|
+
format: "esm",
|
|
698
|
+
platform: "neutral",
|
|
699
|
+
write: false,
|
|
700
|
+
logLevel: "silent",
|
|
701
|
+
sourcemap: "inline",
|
|
702
|
+
alias: ALIAS
|
|
703
|
+
};
|
|
684
704
|
try {
|
|
685
|
-
const out = await build(
|
|
686
|
-
entryPoints: [
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
platform: "neutral",
|
|
690
|
-
write: false,
|
|
691
|
-
logLevel: "silent",
|
|
692
|
-
sourcemap: "inline",
|
|
693
|
-
// both specifiers accepted: the guide's canonical "@reframe/core" and
|
|
694
|
-
// the published package name
|
|
695
|
-
alias: { "@reframe/core": CORE_ENTRY, "reframe-video": CORE_ENTRY }
|
|
696
|
-
});
|
|
697
|
-
code = out.outputFiles[0].text;
|
|
705
|
+
const out = await build(
|
|
706
|
+
"path" in input ? { ...common, entryPoints: [input.path] } : { ...common, stdin: { contents: input.code, resolveDir: input.resolveDir, loader: "ts", sourcefile: "scene.ts" } }
|
|
707
|
+
);
|
|
708
|
+
return out.outputFiles[0].text;
|
|
698
709
|
} catch (err) {
|
|
699
|
-
throw new
|
|
700
|
-
${err instanceof Error ? err.message : String(err)}`);
|
|
710
|
+
throw new SceneLoadError("bundle", clean(err), { cause: err });
|
|
701
711
|
}
|
|
702
|
-
|
|
703
|
-
|
|
712
|
+
}
|
|
713
|
+
async function importDefault(code, label) {
|
|
714
|
+
let mod;
|
|
715
|
+
try {
|
|
716
|
+
mod = await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`);
|
|
717
|
+
} catch (err) {
|
|
718
|
+
const kind = err instanceof Error && err.name === "SceneValidationError" ? "validation" : "eval";
|
|
719
|
+
throw new SceneLoadError(kind, clean(err), { cause: err });
|
|
720
|
+
}
|
|
721
|
+
if (mod.default === void 0) throw new SceneLoadError("eval", `${label} must default-export a scene or composition`);
|
|
704
722
|
return mod.default;
|
|
705
723
|
}
|
|
724
|
+
async function loadDefault(path3) {
|
|
725
|
+
if (path3.endsWith(".json")) {
|
|
726
|
+
try {
|
|
727
|
+
return JSON.parse(await readFile(path3, "utf8"));
|
|
728
|
+
} catch (err) {
|
|
729
|
+
throw new SceneLoadError("eval", `failed to read ${path3}: ${clean(err)}`, { cause: err });
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return importDefault(await bundle({ path: path3 }), path3);
|
|
733
|
+
}
|
|
706
734
|
function isComposition(def) {
|
|
707
735
|
return typeof def === "object" && def !== null && Array.isArray(def.scenes);
|
|
708
736
|
}
|
|
709
|
-
|
|
710
|
-
const def = await loadDefault(path3);
|
|
737
|
+
function asScene(def, label) {
|
|
711
738
|
if (isComposition(def)) {
|
|
712
|
-
throw new
|
|
739
|
+
throw new SceneLoadError("validation", `${label} is a composition \u2014 render it directly, not as a single scene`);
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
validateScene(def);
|
|
743
|
+
} catch (err) {
|
|
744
|
+
throw new SceneLoadError("validation", clean(err), { cause: err });
|
|
713
745
|
}
|
|
714
|
-
validateScene(def);
|
|
715
746
|
return def;
|
|
716
747
|
}
|
|
748
|
+
async function loadScene(path3) {
|
|
749
|
+
return asScene(await loadDefault(path3), path3);
|
|
750
|
+
}
|
|
717
751
|
|
|
718
752
|
// ../render-cli/src/labels.ts
|
|
719
753
|
var path2 = process.argv[2];
|
|
@@ -721,11 +755,17 @@ if (!path2) {
|
|
|
721
755
|
console.error("usage: reframe labels <scene.ts|.json>");
|
|
722
756
|
process.exit(1);
|
|
723
757
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
console.log(`# ${
|
|
729
|
-
|
|
730
|
-
|
|
758
|
+
async function main() {
|
|
759
|
+
const scene = await loadScene(path2);
|
|
760
|
+
const compiled = compileScene(scene);
|
|
761
|
+
const rows = [...compiled.labelTimes.entries()].sort((a, b) => a[1].t0 - b[1].t0 || a[0].localeCompare(b[0]));
|
|
762
|
+
console.log(`# ${scene.id} \u2014 ${rows.length} labels \xB7 ${compiled.duration.toFixed(2)}s @ ${scene.fps ?? 30}fps`);
|
|
763
|
+
console.log(`# ${"start".padStart(7)} ${"end".padStart(7)} label`);
|
|
764
|
+
for (const [name, { t0, t1 }] of rows) {
|
|
765
|
+
console.log(`${`${t0.toFixed(2)}s`.padStart(8)} ${`${t1.toFixed(2)}s`.padStart(8)} ${name}`);
|
|
766
|
+
}
|
|
731
767
|
}
|
|
768
|
+
main().catch((err) => {
|
|
769
|
+
console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
770
|
+
process.exit(1);
|
|
771
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./types-renderer/index.js";
|
package/dist/renderer-canvas.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DisplayList -> Canvas 2D. Drawing only — all animation math lives in
|
|
3
|
+
* @reframe/core's evaluate(). Restricted to the plain Canvas 2D API so the
|
|
4
|
+
* same code runs in the browser (preview), under Playwright (export), and
|
|
5
|
+
* could port to skia-canvas later.
|
|
6
|
+
*/
|
|
7
|
+
import type { CompiledScene, DisplayList, SceneIR } from "../types/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Decoded images keyed by the RAW src string from the IR (never a resolved
|
|
10
|
+
* path/URL — the DisplayList stays machine-independent). Consumers populate
|
|
11
|
+
* it before the first frame; a plain Map satisfies the interface.
|
|
12
|
+
*/
|
|
13
|
+
export interface ImageRegistry {
|
|
14
|
+
get(src: string): CanvasImageSource | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decoded video frames keyed by the RAW src string + frame index. A video is
|
|
18
|
+
* rendered as a frame sequence (extracted at the scene fps): `frame(src, i)`
|
|
19
|
+
* returns the i-th source frame, clamped to the available range by the consumer.
|
|
20
|
+
*/
|
|
21
|
+
export interface VideoRegistry {
|
|
22
|
+
frame(src: string, index: number): CanvasImageSource | undefined;
|
|
23
|
+
}
|
|
24
|
+
export declare function renderFrame(ctx: CanvasRenderingContext2D, compiled: CompiledScene, t: number, images?: ImageRegistry, videos?: VideoRegistry): void;
|
|
25
|
+
export declare function drawDisplayList(ctx: CanvasRenderingContext2D, ops: DisplayList, images?: ImageRegistry, videos?: VideoRegistry): void;
|
|
26
|
+
/** Center cover-crop: the source rect (in image pixels) that fills a dw×dh box at
|
|
27
|
+
* the image's natural aspect — the larger axis is cropped equally on both sides. */
|
|
28
|
+
export declare function coverRect(iw: number, ih: number, dw: number, dh: number): {
|
|
29
|
+
sx: number;
|
|
30
|
+
sy: number;
|
|
31
|
+
sw: number;
|
|
32
|
+
sh: number;
|
|
33
|
+
};
|
|
34
|
+
export type { SceneIR };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.19",
|
|
4
4
|
"description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"motion-graphics",
|
|
@@ -28,7 +28,16 @@
|
|
|
28
28
|
".": {
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
30
|
"import": "./dist/index.js"
|
|
31
|
-
}
|
|
31
|
+
},
|
|
32
|
+
"./renderer": {
|
|
33
|
+
"types": "./dist/renderer-canvas.d.ts",
|
|
34
|
+
"import": "./dist/renderer-canvas.js"
|
|
35
|
+
},
|
|
36
|
+
"./compile": {
|
|
37
|
+
"types": "./dist/compile-api.d.ts",
|
|
38
|
+
"import": "./dist/compile-api.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
32
41
|
},
|
|
33
42
|
"files": [
|
|
34
43
|
"dist",
|