@hyperframes/studio 0.7.102 → 0.7.104
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-BTnkvhf3.js → hyperframes-player-Bj2HAymq.js} +1 -1
- package/dist/assets/{index-CnrSn9Ua.js → index-8UzO6PkK.js} +1 -1
- package/dist/assets/{index-DN614u1l.js → index-DzfDa1ci.js} +212 -212
- package/dist/assets/{index-B4MocVKM.js → index-TDfRaO1Y.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/index.js +779 -294
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +6 -2
- package/src/captions/hooks/useCaptionSync.ts +2 -1
- package/src/components/editor/DomEditOverlay.test.ts +89 -2
- package/src/components/editor/DomEditOverlay.tsx +40 -23
- package/src/components/editor/domEditOverlayGeometry.test.ts +94 -0
- package/src/components/editor/domEditOverlayGeometry.ts +59 -2
- package/src/components/editor/domEditOverlayGestures.ts +42 -0
- package/src/components/editor/domEditOverlayStartGesture.ts +17 -0
- package/src/components/editor/groupDragMove.ts +110 -0
- package/src/components/editor/groupDropKeepsSelection.test.ts +70 -0
- package/src/components/editor/manualEditsDom.ts +15 -13
- package/src/components/editor/manualOffsetDrag.test.ts +92 -5
- package/src/components/editor/manualOffsetDrag.ts +22 -17
- package/src/components/editor/reapplyBoxSizeAfterSeek.test.ts +64 -0
- package/src/components/editor/snapEngageTravel.test.ts +65 -0
- package/src/components/editor/snapEngine.ts +21 -1
- package/src/components/editor/useDomEditOverlayGestures.ts +40 -53
- package/src/components/editor/useDomEditOverlayRects.ts +4 -2
- package/src/hooks/domSelectionTimelineMirror.ts +73 -0
- package/src/hooks/gestureTransaction.test.ts +7 -12
- package/src/hooks/gestureTransaction.ts +7 -20
- package/src/hooks/gsapRuntimePatch.test.ts +43 -0
- package/src/hooks/gsapRuntimePatch.ts +11 -1
- package/src/hooks/gsapScriptCommitHelpers.ts +2 -1
- package/src/hooks/gsapScriptCommitTypes.ts +20 -0
- package/src/hooks/keyframeCacheAstLoad.test.ts +108 -0
- package/src/hooks/keyframeCacheAstLoad.ts +29 -1
- package/src/hooks/timelineTimingSync.ts +4 -1
- package/src/hooks/useDomEditCommits.ts +2 -1
- package/src/hooks/useDomEditCommitsHelpers.ts +2 -1
- package/src/hooks/useDomEditPreviewSync.ts +23 -0
- package/src/hooks/useDomEditSession.test.tsx +43 -62
- package/src/hooks/useDomEditSession.ts +10 -2
- package/src/hooks/useDomEditWiring.ts +6 -0
- package/src/hooks/useDomSelection.test.ts +133 -3
- package/src/hooks/useDomSelection.ts +67 -40
- package/src/hooks/useDomSelectionSelectionGuards.test.ts +6 -2
- package/src/hooks/useElementLifecycleOps.ts +2 -1
- package/src/hooks/useExternalFileChangeCoordinator.ts +16 -3
- package/src/hooks/useFileManager.ts +2 -8
- package/src/hooks/useGroupCommits.ts +2 -1
- package/src/hooks/useGsapAnimationFetchFallback.ts +6 -1
- package/src/hooks/useGsapAwareEditing.test.tsx +36 -0
- package/src/hooks/useGsapAwareEditing.ts +88 -15
- package/src/hooks/useGsapInteractionFailureTelemetry.ts +5 -5
- package/src/hooks/useGsapScriptCommits.test.tsx +193 -7
- package/src/hooks/useGsapScriptCommits.ts +52 -15
- package/src/hooks/useStudioUrlState.ts +130 -7
- package/src/hooks/useTimelineEditing.ts +2 -1
- package/src/hooks/useTimelineSelectionPreviewSync.ts +13 -0
- package/src/player/hooks/useTimelinePlayer.ts +2 -0
- package/src/utils/dragDebug.ts +98 -0
- package/src/utils/razorSplitTransaction.test.ts +15 -3
- package/src/utils/razorSplitTransaction.ts +2 -0
- package/src/utils/reloadDebug.ts +10 -0
- package/src/utils/resizeDebug.ts +19 -29
- package/src/utils/selectDebug.ts +9 -0
- package/src/utils/studioDebug.test.ts +32 -0
- package/src/utils/studioDebug.ts +30 -0
- package/src/utils/studioFileVersion.ts +16 -1
- package/src/utils/studioUrlState.test.ts +163 -1
- package/src/utils/studioUrlState.ts +60 -3
- package/src/utils/timelineCompositionInsert.ts +2 -1
package/dist/index.js
CHANGED
|
@@ -1134,7 +1134,7 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1134
1134
|
motionPathCreateAvailable: false,
|
|
1135
1135
|
setMotionPathCreateAvailable: (available) => set({ motionPathCreateAvailable: available }),
|
|
1136
1136
|
autoKeyframeEnabled: true,
|
|
1137
|
-
setAutoKeyframeEnabled: (
|
|
1137
|
+
setAutoKeyframeEnabled: (enabled) => set({ autoKeyframeEnabled: enabled }),
|
|
1138
1138
|
selectedElementIds: /* @__PURE__ */ new Set(),
|
|
1139
1139
|
setSelection: (ids, anchor) => set(resolveElementSelection(ids, anchor)),
|
|
1140
1140
|
addSelectedElementId: (id) => set((s) => {
|
|
@@ -1228,14 +1228,14 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1228
1228
|
writeStudioUiPreferences({ audioMuted: muted });
|
|
1229
1229
|
set({ audioMuted: muted });
|
|
1230
1230
|
},
|
|
1231
|
-
setLoopEnabled: (
|
|
1231
|
+
setLoopEnabled: (enabled) => set({ loopEnabled: enabled }),
|
|
1232
1232
|
setZoomMode: (mode) => set({ zoomMode: mode }),
|
|
1233
1233
|
clearSelectedElementIds: () => set({ selectedElementIds: /* @__PURE__ */ new Set() }),
|
|
1234
1234
|
setSelectedElementIds: (ids) => set({ selectedElementIds: new Set(ids) }),
|
|
1235
1235
|
timelineSnapEnabled: readStudioUiPreferences().timelineSnapEnabled ?? true,
|
|
1236
|
-
setTimelineSnapEnabled: (
|
|
1237
|
-
writeStudioUiPreferences({ timelineSnapEnabled:
|
|
1238
|
-
set({ timelineSnapEnabled:
|
|
1236
|
+
setTimelineSnapEnabled: (enabled) => {
|
|
1237
|
+
writeStudioUiPreferences({ timelineSnapEnabled: enabled });
|
|
1238
|
+
set({ timelineSnapEnabled: enabled });
|
|
1239
1239
|
},
|
|
1240
1240
|
timeDisplayMode: readStudioUiPreferences().timeDisplayMode ?? "time",
|
|
1241
1241
|
setTimeDisplayMode: (mode) => {
|
|
@@ -1552,13 +1552,13 @@ function isAutomatedBrowser() {
|
|
|
1552
1552
|
return navigator.webdriver === true;
|
|
1553
1553
|
}
|
|
1554
1554
|
var decisions = /* @__PURE__ */ new Map();
|
|
1555
|
-
var forcedOutcome = (
|
|
1556
|
-
enabled
|
|
1557
|
-
reason:
|
|
1555
|
+
var forcedOutcome = (enabled) => ({
|
|
1556
|
+
enabled,
|
|
1557
|
+
reason: enabled ? "forced_on" : "forced_off"
|
|
1558
1558
|
});
|
|
1559
|
-
var cohortOutcome = (
|
|
1560
|
-
enabled
|
|
1561
|
-
reason:
|
|
1559
|
+
var cohortOutcome = (enabled) => ({
|
|
1560
|
+
enabled,
|
|
1561
|
+
reason: enabled ? "in_cohort" : "out_of_cohort"
|
|
1562
1562
|
});
|
|
1563
1563
|
function decideStudioCanary(name) {
|
|
1564
1564
|
const definition = findCanary(name);
|
|
@@ -1589,8 +1589,8 @@ function resolveCanary(name) {
|
|
|
1589
1589
|
function canaryEventProperties() {
|
|
1590
1590
|
return canaryFeatureProperties(
|
|
1591
1591
|
CANARIES.map((c) => {
|
|
1592
|
-
const { enabled
|
|
1593
|
-
return { name: c.name, enabled
|
|
1592
|
+
const { enabled, reason } = resolveCanary(c.name);
|
|
1593
|
+
return { name: c.name, enabled, reason };
|
|
1594
1594
|
})
|
|
1595
1595
|
);
|
|
1596
1596
|
}
|
|
@@ -11932,6 +11932,45 @@ function patchDocumentRootDuration(doc, contentEnd) {
|
|
|
11932
11932
|
return true;
|
|
11933
11933
|
}
|
|
11934
11934
|
|
|
11935
|
+
// src/utils/studioFileVersion.ts
|
|
11936
|
+
var WRITE_TOKEN_TTL_MS = 5 * 6e4;
|
|
11937
|
+
var studioWriteTokens = /* @__PURE__ */ new Map();
|
|
11938
|
+
function pruneStudioWriteTokens(now2) {
|
|
11939
|
+
for (const [token, createdAt] of studioWriteTokens) {
|
|
11940
|
+
if (now2 - createdAt >= WRITE_TOKEN_TTL_MS) studioWriteTokens.delete(token);
|
|
11941
|
+
}
|
|
11942
|
+
}
|
|
11943
|
+
function markStudioWriteToken(token, now2 = Date.now()) {
|
|
11944
|
+
pruneStudioWriteTokens(now2);
|
|
11945
|
+
studioWriteTokens.set(token, now2);
|
|
11946
|
+
}
|
|
11947
|
+
function consumeStudioWriteToken(token, now2 = Date.now()) {
|
|
11948
|
+
pruneStudioWriteTokens(now2);
|
|
11949
|
+
if (!token || !studioWriteTokens.has(token)) return false;
|
|
11950
|
+
studioWriteTokens.delete(token);
|
|
11951
|
+
return true;
|
|
11952
|
+
}
|
|
11953
|
+
async function studioFileContentVersion(content) {
|
|
11954
|
+
const bytes = new TextEncoder().encode(content);
|
|
11955
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
11956
|
+
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
11957
|
+
""
|
|
11958
|
+
);
|
|
11959
|
+
return `"sha256:${hex}"`;
|
|
11960
|
+
}
|
|
11961
|
+
async function studioExpectedFileVersion(versions, path, expectedContent) {
|
|
11962
|
+
if (expectedContent !== void 0) return studioFileContentVersion(expectedContent);
|
|
11963
|
+
return versions.get(path);
|
|
11964
|
+
}
|
|
11965
|
+
function createStudioWriteToken() {
|
|
11966
|
+
return globalThis.crypto.randomUUID();
|
|
11967
|
+
}
|
|
11968
|
+
function studioWriteHeaders() {
|
|
11969
|
+
const token = createStudioWriteToken();
|
|
11970
|
+
markStudioWriteToken(token);
|
|
11971
|
+
return { "X-Hyperframes-Write-Token": token };
|
|
11972
|
+
}
|
|
11973
|
+
|
|
11935
11974
|
// src/hooks/timelineTimingSync.ts
|
|
11936
11975
|
var GsapPreviewConvergenceError = class extends Error {
|
|
11937
11976
|
};
|
|
@@ -11973,6 +12012,8 @@ async function rollbackOwnedMutation(projectId, targetPath, expected, restore) {
|
|
|
11973
12012
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutation-rollback/${encodeURIComponent(targetPath)}`,
|
|
11974
12013
|
{
|
|
11975
12014
|
method: "POST",
|
|
12015
|
+
// Deliberately unclaimed: a rollback runs because a mutation did not
|
|
12016
|
+
// converge, so let the restored file reload the preview.
|
|
11976
12017
|
headers: { "Content-Type": "application/json" },
|
|
11977
12018
|
body: JSON.stringify({ expected, restore })
|
|
11978
12019
|
}
|
|
@@ -12031,7 +12072,7 @@ async function postGsapMutation(projectId, filePath, mutation, fallback) {
|
|
|
12031
12072
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(filePath)}`,
|
|
12032
12073
|
{
|
|
12033
12074
|
method: "POST",
|
|
12034
|
-
headers: { "Content-Type": "application/json" },
|
|
12075
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
12035
12076
|
body: JSON.stringify(mutation)
|
|
12036
12077
|
}
|
|
12037
12078
|
);
|
|
@@ -15791,7 +15832,7 @@ var TrackGapContextMenu = memo13(function TrackGapContextMenu2({
|
|
|
15791
15832
|
const overflowY = y + menuHeight - window.innerHeight;
|
|
15792
15833
|
const adjustedX = x + menuWidth > window.innerWidth ? x - menuWidth : x;
|
|
15793
15834
|
const adjustedY = overflowY > 0 ? y - overflowY - 8 : y;
|
|
15794
|
-
const itemClass = (
|
|
15835
|
+
const itemClass = (enabled) => `w-full flex items-center justify-between px-3 py-1.5 text-xs text-left ${enabled ? "text-neutral-300 hover:bg-neutral-800 cursor-pointer" : "text-neutral-600 cursor-not-allowed"}`;
|
|
15795
15836
|
const closeGapTitle = canCloseGap ? void 0 : gapWidth == null ? "No gap here" : "A clip on this track can't be moved";
|
|
15796
15837
|
const closeAllTitle = canCloseAllGaps ? void 0 : hasAnyGaps ? "A clip on this track can't be moved" : "No gaps on this track";
|
|
15797
15838
|
return createPortal4(
|
|
@@ -17911,7 +17952,7 @@ function extractTimelineVirtualRowSeed(count, pinnedRowIndexes) {
|
|
|
17911
17952
|
return [...indexes].sort((left, right) => left - right);
|
|
17912
17953
|
}
|
|
17913
17954
|
function useTimelineVirtualRows({
|
|
17914
|
-
enabled
|
|
17955
|
+
enabled,
|
|
17915
17956
|
scrollRef,
|
|
17916
17957
|
viewport,
|
|
17917
17958
|
rowGeometry,
|
|
@@ -17944,7 +17985,7 @@ function useTimelineVirtualRows({
|
|
|
17944
17985
|
);
|
|
17945
17986
|
const initialOffset = useCallback19(() => viewport.scrollTop, [viewport.scrollTop]);
|
|
17946
17987
|
const virtualizer = useVirtualizer({
|
|
17947
|
-
enabled:
|
|
17988
|
+
enabled: enabled && viewportReady,
|
|
17948
17989
|
useFlushSync: false,
|
|
17949
17990
|
count: rowGeometry.rowKeys.length,
|
|
17950
17991
|
getScrollElement: () => scrollRef.current,
|
|
@@ -17957,20 +17998,20 @@ function useTimelineVirtualRows({
|
|
|
17957
17998
|
initialOffset
|
|
17958
17999
|
});
|
|
17959
18000
|
useEffect22(() => {
|
|
17960
|
-
if (!
|
|
18001
|
+
if (!enabled || !viewportReady) return;
|
|
17961
18002
|
virtualizer.measure();
|
|
17962
18003
|
scrollRef.current?.dispatchEvent(new Event("scroll"));
|
|
17963
|
-
}, [
|
|
18004
|
+
}, [enabled, scrollRef, sessionEpoch, viewportReady, virtualizer]);
|
|
17964
18005
|
const focusedRowIndex = focusedRowKey === void 0 ? -1 : rowGeometry.getRowIndex(focusedRowKey);
|
|
17965
18006
|
const focusedRowHeight = focusedRowIndex < 0 ? 0 : rowGeometry.getRowHeight(focusedRowIndex);
|
|
17966
18007
|
useEffect22(() => {
|
|
17967
|
-
if (
|
|
18008
|
+
if (enabled && viewportReady && focusedRowIndex >= 0) {
|
|
17968
18009
|
virtualizer.resizeItem(focusedRowIndex, focusedRowHeight);
|
|
17969
18010
|
}
|
|
17970
|
-
}, [
|
|
18011
|
+
}, [enabled, focusedRowHeight, focusedRowIndex, sessionEpoch, viewportReady, virtualizer]);
|
|
17971
18012
|
const compatibilityRows = useMemo23(
|
|
17972
|
-
() =>
|
|
17973
|
-
[
|
|
18013
|
+
() => enabled ? null : rowGeometry.rowKeys.map((rowKey, index) => ({ index, rowKey })),
|
|
18014
|
+
[enabled, rowGeometry]
|
|
17974
18015
|
);
|
|
17975
18016
|
if (compatibilityRows) return compatibilityRows;
|
|
17976
18017
|
if (!viewportReady) {
|
|
@@ -18010,7 +18051,7 @@ function useTimelineRowVirtualization({
|
|
|
18010
18051
|
lastScrollLeftRef,
|
|
18011
18052
|
syncScrollViewport
|
|
18012
18053
|
}) {
|
|
18013
|
-
const
|
|
18054
|
+
const enabled = STUDIO_TIMELINE_ROW_VIRTUALIZATION_ENABLED;
|
|
18014
18055
|
const [domFocusedRowKey, setDomFocusedRowKey] = useState16();
|
|
18015
18056
|
const onTimelineFocus = useCallback20((event) => {
|
|
18016
18057
|
setDomFocusedRowKey(getFocusedTimelineRowKey(event.target));
|
|
@@ -18045,7 +18086,7 @@ function useTimelineRowVirtualization({
|
|
|
18045
18086
|
]
|
|
18046
18087
|
);
|
|
18047
18088
|
const virtualRows = useTimelineVirtualRows({
|
|
18048
|
-
enabled
|
|
18089
|
+
enabled,
|
|
18049
18090
|
scrollRef,
|
|
18050
18091
|
viewport,
|
|
18051
18092
|
rowGeometry,
|
|
@@ -18080,7 +18121,7 @@ function useTimelineRowVirtualization({
|
|
|
18080
18121
|
previousLayoutRef.current = rowGeometry;
|
|
18081
18122
|
}, [lastScrollLeftRef, rowGeometry, scrollRef, sessionEpoch, syncScrollViewport]);
|
|
18082
18123
|
return {
|
|
18083
|
-
enabled
|
|
18124
|
+
enabled,
|
|
18084
18125
|
virtualRows,
|
|
18085
18126
|
timelineFocusProps: { onFocus: onTimelineFocus, onBlur: onTimelineBlur }
|
|
18086
18127
|
};
|
|
@@ -20219,6 +20260,29 @@ function useTimelinePlayerLoop({
|
|
|
20219
20260
|
return { startRAFLoop, stopRAFLoop, stopReverseLoop };
|
|
20220
20261
|
}
|
|
20221
20262
|
|
|
20263
|
+
// src/utils/studioDebug.ts
|
|
20264
|
+
function makeStudioDebugLogger(name) {
|
|
20265
|
+
let enabled = null;
|
|
20266
|
+
return (stage, data = {}) => {
|
|
20267
|
+
if (enabled === null) {
|
|
20268
|
+
try {
|
|
20269
|
+
enabled = localStorage.getItem(`hf-${name}-debug`) === "1";
|
|
20270
|
+
} catch {
|
|
20271
|
+
enabled = false;
|
|
20272
|
+
}
|
|
20273
|
+
}
|
|
20274
|
+
if (!enabled) return;
|
|
20275
|
+
const details = typeof data === "function" ? data() : data;
|
|
20276
|
+
if (!details) return;
|
|
20277
|
+
console.log(
|
|
20278
|
+
`[hf-${name}] ${JSON.stringify({ stage, t: Math.round(performance.now()), ...details })}`
|
|
20279
|
+
);
|
|
20280
|
+
};
|
|
20281
|
+
}
|
|
20282
|
+
|
|
20283
|
+
// src/utils/reloadDebug.ts
|
|
20284
|
+
var logReload = makeStudioDebugLogger("reload");
|
|
20285
|
+
|
|
20222
20286
|
// src/player/lib/playbackScrub.ts
|
|
20223
20287
|
function scrubMusicAtSeek(iframe, nextTime) {
|
|
20224
20288
|
const s = usePlayerStore.getState();
|
|
@@ -20842,6 +20906,7 @@ function useTimelinePlayer() {
|
|
|
20842
20906
|
const refreshPlayer = useCallback28(() => {
|
|
20843
20907
|
const iframe = iframeRef.current;
|
|
20844
20908
|
if (!iframe) return;
|
|
20909
|
+
logReload("refreshPlayer", () => ({ stack: new Error("refreshPlayer").stack }));
|
|
20845
20910
|
saveSeekPosition();
|
|
20846
20911
|
iframe.style.visibility = "hidden";
|
|
20847
20912
|
const src = iframe.src;
|
|
@@ -23763,7 +23828,14 @@ function readElementTransformSnapshot(win, element) {
|
|
|
23763
23828
|
if (!DOMMatrixCtor) return null;
|
|
23764
23829
|
const cs = win.getComputedStyle(element);
|
|
23765
23830
|
try {
|
|
23766
|
-
|
|
23831
|
+
let matrix = new DOMMatrixCtor();
|
|
23832
|
+
for (let node = element; node; node = node.parentElement) {
|
|
23833
|
+
const transform = node === element ? cs.transform : win.getComputedStyle(node).transform;
|
|
23834
|
+
if (transform && transform !== "none") {
|
|
23835
|
+
matrix = new DOMMatrixCtor(transform).multiply(matrix);
|
|
23836
|
+
}
|
|
23837
|
+
if (node.hasAttribute("data-composition-id")) break;
|
|
23838
|
+
}
|
|
23767
23839
|
return { matrix, cs };
|
|
23768
23840
|
} catch {
|
|
23769
23841
|
return null;
|
|
@@ -23772,7 +23844,12 @@ function readElementTransformSnapshot(win, element) {
|
|
|
23772
23844
|
function rotationDegreesFromMatrix(matrix) {
|
|
23773
23845
|
const a = Number.isFinite(matrix.a) ? matrix.a : 1;
|
|
23774
23846
|
const b = Number.isFinite(matrix.b) ? matrix.b : 0;
|
|
23775
|
-
const
|
|
23847
|
+
const c = Number.isFinite(matrix.c) ? matrix.c : 0;
|
|
23848
|
+
const d = Number.isFinite(matrix.d) ? matrix.d : 1;
|
|
23849
|
+
const fromX = Math.atan2(b, a) * 180 / Math.PI;
|
|
23850
|
+
const determinant = a * d - b * c;
|
|
23851
|
+
const fromY = Math.atan2(-c, d) * 180 / Math.PI;
|
|
23852
|
+
const deg = determinant < 0 && Math.abs(fromY) < Math.abs(fromX) ? fromY : fromX;
|
|
23776
23853
|
return Number.isFinite(deg) ? deg : 0;
|
|
23777
23854
|
}
|
|
23778
23855
|
var ROTATION_GATE_EPSILON_DEG = 1e-4;
|
|
@@ -23903,6 +23980,10 @@ function orientedOverlayRect(overlayEl, iframe, element) {
|
|
|
23903
23980
|
angle
|
|
23904
23981
|
};
|
|
23905
23982
|
}
|
|
23983
|
+
function orientedVisibleOverlayRect(overlayEl, iframe, element) {
|
|
23984
|
+
const rect = orientedOverlayRect(overlayEl, iframe, element);
|
|
23985
|
+
return rect ? { ...rect, ...hugRectForElement(rect, element) } : null;
|
|
23986
|
+
}
|
|
23906
23987
|
var OVERLAY_RECT_EPSILON_PX = 0.5;
|
|
23907
23988
|
var OVERLAY_RECT_ANGLE_EPSILON_DEG = 0.1;
|
|
23908
23989
|
function rectsEqual(a, b) {
|
|
@@ -24234,6 +24315,9 @@ function useCanvasContextMenuState({
|
|
|
24234
24315
|
return { contextMenu, closeContextMenu, handleContextMenu };
|
|
24235
24316
|
}
|
|
24236
24317
|
|
|
24318
|
+
// src/utils/selectDebug.ts
|
|
24319
|
+
var logSelect = makeStudioDebugLogger("select");
|
|
24320
|
+
|
|
24237
24321
|
// src/components/editor/domEditOverlayGestures.ts
|
|
24238
24322
|
var BLOCKED_MOVE_THRESHOLD_PX = 4;
|
|
24239
24323
|
var ROTATION_COMMIT_EPSILON_DEGREES = 0.05;
|
|
@@ -24242,6 +24326,19 @@ var ROTATED_SNAP_BYPASS_DEGREES = 0.5;
|
|
|
24242
24326
|
function focusDomEditOverlayElement(element) {
|
|
24243
24327
|
element?.focus({ preventScroll: true });
|
|
24244
24328
|
}
|
|
24329
|
+
function hoverCacheDescribesPoint(cachedElement, elementAtPoint) {
|
|
24330
|
+
if (!cachedElement || !elementAtPoint) return false;
|
|
24331
|
+
return cachedElement === elementAtPoint || cachedElement.contains(elementAtPoint);
|
|
24332
|
+
}
|
|
24333
|
+
function resolveShiftClickCandidate(input) {
|
|
24334
|
+
const describes = hoverCacheDescribesPoint(input.cached?.element, input.elementAtPoint);
|
|
24335
|
+
logSelect("shift-pointerdown", {
|
|
24336
|
+
candidate: input.cached ? input.cached.selector ?? null : null,
|
|
24337
|
+
pointTarget: input.elementAtPoint?.id ?? input.elementAtPoint?.tagName ?? null,
|
|
24338
|
+
cacheIsAboutThisPoint: describes
|
|
24339
|
+
});
|
|
24340
|
+
return describes ? input.cached : null;
|
|
24341
|
+
}
|
|
24245
24342
|
function resolveResizeCenterAnchorOffset(input) {
|
|
24246
24343
|
return {
|
|
24247
24344
|
dx: (input.originWidth - input.overlayWidth) / 2,
|
|
@@ -24381,7 +24478,7 @@ function useDomEditOverlayRects({
|
|
|
24381
24478
|
for (let i = 0; i < descendants.length; i++) {
|
|
24382
24479
|
const child = descendants[i];
|
|
24383
24480
|
if (!child.getBoundingClientRect) continue;
|
|
24384
|
-
const r =
|
|
24481
|
+
const r = orientedVisibleOverlayRect(overlayEl, iframe, child);
|
|
24385
24482
|
if (r && r.width > 2 && r.height > 2) nextChildRects.push(r);
|
|
24386
24483
|
}
|
|
24387
24484
|
if (!childRectsEqual(childRectsRef.current, nextChildRects)) {
|
|
@@ -25071,25 +25168,16 @@ function queryStudioElements(doc, attr) {
|
|
|
25071
25168
|
}
|
|
25072
25169
|
function reapplyPathOffsets(doc) {
|
|
25073
25170
|
for (const el of queryStudioElements(doc, STUDIO_PATH_OFFSET_ATTR)) {
|
|
25074
|
-
|
|
25171
|
+
if (gsapAnimatesProperty(el, "x", "y")) continue;
|
|
25075
25172
|
const x = el.style.getPropertyValue(STUDIO_OFFSET_X_PROP);
|
|
25076
25173
|
const y = el.style.getPropertyValue(STUDIO_OFFSET_Y_PROP);
|
|
25077
|
-
if (
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
el,
|
|
25081
|
-
{
|
|
25082
|
-
x: Number.parseFloat(x) || 0,
|
|
25083
|
-
y: Number.parseFloat(y) || 0
|
|
25084
|
-
},
|
|
25085
|
-
{ updateBase: false }
|
|
25086
|
-
);
|
|
25087
|
-
}
|
|
25174
|
+
if (!x && !y) continue;
|
|
25175
|
+
const offset = { x: Number.parseFloat(x) || 0, y: Number.parseFloat(y) || 0 };
|
|
25176
|
+
applyStudioPathOffset(el, offset, { updateBase: false });
|
|
25088
25177
|
}
|
|
25089
25178
|
}
|
|
25090
25179
|
function reapplyBoxSizes(doc) {
|
|
25091
25180
|
for (const el of queryStudioElements(doc, STUDIO_BOX_SIZE_ATTR)) {
|
|
25092
|
-
if (gsapAnimatesProperty(el, "width", "height")) continue;
|
|
25093
25181
|
const w = Number.parseFloat(el.style.getPropertyValue(STUDIO_WIDTH_PROP));
|
|
25094
25182
|
const h = Number.parseFloat(el.style.getPropertyValue(STUDIO_HEIGHT_PROP));
|
|
25095
25183
|
if (Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) {
|
|
@@ -25658,12 +25746,6 @@ function readTransformWDivisor(element) {
|
|
|
25658
25746
|
return Number.isFinite(w) && w > 0 ? w : 1;
|
|
25659
25747
|
}
|
|
25660
25748
|
function measureManualOffsetDragScreenToOffsetMatrix(element, initialOffset, options = {}) {
|
|
25661
|
-
if (!element.hasAttribute("data-hf-studio-path-offset") && initialOffset.x === 0 && initialOffset.y === 0) {
|
|
25662
|
-
const sx = options.scaleX || 1;
|
|
25663
|
-
const sy = options.scaleY || 1;
|
|
25664
|
-
const w = readTransformWDivisor(element);
|
|
25665
|
-
return { ok: true, matrix: { a: w / sx, b: 0, c: 0, d: w / sy } };
|
|
25666
|
-
}
|
|
25667
25749
|
const probeSize = options.probeSize ?? DEFAULT_OFFSET_PROBE_PX;
|
|
25668
25750
|
if (!Number.isFinite(probeSize) || probeSize <= 0) {
|
|
25669
25751
|
return { ok: false, reason: "Invalid movement probe size." };
|
|
@@ -26011,6 +26093,10 @@ function readElementGsapNumber(element, prop) {
|
|
|
26011
26093
|
|
|
26012
26094
|
// src/components/editor/snapEngine.ts
|
|
26013
26095
|
var SNAP_THRESHOLD_PX = 6;
|
|
26096
|
+
var SNAP_ENGAGE_TRAVEL_PX = 4;
|
|
26097
|
+
function snapEngagedForTravel(dx, dy) {
|
|
26098
|
+
return Math.hypot(dx, dy) >= SNAP_ENGAGE_TRAVEL_PX;
|
|
26099
|
+
}
|
|
26014
26100
|
var EQUIDISTANCE_TOLERANCE_PX = 1;
|
|
26015
26101
|
function rectRight(r) {
|
|
26016
26102
|
return r.left + r.width;
|
|
@@ -26173,7 +26259,7 @@ var DISABLED_RESULT = (dx, dy) => ({
|
|
|
26173
26259
|
spacingGuides: []
|
|
26174
26260
|
});
|
|
26175
26261
|
function resolveSnapAdjustment(input) {
|
|
26176
|
-
if (input.disabled || input.threshold <= 0) {
|
|
26262
|
+
if (input.disabled || input.disabledForTravel || input.threshold <= 0) {
|
|
26177
26263
|
return DISABLED_RESULT(input.proposedDx, input.proposedDy);
|
|
26178
26264
|
}
|
|
26179
26265
|
const mr = input.movingRect;
|
|
@@ -26383,38 +26469,22 @@ function buildExcludeElements(input) {
|
|
|
26383
26469
|
}
|
|
26384
26470
|
|
|
26385
26471
|
// src/utils/resizeDebug.ts
|
|
26472
|
+
var logResize = makeStudioDebugLogger("resize");
|
|
26386
26473
|
var moveN = 0;
|
|
26387
|
-
var enabled = null;
|
|
26388
|
-
function isEnabled2() {
|
|
26389
|
-
if (enabled === null) {
|
|
26390
|
-
try {
|
|
26391
|
-
enabled = localStorage.getItem("hf-resize-debug") === "1";
|
|
26392
|
-
} catch {
|
|
26393
|
-
enabled = false;
|
|
26394
|
-
}
|
|
26395
|
-
}
|
|
26396
|
-
return enabled;
|
|
26397
|
-
}
|
|
26398
|
-
function logResize(stage, data) {
|
|
26399
|
-
if (!isEnabled2()) return;
|
|
26400
|
-
console.log(
|
|
26401
|
-
`[hf-resize] ${JSON.stringify({ stage, t: Math.round(performance.now()), ...data })}`
|
|
26402
|
-
);
|
|
26403
|
-
}
|
|
26404
26474
|
function logResizeMove(data) {
|
|
26405
|
-
|
|
26406
|
-
|
|
26407
|
-
|
|
26475
|
+
logResize("move", () => {
|
|
26476
|
+
moveN += 1;
|
|
26477
|
+
return moveN % 8 === 1 ? { n: moveN, ...data } : null;
|
|
26478
|
+
});
|
|
26408
26479
|
}
|
|
26409
26480
|
function resetResizeMoveLog() {
|
|
26410
26481
|
moveN = 0;
|
|
26411
26482
|
}
|
|
26412
26483
|
function logResizeSettle(el, tag) {
|
|
26413
|
-
|
|
26414
|
-
const snap = (phase) => {
|
|
26484
|
+
const snapshot = (phase) => {
|
|
26415
26485
|
const r = el.getBoundingClientRect();
|
|
26416
26486
|
const cs = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
26417
|
-
|
|
26487
|
+
return {
|
|
26418
26488
|
tag,
|
|
26419
26489
|
phase,
|
|
26420
26490
|
rect: { x: r.x, y: r.y, w: r.width, h: r.height },
|
|
@@ -26422,10 +26492,75 @@ function logResizeSettle(el, tag) {
|
|
|
26422
26492
|
cssH: cs?.height,
|
|
26423
26493
|
transform: cs?.transform,
|
|
26424
26494
|
inlineStyle: el.getAttribute("style")
|
|
26425
|
-
}
|
|
26495
|
+
};
|
|
26426
26496
|
};
|
|
26427
|
-
|
|
26428
|
-
|
|
26497
|
+
logResize("settle", () => {
|
|
26498
|
+
const current = snapshot("t0");
|
|
26499
|
+
el.ownerDocument.defaultView?.setTimeout(
|
|
26500
|
+
() => logResize("settle", () => snapshot("t200")),
|
|
26501
|
+
200
|
|
26502
|
+
);
|
|
26503
|
+
return current;
|
|
26504
|
+
});
|
|
26505
|
+
}
|
|
26506
|
+
|
|
26507
|
+
// src/utils/dragDebug.ts
|
|
26508
|
+
var logDrag = makeStudioDebugLogger("drag");
|
|
26509
|
+
var moveN2 = 0;
|
|
26510
|
+
function logDragMove(data) {
|
|
26511
|
+
logDrag("move", () => {
|
|
26512
|
+
moveN2 += 1;
|
|
26513
|
+
return moveN2 % 8 === 1 ? { n: moveN2, ...data } : null;
|
|
26514
|
+
});
|
|
26515
|
+
}
|
|
26516
|
+
function resetDragMoveLog() {
|
|
26517
|
+
moveN2 = 0;
|
|
26518
|
+
}
|
|
26519
|
+
function readDragPositions(elements) {
|
|
26520
|
+
const positions = {};
|
|
26521
|
+
for (const { key: key2, element } of elements) {
|
|
26522
|
+
const rect = element.getBoundingClientRect();
|
|
26523
|
+
positions[key2] = `${Math.round(rect.left)},${Math.round(rect.top)}`;
|
|
26524
|
+
}
|
|
26525
|
+
return positions;
|
|
26526
|
+
}
|
|
26527
|
+
function findNonRigidMembers(before, after) {
|
|
26528
|
+
const moves = /* @__PURE__ */ new Map();
|
|
26529
|
+
for (const key2 of Object.keys(after)) {
|
|
26530
|
+
const from = before[key2]?.split(",").map(Number);
|
|
26531
|
+
const to = after[key2]?.split(",").map(Number);
|
|
26532
|
+
if (!from || !to || from.length !== 2 || to.length !== 2) continue;
|
|
26533
|
+
moves.set(key2, `${Math.round(to[0] - from[0])},${Math.round(to[1] - from[1])}`);
|
|
26534
|
+
}
|
|
26535
|
+
const counts = /* @__PURE__ */ new Map();
|
|
26536
|
+
for (const move of moves.values()) counts.set(move, (counts.get(move) ?? 0) + 1);
|
|
26537
|
+
let common = "";
|
|
26538
|
+
let best = 0;
|
|
26539
|
+
for (const [move, count] of counts) {
|
|
26540
|
+
if (count > best) [common, best] = [move, count];
|
|
26541
|
+
}
|
|
26542
|
+
return [...moves].filter(([, move]) => move !== common).map(([key2, move]) => `${key2.split("|")[2] ?? key2} moved ${move}, group moved ${common}`);
|
|
26543
|
+
}
|
|
26544
|
+
function logDragSettle(stage, elements) {
|
|
26545
|
+
logDrag(stage, () => {
|
|
26546
|
+
const at = readDragPositions(elements);
|
|
26547
|
+
const win = elements[0]?.element.ownerDocument.defaultView;
|
|
26548
|
+
if (win) {
|
|
26549
|
+
win.setTimeout(
|
|
26550
|
+
() => logDrag(`${stage}+120ms`, () => ({ at: readDragPositions(elements) })),
|
|
26551
|
+
120
|
|
26552
|
+
);
|
|
26553
|
+
win.setTimeout(
|
|
26554
|
+
() => logDrag(`${stage}+400ms`, () => ({ at: readDragPositions(elements) })),
|
|
26555
|
+
400
|
|
26556
|
+
);
|
|
26557
|
+
win.setTimeout(
|
|
26558
|
+
() => logDrag(`${stage}+900ms`, () => ({ at: readDragPositions(elements) })),
|
|
26559
|
+
900
|
|
26560
|
+
);
|
|
26561
|
+
}
|
|
26562
|
+
return { at };
|
|
26563
|
+
});
|
|
26429
26564
|
}
|
|
26430
26565
|
|
|
26431
26566
|
// src/components/editor/domEditOverlayStartGesture.ts
|
|
@@ -26460,6 +26595,22 @@ function startGroupDrag(e, opts) {
|
|
|
26460
26595
|
}
|
|
26461
26596
|
members.push(result.member);
|
|
26462
26597
|
}
|
|
26598
|
+
resetDragMoveLog();
|
|
26599
|
+
logDrag("group-start", {
|
|
26600
|
+
// A member whose mapping differs from its neighbours travels a different
|
|
26601
|
+
// distance for the same pointer delta, which is the group coming apart.
|
|
26602
|
+
members: Object.fromEntries(
|
|
26603
|
+
members.map((member) => [
|
|
26604
|
+
member.key,
|
|
26605
|
+
{
|
|
26606
|
+
map: `${member.screenToOffset.a.toFixed(3)},${member.screenToOffset.d.toFixed(3)}`,
|
|
26607
|
+
base: `${Math.round(member.baseGsap.x)},${Math.round(member.baseGsap.y)}`,
|
|
26608
|
+
offset: `${Math.round(member.initialOffset.x)},${Math.round(member.initialOffset.y)}`
|
|
26609
|
+
}
|
|
26610
|
+
])
|
|
26611
|
+
),
|
|
26612
|
+
at: readDragPositions(members)
|
|
26613
|
+
});
|
|
26463
26614
|
const overlayEl = opts.overlayRef.current;
|
|
26464
26615
|
const iframe = opts.iframeRef.current;
|
|
26465
26616
|
const snapContext = overlayEl && iframe ? collectSnapContext({
|
|
@@ -26624,6 +26775,76 @@ function startGesture(kind, e, opts, options) {
|
|
|
26624
26775
|
return true;
|
|
26625
26776
|
}
|
|
26626
26777
|
|
|
26778
|
+
// src/components/editor/groupDragMove.ts
|
|
26779
|
+
function createGroupDragMover(opts, setDraftGroupOverlayItems) {
|
|
26780
|
+
let lastGroupPositions = {};
|
|
26781
|
+
let lastGesture = null;
|
|
26782
|
+
const snapGroupDelta = (groupG, e, proposed) => {
|
|
26783
|
+
const sc = groupG.snapContext;
|
|
26784
|
+
if (!sc?.snapEnabled || sc.targets.length === 0) return proposed;
|
|
26785
|
+
if (!snapEngagedForTravel(proposed.dx, proposed.dy)) {
|
|
26786
|
+
opts.snapGuidesRef.current = null;
|
|
26787
|
+
return proposed;
|
|
26788
|
+
}
|
|
26789
|
+
const groupBounds = resolveDomEditGroupOverlayRect(groupG.originItems.map((i) => i.rect));
|
|
26790
|
+
if (!groupBounds) return proposed;
|
|
26791
|
+
const allTargets = sc.compositionTarget ? [...sc.targets, sc.compositionTarget] : sc.targets;
|
|
26792
|
+
const snap = resolveSnapAdjustment({
|
|
26793
|
+
movingRect: groupBounds,
|
|
26794
|
+
proposedDx: proposed.dx,
|
|
26795
|
+
proposedDy: proposed.dy,
|
|
26796
|
+
targets: allTargets,
|
|
26797
|
+
gridEdges: sc.gridEdges ?? void 0,
|
|
26798
|
+
threshold: SNAP_THRESHOLD_PX,
|
|
26799
|
+
disabled: e.altKey
|
|
26800
|
+
});
|
|
26801
|
+
const movingRect = {
|
|
26802
|
+
...groupBounds,
|
|
26803
|
+
left: groupBounds.left + snap.dx,
|
|
26804
|
+
top: groupBounds.top + snap.dy
|
|
26805
|
+
};
|
|
26806
|
+
const spacingGuides = e.altKey ? [] : resolveEquidistanceGuides({
|
|
26807
|
+
movingRect,
|
|
26808
|
+
targets: allTargets,
|
|
26809
|
+
threshold: SNAP_THRESHOLD_PX
|
|
26810
|
+
});
|
|
26811
|
+
opts.snapGuidesRef.current = { guides: snap.guides, spacingGuides };
|
|
26812
|
+
return { dx: snap.dx, dy: snap.dy };
|
|
26813
|
+
};
|
|
26814
|
+
const moveGroupDrag = (groupG, e) => {
|
|
26815
|
+
if (groupG !== lastGesture) {
|
|
26816
|
+
lastGesture = groupG;
|
|
26817
|
+
lastGroupPositions = {};
|
|
26818
|
+
}
|
|
26819
|
+
const { dx, dy } = snapGroupDelta(groupG, e, {
|
|
26820
|
+
dx: e.clientX - groupG.startX,
|
|
26821
|
+
dy: e.clientY - groupG.startY
|
|
26822
|
+
});
|
|
26823
|
+
groupG.lastSnappedDx = dx;
|
|
26824
|
+
groupG.lastSnappedDy = dy;
|
|
26825
|
+
setDraftGroupOverlayItems(
|
|
26826
|
+
groupG.originItems.map((i) => ({
|
|
26827
|
+
...i,
|
|
26828
|
+
rect: { ...i.rect, left: i.rect.left + dx, top: i.rect.top + dy }
|
|
26829
|
+
}))
|
|
26830
|
+
);
|
|
26831
|
+
const offsets = {};
|
|
26832
|
+
for (const m of groupG.members) {
|
|
26833
|
+
const n = applyManualOffsetDragDraft(m, dx, dy);
|
|
26834
|
+
offsets[m.key] = `${Math.round(n.x)},${Math.round(n.y)}`;
|
|
26835
|
+
}
|
|
26836
|
+
const at = readDragPositions(groupG.members);
|
|
26837
|
+
const px = Math.round(e.clientX - groupG.startX);
|
|
26838
|
+
const py = Math.round(e.clientY - groupG.startY);
|
|
26839
|
+
const trace = { pointer: `${px},${py}`, applied: `${Math.round(dx)},${Math.round(dy)}`, at };
|
|
26840
|
+
const drift = findNonRigidMembers(lastGroupPositions, at);
|
|
26841
|
+
if (drift.length > 0) logDrag("drift", { ...trace, drift });
|
|
26842
|
+
lastGroupPositions = at;
|
|
26843
|
+
logDragMove({ ...trace, offsets });
|
|
26844
|
+
};
|
|
26845
|
+
return moveGroupDrag;
|
|
26846
|
+
}
|
|
26847
|
+
|
|
26627
26848
|
// src/components/editor/useDomEditOverlayGestures.ts
|
|
26628
26849
|
function createDomEditOverlayGestureHandlers(opts) {
|
|
26629
26850
|
const setDraftOverlayRect = (next) => {
|
|
@@ -26652,6 +26873,7 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
26652
26873
|
};
|
|
26653
26874
|
const startGroupDrag2 = (e) => startGroupDrag(e, opts);
|
|
26654
26875
|
const startGesture2 = (kind, e, options) => startGesture(kind, e, opts, options);
|
|
26876
|
+
const moveGroupDrag = createGroupDragMover(opts, setDraftGroupOverlayItems);
|
|
26655
26877
|
const onPointerMove = (e) => {
|
|
26656
26878
|
const g = opts.gestureRef.current;
|
|
26657
26879
|
const groupG = opts.groupGestureRef.current;
|
|
@@ -26672,49 +26894,7 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
26672
26894
|
return;
|
|
26673
26895
|
}
|
|
26674
26896
|
if (groupG) {
|
|
26675
|
-
|
|
26676
|
-
let dy2 = e.clientY - groupG.startY;
|
|
26677
|
-
const sc = groupG.snapContext;
|
|
26678
|
-
if (sc?.snapEnabled && sc.targets.length > 0) {
|
|
26679
|
-
const groupBounds = resolveDomEditGroupOverlayRect(
|
|
26680
|
-
groupG.originItems.map((item) => item.rect)
|
|
26681
|
-
);
|
|
26682
|
-
if (groupBounds) {
|
|
26683
|
-
const allTargets = sc.compositionTarget ? [...sc.targets, sc.compositionTarget] : sc.targets;
|
|
26684
|
-
const snap = resolveSnapAdjustment({
|
|
26685
|
-
movingRect: groupBounds,
|
|
26686
|
-
proposedDx: dx2,
|
|
26687
|
-
proposedDy: dy2,
|
|
26688
|
-
targets: allTargets,
|
|
26689
|
-
gridEdges: sc.gridEdges ?? void 0,
|
|
26690
|
-
threshold: SNAP_THRESHOLD_PX,
|
|
26691
|
-
disabled: e.altKey
|
|
26692
|
-
});
|
|
26693
|
-
dx2 = snap.dx;
|
|
26694
|
-
dy2 = snap.dy;
|
|
26695
|
-
const movedRect = {
|
|
26696
|
-
left: groupBounds.left + dx2,
|
|
26697
|
-
top: groupBounds.top + dy2,
|
|
26698
|
-
width: groupBounds.width,
|
|
26699
|
-
height: groupBounds.height
|
|
26700
|
-
};
|
|
26701
|
-
const spacingGuides = e.altKey ? [] : resolveEquidistanceGuides({
|
|
26702
|
-
movingRect: movedRect,
|
|
26703
|
-
targets: allTargets,
|
|
26704
|
-
threshold: SNAP_THRESHOLD_PX
|
|
26705
|
-
});
|
|
26706
|
-
opts.snapGuidesRef.current = { guides: snap.guides, spacingGuides };
|
|
26707
|
-
}
|
|
26708
|
-
}
|
|
26709
|
-
groupG.lastSnappedDx = dx2;
|
|
26710
|
-
groupG.lastSnappedDy = dy2;
|
|
26711
|
-
setDraftGroupOverlayItems(
|
|
26712
|
-
groupG.originItems.map((item) => ({
|
|
26713
|
-
...item,
|
|
26714
|
-
rect: { ...item.rect, left: item.rect.left + dx2, top: item.rect.top + dy2 }
|
|
26715
|
-
}))
|
|
26716
|
-
);
|
|
26717
|
-
for (const member of groupG.members) applyManualOffsetDragDraft(member, dx2, dy2);
|
|
26897
|
+
moveGroupDrag(groupG, e);
|
|
26718
26898
|
return;
|
|
26719
26899
|
}
|
|
26720
26900
|
if (!g || !sel) return;
|
|
@@ -26756,6 +26936,9 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
26756
26936
|
movingRect,
|
|
26757
26937
|
proposedDx: dx,
|
|
26758
26938
|
proposedDy: dy,
|
|
26939
|
+
// Same reason as the group path: a snap on a drag that has not travelled
|
|
26940
|
+
// yet moves the element while the pointer is still.
|
|
26941
|
+
disabledForTravel: !snapEngagedForTravel(dx, dy),
|
|
26759
26942
|
targets: allTargets,
|
|
26760
26943
|
gridEdges: sc.gridEdges ?? void 0,
|
|
26761
26944
|
threshold: SNAP_THRESHOLD_PX,
|
|
@@ -26840,9 +27023,9 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
26840
27023
|
opts.rafPausedRef.current = false;
|
|
26841
27024
|
const rawDx = e.clientX - groupG.startX;
|
|
26842
27025
|
const rawDy = e.clientY - groupG.startY;
|
|
27026
|
+
opts.suppressNextBoxClickRef.current = true;
|
|
26843
27027
|
if (Math.hypot(rawDx, rawDy) < BLOCKED_MOVE_THRESHOLD_PX) {
|
|
26844
27028
|
restoreGroupPathOffsets(groupG);
|
|
26845
|
-
opts.suppressNextBoxClickRef.current = true;
|
|
26846
27029
|
return;
|
|
26847
27030
|
}
|
|
26848
27031
|
const dx = groupG.lastSnappedDx ?? rawDx;
|
|
@@ -26857,12 +27040,27 @@ function createDomEditOverlayGestureHandlers(opts) {
|
|
|
26857
27040
|
selection: member.selection,
|
|
26858
27041
|
next: applyManualOffsetDragCommit(member, dx, dy)
|
|
26859
27042
|
}));
|
|
27043
|
+
logDrag("drop", {
|
|
27044
|
+
pointer: `${Math.round(rawDx)},${Math.round(rawDy)}`,
|
|
27045
|
+
applied: `${Math.round(dx)},${Math.round(dy)}`,
|
|
27046
|
+
committed: Object.fromEntries(
|
|
27047
|
+
updates.map((update, index) => [
|
|
27048
|
+
groupG.members[index]?.key ?? String(index),
|
|
27049
|
+
`${Math.round(update.next.x)},${Math.round(update.next.y)}`
|
|
27050
|
+
])
|
|
27051
|
+
),
|
|
27052
|
+
at: readDragPositions(groupG.members)
|
|
27053
|
+
});
|
|
26860
27054
|
void Promise.resolve(opts.onGroupPathOffsetCommitRef.current(updates)).catch(() => {
|
|
26861
27055
|
for (const member of groupG.members) {
|
|
26862
27056
|
if (member.gestureToken && isStudioManualEditGestureCurrent(member.element, member.gestureToken))
|
|
26863
27057
|
restoreStudioPathOffset(member.element, member.initialPathOffset);
|
|
26864
27058
|
}
|
|
26865
|
-
}).finally(() =>
|
|
27059
|
+
}).finally(() => {
|
|
27060
|
+
logDrag("committed", { at: readDragPositions(groupG.members) });
|
|
27061
|
+
endManualOffsetDragMembers(groupG.members);
|
|
27062
|
+
logDragSettle("settle", groupG.members);
|
|
27063
|
+
});
|
|
26866
27064
|
return;
|
|
26867
27065
|
}
|
|
26868
27066
|
if (!g || !sel) {
|
|
@@ -28194,18 +28392,18 @@ var CanvasContextMenu = memo21(function CanvasContextMenu2({
|
|
|
28194
28392
|
},
|
|
28195
28393
|
children: [
|
|
28196
28394
|
hasZActions && Z_ACTIONS.map(({ action, label }) => {
|
|
28197
|
-
const
|
|
28395
|
+
const enabled = isZOrderActionEnabled(el, action);
|
|
28198
28396
|
return /* @__PURE__ */ jsxs47(
|
|
28199
28397
|
"button",
|
|
28200
28398
|
{
|
|
28201
28399
|
type: "button",
|
|
28202
|
-
className: `w-full flex items-center px-3 py-1.5 text-xs text-left ${
|
|
28203
|
-
disabled: !
|
|
28400
|
+
className: `w-full flex items-center px-3 py-1.5 text-xs text-left ${enabled ? "text-neutral-300 hover:bg-neutral-800 cursor-pointer" : "text-neutral-600 cursor-not-allowed"}`,
|
|
28401
|
+
disabled: !enabled,
|
|
28204
28402
|
onPointerDown: (e) => {
|
|
28205
28403
|
if (e.button !== 0) return;
|
|
28206
28404
|
e.preventDefault();
|
|
28207
28405
|
e.stopPropagation();
|
|
28208
|
-
if (
|
|
28406
|
+
if (enabled) handleZAction(action);
|
|
28209
28407
|
},
|
|
28210
28408
|
children: [
|
|
28211
28409
|
/* @__PURE__ */ jsx63(ZActionIcon, { action }),
|
|
@@ -28418,6 +28616,7 @@ var DomEditOverlay = memo22(function DomEditOverlay2({
|
|
|
28418
28616
|
const handleOverlayMouseDown = (event) => {
|
|
28419
28617
|
if (!allowCanvasMovement) return;
|
|
28420
28618
|
if (suppressNextOverlayMouseDownRef.current) {
|
|
28619
|
+
logSelect("mousedown-suppressed", { shift: event.shiftKey });
|
|
28421
28620
|
suppressNextOverlayMouseDownRef.current = false;
|
|
28422
28621
|
suppressNextBoxMouseDownRef.current = false;
|
|
28423
28622
|
suppressNextBoxClickRef.current = false;
|
|
@@ -28426,7 +28625,9 @@ var DomEditOverlay = memo22(function DomEditOverlay2({
|
|
|
28426
28625
|
return;
|
|
28427
28626
|
}
|
|
28428
28627
|
const target = event.target;
|
|
28429
|
-
|
|
28628
|
+
const onBox = Boolean(target?.closest('[data-dom-edit-selection-box="true"]'));
|
|
28629
|
+
logSelect("mousedown", { shift: event.shiftKey, onBox });
|
|
28630
|
+
if (onBox) return;
|
|
28430
28631
|
onCanvasMouseDown(event, { hoverSelection: hoverSelectionRef.current });
|
|
28431
28632
|
if (event.shiftKey) {
|
|
28432
28633
|
suppressNextBoxMouseDownRef.current = true;
|
|
@@ -28436,7 +28637,16 @@ var DomEditOverlay = memo22(function DomEditOverlay2({
|
|
|
28436
28637
|
const handleOverlayPointerDown = (event) => {
|
|
28437
28638
|
if (!allowCanvasMovement || event.button !== 0) return;
|
|
28438
28639
|
if (event.shiftKey) {
|
|
28439
|
-
const
|
|
28640
|
+
const shiftIframe = iframeRef.current;
|
|
28641
|
+
const candidate = resolveShiftClickCandidate({
|
|
28642
|
+
cached: hoverSelectionRef.current,
|
|
28643
|
+
elementAtPoint: shiftIframe ? getPreviewTargetFromPointer(
|
|
28644
|
+
shiftIframe,
|
|
28645
|
+
event.clientX,
|
|
28646
|
+
event.clientY,
|
|
28647
|
+
activeCompositionPathRef.current
|
|
28648
|
+
) : null
|
|
28649
|
+
});
|
|
28440
28650
|
if (!candidate) return;
|
|
28441
28651
|
event.preventDefault();
|
|
28442
28652
|
event.stopPropagation();
|
|
@@ -28460,24 +28670,21 @@ var DomEditOverlay = memo22(function DomEditOverlay2({
|
|
|
28460
28670
|
const overlayEl = overlayRef.current;
|
|
28461
28671
|
if (overlayEl) {
|
|
28462
28672
|
const oRect = overlayEl.getBoundingClientRect();
|
|
28673
|
+
event.preventDefault();
|
|
28674
|
+
event.stopPropagation();
|
|
28675
|
+
suppressNextOverlayMouseDownRef.current = true;
|
|
28676
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
28463
28677
|
const cx = event.clientX - oRect.left;
|
|
28464
28678
|
const cy = event.clientY - oRect.top;
|
|
28465
|
-
|
|
28466
|
-
|
|
28467
|
-
|
|
28468
|
-
|
|
28469
|
-
|
|
28470
|
-
event.
|
|
28471
|
-
|
|
28472
|
-
|
|
28473
|
-
|
|
28474
|
-
currentX: cx,
|
|
28475
|
-
currentY: cy,
|
|
28476
|
-
pointerId: event.pointerId,
|
|
28477
|
-
pastThreshold: false
|
|
28478
|
-
};
|
|
28479
|
-
return;
|
|
28480
|
-
}
|
|
28679
|
+
marquee.marqueeRef.current = {
|
|
28680
|
+
startX: cx,
|
|
28681
|
+
startY: cy,
|
|
28682
|
+
currentX: cx,
|
|
28683
|
+
currentY: cy,
|
|
28684
|
+
pointerId: event.pointerId,
|
|
28685
|
+
pastThreshold: false
|
|
28686
|
+
};
|
|
28687
|
+
return;
|
|
28481
28688
|
}
|
|
28482
28689
|
}
|
|
28483
28690
|
};
|
|
@@ -28575,7 +28782,8 @@ var DomEditOverlay = memo22(function DomEditOverlay2({
|
|
|
28575
28782
|
left: cr.left,
|
|
28576
28783
|
top: cr.top,
|
|
28577
28784
|
width: cr.width,
|
|
28578
|
-
height: cr.height
|
|
28785
|
+
height: cr.height,
|
|
28786
|
+
transform: cr.angle ? `rotate(${cr.angle}deg)` : void 0
|
|
28579
28787
|
}
|
|
28580
28788
|
},
|
|
28581
28789
|
i
|
|
@@ -28862,18 +29070,16 @@ async function dispatchBufferedCommits(calls) {
|
|
|
28862
29070
|
}
|
|
28863
29071
|
return reloadsRequested(calls);
|
|
28864
29072
|
}
|
|
28865
|
-
|
|
28866
|
-
if (import.meta.env.DEV) console.info(`[hf-commit] ${stage}`, data);
|
|
28867
|
-
}
|
|
29073
|
+
var logCommit = makeStudioDebugLogger("commit");
|
|
28868
29074
|
function runGestureTransaction(tx) {
|
|
28869
29075
|
const startedAt = performance.now();
|
|
28870
29076
|
const coalesceKey = `tx:${tx.label}:${++transactionCounter}`;
|
|
28871
29077
|
let mutationCount = 0;
|
|
28872
29078
|
let reloadCount = 0;
|
|
28873
29079
|
const bufferedCommits = [];
|
|
28874
|
-
|
|
29080
|
+
logCommit("start", { label: tx.label, coalesceKey });
|
|
28875
29081
|
tx.settle();
|
|
28876
|
-
|
|
29082
|
+
logCommit("settled", { label: tx.label, coalesceKey });
|
|
28877
29083
|
const before = !tx.skipPixelAssert ? readPixelRect(tx.element) : null;
|
|
28878
29084
|
const commit = (commitMutation) => {
|
|
28879
29085
|
const wrapped = (selection, mutation, options) => {
|
|
@@ -28892,19 +29098,12 @@ function runGestureTransaction(tx) {
|
|
|
28892
29098
|
return tx.persist(commit).then(async () => {
|
|
28893
29099
|
reloadCount = await dispatchBufferedCommits(bufferedCommits);
|
|
28894
29100
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
28895
|
-
|
|
29101
|
+
logCommit("persisted", { label: tx.label, coalesceKey });
|
|
28896
29102
|
if (before) {
|
|
28897
29103
|
const after = readPixelRect(tx.element);
|
|
28898
29104
|
const delta = pixelDelta(before, after);
|
|
28899
29105
|
if (exceedsPixelTolerance(delta)) {
|
|
28900
|
-
|
|
28901
|
-
console.error("[hf-commit] persist changed pixels", {
|
|
28902
|
-
label: tx.label,
|
|
28903
|
-
before,
|
|
28904
|
-
after,
|
|
28905
|
-
delta
|
|
28906
|
-
});
|
|
28907
|
-
}
|
|
29106
|
+
logCommit("persist-changed-pixels", { label: tx.label, before, after, delta });
|
|
28908
29107
|
trackStudioEvent("commit_invariant_violation", {
|
|
28909
29108
|
label: tx.label,
|
|
28910
29109
|
delta_x: roundToOneDecimal(delta.x),
|
|
@@ -28932,7 +29131,7 @@ function runGestureTransaction(tx) {
|
|
|
28932
29131
|
error_name: error instanceof Error ? error.name : "unknown",
|
|
28933
29132
|
restore_ran: true
|
|
28934
29133
|
});
|
|
28935
|
-
|
|
29134
|
+
logCommit("restore", { label: tx.label, coalesceKey });
|
|
28936
29135
|
throw error;
|
|
28937
29136
|
});
|
|
28938
29137
|
}
|
|
@@ -31867,7 +32066,7 @@ function useElementLifecycleOps({
|
|
|
31867
32066
|
`/api/projects/${pid}/file-mutations/remove-element/${encodeURIComponent(targetPath)}`,
|
|
31868
32067
|
{
|
|
31869
32068
|
method: "POST",
|
|
31870
|
-
headers: { "Content-Type": "application/json" },
|
|
32069
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
31871
32070
|
body: JSON.stringify({ target: patchTarget })
|
|
31872
32071
|
}
|
|
31873
32072
|
);
|
|
@@ -33285,7 +33484,19 @@ import { isStudioHoldSet } from "@hyperframes/core/gsap-parser";
|
|
|
33285
33484
|
function hasAnimations(value) {
|
|
33286
33485
|
return typeof value === "object" && value !== null && "animations" in value && Array.isArray(value.animations);
|
|
33287
33486
|
}
|
|
33288
|
-
|
|
33487
|
+
var inFlightParses = /* @__PURE__ */ new Map();
|
|
33488
|
+
function fetchParsedAnimations(projectId, sourceFile, options = {}) {
|
|
33489
|
+
const key2 = `${projectId}|${sourceFile}`;
|
|
33490
|
+
if (options.fresh) inFlightParses.delete(key2);
|
|
33491
|
+
const inFlight = inFlightParses.get(key2);
|
|
33492
|
+
if (inFlight) return inFlight;
|
|
33493
|
+
const request = requestParsedAnimations(projectId, sourceFile).finally(() => {
|
|
33494
|
+
if (inFlightParses.get(key2) === request) inFlightParses.delete(key2);
|
|
33495
|
+
});
|
|
33496
|
+
inFlightParses.set(key2, request);
|
|
33497
|
+
return request;
|
|
33498
|
+
}
|
|
33499
|
+
async function requestParsedAnimations(projectId, sourceFile) {
|
|
33289
33500
|
try {
|
|
33290
33501
|
const res = await fetch(
|
|
33291
33502
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-animations/${encodeURIComponent(sourceFile)}`,
|
|
@@ -34317,6 +34528,11 @@ function useTimelineSelectionPreviewSync({
|
|
|
34317
34528
|
const currentAnchor = currentDomEditSelection ? resolveTimelineIdForSelection(currentDomEditSelection, timelineElements, activeCompPath) : null;
|
|
34318
34529
|
if (selectedIds.length === 0) {
|
|
34319
34530
|
missingSelectionKeyRef.current = "";
|
|
34531
|
+
logSelect("timeline-empty", {
|
|
34532
|
+
had: currentIds.length,
|
|
34533
|
+
previousKey: previousSelectedKey.length > 0,
|
|
34534
|
+
clearing: previousSelectedKey.length > 0 && currentIds.length > 0
|
|
34535
|
+
});
|
|
34320
34536
|
if (previousSelectedKey.length > 0 && currentIds.length > 0) {
|
|
34321
34537
|
applyDomSelection(null, { revealPanel: false });
|
|
34322
34538
|
}
|
|
@@ -34346,6 +34562,11 @@ function useTimelineSelectionPreviewSync({
|
|
|
34346
34562
|
return;
|
|
34347
34563
|
}
|
|
34348
34564
|
missingSelectionKeyRef.current = "";
|
|
34565
|
+
logSelect("timeline-sync", {
|
|
34566
|
+
wanted: selectedIds.length,
|
|
34567
|
+
had: currentIds.length,
|
|
34568
|
+
resolved: selections.length
|
|
34569
|
+
});
|
|
34349
34570
|
if (selections.length === 0) {
|
|
34350
34571
|
applyDomSelection(null, { revealPanel: false });
|
|
34351
34572
|
} else if (selections.length === 1) {
|
|
@@ -37799,13 +38020,13 @@ function useColorGradingController({
|
|
|
37799
38020
|
postColorGrading
|
|
37800
38021
|
});
|
|
37801
38022
|
const postCompare = useCallback57(
|
|
37802
|
-
(
|
|
38023
|
+
(enabled) => {
|
|
37803
38024
|
postRuntimeControlMessage(
|
|
37804
38025
|
previewIframeRef?.current?.contentWindow,
|
|
37805
38026
|
"set-color-grading-compare",
|
|
37806
38027
|
{
|
|
37807
38028
|
target,
|
|
37808
|
-
compare: { enabled
|
|
38029
|
+
compare: { enabled, position: 1, lineWidth: 0 }
|
|
37809
38030
|
}
|
|
37810
38031
|
);
|
|
37811
38032
|
},
|
|
@@ -37886,8 +38107,8 @@ function useColorGradingController({
|
|
|
37886
38107
|
]
|
|
37887
38108
|
);
|
|
37888
38109
|
const commitCompare = useCallback57(
|
|
37889
|
-
(
|
|
37890
|
-
const nextEnabled =
|
|
38110
|
+
(enabled) => {
|
|
38111
|
+
const nextEnabled = enabled && isHfColorGradingActive(grading);
|
|
37891
38112
|
setCompareEnabled(nextEnabled);
|
|
37892
38113
|
if (nextEnabled) postColorGrading(grading);
|
|
37893
38114
|
postCompare(nextEnabled);
|
|
@@ -39572,11 +39793,11 @@ function useVariablePromoteChannel(channel) {
|
|
|
39572
39793
|
import { Fragment as Fragment25, jsx as jsx86, jsxs as jsxs66 } from "react/jsx-runtime";
|
|
39573
39794
|
function PromotableControl({
|
|
39574
39795
|
channel,
|
|
39575
|
-
enabled
|
|
39796
|
+
enabled = true,
|
|
39576
39797
|
children
|
|
39577
39798
|
}) {
|
|
39578
39799
|
const promote = useVariablePromoteChannel(channel);
|
|
39579
|
-
const danglingId =
|
|
39800
|
+
const danglingId = enabled && promote && promote.boundId != null && promote.declaration == null ? promote.boundId : null;
|
|
39580
39801
|
useEffect51(() => {
|
|
39581
39802
|
if (danglingId != null) {
|
|
39582
39803
|
console.warn(
|
|
@@ -39584,7 +39805,7 @@ function PromotableControl({
|
|
|
39584
39805
|
);
|
|
39585
39806
|
}
|
|
39586
39807
|
}, [danglingId]);
|
|
39587
|
-
if (!promote || !
|
|
39808
|
+
if (!promote || !enabled) return /* @__PURE__ */ jsx86(Fragment25, { children: children({ bound: false }) });
|
|
39588
39809
|
const bound = promote.boundId != null && promote.declaration != null;
|
|
39589
39810
|
const canPromote = promote.action != null && !bound;
|
|
39590
39811
|
const defaultValue = promote.declaration?.default;
|
|
@@ -42301,8 +42522,8 @@ var AnimationCard = memo29(function AnimationCard2({
|
|
|
42301
42522
|
animation.properties.x != null || animation.properties.y != null ? 1 : 0,
|
|
42302
42523
|
(animation.keyframes?.keyframes?.length ?? 0) - 1
|
|
42303
42524
|
),
|
|
42304
|
-
onToggle: (
|
|
42305
|
-
enabled
|
|
42525
|
+
onToggle: (enabled) => onSetArcPath(animation.id, {
|
|
42526
|
+
enabled,
|
|
42306
42527
|
segments: animation.arcPath?.segments
|
|
42307
42528
|
}),
|
|
42308
42529
|
onUpdateSegment: (index, update) => onUpdateArcSegment?.(animation.id, index, update),
|
|
@@ -46775,7 +46996,7 @@ function PropertyPanelColorSecondary({
|
|
|
46775
46996
|
{
|
|
46776
46997
|
label: "Selection enabled",
|
|
46777
46998
|
checked: selected.enabled,
|
|
46778
|
-
onChange: (
|
|
46999
|
+
onChange: (enabled) => replaceSelected({ ...selected, enabled })
|
|
46779
47000
|
}
|
|
46780
47001
|
),
|
|
46781
47002
|
/* @__PURE__ */ jsxs97(
|
|
@@ -51854,7 +52075,7 @@ function useCaptionSync(projectId) {
|
|
|
51854
52075
|
const overrides = buildOverrides(state.model);
|
|
51855
52076
|
fetch(`/api/projects/${pid}/files/${encodeURIComponent("caption-overrides.json")}`, {
|
|
51856
52077
|
method: "PUT",
|
|
51857
|
-
headers: { "Content-Type": "text/plain" },
|
|
52078
|
+
headers: { "Content-Type": "text/plain", ...studioWriteHeaders() },
|
|
51858
52079
|
body: JSON.stringify(overrides, null, 2)
|
|
51859
52080
|
}).catch((error) => {
|
|
51860
52081
|
trackEvent("studio_caption_autosave_failed", { error: String(error) });
|
|
@@ -52548,40 +52769,6 @@ function usePanelLayout(initialState2) {
|
|
|
52548
52769
|
// src/hooks/useFileManager.ts
|
|
52549
52770
|
import { useState as useState84, useCallback as useCallback72, useMemo as useMemo49, useRef as useRef92 } from "react";
|
|
52550
52771
|
|
|
52551
|
-
// src/utils/studioFileVersion.ts
|
|
52552
|
-
var WRITE_TOKEN_TTL_MS = 5 * 6e4;
|
|
52553
|
-
var studioWriteTokens = /* @__PURE__ */ new Map();
|
|
52554
|
-
function pruneStudioWriteTokens(now2) {
|
|
52555
|
-
for (const [token, createdAt] of studioWriteTokens) {
|
|
52556
|
-
if (now2 - createdAt >= WRITE_TOKEN_TTL_MS) studioWriteTokens.delete(token);
|
|
52557
|
-
}
|
|
52558
|
-
}
|
|
52559
|
-
function markStudioWriteToken(token, now2 = Date.now()) {
|
|
52560
|
-
pruneStudioWriteTokens(now2);
|
|
52561
|
-
studioWriteTokens.set(token, now2);
|
|
52562
|
-
}
|
|
52563
|
-
function consumeStudioWriteToken(token, now2 = Date.now()) {
|
|
52564
|
-
pruneStudioWriteTokens(now2);
|
|
52565
|
-
if (!token || !studioWriteTokens.has(token)) return false;
|
|
52566
|
-
studioWriteTokens.delete(token);
|
|
52567
|
-
return true;
|
|
52568
|
-
}
|
|
52569
|
-
async function studioFileContentVersion(content) {
|
|
52570
|
-
const bytes = new TextEncoder().encode(content);
|
|
52571
|
-
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
52572
|
-
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
52573
|
-
""
|
|
52574
|
-
);
|
|
52575
|
-
return `"sha256:${hex}"`;
|
|
52576
|
-
}
|
|
52577
|
-
async function studioExpectedFileVersion(versions, path, expectedContent) {
|
|
52578
|
-
if (expectedContent !== void 0) return studioFileContentVersion(expectedContent);
|
|
52579
|
-
return versions.get(path);
|
|
52580
|
-
}
|
|
52581
|
-
function createStudioWriteToken() {
|
|
52582
|
-
return globalThis.crypto.randomUUID();
|
|
52583
|
-
}
|
|
52584
|
-
|
|
52585
52772
|
// src/hooks/useFileTree.ts
|
|
52586
52773
|
import { useState as useState83, useCallback as useCallback70, useEffect as useEffect75, useMemo as useMemo48 } from "react";
|
|
52587
52774
|
function useFileTree({ projectId, projectIdRef }) {
|
|
@@ -52827,8 +53014,6 @@ function useFileManager({
|
|
|
52827
53014
|
}
|
|
52828
53015
|
}
|
|
52829
53016
|
await retryStudioSave(async () => {
|
|
52830
|
-
const writeToken = createStudioWriteToken();
|
|
52831
|
-
markStudioWriteToken(writeToken);
|
|
52832
53017
|
let response;
|
|
52833
53018
|
try {
|
|
52834
53019
|
response = await fetch(
|
|
@@ -52837,7 +53022,7 @@ function useFileManager({
|
|
|
52837
53022
|
method: "PUT",
|
|
52838
53023
|
headers: {
|
|
52839
53024
|
"Content-Type": "text/plain",
|
|
52840
|
-
|
|
53025
|
+
...studioWriteHeaders(),
|
|
52841
53026
|
...expectedVersion ? { "If-Match": expectedVersion } : { "If-None-Match": "*" }
|
|
52842
53027
|
},
|
|
52843
53028
|
body: content
|
|
@@ -53704,6 +53889,7 @@ async function requestAtomicCut(projectId, intents) {
|
|
|
53704
53889
|
});
|
|
53705
53890
|
}
|
|
53706
53891
|
const transactionToken = `cut:${crypto.randomUUID()}`;
|
|
53892
|
+
markStudioWriteToken(transactionToken);
|
|
53707
53893
|
const response = await fetch(buildProjectApiPath(projectId, "/file-mutations/split-batch"), {
|
|
53708
53894
|
method: "POST",
|
|
53709
53895
|
headers: {
|
|
@@ -53900,7 +54086,7 @@ async function insertTimelineComposition(input) {
|
|
|
53900
54086
|
),
|
|
53901
54087
|
{
|
|
53902
54088
|
method: "POST",
|
|
53903
|
-
headers: { "Content-Type": "application/json" },
|
|
54089
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
53904
54090
|
body: JSON.stringify({
|
|
53905
54091
|
sourcePath: input.sourcePath,
|
|
53906
54092
|
start: input.start,
|
|
@@ -55007,7 +55193,7 @@ function useTimelineEditing({
|
|
|
55007
55193
|
`/api/projects/${pid}/file-mutations/remove-element/${encodeURIComponent(targetPath)}`,
|
|
55008
55194
|
{
|
|
55009
55195
|
method: "POST",
|
|
55010
|
-
headers: { "Content-Type": "application/json" },
|
|
55196
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
55011
55197
|
body: JSON.stringify({ target: patchTarget })
|
|
55012
55198
|
}
|
|
55013
55199
|
);
|
|
@@ -55407,6 +55593,42 @@ function useStudioTestHooks({
|
|
|
55407
55593
|
}, [applyDomSelection, buildDomSelectionFromTarget, previewIframeRef]);
|
|
55408
55594
|
}
|
|
55409
55595
|
|
|
55596
|
+
// src/hooks/domSelectionTimelineMirror.ts
|
|
55597
|
+
function announceTimelineSelection(deps, group, primary) {
|
|
55598
|
+
const {
|
|
55599
|
+
timelineElements,
|
|
55600
|
+
getTimelineSelectionSet: getTimelineSelectionSet2,
|
|
55601
|
+
setSelectedTimelineElementId,
|
|
55602
|
+
setTimelineSelectionSet
|
|
55603
|
+
} = deps;
|
|
55604
|
+
if (!primary) {
|
|
55605
|
+
setTimelineSelectionSet(/* @__PURE__ */ new Set());
|
|
55606
|
+
setSelectedTimelineElementId(null);
|
|
55607
|
+
return;
|
|
55608
|
+
}
|
|
55609
|
+
const timelineIdFor = (selection) => findMatchingTimelineElementId(selection, timelineElements) ?? findTimelineIdByAncestor(
|
|
55610
|
+
selection.element,
|
|
55611
|
+
timelineElements,
|
|
55612
|
+
selection.sourceFile || "index.html"
|
|
55613
|
+
);
|
|
55614
|
+
const members = group.map(timelineIdFor).filter((id) => Boolean(id));
|
|
55615
|
+
const anchor = timelineIdFor(primary);
|
|
55616
|
+
const publishedMembers = new Set(members);
|
|
55617
|
+
if (anchor) publishedMembers.add(anchor);
|
|
55618
|
+
const timelineAnchor = anchor ?? members[0] ?? null;
|
|
55619
|
+
logSelect("announce", {
|
|
55620
|
+
group: group.length,
|
|
55621
|
+
published: publishedMembers.size,
|
|
55622
|
+
anchor,
|
|
55623
|
+
anchorPublished: anchor != null && publishedMembers.has(anchor)
|
|
55624
|
+
});
|
|
55625
|
+
if (!timelineAnchor) return;
|
|
55626
|
+
if (group.length > 1 || !getTimelineSelectionSet2().has(timelineAnchor)) {
|
|
55627
|
+
setTimelineSelectionSet(publishedMembers);
|
|
55628
|
+
}
|
|
55629
|
+
setSelectedTimelineElementId(timelineAnchor, { preserveSet: true });
|
|
55630
|
+
}
|
|
55631
|
+
|
|
55410
55632
|
// src/hooks/useDomSelection.ts
|
|
55411
55633
|
function useDomSelection({
|
|
55412
55634
|
projectId,
|
|
@@ -55416,7 +55638,9 @@ function useDomSelection({
|
|
|
55416
55638
|
captionEditMode,
|
|
55417
55639
|
previewIframeRef,
|
|
55418
55640
|
timelineElements,
|
|
55641
|
+
getTimelineSelectionSet: getTimelineSelectionSet2,
|
|
55419
55642
|
setSelectedTimelineElementId,
|
|
55643
|
+
setTimelineSelectionSet,
|
|
55420
55644
|
setRightCollapsed,
|
|
55421
55645
|
setRightPanelTab,
|
|
55422
55646
|
previewIframe,
|
|
@@ -55439,15 +55663,34 @@ function useDomSelection({
|
|
|
55439
55663
|
domEditGroupSelectionsRef.current = domEditGroupSelections;
|
|
55440
55664
|
domEditHoverSelectionRef.current = domEditHoverSelection;
|
|
55441
55665
|
activeGroupElementRef.current = activeGroupElement;
|
|
55666
|
+
const announceTimelineSelection2 = useCallback81(
|
|
55667
|
+
(group, primary) => announceTimelineSelection(
|
|
55668
|
+
{
|
|
55669
|
+
timelineElements,
|
|
55670
|
+
getTimelineSelectionSet: getTimelineSelectionSet2,
|
|
55671
|
+
setSelectedTimelineElementId,
|
|
55672
|
+
setTimelineSelectionSet
|
|
55673
|
+
},
|
|
55674
|
+
group,
|
|
55675
|
+
primary
|
|
55676
|
+
),
|
|
55677
|
+
[
|
|
55678
|
+
getTimelineSelectionSet2,
|
|
55679
|
+
setSelectedTimelineElementId,
|
|
55680
|
+
setTimelineSelectionSet,
|
|
55681
|
+
timelineElements
|
|
55682
|
+
]
|
|
55683
|
+
);
|
|
55442
55684
|
const applyDomSelection = useCallback81(
|
|
55443
55685
|
// fallow-ignore-next-line complexity
|
|
55444
55686
|
(selection, options) => {
|
|
55445
55687
|
if (!selection) {
|
|
55688
|
+
logSelect("clear", { hadGroup: domEditGroupSelectionsRef.current.length });
|
|
55446
55689
|
domEditSelectionRef.current = null;
|
|
55447
55690
|
domEditGroupSelectionsRef.current = [];
|
|
55448
55691
|
setDomEditSelection(null);
|
|
55449
55692
|
setDomEditGroupSelections([]);
|
|
55450
|
-
|
|
55693
|
+
announceTimelineSelection2([], null);
|
|
55451
55694
|
return;
|
|
55452
55695
|
}
|
|
55453
55696
|
const isAdditiveSelection = Boolean(options?.additive);
|
|
@@ -55457,6 +55700,13 @@ function useDomSelection({
|
|
|
55457
55700
|
const wasInGroup = domEditSelectionInGroup(currentGroup, selection);
|
|
55458
55701
|
const nextGroup = options?.preserveGroup ? replaceDomEditGroupSelection(currentGroup, selection) : isAdditiveSelection ? toggleDomEditGroupSelection(currentGroup, selection) : [selection];
|
|
55459
55702
|
const nextSelection = options?.preserveGroup ? selection : isAdditiveSelection && wasInGroup ? domEditSelectionsTargetSame(currentSelection, selection) ? nextGroup[0] ?? null : domEditSelectionInGroup(nextGroup, currentSelection) ? currentSelection : nextGroup[0] ?? null : selection;
|
|
55703
|
+
logSelect("apply", {
|
|
55704
|
+
additive: isAdditiveSelection,
|
|
55705
|
+
target: selection.selector ?? selection.id ?? null,
|
|
55706
|
+
wasInGroup,
|
|
55707
|
+
prevGroup: previousGroup.length,
|
|
55708
|
+
nextGroup: nextGroup.length
|
|
55709
|
+
});
|
|
55460
55710
|
domEditSelectionRef.current = nextSelection;
|
|
55461
55711
|
domEditGroupSelectionsRef.current = nextGroup;
|
|
55462
55712
|
setDomEditSelection(nextSelection);
|
|
@@ -55473,17 +55723,12 @@ function useDomSelection({
|
|
|
55473
55723
|
setRightPanelTab("design");
|
|
55474
55724
|
}
|
|
55475
55725
|
}
|
|
55476
|
-
|
|
55477
|
-
nextSelection.element,
|
|
55478
|
-
timelineElements,
|
|
55479
|
-
nextSelection.sourceFile || "index.html"
|
|
55480
|
-
);
|
|
55481
|
-
setSelectedTimelineElementId(nextSelectedTimelineId, { preserveSet: true });
|
|
55726
|
+
announceTimelineSelection2(nextGroup, nextSelection);
|
|
55482
55727
|
return;
|
|
55483
55728
|
}
|
|
55484
|
-
|
|
55729
|
+
announceTimelineSelection2([], null);
|
|
55485
55730
|
},
|
|
55486
|
-
[
|
|
55731
|
+
[announceTimelineSelection2, setRightCollapsed, setRightPanelTab]
|
|
55487
55732
|
);
|
|
55488
55733
|
const clearDomSelection = useCallback81(() => {
|
|
55489
55734
|
applyDomSelection(null, { revealPanel: false });
|
|
@@ -55595,6 +55840,8 @@ function useDomSelection({
|
|
|
55595
55840
|
},
|
|
55596
55841
|
[applyDomSelection, buildDomSelectionForTimelineElement]
|
|
55597
55842
|
);
|
|
55843
|
+
const refreshDomEditGroupSelectionsFromPreviewRef = useRef98(async () => {
|
|
55844
|
+
});
|
|
55598
55845
|
const refreshDomEditSelectionFromPreview = useCallback81(
|
|
55599
55846
|
// fallow-ignore-next-line complexity
|
|
55600
55847
|
async (selection) => {
|
|
@@ -55608,6 +55855,15 @@ function useDomSelection({
|
|
|
55608
55855
|
if (!doc) return;
|
|
55609
55856
|
const element = findElementForSelection(doc, selection, activeCompPath);
|
|
55610
55857
|
if (!element) {
|
|
55858
|
+
const group = domEditGroupSelectionsRef.current;
|
|
55859
|
+
logSelect("refresh-lost", {
|
|
55860
|
+
target: selection.selector ?? selection.id ?? null,
|
|
55861
|
+
group: group.length
|
|
55862
|
+
});
|
|
55863
|
+
if (group.length > 1) {
|
|
55864
|
+
await refreshDomEditGroupSelectionsFromPreviewRef.current(group);
|
|
55865
|
+
return;
|
|
55866
|
+
}
|
|
55611
55867
|
applyDomSelection(null, { revealPanel: false });
|
|
55612
55868
|
return;
|
|
55613
55869
|
}
|
|
@@ -55646,22 +55902,13 @@ function useDomSelection({
|
|
|
55646
55902
|
domEditGroupSelectionsRef.current = nextGroup;
|
|
55647
55903
|
setDomEditSelection(nextSelection);
|
|
55648
55904
|
setDomEditGroupSelections(nextGroup);
|
|
55649
|
-
|
|
55650
|
-
setSelectedTimelineElementId(
|
|
55651
|
-
findMatchingTimelineElementId(nextSelection, timelineElements)
|
|
55652
|
-
);
|
|
55653
|
-
} else {
|
|
55654
|
-
setSelectedTimelineElementId(null);
|
|
55655
|
-
}
|
|
55905
|
+
announceTimelineSelection2(nextGroup, nextSelection);
|
|
55656
55906
|
},
|
|
55657
|
-
[
|
|
55658
|
-
activeCompPath,
|
|
55659
|
-
buildDomSelectionFromTarget,
|
|
55660
|
-
setSelectedTimelineElementId,
|
|
55661
|
-
timelineElements,
|
|
55662
|
-
previewIframeRef
|
|
55663
|
-
]
|
|
55907
|
+
[activeCompPath, announceTimelineSelection2, buildDomSelectionFromTarget, previewIframeRef]
|
|
55664
55908
|
);
|
|
55909
|
+
useEffect79(() => {
|
|
55910
|
+
refreshDomEditGroupSelectionsFromPreviewRef.current = refreshDomEditGroupSelectionsFromPreview;
|
|
55911
|
+
}, [refreshDomEditGroupSelectionsFromPreview]);
|
|
55665
55912
|
useEffect79(() => {
|
|
55666
55913
|
updateDomEditHoverSelection(null);
|
|
55667
55914
|
}, [activeCompPath, projectId, previewIframe, refreshKey, updateDomEditHoverSelection]);
|
|
@@ -55692,6 +55939,7 @@ function useDomSelection({
|
|
|
55692
55939
|
const applyMarqueeSelection = useCallback81(
|
|
55693
55940
|
// fallow-ignore-next-line complexity
|
|
55694
55941
|
(selections, additive) => {
|
|
55942
|
+
logSelect("marquee", { hits: selections.length, additive });
|
|
55695
55943
|
if (selections.length === 0) {
|
|
55696
55944
|
if (!additive) applyDomSelection(null, { revealPanel: false });
|
|
55697
55945
|
return;
|
|
@@ -55715,14 +55963,9 @@ function useDomSelection({
|
|
|
55715
55963
|
domEditGroupSelectionsRef.current = nextGroup;
|
|
55716
55964
|
setDomEditSelection(nextSelection);
|
|
55717
55965
|
setDomEditGroupSelections(nextGroup);
|
|
55718
|
-
|
|
55719
|
-
nextSelection.element,
|
|
55720
|
-
timelineElements,
|
|
55721
|
-
nextSelection.sourceFile || "index.html"
|
|
55722
|
-
);
|
|
55723
|
-
setSelectedTimelineElementId(nextTimelineId);
|
|
55966
|
+
announceTimelineSelection2(nextGroup, nextSelection);
|
|
55724
55967
|
},
|
|
55725
|
-
[applyDomSelection,
|
|
55968
|
+
[applyDomSelection, announceTimelineSelection2]
|
|
55726
55969
|
);
|
|
55727
55970
|
return {
|
|
55728
55971
|
// State
|
|
@@ -57132,7 +57375,7 @@ async function assignGsapTargetAutoIdIfNeeded({
|
|
|
57132
57375
|
`/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element/${encodeURIComponent(targetPath)}`,
|
|
57133
57376
|
{
|
|
57134
57377
|
method: "POST",
|
|
57135
|
-
headers: { "Content-Type": "application/json" },
|
|
57378
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
57136
57379
|
body: JSON.stringify(patchBody)
|
|
57137
57380
|
}
|
|
57138
57381
|
);
|
|
@@ -57198,7 +57441,7 @@ async function patchElementBatches(projectId, batches) {
|
|
|
57198
57441
|
`/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element-batches`,
|
|
57199
57442
|
{
|
|
57200
57443
|
method: "POST",
|
|
57201
|
-
headers: { "Content-Type": "application/json" },
|
|
57444
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
57202
57445
|
body
|
|
57203
57446
|
}
|
|
57204
57447
|
);
|
|
@@ -57323,7 +57566,7 @@ function useDomEditCommits({
|
|
|
57323
57566
|
`/api/projects/${pid}/file-mutations/patch-element/${encodeURIComponent(targetPath)}`,
|
|
57324
57567
|
{
|
|
57325
57568
|
method: "POST",
|
|
57326
|
-
headers: { "Content-Type": "application/json" },
|
|
57569
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
57327
57570
|
body: JSON.stringify(patchBody)
|
|
57328
57571
|
}
|
|
57329
57572
|
);
|
|
@@ -57556,7 +57799,7 @@ async function commitStructuralMutation(pid, targetPath, route, body, label, dep
|
|
|
57556
57799
|
`/api/projects/${pid}/file-mutations/${route}/${encodeURIComponent(targetPath)}`,
|
|
57557
57800
|
{
|
|
57558
57801
|
method: "POST",
|
|
57559
|
-
headers: { "Content-Type": "application/json" },
|
|
57802
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
57560
57803
|
body: JSON.stringify(body)
|
|
57561
57804
|
}
|
|
57562
57805
|
);
|
|
@@ -57785,7 +58028,7 @@ function applyChange(tween, change) {
|
|
|
57785
58028
|
return rebuildKeyframeTween(tween, change.pct, change.props);
|
|
57786
58029
|
return false;
|
|
57787
58030
|
}
|
|
57788
|
-
function patchRuntimeTweenInPlace(iframe, selector, change, compositionId) {
|
|
58031
|
+
function patchRuntimeTweenInPlace(iframe, selector, change, compositionId, deferSeek = false) {
|
|
57789
58032
|
if (!iframe) return false;
|
|
57790
58033
|
if (change.kind === "global-set") return applyGlobalSet(iframe, selector, change.props);
|
|
57791
58034
|
try {
|
|
@@ -57803,7 +58046,7 @@ function patchRuntimeTweenInPlace(iframe, selector, change, compositionId) {
|
|
|
57803
58046
|
if (change.kind !== "keyframe-rebuild") {
|
|
57804
58047
|
tween.invalidate?.();
|
|
57805
58048
|
}
|
|
57806
|
-
seekToCurrent(iframe, timeline);
|
|
58049
|
+
if (!deferSeek) seekToCurrent(iframe, timeline);
|
|
57807
58050
|
return true;
|
|
57808
58051
|
} catch {
|
|
57809
58052
|
return false;
|
|
@@ -58722,7 +58965,7 @@ async function mutateGsapScript(projectId, sourceFile, mutation) {
|
|
|
58722
58965
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(sourceFile)}`,
|
|
58723
58966
|
{
|
|
58724
58967
|
method: "POST",
|
|
58725
|
-
headers: { "Content-Type": "application/json" },
|
|
58968
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
58726
58969
|
body: JSON.stringify(mutation)
|
|
58727
58970
|
}
|
|
58728
58971
|
);
|
|
@@ -58736,7 +58979,7 @@ async function mutateGsapScriptBatch(projectId, sourceFile, mutations) {
|
|
|
58736
58979
|
`/api/projects/${encodeURIComponent(projectId)}/gsap-mutations-batch/${encodeURIComponent(sourceFile)}`,
|
|
58737
58980
|
{
|
|
58738
58981
|
method: "POST",
|
|
58739
|
-
headers: { "Content-Type": "application/json" },
|
|
58982
|
+
headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
|
|
58740
58983
|
body: JSON.stringify({ mutations })
|
|
58741
58984
|
}
|
|
58742
58985
|
);
|
|
@@ -58777,7 +59020,7 @@ async function runMutationRequest(mutations, options, showToast, request) {
|
|
|
58777
59020
|
}
|
|
58778
59021
|
function finishUnchangedMutation(iframe, result, options, reloadPreview) {
|
|
58779
59022
|
if (result.changed !== false) return false;
|
|
58780
|
-
if (!options.skipReload && options.
|
|
59023
|
+
if (!options.skipReload && (instantPatchesFor(options).length > 0 || options.previewFallbackLatch?.pending)) {
|
|
58781
59024
|
applyPreviewSync(iframe, result, options, reloadPreview);
|
|
58782
59025
|
}
|
|
58783
59026
|
return true;
|
|
@@ -58834,15 +59077,31 @@ function softReloadOrEscalate(iframe, scriptText, reloadPreview, origin, authore
|
|
|
58834
59077
|
if (result === "cannot-soft-reload") reloadPreview();
|
|
58835
59078
|
}
|
|
58836
59079
|
function applyPreviewSync(iframe, result, options, reloadPreview) {
|
|
58837
|
-
|
|
58838
|
-
|
|
58839
|
-
|
|
58840
|
-
|
|
58841
|
-
|
|
59080
|
+
const patches = instantPatchesFor(options);
|
|
59081
|
+
let needsFallback = options.previewFallbackLatch?.pending === true;
|
|
59082
|
+
if (patches.length > 0) {
|
|
59083
|
+
const deferSeek = options.deferPreviewSync === true;
|
|
59084
|
+
const missed = patches.find(
|
|
59085
|
+
(patch, index) => !patchRuntimeTweenInPlace(
|
|
59086
|
+
iframe,
|
|
59087
|
+
patch.selector,
|
|
59088
|
+
patch.change,
|
|
59089
|
+
void 0,
|
|
59090
|
+
deferSeek || index < patches.length - 1
|
|
59091
|
+
)
|
|
58842
59092
|
);
|
|
58843
|
-
if (
|
|
58844
|
-
|
|
59093
|
+
if (missed) {
|
|
59094
|
+
trackStudioEvent("gsap_instant_patch_fallback", { selector: missed.selector });
|
|
59095
|
+
needsFallback = true;
|
|
59096
|
+
}
|
|
59097
|
+
if (!needsFallback) return;
|
|
58845
59098
|
}
|
|
59099
|
+
if (options.deferPreviewSync && options.previewFallbackLatch) {
|
|
59100
|
+
options.previewFallbackLatch.pending = needsFallback;
|
|
59101
|
+
return;
|
|
59102
|
+
}
|
|
59103
|
+
if (options.deferPreviewSync && !needsFallback) return;
|
|
59104
|
+
if (options.previewFallbackLatch) options.previewFallbackLatch.pending = false;
|
|
58846
59105
|
if (options.softReload && result.scriptText) {
|
|
58847
59106
|
softReloadOrEscalate(
|
|
58848
59107
|
iframe,
|
|
@@ -58855,6 +59114,12 @@ function applyPreviewSync(iframe, result, options, reloadPreview) {
|
|
|
58855
59114
|
reloadPreview();
|
|
58856
59115
|
}
|
|
58857
59116
|
}
|
|
59117
|
+
function instantPatchesFor(options) {
|
|
59118
|
+
return [
|
|
59119
|
+
...options.instantPatches ?? [],
|
|
59120
|
+
...options.instantPatch ? [options.instantPatch] : []
|
|
59121
|
+
];
|
|
59122
|
+
}
|
|
58858
59123
|
function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef, editHistory, domEditSaveTimestampRef, reloadPreview, onCacheInvalidate, onFileContentChanged, showToast, sdkSession, publishSdkSession, writeProjectFile, forceReloadSdkSession }) {
|
|
58859
59124
|
const activeProjectId = projectIdRef.current;
|
|
58860
59125
|
const activeCompPathRef = useRef104(activeCompPath);
|
|
@@ -58924,7 +59189,9 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
58924
59189
|
);
|
|
58925
59190
|
if (!result) return;
|
|
58926
59191
|
options.onResult?.(result);
|
|
58927
|
-
|
|
59192
|
+
const instantPatches = calls.map(({ options: callOptions }) => callOptions.instantPatch).filter((patch) => patch !== void 0);
|
|
59193
|
+
const { instantPatch: _instantPatch, ...batchOptions } = options;
|
|
59194
|
+
await finalizeSuccessfulMutation(pid, compositionPath, last.selection, last.mutation, targetPath, result, instantPatches.length > 0 ? { ...batchOptions, instantPatches } : batchOptions);
|
|
58928
59195
|
}, [showToast, finalizeSuccessfulMutation]);
|
|
58929
59196
|
const commitMutation = useMemo50(() => {
|
|
58930
59197
|
const serializeFile = (file, task) => {
|
|
@@ -59050,8 +59317,10 @@ function useDomEditPreviewSync({
|
|
|
59050
59317
|
activeCompPath,
|
|
59051
59318
|
captionEditMode,
|
|
59052
59319
|
domEditSelectionRef,
|
|
59320
|
+
domEditGroupSelectionsRef,
|
|
59053
59321
|
domEditSelection,
|
|
59054
59322
|
applyDomSelection,
|
|
59323
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
59055
59324
|
buildDomSelectionFromTarget,
|
|
59056
59325
|
refreshPreviewDocumentVersion,
|
|
59057
59326
|
syncPreviewHistoryHotkey,
|
|
@@ -59076,6 +59345,15 @@ function useDomEditPreviewSync({
|
|
|
59076
59345
|
reapplyPositionEditsAfterSeek(doc);
|
|
59077
59346
|
const nextElement = findElementForSelection(doc, currentSelection, activeCompPath);
|
|
59078
59347
|
if (!nextElement) {
|
|
59348
|
+
const group = domEditGroupSelectionsRef.current;
|
|
59349
|
+
logSelect("preview-sync-lost", {
|
|
59350
|
+
target: currentSelection.selector ?? currentSelection.id ?? null,
|
|
59351
|
+
group: group.length
|
|
59352
|
+
});
|
|
59353
|
+
if (group.length > 1) {
|
|
59354
|
+
await refreshDomEditGroupSelectionsFromPreview(group);
|
|
59355
|
+
return;
|
|
59356
|
+
}
|
|
59079
59357
|
applyDomSelection(null, { revealPanel: false });
|
|
59080
59358
|
return;
|
|
59081
59359
|
}
|
|
@@ -59103,8 +59381,10 @@ function useDomEditPreviewSync({
|
|
|
59103
59381
|
applyDomSelection,
|
|
59104
59382
|
buildDomSelectionFromTarget,
|
|
59105
59383
|
captionEditMode,
|
|
59384
|
+
domEditGroupSelectionsRef,
|
|
59106
59385
|
domEditSelectionRef,
|
|
59107
59386
|
previewIframe,
|
|
59387
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
59108
59388
|
refreshPreviewDocumentVersion,
|
|
59109
59389
|
syncPreviewHistoryHotkey,
|
|
59110
59390
|
applyStudioManualEditsToPreviewRef,
|
|
@@ -59140,11 +59420,12 @@ function selectElementAnimationsOrRetry(parsed, target) {
|
|
|
59140
59420
|
if (parsed.animations.length === 0) return { kind: "cold" };
|
|
59141
59421
|
return { kind: "resolved", animations: getAnimationsForElement(parsed.animations, target) };
|
|
59142
59422
|
}
|
|
59143
|
-
async function fetchElementAnimationsWithRetry(projectId, gsapSourceFile, target, failOnFetchError) {
|
|
59423
|
+
async function fetchElementAnimationsWithRetry(projectId, gsapSourceFile, target, failOnFetchError, fresh) {
|
|
59144
59424
|
let coldAttempts = 0;
|
|
59145
59425
|
let errorAttempts = 0;
|
|
59146
59426
|
for (; ; ) {
|
|
59147
|
-
const parsed = await fetchParsedAnimations(projectId, gsapSourceFile);
|
|
59427
|
+
const parsed = await fetchParsedAnimations(projectId, gsapSourceFile, { fresh });
|
|
59428
|
+
fresh = false;
|
|
59148
59429
|
const outcome = selectElementAnimationsOrRetry(parsed, target);
|
|
59149
59430
|
if (outcome.kind === "resolved") return outcome.animations;
|
|
59150
59431
|
if (outcome.kind === "fetch-error") {
|
|
@@ -59170,7 +59451,8 @@ function useGsapAnimationFetchFallback(projectId, gsapSourceFile) {
|
|
|
59170
59451
|
projectId,
|
|
59171
59452
|
gsapSourceFile,
|
|
59172
59453
|
target,
|
|
59173
|
-
options?.failOnFetchError === true
|
|
59454
|
+
options?.failOnFetchError === true,
|
|
59455
|
+
options?.fresh === true
|
|
59174
59456
|
);
|
|
59175
59457
|
},
|
|
59176
59458
|
[projectId, gsapSourceFile]
|
|
@@ -59185,12 +59467,12 @@ function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
|
|
|
59185
59467
|
trackStudioSaveFailure({
|
|
59186
59468
|
source: "gsap_commit",
|
|
59187
59469
|
error,
|
|
59188
|
-
filePath: selection
|
|
59470
|
+
filePath: selection?.sourceFile ?? activeCompPath ?? "index.html",
|
|
59189
59471
|
mutationType,
|
|
59190
59472
|
label,
|
|
59191
|
-
targetId: selection
|
|
59192
|
-
targetSelector: selection
|
|
59193
|
-
targetSourceFile: selection
|
|
59473
|
+
targetId: selection?.id,
|
|
59474
|
+
targetSelector: selection?.selector,
|
|
59475
|
+
targetSourceFile: selection?.sourceFile
|
|
59194
59476
|
});
|
|
59195
59477
|
showToast(
|
|
59196
59478
|
isGsapEditBlockedError(error) ? error.message : "Failed to save animated edit.",
|
|
@@ -59502,6 +59784,8 @@ function useDomEditWiring({
|
|
|
59502
59784
|
activeCompPath,
|
|
59503
59785
|
domEditSelection,
|
|
59504
59786
|
domEditSelectionRef,
|
|
59787
|
+
domEditGroupSelectionsRef,
|
|
59788
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
59505
59789
|
previewIframeRef,
|
|
59506
59790
|
previewIframe,
|
|
59507
59791
|
captionEditMode,
|
|
@@ -59615,8 +59899,10 @@ function useDomEditWiring({
|
|
|
59615
59899
|
activeCompPath,
|
|
59616
59900
|
captionEditMode,
|
|
59617
59901
|
domEditSelectionRef,
|
|
59902
|
+
domEditGroupSelectionsRef,
|
|
59618
59903
|
domEditSelection,
|
|
59619
59904
|
applyDomSelection,
|
|
59905
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
59620
59906
|
buildDomSelectionFromTarget,
|
|
59621
59907
|
refreshPreviewDocumentVersion,
|
|
59622
59908
|
syncPreviewHistoryHotkey,
|
|
@@ -60302,6 +60588,14 @@ function useAnimatedPropertyCommit(deps) {
|
|
|
60302
60588
|
|
|
60303
60589
|
// src/hooks/useGsapAwareEditing.ts
|
|
60304
60590
|
var groupDragCommitCounter = 0;
|
|
60591
|
+
function firstPreflightFailure(results, updates) {
|
|
60592
|
+
for (const [index, result] of results.entries()) {
|
|
60593
|
+
if (result.status !== "rejected") continue;
|
|
60594
|
+
const selection = updates[index]?.selection;
|
|
60595
|
+
if (selection) return { error: result.reason, selection };
|
|
60596
|
+
}
|
|
60597
|
+
return null;
|
|
60598
|
+
}
|
|
60305
60599
|
function useGsapAwareEditing({
|
|
60306
60600
|
domEditSelection,
|
|
60307
60601
|
selectedGsapAnimations,
|
|
@@ -60349,14 +60643,37 @@ function useGsapAwareEditing({
|
|
|
60349
60643
|
async (updates) => {
|
|
60350
60644
|
if (!gsapCommitMutation || updates.length === 0) return;
|
|
60351
60645
|
const coalesceKey = `group-drag:${++groupDragCommitCounter}`;
|
|
60352
|
-
|
|
60646
|
+
let renderOnCommit = false;
|
|
60647
|
+
const previewFallbackLatch = { pending: false };
|
|
60648
|
+
const withGroupOptions = (options) => ({
|
|
60353
60649
|
...options,
|
|
60354
60650
|
coalesceKey,
|
|
60355
|
-
coalesceMs: Number.POSITIVE_INFINITY
|
|
60651
|
+
coalesceMs: Number.POSITIVE_INFINITY,
|
|
60652
|
+
deferPreviewSync: !renderOnCommit,
|
|
60653
|
+
previewFallbackLatch
|
|
60356
60654
|
});
|
|
60655
|
+
const queued = [];
|
|
60656
|
+
const flushQueued = async () => {
|
|
60657
|
+
if (queued.length === 0) return;
|
|
60658
|
+
const calls = queued.splice(0, queued.length);
|
|
60659
|
+
if (!gsapCommitMutation.batch) {
|
|
60660
|
+
for (const call of calls) {
|
|
60661
|
+
await gsapCommitMutation(call.selection, call.mutation, call.options);
|
|
60662
|
+
}
|
|
60663
|
+
return;
|
|
60664
|
+
}
|
|
60665
|
+
await gsapCommitMutation.batch(calls, {
|
|
60666
|
+
...calls.at(-1)?.options ?? { label: "Move animated layer (group)" },
|
|
60667
|
+
label: "Move animated layer (group)"
|
|
60668
|
+
});
|
|
60669
|
+
};
|
|
60670
|
+
const coalescedCommit = (selection, mutation, options) => {
|
|
60671
|
+
queued.push({ selection, mutation, options: withGroupOptions(options) });
|
|
60672
|
+
return Promise.resolve();
|
|
60673
|
+
};
|
|
60357
60674
|
const preflightAnimations = /* @__PURE__ */ new Map();
|
|
60358
|
-
|
|
60359
|
-
|
|
60675
|
+
const preflightResults = await Promise.allSettled(
|
|
60676
|
+
updates.map(async ({ selection }) => {
|
|
60360
60677
|
const animations = await makeFetchFallback(selection, { failOnFetchError: true })();
|
|
60361
60678
|
preflightAnimations.set(selection, animations);
|
|
60362
60679
|
const outcome = await tryGsapDragIntercept(
|
|
@@ -60369,12 +60686,20 @@ function useGsapAwareEditing({
|
|
|
60369
60686
|
{ preflightOnly: true }
|
|
60370
60687
|
);
|
|
60371
60688
|
assertGsapEditPersisted(outcome);
|
|
60372
|
-
}
|
|
60373
|
-
|
|
60374
|
-
|
|
60375
|
-
|
|
60689
|
+
})
|
|
60690
|
+
);
|
|
60691
|
+
const preflightFailure = firstPreflightFailure(preflightResults, updates);
|
|
60692
|
+
if (preflightFailure) {
|
|
60693
|
+
trackGsapInteractionFailure(
|
|
60694
|
+
preflightFailure.error,
|
|
60695
|
+
preflightFailure.selection,
|
|
60696
|
+
"drag",
|
|
60697
|
+
"Move animated layer (group)"
|
|
60698
|
+
);
|
|
60699
|
+
throw preflightFailure.error;
|
|
60376
60700
|
}
|
|
60377
|
-
for (const { selection, next } of updates) {
|
|
60701
|
+
for (const [index, { selection, next }] of updates.entries()) {
|
|
60702
|
+
renderOnCommit = index === updates.length - 1;
|
|
60378
60703
|
try {
|
|
60379
60704
|
const outcome = await tryGsapDragIntercept(
|
|
60380
60705
|
selection,
|
|
@@ -60382,7 +60707,13 @@ function useGsapAwareEditing({
|
|
|
60382
60707
|
preflightAnimations.get(selection) ?? [],
|
|
60383
60708
|
previewIframeRef.current,
|
|
60384
60709
|
coalescedCommit,
|
|
60385
|
-
|
|
60710
|
+
// The intercept re-reads the file to resolve a stale or shared tween.
|
|
60711
|
+
// Anything already queued has to be on disk before that read, or it
|
|
60712
|
+
// resolves against a file missing writes it is about to build on.
|
|
60713
|
+
async () => {
|
|
60714
|
+
await flushQueued();
|
|
60715
|
+
return makeFetchFallback(selection, { fresh: true })();
|
|
60716
|
+
},
|
|
60386
60717
|
{ preflightPassed: true }
|
|
60387
60718
|
);
|
|
60388
60719
|
assertGsapEditPersisted(outcome);
|
|
@@ -60391,6 +60722,12 @@ function useGsapAwareEditing({
|
|
|
60391
60722
|
throw error;
|
|
60392
60723
|
}
|
|
60393
60724
|
}
|
|
60725
|
+
try {
|
|
60726
|
+
await flushQueued();
|
|
60727
|
+
} catch (error) {
|
|
60728
|
+
trackGsapInteractionFailure(error, null, "drag", "Move animated layer (group)");
|
|
60729
|
+
throw error;
|
|
60730
|
+
}
|
|
60394
60731
|
},
|
|
60395
60732
|
[gsapCommitMutation, previewIframeRef, makeFetchFallback, trackGsapInteractionFailure]
|
|
60396
60733
|
);
|
|
@@ -60787,13 +61124,14 @@ function useKeyframeEaseCommits({
|
|
|
60787
61124
|
function useDomEditSession({
|
|
60788
61125
|
projectId,
|
|
60789
61126
|
activeCompPath,
|
|
60790
|
-
isMasterView,
|
|
60791
61127
|
compIdToSrc,
|
|
60792
61128
|
captionEditMode,
|
|
60793
61129
|
compositionLoading,
|
|
60794
61130
|
previewIframeRef,
|
|
60795
61131
|
timelineElements,
|
|
61132
|
+
getTimelineSelectionSet: getTimelineSelectionSet2,
|
|
60796
61133
|
setSelectedTimelineElementId,
|
|
61134
|
+
setTimelineSelectionSet,
|
|
60797
61135
|
setRightCollapsed,
|
|
60798
61136
|
setRightPanelTab,
|
|
60799
61137
|
showToast,
|
|
@@ -60823,6 +61161,7 @@ function useDomEditSession({
|
|
|
60823
61161
|
publishSdkSession,
|
|
60824
61162
|
forceReloadSdkSession
|
|
60825
61163
|
}) {
|
|
61164
|
+
const isMasterView = !activeCompPath || activeCompPath === "index.html";
|
|
60826
61165
|
void _setRefreshKey;
|
|
60827
61166
|
const {
|
|
60828
61167
|
domEditSelection,
|
|
@@ -60841,6 +61180,7 @@ function useDomEditSession({
|
|
|
60841
61180
|
buildDomSelectionForTimelineElement,
|
|
60842
61181
|
handleTimelineElementSelect,
|
|
60843
61182
|
refreshDomEditSelectionFromPreview,
|
|
61183
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
60844
61184
|
applyMarqueeSelection
|
|
60845
61185
|
} = useDomSelection({
|
|
60846
61186
|
projectId,
|
|
@@ -60850,7 +61190,9 @@ function useDomEditSession({
|
|
|
60850
61190
|
captionEditMode,
|
|
60851
61191
|
previewIframeRef,
|
|
60852
61192
|
timelineElements,
|
|
61193
|
+
getTimelineSelectionSet: getTimelineSelectionSet2,
|
|
60853
61194
|
setSelectedTimelineElementId,
|
|
61195
|
+
setTimelineSelectionSet,
|
|
60854
61196
|
setRightCollapsed,
|
|
60855
61197
|
setRightPanelTab,
|
|
60856
61198
|
previewIframe,
|
|
@@ -61061,6 +61403,8 @@ function useDomEditSession({
|
|
|
61061
61403
|
activeCompPath,
|
|
61062
61404
|
domEditSelection,
|
|
61063
61405
|
domEditSelectionRef,
|
|
61406
|
+
domEditGroupSelectionsRef,
|
|
61407
|
+
refreshDomEditGroupSelectionsFromPreview,
|
|
61064
61408
|
previewIframeRef,
|
|
61065
61409
|
previewIframe,
|
|
61066
61410
|
captionEditMode,
|
|
@@ -61611,6 +61955,7 @@ function useExternalFileChangeCoordinator({
|
|
|
61611
61955
|
}, [loadConflictSnapshot, projectId, recoveryFilePath]);
|
|
61612
61956
|
const reloadAcceptedGeneration = useCallback104(
|
|
61613
61957
|
(path) => {
|
|
61958
|
+
logReload("reload", { path, by: "external-change coordinator" });
|
|
61614
61959
|
reloadPreview();
|
|
61615
61960
|
reloadSdkSession(path);
|
|
61616
61961
|
},
|
|
@@ -61632,10 +61977,21 @@ function useExternalFileChangeCoordinator({
|
|
|
61632
61977
|
const pendingTimelinePaths = pendingTimelineEditPathRef.current;
|
|
61633
61978
|
pendingTimelinePaths.delete(path);
|
|
61634
61979
|
const content = readFileChangeContent(payload);
|
|
61635
|
-
|
|
61636
|
-
|
|
61980
|
+
const token = readFileChangeWriteToken(payload);
|
|
61981
|
+
logReload("file-change", { path, token: token ?? null, hasContent: content != null });
|
|
61982
|
+
if (consumeStudioWriteToken(token)) {
|
|
61983
|
+
logReload("suppressed", { path, why: "own write token" });
|
|
61984
|
+
return;
|
|
61985
|
+
}
|
|
61986
|
+
if (content != null && isSelfWriteEcho(path, content)) {
|
|
61987
|
+
logReload("suppressed", { path, why: "own content echo" });
|
|
61988
|
+
return;
|
|
61989
|
+
}
|
|
61637
61990
|
const identity = eventIdentity(path, payload);
|
|
61638
|
-
if (!allowDuplicate && identity != null && identity === lastEventIdentityRef.current)
|
|
61991
|
+
if (!allowDuplicate && identity != null && identity === lastEventIdentityRef.current) {
|
|
61992
|
+
logReload("suppressed", { path, why: "duplicate event" });
|
|
61993
|
+
return;
|
|
61994
|
+
}
|
|
61639
61995
|
lastEventIdentityRef.current = identity;
|
|
61640
61996
|
const generation = ++generationRef.current;
|
|
61641
61997
|
const result = await drainPendingChanges();
|
|
@@ -63966,17 +64322,50 @@ function parseNumber(value) {
|
|
|
63966
64322
|
function parseTab(value) {
|
|
63967
64323
|
return VALID_TABS.includes(value) ? value : null;
|
|
63968
64324
|
}
|
|
64325
|
+
function optionalString(value) {
|
|
64326
|
+
return typeof value === "string" ? value : void 0;
|
|
64327
|
+
}
|
|
64328
|
+
function normalizedIndex(value) {
|
|
64329
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
|
|
64330
|
+
}
|
|
64331
|
+
function parseSelectionTarget(value) {
|
|
64332
|
+
if (!value || typeof value !== "object") return null;
|
|
64333
|
+
const sourceFile = optionalString(Reflect.get(value, "sourceFile"));
|
|
64334
|
+
const id = optionalString(Reflect.get(value, "id"));
|
|
64335
|
+
const selector = optionalString(Reflect.get(value, "selector"));
|
|
64336
|
+
if (!id && !selector) return null;
|
|
64337
|
+
return {
|
|
64338
|
+
sourceFile,
|
|
64339
|
+
id,
|
|
64340
|
+
selector,
|
|
64341
|
+
selectorIndex: normalizedIndex(Reflect.get(value, "selectorIndex"))
|
|
64342
|
+
};
|
|
64343
|
+
}
|
|
64344
|
+
function parseGroup(value) {
|
|
64345
|
+
if (!value) return void 0;
|
|
64346
|
+
let parsed;
|
|
64347
|
+
try {
|
|
64348
|
+
parsed = JSON.parse(value);
|
|
64349
|
+
} catch {
|
|
64350
|
+
const legacy = value.split(",").map((id) => id.trim()).filter(Boolean).map((id) => ({ id }));
|
|
64351
|
+
return legacy.length > 0 ? legacy : void 0;
|
|
64352
|
+
}
|
|
64353
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
64354
|
+
const targets = parsed.map(parseSelectionTarget).filter((target) => target !== null);
|
|
64355
|
+
return targets.length > 0 ? targets : void 0;
|
|
64356
|
+
}
|
|
63969
64357
|
function normalizeSelection(params) {
|
|
63970
64358
|
const sourceFile = params.get("selFile") || void 0;
|
|
63971
64359
|
const id = params.get("selId") || void 0;
|
|
63972
64360
|
const selector = params.get("selSelector") || void 0;
|
|
63973
|
-
const selectorIndex = parseNumber(params.get("selIndex"));
|
|
63974
64361
|
if (!sourceFile && !id && !selector) return null;
|
|
64362
|
+
const selectorIndex = parseNumber(params.get("selIndex"));
|
|
63975
64363
|
return {
|
|
63976
64364
|
sourceFile,
|
|
63977
64365
|
id,
|
|
63978
64366
|
selector,
|
|
63979
|
-
selectorIndex: selectorIndex != null ? Math.max(0, Math.floor(selectorIndex)) : void 0
|
|
64367
|
+
selectorIndex: selectorIndex != null ? Math.max(0, Math.floor(selectorIndex)) : void 0,
|
|
64368
|
+
group: parseGroup(params.get("selGroup"))
|
|
63980
64369
|
};
|
|
63981
64370
|
}
|
|
63982
64371
|
function defaultStudioUrlState() {
|
|
@@ -64023,13 +64412,15 @@ function buildStudioHash(projectId, state) {
|
|
|
64023
64412
|
if (typeof state.selection.selectorIndex === "number") {
|
|
64024
64413
|
params.set("selIndex", String(Math.max(0, Math.floor(state.selection.selectorIndex))));
|
|
64025
64414
|
}
|
|
64415
|
+
if (state.selection.group?.length) {
|
|
64416
|
+
params.set("selGroup", JSON.stringify(state.selection.group));
|
|
64417
|
+
}
|
|
64026
64418
|
}
|
|
64027
64419
|
return buildProjectHash(projectId, params);
|
|
64028
64420
|
}
|
|
64029
64421
|
|
|
64030
64422
|
// src/hooks/useStudioUrlState.ts
|
|
64031
|
-
function
|
|
64032
|
-
if (!selection) return null;
|
|
64423
|
+
function toPersistedTarget(selection) {
|
|
64033
64424
|
if (!selection.id && !selection.selector) return null;
|
|
64034
64425
|
return {
|
|
64035
64426
|
sourceFile: selection.sourceFile || void 0,
|
|
@@ -64038,11 +64429,78 @@ function toPersistedSelection(selection) {
|
|
|
64038
64429
|
selectorIndex: selection.selectorIndex ?? void 0
|
|
64039
64430
|
};
|
|
64040
64431
|
}
|
|
64432
|
+
function selectionTargetKey(selection) {
|
|
64433
|
+
return [
|
|
64434
|
+
selection.sourceFile ?? "",
|
|
64435
|
+
selection.id ?? "",
|
|
64436
|
+
selection.selector ?? "",
|
|
64437
|
+
selection.selectorIndex ?? ""
|
|
64438
|
+
].join("|");
|
|
64439
|
+
}
|
|
64440
|
+
function toPersistedSelection(selection, group = []) {
|
|
64441
|
+
if (!selection) return null;
|
|
64442
|
+
const primary = toPersistedTarget(selection);
|
|
64443
|
+
if (!primary) return null;
|
|
64444
|
+
const primaryKey = selectionTargetKey(primary);
|
|
64445
|
+
const members = /* @__PURE__ */ new Map();
|
|
64446
|
+
for (const member of group) {
|
|
64447
|
+
const target = toPersistedTarget(member);
|
|
64448
|
+
if (!target) continue;
|
|
64449
|
+
const key2 = selectionTargetKey(target);
|
|
64450
|
+
if (key2 !== primaryKey) members.set(key2, target);
|
|
64451
|
+
}
|
|
64452
|
+
return {
|
|
64453
|
+
...primary,
|
|
64454
|
+
group: members.size > 0 ? [...members.values()] : void 0
|
|
64455
|
+
};
|
|
64456
|
+
}
|
|
64041
64457
|
function replaceHash(nextHash) {
|
|
64042
64458
|
if (typeof window === "undefined") return;
|
|
64043
64459
|
if (window.location.hash === nextHash) return;
|
|
64044
64460
|
window.history.replaceState(null, "", nextHash);
|
|
64045
64461
|
}
|
|
64462
|
+
function findUrlSelectionElement(doc, target, fallbackSourceFile, activeCompPath) {
|
|
64463
|
+
return findElementForSelection(
|
|
64464
|
+
doc,
|
|
64465
|
+
{
|
|
64466
|
+
sourceFile: target.sourceFile ?? fallbackSourceFile,
|
|
64467
|
+
id: target.id,
|
|
64468
|
+
selector: target.selector,
|
|
64469
|
+
selectorIndex: target.selectorIndex
|
|
64470
|
+
},
|
|
64471
|
+
activeCompPath
|
|
64472
|
+
);
|
|
64473
|
+
}
|
|
64474
|
+
async function buildOptionalDomSelection(element, buildDomSelection) {
|
|
64475
|
+
if (!element) return null;
|
|
64476
|
+
return buildDomSelection(element, { preferClipAncestor: false });
|
|
64477
|
+
}
|
|
64478
|
+
async function resolveUrlSelections({
|
|
64479
|
+
doc,
|
|
64480
|
+
primaryElement,
|
|
64481
|
+
selection,
|
|
64482
|
+
group,
|
|
64483
|
+
activeCompPath,
|
|
64484
|
+
isCurrent,
|
|
64485
|
+
buildDomSelection
|
|
64486
|
+
}) {
|
|
64487
|
+
const primary = await buildDomSelection(primaryElement, { preferClipAncestor: false });
|
|
64488
|
+
if (!isCurrent()) return null;
|
|
64489
|
+
if (!primary) return [];
|
|
64490
|
+
const members = [primary];
|
|
64491
|
+
for (const member of group) {
|
|
64492
|
+
const element = findUrlSelectionElement(
|
|
64493
|
+
doc,
|
|
64494
|
+
member,
|
|
64495
|
+
selection.sourceFile ?? "",
|
|
64496
|
+
activeCompPath
|
|
64497
|
+
);
|
|
64498
|
+
const resolved = await buildOptionalDomSelection(element, buildDomSelection);
|
|
64499
|
+
if (!isCurrent()) return null;
|
|
64500
|
+
if (resolved) members.push(resolved);
|
|
64501
|
+
}
|
|
64502
|
+
return members;
|
|
64503
|
+
}
|
|
64046
64504
|
function useStudioUrlState({
|
|
64047
64505
|
projectId,
|
|
64048
64506
|
activeCompPath,
|
|
@@ -64055,6 +64513,8 @@ function useStudioUrlState({
|
|
|
64055
64513
|
rightCollapsed,
|
|
64056
64514
|
activeCompPathHydrated,
|
|
64057
64515
|
domEditSelection,
|
|
64516
|
+
domEditGroupSelections,
|
|
64517
|
+
applyMarqueeSelection,
|
|
64058
64518
|
buildDomSelectionFromTarget,
|
|
64059
64519
|
applyDomSelection,
|
|
64060
64520
|
setRightPanelTab,
|
|
@@ -64067,6 +64527,7 @@ function useStudioUrlState({
|
|
|
64067
64527
|
const [selectionHydrated, setSelectionHydrated] = useState97(initialState2.selection == null);
|
|
64068
64528
|
const pendingSelectionRef = useRef120(initialState2.selection);
|
|
64069
64529
|
const stableTimeRef = useRef120(initialState2.currentTime);
|
|
64530
|
+
const selectionApplySeqRef = useRef120(0);
|
|
64070
64531
|
const buildUrlState = useCallback117(
|
|
64071
64532
|
() => ({
|
|
64072
64533
|
activeCompPath,
|
|
@@ -64074,12 +64535,13 @@ function useStudioUrlState({
|
|
|
64074
64535
|
rightPanelTab,
|
|
64075
64536
|
rightCollapsed,
|
|
64076
64537
|
timelineVisible: null,
|
|
64077
|
-
selection: hydratedSelectionRef.current ? toPersistedSelection(domEditSelection) : pendingSelectionRef.current
|
|
64538
|
+
selection: hydratedSelectionRef.current ? toPersistedSelection(domEditSelection, domEditGroupSelections) : pendingSelectionRef.current
|
|
64078
64539
|
}),
|
|
64079
|
-
[activeCompPath, domEditSelection, rightCollapsed, rightPanelTab]
|
|
64540
|
+
[activeCompPath, domEditGroupSelections, domEditSelection, rightCollapsed, rightPanelTab]
|
|
64080
64541
|
);
|
|
64081
64542
|
const applyUrlSelection = useCallback117(
|
|
64082
64543
|
(selection) => {
|
|
64544
|
+
const applySeq = ++selectionApplySeqRef.current;
|
|
64083
64545
|
if (!selection) {
|
|
64084
64546
|
applyDomSelection(null, { revealPanel: false });
|
|
64085
64547
|
return true;
|
|
@@ -64105,12 +64567,31 @@ function useStudioUrlState({
|
|
|
64105
64567
|
applyDomSelection(null, { revealPanel: false });
|
|
64106
64568
|
return true;
|
|
64107
64569
|
}
|
|
64108
|
-
|
|
64109
|
-
|
|
64570
|
+
const group = selection.group ?? [];
|
|
64571
|
+
void resolveUrlSelections({
|
|
64572
|
+
doc,
|
|
64573
|
+
primaryElement: element,
|
|
64574
|
+
selection,
|
|
64575
|
+
group,
|
|
64576
|
+
activeCompPath,
|
|
64577
|
+
isCurrent: () => applySeq === selectionApplySeqRef.current,
|
|
64578
|
+
buildDomSelection: buildDomSelectionFromTarget
|
|
64579
|
+
}).then((members) => {
|
|
64580
|
+
if (!members) return;
|
|
64581
|
+
const primary = members[0];
|
|
64582
|
+
if (!primary) return applyDomSelection(null, { revealPanel: false });
|
|
64583
|
+
if (group.length === 0) return applyDomSelection(primary, { revealPanel: false });
|
|
64584
|
+
applyMarqueeSelection(members, false);
|
|
64110
64585
|
});
|
|
64111
64586
|
return true;
|
|
64112
64587
|
},
|
|
64113
|
-
[
|
|
64588
|
+
[
|
|
64589
|
+
activeCompPath,
|
|
64590
|
+
applyDomSelection,
|
|
64591
|
+
applyMarqueeSelection,
|
|
64592
|
+
buildDomSelectionFromTarget,
|
|
64593
|
+
previewIframeRef
|
|
64594
|
+
]
|
|
64114
64595
|
);
|
|
64115
64596
|
useEffect92(() => {
|
|
64116
64597
|
if (!projectId || hydratedSeekRef.current || compositionLoading) return;
|
|
@@ -73055,7 +73536,7 @@ function isTextInput(el) {
|
|
|
73055
73536
|
return el.isContentEditable === true;
|
|
73056
73537
|
}
|
|
73057
73538
|
function useKeyframeKeyboard({
|
|
73058
|
-
enabled
|
|
73539
|
+
enabled,
|
|
73059
73540
|
onAddKeyframe,
|
|
73060
73541
|
onDeleteKeyframe,
|
|
73061
73542
|
onPrevKeyframe,
|
|
@@ -73066,7 +73547,7 @@ function useKeyframeKeyboard({
|
|
|
73066
73547
|
}) {
|
|
73067
73548
|
const handler = useCallback146(
|
|
73068
73549
|
(e) => {
|
|
73069
|
-
if (!
|
|
73550
|
+
if (!enabled) return;
|
|
73070
73551
|
if (isTextInput(document.activeElement)) return;
|
|
73071
73552
|
if (e.metaKey || e.ctrlKey) return;
|
|
73072
73553
|
const hasSelectedKeyframes = usePlayerStore.getState().selectedKeyframes.size > 0;
|
|
@@ -73105,7 +73586,7 @@ function useKeyframeKeyboard({
|
|
|
73105
73586
|
}
|
|
73106
73587
|
},
|
|
73107
73588
|
[
|
|
73108
|
-
|
|
73589
|
+
enabled,
|
|
73109
73590
|
onAddKeyframe,
|
|
73110
73591
|
onDeleteKeyframe,
|
|
73111
73592
|
onPrevKeyframe,
|
|
@@ -73116,10 +73597,10 @@ function useKeyframeKeyboard({
|
|
|
73116
73597
|
]
|
|
73117
73598
|
);
|
|
73118
73599
|
useEffect110(() => {
|
|
73119
|
-
if (!
|
|
73600
|
+
if (!enabled) return;
|
|
73120
73601
|
window.addEventListener("keydown", handler, { capture: true });
|
|
73121
73602
|
return () => window.removeEventListener("keydown", handler, { capture: true });
|
|
73122
|
-
}, [
|
|
73603
|
+
}, [enabled, handler]);
|
|
73123
73604
|
}
|
|
73124
73605
|
|
|
73125
73606
|
// src/components/TimelineToolbar.tsx
|
|
@@ -75094,6 +75575,7 @@ function useTimelineAddAtPlayhead(addAsset, addComposition) {
|
|
|
75094
75575
|
|
|
75095
75576
|
// src/App.tsx
|
|
75096
75577
|
import { jsx as jsx184, jsxs as jsxs153 } from "react/jsx-runtime";
|
|
75578
|
+
var getTimelineSelectionSet = () => usePlayerStore.getState().selectedElementIds;
|
|
75097
75579
|
function StudioApp() {
|
|
75098
75580
|
const { projectId, resolving, waitingForServer } = useServerConnection();
|
|
75099
75581
|
const initialUrlStateRef = useRef142(readStudioUrlStateFromWindow());
|
|
@@ -75119,9 +75601,9 @@ function StudioApp() {
|
|
|
75119
75601
|
const captionSync = useCaptionSync(projectId);
|
|
75120
75602
|
const timelineElements = usePlayerStore((s) => s.elements);
|
|
75121
75603
|
const setSelectedTimelineElementId = usePlayerStore((s) => s.setSelectedElementId);
|
|
75604
|
+
const setTimelineSelectionSet = usePlayerStore((s) => s.setSelectedElementIds);
|
|
75122
75605
|
const timelineDuration2 = usePlayerStore((s) => s.duration);
|
|
75123
75606
|
const isPlaying = usePlayerStore((s) => s.isPlaying);
|
|
75124
|
-
const isMasterView = !activeCompPath || activeCompPath === "index.html";
|
|
75125
75607
|
const effectiveTimelineDuration = useMemo66(() => {
|
|
75126
75608
|
const maxEnd = timelineElements.length > 0 ? Math.max(...timelineElements.map((el) => el.start + el.duration)) : 0;
|
|
75127
75609
|
return Math.max(timelineDuration2, maxEnd);
|
|
@@ -75299,13 +75781,14 @@ function StudioApp() {
|
|
|
75299
75781
|
const domEditSession = useDomEditSession({
|
|
75300
75782
|
projectId,
|
|
75301
75783
|
activeCompPath,
|
|
75302
|
-
isMasterView,
|
|
75303
75784
|
compIdToSrc,
|
|
75304
75785
|
captionEditMode,
|
|
75305
75786
|
compositionLoading,
|
|
75306
75787
|
previewIframeRef,
|
|
75307
75788
|
timelineElements,
|
|
75789
|
+
getTimelineSelectionSet,
|
|
75308
75790
|
setSelectedTimelineElementId,
|
|
75791
|
+
setTimelineSelectionSet,
|
|
75309
75792
|
setRightCollapsed: panelLayout.setRightCollapsed,
|
|
75310
75793
|
setRightPanelTab: panelLayout.setRightPanelTab,
|
|
75311
75794
|
showToast,
|
|
@@ -75444,6 +75927,8 @@ function StudioApp() {
|
|
|
75444
75927
|
rightCollapsed: panelLayout.rightCollapsed,
|
|
75445
75928
|
activeCompPathHydrated,
|
|
75446
75929
|
domEditSelection: domEditSession.domEditSelection,
|
|
75930
|
+
domEditGroupSelections: domEditSession.domEditGroupSelections,
|
|
75931
|
+
applyMarqueeSelection: domEditSession.applyMarqueeSelection,
|
|
75447
75932
|
buildDomSelectionFromTarget: domEditSession.buildDomSelectionFromTarget,
|
|
75448
75933
|
applyDomSelection: domEditSession.applyDomSelection,
|
|
75449
75934
|
setRightPanelTab: panelLayout.setRightPanelTab,
|