egaki 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +145 -9
- package/dist/cli/cache-utils.d.ts +33 -0
- package/dist/cli/cache-utils.d.ts.map +1 -0
- package/dist/cli/cache-utils.js +157 -0
- package/dist/cli/cached-generate.d.ts +105 -0
- package/dist/cli/cached-generate.d.ts.map +1 -0
- package/dist/cli/cached-generate.js +208 -0
- package/dist/cli/cached-generate.test.d.ts +11 -0
- package/dist/cli/cached-generate.test.d.ts.map +1 -0
- package/dist/cli/cached-generate.test.js +431 -0
- package/dist/cli/cartesia-provider.d.ts +16 -0
- package/dist/cli/cartesia-provider.d.ts.map +1 -0
- package/dist/cli/cartesia-provider.js +184 -0
- package/dist/cli/chatgpt-auth.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/{cli.js → cli/cli.js} +626 -600
- package/dist/cli/clone-generate.d.ts +26 -0
- package/dist/cli/clone-generate.d.ts.map +1 -0
- package/dist/cli/clone-generate.js +148 -0
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/{credentials.js → cli/credentials.js} +20 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts +26 -0
- package/dist/cli/elevenlabs-speech-provider.d.ts.map +1 -0
- package/dist/cli/elevenlabs-speech-provider.js +199 -0
- package/dist/cli/generate.d.ts +140 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +604 -0
- package/dist/cli/generate.test.d.ts +2 -0
- package/dist/cli/generate.test.d.ts.map +1 -0
- package/dist/cli/generate.test.js +131 -0
- package/dist/cli/login.d.ts.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +7 -0
- package/dist/{model-catalog.d.ts → cli/model-catalog.d.ts} +6 -0
- package/dist/cli/model-catalog.d.ts.map +1 -0
- package/dist/{model-catalog.js → cli/model-catalog.js} +714 -4
- package/dist/{models.d.ts → cli/models.d.ts} +12 -5
- package/dist/cli/models.d.ts.map +1 -0
- package/dist/{models.js → cli/models.js} +58 -57
- package/dist/cli/open-browser.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.d.ts +3 -0
- package/dist/cli/openai-speech-provider.d.ts.map +1 -0
- package/dist/cli/openai-speech-provider.js +28 -0
- package/dist/cli/separation-generate.d.ts +34 -0
- package/dist/cli/separation-generate.d.ts.map +1 -0
- package/dist/cli/separation-generate.js +117 -0
- package/dist/cli/speech-catalog.d.ts +35 -0
- package/dist/cli/speech-catalog.d.ts.map +1 -0
- package/dist/cli/speech-catalog.js +195 -0
- package/dist/cli/speech-generate.d.ts +86 -0
- package/dist/cli/speech-generate.d.ts.map +1 -0
- package/dist/cli/speech-generate.js +156 -0
- package/dist/cli/speech-models.d.ts +8 -0
- package/dist/cli/speech-models.d.ts.map +1 -0
- package/dist/cli/speech-models.js +109 -0
- package/dist/cli/speech-provider.test.d.ts +2 -0
- package/dist/cli/speech-provider.test.d.ts.map +1 -0
- package/dist/cli/speech-provider.test.js +197 -0
- package/dist/cli/subscription.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.d.ts +35 -0
- package/dist/cli/transcription-catalog.d.ts.map +1 -0
- package/dist/cli/transcription-catalog.js +192 -0
- package/dist/cli/transcription-generate.d.ts +62 -0
- package/dist/cli/transcription-generate.d.ts.map +1 -0
- package/dist/cli/transcription-generate.js +144 -0
- package/dist/cli/transcription-models.d.ts +9 -0
- package/dist/cli/transcription-models.d.ts.map +1 -0
- package/dist/cli/transcription-models.js +100 -0
- package/dist/cli/transcription-providers.d.ts +22 -0
- package/dist/cli/transcription-providers.d.ts.map +1 -0
- package/dist/cli/transcription-providers.js +290 -0
- package/dist/cli/video-model-catalog.d.ts.map +1 -0
- package/dist/cli/xai-auth.d.ts.map +1 -0
- package/dist/{xai-auth.js → cli/xai-auth.js} +1 -1
- package/dist/vite/angled-screen.d.ts +57 -0
- package/dist/vite/angled-screen.d.ts.map +1 -0
- package/dist/vite/angled-screen.js +115 -0
- package/dist/vite/app.d.ts +51 -0
- package/dist/vite/app.d.ts.map +1 -0
- package/dist/vite/app.js +150 -0
- package/dist/vite/bands-shader.d.ts +47 -0
- package/dist/vite/bands-shader.d.ts.map +1 -0
- package/dist/vite/bands-shader.js +267 -0
- package/dist/vite/code-block.d.ts +107 -0
- package/dist/vite/code-block.d.ts.map +1 -0
- package/dist/vite/code-block.js +641 -0
- package/dist/vite/components.d.ts +36 -0
- package/dist/vite/components.d.ts.map +1 -0
- package/dist/vite/components.js +113 -0
- package/dist/vite/dispersion-rings-shader.d.ts +44 -0
- package/dist/vite/dispersion-rings-shader.d.ts.map +1 -0
- package/dist/vite/dispersion-rings-shader.js +282 -0
- package/dist/vite/easing-curves.d.ts +129 -0
- package/dist/vite/easing-curves.d.ts.map +1 -0
- package/dist/vite/easing-curves.js +584 -0
- package/dist/vite/easing-curves.test.d.ts +2 -0
- package/dist/vite/easing-curves.test.d.ts.map +1 -0
- package/dist/vite/easing-curves.test.js +200 -0
- package/dist/vite/generated-media-client.d.ts +35 -0
- package/dist/vite/generated-media-client.d.ts.map +1 -0
- package/dist/vite/generated-media-client.js +76 -0
- package/dist/vite/keyframes.d.ts +129 -0
- package/dist/vite/keyframes.d.ts.map +1 -0
- package/dist/vite/keyframes.js +194 -0
- package/dist/vite/layout-editor.d.ts +45 -0
- package/dist/vite/layout-editor.d.ts.map +1 -0
- package/dist/vite/layout-editor.js +587 -0
- package/dist/vite/layout-transition.d.ts +136 -0
- package/dist/vite/layout-transition.d.ts.map +1 -0
- package/dist/vite/layout-transition.js +581 -0
- package/dist/vite/liquid-gradient-shader.d.ts +62 -0
- package/dist/vite/liquid-gradient-shader.d.ts.map +1 -0
- package/dist/vite/liquid-gradient-shader.js +413 -0
- package/dist/vite/mdx-client.d.ts +15 -0
- package/dist/vite/mdx-client.d.ts.map +1 -0
- package/dist/vite/mdx-client.js +265 -0
- package/dist/vite/mdx-parse.d.ts +102 -0
- package/dist/vite/mdx-parse.d.ts.map +1 -0
- package/dist/vite/mdx-parse.js +251 -0
- package/dist/vite/mdx-provided-components.d.ts +18 -0
- package/dist/vite/mdx-provided-components.d.ts.map +1 -0
- package/dist/vite/mdx-provided-components.js +14 -0
- package/dist/vite/mdx-video.d.ts +327 -0
- package/dist/vite/mdx-video.d.ts.map +1 -0
- package/dist/vite/mdx-video.js +596 -0
- package/dist/vite/mdx-video.test.d.ts +9 -0
- package/dist/vite/mdx-video.test.d.ts.map +1 -0
- package/dist/vite/mdx-video.test.js +1945 -0
- package/dist/vite/media-components.d.ts +45 -0
- package/dist/vite/media-components.d.ts.map +1 -0
- package/dist/vite/media-components.js +355 -0
- package/dist/vite/media-duration-store.d.ts +66 -0
- package/dist/vite/media-duration-store.d.ts.map +1 -0
- package/dist/vite/media-duration-store.js +204 -0
- package/dist/vite/player-page.d.ts +35 -0
- package/dist/vite/player-page.d.ts.map +1 -0
- package/dist/vite/player-page.js +720 -0
- package/dist/vite/render-client.d.ts +18 -0
- package/dist/vite/render-client.d.ts.map +1 -0
- package/dist/vite/render-client.js +67 -0
- package/dist/vite/sdk.d.ts +221 -0
- package/dist/vite/sdk.d.ts.map +1 -0
- package/dist/vite/sdk.js +355 -0
- package/dist/vite/server-components.d.ts +55 -0
- package/dist/vite/server-components.d.ts.map +1 -0
- package/dist/vite/server-components.js +156 -0
- package/dist/vite/server-mdx.d.ts +74 -0
- package/dist/vite/server-mdx.d.ts.map +1 -0
- package/dist/vite/server-mdx.js +259 -0
- package/dist/vite/shader-renderer.d.ts +48 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -0
- package/dist/vite/shader-renderer.js +411 -0
- package/dist/vite/store-hooks.d.ts +12 -0
- package/dist/vite/store-hooks.d.ts.map +1 -0
- package/dist/vite/store-hooks.js +93 -0
- package/dist/vite/store.d.ts +75 -0
- package/dist/vite/store.d.ts.map +1 -0
- package/dist/vite/store.js +52 -0
- package/dist/vite/store.test.d.ts +6 -0
- package/dist/vite/store.test.d.ts.map +1 -0
- package/dist/vite/store.test.js +172 -0
- package/dist/vite/tweakpane-hook.d.ts +94 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -0
- package/dist/vite/tweakpane-hook.js +450 -0
- package/dist/vite/vite-plugin.d.ts +28 -0
- package/dist/vite/vite-plugin.d.ts.map +1 -0
- package/dist/vite/vite-plugin.js +539 -0
- package/dist/vite/wave-gradient-shader.d.ts +41 -0
- package/dist/vite/wave-gradient-shader.d.ts.map +1 -0
- package/dist/vite/wave-gradient-shader.js +207 -0
- package/package.json +101 -24
- package/skills/egaki/SKILL.md +162 -1
- package/src/cli/cache-utils.ts +169 -0
- package/src/cli/cached-generate.test.ts +531 -0
- package/src/cli/cached-generate.ts +348 -0
- package/src/cli/cartesia-provider.ts +249 -0
- package/src/{cli.ts → cli/cli.ts} +836 -884
- package/src/cli/clone-generate.ts +202 -0
- package/src/{credentials.ts → cli/credentials.ts} +20 -0
- package/src/cli/elevenlabs-speech-provider.ts +243 -0
- package/src/cli/generate.test.ts +166 -0
- package/src/cli/generate.ts +912 -0
- package/src/cli/main.ts +8 -0
- package/src/{model-catalog.ts → cli/model-catalog.ts} +787 -58
- package/src/{models.ts → cli/models.ts} +65 -86
- package/src/cli/openai-speech-provider.ts +34 -0
- package/src/cli/separation-generate.ts +187 -0
- package/src/cli/speech-catalog.ts +253 -0
- package/src/cli/speech-generate.ts +275 -0
- package/src/cli/speech-models.ts +146 -0
- package/src/cli/speech-provider.test.ts +226 -0
- package/src/cli/transcription-catalog.ts +250 -0
- package/src/cli/transcription-generate.ts +214 -0
- package/src/cli/transcription-models.ts +136 -0
- package/src/cli/transcription-providers.ts +427 -0
- package/src/{xai-auth.ts → cli/xai-auth.ts} +1 -1
- package/src/vite/angled-screen.tsx +214 -0
- package/src/vite/app.tsx +197 -0
- package/src/vite/bands-shader.tsx +319 -0
- package/src/vite/code-block.tsx +939 -0
- package/src/vite/components.tsx +278 -0
- package/src/vite/dispersion-rings-shader.tsx +331 -0
- package/src/vite/easing-curves.test.ts +232 -0
- package/src/vite/easing-curves.ts +743 -0
- package/src/vite/generated-media-client.tsx +121 -0
- package/src/vite/keyframes.tsx +347 -0
- package/src/vite/layout-editor.tsx +709 -0
- package/src/vite/layout-transition.tsx +706 -0
- package/src/vite/liquid-gradient-shader.tsx +474 -0
- package/src/vite/mdx-client.tsx +420 -0
- package/src/vite/mdx-parse.ts +343 -0
- package/src/vite/mdx-provided-components.ts +19 -0
- package/src/vite/mdx-video.test.tsx +2219 -0
- package/src/vite/mdx-video.tsx +976 -0
- package/src/vite/media-components.tsx +473 -0
- package/src/vite/media-duration-store.ts +225 -0
- package/src/vite/motion-timing.ts +194 -0
- package/src/vite/player-page.tsx +1107 -0
- package/src/vite/render-client.ts +84 -0
- package/src/vite/sdk.ts +598 -0
- package/src/vite/server-components.tsx +241 -0
- package/src/vite/server-mdx.ts +285 -0
- package/src/vite/shader-renderer.tsx +535 -0
- package/src/vite/store-hooks.ts +117 -0
- package/src/vite/store.test.ts +202 -0
- package/src/vite/store.ts +115 -0
- package/src/vite/styles.css +21 -0
- package/src/vite/tweakpane-hook.tsx +593 -0
- package/src/vite/virtual-modules.d.ts +30 -0
- package/src/vite/vite-plugin.ts +585 -0
- package/src/vite/wave-gradient-shader.tsx +253 -0
- package/README.md +0 -370
- package/dist/antigravity-auth.d.ts +0 -15
- package/dist/antigravity-auth.d.ts.map +0 -1
- package/dist/antigravity-auth.js +0 -544
- package/dist/antigravity-image.d.ts +0 -11
- package/dist/antigravity-image.d.ts.map +0 -1
- package/dist/antigravity-image.js +0 -285
- package/dist/chatgpt-auth.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.d.ts +0 -11
- package/dist/gateway-model-candidates.generated.d.ts.map +0 -1
- package/dist/gateway-model-candidates.generated.js +0 -64
- package/dist/login.d.ts.map +0 -1
- package/dist/model-catalog.d.ts.map +0 -1
- package/dist/models.d.ts.map +0 -1
- package/dist/open-browser.d.ts.map +0 -1
- package/dist/subscription.d.ts.map +0 -1
- package/dist/video-model-catalog.d.ts.map +0 -1
- package/dist/xai-auth.d.ts.map +0 -1
- /package/dist/{chatgpt-auth.d.ts → cli/chatgpt-auth.d.ts} +0 -0
- /package/dist/{chatgpt-auth.js → cli/chatgpt-auth.js} +0 -0
- /package/dist/{credentials.d.ts → cli/credentials.d.ts} +0 -0
- /package/dist/{login.d.ts → cli/login.d.ts} +0 -0
- /package/dist/{login.js → cli/login.js} +0 -0
- /package/dist/{open-browser.d.ts → cli/open-browser.d.ts} +0 -0
- /package/dist/{open-browser.js → cli/open-browser.js} +0 -0
- /package/dist/{subscription.d.ts → cli/subscription.d.ts} +0 -0
- /package/dist/{subscription.js → cli/subscription.js} +0 -0
- /package/dist/{video-model-catalog.d.ts → cli/video-model-catalog.d.ts} +0 -0
- /package/dist/{video-model-catalog.js → cli/video-model-catalog.js} +0 -0
- /package/dist/{xai-auth.d.ts → cli/xai-auth.d.ts} +0 -0
- /package/src/{chatgpt-auth.ts → cli/chatgpt-auth.ts} +0 -0
- /package/src/{login.ts → cli/login.ts} +0 -0
- /package/src/{open-browser.ts → cli/open-browser.ts} +0 -0
- /package/src/{subscription.ts → cli/subscription.ts} +0 -0
- /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;AA4BhD,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"}
|