baldart 3.6.2
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/CHANGELOG.md +599 -0
- package/README.md +566 -0
- package/VERSION +1 -0
- package/bin/baldart.js +143 -0
- package/framework/.claude/agents/REGISTRY.md +169 -0
- package/framework/.claude/agents/api-perf-cost-auditor.md +291 -0
- package/framework/.claude/agents/code-reviewer.md +350 -0
- package/framework/.claude/agents/codebase-architect.md +391 -0
- package/framework/.claude/agents/coder.md +291 -0
- package/framework/.claude/agents/deep-human-insight.md +198 -0
- package/framework/.claude/agents/doc-reviewer.md +440 -0
- package/framework/.claude/agents/email-deliverability-architect.md +193 -0
- package/framework/.claude/agents/hybrid-ml-architect.md +285 -0
- package/framework/.claude/agents/hyper-gamification-designer.md +149 -0
- package/framework/.claude/agents/legal-counsel-gdpr.md +179 -0
- package/framework/.claude/agents/marketing-conversion-strategist.md +162 -0
- package/framework/.claude/agents/motion-expert.md +108 -0
- package/framework/.claude/agents/onboarding-architect-lead.md +230 -0
- package/framework/.claude/agents/plan-auditor.md +546 -0
- package/framework/.claude/agents/prd-card-writer.md +372 -0
- package/framework/.claude/agents/prd.md +744 -0
- package/framework/.claude/agents/qa-sentinel.md +305 -0
- package/framework/.claude/agents/remotion-animator-orchestrator.md +218 -0
- package/framework/.claude/agents/security-reviewer.md +276 -0
- package/framework/.claude/agents/senior-researcher.md +175 -0
- package/framework/.claude/agents/seo-analytics-strategist.md +156 -0
- package/framework/.claude/agents/skill-improver.md +61 -0
- package/framework/.claude/agents/ui-expert.md +191 -0
- package/framework/.claude/agents/visual-designer.md +190 -0
- package/framework/.claude/agents/website-orchestrator.md +118 -0
- package/framework/.claude/agents/wiki-curator.md +145 -0
- package/framework/.claude/commands/baldart-push.md +15 -0
- package/framework/.claude/commands/check.md +237 -0
- package/framework/.claude/commands/codexreview.md +203 -0
- package/framework/.claude/commands/design-review.md +11 -0
- package/framework/.claude/commands/issue-review.md +34 -0
- package/framework/.claude/commands/new.md +331 -0
- package/framework/.claude/commands/qa.md +257 -0
- package/framework/.claude/hooks/framework-edit-gate.js +208 -0
- package/framework/.claude/hooks/lint-before-commit.sh.template +66 -0
- package/framework/.claude/settings.local.json.example +32 -0
- package/framework/.claude/skills/api-design-principles/SKILL.md +567 -0
- package/framework/.claude/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/framework/.claude/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/framework/.claude/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/framework/.claude/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/framework/.claude/skills/baldart-push/SKILL.md +222 -0
- package/framework/.claude/skills/bug/SKILL.md +200 -0
- package/framework/.claude/skills/bug/references/logging-patterns.md +174 -0
- package/framework/.claude/skills/capture/SKILL.md +125 -0
- package/framework/.claude/skills/capture/references/synthesis-template.md +42 -0
- package/framework/.claude/skills/context-primer/SKILL.md +189 -0
- package/framework/.claude/skills/copywriting/SKILL.md +273 -0
- package/framework/.claude/skills/copywriting/references/copy-frameworks.md +338 -0
- package/framework/.claude/skills/copywriting/references/natural-transitions.md +252 -0
- package/framework/.claude/skills/doc-writing-for-rag/SKILL.md +119 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/before-after-examples.md +291 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/compact-templates.md +183 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/frontmatter-minimal.md +112 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/line-count-targets.md +110 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/schemas-and-errors.md +129 -0
- package/framework/.claude/skills/find-skills/SKILL.md +133 -0
- package/framework/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/framework/.claude/skills/frontend-design/SKILL.md +84 -0
- package/framework/.claude/skills/gamification-design/SKILL.md +130 -0
- package/framework/.claude/skills/issue-review/SKILL.md +45 -0
- package/framework/.claude/skills/kie-ai/SKILL.md +262 -0
- package/framework/.claude/skills/kie-ai/references/models-catalog.md +272 -0
- package/framework/.claude/skills/kie-ai/scripts/kie_api.sh +209 -0
- package/framework/.claude/skills/kie-ai/scripts/remove_greenscreen.py +69 -0
- package/framework/.claude/skills/kie-ai/scripts/setup_api_key.sh +77 -0
- package/framework/.claude/skills/motion-design/LICENSE +21 -0
- package/framework/.claude/skills/motion-design/README.md +82 -0
- package/framework/.claude/skills/motion-design/SKILL.md +336 -0
- package/framework/.claude/skills/motion-design/director/choreography.md +93 -0
- package/framework/.claude/skills/motion-design/director/context-adaptation.md +83 -0
- package/framework/.claude/skills/motion-design/director/core-philosophy.md +53 -0
- package/framework/.claude/skills/motion-design/director/decision-framework.md +91 -0
- package/framework/.claude/skills/motion-design/director/disney-principles.md +102 -0
- package/framework/.claude/skills/motion-design/director/emotion-mapping.md +71 -0
- package/framework/.claude/skills/motion-design/director/motion-personality.md +89 -0
- package/framework/.claude/skills/motion-design/director/narrative-structure.md +62 -0
- package/framework/.claude/skills/motion-design/patterns/ambient-continuous.md +81 -0
- package/framework/.claude/skills/motion-design/patterns/entrance-exit.md +82 -0
- package/framework/.claude/skills/motion-design/patterns/multi-element.md +69 -0
- package/framework/.claude/skills/motion-design/patterns/state-feedback.md +96 -0
- package/framework/.claude/skills/motion-design/reference/property-selection.md +95 -0
- package/framework/.claude/skills/motion-design/reference/quality-checklist.md +67 -0
- package/framework/.claude/skills/motion-design/reference/timing-easing-tables.md +106 -0
- package/framework/.claude/skills/motion-design/reference/troubleshooting.md +73 -0
- package/framework/.claude/skills/new/SKILL.md +1687 -0
- package/framework/.claude/skills/playwright-skill/API_REFERENCE.md +652 -0
- package/framework/.claude/skills/playwright-skill/SKILL.md +157 -0
- package/framework/.claude/skills/playwright-skill/package.json +26 -0
- package/framework/.claude/skills/prd/SKILL.md +228 -0
- package/framework/.claude/skills/prd/assets/card-template.yml +232 -0
- package/framework/.claude/skills/prd/assets/epic-template.yml +190 -0
- package/framework/.claude/skills/prd/assets/prd-template.md +230 -0
- package/framework/.claude/skills/prd/assets/state-template.md +78 -0
- package/framework/.claude/skills/prd/references/api-perf-gate.md +152 -0
- package/framework/.claude/skills/prd/references/audit-phase.md +478 -0
- package/framework/.claude/skills/prd/references/backlog-phase.md +145 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +359 -0
- package/framework/.claude/skills/prd/references/impact-analysis.md +233 -0
- package/framework/.claude/skills/prd/references/prd-add-phase.md +214 -0
- package/framework/.claude/skills/prd/references/prd-writing-phase.md +145 -0
- package/framework/.claude/skills/prd/references/research-phase.md +216 -0
- package/framework/.claude/skills/prd/references/ui-design-phase.md +61 -0
- package/framework/.claude/skills/prd/references/validation-phase.md +72 -0
- package/framework/.claude/skills/prd-add/SKILL.md +222 -0
- package/framework/.claude/skills/prd-add/references/impact-analysis.md +233 -0
- package/framework/.claude/skills/remotion-best-practices/SKILL.md +48 -0
- package/framework/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
- package/framework/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
- package/framework/.claude/skills/remotion-best-practices/rules/audio.md +169 -0
- package/framework/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/framework/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/framework/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/compositions.md +141 -0
- package/framework/.claude/skills/remotion-best-practices/rules/display-captions.md +184 -0
- package/framework/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/framework/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/gifs.md +141 -0
- package/framework/.claude/skills/remotion-best-practices/rules/images.md +130 -0
- package/framework/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
- package/framework/.claude/skills/remotion-best-practices/rules/light-leaks.md +73 -0
- package/framework/.claude/skills/remotion-best-practices/rules/lottie.md +67 -0
- package/framework/.claude/skills/remotion-best-practices/rules/maps.md +401 -0
- package/framework/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
- package/framework/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/framework/.claude/skills/remotion-best-practices/rules/parameters.md +98 -0
- package/framework/.claude/skills/remotion-best-practices/rules/sequencing.md +118 -0
- package/framework/.claude/skills/remotion-best-practices/rules/subtitles.md +36 -0
- package/framework/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/framework/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/framework/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transitions.md +197 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
- package/framework/.claude/skills/remotion-best-practices/rules/trimming.md +52 -0
- package/framework/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
- package/framework/.claude/skills/seo-audit/SKILL.md +394 -0
- package/framework/.claude/skills/seo-audit/references/aeo-geo-patterns.md +279 -0
- package/framework/.claude/skills/seo-audit/references/ai-writing-detection.md +190 -0
- package/framework/.claude/skills/simplify/SKILL.md +137 -0
- package/framework/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/framework/.claude/skills/skill-creator/SKILL.md +356 -0
- package/framework/.claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/framework/.claude/skills/skill-creator/references/workflows.md +28 -0
- package/framework/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/framework/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/framework/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/framework/.claude/skills/ui-design/SKILL.md +199 -0
- package/framework/.claude/skills/ui-design/references/component-discovery.md +54 -0
- package/framework/.claude/skills/ui-design/references/evaluation.md +171 -0
- package/framework/.claude/skills/ui-design/references/generation.md +109 -0
- package/framework/.claude/skills/ui-design/references/inventory.md +59 -0
- package/framework/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/framework/.claude/skills/webapp-testing/SKILL.md +123 -0
- package/framework/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/framework/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/framework/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/framework/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/framework/.claude/skills/worktree-manager/SKILL.md +680 -0
- package/framework/AGENTS.md +240 -0
- package/framework/agents/api-contracts.md +137 -0
- package/framework/agents/architecture.md +145 -0
- package/framework/agents/coding-standards.md +148 -0
- package/framework/agents/data-model.md +110 -0
- package/framework/agents/deployment-protocol.md +232 -0
- package/framework/agents/design-review.md +172 -0
- package/framework/agents/env-reference.md +171 -0
- package/framework/agents/github-issue-subagent.md +252 -0
- package/framework/agents/index.md +261 -0
- package/framework/agents/llm-wiki-methodology.md +216 -0
- package/framework/agents/maintenance-protocol.md +305 -0
- package/framework/agents/observability.md +162 -0
- package/framework/agents/performance.md +155 -0
- package/framework/agents/project-context.md +145 -0
- package/framework/agents/runbook.md +208 -0
- package/framework/agents/security.md +168 -0
- package/framework/agents/skills-mapping.md +286 -0
- package/framework/agents/testing.md +111 -0
- package/framework/agents/workflows.md +215 -0
- package/framework/docs/PROJECT-CONFIGURATION.md +336 -0
- package/framework/docs/references/brand-guidelines.md +170 -0
- package/framework/docs/references/ui-guidelines.template.md +182 -0
- package/framework/routines/code-review.routine.yml +46 -0
- package/framework/routines/doc-review.routine.yml +45 -0
- package/framework/routines/ds-drift.routine.yml +52 -0
- package/framework/routines/full-sweep.routine.yml +51 -0
- package/framework/routines/index.yml +70 -0
- package/framework/routines/skill-improve.routine.yml +50 -0
- package/framework/routines/wiki-review.routine.yml +45 -0
- package/framework/templates/baldart.config.template.yml +113 -0
- package/framework/templates/breaking-change-checklist.md +484 -0
- package/framework/templates/feature-card.template.yml +125 -0
- package/framework/templates/overlays/README.md +44 -0
- package/framework/templates/overlays/copywriting.fidelity-example.md +62 -0
- package/framework/templates/overlays/ui-design.fidelity-example.md +75 -0
- package/framework/templates/skill-project-context.snippet.md +19 -0
- package/framework/templates/spec.template.md +208 -0
- package/package.json +51 -0
- package/src/commands/add.js +229 -0
- package/src/commands/configure.js +385 -0
- package/src/commands/doctor.js +486 -0
- package/src/commands/migrate.js +185 -0
- package/src/commands/push.js +0 -0
- package/src/commands/routines.js +269 -0
- package/src/commands/status.js +130 -0
- package/src/commands/update.js +419 -0
- package/src/commands/version.js +88 -0
- package/src/utils/contamination.js +400 -0
- package/src/utils/git.js +181 -0
- package/src/utils/hooks.js +152 -0
- package/src/utils/routine-adapters/claude-code-cloud.js +78 -0
- package/src/utils/routine-adapters/cron.js +138 -0
- package/src/utils/routine-adapters/github-actions.js +141 -0
- package/src/utils/routine-adapters/index.js +21 -0
- package/src/utils/routines.js +166 -0
- package/src/utils/state.js +143 -0
- package/src/utils/symlinks.js +425 -0
- package/src/utils/ui.js +133 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calculate-metadata
|
|
3
|
+
description: Dynamically set composition duration, dimensions, and props
|
|
4
|
+
metadata:
|
|
5
|
+
tags: calculateMetadata, duration, dimensions, props, dynamic
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Using calculateMetadata
|
|
9
|
+
|
|
10
|
+
Use `calculateMetadata` on a `<Composition>` to dynamically set duration, dimensions, and transform props before rendering.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
<Composition id="MyComp" component={MyComponent} durationInFrames={300} fps={30} width={1920} height={1080} defaultProps={{videoSrc: 'https://remotion.media/video.mp4'}} calculateMetadata={calculateMetadata} />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setting duration based on a video
|
|
17
|
+
|
|
18
|
+
Use the `getMediaMetadata()` function from the mediabunny/metadata skill to get the video duration:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import {CalculateMetadataFunction} from 'remotion';
|
|
22
|
+
import {getMediaMetadata} from '../get-media-metadata';
|
|
23
|
+
|
|
24
|
+
const calculateMetadata: CalculateMetadataFunction<Props> = async ({props}) => {
|
|
25
|
+
const {durationInSeconds} = await getMediaMetadata(props.videoSrc);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
durationInFrames: Math.ceil(durationInSeconds * 30),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Matching dimensions of a video
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
const calculateMetadata: CalculateMetadataFunction<Props> = async ({props}) => {
|
|
37
|
+
const {durationInSeconds, dimensions} = await getMediaMetadata(props.videoSrc);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
durationInFrames: Math.ceil(durationInSeconds * 30),
|
|
41
|
+
width: dimensions?.width ?? 1920,
|
|
42
|
+
height: dimensions?.height ?? 1080,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Setting duration based on multiple videos
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
const calculateMetadata: CalculateMetadataFunction<Props> = async ({props}) => {
|
|
51
|
+
const metadataPromises = props.videos.map((video) => getMediaMetadata(video.src));
|
|
52
|
+
const allMetadata = await Promise.all(metadataPromises);
|
|
53
|
+
|
|
54
|
+
const totalDuration = allMetadata.reduce((sum, meta) => sum + meta.durationInSeconds, 0);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
durationInFrames: Math.ceil(totalDuration * 30),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Setting a default outName
|
|
63
|
+
|
|
64
|
+
Set the default output filename based on props:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
const calculateMetadata: CalculateMetadataFunction<Props> = async ({props}) => {
|
|
68
|
+
return {
|
|
69
|
+
defaultOutName: `video-${props.id}.mp4`,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Transforming props
|
|
75
|
+
|
|
76
|
+
Fetch data or transform props before rendering:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const calculateMetadata: CalculateMetadataFunction<Props> = async ({props, abortSignal}) => {
|
|
80
|
+
const response = await fetch(props.dataUrl, {signal: abortSignal});
|
|
81
|
+
const data = await response.json();
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
props: {
|
|
85
|
+
...props,
|
|
86
|
+
fetchedData: data,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The `abortSignal` cancels stale requests when props change in the Studio.
|
|
93
|
+
|
|
94
|
+
## Return value
|
|
95
|
+
|
|
96
|
+
All fields are optional. Returned values override the `<Composition>` props:
|
|
97
|
+
|
|
98
|
+
- `durationInFrames`: Number of frames
|
|
99
|
+
- `width`: Composition width in pixels
|
|
100
|
+
- `height`: Composition height in pixels
|
|
101
|
+
- `fps`: Frames per second
|
|
102
|
+
- `props`: Transformed props passed to the component
|
|
103
|
+
- `defaultOutName`: Default output filename
|
|
104
|
+
- `defaultCodec`: Default codec for rendering
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: can-decode
|
|
3
|
+
description: Check if a video can be decoded by the browser using Mediabunny
|
|
4
|
+
metadata:
|
|
5
|
+
tags: decode, validation, video, audio, compatibility, browser
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Checking if a video can be decoded
|
|
9
|
+
|
|
10
|
+
Use Mediabunny to check if a video can be decoded by the browser before attempting to play it.
|
|
11
|
+
|
|
12
|
+
## The `canDecode()` function
|
|
13
|
+
|
|
14
|
+
This function can be copy-pasted into any project.
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Input, ALL_FORMATS, UrlSource } from "mediabunny";
|
|
18
|
+
|
|
19
|
+
export const canDecode = async (src: string) => {
|
|
20
|
+
const input = new Input({
|
|
21
|
+
formats: ALL_FORMATS,
|
|
22
|
+
source: new UrlSource(src, {
|
|
23
|
+
getRetryDelay: () => null,
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await input.getFormat();
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const videoTrack = await input.getPrimaryVideoTrack();
|
|
34
|
+
if (videoTrack && !(await videoTrack.canDecode())) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const audioTrack = await input.getPrimaryAudioTrack();
|
|
39
|
+
if (audioTrack && !(await audioTrack.canDecode())) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
const src = "https://remotion.media/video.mp4";
|
|
51
|
+
const isDecodable = await canDecode(src);
|
|
52
|
+
|
|
53
|
+
if (isDecodable) {
|
|
54
|
+
console.log("Video can be decoded");
|
|
55
|
+
} else {
|
|
56
|
+
console.log("Video cannot be decoded by this browser");
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Using with Blob
|
|
61
|
+
|
|
62
|
+
For file uploads or drag-and-drop, use `BlobSource`:
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { Input, ALL_FORMATS, BlobSource } from "mediabunny";
|
|
66
|
+
|
|
67
|
+
export const canDecodeBlob = async (blob: Blob) => {
|
|
68
|
+
const input = new Input({
|
|
69
|
+
formats: ALL_FORMATS,
|
|
70
|
+
source: new BlobSource(blob),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Same validation logic as above
|
|
74
|
+
};
|
|
75
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: charts
|
|
3
|
+
description: Chart and data visualization patterns for Remotion. Use when creating bar charts, pie charts, histograms, progress bars, or any data-driven animations.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: charts, data, visualization, bar-chart, pie-chart, graphs
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Charts in Remotion
|
|
9
|
+
|
|
10
|
+
You can create bar charts in Remotion by using regular React code - HTML and SVG is allowed, as well as D3.js.
|
|
11
|
+
|
|
12
|
+
## No animations not powered by `useCurrentFrame()`
|
|
13
|
+
|
|
14
|
+
Disable all animations by third party libraries.
|
|
15
|
+
They will cause flickering during rendering.
|
|
16
|
+
Instead, drive all animations from `useCurrentFrame()`.
|
|
17
|
+
|
|
18
|
+
## Bar Chart Animations
|
|
19
|
+
|
|
20
|
+
See [Bar Chart Example](assets/charts/bar-chart.tsx) for a basic example implmentation.
|
|
21
|
+
|
|
22
|
+
### Staggered Bars
|
|
23
|
+
|
|
24
|
+
You can animate the height of the bars and stagger them like this:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
const STAGGER_DELAY = 5;
|
|
28
|
+
const frame = useCurrentFrame();
|
|
29
|
+
const {fps} = useVideoConfig();
|
|
30
|
+
|
|
31
|
+
const bars = data.map((item, i) => {
|
|
32
|
+
const delay = i * STAGGER_DELAY;
|
|
33
|
+
const height = spring({
|
|
34
|
+
frame,
|
|
35
|
+
fps,
|
|
36
|
+
delay,
|
|
37
|
+
config: {damping: 200},
|
|
38
|
+
});
|
|
39
|
+
return <div style={{height: height * item.value}} />;
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Pie Chart Animation
|
|
44
|
+
|
|
45
|
+
Animate segments using stroke-dashoffset, starting from 12 o'clock.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const frame = useCurrentFrame();
|
|
49
|
+
const {fps} = useVideoConfig();
|
|
50
|
+
|
|
51
|
+
const progress = interpolate(frame, [0, 100], [0, 1]);
|
|
52
|
+
|
|
53
|
+
const circumference = 2 * Math.PI * radius;
|
|
54
|
+
const segmentLength = (value / total) * circumference;
|
|
55
|
+
const offset = interpolate(progress, [0, 1], [segmentLength, 0]);
|
|
56
|
+
|
|
57
|
+
<circle r={radius} cx={center} cy={center} fill="none" stroke={color} strokeWidth={strokeWidth} strokeDasharray={`${segmentLength} ${circumference}`} strokeDashoffset={offset} transform={`rotate(-90 ${center} ${center})`} />;
|
|
58
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions
|
|
3
|
+
description: Defining compositions, stills, folders, default props and dynamic metadata
|
|
4
|
+
metadata:
|
|
5
|
+
tags: composition, still, folder, props, metadata
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A `<Composition>` defines the component, width, height, fps and duration of a renderable video.
|
|
9
|
+
|
|
10
|
+
It normally is placed in the `src/Root.tsx` file.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import {Composition} from 'remotion';
|
|
14
|
+
import {MyComposition} from './MyComposition';
|
|
15
|
+
|
|
16
|
+
export const RemotionRoot = () => {
|
|
17
|
+
return <Composition id="MyComposition" component={MyComposition} durationInFrames={100} fps={30} width={1080} height={1080} />;
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Default Props
|
|
22
|
+
|
|
23
|
+
Pass `defaultProps` to provide initial values for your component.
|
|
24
|
+
Values must be JSON-serializable (`Date`, `Map`, `Set`, and `staticFile()` are supported).
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {Composition} from 'remotion';
|
|
28
|
+
import {MyComposition, MyCompositionProps} from './MyComposition';
|
|
29
|
+
|
|
30
|
+
export const RemotionRoot = () => {
|
|
31
|
+
return (
|
|
32
|
+
<Composition
|
|
33
|
+
id="MyComposition"
|
|
34
|
+
component={MyComposition}
|
|
35
|
+
durationInFrames={100}
|
|
36
|
+
fps={30}
|
|
37
|
+
width={1080}
|
|
38
|
+
height={1080}
|
|
39
|
+
defaultProps={
|
|
40
|
+
{
|
|
41
|
+
title: 'Hello World',
|
|
42
|
+
color: '#ff0000',
|
|
43
|
+
} satisfies MyCompositionProps
|
|
44
|
+
}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `type` declarations for props rather than `interface` to ensure `defaultProps` type safety.
|
|
51
|
+
|
|
52
|
+
## Folders
|
|
53
|
+
|
|
54
|
+
Use `<Folder>` to organize compositions in the sidebar.
|
|
55
|
+
Folder names can only contain letters, numbers, and hyphens.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import {Composition, Folder} from 'remotion';
|
|
59
|
+
|
|
60
|
+
export const RemotionRoot = () => {
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<Folder name="Marketing">
|
|
64
|
+
<Composition id="Promo" /* ... */ />
|
|
65
|
+
<Composition id="Ad" /* ... */ />
|
|
66
|
+
</Folder>
|
|
67
|
+
<Folder name="Social">
|
|
68
|
+
<Folder name="Instagram">
|
|
69
|
+
<Composition id="Story" /* ... */ />
|
|
70
|
+
<Composition id="Reel" /* ... */ />
|
|
71
|
+
</Folder>
|
|
72
|
+
</Folder>
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Stills
|
|
79
|
+
|
|
80
|
+
Use `<Still>` for single-frame images. It does not require `durationInFrames` or `fps`.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import {Still} from 'remotion';
|
|
84
|
+
import {Thumbnail} from './Thumbnail';
|
|
85
|
+
|
|
86
|
+
export const RemotionRoot = () => {
|
|
87
|
+
return <Still id="Thumbnail" component={Thumbnail} width={1280} height={720} />;
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Calculate Metadata
|
|
92
|
+
|
|
93
|
+
Use `calculateMetadata` to make dimensions, duration, or props dynamic based on data.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import {Composition, CalculateMetadataFunction} from 'remotion';
|
|
97
|
+
import {MyComposition, MyCompositionProps} from './MyComposition';
|
|
98
|
+
|
|
99
|
+
const calculateMetadata: CalculateMetadataFunction<MyCompositionProps> = async ({props, abortSignal}) => {
|
|
100
|
+
const data = await fetch(`https://api.example.com/video/${props.videoId}`, {
|
|
101
|
+
signal: abortSignal,
|
|
102
|
+
}).then((res) => res.json());
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
durationInFrames: Math.ceil(data.duration * 30),
|
|
106
|
+
props: {
|
|
107
|
+
...props,
|
|
108
|
+
videoUrl: data.url,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const RemotionRoot = () => {
|
|
114
|
+
return (
|
|
115
|
+
<Composition
|
|
116
|
+
id="MyComposition"
|
|
117
|
+
component={MyComposition}
|
|
118
|
+
durationInFrames={100} // Placeholder, will be overridden
|
|
119
|
+
fps={30}
|
|
120
|
+
width={1080}
|
|
121
|
+
height={1080}
|
|
122
|
+
defaultProps={{videoId: 'abc123'}}
|
|
123
|
+
calculateMetadata={calculateMetadata}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The function can return `props`, `durationInFrames`, `width`, `height`, `fps`, and codec-related defaults. It runs once before rendering begins.
|
|
130
|
+
|
|
131
|
+
## Nesting compositions within another
|
|
132
|
+
|
|
133
|
+
To add a composition within another composition, you can use the `<Sequence>` component with a `width` and `height` prop to specify the size of the composition.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<AbsoluteFill>
|
|
137
|
+
<Sequence width={COMPOSITION_WIDTH} height={COMPOSITION_HEIGHT}>
|
|
138
|
+
<CompositionComponent />
|
|
139
|
+
</Sequence>
|
|
140
|
+
</AbsoluteFill>
|
|
141
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: display-captions
|
|
3
|
+
description: Displaying captions in Remotion with TikTok-style pages and word highlighting
|
|
4
|
+
metadata:
|
|
5
|
+
tags: captions, subtitles, display, tiktok, highlight
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Displaying captions in Remotion
|
|
9
|
+
|
|
10
|
+
This guide explains how to display captions in Remotion, assuming you already have captions in the [`Caption`](https://www.remotion.dev/docs/captions/caption) format.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
Read [Transcribing audio](transcribe-captions.md) for how to generate captions.
|
|
15
|
+
|
|
16
|
+
First, the [`@remotion/captions`](https://www.remotion.dev/docs/captions) package needs to be installed.
|
|
17
|
+
If it is not installed, use the following command:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx remotion add @remotion/captions
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Fetching captions
|
|
24
|
+
|
|
25
|
+
First, fetch your captions JSON file. Use [`useDelayRender()`](https://www.remotion.dev/docs/use-delay-render) to hold the render until the captions are loaded:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { useState, useEffect, useCallback } from "react";
|
|
29
|
+
import { AbsoluteFill, staticFile, useDelayRender } from "remotion";
|
|
30
|
+
import type { Caption } from "@remotion/captions";
|
|
31
|
+
|
|
32
|
+
export const MyComponent: React.FC = () => {
|
|
33
|
+
const [captions, setCaptions] = useState<Caption[] | null>(null);
|
|
34
|
+
const { delayRender, continueRender, cancelRender } = useDelayRender();
|
|
35
|
+
const [handle] = useState(() => delayRender());
|
|
36
|
+
|
|
37
|
+
const fetchCaptions = useCallback(async () => {
|
|
38
|
+
try {
|
|
39
|
+
// Assuming captions.json is in the public/ folder.
|
|
40
|
+
const response = await fetch(staticFile("captions123.json"));
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
setCaptions(data);
|
|
43
|
+
continueRender(handle);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
cancelRender(e);
|
|
46
|
+
}
|
|
47
|
+
}, [continueRender, cancelRender, handle]);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
fetchCaptions();
|
|
51
|
+
}, [fetchCaptions]);
|
|
52
|
+
|
|
53
|
+
if (!captions) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return <AbsoluteFill>{/* Render captions here */}</AbsoluteFill>;
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Creating pages
|
|
62
|
+
|
|
63
|
+
Use `createTikTokStyleCaptions()` to group captions into pages. The `combineTokensWithinMilliseconds` option controls how many words appear at once:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useMemo } from "react";
|
|
67
|
+
import { createTikTokStyleCaptions } from "@remotion/captions";
|
|
68
|
+
import type { Caption } from "@remotion/captions";
|
|
69
|
+
|
|
70
|
+
// How often captions should switch (in milliseconds)
|
|
71
|
+
// Higher values = more words per page
|
|
72
|
+
// Lower values = fewer words (more word-by-word)
|
|
73
|
+
const SWITCH_CAPTIONS_EVERY_MS = 1200;
|
|
74
|
+
|
|
75
|
+
const { pages } = useMemo(() => {
|
|
76
|
+
return createTikTokStyleCaptions({
|
|
77
|
+
captions,
|
|
78
|
+
combineTokensWithinMilliseconds: SWITCH_CAPTIONS_EVERY_MS,
|
|
79
|
+
});
|
|
80
|
+
}, [captions]);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Rendering with Sequences
|
|
84
|
+
|
|
85
|
+
Map over the pages and render each one in a `<Sequence>`. Calculate the start frame and duration from the page timing:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { Sequence, useVideoConfig, AbsoluteFill } from "remotion";
|
|
89
|
+
import type { TikTokPage } from "@remotion/captions";
|
|
90
|
+
|
|
91
|
+
const CaptionedContent: React.FC = () => {
|
|
92
|
+
const { fps } = useVideoConfig();
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<AbsoluteFill>
|
|
96
|
+
{pages.map((page, index) => {
|
|
97
|
+
const nextPage = pages[index + 1] ?? null;
|
|
98
|
+
const startFrame = (page.startMs / 1000) * fps;
|
|
99
|
+
const endFrame = Math.min(
|
|
100
|
+
nextPage ? (nextPage.startMs / 1000) * fps : Infinity,
|
|
101
|
+
startFrame + (SWITCH_CAPTIONS_EVERY_MS / 1000) * fps,
|
|
102
|
+
);
|
|
103
|
+
const durationInFrames = endFrame - startFrame;
|
|
104
|
+
|
|
105
|
+
if (durationInFrames <= 0) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Sequence
|
|
111
|
+
key={index}
|
|
112
|
+
from={startFrame}
|
|
113
|
+
durationInFrames={durationInFrames}
|
|
114
|
+
>
|
|
115
|
+
<CaptionPage page={page} />
|
|
116
|
+
</Sequence>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</AbsoluteFill>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## White-space preservation
|
|
125
|
+
|
|
126
|
+
The captions are whitespace sensitive. You should include spaces in the `text` field before each word. Use `whiteSpace: "pre"` to preserve the whitespace in the captions.
|
|
127
|
+
|
|
128
|
+
## Separate component for captions
|
|
129
|
+
|
|
130
|
+
Put captioning logic in a separate component.
|
|
131
|
+
Make a new file for it.
|
|
132
|
+
|
|
133
|
+
## Word highlighting
|
|
134
|
+
|
|
135
|
+
A caption page contains `tokens` which you can use to highlight the currently spoken word:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
|
|
139
|
+
import type { TikTokPage } from "@remotion/captions";
|
|
140
|
+
|
|
141
|
+
const HIGHLIGHT_COLOR = "#39E508";
|
|
142
|
+
|
|
143
|
+
const CaptionPage: React.FC<{ page: TikTokPage }> = ({ page }) => {
|
|
144
|
+
const frame = useCurrentFrame();
|
|
145
|
+
const { fps } = useVideoConfig();
|
|
146
|
+
|
|
147
|
+
// Current time relative to the start of the sequence
|
|
148
|
+
const currentTimeMs = (frame / fps) * 1000;
|
|
149
|
+
// Convert to absolute time by adding the page start
|
|
150
|
+
const absoluteTimeMs = page.startMs + currentTimeMs;
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
|
|
154
|
+
<div style={{ fontSize: 80, fontWeight: "bold", whiteSpace: "pre" }}>
|
|
155
|
+
{page.tokens.map((token) => {
|
|
156
|
+
const isActive =
|
|
157
|
+
token.fromMs <= absoluteTimeMs && token.toMs > absoluteTimeMs;
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<span
|
|
161
|
+
key={token.fromMs}
|
|
162
|
+
style={{ color: isActive ? HIGHLIGHT_COLOR : "white" }}
|
|
163
|
+
>
|
|
164
|
+
{token.text}
|
|
165
|
+
</span>
|
|
166
|
+
);
|
|
167
|
+
})}
|
|
168
|
+
</div>
|
|
169
|
+
</AbsoluteFill>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Display captions alongside video content
|
|
175
|
+
|
|
176
|
+
By default, put the captions alongside the video content, so the captions are in sync.
|
|
177
|
+
For each video, make a new captions JSON file.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<AbsoluteFill>
|
|
181
|
+
<Video src={staticFile("video.mp4")} />
|
|
182
|
+
<CaptionPage page={page} />
|
|
183
|
+
</AbsoluteFill>
|
|
184
|
+
```
|