egaki 0.6.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 (271) hide show
  1. package/CHANGELOG.md +140 -9
  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.js → cli/cli.js} +626 -600
  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/cli/credentials.d.ts.map +1 -0
  22. package/dist/{credentials.js → cli/credentials.js} +20 -0
  23. package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
  24. package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
  25. package/dist/cli/elevenlabs-speech-provider.js +199 -0
  26. package/dist/cli/generate.d.ts +140 -0
  27. package/dist/cli/generate.d.ts.map +1 -0
  28. package/dist/cli/generate.js +604 -0
  29. package/dist/cli/generate.test.d.ts +2 -0
  30. package/dist/cli/generate.test.d.ts.map +1 -0
  31. package/dist/cli/generate.test.js +131 -0
  32. package/dist/cli/login.d.ts.map +1 -0
  33. package/dist/cli/main.d.ts +3 -0
  34. package/dist/cli/main.d.ts.map +1 -0
  35. package/dist/cli/main.js +7 -0
  36. package/dist/{model-catalog.d.ts → cli/model-catalog.d.ts} +6 -0
  37. package/dist/cli/model-catalog.d.ts.map +1 -0
  38. package/dist/{model-catalog.js → cli/model-catalog.js} +714 -4
  39. package/dist/{models.d.ts → cli/models.d.ts} +12 -5
  40. package/dist/cli/models.d.ts.map +1 -0
  41. package/dist/{models.js → cli/models.js} +58 -57
  42. package/dist/cli/open-browser.d.ts.map +1 -0
  43. package/dist/cli/openai-speech-provider.d.ts +3 -0
  44. package/dist/cli/openai-speech-provider.d.ts.map +1 -0
  45. package/dist/cli/openai-speech-provider.js +28 -0
  46. package/dist/cli/separation-generate.d.ts +34 -0
  47. package/dist/cli/separation-generate.d.ts.map +1 -0
  48. package/dist/cli/separation-generate.js +117 -0
  49. package/dist/cli/speech-catalog.d.ts +35 -0
  50. package/dist/cli/speech-catalog.d.ts.map +1 -0
  51. package/dist/cli/speech-catalog.js +195 -0
  52. package/dist/cli/speech-generate.d.ts +86 -0
  53. package/dist/cli/speech-generate.d.ts.map +1 -0
  54. package/dist/cli/speech-generate.js +156 -0
  55. package/dist/cli/speech-models.d.ts +8 -0
  56. package/dist/cli/speech-models.d.ts.map +1 -0
  57. package/dist/cli/speech-models.js +109 -0
  58. package/dist/cli/speech-provider.test.d.ts +2 -0
  59. package/dist/cli/speech-provider.test.d.ts.map +1 -0
  60. package/dist/cli/speech-provider.test.js +197 -0
  61. package/dist/cli/subscription.d.ts.map +1 -0
  62. package/dist/cli/transcription-catalog.d.ts +35 -0
  63. package/dist/cli/transcription-catalog.d.ts.map +1 -0
  64. package/dist/cli/transcription-catalog.js +192 -0
  65. package/dist/cli/transcription-generate.d.ts +62 -0
  66. package/dist/cli/transcription-generate.d.ts.map +1 -0
  67. package/dist/cli/transcription-generate.js +144 -0
  68. package/dist/cli/transcription-models.d.ts +9 -0
  69. package/dist/cli/transcription-models.d.ts.map +1 -0
  70. package/dist/cli/transcription-models.js +100 -0
  71. package/dist/cli/transcription-providers.d.ts +22 -0
  72. package/dist/cli/transcription-providers.d.ts.map +1 -0
  73. package/dist/cli/transcription-providers.js +290 -0
  74. package/dist/cli/video-model-catalog.d.ts.map +1 -0
  75. package/dist/cli/xai-auth.d.ts.map +1 -0
  76. package/dist/{xai-auth.js → cli/xai-auth.js} +1 -1
  77. package/dist/vite/angled-screen.d.ts +57 -0
  78. package/dist/vite/angled-screen.d.ts.map +1 -0
  79. package/dist/vite/angled-screen.js +115 -0
  80. package/dist/vite/app.d.ts +51 -0
  81. package/dist/vite/app.d.ts.map +1 -0
  82. package/dist/vite/app.js +150 -0
  83. package/dist/vite/bands-shader.d.ts +47 -0
  84. package/dist/vite/bands-shader.d.ts.map +1 -0
  85. package/dist/vite/bands-shader.js +267 -0
  86. package/dist/vite/code-block.d.ts +107 -0
  87. package/dist/vite/code-block.d.ts.map +1 -0
  88. package/dist/vite/code-block.js +641 -0
  89. package/dist/vite/components.d.ts +36 -0
  90. package/dist/vite/components.d.ts.map +1 -0
  91. package/dist/vite/components.js +113 -0
  92. package/dist/vite/dispersion-rings-shader.d.ts +44 -0
  93. package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
  94. package/dist/vite/dispersion-rings-shader.js +282 -0
  95. package/dist/vite/easing-curves.d.ts +129 -0
  96. package/dist/vite/easing-curves.d.ts.map +1 -0
  97. package/dist/vite/easing-curves.js +584 -0
  98. package/dist/vite/easing-curves.test.d.ts +2 -0
  99. package/dist/vite/easing-curves.test.d.ts.map +1 -0
  100. package/dist/vite/easing-curves.test.js +200 -0
  101. package/dist/vite/generated-media-client.d.ts +35 -0
  102. package/dist/vite/generated-media-client.d.ts.map +1 -0
  103. package/dist/vite/generated-media-client.js +76 -0
  104. package/dist/vite/keyframes.d.ts +129 -0
  105. package/dist/vite/keyframes.d.ts.map +1 -0
  106. package/dist/vite/keyframes.js +194 -0
  107. package/dist/vite/layout-editor.d.ts +45 -0
  108. package/dist/vite/layout-editor.d.ts.map +1 -0
  109. package/dist/vite/layout-editor.js +587 -0
  110. package/dist/vite/layout-transition.d.ts +136 -0
  111. package/dist/vite/layout-transition.d.ts.map +1 -0
  112. package/dist/vite/layout-transition.js +581 -0
  113. package/dist/vite/liquid-gradient-shader.d.ts +62 -0
  114. package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
  115. package/dist/vite/liquid-gradient-shader.js +413 -0
  116. package/dist/vite/mdx-client.d.ts +15 -0
  117. package/dist/vite/mdx-client.d.ts.map +1 -0
  118. package/dist/vite/mdx-client.js +265 -0
  119. package/dist/vite/mdx-parse.d.ts +102 -0
  120. package/dist/vite/mdx-parse.d.ts.map +1 -0
  121. package/dist/vite/mdx-parse.js +251 -0
  122. package/dist/vite/mdx-provided-components.d.ts +18 -0
  123. package/dist/vite/mdx-provided-components.d.ts.map +1 -0
  124. package/dist/vite/mdx-provided-components.js +14 -0
  125. package/dist/vite/mdx-video.d.ts +327 -0
  126. package/dist/vite/mdx-video.d.ts.map +1 -0
  127. package/dist/vite/mdx-video.js +596 -0
  128. package/dist/vite/mdx-video.test.d.ts +9 -0
  129. package/dist/vite/mdx-video.test.d.ts.map +1 -0
  130. package/dist/vite/mdx-video.test.js +1945 -0
  131. package/dist/vite/media-components.d.ts +45 -0
  132. package/dist/vite/media-components.d.ts.map +1 -0
  133. package/dist/vite/media-components.js +355 -0
  134. package/dist/vite/media-duration-store.d.ts +66 -0
  135. package/dist/vite/media-duration-store.d.ts.map +1 -0
  136. package/dist/vite/media-duration-store.js +204 -0
  137. package/dist/vite/player-page.d.ts +35 -0
  138. package/dist/vite/player-page.d.ts.map +1 -0
  139. package/dist/vite/player-page.js +720 -0
  140. package/dist/vite/render-client.d.ts +18 -0
  141. package/dist/vite/render-client.d.ts.map +1 -0
  142. package/dist/vite/render-client.js +67 -0
  143. package/dist/vite/sdk.d.ts +221 -0
  144. package/dist/vite/sdk.d.ts.map +1 -0
  145. package/dist/vite/sdk.js +355 -0
  146. package/dist/vite/server-components.d.ts +55 -0
  147. package/dist/vite/server-components.d.ts.map +1 -0
  148. package/dist/vite/server-components.js +156 -0
  149. package/dist/vite/server-mdx.d.ts +74 -0
  150. package/dist/vite/server-mdx.d.ts.map +1 -0
  151. package/dist/vite/server-mdx.js +259 -0
  152. package/dist/vite/shader-renderer.d.ts +48 -0
  153. package/dist/vite/shader-renderer.d.ts.map +1 -0
  154. package/dist/vite/shader-renderer.js +411 -0
  155. package/dist/vite/store-hooks.d.ts +12 -0
  156. package/dist/vite/store-hooks.d.ts.map +1 -0
  157. package/dist/vite/store-hooks.js +93 -0
  158. package/dist/vite/store.d.ts +75 -0
  159. package/dist/vite/store.d.ts.map +1 -0
  160. package/dist/vite/store.js +52 -0
  161. package/dist/vite/store.test.d.ts +6 -0
  162. package/dist/vite/store.test.d.ts.map +1 -0
  163. package/dist/vite/store.test.js +172 -0
  164. package/dist/vite/tweakpane-hook.d.ts +94 -0
  165. package/dist/vite/tweakpane-hook.d.ts.map +1 -0
  166. package/dist/vite/tweakpane-hook.js +450 -0
  167. package/dist/vite/vite-plugin.d.ts +28 -0
  168. package/dist/vite/vite-plugin.d.ts.map +1 -0
  169. package/dist/vite/vite-plugin.js +538 -0
  170. package/dist/vite/wave-gradient-shader.d.ts +41 -0
  171. package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
  172. package/dist/vite/wave-gradient-shader.js +207 -0
  173. package/package.json +101 -24
  174. package/skills/egaki/SKILL.md +162 -1
  175. package/src/cli/cache-utils.ts +169 -0
  176. package/src/cli/cached-generate.test.ts +531 -0
  177. package/src/cli/cached-generate.ts +348 -0
  178. package/src/cli/cartesia-provider.ts +249 -0
  179. package/src/{cli.ts → cli/cli.ts} +836 -884
  180. package/src/cli/clone-generate.ts +202 -0
  181. package/src/{credentials.ts → cli/credentials.ts} +20 -0
  182. package/src/cli/elevenlabs-speech-provider.ts +243 -0
  183. package/src/cli/generate.test.ts +166 -0
  184. package/src/cli/generate.ts +912 -0
  185. package/src/cli/main.ts +8 -0
  186. package/src/{model-catalog.ts → cli/model-catalog.ts} +787 -58
  187. package/src/{models.ts → cli/models.ts} +65 -86
  188. package/src/cli/openai-speech-provider.ts +34 -0
  189. package/src/cli/separation-generate.ts +187 -0
  190. package/src/cli/speech-catalog.ts +253 -0
  191. package/src/cli/speech-generate.ts +275 -0
  192. package/src/cli/speech-models.ts +146 -0
  193. package/src/cli/speech-provider.test.ts +226 -0
  194. package/src/cli/transcription-catalog.ts +250 -0
  195. package/src/cli/transcription-generate.ts +214 -0
  196. package/src/cli/transcription-models.ts +136 -0
  197. package/src/cli/transcription-providers.ts +427 -0
  198. package/src/{xai-auth.ts → cli/xai-auth.ts} +1 -1
  199. package/src/vite/angled-screen.tsx +214 -0
  200. package/src/vite/app.tsx +197 -0
  201. package/src/vite/bands-shader.tsx +319 -0
  202. package/src/vite/code-block.tsx +939 -0
  203. package/src/vite/components.tsx +278 -0
  204. package/src/vite/dispersion-rings-shader.tsx +331 -0
  205. package/src/vite/easing-curves.test.ts +232 -0
  206. package/src/vite/easing-curves.ts +743 -0
  207. package/src/vite/generated-media-client.tsx +121 -0
  208. package/src/vite/keyframes.tsx +347 -0
  209. package/src/vite/layout-editor.tsx +709 -0
  210. package/src/vite/layout-transition.tsx +706 -0
  211. package/src/vite/liquid-gradient-shader.tsx +474 -0
  212. package/src/vite/mdx-client.tsx +420 -0
  213. package/src/vite/mdx-parse.ts +343 -0
  214. package/src/vite/mdx-provided-components.ts +19 -0
  215. package/src/vite/mdx-video.test.tsx +2219 -0
  216. package/src/vite/mdx-video.tsx +976 -0
  217. package/src/vite/media-components.tsx +473 -0
  218. package/src/vite/media-duration-store.ts +225 -0
  219. package/src/vite/motion-timing.ts +194 -0
  220. package/src/vite/player-page.tsx +1107 -0
  221. package/src/vite/render-client.ts +84 -0
  222. package/src/vite/sdk.ts +598 -0
  223. package/src/vite/server-components.tsx +241 -0
  224. package/src/vite/server-mdx.ts +285 -0
  225. package/src/vite/shader-renderer.tsx +535 -0
  226. package/src/vite/store-hooks.ts +117 -0
  227. package/src/vite/store.test.ts +202 -0
  228. package/src/vite/store.ts +115 -0
  229. package/src/vite/styles.css +21 -0
  230. package/src/vite/tweakpane-hook.tsx +593 -0
  231. package/src/vite/virtual-modules.d.ts +30 -0
  232. package/src/vite/vite-plugin.ts +584 -0
  233. package/src/vite/wave-gradient-shader.tsx +253 -0
  234. package/README.md +0 -370
  235. package/dist/antigravity-auth.d.ts +0 -15
  236. package/dist/antigravity-auth.d.ts.map +0 -1
  237. package/dist/antigravity-auth.js +0 -544
  238. package/dist/antigravity-image.d.ts +0 -11
  239. package/dist/antigravity-image.d.ts.map +0 -1
  240. package/dist/antigravity-image.js +0 -285
  241. package/dist/chatgpt-auth.d.ts.map +0 -1
  242. package/dist/cli.d.ts +0 -3
  243. package/dist/cli.d.ts.map +0 -1
  244. package/dist/credentials.d.ts.map +0 -1
  245. package/dist/gateway-model-candidates.generated.d.ts +0 -11
  246. package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
  247. package/dist/gateway-model-candidates.generated.js +0 -64
  248. package/dist/login.d.ts.map +0 -1
  249. package/dist/model-catalog.d.ts.map +0 -1
  250. package/dist/models.d.ts.map +0 -1
  251. package/dist/open-browser.d.ts.map +0 -1
  252. package/dist/subscription.d.ts.map +0 -1
  253. package/dist/video-model-catalog.d.ts.map +0 -1
  254. package/dist/xai-auth.d.ts.map +0 -1
  255. /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
  256. /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
  257. /package/dist/{credentials.d.ts → cli/credentials.d.ts} +0 -0
  258. /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
  259. /package/dist/{login.js → cli/login.js} +0 -0
  260. /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
  261. /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
  262. /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
  263. /package/dist/{subscription.js → cli/subscription.js} +0 -0
  264. /package/dist/{video-model-catalog.d.ts → cli/video-model-catalog.d.ts} +0 -0
  265. /package/dist/{video-model-catalog.js → cli/video-model-catalog.js} +0 -0
  266. /package/dist/{xai-auth.d.ts → cli/xai-auth.d.ts} +0 -0
  267. /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
  268. /package/src/{login.ts → cli/login.ts} +0 -0
  269. /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
  270. /package/src/{subscription.ts → cli/subscription.ts} +0 -0
  271. /package/src/{video-model-catalog.ts → cli/video-model-catalog.ts} +0 -0
@@ -0,0 +1,450 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Tweakpane integration for the egaki video player.
5
+ *
6
+ * Provides useTweakpane() — a React hook that registers a folder of
7
+ * parameters in a shared tweakpane Pane singleton. Each component gets
8
+ * its own folder; when the component unmounts the folder is disposed and
9
+ * removed from the UI. This means only currently-visible components show
10
+ * their parameters.
11
+ *
12
+ * The Pane is mounted into a sidebar container managed by <TweakpaneRoot />,
13
+ * rendered once in player-page.tsx. The sidebar is always visible on the
14
+ * right side of the page and takes its own space in the layout (no floating
15
+ * overlay). A single "Copy changes" button at the top of the pane serializes
16
+ * all active component parameters as a structured prompt for AI agents.
17
+ *
18
+ * Tweakpane is pure DOM (no React dependency), so there are no dual-React
19
+ * or SSR issues. During SSR (typeof window === 'undefined') the hook
20
+ * returns the raw default values and skips all DOM work.
21
+ */
22
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore, createContext, useContext } from 'react';
23
+ import { LayoutContainerContext, useIsPremounting } from "./mdx-video.js";
24
+ export const TweakpaneContext = createContext(null);
25
+ // ---------------------------------------------------------------------------
26
+ // Global disable switch
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Set to `true` to completely disable tweakpane across all components.
30
+ * useTweakpane() will return raw default values without mounting any UI.
31
+ */
32
+ export let TWEAKPANE_DISABLED = false;
33
+ // ---------------------------------------------------------------------------
34
+ // Singleton pane state (module-level, not React state)
35
+ // ---------------------------------------------------------------------------
36
+ /** Lazily imported tweakpane Pane class and essentials plugin module. */
37
+ let PaneClass = null;
38
+ let EssentialsPlugin = null;
39
+ let paneInstance = null;
40
+ /** External container element set by TweakpaneRoot. The Pane mounts into
41
+ * this DOM node instead of creating its own floating div. */
42
+ let externalContainer = null;
43
+ /** Persisted expanded state for the root pane across destroy/recreate cycles. */
44
+ let paneExpandedState = true;
45
+ const activeFolders = new Map();
46
+ /** Listeners notified when activeFolders changes (for copy button visibility). */
47
+ const folderListeners = new Set();
48
+ function notifyFolderListeners() {
49
+ folderListeners.forEach((fn) => fn());
50
+ }
51
+ /** Shallow-clone arrays and plain objects so tweakpane's in-place mutations
52
+ * don't leak between params and defaults references. */
53
+ function cloneValue(value) {
54
+ if (Array.isArray(value))
55
+ return [...value];
56
+ if (typeof value === 'object' && value !== null)
57
+ return { ...value };
58
+ return value;
59
+ }
60
+ function resolveDefault(v) {
61
+ if (isCubicBezierParam(v))
62
+ return [...v.value];
63
+ if (isRangeParam(v))
64
+ return { ...v.value };
65
+ if (typeof v === 'object' && v !== null && 'value' in v)
66
+ return v.value;
67
+ return v;
68
+ }
69
+ function isSliderParam(v) {
70
+ return typeof v === 'object' && v !== null && 'value' in v && !('options' in v) && !('type' in v);
71
+ }
72
+ function isSelectParam(v) {
73
+ return typeof v === 'object' && v !== null && 'options' in v;
74
+ }
75
+ function isCubicBezierParam(v) {
76
+ return typeof v === 'object' && v !== null && 'type' in v && v.type === 'cubicBezier';
77
+ }
78
+ function isRangeParam(v) {
79
+ return typeof v === 'object' && v !== null && 'type' in v && v.type === 'range';
80
+ }
81
+ /** Guard against concurrent ensurePane() calls during the async import. */
82
+ let panePromise = null;
83
+ async function ensurePane() {
84
+ if (paneInstance)
85
+ return paneInstance;
86
+ if (panePromise)
87
+ return panePromise;
88
+ panePromise = (async () => {
89
+ if (!PaneClass) {
90
+ // Load tweakpane and the essentials plugin (cubic bezier blade) together.
91
+ // Both are cached at module level so subsequent pane re-creations (after
92
+ // disposePane on scene change) register the plugin synchronously, avoiding
93
+ // a race where concurrent ensurePane() callers get a pane without the
94
+ // plugin registered.
95
+ const [mod, essentials] = await Promise.all([
96
+ import('tweakpane'),
97
+ import('@tweakpane/plugin-essentials').catch(() => null),
98
+ ]);
99
+ PaneClass = mod.Pane;
100
+ EssentialsPlugin = essentials;
101
+ }
102
+ if (!externalContainer) {
103
+ console.warn('[egaki] TweakpaneRoot not mounted — tweakpane has no container');
104
+ return null;
105
+ }
106
+ paneInstance = new PaneClass({ container: externalContainer, title: 'Parameters', expanded: paneExpandedState });
107
+ if (EssentialsPlugin)
108
+ paneInstance.registerPlugin(EssentialsPlugin);
109
+ paneInstance.on('fold', (ev) => {
110
+ paneExpandedState = ev.expanded;
111
+ });
112
+ return paneInstance;
113
+ })();
114
+ const pane = await panePromise;
115
+ panePromise = null;
116
+ return pane;
117
+ }
118
+ function disposePane() {
119
+ if (paneInstance) {
120
+ paneInstance.dispose();
121
+ paneInstance = null;
122
+ }
123
+ // Don't remove the container — it's owned by TweakpaneRoot (React).
124
+ // The Pane's dispose() already removes its own element from the container.
125
+ }
126
+ // ---------------------------------------------------------------------------
127
+ // Copy prompt serialization
128
+ // ---------------------------------------------------------------------------
129
+ function getCurrentSection(frame, sections) {
130
+ let acc = 0;
131
+ for (const s of sections) {
132
+ if (frame < acc + s.durationInFrames)
133
+ return s;
134
+ acc += s.durationInFrames;
135
+ }
136
+ return sections.length > 0 ? sections[sections.length - 1] : null;
137
+ }
138
+ function formatValue(v) {
139
+ if (Array.isArray(v) && v.length === 4 && v.every((x) => typeof x === 'number')) {
140
+ // Cubic bezier control points — format as cubicBezier() call so agents
141
+ // can paste it directly as an easing prop value.
142
+ return `cubicBezier(${v.join(', ')})`;
143
+ }
144
+ if (typeof v === 'object' && v !== null && 'min' in v && 'max' in v) {
145
+ const r = v;
146
+ const fmtNum = (n) => Number.isInteger(n) ? String(n) : n.toFixed(4).replace(/0+$/, '').replace(/\.$/, '');
147
+ return `{ min: ${fmtNum(r.min)}, max: ${fmtNum(r.max)} }`;
148
+ }
149
+ if (typeof v === 'number') {
150
+ return Number.isInteger(v) ? String(v) : v.toFixed(4).replace(/0+$/, '').replace(/\.$/, '');
151
+ }
152
+ return JSON.stringify(v);
153
+ }
154
+ function serializeTweakpanePrompt(ctx) {
155
+ if (activeFolders.size === 0)
156
+ return '';
157
+ const parts = ['## Parameter changes\n'];
158
+ if (ctx?.entryPath) {
159
+ parts.push(`File: ${ctx.entryPath}`);
160
+ }
161
+ // Frame + section context
162
+ if (ctx) {
163
+ const currentFrame = ctx.playerRef.current?.getCurrentFrame() ?? null;
164
+ if (currentFrame !== null) {
165
+ const seconds = (currentFrame / ctx.fps).toFixed(1);
166
+ parts.push(`Current frame: ${currentFrame} (${seconds}s at ${ctx.fps}fps)`);
167
+ const section = getCurrentSection(currentFrame, ctx.sections);
168
+ if (section?.heading) {
169
+ parts.push(`Section: "${section.heading}"`);
170
+ }
171
+ parts.push('');
172
+ }
173
+ }
174
+ let hasAnyChanges = false;
175
+ for (const [id, entry] of activeFolders) {
176
+ const changed = [];
177
+ for (const [key, current] of Object.entries(entry.params)) {
178
+ const def = entry.defaults[key];
179
+ if (current !== def) {
180
+ changed.push(` ${key}: ${formatValue(current)}`);
181
+ }
182
+ }
183
+ if (changed.length > 0) {
184
+ hasAnyChanges = true;
185
+ parts.push(`**${entry.label}**`);
186
+ parts.push(changed.join('\n'));
187
+ parts.push('');
188
+ }
189
+ }
190
+ // If nothing changed from defaults, still show all current values
191
+ if (!hasAnyChanges) {
192
+ parts.push('No parameters changed from defaults.\n');
193
+ for (const [id, entry] of activeFolders) {
194
+ parts.push(`**${entry.label}** (current values)`);
195
+ for (const [key, current] of Object.entries(entry.params)) {
196
+ parts.push(` ${key}: ${formatValue(current)}`);
197
+ }
198
+ parts.push('');
199
+ }
200
+ }
201
+ parts.push('Apply these values as the new props in the component code.');
202
+ return parts.join('\n');
203
+ }
204
+ // Module-level ref for the context value (set by TweakpaneRoot, read by the
205
+ // copy button handler). Avoids threading React context into the imperative
206
+ // tweakpane button click handler.
207
+ let contextRef = null;
208
+ // Stable module-level callbacks for useSyncExternalStore in CopyChangesButton.
209
+ function subscribeFolders(cb) {
210
+ folderListeners.add(cb);
211
+ return () => { folderListeners.delete(cb); };
212
+ }
213
+ const getHasActiveFolders = () => activeFolders.size > 0;
214
+ const getServerHasActiveFolders = () => false;
215
+ /** React component: copy button rendered above the tweakpane pane. */
216
+ function CopyChangesButton() {
217
+ const [copied, setCopied] = useState(false);
218
+ const hasActiveFolders = useSyncExternalStore(subscribeFolders, getHasActiveFolders, getServerHasActiveFolders);
219
+ if (!hasActiveFolders)
220
+ return null;
221
+ return (_jsx("button", { onClick: () => {
222
+ const text = serializeTweakpanePrompt(contextRef);
223
+ if (!text)
224
+ return;
225
+ navigator.clipboard.writeText(text).catch(() => { });
226
+ setCopied(true);
227
+ setTimeout(() => setCopied(false), 1500);
228
+ }, style: {
229
+ display: 'flex',
230
+ alignItems: 'center',
231
+ justifyContent: 'center',
232
+ gap: 6,
233
+ margin: '8px 8px 4px',
234
+ width: 'calc(100% - 16px)',
235
+ padding: '7px 12px',
236
+ background: copied ? 'rgba(34,197,94,0.15)' : 'rgba(255,255,255,0.06)',
237
+ border: '1px solid rgba(255,255,255,0.08)',
238
+ borderRadius: 6,
239
+ color: copied ? '#4ade80' : '#a1a1aa',
240
+ fontSize: 12,
241
+ fontWeight: 500,
242
+ fontFamily: '-apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif',
243
+ cursor: 'pointer',
244
+ transition: 'background 0.15s, color 0.15s',
245
+ }, children: copied ? '✓ Copied prompt' : '📋 Copy changes' }));
246
+ }
247
+ // ---------------------------------------------------------------------------
248
+ // useTweakpane hook
249
+ // ---------------------------------------------------------------------------
250
+ /**
251
+ * Register a folder of tweakable parameters for the current component.
252
+ *
253
+ * @param label Folder title shown in the pane UI. Also set as
254
+ * `data-tweakpane="label"` on a wrapper div for agent inspection.
255
+ * @param schema Parameter definitions. Each key maps to either:
256
+ * - a bare `number`, `boolean`, or `string` (auto-inferred control)
257
+ * - a `{ value, min?, max?, step? }` object for explicit slider range
258
+ *
259
+ * Returns a reactive object with current values. Updating a control in the
260
+ * pane immediately triggers a React re-render with fresh values.
261
+ *
262
+ * On unmount the folder is disposed and removed from the pane.
263
+ */
264
+ export function useTweakpane(label, schema) {
265
+ // Skip registration inside layout-transition ghost renders (the previous
266
+ // section is re-rendered hidden for FLIP measurement; we don't want its
267
+ // components polluting the tweakpane UI) and premounted sequences
268
+ // (premountFor renders the next section early but invisible for preloading;
269
+ // Remotion freezes useCurrentFrame() at 0 and sets opacity: 0).
270
+ const container = useContext(LayoutContainerContext);
271
+ const isGhost = container === 'ghost';
272
+ const isPremounting = useIsPremounting();
273
+ const skip = isGhost || isPremounting;
274
+ const [values, setValues] = useState(() => {
275
+ const v = {};
276
+ for (const [key, param] of Object.entries(schema)) {
277
+ v[key] = resolveDefault(param);
278
+ }
279
+ return v;
280
+ });
281
+ // Track the schema for updates
282
+ const schemaRef = useRef(schema);
283
+ schemaRef.current = schema;
284
+ // Stable ID for this hook instance
285
+ const idRef = useRef(`${label}-${Math.random().toString(36).slice(2, 8)}`);
286
+ useEffect(() => {
287
+ // SSR guard, ghost/premount render, or globally disabled — skip all pane work.
288
+ // `skip` is in the dep array so the effect re-runs when premounting ends and
289
+ // the section becomes active (registers tweakpane) or vice versa (tears down).
290
+ if (typeof window === 'undefined' || skip || TWEAKPANE_DISABLED)
291
+ return;
292
+ const id = idRef.current;
293
+ // Mutable params object that tweakpane binds to
294
+ const params = {};
295
+ const defaults = {};
296
+ for (const [key, param] of Object.entries(schemaRef.current)) {
297
+ const def = resolveDefault(param);
298
+ // Clone object/array values so tweakpane's in-place mutations on params
299
+ // don't corrupt the defaults (used by "Copy changes" diffing).
300
+ params[key] = cloneValue(def);
301
+ defaults[key] = cloneValue(def);
302
+ }
303
+ let folder = null;
304
+ let disposed = false;
305
+ void (async () => {
306
+ const pane = await ensurePane();
307
+ if (disposed || !pane)
308
+ return;
309
+ folder = pane.addFolder({ title: label, expanded: true });
310
+ for (const [key, param] of Object.entries(schemaRef.current)) {
311
+ if (isCubicBezierParam(param)) {
312
+ // Cubic bezier blade — visual curve editor
313
+ const blade = folder.addBlade({
314
+ view: 'cubicbezier',
315
+ value: param.value,
316
+ expanded: true,
317
+ label: key,
318
+ picker: 'inline',
319
+ });
320
+ blade.on('change', (ev) => {
321
+ const val = ev.value;
322
+ const tuple = [
323
+ Math.round(val.x1 * 1000) / 1000,
324
+ Math.round(val.y1 * 1000) / 1000,
325
+ Math.round(val.x2 * 1000) / 1000,
326
+ Math.round(val.y2 * 1000) / 1000,
327
+ ];
328
+ params[key] = tuple;
329
+ setValues((prev) => ({ ...prev, [key]: tuple }));
330
+ });
331
+ continue;
332
+ }
333
+ if (isRangeParam(param)) {
334
+ // Range slider from plugin-essentials — min/max pair as one control
335
+ folder.addBinding(params, key, {
336
+ min: param.min,
337
+ max: param.max,
338
+ step: param.step,
339
+ }).on('change', (ev) => {
340
+ const val = ev.value;
341
+ setValues((prev) => ({ ...prev, [key]: { min: val.min, max: val.max } }));
342
+ });
343
+ continue;
344
+ }
345
+ const opts = {};
346
+ if (isSelectParam(param)) {
347
+ // Build { Label: value } map for tweakpane list binding
348
+ const optionsMap = {};
349
+ for (const opt of param.options)
350
+ optionsMap[opt] = opt;
351
+ opts.options = optionsMap;
352
+ }
353
+ else if (isSliderParam(param)) {
354
+ if (param.min !== undefined)
355
+ opts.min = param.min;
356
+ if (param.max !== undefined)
357
+ opts.max = param.max;
358
+ if (param.step !== undefined)
359
+ opts.step = param.step;
360
+ }
361
+ else if (typeof param === 'number') {
362
+ // Auto-infer range for bare numbers
363
+ if (param >= 0 && param <= 1) {
364
+ opts.min = 0;
365
+ opts.max = 1;
366
+ opts.step = 0.01;
367
+ }
368
+ else if (param >= 0 && param <= 10) {
369
+ opts.min = 0;
370
+ opts.max = param * 3 || 10;
371
+ opts.step = 0.1;
372
+ }
373
+ else if (param >= 0 && param <= 100) {
374
+ opts.min = 0;
375
+ opts.max = param * 3 || 100;
376
+ opts.step = 1;
377
+ }
378
+ else if (param >= 0) {
379
+ opts.min = 0;
380
+ opts.max = param * 3 || 1000;
381
+ opts.step = 10;
382
+ }
383
+ }
384
+ folder.addBinding(params, key, opts).on('change', (ev) => {
385
+ setValues((prev) => ({ ...prev, [key]: ev.value }));
386
+ });
387
+ }
388
+ // Register for copy prompt
389
+ activeFolders.set(id, { label, params, defaults });
390
+ notifyFolderListeners();
391
+ })();
392
+ return () => {
393
+ disposed = true;
394
+ if (folder) {
395
+ folder.dispose();
396
+ folder = null;
397
+ }
398
+ activeFolders.delete(id);
399
+ notifyFolderListeners();
400
+ // If no more folders, tear down the pane entirely
401
+ if (activeFolders.size === 0) {
402
+ disposePane();
403
+ }
404
+ };
405
+ }, [label, skip]);
406
+ return values;
407
+ }
408
+ // ---------------------------------------------------------------------------
409
+ // TweakpaneRoot — sidebar container for the tweakpane pane
410
+ // ---------------------------------------------------------------------------
411
+ /** Sidebar width constant shared with player-page layout. */
412
+ export const SIDEBAR_WIDTH = 280;
413
+ /**
414
+ * Right sidebar that hosts the tweakpane Pane. Always visible in the page
415
+ * layout; the Pane mounts into the scrollable inner div. Also provides
416
+ * player metadata (frame, fps, sections) to the copy prompt button.
417
+ */
418
+ export function TweakpaneRoot({ playerRef, fps, sections, entryPath, }) {
419
+ // Ref callback sets the container synchronously when the DOM node mounts,
420
+ // before any child useEffect (where useTweakpane calls ensurePane). On
421
+ // unmount the callback receives null, so we clean up the pane.
422
+ const containerRefCallback = useCallback((node) => {
423
+ if (node) {
424
+ externalContainer = node;
425
+ }
426
+ else {
427
+ disposePane();
428
+ externalContainer = null;
429
+ }
430
+ }, []);
431
+ // Keep module-level ref in sync for the copy button handler
432
+ useEffect(() => {
433
+ contextRef = { playerRef, fps, sections, entryPath };
434
+ return () => {
435
+ contextRef = null;
436
+ };
437
+ }, [playerRef, fps, sections, entryPath]);
438
+ return (_jsxs("div", { className: 'egaki-sidebar', style: {
439
+ width: SIDEBAR_WIDTH,
440
+ minWidth: SIDEBAR_WIDTH,
441
+ height: '100vh',
442
+ position: 'sticky',
443
+ top: 0,
444
+ overflowY: 'auto',
445
+ overflowX: 'hidden',
446
+ background: '#111',
447
+ borderLeft: '1px solid rgba(255,255,255,0.08)',
448
+ flexShrink: 0,
449
+ }, children: [_jsx(CopyChangesButton, {}), _jsx("div", { ref: containerRefCallback, id: 'egaki-tweakpane', style: { width: '100%' } })] }));
450
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Vite plugin for the video framework.
3
+ *
4
+ * Discovers MDX files in the project root and serves each as a page route.
5
+ * The default entry (video.mdx or index.mdx) is served at /; others at
6
+ * /<relative-path-without-extension>. Auto-injects spiceflow + react plugins.
7
+ *
8
+ * Usage in vite.config.ts:
9
+ * import { video } from 'egaki/vite'
10
+ * export default defineConfig({ plugins: [video()] })
11
+ * // or with explicit default entry:
12
+ * export default defineConfig({ plugins: [video({ entry: './video.mdx' })] })
13
+ */
14
+ import type { PluginOption } from 'vite';
15
+ export interface VideoPluginOptions {
16
+ /** Path to the default MDX entry file (relative to vite root or absolute).
17
+ * When omitted, auto-discovers: video.mdx > index.mdx > first .mdx found.
18
+ * All other .mdx files in the project become additional routes. */
19
+ entry?: string;
20
+ }
21
+ /**
22
+ * Compare old and new MDX sources section-by-section. Returns the index
23
+ * of the first added or edited section, or null if nothing changed (or
24
+ * only deletions occurred, which we skip per design).
25
+ */
26
+ export declare function findChangedSectionIndex(oldSource: string, newSource: string): number | null;
27
+ export declare function video(options?: VideoPluginOptions): PluginOption[];
28
+ //# sourceMappingURL=vite-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../../src/vite/vite-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,MAAM,CAAA;AA2BhD,MAAM,WAAW,kBAAkB;IACjC;;wEAEoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAuCD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB3F;AAoCD,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,YAAY,EAAE,CAwalE"}