@hyperframes/studio 0.8.131 → 0.8.133
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-BpOOAOcm.js → hyperframes-player-Cjcwveg0.js} +1 -1
- package/dist/assets/{index-DbSXe5_2.js → index-CwgWmfgx.js} +1 -1
- package/dist/assets/{index-BEcHKkGY.js → index-Dj21zXWA.js} +136 -136
- package/dist/assets/{index-DASBNWML.js → index-ME-Z5jh6.js} +1 -1
- package/dist/{chunk-DRVWEPO5.js → chunk-FOEID72M.js} +3 -1
- package/dist/{chunk-DRVWEPO5.js.map → chunk-FOEID72M.js.map} +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.html +1 -1
- package/dist/index.js +51 -108
- package/dist/index.js.map +1 -1
- package/dist/player.js +1 -1
- package/package.json +8 -8
- package/src/player/components/timelineClipDragCommit.test.ts +22 -0
- package/src/player/components/timelineClipDragCommit.ts +7 -10
- package/src/player/components/timelineStackingSync.test.ts +101 -142
- package/src/player/components/timelineStackingSync.ts +51 -235
- package/src/player/components/useTimelineStackingSync.ts +1 -1
- package/src/player/hooks/usePlaybackKeyboard.ts +1 -0
- package/src/player/hooks/useTimelinePlayer.playLock.test.ts +137 -0
- package/src/player/hooks/useTimelinePlayer.ts +15 -6
- package/src/player/hooks/useTimelinePlayerLoop.ts +1 -1
- package/src/player/lib/playbackSeek.ts +2 -0
- package/src/player/store/readinessSlice.ts +4 -0
- package/src/timelineStackingSyncExport.test.tsx +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -272,6 +272,8 @@ interface PlaybackReadinessSlice {
|
|
|
272
272
|
previewBooted: boolean;
|
|
273
273
|
setTimelineReady: (ready: boolean) => void;
|
|
274
274
|
markPreviewBooted: () => void;
|
|
275
|
+
playLocked: boolean;
|
|
276
|
+
setPlayLocked: (locked: boolean) => void;
|
|
275
277
|
/** Sets timelineReady once doc's readiness inputs settle, or immediately
|
|
276
278
|
* if doc is null. A wait a later call supersedes never wins the race.
|
|
277
279
|
* Waits for doc's load step first: it pauses and rewinds the preview, so a
|
package/dist/index.html
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
document.documentElement.dataset.theme = "paper";
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
|
-
<script type="module" crossorigin src="/assets/index-
|
|
17
|
+
<script type="module" crossorigin src="/assets/index-Dj21zXWA.js"></script>
|
|
18
18
|
<link rel="stylesheet" crossorigin href="/assets/index-D11atkBt.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
usePreviewVariablesStore,
|
|
40
40
|
whenPreviewBooted,
|
|
41
41
|
writeStudioUiPreferences
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-FOEID72M.js";
|
|
43
43
|
import {
|
|
44
44
|
Button,
|
|
45
45
|
ContextMenu,
|
|
@@ -9099,100 +9099,37 @@ function paintsAbove(a, b) {
|
|
|
9099
9099
|
if (a.domIndex != null && b.domIndex != null) return a.domIndex > b.domIndex;
|
|
9100
9100
|
return false;
|
|
9101
9101
|
}
|
|
9102
|
-
function
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
const visualOverlap = overlapping.filter((o) => !o.isAudio);
|
|
9107
|
-
if (visualOverlap.length === 0) return false;
|
|
9108
|
-
const below = visualOverlap.filter((o) => laneIsAbove(edited, o));
|
|
9109
|
-
const above = visualOverlap.filter((o) => laneIsAbove(o, edited));
|
|
9110
|
-
const correct = below.every((o) => paintsAbove(edited, o)) && above.every((o) => paintsAbove(o, edited));
|
|
9111
|
-
if (correct) return false;
|
|
9112
|
-
const maxBelow = boundaryZ(below, (zs) => Math.max(...zs));
|
|
9113
|
-
const single = trySingleZ(edited, below, above);
|
|
9114
|
-
if (single != null) {
|
|
9115
|
-
if (single !== edited.zIndex) patchZ(edited, single);
|
|
9116
|
-
return single !== edited.zIndex;
|
|
9117
|
-
}
|
|
9118
|
-
const target = maxBelow != null ? maxBelow + 1 : 0;
|
|
9119
|
-
const clamped = Math.max(0, target);
|
|
9120
|
-
if (clamped !== edited.zIndex) patchZ(edited, clamped);
|
|
9121
|
-
liftAbove(edited, overlappersOf, patchZ);
|
|
9122
|
-
return true;
|
|
9123
|
-
}
|
|
9124
|
-
function zBetweenNeighbours(maxBelow, minAbove, correctAt) {
|
|
9125
|
-
if (minAbove - maxBelow >= 2) {
|
|
9126
|
-
const mid = Math.floor((maxBelow + minAbove) / 2);
|
|
9127
|
-
return mid > maxBelow && mid < minAbove ? mid : null;
|
|
9128
|
-
}
|
|
9129
|
-
return correctAt(minAbove) ? minAbove : null;
|
|
9130
|
-
}
|
|
9131
|
-
function zBelowOnly(minAbove, correctAt) {
|
|
9132
|
-
const candidate = minAbove - 1;
|
|
9133
|
-
if (candidate >= 0) return candidate;
|
|
9134
|
-
return correctAt(minAbove) ? minAbove : null;
|
|
9135
|
-
}
|
|
9136
|
-
function trySingleZ(edited, below, above) {
|
|
9137
|
-
const maxBelow = boundaryZ(below, (zs) => Math.max(...zs));
|
|
9138
|
-
const minAbove = boundaryZ(above, (zs) => Math.min(...zs));
|
|
9139
|
-
const correctAt = (z) => {
|
|
9140
|
-
const probe2 = { ...edited, zIndex: z };
|
|
9141
|
-
return below.every((b) => paintsAbove(probe2, b)) && above.every((a) => paintsAbove(a, probe2));
|
|
9142
|
-
};
|
|
9143
|
-
if (maxBelow != null && minAbove != null)
|
|
9144
|
-
return zBetweenNeighbours(maxBelow, minAbove, correctAt);
|
|
9145
|
-
if (maxBelow != null) return maxBelow + 1;
|
|
9146
|
-
if (minAbove != null) return zBelowOnly(minAbove, correctAt);
|
|
9147
|
-
return null;
|
|
9102
|
+
function raisedZ(clip, overlapping) {
|
|
9103
|
+
const below = overlapping.filter((o) => laneIsAbove(clip, o));
|
|
9104
|
+
if (below.every((o) => paintsAbove(clip, o))) return null;
|
|
9105
|
+
return Math.max(...below.map((o) => o.zIndex)) + 1;
|
|
9148
9106
|
}
|
|
9149
|
-
function
|
|
9150
|
-
const
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
}
|
|
9107
|
+
function loweredZ(clip, overlapping) {
|
|
9108
|
+
const above = overlapping.filter((o) => laneIsAbove(o, clip));
|
|
9109
|
+
if (above.every((o) => paintsAbove(o, clip))) return null;
|
|
9110
|
+
const keepOver = overlapping.filter((o) => laneIsAbove(clip, o) && paintsAbove(clip, o));
|
|
9111
|
+
const floor = Math.max(
|
|
9112
|
+
0,
|
|
9113
|
+
...keepOver.map(
|
|
9114
|
+
(o) => paintsAbove({ ...clip, zIndex: o.zIndex }, o) ? o.zIndex : o.zIndex + 1
|
|
9115
|
+
)
|
|
9116
|
+
);
|
|
9117
|
+
const z = Math.max(floor, Math.min(...above.map((o) => o.zIndex)) - 1);
|
|
9118
|
+
return z < clip.zIndex ? z : null;
|
|
9162
9119
|
}
|
|
9163
|
-
function computeStackingPatches(elements, editedKeys) {
|
|
9120
|
+
function computeStackingPatches(elements, editedKeys, direction) {
|
|
9164
9121
|
const editedSet = new Set(editedKeys);
|
|
9165
|
-
|
|
9166
|
-
const
|
|
9167
|
-
const editedScopes = new Set(allResolved.filter((e) => editedSet.has(e.key)).map(paintScopeKey));
|
|
9168
|
-
const resolved2 = allResolved.filter((e) => editedScopes.has(paintScopeKey(e)));
|
|
9169
|
-
const byKey = new Map(resolved2.map((e) => [e.key, { ...e }]));
|
|
9170
|
-
const edited = resolved2.filter((e) => editedSet.has(e.key) && !e.isAudio).map((e) => byKey.get(e.key)).sort((a, b) => b.track - a.track);
|
|
9171
|
-
const changed = /* @__PURE__ */ new Map();
|
|
9172
|
-
const patchZ = (clip, z) => {
|
|
9173
|
-
clip.zIndex = z;
|
|
9174
|
-
changed.set(clip.key, z);
|
|
9175
|
-
};
|
|
9176
|
-
const all = [...byKey.values()];
|
|
9177
|
-
const overlappersOf = (clip) => all.filter(
|
|
9178
|
-
(o) => o.key !== clip.key && !o.isAudio && samePaintScope(clip, o) && overlapsInTime(clip, o)
|
|
9179
|
-
);
|
|
9180
|
-
for (const clip of edited) {
|
|
9181
|
-
resolveEditedZ(clip, overlappersOf(clip), overlappersOf, patchZ);
|
|
9182
|
-
}
|
|
9183
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
9122
|
+
const live = elements.filter((e) => Number.isFinite(e.zIndex) && !e.isAudio).map((e) => ({ ...e }));
|
|
9123
|
+
const edited = live.filter((e) => editedSet.has(e.key)).sort((a, b) => direction === "up" ? b.track - a.track : a.track - b.track);
|
|
9184
9124
|
const patches = [];
|
|
9185
9125
|
for (const clip of edited) {
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
patches.push({ key: key2, zIndex });
|
|
9194
|
-
emitted.add(key2);
|
|
9195
|
-
}
|
|
9126
|
+
const overlapping = live.filter(
|
|
9127
|
+
(o) => o.key !== clip.key && overlapsInTime(clip, o) && samePaintScope(clip, o)
|
|
9128
|
+
);
|
|
9129
|
+
const zIndex = direction === "up" ? raisedZ(clip, overlapping) : loweredZ(clip, overlapping);
|
|
9130
|
+
if (zIndex === null) continue;
|
|
9131
|
+
clip.zIndex = zIndex;
|
|
9132
|
+
patches.push({ key: clip.key, zIndex });
|
|
9196
9133
|
}
|
|
9197
9134
|
return patches;
|
|
9198
9135
|
}
|
|
@@ -9841,7 +9778,6 @@ function commitDraggedClipMove(drag, deps) {
|
|
|
9841
9778
|
if (multi?.keys.has(keyOf6(e))) return { ...e, start: multi.movedStart(e) };
|
|
9842
9779
|
return e;
|
|
9843
9780
|
});
|
|
9844
|
-
const multiKeys = multi ? multi.keys : null;
|
|
9845
9781
|
if (!isVertical || !deps.readZIndex || !deps.onStackingPatches) {
|
|
9846
9782
|
void refreshAfterDurableLaneMove(
|
|
9847
9783
|
persistMoveEdits(edits, deps, coalesceKey, "lane-reorder"),
|
|
@@ -9857,7 +9793,6 @@ function commitDraggedClipMove(drag, deps) {
|
|
|
9857
9793
|
dragKey,
|
|
9858
9794
|
drag.element.track,
|
|
9859
9795
|
drag.previewTrack,
|
|
9860
|
-
multiKeys,
|
|
9861
9796
|
deps,
|
|
9862
9797
|
coalesceKey
|
|
9863
9798
|
)
|
|
@@ -9947,7 +9882,6 @@ function commitTrackInsert(drag, deps, multi) {
|
|
|
9947
9882
|
dragKey,
|
|
9948
9883
|
drag.element.track,
|
|
9949
9884
|
drag.insertRow,
|
|
9950
|
-
multi ? multi.keys : null,
|
|
9951
9885
|
deps,
|
|
9952
9886
|
coalesceKey
|
|
9953
9887
|
)
|
|
@@ -9975,7 +9909,7 @@ function commitZMirrorLaneMove(element, move, deps, coalesceKey) {
|
|
|
9975
9909
|
deps
|
|
9976
9910
|
);
|
|
9977
9911
|
}
|
|
9978
|
-
function syncStackingForEdit(candidate, dragKey, currentLane, aimedLane,
|
|
9912
|
+
function syncStackingForEdit(candidate, dragKey, currentLane, aimedLane, deps, coalesceKey) {
|
|
9979
9913
|
const { readZIndex, onStackingPatches } = deps;
|
|
9980
9914
|
if (!readZIndex || !onStackingPatches) return Promise.resolve();
|
|
9981
9915
|
if (aimedLane === currentLane) return Promise.resolve();
|
|
@@ -9990,11 +9924,11 @@ function syncStackingForEdit(candidate, dragKey, currentLane, aimedLane, multiKe
|
|
|
9990
9924
|
domIndex,
|
|
9991
9925
|
stackingContextId: el.stackingContextId ?? null
|
|
9992
9926
|
}));
|
|
9993
|
-
const
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9927
|
+
const patches = computeStackingPatches(
|
|
9928
|
+
stackingEls,
|
|
9929
|
+
[dragKey],
|
|
9930
|
+
aimedLane < currentLane ? "up" : "down"
|
|
9931
|
+
);
|
|
9998
9932
|
if (patches.length === 0) return Promise.resolve();
|
|
9999
9933
|
return Promise.resolve(onStackingPatches(patches, coalesceKey)).then(() => void 0);
|
|
10000
9934
|
}
|
|
@@ -28278,6 +28212,7 @@ function usePlaybackKeyboard({
|
|
|
28278
28212
|
);
|
|
28279
28213
|
const shuttle = useCallback38(
|
|
28280
28214
|
(direction) => {
|
|
28215
|
+
if (usePlayerStore.getState().playLocked) return;
|
|
28281
28216
|
if (shuttleDirectionRef.current === direction) {
|
|
28282
28217
|
shuttleSpeedIndexRef.current = Math.min(
|
|
28283
28218
|
shuttleSpeedIndexRef.current + 1,
|
|
@@ -31213,7 +31148,7 @@ function useTimelinePlayerLoop({
|
|
|
31213
31148
|
const looping = usePlayerStore.getState().loopEnabled && dur > 0;
|
|
31214
31149
|
setLoopStart(looping ? loopStart : null);
|
|
31215
31150
|
if (time >= loopEnd) {
|
|
31216
|
-
if (looping) {
|
|
31151
|
+
if (looping && !usePlayerStore.getState().playLocked) {
|
|
31217
31152
|
adapter.seek(loopStart, { keepPlaying: true });
|
|
31218
31153
|
liveTime.notify(loopStart);
|
|
31219
31154
|
adapter.play();
|
|
@@ -31506,7 +31441,7 @@ function pumpProbeQueue() {
|
|
|
31506
31441
|
|
|
31507
31442
|
// src/player/lib/playbackSeek.ts
|
|
31508
31443
|
function shouldResumeForwardPlaybackAfterSeek(input) {
|
|
31509
|
-
return input.keepPlaying && !input.wasReverseShuttle && input.storeWasPlaying && (input.duration <= 0 || input.nextTime < input.duration);
|
|
31444
|
+
return !input.playLocked && input.keepPlaying && !input.wasReverseShuttle && input.storeWasPlaying && (input.duration <= 0 || input.nextTime < input.duration);
|
|
31510
31445
|
}
|
|
31511
31446
|
function shouldStopAfterSeek(input) {
|
|
31512
31447
|
return !input.keepPlaying || input.wasReverseShuttle;
|
|
@@ -31709,7 +31644,8 @@ function useTimelinePlayer({
|
|
|
31709
31644
|
applyPreviewAudioFlags(iframeRef.current, audioMuted, audioVolume);
|
|
31710
31645
|
}, []);
|
|
31711
31646
|
const play = useCallback42(() => {
|
|
31712
|
-
|
|
31647
|
+
const { timelineReady, playLocked } = usePlayerStore.getState();
|
|
31648
|
+
if (!timelineReady || playLocked) return;
|
|
31713
31649
|
stopRAFLoop();
|
|
31714
31650
|
stopReverseLoop();
|
|
31715
31651
|
stopScrubPreviewAudio();
|
|
@@ -31793,9 +31729,8 @@ function useTimelinePlayer({
|
|
|
31793
31729
|
const pause = useCallback42(() => {
|
|
31794
31730
|
stopReverseLoop();
|
|
31795
31731
|
const adapter = getAdapter();
|
|
31796
|
-
|
|
31797
|
-
adapter.
|
|
31798
|
-
setCurrentTime(adapter.getTime());
|
|
31732
|
+
adapter?.pause();
|
|
31733
|
+
if (adapter) setCurrentTime(adapter.getTime());
|
|
31799
31734
|
setIsPlaying(false);
|
|
31800
31735
|
shuttleDirectionRef.current = null;
|
|
31801
31736
|
shuttleSpeedIndexRef.current = 0;
|
|
@@ -31813,17 +31748,19 @@ function useTimelinePlayer({
|
|
|
31813
31748
|
const duration = Math.max(0, adapter.getDuration());
|
|
31814
31749
|
const nextTime = clampToDuration(time, duration);
|
|
31815
31750
|
const keepPlaying = options?.keepPlaying === true;
|
|
31751
|
+
const { playLocked, isPlaying: storeWasPlaying } = usePlayerStore.getState();
|
|
31816
31752
|
const shouldResumeAfterSeek = shouldResumeForwardPlaybackAfterSeek({
|
|
31817
31753
|
keepPlaying,
|
|
31818
31754
|
wasReverseShuttle,
|
|
31819
|
-
storeWasPlaying
|
|
31755
|
+
storeWasPlaying,
|
|
31820
31756
|
duration,
|
|
31821
|
-
nextTime
|
|
31757
|
+
nextTime,
|
|
31758
|
+
playLocked
|
|
31822
31759
|
});
|
|
31823
31760
|
adapter.seek(nextTime, options);
|
|
31824
31761
|
publishSeek(nextTime, options);
|
|
31825
31762
|
setCurrentTime(nextTime);
|
|
31826
|
-
if (!
|
|
31763
|
+
if (!keepPlaying && !playLocked) scrubMusicAtSeek(iframeRef.current, nextTime);
|
|
31827
31764
|
if (shouldResumeAfterSeek) {
|
|
31828
31765
|
stopRAFLoop();
|
|
31829
31766
|
applyPlaybackRate(usePlayerStore.getState().playbackRate);
|
|
@@ -31855,6 +31792,12 @@ function useTimelinePlayer({
|
|
|
31855
31792
|
shuttleSpeedIndexRef
|
|
31856
31793
|
]
|
|
31857
31794
|
);
|
|
31795
|
+
useEffect35(
|
|
31796
|
+
() => usePlayerStore.subscribe((state, prev) => {
|
|
31797
|
+
if (state.playLocked && !prev.playLocked && state.isPlaying) pause();
|
|
31798
|
+
}),
|
|
31799
|
+
[pause]
|
|
31800
|
+
);
|
|
31858
31801
|
useEffect35(() => {
|
|
31859
31802
|
return usePlayerStore.subscribe((state, prev) => {
|
|
31860
31803
|
if (state.requestedSeekTime !== null && state.requestedSeekTime !== prev.requestedSeekTime) {
|