@vibecuting/video-project-core 0.1.29 → 0.1.31

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.
Files changed (182) hide show
  1. package/package.json +9 -5
  2. package/src/base/BaseRemotionScene.test.tsx +11 -1
  3. package/src/base/index.ts +1 -0
  4. package/src/base/utils/asset-source.ts +11 -0
  5. package/src/base/utils/background-audio-layer.tsx +90 -0
  6. package/src/base/utils/background-layer.tsx +84 -0
  7. package/src/base/utils/chapter-progress-foreground.tsx +125 -0
  8. package/src/base/utils/chapter-progress-state.test.ts +28 -0
  9. package/src/base/utils/chapter-progress-state.ts +33 -0
  10. package/src/base/utils/display-motion.test.ts +21 -0
  11. package/src/base/utils/display-motion.ts +52 -0
  12. package/src/base/utils/display-surface.ts +57 -0
  13. package/src/base/utils/index.ts +10 -0
  14. package/src/base/utils/subtitle-style.ts +28 -0
  15. package/src/base/utils/video-layer-types.test.ts +145 -0
  16. package/src/base/utils/video-layer-types.ts +220 -0
  17. package/src/base/utils/voice-captions-foreground.tsx +169 -0
  18. package/src/core/intro/index.ts +18 -18
  19. package/src/core/outro/index.ts +33 -33
  20. package/src/core/scene/2dscene/{default-2d-scene-Horizontal.stories.tsx → default-2d-scene/default-2d-scene-Horizontal.stories.tsx} +1 -1
  21. package/src/core/scene/2dscene/{default-2d-scene-Horizontal.tsx → default-2d-scene/default-2d-scene-Horizontal.tsx} +1 -1
  22. package/src/core/scene/2dscene/{default-2d-scene-Landscape.tsx → default-2d-scene/default-2d-scene-Landscape.tsx} +1 -1
  23. package/src/core/scene/2dscene/{default-2d-scene.stories.tsx → default-2d-scene/default-2d-scene.stories.tsx} +1 -1
  24. package/src/core/scene/2dscene/{default-2d-scene.tsx → default-2d-scene/default-2d-scene.tsx} +4 -4
  25. package/src/core/scene/2dscene/index.ts +3 -3
  26. package/src/core/scene/3dscene/{default-3d-scene-Horizontal.stories.tsx → default-3d-scene/default-3d-scene-Horizontal.stories.tsx} +1 -1
  27. package/src/core/scene/3dscene/{default-3d-scene-Horizontal.tsx → default-3d-scene/default-3d-scene-Horizontal.tsx} +1 -1
  28. package/src/core/scene/3dscene/{default-3d-scene-Landscape.tsx → default-3d-scene/default-3d-scene-Landscape.tsx} +1 -1
  29. package/src/core/scene/3dscene/{default-3d-scene.stories.tsx → default-3d-scene/default-3d-scene.stories.tsx} +1 -1
  30. package/src/core/scene/3dscene/{default-3d-scene.tsx → default-3d-scene/default-3d-scene.tsx} +4 -4
  31. package/src/core/scene/3dscene/index.ts +3 -3
  32. package/src/core/scene/html-in-canvas/browser/default-html-in-canvas-browser-scene-Horizontal.stories.tsx +35 -0
  33. package/src/core/scene/html-in-canvas/browser/default-html-in-canvas-browser-scene-Landscape.stories.tsx +80 -0
  34. package/src/core/scene/html-in-canvas/dashboard/default-html-in-canvas-dashboard-scene-Horizontal.stories.tsx +35 -0
  35. package/src/core/scene/html-in-canvas/dashboard/default-html-in-canvas-dashboard-scene-Landscape.stories.tsx +35 -0
  36. package/src/core/scene/html-in-canvas/dashboard-shell/default-dashboard-shell-html-in-canvas-scene.stories.tsx +40 -0
  37. package/src/core/scene/html-in-canvas/dashboard-shell/default-dashboard-shell-html-in-canvas-scene.tsx +44 -0
  38. package/src/core/scene/html-in-canvas/dashboard-shell/index.ts +8 -0
  39. package/src/core/scene/html-in-canvas/docs/default-html-in-canvas-docs-scene-Horizontal.stories.tsx +35 -0
  40. package/src/core/scene/html-in-canvas/docs/default-html-in-canvas-docs-scene-Landscape.stories.tsx +35 -0
  41. package/src/core/scene/html-in-canvas/document/article/default-article-html-in-canvas-scene.stories.tsx +38 -0
  42. package/src/core/scene/html-in-canvas/document/article/default-article-html-in-canvas-scene.tsx +50 -0
  43. package/src/core/scene/html-in-canvas/document/article/index.ts +8 -0
  44. package/src/core/scene/html-in-canvas/document/default-document-html-in-canvas-scene.stories.tsx +40 -0
  45. package/src/core/scene/html-in-canvas/document/default-document-html-in-canvas-scene.tsx +50 -0
  46. package/src/core/scene/html-in-canvas/document/index.ts +31 -0
  47. package/src/core/scene/html-in-canvas/document/report/default-report-html-in-canvas-scene.stories.tsx +38 -0
  48. package/src/core/scene/html-in-canvas/document/report/default-report-html-in-canvas-scene.tsx +51 -0
  49. package/src/core/scene/html-in-canvas/document/report/index.ts +8 -0
  50. package/src/core/scene/html-in-canvas/document/whitepaper/default-whitepaper-html-in-canvas-scene.stories.tsx +41 -0
  51. package/src/core/scene/html-in-canvas/document/whitepaper/default-whitepaper-html-in-canvas-scene.tsx +50 -0
  52. package/src/core/scene/html-in-canvas/document/whitepaper/index.ts +8 -0
  53. package/src/core/scene/html-in-canvas/editor/default-html-in-canvas-editor-scene-Horizontal.stories.tsx +35 -0
  54. package/src/core/scene/html-in-canvas/editor/default-html-in-canvas-editor-scene-Landscape.stories.tsx +35 -0
  55. package/src/core/scene/html-in-canvas/full-bleed/default-full-bleed-html-in-canvas-scene.stories.tsx +40 -0
  56. package/src/core/scene/html-in-canvas/full-bleed/default-full-bleed-html-in-canvas-scene.tsx +38 -0
  57. package/src/core/scene/html-in-canvas/full-bleed/index.ts +8 -0
  58. package/src/core/scene/html-in-canvas/index.ts +6 -0
  59. package/src/core/scene/html-in-canvas/landing-page/default-landing-page-html-in-canvas-scene.stories.tsx +40 -0
  60. package/src/core/scene/html-in-canvas/landing-page/default-landing-page-html-in-canvas-scene.tsx +46 -0
  61. package/src/core/scene/html-in-canvas/landing-page/feature-grid/default-feature-grid-html-in-canvas-scene.stories.tsx +38 -0
  62. package/src/core/scene/html-in-canvas/landing-page/feature-grid/default-feature-grid-html-in-canvas-scene.tsx +41 -0
  63. package/src/core/scene/html-in-canvas/landing-page/feature-grid/index.ts +8 -0
  64. package/src/core/scene/html-in-canvas/landing-page/hero/default-hero-html-in-canvas-scene.stories.tsx +38 -0
  65. package/src/core/scene/html-in-canvas/landing-page/hero/default-hero-html-in-canvas-scene.tsx +46 -0
  66. package/src/core/scene/html-in-canvas/landing-page/hero/index.ts +8 -0
  67. package/src/core/scene/html-in-canvas/landing-page/index.ts +31 -0
  68. package/src/core/scene/html-in-canvas/landing-page/pricing/default-pricing-html-in-canvas-scene.stories.tsx +38 -0
  69. package/src/core/scene/html-in-canvas/landing-page/pricing/default-pricing-html-in-canvas-scene.tsx +42 -0
  70. package/src/core/scene/html-in-canvas/landing-page/pricing/index.ts +8 -0
  71. package/src/core/scene/html-in-canvas/media-wall/collage/default-collage-html-in-canvas-scene.stories.tsx +38 -0
  72. package/src/core/scene/html-in-canvas/media-wall/collage/default-collage-html-in-canvas-scene.tsx +42 -0
  73. package/src/core/scene/html-in-canvas/media-wall/collage/index.ts +8 -0
  74. package/src/core/scene/html-in-canvas/media-wall/default-media-wall-html-in-canvas-scene.stories.tsx +40 -0
  75. package/src/core/scene/html-in-canvas/media-wall/default-media-wall-html-in-canvas-scene.tsx +35 -0
  76. package/src/core/scene/html-in-canvas/media-wall/gallery/default-gallery-html-in-canvas-scene.stories.tsx +38 -0
  77. package/src/core/scene/html-in-canvas/media-wall/gallery/default-gallery-html-in-canvas-scene.tsx +43 -0
  78. package/src/core/scene/html-in-canvas/media-wall/gallery/index.ts +8 -0
  79. package/src/core/scene/html-in-canvas/media-wall/grid/default-grid-html-in-canvas-scene.stories.tsx +38 -0
  80. package/src/core/scene/html-in-canvas/media-wall/grid/default-grid-html-in-canvas-scene.tsx +35 -0
  81. package/src/core/scene/html-in-canvas/media-wall/grid/index.ts +8 -0
  82. package/src/core/scene/html-in-canvas/media-wall/index.ts +31 -0
  83. package/src/core/scene/html-in-canvas/shared/default-html-in-canvas-scene.tsx +1315 -0
  84. package/src/core/scene/index.ts +1 -0
  85. package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene.stories.tsx → default-3dvr-photo-scene/default-3dvr-photo-scene.stories.tsx} +2 -2
  86. package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene.tsx → default-3dvr-photo-scene/default-3dvr-photo-scene.tsx} +4 -4
  87. package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene-Horizontal.stories.tsx → default-3dvr-photo-scene-Horizontal/default-3dvr-photo-scene-Horizontal.stories.tsx} +1 -1
  88. package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene-Horizontal.tsx → default-3dvr-photo-scene-Horizontal/default-3dvr-photo-scene-Horizontal.tsx} +4 -4
  89. package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene-Landscape.tsx → default-3dvr-photo-scene-Landscape/default-3dvr-photo-scene-Landscape.tsx} +4 -4
  90. package/src/core/scene/photo/3dvr-photo/default-dolly-3dvr-photo-scene/default-dolly-3dvr-photo-scene.stories.tsx +38 -0
  91. package/src/core/scene/photo/3dvr-photo/default-dolly-3dvr-photo-scene/default-dolly-3dvr-photo-scene.tsx +36 -0
  92. package/src/core/scene/photo/3dvr-photo/default-orbit-3dvr-photo-scene/default-orbit-3dvr-photo-scene.stories.tsx +38 -0
  93. package/src/core/scene/photo/3dvr-photo/default-orbit-3dvr-photo-scene/default-orbit-3dvr-photo-scene.tsx +34 -0
  94. package/src/core/scene/photo/3dvr-photo/default-poster-3dvr-photo-scene/default-poster-3dvr-photo-scene.stories.tsx +38 -0
  95. package/src/core/scene/photo/3dvr-photo/default-poster-3dvr-photo-scene/default-poster-3dvr-photo-scene.tsx +39 -0
  96. package/src/core/scene/photo/3dvr-photo/index.ts +6 -3
  97. package/src/core/scene/photo/effects-photo/default-chrome-effects-photo-scene/default-chrome-effects-photo-scene.stories.tsx +24 -0
  98. package/src/core/scene/photo/effects-photo/default-chrome-effects-photo-scene/default-chrome-effects-photo-scene.tsx +39 -0
  99. package/src/core/scene/photo/effects-photo/default-cool-tone-effects-photo-scene/default-cool-tone-effects-photo-scene.stories.tsx +24 -0
  100. package/src/core/scene/photo/effects-photo/default-cool-tone-effects-photo-scene/default-cool-tone-effects-photo-scene.tsx +39 -0
  101. package/src/core/scene/photo/effects-photo/default-dream-effects-photo-scene/default-dream-effects-photo-scene.stories.tsx +23 -0
  102. package/src/core/scene/photo/effects-photo/default-dream-effects-photo-scene/default-dream-effects-photo-scene.tsx +37 -0
  103. package/src/core/scene/photo/effects-photo/default-duotone-effects-photo-scene/default-duotone-effects-photo-scene.stories.tsx +25 -0
  104. package/src/core/scene/photo/effects-photo/default-duotone-effects-photo-scene/default-duotone-effects-photo-scene.tsx +41 -0
  105. package/src/core/scene/photo/effects-photo/{default-effects-photo-scene.stories.tsx → default-effects-photo-scene/default-effects-photo-scene.stories.tsx} +2 -2
  106. package/src/core/scene/photo/effects-photo/default-effects-photo-scene/default-effects-photo-scene.tsx +639 -0
  107. package/src/core/scene/photo/effects-photo/{default-effects-photo-scene-Horizontal.stories.tsx → default-effects-photo-scene-Horizontal/default-effects-photo-scene-Horizontal.stories.tsx} +1 -1
  108. package/src/core/scene/photo/effects-photo/{default-effects-photo-scene-Horizontal.tsx → default-effects-photo-scene-Horizontal/default-effects-photo-scene-Horizontal.tsx} +4 -4
  109. package/src/core/scene/photo/effects-photo/{default-effects-photo-scene-Landscape.tsx → default-effects-photo-scene-Landscape/default-effects-photo-scene-Landscape.tsx} +4 -4
  110. package/src/core/scene/photo/effects-photo/default-glow-effects-photo-scene/default-glow-effects-photo-scene.stories.tsx +39 -0
  111. package/src/core/scene/photo/effects-photo/default-glow-effects-photo-scene/default-glow-effects-photo-scene.tsx +37 -0
  112. package/src/core/scene/photo/effects-photo/default-grain-effects-photo-scene/default-grain-effects-photo-scene.stories.tsx +39 -0
  113. package/src/core/scene/photo/effects-photo/default-grain-effects-photo-scene/default-grain-effects-photo-scene.tsx +37 -0
  114. package/src/core/scene/photo/effects-photo/default-halation-effects-photo-scene/default-halation-effects-photo-scene.stories.tsx +24 -0
  115. package/src/core/scene/photo/effects-photo/default-halation-effects-photo-scene/default-halation-effects-photo-scene.tsx +39 -0
  116. package/src/core/scene/photo/effects-photo/default-infrared-effects-photo-scene/default-infrared-effects-photo-scene.stories.tsx +24 -0
  117. package/src/core/scene/photo/effects-photo/default-infrared-effects-photo-scene/default-infrared-effects-photo-scene.tsx +39 -0
  118. package/src/core/scene/photo/effects-photo/default-light-leak-effects-photo-scene/default-light-leak-effects-photo-scene.stories.tsx +24 -0
  119. package/src/core/scene/photo/effects-photo/default-light-leak-effects-photo-scene/default-light-leak-effects-photo-scene.tsx +39 -0
  120. package/src/core/scene/photo/effects-photo/default-mono-effects-photo-scene/default-mono-effects-photo-scene.stories.tsx +23 -0
  121. package/src/core/scene/photo/effects-photo/default-mono-effects-photo-scene/default-mono-effects-photo-scene.tsx +37 -0
  122. package/src/core/scene/photo/effects-photo/default-noir-effects-photo-scene/default-noir-effects-photo-scene.stories.tsx +23 -0
  123. package/src/core/scene/photo/effects-photo/default-noir-effects-photo-scene/default-noir-effects-photo-scene.tsx +37 -0
  124. package/src/core/scene/photo/effects-photo/default-pastel-effects-photo-scene/default-pastel-effects-photo-scene.stories.tsx +24 -0
  125. package/src/core/scene/photo/effects-photo/default-pastel-effects-photo-scene/default-pastel-effects-photo-scene.tsx +39 -0
  126. package/src/core/scene/photo/effects-photo/default-retro-effects-photo-scene/default-retro-effects-photo-scene.stories.tsx +24 -0
  127. package/src/core/scene/photo/effects-photo/default-retro-effects-photo-scene/default-retro-effects-photo-scene.tsx +39 -0
  128. package/src/core/scene/photo/effects-photo/default-sepia-effects-photo-scene/default-sepia-effects-photo-scene.stories.tsx +23 -0
  129. package/src/core/scene/photo/effects-photo/default-sepia-effects-photo-scene/default-sepia-effects-photo-scene.tsx +37 -0
  130. package/src/core/scene/photo/effects-photo/default-soft-focus-effects-photo-scene/default-soft-focus-effects-photo-scene.stories.tsx +39 -0
  131. package/src/core/scene/photo/effects-photo/default-soft-focus-effects-photo-scene/default-soft-focus-effects-photo-scene.tsx +37 -0
  132. package/src/core/scene/photo/effects-photo/default-vibrant-effects-photo-scene/default-vibrant-effects-photo-scene.stories.tsx +23 -0
  133. package/src/core/scene/photo/effects-photo/default-vibrant-effects-photo-scene/default-vibrant-effects-photo-scene.tsx +37 -0
  134. package/src/core/scene/photo/effects-photo/default-vignette-effects-photo-scene/default-vignette-effects-photo-scene.stories.tsx +39 -0
  135. package/src/core/scene/photo/effects-photo/default-vignette-effects-photo-scene/default-vignette-effects-photo-scene.tsx +37 -0
  136. package/src/core/scene/photo/effects-photo/default-warmth-effects-photo-scene/default-warmth-effects-photo-scene.stories.tsx +40 -0
  137. package/src/core/scene/photo/effects-photo/default-warmth-effects-photo-scene/default-warmth-effects-photo-scene.tsx +39 -0
  138. package/src/core/scene/photo/effects-photo/index.ts +21 -3
  139. package/src/core/scene/photo/index.ts +25 -9
  140. package/src/core/scene/photo/{default-live-photo-scene-Horizontal.stories.tsx → live-photo/default-live-photo-scene-Horizontal.stories.tsx} +1 -1
  141. package/src/core/scene/photo/{default-live-photo-scene-Horizontal.tsx → live-photo/default-live-photo-scene-Horizontal.tsx} +1 -1
  142. package/src/core/scene/photo/{default-live-photo-scene-Landscape.tsx → live-photo/default-live-photo-scene-Landscape.tsx} +1 -1
  143. package/src/core/scene/photo/{default-live-photo-scene.stories.tsx → live-photo/default-live-photo-scene.stories.tsx} +1 -1
  144. package/src/core/scene/photo/{default-live-photo-scene.tsx → live-photo/default-live-photo-scene.tsx} +5 -3
  145. package/src/core/scene/photo/photo-storybook.test.tsx +2 -2
  146. package/src/core/scene/photo/{default-photo-strip-scene-Horizontal.tsx → photo-strip/default-photo-strip-scene-Horizontal.tsx} +1 -1
  147. package/src/core/scene/photo/{default-photo-strip-scene-Landscape.tsx → photo-strip/default-photo-strip-scene-Landscape.tsx} +1 -1
  148. package/src/core/scene/photo/{default-photo-strip-scene.stories.tsx → photo-strip/default-photo-strip-scene.stories.tsx} +1 -1
  149. package/src/core/scene/photo/{default-photo-strip-scene.tsx → photo-strip/default-photo-strip-scene.tsx} +2 -2
  150. package/src/core/scene/photo/{default-photo-wall-scene-Horizontal.tsx → photo-wall/default-photo-wall-scene-Horizontal.tsx} +1 -1
  151. package/src/core/scene/photo/{default-photo-wall-scene-Landscape.tsx → photo-wall/default-photo-wall-scene-Landscape.tsx} +1 -1
  152. package/src/core/scene/photo/{default-photo-wall-scene.stories.tsx → photo-wall/default-photo-wall-scene.stories.tsx} +1 -1
  153. package/src/core/scene/photo/{default-photo-wall-scene.tsx → photo-wall/default-photo-wall-scene.tsx} +2 -2
  154. package/src/core/scene/slide/{default-slide-scene-Horizontal.animated.stories.tsx → default-slide-scene/default-slide-scene-Horizontal.animated.stories.tsx} +1 -1
  155. package/src/core/scene/slide/{default-slide-scene-Horizontal.tsx → default-slide-scene/default-slide-scene-Horizontal.tsx} +1 -1
  156. package/src/core/scene/slide/{default-slide-scene-Landscape.tsx → default-slide-scene/default-slide-scene-Landscape.tsx} +1 -1
  157. package/src/core/scene/slide/{default-slide-scene.animated.stories.tsx → default-slide-scene/default-slide-scene.animated.stories.tsx} +1 -1
  158. package/src/core/scene/slide/{default-slide-scene.tsx → default-slide-scene/default-slide-scene.tsx} +5 -5
  159. package/src/core/scene/slide/index.ts +3 -3
  160. package/src/core/scene/video/{default-video-scene-Horizontal.stories.tsx → default-video-scene/default-video-scene-Horizontal.stories.tsx} +1 -1
  161. package/src/core/scene/video/{default-video-scene-Horizontal.tsx → default-video-scene/default-video-scene-Horizontal.tsx} +1 -1
  162. package/src/core/scene/video/{default-video-scene-Landscape.tsx → default-video-scene/default-video-scene-Landscape.tsx} +1 -1
  163. package/src/core/scene/video/{default-video-scene.stories.tsx → default-video-scene/default-video-scene.stories.tsx} +1 -1
  164. package/src/core/scene/video/{default-video-scene.tsx → default-video-scene/default-video-scene.tsx} +3 -3
  165. package/src/core/scene/video/{default-video-split-scene-Horizontal.tsx → default-video-split-scene/default-video-split-scene-Horizontal.tsx} +1 -1
  166. package/src/core/scene/video/{default-video-split-scene-Landscape.tsx → default-video-split-scene/default-video-split-scene-Landscape.tsx} +1 -1
  167. package/src/core/scene/video/{default-video-split-scene.stories.tsx → default-video-split-scene/default-video-split-scene.stories.tsx} +1 -1
  168. package/src/core/scene/video/{default-video-split-scene.tsx → default-video-split-scene/default-video-split-scene.tsx} +2 -2
  169. package/src/core/scene/video/{default-video-wall-scene-Horizontal.tsx → default-video-wall-scene/default-video-wall-scene-Horizontal.tsx} +1 -1
  170. package/src/core/scene/video/{default-video-wall-scene-Landscape.tsx → default-video-wall-scene/default-video-wall-scene-Landscape.tsx} +1 -1
  171. package/src/core/scene/video/{default-video-wall-scene.stories.tsx → default-video-wall-scene/default-video-wall-scene.stories.tsx} +1 -1
  172. package/src/core/scene/video/{default-video-wall-scene.tsx → default-video-wall-scene/default-video-wall-scene.tsx} +2 -2
  173. package/src/core/scene/video/index.ts +9 -9
  174. package/src/lint/index.ts +2 -0
  175. package/src/lint/scene-convention-lint.test.ts +86 -0
  176. package/src/lint/scene-convention-lint.ts +247 -0
  177. package/src/storybook/index.ts +19 -0
  178. package/src/storybook/remotion-preview.test.tsx +53 -0
  179. package/src/storybook/remotion-preview.tsx +85 -11
  180. package/src/core/scene/photo/effects-photo/default-effects-photo-scene.tsx +0 -352
  181. /package/src/core/scene/slide/{default-slide-scene-Horizontal.stories.tsx → default-slide-scene/default-slide-scene-Horizontal.stories.tsx} +0 -0
  182. /package/src/core/scene/slide/{default-slide-scene.stories.tsx → default-slide-scene/default-slide-scene.stories.tsx} +0 -0
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@vibecuting/video-project-core",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "main": "src/index.ts",
7
7
  "types": "src/index.ts",
8
8
  "exports": {
9
- ".": "./src/index.ts"
9
+ ".": "./src/index.ts",
10
+ "./storybook": "./src/storybook/index.ts",
11
+ "./storybook/remotion-preview": "./src/storybook/remotion-preview.tsx",
12
+ "./lint": "./src/lint/index.ts"
10
13
  },
11
14
  "publishConfig": {
12
15
  "registry": "https://registry.npmjs.org",
@@ -16,13 +19,13 @@
16
19
  "dev": "storybook dev -p 6010 --host 0.0.0.0",
17
20
  "preview": "storybook dev -p 6010 --host 0.0.0.0",
18
21
  "build:storybook": "storybook build",
19
- "lint": "node ../../../scripts/run-eslint.mjs src .storybook",
20
- "lint:fix": "node ../../../scripts/run-eslint.mjs src .storybook --fix",
22
+ "lint": "node ../../../scripts/run-eslint.mjs src .storybook && node ../../../scripts/lint-vibecuting-scenes.mjs --all",
23
+ "lint:fix": "node ../../../scripts/run-eslint.mjs src .storybook --fix && node ../../../scripts/lint-vibecuting-scenes.mjs --all --fix",
21
24
  "typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
22
25
  "test": "pnpm exec vitest run"
23
26
  },
24
27
  "dependencies": {
25
- "@vibecuting/component-project-helper": "0.1.29",
28
+ "@vibecuting/component-project-helper": "0.1.31",
26
29
  "@remotion/transitions": "4.0.473",
27
30
  "mermaid": "^11.12.2",
28
31
  "react": "^19.0.0",
@@ -37,6 +40,7 @@
37
40
  "@storybook/react-vite": "^8.6.12",
38
41
  "@types/react": "18.2.20",
39
42
  "@types/react-dom": "18.2.7",
43
+ "react-dom": "19.2.7",
40
44
  "remotion": "4.0.473",
41
45
  "storybook": "^8.6.12",
42
46
  "typescript": "5.9.3",
@@ -1,8 +1,18 @@
1
1
  import { renderToStaticMarkup } from 'react-dom/server'
2
- import { describe, expect, it } from 'vitest'
2
+ import { describe, expect, it, vi } from 'vitest'
3
3
 
4
4
  import { BaseRemotionScene } from './BaseRemotionScene'
5
5
 
6
+ vi.mock('remotion', () => ({
7
+ useCurrentFrame: () => 0,
8
+ useVideoConfig: () => ({
9
+ fps: 30,
10
+ width: 1920,
11
+ height: 1080,
12
+ durationInFrames: 150,
13
+ }),
14
+ }))
15
+
6
16
  describe('BaseRemotionScene', () => {
7
17
  it('renders a reusable scene frame', () => {
8
18
  const html = renderToStaticMarkup(
package/src/base/index.ts CHANGED
@@ -14,3 +14,4 @@ export {
14
14
  } from './use-scene-config'
15
15
  export { useSceneMotion } from './use-scene-motion'
16
16
  export { useRemotionSceneRuntime } from './use-remotion-scene-runtime'
17
+ export * from './utils'
@@ -0,0 +1,11 @@
1
+ import { staticFile } from "remotion";
2
+
3
+ export const resolveVideoLayerAssetSource = (source: string) => {
4
+ if (/^https?:\/\//.test(source)) {
5
+ return source;
6
+ }
7
+
8
+ const normalizedSource = source.replace(/^public\//, "").replace(/^\/+/, "");
9
+
10
+ return staticFile(normalizedSource);
11
+ };
@@ -0,0 +1,90 @@
1
+ import { Audio, Sequence } from "remotion";
2
+ import type {
3
+ VideoLayerAudioCue,
4
+ VideoLayerBackgroundMusicPlan,
5
+ } from "./video-layer-types";
6
+ import { resolveVideoLayerAssetSource } from "./asset-source";
7
+
8
+ export type VideoLayerChapterAudioTiming = {
9
+ key: string;
10
+ startFrame: number;
11
+ durationInFrames: number;
12
+ };
13
+
14
+ export type VideoLayerChapterAudioPlan = {
15
+ id: string;
16
+ bgm?: VideoLayerBackgroundMusicPlan | null;
17
+ };
18
+
19
+ export type VideoBackgroundAudioLayerProps = {
20
+ chapters?: VideoLayerChapterAudioPlan[];
21
+ chapterTimings?: VideoLayerChapterAudioTiming[];
22
+ cues?: VideoLayerAudioCue[];
23
+ };
24
+
25
+ export const getVideoLayerCueVolume = (cue: VideoLayerAudioCue, frame: number) => {
26
+ const baseVolume = cue.volume ?? 1;
27
+ const fadeInFrames = cue.fadeInFrames ?? 0;
28
+ const fadeOutFrames = cue.fadeOutFrames ?? 0;
29
+ const fadeInVolume =
30
+ fadeInFrames > 0 ? Math.min(1, Math.max(0, frame / fadeInFrames)) : 1;
31
+ const fadeOutStartFrame = cue.durationInFrames - fadeOutFrames;
32
+ const fadeOutVolume =
33
+ fadeOutFrames > 0 && frame >= fadeOutStartFrame
34
+ ? Math.min(1, Math.max(0, (cue.durationInFrames - frame) / fadeOutFrames))
35
+ : 1;
36
+
37
+ return baseVolume * Math.min(fadeInVolume, fadeOutVolume);
38
+ };
39
+
40
+ export const buildVideoLayerBgmCues = (
41
+ chapters: VideoLayerChapterAudioPlan[],
42
+ chapterTimings: VideoLayerChapterAudioTiming[],
43
+ ): VideoLayerAudioCue[] => {
44
+ return chapters.flatMap((chapter) => {
45
+ const chapterTiming = chapterTimings.find((item) => item.key === chapter.id);
46
+
47
+ if (!chapter.bgm?.source || !chapterTiming) {
48
+ return [];
49
+ }
50
+
51
+ return [
52
+ {
53
+ id: `bgm-${chapter.id}`,
54
+ source: chapter.bgm.source,
55
+ startFrame: chapterTiming.startFrame,
56
+ durationInFrames: chapterTiming.durationInFrames,
57
+ volume: chapter.bgm.volume,
58
+ fadeInFrames: chapter.bgm.fadeInFrames,
59
+ fadeOutFrames: chapter.bgm.fadeOutFrames,
60
+ },
61
+ ];
62
+ });
63
+ };
64
+
65
+ export const VideoBackgroundAudioLayer = ({
66
+ chapters = [],
67
+ chapterTimings = [],
68
+ cues = [],
69
+ }: VideoBackgroundAudioLayerProps) => {
70
+ const audioCues = [...buildVideoLayerBgmCues(chapters, chapterTimings), ...cues];
71
+
72
+ return (
73
+ <>
74
+ {audioCues.map((cue) => (
75
+ <Sequence
76
+ key={cue.id}
77
+ from={cue.startFrame}
78
+ durationInFrames={cue.durationInFrames}
79
+ >
80
+ <Audio
81
+ src={resolveVideoLayerAssetSource(cue.source)}
82
+ volume={(frame: number) => getVideoLayerCueVolume(cue, frame)}
83
+ />
84
+ </Sequence>
85
+ ))}
86
+ </>
87
+ );
88
+ };
89
+
90
+ export const BackgroundAudioLayer = VideoBackgroundAudioLayer;
@@ -0,0 +1,84 @@
1
+ import { Img } from "remotion";
2
+ import type { VideoLayerBackgroundConfig } from "./video-layer-types";
3
+
4
+ export type VideoBackgroundLayerProps = {
5
+ config: VideoLayerBackgroundConfig;
6
+ };
7
+
8
+ export const VideoBackgroundLayer = ({ config }: VideoBackgroundLayerProps) => {
9
+ const backgroundStyle =
10
+ config.mode === "gradient" && config.gradient ? config.gradient : config.baseColor;
11
+
12
+ return (
13
+ <div
14
+ style={{
15
+ position: "absolute",
16
+ inset: 0,
17
+ overflow: "hidden",
18
+ background: config.baseColor,
19
+ }}
20
+ >
21
+ <div
22
+ style={{
23
+ position: "absolute",
24
+ inset: 0,
25
+ background: backgroundStyle,
26
+ }}
27
+ />
28
+ <div
29
+ style={{
30
+ position: "absolute",
31
+ inset: 0,
32
+ background:
33
+ "radial-gradient(circle at 50% 26%, rgba(255, 255, 255, 0.16), transparent 18%), radial-gradient(circle at 50% 62%, rgba(56, 189, 248, 0.18), transparent 24%), radial-gradient(circle at center, transparent 34%, rgba(2, 6, 23, 0.28) 72%, rgba(2, 6, 23, 0.52) 100%)",
34
+ mixBlendMode: "screen",
35
+ opacity: 0.95,
36
+ }}
37
+ />
38
+ <div
39
+ style={{
40
+ position: "absolute",
41
+ inset: 0,
42
+ background:
43
+ "repeating-linear-gradient(0deg, rgba(15, 23, 42, 0.025) 0, rgba(15, 23, 42, 0.025) 1px, transparent 1px, transparent 4px)",
44
+ opacity: 0.18,
45
+ mixBlendMode: "multiply",
46
+ }}
47
+ />
48
+ <div
49
+ style={{
50
+ position: "absolute",
51
+ inset: 0,
52
+ background:
53
+ "linear-gradient(180deg, rgba(2, 6, 23, 0.14) 0%, transparent 26%, transparent 72%, rgba(2, 6, 23, 0.22) 100%)",
54
+ }}
55
+ />
56
+ {config.mode === "image" && config.imageUrl ? (
57
+ <Img
58
+ src={config.imageUrl}
59
+ style={{
60
+ position: "absolute",
61
+ inset: 0,
62
+ width: "100%",
63
+ height: "100%",
64
+ objectFit: "cover",
65
+ opacity: config.imageOpacity ?? 1,
66
+ }}
67
+ />
68
+ ) : null}
69
+ {config.accentColor ? (
70
+ <div
71
+ style={{
72
+ position: "absolute",
73
+ inset: 0,
74
+ background: `radial-gradient(circle at 22% 18%, ${config.accentColor}, transparent 26%), radial-gradient(circle at 78% 82%, ${config.accentColor}, transparent 28%)`,
75
+ mixBlendMode: "multiply",
76
+ opacity: 0.72,
77
+ }}
78
+ />
79
+ ) : null}
80
+ </div>
81
+ );
82
+ };
83
+
84
+ export const BackgroundLayer = VideoBackgroundLayer;
@@ -0,0 +1,125 @@
1
+ import { useCurrentFrame, useVideoConfig } from "remotion";
2
+ import { getVideoLayerChapterState } from "./chapter-progress-state";
3
+ import { buildVideoLayerDisplaySurfaceStyle } from "./display-surface";
4
+ import type {
5
+ VideoLayerChapterProgressItem,
6
+ VideoLayerDesign,
7
+ } from "./video-layer-types";
8
+
9
+ export type VideoChapterProgressForegroundProps = {
10
+ design: VideoLayerDesign;
11
+ chapters: VideoLayerChapterProgressItem[];
12
+ };
13
+
14
+ export const VideoChapterProgressForeground = ({
15
+ design,
16
+ chapters,
17
+ }: VideoChapterProgressForegroundProps) => {
18
+ const frame = useCurrentFrame();
19
+ const { fps, width, height } = useVideoConfig();
20
+ const isPortrait = height > width;
21
+
22
+ if (!design.display.chapterInfo.enabled || chapters.length === 0) {
23
+ return null;
24
+ }
25
+
26
+ const { activeChapterIndex, activeChapterProgress } = getVideoLayerChapterState(
27
+ frame,
28
+ chapters,
29
+ );
30
+ const chapterSurface = design.display.chapterInfo;
31
+
32
+ return (
33
+ <div style={buildVideoLayerDisplaySurfaceStyle(chapterSurface, frame, fps)}>
34
+ <div
35
+ style={{
36
+ display: "flex",
37
+ alignItems: "center",
38
+ gap: 0,
39
+ justifyContent: "stretch",
40
+ whiteSpace: "nowrap",
41
+ fontSize: isPortrait
42
+ ? Math.round(height * 0.013)
43
+ : chapterSurface.style.fontSize - 2,
44
+ fontWeight: 800,
45
+ lineHeight: 1,
46
+ width: "100%",
47
+ minHeight: isPortrait ? Math.round(height * 0.032) : 48,
48
+ boxShadow: isPortrait ? "0 18px 38px rgba(2, 6, 23, 0.32)" : undefined,
49
+ }}
50
+ >
51
+ {chapters.map((chapter, index) => {
52
+ const isActive = index === activeChapterIndex;
53
+ const isDone = index < activeChapterIndex;
54
+ const filled = Math.max(
55
+ 0,
56
+ Math.min(1, isDone ? 1 : isActive ? activeChapterProgress : 0),
57
+ );
58
+ const baseBackground =
59
+ chapterSurface.style.backgroundColor ?? "rgba(2, 6, 23, 0.72)";
60
+ const labelColor =
61
+ isActive || isDone
62
+ ? chapterSurface.style.foregroundColor ?? chapterSurface.style.color
63
+ : "rgba(255, 255, 255, 0.74)";
64
+ const progressBackground =
65
+ chapterSurface.style.foregroundGradient ??
66
+ (isDone
67
+ ? "linear-gradient(90deg, rgba(34, 197, 94, 0.9), rgba(59, 130, 246, 0.9))"
68
+ : "linear-gradient(90deg, rgba(96, 165, 250, 0.92), rgba(168, 85, 247, 0.9))");
69
+
70
+ return (
71
+ <div
72
+ key={chapter.title}
73
+ style={{
74
+ flex: 1,
75
+ position: "relative",
76
+ display: "flex",
77
+ alignItems: "center",
78
+ justifyContent: "center",
79
+ minWidth: 0,
80
+ minHeight: isPortrait ? Math.round(height * 0.032) : 48,
81
+ padding: 0,
82
+ borderRadius: 0,
83
+ overflow: "hidden",
84
+ background: baseBackground,
85
+ borderRight:
86
+ index < chapters.length - 1
87
+ ? "1px solid rgba(255, 255, 255, 0.08)"
88
+ : "none",
89
+ }}
90
+ >
91
+ <div
92
+ style={{
93
+ position: "absolute",
94
+ inset: 0,
95
+ width: `${filled * 100}%`,
96
+ background: progressBackground,
97
+ opacity: isDone ? 0.95 : filled > 0 ? 0.88 : 0.28,
98
+ }}
99
+ />
100
+ <span
101
+ style={{
102
+ position: "relative",
103
+ zIndex: 1,
104
+ display: "inline-flex",
105
+ alignItems: "center",
106
+ justifyContent: "center",
107
+ borderRadius: 0,
108
+ color: labelColor,
109
+ textShadow: isActive || isDone ? chapterSurface.style.shadow : "none",
110
+ padding: 0,
111
+ letterSpacing: isPortrait ? "0.03em" : "normal",
112
+ whiteSpace: "nowrap",
113
+ }}
114
+ >
115
+ {chapter.title}
116
+ </span>
117
+ </div>
118
+ );
119
+ })}
120
+ </div>
121
+ </div>
122
+ );
123
+ };
124
+
125
+ export const ChapterProgressForeground = VideoChapterProgressForeground;
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getVideoLayerChapterState } from "./chapter-progress-state";
3
+
4
+ const chapters = [
5
+ { title: "Intro", durationInFrames: 30 },
6
+ { title: "Middle", durationInFrames: 60 },
7
+ { title: "Outro", durationInFrames: 30 },
8
+ ];
9
+
10
+ describe("getVideoLayerChapterState", () => {
11
+ it("returns the active chapter and normalized progress", () => {
12
+ expect(getVideoLayerChapterState(0, chapters)).toEqual({
13
+ activeChapterIndex: 0,
14
+ activeChapterProgress: 0,
15
+ });
16
+ expect(getVideoLayerChapterState(45, chapters)).toEqual({
17
+ activeChapterIndex: 1,
18
+ activeChapterProgress: 0.25,
19
+ });
20
+ });
21
+
22
+ it("clamps progress on the last chapter", () => {
23
+ expect(getVideoLayerChapterState(999, chapters)).toEqual({
24
+ activeChapterIndex: 2,
25
+ activeChapterProgress: 1,
26
+ });
27
+ });
28
+ });
@@ -0,0 +1,33 @@
1
+ import type { VideoLayerChapterProgressItem } from "./video-layer-types";
2
+
3
+ export const getVideoLayerChapterState = (
4
+ frame: number,
5
+ chapters: VideoLayerChapterProgressItem[],
6
+ ) => {
7
+ let cursor = 0;
8
+
9
+ for (let index = 0; index < chapters.length; index += 1) {
10
+ const chapter = chapters[index];
11
+ const start = cursor;
12
+ const end = start + chapter.durationInFrames;
13
+
14
+ if (frame < end || index === chapters.length - 1) {
15
+ const progress = Math.max(
16
+ 0,
17
+ Math.min(1, (frame - start) / chapter.durationInFrames),
18
+ );
19
+
20
+ return {
21
+ activeChapterIndex: index,
22
+ activeChapterProgress: progress,
23
+ };
24
+ }
25
+
26
+ cursor = end;
27
+ }
28
+
29
+ return {
30
+ activeChapterIndex: 0,
31
+ activeChapterProgress: 0,
32
+ };
33
+ };
@@ -0,0 +1,21 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { resolveDisplayMotionStyle } from "./display-motion";
3
+
4
+ describe("resolveDisplayMotionStyle", () => {
5
+ it("returns no transform when motion is missing or static", () => {
6
+ expect(resolveDisplayMotionStyle(undefined, 10, 30)).toEqual({});
7
+ expect(
8
+ resolveDisplayMotionStyle({ enabled: true, strategy: "static" }, 10, 30),
9
+ ).toEqual({});
10
+ });
11
+
12
+ it("returns deterministic drift transforms", () => {
13
+ expect(
14
+ resolveDisplayMotionStyle(
15
+ { enabled: true, strategy: "drift", amplitudeX: 10, amplitudeY: 4, speed: 0.5 },
16
+ 15,
17
+ 30,
18
+ ),
19
+ ).toEqual({ transform: "translate3d(10.00px, 4.00px, 0)" });
20
+ });
21
+ });
@@ -0,0 +1,52 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { VideoLayerSurfaceMotionConfig } from "./video-layer-types";
3
+
4
+ const getMotionOffset = (
5
+ motion: VideoLayerSurfaceMotionConfig,
6
+ frame: number,
7
+ fps: number,
8
+ ) => {
9
+ if (!motion.enabled) {
10
+ return {};
11
+ }
12
+
13
+ const strategy = motion.strategy ?? "static";
14
+ if (strategy === "static") {
15
+ return {};
16
+ }
17
+
18
+ const speed = motion.speed ?? 1;
19
+ const phase = motion.phase ?? 0;
20
+ const amplitudeX = motion.amplitudeX ?? 0;
21
+ const amplitudeY = motion.amplitudeY ?? 0;
22
+ const t = frame / Math.max(1, fps);
23
+ const wave = Math.sin((t + phase) * speed * Math.PI * 2);
24
+
25
+ if (strategy === "drift") {
26
+ return {
27
+ transform: `translate3d(${(wave * amplitudeX).toFixed(2)}px, ${(wave * amplitudeY).toFixed(2)}px, 0)`,
28
+ } satisfies CSSProperties;
29
+ }
30
+
31
+ if (strategy === "float") {
32
+ return {
33
+ transform: `translate3d(0, ${(wave * amplitudeY).toFixed(2)}px, 0)`,
34
+ } satisfies CSSProperties;
35
+ }
36
+
37
+ return {
38
+ transform: `translate3d(${(wave * amplitudeX * 0.5).toFixed(2)}px, ${(wave * amplitudeY * 0.5).toFixed(2)}px, 0)`,
39
+ } satisfies CSSProperties;
40
+ };
41
+
42
+ export const resolveDisplayMotionStyle = (
43
+ motion: VideoLayerSurfaceMotionConfig | undefined,
44
+ frame: number,
45
+ fps: number,
46
+ ): CSSProperties => {
47
+ if (!motion) {
48
+ return {};
49
+ }
50
+
51
+ return getMotionOffset(motion, frame, fps);
52
+ };
@@ -0,0 +1,57 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { VideoLayerSurfaceConfig } from "./video-layer-types";
3
+ import { resolveDisplayMotionStyle } from "./display-motion";
4
+
5
+ const getJustifyContent = (align: VideoLayerSurfaceConfig["position"]["align"]) => {
6
+ if (align === "center") {
7
+ return "center";
8
+ }
9
+
10
+ if (align === "right") {
11
+ return "flex-end";
12
+ }
13
+
14
+ return "flex-start";
15
+ };
16
+
17
+ export const buildVideoLayerDisplaySurfaceStyle = (
18
+ surface: VideoLayerSurfaceConfig,
19
+ frame: number,
20
+ fps: number,
21
+ overrides: CSSProperties = {},
22
+ ): CSSProperties => {
23
+ const placement = surface.position.placement;
24
+ const motionStyle = resolveDisplayMotionStyle(surface.motion, frame, fps);
25
+ const placementTransform = placement === "middle" ? "translateY(-50%)" : undefined;
26
+ const transform = [placementTransform, motionStyle.transform]
27
+ .filter(Boolean)
28
+ .join(" ");
29
+
30
+ return {
31
+ position: "absolute",
32
+ left: 0,
33
+ right: 0,
34
+ top: placement === "bottom" ? undefined : placement === "middle" ? "50%" : 0,
35
+ bottom: placement === "bottom" ? 0 : undefined,
36
+ display: "flex",
37
+ justifyContent: getJustifyContent(surface.position.align),
38
+ textAlign: surface.position.align ?? "left",
39
+ width: "100%",
40
+ maxWidth: "none",
41
+ boxSizing: "border-box",
42
+ padding: surface.style.padding,
43
+ borderRadius:
44
+ placement === "top" || placement === "bottom" ? 0 : surface.style.borderRadius,
45
+ background: surface.style.backgroundColor ?? "transparent",
46
+ color: surface.style.color,
47
+ fontFamily: surface.style.fontFamily,
48
+ fontSize: surface.style.fontSize,
49
+ fontWeight: surface.style.fontWeight,
50
+ lineHeight: surface.style.lineHeight ?? 1.4,
51
+ textShadow: surface.style.shadow,
52
+ opacity: surface.style.opacity,
53
+ ...motionStyle,
54
+ transform: transform || undefined,
55
+ ...overrides,
56
+ };
57
+ };
@@ -0,0 +1,10 @@
1
+ export * from "./asset-source";
2
+ export * from "./background-audio-layer";
3
+ export * from "./background-layer";
4
+ export * from "./chapter-progress-foreground";
5
+ export * from "./chapter-progress-state";
6
+ export * from "./display-motion";
7
+ export * from "./display-surface";
8
+ export * from "./subtitle-style";
9
+ export * from "./video-layer-types";
10
+ export * from "./voice-captions-foreground";
@@ -0,0 +1,28 @@
1
+ import type { VideoLayerSurfaceConfig } from "./video-layer-types";
2
+
3
+ export type VideoLayerSubtitleViewport = {
4
+ width: number;
5
+ height: number;
6
+ };
7
+
8
+ export const resolveVideoLayerSubtitleStyle = (
9
+ surface: VideoLayerSurfaceConfig,
10
+ viewport: VideoLayerSubtitleViewport,
11
+ ): VideoLayerSurfaceConfig => {
12
+ const isPortrait = viewport.height > viewport.width;
13
+ const variant = isPortrait
14
+ ? surface.responsiveStyle?.portrait
15
+ : surface.responsiveStyle?.landscape;
16
+
17
+ if (!variant) {
18
+ return surface;
19
+ }
20
+
21
+ return {
22
+ ...surface,
23
+ style: {
24
+ ...surface.style,
25
+ ...variant,
26
+ },
27
+ };
28
+ };