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,411 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * WebGL2 shader component factory for egaki video.
5
+ *
6
+ * Egaki's equivalent of Framer's defineShader() API, adapted for Remotion's
7
+ * frame-based rendering. Time is derived from useCurrentFrame()/fps so shaders
8
+ * are deterministic and seekable. Property controls are exposed via useTweakpane
9
+ * for live editing in the player.
10
+ *
11
+ * Port of Framer's WebGL2 shader infrastructure. Original sources (in unframer):
12
+ * - defineShader(): unframer/src/framer.js:50569-50588
13
+ * - generateShaderHead(): unframer/src/framer.js:50493-50546
14
+ * - WebGL2ShaderRenderer: unframer/src/framer.js:49750-50346
15
+ * - ShaderCanvas: unframer/src/framer.js:51376-51593
16
+ * - DEFAULT_VERTEX_SHADER: unframer/src/framer.js:51184-51196
17
+ * - builtInUniforms: unframer/src/framer.js:49718-49747
18
+ * - setUniform(): unframer/src/framer.js:50224-50246
19
+ * - colorToVec4(): unframer/src/framer.js:50923-50927
20
+ * - bindFullScreenQuadAttribs: unframer/src/framer.js:50354-50366
21
+ *
22
+ * Key differences from Framer:
23
+ * - u_time = frame / fps (not wall-clock performance.now())
24
+ * - No requestAnimationFrame loop; one GL draw per Remotion frame
25
+ * - useTweakpane replaces Framer's ControlType property controls UI
26
+ * - No mouse input, heightmaps, or multi-pass buffers (can add later)
27
+ * - preserveDrawingBuffer=true for @remotion/web-renderer HtmlInCanvas
28
+ */
29
+ import { useCurrentFrame, useVideoConfig } from 'remotion';
30
+ import { useLayoutEffect, useRef } from 'react';
31
+ import { useTweakpane } from "./tweakpane-hook.js";
32
+ // ---------------------------------------------------------------------------
33
+ // Color parsing
34
+ // ---------------------------------------------------------------------------
35
+ /** Parse a hex color string (#RGB, #RRGGBB, #RRGGBBAA) to [r, g, b, a] in 0-1 range. */
36
+ function parseHexColor(hex) {
37
+ let h = hex.replace('#', '');
38
+ if (h.length === 3 || h.length === 4) {
39
+ h = h
40
+ .split('')
41
+ .map((c) => c + c)
42
+ .join('');
43
+ }
44
+ const r = parseInt(h.slice(0, 2), 16) / 255;
45
+ const g = parseInt(h.slice(2, 4), 16) / 255;
46
+ const b = parseInt(h.slice(4, 6), 16) / 255;
47
+ const a = h.length >= 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1;
48
+ return [r, g, b, a];
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // GLSL constants
52
+ // ---------------------------------------------------------------------------
53
+ /**
54
+ * Fullscreen quad vertex shader. Two attributes:
55
+ * a_position — clip-space quad corners (2 triangles covering [-1,1]²)
56
+ * a_texCoord — UV coordinates [0,1]² passed as v_uv to the fragment shader
57
+ *
58
+ * Identical to Framer's DEFAULT_VERTEX_SHADER (framer.js:51184-51196).
59
+ */
60
+ const VERTEX_SHADER = `#version 300 es
61
+ precision highp float;
62
+ in vec2 a_position;
63
+ in vec2 a_texCoord;
64
+ out vec2 v_uv;
65
+ void main() {
66
+ v_uv = a_texCoord;
67
+ gl_Position = vec4(a_position, 0.0, 1.0);
68
+ }
69
+ `;
70
+ /**
71
+ * Build the full fragment shader source by prepending uniform declarations
72
+ * and built-in uniforms before the user-provided shader body.
73
+ *
74
+ * Mirrors Framer's generateShaderHead() (framer.js:50493-50546).
75
+ * Property control keys are converted to uniform names with a `u_` prefix,
76
+ * matching Framer's toUniformName() convention (framer.js:50458-50459).
77
+ */
78
+ function generateFragmentSource(body, controls) {
79
+ const lines = [
80
+ '#version 300 es',
81
+ 'precision highp float;',
82
+ '',
83
+ 'in vec2 v_uv;',
84
+ 'out vec4 fragColor;',
85
+ ];
86
+ // #define for array max counts (Framer's toArrayMaxLengthName, framer.js:50470-50473)
87
+ for (const [key, ctrl] of Object.entries(controls)) {
88
+ if (ctrl.type === 'array') {
89
+ const constName = key.replace(/[a-z0-9](?=[A-Z])/g, '$&_').toUpperCase();
90
+ lines.push('', `#define NUM_${constName} ${ctrl.maxCount}`);
91
+ }
92
+ }
93
+ lines.push('');
94
+ // Uniform declarations from property controls
95
+ for (const [key, ctrl] of Object.entries(controls)) {
96
+ const uName = `u_${key}`;
97
+ if (ctrl.type === 'array') {
98
+ const constName = key.replace(/[a-z0-9](?=[A-Z])/g, '$&_').toUpperCase();
99
+ lines.push(`uniform vec4 ${uName}[NUM_${constName}];`);
100
+ lines.push(`uniform int ${uName}_length;`);
101
+ }
102
+ else {
103
+ lines.push(`uniform float ${uName};`);
104
+ }
105
+ }
106
+ lines.push('');
107
+ // Built-in uniforms (Framer's builtInUniforms, framer.js:49718-49747)
108
+ lines.push('uniform float u_time;', 'uniform vec2 u_resolution;', 'uniform float u_deltaTime;', 'uniform float u_pixelRatio;', 'uniform vec4 u_mousePosition;', 'uniform float u_mousePointerDown;', 'uniform float u_mouseHover;', '');
109
+ lines.push(body);
110
+ return lines.join('\n');
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // WebGL helpers
114
+ // ---------------------------------------------------------------------------
115
+ function compileGlShader(gl, type, source) {
116
+ const shader = gl.createShader(type);
117
+ if (!shader)
118
+ throw new Error('Failed to create shader');
119
+ gl.shaderSource(shader, source);
120
+ gl.compileShader(shader);
121
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
122
+ const log = gl.getShaderInfoLog(shader);
123
+ gl.deleteShader(shader);
124
+ throw new Error(`Shader compile error:\n${log}`);
125
+ }
126
+ return shader;
127
+ }
128
+ function linkGlProgram(gl, vs, fs) {
129
+ const program = gl.createProgram();
130
+ if (!program)
131
+ throw new Error('Failed to create program');
132
+ gl.attachShader(program, vs);
133
+ gl.attachShader(program, fs);
134
+ gl.linkProgram(program);
135
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
136
+ const log = gl.getProgramInfoLog(program);
137
+ gl.deleteProgram(program);
138
+ throw new Error(`Program link error:\n${log}`);
139
+ }
140
+ return program;
141
+ }
142
+ const BUILTIN_UNIFORM_NAMES = [
143
+ 'u_time',
144
+ 'u_resolution',
145
+ 'u_deltaTime',
146
+ 'u_pixelRatio',
147
+ 'u_mousePosition',
148
+ 'u_mousePointerDown',
149
+ 'u_mouseHover',
150
+ ];
151
+ // ---------------------------------------------------------------------------
152
+ // defineShader
153
+ // ---------------------------------------------------------------------------
154
+ /**
155
+ * Create a React component that renders a WebGL2 fragment shader full-screen.
156
+ *
157
+ * The returned component accepts all property control keys as optional props
158
+ * plus a `style` prop. Number controls are exposed in tweakpane as sliders
159
+ * (unless `hidden: true`). Color array items are exposed as individual
160
+ * tweakpane color pickers with swatch UI.
161
+ *
162
+ * Time and resolution are derived from Remotion's hooks, making the shader
163
+ * fully deterministic and seekable.
164
+ */
165
+ export function defineShader(config) {
166
+ const fragmentSource = generateFragmentSource(config.fragment, config.propertyControls);
167
+ // Pre-compute per-control metadata so the draw path avoids Object.entries()
168
+ const numberMetas = [];
169
+ const arrayMetas = [];
170
+ const tweakpaneDefaults = {};
171
+ const arrayDefaults = {};
172
+ for (const [key, ctrl] of Object.entries(config.propertyControls)) {
173
+ if (ctrl.type === 'number') {
174
+ numberMetas.push({
175
+ key,
176
+ uniformName: `u_${key}`,
177
+ defaultValue: ctrl.defaultValue,
178
+ hidden: !!ctrl.hidden,
179
+ });
180
+ if (!ctrl.hidden) {
181
+ tweakpaneDefaults[key] = {
182
+ value: ctrl.defaultValue,
183
+ min: ctrl.min,
184
+ max: ctrl.max,
185
+ step: ctrl.step,
186
+ };
187
+ }
188
+ }
189
+ else if (ctrl.type === 'array') {
190
+ arrayMetas.push({
191
+ key,
192
+ uniformName: `u_${key}`,
193
+ lengthName: `u_${key}_length`,
194
+ maxCount: ctrl.maxCount,
195
+ });
196
+ arrayDefaults[key] = ctrl.defaultValue;
197
+ }
198
+ }
199
+ // Build tweakpane keys for color array items. Each color slot up to maxCount
200
+ // becomes an individual tweakpane color picker (e.g. "colors 1", "colors 2").
201
+ // A "{key} count" slider controls how many are active (sent as u_{key}_length).
202
+ // Tweakpane auto-detects #hex strings as color inputs with a swatch.
203
+ const arrayTweakpaneKeys = [];
204
+ for (const m of arrayMetas) {
205
+ const ctrl = config.propertyControls[m.key];
206
+ const visible = ctrl.maxVisible ?? ctrl.maxCount;
207
+ const tpKeys = [];
208
+ for (let i = 0; i < visible; i++) {
209
+ tpKeys.push(`${m.key} ${i + 1}`);
210
+ }
211
+ arrayTweakpaneKeys.push({
212
+ arrayKey: m.key,
213
+ countKey: `${m.key} count`,
214
+ tpKeys,
215
+ defaults: ctrl.defaultValue,
216
+ maxCount: ctrl.maxCount,
217
+ });
218
+ }
219
+ function ShaderComponent(props) {
220
+ const frame = useCurrentFrame();
221
+ const { fps } = useVideoConfig();
222
+ const time = frame / fps;
223
+ const canvasRef = useRef(null);
224
+ const glStateRef = useRef(null);
225
+ // Build tweakpane schema with props overriding defaults.
226
+ // Number controls become sliders; color array items become individual
227
+ // color pickers (tweakpane auto-detects #hex strings as color inputs).
228
+ const tpSchema = {};
229
+ for (const [key, base] of Object.entries(tweakpaneDefaults)) {
230
+ tpSchema[key] = { ...base, value: props[key] ?? base.value };
231
+ }
232
+ for (const { arrayKey, countKey, tpKeys, defaults } of arrayTweakpaneKeys) {
233
+ const propColors = props[arrayKey];
234
+ const activeCount = Math.min(propColors?.length ?? defaults.length, tpKeys.length);
235
+ tpSchema[countKey] = { value: activeCount, min: 1, max: tpKeys.length, step: 1 };
236
+ for (let i = 0; i < tpKeys.length; i++) {
237
+ tpSchema[tpKeys[i]] = propColors?.[i] ?? defaults[i] ?? '#000000';
238
+ }
239
+ }
240
+ const tp = useTweakpane(config.title, tpSchema);
241
+ // Initialize WebGL2 on mount (useLayoutEffect so the first frame draws)
242
+ useLayoutEffect(() => {
243
+ const canvas = canvasRef.current;
244
+ if (!canvas)
245
+ return;
246
+ const gl = canvas.getContext('webgl2', {
247
+ alpha: true,
248
+ premultipliedAlpha: false,
249
+ antialias: false,
250
+ // Must be true for @remotion/web-renderer HtmlInCanvas screenshots
251
+ preserveDrawingBuffer: true,
252
+ });
253
+ if (!gl) {
254
+ console.error(`[${config.title}] WebGL2 not supported`);
255
+ return;
256
+ }
257
+ let vs = null;
258
+ let fs = null;
259
+ try {
260
+ vs = compileGlShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);
261
+ fs = compileGlShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
262
+ const program = linkGlProgram(gl, vs, fs);
263
+ // Shaders can be deleted after successful link
264
+ gl.deleteShader(vs);
265
+ gl.deleteShader(fs);
266
+ vs = null;
267
+ fs = null;
268
+ // Fullscreen quad VAO (2 triangles, 6 vertices)
269
+ // Position/texCoord data matches Framer's createStaticArrayBuffer (framer.js:50347-50352)
270
+ const vao = gl.createVertexArray();
271
+ gl.bindVertexArray(vao);
272
+ const posBuf = gl.createBuffer();
273
+ gl.bindBuffer(gl.ARRAY_BUFFER, posBuf);
274
+ // prettier-ignore
275
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
276
+ -1, -1, 1, -1, -1, 1,
277
+ -1, 1, 1, -1, 1, 1,
278
+ ]), gl.STATIC_DRAW);
279
+ const posLoc = gl.getAttribLocation(program, 'a_position');
280
+ if (posLoc >= 0) {
281
+ gl.enableVertexAttribArray(posLoc);
282
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
283
+ }
284
+ const texBuf = gl.createBuffer();
285
+ gl.bindBuffer(gl.ARRAY_BUFFER, texBuf);
286
+ // prettier-ignore
287
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
288
+ 0, 0, 1, 0, 0, 1,
289
+ 0, 1, 1, 0, 1, 1,
290
+ ]), gl.STATIC_DRAW);
291
+ const texLoc = gl.getAttribLocation(program, 'a_texCoord');
292
+ if (texLoc >= 0) {
293
+ gl.enableVertexAttribArray(texLoc);
294
+ gl.vertexAttribPointer(texLoc, 2, gl.FLOAT, false, 0, 0);
295
+ }
296
+ gl.bindVertexArray(null);
297
+ // Look up all uniform locations once
298
+ const locs = {};
299
+ for (const name of BUILTIN_UNIFORM_NAMES) {
300
+ locs[name] = gl.getUniformLocation(program, name);
301
+ }
302
+ for (const m of numberMetas) {
303
+ locs[m.uniformName] = gl.getUniformLocation(program, m.uniformName);
304
+ }
305
+ for (const m of arrayMetas) {
306
+ locs[m.uniformName] = gl.getUniformLocation(program, m.uniformName);
307
+ locs[m.lengthName] = gl.getUniformLocation(program, m.lengthName);
308
+ }
309
+ // Bind program + VAO for all subsequent draws (single-pass hot path,
310
+ // mirrors Framer's constructor binding in framer.js:49795-49797)
311
+ gl.useProgram(program);
312
+ gl.bindVertexArray(vao);
313
+ gl.clearColor(0, 0, 0, 0);
314
+ glStateRef.current = { gl, program, vao, posBuf, texBuf, locs };
315
+ }
316
+ catch (e) {
317
+ console.error(`[${config.title}] Shader init failed:`, e);
318
+ if (vs)
319
+ gl.deleteShader(vs);
320
+ if (fs)
321
+ gl.deleteShader(fs);
322
+ }
323
+ return () => {
324
+ const state = glStateRef.current;
325
+ if (state) {
326
+ const { gl: g, program, vao, posBuf, texBuf } = state;
327
+ g.deleteProgram(program);
328
+ g.deleteVertexArray(vao);
329
+ g.deleteBuffer(posBuf);
330
+ g.deleteBuffer(texBuf);
331
+ glStateRef.current = null;
332
+ }
333
+ };
334
+ }, []);
335
+ // Draw every Remotion frame. No deps = runs on every render, which is
336
+ // triggered by useCurrentFrame() changing each frame.
337
+ useLayoutEffect(() => {
338
+ const state = glStateRef.current;
339
+ const canvas = canvasRef.current;
340
+ if (!state || !canvas)
341
+ return;
342
+ const { gl, locs } = state;
343
+ const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;
344
+ const w = Math.round(canvas.clientWidth * dpr);
345
+ const h = Math.round(canvas.clientHeight * dpr);
346
+ // Resize backing buffer to match CSS size × DPR
347
+ if (canvas.width !== w || canvas.height !== h) {
348
+ canvas.width = w;
349
+ canvas.height = h;
350
+ gl.viewport(0, 0, w, h);
351
+ }
352
+ // Built-in uniforms (mirrors updatePassBuiltIns, framer.js:50318-50344)
353
+ const loc = locs;
354
+ if (loc.u_time !== null)
355
+ gl.uniform1f(loc.u_time, time);
356
+ if (loc.u_resolution !== null)
357
+ gl.uniform2f(loc.u_resolution, w, h);
358
+ if (loc.u_deltaTime !== null)
359
+ gl.uniform1f(loc.u_deltaTime, 1 / fps);
360
+ if (loc.u_pixelRatio !== null)
361
+ gl.uniform1f(loc.u_pixelRatio, dpr);
362
+ if (loc.u_mousePosition !== null)
363
+ gl.uniform4fv(loc.u_mousePosition, [0, 0, 0, 0]);
364
+ if (loc.u_mousePointerDown !== null)
365
+ gl.uniform1f(loc.u_mousePointerDown, 0);
366
+ if (loc.u_mouseHover !== null)
367
+ gl.uniform1f(loc.u_mouseHover, 0);
368
+ // Number control uniforms (from tweakpane for visible, props for hidden)
369
+ for (const m of numberMetas) {
370
+ const l = loc[m.uniformName];
371
+ if (l == null)
372
+ continue;
373
+ const value = m.hidden
374
+ ? (props[m.key] ?? m.defaultValue)
375
+ : (tp[m.key] ?? m.defaultValue);
376
+ gl.uniform1f(l, value);
377
+ }
378
+ // Color array uniforms — read count + individual colors from tweakpane
379
+ for (const { arrayKey, countKey, tpKeys } of arrayTweakpaneKeys) {
380
+ const m = arrayMetas.find((am) => am.key === arrayKey);
381
+ const count = tp[countKey];
382
+ const flat = [];
383
+ for (let i = 0; i < m.maxCount; i++) {
384
+ if (i < tpKeys.length) {
385
+ flat.push(...parseHexColor(tp[tpKeys[i]] ?? '#000000'));
386
+ }
387
+ else {
388
+ flat.push(0, 0, 0, 0);
389
+ }
390
+ }
391
+ const uLoc = loc[m.uniformName];
392
+ if (uLoc != null)
393
+ gl.uniform4fv(uLoc, flat);
394
+ const lLoc = loc[m.lengthName];
395
+ if (lLoc != null)
396
+ gl.uniform1i(lLoc, count);
397
+ }
398
+ // Draw the fullscreen quad
399
+ gl.clear(gl.COLOR_BUFFER_BIT);
400
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
401
+ });
402
+ return (_jsx("canvas", { ref: canvasRef, style: {
403
+ display: 'block',
404
+ width: '100%',
405
+ height: '100%',
406
+ ...props.style,
407
+ } }));
408
+ }
409
+ ShaderComponent.displayName = config.title;
410
+ return ShaderComponent;
411
+ }
@@ -0,0 +1,12 @@
1
+ import { type GenerationStatus, type GenerationError } from './store.ts';
2
+ type EagerModules = Record<string, Record<string, any>>;
3
+ /** Hook: per-section max media durations. */
4
+ export declare function useMediaDurations(): Record<string, number>;
5
+ /** Hook: generation counts while media is being generated, or null. */
6
+ export declare function useGenerationStatus(): GenerationStatus | null;
7
+ /** Hook: recent generation errors (auto-cleared after 8s). */
8
+ export declare function useGenerationErrors(): GenerationError[];
9
+ /** Hook: user modules from virtual:egaki-modules. */
10
+ export declare function useModules(): EagerModules;
11
+ export type { GenerationStatus, GenerationError };
12
+ //# sourceMappingURL=store-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-hooks.d.ts","sourceRoot":"","sources":["../../src/vite/store-hooks.ts"],"names":[],"mappings":"AAgBA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,YAAY,CAAA;AAGnB,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;AAuEvD,6CAA6C;AAC7C,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE1D;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,eAAe,EAAE,CAEvD;AAED,qDAAqD;AACrD,wBAAgB,UAAU,IAAI,YAAY,CAEzC;AAED,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,93 @@
1
+ 'use client';
2
+ /**
3
+ * React hooks for the egaki client state store.
4
+ *
5
+ * Separated from store.ts so the vanilla store has no React dependency and
6
+ * can be imported from any environment (RSC, SSR, client). These hooks are
7
+ * only imported by 'use client' components.
8
+ *
9
+ * Uses useSyncExternalStore with stable module-level selectors. This avoids
10
+ * zustand's useStore + useShallow which internally calls useRef and fails
11
+ * during Spiceflow's SSR pass when the Vite SSR optimizer creates a split
12
+ * React instance (optimized react-dom/server.edge vs real pnpm react).
13
+ */
14
+ import { useSyncExternalStore } from 'react';
15
+ import { egakiStore, selectMediaDurations, selectGenerationStatus, } from "./store.js";
16
+ // ---------------------------------------------------------------------------
17
+ // Stable subscribe/getSnapshot functions (module-level constants so
18
+ // useSyncExternalStore never re-subscribes on re-render).
19
+ //
20
+ // getSnapshot must return a referentially stable value when the derived
21
+ // result hasn't changed. useSyncExternalStore compares snapshots with
22
+ // Object.is(), so returning a new object every call causes infinite
23
+ // re-render loops. We cache the last snapshot and shallow-compare.
24
+ // ---------------------------------------------------------------------------
25
+ function shallowEqualRecord(a, b) {
26
+ const keysA = Object.keys(a);
27
+ const keysB = Object.keys(b);
28
+ if (keysA.length !== keysB.length)
29
+ return false;
30
+ for (const key of keysA) {
31
+ if (a[key] !== b[key])
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ function shallowEqualStatus(a, b) {
37
+ if (a === b)
38
+ return true;
39
+ if (a === null || b === null)
40
+ return false;
41
+ if (a.total !== b.total)
42
+ return false;
43
+ return shallowEqualRecord(a.counts, b.counts);
44
+ }
45
+ // Media durations
46
+ const subscribeMediaDurations = (callback) => egakiStore.subscribe((state) => state.sectionReports, callback);
47
+ let cachedMediaDurations = {};
48
+ const getMediaDurations = () => {
49
+ const next = selectMediaDurations(egakiStore.getState());
50
+ if (!shallowEqualRecord(cachedMediaDurations, next)) {
51
+ cachedMediaDurations = next;
52
+ }
53
+ return cachedMediaDurations;
54
+ };
55
+ const getMediaDurationsServer = () => cachedMediaDurations;
56
+ // Generation status
57
+ const subscribeGenerationStatus = (callback) => egakiStore.subscribe((state) => state.serverGenerationStatus, callback);
58
+ let cachedGenerationStatus = null;
59
+ const getGenerationStatus = () => {
60
+ const next = selectGenerationStatus(egakiStore.getState());
61
+ if (!shallowEqualStatus(cachedGenerationStatus, next)) {
62
+ cachedGenerationStatus = next;
63
+ }
64
+ return cachedGenerationStatus;
65
+ };
66
+ const getGenerationStatusServer = () => null;
67
+ // Generation errors
68
+ const subscribeGenerationErrors = (callback) => egakiStore.subscribe((state) => state.serverGenerationErrors, callback);
69
+ const getGenerationErrors = () => egakiStore.getState().serverGenerationErrors;
70
+ const EMPTY_ERRORS = [];
71
+ const getGenerationErrorsServer = () => EMPTY_ERRORS;
72
+ // Modules
73
+ const subscribeModules = (callback) => egakiStore.subscribe((state) => state.modules, callback);
74
+ const getModules = () => egakiStore.getState().modules;
75
+ // ---------------------------------------------------------------------------
76
+ // Hooks
77
+ // ---------------------------------------------------------------------------
78
+ /** Hook: per-section max media durations. */
79
+ export function useMediaDurations() {
80
+ return useSyncExternalStore(subscribeMediaDurations, getMediaDurations, getMediaDurationsServer);
81
+ }
82
+ /** Hook: generation counts while media is being generated, or null. */
83
+ export function useGenerationStatus() {
84
+ return useSyncExternalStore(subscribeGenerationStatus, getGenerationStatus, getGenerationStatusServer);
85
+ }
86
+ /** Hook: recent generation errors (auto-cleared after 8s). */
87
+ export function useGenerationErrors() {
88
+ return useSyncExternalStore(subscribeGenerationErrors, getGenerationErrors, getGenerationErrorsServer);
89
+ }
90
+ /** Hook: user modules from virtual:egaki-modules. */
91
+ export function useModules() {
92
+ return useSyncExternalStore(subscribeModules, getModules, getModules);
93
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Centralized client-side state store (zustand/vanilla).
3
+ *
4
+ * Pure vanilla store with no React imports. State slices:
5
+ *
6
+ * 1. **modules** — eagerly-imported user .tsx/.ts modules, updated via HMR
7
+ * 2. **sectionReports** — per-section effective media durations reported by
8
+ * Audio/Video components (ephemeral, not persisted)
9
+ * 3. **serverGenerationStatus** — server-reported generation progress from
10
+ * the /api/generation-progress SSE stream, for the toolbar status indicator
11
+ *
12
+ * React hooks live in store-hooks.ts ('use client') to keep this file
13
+ * importable from any environment (RSC, SSR, client) without pulling in React.
14
+ */
15
+ /** Same as safe-mdx's EagerModules: Record<string, Record<string, any>> */
16
+ type EagerModules = Record<string, Record<string, any>>;
17
+ export interface GenerationStatus {
18
+ /** Count of active generations per namespace (e.g. { image: 2, video: 1 }) */
19
+ counts: Record<string, number>;
20
+ total: number;
21
+ }
22
+ /** Individual generation entry from the server progress stream. */
23
+ export interface GenerationProgressEntry {
24
+ key: string;
25
+ namespace: string;
26
+ label: string;
27
+ model?: string;
28
+ startedAt: number;
29
+ elapsedMs: number;
30
+ params: Record<string, unknown>;
31
+ }
32
+ /** A generation that failed, reported via the progress stream. */
33
+ export interface GenerationError {
34
+ key: string;
35
+ namespace: string;
36
+ label: string;
37
+ model?: string;
38
+ error: string;
39
+ durationMs: number;
40
+ }
41
+ export interface EgakiClientState {
42
+ /** User-imported modules from virtual:egaki-modules. Updated by HMR. */
43
+ modules: EagerModules;
44
+ /**
45
+ * Per-instance section duration reports (ephemeral).
46
+ * sectionIndex → Map<instanceId, effectiveSeconds>
47
+ * Each Audio/Video component reports its effective playback duration.
48
+ */
49
+ sectionReports: Map<number, Map<string, number>>;
50
+ /** Server-reported generation progress from /api/generation-progress SSE stream. */
51
+ serverGenerationStatus: GenerationStatus | null;
52
+ /** Detailed generation entries from the server progress stream. */
53
+ serverGenerationEntries: GenerationProgressEntry[];
54
+ /** Recent generation errors from the server progress stream.
55
+ * Errors auto-clear after ERROR_DISPLAY_DURATION_MS. */
56
+ serverGenerationErrors: GenerationError[];
57
+ }
58
+ export declare const egakiStore: Omit<import("zustand/vanilla").StoreApi<EgakiClientState>, "subscribe"> & {
59
+ subscribe: {
60
+ (listener: (selectedState: EgakiClientState, previousSelectedState: EgakiClientState) => void): () => void;
61
+ <U>(selector: (state: EgakiClientState) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
62
+ equalityFn?: ((a: U, b: U) => boolean) | undefined;
63
+ fireImmediately?: boolean;
64
+ } | undefined): () => void;
65
+ };
66
+ };
67
+ /**
68
+ * Derive per-section max media durations.
69
+ * Returns Record<string, number> (section-index-as-string → seconds).
70
+ */
71
+ export declare function selectMediaDurations(state: EgakiClientState): Record<string, number>;
72
+ /** Generation status from the server progress stream, or null when idle. */
73
+ export declare function selectGenerationStatus(state: EgakiClientState): GenerationStatus | null;
74
+ export {};
75
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/vite/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,2EAA2E;AAC3E,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,KAAK,EAAE,MAAM,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,OAAO,EAAE,YAAY,CAAA;IAErB;;;;OAIG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAEhD,oFAAoF;IACpF,sBAAsB,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAE/C,mEAAmE;IACnE,uBAAuB,EAAE,uBAAuB,EAAE,CAAA;IAElD;6DACyD;IACzD,sBAAsB,EAAE,eAAe,EAAE,CAAA;CAC1C;AAMD,eAAO,MAAM,UAAU;;;;;;;;CAQtB,CAAA;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWpF;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,IAAI,CAEvF"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Centralized client-side state store (zustand/vanilla).
3
+ *
4
+ * Pure vanilla store with no React imports. State slices:
5
+ *
6
+ * 1. **modules** — eagerly-imported user .tsx/.ts modules, updated via HMR
7
+ * 2. **sectionReports** — per-section effective media durations reported by
8
+ * Audio/Video components (ephemeral, not persisted)
9
+ * 3. **serverGenerationStatus** — server-reported generation progress from
10
+ * the /api/generation-progress SSE stream, for the toolbar status indicator
11
+ *
12
+ * React hooks live in store-hooks.ts ('use client') to keep this file
13
+ * importable from any environment (RSC, SSR, client) without pulling in React.
14
+ */
15
+ import { createStore } from 'zustand/vanilla';
16
+ import { subscribeWithSelector } from 'zustand/middleware';
17
+ // ---------------------------------------------------------------------------
18
+ // Store
19
+ // ---------------------------------------------------------------------------
20
+ export const egakiStore = createStore()(subscribeWithSelector(() => ({
21
+ modules: {},
22
+ sectionReports: new Map(),
23
+ serverGenerationStatus: null,
24
+ serverGenerationEntries: [],
25
+ serverGenerationErrors: [],
26
+ })));
27
+ // ---------------------------------------------------------------------------
28
+ // Selectors — derive values from state
29
+ // ---------------------------------------------------------------------------
30
+ /**
31
+ * Derive per-section max media durations.
32
+ * Returns Record<string, number> (section-index-as-string → seconds).
33
+ */
34
+ export function selectMediaDurations(state) {
35
+ const obj = {};
36
+ for (const [idx, reports] of state.sectionReports) {
37
+ if (reports.size === 0)
38
+ continue;
39
+ let max = 0;
40
+ for (const dur of reports.values()) {
41
+ if (dur > max)
42
+ max = dur;
43
+ }
44
+ if (max > 0)
45
+ obj[String(idx)] = max;
46
+ }
47
+ return obj;
48
+ }
49
+ /** Generation status from the server progress stream, or null when idle. */
50
+ export function selectGenerationStatus(state) {
51
+ return state.serverGenerationStatus;
52
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tests for the centralized egaki client store.
3
+ * Pure state transitions — no React, no mocks, just data in and data out.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../src/vite/store.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}