egaki 0.7.1 → 0.8.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.
- package/CHANGELOG.md +32 -0
- package/dist/cli/bpm-detect.d.ts +7 -0
- package/dist/cli/bpm-detect.d.ts.map +1 -0
- package/dist/cli/bpm-detect.js +123 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +115 -0
- package/dist/cli/loudness-detect.d.ts +13 -0
- package/dist/cli/loudness-detect.d.ts.map +1 -0
- package/dist/cli/loudness-detect.js +96 -0
- package/dist/vite/code-block.d.ts.map +1 -1
- package/dist/vite/code-block.js +26 -5
- package/dist/vite/layout-editor.js +1 -1
- package/dist/vite/mdx-video.test.js +0 -79
- package/dist/vite/player-page.d.ts.map +1 -1
- package/dist/vite/player-page.js +6 -94
- package/dist/vite/store-hooks.d.ts +2 -0
- package/dist/vite/store-hooks.d.ts.map +1 -1
- package/dist/vite/store-hooks.js +8 -0
- package/dist/vite/store.d.ts +5 -0
- package/dist/vite/store.d.ts.map +1 -1
- package/dist/vite/store.js +5 -0
- package/dist/vite/tweakpane-hook.d.ts +6 -3
- package/dist/vite/tweakpane-hook.d.ts.map +1 -1
- package/dist/vite/tweakpane-hook.js +27 -16
- package/dist/vite/vite-plugin.d.ts +0 -6
- package/dist/vite/vite-plugin.d.ts.map +1 -1
- package/dist/vite/vite-plugin.js +1 -92
- package/package.json +9 -9
- package/skills/egaki/SKILL.md +26 -224
- package/src/cli/bpm-detect.ts +165 -0
- package/src/cli/cli.ts +149 -0
- package/src/cli/loudness-detect.ts +129 -0
- package/src/vite/code-block.tsx +28 -6
- package/src/vite/layout-editor.tsx +1 -1
- package/src/vite/mdx-video.test.tsx +0 -94
- package/src/vite/player-page.tsx +28 -117
- package/src/vite/store-hooks.ts +11 -0
- package/src/vite/store.ts +10 -0
- package/src/vite/tweakpane-hook.tsx +71 -24
- package/src/vite/vite-plugin.ts +1 -91
package/dist/vite/player-page.js
CHANGED
|
@@ -17,26 +17,13 @@ import { AbsoluteFill, Freeze, Sequence, Series, useCurrentFrame, useDelayRender
|
|
|
17
17
|
import { renderInBrowser } from "./render-client.js";
|
|
18
18
|
import { createSpiceflowFetch } from 'spiceflow/client';
|
|
19
19
|
import { egakiStore } from "./store.js";
|
|
20
|
-
import { useGenerationStatus, useGenerationErrors } from "./store-hooks.js";
|
|
20
|
+
import { useGenerationStatus, useGenerationErrors, useSidebarOpen } from "./store-hooks.js";
|
|
21
21
|
import { egakiSDK } from "./sdk.js";
|
|
22
22
|
import { LayoutEditor } from "./layout-editor.js";
|
|
23
23
|
import { TweakpaneRoot, SIDEBAR_WIDTH } from "./tweakpane-hook.js";
|
|
24
24
|
import { Fill, LayoutAnimationLayer, LayoutGhost, LayoutTransitionProvider, MotionTimingSync, ServerSlotsContext, } from "./mdx-video.js";
|
|
25
25
|
import { SectionIndexContext } from "./media-duration-store.js";
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
|
-
// HMR scene-changed: store pending seek at module scope so it survives
|
|
28
|
-
// component remounts caused by rsc:update (spiceflow payload swap).
|
|
29
|
-
// The vite plugin sends egaki:scene-changed with { sectionIndex } when
|
|
30
|
-
// the user edits a specific section of the entry MDX.
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
let pendingSceneSeek = null;
|
|
33
|
-
/** Cancels the auto-pause listener from a previous scene-seek, so a new
|
|
34
|
-
* HMR during playback doesn't leave stale listeners that pause the wrong scene. */
|
|
35
|
-
let cancelPreviousAutoPlay = null;
|
|
36
|
-
/** Monotonic counter to invalidate stale requestAnimationFrame callbacks
|
|
37
|
-
* when rapid successive HMR edits arrive before the first rAF fires. */
|
|
38
|
-
let sceneSeekVersion = 0;
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
27
|
// Generation progress client — fetches /api/generation-progress on page
|
|
41
28
|
// load and on every rsc:update using the typed Spiceflow fetch client.
|
|
42
29
|
// The route is an async generator that yields progress events as SSE;
|
|
@@ -107,9 +94,6 @@ function connectToProgress() {
|
|
|
107
94
|
if (typeof window !== 'undefined')
|
|
108
95
|
connectToProgress();
|
|
109
96
|
if (import.meta.hot) {
|
|
110
|
-
import.meta.hot.on('egaki:scene-changed', (data) => {
|
|
111
|
-
pendingSceneSeek = data;
|
|
112
|
-
});
|
|
113
97
|
// Reconnect on rsc:update — new generations may have started from
|
|
114
98
|
// MDX edits that trigger server-side rendering of <Server> blocks.
|
|
115
99
|
import.meta.hot.on('rsc:update', () => {
|
|
@@ -291,79 +275,6 @@ export function PlayerPage({ sections, totalDuration, preamble, entryPath, hasUn
|
|
|
291
275
|
playerContainerRef,
|
|
292
276
|
});
|
|
293
277
|
}, [Component, totalDuration, sections, fps, width, height, scale]);
|
|
294
|
-
// Consume pending scene seek after the RSC refetch delivers new sections.
|
|
295
|
-
// The module-level pendingSceneSeek is set by import.meta.hot.on before
|
|
296
|
-
// React re-renders; this effect fires after the commit with fresh data.
|
|
297
|
-
// Seeks to the changed section's start frame, plays it, and pauses at
|
|
298
|
-
// the section's end so the user sees exactly the content they edited.
|
|
299
|
-
useEffect(() => {
|
|
300
|
-
if (pendingSceneSeek == null)
|
|
301
|
-
return;
|
|
302
|
-
// Ignore scene changes from a different entry MDX file.
|
|
303
|
-
if (pendingSceneSeek.file !== entryPath) {
|
|
304
|
-
pendingSceneSeek = null;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
const idx = pendingSceneSeek.sectionIndex;
|
|
308
|
-
pendingSceneSeek = null;
|
|
309
|
-
// Wait one frame for the Remotion Player to initialize after remount.
|
|
310
|
-
// Version token ensures stale rAF callbacks from rapid edits are ignored.
|
|
311
|
-
const version = ++sceneSeekVersion;
|
|
312
|
-
requestAnimationFrame(() => {
|
|
313
|
-
if (version !== sceneSeekVersion)
|
|
314
|
-
return;
|
|
315
|
-
const player = playerRef.current;
|
|
316
|
-
if (!player || idx < 0 || idx >= sections.length)
|
|
317
|
-
return;
|
|
318
|
-
// Compute section boundaries from current sections
|
|
319
|
-
let startFrame = 0;
|
|
320
|
-
for (let i = 0; i < idx; i++) {
|
|
321
|
-
startFrame += sections[i].durationInFrames;
|
|
322
|
-
}
|
|
323
|
-
const endFrame = startFrame + sections[idx].durationInFrames - 1;
|
|
324
|
-
player.seekTo(startFrame);
|
|
325
|
-
// Cancel any auto-pause from a previous HMR scene-seek so stale
|
|
326
|
-
// listeners don't pause playback in the wrong section.
|
|
327
|
-
cancelPreviousAutoPlay?.();
|
|
328
|
-
cancelPreviousAutoPlay = null;
|
|
329
|
-
// Only auto-play if currently paused (after rsc:update remount
|
|
330
|
-
// the player always starts paused at frame 0)
|
|
331
|
-
if (!player.isPlaying()) {
|
|
332
|
-
player.play();
|
|
333
|
-
// Pause at section end so only the edited section plays.
|
|
334
|
-
// If the user manually seeks (clicks timeline, arrow keys, etc.)
|
|
335
|
-
// cancel the auto-pause so we don't randomly stop playback later.
|
|
336
|
-
let cancelled = false;
|
|
337
|
-
const cleanup = () => {
|
|
338
|
-
if (cancelled)
|
|
339
|
-
return;
|
|
340
|
-
cancelled = true;
|
|
341
|
-
cancelPreviousAutoPlay = null;
|
|
342
|
-
player.removeEventListener('frameupdate', onFrame);
|
|
343
|
-
player.removeEventListener('seeked', onSeek);
|
|
344
|
-
};
|
|
345
|
-
cancelPreviousAutoPlay = cleanup;
|
|
346
|
-
const onFrame = () => {
|
|
347
|
-
if (cancelled)
|
|
348
|
-
return;
|
|
349
|
-
if (player.getCurrentFrame() >= endFrame) {
|
|
350
|
-
player.pause();
|
|
351
|
-
cleanup();
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
const onSeek = (e) => {
|
|
355
|
-
// User seeked away — cancel auto-pause. Ignore seeks within
|
|
356
|
-
// the target section range (could be our own seekTo settling).
|
|
357
|
-
const frame = e?.detail?.frame ?? player.getCurrentFrame();
|
|
358
|
-
if (frame < startFrame || frame > endFrame) {
|
|
359
|
-
cleanup();
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
player.addEventListener('frameupdate', onFrame);
|
|
363
|
-
player.addEventListener('seeked', onSeek);
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
}, [sections]);
|
|
367
278
|
// Defer Player mount to client only. Remotion's Player and all composition
|
|
368
279
|
// components (Series, AbsoluteFill, Audio, Video) use React context provided
|
|
369
280
|
// by Player at runtime. During SSR there's no Remotion context, so hooks like
|
|
@@ -373,6 +284,7 @@ export function PlayerPage({ sections, totalDuration, preamble, entryPath, hasUn
|
|
|
373
284
|
const mounted = useSyncExternalStore(subscribeNoop, getClientMounted, getServerMounted);
|
|
374
285
|
const generationStatus = useGenerationStatus();
|
|
375
286
|
const generationErrors = useGenerationErrors();
|
|
287
|
+
const sidebarOpen = useSidebarOpen();
|
|
376
288
|
const [editing, setEditing] = useState(false);
|
|
377
289
|
const [resetKey, setResetKey] = useState(0);
|
|
378
290
|
const [rendering, setRendering] = useState(false);
|
|
@@ -702,9 +614,9 @@ export function PlayerPage({ sections, totalDuration, preamble, entryPath, hasUn
|
|
|
702
614
|
margin: 0,
|
|
703
615
|
lineHeight: 1.5,
|
|
704
616
|
}, children: error.stack || error.message })] }) })) }, resetKey)) : (_jsx("div", { className: 'aspect-video bg-[#050505]' })) }) }), _jsx(TweakpaneRoot, { playerRef: playerRef, fps: fps, sections: sections, entryPath: entryPath }), _jsxs("div", { className: 'fixed bottom-6 flex items-center gap-1.5 rounded-full bg-[#1c1c1c] border border-white/10 px-2 py-1.5 shadow-2xl', style: {
|
|
705
|
-
left: `calc((100% - ${SIDEBAR_WIDTH}px - 0.75rem) / 2)
|
|
617
|
+
left: sidebarOpen ? `calc((100% - ${SIDEBAR_WIDTH}px - 0.75rem) / 2)` : '50%',
|
|
706
618
|
transform: 'translateX(-50%)',
|
|
707
|
-
}, children: [availableEntries.length > 1 && (_jsxs(_Fragment, { children: [_jsx("select", { value: currentRoute, onChange: (e) => {
|
|
619
|
+
}, children: [_jsx("button", { onClick: goToPrevScene, disabled: !hasPrevScene, className: 'flex items-center justify-center rounded-full w-7 h-7 text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-default disabled:hover:bg-transparent disabled:hover:text-zinc-400', title: 'Previous scene (\u2193)', children: _jsx(ChevronDownIcon, {}) }), _jsx(ToolbarSeparator, {}), availableEntries.length > 1 && (_jsxs(_Fragment, { children: [_jsx("select", { value: currentRoute, onChange: (e) => {
|
|
708
620
|
const route = e.target.value;
|
|
709
621
|
const base = import.meta.env.BASE_URL ?? '/';
|
|
710
622
|
window.location.href = `${base}${route}`;
|
|
@@ -714,7 +626,7 @@ export function PlayerPage({ sections, totalDuration, preamble, entryPath, hasUn
|
|
|
714
626
|
const next = PLAYBACK_RATES[(idx + 1) % PLAYBACK_RATES.length];
|
|
715
627
|
return next;
|
|
716
628
|
});
|
|
717
|
-
}, className: 'flex items-center rounded-full px-2.5 py-1.5 text-[13px] font-medium tabular-nums text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer min-w-[3rem] justify-center', title: 'Playback speed (click to cycle)', children: playbackRate === 1 ? '1x' : `${playbackRate}x` }), _jsx(ToolbarSeparator, {}),
|
|
629
|
+
}, className: 'flex items-center rounded-full px-2.5 py-1.5 text-[13px] font-medium tabular-nums text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer min-w-[3rem] justify-center', title: 'Playback speed (click to cycle)', children: playbackRate === 1 ? '1x' : `${playbackRate}x` }), _jsx(ToolbarSeparator, {}), _jsx(LayoutEditor, { playerContainerRef: playerContainerRef, playerRef: playerRef, editing: editing, onEditingChange: setEditing, onReset: () => setResetKey((k) => k + 1), sections: sections, fps: fps, entryPath: entryPath }), generationStatus && (_jsxs(_Fragment, { children: [_jsx(ToolbarSeparator, {}), _jsxs("div", { className: 'flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] font-medium text-amber-400/90', children: [_jsx(SpinnerIcon, {}), _jsx("span", { children: formatGenerationStatus(generationStatus) })] })] })), generationErrors.length > 0 && (_jsxs(_Fragment, { children: [_jsx(ToolbarSeparator, {}), _jsxs("div", { className: 'flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] font-medium text-red-400/90', children: [_jsx("span", { style: { fontSize: 14, lineHeight: 1 }, children: "\u2715" }), _jsx("span", { children: generationErrors.length === 1
|
|
718
630
|
? `${generationErrors[0].namespace} failed: ${generationErrors[0].error.slice(0, 60)}`
|
|
719
|
-
: `${generationErrors.length} generations failed` })] })] }))] })] }));
|
|
631
|
+
: `${generationErrors.length} generations failed` })] })] })), _jsx(ToolbarSeparator, {}), _jsx("button", { onClick: goToNextScene, disabled: !hasNextScene, className: 'flex items-center justify-center rounded-full w-7 h-7 text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer disabled:opacity-30 disabled:cursor-default disabled:hover:bg-transparent disabled:hover:text-zinc-400', title: 'Next scene (\u2191)', children: _jsx(ChevronUpIcon, {}) })] })] }));
|
|
720
632
|
}
|
|
@@ -6,6 +6,8 @@ export declare function useMediaDurations(): Record<string, number>;
|
|
|
6
6
|
export declare function useGenerationStatus(): GenerationStatus | null;
|
|
7
7
|
/** Hook: recent generation errors (auto-cleared after 8s). */
|
|
8
8
|
export declare function useGenerationErrors(): GenerationError[];
|
|
9
|
+
/** Hook: whether the right tweakpane sidebar is open. */
|
|
10
|
+
export declare function useSidebarOpen(): boolean;
|
|
9
11
|
/** Hook: user modules from virtual:egaki-modules. */
|
|
10
12
|
export declare function useModules(): EagerModules;
|
|
11
13
|
export type { GenerationStatus, GenerationError };
|
|
@@ -1 +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;
|
|
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;AA6EvD,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,yDAAyD;AACzD,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,qDAAqD;AACrD,wBAAgB,UAAU,IAAI,YAAY,CAEzC;AAED,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAA"}
|
package/dist/vite/store-hooks.js
CHANGED
|
@@ -69,6 +69,10 @@ const subscribeGenerationErrors = (callback) => egakiStore.subscribe((state) =>
|
|
|
69
69
|
const getGenerationErrors = () => egakiStore.getState().serverGenerationErrors;
|
|
70
70
|
const EMPTY_ERRORS = [];
|
|
71
71
|
const getGenerationErrorsServer = () => EMPTY_ERRORS;
|
|
72
|
+
// Sidebar open state
|
|
73
|
+
const subscribeSidebarOpen = (callback) => egakiStore.subscribe((state) => state.sidebarOpen, callback);
|
|
74
|
+
const getSidebarOpen = () => egakiStore.getState().sidebarOpen;
|
|
75
|
+
const getSidebarOpenServer = () => false;
|
|
72
76
|
// Modules
|
|
73
77
|
const subscribeModules = (callback) => egakiStore.subscribe((state) => state.modules, callback);
|
|
74
78
|
const getModules = () => egakiStore.getState().modules;
|
|
@@ -87,6 +91,10 @@ export function useGenerationStatus() {
|
|
|
87
91
|
export function useGenerationErrors() {
|
|
88
92
|
return useSyncExternalStore(subscribeGenerationErrors, getGenerationErrors, getGenerationErrorsServer);
|
|
89
93
|
}
|
|
94
|
+
/** Hook: whether the right tweakpane sidebar is open. */
|
|
95
|
+
export function useSidebarOpen() {
|
|
96
|
+
return useSyncExternalStore(subscribeSidebarOpen, getSidebarOpen, getSidebarOpenServer);
|
|
97
|
+
}
|
|
90
98
|
/** Hook: user modules from virtual:egaki-modules. */
|
|
91
99
|
export function useModules() {
|
|
92
100
|
return useSyncExternalStore(subscribeModules, getModules, getModules);
|
package/dist/vite/store.d.ts
CHANGED
|
@@ -54,6 +54,9 @@ export interface EgakiClientState {
|
|
|
54
54
|
/** Recent generation errors from the server progress stream.
|
|
55
55
|
* Errors auto-clear after ERROR_DISPLAY_DURATION_MS. */
|
|
56
56
|
serverGenerationErrors: GenerationError[];
|
|
57
|
+
/** Whether the right tweakpane sidebar is open. Closed by default so the
|
|
58
|
+
* video takes the full width. */
|
|
59
|
+
sidebarOpen: boolean;
|
|
57
60
|
}
|
|
58
61
|
export declare const egakiStore: Omit<import("zustand/vanilla").StoreApi<EgakiClientState>, "subscribe"> & {
|
|
59
62
|
subscribe: {
|
|
@@ -64,6 +67,8 @@ export declare const egakiStore: Omit<import("zustand/vanilla").StoreApi<EgakiCl
|
|
|
64
67
|
} | undefined): () => void;
|
|
65
68
|
};
|
|
66
69
|
};
|
|
70
|
+
/** Open or close the right tweakpane sidebar. */
|
|
71
|
+
export declare function setSidebarOpen(open: boolean): void;
|
|
67
72
|
/**
|
|
68
73
|
* Derive per-section max media durations.
|
|
69
74
|
* Returns Record<string, number> (section-index-as-string → seconds).
|
package/dist/vite/store.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;IAEzC;sCACkC;IAClC,WAAW,EAAE,OAAO,CAAA;CACrB;AAMD,eAAO,MAAM,UAAU;;;;;;;;CAStB,CAAA;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,QAE3C;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"}
|
package/dist/vite/store.js
CHANGED
|
@@ -23,7 +23,12 @@ export const egakiStore = createStore()(subscribeWithSelector(() => ({
|
|
|
23
23
|
serverGenerationStatus: null,
|
|
24
24
|
serverGenerationEntries: [],
|
|
25
25
|
serverGenerationErrors: [],
|
|
26
|
+
sidebarOpen: false,
|
|
26
27
|
})));
|
|
28
|
+
/** Open or close the right tweakpane sidebar. */
|
|
29
|
+
export function setSidebarOpen(open) {
|
|
30
|
+
egakiStore.setState({ sidebarOpen: open });
|
|
31
|
+
}
|
|
27
32
|
// ---------------------------------------------------------------------------
|
|
28
33
|
// Selectors — derive values from state
|
|
29
34
|
// ---------------------------------------------------------------------------
|
|
@@ -77,9 +77,12 @@ export declare function useTweakpane<T extends ParamSchema>(label: string, schem
|
|
|
77
77
|
/** Sidebar width constant shared with player-page layout. */
|
|
78
78
|
export declare const SIDEBAR_WIDTH = 280;
|
|
79
79
|
/**
|
|
80
|
-
* Right sidebar that hosts the tweakpane Pane.
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* Right sidebar that hosts the tweakpane Pane. Collapsed by default so the
|
|
81
|
+
* video takes the full width; a fixed top-right button opens it. The sidebar
|
|
82
|
+
* container stays mounted while collapsed (hidden via display: none) because
|
|
83
|
+
* useTweakpane registers controls into it — unmounting would lose all
|
|
84
|
+
* registrations until components remount. Also provides player metadata
|
|
85
|
+
* (frame, fps, sections) to the copy prompt button.
|
|
83
86
|
*/
|
|
84
87
|
export declare function TweakpaneRoot({ playerRef, fps, sections, entryPath, }: {
|
|
85
88
|
playerRef: React.RefObject<PlayerRef | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tweakpane-hook.d.ts","sourceRoot":"","sources":["../../src/vite/tweakpane-hook.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"tweakpane-hook.d.ts","sourceRoot":"","sources":["../../src/vite/tweakpane-hook.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* Tweakpane integration for the egaki video player.
|
|
5
5
|
*
|
|
@@ -21,6 +21,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
21
21
|
*/
|
|
22
22
|
import { useCallback, useEffect, useRef, useState, useSyncExternalStore, createContext, useContext } from 'react';
|
|
23
23
|
import { LayoutContainerContext, useIsPremounting } from "./mdx-video.js";
|
|
24
|
+
import { setSidebarOpen } from "./store.js";
|
|
25
|
+
import { useSidebarOpen } from "./store-hooks.js";
|
|
24
26
|
export const TweakpaneContext = createContext(null);
|
|
25
27
|
// ---------------------------------------------------------------------------
|
|
26
28
|
// Global disable switch
|
|
@@ -410,10 +412,17 @@ export function useTweakpane(label, schema) {
|
|
|
410
412
|
// ---------------------------------------------------------------------------
|
|
411
413
|
/** Sidebar width constant shared with player-page layout. */
|
|
412
414
|
export const SIDEBAR_WIDTH = 280;
|
|
415
|
+
/** Lucide-style panel-right icon used for both opening and closing the sidebar. */
|
|
416
|
+
function PanelRightIcon() {
|
|
417
|
+
return (_jsxs("svg", { width: '16', height: '16', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '1.5', strokeLinecap: 'round', strokeLinejoin: 'round', children: [_jsx("rect", { x: '3', y: '3', width: '18', height: '18', rx: '2' }), _jsx("line", { x1: '15', y1: '3', x2: '15', y2: '21' })] }));
|
|
418
|
+
}
|
|
413
419
|
/**
|
|
414
|
-
* Right sidebar that hosts the tweakpane Pane.
|
|
415
|
-
*
|
|
416
|
-
*
|
|
420
|
+
* Right sidebar that hosts the tweakpane Pane. Collapsed by default so the
|
|
421
|
+
* video takes the full width; a fixed top-right button opens it. The sidebar
|
|
422
|
+
* container stays mounted while collapsed (hidden via display: none) because
|
|
423
|
+
* useTweakpane registers controls into it — unmounting would lose all
|
|
424
|
+
* registrations until components remount. Also provides player metadata
|
|
425
|
+
* (frame, fps, sections) to the copy prompt button.
|
|
417
426
|
*/
|
|
418
427
|
export function TweakpaneRoot({ playerRef, fps, sections, entryPath, }) {
|
|
419
428
|
// Ref callback sets the container synchronously when the DOM node mounts,
|
|
@@ -435,16 +444,18 @@ export function TweakpaneRoot({ playerRef, fps, sections, entryPath, }) {
|
|
|
435
444
|
contextRef = null;
|
|
436
445
|
};
|
|
437
446
|
}, [playerRef, fps, sections, entryPath]);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
447
|
+
const sidebarOpen = useSidebarOpen();
|
|
448
|
+
return (_jsxs(_Fragment, { children: [!sidebarOpen && (_jsx("button", { onClick: () => setSidebarOpen(true), title: 'Open controls sidebar', className: 'fixed top-4 right-4 z-10 flex items-center justify-center rounded-lg w-8 h-8 text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer', children: _jsx(PanelRightIcon, {}) })), _jsxs("div", { className: 'egaki-sidebar', style: {
|
|
449
|
+
width: SIDEBAR_WIDTH,
|
|
450
|
+
minWidth: SIDEBAR_WIDTH,
|
|
451
|
+
height: '100vh',
|
|
452
|
+
position: 'sticky',
|
|
453
|
+
top: 0,
|
|
454
|
+
overflowY: 'auto',
|
|
455
|
+
overflowX: 'hidden',
|
|
456
|
+
background: '#111',
|
|
457
|
+
borderLeft: '1px solid rgba(255,255,255,0.08)',
|
|
458
|
+
flexShrink: 0,
|
|
459
|
+
display: sidebarOpen ? undefined : 'none',
|
|
460
|
+
}, children: [_jsx("div", { className: 'flex items-center justify-end px-2 pt-2', children: _jsx("button", { onClick: () => setSidebarOpen(false), title: 'Close controls sidebar', className: 'flex items-center justify-center rounded-lg w-8 h-8 text-zinc-400 hover:text-zinc-200 hover:bg-white/5 transition-colors cursor-pointer', children: _jsx(PanelRightIcon, {}) }) }), _jsx(CopyChangesButton, {}), _jsx("div", { ref: containerRefCallback, id: 'egaki-tweakpane', style: { width: '100%' } })] })] }));
|
|
450
461
|
}
|
|
@@ -18,11 +18,5 @@ export interface VideoPluginOptions {
|
|
|
18
18
|
* All other .mdx files in the project become additional routes. */
|
|
19
19
|
entry?: string;
|
|
20
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
21
|
export declare function video(options?: VideoPluginOptions): PluginOption[];
|
|
28
22
|
//# sourceMappingURL=vite-plugin.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAiDD,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,YAAY,EAAE,CAoYlE"}
|
package/dist/vite/vite-plugin.js
CHANGED
|
@@ -46,60 +46,6 @@ function resolveSourceToFile(root, source) {
|
|
|
46
46
|
}
|
|
47
47
|
return undefined;
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Extract raw source text ranges per section directly from the mdast.
|
|
51
|
-
* Works with heading nodes' positions to capture the FULL raw heading
|
|
52
|
-
* text including props like `duration=3s` and `transition=20` that
|
|
53
|
-
* `splitIntoSections` strips during parsing. Each section spans from
|
|
54
|
-
* its heading's start offset to the next heading's start (or EOF).
|
|
55
|
-
*/
|
|
56
|
-
function sectionRawTexts(source, mdast) {
|
|
57
|
-
// Collect the start offsets of each heading node
|
|
58
|
-
const headingStarts = [];
|
|
59
|
-
for (const node of mdast.children) {
|
|
60
|
-
if (node.type === 'heading' && node.position?.start?.offset != null) {
|
|
61
|
-
headingStarts.push(node.position.start.offset);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// Slice source between consecutive headings, trimming trailing whitespace
|
|
65
|
-
// so boundary changes (adding/removing a following section) don't cause
|
|
66
|
-
// false positives on the preceding section.
|
|
67
|
-
return headingStarts.map((start, i) => {
|
|
68
|
-
const end = i + 1 < headingStarts.length ? headingStarts[i + 1] : source.length;
|
|
69
|
-
return source.slice(start, end).trimEnd();
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Compare old and new MDX sources section-by-section. Returns the index
|
|
74
|
-
* of the first added or edited section, or null if nothing changed (or
|
|
75
|
-
* only deletions occurred, which we skip per design).
|
|
76
|
-
*/
|
|
77
|
-
export function findChangedSectionIndex(oldSource, newSource) {
|
|
78
|
-
try {
|
|
79
|
-
const oldTexts = sectionRawTexts(oldSource, mdxParse(oldSource));
|
|
80
|
-
const newTexts = sectionRawTexts(newSource, mdxParse(newSource));
|
|
81
|
-
const maxLen = Math.max(oldTexts.length, newTexts.length);
|
|
82
|
-
for (let i = 0; i < maxLen; i++) {
|
|
83
|
-
if (i >= oldTexts.length)
|
|
84
|
-
return i; // new section added
|
|
85
|
-
if (i >= newTexts.length)
|
|
86
|
-
return null; // section deleted, skip
|
|
87
|
-
if (oldTexts[i] !== newTexts[i]) {
|
|
88
|
-
// Distinguish edit from deletion: if the next old section matches
|
|
89
|
-
// the current new section, a section was deleted (shifted down).
|
|
90
|
-
// Look ahead to detect deletions at any position, not just the tail.
|
|
91
|
-
if (newTexts.length < oldTexts.length && oldTexts[i + 1] === newTexts[i]) {
|
|
92
|
-
return null; // deletion, skip
|
|
93
|
-
}
|
|
94
|
-
return i; // content changed
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return null; // no scene-level change (frontmatter/preamble only)
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
return null; // parse error, don't seek
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
49
|
/** Discover .mdx files in the project root directory (non-recursive).
|
|
104
50
|
* Only root-level files become entries to avoid import resolution issues
|
|
105
51
|
* with nested paths. Returns a map of routePath → absolutePath. */
|
|
@@ -146,8 +92,6 @@ export function video(options) {
|
|
|
146
92
|
let defaultRoute = '';
|
|
147
93
|
/** Whether the user's project has `motion` (framer-motion) installed. */
|
|
148
94
|
let hasMotion = false;
|
|
149
|
-
/** Cached previous MDX sources for section-level diff detection, keyed by abs path. */
|
|
150
|
-
const previousMdxSources = new Map();
|
|
151
95
|
/** Set of absolute paths of all entry MDX files for quick lookup. */
|
|
152
96
|
let entryPathSet = new Set();
|
|
153
97
|
/** Is this file referenced inside a <Server> block of any entry MDX?
|
|
@@ -193,13 +137,6 @@ export function video(options) {
|
|
|
193
137
|
}
|
|
194
138
|
defaultRoute = resolveDefaultRoute(mdxEntries, options?.entry, root);
|
|
195
139
|
entryPathSet = new Set(mdxEntries.values());
|
|
196
|
-
// Seed previous sources for section-level diff detection.
|
|
197
|
-
for (const [, absPath] of mdxEntries) {
|
|
198
|
-
try {
|
|
199
|
-
previousMdxSources.set(absPath, fs.readFileSync(absPath, 'utf-8'));
|
|
200
|
-
}
|
|
201
|
-
catch { /* ignore read errors */ }
|
|
202
|
-
}
|
|
203
140
|
// Auto-generate egaki-env.d.ts so MDX LSP knows about built-in
|
|
204
141
|
// components via the global MDXProvidedComponents type. Same
|
|
205
142
|
// pattern Vite uses for vite-env.d.ts.
|
|
@@ -407,21 +344,7 @@ export function video(options) {
|
|
|
407
344
|
mdxEntries.set(relPath.replace(/\.mdx$/, ''), absEntry);
|
|
408
345
|
}
|
|
409
346
|
defaultRoute = resolveDefaultRoute(mdxEntries, options?.entry, root);
|
|
410
|
-
|
|
411
|
-
// Sync previousMdxSources: seed new entries, prune removed ones.
|
|
412
|
-
for (const file of previousMdxSources.keys()) {
|
|
413
|
-
if (!nextEntryPathSet.has(file))
|
|
414
|
-
previousMdxSources.delete(file);
|
|
415
|
-
}
|
|
416
|
-
for (const file of nextEntryPathSet) {
|
|
417
|
-
if (!previousMdxSources.has(file)) {
|
|
418
|
-
try {
|
|
419
|
-
previousMdxSources.set(file, fs.readFileSync(file, 'utf-8'));
|
|
420
|
-
}
|
|
421
|
-
catch { /* file may be mid-write */ }
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
entryPathSet = nextEntryPathSet;
|
|
347
|
+
entryPathSet = new Set(mdxEntries.values());
|
|
425
348
|
invalidateVirtual([RESOLVED_APP, RESOLVED_MDX, RESOLVED_MODULES]);
|
|
426
349
|
if (this.environment.name === 'client') {
|
|
427
350
|
ctx.server.environments.client?.hot.send({ type: 'full-reload' });
|
|
@@ -431,25 +354,11 @@ export function video(options) {
|
|
|
431
354
|
if (isEntryMdx) {
|
|
432
355
|
invalidateVirtual([RESOLVED_APP, RESOLVED_MDX]);
|
|
433
356
|
if (this.environment.name === 'client') {
|
|
434
|
-
// Section-level diff detection for the changed entry.
|
|
435
|
-
const newMdxSource = fs.readFileSync(normalizedFile, 'utf-8');
|
|
436
|
-
const prevSource = previousMdxSources.get(normalizedFile);
|
|
437
|
-
const changedSection = prevSource != null
|
|
438
|
-
? findChangedSectionIndex(prevSource, newMdxSource)
|
|
439
|
-
: null;
|
|
440
|
-
previousMdxSources.set(normalizedFile, newMdxSource);
|
|
441
357
|
ctx.server.environments.client?.hot.send({
|
|
442
358
|
type: 'custom',
|
|
443
359
|
event: 'rsc:update',
|
|
444
360
|
data: { file: ctx.file },
|
|
445
361
|
});
|
|
446
|
-
if (changedSection != null) {
|
|
447
|
-
ctx.server.environments.client?.hot.send({
|
|
448
|
-
type: 'custom',
|
|
449
|
-
event: 'egaki:scene-changed',
|
|
450
|
-
data: { sectionIndex: changedSection, file: normalizedFile },
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
362
|
}
|
|
454
363
|
return [];
|
|
455
364
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "egaki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI image, video, and speech generation CLI with MDX-to-video framework",
|
|
6
6
|
"main": "./dist/cli/main.js",
|
|
@@ -111,17 +111,18 @@
|
|
|
111
111
|
"tweakpane": "^4.0.5",
|
|
112
112
|
"yaml": "^2.9.0",
|
|
113
113
|
"zod": "^4.3.6",
|
|
114
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
115
|
+
"spiceflow": "1.26.0-rsc.7",
|
|
114
116
|
"zustand": "^5.0.14"
|
|
115
117
|
},
|
|
116
118
|
"peerDependencies": {
|
|
117
|
-
"
|
|
119
|
+
"vite": ">=6",
|
|
118
120
|
"motion": ">=12",
|
|
119
121
|
"motion-dom": ">=12",
|
|
120
|
-
"motion-utils": ">=12"
|
|
121
|
-
"spiceflow": ">=1.26.0-rsc.5"
|
|
122
|
+
"motion-utils": ">=12"
|
|
122
123
|
},
|
|
123
124
|
"peerDependenciesMeta": {
|
|
124
|
-
"
|
|
125
|
+
"vite": {
|
|
125
126
|
"optional": true
|
|
126
127
|
},
|
|
127
128
|
"motion": {
|
|
@@ -132,15 +133,14 @@
|
|
|
132
133
|
},
|
|
133
134
|
"motion-utils": {
|
|
134
135
|
"optional": true
|
|
135
|
-
},
|
|
136
|
-
"spiceflow": {
|
|
137
|
-
"optional": true
|
|
138
136
|
}
|
|
139
137
|
},
|
|
138
|
+
"optionalDependencies": {
|
|
139
|
+
"node-web-audio-api": ">=1"
|
|
140
|
+
},
|
|
140
141
|
"devDependencies": {
|
|
141
142
|
"@types/js-yaml": "^4.0.9",
|
|
142
143
|
"@types/react": "^19.2.17",
|
|
143
|
-
"@vitejs/plugin-react": "^6.0.2",
|
|
144
144
|
"vite": "^8.0.10",
|
|
145
145
|
"vitest": "^4.1.8"
|
|
146
146
|
},
|