egaki 0.8.0 → 0.9.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 +24 -0
- package/dist/vite/angled-screen-shader.d.ts +45 -0
- package/dist/vite/angled-screen-shader.d.ts.map +1 -0
- package/dist/vite/angled-screen-shader.js +573 -0
- package/dist/vite/angled-screen.d.ts +12 -3
- package/dist/vite/angled-screen.d.ts.map +1 -1
- package/dist/vite/angled-screen.js +12 -3
- package/dist/vite/mdx-video.d.ts +4 -2
- package/dist/vite/mdx-video.d.ts.map +1 -1
- package/dist/vite/mdx-video.js +4 -2
- package/dist/vite/mdx-video.test.js +1 -0
- package/dist/vite/render-client.d.ts +27 -0
- package/dist/vite/render-client.d.ts.map +1 -1
- package/dist/vite/render-client.js +47 -51
- package/dist/vite/sdk.d.ts.map +1 -1
- package/dist/vite/sdk.js +4 -3
- package/dist/vite/shader-renderer.d.ts +4 -0
- package/dist/vite/shader-renderer.d.ts.map +1 -1
- package/dist/vite/shader-renderer.js +3 -3
- package/dist/vite/tweakpane-hook.d.ts +6 -0
- package/dist/vite/tweakpane-hook.d.ts.map +1 -1
- package/dist/vite/tweakpane-hook.js +101 -4
- package/package.json +6 -6
- package/src/vite/angled-screen-shader.tsx +706 -0
- package/src/vite/angled-screen.tsx +13 -4
- package/src/vite/mdx-video.test.tsx +1 -0
- package/src/vite/mdx-video.tsx +4 -1
- package/src/vite/render-client.ts +52 -51
- package/src/vite/sdk.ts +4 -3
- package/src/vite/shader-renderer.tsx +3 -3
- package/src/vite/tweakpane-hook.tsx +102 -4
|
@@ -18,6 +18,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
* Tweakpane is pure DOM (no React dependency), so there are no dual-React
|
|
19
19
|
* or SSR issues. During SSR (typeof window === 'undefined') the hook
|
|
20
20
|
* returns the raw default values and skips all DOM work.
|
|
21
|
+
*
|
|
22
|
+
* Live props vs user overrides: each param follows the LIVE schema value
|
|
23
|
+
* passed on every render (so animated props like translateX={interpolate(...)}
|
|
24
|
+
* work) until the user touches that control in the pane. Once touched, the
|
|
25
|
+
* user's value wins for that key until the component remounts. Untouched
|
|
26
|
+
* controls are refreshed in the pane every render so animated values are
|
|
27
|
+
* visible while playing. Programmatic refreshes are guarded so tweakpane's
|
|
28
|
+
* change events (which fire on refresh() too) don't mark keys as touched.
|
|
21
29
|
*/
|
|
22
30
|
import { useCallback, useEffect, useRef, useState, useSyncExternalStore, createContext, useContext } from 'react';
|
|
23
31
|
import { LayoutContainerContext, useIsPremounting } from "./mdx-video.js";
|
|
@@ -59,6 +67,22 @@ function cloneValue(value) {
|
|
|
59
67
|
return { ...value };
|
|
60
68
|
return value;
|
|
61
69
|
}
|
|
70
|
+
/** Deep-equal for param values: primitives, small arrays (bezier tuples),
|
|
71
|
+
* and flat objects (range {min, max}). */
|
|
72
|
+
function valuesEqual(a, b) {
|
|
73
|
+
if (a === b)
|
|
74
|
+
return true;
|
|
75
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
76
|
+
return a.length === b.length && a.every((x, i) => x === b[i]);
|
|
77
|
+
}
|
|
78
|
+
if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
|
|
79
|
+
const ra = a;
|
|
80
|
+
const rb = b;
|
|
81
|
+
const ka = Object.keys(ra);
|
|
82
|
+
return ka.length === Object.keys(rb).length && ka.every((k) => ra[k] === rb[k]);
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
62
86
|
function resolveDefault(v) {
|
|
63
87
|
if (isCubicBezierParam(v))
|
|
64
88
|
return [...v.value];
|
|
@@ -178,7 +202,10 @@ function serializeTweakpanePrompt(ctx) {
|
|
|
178
202
|
const changed = [];
|
|
179
203
|
for (const [key, current] of Object.entries(entry.params)) {
|
|
180
204
|
const def = entry.defaults[key];
|
|
181
|
-
|
|
205
|
+
// Only user-touched keys count as changed. Untouched keys mirror live
|
|
206
|
+
// (possibly animated) props, so comparing them to mount-time defaults
|
|
207
|
+
// would produce false positives on every playing frame.
|
|
208
|
+
if (entry.touched.has(key) && !valuesEqual(current, def)) {
|
|
182
209
|
changed.push(` ${key}: ${formatValue(current)}`);
|
|
183
210
|
}
|
|
184
211
|
}
|
|
@@ -261,6 +288,12 @@ function CopyChangesButton() {
|
|
|
261
288
|
* Returns a reactive object with current values. Updating a control in the
|
|
262
289
|
* pane immediately triggers a React re-render with fresh values.
|
|
263
290
|
*
|
|
291
|
+
* Values follow live props: each key returns the schema value from the
|
|
292
|
+
* CURRENT render until the user touches that control in the pane, so
|
|
293
|
+
* animated props (e.g. `translateX={interpolate(frame, ...)}`) flow through
|
|
294
|
+
* every frame. Once touched, the user's pane value wins for that key until
|
|
295
|
+
* the component remounts.
|
|
296
|
+
*
|
|
264
297
|
* On unmount the folder is disposed and removed from the pane.
|
|
265
298
|
*/
|
|
266
299
|
export function useTweakpane(label, schema) {
|
|
@@ -283,6 +316,21 @@ export function useTweakpane(label, schema) {
|
|
|
283
316
|
// Track the schema for updates
|
|
284
317
|
const schemaRef = useRef(schema);
|
|
285
318
|
schemaRef.current = schema;
|
|
319
|
+
// Keys the user changed via the pane UI. Untouched keys follow the live
|
|
320
|
+
// schema value on every render (so animated props work); touched keys keep
|
|
321
|
+
// the user's pane value until remount.
|
|
322
|
+
const touchedRef = useRef(new Set());
|
|
323
|
+
// Latest user override values, mirrored from state so the mount effect can
|
|
324
|
+
// seed rebuilt folders without re-running on every pane change.
|
|
325
|
+
const valuesRef = useRef(values);
|
|
326
|
+
valuesRef.current = values;
|
|
327
|
+
// Live handles to the mounted folder + bound params, used by the per-render
|
|
328
|
+
// sync effect below to push animated prop values into the pane.
|
|
329
|
+
const liveRef = useRef(null);
|
|
330
|
+
// True while we call folder.refresh() programmatically. tweakpane emits
|
|
331
|
+
// change events on refresh() too, so handlers must ignore those or every
|
|
332
|
+
// animated frame would mark keys as user-touched.
|
|
333
|
+
const refreshingRef = useRef(false);
|
|
286
334
|
// Stable ID for this hook instance
|
|
287
335
|
const idRef = useRef(`${label}-${Math.random().toString(36).slice(2, 8)}`);
|
|
288
336
|
useEffect(() => {
|
|
@@ -299,7 +347,10 @@ export function useTweakpane(label, schema) {
|
|
|
299
347
|
const def = resolveDefault(param);
|
|
300
348
|
// Clone object/array values so tweakpane's in-place mutations on params
|
|
301
349
|
// don't corrupt the defaults (used by "Copy changes" diffing).
|
|
302
|
-
|
|
350
|
+
// Touched keys keep the user's override when the folder is rebuilt
|
|
351
|
+
// (e.g. premount → active transition re-runs this effect).
|
|
352
|
+
const touched = touchedRef.current.has(key);
|
|
353
|
+
params[key] = cloneValue(touched ? valuesRef.current[key] : def);
|
|
303
354
|
defaults[key] = cloneValue(def);
|
|
304
355
|
}
|
|
305
356
|
let folder = null;
|
|
@@ -320,6 +371,8 @@ export function useTweakpane(label, schema) {
|
|
|
320
371
|
picker: 'inline',
|
|
321
372
|
});
|
|
322
373
|
blade.on('change', (ev) => {
|
|
374
|
+
if (refreshingRef.current)
|
|
375
|
+
return;
|
|
323
376
|
const val = ev.value;
|
|
324
377
|
const tuple = [
|
|
325
378
|
Math.round(val.x1 * 1000) / 1000,
|
|
@@ -328,6 +381,7 @@ export function useTweakpane(label, schema) {
|
|
|
328
381
|
Math.round(val.y2 * 1000) / 1000,
|
|
329
382
|
];
|
|
330
383
|
params[key] = tuple;
|
|
384
|
+
touchedRef.current.add(key);
|
|
331
385
|
setValues((prev) => ({ ...prev, [key]: tuple }));
|
|
332
386
|
});
|
|
333
387
|
continue;
|
|
@@ -339,7 +393,10 @@ export function useTweakpane(label, schema) {
|
|
|
339
393
|
max: param.max,
|
|
340
394
|
step: param.step,
|
|
341
395
|
}).on('change', (ev) => {
|
|
396
|
+
if (refreshingRef.current)
|
|
397
|
+
return;
|
|
342
398
|
const val = ev.value;
|
|
399
|
+
touchedRef.current.add(key);
|
|
343
400
|
setValues((prev) => ({ ...prev, [key]: { min: val.min, max: val.max } }));
|
|
344
401
|
});
|
|
345
402
|
continue;
|
|
@@ -384,15 +441,20 @@ export function useTweakpane(label, schema) {
|
|
|
384
441
|
}
|
|
385
442
|
}
|
|
386
443
|
folder.addBinding(params, key, opts).on('change', (ev) => {
|
|
444
|
+
if (refreshingRef.current)
|
|
445
|
+
return;
|
|
446
|
+
touchedRef.current.add(key);
|
|
387
447
|
setValues((prev) => ({ ...prev, [key]: ev.value }));
|
|
388
448
|
});
|
|
389
449
|
}
|
|
450
|
+
liveRef.current = { folder, params };
|
|
390
451
|
// Register for copy prompt
|
|
391
|
-
activeFolders.set(id, { label, params, defaults });
|
|
452
|
+
activeFolders.set(id, { label, params, defaults, touched: touchedRef.current });
|
|
392
453
|
notifyFolderListeners();
|
|
393
454
|
})();
|
|
394
455
|
return () => {
|
|
395
456
|
disposed = true;
|
|
457
|
+
liveRef.current = null;
|
|
396
458
|
if (folder) {
|
|
397
459
|
folder.dispose();
|
|
398
460
|
folder = null;
|
|
@@ -405,7 +467,42 @@ export function useTweakpane(label, schema) {
|
|
|
405
467
|
}
|
|
406
468
|
};
|
|
407
469
|
}, [label, skip]);
|
|
408
|
-
|
|
470
|
+
// Push live (possibly animated) prop values into the pane for untouched
|
|
471
|
+
// keys. Runs after every render — during playback the component re-renders
|
|
472
|
+
// each frame, so animated values are visible moving in the pane UI.
|
|
473
|
+
// folder.refresh() emits change events; refreshingRef makes handlers
|
|
474
|
+
// ignore them so refreshes never mark keys as touched.
|
|
475
|
+
useEffect(() => {
|
|
476
|
+
const live = liveRef.current;
|
|
477
|
+
if (!live)
|
|
478
|
+
return;
|
|
479
|
+
let dirty = false;
|
|
480
|
+
for (const [key, param] of Object.entries(schemaRef.current)) {
|
|
481
|
+
if (touchedRef.current.has(key))
|
|
482
|
+
continue;
|
|
483
|
+
// Bezier blades are not bindings — refresh() won't read params for
|
|
484
|
+
// them. The returned value still follows live props below.
|
|
485
|
+
if (isCubicBezierParam(param))
|
|
486
|
+
continue;
|
|
487
|
+
const liveVal = resolveDefault(param);
|
|
488
|
+
if (!valuesEqual(live.params[key], liveVal)) {
|
|
489
|
+
live.params[key] = cloneValue(liveVal);
|
|
490
|
+
dirty = true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (dirty) {
|
|
494
|
+
refreshingRef.current = true;
|
|
495
|
+
live.folder.refresh();
|
|
496
|
+
refreshingRef.current = false;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
// Merge: user-touched keys return the pane value, untouched keys follow
|
|
500
|
+
// the live schema value so animated props work without any extra wiring.
|
|
501
|
+
const merged = {};
|
|
502
|
+
for (const [key, param] of Object.entries(schema)) {
|
|
503
|
+
merged[key] = touchedRef.current.has(key) ? values[key] : resolveDefault(param);
|
|
504
|
+
}
|
|
505
|
+
return merged;
|
|
409
506
|
}
|
|
410
507
|
// ---------------------------------------------------------------------------
|
|
411
508
|
// TweakpaneRoot — sidebar container for the tweakpane pane
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "egaki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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",
|
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
"@ai-sdk/xai": "^3.0.92",
|
|
89
89
|
"@clack/prompts": "^1.0.1",
|
|
90
90
|
"@fal-ai/client": "^1.10.1",
|
|
91
|
-
"@remotion/media": "4.0.
|
|
92
|
-
"@remotion/player": "4.0.
|
|
93
|
-
"@remotion/web-renderer": "4.0.
|
|
91
|
+
"@remotion/media": "4.0.494",
|
|
92
|
+
"@remotion/player": "4.0.494",
|
|
93
|
+
"@remotion/web-renderer": "4.0.494",
|
|
94
94
|
"@tailwindcss/vite": "^4.2.2",
|
|
95
95
|
"@tweakpane/core": "^2.0.5",
|
|
96
96
|
"@tweakpane/plugin-essentials": "^0.2.1",
|
|
@@ -98,12 +98,12 @@
|
|
|
98
98
|
"eventsource-parser": "^3.0.6",
|
|
99
99
|
"goke": "^6.12.1",
|
|
100
100
|
"js-yaml": "^4.1.1",
|
|
101
|
-
"mediabunny": "^1.
|
|
101
|
+
"mediabunny": "^1.50.8",
|
|
102
102
|
"moveable": "^0.53.0",
|
|
103
103
|
"picocolors": "^1.1.1",
|
|
104
104
|
"react": "19.2.7",
|
|
105
105
|
"react-dom": "19.2.7",
|
|
106
|
-
"remotion": "4.0.
|
|
106
|
+
"remotion": "4.0.494",
|
|
107
107
|
"safe-mdx": "^1.11.3",
|
|
108
108
|
"shiki": "^4.2.0",
|
|
109
109
|
"string-dedent": "^3.0.2",
|