egaki 0.5.0 → 0.7.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 (280) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/cli/cache-utils.d.ts +33 -0
  3. package/dist/cli/cache-utils.d.ts.map +1 -0
  4. package/dist/cli/cache-utils.js +157 -0
  5. package/dist/cli/cached-generate.d.ts +105 -0
  6. package/dist/cli/cached-generate.d.ts.map +1 -0
  7. package/dist/cli/cached-generate.js +208 -0
  8. package/dist/cli/cached-generate.test.d.ts +11 -0
  9. package/dist/cli/cached-generate.test.d.ts.map +1 -0
  10. package/dist/cli/cached-generate.test.js +431 -0
  11. package/dist/cli/cartesia-provider.d.ts +16 -0
  12. package/dist/cli/cartesia-provider.d.ts.map +1 -0
  13. package/dist/cli/cartesia-provider.js +184 -0
  14. package/dist/cli/chatgpt-auth.d.ts.map +1 -0
  15. package/dist/cli/cli.d.ts +4 -0
  16. package/dist/cli/cli.d.ts.map +1 -0
  17. package/dist/cli/cli.js +1329 -0
  18. package/dist/cli/clone-generate.d.ts +26 -0
  19. package/dist/cli/clone-generate.d.ts.map +1 -0
  20. package/dist/cli/clone-generate.js +148 -0
  21. package/dist/{credentials.d.ts → cli/credentials.d.ts} +11 -2
  22. package/dist/cli/credentials.d.ts.map +1 -0
  23. package/dist/{credentials.js → cli/credentials.js} +88 -5
  24. package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
  25. package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
  26. package/dist/cli/elevenlabs-speech-provider.js +199 -0
  27. package/dist/cli/generate.d.ts +140 -0
  28. package/dist/cli/generate.d.ts.map +1 -0
  29. package/dist/cli/generate.js +604 -0
  30. package/dist/cli/generate.test.d.ts +2 -0
  31. package/dist/cli/generate.test.d.ts.map +1 -0
  32. package/dist/cli/generate.test.js +131 -0
  33. package/dist/cli/login.d.ts.map +1 -0
  34. package/dist/{login.js → cli/login.js} +45 -4
  35. package/dist/cli/main.d.ts +3 -0
  36. package/dist/cli/main.d.ts.map +1 -0
  37. package/dist/cli/main.js +7 -0
  38. package/dist/cli/model-catalog.d.ts +141 -0
  39. package/dist/cli/model-catalog.d.ts.map +1 -0
  40. package/dist/cli/model-catalog.js +2135 -0
  41. package/dist/{models.d.ts → cli/models.d.ts} +16 -9
  42. package/dist/cli/models.d.ts.map +1 -0
  43. package/dist/cli/models.js +313 -0
  44. package/dist/cli/open-browser.d.ts.map +1 -0
  45. package/dist/cli/openai-speech-provider.d.ts +3 -0
  46. package/dist/cli/openai-speech-provider.d.ts.map +1 -0
  47. package/dist/cli/openai-speech-provider.js +28 -0
  48. package/dist/cli/separation-generate.d.ts +34 -0
  49. package/dist/cli/separation-generate.d.ts.map +1 -0
  50. package/dist/cli/separation-generate.js +117 -0
  51. package/dist/cli/speech-catalog.d.ts +35 -0
  52. package/dist/cli/speech-catalog.d.ts.map +1 -0
  53. package/dist/cli/speech-catalog.js +195 -0
  54. package/dist/cli/speech-generate.d.ts +86 -0
  55. package/dist/cli/speech-generate.d.ts.map +1 -0
  56. package/dist/cli/speech-generate.js +156 -0
  57. package/dist/cli/speech-models.d.ts +8 -0
  58. package/dist/cli/speech-models.d.ts.map +1 -0
  59. package/dist/cli/speech-models.js +109 -0
  60. package/dist/cli/speech-provider.test.d.ts +2 -0
  61. package/dist/cli/speech-provider.test.d.ts.map +1 -0
  62. package/dist/cli/speech-provider.test.js +197 -0
  63. package/dist/cli/subscription.d.ts.map +1 -0
  64. package/dist/cli/transcription-catalog.d.ts +35 -0
  65. package/dist/cli/transcription-catalog.d.ts.map +1 -0
  66. package/dist/cli/transcription-catalog.js +192 -0
  67. package/dist/cli/transcription-generate.d.ts +62 -0
  68. package/dist/cli/transcription-generate.d.ts.map +1 -0
  69. package/dist/cli/transcription-generate.js +144 -0
  70. package/dist/cli/transcription-models.d.ts +9 -0
  71. package/dist/cli/transcription-models.d.ts.map +1 -0
  72. package/dist/cli/transcription-models.js +100 -0
  73. package/dist/cli/transcription-providers.d.ts +22 -0
  74. package/dist/cli/transcription-providers.d.ts.map +1 -0
  75. package/dist/cli/transcription-providers.js +290 -0
  76. package/dist/cli/video-model-catalog.d.ts +2 -0
  77. package/dist/cli/video-model-catalog.d.ts.map +1 -0
  78. package/dist/cli/video-model-catalog.js +5 -0
  79. package/dist/cli/xai-auth.d.ts +29 -0
  80. package/dist/cli/xai-auth.d.ts.map +1 -0
  81. package/dist/cli/xai-auth.js +325 -0
  82. package/dist/vite/angled-screen.d.ts +57 -0
  83. package/dist/vite/angled-screen.d.ts.map +1 -0
  84. package/dist/vite/angled-screen.js +115 -0
  85. package/dist/vite/app.d.ts +51 -0
  86. package/dist/vite/app.d.ts.map +1 -0
  87. package/dist/vite/app.js +150 -0
  88. package/dist/vite/bands-shader.d.ts +47 -0
  89. package/dist/vite/bands-shader.d.ts.map +1 -0
  90. package/dist/vite/bands-shader.js +267 -0
  91. package/dist/vite/code-block.d.ts +107 -0
  92. package/dist/vite/code-block.d.ts.map +1 -0
  93. package/dist/vite/code-block.js +641 -0
  94. package/dist/vite/components.d.ts +36 -0
  95. package/dist/vite/components.d.ts.map +1 -0
  96. package/dist/vite/components.js +113 -0
  97. package/dist/vite/dispersion-rings-shader.d.ts +44 -0
  98. package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
  99. package/dist/vite/dispersion-rings-shader.js +282 -0
  100. package/dist/vite/easing-curves.d.ts +129 -0
  101. package/dist/vite/easing-curves.d.ts.map +1 -0
  102. package/dist/vite/easing-curves.js +584 -0
  103. package/dist/vite/easing-curves.test.d.ts +2 -0
  104. package/dist/vite/easing-curves.test.d.ts.map +1 -0
  105. package/dist/vite/easing-curves.test.js +200 -0
  106. package/dist/vite/generated-media-client.d.ts +35 -0
  107. package/dist/vite/generated-media-client.d.ts.map +1 -0
  108. package/dist/vite/generated-media-client.js +76 -0
  109. package/dist/vite/keyframes.d.ts +129 -0
  110. package/dist/vite/keyframes.d.ts.map +1 -0
  111. package/dist/vite/keyframes.js +194 -0
  112. package/dist/vite/layout-editor.d.ts +45 -0
  113. package/dist/vite/layout-editor.d.ts.map +1 -0
  114. package/dist/vite/layout-editor.js +587 -0
  115. package/dist/vite/layout-transition.d.ts +136 -0
  116. package/dist/vite/layout-transition.d.ts.map +1 -0
  117. package/dist/vite/layout-transition.js +581 -0
  118. package/dist/vite/liquid-gradient-shader.d.ts +62 -0
  119. package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
  120. package/dist/vite/liquid-gradient-shader.js +413 -0
  121. package/dist/vite/mdx-client.d.ts +15 -0
  122. package/dist/vite/mdx-client.d.ts.map +1 -0
  123. package/dist/vite/mdx-client.js +265 -0
  124. package/dist/vite/mdx-parse.d.ts +102 -0
  125. package/dist/vite/mdx-parse.d.ts.map +1 -0
  126. package/dist/vite/mdx-parse.js +251 -0
  127. package/dist/vite/mdx-provided-components.d.ts +18 -0
  128. package/dist/vite/mdx-provided-components.d.ts.map +1 -0
  129. package/dist/vite/mdx-provided-components.js +14 -0
  130. package/dist/vite/mdx-video.d.ts +327 -0
  131. package/dist/vite/mdx-video.d.ts.map +1 -0
  132. package/dist/vite/mdx-video.js +596 -0
  133. package/dist/vite/mdx-video.test.d.ts +9 -0
  134. package/dist/vite/mdx-video.test.d.ts.map +1 -0
  135. package/dist/vite/mdx-video.test.js +1945 -0
  136. package/dist/vite/media-components.d.ts +45 -0
  137. package/dist/vite/media-components.d.ts.map +1 -0
  138. package/dist/vite/media-components.js +355 -0
  139. package/dist/vite/media-duration-store.d.ts +66 -0
  140. package/dist/vite/media-duration-store.d.ts.map +1 -0
  141. package/dist/vite/media-duration-store.js +204 -0
  142. package/dist/vite/player-page.d.ts +35 -0
  143. package/dist/vite/player-page.d.ts.map +1 -0
  144. package/dist/vite/player-page.js +720 -0
  145. package/dist/vite/render-client.d.ts +18 -0
  146. package/dist/vite/render-client.d.ts.map +1 -0
  147. package/dist/vite/render-client.js +67 -0
  148. package/dist/vite/sdk.d.ts +221 -0
  149. package/dist/vite/sdk.d.ts.map +1 -0
  150. package/dist/vite/sdk.js +355 -0
  151. package/dist/vite/server-components.d.ts +55 -0
  152. package/dist/vite/server-components.d.ts.map +1 -0
  153. package/dist/vite/server-components.js +156 -0
  154. package/dist/vite/server-mdx.d.ts +74 -0
  155. package/dist/vite/server-mdx.d.ts.map +1 -0
  156. package/dist/vite/server-mdx.js +259 -0
  157. package/dist/vite/shader-renderer.d.ts +48 -0
  158. package/dist/vite/shader-renderer.d.ts.map +1 -0
  159. package/dist/vite/shader-renderer.js +411 -0
  160. package/dist/vite/store-hooks.d.ts +12 -0
  161. package/dist/vite/store-hooks.d.ts.map +1 -0
  162. package/dist/vite/store-hooks.js +93 -0
  163. package/dist/vite/store.d.ts +75 -0
  164. package/dist/vite/store.d.ts.map +1 -0
  165. package/dist/vite/store.js +52 -0
  166. package/dist/vite/store.test.d.ts +6 -0
  167. package/dist/vite/store.test.d.ts.map +1 -0
  168. package/dist/vite/store.test.js +172 -0
  169. package/dist/vite/tweakpane-hook.d.ts +94 -0
  170. package/dist/vite/tweakpane-hook.d.ts.map +1 -0
  171. package/dist/vite/tweakpane-hook.js +450 -0
  172. package/dist/vite/vite-plugin.d.ts +28 -0
  173. package/dist/vite/vite-plugin.d.ts.map +1 -0
  174. package/dist/vite/vite-plugin.js +538 -0
  175. package/dist/vite/wave-gradient-shader.d.ts +41 -0
  176. package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
  177. package/dist/vite/wave-gradient-shader.js +207 -0
  178. package/package.json +103 -25
  179. package/skills/egaki/SKILL.md +162 -1
  180. package/src/cli/cache-utils.ts +169 -0
  181. package/src/cli/cached-generate.test.ts +531 -0
  182. package/src/cli/cached-generate.ts +348 -0
  183. package/src/cli/cartesia-provider.ts +249 -0
  184. package/src/cli/cli.ts +1818 -0
  185. package/src/cli/clone-generate.ts +202 -0
  186. package/src/{credentials.ts → cli/credentials.ts} +90 -7
  187. package/src/cli/elevenlabs-speech-provider.ts +243 -0
  188. package/src/cli/generate.test.ts +166 -0
  189. package/src/cli/generate.ts +912 -0
  190. package/src/{login.ts → cli/login.ts} +48 -3
  191. package/src/cli/main.ts +8 -0
  192. package/src/cli/model-catalog.ts +2336 -0
  193. package/src/cli/models.ts +408 -0
  194. package/src/cli/openai-speech-provider.ts +34 -0
  195. package/src/cli/separation-generate.ts +187 -0
  196. package/src/cli/speech-catalog.ts +253 -0
  197. package/src/cli/speech-generate.ts +275 -0
  198. package/src/cli/speech-models.ts +146 -0
  199. package/src/cli/speech-provider.test.ts +226 -0
  200. package/src/cli/transcription-catalog.ts +250 -0
  201. package/src/cli/transcription-generate.ts +214 -0
  202. package/src/cli/transcription-models.ts +136 -0
  203. package/src/cli/transcription-providers.ts +427 -0
  204. package/src/cli/video-model-catalog.ts +14 -0
  205. package/src/cli/xai-auth.ts +396 -0
  206. package/src/vite/angled-screen.tsx +214 -0
  207. package/src/vite/app.tsx +197 -0
  208. package/src/vite/bands-shader.tsx +319 -0
  209. package/src/vite/code-block.tsx +939 -0
  210. package/src/vite/components.tsx +278 -0
  211. package/src/vite/dispersion-rings-shader.tsx +331 -0
  212. package/src/vite/easing-curves.test.ts +232 -0
  213. package/src/vite/easing-curves.ts +743 -0
  214. package/src/vite/generated-media-client.tsx +121 -0
  215. package/src/vite/keyframes.tsx +347 -0
  216. package/src/vite/layout-editor.tsx +709 -0
  217. package/src/vite/layout-transition.tsx +706 -0
  218. package/src/vite/liquid-gradient-shader.tsx +474 -0
  219. package/src/vite/mdx-client.tsx +420 -0
  220. package/src/vite/mdx-parse.ts +343 -0
  221. package/src/vite/mdx-provided-components.ts +19 -0
  222. package/src/vite/mdx-video.test.tsx +2219 -0
  223. package/src/vite/mdx-video.tsx +976 -0
  224. package/src/vite/media-components.tsx +473 -0
  225. package/src/vite/media-duration-store.ts +225 -0
  226. package/src/vite/motion-timing.ts +194 -0
  227. package/src/vite/player-page.tsx +1107 -0
  228. package/src/vite/render-client.ts +84 -0
  229. package/src/vite/sdk.ts +598 -0
  230. package/src/vite/server-components.tsx +241 -0
  231. package/src/vite/server-mdx.ts +285 -0
  232. package/src/vite/shader-renderer.tsx +535 -0
  233. package/src/vite/store-hooks.ts +117 -0
  234. package/src/vite/store.test.ts +202 -0
  235. package/src/vite/store.ts +115 -0
  236. package/src/vite/styles.css +21 -0
  237. package/src/vite/tweakpane-hook.tsx +593 -0
  238. package/src/vite/virtual-modules.d.ts +30 -0
  239. package/src/vite/vite-plugin.ts +584 -0
  240. package/src/vite/wave-gradient-shader.tsx +253 -0
  241. package/README.md +0 -266
  242. package/dist/antigravity-auth.d.ts +0 -15
  243. package/dist/antigravity-auth.d.ts.map +0 -1
  244. package/dist/antigravity-auth.js +0 -544
  245. package/dist/antigravity-image.d.ts +0 -11
  246. package/dist/antigravity-image.d.ts.map +0 -1
  247. package/dist/antigravity-image.js +0 -285
  248. package/dist/chatgpt-auth.d.ts.map +0 -1
  249. package/dist/cli.d.ts +0 -3
  250. package/dist/cli.d.ts.map +0 -1
  251. package/dist/cli.js +0 -968
  252. package/dist/credentials.d.ts.map +0 -1
  253. package/dist/gateway-model-candidates.generated.d.ts +0 -11
  254. package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
  255. package/dist/gateway-model-candidates.generated.js +0 -64
  256. package/dist/login.d.ts.map +0 -1
  257. package/dist/model-catalog.d.ts +0 -42
  258. package/dist/model-catalog.d.ts.map +0 -1
  259. package/dist/model-catalog.js +0 -1001
  260. package/dist/models.d.ts.map +0 -1
  261. package/dist/models.js +0 -246
  262. package/dist/open-browser.d.ts.map +0 -1
  263. package/dist/subscription.d.ts.map +0 -1
  264. package/dist/video-model-catalog.d.ts +0 -52
  265. package/dist/video-model-catalog.d.ts.map +0 -1
  266. package/dist/video-model-catalog.js +0 -306
  267. package/src/cli.ts +0 -1378
  268. package/src/model-catalog.ts +0 -1077
  269. package/src/models.ts +0 -309
  270. package/src/video-model-catalog.ts +0 -363
  271. /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
  272. /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
  273. /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
  274. /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
  275. /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
  276. /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
  277. /package/dist/{subscription.js → cli/subscription.js} +0 -0
  278. /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
  279. /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
  280. /package/src/{subscription.ts → cli/subscription.ts} +0 -0
@@ -0,0 +1,581 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * LayoutTransition — FLIP-based layout animation across section boundaries.
5
+ *
6
+ * <LayoutTransition id="x"> wraps an element that should animate from its
7
+ * position in the previous section to its position in the current section.
8
+ * When two consecutive sections both contain <LayoutTransition id="x">,
9
+ * the element in the current section starts at the previous section's
10
+ * position and springs to its natural position.
11
+ *
12
+ * Architecture (no temporal state — seek-safe by design):
13
+ * - The previous section is re-rendered in a hidden "ghost" container,
14
+ * pinned at its last frame via Remotion <Freeze>. This happens in
15
+ * SectionWithLayoutTransition (player-page.tsx).
16
+ * - LayoutContainerContext tells each LayoutTransition whether it lives
17
+ * in the ghost or the visible container.
18
+ * - Entries register into LayoutRegistryContext in useLayoutEffect.
19
+ * React runs effects in tree order, so all LayoutTransition effects
20
+ * (descendants of containers rendered before the layer) complete before
21
+ * LayoutAnimationLayer's effect runs.
22
+ * - LayoutAnimationLayer measures ghost vs visible rects every frame and
23
+ * writes interpolated FLIP transforms directly to the visible wrappers.
24
+ *
25
+ * Hard-won correctness details (do not "simplify" these away):
26
+ * - getBoundingClientRect() INCLUDES CSS transforms. Transforms must be
27
+ * reset before measuring, or each frame compounds the previous frame's
28
+ * FLIP offset and the element drifts.
29
+ * - The Remotion Player scales the composition to fit the viewport.
30
+ * Client-px deltas must be divided by that scale before being used as
31
+ * transform values in composition space. The layer measures the scale
32
+ * from its own composition-sized AbsoluteFill.
33
+ * - The web-renderer does not support z-index; layer order is DOM order.
34
+ * The ghost renders BEFORE the visible content so the visible section
35
+ * paints over it even if a renderer ignores visibility:hidden.
36
+ */
37
+ import { createContext, useContext, useLayoutEffect, useRef, useState, } from 'react';
38
+ import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig, } from 'remotion';
39
+ import { dspring } from "./mdx-video.js";
40
+ const LayoutRegistryContext = createContext(null);
41
+ export const LayoutContainerContext = createContext('visible');
42
+ /** Parse rgb()/rgba()/hex color string to [r, g, b, a] tuple. */
43
+ function parseColor(str) {
44
+ if (!str || str === 'transparent' || str === 'rgba(0, 0, 0, 0)')
45
+ return null;
46
+ // rgb(r, g, b) or rgba(r, g, b, a)
47
+ const m = str.match(/rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)/);
48
+ if (m && m[1] && m[2] && m[3])
49
+ return [+m[1], +m[2], +m[3], m[4] !== undefined ? +m[4] : 1];
50
+ return null;
51
+ }
52
+ /** Mix two RGBA colors by progress (0–1). */
53
+ function mixColor(from, to, progress) {
54
+ const r = Math.round(from[0] + (to[0] - from[0]) * progress);
55
+ const g = Math.round(from[1] + (to[1] - from[1]) * progress);
56
+ const b = Math.round(from[2] + (to[2] - from[2]) * progress);
57
+ const a = from[3] + (to[3] - from[3]) * progress;
58
+ return a >= 1 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${a.toFixed(3)})`;
59
+ }
60
+ /** Parse a borderRadius value (px or %) to a number. Returns 0 for unparseable. */
61
+ function parseBorderRadius(val) {
62
+ return parseFloat(val) || 0;
63
+ }
64
+ /** Linearly interpolate two numbers. */
65
+ function mixNumber(from, to, progress) {
66
+ return from + (to - from) * progress;
67
+ }
68
+ /** CSS border-radius corner property names in kebab-case for
69
+ * getPropertyValue() reads and setProperty() writes. */
70
+ const BORDER_RADIUS_PROPS = [
71
+ 'border-top-left-radius',
72
+ 'border-top-right-radius',
73
+ 'border-bottom-left-radius',
74
+ 'border-bottom-right-radius',
75
+ ];
76
+ /**
77
+ * Parse a simple box-shadow string into its numeric components.
78
+ * Returns null for 'none', multiple shadows, or unparseable values.
79
+ *
80
+ * getComputedStyle returns shadows like:
81
+ * "rgba(0, 0, 0, 0.2) 0px 4px 12px 0px"
82
+ * "0px 4px 12px 0px rgba(0, 0, 0, 0.2)"
83
+ *
84
+ * Multiple shadows are separated by commas OUTSIDE parentheses.
85
+ * We must not reject commas inside rgba().
86
+ */
87
+ function parseBoxShadow(val) {
88
+ if (!val || val === 'none')
89
+ return null;
90
+ // Check for multiple shadows: commas outside parentheses.
91
+ // Walk the string tracking paren depth; if we find a comma at depth 0
92
+ // there are multiple shadows, which we don't support.
93
+ let depth = 0;
94
+ for (let i = 0; i < val.length; i++) {
95
+ const ch = val[i];
96
+ if (ch === '(')
97
+ depth++;
98
+ else if (ch === ')')
99
+ depth--;
100
+ else if (ch === ',' && depth === 0)
101
+ return null;
102
+ }
103
+ // Extract the color function or keyword
104
+ const colorMatch = val.match(/(rgba?\([^)]+\)|#[0-9a-fA-F]+)/);
105
+ if (!colorMatch || !colorMatch[1])
106
+ return null;
107
+ const color = colorMatch[1];
108
+ const withoutColor = val.replace(color, '').trim();
109
+ const nums = withoutColor.split(/\s+/).map(parseFloat).filter((n) => !isNaN(n));
110
+ if (nums.length < 2 || nums[0] === undefined || nums[1] === undefined)
111
+ return null;
112
+ return {
113
+ offsetX: nums[0],
114
+ offsetY: nums[1],
115
+ blur: nums[2] ?? 0,
116
+ spread: nums[3] ?? 0,
117
+ color,
118
+ };
119
+ }
120
+ /**
121
+ * Apply FLIP transform and interpolated visual styles to an element.
122
+ *
123
+ * Reads computed styles from the source/target's first child elements (where
124
+ * user visual styles like borderRadius and backgroundColor live) and applies
125
+ * interpolated values to the wrapper element during the transition.
126
+ *
127
+ * Scale correction for borderRadius and boxShadow follows Framer Motion's
128
+ * approach: borderRadius is counter-scaled by the projection delta so corners
129
+ * don't distort; boxShadow offsets and blur are divided by the scale so
130
+ * shadows don't squish.
131
+ */
132
+ function applyFlip({ el, sourceEl, fromRect, toRect, progress, playerScale, entry, }) {
133
+ const inv = 1 - progress;
134
+ let dx = ((fromRect.x - toRect.x) / playerScale) * inv;
135
+ let dy = ((fromRect.y - toRect.y) / playerScale) * inv;
136
+ let sx = 1 + (fromRect.width / toRect.width - 1) * inv;
137
+ let sy = 1 + (fromRect.height / toRect.height - 1) * inv;
138
+ // Mode: constrain which axes animate
139
+ if (entry.mode === 'position') {
140
+ sx = 1;
141
+ sy = 1;
142
+ }
143
+ if (entry.mode === 'size') {
144
+ dx = 0;
145
+ dy = 0;
146
+ }
147
+ el.style.transform = `translate(${dx}px, ${dy}px) scale(${sx}, ${sy})`;
148
+ // Read computed styles from the visual content elements (first child),
149
+ // since user styles live on children, not the LayoutTransition wrapper.
150
+ // Interpolated visual styles are applied to targetVisual (not the wrapper)
151
+ // so they override the child's own styles during the transition. If we
152
+ // wrote to the wrapper, the child's own background/shadow would paint
153
+ // over our interpolated values.
154
+ const sourceVisual = sourceEl.firstElementChild;
155
+ const targetVisual = el.firstElementChild;
156
+ if (!(sourceVisual instanceof HTMLElement) || !(targetVisual instanceof HTMLElement))
157
+ return;
158
+ const fromStyles = getComputedStyle(sourceVisual);
159
+ const toStyles = getComputedStyle(targetVisual);
160
+ // --- Border radius interpolation + scale correction ---
161
+ // Interpolate each corner independently, then counter-scale per axis
162
+ // so corners appear visually correct despite the scale transform.
163
+ // Uses elliptical border-radius (xR / sx, yR / sy) for accurate
164
+ // correction under non-uniform scale, unlike simple average scaling.
165
+ // Only processes px values; % units pass through unchanged (mixing
166
+ // incompatible units would produce incorrect results).
167
+ const hasRadiusChange = BORDER_RADIUS_PROPS.some((prop) => fromStyles.getPropertyValue(prop) !== toStyles.getPropertyValue(prop));
168
+ if (hasRadiusChange || sx !== 1 || sy !== 1) {
169
+ let hasNonZeroRadius = false;
170
+ for (const prop of BORDER_RADIUS_PROPS) {
171
+ const fromVal = fromStyles.getPropertyValue(prop);
172
+ const toVal = toStyles.getPropertyValue(prop);
173
+ const fromIsPercent = fromVal.includes('%');
174
+ const toIsPercent = toVal.includes('%');
175
+ // Only mix compatible units. If either is %, skip interpolation
176
+ // and use the target value directly (same as Motion's fallback).
177
+ if (fromIsPercent || toIsPercent) {
178
+ patchStyle(el, targetVisual, prop, toVal);
179
+ hasNonZeroRadius = hasNonZeroRadius || parseBorderRadius(toVal) > 0;
180
+ continue;
181
+ }
182
+ const fromR = parseBorderRadius(fromVal);
183
+ const toR = parseBorderRadius(toVal);
184
+ const mixed = mixNumber(fromR, toR, progress);
185
+ // Elliptical counter-scale: divide by each axis's scale separately
186
+ // so non-uniform scales (e.g. scale(2, 1)) correct each direction.
187
+ const correctedX = sx !== 0 ? Math.max(mixed / sx, 0) : mixed;
188
+ const correctedY = sy !== 0 ? Math.max(mixed / sy, 0) : mixed;
189
+ patchStyle(el, targetVisual, prop, `${correctedX}px ${correctedY}px`);
190
+ hasNonZeroRadius = hasNonZeroRadius || mixed > 0;
191
+ }
192
+ // Only set overflow:hidden when there's a non-zero radius that needs
193
+ // child clipping. Setting it unconditionally can crop content.
194
+ if (hasNonZeroRadius)
195
+ patchStyle(el, targetVisual, 'overflow', 'hidden');
196
+ }
197
+ // --- Background color interpolation ---
198
+ // Motion doesn't interpolate background during layout transitions,
199
+ // but for video this is valuable: hard color cuts between sections
200
+ // look jarring. We interpolate if both sides have a non-transparent bg.
201
+ const fromColor = parseColor(fromStyles.backgroundColor);
202
+ const toColor = parseColor(toStyles.backgroundColor);
203
+ if (fromColor && toColor) {
204
+ patchStyle(el, targetVisual, 'background-color', mixColor(fromColor, toColor, progress));
205
+ }
206
+ // --- Box shadow scale correction ---
207
+ // When the element is scaled, box-shadow offsets and blur get squished.
208
+ // Counter-scale them by the projection delta, same as Motion's
209
+ // correctBoxShadow. offsetX by sx, offsetY by sy, blur+spread by
210
+ // average scale. Only handles single shadows for simplicity.
211
+ const targetShadow = toStyles.boxShadow;
212
+ if (targetShadow && targetShadow !== 'none') {
213
+ const parsed = parseBoxShadow(targetShadow);
214
+ if (parsed) {
215
+ const avgScale = (sx + sy) / 2;
216
+ if (avgScale !== 0 && avgScale !== 1) {
217
+ patchStyle(el, targetVisual, 'box-shadow', `${parsed.offsetX / sx}px ${parsed.offsetY / sy}px ${parsed.blur / avgScale}px ${parsed.spread / avgScale}px ${parsed.color}`);
218
+ }
219
+ }
220
+ }
221
+ // --- Opacity interpolation ---
222
+ // Linearly interpolate opacity when source and target differ.
223
+ // Applied to the visual child (not wrapper) to avoid multiplying
224
+ // with the child's own opacity.
225
+ const toOpacity = parseFloat(toStyles.opacity) || 1;
226
+ const fromOpacity = parseFloat(fromStyles.opacity) || 1;
227
+ if (fromOpacity !== toOpacity) {
228
+ patchStyle(el, targetVisual, 'opacity', String(mixNumber(fromOpacity, toOpacity, progress)));
229
+ }
230
+ }
231
+ /** Tracks imperative style overrides applied by applyFlip. Keyed by the
232
+ * patched element (the visual child). Each entry maps CSS property →
233
+ * { applied, previous }. On reset, restores the previous value (what the
234
+ * DOM had before we overwrote it, typically a React inline style) instead
235
+ * of clearing to empty. Only restores if the DOM still has our applied
236
+ * value; if React changed it between frames, we leave it alone.
237
+ *
238
+ * Limitation: if a user component animates the SAME CSS property that
239
+ * applyFlip patches (e.g. backgroundColor via interpolate()) and React's
240
+ * new value happens to serialize identically to our last applied value,
241
+ * reset will incorrectly restore the stale previous. This only matters
242
+ * on the transition end frame (|progress - 1| < 0.001) when applyFlip is
243
+ * skipped. Avoid animating borderRadius, backgroundColor, boxShadow,
244
+ * overflow, or opacity on elements inside LayoutTransition during the
245
+ * transition window. */
246
+ const appliedPatches = new WeakMap();
247
+ /** Maps each LayoutTransition wrapper → set of child elements we patched.
248
+ * This ensures resetFlipStyles restores ALL patched children even if
249
+ * the child order changes between frames (e.g. React inserts a new
250
+ * element before the patched child, making it no longer firstElementChild). */
251
+ const patchedChildrenByWrapper = new WeakMap();
252
+ /** Record an imperative style write so resetFlipStyles can undo it safely.
253
+ * @param wrapper — the LayoutTransition wrapper div (for tracking)
254
+ * @param el — the actual visual child element being patched
255
+ * Saves the current DOM value before overwriting, so it can be restored. */
256
+ function patchStyle(wrapper, el, prop, value) {
257
+ let patches = appliedPatches.get(el);
258
+ if (!patches) {
259
+ patches = new Map();
260
+ appliedPatches.set(el, patches);
261
+ }
262
+ // Only capture previous on first patch per reset cycle; subsequent
263
+ // patches within the same frame update `applied` but keep the original
264
+ // previous so we restore to what React had, not our last intermediate.
265
+ const existing = patches.get(prop);
266
+ const previous = existing ? existing.previous : el.style.getPropertyValue(prop);
267
+ patches.set(prop, { applied: value, previous });
268
+ el.style.setProperty(prop, value);
269
+ // Track this child under its wrapper so resetFlipStyles can find it
270
+ // even if it's no longer firstElementChild.
271
+ let children = patchedChildrenByWrapper.get(wrapper);
272
+ if (!children) {
273
+ children = new Set();
274
+ patchedChildrenByWrapper.set(wrapper, children);
275
+ }
276
+ children.add(el);
277
+ }
278
+ /** Reset all styles applied by applyFlip.
279
+ * Clears transform on the wrapper. For child visual styles, restores
280
+ * each property to its previous value (the React inline style that was
281
+ * there before we overwrote it). Only restores if the DOM still has our
282
+ * applied value; if React changed it between frames, that value is kept.
283
+ * Iterates all tracked patched children, not just firstElementChild,
284
+ * so children that moved position in the DOM are still restored. */
285
+ function resetFlipStyles(el) {
286
+ el.style.transform = '';
287
+ const children = patchedChildrenByWrapper.get(el);
288
+ if (!children)
289
+ return;
290
+ for (const child of children) {
291
+ const patches = appliedPatches.get(child);
292
+ if (!patches)
293
+ continue;
294
+ for (const [prop, { applied, previous }] of patches) {
295
+ if (child.style.getPropertyValue(prop) === applied) {
296
+ child.style.setProperty(prop, previous);
297
+ }
298
+ }
299
+ appliedPatches.delete(child);
300
+ }
301
+ patchedChildrenByWrapper.delete(el);
302
+ }
303
+ // ---------------------------------------------------------------------------
304
+ // Components
305
+ // ---------------------------------------------------------------------------
306
+ /**
307
+ * Provides the registry that connects LayoutTransition elements (in both
308
+ * the ghost and visible containers) with the LayoutAnimationLayer.
309
+ * One provider per section — wraps ghost + visible + animation layer.
310
+ */
311
+ export function LayoutTransitionProvider({ children }) {
312
+ const [registry] = useState(() => {
313
+ const entries = new Set();
314
+ return {
315
+ entries,
316
+ register(entry) {
317
+ entries.add(entry);
318
+ return () => {
319
+ entries.delete(entry);
320
+ };
321
+ },
322
+ };
323
+ });
324
+ return (_jsx(LayoutRegistryContext.Provider, { value: registry, children: children }));
325
+ }
326
+ /**
327
+ * Marks its subtree as the hidden ghost copy of the previous section.
328
+ * LayoutTransition elements inside register as 'ghost' and are only
329
+ * used as measurement sources, never animated.
330
+ */
331
+ export function LayoutGhost({ children }) {
332
+ return (_jsx(LayoutContainerContext.Provider, { value: "ghost", children: children }));
333
+ }
334
+ /**
335
+ * Wraps an element that should keep visual continuity across section
336
+ * boundaries. Matching is by `id`: if the previous section also rendered
337
+ * <LayoutTransition id="x">, the element springs from its old position
338
+ * to its new one at the start of the section.
339
+ *
340
+ * Usage in MDX:
341
+ * ```mdx
342
+ * # Scene 1 duration=5s
343
+ *
344
+ * <LayoutTransition id="title">
345
+ * <BlurReveal text="Hello" />
346
+ * </LayoutTransition>
347
+ *
348
+ * # Scene 2 duration=5s
349
+ *
350
+ * <LayoutTransition id="title" duration={25} bounce={0.2}>
351
+ * <BlurReveal text="Hello" />
352
+ * </LayoutTransition>
353
+ * ```
354
+ *
355
+ * If no matching id exists in the previous section, children render
356
+ * normally with no animation.
357
+ */
358
+ export function LayoutTransition({ id, duration = 20, bounce = 0.15, easing, mode = 'both', showFrom, showUpTo, children, }) {
359
+ const ref = useRef(null);
360
+ const registry = useContext(LayoutRegistryContext);
361
+ const container = useContext(LayoutContainerContext);
362
+ // useCurrentFrame() throws outside Remotion context (SSR tests). The
363
+ // try-catch is safe: the hook is always called unconditionally so hook
364
+ // order is stable; we just handle the missing context gracefully.
365
+ let frame = 0;
366
+ try {
367
+ frame = useCurrentFrame();
368
+ }
369
+ catch { }
370
+ const hasTimeRange = showFrom !== undefined || showUpTo !== undefined;
371
+ const rangeFrom = showFrom ?? 0;
372
+ const rangeUpTo = showUpTo ?? Infinity;
373
+ const isActive = !hasTimeRange || (frame >= rangeFrom && frame < rangeUpTo);
374
+ // One stable entry object per component instance. Fields are refreshed
375
+ // on every render (cheap own-ref mutation) so the animation layer always
376
+ // reads current props without re-registration.
377
+ const entryRef = useRef(null);
378
+ if (entryRef.current === null) {
379
+ entryRef.current = {
380
+ id, container, ref, durationInFrames: duration, bounce, easing: easing ?? null,
381
+ mode,
382
+ showFrom: hasTimeRange ? rangeFrom : undefined,
383
+ showUpTo: hasTimeRange ? rangeUpTo : undefined,
384
+ };
385
+ }
386
+ entryRef.current.id = id;
387
+ entryRef.current.container = container;
388
+ entryRef.current.durationInFrames = duration;
389
+ entryRef.current.bounce = bounce;
390
+ entryRef.current.easing = easing ?? null;
391
+ entryRef.current.mode = mode;
392
+ entryRef.current.showFrom = hasTimeRange ? rangeFrom : undefined;
393
+ entryRef.current.showUpTo = hasTimeRange ? rangeUpTo : undefined;
394
+ useLayoutEffect(() => {
395
+ if (!registry)
396
+ return;
397
+ return registry.register(entryRef.current);
398
+ }, [registry]);
399
+ // transformOrigin '0 0': FLIP deltas are computed from top-left corners,
400
+ // so the scale component must also originate from the top-left.
401
+ //
402
+ // width: fit-content: prevents the wrapper from stretching to 100% width
403
+ // in block-layout parents. Without this, a LayoutTransition in a block
404
+ // parent (e.g. display:block div) measures as full-width even though the
405
+ // content is small. When the matching element in the next section is in a
406
+ // flex parent (content-sized), the FLIP scale ratio becomes enormous
407
+ // (e.g. 1920px / 364px ≈ 5.3x), grossly distorting text. fit-content
408
+ // ensures the wrapper always matches its content dimensions regardless
409
+ // of parent layout, so FLIP scale ratios reflect actual content size
410
+ // differences, not layout context differences.
411
+ //
412
+ // When using time ranges, inactive instances are visibility:hidden (keeps
413
+ // layout footprint for FLIP measurement) instead of display:none.
414
+ return (_jsx("div", { ref: ref, "data-layout-id": id, style: {
415
+ transformOrigin: '0 0',
416
+ width: 'fit-content',
417
+ ...(hasTimeRange && !isActive
418
+ ? { visibility: 'hidden', pointerEvents: 'none' }
419
+ : {}),
420
+ }, children: children }));
421
+ }
422
+ /**
423
+ * Measures ghost vs visible LayoutTransition elements after every render
424
+ * and writes FLIP transforms to the visible wrappers. Must be rendered
425
+ * AFTER both containers (sibling order matters: its layout effect has to
426
+ * run after all LayoutTransition registrations).
427
+ *
428
+ * Renders an empty composition-sized AbsoluteFill used only to measure
429
+ * the Player's scale factor (composition px vs client px).
430
+ */
431
+ export function LayoutAnimationLayer() {
432
+ const registry = useContext(LayoutRegistryContext);
433
+ const frame = useCurrentFrame();
434
+ const { fps, width } = useVideoConfig();
435
+ const rootRef = useRef(null);
436
+ // No dependency array: must re-run on every frame (the Player re-renders
437
+ // each frame, and rect positions can change with frame-driven layout).
438
+ useLayoutEffect(() => {
439
+ if (!registry)
440
+ return;
441
+ const entries = [...registry.entries];
442
+ // Categorize entries by container and timing mode.
443
+ const allGhosts = entries.filter((e) => e.container === 'ghost');
444
+ const allVisible = entries.filter((e) => e.container === 'visible');
445
+ // Timed visible entries for intra-scene FLIP (ghost entries excluded
446
+ // to prevent cross-section pollution).
447
+ const timedVisible = allVisible.filter((e) => e.showFrom !== undefined);
448
+ // Reset ALL styles BEFORE measuring: getBoundingClientRect() includes
449
+ // transforms, so measuring a transformed element would compound the
450
+ // previous frame's FLIP offset. Also clear interpolated visual styles
451
+ // (borderRadius, backgroundColor, etc.) so getComputedStyle reads the
452
+ // element's natural values, not our previous frame's overrides.
453
+ for (const e of entries) {
454
+ if (e.ref.current)
455
+ resetFlipStyles(e.ref.current);
456
+ }
457
+ const rootRect = rootRef.current?.getBoundingClientRect();
458
+ if (!rootRect || rootRect.width === 0)
459
+ return;
460
+ // Player scale: composition is 1920 wide but rendered smaller/larger
461
+ // in the viewport. Translate deltas are measured in client px and
462
+ // applied in composition px, so divide by this scale. Scale ratios
463
+ // (sx, sy) are dimensionless and unaffected.
464
+ const playerScale = rootRect.width / width;
465
+ // --- Cross-section FLIP (ghost → visible) ---
466
+ // Build one "representative" per id from each side so cross-section
467
+ // FLIP works even when the previous section used untimed entries and
468
+ // the current section uses timed entries (or vice versa).
469
+ if (allGhosts.length > 0) {
470
+ // Ghost representatives: for untimed ghosts, use directly. For timed
471
+ // ghosts, find the one that was active at the frozen frame. The ghost
472
+ // container freezes at prevDurationInFrames-1, so the active timed
473
+ // ghost has NO inline visibility:hidden (set by LayoutTransition).
474
+ const ghostReps = new Map();
475
+ for (const e of allGhosts) {
476
+ if (ghostReps.has(e.id))
477
+ continue;
478
+ if (e.showFrom === undefined) {
479
+ ghostReps.set(e.id, e);
480
+ }
481
+ else if (e.ref.current?.style.visibility !== 'hidden') {
482
+ ghostReps.set(e.id, e);
483
+ }
484
+ }
485
+ // Visible representatives: untimed visible first, then active timed.
486
+ const visibleReps = new Map();
487
+ for (const e of allVisible) {
488
+ if (visibleReps.has(e.id))
489
+ continue;
490
+ if (e.showFrom === undefined) {
491
+ visibleReps.set(e.id, e);
492
+ }
493
+ else if (frame >= (e.showFrom ?? 0) && frame < (e.showUpTo ?? Infinity)) {
494
+ visibleReps.set(e.id, e);
495
+ }
496
+ }
497
+ for (const [id, e] of visibleReps) {
498
+ const el = e.ref.current;
499
+ if (!el)
500
+ continue;
501
+ const ghost = ghostReps.get(id);
502
+ const ghostEl = ghost?.ref.current;
503
+ if (!ghostEl)
504
+ continue;
505
+ const progress = e.easing
506
+ ? interpolate(frame, [0, e.durationInFrames], [0, 1], {
507
+ extrapolateLeft: 'clamp',
508
+ extrapolateRight: 'clamp',
509
+ easing: e.easing,
510
+ })
511
+ : dspring(frame, fps, e.durationInFrames / fps, e.bounce);
512
+ // Must check |progress - 1| not just progress > threshold, otherwise
513
+ // bouncy springs that overshoot past 1.0 get their overshoot frames
514
+ // skipped and the bounce is invisible.
515
+ if (Math.abs(progress - 1) < 0.001)
516
+ continue;
517
+ const fromRect = ghostEl.getBoundingClientRect();
518
+ const toRect = el.getBoundingClientRect();
519
+ if (toRect.width === 0 || toRect.height === 0 || fromRect.width === 0)
520
+ continue;
521
+ applyFlip({ el, sourceEl: ghostEl, fromRect, toRect, progress, playerScale, entry: e });
522
+ }
523
+ }
524
+ // --- Intra-scene FLIP (timed entries within same section) ---
525
+ if (timedVisible.length > 0) {
526
+ // Group timed entries by id, sort each group by showFrom.
527
+ const timedById = new Map();
528
+ for (const e of timedVisible) {
529
+ const list = timedById.get(e.id);
530
+ if (list)
531
+ list.push(e);
532
+ else
533
+ timedById.set(e.id, [e]);
534
+ }
535
+ for (const [, group] of timedById) {
536
+ if (group.length < 2)
537
+ continue;
538
+ group.sort((a, b) => (a.showFrom ?? 0) - (b.showFrom ?? 0));
539
+ // Find the currently active entry.
540
+ const active = group.find((e) => frame >= (e.showFrom ?? 0) && frame < (e.showUpTo ?? Infinity));
541
+ if (!active?.ref.current)
542
+ continue;
543
+ // Find previous: the entry whose range ended most recently before
544
+ // the active entry's range starts. Since ranges don't overlap and
545
+ // are sorted, it's the last entry with showUpTo <= active.showFrom.
546
+ const activeFrom = active.showFrom ?? 0;
547
+ let prev;
548
+ for (let i = group.length - 1; i >= 0; i--) {
549
+ const e = group[i];
550
+ if (e !== active && (e.showUpTo ?? Infinity) <= activeFrom) {
551
+ prev = e;
552
+ break;
553
+ }
554
+ }
555
+ if (!prev?.ref.current)
556
+ continue;
557
+ // Transition starts when the active entry's range begins.
558
+ const localFrame = frame - activeFrom;
559
+ const progress = active.easing
560
+ ? interpolate(localFrame, [0, active.durationInFrames], [0, 1], {
561
+ extrapolateLeft: 'clamp',
562
+ extrapolateRight: 'clamp',
563
+ easing: active.easing,
564
+ })
565
+ : dspring(localFrame, fps, active.durationInFrames / fps, active.bounce);
566
+ // Must check |progress - 1| not just progress > threshold, otherwise
567
+ // bouncy springs that overshoot past 1.0 get their overshoot frames
568
+ // skipped and the bounce is invisible.
569
+ if (Math.abs(progress - 1) < 0.001)
570
+ continue;
571
+ // FLIP: measure previous (visibility:hidden, keeps layout) → active.
572
+ const fromRect = prev.ref.current.getBoundingClientRect();
573
+ const toRect = active.ref.current.getBoundingClientRect();
574
+ if (toRect.width === 0 || toRect.height === 0 || fromRect.width === 0)
575
+ continue;
576
+ applyFlip({ el: active.ref.current, sourceEl: prev.ref.current, fromRect, toRect, progress, playerScale, entry: active });
577
+ }
578
+ }
579
+ });
580
+ return _jsx(AbsoluteFill, { ref: rootRef, style: { pointerEvents: 'none' } });
581
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * LiquidGradientShader — animated liquid turbulence gradient with Oklab color mixing.
3
+ *
4
+ * Port of Framer's LiquidGradient shader module for Remotion/egaki.
5
+ * Original source:
6
+ * https://framerusercontent.com/modules/QEytd7xyjUsL7yuVUzSs/O7s2vxltmhiskELYwyrY/LiquidGradient.js
7
+ *
8
+ * A multi-color gradient is warped through seed-dependent turbulence with
9
+ * configurable amplitude, frequency, and iteration count. Colors are mixed
10
+ * in Oklab/LCH perceptual color space for smooth, natural transitions.
11
+ * Post-processing includes soft gamut mapping, exposure, contrast, and
12
+ * saturation controls. Optional dithering (IGN smooth or grain noise)
13
+ * reduces banding artifacts.
14
+ */
15
+ import type { CSSProperties } from 'react';
16
+ export interface LiquidGradientShaderProps {
17
+ /** Color palette for the gradient (max 8). Hex strings like "#2962FF". */
18
+ colors?: string[];
19
+ /** Seed for turbulence variation. Different seeds = different flow patterns. */
20
+ seed?: number;
21
+ /** Animation speed multiplier. 0 = frozen, 0.3 = default, 2 = fast. */
22
+ speed?: number;
23
+ /** Loop duration in seconds. 0 = no loop, >0 = seamless loop over that many seconds. */
24
+ loop?: number;
25
+ /** UV scale of the turbulence field. Higher = more zoomed in. */
26
+ scale?: number;
27
+ /** Turbulence amplitude. Controls how far the flow displaces colors. */
28
+ turbAmp?: number;
29
+ /** Turbulence frequency. Controls the spacing of turbulence waves. */
30
+ turbFreq?: number;
31
+ /** Turbulence iteration count. Higher = more detail, more GPU cost. */
32
+ turbIter?: number;
33
+ /** Wave frequency for the final color band mapping. */
34
+ waveFreq?: number;
35
+ /** Distribution bias. Shifts where color bands fall in the gradient. */
36
+ distBias?: number;
37
+ /** Jellify mode (0 or 1). Squashes one axis for a jelly-like feel. */
38
+ jellify?: number;
39
+ /** Dither mode: 0 = Off, 1 = Smooth (IGN), 2 = Grain (quick noise). */
40
+ ditherMode?: number;
41
+ /** Dither amount. Higher = more visible noise to fight banding. */
42
+ dither?: number;
43
+ /** Exposure multiplier. Brightens or darkens the overall output. */
44
+ exposure?: number;
45
+ /** Contrast adjustment applied in Oklab space. */
46
+ contrast?: number;
47
+ /** Saturation adjustment applied in Oklab space. */
48
+ saturation?: number;
49
+ style?: CSSProperties;
50
+ }
51
+ /**
52
+ * Property controls matching the original Framer LiquidGradient module.
53
+ * Ranges, defaults, and step sizes are preserved from the original.
54
+ *
55
+ * Notes on type mapping:
56
+ * - `jellify` is a Framer Boolean; mapped to a number 0/1 toggle since
57
+ * defineShader only supports number controls. The GLSL uses `> 0.5`.
58
+ * - `ditherMode` is a Framer Enum [0,1,2]; mapped to a number with step 1.
59
+ * - `loop` default 0 means no looping; values > 0 set the loop duration.
60
+ */
61
+ export declare const LiquidGradientShader: React.FC<LiquidGradientShaderProps>;
62
+ //# sourceMappingURL=liquid-gradient-shader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"liquid-gradient-shader.d.ts","sourceRoot":"","sources":["../../src/vite/liquid-gradient-shader.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAO1C,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wFAAwF;IACxF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB;AAgSD;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAqH3B,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAA"}