create-effect-motion 0.4.0 → 0.5.0

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/create.js CHANGED
@@ -127,7 +127,7 @@ const handler = (input) => Effect.gen(function* () {
127
127
  `${name} is ready.`,
128
128
  dir === cwd ? "" : ` cd ${path.relative(cwd, dir)}`,
129
129
  ` ${pm === "npm" ? "npm run" : pm} studio # preview scenes with hot reload`,
130
- ` ${pm === "npm" ? "npm run" : pm} render # render targets from motion.config.ts`,
130
+ ` ${pm === "npm" ? "npm run" : pm} render # execute render.ts (the render program)`,
131
131
  ]
132
132
  .filter((line) => line !== "")
133
133
  .join("\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-effect-motion",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Scaffold a new effect-motion project — run with `pnpm create effect-motion` (or npm/yarn/bun create)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4,14 +4,15 @@ This is an [effect-motion](https://github.com/julia-script/effect-motion) projec
4
4
 
5
5
  ## Layout and commands
6
6
 
7
- - `src/scenes/*.ts` — one scene per module, each exporting `scene`. Any file here is previewable without registration.
7
+ - `src/scenes/*.ts` — one scene per module, each exporting `scene`. `Scene.make("Display Name", gen, meta?)` optionally names a scene for the studio picker.
8
8
  - `src/main.ts` — the movie: an ordinary scene that sequences the others (`Scene.play` + `handle.finished`). Nothing is special about it.
9
- - `motion.config.ts` — render targets. Output is always `<output>/<name>.mp4`; never write output paths by hand.
9
+ - `studio.ts` — the studio registration: `studioConfig({ scenes, layers })`. Record keys are unique identifiers; ONLY registered scenes appear in the picker, so add an import + entry for every new scene. Scenes with typed resources (fonts, images) need their loaders in `layers` — the file will not compile until every registered scene is covered.
10
+ - `render.ts` — an ordinary program default-exporting a `Video.render(...)` effect. More outputs are more calls; loader layers are provided here with `Effect.provide` (compile-checked). Knobs (paths, fps, seed) live in this code — there are no CLI flags.
10
11
  - `src/assets/` — static files (images, fonts).
11
- - `motion studio` — browser preview with hot reload (scene picker lists config targets plus unregistered scenes).
12
- - `motion render [name...]` — render targets; `motion render ./src/scenes/foo.ts` renders one file with defaults. Flags beat config beat library defaults. `--verbose` prints full error cause chains.
12
+ - `motion studio [file]` — browser preview with hot reload of `studio.ts` (or the given entrypoint).
13
+ - `motion render [file]` — execute `render.ts` (or the given entrypoint) with the platform provided. `--verbose` prints full error cause chains. The same file runs standalone via `tsx render.ts` by piping through `NodeServices` from `@effect/platform-node`.
13
14
 
14
- Verify a scene change by rendering it (`motion render <target>`) or checking it in the running studio — not by reading code alone.
15
+ Verify a scene change by rendering it (`motion render`) or checking it in the running studio — not by reading code alone.
15
16
 
16
17
  ## Writing scenes
17
18
 
@@ -39,21 +40,26 @@ export const scene = Scene.make(function* () {
39
40
 
40
41
  - **Never** use `Math.random()`, `Date.now()`, or any wall-clock/OS state in a scene — every run must be byte-identical. Use the provided seeded random (`Effect.random`, seeded from `settings.seed`).
41
42
  - Durations land exactly on target on the final frame; springs snap on settle. Don't add "fudge" frames.
42
- - Scene coordinates are the `settings.width`/`height` of the target that renders them (this template: 1920×1080). `dpr` scales output pixels, not coordinates.
43
+ - Scene coordinates are the scene's OWN comp config — `Scene.make(gen, { width, height, backgroundColor })` (this template: 1920×1080). `dpr` (a `Video.render` option) scales output pixels, not coordinates.
43
44
  - The `effect` dependency is pinned **exactly** — upgrading it can change seeded-random sequences. Never bump it casually; upgrade `effect` and `effect-motion` together, deliberately.
44
45
 
45
- ## Config
46
+ ## Entrypoints
46
47
 
47
48
  ```ts
48
- export default defineConfig({
49
- targets: [{
50
- name: "intro", // unique — doubles as the output basename
51
- scene: "./src/scenes/intro.ts",
52
- settings: { width: 1920, height: 1080, frameRate: 60, dpr: 1 },
53
- output: "./output", // a DIRECTORY; file name is derived
54
- // frames: 600 // REQUIRED if the scene is infinite
55
- }],
49
+ // studio.ts — what the studio previews
50
+ export default studioConfig({
51
+ scenes: {
52
+ intro, // key = identifier, label = scene name ?? key
53
+ fancy: { scene: fancy, fps: 30 }, // per-entry player options
54
+ },
55
+ // layers: Layer.mergeAll(Font.layer(...), …) // REQUIRED once a scene declares resources
56
+ });
57
+
58
+ // render.ts — what `motion render` executes
59
+ export default Effect.gen(function* () {
60
+ yield* Video.render(intro, "./output/intro.mp4", { settings: { frameRate: 60 } });
61
+ // yield* Video.render(intro, "./output/intro-hd.mp4", { dpr: 2 }); // more outputs = more calls
56
62
  });
57
63
  ```
58
64
 
59
- A scene used by several targets renders once per target (e.g. different resolutions). An infinite scene (one that never finishes) must set `frames`, or rendering would never end.
65
+ Render the same scene several times for variants (resolutions, dpr). An infinite scene (one that never finishes) must pass `frames` in its `Video.render` options, or rendering would never end.
@@ -0,0 +1,14 @@
1
+ import { Video } from "@effect-motion/export";
2
+ import * as Effect from "effect/Effect";
3
+ import { scene as main } from "./src/main";
4
+
5
+ // An ordinary program: `motion render` executes this default export with
6
+ // the platform provided (it also runs standalone via `tsx render.ts` by
7
+ // piping through NodeServices from @effect/platform-node). More outputs
8
+ // are more Video.render calls; scenes with typed resources provide their
9
+ // loader layers right here with Effect.provide — checked at compile time.
10
+ export default Effect.gen(function* () {
11
+ yield* Video.render(main, "./output/main.mp4", {
12
+ settings: { frameRate: 60 },
13
+ });
14
+ });
@@ -2,8 +2,8 @@ import { Scene } from "effect-motion";
2
2
  import { scene as helloWorld } from "./scenes/hello-world";
3
3
 
4
4
  // The movie: an ordinary scene that sequences the scenes in src/scenes.
5
- // Nothing is special about this file — it is one more target in
6
- // motion.config.ts. Add scenes and chain them here.
5
+ // Nothing is special about this file — studio.ts registers it and
6
+ // render.ts renders it like any other scene. Add scenes and chain them here.
7
7
  export const scene = Scene.make(function* () {
8
8
  const hello = yield* Scene.play(helloWorld);
9
9
  yield* hello.finished;
@@ -1,15 +1,17 @@
1
1
  import { Color, Motion, Scene, Shapes } from "effect-motion";
2
2
 
3
3
  // A scene is a generator: instantiate entities, then yield animations.
4
- // Preview it with `motion studio`, render it with `motion render`.
5
- export const scene = Scene.make(function* () {
4
+ // The leading string is a DISPLAY name (the studio picker label); the
5
+ // studio.ts record key is its identifier. Preview with `motion studio`,
6
+ // render with `motion render`.
7
+ export const scene = Scene.make("Hello World", function* () {
6
8
  const circle = yield* Scene.instantiate(Shapes.Circle, {
7
- x: 300,
8
- y: 540,
9
+ x: -660,
10
+ y: 0,
9
11
  radius: 80,
10
12
  fill: Color.hex("#7f5af0"),
11
13
  });
12
14
 
13
- yield* Motion.tweenTo(circle, { x: 1620 }, "1200 millis", "easeInOutCubic");
15
+ yield* Motion.tweenTo(circle, { x: 660 }, "1200 millis", "easeInOutCubic");
14
16
  yield* Motion.fadeTo(circle, 0, "400 millis");
15
17
  });
@@ -0,0 +1,16 @@
1
+ import { studioConfig } from "@effect-motion/cli";
2
+ import { scene as main } from "./src/main";
3
+ import { scene as helloWorld } from "./src/scenes/hello-world";
4
+
5
+ // The studio's registration: keys are the picker's unique identifiers
6
+ // (labels come from Scene.make's display name when set). Register every
7
+ // scene you want to preview — `motion studio` serves exactly this record.
8
+ // When a scene declares typed resources (fonts, images), provide their
9
+ // loaders here as one `layers` (Layer.mergeAll(Font.layer(...), ...)) —
10
+ // the file will not compile until every registered scene is covered.
11
+ export default studioConfig({
12
+ scenes: {
13
+ "hello-world": helloWorld,
14
+ main,
15
+ },
16
+ });
@@ -11,5 +11,5 @@
11
11
  "lib": ["ES2022"],
12
12
  "types": ["node"]
13
13
  },
14
- "include": ["src", "motion.config.ts"]
14
+ "include": ["src", "studio.ts", "render.ts"]
15
15
  }
@@ -1,20 +0,0 @@
1
- import { defineConfig } from "@effect-motion/cli";
2
-
3
- // Each target is one rendered video: <output>/<name>.mp4
4
- // `motion render` renders all of them; `motion render <name>` picks one.
5
- export default defineConfig({
6
- targets: [
7
- {
8
- name: "hello-world",
9
- scene: "./src/scenes/hello-world.ts",
10
- settings: { width: 1920, height: 1080, frameRate: 60 },
11
- output: "./output",
12
- },
13
- {
14
- name: "main",
15
- scene: "./src/main.ts",
16
- settings: { width: 1920, height: 1080, frameRate: 60 },
17
- output: "./output",
18
- },
19
- ],
20
- });