@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.
- package/package.json +9 -5
- package/src/base/BaseRemotionScene.test.tsx +11 -1
- package/src/base/index.ts +1 -0
- package/src/base/utils/asset-source.ts +11 -0
- package/src/base/utils/background-audio-layer.tsx +90 -0
- package/src/base/utils/background-layer.tsx +84 -0
- package/src/base/utils/chapter-progress-foreground.tsx +125 -0
- package/src/base/utils/chapter-progress-state.test.ts +28 -0
- package/src/base/utils/chapter-progress-state.ts +33 -0
- package/src/base/utils/display-motion.test.ts +21 -0
- package/src/base/utils/display-motion.ts +52 -0
- package/src/base/utils/display-surface.ts +57 -0
- package/src/base/utils/index.ts +10 -0
- package/src/base/utils/subtitle-style.ts +28 -0
- package/src/base/utils/video-layer-types.test.ts +145 -0
- package/src/base/utils/video-layer-types.ts +220 -0
- package/src/base/utils/voice-captions-foreground.tsx +169 -0
- package/src/core/intro/index.ts +18 -18
- package/src/core/outro/index.ts +33 -33
- package/src/core/scene/2dscene/{default-2d-scene-Horizontal.stories.tsx → default-2d-scene/default-2d-scene-Horizontal.stories.tsx} +1 -1
- package/src/core/scene/2dscene/{default-2d-scene-Horizontal.tsx → default-2d-scene/default-2d-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/2dscene/{default-2d-scene-Landscape.tsx → default-2d-scene/default-2d-scene-Landscape.tsx} +1 -1
- package/src/core/scene/2dscene/{default-2d-scene.stories.tsx → default-2d-scene/default-2d-scene.stories.tsx} +1 -1
- package/src/core/scene/2dscene/{default-2d-scene.tsx → default-2d-scene/default-2d-scene.tsx} +4 -4
- package/src/core/scene/2dscene/index.ts +3 -3
- package/src/core/scene/3dscene/{default-3d-scene-Horizontal.stories.tsx → default-3d-scene/default-3d-scene-Horizontal.stories.tsx} +1 -1
- package/src/core/scene/3dscene/{default-3d-scene-Horizontal.tsx → default-3d-scene/default-3d-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/3dscene/{default-3d-scene-Landscape.tsx → default-3d-scene/default-3d-scene-Landscape.tsx} +1 -1
- package/src/core/scene/3dscene/{default-3d-scene.stories.tsx → default-3d-scene/default-3d-scene.stories.tsx} +1 -1
- package/src/core/scene/3dscene/{default-3d-scene.tsx → default-3d-scene/default-3d-scene.tsx} +4 -4
- package/src/core/scene/3dscene/index.ts +3 -3
- package/src/core/scene/html-in-canvas/browser/default-html-in-canvas-browser-scene-Horizontal.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/browser/default-html-in-canvas-browser-scene-Landscape.stories.tsx +80 -0
- package/src/core/scene/html-in-canvas/dashboard/default-html-in-canvas-dashboard-scene-Horizontal.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/dashboard/default-html-in-canvas-dashboard-scene-Landscape.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/dashboard-shell/default-dashboard-shell-html-in-canvas-scene.stories.tsx +40 -0
- package/src/core/scene/html-in-canvas/dashboard-shell/default-dashboard-shell-html-in-canvas-scene.tsx +44 -0
- package/src/core/scene/html-in-canvas/dashboard-shell/index.ts +8 -0
- package/src/core/scene/html-in-canvas/docs/default-html-in-canvas-docs-scene-Horizontal.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/docs/default-html-in-canvas-docs-scene-Landscape.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/document/article/default-article-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/document/article/default-article-html-in-canvas-scene.tsx +50 -0
- package/src/core/scene/html-in-canvas/document/article/index.ts +8 -0
- package/src/core/scene/html-in-canvas/document/default-document-html-in-canvas-scene.stories.tsx +40 -0
- package/src/core/scene/html-in-canvas/document/default-document-html-in-canvas-scene.tsx +50 -0
- package/src/core/scene/html-in-canvas/document/index.ts +31 -0
- package/src/core/scene/html-in-canvas/document/report/default-report-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/document/report/default-report-html-in-canvas-scene.tsx +51 -0
- package/src/core/scene/html-in-canvas/document/report/index.ts +8 -0
- package/src/core/scene/html-in-canvas/document/whitepaper/default-whitepaper-html-in-canvas-scene.stories.tsx +41 -0
- package/src/core/scene/html-in-canvas/document/whitepaper/default-whitepaper-html-in-canvas-scene.tsx +50 -0
- package/src/core/scene/html-in-canvas/document/whitepaper/index.ts +8 -0
- package/src/core/scene/html-in-canvas/editor/default-html-in-canvas-editor-scene-Horizontal.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/editor/default-html-in-canvas-editor-scene-Landscape.stories.tsx +35 -0
- package/src/core/scene/html-in-canvas/full-bleed/default-full-bleed-html-in-canvas-scene.stories.tsx +40 -0
- package/src/core/scene/html-in-canvas/full-bleed/default-full-bleed-html-in-canvas-scene.tsx +38 -0
- package/src/core/scene/html-in-canvas/full-bleed/index.ts +8 -0
- package/src/core/scene/html-in-canvas/index.ts +6 -0
- package/src/core/scene/html-in-canvas/landing-page/default-landing-page-html-in-canvas-scene.stories.tsx +40 -0
- package/src/core/scene/html-in-canvas/landing-page/default-landing-page-html-in-canvas-scene.tsx +46 -0
- package/src/core/scene/html-in-canvas/landing-page/feature-grid/default-feature-grid-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/landing-page/feature-grid/default-feature-grid-html-in-canvas-scene.tsx +41 -0
- package/src/core/scene/html-in-canvas/landing-page/feature-grid/index.ts +8 -0
- package/src/core/scene/html-in-canvas/landing-page/hero/default-hero-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/landing-page/hero/default-hero-html-in-canvas-scene.tsx +46 -0
- package/src/core/scene/html-in-canvas/landing-page/hero/index.ts +8 -0
- package/src/core/scene/html-in-canvas/landing-page/index.ts +31 -0
- package/src/core/scene/html-in-canvas/landing-page/pricing/default-pricing-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/landing-page/pricing/default-pricing-html-in-canvas-scene.tsx +42 -0
- package/src/core/scene/html-in-canvas/landing-page/pricing/index.ts +8 -0
- package/src/core/scene/html-in-canvas/media-wall/collage/default-collage-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/media-wall/collage/default-collage-html-in-canvas-scene.tsx +42 -0
- package/src/core/scene/html-in-canvas/media-wall/collage/index.ts +8 -0
- package/src/core/scene/html-in-canvas/media-wall/default-media-wall-html-in-canvas-scene.stories.tsx +40 -0
- package/src/core/scene/html-in-canvas/media-wall/default-media-wall-html-in-canvas-scene.tsx +35 -0
- package/src/core/scene/html-in-canvas/media-wall/gallery/default-gallery-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/media-wall/gallery/default-gallery-html-in-canvas-scene.tsx +43 -0
- package/src/core/scene/html-in-canvas/media-wall/gallery/index.ts +8 -0
- package/src/core/scene/html-in-canvas/media-wall/grid/default-grid-html-in-canvas-scene.stories.tsx +38 -0
- package/src/core/scene/html-in-canvas/media-wall/grid/default-grid-html-in-canvas-scene.tsx +35 -0
- package/src/core/scene/html-in-canvas/media-wall/grid/index.ts +8 -0
- package/src/core/scene/html-in-canvas/media-wall/index.ts +31 -0
- package/src/core/scene/html-in-canvas/shared/default-html-in-canvas-scene.tsx +1315 -0
- package/src/core/scene/index.ts +1 -0
- 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
- package/src/core/scene/photo/3dvr-photo/{default-3dvr-photo-scene.tsx → default-3dvr-photo-scene/default-3dvr-photo-scene.tsx} +4 -4
- 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
- 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
- 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
- package/src/core/scene/photo/3dvr-photo/default-dolly-3dvr-photo-scene/default-dolly-3dvr-photo-scene.stories.tsx +38 -0
- package/src/core/scene/photo/3dvr-photo/default-dolly-3dvr-photo-scene/default-dolly-3dvr-photo-scene.tsx +36 -0
- package/src/core/scene/photo/3dvr-photo/default-orbit-3dvr-photo-scene/default-orbit-3dvr-photo-scene.stories.tsx +38 -0
- package/src/core/scene/photo/3dvr-photo/default-orbit-3dvr-photo-scene/default-orbit-3dvr-photo-scene.tsx +34 -0
- package/src/core/scene/photo/3dvr-photo/default-poster-3dvr-photo-scene/default-poster-3dvr-photo-scene.stories.tsx +38 -0
- package/src/core/scene/photo/3dvr-photo/default-poster-3dvr-photo-scene/default-poster-3dvr-photo-scene.tsx +39 -0
- package/src/core/scene/photo/3dvr-photo/index.ts +6 -3
- package/src/core/scene/photo/effects-photo/default-chrome-effects-photo-scene/default-chrome-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-chrome-effects-photo-scene/default-chrome-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-cool-tone-effects-photo-scene/default-cool-tone-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-cool-tone-effects-photo-scene/default-cool-tone-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-dream-effects-photo-scene/default-dream-effects-photo-scene.stories.tsx +23 -0
- package/src/core/scene/photo/effects-photo/default-dream-effects-photo-scene/default-dream-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-duotone-effects-photo-scene/default-duotone-effects-photo-scene.stories.tsx +25 -0
- package/src/core/scene/photo/effects-photo/default-duotone-effects-photo-scene/default-duotone-effects-photo-scene.tsx +41 -0
- 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
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene/default-effects-photo-scene.tsx +639 -0
- 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
- 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
- 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
- package/src/core/scene/photo/effects-photo/default-glow-effects-photo-scene/default-glow-effects-photo-scene.stories.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-glow-effects-photo-scene/default-glow-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-grain-effects-photo-scene/default-grain-effects-photo-scene.stories.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-grain-effects-photo-scene/default-grain-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-halation-effects-photo-scene/default-halation-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-halation-effects-photo-scene/default-halation-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-infrared-effects-photo-scene/default-infrared-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-infrared-effects-photo-scene/default-infrared-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-light-leak-effects-photo-scene/default-light-leak-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-light-leak-effects-photo-scene/default-light-leak-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-mono-effects-photo-scene/default-mono-effects-photo-scene.stories.tsx +23 -0
- package/src/core/scene/photo/effects-photo/default-mono-effects-photo-scene/default-mono-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-noir-effects-photo-scene/default-noir-effects-photo-scene.stories.tsx +23 -0
- package/src/core/scene/photo/effects-photo/default-noir-effects-photo-scene/default-noir-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-pastel-effects-photo-scene/default-pastel-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-pastel-effects-photo-scene/default-pastel-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-retro-effects-photo-scene/default-retro-effects-photo-scene.stories.tsx +24 -0
- package/src/core/scene/photo/effects-photo/default-retro-effects-photo-scene/default-retro-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-sepia-effects-photo-scene/default-sepia-effects-photo-scene.stories.tsx +23 -0
- package/src/core/scene/photo/effects-photo/default-sepia-effects-photo-scene/default-sepia-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-soft-focus-effects-photo-scene/default-soft-focus-effects-photo-scene.stories.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-soft-focus-effects-photo-scene/default-soft-focus-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-vibrant-effects-photo-scene/default-vibrant-effects-photo-scene.stories.tsx +23 -0
- package/src/core/scene/photo/effects-photo/default-vibrant-effects-photo-scene/default-vibrant-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-vignette-effects-photo-scene/default-vignette-effects-photo-scene.stories.tsx +39 -0
- package/src/core/scene/photo/effects-photo/default-vignette-effects-photo-scene/default-vignette-effects-photo-scene.tsx +37 -0
- package/src/core/scene/photo/effects-photo/default-warmth-effects-photo-scene/default-warmth-effects-photo-scene.stories.tsx +40 -0
- package/src/core/scene/photo/effects-photo/default-warmth-effects-photo-scene/default-warmth-effects-photo-scene.tsx +39 -0
- package/src/core/scene/photo/effects-photo/index.ts +21 -3
- package/src/core/scene/photo/index.ts +25 -9
- package/src/core/scene/photo/{default-live-photo-scene-Horizontal.stories.tsx → live-photo/default-live-photo-scene-Horizontal.stories.tsx} +1 -1
- package/src/core/scene/photo/{default-live-photo-scene-Horizontal.tsx → live-photo/default-live-photo-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/photo/{default-live-photo-scene-Landscape.tsx → live-photo/default-live-photo-scene-Landscape.tsx} +1 -1
- package/src/core/scene/photo/{default-live-photo-scene.stories.tsx → live-photo/default-live-photo-scene.stories.tsx} +1 -1
- package/src/core/scene/photo/{default-live-photo-scene.tsx → live-photo/default-live-photo-scene.tsx} +5 -3
- package/src/core/scene/photo/photo-storybook.test.tsx +2 -2
- package/src/core/scene/photo/{default-photo-strip-scene-Horizontal.tsx → photo-strip/default-photo-strip-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-strip-scene-Landscape.tsx → photo-strip/default-photo-strip-scene-Landscape.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-strip-scene.stories.tsx → photo-strip/default-photo-strip-scene.stories.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-strip-scene.tsx → photo-strip/default-photo-strip-scene.tsx} +2 -2
- package/src/core/scene/photo/{default-photo-wall-scene-Horizontal.tsx → photo-wall/default-photo-wall-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-wall-scene-Landscape.tsx → photo-wall/default-photo-wall-scene-Landscape.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-wall-scene.stories.tsx → photo-wall/default-photo-wall-scene.stories.tsx} +1 -1
- package/src/core/scene/photo/{default-photo-wall-scene.tsx → photo-wall/default-photo-wall-scene.tsx} +2 -2
- package/src/core/scene/slide/{default-slide-scene-Horizontal.animated.stories.tsx → default-slide-scene/default-slide-scene-Horizontal.animated.stories.tsx} +1 -1
- package/src/core/scene/slide/{default-slide-scene-Horizontal.tsx → default-slide-scene/default-slide-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/slide/{default-slide-scene-Landscape.tsx → default-slide-scene/default-slide-scene-Landscape.tsx} +1 -1
- package/src/core/scene/slide/{default-slide-scene.animated.stories.tsx → default-slide-scene/default-slide-scene.animated.stories.tsx} +1 -1
- package/src/core/scene/slide/{default-slide-scene.tsx → default-slide-scene/default-slide-scene.tsx} +5 -5
- package/src/core/scene/slide/index.ts +3 -3
- package/src/core/scene/video/{default-video-scene-Horizontal.stories.tsx → default-video-scene/default-video-scene-Horizontal.stories.tsx} +1 -1
- package/src/core/scene/video/{default-video-scene-Horizontal.tsx → default-video-scene/default-video-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/video/{default-video-scene-Landscape.tsx → default-video-scene/default-video-scene-Landscape.tsx} +1 -1
- package/src/core/scene/video/{default-video-scene.stories.tsx → default-video-scene/default-video-scene.stories.tsx} +1 -1
- package/src/core/scene/video/{default-video-scene.tsx → default-video-scene/default-video-scene.tsx} +3 -3
- package/src/core/scene/video/{default-video-split-scene-Horizontal.tsx → default-video-split-scene/default-video-split-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/video/{default-video-split-scene-Landscape.tsx → default-video-split-scene/default-video-split-scene-Landscape.tsx} +1 -1
- package/src/core/scene/video/{default-video-split-scene.stories.tsx → default-video-split-scene/default-video-split-scene.stories.tsx} +1 -1
- package/src/core/scene/video/{default-video-split-scene.tsx → default-video-split-scene/default-video-split-scene.tsx} +2 -2
- package/src/core/scene/video/{default-video-wall-scene-Horizontal.tsx → default-video-wall-scene/default-video-wall-scene-Horizontal.tsx} +1 -1
- package/src/core/scene/video/{default-video-wall-scene-Landscape.tsx → default-video-wall-scene/default-video-wall-scene-Landscape.tsx} +1 -1
- package/src/core/scene/video/{default-video-wall-scene.stories.tsx → default-video-wall-scene/default-video-wall-scene.stories.tsx} +1 -1
- package/src/core/scene/video/{default-video-wall-scene.tsx → default-video-wall-scene/default-video-wall-scene.tsx} +2 -2
- package/src/core/scene/video/index.ts +9 -9
- package/src/lint/index.ts +2 -0
- package/src/lint/scene-convention-lint.test.ts +86 -0
- package/src/lint/scene-convention-lint.ts +247 -0
- package/src/storybook/index.ts +19 -0
- package/src/storybook/remotion-preview.test.tsx +53 -0
- package/src/storybook/remotion-preview.tsx +85 -11
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene.tsx +0 -352
- /package/src/core/scene/slide/{default-slide-scene-Horizontal.stories.tsx → default-slide-scene/default-slide-scene-Horizontal.stories.tsx} +0 -0
- /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.
|
|
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.
|
|
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
|
@@ -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
|
+
};
|