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/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
- async function loadDefault(path3) {
682
- if (path3.endsWith(".json")) return JSON.parse(await readFile(path3, "utf8"));
683
- let code;
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: [path3],
687
- bundle: true,
688
- format: "esm",
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 Error(`failed to bundle ${path3}:
700
- ${err instanceof Error ? err.message : String(err)}`);
710
+ throw new SceneLoadError("bundle", clean(err), { cause: err });
701
711
  }
702
- const mod = await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`);
703
- if (mod.default === void 0) throw new Error(`${path3} must default-export a scene or composition`);
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
- async function loadScene(path3) {
710
- const def = await loadDefault(path3);
737
+ function asScene(def, label) {
711
738
  if (isComposition(def)) {
712
- throw new Error(`${path3} is a composition \u2014 render it directly, not as a single scene`);
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
- var scene = await loadScene(path2);
725
- var compiled = compileScene(scene);
726
- var rows = [...compiled.labelTimes.entries()].sort((a, b) => a[1].t0 - b[1].t0 || a[0].localeCompare(b[0]));
727
- console.log(`# ${scene.id} \u2014 ${rows.length} labels \xB7 ${compiled.duration.toFixed(2)}s @ ${scene.fps ?? 30}fps`);
728
- console.log(`# ${"start".padStart(7)} ${"end".padStart(7)} label`);
729
- for (const [name, { t0, t1 }] of rows) {
730
- console.log(`${`${t0.toFixed(2)}s`.padStart(8)} ${`${t1.toFixed(2)}s`.padStart(8)} ${name}`);
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";
@@ -1,5 +1,5 @@
1
1
  // ../renderer-canvas/src/index.ts
2
- import { evaluate } from "@reframe/core";
2
+ import { evaluate } from "reframe-video";
3
3
  function resolvePaint(ctx, paint, box) {
4
4
  if (typeof paint === "string") return paint;
5
5
  const { x, y, w, h } = box;
@@ -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.18",
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",