remotion-claude-agent-demo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +160 -0
  2. package/apps/web/README.md +36 -0
  3. package/apps/web/env.example +20 -0
  4. package/apps/web/eslint.config.mjs +18 -0
  5. package/apps/web/next.config.ts +7 -0
  6. package/apps/web/package-lock.json +10348 -0
  7. package/apps/web/package.json +35 -0
  8. package/apps/web/postcss.config.mjs +7 -0
  9. package/apps/web/public/file.svg +1 -0
  10. package/apps/web/public/globe.svg +1 -0
  11. package/apps/web/public/next.svg +1 -0
  12. package/apps/web/public/vercel.svg +1 -0
  13. package/apps/web/public/window.svg +1 -0
  14. package/apps/web/src/app/.well-known/agent-card.json/route.ts +50 -0
  15. package/apps/web/src/app/background-tasks/[jobId]/cancel/route.ts +29 -0
  16. package/apps/web/src/app/events/stream/route.ts +58 -0
  17. package/apps/web/src/app/favicon.ico +0 -0
  18. package/apps/web/src/app/globals.css +174 -0
  19. package/apps/web/src/app/layout.tsx +34 -0
  20. package/apps/web/src/app/messages/answer/route.ts +57 -0
  21. package/apps/web/src/app/messages/stream/route.ts +381 -0
  22. package/apps/web/src/app/page.tsx +358 -0
  23. package/apps/web/src/app/tasks/[taskId]/cancel/route.ts +24 -0
  24. package/apps/web/src/app/tasks/[taskId]/route.ts +24 -0
  25. package/apps/web/src/app/tasks/route.ts +13 -0
  26. package/apps/web/src/components/chat/agent-blocks.tsx +111 -0
  27. package/apps/web/src/components/chat/ask-user-question-panel.tsx +172 -0
  28. package/apps/web/src/components/chat/session-sidebar.tsx +222 -0
  29. package/apps/web/src/components/chat/subagent-activity-sidebar.tsx +248 -0
  30. package/apps/web/src/components/chat/tool-blocks.tsx +550 -0
  31. package/apps/web/src/lib/a2a/activity-store.ts +150 -0
  32. package/apps/web/src/lib/a2a/client.ts +357 -0
  33. package/apps/web/src/lib/a2a/sse.ts +19 -0
  34. package/apps/web/src/lib/a2a/task-store.ts +111 -0
  35. package/apps/web/src/lib/a2a/types.ts +216 -0
  36. package/apps/web/src/lib/agent/answer-store.ts +109 -0
  37. package/apps/web/src/lib/agent/background-delivery.ts +343 -0
  38. package/apps/web/src/lib/agent/background-tool.ts +78 -0
  39. package/apps/web/src/lib/agent/background.ts +452 -0
  40. package/apps/web/src/lib/agent/chat.ts +543 -0
  41. package/apps/web/src/lib/agent/session-store.ts +26 -0
  42. package/apps/web/src/lib/chat/types.ts +44 -0
  43. package/apps/web/src/lib/env.ts +31 -0
  44. package/apps/web/src/lib/hooks/useA2AChat.ts +863 -0
  45. package/apps/web/src/lib/state/chat-atoms.ts +52 -0
  46. package/apps/web/src/lib/workspace.ts +9 -0
  47. package/apps/web/tsconfig.json +35 -0
  48. package/bin/remotion-agent.js +451 -0
  49. package/package.json +34 -0
  50. package/templates/.claude/CLAUDE.md +95 -0
  51. package/templates/.claude/README.md +129 -0
  52. package/templates/.claude/agents/composer-agent.md +188 -0
  53. package/templates/.claude/agents/crafter.md +181 -0
  54. package/templates/.claude/agents/creator.md +134 -0
  55. package/templates/.claude/agents/perceiver.md +92 -0
  56. package/templates/.claude/settings.json +36 -0
  57. package/templates/.claude/settings.local.json +39 -0
  58. package/templates/.claude/skills/agent-browser/SKILL.md +349 -0
  59. package/templates/.claude/skills/agent-browser/references/authentication.md +188 -0
  60. package/templates/.claude/skills/agent-browser/references/proxy-support.md +175 -0
  61. package/templates/.claude/skills/agent-browser/references/session-management.md +181 -0
  62. package/templates/.claude/skills/agent-browser/references/snapshot-refs.md +186 -0
  63. package/templates/.claude/skills/agent-browser/references/video-recording.md +162 -0
  64. package/templates/.claude/skills/agent-browser/templates/authenticated-session.sh +91 -0
  65. package/templates/.claude/skills/agent-browser/templates/capture-workflow.sh +68 -0
  66. package/templates/.claude/skills/agent-browser/templates/form-automation.sh +64 -0
  67. package/templates/.claude/skills/algorithmic-art/LICENSE.txt +202 -0
  68. package/templates/.claude/skills/algorithmic-art/SKILL.md +405 -0
  69. package/templates/.claude/skills/algorithmic-art/templates/generator_template.js +223 -0
  70. package/templates/.claude/skills/algorithmic-art/templates/viewer.html +599 -0
  71. package/templates/.claude/skills/asset-validator/SKILL.md +376 -0
  72. package/templates/.claude/skills/audio-video-sync/SKILL.md +219 -0
  73. package/templates/.claude/skills/bgm-manager/SKILL.md +334 -0
  74. package/templates/.claude/skills/remotion-best-practices/SKILL.md +45 -0
  75. package/templates/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
  76. package/templates/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
  77. package/templates/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  78. package/templates/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  79. package/templates/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
  80. package/templates/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
  81. package/templates/.claude/skills/remotion-best-practices/rules/audio.md +172 -0
  82. package/templates/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
  83. package/templates/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
  84. package/templates/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
  85. package/templates/.claude/skills/remotion-best-practices/rules/compositions.md +141 -0
  86. package/templates/.claude/skills/remotion-best-practices/rules/display-captions.md +126 -0
  87. package/templates/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  88. package/templates/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
  89. package/templates/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  90. package/templates/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  91. package/templates/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
  92. package/templates/.claude/skills/remotion-best-practices/rules/gifs.md +138 -0
  93. package/templates/.claude/skills/remotion-best-practices/rules/images.md +130 -0
  94. package/templates/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
  95. package/templates/.claude/skills/remotion-best-practices/rules/lottie.md +68 -0
  96. package/templates/.claude/skills/remotion-best-practices/rules/maps.md +403 -0
  97. package/templates/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
  98. package/templates/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
  99. package/templates/.claude/skills/remotion-best-practices/rules/parameters.md +98 -0
  100. package/templates/.claude/skills/remotion-best-practices/rules/sequencing.md +118 -0
  101. package/templates/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
  102. package/templates/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
  103. package/templates/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
  104. package/templates/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
  105. package/templates/.claude/skills/remotion-best-practices/rules/transitions.md +122 -0
  106. package/templates/.claude/skills/remotion-best-practices/rules/trimming.md +53 -0
  107. package/templates/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
  108. package/templates/.claude/skills/remotion-components/SKILL.md +453 -0
  109. package/templates/.claude/skills/render-config/SKILL.md +290 -0
  110. package/templates/.claude/skills/script-writer/SKILL.md +59 -0
  111. package/templates/.claude/skills/style-director/script-writer/SKILL.md +82 -0
  112. package/templates/.claude/skills/style-director/style-director/SKILL.md +287 -0
  113. package/templates/.claude/skills/style-director/style-director/references/audience-and-scenarios.md +43 -0
  114. package/templates/.claude/skills/style-director/style-director/references/interaction-innovation.md +26 -0
  115. package/templates/.claude/skills/style-director/style-director/references/motion-grammar.md +66 -0
  116. package/templates/.claude/skills/style-director/style-director/references/quality-checklist.md +29 -0
  117. package/templates/.claude/skills/style-director/style-director/references/scene-recipes.md +38 -0
  118. package/templates/.claude/skills/style-director/style-director/references/visual-style-system.md +148 -0
  119. package/templates/.claude/skills/subtitle-composer/SKILL.md +304 -0
  120. package/templates/.claude/skills/subtitle-processor/SKILL.md +308 -0
  121. package/templates/.claude/skills/timeline-generator/SKILL.md +253 -0
  122. package/templates/.claude/skills/video-preflight-check/SKILL.md +353 -0
  123. package/templates/.claude/skills/voice-synthesizer/SKILL.md +296 -0
  124. package/templates/.claude/skills/voice-synthesizer/scripts/synthesize_voice.py +315 -0
  125. package/templates/.claude/skills/voice-synthesizer/scripts/tts_cli.py +142 -0
  126. package/templates/.claude/skills/web-design-guidelines/SKILL.md +36 -0
  127. package/templates/.claude/skills/youtube-downloader/SKILL.md +99 -0
  128. package/templates/.claude/skills/youtube-downloader/scripts/download_video.py +145 -0
@@ -0,0 +1,403 @@
1
+ ---
2
+ name: maps
3
+ description: Make map animations with Mapbox
4
+ metadata:
5
+ tags: map, map animation, mapbox
6
+ ---
7
+
8
+ Maps can be added to a Remotion video with Mapbox.
9
+ The [Mapbox documentation](https://docs.mapbox.com/mapbox-gl-js/api/) has the API reference.
10
+
11
+ ## Prerequisites
12
+
13
+ Mapbox and `@turf/turf` need to be installed.
14
+
15
+ Search the project for lockfiles and run the correct command depending on the package manager:
16
+
17
+ If `package-lock.json` is found, use the following command:
18
+
19
+ ```bash
20
+ npm i mapbox-gl @turf/turf @types/mapbox-gl
21
+ ```
22
+
23
+ If `bun.lock` is found, use the following command:
24
+
25
+ ```bash
26
+ bun i mapbox-gl @turf/turf @types/mapbox-gl
27
+ ```
28
+
29
+ If `yarn.lock` is found, use the following command:
30
+
31
+ ```bash
32
+ yarn add mapbox-gl @turf/turf @types/mapbox-gl
33
+ ```
34
+
35
+ If `pnpm-lock.yaml` is found, use the following command:
36
+
37
+ ```bash
38
+ pnpm i mapbox-gl @turf/turf @types/mapbox-gl
39
+ ```
40
+
41
+ The user needs to create a free Mapbox account and create an access token by visiting https://console.mapbox.com/account/access-tokens/.
42
+
43
+ The mapbox token needs to be added to the `.env` file:
44
+
45
+ ```txt title=".env"
46
+ REMOTION_MAPBOX_TOKEN==pk.your-mapbox-access-token
47
+ ```
48
+
49
+ ## Adding a map
50
+
51
+ Here is a basic example of a map in Remotion.
52
+
53
+ ```tsx
54
+ import {useEffect, useMemo, useRef, useState} from 'react';
55
+ import {AbsoluteFill, useDelayRender, useVideoConfig} from 'remotion';
56
+ import mapboxgl, {Map} from 'mapbox-gl';
57
+
58
+ export const lineCoordinates = [
59
+ [6.56158447265625, 46.059891147620725],
60
+ [6.5691375732421875, 46.05679376154153],
61
+ [6.5842437744140625, 46.05059898938315],
62
+ [6.594886779785156, 46.04702502069337],
63
+ [6.601066589355469, 46.0460718554722],
64
+ [6.6089630126953125, 46.0365370783104],
65
+ [6.6185760498046875, 46.018420689207964],
66
+ ];
67
+
68
+ mapboxgl.accessToken = process.env.REMOTION_MAPBOX_TOKEN as string;
69
+
70
+ export const MyComposition = () => {
71
+ const ref = useRef<HTMLDivElement>(null);
72
+ const {delayRender, continueRender} = useDelayRender();
73
+
74
+ const {width, height} = useVideoConfig();
75
+ const [handle] = useState(() => delayRender('Loading map...'));
76
+ const [map, setMap] = useState<Map | null>(null);
77
+
78
+ useEffect(() => {
79
+ const _map = new Map({
80
+ container: ref.current!,
81
+ zoom: 11.53,
82
+ center: [6.5615, 46.0598],
83
+ pitch: 65,
84
+ bearing: 0,
85
+ style: '⁠mapbox://styles/mapbox/standard',
86
+ interactive: false,
87
+ fadeDuration: 0,
88
+ });
89
+
90
+ _map.on('style.load', () => {
91
+ // Hide all features from the Mapbox Standard style
92
+ const hideFeatures = [
93
+ 'showRoadsAndTransit',
94
+ 'showRoads',
95
+ 'showTransit',
96
+ 'showPedestrianRoads',
97
+ 'showRoadLabels',
98
+ 'showTransitLabels',
99
+ 'showPlaceLabels',
100
+ 'showPointOfInterestLabels',
101
+ 'showPointsOfInterest',
102
+ 'showAdminBoundaries',
103
+ 'showLandmarkIcons',
104
+ 'showLandmarkIconLabels',
105
+ 'show3dObjects',
106
+ 'show3dBuildings',
107
+ 'show3dTrees',
108
+ 'show3dLandmarks',
109
+ 'show3dFacades',
110
+ ];
111
+ for (const feature of hideFeatures) {
112
+ _map.setConfigProperty('basemap', feature, false);
113
+ }
114
+
115
+ _map.setConfigProperty('basemap', 'colorMotorways', 'rgba(0, 0, 0, 0)');
116
+ _map.setConfigProperty('basemap', 'colorRoads', 'rgba(0, 0, 0, 0)');
117
+ _map.setConfigProperty('basemap', 'colorTrunks', 'rgba(0, 0, 0, 0)');
118
+
119
+ _map.addSource('trace', {
120
+ type: 'geojson',
121
+ data: {
122
+ type: 'Feature',
123
+ properties: {},
124
+ geometry: {
125
+ type: 'LineString',
126
+ coordinates: lineCoordinates,
127
+ },
128
+ },
129
+ });
130
+ _map.addLayer({
131
+ type: 'line',
132
+ source: 'trace',
133
+ id: 'line',
134
+ paint: {
135
+ 'line-color': 'black',
136
+ 'line-width': 5,
137
+ },
138
+ layout: {
139
+ 'line-cap': 'round',
140
+ 'line-join': 'round',
141
+ },
142
+ });
143
+ });
144
+
145
+ _map.on('load', () => {
146
+ continueRender(handle);
147
+ setMap(_map);
148
+ });
149
+ }, [handle, lineCoordinates]);
150
+
151
+ const style: React.CSSProperties = useMemo(() => ({width, height, position: 'absolute'}), [width, height]);
152
+
153
+ return <AbsoluteFill ref={ref} style={style} />;
154
+ };
155
+ ```
156
+
157
+ The following is important in Remotion:
158
+
159
+ - Animations must be driven by `useCurrentFrame()` and animations that Mapbox brings itself should be disabled. For example, the `fadeDuration` prop should be set to `0`, `interactive` should be set to `false`, etc.
160
+ - Loading the map should be delayed using `useDelayRender()` and the map should be set to `null` until it is loaded.
161
+ - The element containing the ref MUST have an explicit width and height and `position: "absolute"`.
162
+ - Do not add a `_map.remove();` cleanup function.
163
+
164
+ ## Drawing lines
165
+
166
+ Unless I request it, do not add a glow effect to the lines.
167
+ Unless I request it, do not add additional points to the lines.
168
+
169
+ ## Map style
170
+
171
+ By default, use the `mapbox://styles/mapbox/standard` style.
172
+ Hide the labels from the base map style.
173
+
174
+ Unless I request otherwise, remove all features from the Mapbox Standard style.
175
+
176
+ ```tsx
177
+ // Hide all features from the Mapbox Standard style
178
+ const hideFeatures = [
179
+ 'showRoadsAndTransit',
180
+ 'showRoads',
181
+ 'showTransit',
182
+ 'showPedestrianRoads',
183
+ 'showRoadLabels',
184
+ 'showTransitLabels',
185
+ 'showPlaceLabels',
186
+ 'showPointOfInterestLabels',
187
+ 'showPointsOfInterest',
188
+ 'showAdminBoundaries',
189
+ 'showLandmarkIcons',
190
+ 'showLandmarkIconLabels',
191
+ 'show3dObjects',
192
+ 'show3dBuildings',
193
+ 'show3dTrees',
194
+ 'show3dLandmarks',
195
+ 'show3dFacades',
196
+ ];
197
+ for (const feature of hideFeatures) {
198
+ _map.setConfigProperty('basemap', feature, false);
199
+ }
200
+
201
+ _map.setConfigProperty('basemap', 'colorMotorways', 'transparent');
202
+ _map.setConfigProperty('basemap', 'colorRoads', 'transparent');
203
+ _map.setConfigProperty('basemap', 'colorTrunks', 'transparent');
204
+ ```
205
+
206
+ ## Animating the camera
207
+
208
+ You can animate the camera along the line by adding a `useEffect` hook that updates the camera position based on the current frame.
209
+
210
+ Unless I ask for it, do not jump between camera angles.
211
+
212
+ ```tsx
213
+ import * as turf from '@turf/turf';
214
+ import {interpolate} from 'remotion';
215
+ import {Easing} from 'remotion';
216
+ import {useCurrentFrame, useVideoConfig, useDelayRender} from 'remotion';
217
+
218
+ const animationDuration = 20;
219
+ const cameraAltitude = 4000;
220
+ ```
221
+
222
+ ```tsx
223
+ const frame = useCurrentFrame();
224
+ const {fps} = useVideoConfig();
225
+ const {delayRender, continueRender} = useDelayRender();
226
+
227
+ useEffect(() => {
228
+ if (!map) {
229
+ return;
230
+ }
231
+ const handle = delayRender('Moving point...');
232
+
233
+ const routeDistance = turf.length(turf.lineString(lineCoordinates));
234
+
235
+ const progress = interpolate(frame / fps, [0.00001, animationDuration], [0, 1], {
236
+ easing: Easing.inOut(Easing.sin),
237
+ extrapolateLeft: 'clamp',
238
+ extrapolateRight: 'clamp',
239
+ });
240
+
241
+ const camera = map.getFreeCameraOptions();
242
+
243
+ const alongRoute = turf.along(turf.lineString(lineCoordinates), routeDistance * progress).geometry.coordinates;
244
+
245
+ camera.lookAtPoint({
246
+ lng: alongRoute[0],
247
+ lat: alongRoute[1],
248
+ });
249
+
250
+ map.setFreeCameraOptions(camera);
251
+ map.once('idle', () => continueRender(handle));
252
+ }, [lineCoordinates, fps, frame, handle, map]);
253
+ ```
254
+
255
+ Notes:
256
+
257
+ IMPORTANT: Keep the camera by default so north is up.
258
+ IMPORTANT: For multi-step animations, set all properties at all stages (zoom, position, line progress) to prevent jumps. Override initial values.
259
+
260
+ - The progress is clamped to a minimum value to avoid the line being empty, which can lead to turf errors
261
+ - See [Timing](./timing.md) for more options for timing.
262
+ - Consider the dimensions of the composition and make the lines thick enough and the label font size large enough to be legible for when the composition is scaled down.
263
+
264
+ ## Animating lines
265
+
266
+ ### Straight lines (linear interpolation)
267
+
268
+ To animate a line that appears straight on the map, use linear interpolation between coordinates. Do NOT use turf's `lineSliceAlong` or `along` functions, as they use geodesic (great circle) calculations which appear curved on a Mercator projection.
269
+
270
+ ```tsx
271
+ const frame = useCurrentFrame();
272
+ const {durationInFrames} = useVideoConfig();
273
+
274
+ useEffect(() => {
275
+ if (!map) return;
276
+
277
+ const animationHandle = delayRender('Animating line...');
278
+
279
+ const progress = interpolate(frame, [0, durationInFrames - 1], [0, 1], {
280
+ extrapolateLeft: 'clamp',
281
+ extrapolateRight: 'clamp',
282
+ easing: Easing.inOut(Easing.cubic),
283
+ });
284
+
285
+ // Linear interpolation for a straight line on the map
286
+ const start = lineCoordinates[0];
287
+ const end = lineCoordinates[1];
288
+ const currentLng = start[0] + (end[0] - start[0]) * progress;
289
+ const currentLat = start[1] + (end[1] - start[1]) * progress;
290
+
291
+ const lineData: GeoJSON.Feature<GeoJSON.LineString> = {
292
+ type: 'Feature',
293
+ properties: {},
294
+ geometry: {
295
+ type: 'LineString',
296
+ coordinates: [start, [currentLng, currentLat]],
297
+ },
298
+ };
299
+
300
+ const source = map.getSource('trace') as mapboxgl.GeoJSONSource;
301
+ if (source) {
302
+ source.setData(lineData);
303
+ }
304
+
305
+ map.once('idle', () => continueRender(animationHandle));
306
+ }, [frame, map, durationInFrames]);
307
+ ```
308
+
309
+ ### Curved lines (geodesic/great circle)
310
+
311
+ To animate a line that follows the geodesic (great circle) path between two points, use turf's `lineSliceAlong`. This is useful for showing flight paths or the actual shortest distance on Earth.
312
+
313
+ ```tsx
314
+ import * as turf from '@turf/turf';
315
+
316
+ const routeLine = turf.lineString(lineCoordinates);
317
+ const routeDistance = turf.length(routeLine);
318
+
319
+ const currentDistance = Math.max(0.001, routeDistance * progress);
320
+ const slicedLine = turf.lineSliceAlong(routeLine, 0, currentDistance);
321
+
322
+ const source = map.getSource('route') as mapboxgl.GeoJSONSource;
323
+ if (source) {
324
+ source.setData(slicedLine);
325
+ }
326
+ ```
327
+
328
+ ## Markers
329
+
330
+ Add labels, and markers where appropriate.
331
+
332
+ ```tsx
333
+ _map.addSource('markers', {
334
+ type: 'geojson',
335
+ data: {
336
+ type: 'FeatureCollection',
337
+ features: [
338
+ {
339
+ type: 'Feature',
340
+ properties: {name: 'Point 1'},
341
+ geometry: {type: 'Point', coordinates: [-118.2437, 34.0522]},
342
+ },
343
+ ],
344
+ },
345
+ });
346
+
347
+ _map.addLayer({
348
+ id: 'city-markers',
349
+ type: 'circle',
350
+ source: 'markers',
351
+ paint: {
352
+ 'circle-radius': 40,
353
+ 'circle-color': '#FF4444',
354
+ 'circle-stroke-width': 4,
355
+ 'circle-stroke-color': '#FFFFFF',
356
+ },
357
+ });
358
+
359
+ _map.addLayer({
360
+ id: 'labels',
361
+ type: 'symbol',
362
+ source: 'markers',
363
+ layout: {
364
+ 'text-field': ['get', 'name'],
365
+ 'text-font': ['DIN Pro Bold', 'Arial Unicode MS Bold'],
366
+ 'text-size': 50,
367
+ 'text-offset': [0, 0.5],
368
+ 'text-anchor': 'top',
369
+ },
370
+ paint: {
371
+ 'text-color': '#FFFFFF',
372
+ 'text-halo-color': '#000000',
373
+ 'text-halo-width': 2,
374
+ },
375
+ });
376
+ ```
377
+
378
+ Make sure they are big enough. Check the composition dimensions and scale the labels accordingly.
379
+ For a composition size of 1920x1080, the label font size should be at least 40px.
380
+
381
+ IMPORTANT: Keep the `text-offset` small enough so it is close to the marker. Consider the marker circle radius. For a circle radius of 40, this is a good offset:
382
+
383
+ ```tsx
384
+ "text-offset": [0, 0.5],
385
+ ```
386
+
387
+ ## 3D buildings
388
+
389
+ To enable 3D buildings, use the following code:
390
+
391
+ ```tsx
392
+ _map.setConfigProperty('basemap', 'show3dObjects', true);
393
+ _map.setConfigProperty('basemap', 'show3dLandmarks', true);
394
+ _map.setConfigProperty('basemap', 'show3dBuildings', true);
395
+ ```
396
+
397
+ ## Rendering
398
+
399
+ When rendering a map animation, make sure to render with the following flags:
400
+
401
+ ```
402
+ npx remotion render --gl=angle --concurrency=1
403
+ ```
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: measuring-dom-nodes
3
+ description: Measuring DOM element dimensions in Remotion
4
+ metadata:
5
+ tags: measure, layout, dimensions, getBoundingClientRect, scale
6
+ ---
7
+
8
+ # Measuring DOM nodes in Remotion
9
+
10
+ Remotion applies a `scale()` transform to the video container, which affects values from `getBoundingClientRect()`. Use `useCurrentScale()` to get correct measurements.
11
+
12
+ ## Measuring element dimensions
13
+
14
+ ```tsx
15
+ import { useCurrentScale } from "remotion";
16
+ import { useRef, useEffect, useState } from "react";
17
+
18
+ export const MyComponent = () => {
19
+ const ref = useRef<HTMLDivElement>(null);
20
+ const scale = useCurrentScale();
21
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
22
+
23
+ useEffect(() => {
24
+ if (!ref.current) return;
25
+ const rect = ref.current.getBoundingClientRect();
26
+ setDimensions({
27
+ width: rect.width / scale,
28
+ height: rect.height / scale,
29
+ });
30
+ }, [scale]);
31
+
32
+ return <div ref={ref}>Content to measure</div>;
33
+ };
34
+ ```
35
+
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: measuring-text
3
+ description: Measuring text dimensions, fitting text to containers, and checking overflow
4
+ metadata:
5
+ tags: measure, text, layout, dimensions, fitText, fillTextBox
6
+ ---
7
+
8
+ # Measuring text in Remotion
9
+
10
+ ## Prerequisites
11
+
12
+ Install @remotion/layout-utils if it is not already installed:
13
+
14
+ ```bash
15
+ npx remotion add @remotion/layout-utils # If project uses npm
16
+ bunx remotion add @remotion/layout-utils # If project uses bun
17
+ yarn remotion add @remotion/layout-utils # If project uses yarn
18
+ pnpm exec remotion add @remotion/layout-utils # If project uses pnpm
19
+ ```
20
+
21
+ ## Measuring text dimensions
22
+
23
+ Use `measureText()` to calculate the width and height of text:
24
+
25
+ ```tsx
26
+ import { measureText } from "@remotion/layout-utils";
27
+
28
+ const { width, height } = measureText({
29
+ text: "Hello World",
30
+ fontFamily: "Arial",
31
+ fontSize: 32,
32
+ fontWeight: "bold",
33
+ });
34
+ ```
35
+
36
+ Results are cached - duplicate calls return the cached result.
37
+
38
+ ## Fitting text to a width
39
+
40
+ Use `fitText()` to find the optimal font size for a container:
41
+
42
+ ```tsx
43
+ import { fitText } from "@remotion/layout-utils";
44
+
45
+ const { fontSize } = fitText({
46
+ text: "Hello World",
47
+ withinWidth: 600,
48
+ fontFamily: "Inter",
49
+ fontWeight: "bold",
50
+ });
51
+
52
+ return (
53
+ <div
54
+ style={{
55
+ fontSize: Math.min(fontSize, 80), // Cap at 80px
56
+ fontFamily: "Inter",
57
+ fontWeight: "bold",
58
+ }}
59
+ >
60
+ Hello World
61
+ </div>
62
+ );
63
+ ```
64
+
65
+ ## Checking text overflow
66
+
67
+ Use `fillTextBox()` to check if text exceeds a box:
68
+
69
+ ```tsx
70
+ import { fillTextBox } from "@remotion/layout-utils";
71
+
72
+ const box = fillTextBox({ maxBoxWidth: 400, maxLines: 3 });
73
+
74
+ const words = ["Hello", "World", "This", "is", "a", "test"];
75
+ for (const word of words) {
76
+ const { exceedsBox } = box.add({
77
+ text: word + " ",
78
+ fontFamily: "Arial",
79
+ fontSize: 24,
80
+ });
81
+ if (exceedsBox) {
82
+ // Text would overflow, handle accordingly
83
+ break;
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Best practices
89
+
90
+ **Load fonts first:** Only call measurement functions after fonts are loaded.
91
+
92
+ ```tsx
93
+ import { loadFont } from "@remotion/google-fonts/Inter";
94
+
95
+ const { fontFamily, waitUntilDone } = loadFont("normal", {
96
+ weights: ["400"],
97
+ subsets: ["latin"],
98
+ });
99
+
100
+ waitUntilDone().then(() => {
101
+ // Now safe to measure
102
+ const { width } = measureText({
103
+ text: "Hello",
104
+ fontFamily,
105
+ fontSize: 32,
106
+ });
107
+ })
108
+ ```
109
+
110
+ **Use validateFontIsLoaded:** Catch font loading issues early:
111
+
112
+ ```tsx
113
+ measureText({
114
+ text: "Hello",
115
+ fontFamily: "MyCustomFont",
116
+ fontSize: 32,
117
+ validateFontIsLoaded: true, // Throws if font not loaded
118
+ });
119
+ ```
120
+
121
+ **Match font properties:** Use the same properties for measurement and rendering:
122
+
123
+ ```tsx
124
+ const fontStyle = {
125
+ fontFamily: "Inter",
126
+ fontSize: 32,
127
+ fontWeight: "bold" as const,
128
+ letterSpacing: "0.5px",
129
+ };
130
+
131
+ const { width } = measureText({
132
+ text: "Hello",
133
+ ...fontStyle,
134
+ });
135
+
136
+ return <div style={fontStyle}>Hello</div>;
137
+ ```
138
+
139
+ **Avoid padding and border:** Use `outline` instead of `border` to prevent layout differences:
140
+
141
+ ```tsx
142
+ <div style={{ outline: "2px solid red" }}>Text</div>
143
+ ```
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: parameters
3
+ description: Make a video parametrizable by adding a Zod schema
4
+ metadata:
5
+ tags: parameters, zod, schema
6
+ ---
7
+
8
+ To make a video parametrizable, a Zod schema can be added to a composition.
9
+
10
+ First, `zod` must be installed - it must be exactly version `3.22.3`.
11
+
12
+ Search the project for lockfiles and run the correct command depending on the package manager:
13
+
14
+ If `package-lock.json` is found, use the following command:
15
+
16
+ ```bash
17
+ npm i zod@3.22.3
18
+ ```
19
+
20
+ If `bun.lockb` is found, use the following command:
21
+
22
+ ```bash
23
+ bun i zod@3.22.3
24
+ ```
25
+
26
+ If `yarn.lock` is found, use the following command:
27
+
28
+ ```bash
29
+ yarn add zod@3.22.3
30
+ ```
31
+
32
+ If `pnpm-lock.yaml` is found, use the following command:
33
+
34
+ ```bash
35
+ pnpm i zod@3.22.3
36
+ ```
37
+
38
+ Then, a Zod schema can be defined alongside the component:
39
+
40
+ ```tsx title="src/MyComposition.tsx"
41
+ import {z} from 'zod';
42
+
43
+ export const MyCompositionSchema = z.object({
44
+ title: z.string(),
45
+ });
46
+
47
+ const MyComponent: React.FC<z.infer<typeof MyCompositionSchema>> = () => {
48
+ return (
49
+ <div>
50
+ <h1>{props.title}</h1>
51
+ </div>
52
+ );
53
+ };
54
+ ```
55
+
56
+ In the root file, the schema can be passed to the composition:
57
+
58
+ ```tsx title="src/Root.tsx"
59
+ import {Composition} from 'remotion';
60
+ import {MycComponent, MyCompositionSchema} from './MyComposition';
61
+
62
+ export const RemotionRoot = () => {
63
+ return <Composition id="MyComposition" component={MyComponent} durationInFrames={100} fps={30} width={1080} height={1080} defaultProps={{title: 'Hello World'}} schema={MyCompositionSchema} />;
64
+ };
65
+ ```
66
+
67
+ Now, the user can edit the parameter visually in the sidebar.
68
+
69
+ All schemas that are supported by Zod are supported by Remotion.
70
+
71
+ Remotion requires that the top-level type is a z.object(), because the collection of props of a React component is always an object.
72
+
73
+ ## Color picker
74
+
75
+ For adding a color picker, use `zColor()` from `@remotion/zod-types`.
76
+
77
+ If it is not installed, use the following command:
78
+
79
+ ```bash
80
+ npx remotion add @remotion/zod-types # If project uses npm
81
+ bunx remotion add @remotion/zod-types # If project uses bun
82
+ yarn remotion add @remotion/zod-types # If project uses yarn
83
+ pnpm exec remotion add @remotion/zod-types # If project uses pnpm
84
+ ```
85
+
86
+ Then import `zColor` from `@remotion/zod-types`:
87
+
88
+ ```tsx
89
+ import {zColor} from '@remotion/zod-types';
90
+ ```
91
+
92
+ Then use it in the schema:
93
+
94
+ ```tsx
95
+ export const MyCompositionSchema = z.object({
96
+ color: zColor(),
97
+ });
98
+ ```