@evatril/video-templates 2.0.3 → 2.0.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evatril/video-templates",
3
- "version": "2.0.3",
3
+ "version": "2.0.6",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "remotion": {
@@ -9,17 +9,17 @@ import {
9
9
  staticFile
10
10
  } from "remotion";
11
11
 
12
- import { Butterflies20012026 } from "../Elements/Butterflies20012026.jsx";
12
+ import { Butterflies20012026 } from "../Elements/Butterflies20012026.jsx";
13
13
  import { OpeningGate20012026 } from "../Elements/OpeningGate20012026.jsx";
14
14
  import { CoupleWalk20012026 } from "../Elements/CoupleWalk20012026.jsx";
15
- import {FlowersSide20012026} from "../Elements/FlowersSide20012026.jsx";
15
+ import { FlowersSide20012026 } from "../Elements/FlowersSide20012026.jsx";
16
16
 
17
17
  export const F20012026_01 = ({
18
18
  firstName, secondName, welcomeMessage,
19
19
  }) => {
20
20
  /* -------------------- HOOKS (ONLY HERE) -------------------- */
21
21
  const frame = useCurrentFrame();
22
- const { fps, width, height, durationInFrames } = useVideoConfig();
22
+ const { fps, width, height } = useVideoConfig();
23
23
 
24
24
  /* -------------------- MESSAGE REVEAL -------------------- */
25
25
  const messageStart = fps * 4;
@@ -58,6 +58,8 @@ export const F20012026_01 = ({
58
58
  const groomAnim = reveal(groomStart);
59
59
  return (
60
60
  <AbsoluteFill style={{ overflow: "hidden" }}>
61
+
62
+ <AbsoluteFill style={{ backgroundColor: "#1a1a1a" }} />
61
63
  {/* ================= BACKGROUND ================= */}
62
64
  <Img
63
65
  src={staticFile("video-images/20012026-bg.png")}
@@ -154,7 +156,7 @@ export const F20012026_01 = ({
154
156
 
155
157
  <h1
156
158
  style={{
157
- width: "100%",
159
+ width: "100%",
158
160
  maxWidth: "80%",
159
161
  fontFamily: "Playfair Display",
160
162
  color: "#6B1E2E",
@@ -171,7 +173,7 @@ export const F20012026_01 = ({
171
173
 
172
174
  }}
173
175
  >
174
- {secondName}
176
+ {secondName}
175
177
  </h1>
176
178
  </div>
177
179
  </div>
@@ -13,22 +13,12 @@ import { SideTrees20012026 } from "../Elements/SideTrees20012026.jsx";
13
13
  import { PeacockDance20012026 } from "../Elements/PeacockDance20012026.jsx";
14
14
  import { SmoothRevealFromTop } from "../Elements/SmoothRevealFromTop.jsx";
15
15
 
16
- export const F20012026_02 = ({ firstName, secondName,firstSideNote,secondSideNote, invitationMessage ,occasionDate,fadeInDuration = 30,
16
+ export const F20012026_02 = ({ firstName, secondName, firstSideNote, secondSideNote, invitationMessage, occasionDate, fadeInDuration = 30,
17
17
  }) => {
18
18
  const frame = useCurrentFrame();
19
19
  const { fps } = useVideoConfig();
20
20
 
21
21
  /* ================= SCENE FADE + ZOOM ================= */
22
- const sceneOpacity = interpolate(
23
- frame,
24
- [0, fadeInDuration],
25
- [0, 1],
26
- {
27
- extrapolateRight: "clamp",
28
- easing: Easing.out(Easing.ease),
29
- }
30
- );
31
-
32
22
  // 🎥 VERY SMOOTH CINEMATIC ZOOM
33
23
  const sceneScale = interpolate(
34
24
  frame,
@@ -42,19 +32,19 @@ export const F20012026_02 = ({ firstName, secondName,firstSideNote,secondSideNot
42
32
 
43
33
  /* ================= SAFE DATE FORMAT ================= */
44
34
  const formatDate = (date) => {
45
- if (!date) return "";
46
- const d = new Date(date);
47
- if (isNaN(d.getTime())) return "";
35
+ if (!date) return "";
36
+ const d = new Date(date);
37
+ if (isNaN(d.getTime())) return "";
48
38
 
49
- return d.toLocaleDateString("en-IN", {
50
- day: "numeric",
51
- month: "long",
52
- year: "numeric",
53
- });
54
- };
39
+ return d.toLocaleDateString("en-IN", {
40
+ day: "numeric",
41
+ month: "long",
42
+ year: "numeric",
43
+ });
44
+ };
55
45
 
56
46
 
57
- const DETAILS_START = fps * 3;
47
+ const DETAILS_START = fps * 3;
58
48
 
59
49
  /* ================= IMAGE ANIMATION ================= */
60
50
  const showImageAfter = fadeInDuration * 1;
@@ -80,232 +70,234 @@ export const F20012026_02 = ({ firstName, secondName,firstSideNote,secondSideNot
80
70
  );
81
71
 
82
72
  return (
83
- <AbsoluteFill
84
- style={{
85
- opacity: sceneOpacity,
86
- transform: `scale(${sceneScale})`,
87
- transformOrigin: "center center",
88
- overflow: "hidden",
89
-
90
- }}
91
- >
92
- {/* ================= BACKGROUND ================= */}
93
- <Img
94
- src={staticFile("video-images/20012026-bg.png")}
73
+ <AbsoluteFill>
74
+ <AbsoluteFill style={{ backgroundColor: "#1a1a1a" }} />
75
+ <AbsoluteFill
95
76
  style={{
96
- position: "absolute",
97
- width: "100%",
98
- height: "100%",
99
- objectFit: "cover",
100
- }}
101
- />
77
+ transform: `scale(${sceneScale})`,
78
+ transformOrigin: "center center",
79
+ overflow: "hidden",
102
80
 
103
- <Img
104
- src={staticFile("video-images/20012026-curtain-floral.png")}
105
- style={{
106
- position: "absolute",
107
- left: "50%",
108
- width: "100%",
109
- opacity: imageOpacity,
110
- transform: `translateX(-50%) scale(${imageScale})`,
111
- }}
112
- />
113
-
114
- {/* ================= DECORATIONS ================= */}
115
- <SideTrees20012026 startAfter={2} />
116
- <PeacockDance20012026 startAfter={3} />
117
-
118
- <div
119
- style={{
120
- position: "relative",
121
- top: "26%",
122
- width: "100%",
123
- display: "flex",
124
- flexDirection: "column",
125
- alignItems: "center",
126
- textAlign: "center",
127
- gap: "15px"
128
81
  }}
129
82
  >
130
- {/* Invitation Message */}
131
- <div
132
- style={{
133
- maxWidth: "50%",
134
- fontFamily: "Dancing Script",
135
- color: "#4A3A2A",
136
- fontSize: 40,
137
- fontWeight: "500",
138
- lineHeight: 1.2,
139
- letterSpacing: "2px",
140
- ...SmoothRevealFromTop({
141
- frame,
142
- startFrame: DETAILS_START,
143
- delay: 0,
144
- })
145
- }}
146
- >
147
- {invitationMessage}
148
- </div>
149
-
150
- {/* Bride Name */}
151
- <h1
83
+ {/* ================= BACKGROUND ================= */}
84
+ <Img
85
+ src={staticFile("video-images/20012026-bg.png")}
152
86
  style={{
153
- maxWidth: "70%",
154
- fontFamily: "Playfair Display",
155
- color: "#7A1E2E",
156
- fontSize: 60,
157
- fontWeight: "600",
158
- lineHeight: 1.2,
159
- letterSpacing: "3px",
160
- whiteSpace: "normal",
161
- wordBreak: "break-word",
162
- overflowWrap: "break-word",
163
- ...SmoothRevealFromTop({
164
- frame,
165
- startFrame: DETAILS_START,
166
- delay: 25,
167
- })
87
+ position: "absolute",
88
+ width: "100%",
89
+ height: "100%",
90
+ objectFit: "cover",
168
91
  }}
169
- >
170
- {firstName}
171
- </h1>
92
+ />
172
93
 
173
- {/* Bride Side Note */}
174
- <div
94
+ <Img
95
+ src={staticFile("video-images/20012026-curtain-floral.png")}
175
96
  style={{
176
- maxWidth: "60%",
177
- fontFamily: "Dancing Script",
178
- color: "#B87A6A",
179
- fontSize: 40,
180
- fontWeight: "600",
181
- lineHeight: 1.2,
182
- letterSpacing: "2px",
183
- whiteSpace: "normal",
184
- wordBreak: "break-word",
185
- overflowWrap: "break-word",
186
- ...SmoothRevealFromTop({
187
- frame,
188
- startFrame: DETAILS_START,
189
- delay: 50,
190
- })
97
+ position: "absolute",
98
+ left: "50%",
99
+ width: "100%",
100
+ opacity: imageOpacity,
101
+ transform: `translateX(-50%) scale(${imageScale})`,
191
102
  }}
192
- >
193
- {firstSideNote}
194
- </div>
103
+ />
195
104
 
196
- {/* & Symbol */}
197
- <div
198
- style={{
199
- fontSize: 40,
200
- color: "#C9A24D",
201
- letterSpacing: "6px",
202
- ...SmoothRevealFromTop({
203
- frame,
204
- startFrame: DETAILS_START,
205
- delay: 70,
206
- })
207
- }}
208
- >
209
- ❁ ❁ ❁
210
- </div>
211
-
212
-
213
- {/* Groom Name */}
214
- <h1
215
- style={{
216
- maxWidth: "70%",
217
- fontFamily: "Playfair Display",
218
- color: "#7A1E2E",
219
- fontSize: 60,
220
- fontWeight: "600",
221
- lineHeight: 1.2,
222
- letterSpacing: "3px",
223
- whiteSpace: "normal",
224
- wordBreak: "break-word",
225
- overflowWrap: "break-word",
226
- ...SmoothRevealFromTop({
227
- frame,
228
- startFrame: DETAILS_START,
229
- delay: 95,
230
- })
231
- }}
232
- >
233
- {secondName}
234
- </h1>
235
-
236
- {/* Groom Side Note */}
237
- <div
238
- style={{
239
- maxWidth: "70%",
240
- fontFamily: "Dancing Script",
241
- color: "#B87A6A",
242
- fontSize: 40,
243
- fontWeight: "600",
244
- lineHeight: 1.2,
245
- letterSpacing: "2px",
246
- whiteSpace: "normal",
247
- wordBreak: "break-word",
248
- overflowWrap: "break-word",
249
- ...SmoothRevealFromTop({
250
- frame,
251
- startFrame: DETAILS_START,
252
- delay: 120,
253
- })
254
- }}
255
- >
256
- {secondSideNote}
257
- </div>
105
+ {/* ================= DECORATIONS ================= */}
106
+ <SideTrees20012026 startAfter={2} />
107
+ <PeacockDance20012026 startAfter={3} />
258
108
 
259
- {/* Date */}
260
109
  <div
261
110
  style={{
262
- maxWidth: "70%",
263
- marginTop: 20,
111
+ position: "relative",
112
+ top: "26%",
113
+ width: "100%",
114
+ display: "flex",
115
+ flexDirection: "column",
116
+ alignItems: "center",
264
117
  textAlign: "center",
265
- ...SmoothRevealFromTop({
266
- frame,
267
- startFrame: DETAILS_START,
268
- delay: 145,
269
- })
118
+ gap: "15px"
270
119
  }}
271
120
  >
272
- {/* -------- Message Line -------- */}
121
+ {/* Invitation Message */}
273
122
  <div
274
123
  style={{
275
- fontSize: 45,
276
- fontFamily:"Great Vibes",
124
+ maxWidth: "50%",
125
+ fontFamily: "Dancing Script",
126
+ color: "#4A3A2A",
127
+ fontSize: 40,
277
128
  fontWeight: "500",
129
+ lineHeight: 1.2,
130
+ letterSpacing: "2px",
131
+ ...SmoothRevealFromTop({
132
+ frame,
133
+ startFrame: DETAILS_START,
134
+ delay: 0,
135
+ })
136
+ }}
137
+ >
138
+ {invitationMessage}
139
+ </div>
140
+
141
+ {/* Bride Name */}
142
+ <h1
143
+ style={{
144
+ maxWidth: "70%",
145
+ fontFamily: "Playfair Display",
146
+ color: "#7A1E2E",
147
+ fontSize: 60,
148
+ fontWeight: "600",
149
+ lineHeight: 1.2,
278
150
  letterSpacing: "3px",
279
- color: "#2F6B4F",
151
+ whiteSpace: "normal",
152
+ wordBreak: "break-word",
153
+ overflowWrap: "break-word",
154
+ ...SmoothRevealFromTop({
155
+ frame,
156
+ startFrame: DETAILS_START,
157
+ delay: 25,
158
+ })
159
+ }}
160
+ >
161
+ {firstName}
162
+ </h1>
163
+
164
+ {/* Bride Side Note */}
165
+ <div
166
+ style={{
167
+ maxWidth: "60%",
168
+ fontFamily: "Dancing Script",
169
+ color: "#B87A6A",
170
+ fontSize: 40,
171
+ fontWeight: "600",
280
172
  lineHeight: 1.2,
281
- marginBottom: 8,
282
- whiteSpace: "nowrap",
173
+ letterSpacing: "2px",
174
+ whiteSpace: "normal",
175
+ wordBreak: "break-word",
176
+ overflowWrap: "break-word",
177
+ ...SmoothRevealFromTop({
178
+ frame,
179
+ startFrame: DETAILS_START,
180
+ delay: 50,
181
+ })
182
+ }}
183
+ >
184
+ {firstSideNote}
185
+ </div>
283
186
 
187
+ {/* & Symbol */}
188
+ <div
189
+ style={{
190
+ fontSize: 40,
191
+ color: "#C9A24D",
192
+ letterSpacing: "6px",
193
+ ...SmoothRevealFromTop({
194
+ frame,
195
+ startFrame: DETAILS_START,
196
+ delay: 70,
197
+ })
284
198
  }}
285
199
  >
286
- Join us on this auspicious day –
200
+
287
201
  </div>
288
202
 
289
- {/* -------- Date Line -------- */}
203
+
204
+ {/* Groom Name */}
205
+ <h1
206
+ style={{
207
+ maxWidth: "70%",
208
+ fontFamily: "Playfair Display",
209
+ color: "#7A1E2E",
210
+ fontSize: 60,
211
+ fontWeight: "600",
212
+ lineHeight: 1.2,
213
+ letterSpacing: "3px",
214
+ whiteSpace: "normal",
215
+ wordBreak: "break-word",
216
+ overflowWrap: "break-word",
217
+ ...SmoothRevealFromTop({
218
+ frame,
219
+ startFrame: DETAILS_START,
220
+ delay: 95,
221
+ })
222
+ }}
223
+ >
224
+ {secondName}
225
+ </h1>
226
+
227
+ {/* Groom Side Note */}
290
228
  <div
291
229
  style={{
292
- fontSize: 55,
293
- fontFamily:"Playfair Display",
294
- fontWeight: "400",
295
- color: "#5A4632",
230
+ maxWidth: "70%",
231
+ fontFamily: "Dancing Script",
232
+ color: "#B87A6A",
233
+ fontSize: 40,
234
+ fontWeight: "600",
296
235
  lineHeight: 1.2,
297
- letterSpacing: "4px",
298
- whiteSpace: "nowrap", // 🔥 force single line
236
+ letterSpacing: "2px",
237
+ whiteSpace: "normal",
238
+ wordBreak: "break-word",
239
+ overflowWrap: "break-word",
240
+ ...SmoothRevealFromTop({
241
+ frame,
242
+ startFrame: DETAILS_START,
243
+ delay: 120,
244
+ })
299
245
  }}
300
246
  >
301
- {formatDate(occasionDate)}
247
+ {secondSideNote}
302
248
  </div>
303
- </div>
304
249
 
250
+ {/* Date */}
251
+ <div
252
+ style={{
253
+ maxWidth: "70%",
254
+ marginTop: 20,
255
+ textAlign: "center",
256
+ ...SmoothRevealFromTop({
257
+ frame,
258
+ startFrame: DETAILS_START,
259
+ delay: 145,
260
+ })
261
+ }}
262
+ >
263
+ {/* -------- Message Line -------- */}
264
+ <div
265
+ style={{
266
+ fontSize: 45,
267
+ fontFamily: "Great Vibes",
268
+ fontWeight: "500",
269
+ letterSpacing: "3px",
270
+ color: "#2F6B4F",
271
+ lineHeight: 1.2,
272
+ marginBottom: 8,
273
+ whiteSpace: "nowrap",
274
+
275
+ }}
276
+ >
277
+ – Join us on this auspicious day –
278
+ </div>
305
279
 
306
- </div>
280
+ {/* -------- Date Line -------- */}
281
+ <div
282
+ style={{
283
+ fontSize: 55,
284
+ fontFamily: "Playfair Display",
285
+ fontWeight: "400",
286
+ color: "#5A4632",
287
+ lineHeight: 1.2,
288
+ letterSpacing: "4px",
289
+ whiteSpace: "nowrap", // 🔥 force single line
290
+ }}
291
+ >
292
+ {formatDate(occasionDate)}
293
+ </div>
294
+ </div>
295
+
296
+
297
+ </div>
307
298
 
308
299
 
300
+ </AbsoluteFill>
309
301
  </AbsoluteFill>
310
302
  );
311
303
  };
@@ -23,16 +23,6 @@ export const F20012026_03 = ({
23
23
  const { fps } = useVideoConfig();
24
24
 
25
25
  /* ================= SCENE REVEAL (SMOKE FOLLOW-UP) ================= */
26
- const sceneOpacity = interpolate(
27
- frame,
28
- [0, fadeInDuration],
29
- [0, 1],
30
- {
31
- extrapolateRight: "clamp",
32
- easing: Easing.out(Easing.ease),
33
- }
34
- );
35
-
36
26
  // 🎥 VERY SMOOTH CINEMATIC ZOOM
37
27
  const sceneScale = interpolate(
38
28
  frame,
@@ -102,9 +92,10 @@ export const F20012026_03 = ({
102
92
  );
103
93
 
104
94
  return (
95
+ <AbsoluteFill>
96
+ <AbsoluteFill style={{ backgroundColor: "#1a1a1a" }} />
105
97
  <AbsoluteFill
106
98
  style={{
107
- opacity: sceneOpacity,
108
99
  transform: `scale(${sceneScale})`,
109
100
  transformOrigin: "center center",
110
101
  overflow: "hidden",
@@ -313,5 +304,7 @@ export const F20012026_03 = ({
313
304
  </div>
314
305
 
315
306
  </AbsoluteFill>
307
+
308
+ </AbsoluteFill>
316
309
  );
317
310
  };
@@ -1,5 +1,12 @@
1
- import React from "react";
2
- import { Sequence, useVideoConfig, Audio, staticFile,AbsoluteFill } from "remotion";
1
+ import React, { useMemo } from "react";
2
+ import {
3
+ Sequence,
4
+ useVideoConfig,
5
+ Audio,
6
+ staticFile,
7
+ AbsoluteFill,
8
+ } from "remotion";
9
+
3
10
  import { F20012026_01 } from "../Frames/F20012026_01.jsx";
4
11
  import { F20012026_02 } from "../Frames/F20012026_02.jsx";
5
12
  import { F20012026_03 } from "../Frames/F20012026_03.jsx";
@@ -18,7 +25,8 @@ export const T20012026_01 = ({
18
25
  }) => {
19
26
  const { durationInFrames } = useVideoConfig();
20
27
 
21
- // Detect creator safely
28
+ /* ---------------- SAFE DATA ---------------- */
29
+
22
30
  const whoIsCreating =
23
31
  event?.occasion_data?.who_is_creating ??
24
32
  event?.who_is_creating;
@@ -26,7 +34,6 @@ export const T20012026_01 = ({
26
34
  const isBrideCreating =
27
35
  whoIsCreating === "Bride" || !whoIsCreating;
28
36
 
29
- // Names
30
37
  const brideName =
31
38
  event?.occasion_data?.bride_name ??
32
39
  event?.bride_name ??
@@ -42,7 +49,6 @@ export const T20012026_01 = ({
42
49
  const firstName = isBrideCreating ? brideName : groomName;
43
50
  const secondName = isBrideCreating ? groomName : brideName;
44
51
 
45
- // Side notes (safe)
46
52
  const firstSideNote = isBrideCreating
47
53
  ? formData?.bride_side_note || ""
48
54
  : formData?.groom_side_note || "";
@@ -51,82 +57,99 @@ export const T20012026_01 = ({
51
57
  ? formData?.groom_side_note || ""
52
58
  : formData?.bride_side_note || "";
53
59
 
54
- // Venue (safe)
55
60
  const venueName =
56
61
  venue?.name || formData?.venue_name || "";
57
62
 
58
63
  const venueAddress =
59
64
  venue?.address || formData?.venue_address || "";
60
65
 
66
+ /* ---------------- TIMING ---------------- */
67
+
61
68
  const framesPerPage = Math.floor(durationInFrames / 3);
62
- const transitionDuration = Math.floor(framesPerPage * 0.25); // ~1 sec
63
-
64
- usePreloadImages([
65
- staticFile("video-images/20012026-bg.png"),
66
- staticFile("video-images/20012026-bride.png"),
67
- staticFile("video-images/20012026-butterfly.png"),
68
- staticFile("video-images/20012026-couple-walk.png"),
69
- staticFile("video-images/20012026-curtain-floral.png"),
70
- staticFile("video-images/20012026-floral-left.png"),
71
- staticFile("video-images/20012026-floral-right.png"),
72
- staticFile("video-images/20012026-groom.png"),
73
- staticFile("video-images/20012026-opening-gate.png"),
74
- staticFile("video-images/20012026-peacock.png"),
75
- staticFile("video-images/20012026-tree-left.png"),
76
- staticFile("video-images/20012026-tree-right.png"),
77
- staticFile("video-images/20012026-wedding-gate.png"),
78
- ]);
69
+ const transitionDuration = Math.floor(framesPerPage * 0.25);
70
+
71
+ /* ---------------- PRELOAD IMAGES ---------------- */
72
+
73
+ const imagesToPreload = useMemo(
74
+ () => [
75
+ staticFile("video-images/20012026-bg.png"),
76
+ staticFile("video-images/20012026-bride.png"),
77
+ staticFile("video-images/20012026-butterfly.png"),
78
+ staticFile("video-images/20012026-couple-walk.png"),
79
+ staticFile("video-images/20012026-curtain-floral.png"),
80
+ staticFile("video-images/20012026-floral-left.png"),
81
+ staticFile("video-images/20012026-floral-right.png"),
82
+ staticFile("video-images/20012026-groom.png"),
83
+ staticFile("video-images/20012026-opening-gate.png"),
84
+ staticFile("video-images/20012026-peacock.png"),
85
+ staticFile("video-images/20012026-tree-left.png"),
86
+ staticFile("video-images/20012026-tree-right.png"),
87
+ staticFile("video-images/20012026-wedding-gate.png"),
88
+ ],
89
+ []
90
+ );
91
+
92
+ usePreloadImages(imagesToPreload);
93
+
94
+ /* ---------------- RENDER ---------------- */
79
95
 
80
96
  return (
81
- <AbsoluteFill style={{ backgroundColor: "#1a1a1a" }}>
82
- {/* 🎵 Background Music */}
83
- <Audio
84
- src={staticFile("video-images/wedding.mp3")}
85
- startFrom={30}
86
- endAt={900}
87
- volume={0.4}
97
+ <AbsoluteFill>
98
+
99
+ {/* 🔒 GLOBAL BACKGROUND (never unmounts) */}
100
+ <AbsoluteFill style={{ backgroundColor: "#1a1a1a" }} />
101
+
102
+ {/* 🎵 MUSIC */}
103
+ <Audio
104
+ src={staticFile("video-images/wedding.mp3")}
105
+ startFrom={30}
106
+ endAt={900}
107
+ volume={0.4}
108
+ />
109
+
110
+ {/* -------- SCENE 1 -------- */}
111
+ <Sequence from={0} durationInFrames={framesPerPage + 2}>
112
+ <F20012026_01
113
+ firstName={firstName}
114
+ secondName={secondName}
115
+ welcomeMessage={welcomeMessage}
116
+ eventDateTime={eventDateTime}
117
+ />
118
+ </Sequence>
119
+
120
+ {/* -------- SCENE 2 -------- */}
121
+ <Sequence from={framesPerPage - 2} durationInFrames={framesPerPage + 2}>
122
+ <F20012026_02
123
+ firstName={firstName}
124
+ secondName={secondName}
125
+ firstSideNote={firstSideNote}
126
+ secondSideNote={secondSideNote}
127
+ invitationMessage={invitationMessage}
128
+ occasionDate={occasionDate}
129
+ fadeInDuration={transitionDuration}
130
+ />
131
+ </Sequence>
132
+
133
+ {/* ☁️ TRANSITION 1 → 2 */}
134
+ <Sequence from={framesPerPage - 2} durationInFrames={transitionDuration + 2}>
135
+ <InkSmokeTransition20012026 />
136
+ </Sequence>
137
+
138
+ {/* -------- SCENE 3 -------- */}
139
+ <Sequence from={framesPerPage * 2 - 2} durationInFrames={framesPerPage + 2}>
140
+ <F20012026_03
141
+ eventDateTime={eventDateTime}
142
+ venueName={venueName}
143
+ venueAddress={venueAddress}
144
+ familyMember={familyMember}
88
145
  />
89
- <Sequence from={0} durationInFrames={framesPerPage + 1}>
90
- <F20012026_01
91
- firstName={firstName}
92
- secondName={secondName}
93
- welcomeMessage={welcomeMessage}
94
- eventDateTime={eventDateTime}
95
- />
96
- </Sequence>
97
-
98
- <Sequence from={framesPerPage} durationInFrames={framesPerPage + 1}>
99
- <F20012026_02
100
- firstName={firstName}
101
- secondName={secondName}
102
- firstSideNote={firstSideNote}
103
- secondSideNote={secondSideNote}
104
- invitationMessage={invitationMessage}
105
- occasionDate={occasionDate}
106
- fadeInDuration={transitionDuration}
107
- />
108
- </Sequence>
109
-
110
- {/* ☁️ TRANSITION: SCENE 1 ➜ 2 */}
111
- <Sequence from={framesPerPage - 1} durationInFrames={transitionDuration + 2}
112
- >
113
- <InkSmokeTransition20012026 />
114
- </Sequence>
115
-
116
- <Sequence from={framesPerPage * 2} durationInFrames={framesPerPage + 1}>
117
- <F20012026_03
118
- eventDateTime={eventDateTime}
119
- venueName={venueName}
120
- venueAddress={venueAddress}
121
- familyMember={familyMember}
122
- />
123
- </Sequence>
124
-
125
- {/* ☁️ TRANSITION: SCENE 2 ➜ 3 */}
126
- <Sequence from={framesPerPage * 2 - 1} durationInFrames={transitionDuration + 2}>
127
- <InkSmokeTransition20012026 />
128
- </Sequence>
129
-
130
- </AbsoluteFill>
146
+ </Sequence>
147
+
148
+ {/* ☁️ TRANSITION 2 → 3 */}
149
+ <Sequence from={framesPerPage * 2 - 2} durationInFrames={transitionDuration + 2}>
150
+ <InkSmokeTransition20012026 />
151
+ </Sequence>
152
+
153
+ </AbsoluteFill>
131
154
  );
132
155
  };
@@ -12,13 +12,17 @@ export const usePreloadImages = (imageSources) => {
12
12
 
13
13
  let cancelled = false;
14
14
 
15
- Promise.all(
16
- imageSources.map((src) =>
17
- prefetch(src)
18
- .then(({ waitUntilDone }) => waitUntilDone())
19
- .catch(() => null) // never block render
20
- )
21
- )
15
+ const promises = imageSources.map((src) => {
16
+ try {
17
+ const { waitUntilDone } = prefetch(src);
18
+ return waitUntilDone();
19
+ } catch (err) {
20
+ console.warn("Prefetch failed:", src, err);
21
+ return Promise.resolve(); // never block render
22
+ }
23
+ });
24
+
25
+ Promise.all(promises)
22
26
  .then(() => {
23
27
  if (!cancelled) {
24
28
  continueRender(handle);