@make-pictures/kits 0.0.0-stage → 2.0.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -2
  3. package/dist/chunk-3SUIELJV.mjs +601 -0
  4. package/dist/chunk-4Z3G6FPZ.mjs +485 -0
  5. package/dist/chunk-AHA23PHC.mjs +678 -0
  6. package/dist/chunk-CW623YVB.mjs +1124 -0
  7. package/dist/chunk-NNA7XXQB.mjs +1059 -0
  8. package/dist/chunk-S4CHTWOS.mjs +1237 -0
  9. package/dist/chunk-WAA7P5FT.mjs +912 -0
  10. package/dist/data-storytelling/AnimatedBarRace.d.ts +37 -0
  11. package/dist/data-storytelling/MetricComparisonCard.d.ts +29 -0
  12. package/dist/data-storytelling/TimelineTrack.d.ts +29 -0
  13. package/dist/data-storytelling/index.cjs +1224 -0
  14. package/dist/data-storytelling/index.d.ts +3 -0
  15. package/dist/data-storytelling/index.mjs +10 -0
  16. package/dist/index.cjs +6010 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.mjs +108 -0
  19. package/dist/isometric-3d/IsometricStudioEnvironment.d.ts +8 -0
  20. package/dist/isometric-3d/Stylized3DCharacter.d.ts +29 -0
  21. package/dist/isometric-3d/index.cjs +611 -0
  22. package/dist/isometric-3d/index.d.ts +2 -0
  23. package/dist/isometric-3d/index.mjs +8 -0
  24. package/dist/kinetic-typography/AnimatedMetricCounter.d.ts +36 -0
  25. package/dist/kinetic-typography/ImpactKeywordCard.d.ts +31 -0
  26. package/dist/kinetic-typography/WordByWordStage.d.ts +47 -0
  27. package/dist/kinetic-typography/index.cjs +665 -0
  28. package/dist/kinetic-typography/index.d.ts +3 -0
  29. package/dist/kinetic-typography/index.mjs +14 -0
  30. package/dist/motion-diagrams-2d/CameraRig.d.ts +34 -0
  31. package/dist/motion-diagrams-2d/CodePayloadToast.d.ts +14 -0
  32. package/dist/motion-diagrams-2d/DataFlowEdge.d.ts +24 -0
  33. package/dist/motion-diagrams-2d/NodeBox.d.ts +65 -0
  34. package/dist/motion-diagrams-2d/SystemNode.d.ts +21 -0
  35. package/dist/motion-diagrams-2d/index.cjs +1137 -0
  36. package/dist/motion-diagrams-2d/index.d.ts +5 -0
  37. package/dist/motion-diagrams-2d/index.mjs +28 -0
  38. package/dist/paper-diorama-3d/ImageCutout3D.d.ts +47 -0
  39. package/dist/paper-diorama-3d/PaperCutout3D.d.ts +62 -0
  40. package/dist/paper-diorama-3d/PaperPopUpBook3D.d.ts +51 -0
  41. package/dist/paper-diorama-3d/PaperStageLighting3D.d.ts +12 -0
  42. package/dist/paper-diorama-3d/index.cjs +1087 -0
  43. package/dist/paper-diorama-3d/index.d.ts +4 -0
  44. package/dist/paper-diorama-3d/index.mjs +34 -0
  45. package/dist/product-frames/BrowserFrame.d.ts +28 -0
  46. package/dist/product-frames/PhoneFrame.d.ts +23 -0
  47. package/dist/product-frames/ScreenSpotlightOverlay.d.ts +34 -0
  48. package/dist/product-frames/frameMedia.d.ts +24 -0
  49. package/dist/product-frames/index.cjs +507 -0
  50. package/dist/product-frames/index.d.ts +8 -0
  51. package/dist/product-frames/index.mjs +12 -0
  52. package/dist/vfx/CinematicLensOverlay.d.ts +29 -0
  53. package/dist/vfx/ParticleField.d.ts +18 -0
  54. package/dist/vfx/SceneTransition.d.ts +24 -0
  55. package/dist/vfx/StagePostProcessing3D.d.ts +44 -0
  56. package/dist/vfx/index.cjs +933 -0
  57. package/dist/vfx/index.d.ts +4 -0
  58. package/dist/vfx/index.mjs +14 -0
  59. package/package.json +95 -4
@@ -0,0 +1,912 @@
1
+ // packages/kits/src/vfx/CinematicLensOverlay.tsx
2
+ import { useMemo } from "react";
3
+ import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
4
+ import { useDirectorCameraTelemetry } from "@make-pictures/core";
5
+ import { createSeededRandom } from "@make-pictures/core";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function clamp(v, min, max) {
8
+ return Math.max(min, Math.min(max, v));
9
+ }
10
+ var CinematicLensOverlay = ({
11
+ vignetteIntensity = 0.36,
12
+ grainOpacity = 0.045,
13
+ chromaticAberration,
14
+ velocityAberrationScale = 0.85,
15
+ warmLightLeak = 0.16,
16
+ lightLeakColor = "#F59E0B",
17
+ seed = 77,
18
+ zIndex = 60
19
+ }) => {
20
+ const frame = useCurrentFrame();
21
+ const { fps, width, height } = useVideoConfig();
22
+ const telemetry = useDirectorCameraTelemetry();
23
+ const leakTraits = useMemo(() => {
24
+ const rand = createSeededRandom(`lens-leak:${seed}`);
25
+ return {
26
+ phaseA: rand() * Math.PI * 2,
27
+ phaseB: rand() * Math.PI * 2,
28
+ baseX: 8 + rand() * 14,
29
+ baseY: 6 + rand() * 14
30
+ };
31
+ }, [seed]);
32
+ const vig = clamp(vignetteIntensity, 0, 1);
33
+ const grain = clamp(grainOpacity, 0, 0.18);
34
+ const leak = clamp(warmLightLeak, 0, 1);
35
+ const rawCa = typeof chromaticAberration === "number" ? chromaticAberration : telemetry.velocity * velocityAberrationScale;
36
+ const ca = clamp(rawCa, 0, 1);
37
+ const f60 = frame * (60 / Math.max(1, fps));
38
+ const leakPulse = 0.72 + 0.28 * (0.5 * Math.sin(f60 * 0.032 + leakTraits.phaseA) + 0.5 * Math.cos(f60 * 0.045 + leakTraits.phaseB));
39
+ const leakX = leakTraits.baseX + Math.sin(f60 * 0.031 + leakTraits.phaseA) * 5;
40
+ const leakY = leakTraits.baseY + Math.cos(f60 * 0.037 + leakTraits.phaseB) * 4;
41
+ const grainSeed = (Math.abs(Math.floor(frame)) + Math.abs(seed)) % 97 + 1;
42
+ const filterId = `mp-film-grain-${Math.abs(seed)}`;
43
+ const fringePx = Math.round(ca * Math.max(width, height) * 0.018);
44
+ return /* @__PURE__ */ jsxs(
45
+ AbsoluteFill,
46
+ {
47
+ style: {
48
+ pointerEvents: "none",
49
+ overflow: "hidden",
50
+ zIndex
51
+ },
52
+ children: [
53
+ ca > 0.015 && fringePx > 0 && /* @__PURE__ */ jsx(
54
+ AbsoluteFill,
55
+ {
56
+ style: {
57
+ mixBlendMode: "screen",
58
+ opacity: clamp(ca * 0.78, 0, 0.72),
59
+ boxShadow: `
60
+ inset ${fringePx}px 0 ${fringePx * 2}px -${Math.max(1, Math.floor(fringePx * 0.35))}px rgba(255, 42, 84, 0.62),
61
+ inset -${fringePx}px 0 ${fringePx * 2}px -${Math.max(1, Math.floor(fringePx * 0.35))}px rgba(0, 240, 255, 0.62)
62
+ `
63
+ }
64
+ }
65
+ ),
66
+ leak > 5e-3 && /* @__PURE__ */ jsx(
67
+ AbsoluteFill,
68
+ {
69
+ style: {
70
+ mixBlendMode: "screen",
71
+ opacity: clamp(leak * leakPulse, 0, 0.85),
72
+ backgroundImage: `
73
+ radial-gradient(ellipse 52% 44% at ${leakX.toFixed(2)}% ${leakY.toFixed(2)}%, ${lightLeakColor}66 0%, ${lightLeakColor}22 46%, transparent 78%),
74
+ radial-gradient(ellipse 40% 36% at ${(100 - leakX * 0.7).toFixed(2)}% ${(96 - leakY * 0.6).toFixed(2)}%, #FB718538 0%, transparent 72%)
75
+ `
76
+ }
77
+ }
78
+ ),
79
+ vig > 5e-3 && /* @__PURE__ */ jsx(
80
+ AbsoluteFill,
81
+ {
82
+ style: {
83
+ backgroundImage: `radial-gradient(ellipse 76% 74% at 50% 50%, transparent 48%, rgba(9, 10, 18, ${(vig * 0.42).toFixed(3)}) 80%, rgba(5, 6, 12, ${(vig * 0.82).toFixed(3)}) 100%)`
84
+ }
85
+ }
86
+ ),
87
+ grain > 2e-3 && /* @__PURE__ */ jsxs(
88
+ "svg",
89
+ {
90
+ width: "100%",
91
+ height: "100%",
92
+ style: {
93
+ position: "absolute",
94
+ inset: 0,
95
+ opacity: grain,
96
+ mixBlendMode: "overlay",
97
+ pointerEvents: "none"
98
+ },
99
+ children: [
100
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("filter", { id: filterId, x: "0%", y: "0%", width: "100%", height: "100%", children: [
101
+ /* @__PURE__ */ jsx(
102
+ "feTurbulence",
103
+ {
104
+ type: "fractalNoise",
105
+ baseFrequency: "0.78",
106
+ numOctaves: 2,
107
+ seed: grainSeed,
108
+ stitchTiles: "stitch"
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx("feColorMatrix", { type: "saturate", values: "0" })
112
+ ] }) }),
113
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", filter: `url(#${filterId})` })
114
+ ]
115
+ }
116
+ )
117
+ ]
118
+ }
119
+ );
120
+ };
121
+
122
+ // packages/kits/src/vfx/ParticleField.tsx
123
+ import { useMemo as useMemo2 } from "react";
124
+ import {
125
+ AbsoluteFill as AbsoluteFill2,
126
+ spring,
127
+ useCurrentFrame as useCurrentFrame2,
128
+ useVideoConfig as useVideoConfig2
129
+ } from "remotion";
130
+ import { SPRING_PRESETS } from "@make-pictures/core";
131
+ import { createSeededRandom as createSeededRandom2 } from "@make-pictures/core";
132
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
133
+ var CONFETTI_PALETTE = [
134
+ "#2563EB",
135
+ "#FF2A85",
136
+ "#10B981",
137
+ "#F59E0B",
138
+ "#38BDF8",
139
+ "#7209B7"
140
+ ];
141
+ function wrap01(v) {
142
+ const m = v % 1;
143
+ return m < 0 ? m + 1 : m;
144
+ }
145
+ function defaultCountForPreset(preset) {
146
+ switch (preset) {
147
+ case "dust-motes":
148
+ return 34;
149
+ case "snow":
150
+ return 48;
151
+ case "rain":
152
+ return 64;
153
+ case "sparks":
154
+ return 38;
155
+ case "confetti":
156
+ return 52;
157
+ case "speed-lines":
158
+ return 28;
159
+ }
160
+ }
161
+ function defaultColorForPreset(preset) {
162
+ switch (preset) {
163
+ case "dust-motes":
164
+ return "#FDE68A";
165
+ case "snow":
166
+ return "#FFFFFF";
167
+ case "rain":
168
+ return "#BAE6FD";
169
+ case "sparks":
170
+ return "#FB923C";
171
+ case "confetti":
172
+ return "#FF2A85";
173
+ case "speed-lines":
174
+ return "#E0E7FF";
175
+ }
176
+ }
177
+ var ParticleField = ({
178
+ preset = "dust-motes",
179
+ count,
180
+ color,
181
+ seed = 108,
182
+ intensity = 1,
183
+ cleanFrameZero = true,
184
+ zIndex = 20
185
+ }) => {
186
+ const frame = useCurrentFrame2();
187
+ const { fps, width, height } = useVideoConfig2();
188
+ const resolvedCount = Math.max(
189
+ 0,
190
+ Math.round((count ?? defaultCountForPreset(preset)) * Math.max(0.25, intensity))
191
+ );
192
+ const baseColor = color ?? defaultColorForPreset(preset);
193
+ const descriptors = useMemo2(() => {
194
+ const list = [];
195
+ for (let i = 0; i < resolvedCount; i++) {
196
+ const rand = createSeededRandom2(`particle:${preset}:${seed}:${i}`);
197
+ const depth = 0.35 + rand() * 0.65;
198
+ const u0 = rand();
199
+ const v0 = rand();
200
+ const swayPhase = rand() * Math.PI * 2;
201
+ let rot0 = rand() * 360;
202
+ let speedU = 0;
203
+ let speedV = 0;
204
+ let swayAmp = 0;
205
+ let swayFreq = 0.035;
206
+ let size = 4;
207
+ let baseOpacity = 0.5;
208
+ let rotSpeed = 0;
209
+ let itemColor = baseColor;
210
+ if (preset === "dust-motes") {
211
+ speedU = (rand() - 0.5) * 0.025 * depth;
212
+ speedV = -(0.018 + rand() * 0.028) * depth;
213
+ swayAmp = 14 + rand() * 24;
214
+ swayFreq = 0.031 + rand() * 0.02;
215
+ size = (2.2 + rand() * 5.2) * depth;
216
+ baseOpacity = 0.22 + rand() * 0.45;
217
+ } else if (preset === "snow") {
218
+ speedU = (rand() - 0.35) * 0.04 * depth;
219
+ speedV = (0.085 + rand() * 0.095) * depth;
220
+ swayAmp = 18 + rand() * 28;
221
+ swayFreq = 0.032 + rand() * 0.02;
222
+ size = (2.8 + rand() * 5.5) * depth;
223
+ baseOpacity = 0.38 + rand() * 0.5;
224
+ } else if (preset === "rain") {
225
+ speedU = -0.18 * depth;
226
+ speedV = (0.95 + rand() * 0.45) * depth;
227
+ size = (26 + rand() * 36) * depth;
228
+ baseOpacity = 0.2 + rand() * 0.34;
229
+ rot0 = 11;
230
+ } else if (preset === "sparks") {
231
+ speedU = (rand() - 0.42) * 0.11 * depth;
232
+ speedV = -(0.22 + rand() * 0.28) * depth;
233
+ swayAmp = 16 + rand() * 30;
234
+ swayFreq = 0.042 + rand() * 0.012;
235
+ size = (2.4 + rand() * 4.2) * depth;
236
+ baseOpacity = 0.55 + rand() * 0.42;
237
+ itemColor = color ?? (rand() > 0.45 ? "#FB923C" : rand() > 0.5 ? "#FDE047" : "#F43F5E");
238
+ } else if (preset === "confetti") {
239
+ speedU = (rand() - 0.5) * 0.07;
240
+ speedV = (0.16 + rand() * 0.16) * depth;
241
+ swayAmp = 24 + rand() * 34;
242
+ swayFreq = 0.036 + rand() * 0.018;
243
+ size = (8 + rand() * 8) * (0.65 + depth * 0.35);
244
+ baseOpacity = 0.78 + rand() * 0.22;
245
+ rotSpeed = (rand() > 0.5 ? 1 : -1) * (95 + rand() * 130);
246
+ itemColor = color ?? CONFETTI_PALETTE[Math.floor(rand() * CONFETTI_PALETTE.length)];
247
+ } else if (preset === "speed-lines") {
248
+ speedU = -(1.45 + rand() * 0.95) * depth;
249
+ speedV = 0;
250
+ size = (120 + rand() * 220) * depth;
251
+ baseOpacity = 0.14 + rand() * 0.32;
252
+ }
253
+ list.push({
254
+ index: i,
255
+ u0,
256
+ v0,
257
+ speedU,
258
+ speedV,
259
+ swayAmp,
260
+ swayFreq,
261
+ swayPhase,
262
+ size,
263
+ baseOpacity,
264
+ rot0,
265
+ rotSpeed,
266
+ color: itemColor,
267
+ depth
268
+ });
269
+ }
270
+ return list;
271
+ }, [preset, resolvedCount, seed, baseColor, color]);
272
+ const enterOpacity = cleanFrameZero ? spring({
273
+ frame: frame - 2,
274
+ fps,
275
+ config: SPRING_PRESETS.smooth
276
+ }) : 1;
277
+ if (enterOpacity <= 5e-3 || resolvedCount === 0 || intensity <= 0) {
278
+ return null;
279
+ }
280
+ const tSec = frame / Math.max(1, fps);
281
+ const f60 = frame * (60 / Math.max(1, fps));
282
+ const margin = 80;
283
+ const spanW = width + margin * 2;
284
+ const spanH = height + margin * 2;
285
+ const ampOpacity = Math.min(1.35, Math.max(0, intensity));
286
+ return /* @__PURE__ */ jsx2(
287
+ AbsoluteFill2,
288
+ {
289
+ style: {
290
+ pointerEvents: "none",
291
+ overflow: "hidden",
292
+ opacity: enterOpacity,
293
+ zIndex
294
+ },
295
+ children: /* @__PURE__ */ jsx2(
296
+ "svg",
297
+ {
298
+ width,
299
+ height,
300
+ viewBox: `0 0 ${width} ${height}`,
301
+ style: { display: "block", width: "100%", height: "100%" },
302
+ children: descriptors.map((p) => {
303
+ const u = wrap01(p.u0 + p.speedU * tSec);
304
+ const v = wrap01(p.v0 + p.speedV * tSec);
305
+ const sway = Math.sin(f60 * p.swayFreq + p.swayPhase) * p.swayAmp;
306
+ const x = -margin + u * spanW + (preset === "speed-lines" ? 0 : sway);
307
+ const y = -margin + v * spanH + (preset === "dust-motes" ? sway * 0.35 : 0);
308
+ const edgeFadeV = v < 0.08 ? v / 0.08 : v > 0.92 ? (1 - v) / 0.08 : 1;
309
+ const twinkle = preset === "dust-motes" || preset === "sparks" ? 0.65 + 0.35 * Math.sin(f60 * 0.045 + p.swayPhase) : 1;
310
+ const alpha = Math.min(
311
+ 1,
312
+ p.baseOpacity * ampOpacity * edgeFadeV * twinkle
313
+ );
314
+ if (alpha <= 0.01) return null;
315
+ if (preset === "rain") {
316
+ const rad = p.rot0 * Math.PI / 180;
317
+ const dx = -Math.sin(rad) * p.size;
318
+ const dy = Math.cos(rad) * p.size;
319
+ return /* @__PURE__ */ jsx2(
320
+ "line",
321
+ {
322
+ x1: x,
323
+ y1: y,
324
+ x2: x + dx,
325
+ y2: y + dy,
326
+ stroke: p.color,
327
+ strokeWidth: 1.4 * p.depth + 0.5,
328
+ strokeLinecap: "round",
329
+ opacity: alpha
330
+ },
331
+ p.index
332
+ );
333
+ }
334
+ if (preset === "speed-lines") {
335
+ const edgeFadeU = u < 0.1 ? u / 0.1 : u > 0.9 ? (1 - u) / 0.1 : 1;
336
+ return /* @__PURE__ */ jsx2(
337
+ "line",
338
+ {
339
+ x1: x,
340
+ y1: y,
341
+ x2: x + p.size,
342
+ y2: y,
343
+ stroke: p.color,
344
+ strokeWidth: 1.6 + p.depth * 2.2,
345
+ strokeLinecap: "round",
346
+ opacity: alpha * edgeFadeU
347
+ },
348
+ p.index
349
+ );
350
+ }
351
+ if (preset === "confetti") {
352
+ const rot = p.rot0 + p.rotSpeed * tSec;
353
+ const tumbleScaleY = Math.max(
354
+ 0.22,
355
+ Math.abs(Math.cos(f60 * 0.052 + p.swayPhase))
356
+ );
357
+ const w = p.size;
358
+ const h = p.size * 0.58;
359
+ return /* @__PURE__ */ jsx2(
360
+ "rect",
361
+ {
362
+ x: -w / 2,
363
+ y: -h / 2,
364
+ width: w,
365
+ height: h,
366
+ rx: 1.8,
367
+ fill: p.color,
368
+ opacity: alpha,
369
+ transform: `translate(${x.toFixed(2)}, ${y.toFixed(2)}) rotate(${rot.toFixed(1)}) scale(1, ${tumbleScaleY.toFixed(2)})`
370
+ },
371
+ p.index
372
+ );
373
+ }
374
+ if (preset === "sparks") {
375
+ return /* @__PURE__ */ jsxs2("g", { opacity: alpha, children: [
376
+ /* @__PURE__ */ jsx2("circle", { cx: x, cy: y, r: p.size * 2.3, fill: p.color, opacity: 0.24 }),
377
+ /* @__PURE__ */ jsx2("circle", { cx: x, cy: y, r: p.size, fill: "#FFFBEB" })
378
+ ] }, p.index);
379
+ }
380
+ return /* @__PURE__ */ jsxs2("g", { opacity: alpha, children: [
381
+ preset === "dust-motes" && /* @__PURE__ */ jsx2("circle", { cx: x, cy: y, r: p.size * 2.2, fill: p.color, opacity: 0.2 }),
382
+ /* @__PURE__ */ jsx2("circle", { cx: x, cy: y, r: p.size, fill: p.color })
383
+ ] }, p.index);
384
+ })
385
+ }
386
+ )
387
+ }
388
+ );
389
+ };
390
+
391
+ // packages/kits/src/vfx/StagePostProcessing3D.tsx
392
+ import { useLayoutEffect, useMemo as useMemo3 } from "react";
393
+ import { useThree } from "@react-three/fiber";
394
+ import { useCurrentFrame as useCurrentFrame3, useVideoConfig as useVideoConfig3 } from "remotion";
395
+ import * as THREE from "three";
396
+ import { createSeededRandom as createSeededRandom3 } from "@make-pictures/core";
397
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
398
+ var radialShadowTextureCache = /* @__PURE__ */ new Map();
399
+ var spotlightBeamTextureCache = /* @__PURE__ */ new Map();
400
+ function getRadialDiscTexture(colorHex) {
401
+ if (typeof document === "undefined") return null;
402
+ const cached = radialShadowTextureCache.get(colorHex);
403
+ if (cached) return cached;
404
+ const canvas = document.createElement("canvas");
405
+ canvas.width = 256;
406
+ canvas.height = 256;
407
+ const ctx = canvas.getContext("2d");
408
+ if (ctx) {
409
+ const grad = ctx.createRadialGradient(128, 128, 8, 128, 128, 124);
410
+ grad.addColorStop(0, colorHex);
411
+ grad.addColorStop(0.45, colorHex);
412
+ grad.addColorStop(1, "rgba(0,0,0,0)");
413
+ ctx.fillStyle = grad;
414
+ ctx.fillRect(0, 0, 256, 256);
415
+ }
416
+ if (ctx) {
417
+ const img = ctx.getImageData(0, 0, 256, 256);
418
+ const data = img.data;
419
+ for (let y = 0; y < 256; y++) {
420
+ for (let x = 0; x < 256; x++) {
421
+ const dx = (x - 128) / 124;
422
+ const dy = (y - 128) / 124;
423
+ const r2 = dx * dx + dy * dy;
424
+ const idx = (y * 256 + x) * 4;
425
+ if (r2 >= 1) {
426
+ data[idx + 3] = 0;
427
+ } else {
428
+ const smooth = Math.pow(1 - r2, 2.2);
429
+ data[idx + 3] = Math.round(smooth * 255);
430
+ }
431
+ }
432
+ }
433
+ ctx.putImageData(img, 0, 0);
434
+ }
435
+ const tex = new THREE.CanvasTexture(canvas);
436
+ tex.needsUpdate = true;
437
+ radialShadowTextureCache.set(colorHex, tex);
438
+ return tex;
439
+ }
440
+ function getVolumetricConeTexture(key) {
441
+ if (typeof document === "undefined") return null;
442
+ const cached = spotlightBeamTextureCache.get(key);
443
+ if (cached) return cached;
444
+ const canvas = document.createElement("canvas");
445
+ canvas.width = 128;
446
+ canvas.height = 256;
447
+ const ctx = canvas.getContext("2d");
448
+ if (ctx) {
449
+ const grad = ctx.createLinearGradient(0, 0, 0, 256);
450
+ grad.addColorStop(0, "rgba(255, 255, 255, 0.0)");
451
+ grad.addColorStop(0.14, "rgba(255, 255, 255, 0.92)");
452
+ grad.addColorStop(0.58, "rgba(255, 255, 255, 0.45)");
453
+ grad.addColorStop(1, "rgba(255, 255, 255, 0.0)");
454
+ ctx.fillStyle = grad;
455
+ ctx.fillRect(0, 0, 128, 256);
456
+ }
457
+ const tex = new THREE.CanvasTexture(canvas);
458
+ tex.needsUpdate = true;
459
+ spotlightBeamTextureCache.set(key, tex);
460
+ return tex;
461
+ }
462
+ var ContactShadowDisc3D = ({
463
+ position = [0, 0.012, 0],
464
+ radius = 0.85,
465
+ opacity = 0.3,
466
+ color = "#0F172A",
467
+ scaleX = 1,
468
+ scaleZ = 1
469
+ }) => {
470
+ const tex = useMemo3(() => getRadialDiscTexture(color), [color]);
471
+ return /* @__PURE__ */ jsxs3(
472
+ "mesh",
473
+ {
474
+ position,
475
+ rotation: [-Math.PI / 2, 0, 0],
476
+ scale: [scaleX, scaleZ, 1],
477
+ renderOrder: 1,
478
+ children: [
479
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [radius * 2, radius * 2] }),
480
+ /* @__PURE__ */ jsx3(
481
+ "meshBasicMaterial",
482
+ {
483
+ map: tex ?? void 0,
484
+ color,
485
+ transparent: true,
486
+ opacity,
487
+ depthWrite: false,
488
+ polygonOffset: true,
489
+ polygonOffsetFactor: -2
490
+ }
491
+ )
492
+ ]
493
+ }
494
+ );
495
+ };
496
+ var StagePostProcessing3D = ({
497
+ fogColor = "#F8F9FF",
498
+ fogDensity = 0.02,
499
+ spotlightBeams = true,
500
+ beamColor = "#FFF7ED",
501
+ beamOpacity = 0.075,
502
+ beamOrigin = [-4.2, 8.2, 2.8],
503
+ beamTarget = [0, 0, 0],
504
+ depthHaze = true,
505
+ hazeColor = "#E0E7FF",
506
+ hazeOpacity = 0.16,
507
+ seed = 303
508
+ }) => {
509
+ const scene = useThree((state) => state.scene);
510
+ const frame = useCurrentFrame3();
511
+ const { fps } = useVideoConfig3();
512
+ useLayoutEffect(() => {
513
+ const previousFog = scene.fog;
514
+ if (fogDensity > 0) {
515
+ scene.fog = new THREE.FogExp2(fogColor, fogDensity);
516
+ } else {
517
+ scene.fog = null;
518
+ }
519
+ return () => {
520
+ scene.fog = previousFog;
521
+ };
522
+ }, [scene, fogColor, fogDensity]);
523
+ const beamCount = typeof spotlightBeams === "number" ? Math.max(0, Math.min(6, Math.round(spotlightBeams))) : spotlightBeams ? 2 : 0;
524
+ const beams = useMemo3(() => {
525
+ if (beamCount === 0) return [];
526
+ const rand = createSeededRandom3(`beams3d:${seed}`);
527
+ const list = [];
528
+ for (let i = 0; i < beamCount; i++) {
529
+ const spreadX = i === 0 ? 0 : (i % 2 === 1 ? 1 : -1) * (3.6 + rand() * 1.8);
530
+ const targetSpreadX = i === 0 ? 0 : spreadX * 0.32;
531
+ list.push({
532
+ origin: [
533
+ beamOrigin[0] + spreadX,
534
+ beamOrigin[1] + (i === 0 ? 0 : rand() * 0.8),
535
+ beamOrigin[2] - i * 0.9
536
+ ],
537
+ target: [
538
+ beamTarget[0] + targetSpreadX,
539
+ beamTarget[1],
540
+ beamTarget[2] - i * 0.35
541
+ ],
542
+ topRadius: 0.28 + rand() * 0.12,
543
+ bottomRadius: 2.15 + rand() * 0.65,
544
+ phase: rand() * Math.PI * 2
545
+ });
546
+ }
547
+ return list;
548
+ }, [beamCount, beamOrigin, beamTarget, seed]);
549
+ const motes = useMemo3(() => {
550
+ const rand = createSeededRandom3(`motes3d:${seed}`);
551
+ return Array.from({ length: 16 }, (_, idx) => ({
552
+ id: idx,
553
+ baseX: (rand() - 0.5) * 7.2,
554
+ baseY: 0.6 + rand() * 4.2,
555
+ baseZ: (rand() - 0.4) * 5.2,
556
+ radius: 0.024 + rand() * 0.032,
557
+ phase: rand() * Math.PI * 2,
558
+ freq: 0.031 + rand() * 0.02
559
+ }));
560
+ }, [seed]);
561
+ const coneTex = useMemo3(() => getVolumetricConeTexture("default"), []);
562
+ const hazeTex = useMemo3(() => getRadialDiscTexture("#FFFFFF"), []);
563
+ const f60 = frame * (60 / Math.max(1, fps));
564
+ return /* @__PURE__ */ jsxs3("group", { name: "stage-post-processing-3d", children: [
565
+ beams.map((b, idx) => {
566
+ const dir = new THREE.Vector3(
567
+ b.target[0] - b.origin[0],
568
+ b.target[1] - b.origin[1],
569
+ b.target[2] - b.origin[2]
570
+ );
571
+ const len = Math.max(0.5, dir.length());
572
+ const mid = [
573
+ (b.origin[0] + b.target[0]) * 0.5,
574
+ (b.origin[1] + b.target[1]) * 0.5,
575
+ (b.origin[2] + b.target[2]) * 0.5
576
+ ];
577
+ const quat = new THREE.Quaternion().setFromUnitVectors(
578
+ new THREE.Vector3(0, -1, 0),
579
+ dir.clone().normalize()
580
+ );
581
+ const euler = new THREE.Euler().setFromQuaternion(quat);
582
+ const breathe = 0.86 + 0.14 * Math.sin(f60 * 0.033 + b.phase);
583
+ return /* @__PURE__ */ jsxs3(
584
+ "mesh",
585
+ {
586
+ position: mid,
587
+ rotation: [euler.x, euler.y, euler.z],
588
+ renderOrder: 10,
589
+ children: [
590
+ /* @__PURE__ */ jsx3(
591
+ "cylinderGeometry",
592
+ {
593
+ args: [b.topRadius, b.bottomRadius, len, 32, 1, true]
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsx3(
597
+ "meshBasicMaterial",
598
+ {
599
+ map: coneTex ?? void 0,
600
+ color: beamColor,
601
+ transparent: true,
602
+ opacity: beamOpacity * breathe,
603
+ blending: THREE.AdditiveBlending,
604
+ depthWrite: false,
605
+ side: THREE.DoubleSide
606
+ }
607
+ )
608
+ ]
609
+ },
610
+ `beam-${idx}`
611
+ );
612
+ }),
613
+ beamCount > 0 && motes.map((m) => {
614
+ const x = m.baseX + Math.sin(f60 * m.freq + m.phase) * 0.22;
615
+ const y = m.baseY + Math.cos(f60 * (m.freq * 0.85) + m.phase) * 0.18;
616
+ const z = m.baseZ + Math.sin(f60 * 0.03 + m.phase * 1.3) * 0.16;
617
+ const twinkle = 0.45 + 0.55 * Math.sin(f60 * 0.042 + m.phase);
618
+ return /* @__PURE__ */ jsxs3("mesh", { position: [x, y, z], renderOrder: 11, children: [
619
+ /* @__PURE__ */ jsx3("sphereGeometry", { args: [m.radius, 10, 10] }),
620
+ /* @__PURE__ */ jsx3(
621
+ "meshBasicMaterial",
622
+ {
623
+ color: beamColor,
624
+ transparent: true,
625
+ opacity: Math.min(0.65, beamOpacity * 5.2 * twinkle),
626
+ blending: THREE.AdditiveBlending,
627
+ depthWrite: false
628
+ }
629
+ )
630
+ ] }, `mote-${m.id}`);
631
+ }),
632
+ depthHaze && /* @__PURE__ */ jsxs3(Fragment, { children: [
633
+ /* @__PURE__ */ jsxs3("mesh", { position: [-2.2, 2.8, -5.8], renderOrder: 2, children: [
634
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [14, 7.5] }),
635
+ /* @__PURE__ */ jsx3(
636
+ "meshBasicMaterial",
637
+ {
638
+ map: hazeTex ?? void 0,
639
+ color: hazeColor,
640
+ transparent: true,
641
+ opacity: hazeOpacity,
642
+ depthWrite: false
643
+ }
644
+ )
645
+ ] }),
646
+ /* @__PURE__ */ jsxs3("mesh", { position: [2.4, 3.2, -7.6], renderOrder: 2, children: [
647
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [16, 8.5] }),
648
+ /* @__PURE__ */ jsx3(
649
+ "meshBasicMaterial",
650
+ {
651
+ map: hazeTex ?? void 0,
652
+ color: hazeColor,
653
+ transparent: true,
654
+ opacity: hazeOpacity * 0.78,
655
+ depthWrite: false
656
+ }
657
+ )
658
+ ] })
659
+ ] })
660
+ ] });
661
+ };
662
+
663
+ // packages/kits/src/vfx/SceneTransition.tsx
664
+ import {
665
+ AbsoluteFill as AbsoluteFill3,
666
+ Easing,
667
+ interpolate,
668
+ useCurrentFrame as useCurrentFrame4,
669
+ useVideoConfig as useVideoConfig4
670
+ } from "remotion";
671
+ import { STUDIO_THEME } from "@make-pictures/core";
672
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
673
+ var SceneTransition = ({
674
+ atFrame,
675
+ durationInFrames = 28,
676
+ type = "iris-wipe",
677
+ accentColor = STUDIO_THEME.colors.brand[600],
678
+ backdropColor = "#0F172A",
679
+ direction = "left",
680
+ seed = 51,
681
+ zIndex = 80
682
+ }) => {
683
+ const frame = useCurrentFrame4();
684
+ const { width, height } = useVideoConfig4();
685
+ const dur = Math.max(6, durationInFrames);
686
+ const half = dur / 2;
687
+ const startFrame = atFrame - half;
688
+ const endFrame = atFrame + half;
689
+ if (frame <= startFrame || frame >= endFrame) {
690
+ return null;
691
+ }
692
+ const u = (frame - startFrame) / dur;
693
+ const coverProgress = u <= 0.5 ? interpolate(u, [0, 0.5], [0, 1], {
694
+ easing: Easing.inOut(Easing.cubic),
695
+ extrapolateLeft: "clamp",
696
+ extrapolateRight: "clamp"
697
+ }) : interpolate(u, [0.5, 1], [1, 0], {
698
+ easing: Easing.inOut(Easing.cubic),
699
+ extrapolateLeft: "clamp",
700
+ extrapolateRight: "clamp"
701
+ });
702
+ const diagRadius = Math.hypot(width, height) * 0.55;
703
+ if (type === "flash-cut") {
704
+ const flashOpacity = u <= 0.5 ? Math.pow(u * 2, 2.2) : Math.pow(Math.max(0, 1 - (u - 0.5) * 2), 1.65);
705
+ return /* @__PURE__ */ jsx4(
706
+ AbsoluteFill3,
707
+ {
708
+ style: {
709
+ pointerEvents: "none",
710
+ zIndex,
711
+ opacity: flashOpacity,
712
+ background: `radial-gradient(circle at 50% 50%, #FFFFFF 0%, #FFFBEB 45%, ${accentColor} 100%)`
713
+ }
714
+ }
715
+ );
716
+ }
717
+ if (type === "whip-pan") {
718
+ const sweep = interpolate(u, [0, 1], [-1.25, 1.25], {
719
+ easing: Easing.inOut(Easing.cubic)
720
+ });
721
+ const isHorizontal = direction === "left" || direction === "right";
722
+ const sign = direction === "left" || direction === "up" ? -1 : 1;
723
+ const tx = isHorizontal ? sweep * sign * width : 0;
724
+ const ty = !isHorizontal ? sweep * sign * height : 0;
725
+ return /* @__PURE__ */ jsx4(
726
+ AbsoluteFill3,
727
+ {
728
+ style: {
729
+ pointerEvents: "none",
730
+ overflow: "hidden",
731
+ zIndex
732
+ },
733
+ children: /* @__PURE__ */ jsx4(
734
+ "div",
735
+ {
736
+ style: {
737
+ position: "absolute",
738
+ left: -width * 0.35,
739
+ top: -height * 0.35,
740
+ width: width * 1.7,
741
+ height: height * 1.7,
742
+ transform: `translate3d(${tx}px, ${ty}px, 0) skewX(${isHorizontal ? -10 * sign : 0}deg)`,
743
+ background: isHorizontal ? `linear-gradient(90deg, transparent 0%, ${accentColor} 16%, ${backdropColor} 32%, ${backdropColor} 68%, ${accentColor} 84%, transparent 100%)` : `linear-gradient(180deg, transparent 0%, ${accentColor} 16%, ${backdropColor} 32%, ${backdropColor} 68%, ${accentColor} 84%, transparent 100%)`,
744
+ boxShadow: `0 0 80px ${accentColor}88`
745
+ }
746
+ }
747
+ )
748
+ }
749
+ );
750
+ }
751
+ if (type === "paper-curtain") {
752
+ const slidePct = (1 - coverProgress) * 104;
753
+ return /* @__PURE__ */ jsxs4(
754
+ AbsoluteFill3,
755
+ {
756
+ style: {
757
+ pointerEvents: "none",
758
+ overflow: "hidden",
759
+ zIndex
760
+ },
761
+ children: [
762
+ /* @__PURE__ */ jsx4(
763
+ "div",
764
+ {
765
+ style: {
766
+ position: "absolute",
767
+ left: 0,
768
+ top: 0,
769
+ width: "53%",
770
+ height: "100%",
771
+ backgroundColor: "#FFFBEB",
772
+ borderRight: `14px solid ${accentColor}`,
773
+ boxShadow: "18px 0 48px rgba(15, 23, 42, 0.35)",
774
+ transform: `translate3d(${-slidePct}%, 0, 0)`
775
+ }
776
+ }
777
+ ),
778
+ /* @__PURE__ */ jsx4(
779
+ "div",
780
+ {
781
+ style: {
782
+ position: "absolute",
783
+ right: 0,
784
+ top: 0,
785
+ width: "53%",
786
+ height: "100%",
787
+ backgroundColor: "#FEF3C7",
788
+ borderLeft: `14px solid ${accentColor}`,
789
+ boxShadow: "-18px 0 48px rgba(15, 23, 42, 0.35)",
790
+ transform: `translate3d(${slidePct}%, 0, 0)`
791
+ }
792
+ }
793
+ )
794
+ ]
795
+ }
796
+ );
797
+ }
798
+ if (type === "ink-wash") {
799
+ const filterId = `mp-ink-wash-${Math.abs(seed)}`;
800
+ const blobRadius = coverProgress * diagRadius * 1.22;
801
+ return /* @__PURE__ */ jsx4(
802
+ AbsoluteFill3,
803
+ {
804
+ style: {
805
+ pointerEvents: "none",
806
+ overflow: "hidden",
807
+ zIndex
808
+ },
809
+ children: /* @__PURE__ */ jsxs4("svg", { width, height, viewBox: `0 0 ${width} ${height}`, children: [
810
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("filter", { id: filterId, x: "-20%", y: "-20%", width: "140%", height: "140%", children: [
811
+ /* @__PURE__ */ jsx4(
812
+ "feTurbulence",
813
+ {
814
+ type: "fractalNoise",
815
+ baseFrequency: "0.012",
816
+ numOctaves: 3,
817
+ seed: Math.abs(seed) % 97,
818
+ result: "noise"
819
+ }
820
+ ),
821
+ /* @__PURE__ */ jsx4(
822
+ "feDisplacementMap",
823
+ {
824
+ in: "SourceGraphic",
825
+ in2: "noise",
826
+ scale: Math.round(140 * Math.sin(Math.PI * u)),
827
+ xChannelSelector: "R",
828
+ yChannelSelector: "G"
829
+ }
830
+ )
831
+ ] }) }),
832
+ /* @__PURE__ */ jsxs4("g", { filter: `url(#${filterId})`, children: [
833
+ /* @__PURE__ */ jsx4(
834
+ "circle",
835
+ {
836
+ cx: width / 2,
837
+ cy: height / 2,
838
+ r: blobRadius + 18,
839
+ fill: accentColor
840
+ }
841
+ ),
842
+ /* @__PURE__ */ jsx4(
843
+ "circle",
844
+ {
845
+ cx: width / 2,
846
+ cy: height / 2,
847
+ r: blobRadius,
848
+ fill: backdropColor
849
+ }
850
+ )
851
+ ] })
852
+ ] })
853
+ }
854
+ );
855
+ }
856
+ const holeRadius = Math.max(0, (1 - coverProgress) * diagRadius);
857
+ const maskId = `mp-iris-mask-${Math.abs(seed)}-${atFrame}`;
858
+ return /* @__PURE__ */ jsx4(
859
+ AbsoluteFill3,
860
+ {
861
+ style: {
862
+ pointerEvents: "none",
863
+ overflow: "hidden",
864
+ zIndex
865
+ },
866
+ children: /* @__PURE__ */ jsxs4("svg", { width, height, viewBox: `0 0 ${width} ${height}`, children: [
867
+ /* @__PURE__ */ jsx4("defs", { children: /* @__PURE__ */ jsxs4("mask", { id: maskId, children: [
868
+ /* @__PURE__ */ jsx4("rect", { x: 0, y: 0, width, height, fill: "white" }),
869
+ /* @__PURE__ */ jsx4(
870
+ "circle",
871
+ {
872
+ cx: width / 2,
873
+ cy: height / 2,
874
+ r: holeRadius,
875
+ fill: "black"
876
+ }
877
+ )
878
+ ] }) }),
879
+ /* @__PURE__ */ jsx4(
880
+ "rect",
881
+ {
882
+ x: 0,
883
+ y: 0,
884
+ width,
885
+ height,
886
+ fill: backdropColor,
887
+ mask: `url(#${maskId})`
888
+ }
889
+ ),
890
+ holeRadius > 2 && /* @__PURE__ */ jsx4(
891
+ "circle",
892
+ {
893
+ cx: width / 2,
894
+ cy: height / 2,
895
+ r: holeRadius,
896
+ fill: "none",
897
+ stroke: accentColor,
898
+ strokeWidth: 10
899
+ }
900
+ )
901
+ ] })
902
+ }
903
+ );
904
+ };
905
+
906
+ export {
907
+ CinematicLensOverlay,
908
+ ParticleField,
909
+ ContactShadowDisc3D,
910
+ StagePostProcessing3D,
911
+ SceneTransition
912
+ };