@hyperframes/studio 0.8.107 → 0.8.109
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-h4mktSdm.js → hyperframes-player-CJYtikAy.js} +1 -1
- package/dist/assets/{index-lrT-gfHa.css → index-BQZBDqMs.css} +1 -1
- package/dist/assets/{index-zoqPmBF6.js → index-CMSgrT2F.js} +1 -1
- package/dist/assets/{index-CM1xA_jD.js → index-CYP6dMd7.js} +212 -210
- package/dist/assets/{index-D8GKc2D9.js → index-HTFVHUJ4.js} +1 -1
- package/dist/{chunk-NCUBAVGK.js → chunk-5VSXRGIX.js} +4 -4
- package/dist/chunk-5VSXRGIX.js.map +1 -0
- package/dist/chunk-6LUNLTHR.js +611 -0
- package/dist/chunk-6LUNLTHR.js.map +1 -0
- package/dist/{chunk-46XP7J3B.js → chunk-PQFEBX32.js} +5 -3
- package/dist/chunk-PQFEBX32.js.map +1 -0
- package/dist/{chunk-4OZC426H.js → chunk-QUXZVHOJ.js} +10 -504
- package/dist/chunk-QUXZVHOJ.js.map +1 -0
- package/dist/{domEditingLayers-AQBDFNUC.js → domEditingLayers-JQPQGGT7.js} +3 -3
- package/dist/index.d.ts +53 -4
- package/dist/index.html +2 -2
- package/dist/index.js +15746 -13173
- package/dist/index.js.map +1 -1
- package/dist/player.js +3 -3
- package/dist/styles.css +1 -1
- package/dist/ui.js +1 -1
- package/package.json +8 -8
- package/src/App.tsx +8 -11
- package/src/components/EditorShell.tsx +6 -0
- package/src/components/LinkedSelectionToggle.tsx +73 -0
- package/src/components/TimelineToolbar.test.tsx +31 -0
- package/src/components/TimelineToolbar.tsx +2 -0
- package/src/components/editor/CropPresetBar.tsx +69 -0
- package/src/components/editor/DomEditCropHandles.test.tsx +44 -0
- package/src/components/editor/DomEditCropHandles.tsx +123 -95
- package/src/components/editor/InlineTextCaret.test.tsx +150 -0
- package/src/components/editor/InlineTextCaret.tsx +211 -0
- package/src/components/editor/InlineTextToolbar.test.tsx +34 -31
- package/src/components/editor/InlineTextToolbar.tsx +7 -5
- package/src/components/editor/PropertyPanelFlat.tsx +4 -1
- package/src/components/editor/carveOutput.ts +54 -0
- package/src/components/editor/cropPresetStore.ts +33 -0
- package/src/components/editor/cropPresets.test.ts +41 -0
- package/src/components/editor/cropPresets.ts +24 -0
- package/src/components/editor/domEditInlineText.ts +6 -4
- package/src/components/editor/domEditInlineTextElement.test.ts +7 -0
- package/src/components/editor/domEditingLayers.test.ts +21 -0
- package/src/components/editor/domEditingLayers.ts +2 -0
- package/src/components/editor/dragUndoPaint.test.ts +71 -0
- package/src/components/editor/linkedSpeedEdits.test.tsx +164 -0
- package/src/components/editor/linkedSpeedEdits.ts +94 -0
- package/src/components/editor/manualOffsetDrag.ts +11 -0
- package/src/components/editor/mediaAudioEdits.test.ts +79 -4
- package/src/components/editor/mediaAudioEdits.ts +66 -24
- package/src/components/editor/mediaLinkEdits.test.ts +321 -0
- package/src/components/editor/mediaLinkEdits.ts +282 -0
- package/src/components/editor/propertyPanelAudioFxGroup.test.tsx +32 -4
- package/src/components/editor/propertyPanelAudioFxGroup.tsx +3 -1
- package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +10 -5
- package/src/components/editor/propertyPanelMediaSection.tsx +6 -1
- package/src/components/editor/useDomEditNudge.ts +17 -12
- package/src/components/editor/useDomEditOverlayGestures.ts +17 -6
- package/src/components/editor/useFxCarve.ts +3 -61
- package/src/components/editor/useFxCarveGrouping.test.ts +13 -0
- package/src/components/editor/useFxCarveGrouping.ts +8 -3
- package/src/components/editor/useInlineTextEditing.tsx +11 -4
- package/src/components/editor/useVolumeAutomation.ts +29 -7
- package/src/components/nle/AudioMeterStrip.test.tsx +53 -0
- package/src/components/nle/AudioMeterStrip.tsx +60 -2
- package/src/components/nle/useTimelineEditCallbacks.ts +12 -0
- package/src/components/ui/Menu.test.tsx +3 -22
- package/src/components/ui/Menu.tsx +2 -2
- package/src/components/ui/Popover.tsx +2 -2
- package/src/components/ui/Select.tsx +2 -2
- package/src/components/ui/Tooltip.tsx +2 -2
- package/src/components/ui/mountHost.testHelpers.ts +8 -0
- package/src/components/ui/valueControls.test.tsx +2 -21
- package/src/contexts/TimelineEditContext.tsx +4 -0
- package/src/hooks/appHotkeysDispatch.ts +2 -1
- package/src/hooks/linkShortcuts.test.ts +70 -0
- package/src/hooks/linkShortcuts.ts +41 -0
- package/src/hooks/timelineLinkEditPlan.test.ts +113 -0
- package/src/hooks/timelineLinkEditPlan.ts +123 -0
- package/src/hooks/timelineMoveAdapter.ts +13 -0
- package/src/hooks/timelineTimingSync.test.ts +59 -0
- package/src/hooks/timelineTimingSync.ts +22 -3
- package/src/hooks/useAppHotkeys.ts +9 -0
- package/src/hooks/useEditHistoryActions.paint.test.tsx +41 -0
- package/src/hooks/useEditHistoryActions.ts +13 -4
- package/src/hooks/useElementPicker.test.ts +62 -13
- package/src/hooks/useElementPicker.ts +10 -2
- package/src/hooks/useFreezeFrame.test.ts +56 -0
- package/src/hooks/useFreezeFrame.ts +121 -0
- package/src/hooks/useGsapScriptCommits.test.tsx +84 -2
- package/src/hooks/useGsapScriptCommits.ts +23 -3
- package/src/hooks/usePersistentEditHistory.test.ts +9 -6
- package/src/hooks/usePersistentEditHistory.ts +1 -1
- package/src/hooks/usePreviewPersistence.history.test.tsx +76 -0
- package/src/hooks/usePreviewPersistence.ts +15 -0
- package/src/hooks/useRazorSplit.test.tsx +48 -0
- package/src/hooks/useRazorSplit.ts +26 -2
- package/src/hooks/useRenderClipContent.test.ts +108 -4
- package/src/hooks/useRenderClipContent.ts +33 -23
- package/src/hooks/useTimelineDeleteOps.ts +10 -4
- package/src/hooks/useTimelineEditGuard.ts +54 -0
- package/src/hooks/useTimelineEditing.ts +38 -57
- package/src/hooks/useTimelineLinkEditing.test.tsx +112 -0
- package/src/hooks/useTimelineLinkEditing.ts +153 -0
- package/src/index.ts +2 -0
- package/src/player/components/AudibleVideoClipContent.tsx +29 -0
- package/src/player/components/AudioWaveform.test.tsx +30 -3
- package/src/player/components/AudioWaveform.tsx +6 -7
- package/src/player/components/ClipBadges.test.tsx +82 -0
- package/src/player/components/ClipBadges.tsx +67 -0
- package/src/player/components/ClipContextMenu.groups.test.tsx +102 -0
- package/src/player/components/ClipContextMenu.test.tsx +10 -4
- package/src/player/components/ClipContextMenu.tsx +124 -77
- package/src/player/components/ClipPeakMarks.test.tsx +41 -0
- package/src/player/components/ClipPeakMarks.tsx +71 -0
- package/src/player/components/OutOfSyncBadge.test.tsx +91 -0
- package/src/player/components/OutOfSyncBadge.tsx +119 -0
- package/src/player/components/TimelineClip.test.tsx +17 -0
- package/src/player/components/TimelineClip.tsx +8 -0
- package/src/player/components/TimelineFxButton.test.tsx +2 -2
- package/src/player/components/TimelineTrackHeader.test.tsx +37 -0
- package/src/player/components/TimelineTrackHeader.tsx +12 -25
- package/src/player/components/audioClipLink.test.ts +228 -31
- package/src/player/components/audioClipLink.ts +72 -25
- package/src/player/components/clipMenuAudioItems.noShell.test.tsx +78 -0
- package/src/player/components/clipMenuAudioItems.test.tsx +168 -0
- package/src/player/components/clipMenuAudioItems.tsx +116 -0
- package/src/player/components/clipMenuDuck.test.ts +117 -0
- package/src/player/components/clipMenuDuck.ts +147 -0
- package/src/player/components/clipMenuLinkItems.test.ts +90 -0
- package/src/player/components/clipMenuLinkItems.tsx +121 -0
- package/src/player/components/clipMenuNormalize.test.ts +95 -0
- package/src/player/components/clipMenuNormalize.ts +96 -0
- package/src/player/components/clipMenuToolItems.test.tsx +139 -0
- package/src/player/components/clipMenuToolItems.tsx +233 -0
- package/src/player/components/clipPeakRuns.test.ts +38 -0
- package/src/player/components/clipPeakRuns.ts +59 -0
- package/src/player/components/clipSync.test.ts +67 -0
- package/src/player/components/clipSync.ts +80 -0
- package/src/player/components/clipToolAttrs.test.ts +161 -0
- package/src/player/components/clipToolAttrs.ts +187 -0
- package/src/player/components/linkLabelColor.test.ts +20 -0
- package/src/player/components/linkLabelColor.ts +24 -0
- package/src/player/components/timelineCallbacks.ts +12 -0
- package/src/player/components/timelineClipDragGestureLifecycle.ts +9 -0
- package/src/player/components/timelineClipDragTypes.ts +2 -0
- package/src/player/components/timelineClipGestureHandlers.ts +4 -1
- package/src/player/components/timelineLinkSelection.ts +23 -0
- package/src/player/components/useClipToolState.ts +92 -0
- package/src/player/components/useTimelineClipContextMenu.ts +2 -1
- package/src/player/components/useTimelineClipDrag.ts +21 -0
- package/src/player/components/useTimelineProviderState.tsx +3 -0
- package/src/player/hooks/timelinePlayerSync.test.ts +6 -0
- package/src/player/hooks/timelinePlayerSync.ts +1 -0
- package/src/player/hooks/useTimelineSyncCallbacks.ts +4 -1
- package/src/player/lib/timelineDOM.test.ts +31 -0
- package/src/player/lib/timelineDOM.ts +13 -0
- package/src/player/lib/timelineElementHelpers.ts +9 -0
- package/src/player/store/timelineElement.ts +12 -0
- package/src/styles/components.css +27 -11
- package/src/styles/theme.css +0 -1
- package/src/timelineChromeExports.test.tsx +7 -0
- package/src/utils/audioMeterMath.test.ts +37 -0
- package/src/utils/audioMeterMath.ts +9 -3
- package/src/utils/exportPeakCeiling.ts +1 -0
- package/src/utils/gsapSoftReload.test.ts +238 -2
- package/src/utils/gsapSoftReload.ts +89 -74
- package/src/utils/gsapUndoRestore.test.ts +4 -3
- package/src/utils/gsapUndoRestore.ts +15 -0
- package/src/utils/linkedClipPreferences.ts +34 -0
- package/src/utils/previewRelativePath.ts +19 -0
- package/src/utils/softReloadTargets.ts +128 -0
- package/src/utils/sourcePatcher.ts +1 -1
- package/src/utils/studioPendingEdits.test.ts +66 -0
- package/src/utils/studioPendingEdits.ts +45 -11
- package/src/utils/studioUiPreferences.ts +8 -0
- package/src/utils/timelineInspector.test.ts +33 -2
- package/src/utils/timelineInspector.ts +16 -4
- package/dist/chunk-46XP7J3B.js.map +0 -1
- package/dist/chunk-4OZC426H.js.map +0 -1
- package/dist/chunk-DI4LCJHL.js +0 -102
- package/dist/chunk-DI4LCJHL.js.map +0 -1
- package/dist/chunk-NCUBAVGK.js.map +0 -1
- /package/dist/{domEditingLayers-AQBDFNUC.js.map → domEditingLayers-JQPQGGT7.js.map} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HyperframesLoader
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5VSXRGIX.js";
|
|
4
4
|
import {
|
|
5
5
|
buildProjectApiPath,
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
6
|
+
splitTimelineElementKey
|
|
7
|
+
} from "./chunk-6LUNLTHR.js";
|
|
8
8
|
|
|
9
9
|
// src/player/lib/time.ts
|
|
10
10
|
var STUDIO_PREVIEW_FPS = 30;
|
|
@@ -72,474 +72,6 @@ function attachPlayerStoreDevHandle(store) {
|
|
|
72
72
|
window.__playerStore = store;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// src/player/lib/playbackAdapter.ts
|
|
76
|
-
function isFinitePositive(value) {
|
|
77
|
-
return Number.isFinite(value) && value > 0;
|
|
78
|
-
}
|
|
79
|
-
function clampTime(time, duration) {
|
|
80
|
-
const safeDuration = Math.max(0, Number.isFinite(duration) ? duration : 0);
|
|
81
|
-
const safeTime = Math.max(0, Number.isFinite(time) ? time : 0);
|
|
82
|
-
return safeDuration > 0 ? Math.min(safeTime, safeDuration) : safeTime;
|
|
83
|
-
}
|
|
84
|
-
function getAdapterDuration(adapter) {
|
|
85
|
-
if (!adapter) return 0;
|
|
86
|
-
try {
|
|
87
|
-
const duration = Number(adapter.getDuration());
|
|
88
|
-
return isFinitePositive(duration) ? duration : 0;
|
|
89
|
-
} catch {
|
|
90
|
-
return 0;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function getDefaultStaticSeekPlaybackClock(win) {
|
|
94
|
-
return {
|
|
95
|
-
now: () => win.performance.now(),
|
|
96
|
-
requestAnimationFrame: (callback) => win.requestAnimationFrame(callback),
|
|
97
|
-
cancelAnimationFrame: (handle) => win.cancelAnimationFrame(handle)
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function createStaticSeekPlaybackAdapter(player, duration, clock, getPlaybackRate = () => 1) {
|
|
101
|
-
const safeDuration = Math.max(0, Number.isFinite(duration) ? duration : 0);
|
|
102
|
-
let currentTime = clampTime(Number(player.getTime?.() ?? 0), safeDuration);
|
|
103
|
-
let playing = false;
|
|
104
|
-
let rafId = 0;
|
|
105
|
-
let playStartTime = currentTime;
|
|
106
|
-
let playStartNow = clock.now();
|
|
107
|
-
const renderSeek = (time) => {
|
|
108
|
-
currentTime = clampTime(time, safeDuration);
|
|
109
|
-
if (typeof player.renderSeek === "function") {
|
|
110
|
-
player.renderSeek(currentTime);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
player.seek?.(currentTime);
|
|
114
|
-
};
|
|
115
|
-
const stopTicker = () => {
|
|
116
|
-
if (rafId) {
|
|
117
|
-
clock.cancelAnimationFrame(rafId);
|
|
118
|
-
rafId = 0;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
const tick = (now) => {
|
|
122
|
-
if (!playing) return;
|
|
123
|
-
const playbackRate = Math.max(0.1, Number(getPlaybackRate()) || 1);
|
|
124
|
-
const elapsed = (now - playStartNow) / 1e3 * playbackRate;
|
|
125
|
-
renderSeek(playStartTime + elapsed);
|
|
126
|
-
if (currentTime >= safeDuration) {
|
|
127
|
-
playing = false;
|
|
128
|
-
rafId = 0;
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
rafId = clock.requestAnimationFrame(tick);
|
|
132
|
-
};
|
|
133
|
-
return {
|
|
134
|
-
play: () => {
|
|
135
|
-
if (playing || safeDuration <= 0) return;
|
|
136
|
-
if (currentTime >= safeDuration) renderSeek(0);
|
|
137
|
-
playing = true;
|
|
138
|
-
playStartTime = currentTime;
|
|
139
|
-
playStartNow = clock.now();
|
|
140
|
-
stopTicker();
|
|
141
|
-
rafId = clock.requestAnimationFrame(tick);
|
|
142
|
-
},
|
|
143
|
-
pause: () => {
|
|
144
|
-
playing = false;
|
|
145
|
-
stopTicker();
|
|
146
|
-
},
|
|
147
|
-
seek: (time, options) => {
|
|
148
|
-
renderSeek(time);
|
|
149
|
-
if (options?.keepPlaying) {
|
|
150
|
-
if (playing) {
|
|
151
|
-
playStartTime = currentTime;
|
|
152
|
-
playStartNow = clock.now();
|
|
153
|
-
}
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
playing = false;
|
|
157
|
-
stopTicker();
|
|
158
|
-
},
|
|
159
|
-
getTime: () => currentTime,
|
|
160
|
-
getDuration: () => safeDuration,
|
|
161
|
-
isPlaying: () => playing
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
function releaseStaticSeekCache(cache, warned) {
|
|
165
|
-
cache.current?.adapter.pause();
|
|
166
|
-
cache.current = null;
|
|
167
|
-
warned.current = false;
|
|
168
|
-
}
|
|
169
|
-
function resolveStaticSeekFallback(opts) {
|
|
170
|
-
const { cache, warned, bestAdapter, effectiveDuration, docDuration } = opts;
|
|
171
|
-
const cached = cache.current;
|
|
172
|
-
if (cached?.player === bestAdapter && cached.duration === effectiveDuration) {
|
|
173
|
-
return cached.adapter;
|
|
174
|
-
}
|
|
175
|
-
cached?.adapter.pause();
|
|
176
|
-
if (!warned.current) {
|
|
177
|
-
warned.current = true;
|
|
178
|
-
console.warn(
|
|
179
|
-
`[useTimelinePlayer] Selected adapter duration (${getAdapterDuration(bestAdapter)}s) does not cover the document duration (${docDuration}s); falling back to seek-driven playback, which never starts media elements or WebAudio. Audio will not play in preview \u2014 extend the GSAP timeline to cover the declared data-duration.`
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
const adapter = createStaticSeekPlaybackAdapter(
|
|
183
|
-
bestAdapter,
|
|
184
|
-
effectiveDuration,
|
|
185
|
-
opts.clock,
|
|
186
|
-
opts.getPlaybackRate
|
|
187
|
-
);
|
|
188
|
-
cache.current = { player: bestAdapter, duration: effectiveDuration, adapter };
|
|
189
|
-
return adapter;
|
|
190
|
-
}
|
|
191
|
-
function wrapTimeline(tl) {
|
|
192
|
-
return {
|
|
193
|
-
play: () => tl.play(),
|
|
194
|
-
pause: () => tl.pause(),
|
|
195
|
-
seek: (t, options) => {
|
|
196
|
-
const shouldPause = !options?.keepPlaying;
|
|
197
|
-
if (shouldPause) tl.pause();
|
|
198
|
-
tl.seek(t);
|
|
199
|
-
if (shouldPause) tl.pause();
|
|
200
|
-
},
|
|
201
|
-
getTime: () => tl.time(),
|
|
202
|
-
getDuration: () => tl.duration(),
|
|
203
|
-
isPlaying: () => tl.isActive()
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// src/player/lib/timelineElementHelpers.ts
|
|
208
|
-
import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
|
|
209
|
-
import { readElementFades } from "@hyperframes/core/audio-fade";
|
|
210
|
-
import {
|
|
211
|
-
clampPlaybackRate,
|
|
212
|
-
readMediaOffsetSeconds
|
|
213
|
-
} from "@hyperframes/parsers/media-duration";
|
|
214
|
-
var LAYER_REVEAL_PRIOR_Z_ATTR = "data-hf-reveal-prior-z";
|
|
215
|
-
var LAYER_REVEAL_PRIOR_POSITION_ATTR = "data-hf-reveal-prior-pos";
|
|
216
|
-
function readLayerRevealPriorZ(el) {
|
|
217
|
-
const raw = el.getAttribute(LAYER_REVEAL_PRIOR_Z_ATTR);
|
|
218
|
-
if (raw == null) return null;
|
|
219
|
-
const n = Number.parseInt(raw, 10);
|
|
220
|
-
return Number.isFinite(n) ? n : null;
|
|
221
|
-
}
|
|
222
|
-
function readTimelineElementZIndex(el) {
|
|
223
|
-
const prior = readLayerRevealPriorZ(el);
|
|
224
|
-
if (prior != null) return prior;
|
|
225
|
-
const html = el;
|
|
226
|
-
const parseZ = (value) => {
|
|
227
|
-
if (value == null || value === "" || value === "auto") return null;
|
|
228
|
-
const n = Number.parseInt(value, 10);
|
|
229
|
-
return Number.isFinite(n) ? n : null;
|
|
230
|
-
};
|
|
231
|
-
const fromInline = parseZ(html.style?.zIndex);
|
|
232
|
-
if (fromInline != null) return fromInline;
|
|
233
|
-
const view = el.ownerDocument?.defaultView;
|
|
234
|
-
if (view?.getComputedStyle) {
|
|
235
|
-
const fromComputed = parseZ(view.getComputedStyle(html).zIndex);
|
|
236
|
-
if (fromComputed != null) return fromComputed;
|
|
237
|
-
}
|
|
238
|
-
return 0;
|
|
239
|
-
}
|
|
240
|
-
function readDurationAttribute(el) {
|
|
241
|
-
if (!el) return 0;
|
|
242
|
-
const duration = Number.parseFloat(el.getAttribute("data-duration") ?? "") || Number.parseFloat(el.getAttribute("data-hf-authored-duration") ?? "");
|
|
243
|
-
return isFinitePositive(duration) ? duration : 0;
|
|
244
|
-
}
|
|
245
|
-
function isTimelineIgnoredElement(el) {
|
|
246
|
-
if (el.tagName.toLowerCase() === HF_AUDIO_GROUP_TAG) return true;
|
|
247
|
-
return Boolean(
|
|
248
|
-
el.closest(
|
|
249
|
-
[
|
|
250
|
-
"[data-hyperframes-ignore]",
|
|
251
|
-
"[data-hyperframes-picker-ignore]",
|
|
252
|
-
"[data-hf-ignore]",
|
|
253
|
-
"[data-hf-color-grading-canvas]"
|
|
254
|
-
].join(",")
|
|
255
|
-
)
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
function furthestClipEndFromDocument(doc) {
|
|
259
|
-
if (!doc) return 0;
|
|
260
|
-
const root = doc.querySelector("[data-composition-id]");
|
|
261
|
-
let maxEnd = 0;
|
|
262
|
-
for (const node of Array.from(doc.querySelectorAll("[data-start]"))) {
|
|
263
|
-
if (node === root || isTimelineIgnoredElement(node)) continue;
|
|
264
|
-
const start = Number.parseFloat(node.getAttribute("data-start") ?? "");
|
|
265
|
-
const duration = readDurationAttribute(node);
|
|
266
|
-
if (!Number.isFinite(start) || start < 0 || duration <= 0) continue;
|
|
267
|
-
maxEnd = Math.max(maxEnd, start + duration);
|
|
268
|
-
}
|
|
269
|
-
return maxEnd;
|
|
270
|
-
}
|
|
271
|
-
function readTimelineDurationFromDocument(doc) {
|
|
272
|
-
if (!doc) return 0;
|
|
273
|
-
const rootDuration = readDurationAttribute(doc.querySelector("[data-composition-id]"));
|
|
274
|
-
if (rootDuration > 0) return rootDuration;
|
|
275
|
-
return furthestClipEndFromDocument(doc);
|
|
276
|
-
}
|
|
277
|
-
function furthestClipEndFromSource(source) {
|
|
278
|
-
if (!source) return 0;
|
|
279
|
-
return furthestClipEndFromDocument(new DOMParser().parseFromString(source, "text/html"));
|
|
280
|
-
}
|
|
281
|
-
function isHtmlElement(el) {
|
|
282
|
-
const HtmlElementCtor = el.ownerDocument.defaultView?.HTMLElement ?? globalThis.HTMLElement;
|
|
283
|
-
return typeof HtmlElementCtor !== "undefined" && el instanceof HtmlElementCtor;
|
|
284
|
-
}
|
|
285
|
-
function isCompositionHost(el) {
|
|
286
|
-
return el.hasAttribute("data-composition-id") || el.hasAttribute("data-composition-src") || el.hasAttribute("data-composition-file");
|
|
287
|
-
}
|
|
288
|
-
function resolveMediaElement(el) {
|
|
289
|
-
const win = el.ownerDocument.defaultView ?? window;
|
|
290
|
-
const MediaElementCtor = win.HTMLMediaElement ?? globalThis.HTMLMediaElement;
|
|
291
|
-
const ImageElementCtor = win.HTMLImageElement ?? globalThis.HTMLImageElement;
|
|
292
|
-
if (el instanceof MediaElementCtor || el instanceof ImageElementCtor) return el;
|
|
293
|
-
if (isCompositionHost(el)) return null;
|
|
294
|
-
const candidate = el.querySelector("video, audio, img");
|
|
295
|
-
return candidate instanceof MediaElementCtor || candidate instanceof ImageElementCtor ? candidate : null;
|
|
296
|
-
}
|
|
297
|
-
function readPlaybackStartAttributes(getAttr) {
|
|
298
|
-
const playbackStartAttr = getAttr("data-playback-start") != null ? "playback-start" : getAttr("data-media-start") != null ? "media-start" : void 0;
|
|
299
|
-
return playbackStartAttr ? { playbackStart: readMediaOffsetSeconds(getAttr), playbackStartAttr } : {};
|
|
300
|
-
}
|
|
301
|
-
function playbackStartAttributeForElement(element) {
|
|
302
|
-
return element.playbackStartAttr === "playback-start" || element.kind === "composition" ? "data-playback-start" : "data-media-start";
|
|
303
|
-
}
|
|
304
|
-
function applyPlaybackMetadataFromElement(entry, el) {
|
|
305
|
-
Object.assign(
|
|
306
|
-
entry,
|
|
307
|
-
readPlaybackStartAttributes((n) => el.getAttribute(n))
|
|
308
|
-
);
|
|
309
|
-
const authoredPlaybackRate = Number.parseFloat(el.getAttribute("data-playback-rate") ?? "");
|
|
310
|
-
if (Number.isFinite(authoredPlaybackRate) && authoredPlaybackRate > 0) {
|
|
311
|
-
entry.playbackRate = clampPlaybackRate(authoredPlaybackRate);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
function setOptional(entry, key, value) {
|
|
315
|
-
if (value === void 0) delete entry[key];
|
|
316
|
-
else entry[key] = value;
|
|
317
|
-
}
|
|
318
|
-
function readVolume(el, media) {
|
|
319
|
-
const volume = Number.parseFloat(
|
|
320
|
-
el.getAttribute("data-volume") ?? media.getAttribute("data-volume") ?? ""
|
|
321
|
-
);
|
|
322
|
-
return Number.isFinite(volume) ? volume : void 0;
|
|
323
|
-
}
|
|
324
|
-
function isVideoAudible(opts) {
|
|
325
|
-
if (opts.hasAudioAttr === "true") return true;
|
|
326
|
-
if (opts.hasAudioAttr === "false" || opts.hasAudioAttr === "") return false;
|
|
327
|
-
return opts.tag.toLowerCase() === "video" && !opts.muted;
|
|
328
|
-
}
|
|
329
|
-
function applyAudioMetadataFromElement(entry, el) {
|
|
330
|
-
const media = resolveMediaElement(el) ?? el;
|
|
331
|
-
const muted = el.hasAttribute("muted") || media.hasAttribute("muted");
|
|
332
|
-
const sound = isVideoAudible({
|
|
333
|
-
tag: el.tagName,
|
|
334
|
-
hasAudioAttr: el.getAttribute("data-has-audio"),
|
|
335
|
-
muted
|
|
336
|
-
});
|
|
337
|
-
setOptional(entry, "hasAudio", sound ? true : void 0);
|
|
338
|
-
setOptional(entry, "muted", muted ? true : void 0);
|
|
339
|
-
setOptional(entry, "volume", readVolume(el, media));
|
|
340
|
-
}
|
|
341
|
-
function applyFadeMetadataFromElement(entry, el) {
|
|
342
|
-
const fades = readElementFades(el);
|
|
343
|
-
setOptional(entry, "fadeIn", fades.fadeIn > 0 ? fades.fadeIn : void 0);
|
|
344
|
-
setOptional(entry, "fadeOut", fades.fadeOut > 0 ? fades.fadeOut : void 0);
|
|
345
|
-
}
|
|
346
|
-
function applyMediaMetadataFromElement(entry, el) {
|
|
347
|
-
applyPlaybackMetadataFromElement(entry, el);
|
|
348
|
-
applyAudioMetadataFromElement(entry, el);
|
|
349
|
-
applyFadeMetadataFromElement(entry, el);
|
|
350
|
-
const mediaEl = resolveMediaElement(el);
|
|
351
|
-
if (!mediaEl) return;
|
|
352
|
-
entry.tag = mediaEl.tagName.toLowerCase();
|
|
353
|
-
const src = mediaEl.getAttribute("src");
|
|
354
|
-
if (src) entry.src = src;
|
|
355
|
-
const win = mediaEl.ownerDocument.defaultView ?? window;
|
|
356
|
-
const MediaElementCtor = win.HTMLMediaElement ?? globalThis.HTMLMediaElement;
|
|
357
|
-
if (typeof MediaElementCtor === "undefined" || !(mediaEl instanceof MediaElementCtor)) return;
|
|
358
|
-
const sourceDurationAttr = el.getAttribute("data-source-duration") ?? mediaEl.getAttribute("data-source-duration");
|
|
359
|
-
const sourceDuration = sourceDurationAttr ? parseFloat(sourceDurationAttr) : mediaEl.duration;
|
|
360
|
-
if (Number.isFinite(sourceDuration) && sourceDuration > 0) {
|
|
361
|
-
entry.sourceDuration = sourceDuration;
|
|
362
|
-
}
|
|
363
|
-
const playbackRate = mediaEl.defaultPlaybackRate;
|
|
364
|
-
if (entry.playbackRate == null && Number.isFinite(playbackRate) && playbackRate > 0) {
|
|
365
|
-
entry.playbackRate = clampPlaybackRate(playbackRate);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
function getTimelineElementDisplayLabel(input) {
|
|
369
|
-
const label = input.label?.trim();
|
|
370
|
-
if (label) return label;
|
|
371
|
-
const id = input.id?.trim();
|
|
372
|
-
if (id) return id;
|
|
373
|
-
const tag = input.tag?.trim().toLowerCase();
|
|
374
|
-
return tag ? `${tag} clip` : "Timeline clip";
|
|
375
|
-
}
|
|
376
|
-
function getTimelineElementSelector(el) {
|
|
377
|
-
if (isHtmlElement(el) && el.id) return `#${CSS.escape(el.id)}`;
|
|
378
|
-
const compId = el.getAttribute("data-composition-id");
|
|
379
|
-
if (compId) return `[data-composition-id="${CSS.escape(compId)}"]`;
|
|
380
|
-
if (isHtmlElement(el)) {
|
|
381
|
-
const classes = el.className.split(/\s+/).filter(Boolean);
|
|
382
|
-
const firstClass = classes.find((className) => className !== "clip") ?? classes[0];
|
|
383
|
-
if (firstClass) return `.${CSS.escape(firstClass)}`;
|
|
384
|
-
}
|
|
385
|
-
return void 0;
|
|
386
|
-
}
|
|
387
|
-
function getTimelineElementSourceFile(el) {
|
|
388
|
-
const ownerRoot = el.parentElement?.closest("[data-composition-id]");
|
|
389
|
-
return ownerRoot?.getAttribute("data-composition-file") ?? ownerRoot?.getAttribute("data-composition-src") ?? void 0;
|
|
390
|
-
}
|
|
391
|
-
function getTimelineElementSelectorIndex(doc, el, selector) {
|
|
392
|
-
return getSourceScopedSelectorIndex(
|
|
393
|
-
doc,
|
|
394
|
-
el,
|
|
395
|
-
selector,
|
|
396
|
-
getTimelineElementSourceFile(el),
|
|
397
|
-
getTimelineElementSourceFile
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
function buildTimelineElementKey(params) {
|
|
401
|
-
const scope = params.sourceFile ?? "index.html";
|
|
402
|
-
if (params.domId) return `${scope}#${params.domId}`;
|
|
403
|
-
if (params.selector) return `${scope}:${params.selector}:${params.selectorIndex ?? 0}`;
|
|
404
|
-
return `${scope}:${params.id}:${params.fallbackIndex}`;
|
|
405
|
-
}
|
|
406
|
-
function splitTimelineElementKey(key) {
|
|
407
|
-
const hashIndex = key.lastIndexOf("#");
|
|
408
|
-
if (hashIndex < 0) return { sourceFile: null, domId: key };
|
|
409
|
-
return { sourceFile: key.slice(0, hashIndex), domId: key.slice(hashIndex + 1) };
|
|
410
|
-
}
|
|
411
|
-
function buildTimelineElementIdentity(params) {
|
|
412
|
-
const id = params.preferredId?.trim() || buildTimelineElementKey({
|
|
413
|
-
id: params.label,
|
|
414
|
-
fallbackIndex: params.fallbackIndex,
|
|
415
|
-
domId: params.domId,
|
|
416
|
-
selector: params.selector,
|
|
417
|
-
selectorIndex: params.selectorIndex,
|
|
418
|
-
sourceFile: params.sourceFile
|
|
419
|
-
});
|
|
420
|
-
const key = buildTimelineElementKey({
|
|
421
|
-
id,
|
|
422
|
-
fallbackIndex: params.fallbackIndex,
|
|
423
|
-
domId: params.domId,
|
|
424
|
-
selector: params.selector,
|
|
425
|
-
selectorIndex: params.selectorIndex,
|
|
426
|
-
sourceFile: params.sourceFile
|
|
427
|
-
});
|
|
428
|
-
return { id, key };
|
|
429
|
-
}
|
|
430
|
-
function getTimelineElementIdentity(element) {
|
|
431
|
-
return element.key ?? element.id;
|
|
432
|
-
}
|
|
433
|
-
function runtimeAudioId(element) {
|
|
434
|
-
return element.domId || null;
|
|
435
|
-
}
|
|
436
|
-
function deriveTimelineStoreKey(params) {
|
|
437
|
-
if (!params.domId && !params.selector) return void 0;
|
|
438
|
-
return buildTimelineElementKey({ id: "", fallbackIndex: 0, ...params });
|
|
439
|
-
}
|
|
440
|
-
function deriveTimelineStoreKeyForDomId(domId, sourceFile) {
|
|
441
|
-
return deriveTimelineStoreKey({ domId, sourceFile });
|
|
442
|
-
}
|
|
443
|
-
function getTimelineDomNodes(doc) {
|
|
444
|
-
const rootComp = doc.querySelector("[data-composition-id]");
|
|
445
|
-
return Array.from(doc.querySelectorAll("[data-start]")).filter(
|
|
446
|
-
(node) => node !== rootComp && !isTimelineIgnoredElement(node)
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
function numbersNearlyEqual(a, b) {
|
|
450
|
-
return Math.abs(a - b) < 1e-3;
|
|
451
|
-
}
|
|
452
|
-
var MANIFEST_CLIP_ATTRS = [
|
|
453
|
-
["data-start", (clip) => clip.start],
|
|
454
|
-
["data-duration", (clip) => clip.duration],
|
|
455
|
-
["data-track-index", (clip) => clip.track]
|
|
456
|
-
];
|
|
457
|
-
function nodeMatchesClipTag(node, clip) {
|
|
458
|
-
return !clip.tagName || node.tagName.toLowerCase() === clip.tagName.toLowerCase();
|
|
459
|
-
}
|
|
460
|
-
function nodeMatchesManifestClip(node, clip) {
|
|
461
|
-
if (!nodeMatchesClipTag(node, clip)) return false;
|
|
462
|
-
return MANIFEST_CLIP_ATTRS.every(([attr, expected]) => {
|
|
463
|
-
const actual = Number.parseFloat(node.getAttribute(attr) ?? "");
|
|
464
|
-
return !Number.isFinite(actual) || numbersNearlyEqual(actual, expected(clip));
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
function nodeInClipScope(node, clip) {
|
|
468
|
-
const scope = clip.compositionAncestors;
|
|
469
|
-
if (!scope) return true;
|
|
470
|
-
const ids = [];
|
|
471
|
-
for (let cursor = node.parentElement; cursor; cursor = cursor.parentElement) {
|
|
472
|
-
const id = cursor.getAttribute("data-composition-id");
|
|
473
|
-
if (id) ids.unshift(id);
|
|
474
|
-
}
|
|
475
|
-
return ids.length === scope.length && ids.every((id, index) => id === scope[index]);
|
|
476
|
-
}
|
|
477
|
-
function findInClipScope(doc, clip, selectors) {
|
|
478
|
-
let lone = null;
|
|
479
|
-
for (const selector of selectors) {
|
|
480
|
-
const nodes = Array.from(doc.querySelectorAll(selector));
|
|
481
|
-
const scoped = nodes.find((node) => nodeInClipScope(node, clip));
|
|
482
|
-
if (scoped) return scoped;
|
|
483
|
-
if (nodes.length === 1) lone ??= nodes[0];
|
|
484
|
-
}
|
|
485
|
-
return lone;
|
|
486
|
-
}
|
|
487
|
-
function previewElementFinder(doc, selector = "[data-hf-id], [id]") {
|
|
488
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
489
|
-
const add = (key, node) => {
|
|
490
|
-
const nodes = byKey.get(key);
|
|
491
|
-
if (nodes) nodes.push(node);
|
|
492
|
-
else byKey.set(key, [node]);
|
|
493
|
-
};
|
|
494
|
-
for (const node of doc.querySelectorAll(selector)) {
|
|
495
|
-
const hfId = node.getAttribute("data-hf-id");
|
|
496
|
-
const id = node.getAttribute("id");
|
|
497
|
-
if (hfId) add(`hf:${hfId}`, node);
|
|
498
|
-
if (id) add(`id:${id}`, node);
|
|
499
|
-
}
|
|
500
|
-
return (target) => {
|
|
501
|
-
const matches = [
|
|
502
|
-
...target.hfId && byKey.get(`hf:${target.hfId}`) || [],
|
|
503
|
-
...byKey.get(`id:${target.domId ?? target.id}`) ?? []
|
|
504
|
-
];
|
|
505
|
-
return matches.find((node) => getTimelineElementSourceFile(node) === target.sourceFile) ?? matches[0] ?? null;
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
function findClipElementById(doc, clip) {
|
|
509
|
-
if (!clip.id) return null;
|
|
510
|
-
const first = doc.getElementById(clip.id);
|
|
511
|
-
if (!first || nodeInClipScope(first, clip)) return first;
|
|
512
|
-
return findInClipScope(doc, clip, [`[id="${CSS.escape(clip.id)}"]`]);
|
|
513
|
-
}
|
|
514
|
-
function findTimelineDomNode(doc, clip) {
|
|
515
|
-
if (!clip.id) return null;
|
|
516
|
-
const first = doc.getElementById(clip.id);
|
|
517
|
-
if (first && nodeInClipScope(first, clip)) return first;
|
|
518
|
-
const id = CSS.escape(clip.id);
|
|
519
|
-
const byOtherKeys = [`[data-hf-id="${id}"]`, `[data-composition-id="${id}"]`, `.${id}`];
|
|
520
|
-
return findInClipScope(doc, clip, first ? [`[id="${id}"]`, ...byOtherKeys] : byOtherKeys);
|
|
521
|
-
}
|
|
522
|
-
function findTimelineDomNodeForClip(doc, clip, fallbackIndex, usedNodes = /* @__PURE__ */ new Set(), getCandidates = () => getTimelineDomNodes(doc)) {
|
|
523
|
-
const byIdentity = findTimelineDomNode(doc, clip);
|
|
524
|
-
if (byIdentity && !usedNodes.has(byIdentity) && nodeMatchesClipTag(byIdentity, clip))
|
|
525
|
-
return byIdentity;
|
|
526
|
-
const candidates = getCandidates().filter((node) => !usedNodes.has(node));
|
|
527
|
-
const exact = candidates.find((node) => nodeMatchesManifestClip(node, clip));
|
|
528
|
-
if (exact) return exact;
|
|
529
|
-
const positional = candidates[fallbackIndex];
|
|
530
|
-
return positional && nodeMatchesClipTag(positional, clip) ? positional : null;
|
|
531
|
-
}
|
|
532
|
-
function createTimelineDomNodeResolver(doc) {
|
|
533
|
-
let candidates;
|
|
534
|
-
const usedNodes = /* @__PURE__ */ new Set();
|
|
535
|
-
const getCandidates = () => candidates ??= getTimelineDomNodes(doc);
|
|
536
|
-
return (clip, index) => {
|
|
537
|
-
const node = findTimelineDomNodeForClip(doc, clip, index, usedNodes, getCandidates);
|
|
538
|
-
if (node) usedNodes.add(node);
|
|
539
|
-
return node;
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
75
|
// src/player/store/playerStoreSelection.ts
|
|
544
76
|
function nextSelectionSet(current, id, preserveSet) {
|
|
545
77
|
if (preserveSet) return id && current.has(id) ? new Set(current) : /* @__PURE__ */ new Set();
|
|
@@ -737,6 +269,12 @@ function readStorage(storage, key) {
|
|
|
737
269
|
if (typeof parsed.agentToolsEnabled === "boolean") {
|
|
738
270
|
preferences.agentToolsEnabled = parsed.agentToolsEnabled;
|
|
739
271
|
}
|
|
272
|
+
if (typeof parsed.linkedSelectionEnabled === "boolean") {
|
|
273
|
+
preferences.linkedSelectionEnabled = parsed.linkedSelectionEnabled;
|
|
274
|
+
}
|
|
275
|
+
if (typeof parsed.syncIndicatorsVisible === "boolean") {
|
|
276
|
+
preferences.syncIndicatorsVisible = parsed.syncIndicatorsVisible;
|
|
277
|
+
}
|
|
740
278
|
const dockLayout = parseDockLayout(parsed.dockLayout);
|
|
741
279
|
if (dockLayout) preferences.dockLayout = dockLayout;
|
|
742
280
|
return preferences;
|
|
@@ -1836,38 +1374,6 @@ export {
|
|
|
1836
1374
|
useMountEffect,
|
|
1837
1375
|
usePreviewVariablesStore,
|
|
1838
1376
|
applyPreviewVariablesToUrl,
|
|
1839
|
-
getAdapterDuration,
|
|
1840
|
-
getDefaultStaticSeekPlaybackClock,
|
|
1841
|
-
releaseStaticSeekCache,
|
|
1842
|
-
resolveStaticSeekFallback,
|
|
1843
|
-
wrapTimeline,
|
|
1844
|
-
LAYER_REVEAL_PRIOR_Z_ATTR,
|
|
1845
|
-
LAYER_REVEAL_PRIOR_POSITION_ATTR,
|
|
1846
|
-
readLayerRevealPriorZ,
|
|
1847
|
-
readTimelineElementZIndex,
|
|
1848
|
-
isTimelineIgnoredElement,
|
|
1849
|
-
furthestClipEndFromDocument,
|
|
1850
|
-
readTimelineDurationFromDocument,
|
|
1851
|
-
furthestClipEndFromSource,
|
|
1852
|
-
resolveMediaElement,
|
|
1853
|
-
readPlaybackStartAttributes,
|
|
1854
|
-
playbackStartAttributeForElement,
|
|
1855
|
-
isVideoAudible,
|
|
1856
|
-
applyMediaMetadataFromElement,
|
|
1857
|
-
getTimelineElementDisplayLabel,
|
|
1858
|
-
getTimelineElementSelector,
|
|
1859
|
-
getTimelineElementSourceFile,
|
|
1860
|
-
getTimelineElementSelectorIndex,
|
|
1861
|
-
buildTimelineElementKey,
|
|
1862
|
-
splitTimelineElementKey,
|
|
1863
|
-
buildTimelineElementIdentity,
|
|
1864
|
-
getTimelineElementIdentity,
|
|
1865
|
-
runtimeAudioId,
|
|
1866
|
-
deriveTimelineStoreKey,
|
|
1867
|
-
deriveTimelineStoreKeyForDomId,
|
|
1868
|
-
previewElementFinder,
|
|
1869
|
-
findClipElementById,
|
|
1870
|
-
createTimelineDomNodeResolver,
|
|
1871
1377
|
PANEL_IDS,
|
|
1872
1378
|
PANEL_DEFINITIONS,
|
|
1873
1379
|
isPanelId,
|
|
@@ -1900,4 +1406,4 @@ export {
|
|
|
1900
1406
|
whenPreviewBooted,
|
|
1901
1407
|
Player
|
|
1902
1408
|
};
|
|
1903
|
-
//# sourceMappingURL=chunk-
|
|
1409
|
+
//# sourceMappingURL=chunk-QUXZVHOJ.js.map
|