@depths/waves 0.2.0 → 0.3.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/README.md +118 -19
- package/dist/{chunk-7QPNRHMW.mjs → chunk-QP54QRAP.mjs} +73 -5
- package/dist/{chunk-PKLHVWMD.mjs → chunk-YYS6AVTN.mjs} +792 -604
- package/dist/cli.js +1098 -756
- package/dist/cli.mjs +90 -3
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1010 -755
- package/dist/index.mjs +2 -2
- package/dist/remotion/index.d.mts +5 -0
- package/dist/remotion/index.d.ts +5 -0
- package/dist/remotion/index.js +1032 -807
- package/dist/remotion/index.mjs +49 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @depths/waves (v0.
|
|
1
|
+
# @depths/waves (v0.3.0)
|
|
2
2
|
|
|
3
3
|
`@depths/waves` is a TypeScript-first library + CLI for rendering videos from a JSON "intermediate representation" (IR) using Remotion.
|
|
4
4
|
|
|
@@ -8,11 +8,18 @@ The intended workflow is:
|
|
|
8
8
|
2. The IR is validated (schema + semantics + registry contracts).
|
|
9
9
|
3. The IR is rendered to an output video file (MP4 by default) using Remotion.
|
|
10
10
|
|
|
11
|
-
v0.2.0
|
|
11
|
+
v0.2.0 introduced a shadcn-like catalog of higher-level "composite" components and a hybrid IR that supports:
|
|
12
12
|
|
|
13
13
|
- **Segments (recommended for agents):** sequential scenes with optional overlaps ("transitions")
|
|
14
14
|
- **Timeline (escape hatch):** explicit timed nodes with overlaps allowed
|
|
15
15
|
|
|
16
|
+
v0.3.0 focuses on **seamless alignment + overlays**:
|
|
17
|
+
|
|
18
|
+
- **Layout-safe primitives:** components that use normal layout flow by default (no accidental `position: absolute`)
|
|
19
|
+
- **Explicit layering primitives:** `Layers` + `Layer` for deterministic overlays (z-index, inset, opacity)
|
|
20
|
+
- **Explicit positioning primitive:** `Frame` for deliberate `x/y/width/height` placement (breaking change: `Box` no longer supports `x/y`)
|
|
21
|
+
- **Visual debugging workflow:** `waves stills` and `--debugBounds/--debugLabels` to inspect alignment issues without pixel tests
|
|
22
|
+
|
|
16
23
|
## Table of contents
|
|
17
24
|
|
|
18
25
|
- [Concept (end-to-end)](#concept-end-to-end)
|
|
@@ -62,7 +69,7 @@ At render time, Waves is "just React + Remotion". The strictness lives earlier (
|
|
|
62
69
|
The codebase has a small number of core concepts:
|
|
63
70
|
|
|
64
71
|
- **Video IR (`VideoIRSchema`)**: the JSON document format you author.
|
|
65
|
-
- v0.
|
|
72
|
+
- v0.3.0 targets `version: "2.0"` only.
|
|
66
73
|
- You author exactly one of `segments[]` (recommended) or `timeline[]` (escape hatch).
|
|
67
74
|
- **Component Registry (`ComponentRegistry`)**: a map from string `type` -> React component + Zod props schema + metadata.
|
|
68
75
|
- Only registered components can render.
|
|
@@ -388,6 +395,8 @@ Flags:
|
|
|
388
395
|
- `--codec h264|h265|vp8|vp9` (optional; default `h264`)
|
|
389
396
|
- `--crf <n>` (optional; forwarded to Remotion renderer)
|
|
390
397
|
- `--concurrency <n|string>` (optional; forwarded to Remotion renderer)
|
|
398
|
+
- `--debugBounds` (optional; draws debug outlines for every rendered node)
|
|
399
|
+
- `--debugLabels` (optional; labels debug outlines with `type#id`)
|
|
391
400
|
- `--register <module>` (repeatable)
|
|
392
401
|
- `--pretty` (only affects the formatting of error JSON when render fails)
|
|
393
402
|
|
|
@@ -396,11 +405,35 @@ Examples:
|
|
|
396
405
|
```bash
|
|
397
406
|
npx waves render --in ./video.v2.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
|
|
398
407
|
npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public
|
|
408
|
+
npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public --debugBounds --debugLabels
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### `waves stills`
|
|
412
|
+
|
|
413
|
+
Renders a set of still images (single frames) from an IR JSON file. This is the recommended way to iterate on alignment and overlays without re-rendering full MP4s.
|
|
414
|
+
|
|
415
|
+
Flags:
|
|
416
|
+
|
|
417
|
+
- `--in <path>` (required)
|
|
418
|
+
- `--outDir <path>` (required)
|
|
419
|
+
- `--frames <csv>` (required; e.g. `"0,30,60"`)
|
|
420
|
+
- `--publicDir <path>` (optional; required if your IR uses `/assets/...` paths)
|
|
421
|
+
- `--imageFormat png|jpeg|webp` (optional; default `png`)
|
|
422
|
+
- `--scale <n>` (optional; default `1`)
|
|
423
|
+
- `--jpegQuality <n>` (optional; default `90`; only used when `--imageFormat jpeg`)
|
|
424
|
+
- `--debugBounds` / `--debugLabels` (same as `waves render`)
|
|
425
|
+
- `--register <module>` (repeatable)
|
|
426
|
+
|
|
427
|
+
Examples:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
npx waves stills --in ./video.v2.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public
|
|
431
|
+
npx waves stills --in ./video.v2.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public --debugBounds --debugLabels
|
|
399
432
|
```
|
|
400
433
|
|
|
401
434
|
## IR v2.0 (authoring contract)
|
|
402
435
|
|
|
403
|
-
v0.
|
|
436
|
+
v0.3.0 targets `version: "2.0"` only.
|
|
404
437
|
|
|
405
438
|
### Recommended: `segments[]` (high-level)
|
|
406
439
|
|
|
@@ -559,10 +592,13 @@ node scripts/generate-readme-components.mjs
|
|
|
559
592
|
| `ImageSequence` | composite | image | no | no | Plays a numbered image sequence (frame-by-frame) |
|
|
560
593
|
| `ImageWithCaption` | composite | image | no | no | Image with a caption strip (top/bottom) or overlay caption |
|
|
561
594
|
| `KenBurnsImage` | composite | image | no | no | Slow zoom and pan (Ken Burns effect) for a still image |
|
|
562
|
-
| `Box` | primitive | layout | yes | no |
|
|
595
|
+
| `Box` | primitive | layout | yes | no | Flow container for layout and backgrounds (layout-safe) |
|
|
563
596
|
| `CardStack` | composite | layout | no | no | Sequential stacked cards (2-5) with flip/slide/fade transitions |
|
|
597
|
+
| `Frame` | primitive | layout | yes | no | Absolute-positioned container (x/y placement) |
|
|
564
598
|
| `Grid` | primitive | layout | yes | no | Grid layout container with configurable rows/columns |
|
|
565
599
|
| `GridLayout` | composite | layout | yes (1..∞) | no | Simple responsive grid layout for child components |
|
|
600
|
+
| `Layer` | primitive | layout | yes (1..∞) | no | One overlay layer with explicit zIndex inside Layers |
|
|
601
|
+
| `Layers` | primitive | layout | yes (1..∞) | no | Overlay container for stacking children (use Layer for zIndex) |
|
|
566
602
|
| `Scene` | primitive | layout | yes | no | Scene container with a background and nested children |
|
|
567
603
|
| `Segment` | primitive | layout | yes (1..1) | yes | Internal segment wrapper (used by v2 segments compiler) |
|
|
568
604
|
| `Shape` | primitive | layout | no | no | Simple rect/circle shape for UI accents |
|
|
@@ -893,8 +929,8 @@ Props:
|
|
|
893
929
|
- category: `layout`
|
|
894
930
|
- internal: `false`
|
|
895
931
|
- children: `yes`
|
|
896
|
-
- description:
|
|
897
|
-
- llmGuidance: Use Box
|
|
932
|
+
- description: Flow container for layout and backgrounds (layout-safe)
|
|
933
|
+
- llmGuidance: Use Box as a container inside Grid/Stack. Box participates in layout flow. For x/y positioning, use Frame.
|
|
898
934
|
|
|
899
935
|
Props:
|
|
900
936
|
|
|
@@ -906,8 +942,6 @@ Props:
|
|
|
906
942
|
| `opacity` | number | yes | 1 | min=0, max=1 |
|
|
907
943
|
| `padding` | number | yes | 0 | min=0 |
|
|
908
944
|
| `width` | number | no | | |
|
|
909
|
-
| `x` | number | yes | 0 | |
|
|
910
|
-
| `y` | number | yes | 0 | |
|
|
911
945
|
|
|
912
946
|
##### `CardStack`
|
|
913
947
|
|
|
@@ -926,6 +960,28 @@ Props:
|
|
|
926
960
|
| `displayDuration` | integer | yes | 90 | min=30, max=150 |
|
|
927
961
|
| `transition` | enum("flip" \| "slide" \| "fade") | yes | "flip" | |
|
|
928
962
|
|
|
963
|
+
##### `Frame`
|
|
964
|
+
|
|
965
|
+
- kind: `primitive`
|
|
966
|
+
- category: `layout`
|
|
967
|
+
- internal: `false`
|
|
968
|
+
- children: `yes`
|
|
969
|
+
- description: Absolute-positioned container (x/y placement)
|
|
970
|
+
- llmGuidance: Use Frame for precise pixel placement (x/y). Use Box for normal layout flow inside Grid/Stack.
|
|
971
|
+
|
|
972
|
+
Props:
|
|
973
|
+
|
|
974
|
+
| Prop | Type | Required | Default | Notes |
|
|
975
|
+
| - | - | - | - | - |
|
|
976
|
+
| `backgroundColor` | string | no | | |
|
|
977
|
+
| `borderRadius` | number | yes | 0 | min=0 |
|
|
978
|
+
| `height` | number | no | | |
|
|
979
|
+
| `opacity` | number | yes | 1 | min=0, max=1 |
|
|
980
|
+
| `padding` | number | yes | 0 | min=0 |
|
|
981
|
+
| `width` | number | no | | |
|
|
982
|
+
| `x` | number | yes | 0 | |
|
|
983
|
+
| `y` | number | yes | 0 | |
|
|
984
|
+
|
|
929
985
|
##### `Grid`
|
|
930
986
|
|
|
931
987
|
- kind: `primitive`
|
|
@@ -964,6 +1020,39 @@ Props:
|
|
|
964
1020
|
| `padding` | number | yes | 40 | min=0, max=100 |
|
|
965
1021
|
| `rows` | integer | yes | 2 | min=1, max=4 |
|
|
966
1022
|
|
|
1023
|
+
##### `Layer`
|
|
1024
|
+
|
|
1025
|
+
- kind: `primitive`
|
|
1026
|
+
- category: `layout`
|
|
1027
|
+
- internal: `false`
|
|
1028
|
+
- children: `yes (1..∞)`
|
|
1029
|
+
- description: One overlay layer with explicit zIndex inside Layers
|
|
1030
|
+
- llmGuidance: Use Layer inside Layers to control stacking. Put exactly one child in a Layer (recommended).
|
|
1031
|
+
|
|
1032
|
+
Props:
|
|
1033
|
+
|
|
1034
|
+
| Prop | Type | Required | Default | Notes |
|
|
1035
|
+
| - | - | - | - | - |
|
|
1036
|
+
| `inset` | number | yes | 0 | min=0 |
|
|
1037
|
+
| `opacity` | number | yes | 1 | min=0, max=1 |
|
|
1038
|
+
| `pointerEvents` | enum("none" \| "auto") | yes | "none" | |
|
|
1039
|
+
| `zIndex` | integer | yes | 0 | min=-9007199254740991, max=9007199254740991 |
|
|
1040
|
+
|
|
1041
|
+
##### `Layers`
|
|
1042
|
+
|
|
1043
|
+
- kind: `primitive`
|
|
1044
|
+
- category: `layout`
|
|
1045
|
+
- internal: `false`
|
|
1046
|
+
- children: `yes (1..∞)`
|
|
1047
|
+
- description: Overlay container for stacking children (use Layer for zIndex)
|
|
1048
|
+
- llmGuidance: Use Layers to stack background/content/overlays. Prefer Layer children with explicit zIndex.
|
|
1049
|
+
|
|
1050
|
+
Props:
|
|
1051
|
+
|
|
1052
|
+
| Prop | Type | Required | Default | Notes |
|
|
1053
|
+
| - | - | - | - | - |
|
|
1054
|
+
| `overflow` | enum("visible" \| "hidden") | yes | "visible" | |
|
|
1055
|
+
|
|
967
1056
|
##### `Scene`
|
|
968
1057
|
|
|
969
1058
|
- kind: `primitive`
|
|
@@ -1174,7 +1263,9 @@ Props:
|
|
|
1174
1263
|
| `color` | string | yes | "#FFFFFF" | |
|
|
1175
1264
|
| `fontSize` | number | yes | 48 | min=12, max=120 |
|
|
1176
1265
|
| `highlightStyle` | enum("word" \| "bounce" \| "none") | yes | "word" | |
|
|
1266
|
+
| `maxWidthPct` | number | yes | 0.92 | min=0.1, max=1 |
|
|
1177
1267
|
| `position` | enum("center" \| "bottom") | yes | "center" | |
|
|
1268
|
+
| `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
|
|
1178
1269
|
| `strokeColor` | string | yes | "#000000" | |
|
|
1179
1270
|
| `strokeWidth` | number | yes | 3 | min=0, max=10 |
|
|
1180
1271
|
| `text` | string | yes | | maxLength=150 |
|
|
@@ -1329,7 +1420,9 @@ Props:
|
|
|
1329
1420
|
| `content` | string | yes | | maxLength=200 |
|
|
1330
1421
|
| `fontFamily` | string | yes | "Inter" | |
|
|
1331
1422
|
| `fontSize` | number | yes | 48 | min=8, max=200 |
|
|
1423
|
+
| `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
|
|
1332
1424
|
| `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
|
|
1425
|
+
| `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
|
|
1333
1426
|
| `splitBy` | enum("word" \| "letter") | yes | "word" | |
|
|
1334
1427
|
| `stagger` | integer | yes | 3 | min=1, max=10 |
|
|
1335
1428
|
|
|
@@ -1372,8 +1465,12 @@ Props:
|
|
|
1372
1465
|
| `animation` | enum("none" \| "fade" \| "slide" \| "zoom") | yes | "fade" | |
|
|
1373
1466
|
| `color` | string | yes | "#FFFFFF" | |
|
|
1374
1467
|
| `content` | string | yes | | |
|
|
1468
|
+
| `fontFamily` | string | yes | "Inter" | |
|
|
1375
1469
|
| `fontSize` | number | yes | 48 | |
|
|
1470
|
+
| `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
|
|
1376
1471
|
| `position` | enum("top" \| "center" \| "bottom" \| "left" \| "right") | yes | "center" | |
|
|
1472
|
+
| `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
|
|
1473
|
+
| `textAlign` | enum("left" \| "center" \| "right") | yes | "center" | |
|
|
1377
1474
|
|
|
1378
1475
|
##### `TypewriterText`
|
|
1379
1476
|
|
|
@@ -1393,7 +1490,9 @@ Props:
|
|
|
1393
1490
|
| `cursorColor` | string | yes | "#FFFFFF" | |
|
|
1394
1491
|
| `fontFamily` | string | yes | "Inter" | |
|
|
1395
1492
|
| `fontSize` | number | yes | 48 | min=8, max=200 |
|
|
1493
|
+
| `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
|
|
1396
1494
|
| `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
|
|
1495
|
+
| `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
|
|
1397
1496
|
| `showCursor` | boolean | yes | true | |
|
|
1398
1497
|
| `speed` | number | yes | 2 | min=0.5, max=5 |
|
|
1399
1498
|
|
|
@@ -1557,7 +1656,7 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
|
|
|
1557
1656
|
"id": "title",
|
|
1558
1657
|
"type": "SplitText",
|
|
1559
1658
|
"props": {
|
|
1560
|
-
"content": "Waves v0.
|
|
1659
|
+
"content": "Waves v0.3.0",
|
|
1561
1660
|
"fontSize": 96,
|
|
1562
1661
|
"splitBy": "word",
|
|
1563
1662
|
"stagger": 3,
|
|
@@ -1568,7 +1667,7 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
|
|
|
1568
1667
|
"id": "subtitle",
|
|
1569
1668
|
"type": "TypewriterText",
|
|
1570
1669
|
"props": {
|
|
1571
|
-
"content": "
|
|
1670
|
+
"content": "Seamless alignment + overlays",
|
|
1572
1671
|
"fontSize": 48,
|
|
1573
1672
|
"position": "bottom",
|
|
1574
1673
|
"speed": 1.5
|
|
@@ -1601,7 +1700,7 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
|
|
|
1601
1700
|
{
|
|
1602
1701
|
"id": "lower-third",
|
|
1603
1702
|
"type": "ThirdLowerBanner",
|
|
1604
|
-
"props": { "name": "Waves", "title": "v0.
|
|
1703
|
+
"props": { "name": "Waves", "title": "v0.3.0 - Alignment + overlays", "accentColor": "#3B82F6" }
|
|
1605
1704
|
},
|
|
1606
1705
|
{
|
|
1607
1706
|
"id": "count",
|
|
@@ -1642,7 +1741,7 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
|
|
|
1642
1741
|
{ "id": "accent", "type": "Shape", "props": { "shape": "rect", "x": 180, "y": 200, "width": 12, "height": 680, "fill": "#3B82F6", "opacity": 1 } },
|
|
1643
1742
|
{
|
|
1644
1743
|
"id": "panel",
|
|
1645
|
-
"type": "
|
|
1744
|
+
"type": "Frame",
|
|
1646
1745
|
"props": { "x": 192, "y": 200, "width": 1548, "height": 680, "padding": 0, "backgroundColor": "rgba(255,255,255,0.06)", "borderRadius": 36, "opacity": 1 },
|
|
1647
1746
|
"children": [
|
|
1648
1747
|
{
|
|
@@ -1653,7 +1752,7 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
|
|
|
1653
1752
|
{
|
|
1654
1753
|
"id": "tile-1",
|
|
1655
1754
|
"type": "Box",
|
|
1656
|
-
"props": { "
|
|
1755
|
+
"props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
|
|
1657
1756
|
"children": [
|
|
1658
1757
|
{ "id": "t1", "type": "Text", "props": { "content": "Primitives", "fontSize": 64, "position": "center", "animation": "fade" } }
|
|
1659
1758
|
]
|
|
@@ -1661,7 +1760,7 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
|
|
|
1661
1760
|
{
|
|
1662
1761
|
"id": "tile-2",
|
|
1663
1762
|
"type": "Box",
|
|
1664
|
-
"props": { "
|
|
1763
|
+
"props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
|
|
1665
1764
|
"children": [
|
|
1666
1765
|
{ "id": "t2", "type": "Text", "props": { "content": "Box + Grid", "fontSize": 54, "position": "center", "animation": "slide" } }
|
|
1667
1766
|
]
|
|
@@ -1669,7 +1768,7 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
|
|
|
1669
1768
|
{
|
|
1670
1769
|
"id": "tile-3",
|
|
1671
1770
|
"type": "Box",
|
|
1672
|
-
"props": { "
|
|
1771
|
+
"props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
|
|
1673
1772
|
"children": [
|
|
1674
1773
|
{ "id": "t3", "type": "Text", "props": { "content": "Shape", "fontSize": 54, "position": "center", "animation": "zoom" } }
|
|
1675
1774
|
]
|
|
@@ -1677,7 +1776,7 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
|
|
|
1677
1776
|
{
|
|
1678
1777
|
"id": "tile-4",
|
|
1679
1778
|
"type": "Box",
|
|
1680
|
-
"props": { "
|
|
1779
|
+
"props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
|
|
1681
1780
|
"children": [
|
|
1682
1781
|
{ "id": "t4", "type": "Text", "props": { "content": "Text", "fontSize": 54, "position": "center", "animation": "fade" } }
|
|
1683
1782
|
]
|
|
@@ -1885,7 +1984,7 @@ This example uses:
|
|
|
1885
1984
|
"type": "Scene",
|
|
1886
1985
|
"props": { "background": { "type": "color", "value": "#000000" } },
|
|
1887
1986
|
"children": [
|
|
1888
|
-
{ "id": "ig", "type": "InstagramStory", "props": { "backgroundColor": "#0B1220", "username": "depths.ai", "text": "Waves v0.
|
|
1987
|
+
{ "id": "ig", "type": "InstagramStory", "props": { "backgroundColor": "#0B1220", "username": "depths.ai", "text": "Waves v0.3.0 ships seamless alignment + overlays", "sticker": "poll" } }
|
|
1889
1988
|
]
|
|
1890
1989
|
}
|
|
1891
1990
|
},
|
|
@@ -1932,7 +2031,7 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
|
|
|
1932
2031
|
"type": "Scene",
|
|
1933
2032
|
"props": { "background": { "type": "color", "value": "#000000" } },
|
|
1934
2033
|
"children": [
|
|
1935
|
-
{ "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.
|
|
2034
|
+
{ "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.3.0", "backgroundColor": "#000000", "primaryColor": "#FFFFFF" } }
|
|
1936
2035
|
]
|
|
1937
2036
|
}
|
|
1938
2037
|
},
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
globalRegistry,
|
|
7
7
|
registerBuiltInComponents,
|
|
8
8
|
zodSchemaToJsonSchema
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-YYS6AVTN.mjs";
|
|
10
10
|
|
|
11
11
|
// src/version.ts
|
|
12
12
|
var __wavesVersion = "0.2.0";
|
|
@@ -362,7 +362,7 @@ function getMaxEnd(nodes) {
|
|
|
362
362
|
|
|
363
363
|
// src/core/engine.ts
|
|
364
364
|
import { bundle } from "@remotion/bundler";
|
|
365
|
-
import { renderMedia, selectComposition } from "@remotion/renderer";
|
|
365
|
+
import { renderMedia, renderStill, selectComposition } from "@remotion/renderer";
|
|
366
366
|
import fs from "fs/promises";
|
|
367
367
|
import path from "path";
|
|
368
368
|
var WavesEngine = class {
|
|
@@ -400,14 +400,16 @@ var WavesEngine = class {
|
|
|
400
400
|
onProgress: () => void 0
|
|
401
401
|
});
|
|
402
402
|
const compositionId = validatedIR.video.id ?? "main";
|
|
403
|
+
const inputProps = options.inputProps ?? {};
|
|
403
404
|
const composition = await selectComposition({
|
|
404
405
|
serveUrl: bundleLocation,
|
|
405
406
|
id: compositionId,
|
|
406
|
-
inputProps
|
|
407
|
+
inputProps
|
|
407
408
|
});
|
|
408
409
|
await renderMedia({
|
|
409
410
|
composition,
|
|
410
411
|
serveUrl: bundleLocation,
|
|
412
|
+
inputProps,
|
|
411
413
|
codec: options.codec ?? "h264",
|
|
412
414
|
outputLocation: options.outputPath,
|
|
413
415
|
crf: options.crf ?? null,
|
|
@@ -420,6 +422,70 @@ var WavesEngine = class {
|
|
|
420
422
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
421
423
|
}
|
|
422
424
|
}
|
|
425
|
+
async renderStills(ir, options) {
|
|
426
|
+
if (this.registry !== globalRegistry) {
|
|
427
|
+
throw new WavesRenderError("WavesEngine currently requires using globalRegistry", {
|
|
428
|
+
hint: "Use `registerBuiltInComponents()` + `globalRegistry` for both validation and rendering."
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const validationResult = this.validator.validate(ir);
|
|
432
|
+
if (!validationResult.success) {
|
|
433
|
+
throw new WavesRenderError("IR validation failed", { errors: validationResult.errors });
|
|
434
|
+
}
|
|
435
|
+
const validatedIR = validationResult.data;
|
|
436
|
+
const requiredTypes = collectComponentTypes(validatedIR);
|
|
437
|
+
for (const type of requiredTypes) {
|
|
438
|
+
if (!this.registry.has(type)) {
|
|
439
|
+
throw new WavesRenderError("Unknown component type", { type });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
443
|
+
const tmpDir = await fs.mkdtemp(path.join(rootDir, ".waves-tmp-"));
|
|
444
|
+
const entryPoint = path.join(tmpDir, "entry.tsx");
|
|
445
|
+
try {
|
|
446
|
+
await fs.writeFile(entryPoint, generateEntryPoint(validatedIR, options), "utf-8");
|
|
447
|
+
await fs.mkdir(options.outputDir, { recursive: true });
|
|
448
|
+
const bundleLocation = await bundle({
|
|
449
|
+
entryPoint,
|
|
450
|
+
rootDir,
|
|
451
|
+
publicDir: options.publicDir ?? null,
|
|
452
|
+
onProgress: () => void 0
|
|
453
|
+
});
|
|
454
|
+
const compositionId = validatedIR.video.id ?? "main";
|
|
455
|
+
const inputProps = options.inputProps ?? {};
|
|
456
|
+
const composition = await selectComposition({
|
|
457
|
+
serveUrl: bundleLocation,
|
|
458
|
+
id: compositionId,
|
|
459
|
+
inputProps
|
|
460
|
+
});
|
|
461
|
+
const frames = Array.from(new Set(options.frames)).sort((a, b) => a - b);
|
|
462
|
+
const written = [];
|
|
463
|
+
for (const frame of frames) {
|
|
464
|
+
const clamped = Math.max(0, Math.min(composition.durationInFrames - 1, Math.floor(frame)));
|
|
465
|
+
const imageFormat = options.imageFormat ?? "png";
|
|
466
|
+
const filename = `${compositionId}-frame-${String(clamped).padStart(6, "0")}.${imageFormat}`;
|
|
467
|
+
const outPath = path.join(options.outputDir, filename);
|
|
468
|
+
const stillOptions = {
|
|
469
|
+
composition,
|
|
470
|
+
serveUrl: bundleLocation,
|
|
471
|
+
inputProps,
|
|
472
|
+
frame: clamped,
|
|
473
|
+
output: outPath,
|
|
474
|
+
imageFormat,
|
|
475
|
+
scale: options.scale ?? 1,
|
|
476
|
+
overwrite: true,
|
|
477
|
+
...imageFormat === "jpeg" ? { jpegQuality: options.jpegQuality ?? 80 } : {}
|
|
478
|
+
};
|
|
479
|
+
await renderStill(stillOptions);
|
|
480
|
+
written.push(outPath);
|
|
481
|
+
}
|
|
482
|
+
return written;
|
|
483
|
+
} catch (error) {
|
|
484
|
+
throw new WavesRenderError("Rendering failed", { originalError: error });
|
|
485
|
+
} finally {
|
|
486
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
487
|
+
}
|
|
488
|
+
}
|
|
423
489
|
};
|
|
424
490
|
function collectComponentTypes(ir) {
|
|
425
491
|
const types = /* @__PURE__ */ new Set();
|
|
@@ -454,8 +520,10 @@ registerBuiltInComponents();
|
|
|
454
520
|
const ir = ${JSON.stringify(ir, null, 2)};
|
|
455
521
|
const compositionId = ${JSON.stringify(compositionId)};
|
|
456
522
|
|
|
457
|
-
const Root = () => {
|
|
458
|
-
|
|
523
|
+
const Root = (props) => {
|
|
524
|
+
const debugBounds = Boolean(props?.__wavesDebugBounds);
|
|
525
|
+
const debugLabels = Boolean(props?.__wavesDebugLabels);
|
|
526
|
+
return <WavesComposition ir={ir} registry={globalRegistry} debug={{ bounds: debugBounds, labels: debugLabels }} />;
|
|
459
527
|
};
|
|
460
528
|
|
|
461
529
|
export const RemotionRoot = () => {
|