@hyperframes/studio 0.7.56 → 0.7.58
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-CtTDO63S.js +459 -0
- package/dist/assets/{index-CmVCjZjp.js → index-B_UvTX3E.js} +205 -205
- package/dist/assets/{index-BWnOxAiH.js → index-C47jAC3Q.js} +1 -1
- package/dist/assets/{index-C4csZims.js → index-DeQPzqwH.js} +1 -1
- package/dist/assets/index-uahwWkgw.css +1 -0
- package/dist/{chunk-5QSIMBEJ.js → chunk-OBAG3GWK.js} +33 -21
- package/dist/chunk-OBAG3GWK.js.map +1 -0
- package/dist/{domEditingLayers-6LQGKPOI.js → domEditingLayers-2CKWGEHS.js} +2 -2
- package/dist/index.d.ts +41 -4
- package/dist/index.html +2 -2
- package/dist/index.js +7436 -5423
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +3 -3
- package/src/components/StudioFeedbackBar.tsx +2 -2
- package/src/components/editor/CanvasContextMenu.test.tsx +166 -2
- package/src/components/editor/CanvasContextMenu.tsx +100 -20
- package/src/components/editor/DomEditOverlay.tsx +34 -62
- package/src/components/editor/LayersPanel.tsx +111 -91
- package/src/components/editor/canvasContextMenuZOrder.test.ts +243 -2
- package/src/components/editor/canvasContextMenuZOrder.ts +184 -23
- package/src/components/editor/domEditingDom.ts +5 -6
- package/src/components/editor/domEditingElement.ts +15 -32
- package/src/components/editor/layersPanelSort.ts +80 -0
- package/src/components/editor/propertyPanelColorGradingSection.tsx +15 -21
- package/src/components/editor/useCanvasContextMenuState.ts +92 -0
- package/src/components/editor/useLayerRevealOverride.test.ts +169 -0
- package/src/components/editor/useLayerRevealOverride.ts +389 -0
- package/src/components/editor/useZOrderCrossedFlash.tsx +68 -0
- package/src/components/nle/AssetPreviewOverlay.test.tsx +106 -0
- package/src/components/nle/AssetPreviewOverlay.tsx +61 -37
- package/src/components/nle/PreviewOverlays.tsx +61 -16
- package/src/components/nle/TimelinePane.test.ts +3 -0
- package/src/components/nle/TimelinePane.tsx +11 -1
- package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +440 -0
- package/src/components/nle/useCanvasZOrderTimelineMirror.ts +191 -0
- package/src/components/nle/useTimelineEditCallbacks.ts +1 -0
- package/src/components/nle/zLaneGesture.test.ts +176 -0
- package/src/components/nle/zLaneGesture.ts +77 -0
- package/src/components/sidebar/AssetCard.test.tsx +106 -0
- package/src/components/sidebar/AssetCard.tsx +19 -2
- package/src/components/sidebar/AudioRow.tsx +19 -2
- package/src/hooks/domEditCommitTypes.ts +29 -2
- package/src/hooks/elementLifecycleOpsTestUtils.ts +25 -0
- package/src/hooks/fetchStubTestUtils.ts +16 -0
- package/src/hooks/timelineEditingHelpers.test.ts +183 -1
- package/src/hooks/timelineEditingHelpers.ts +63 -259
- package/src/hooks/timelineMoveAdapter.test.ts +44 -40
- package/src/hooks/timelineMoveAdapter.ts +7 -4
- package/src/hooks/timelineTimingSync.test.ts +820 -0
- package/src/hooks/timelineTimingSync.ts +597 -0
- package/src/hooks/timelineTrackVisibility.test.ts +3 -1
- package/src/hooks/timelineTrackVisibility.ts +4 -3
- package/src/hooks/useCaptionDetection.ts +2 -0
- package/src/hooks/useClipboard.ts +19 -17
- package/src/hooks/useCompositionDimensions.ts +32 -5
- package/src/hooks/useDomEditCommits.test.tsx +392 -23
- package/src/hooks/useDomEditCommits.ts +180 -53
- package/src/hooks/useDomEditSession.test.tsx +3 -1
- package/src/hooks/useDomEditSession.ts +1 -1
- package/src/hooks/useElementLifecycleOps.test.tsx +559 -28
- package/src/hooks/useElementLifecycleOps.ts +175 -69
- package/src/hooks/useElementPicker.ts +7 -8
- package/src/hooks/usePreviewPersistence.ts +2 -2
- package/src/hooks/useTimelineAssetDropOps.ts +175 -0
- package/src/hooks/useTimelineEditing.test.tsx +591 -277
- package/src/hooks/useTimelineEditing.ts +202 -282
- package/src/hooks/useTimelineEditingTypes.ts +1 -1
- package/src/hooks/useTimelineGroupEditing.ts +187 -134
- package/src/player/components/PlayerControls.tsx +5 -5
- package/src/player/components/PlayheadIndicator.tsx +7 -2
- package/src/player/components/Timeline.test.ts +105 -18
- package/src/player/components/Timeline.tsx +69 -98
- package/src/player/components/TimelineCanvas.tsx +47 -7
- package/src/player/components/TimelineLanes.tsx +32 -10
- package/src/player/components/TimelineOverlays.tsx +36 -0
- package/src/player/components/TimelineRuler.tsx +70 -47
- package/src/player/components/TrackGapContextMenu.tsx +116 -0
- package/src/player/components/timelineCallbacks.ts +4 -1
- package/src/player/components/timelineClipDragCommit.test.ts +370 -11
- package/src/player/components/timelineClipDragCommit.ts +267 -68
- package/src/player/components/timelineGapCommit.test.ts +163 -0
- package/src/player/components/timelineGapCommit.ts +103 -0
- package/src/player/components/timelineGaps.test.ts +221 -0
- package/src/player/components/timelineGaps.ts +182 -0
- package/src/player/components/timelineLayout.test.ts +2 -1
- package/src/player/components/timelineLayout.ts +119 -30
- package/src/player/components/timelineMarquee.test.ts +20 -10
- package/src/player/components/timelineMarquee.ts +9 -2
- package/src/player/components/timelineRevealScroll.test.ts +133 -0
- package/src/player/components/timelineRevealScroll.ts +91 -0
- package/src/player/components/timelineStackingSync.test.ts +102 -1
- package/src/player/components/timelineStackingSync.ts +45 -5
- package/src/player/components/timelineTrackPersistPipeline.test.ts +168 -0
- package/src/player/components/timelineZMirror.test.ts +667 -0
- package/src/player/components/timelineZMirror.ts +393 -0
- package/src/player/components/timelineZones.test.ts +122 -310
- package/src/player/components/timelineZones.ts +82 -135
- package/src/player/components/timelineZoom.test.ts +2 -2
- package/src/player/components/timelineZoom.ts +7 -3
- package/src/player/components/useTimelineClipDrag.ts +1 -1
- package/src/player/components/useTimelineGapHighlights.test.ts +119 -0
- package/src/player/components/useTimelineGapHighlights.ts +110 -0
- package/src/player/components/useTimelinePlayhead.ts +4 -3
- package/src/player/components/useTimelineRangeSelection.ts +6 -4
- package/src/player/components/useTimelineRevealClip.ts +56 -0
- package/src/player/components/useTimelineScrollViewport.ts +73 -0
- package/src/player/components/useTimelineStackingSync.test.tsx +11 -2
- package/src/player/components/useTimelineStackingSync.ts +17 -4
- package/src/player/components/useTimelineTrackDerivations.ts +38 -0
- package/src/player/components/useTrackGapMenu.ts +155 -0
- package/src/player/hooks/useExpandedTimelineElements.test.ts +67 -4
- package/src/player/hooks/useExpandedTimelineElements.ts +19 -1
- package/src/player/hooks/useTimelinePlayer.seek.test.ts +55 -0
- package/src/player/hooks/useTimelinePlayer.test.ts +36 -3
- package/src/player/hooks/useTimelinePlayer.ts +4 -0
- package/src/player/hooks/useTimelineSyncCallbacks.ts +11 -1
- package/src/player/index.ts +3 -1
- package/src/player/lib/layerOrdering.test.ts +1 -15
- package/src/player/lib/layerOrdering.ts +5 -28
- package/src/player/lib/runtimeProtocol.test.ts +48 -0
- package/src/player/lib/runtimeProtocol.ts +65 -0
- package/src/player/lib/timelineDOM.test.ts +72 -0
- package/src/player/lib/timelineDOM.ts +12 -0
- package/src/player/lib/timelineElementHelpers.ts +54 -11
- package/src/player/lib/timelineIframeHelpers.ts +2 -4
- package/src/player/store/playerStore.test.ts +38 -13
- package/src/player/store/playerStore.ts +49 -1
- package/src/telemetry/events.test.ts +10 -0
- package/src/telemetry/events.ts +1 -0
- package/src/utils/assetPreviewDismiss.test.ts +27 -0
- package/src/utils/assetPreviewDismiss.ts +29 -0
- package/src/utils/domEditSaveQueue.ts +4 -3
- package/src/utils/editHistory.test.ts +82 -0
- package/src/utils/editHistory.ts +14 -2
- package/src/utils/gsapSoftReload.test.ts +54 -115
- package/src/utils/gsapSoftReload.ts +42 -152
- package/src/utils/gsapUndoRestore.test.ts +269 -0
- package/src/utils/gsapUndoRestore.ts +269 -0
- package/src/utils/sourceScopedSelectorIndex.ts +29 -0
- package/src/utils/timelineAssetDrop.test.ts +144 -52
- package/src/utils/timelineAssetDrop.ts +62 -86
- package/dist/assets/hyperframes-player-BBrKzTGd.js +0 -459
- package/dist/assets/index-D-GyYi2d.css +0 -1
- package/dist/chunk-5QSIMBEJ.js.map +0 -1
- /package/dist/{domEditingLayers-6LQGKPOI.js.map → domEditingLayers-2CKWGEHS.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AUDIO_EXT, IMAGE_EXT, VIDEO_EXT } from "./mediaTypes";
|
|
2
|
-
import { patchRootCompositionDuration, readRootCompositionDuration } from "./rootDuration";
|
|
3
2
|
import { roundToCenti } from "./rounding";
|
|
4
3
|
import { COMPOSITION_ROOT_OPEN_TAG_RE } from "./compositionPatterns";
|
|
4
|
+
import { patchRootCompositionDuration, readRootCompositionDuration } from "./rootDuration";
|
|
5
5
|
|
|
6
6
|
export const TIMELINE_ASSET_MIME = "application/x-hyperframes-asset";
|
|
7
7
|
export const TIMELINE_BLOCK_MIME = "application/x-hyperframes-block";
|
|
@@ -49,58 +49,66 @@ export function resolveTimelineAssetSrc(targetPath: string, assetPath: string):
|
|
|
49
49
|
return relative || assetPath.split("/").pop() || assetPath;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Sequence one or more dropped files end-to-end starting at the drop point, all on
|
|
54
|
+
* the track the user dropped onto. The clip lands where the ghost showed it — we do
|
|
55
|
+
* NOT bump to a different track on overlap (that produced surprise "new tracks" and,
|
|
56
|
+
* because it jumped past high indices like a grain-overlay track, wild numbers).
|
|
57
|
+
* HyperFrames allows time-overlap on a track; the user can nudge if they want a gap.
|
|
58
|
+
*/
|
|
52
59
|
export function buildTimelineFileDropPlacements(
|
|
53
60
|
placement: { start: number; track: number },
|
|
54
61
|
durations: number[],
|
|
55
|
-
occupiedClips: Array<{ start: number; duration: number; track: number }> = [],
|
|
56
62
|
): Array<{ start: number; track: number }> {
|
|
57
63
|
let nextStart = roundToCenti(Math.max(0, placement.start));
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
(end, duration) => roundToCenti(end + duration),
|
|
64
|
-
sequenceStart,
|
|
65
|
-
);
|
|
66
|
-
const overlapsDropTrack = occupiedClips.some((clip) => {
|
|
67
|
-
if (clip.track !== placement.track) return false;
|
|
68
|
-
const clipStart = Math.max(0, clip.start);
|
|
69
|
-
const clipEnd = clipStart + Math.max(0, clip.duration);
|
|
70
|
-
return sequenceStart < clipEnd && sequenceEnd > clipStart;
|
|
71
|
-
});
|
|
72
|
-
const track = overlapsDropTrack
|
|
73
|
-
? Math.max(placement.track, ...occupiedClips.map((clip) => clip.track)) + 1
|
|
74
|
-
: placement.track;
|
|
75
|
-
|
|
76
|
-
return resolvedDurations.map((duration) => {
|
|
64
|
+
return durations.map((rawDuration) => {
|
|
65
|
+
const duration =
|
|
66
|
+
Number.isFinite(rawDuration) && rawDuration > 0
|
|
67
|
+
? rawDuration
|
|
68
|
+
: FALLBACK_TIMELINE_FILE_DROP_DURATION;
|
|
77
69
|
const start = nextStart;
|
|
78
70
|
nextStart = roundToCenti(nextStart + duration);
|
|
79
|
-
return { start, track };
|
|
71
|
+
return { start, track: placement.track };
|
|
80
72
|
});
|
|
81
73
|
}
|
|
82
74
|
|
|
83
|
-
export function
|
|
84
|
-
left: number;
|
|
85
|
-
top: number;
|
|
75
|
+
export function resolveTimelineAssetCompositionSize(source: string): {
|
|
86
76
|
width: number;
|
|
87
77
|
height: number;
|
|
88
78
|
} {
|
|
89
79
|
const width = Number.parseFloat(source.match(/\bdata-width=(["'])([^"']+)\1/i)?.[2] ?? "");
|
|
90
80
|
const height = Number.parseFloat(source.match(/\bdata-height=(["'])([^"']+)\1/i)?.[2] ?? "");
|
|
91
|
-
|
|
92
81
|
return {
|
|
93
|
-
left: 0,
|
|
94
|
-
top: 0,
|
|
95
82
|
width: Number.isFinite(width) && width > 0 ? Math.round(width) : 640,
|
|
96
83
|
height: Number.isFinite(height) && height > 0 ? Math.round(height) : 360,
|
|
97
84
|
};
|
|
98
85
|
}
|
|
99
86
|
|
|
87
|
+
/**
|
|
88
|
+
* CapCut-style placement: natural size when it fits, scaled-to-fit when
|
|
89
|
+
* oversized, always centered. Unknown natural size → full-frame.
|
|
90
|
+
*/
|
|
91
|
+
export function fitTimelineAssetGeometry(
|
|
92
|
+
natural: { width: number; height: number } | null,
|
|
93
|
+
comp: { width: number; height: number },
|
|
94
|
+
): { left: number; top: number; width: number; height: number } {
|
|
95
|
+
if (!natural || natural.width <= 0 || natural.height <= 0) {
|
|
96
|
+
return { left: 0, top: 0, width: comp.width, height: comp.height };
|
|
97
|
+
}
|
|
98
|
+
const scale = Math.min(1, comp.width / natural.width, comp.height / natural.height);
|
|
99
|
+
const width = Math.round(natural.width * scale);
|
|
100
|
+
const height = Math.round(natural.height * scale);
|
|
101
|
+
return {
|
|
102
|
+
left: Math.round((comp.width - width) / 2),
|
|
103
|
+
top: Math.round((comp.height - height) / 2),
|
|
104
|
+
width,
|
|
105
|
+
height,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
export function buildTimelineAssetInsertHtml(input: {
|
|
101
110
|
id: string;
|
|
102
|
-
|
|
103
|
-
hfId?: string;
|
|
111
|
+
hfId: string;
|
|
104
112
|
assetPath: string;
|
|
105
113
|
kind: TimelineAssetKind;
|
|
106
114
|
start: number;
|
|
@@ -109,7 +117,7 @@ export function buildTimelineAssetInsertHtml(input: {
|
|
|
109
117
|
zIndex: number;
|
|
110
118
|
geometry?: { left: number; top: number; width: number; height: number };
|
|
111
119
|
}): string {
|
|
112
|
-
const sharedAttrs = `id="${input.id}" class="clip" src="${input.assetPath}" data-start="${input.start}" data-duration="${input.duration}" data-track-index="${input.track}"`;
|
|
120
|
+
const sharedAttrs = `id="${input.id}" data-hf-id="${input.hfId}" class="clip" src="${input.assetPath}" data-start="${input.start}" data-duration="${input.duration}" data-track-index="${input.track}"`;
|
|
113
121
|
const geometry = input.geometry ?? { left: 0, top: 0, width: 640, height: 360 };
|
|
114
122
|
const visualStyles = `position: absolute; left: ${geometry.left}px; top: ${geometry.top}px; width: ${geometry.width}px; height: ${geometry.height}px; object-fit: contain; z-index: ${input.zIndex}`;
|
|
115
123
|
|
|
@@ -121,23 +129,18 @@ export function buildTimelineAssetInsertHtml(input: {
|
|
|
121
129
|
return `<video ${sharedAttrs} muted playsinline style="${visualStyles}"></video>`;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
|
-
return `<audio ${sharedAttrs} style="z-index: ${input.zIndex}"></audio>`;
|
|
132
|
+
return `<audio ${sharedAttrs} data-volume="1" style="z-index: ${input.zIndex}"></audio>`;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const indented = assetHtml
|
|
137
|
-
.split("\n")
|
|
138
|
-
.map((line, i) => (i === 0 ? line : childIndent + line))
|
|
139
|
-
.join("\n");
|
|
140
|
-
return `${source.slice(0, insertAt)}\n${childIndent}${indented}${source.slice(insertAt)}`;
|
|
135
|
+
/**
|
|
136
|
+
* A clip inserted past the composition end would exist in the HTML but never
|
|
137
|
+
* appear on the timeline or in playback. Extend the root's data-duration to
|
|
138
|
+
* cover it (mirrors blockInstaller's behavior for installed blocks).
|
|
139
|
+
*/
|
|
140
|
+
export function extendCompositionDurationIfNeeded(source: string, requiredEnd: number): string {
|
|
141
|
+
const rootDur = readRootCompositionDuration(source);
|
|
142
|
+
if (rootDur == null || !Number.isFinite(rootDur) || requiredEnd <= rootDur) return source;
|
|
143
|
+
return patchRootCompositionDuration(source, String(roundToCenti(requiredEnd)));
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
/**
|
|
@@ -156,45 +159,18 @@ export function setCompositionDurationToContent(source: string, contentEnd: numb
|
|
|
156
159
|
return patchRootCompositionDuration(source, String(next));
|
|
157
160
|
}
|
|
158
161
|
|
|
159
|
-
export function
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Set the composition root's `data-duration` to `contentEnd` (grow OR shrink) so the
|
|
167
|
-
* timeline length tracks content — the content-driven counterpart to
|
|
168
|
-
* extendCompositionDurationIfNeeded's grow-only ratchet. Used after edits that can
|
|
169
|
-
* reduce the furthest clip end (delete/trim). No-op when `contentEnd` is not > 0, so
|
|
170
|
-
* an empty timeline keeps its declared duration instead of collapsing to 0.
|
|
171
|
-
*/
|
|
172
|
-
export function fitTimelineAssetGeometry(
|
|
173
|
-
natural: { width: number; height: number } | null,
|
|
174
|
-
comp: { width: number; height: number },
|
|
175
|
-
): { left: number; top: number; width: number; height: number } {
|
|
176
|
-
if (!natural || natural.width <= 0 || natural.height <= 0) {
|
|
177
|
-
return { left: 0, top: 0, width: comp.width, height: comp.height };
|
|
162
|
+
export function insertTimelineAssetIntoSource(source: string, assetHtml: string): string {
|
|
163
|
+
const match = COMPOSITION_ROOT_OPEN_TAG_RE.exec(source);
|
|
164
|
+
if (!match || match.index == null) {
|
|
165
|
+
throw new Error("No composition root found in target source");
|
|
178
166
|
}
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function resolveTimelineAssetCompositionSize(source: string): {
|
|
191
|
-
width: number;
|
|
192
|
-
height: number;
|
|
193
|
-
} {
|
|
194
|
-
const width = Number.parseFloat(source.match(/\bdata-width=(["'])([^"']+)\1/i)?.[2] ?? "");
|
|
195
|
-
const height = Number.parseFloat(source.match(/\bdata-height=(["'])([^"']+)\1/i)?.[2] ?? "");
|
|
196
|
-
return {
|
|
197
|
-
width: Number.isFinite(width) && width > 0 ? Math.round(width) : 640,
|
|
198
|
-
height: Number.isFinite(height) && height > 0 ? Math.round(height) : 360,
|
|
199
|
-
};
|
|
167
|
+
const insertAt = match.index + match[0].length;
|
|
168
|
+
const lineStart = source.lastIndexOf("\n", match.index);
|
|
169
|
+
const leadingWhitespace = source.slice(lineStart + 1, match.index).match(/^(\s*)/)?.[1] ?? "";
|
|
170
|
+
const childIndent = leadingWhitespace + " ";
|
|
171
|
+
const indented = assetHtml
|
|
172
|
+
.split("\n")
|
|
173
|
+
.map((line, i) => (i === 0 ? line : childIndent + line))
|
|
174
|
+
.join("\n");
|
|
175
|
+
return `${source.slice(0, insertAt)}\n${childIndent}${indented}${source.slice(insertAt)}`;
|
|
200
176
|
}
|