@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 CHANGED
@@ -1,4 +1,4 @@
1
- # @depths/waves (v0.2.0)
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 adds a shadcn-like catalog of higher-level "composite" components and a hybrid IR that supports:
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.2.0 targets `version: "2.0"` only.
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.2.0 targets `version: "2.0"` only.
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 | Positioned container box for layout and backgrounds |
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: Positioned container box for layout and backgrounds
897
- - llmGuidance: Use Box to position content by x/y and optionally constrain width/height. Prefer Box+Stack/Grid for layouts.
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.2.0",
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": "Composite components + hybrid IR",
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.2.0 - Composites + transitions", "accentColor": "#3B82F6" }
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": "Box",
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": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
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": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
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": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
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": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
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.2.0 ships composites + hybrid IR", "sticker": "poll" } }
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.2.0", "backgroundColor": "#000000", "primaryColor": "#FFFFFF" } }
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-PKLHVWMD.mjs";
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
- return <WavesComposition ir={ir} registry={globalRegistry} />;
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 = () => {