@hyperframes/studio 0.8.65 → 0.8.66
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/dist/assets/{hyperframes-player-CwFrSySS.js → hyperframes-player-BRlwG-p9.js} +1 -1
- package/dist/assets/{index-Cs1lQrQ6.js → index-BzGcmMBS.js} +106 -106
- package/dist/assets/{index-wQFEeTL3.js → index-D6Jy-2Zs.js} +1 -1
- package/dist/assets/{index-a5nEhdAU.js → index-DJl2JKQj.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/components/editor/manualEdits.test.ts +100 -0
- package/src/components/editor/manualEdits.ts +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.66",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"dockview-react": "8.3.1",
|
|
52
52
|
"gsap": "^3.13.0",
|
|
53
53
|
"mediabunny": "^1.45.3",
|
|
54
|
-
"@hyperframes/
|
|
55
|
-
"@hyperframes/
|
|
56
|
-
"@hyperframes/parsers": "0.8.
|
|
57
|
-
"@hyperframes/
|
|
58
|
-
"@hyperframes/studio-server": "0.8.
|
|
54
|
+
"@hyperframes/core": "0.8.66",
|
|
55
|
+
"@hyperframes/player": "0.8.66",
|
|
56
|
+
"@hyperframes/parsers": "0.8.66",
|
|
57
|
+
"@hyperframes/sdk": "0.8.66",
|
|
58
|
+
"@hyperframes/studio-server": "0.8.66"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@tailwindcss/vite": "^4.3.3",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"vite": "^6.4.2",
|
|
72
72
|
"vitest": "^4.1.11",
|
|
73
73
|
"zustand": "^5.0.0",
|
|
74
|
-
"@hyperframes/producer": "0.8.
|
|
74
|
+
"@hyperframes/producer": "0.8.66"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"react": "19",
|
|
@@ -474,6 +474,106 @@ describe("studio manual edits", () => {
|
|
|
474
474
|
expect(frames).toHaveLength(0);
|
|
475
475
|
});
|
|
476
476
|
|
|
477
|
+
it("does not reapply every frame while the player is paused over unpaused scene timelines", () => {
|
|
478
|
+
const window = new Window();
|
|
479
|
+
const frames: FrameRequestCallback[] = [];
|
|
480
|
+
let playing = false;
|
|
481
|
+
const previewWindow = window as unknown as Parameters<
|
|
482
|
+
typeof installStudioManualEditSeekReapply
|
|
483
|
+
>[0] & {
|
|
484
|
+
__player: Record<string, unknown>;
|
|
485
|
+
__timelines: Record<string, Record<string, unknown>>;
|
|
486
|
+
requestAnimationFrame: (callback: FrameRequestCallback) => number;
|
|
487
|
+
};
|
|
488
|
+
previewWindow.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
489
|
+
frames.push(callback);
|
|
490
|
+
return frames.length;
|
|
491
|
+
};
|
|
492
|
+
previewWindow.__player = {
|
|
493
|
+
play: () => {
|
|
494
|
+
playing = true;
|
|
495
|
+
},
|
|
496
|
+
isPlaying: () => playing,
|
|
497
|
+
};
|
|
498
|
+
// A GSAP child of a paused master: not paused itself, inactive, time left.
|
|
499
|
+
const master = { paused: () => true };
|
|
500
|
+
previewWindow.__timelines = {
|
|
501
|
+
scene0: {
|
|
502
|
+
parent: master,
|
|
503
|
+
play: () => {},
|
|
504
|
+
paused: () => false,
|
|
505
|
+
isActive: () => false,
|
|
506
|
+
time: () => 0,
|
|
507
|
+
duration: () => 5,
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
expect(installStudioManualEditSeekReapply(previewWindow, () => {})).toBe(true);
|
|
512
|
+
expect(frames).toHaveLength(0);
|
|
513
|
+
|
|
514
|
+
(previewWindow.__player.play as () => void)();
|
|
515
|
+
expect(frames).toHaveLength(1);
|
|
516
|
+
playing = false;
|
|
517
|
+
frames.shift()?.(16);
|
|
518
|
+
expect(frames).toHaveLength(0);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it("tolerates a timeline whose parent chain loops", () => {
|
|
522
|
+
const window = new Window();
|
|
523
|
+
const previewWindow = window as unknown as Parameters<
|
|
524
|
+
typeof installStudioManualEditSeekReapply
|
|
525
|
+
>[0] & {
|
|
526
|
+
__timelines: Record<string, Record<string, unknown>>;
|
|
527
|
+
requestAnimationFrame: (callback: FrameRequestCallback) => number;
|
|
528
|
+
};
|
|
529
|
+
previewWindow.requestAnimationFrame = () => 1;
|
|
530
|
+
const loop: Record<string, unknown> = { paused: () => false };
|
|
531
|
+
loop.parent = loop;
|
|
532
|
+
previewWindow.__timelines = {
|
|
533
|
+
scene0: {
|
|
534
|
+
parent: loop,
|
|
535
|
+
play: () => {},
|
|
536
|
+
paused: () => false,
|
|
537
|
+
time: () => 0,
|
|
538
|
+
duration: () => 5,
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
expect(() => installStudioManualEditSeekReapply(previewWindow, () => {})).not.toThrow();
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("keeps reapplying while Studio drives a timeline directly and the player reads idle", () => {
|
|
546
|
+
const window = new Window();
|
|
547
|
+
const frames: FrameRequestCallback[] = [];
|
|
548
|
+
let paused = true;
|
|
549
|
+
const previewWindow = window as unknown as Parameters<
|
|
550
|
+
typeof installStudioManualEditSeekReapply
|
|
551
|
+
>[0] & {
|
|
552
|
+
__player: Record<string, unknown>;
|
|
553
|
+
__timeline: Record<string, unknown>;
|
|
554
|
+
requestAnimationFrame: (callback: FrameRequestCallback) => number;
|
|
555
|
+
};
|
|
556
|
+
previewWindow.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
557
|
+
frames.push(callback);
|
|
558
|
+
return frames.length;
|
|
559
|
+
};
|
|
560
|
+
previewWindow.__player = { play: () => {}, isPlaying: () => false };
|
|
561
|
+
previewWindow.__timeline = {
|
|
562
|
+
play: () => {
|
|
563
|
+
paused = false;
|
|
564
|
+
},
|
|
565
|
+
paused: () => paused,
|
|
566
|
+
isActive: () => !paused,
|
|
567
|
+
time: () => 1,
|
|
568
|
+
duration: () => 5,
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
expect(installStudioManualEditSeekReapply(previewWindow, () => {})).toBe(true);
|
|
572
|
+
(previewWindow.__timeline.play as () => void)();
|
|
573
|
+
frames.shift()?.(16);
|
|
574
|
+
expect(frames).toHaveLength(1);
|
|
575
|
+
});
|
|
576
|
+
|
|
477
577
|
it("stops playback reapply after an unpaused timeline has completed", () => {
|
|
478
578
|
const window = new Window();
|
|
479
579
|
const frames: FrameRequestCallback[] = [];
|
|
@@ -120,6 +120,21 @@ function hasRemainingTimelineTime(owner: Record<string, unknown>): boolean {
|
|
|
120
120
|
return time < duration;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
function hasPausedAncestor(owner: Record<string, unknown>): boolean {
|
|
124
|
+
let ancestor = owner.parent;
|
|
125
|
+
for (let depth = 0; depth < 32 && ancestor && typeof ancestor === "object"; depth++) {
|
|
126
|
+
const node = ancestor as Record<string, unknown>;
|
|
127
|
+
const paused = node.paused;
|
|
128
|
+
try {
|
|
129
|
+
if (typeof paused === "function" && paused.call(node)) return true;
|
|
130
|
+
} catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
ancestor = node.parent;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
123
138
|
function isTimelinePlaying(owner: Record<string, unknown> | undefined): boolean {
|
|
124
139
|
if (!owner) return false;
|
|
125
140
|
const isPlaying = owner.isPlaying;
|
|
@@ -148,6 +163,8 @@ function isTimelinePlaying(owner: Record<string, unknown> | undefined): boolean
|
|
|
148
163
|
}
|
|
149
164
|
}
|
|
150
165
|
|
|
166
|
+
// A GSAP child of a paused timeline reads unpaused with time left; only its ancestors know.
|
|
167
|
+
if (hasPausedAncestor(owner)) return false;
|
|
151
168
|
return hasRemainingTimelineTime(owner);
|
|
152
169
|
}
|
|
153
170
|
|