@hyperframes/studio 0.8.106 → 0.8.107
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-dl6K6ABp.js → hyperframes-player-h4mktSdm.js} +1 -1
- package/dist/assets/{index-CMiYVGv5.js → index-CM1xA_jD.js} +195 -200
- package/dist/assets/{index-ooaZZhKf.js → index-D8GKc2D9.js} +1 -1
- package/dist/assets/{index-DKT5NF1Y.js → index-zoqPmBF6.js} +1 -1
- package/dist/{chunk-ICTZJPQQ.js → chunk-4OZC426H.js} +12 -3
- package/dist/chunk-4OZC426H.js.map +1 -0
- package/dist/index.d.ts +367 -11
- package/dist/index.html +1 -1
- package/dist/index.js +1064 -714
- package/dist/index.js.map +1 -1
- package/dist/player.js +1 -1
- package/package.json +8 -8
- package/src/App.tsx +5 -3
- package/src/agentToolsExport.test.ts +8 -0
- package/src/captions/keyboard.test.ts +6 -0
- package/src/components/StudioRightPanels.tsx +2 -0
- package/src/components/TimelineToolbar.test.tsx +27 -0
- package/src/components/TimelineToolbar.tsx +2 -5
- package/src/components/editor/DomEditOverlay.tsx +12 -18
- package/src/components/editor/PropertyPanel.tsx +1 -14
- package/src/components/editor/PropertyPanelFlat.tsx +3 -6
- package/src/components/editor/SnapToolbar.test.tsx +32 -1
- package/src/components/editor/SnapToolbar.tsx +3 -4
- package/src/components/editor/domEditOverlayStartGesture.ts +5 -2
- package/src/components/editor/fontAssets.test.ts +12 -22
- package/src/components/editor/gestureHoldsPreviewReload.test.tsx +96 -33
- package/src/components/editor/gsapAnimatesProperty.ts +3 -0
- package/src/components/editor/manualEdits.test.ts +4 -3
- package/src/components/editor/manualEditsDom.ts +28 -5
- package/src/components/editor/manualEditsDomPatches.ts +5 -1
- package/src/components/editor/manualOffsetDrag.ts +4 -1
- package/src/components/editor/mediaAudioEdits.test.ts +212 -0
- package/src/components/editor/mediaAudioEdits.ts +253 -0
- package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +286 -87
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +17 -20
- package/src/components/editor/propertyPanelFlatProps.ts +1 -0
- package/src/components/editor/propertyPanelMediaSection.tsx +17 -27
- package/src/components/editor/propertyPanelTypes.ts +2 -0
- package/src/components/editor/useDomEditNudge.test.tsx +134 -2
- package/src/components/editor/useDomEditNudge.ts +41 -23
- package/src/components/panels/SlideshowPanel.test.ts +27 -0
- package/src/components/panels/SlideshowPanel.tsx +12 -7
- package/src/contexts/DomEditContext.test.tsx +62 -0
- package/src/contexts/DomEditContext.tsx +94 -77
- package/src/hooks/canvasQuickSaves.test.tsx +72 -7
- package/src/hooks/domEditCommitTypes.ts +10 -0
- package/src/hooks/domEditPersistFailure.ts +12 -1
- package/src/hooks/domStyleCommit.fonts.test.ts +5 -4
- package/src/hooks/domStyleCommit.ts +2 -8
- package/src/hooks/gsapDragCommit.ts +3 -8
- package/src/hooks/gsapDragPositionCommit.ts +6 -1
- package/src/hooks/gsapEditOutcome.ts +2 -1
- package/src/hooks/gsapResizeElementSize.test.tsx +136 -0
- package/src/hooks/gsapResizeGeometrySweep.test.ts +3 -1
- package/src/hooks/gsapResizeIntercept.test.ts +3 -11
- package/src/hooks/gsapResizeIntercept.ts +6 -2
- package/src/hooks/gsapResizeMixedTween.test.ts +1 -2
- package/src/hooks/gsapRuntimeBridge.test.ts +55 -0
- package/src/hooks/gsapRuntimeBridge.ts +5 -3
- package/src/hooks/gsapRuntimeKeyframes.ts +1 -3
- package/src/hooks/inspectorSaveReload.test.tsx +107 -0
- package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +16 -1
- package/src/hooks/useAppHotkeys.test.ts +17 -0
- package/src/hooks/useAppHotkeys.textEditing.test.tsx +1 -1
- package/src/hooks/useAppHotkeys.ts +3 -3
- package/src/hooks/useAutomationSelectionKeyboard.test.tsx +17 -0
- package/src/hooks/useAutomationSelectionKeyboard.ts +3 -9
- package/src/hooks/useDomEditAttributeCommits.ts +71 -29
- package/src/hooks/useDomEditCommits.test.tsx +172 -92
- package/src/hooks/useDomEditCommits.ts +2 -0
- package/src/hooks/useDomEditCommitsHelpers.ts +4 -6
- package/src/hooks/useDomEditPersist.ts +28 -13
- package/src/hooks/useDomEditPositionPatchCommit.ts +2 -0
- package/src/hooks/useDomEditSession.ts +2 -3
- package/src/hooks/useDomEditTextCommits.ts +4 -8
- package/src/hooks/useDomGeometryCommits.ts +11 -2
- package/src/hooks/useEditHistoryActions.paint.test.tsx +122 -3
- package/src/hooks/useGsapAwareEditing.ts +6 -6
- package/src/hooks/useGsapScriptCommits.test.tsx +19 -0
- package/src/hooks/useGsapScriptCommits.ts +5 -4
- package/src/hooks/useInlineTextEdit.test.tsx +27 -0
- package/src/hooks/useInlineTextEdit.ts +9 -7
- package/src/hooks/useKeyframeKeyboard.test.tsx +15 -0
- package/src/hooks/useKeyframeKeyboard.ts +2 -8
- package/src/hooks/usePreviewPersistence.ts +6 -0
- package/src/hooks/useTrackPendingTimelineEdit.ts +2 -6
- package/src/index.ts +7 -0
- package/src/player/components/Timeline.test.ts +0 -21
- package/src/player/components/Timeline.tsx +0 -1
- package/src/player/components/timelineLayout.ts +0 -28
- package/src/player/hooks/usePlaybackKeyboard.test.ts +43 -0
- package/src/player/lib/playbackShortcuts.ts +3 -7
- package/src/player/lib/timelineElementHelpers.test.ts +16 -0
- package/src/player/lib/timelineElementHelpers.ts +16 -2
- package/src/player/lib/timelineViewportBudgets.test.ts +2 -0
- package/src/player/lib/timelineViewportBudgets.ts +4 -0
- package/src/utils/gsapSoftReload.ts +3 -2
- package/src/utils/gsapUndoRestore.browser.test.ts +94 -0
- package/src/utils/gsapUndoRestore.test.ts +145 -1
- package/src/utils/gsapUndoRestore.ts +70 -21
- package/src/utils/sourcePatcher.ts +1 -1
- package/src/utils/studioFontHelpers.ts +2 -23
- package/src/utils/studioHelpers.test.ts +30 -0
- package/src/utils/studioHelpers.ts +18 -4
- package/src/utils/studioPendingEdits.test.ts +63 -0
- package/src/utils/studioPendingEdits.ts +16 -7
- package/src/utils/typingTarget.test.ts +13 -0
- package/src/utils/typingTarget.ts +5 -2
- package/src/webmcp/useStudioAgentTools.ts +3 -1
- package/dist/chunk-ICTZJPQQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
isPreviewBooted,
|
|
42
42
|
isRevealedAudioFxRequestCurrent,
|
|
43
43
|
isTimelineIgnoredElement,
|
|
44
|
+
isVideoAudible,
|
|
44
45
|
liveTime,
|
|
45
46
|
panelsInZone,
|
|
46
47
|
parseDockLayout,
|
|
@@ -67,7 +68,7 @@ import {
|
|
|
67
68
|
whenPreviewBooted,
|
|
68
69
|
wrapTimeline,
|
|
69
70
|
writeStudioUiPreferences
|
|
70
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-4OZC426H.js";
|
|
71
72
|
import {
|
|
72
73
|
Button,
|
|
73
74
|
ContextMenu,
|
|
@@ -9976,7 +9977,395 @@ import { useCallback as useCallback16 } from "react";
|
|
|
9976
9977
|
|
|
9977
9978
|
// src/contexts/DomEditContext.tsx
|
|
9978
9979
|
import { useCallback as useCallback15, useContext as useContext3, useMemo as useMemo9, useRef as useRef16 } from "react";
|
|
9980
|
+
|
|
9981
|
+
// src/utils/studioSaveDiagnostics.ts
|
|
9982
|
+
var STUDIO_SAVE_ATTEMPT_PROPERTY = "__studioSaveAttempt";
|
|
9983
|
+
var StudioSaveHttpError = class extends Error {
|
|
9984
|
+
statusCode;
|
|
9985
|
+
alreadyToasted;
|
|
9986
|
+
constructor(message, statusCode, options = {}) {
|
|
9987
|
+
super(message);
|
|
9988
|
+
this.name = "StudioSaveHttpError";
|
|
9989
|
+
this.statusCode = statusCode;
|
|
9990
|
+
this.alreadyToasted = options.alreadyToasted ?? false;
|
|
9991
|
+
}
|
|
9992
|
+
};
|
|
9993
|
+
var StudioSaveNetworkError = class extends Error {
|
|
9994
|
+
constructor(message, options) {
|
|
9995
|
+
super(message, options);
|
|
9996
|
+
this.name = "StudioSaveNetworkError";
|
|
9997
|
+
}
|
|
9998
|
+
};
|
|
9999
|
+
var StudioFileConflictError = class extends StudioSaveHttpError {
|
|
10000
|
+
filePath;
|
|
10001
|
+
currentVersion;
|
|
10002
|
+
currentContent;
|
|
10003
|
+
attemptedContent;
|
|
10004
|
+
constructor(input) {
|
|
10005
|
+
super(`Save conflict: ${input.filePath} changed outside this Studio session`, 409);
|
|
10006
|
+
this.name = "StudioFileConflictError";
|
|
10007
|
+
this.filePath = input.filePath;
|
|
10008
|
+
this.currentVersion = input.currentVersion;
|
|
10009
|
+
this.currentContent = input.currentContent;
|
|
10010
|
+
this.attemptedContent = input.attemptedContent;
|
|
10011
|
+
}
|
|
10012
|
+
};
|
|
10013
|
+
function readNumericProperty(value, key2) {
|
|
10014
|
+
const record = value;
|
|
10015
|
+
const property = record[key2];
|
|
10016
|
+
return typeof property === "number" && Number.isFinite(property) ? property : void 0;
|
|
10017
|
+
}
|
|
10018
|
+
function createStudioSaveAbortError() {
|
|
10019
|
+
if (typeof DOMException !== "undefined") return new DOMException("Save aborted", "AbortError");
|
|
10020
|
+
const error = new Error("Save aborted");
|
|
10021
|
+
error.name = "AbortError";
|
|
10022
|
+
return error;
|
|
10023
|
+
}
|
|
10024
|
+
function throwIfStudioSaveAborted(signal) {
|
|
10025
|
+
if (signal?.aborted) throw createStudioSaveAbortError();
|
|
10026
|
+
}
|
|
10027
|
+
function attachStudioSaveAttempt(error, attempt) {
|
|
10028
|
+
if (!error || typeof error !== "object") return error;
|
|
10029
|
+
try {
|
|
10030
|
+
Object.defineProperty(error, STUDIO_SAVE_ATTEMPT_PROPERTY, {
|
|
10031
|
+
value: attempt,
|
|
10032
|
+
configurable: true
|
|
10033
|
+
});
|
|
10034
|
+
} catch {
|
|
10035
|
+
}
|
|
10036
|
+
return error;
|
|
10037
|
+
}
|
|
10038
|
+
function getStudioSaveErrorMessage(error) {
|
|
10039
|
+
if (error instanceof Error && error.message) return error.message;
|
|
10040
|
+
if (typeof error === "string" && error.trim()) return error;
|
|
10041
|
+
return "Unknown save failure";
|
|
10042
|
+
}
|
|
10043
|
+
function markStudioSaveErrorAlreadyToasted(error) {
|
|
10044
|
+
if (!error || typeof error !== "object") return error;
|
|
10045
|
+
try {
|
|
10046
|
+
Object.defineProperty(error, "alreadyToasted", { value: true, configurable: true });
|
|
10047
|
+
} catch {
|
|
10048
|
+
}
|
|
10049
|
+
return error;
|
|
10050
|
+
}
|
|
10051
|
+
function isStudioSaveErrorAlreadyToasted(error) {
|
|
10052
|
+
if (!error || typeof error !== "object") return false;
|
|
10053
|
+
if (error.alreadyToasted === true) return true;
|
|
10054
|
+
const cause = error.cause;
|
|
10055
|
+
return cause !== error && isStudioSaveErrorAlreadyToasted(cause);
|
|
10056
|
+
}
|
|
10057
|
+
function getStudioSaveStatusCode(error) {
|
|
10058
|
+
if (!error || typeof error !== "object") return void 0;
|
|
10059
|
+
const direct = readNumericProperty(error, "statusCode") ?? readNumericProperty(error, "status") ?? readNumericProperty(error, "status_code");
|
|
10060
|
+
if (direct != null) return direct;
|
|
10061
|
+
const cause = error.cause;
|
|
10062
|
+
if (cause && cause !== error) return getStudioSaveStatusCode(cause);
|
|
10063
|
+
return void 0;
|
|
10064
|
+
}
|
|
10065
|
+
function getStudioSaveAttempt(error) {
|
|
10066
|
+
if (!error || typeof error !== "object") return void 0;
|
|
10067
|
+
const direct = readNumericProperty(error, STUDIO_SAVE_ATTEMPT_PROPERTY);
|
|
10068
|
+
if (direct != null) return direct;
|
|
10069
|
+
const cause = error.cause;
|
|
10070
|
+
if (cause && cause !== error) return getStudioSaveAttempt(cause);
|
|
10071
|
+
return void 0;
|
|
10072
|
+
}
|
|
10073
|
+
function isStudioSaveAbortError(error) {
|
|
10074
|
+
return error instanceof Error && error.name === "AbortError";
|
|
10075
|
+
}
|
|
10076
|
+
function isRetryableStudioSaveError(error) {
|
|
10077
|
+
if (isStudioSaveAbortError(error)) return false;
|
|
10078
|
+
if (error instanceof StudioSaveNetworkError) return true;
|
|
10079
|
+
const statusCode = getStudioSaveStatusCode(error);
|
|
10080
|
+
if (statusCode == null) return false;
|
|
10081
|
+
return statusCode === 408 || statusCode === 425 || statusCode === 429 || statusCode >= 500;
|
|
10082
|
+
}
|
|
10083
|
+
function buildStudioSaveFailureProperties(input) {
|
|
10084
|
+
const statusCode = input.statusCode ?? getStudioSaveStatusCode(input.error) ?? null;
|
|
10085
|
+
const attempt = input.attempt ?? getStudioSaveAttempt(input.error) ?? void 0;
|
|
10086
|
+
return {
|
|
10087
|
+
source: input.source,
|
|
10088
|
+
error_message: getStudioSaveErrorMessage(input.error),
|
|
10089
|
+
status_code: statusCode,
|
|
10090
|
+
file_path: input.filePath ?? input.targetSourceFile ?? void 0,
|
|
10091
|
+
mutation_type: input.mutationType ?? void 0,
|
|
10092
|
+
attempt,
|
|
10093
|
+
label: input.label ?? void 0,
|
|
10094
|
+
target_id: input.targetId ?? void 0,
|
|
10095
|
+
target_selector: input.targetSelector ?? void 0,
|
|
10096
|
+
target_source_file: input.targetSourceFile ?? void 0,
|
|
10097
|
+
block_detail: gsapBlockDetail(input.error)
|
|
10098
|
+
};
|
|
10099
|
+
}
|
|
10100
|
+
function gsapBlockDetail(error) {
|
|
10101
|
+
if (!(error instanceof Error) || error.name !== "GsapEditBlockedError") return void 0;
|
|
10102
|
+
const detail = error.detail;
|
|
10103
|
+
return typeof detail === "string" ? detail : void 0;
|
|
10104
|
+
}
|
|
10105
|
+
function trackStudioSaveFailure(input) {
|
|
10106
|
+
trackStudioEvent("save_failure", buildStudioSaveFailureProperties(input));
|
|
10107
|
+
}
|
|
10108
|
+
function trackStudioEditBlocked(input) {
|
|
10109
|
+
trackStudioEvent("edit_blocked", buildStudioSaveFailureProperties(input));
|
|
10110
|
+
}
|
|
10111
|
+
async function createStudioSaveHttpError(response, fallbackMessage, options = {}) {
|
|
10112
|
+
let body = "";
|
|
10113
|
+
try {
|
|
10114
|
+
body = await response.text();
|
|
10115
|
+
} catch {
|
|
10116
|
+
body = "";
|
|
10117
|
+
}
|
|
10118
|
+
const detail = body.trim().slice(0, 300);
|
|
10119
|
+
const message = detail ? `${fallbackMessage} (${response.status}): ${detail}` : `${fallbackMessage} (${response.status})`;
|
|
10120
|
+
return new StudioSaveHttpError(message, response.status, options);
|
|
10121
|
+
}
|
|
10122
|
+
async function retryStudioSave(operation, options = {}) {
|
|
10123
|
+
const retries = options.retries ?? 3;
|
|
10124
|
+
const baseDelayMs = options.baseDelayMs ?? 500;
|
|
10125
|
+
const maxDelayMs = options.maxDelayMs ?? 8e3;
|
|
10126
|
+
const jitterRatio = options.jitterRatio ?? 0.25;
|
|
10127
|
+
const random = options.random ?? Math.random;
|
|
10128
|
+
const shouldRetry = options.shouldRetry ?? isRetryableStudioSaveError;
|
|
10129
|
+
const sleep = options.sleep ?? ((delayMs, signal) => new Promise((resolve, reject) => {
|
|
10130
|
+
throwIfStudioSaveAborted(signal);
|
|
10131
|
+
const onAbort = () => {
|
|
10132
|
+
globalThis.clearTimeout(timeout);
|
|
10133
|
+
reject(createStudioSaveAbortError());
|
|
10134
|
+
};
|
|
10135
|
+
const timeout = globalThis.setTimeout(() => {
|
|
10136
|
+
signal?.removeEventListener("abort", onAbort);
|
|
10137
|
+
resolve();
|
|
10138
|
+
}, delayMs);
|
|
10139
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
10140
|
+
}));
|
|
10141
|
+
const maxAttempts = retries + 1;
|
|
10142
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
10143
|
+
try {
|
|
10144
|
+
throwIfStudioSaveAborted(options.signal);
|
|
10145
|
+
return await operation(attempt);
|
|
10146
|
+
} catch (error) {
|
|
10147
|
+
const failure = attachStudioSaveAttempt(error, attempt);
|
|
10148
|
+
if (attempt >= maxAttempts || !shouldRetry(failure, attempt)) throw failure;
|
|
10149
|
+
const retryIndex = attempt - 1;
|
|
10150
|
+
const exponentialDelay = Math.min(baseDelayMs * 2 ** retryIndex, maxDelayMs);
|
|
10151
|
+
const jitterSpan = exponentialDelay * jitterRatio;
|
|
10152
|
+
const jitteredDelay = Math.round(exponentialDelay + (random() * 2 - 1) * jitterSpan);
|
|
10153
|
+
const delayMs = Math.max(0, Math.min(maxDelayMs, jitteredDelay));
|
|
10154
|
+
await sleep(delayMs, options.signal);
|
|
10155
|
+
}
|
|
10156
|
+
}
|
|
10157
|
+
throw new Error("Save retry loop exited unexpectedly");
|
|
10158
|
+
}
|
|
10159
|
+
|
|
10160
|
+
// src/utils/typingTarget.ts
|
|
10161
|
+
function isTypingTarget(target) {
|
|
10162
|
+
const element = asElement(target);
|
|
10163
|
+
if (!element) return false;
|
|
10164
|
+
if (element.isContentEditable) return true;
|
|
10165
|
+
return element.closest(TYPING_SELECTOR) !== null;
|
|
10166
|
+
}
|
|
10167
|
+
function ownsPlainKeys(target) {
|
|
10168
|
+
return isTypingTarget(target) || asElement(target)?.closest(KEY_OWNING_CONTROLS) != null;
|
|
10169
|
+
}
|
|
10170
|
+
var KEY_OWNING_CONTROLS = "[role='slider'],video[controls],audio[controls]";
|
|
10171
|
+
var TYPING_SELECTOR = [
|
|
10172
|
+
"input",
|
|
10173
|
+
"textarea",
|
|
10174
|
+
"select",
|
|
10175
|
+
"[contenteditable]:not([contenteditable='false'])",
|
|
10176
|
+
"[role='textbox']",
|
|
10177
|
+
"[role='searchbox']",
|
|
10178
|
+
"[role='combobox']",
|
|
10179
|
+
// A switch is a button in the DOM, but the native checkbox it replaces was an
|
|
10180
|
+
// `input` and matched above. Without this row the shared Toggle would let a
|
|
10181
|
+
// global shortcut claim Space instead of flipping the control (KTD13).
|
|
10182
|
+
"[role='switch']",
|
|
10183
|
+
".cm-editor"
|
|
10184
|
+
].join(",");
|
|
10185
|
+
function asElement(target) {
|
|
10186
|
+
if (!target || typeof target !== "object") return null;
|
|
10187
|
+
const candidate = target;
|
|
10188
|
+
return typeof candidate.closest === "function" ? target : null;
|
|
10189
|
+
}
|
|
10190
|
+
|
|
10191
|
+
// src/utils/studioPendingEdits.ts
|
|
10192
|
+
var STUDIO_FLUSH_PENDING_EDITS_EVENT = "hf-studio-flush-pending-edits";
|
|
10193
|
+
var pendingEditPromises = /* @__PURE__ */ new Set();
|
|
10194
|
+
function waitForPostBlurEffects() {
|
|
10195
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
10196
|
+
}
|
|
10197
|
+
function inspectDrainFailures(results) {
|
|
10198
|
+
let firstFailure;
|
|
10199
|
+
for (const result of results) {
|
|
10200
|
+
if (result.status !== "rejected") continue;
|
|
10201
|
+
if (result.reason instanceof StudioFileConflictError) return { conflict: result.reason };
|
|
10202
|
+
firstFailure ??= result;
|
|
10203
|
+
}
|
|
10204
|
+
return { firstFailure };
|
|
10205
|
+
}
|
|
10206
|
+
function focusedField() {
|
|
10207
|
+
const active = document.activeElement;
|
|
10208
|
+
return active instanceof HTMLElement && isTypingTarget(active) ? active : null;
|
|
10209
|
+
}
|
|
10210
|
+
function hasStudioPendingEdits() {
|
|
10211
|
+
return pendingEditPromises.size > 0 || focusedField() !== null;
|
|
10212
|
+
}
|
|
10213
|
+
function isStudioEditSaving() {
|
|
10214
|
+
return pendingEditPromises.size > 0;
|
|
10215
|
+
}
|
|
10216
|
+
function afterStudioPendingEdits(run) {
|
|
10217
|
+
let waiting = true;
|
|
10218
|
+
const check = () => {
|
|
10219
|
+
if (!waiting) return;
|
|
10220
|
+
if (isStudioEditSaving()) {
|
|
10221
|
+
void Promise.allSettled([...pendingEditPromises]).then(check);
|
|
10222
|
+
return;
|
|
10223
|
+
}
|
|
10224
|
+
waiting = false;
|
|
10225
|
+
run();
|
|
10226
|
+
};
|
|
10227
|
+
check();
|
|
10228
|
+
return () => {
|
|
10229
|
+
waiting = false;
|
|
10230
|
+
};
|
|
10231
|
+
}
|
|
10232
|
+
function trackStudioPendingEdit(result) {
|
|
10233
|
+
if (!result) return void 0;
|
|
10234
|
+
const promise = Promise.resolve(result);
|
|
10235
|
+
pendingEditPromises.add(promise);
|
|
10236
|
+
promise.then(
|
|
10237
|
+
() => pendingEditPromises.delete(promise),
|
|
10238
|
+
() => pendingEditPromises.delete(promise)
|
|
10239
|
+
);
|
|
10240
|
+
return promise;
|
|
10241
|
+
}
|
|
10242
|
+
function trackedStudioEdit(edit) {
|
|
10243
|
+
return (...args) => {
|
|
10244
|
+
const result = edit(...args);
|
|
10245
|
+
if (result instanceof Promise) trackStudioPendingEdit(result);
|
|
10246
|
+
return result;
|
|
10247
|
+
};
|
|
10248
|
+
}
|
|
10249
|
+
async function flushStudioPendingEdits() {
|
|
10250
|
+
const active = focusedField();
|
|
10251
|
+
if (active) {
|
|
10252
|
+
active.blur();
|
|
10253
|
+
await Promise.resolve();
|
|
10254
|
+
await waitForPostBlurEffects();
|
|
10255
|
+
}
|
|
10256
|
+
const detail = { promises: [] };
|
|
10257
|
+
window.dispatchEvent(
|
|
10258
|
+
new CustomEvent(STUDIO_FLUSH_PENDING_EDITS_EVENT, { detail })
|
|
10259
|
+
);
|
|
10260
|
+
let conflict;
|
|
10261
|
+
let firstFailure;
|
|
10262
|
+
while (detail.promises.length > 0 || pendingEditPromises.size > 0) {
|
|
10263
|
+
const promises = [...detail.promises, ...pendingEditPromises];
|
|
10264
|
+
detail.promises = [];
|
|
10265
|
+
const batchFailures = inspectDrainFailures(await Promise.allSettled(promises));
|
|
10266
|
+
conflict ??= batchFailures.conflict;
|
|
10267
|
+
firstFailure ??= batchFailures.firstFailure;
|
|
10268
|
+
}
|
|
10269
|
+
if (conflict) return { status: "conflict", error: conflict };
|
|
10270
|
+
return firstFailure ? { status: "failed", error: firstFailure.reason } : { status: "clean" };
|
|
10271
|
+
}
|
|
10272
|
+
function addStudioPendingEditFlushListener(handler) {
|
|
10273
|
+
const listener2 = (event) => {
|
|
10274
|
+
const detail = event.detail;
|
|
10275
|
+
if (!detail?.promises) return;
|
|
10276
|
+
const promise = trackStudioPendingEdit(handler());
|
|
10277
|
+
if (promise) detail.promises.push(promise);
|
|
10278
|
+
};
|
|
10279
|
+
window.addEventListener(STUDIO_FLUSH_PENDING_EDITS_EVENT, listener2);
|
|
10280
|
+
return () => window.removeEventListener(STUDIO_FLUSH_PENDING_EDITS_EVENT, listener2);
|
|
10281
|
+
}
|
|
10282
|
+
|
|
10283
|
+
// src/contexts/DomEditContext.tsx
|
|
9979
10284
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
10285
|
+
var SAVES_AN_EDIT = {
|
|
10286
|
+
handleTimelineElementSelect: false,
|
|
10287
|
+
handlePreviewCanvasMouseDown: false,
|
|
10288
|
+
handlePreviewCanvasPointerMove: false,
|
|
10289
|
+
handlePreviewCanvasPointerLeave: false,
|
|
10290
|
+
applyDomSelection: false,
|
|
10291
|
+
clearDomSelection: false,
|
|
10292
|
+
refreshDomEditSelectionFromPreview: false,
|
|
10293
|
+
handleDomStyleCommit: true,
|
|
10294
|
+
handleDomStyleCommitForSelection: true,
|
|
10295
|
+
handleDomAttributeCommit: true,
|
|
10296
|
+
handleDomAttributeLiveCommit: false,
|
|
10297
|
+
handleDomAttributeQuietCommit: true,
|
|
10298
|
+
handleDomHtmlAttributeCommit: true,
|
|
10299
|
+
handleDomAttributesCommit: true,
|
|
10300
|
+
handleDomAttributeBatchCommit: true,
|
|
10301
|
+
handleDomPathOffsetCommit: true,
|
|
10302
|
+
handleDomGroupPathOffsetCommit: true,
|
|
10303
|
+
handleDomZIndexReorderCommit: true,
|
|
10304
|
+
handleDomBoxSizeCommit: true,
|
|
10305
|
+
handleDomRotationCommit: true,
|
|
10306
|
+
handleDomManualEditsReset: true,
|
|
10307
|
+
handleDomTextCommit: true,
|
|
10308
|
+
handleDomTextCommitForSelection: true,
|
|
10309
|
+
handleDomRichTextCommit: true,
|
|
10310
|
+
handleDomTextFieldStyleCommit: true,
|
|
10311
|
+
handleDomAddTextField: true,
|
|
10312
|
+
handleDomRemoveTextField: true,
|
|
10313
|
+
getGsapAnimationsForSelection: false,
|
|
10314
|
+
handleAskAgent: false,
|
|
10315
|
+
handleAgentModalSubmit: false,
|
|
10316
|
+
handleBlockedDomMove: false,
|
|
10317
|
+
handleDomManualDragStart: false,
|
|
10318
|
+
handleDomEditElementDelete: true,
|
|
10319
|
+
handleGroupSelection: true,
|
|
10320
|
+
handleUngroupSelection: true,
|
|
10321
|
+
setActiveGroupElement: false,
|
|
10322
|
+
buildDomSelectionFromTarget: false,
|
|
10323
|
+
buildDomSelectionForTimelineElement: false,
|
|
10324
|
+
updateDomEditHoverSelection: false,
|
|
10325
|
+
resolveImportedFontAsset: false,
|
|
10326
|
+
setAgentModalOpen: false,
|
|
10327
|
+
setAgentPromptSelectionContext: false,
|
|
10328
|
+
setAgentModalAnchorPoint: false,
|
|
10329
|
+
handleGsapUpdateProperty: true,
|
|
10330
|
+
handleGsapUpdateMeta: true,
|
|
10331
|
+
handleGsapDeleteAnimation: true,
|
|
10332
|
+
handleGsapDeleteAllForElement: true,
|
|
10333
|
+
handleGsapAddAnimation: true,
|
|
10334
|
+
handleGsapAddProperty: true,
|
|
10335
|
+
handleGsapRemoveProperty: true,
|
|
10336
|
+
handleGsapUpdateFromProperty: true,
|
|
10337
|
+
handleGsapAddFromProperty: true,
|
|
10338
|
+
handleGsapRemoveFromProperty: true,
|
|
10339
|
+
handleGsapAddKeyframe: true,
|
|
10340
|
+
handleGsapAddKeyframeBatch: true,
|
|
10341
|
+
handleGsapRemoveKeyframe: true,
|
|
10342
|
+
handleGsapMoveKeyframeToPlayhead: true,
|
|
10343
|
+
handleGsapMoveKeyframe: true,
|
|
10344
|
+
handleGsapResizeKeyframedTween: true,
|
|
10345
|
+
handleGsapConvertToKeyframes: true,
|
|
10346
|
+
handleGsapRemoveAllKeyframes: true,
|
|
10347
|
+
handleResetSelectedElementKeyframes: true,
|
|
10348
|
+
commitAnimatedProperty: true,
|
|
10349
|
+
commitAnimatedProperties: true,
|
|
10350
|
+
handleSetArcPath: true,
|
|
10351
|
+
handleUpdateArcSegment: true,
|
|
10352
|
+
handleUnroll: true,
|
|
10353
|
+
invalidateGsapCache: false,
|
|
10354
|
+
previewIframeRef: false,
|
|
10355
|
+
commitMutation: true,
|
|
10356
|
+
applyMarqueeSelection: false,
|
|
10357
|
+
handleUpdateKeyframeEase: true,
|
|
10358
|
+
handleUpdateSegmentEase: true,
|
|
10359
|
+
handleSetAllKeyframeEases: true
|
|
10360
|
+
};
|
|
10361
|
+
function trackEditCommits(actions) {
|
|
10362
|
+
const tracked = { ...actions };
|
|
10363
|
+
for (const key2 of Object.keys(SAVES_AN_EDIT)) {
|
|
10364
|
+
if (!SAVES_AN_EDIT[key2]) continue;
|
|
10365
|
+
tracked[key2] = trackedStudioEdit(actions[key2]);
|
|
10366
|
+
}
|
|
10367
|
+
return tracked;
|
|
10368
|
+
}
|
|
9980
10369
|
var DomEditActionsContext = createStableContext(
|
|
9981
10370
|
"DomEditActionsContext",
|
|
9982
10371
|
null
|
|
@@ -10028,6 +10417,7 @@ function DomEditProvider({
|
|
|
10028
10417
|
handleDomAttributeQuietCommit,
|
|
10029
10418
|
handleDomHtmlAttributeCommit,
|
|
10030
10419
|
handleDomAttributesCommit,
|
|
10420
|
+
handleDomAttributeBatchCommit,
|
|
10031
10421
|
handleDomPathOffsetCommit,
|
|
10032
10422
|
handleDomGroupPathOffsetCommit,
|
|
10033
10423
|
handleDomZIndexReorderCommit,
|
|
@@ -10100,7 +10490,7 @@ function DomEditProvider({
|
|
|
10100
10490
|
(mutation, options) => commitMutationRef.current(mutation, options),
|
|
10101
10491
|
[]
|
|
10102
10492
|
);
|
|
10103
|
-
const
|
|
10493
|
+
const untrackedActions = useMemo9(
|
|
10104
10494
|
() => ({
|
|
10105
10495
|
handleTimelineElementSelect,
|
|
10106
10496
|
handlePreviewCanvasMouseDown,
|
|
@@ -10116,6 +10506,7 @@ function DomEditProvider({
|
|
|
10116
10506
|
handleDomAttributeQuietCommit,
|
|
10117
10507
|
handleDomHtmlAttributeCommit,
|
|
10118
10508
|
handleDomAttributesCommit,
|
|
10509
|
+
handleDomAttributeBatchCommit,
|
|
10119
10510
|
handleDomPathOffsetCommit,
|
|
10120
10511
|
handleDomGroupPathOffsetCommit,
|
|
10121
10512
|
handleDomZIndexReorderCommit,
|
|
@@ -10191,6 +10582,7 @@ function DomEditProvider({
|
|
|
10191
10582
|
handleDomAttributeQuietCommit,
|
|
10192
10583
|
handleDomHtmlAttributeCommit,
|
|
10193
10584
|
handleDomAttributesCommit,
|
|
10585
|
+
handleDomAttributeBatchCommit,
|
|
10194
10586
|
handleDomPathOffsetCommit,
|
|
10195
10587
|
handleDomGroupPathOffsetCommit,
|
|
10196
10588
|
handleDomZIndexReorderCommit,
|
|
@@ -10252,6 +10644,7 @@ function DomEditProvider({
|
|
|
10252
10644
|
handleSetAllKeyframeEases
|
|
10253
10645
|
]
|
|
10254
10646
|
);
|
|
10647
|
+
const actions = useMemo9(() => trackEditCommits(untrackedActions), [untrackedActions]);
|
|
10255
10648
|
const selection = useMemo9(
|
|
10256
10649
|
() => ({
|
|
10257
10650
|
domEditSelection,
|
|
@@ -11855,6 +12248,8 @@ var TIMELINE_VIEWPORT_BUDGETS = Object.freeze({
|
|
|
11855
12248
|
frameIntervalP95Ms: 33.3,
|
|
11856
12249
|
constrainedInteractionP95Ms: 75,
|
|
11857
12250
|
constrainedFrameIntervalP95Ms: 75,
|
|
12251
|
+
ciVirtualizedInteractionP95Ms: 58.3,
|
|
12252
|
+
ciVirtualizedFrameIntervalP95Ms: 25,
|
|
11858
12253
|
longTaskLimitMs: 50,
|
|
11859
12254
|
constrainedLongTaskLimitMs: 300,
|
|
11860
12255
|
memoryReturnToleranceRatio: 0.15,
|
|
@@ -16956,36 +17351,6 @@ import {
|
|
|
16956
17351
|
resolveAutomationRange as resolveAutomationRange5
|
|
16957
17352
|
} from "@hyperframes/core/audio-automation";
|
|
16958
17353
|
|
|
16959
|
-
// src/utils/typingTarget.ts
|
|
16960
|
-
function isTypingTarget(target) {
|
|
16961
|
-
const element = asElement(target);
|
|
16962
|
-
if (!element) return false;
|
|
16963
|
-
if (element.isContentEditable) return true;
|
|
16964
|
-
return element.closest(TYPING_SELECTOR) !== null;
|
|
16965
|
-
}
|
|
16966
|
-
function ownsPlainKeys(target) {
|
|
16967
|
-
return isTypingTarget(target) || asElement(target)?.closest("[role='slider']") != null;
|
|
16968
|
-
}
|
|
16969
|
-
var TYPING_SELECTOR = [
|
|
16970
|
-
"input",
|
|
16971
|
-
"textarea",
|
|
16972
|
-
"select",
|
|
16973
|
-
"[contenteditable]:not([contenteditable='false'])",
|
|
16974
|
-
"[role='textbox']",
|
|
16975
|
-
"[role='searchbox']",
|
|
16976
|
-
"[role='combobox']",
|
|
16977
|
-
// A switch is a button in the DOM, but the native checkbox it replaces was an
|
|
16978
|
-
// `input` and matched above. Without this row the shared Toggle would let a
|
|
16979
|
-
// global shortcut claim Space instead of flipping the control (KTD13).
|
|
16980
|
-
"[role='switch']",
|
|
16981
|
-
".cm-editor"
|
|
16982
|
-
].join(",");
|
|
16983
|
-
function asElement(target) {
|
|
16984
|
-
if (!target || typeof target !== "object") return null;
|
|
16985
|
-
const candidate = target;
|
|
16986
|
-
return typeof candidate.closest === "function" ? target : null;
|
|
16987
|
-
}
|
|
16988
|
-
|
|
16989
17354
|
// src/utils/studioHelpers.ts
|
|
16990
17355
|
function getTimelineElementLabel(element) {
|
|
16991
17356
|
return element.label || element.id || element.tag;
|
|
@@ -17167,23 +17532,25 @@ function mediaMetadataUrl(projectId, assetPath) {
|
|
|
17167
17532
|
}
|
|
17168
17533
|
async function resolveDroppedAssetHasAudio(projectId, assetPath, kind) {
|
|
17169
17534
|
if (kind !== "video") return false;
|
|
17535
|
+
return await resolveAssetHasAudio(projectId, assetPath) === true;
|
|
17536
|
+
}
|
|
17537
|
+
function isPlainRecord(value) {
|
|
17538
|
+
return typeof value === "object" && value !== null;
|
|
17539
|
+
}
|
|
17540
|
+
async function resolveAssetHasAudio(projectId, assetPath) {
|
|
17170
17541
|
try {
|
|
17171
17542
|
const response = await fetch(mediaMetadataUrl(projectId, assetPath));
|
|
17172
|
-
if (!response.ok) return
|
|
17543
|
+
if (!response.ok) return null;
|
|
17173
17544
|
const data = await response.json();
|
|
17174
|
-
|
|
17545
|
+
const metadata = isPlainRecord(data) ? data.metadata : void 0;
|
|
17546
|
+
const hasAudio = isPlainRecord(metadata) ? metadata.hasAudio : void 0;
|
|
17547
|
+
return typeof hasAudio === "boolean" ? hasAudio : null;
|
|
17175
17548
|
} catch {
|
|
17176
|
-
return
|
|
17549
|
+
return null;
|
|
17177
17550
|
}
|
|
17178
17551
|
}
|
|
17179
17552
|
|
|
17180
17553
|
// src/hooks/useAutomationSelectionKeyboard.ts
|
|
17181
|
-
function isTextInput(el) {
|
|
17182
|
-
if (!el) return false;
|
|
17183
|
-
const tag = el.tagName;
|
|
17184
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
17185
|
-
return el instanceof HTMLElement && el.isContentEditable;
|
|
17186
|
-
}
|
|
17187
17554
|
function isChord(e, letter) {
|
|
17188
17555
|
return (e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey && e.key.toLowerCase() === letter;
|
|
17189
17556
|
}
|
|
@@ -17286,7 +17653,7 @@ function handleCopy(e, state, lanes, sel) {
|
|
|
17286
17653
|
return true;
|
|
17287
17654
|
}
|
|
17288
17655
|
function automationOwnsKey(e) {
|
|
17289
|
-
if (
|
|
17656
|
+
if (isTypingTarget(document.activeElement)) return false;
|
|
17290
17657
|
const state = usePlayerStore.getState();
|
|
17291
17658
|
if (isChord(e, "c")) return state.automationSelection !== null;
|
|
17292
17659
|
if (!isChord(e, "v")) return false;
|
|
@@ -17311,7 +17678,7 @@ function useAutomationSelectionKeyboard({
|
|
|
17311
17678
|
}) {
|
|
17312
17679
|
useEffect25(() => {
|
|
17313
17680
|
const handler = (e) => {
|
|
17314
|
-
if (
|
|
17681
|
+
if (isTypingTarget(document.activeElement)) return;
|
|
17315
17682
|
if (e.defaultPrevented) return;
|
|
17316
17683
|
const state = usePlayerStore.getState();
|
|
17317
17684
|
if (handlePaste(e, state, lanes)) return;
|
|
@@ -19908,185 +20275,6 @@ function serializeStudioFileMutations(writer, targetPaths, task) {
|
|
|
19908
20275
|
return acquire(0);
|
|
19909
20276
|
}
|
|
19910
20277
|
|
|
19911
|
-
// src/utils/studioSaveDiagnostics.ts
|
|
19912
|
-
var STUDIO_SAVE_ATTEMPT_PROPERTY = "__studioSaveAttempt";
|
|
19913
|
-
var StudioSaveHttpError = class extends Error {
|
|
19914
|
-
statusCode;
|
|
19915
|
-
alreadyToasted;
|
|
19916
|
-
constructor(message, statusCode, options = {}) {
|
|
19917
|
-
super(message);
|
|
19918
|
-
this.name = "StudioSaveHttpError";
|
|
19919
|
-
this.statusCode = statusCode;
|
|
19920
|
-
this.alreadyToasted = options.alreadyToasted ?? false;
|
|
19921
|
-
}
|
|
19922
|
-
};
|
|
19923
|
-
var StudioSaveNetworkError = class extends Error {
|
|
19924
|
-
constructor(message, options) {
|
|
19925
|
-
super(message, options);
|
|
19926
|
-
this.name = "StudioSaveNetworkError";
|
|
19927
|
-
}
|
|
19928
|
-
};
|
|
19929
|
-
var StudioFileConflictError = class extends StudioSaveHttpError {
|
|
19930
|
-
filePath;
|
|
19931
|
-
currentVersion;
|
|
19932
|
-
currentContent;
|
|
19933
|
-
attemptedContent;
|
|
19934
|
-
constructor(input) {
|
|
19935
|
-
super(`Save conflict: ${input.filePath} changed outside this Studio session`, 409);
|
|
19936
|
-
this.name = "StudioFileConflictError";
|
|
19937
|
-
this.filePath = input.filePath;
|
|
19938
|
-
this.currentVersion = input.currentVersion;
|
|
19939
|
-
this.currentContent = input.currentContent;
|
|
19940
|
-
this.attemptedContent = input.attemptedContent;
|
|
19941
|
-
}
|
|
19942
|
-
};
|
|
19943
|
-
function readNumericProperty(value, key2) {
|
|
19944
|
-
const record = value;
|
|
19945
|
-
const property = record[key2];
|
|
19946
|
-
return typeof property === "number" && Number.isFinite(property) ? property : void 0;
|
|
19947
|
-
}
|
|
19948
|
-
function createStudioSaveAbortError() {
|
|
19949
|
-
if (typeof DOMException !== "undefined") return new DOMException("Save aborted", "AbortError");
|
|
19950
|
-
const error = new Error("Save aborted");
|
|
19951
|
-
error.name = "AbortError";
|
|
19952
|
-
return error;
|
|
19953
|
-
}
|
|
19954
|
-
function throwIfStudioSaveAborted(signal) {
|
|
19955
|
-
if (signal?.aborted) throw createStudioSaveAbortError();
|
|
19956
|
-
}
|
|
19957
|
-
function attachStudioSaveAttempt(error, attempt) {
|
|
19958
|
-
if (!error || typeof error !== "object") return error;
|
|
19959
|
-
try {
|
|
19960
|
-
Object.defineProperty(error, STUDIO_SAVE_ATTEMPT_PROPERTY, {
|
|
19961
|
-
value: attempt,
|
|
19962
|
-
configurable: true
|
|
19963
|
-
});
|
|
19964
|
-
} catch {
|
|
19965
|
-
}
|
|
19966
|
-
return error;
|
|
19967
|
-
}
|
|
19968
|
-
function getStudioSaveErrorMessage(error) {
|
|
19969
|
-
if (error instanceof Error && error.message) return error.message;
|
|
19970
|
-
if (typeof error === "string" && error.trim()) return error;
|
|
19971
|
-
return "Unknown save failure";
|
|
19972
|
-
}
|
|
19973
|
-
function markStudioSaveErrorAlreadyToasted(error) {
|
|
19974
|
-
if (!error || typeof error !== "object") return error;
|
|
19975
|
-
try {
|
|
19976
|
-
Object.defineProperty(error, "alreadyToasted", { value: true, configurable: true });
|
|
19977
|
-
} catch {
|
|
19978
|
-
}
|
|
19979
|
-
return error;
|
|
19980
|
-
}
|
|
19981
|
-
function isStudioSaveErrorAlreadyToasted(error) {
|
|
19982
|
-
if (!error || typeof error !== "object") return false;
|
|
19983
|
-
if (error.alreadyToasted === true) return true;
|
|
19984
|
-
const cause = error.cause;
|
|
19985
|
-
return cause !== error && isStudioSaveErrorAlreadyToasted(cause);
|
|
19986
|
-
}
|
|
19987
|
-
function getStudioSaveStatusCode(error) {
|
|
19988
|
-
if (!error || typeof error !== "object") return void 0;
|
|
19989
|
-
const direct = readNumericProperty(error, "statusCode") ?? readNumericProperty(error, "status") ?? readNumericProperty(error, "status_code");
|
|
19990
|
-
if (direct != null) return direct;
|
|
19991
|
-
const cause = error.cause;
|
|
19992
|
-
if (cause && cause !== error) return getStudioSaveStatusCode(cause);
|
|
19993
|
-
return void 0;
|
|
19994
|
-
}
|
|
19995
|
-
function getStudioSaveAttempt(error) {
|
|
19996
|
-
if (!error || typeof error !== "object") return void 0;
|
|
19997
|
-
const direct = readNumericProperty(error, STUDIO_SAVE_ATTEMPT_PROPERTY);
|
|
19998
|
-
if (direct != null) return direct;
|
|
19999
|
-
const cause = error.cause;
|
|
20000
|
-
if (cause && cause !== error) return getStudioSaveAttempt(cause);
|
|
20001
|
-
return void 0;
|
|
20002
|
-
}
|
|
20003
|
-
function isStudioSaveAbortError(error) {
|
|
20004
|
-
return error instanceof Error && error.name === "AbortError";
|
|
20005
|
-
}
|
|
20006
|
-
function isRetryableStudioSaveError(error) {
|
|
20007
|
-
if (isStudioSaveAbortError(error)) return false;
|
|
20008
|
-
if (error instanceof StudioSaveNetworkError) return true;
|
|
20009
|
-
const statusCode = getStudioSaveStatusCode(error);
|
|
20010
|
-
if (statusCode == null) return false;
|
|
20011
|
-
return statusCode === 408 || statusCode === 425 || statusCode === 429 || statusCode >= 500;
|
|
20012
|
-
}
|
|
20013
|
-
function buildStudioSaveFailureProperties(input) {
|
|
20014
|
-
const statusCode = input.statusCode ?? getStudioSaveStatusCode(input.error) ?? null;
|
|
20015
|
-
const attempt = input.attempt ?? getStudioSaveAttempt(input.error) ?? void 0;
|
|
20016
|
-
return {
|
|
20017
|
-
source: input.source,
|
|
20018
|
-
error_message: getStudioSaveErrorMessage(input.error),
|
|
20019
|
-
status_code: statusCode,
|
|
20020
|
-
file_path: input.filePath ?? input.targetSourceFile ?? void 0,
|
|
20021
|
-
mutation_type: input.mutationType ?? void 0,
|
|
20022
|
-
attempt,
|
|
20023
|
-
label: input.label ?? void 0,
|
|
20024
|
-
target_id: input.targetId ?? void 0,
|
|
20025
|
-
target_selector: input.targetSelector ?? void 0,
|
|
20026
|
-
target_source_file: input.targetSourceFile ?? void 0,
|
|
20027
|
-
block_detail: gsapBlockDetail(input.error)
|
|
20028
|
-
};
|
|
20029
|
-
}
|
|
20030
|
-
function gsapBlockDetail(error) {
|
|
20031
|
-
if (!(error instanceof Error) || error.name !== "GsapEditBlockedError") return void 0;
|
|
20032
|
-
const detail = error.detail;
|
|
20033
|
-
return typeof detail === "string" ? detail : void 0;
|
|
20034
|
-
}
|
|
20035
|
-
function trackStudioSaveFailure(input) {
|
|
20036
|
-
trackStudioEvent("save_failure", buildStudioSaveFailureProperties(input));
|
|
20037
|
-
}
|
|
20038
|
-
function trackStudioEditBlocked(input) {
|
|
20039
|
-
trackStudioEvent("edit_blocked", buildStudioSaveFailureProperties(input));
|
|
20040
|
-
}
|
|
20041
|
-
async function createStudioSaveHttpError(response, fallbackMessage, options = {}) {
|
|
20042
|
-
let body = "";
|
|
20043
|
-
try {
|
|
20044
|
-
body = await response.text();
|
|
20045
|
-
} catch {
|
|
20046
|
-
body = "";
|
|
20047
|
-
}
|
|
20048
|
-
const detail = body.trim().slice(0, 300);
|
|
20049
|
-
const message = detail ? `${fallbackMessage} (${response.status}): ${detail}` : `${fallbackMessage} (${response.status})`;
|
|
20050
|
-
return new StudioSaveHttpError(message, response.status, options);
|
|
20051
|
-
}
|
|
20052
|
-
async function retryStudioSave(operation, options = {}) {
|
|
20053
|
-
const retries = options.retries ?? 3;
|
|
20054
|
-
const baseDelayMs = options.baseDelayMs ?? 500;
|
|
20055
|
-
const maxDelayMs = options.maxDelayMs ?? 8e3;
|
|
20056
|
-
const jitterRatio = options.jitterRatio ?? 0.25;
|
|
20057
|
-
const random = options.random ?? Math.random;
|
|
20058
|
-
const shouldRetry = options.shouldRetry ?? isRetryableStudioSaveError;
|
|
20059
|
-
const sleep = options.sleep ?? ((delayMs, signal) => new Promise((resolve, reject) => {
|
|
20060
|
-
throwIfStudioSaveAborted(signal);
|
|
20061
|
-
const onAbort = () => {
|
|
20062
|
-
globalThis.clearTimeout(timeout);
|
|
20063
|
-
reject(createStudioSaveAbortError());
|
|
20064
|
-
};
|
|
20065
|
-
const timeout = globalThis.setTimeout(() => {
|
|
20066
|
-
signal?.removeEventListener("abort", onAbort);
|
|
20067
|
-
resolve();
|
|
20068
|
-
}, delayMs);
|
|
20069
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
20070
|
-
}));
|
|
20071
|
-
const maxAttempts = retries + 1;
|
|
20072
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
20073
|
-
try {
|
|
20074
|
-
throwIfStudioSaveAborted(options.signal);
|
|
20075
|
-
return await operation(attempt);
|
|
20076
|
-
} catch (error) {
|
|
20077
|
-
const failure = attachStudioSaveAttempt(error, attempt);
|
|
20078
|
-
if (attempt >= maxAttempts || !shouldRetry(failure, attempt)) throw failure;
|
|
20079
|
-
const retryIndex = attempt - 1;
|
|
20080
|
-
const exponentialDelay = Math.min(baseDelayMs * 2 ** retryIndex, maxDelayMs);
|
|
20081
|
-
const jitterSpan = exponentialDelay * jitterRatio;
|
|
20082
|
-
const jitteredDelay = Math.round(exponentialDelay + (random() * 2 - 1) * jitterSpan);
|
|
20083
|
-
const delayMs = Math.max(0, Math.min(maxDelayMs, jitteredDelay));
|
|
20084
|
-
await sleep(delayMs, options.signal);
|
|
20085
|
-
}
|
|
20086
|
-
}
|
|
20087
|
-
throw new Error("Save retry loop exited unexpectedly");
|
|
20088
|
-
}
|
|
20089
|
-
|
|
20090
20278
|
// src/utils/studioFileHistory.ts
|
|
20091
20279
|
async function readProjectFileContent(pid, path) {
|
|
20092
20280
|
const response = await fetch(buildProjectApiPath(pid, `/files/${encodeURIComponent(path)}`));
|
|
@@ -20357,7 +20545,7 @@ function applySoftReloadFinalization(iframe, currentTime) {
|
|
|
20357
20545
|
}
|
|
20358
20546
|
}
|
|
20359
20547
|
function applySoftReload(iframe, scriptText, options = {}) {
|
|
20360
|
-
const { onAsyncFailure, currentTimeOverride, authoredHtml } = options;
|
|
20548
|
+
const { onAsyncFailure, currentTimeOverride, authoredHtml, reparse = [] } = options;
|
|
20361
20549
|
if (!iframe || !scriptText) return "cannot-soft-reload";
|
|
20362
20550
|
const win = iframe.contentWindow;
|
|
20363
20551
|
const doc = iframe.contentDocument;
|
|
@@ -20391,7 +20579,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
|
|
|
20391
20579
|
const readAuthoredOpacity = (el) => findAuthoredStyle(el)?.opacity ?? readStampedAuthoredOpacity(el);
|
|
20392
20580
|
const doReload = () => {
|
|
20393
20581
|
const timelines = win.__timelines;
|
|
20394
|
-
const allTargets = [];
|
|
20582
|
+
const allTargets = [...reparse];
|
|
20395
20583
|
if (timelines) {
|
|
20396
20584
|
for (const key2 of targetKeys) {
|
|
20397
20585
|
const tl = timelines[key2];
|
|
@@ -24244,18 +24432,14 @@ var PLAYBACK_SHORTCUT_IGNORED_SELECTOR = [
|
|
|
24244
24432
|
"a[href]",
|
|
24245
24433
|
"[role='button']",
|
|
24246
24434
|
"[role='checkbox']",
|
|
24247
|
-
"[role='combobox']",
|
|
24248
24435
|
"[role='menuitem']",
|
|
24249
24436
|
// Base UI's menu radio item is a `<div>`, so `button` above no longer catches it.
|
|
24250
24437
|
"[role='menuitemradio']",
|
|
24251
24438
|
"[role='radio']",
|
|
24252
|
-
"[role='
|
|
24253
|
-
"[role='spinbutton']",
|
|
24254
|
-
"[role='switch']",
|
|
24255
|
-
"[role='textbox']"
|
|
24439
|
+
"[role='spinbutton']"
|
|
24256
24440
|
].join(",");
|
|
24257
24441
|
function shouldIgnorePlaybackShortcutTarget(target) {
|
|
24258
|
-
if (
|
|
24442
|
+
if (ownsPlainKeys(target)) return true;
|
|
24259
24443
|
if (!target || typeof target !== "object") return false;
|
|
24260
24444
|
const candidate = target;
|
|
24261
24445
|
if (typeof candidate.closest !== "function") return false;
|
|
@@ -25729,6 +25913,7 @@ var STUDIO_MANUAL_EDITS_PLAYBACK_FRAME_PROP = "__hfStudioManualEditsPlaybackFram
|
|
|
25729
25913
|
var STUDIO_ROTATION_TRANSFORM_ORIGIN = "center center";
|
|
25730
25914
|
|
|
25731
25915
|
// src/components/editor/gsapAnimatesProperty.ts
|
|
25916
|
+
var gsapRendersTransform = (el) => !!el._gsap?.renderTransform;
|
|
25732
25917
|
function gsapAnimatesProperty(el, ...props) {
|
|
25733
25918
|
const win = el.ownerDocument.defaultView;
|
|
25734
25919
|
if (!win?.__timelines) return false;
|
|
@@ -25785,11 +25970,136 @@ function splitTopLevelWhitespace(value) {
|
|
|
25785
25970
|
return parts;
|
|
25786
25971
|
}
|
|
25787
25972
|
|
|
25973
|
+
// src/components/editor/manualEditsDomPatches.ts
|
|
25974
|
+
function collectInlineStyleOps(element, properties, ops) {
|
|
25975
|
+
for (const prop of properties) {
|
|
25976
|
+
const val = element.style.getPropertyValue(prop);
|
|
25977
|
+
if (val) ops.push({ type: "inline-style", property: prop, value: val });
|
|
25978
|
+
}
|
|
25979
|
+
}
|
|
25980
|
+
function collectAttributeOps(element, attrNames, ops) {
|
|
25981
|
+
for (const attr of attrNames) {
|
|
25982
|
+
const val = element.getAttribute(attr);
|
|
25983
|
+
if (val !== null) ops.push({ type: "attribute", property: attr, value: val });
|
|
25984
|
+
}
|
|
25985
|
+
}
|
|
25986
|
+
function appendTransformDisplayOps(element, ops) {
|
|
25987
|
+
const val = element.getAttribute(STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR);
|
|
25988
|
+
if (val !== null) {
|
|
25989
|
+
ops.push({ type: "inline-style", property: "display", value: val || null });
|
|
25990
|
+
ops.push({ type: "attribute", property: STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, value: null });
|
|
25991
|
+
}
|
|
25992
|
+
}
|
|
25993
|
+
function buildClearPathOffsetPatches(element) {
|
|
25994
|
+
const originalInlineTranslate = element.getAttribute(STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR);
|
|
25995
|
+
const ops = [
|
|
25996
|
+
{ type: "inline-style", property: STUDIO_OFFSET_X_PROP, value: null },
|
|
25997
|
+
{ type: "inline-style", property: STUDIO_OFFSET_Y_PROP, value: null },
|
|
25998
|
+
{ type: "inline-style", property: "translate", value: originalInlineTranslate || null },
|
|
25999
|
+
{ type: "attribute", property: STUDIO_PATH_OFFSET_ATTR, value: null },
|
|
26000
|
+
{ type: "attribute", property: STUDIO_ORIGINAL_TRANSLATE_ATTR, value: null },
|
|
26001
|
+
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR, value: null }
|
|
26002
|
+
];
|
|
26003
|
+
appendTransformDisplayOps(element, ops);
|
|
26004
|
+
return ops;
|
|
26005
|
+
}
|
|
26006
|
+
var BOX_SIZE_STYLE_PROPS = [
|
|
26007
|
+
"width",
|
|
26008
|
+
"height",
|
|
26009
|
+
"min-width",
|
|
26010
|
+
"min-height",
|
|
26011
|
+
"max-width",
|
|
26012
|
+
"max-height",
|
|
26013
|
+
"flex-basis",
|
|
26014
|
+
"flex-grow",
|
|
26015
|
+
"flex-shrink",
|
|
26016
|
+
"box-sizing",
|
|
26017
|
+
"scale",
|
|
26018
|
+
"transform-origin",
|
|
26019
|
+
"display"
|
|
26020
|
+
];
|
|
26021
|
+
var BOX_SIZE_ORIG_ATTRS = [
|
|
26022
|
+
[STUDIO_ORIGINAL_WIDTH_ATTR, "width"],
|
|
26023
|
+
[STUDIO_ORIGINAL_HEIGHT_ATTR, "height"],
|
|
26024
|
+
// Records a measurement rather than a style, so it restores nothing.
|
|
26025
|
+
[STUDIO_ORIGINAL_BOX_WIDTH_ATTR, ""],
|
|
26026
|
+
[STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, ""],
|
|
26027
|
+
[STUDIO_ORIGINAL_MIN_WIDTH_ATTR, "min-width"],
|
|
26028
|
+
[STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, "min-height"],
|
|
26029
|
+
[STUDIO_ORIGINAL_MAX_WIDTH_ATTR, "max-width"],
|
|
26030
|
+
[STUDIO_ORIGINAL_MAX_HEIGHT_ATTR, "max-height"],
|
|
26031
|
+
[STUDIO_ORIGINAL_FLEX_BASIS_ATTR, "flex-basis"],
|
|
26032
|
+
[STUDIO_ORIGINAL_FLEX_GROW_ATTR, "flex-grow"],
|
|
26033
|
+
[STUDIO_ORIGINAL_FLEX_SHRINK_ATTR, "flex-shrink"],
|
|
26034
|
+
[STUDIO_ORIGINAL_BOX_SIZING_ATTR, "box-sizing"],
|
|
26035
|
+
[STUDIO_ORIGINAL_SCALE_ATTR, "scale"],
|
|
26036
|
+
[STUDIO_ORIGINAL_TRANSFORM_ORIGIN_ATTR, "transform-origin"],
|
|
26037
|
+
[STUDIO_ORIGINAL_DISPLAY_ATTR, "display"],
|
|
26038
|
+
[STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, ""]
|
|
26039
|
+
];
|
|
26040
|
+
function buildBoxSizePatches(element) {
|
|
26041
|
+
const ops = [];
|
|
26042
|
+
collectInlineStyleOps(element, [STUDIO_WIDTH_PROP, STUDIO_HEIGHT_PROP], ops);
|
|
26043
|
+
const withoutGsapScaleMask = gsapRendersTransform(element) ? BOX_SIZE_STYLE_PROPS.filter((prop) => prop !== "scale") : BOX_SIZE_STYLE_PROPS;
|
|
26044
|
+
collectInlineStyleOps(element, withoutGsapScaleMask, ops);
|
|
26045
|
+
ops.push({ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: "true" });
|
|
26046
|
+
collectAttributeOps(
|
|
26047
|
+
element,
|
|
26048
|
+
BOX_SIZE_ORIG_ATTRS.map(([attr]) => attr),
|
|
26049
|
+
ops
|
|
26050
|
+
);
|
|
26051
|
+
return ops;
|
|
26052
|
+
}
|
|
26053
|
+
function buildClearBoxSizePatches(element) {
|
|
26054
|
+
const ops = [
|
|
26055
|
+
{ type: "inline-style", property: STUDIO_WIDTH_PROP, value: null },
|
|
26056
|
+
{ type: "inline-style", property: STUDIO_HEIGHT_PROP, value: null },
|
|
26057
|
+
{ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: null }
|
|
26058
|
+
];
|
|
26059
|
+
for (const [attrName, styleProp] of BOX_SIZE_ORIG_ATTRS) {
|
|
26060
|
+
const origVal = element.getAttribute(attrName);
|
|
26061
|
+
if (origVal !== null && styleProp) {
|
|
26062
|
+
ops.push({ type: "inline-style", property: styleProp, value: origVal || null });
|
|
26063
|
+
}
|
|
26064
|
+
ops.push({ type: "attribute", property: attrName, value: null });
|
|
26065
|
+
}
|
|
26066
|
+
return ops;
|
|
26067
|
+
}
|
|
26068
|
+
function buildClearRotationPatches(element) {
|
|
26069
|
+
const origInlineRotate = element.getAttribute(STUDIO_ORIGINAL_INLINE_ROTATE_ATTR);
|
|
26070
|
+
const origRotationTransformOrigin = element.getAttribute(
|
|
26071
|
+
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR
|
|
26072
|
+
);
|
|
26073
|
+
const ops = [
|
|
26074
|
+
{ type: "inline-style", property: STUDIO_ROTATION_PROP, value: null },
|
|
26075
|
+
{ type: "inline-style", property: "rotate", value: origInlineRotate || null },
|
|
26076
|
+
{
|
|
26077
|
+
type: "inline-style",
|
|
26078
|
+
property: "transform-origin",
|
|
26079
|
+
value: origRotationTransformOrigin !== null ? origRotationTransformOrigin || null : null
|
|
26080
|
+
},
|
|
26081
|
+
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: null },
|
|
26082
|
+
{ type: "attribute", property: STUDIO_ROTATION_DRAFT_ATTR, value: null },
|
|
26083
|
+
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATE_ATTR, value: null },
|
|
26084
|
+
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, value: null },
|
|
26085
|
+
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, value: null }
|
|
26086
|
+
];
|
|
26087
|
+
appendTransformDisplayOps(element, ops);
|
|
26088
|
+
return ops;
|
|
26089
|
+
}
|
|
26090
|
+
|
|
25788
26091
|
// src/components/editor/manualEditsDom.ts
|
|
25789
26092
|
var studioManualEditGestureId = 0;
|
|
25790
|
-
|
|
26093
|
+
var MOVE_DRAWS = ["translate", STUDIO_OFFSET_X_PROP, STUDIO_OFFSET_Y_PROP];
|
|
26094
|
+
var GESTURE_DRAWS = {
|
|
26095
|
+
move: MOVE_DRAWS,
|
|
26096
|
+
resize: [...MOVE_DRAWS, STUDIO_WIDTH_PROP, STUDIO_HEIGHT_PROP, ...BOX_SIZE_STYLE_PROPS],
|
|
26097
|
+
rotate: ["rotate", "transform", "transform-origin", "display", STUDIO_ROTATION_PROP],
|
|
26098
|
+
edit: []
|
|
26099
|
+
};
|
|
26100
|
+
function beginStudioManualEditGesture(element, draws) {
|
|
25791
26101
|
studioManualEditGestureId += 1;
|
|
25792
|
-
const token = `gesture-${studioManualEditGestureId}`;
|
|
26102
|
+
const token = `gesture-${studioManualEditGestureId}:${draws}`;
|
|
25793
26103
|
element.setAttribute(STUDIO_MANUAL_EDIT_GESTURE_ATTR, token);
|
|
25794
26104
|
return token;
|
|
25795
26105
|
}
|
|
@@ -25818,9 +26128,18 @@ var gestureSaves = /* @__PURE__ */ new WeakMap();
|
|
|
25818
26128
|
function studioManualEditSavesIn(doc) {
|
|
25819
26129
|
return gestureSaves.get(doc) ?? 0;
|
|
25820
26130
|
}
|
|
25821
|
-
function
|
|
26131
|
+
function countStudioManualEditSave(element, save) {
|
|
25822
26132
|
const doc = element.ownerDocument;
|
|
25823
|
-
gestureSaves.set(doc, studioManualEditSavesIn(doc) + 1);
|
|
26133
|
+
const count = () => void gestureSaves.set(doc, studioManualEditSavesIn(doc) + 1);
|
|
26134
|
+
count();
|
|
26135
|
+
const result = save();
|
|
26136
|
+
void Promise.resolve(result).then(count, count);
|
|
26137
|
+
return result;
|
|
26138
|
+
}
|
|
26139
|
+
function studioGestureDraws(element) {
|
|
26140
|
+
const token = element.getAttribute(STUDIO_MANUAL_EDIT_GESTURE_ATTR);
|
|
26141
|
+
if (token === null) return null;
|
|
26142
|
+
return GESTURE_DRAWS[token.split(":")[1]] ?? [];
|
|
25824
26143
|
}
|
|
25825
26144
|
function isStudioManualEditGestureActive(element) {
|
|
25826
26145
|
return element.hasAttribute(STUDIO_MANUAL_EDIT_GESTURE_ATTR);
|
|
@@ -26264,90 +26583,6 @@ function sceneSwapFor(iframe) {
|
|
|
26264
26583
|
};
|
|
26265
26584
|
}
|
|
26266
26585
|
|
|
26267
|
-
// src/utils/studioPendingEdits.ts
|
|
26268
|
-
var STUDIO_FLUSH_PENDING_EDITS_EVENT = "hf-studio-flush-pending-edits";
|
|
26269
|
-
var pendingEditPromises = /* @__PURE__ */ new Set();
|
|
26270
|
-
function waitForPostBlurEffects() {
|
|
26271
|
-
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
26272
|
-
}
|
|
26273
|
-
function inspectDrainFailures(results) {
|
|
26274
|
-
let firstFailure;
|
|
26275
|
-
for (const result of results) {
|
|
26276
|
-
if (result.status !== "rejected") continue;
|
|
26277
|
-
if (result.reason instanceof StudioFileConflictError) return { conflict: result.reason };
|
|
26278
|
-
firstFailure ??= result;
|
|
26279
|
-
}
|
|
26280
|
-
return { firstFailure };
|
|
26281
|
-
}
|
|
26282
|
-
function focusedField() {
|
|
26283
|
-
const active = document.activeElement;
|
|
26284
|
-
return active instanceof HTMLElement && active.matches('input, textarea, select, [contenteditable="true"], [role="textbox"]') ? active : null;
|
|
26285
|
-
}
|
|
26286
|
-
function hasStudioPendingEdits() {
|
|
26287
|
-
return pendingEditPromises.size > 0 || focusedField() !== null;
|
|
26288
|
-
}
|
|
26289
|
-
function isStudioEditSaving() {
|
|
26290
|
-
return pendingEditPromises.size > 0;
|
|
26291
|
-
}
|
|
26292
|
-
function afterStudioPendingEdits(run) {
|
|
26293
|
-
let waiting = true;
|
|
26294
|
-
const check = () => {
|
|
26295
|
-
if (!waiting) return;
|
|
26296
|
-
if (isStudioEditSaving()) {
|
|
26297
|
-
void Promise.allSettled([...pendingEditPromises]).then(check);
|
|
26298
|
-
return;
|
|
26299
|
-
}
|
|
26300
|
-
waiting = false;
|
|
26301
|
-
run();
|
|
26302
|
-
};
|
|
26303
|
-
check();
|
|
26304
|
-
return () => {
|
|
26305
|
-
waiting = false;
|
|
26306
|
-
};
|
|
26307
|
-
}
|
|
26308
|
-
function trackStudioPendingEdit(result) {
|
|
26309
|
-
if (!result) return void 0;
|
|
26310
|
-
const promise = Promise.resolve(result);
|
|
26311
|
-
pendingEditPromises.add(promise);
|
|
26312
|
-
promise.then(
|
|
26313
|
-
() => pendingEditPromises.delete(promise),
|
|
26314
|
-
() => pendingEditPromises.delete(promise)
|
|
26315
|
-
);
|
|
26316
|
-
return promise;
|
|
26317
|
-
}
|
|
26318
|
-
async function flushStudioPendingEdits() {
|
|
26319
|
-
const active = focusedField();
|
|
26320
|
-
if (active) {
|
|
26321
|
-
active.blur();
|
|
26322
|
-
await Promise.resolve();
|
|
26323
|
-
await waitForPostBlurEffects();
|
|
26324
|
-
}
|
|
26325
|
-
const detail = { promises: [] };
|
|
26326
|
-
window.dispatchEvent(
|
|
26327
|
-
new CustomEvent(STUDIO_FLUSH_PENDING_EDITS_EVENT, { detail })
|
|
26328
|
-
);
|
|
26329
|
-
let firstFailure;
|
|
26330
|
-
while (detail.promises.length > 0 || pendingEditPromises.size > 0) {
|
|
26331
|
-
const promises = [...detail.promises, ...pendingEditPromises];
|
|
26332
|
-
detail.promises = [];
|
|
26333
|
-
const results = await Promise.allSettled(promises);
|
|
26334
|
-
const batchFailures = inspectDrainFailures(results);
|
|
26335
|
-
if (batchFailures.conflict) return { status: "conflict", error: batchFailures.conflict };
|
|
26336
|
-
firstFailure ??= batchFailures.firstFailure;
|
|
26337
|
-
}
|
|
26338
|
-
return firstFailure ? { status: "failed", error: firstFailure.reason } : { status: "clean" };
|
|
26339
|
-
}
|
|
26340
|
-
function addStudioPendingEditFlushListener(handler) {
|
|
26341
|
-
const listener2 = (event) => {
|
|
26342
|
-
const detail = event.detail;
|
|
26343
|
-
if (!detail?.promises) return;
|
|
26344
|
-
const promise = trackStudioPendingEdit(handler());
|
|
26345
|
-
if (promise) detail.promises.push(promise);
|
|
26346
|
-
};
|
|
26347
|
-
window.addEventListener(STUDIO_FLUSH_PENDING_EDITS_EVENT, listener2);
|
|
26348
|
-
return () => window.removeEventListener(STUDIO_FLUSH_PENDING_EDITS_EVENT, listener2);
|
|
26349
|
-
}
|
|
26350
|
-
|
|
26351
26586
|
// src/player/hooks/useShadowPreviewReload.ts
|
|
26352
26587
|
var SHADOW_READY_TIMEOUT_MS = 15e3;
|
|
26353
26588
|
var SHADOW_READY_BUDGETS = 3;
|
|
@@ -33045,125 +33280,6 @@ function computeDraggedGsapPosition(element, studioOffset, fallbackBase) {
|
|
|
33045
33280
|
|
|
33046
33281
|
// src/hooks/gsapRuntimeKeyframes.ts
|
|
33047
33282
|
import { buildArcPath } from "@hyperframes/core/gsap-parser-acorn";
|
|
33048
|
-
|
|
33049
|
-
// src/components/editor/manualEditsDomPatches.ts
|
|
33050
|
-
function collectInlineStyleOps(element, properties, ops) {
|
|
33051
|
-
for (const prop of properties) {
|
|
33052
|
-
const val = element.style.getPropertyValue(prop);
|
|
33053
|
-
if (val) ops.push({ type: "inline-style", property: prop, value: val });
|
|
33054
|
-
}
|
|
33055
|
-
}
|
|
33056
|
-
function collectAttributeOps(element, attrNames, ops) {
|
|
33057
|
-
for (const attr of attrNames) {
|
|
33058
|
-
const val = element.getAttribute(attr);
|
|
33059
|
-
if (val !== null) ops.push({ type: "attribute", property: attr, value: val });
|
|
33060
|
-
}
|
|
33061
|
-
}
|
|
33062
|
-
function appendTransformDisplayOps(element, ops) {
|
|
33063
|
-
const val = element.getAttribute(STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR);
|
|
33064
|
-
if (val !== null) {
|
|
33065
|
-
ops.push({ type: "inline-style", property: "display", value: val || null });
|
|
33066
|
-
ops.push({ type: "attribute", property: STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, value: null });
|
|
33067
|
-
}
|
|
33068
|
-
}
|
|
33069
|
-
function buildClearPathOffsetPatches(element) {
|
|
33070
|
-
const originalInlineTranslate = element.getAttribute(STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR);
|
|
33071
|
-
const ops = [
|
|
33072
|
-
{ type: "inline-style", property: STUDIO_OFFSET_X_PROP, value: null },
|
|
33073
|
-
{ type: "inline-style", property: STUDIO_OFFSET_Y_PROP, value: null },
|
|
33074
|
-
{ type: "inline-style", property: "translate", value: originalInlineTranslate || null },
|
|
33075
|
-
{ type: "attribute", property: STUDIO_PATH_OFFSET_ATTR, value: null },
|
|
33076
|
-
{ type: "attribute", property: STUDIO_ORIGINAL_TRANSLATE_ATTR, value: null },
|
|
33077
|
-
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR, value: null }
|
|
33078
|
-
];
|
|
33079
|
-
appendTransformDisplayOps(element, ops);
|
|
33080
|
-
return ops;
|
|
33081
|
-
}
|
|
33082
|
-
var BOX_SIZE_STYLE_PROPS = [
|
|
33083
|
-
"width",
|
|
33084
|
-
"height",
|
|
33085
|
-
"min-width",
|
|
33086
|
-
"min-height",
|
|
33087
|
-
"max-width",
|
|
33088
|
-
"max-height",
|
|
33089
|
-
"flex-basis",
|
|
33090
|
-
"flex-grow",
|
|
33091
|
-
"flex-shrink",
|
|
33092
|
-
"box-sizing",
|
|
33093
|
-
"scale",
|
|
33094
|
-
"transform-origin",
|
|
33095
|
-
"display"
|
|
33096
|
-
];
|
|
33097
|
-
var BOX_SIZE_ORIG_ATTRS = [
|
|
33098
|
-
[STUDIO_ORIGINAL_WIDTH_ATTR, "width"],
|
|
33099
|
-
[STUDIO_ORIGINAL_HEIGHT_ATTR, "height"],
|
|
33100
|
-
// Records a measurement rather than a style, so it restores nothing.
|
|
33101
|
-
[STUDIO_ORIGINAL_BOX_WIDTH_ATTR, ""],
|
|
33102
|
-
[STUDIO_ORIGINAL_BOX_HEIGHT_ATTR, ""],
|
|
33103
|
-
[STUDIO_ORIGINAL_MIN_WIDTH_ATTR, "min-width"],
|
|
33104
|
-
[STUDIO_ORIGINAL_MIN_HEIGHT_ATTR, "min-height"],
|
|
33105
|
-
[STUDIO_ORIGINAL_MAX_WIDTH_ATTR, "max-width"],
|
|
33106
|
-
[STUDIO_ORIGINAL_MAX_HEIGHT_ATTR, "max-height"],
|
|
33107
|
-
[STUDIO_ORIGINAL_FLEX_BASIS_ATTR, "flex-basis"],
|
|
33108
|
-
[STUDIO_ORIGINAL_FLEX_GROW_ATTR, "flex-grow"],
|
|
33109
|
-
[STUDIO_ORIGINAL_FLEX_SHRINK_ATTR, "flex-shrink"],
|
|
33110
|
-
[STUDIO_ORIGINAL_BOX_SIZING_ATTR, "box-sizing"],
|
|
33111
|
-
[STUDIO_ORIGINAL_SCALE_ATTR, "scale"],
|
|
33112
|
-
[STUDIO_ORIGINAL_TRANSFORM_ORIGIN_ATTR, "transform-origin"],
|
|
33113
|
-
[STUDIO_ORIGINAL_DISPLAY_ATTR, "display"],
|
|
33114
|
-
[STUDIO_ORIGINAL_TRANSFORM_DISPLAY_ATTR, ""]
|
|
33115
|
-
];
|
|
33116
|
-
function buildBoxSizePatches(element) {
|
|
33117
|
-
const ops = [];
|
|
33118
|
-
collectInlineStyleOps(element, [STUDIO_WIDTH_PROP, STUDIO_HEIGHT_PROP], ops);
|
|
33119
|
-
collectInlineStyleOps(element, BOX_SIZE_STYLE_PROPS, ops);
|
|
33120
|
-
ops.push({ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: "true" });
|
|
33121
|
-
collectAttributeOps(
|
|
33122
|
-
element,
|
|
33123
|
-
BOX_SIZE_ORIG_ATTRS.map(([attr]) => attr),
|
|
33124
|
-
ops
|
|
33125
|
-
);
|
|
33126
|
-
return ops;
|
|
33127
|
-
}
|
|
33128
|
-
function buildClearBoxSizePatches(element) {
|
|
33129
|
-
const ops = [
|
|
33130
|
-
{ type: "inline-style", property: STUDIO_WIDTH_PROP, value: null },
|
|
33131
|
-
{ type: "inline-style", property: STUDIO_HEIGHT_PROP, value: null },
|
|
33132
|
-
{ type: "attribute", property: STUDIO_BOX_SIZE_ATTR, value: null }
|
|
33133
|
-
];
|
|
33134
|
-
for (const [attrName, styleProp] of BOX_SIZE_ORIG_ATTRS) {
|
|
33135
|
-
const origVal = element.getAttribute(attrName);
|
|
33136
|
-
if (origVal !== null && styleProp) {
|
|
33137
|
-
ops.push({ type: "inline-style", property: styleProp, value: origVal || null });
|
|
33138
|
-
}
|
|
33139
|
-
ops.push({ type: "attribute", property: attrName, value: null });
|
|
33140
|
-
}
|
|
33141
|
-
return ops;
|
|
33142
|
-
}
|
|
33143
|
-
function buildClearRotationPatches(element) {
|
|
33144
|
-
const origInlineRotate = element.getAttribute(STUDIO_ORIGINAL_INLINE_ROTATE_ATTR);
|
|
33145
|
-
const origRotationTransformOrigin = element.getAttribute(
|
|
33146
|
-
STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR
|
|
33147
|
-
);
|
|
33148
|
-
const ops = [
|
|
33149
|
-
{ type: "inline-style", property: STUDIO_ROTATION_PROP, value: null },
|
|
33150
|
-
{ type: "inline-style", property: "rotate", value: origInlineRotate || null },
|
|
33151
|
-
{
|
|
33152
|
-
type: "inline-style",
|
|
33153
|
-
property: "transform-origin",
|
|
33154
|
-
value: origRotationTransformOrigin !== null ? origRotationTransformOrigin || null : null
|
|
33155
|
-
},
|
|
33156
|
-
{ type: "attribute", property: STUDIO_ROTATION_ATTR, value: null },
|
|
33157
|
-
{ type: "attribute", property: STUDIO_ROTATION_DRAFT_ATTR, value: null },
|
|
33158
|
-
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATE_ATTR, value: null },
|
|
33159
|
-
{ type: "attribute", property: STUDIO_ORIGINAL_INLINE_ROTATE_ATTR, value: null },
|
|
33160
|
-
{ type: "attribute", property: STUDIO_ORIGINAL_ROTATION_TRANSFORM_ORIGIN_ATTR, value: null }
|
|
33161
|
-
];
|
|
33162
|
-
appendTransformDisplayOps(element, ops);
|
|
33163
|
-
return ops;
|
|
33164
|
-
}
|
|
33165
|
-
|
|
33166
|
-
// src/hooks/gsapRuntimeKeyframes.ts
|
|
33167
33283
|
var FLAT_SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
33168
33284
|
"ease",
|
|
33169
33285
|
"duration",
|
|
@@ -33382,7 +33498,6 @@ var ROTATION_CHANNELS = [
|
|
|
33382
33498
|
...["rotation", "rotationX", "rotationY", "rotationZ"],
|
|
33383
33499
|
...["rotate", "rotateX", "rotateY", "rotateZ"]
|
|
33384
33500
|
];
|
|
33385
|
-
var gsapRendersTransform = (el) => !!el._gsap?.renderTransform;
|
|
33386
33501
|
function gsapWritesPosition(el) {
|
|
33387
33502
|
return gsapRendersTransform(el) || gsapWritesChannels(el, MOVE_CHANNELS);
|
|
33388
33503
|
}
|
|
@@ -33756,7 +33871,10 @@ function createManualOffsetDragMember(input) {
|
|
|
33756
33871
|
}
|
|
33757
33872
|
}
|
|
33758
33873
|
const initialPathOffset = captureStudioPathOffset(input.element);
|
|
33759
|
-
const gestureToken = beginStudioManualEditGesture(
|
|
33874
|
+
const gestureToken = beginStudioManualEditGesture(
|
|
33875
|
+
input.element,
|
|
33876
|
+
input.gesture === "resize" ? "resize" : "move"
|
|
33877
|
+
);
|
|
33760
33878
|
const measured = measureManualOffsetDragScreenToOffsetMatrix(input.element, initialOffset, {
|
|
33761
33879
|
scaleX: input.rect.editScaleX,
|
|
33762
33880
|
scaleY: input.rect.editScaleY,
|
|
@@ -34710,10 +34828,13 @@ function startGesture(kind, e, opts, options) {
|
|
|
34710
34828
|
manualEditDragToken = result.member.gestureToken;
|
|
34711
34829
|
if (result.member.plainTranslate) applyManualOffsetDragDraft(result.member, 0, 0);
|
|
34712
34830
|
} else {
|
|
34713
|
-
manualEditDragToken = beginStudioManualEditGesture(sel.element);
|
|
34831
|
+
manualEditDragToken = beginStudioManualEditGesture(sel.element, "resize");
|
|
34714
34832
|
}
|
|
34715
34833
|
} else {
|
|
34716
|
-
manualEditDragToken = beginStudioManualEditGesture(
|
|
34834
|
+
manualEditDragToken = beginStudioManualEditGesture(
|
|
34835
|
+
sel.element,
|
|
34836
|
+
kind === "rotate" ? "rotate" : "resize"
|
|
34837
|
+
);
|
|
34717
34838
|
}
|
|
34718
34839
|
}
|
|
34719
34840
|
try {
|
|
@@ -35413,25 +35534,31 @@ function useDomEditNudge(params) {
|
|
|
35413
35534
|
paramsRef.current = params;
|
|
35414
35535
|
const commitSession = () => {
|
|
35415
35536
|
const session = sessionRef.current;
|
|
35416
|
-
if (!session) return;
|
|
35537
|
+
if (!session) return void 0;
|
|
35417
35538
|
sessionRef.current = null;
|
|
35418
35539
|
if (session.timer) clearTimeout(session.timer);
|
|
35419
|
-
const updates = session.members.map((member) => ({
|
|
35420
|
-
selection: member.selection,
|
|
35421
|
-
next: applyManualOffsetNudgeCommit(member, session.accum),
|
|
35422
|
-
plainTranslate: member.plainTranslate
|
|
35423
|
-
}));
|
|
35424
35540
|
const p = paramsRef.current;
|
|
35425
|
-
|
|
35426
|
-
|
|
35427
|
-
|
|
35428
|
-
|
|
35429
|
-
|
|
35430
|
-
|
|
35431
|
-
|
|
35541
|
+
let saved2;
|
|
35542
|
+
try {
|
|
35543
|
+
const updates = session.members.map((member) => ({
|
|
35544
|
+
selection: member.selection,
|
|
35545
|
+
next: applyManualOffsetNudgeCommit(member, session.accum),
|
|
35546
|
+
plainTranslate: member.plainTranslate
|
|
35547
|
+
}));
|
|
35548
|
+
const commit = session.isGroup ? p.onGroupPathOffsetCommitRef.current(updates) : p.onPathOffsetCommitRef.current(updates[0].selection, updates[0].next, {
|
|
35549
|
+
plainTranslate: updates[0].plainTranslate
|
|
35550
|
+
});
|
|
35551
|
+
saved2 = Promise.resolve(commit).catch(() => {
|
|
35552
|
+
for (const member of session.members) {
|
|
35553
|
+
if (isStudioManualEditGestureCurrent(member.element, member.gestureToken)) {
|
|
35554
|
+
restoreStudioPathOffset(member.element, member.initialPathOffset);
|
|
35555
|
+
}
|
|
35432
35556
|
}
|
|
35433
|
-
}
|
|
35434
|
-
|
|
35557
|
+
}).finally(() => endManualOffsetDragMembers(session.members));
|
|
35558
|
+
return saved2;
|
|
35559
|
+
} finally {
|
|
35560
|
+
session.endPendingEdit(saved2);
|
|
35561
|
+
}
|
|
35435
35562
|
};
|
|
35436
35563
|
const commitSessionRef = useRef64(commitSession);
|
|
35437
35564
|
commitSessionRef.current = commitSession;
|
|
@@ -35442,6 +35569,7 @@ function useDomEditNudge(params) {
|
|
|
35442
35569
|
if (session.timer) clearTimeout(session.timer);
|
|
35443
35570
|
restoreManualOffsetDragMembers(session.members);
|
|
35444
35571
|
endManualOffsetDragMembers(session.members);
|
|
35572
|
+
session.endPendingEdit();
|
|
35445
35573
|
}, [params.allowCanvasMovement]);
|
|
35446
35574
|
const beginSession = (event) => {
|
|
35447
35575
|
const p = paramsRef.current;
|
|
@@ -35461,7 +35589,10 @@ function useDomEditNudge(params) {
|
|
|
35461
35589
|
}
|
|
35462
35590
|
if (members.length === 0) return null;
|
|
35463
35591
|
p.onManualDragStartRef.current?.();
|
|
35464
|
-
|
|
35592
|
+
let endPendingEdit = () => {
|
|
35593
|
+
};
|
|
35594
|
+
trackStudioPendingEdit(new Promise((resolve) => endPendingEdit = resolve));
|
|
35595
|
+
return { members, isGroup, accum: { x: 0, y: 0 }, timer: null, endPendingEdit };
|
|
35465
35596
|
};
|
|
35466
35597
|
const handleKeyDown = (event) => {
|
|
35467
35598
|
const p = paramsRef.current;
|
|
@@ -35483,14 +35614,18 @@ function useDomEditNudge(params) {
|
|
|
35483
35614
|
useMountEffect(() => {
|
|
35484
35615
|
const listener2 = (event) => handleKeyDownRef.current(event);
|
|
35485
35616
|
window.addEventListener("keydown", listener2, true);
|
|
35617
|
+
const stopCommitOnUndoDrain = addStudioPendingEditFlushListener(
|
|
35618
|
+
() => commitSessionRef.current()
|
|
35619
|
+
);
|
|
35486
35620
|
return () => {
|
|
35487
35621
|
window.removeEventListener("keydown", listener2, true);
|
|
35622
|
+
stopCommitOnUndoDrain();
|
|
35488
35623
|
commitSessionRef.current();
|
|
35489
35624
|
};
|
|
35490
35625
|
});
|
|
35491
35626
|
const selectionKey = selectionIdentityKey(params.selection);
|
|
35492
35627
|
const groupSelectionsKey = groupSelectionsIdentityKey(params.groupSelections);
|
|
35493
|
-
useEffect44(() => () => commitSessionRef.current(), [selectionKey, groupSelectionsKey]);
|
|
35628
|
+
useEffect44(() => () => void commitSessionRef.current(), [selectionKey, groupSelectionsKey]);
|
|
35494
35629
|
useEffect44(() => {
|
|
35495
35630
|
const targets = params.groupSelections.length > 1 ? params.groupSelections : params.selection ? [params.selection] : [];
|
|
35496
35631
|
if (!params.allowCanvasMovement || !canCanvasNudgeTargets(targets)) return;
|
|
@@ -37166,7 +37301,7 @@ function useInlineTextEdit({
|
|
|
37166
37301
|
original: element.innerHTML,
|
|
37167
37302
|
outline: element.style.outline,
|
|
37168
37303
|
outlineOffset: element.style.outlineOffset,
|
|
37169
|
-
gesture: beginStudioManualEditGesture(element)
|
|
37304
|
+
gesture: beginStudioManualEditGesture(element, "edit")
|
|
37170
37305
|
};
|
|
37171
37306
|
element.style.outline = EDITING_OUTLINE;
|
|
37172
37307
|
element.style.outlineOffset = "2px";
|
|
@@ -37194,9 +37329,11 @@ function useInlineTextEdit({
|
|
|
37194
37329
|
let save = () => {
|
|
37195
37330
|
};
|
|
37196
37331
|
trackStudioPendingEdit(new Promise((resolve) => save = resolve));
|
|
37197
|
-
|
|
37198
|
-
|
|
37199
|
-
|
|
37332
|
+
const commitText = () => {
|
|
37333
|
+
teardown();
|
|
37334
|
+
return onCommit({ element: open.element, html: html2, previousHtml: open.original });
|
|
37335
|
+
};
|
|
37336
|
+
save(countStudioManualEditSave(open.element, commitText));
|
|
37200
37337
|
}, [onCommit, teardown]);
|
|
37201
37338
|
const cancel = useCallback59(() => {
|
|
37202
37339
|
const open = openRef.current;
|
|
@@ -37649,29 +37786,20 @@ var DomEditOverlay = memo26(function DomEditOverlay2({
|
|
|
37649
37786
|
return () => onTextEditingChangeRef.current?.(false);
|
|
37650
37787
|
}, [inlineText.editing]);
|
|
37651
37788
|
const onPathOffsetCommitRef = useRef70(onPathOffsetCommit);
|
|
37652
|
-
onPathOffsetCommitRef.current = (sel, ...rest) =>
|
|
37653
|
-
noteStudioManualEditSave(sel.element);
|
|
37654
|
-
return onPathOffsetCommit(sel, ...rest);
|
|
37655
|
-
};
|
|
37789
|
+
onPathOffsetCommitRef.current = (sel, ...rest) => countStudioManualEditSave(sel.element, () => onPathOffsetCommit(sel, ...rest));
|
|
37656
37790
|
const onGroupPathOffsetCommitRef = useRef70(onGroupPathOffsetCommit);
|
|
37657
37791
|
onGroupPathOffsetCommitRef.current = (updates) => {
|
|
37658
|
-
|
|
37659
|
-
return
|
|
37792
|
+
const save = () => onGroupPathOffsetCommit(updates);
|
|
37793
|
+
return updates[0] ? countStudioManualEditSave(updates[0].selection.element, save) : save();
|
|
37660
37794
|
};
|
|
37661
37795
|
const onBoxSizeCommitRef = useRef70(onBoxSizeCommit);
|
|
37662
|
-
onBoxSizeCommitRef.current = (sel, ...rest) =>
|
|
37663
|
-
noteStudioManualEditSave(sel.element);
|
|
37664
|
-
return onBoxSizeCommit(sel, ...rest);
|
|
37665
|
-
};
|
|
37796
|
+
onBoxSizeCommitRef.current = (sel, ...rest) => countStudioManualEditSave(sel.element, () => onBoxSizeCommit(sel, ...rest));
|
|
37666
37797
|
const onRotationCommitRef = useRef70(onRotationCommit);
|
|
37667
|
-
onRotationCommitRef.current = (sel, next) =>
|
|
37668
|
-
noteStudioManualEditSave(sel.element);
|
|
37669
|
-
return onRotationCommit(sel, next);
|
|
37670
|
-
};
|
|
37798
|
+
onRotationCommitRef.current = (sel, next) => countStudioManualEditSave(sel.element, () => onRotationCommit(sel, next));
|
|
37671
37799
|
const onStyleCommitRef = useRef70(onStyleCommit);
|
|
37672
37800
|
onStyleCommitRef.current = onStyleCommit && ((property, value) => {
|
|
37673
|
-
|
|
37674
|
-
return
|
|
37801
|
+
const save = () => onStyleCommit(property, value);
|
|
37802
|
+
return selectionRef.current ? countStudioManualEditSave(selectionRef.current.element, save) : save();
|
|
37675
37803
|
});
|
|
37676
37804
|
const onBlockedMoveRef = useRef70(onBlockedMove);
|
|
37677
37805
|
onBlockedMoveRef.current = onBlockedMove;
|
|
@@ -42444,10 +42572,8 @@ var SnapToolbar = memo28(function SnapToolbar2() {
|
|
|
42444
42572
|
useEffect55(() => {
|
|
42445
42573
|
const handleKeyDown = (e) => {
|
|
42446
42574
|
if (e.defaultPrevented) return;
|
|
42447
|
-
|
|
42448
|
-
if (
|
|
42449
|
-
if (t2 instanceof HTMLElement && t2.isContentEditable) return;
|
|
42450
|
-
if (t2 instanceof HTMLIFrameElement) return;
|
|
42575
|
+
if (ownsPlainKeys(e.target)) return;
|
|
42576
|
+
if (e.target instanceof HTMLIFrameElement) return;
|
|
42451
42577
|
if (e.key === "s" && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
|
42452
42578
|
e.preventDefault();
|
|
42453
42579
|
updatePrefs({ snapEnabled: !prefs.snapEnabled });
|
|
@@ -42824,6 +42950,9 @@ import { useCallback as useCallback67, useMemo as useMemo44 } from "react";
|
|
|
42824
42950
|
// src/hooks/useGsapTweenCache.ts
|
|
42825
42951
|
import { useEffect as useEffect57, useMemo as useMemo43, useRef as useRef77, useState as useState49, useCallback as useCallback66 } from "react";
|
|
42826
42952
|
|
|
42953
|
+
// src/hooks/gsapRuntimeBridge.ts
|
|
42954
|
+
import { isXYPositionWrite } from "@hyperframes/parsers/gsap-constants";
|
|
42955
|
+
|
|
42827
42956
|
// src/hooks/gsapRuntimeReaders.ts
|
|
42828
42957
|
import { classifyPropertyGroup as classifyPropertyGroup3 } from "@hyperframes/core/gsap-parser";
|
|
42829
42958
|
import {
|
|
@@ -43005,6 +43134,7 @@ function readAllAnimatedProperties(iframe, selector, anim, group) {
|
|
|
43005
43134
|
}
|
|
43006
43135
|
|
|
43007
43136
|
// src/hooks/gsapDragPositionCommit.ts
|
|
43137
|
+
import { PROPERTY_GROUPS } from "@hyperframes/parsers/gsap-constants";
|
|
43008
43138
|
function buildTemporalArcKeyframes(anim, percentage, properties) {
|
|
43009
43139
|
return [
|
|
43010
43140
|
...(anim.keyframes?.keyframes ?? []).filter((keyframe) => Math.abs(keyframe.percentage - percentage) > KEYFRAME_PCT_MATCH).map((keyframe) => ({
|
|
@@ -43099,7 +43229,11 @@ async function commitFlatViaKeyframes(selection, anim, properties, callbacks, be
|
|
|
43099
43229
|
if (runtime && ts !== null) {
|
|
43100
43230
|
const { gsapLib, el, mainTl } = runtime;
|
|
43101
43231
|
const draggedValues = {};
|
|
43102
|
-
|
|
43232
|
+
const channelsTheClearWipes = /* @__PURE__ */ new Set([
|
|
43233
|
+
...Object.keys(properties),
|
|
43234
|
+
...PROPERTY_GROUPS.position
|
|
43235
|
+
]);
|
|
43236
|
+
for (const key2 of channelsTheClearWipes) {
|
|
43103
43237
|
const v = Number(gsapLib.getProperty(el, key2));
|
|
43104
43238
|
if (Number.isFinite(v)) draggedValues[key2] = v;
|
|
43105
43239
|
}
|
|
@@ -43573,7 +43707,7 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
43573
43707
|
if (!selector) return { status: "blocked", reason: "no-selector" };
|
|
43574
43708
|
const commitMutation = oneUndoStep(gestureCommit);
|
|
43575
43709
|
let workingAnimations = animations;
|
|
43576
|
-
const isPosWrite = (a) => a.targetSelector === selector && a
|
|
43710
|
+
const isPosWrite = (a) => a.targetSelector === selector && isXYPositionWrite(a);
|
|
43577
43711
|
if (animations.filter(isPosWrite).length > 1 && fetchFallbackAnimations) {
|
|
43578
43712
|
const fresh = await fetchFallbackAnimations();
|
|
43579
43713
|
const dupes = fresh.filter(isPosWrite);
|
|
@@ -43627,7 +43761,7 @@ async function tryGsapDragIntercept(selection, offset, allAnimations, iframe, ge
|
|
|
43627
43761
|
if (fetchFallbackAnimations) {
|
|
43628
43762
|
const fresh = await fetchFallbackAnimations();
|
|
43629
43763
|
const freshMatch = fresh.find(
|
|
43630
|
-
(a) => a.targetSelector === posAnim.targetSelector && a.propertyGroup === posAnim.propertyGroup
|
|
43764
|
+
(a) => a.targetSelector === posAnim.targetSelector && a.propertyGroup === posAnim.propertyGroup && isXYPositionWrite(a) === isXYPositionWrite(posAnim)
|
|
43631
43765
|
);
|
|
43632
43766
|
if (freshMatch && freshMatch.id !== posAnim.id) {
|
|
43633
43767
|
posAnim = freshMatch;
|
|
@@ -48839,14 +48973,199 @@ import {
|
|
|
48839
48973
|
audioGainToFaderPosition as audioGainToFaderPosition2,
|
|
48840
48974
|
audioGainToText as audioGainToText2
|
|
48841
48975
|
} from "@hyperframes/core/audio-gain";
|
|
48976
|
+
|
|
48977
|
+
// src/components/editor/mediaAudioEdits.ts
|
|
48978
|
+
import {
|
|
48979
|
+
HF_AUDIO_AUTOMATION_ATTR as HF_AUDIO_AUTOMATION_ATTR4,
|
|
48980
|
+
RATE_TARGET,
|
|
48981
|
+
parseAutomation as parseAutomation3,
|
|
48982
|
+
serializeAutomation as serializeAutomation2
|
|
48983
|
+
} from "@hyperframes/core/audio-automation";
|
|
48984
|
+
import { HF_AUDIO_CARVE_ATTR as HF_AUDIO_CARVE_ATTR2 } from "@hyperframes/core/audio-carve";
|
|
48985
|
+
import { HF_AUDIO_FADE_IN_ATTR as HF_AUDIO_FADE_IN_ATTR2, HF_AUDIO_FADE_OUT_ATTR as HF_AUDIO_FADE_OUT_ATTR2 } from "@hyperframes/core/audio-fade";
|
|
48986
|
+
import { HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR4 } from "@hyperframes/core/audio-fx";
|
|
48987
|
+
import { HF_AUDIO_GROUP_ATTR as HF_AUDIO_GROUP_ATTR3 } from "@hyperframes/core/audio-groups";
|
|
48988
|
+
var MEDIA_LINK_ATTR = "data-link";
|
|
48989
|
+
var SOUND_KEPT_ON_LINKED_AUDIO_STAGE = "Background removed. Sound kept on a linked audio track.";
|
|
48990
|
+
var MOVED_SOUND_ATTRS = [
|
|
48991
|
+
"data-volume",
|
|
48992
|
+
HF_AUDIO_FADE_IN_ATTR2,
|
|
48993
|
+
HF_AUDIO_FADE_OUT_ATTR2,
|
|
48994
|
+
HF_AUDIO_FX_ATTR4,
|
|
48995
|
+
HF_AUDIO_CARVE_ATTR2,
|
|
48996
|
+
HF_AUDIO_GROUP_ATTR3
|
|
48997
|
+
];
|
|
48998
|
+
var COPIED_TIMING_ATTRS = [
|
|
48999
|
+
"start",
|
|
49000
|
+
"end",
|
|
49001
|
+
"duration",
|
|
49002
|
+
"media-start",
|
|
49003
|
+
"playback-start",
|
|
49004
|
+
"playback-rate",
|
|
49005
|
+
"source-duration"
|
|
49006
|
+
];
|
|
49007
|
+
var htmlAttr = (property, value) => ({
|
|
49008
|
+
type: "html-attribute",
|
|
49009
|
+
property,
|
|
49010
|
+
value
|
|
49011
|
+
});
|
|
49012
|
+
var dataAttr = (property, value) => ({
|
|
49013
|
+
type: "attribute",
|
|
49014
|
+
property,
|
|
49015
|
+
value
|
|
49016
|
+
});
|
|
49017
|
+
function stripDataPrefix(name) {
|
|
49018
|
+
return name.startsWith("data-") ? name.slice("data-".length) : name;
|
|
49019
|
+
}
|
|
49020
|
+
function probedHasAudioValue(probedHasAudio) {
|
|
49021
|
+
if (probedHasAudio === null) return null;
|
|
49022
|
+
return probedHasAudio ? "true" : "false";
|
|
49023
|
+
}
|
|
49024
|
+
function mutedToggleOps(input) {
|
|
49025
|
+
const muted = htmlAttr("muted", input.nextMuted ? "true" : null);
|
|
49026
|
+
if (!input.isVideo) return [muted];
|
|
49027
|
+
const hasAudio = input.nextMuted ? null : probedHasAudioValue(input.probedHasAudio);
|
|
49028
|
+
return [muted, dataAttr("has-audio", hasAudio)];
|
|
49029
|
+
}
|
|
49030
|
+
function hasAudioToggleOps(next) {
|
|
49031
|
+
return next ? [dataAttr("has-audio", "true"), htmlAttr("muted", null)] : [dataAttr("has-audio", null), htmlAttr("muted", "true")];
|
|
49032
|
+
}
|
|
49033
|
+
function cutoutOps(input) {
|
|
49034
|
+
const src = htmlAttr("src", input.cutoutSrc);
|
|
49035
|
+
return input.isVideo ? [src, htmlAttr("muted", "true"), dataAttr("has-audio", null)] : [src];
|
|
49036
|
+
}
|
|
49037
|
+
function firstFreeName(base2, taken) {
|
|
49038
|
+
if (!taken.has(base2)) return base2;
|
|
49039
|
+
let n = 2;
|
|
49040
|
+
while (taken.has(`${base2}-${n}`)) n += 1;
|
|
49041
|
+
return `${base2}-${n}`;
|
|
49042
|
+
}
|
|
49043
|
+
function mintLinkId(doc) {
|
|
49044
|
+
const taken = /* @__PURE__ */ new Set();
|
|
49045
|
+
for (const el of Array.from(doc.querySelectorAll(`[id], [${MEDIA_LINK_ATTR}]`))) {
|
|
49046
|
+
if (el.id) taken.add(el.id);
|
|
49047
|
+
const link = el.getAttribute(MEDIA_LINK_ATTR);
|
|
49048
|
+
if (link) taken.add(link);
|
|
49049
|
+
}
|
|
49050
|
+
return firstFreeName("link", taken);
|
|
49051
|
+
}
|
|
49052
|
+
function firstFreeTrackIndex(source) {
|
|
49053
|
+
const used = new Set(
|
|
49054
|
+
Array.from(source.matchAll(/\bdata-track-index=["'](-?\d+)["']/g), (m) => Number(m[1]))
|
|
49055
|
+
);
|
|
49056
|
+
let track2 = 0;
|
|
49057
|
+
while (used.has(track2)) track2 += 1;
|
|
49058
|
+
return track2;
|
|
49059
|
+
}
|
|
49060
|
+
function splitAutomation(raw) {
|
|
49061
|
+
let lanes;
|
|
49062
|
+
try {
|
|
49063
|
+
lanes = parseAutomation3(raw).lanes;
|
|
49064
|
+
} catch {
|
|
49065
|
+
return { videoKeeps: null, audioTakes: raw };
|
|
49066
|
+
}
|
|
49067
|
+
const rateLanes = lanes.filter((lane) => lane.target === RATE_TARGET);
|
|
49068
|
+
const soundLanes = lanes.filter((lane) => lane.target !== RATE_TARGET);
|
|
49069
|
+
return {
|
|
49070
|
+
videoKeeps: rateLanes.length > 0 ? serializeAutomation2({ version: 1, lanes: rateLanes }) : null,
|
|
49071
|
+
audioTakes: serializeAutomation2({ version: 1, lanes: [...soundLanes, ...rateLanes] })
|
|
49072
|
+
};
|
|
49073
|
+
}
|
|
49074
|
+
function formatAttrs(attrs) {
|
|
49075
|
+
return attrs.map(([name, value]) => `${name}="${escapeHtmlAttribute(value)}"`).join(" ");
|
|
49076
|
+
}
|
|
49077
|
+
function insertBeforeTarget(source, target, markup) {
|
|
49078
|
+
const match = findTagByTarget(source, target);
|
|
49079
|
+
if (!match) return source;
|
|
49080
|
+
const lineStart = source.lastIndexOf("\n", match.start) + 1;
|
|
49081
|
+
const indent = /^[ \t]*$/.test(source.slice(lineStart, match.start)) ? source.slice(lineStart, match.start) : "";
|
|
49082
|
+
const separator = indent ? `
|
|
49083
|
+
${indent}` : "";
|
|
49084
|
+
return `${source.slice(0, match.start)}${markup}${separator}${source.slice(match.start)}`;
|
|
49085
|
+
}
|
|
49086
|
+
function buildKeepSoundCutoutEdit(input) {
|
|
49087
|
+
const { video, target } = input;
|
|
49088
|
+
const linkId = mintLinkId(video.ownerDocument);
|
|
49089
|
+
const originalSrc = video.getAttribute("src") ?? "";
|
|
49090
|
+
const movedSound = [];
|
|
49091
|
+
const ops = [...cutoutOps({ isVideo: true, cutoutSrc: input.cutoutSrc })];
|
|
49092
|
+
for (const name of MOVED_SOUND_ATTRS) {
|
|
49093
|
+
const value = video.getAttribute(name);
|
|
49094
|
+
if (value === null) continue;
|
|
49095
|
+
movedSound.push([name, value]);
|
|
49096
|
+
ops.push(dataAttr(stripDataPrefix(name), null));
|
|
49097
|
+
}
|
|
49098
|
+
const automation = video.getAttribute(HF_AUDIO_AUTOMATION_ATTR4);
|
|
49099
|
+
if (automation !== null) {
|
|
49100
|
+
const { videoKeeps, audioTakes } = splitAutomation(automation);
|
|
49101
|
+
movedSound.push([HF_AUDIO_AUTOMATION_ATTR4, audioTakes]);
|
|
49102
|
+
ops.push(dataAttr(stripDataPrefix(HF_AUDIO_AUTOMATION_ATTR4), videoKeeps));
|
|
49103
|
+
}
|
|
49104
|
+
ops.push(dataAttr(stripDataPrefix(MEDIA_LINK_ATTR), linkId));
|
|
49105
|
+
const prepareContent = (source) => {
|
|
49106
|
+
const audioId = firstFreeName(
|
|
49107
|
+
`${input.videoId || "video"}-audio`,
|
|
49108
|
+
new Set(collectHtmlIds(source))
|
|
49109
|
+
);
|
|
49110
|
+
const timing = [];
|
|
49111
|
+
for (const name of COPIED_TIMING_ATTRS) {
|
|
49112
|
+
const value = readAttributeByTarget(source, target, name);
|
|
49113
|
+
if (value !== void 0) timing.push([`data-${name}`, value]);
|
|
49114
|
+
}
|
|
49115
|
+
const attrs = [
|
|
49116
|
+
["id", audioId],
|
|
49117
|
+
["data-hf-id", `hf-${generateId()}`],
|
|
49118
|
+
["class", "clip"],
|
|
49119
|
+
["src", originalSrc],
|
|
49120
|
+
...timing,
|
|
49121
|
+
["data-track-index", String(firstFreeTrackIndex(source))],
|
|
49122
|
+
[MEDIA_LINK_ATTR, linkId],
|
|
49123
|
+
...movedSound
|
|
49124
|
+
];
|
|
49125
|
+
return insertBeforeTarget(source, target, `<audio ${formatAttrs(attrs)}></audio>`);
|
|
49126
|
+
};
|
|
49127
|
+
return { ops, prepareContent };
|
|
49128
|
+
}
|
|
49129
|
+
var isVideoSelection = (element) => element.tagName === "video";
|
|
49130
|
+
async function commitMutedToggle(ctx3, nextMuted) {
|
|
49131
|
+
const isVideo = isVideoSelection(ctx3.element);
|
|
49132
|
+
const probedHasAudio = isVideo && !nextMuted && ctx3.projectId && ctx3.projectSrc ? await resolveAssetHasAudio(ctx3.projectId, ctx3.projectSrc) : null;
|
|
49133
|
+
await ctx3.commit(ctx3.element, mutedToggleOps({ isVideo, nextMuted, probedHasAudio }), {
|
|
49134
|
+
label: "Edit muted"
|
|
49135
|
+
});
|
|
49136
|
+
}
|
|
49137
|
+
async function commitHasAudioToggle(ctx3, next) {
|
|
49138
|
+
await ctx3.commit(ctx3.element, hasAudioToggleOps(next), { label: "Edit has audio" });
|
|
49139
|
+
}
|
|
49140
|
+
async function commitCutout(ctx3, cutoutSrc, keepSound) {
|
|
49141
|
+
const isVideo = isVideoSelection(ctx3.element);
|
|
49142
|
+
const label = "Remove background";
|
|
49143
|
+
const keep = isVideo && keepSound;
|
|
49144
|
+
const landed = keep ? await commitKeepSoundCutout(ctx3, cutoutSrc, label) : await ctx3.commit(ctx3.element, cutoutOps({ isVideo, cutoutSrc }), { label });
|
|
49145
|
+
if (!landed) throw new Error("Couldn't apply the cutout");
|
|
49146
|
+
return keep ? SOUND_KEPT_ON_LINKED_AUDIO_STAGE : "Applied cutout";
|
|
49147
|
+
}
|
|
49148
|
+
function commitKeepSoundCutout(ctx3, cutoutSrc, label) {
|
|
49149
|
+
const edit = buildKeepSoundCutoutEdit({
|
|
49150
|
+
video: ctx3.element.element,
|
|
49151
|
+
videoId: ctx3.element.id ?? null,
|
|
49152
|
+
target: buildDomEditPatchTarget(ctx3.element),
|
|
49153
|
+
cutoutSrc
|
|
49154
|
+
});
|
|
49155
|
+
return ctx3.commit(ctx3.element, edit.ops, { label, prepareContent: edit.prepareContent });
|
|
49156
|
+
}
|
|
49157
|
+
|
|
49158
|
+
// src/components/editor/propertyPanelMediaSection.tsx
|
|
48842
49159
|
import { Fragment as Fragment31, jsx as jsx110, jsxs as jsxs77 } from "react/jsx-runtime";
|
|
48843
49160
|
function MediaSection({
|
|
49161
|
+
projectId = null,
|
|
48844
49162
|
projectDir,
|
|
48845
49163
|
element,
|
|
48846
49164
|
styles,
|
|
48847
49165
|
onSetStyle,
|
|
48848
49166
|
onSetAttribute,
|
|
48849
49167
|
onSetHtmlAttribute,
|
|
49168
|
+
onSetAttributeBatch,
|
|
48850
49169
|
onRemoveBackground
|
|
48851
49170
|
}) {
|
|
48852
49171
|
const track2 = useTrackDesignInput();
|
|
@@ -48860,7 +49179,11 @@ function MediaSection({
|
|
|
48860
49179
|
const { mediaStart, mediaStartAttr } = readClipInPoint(element.dataAttributes);
|
|
48861
49180
|
const hasLoop = el.hasAttribute("loop");
|
|
48862
49181
|
const hasMuted = el.hasAttribute("muted");
|
|
48863
|
-
const hasAudio =
|
|
49182
|
+
const hasAudio = isVideoAudible({
|
|
49183
|
+
tag: el.tagName,
|
|
49184
|
+
hasAudioAttr: element.dataAttributes["has-audio"],
|
|
49185
|
+
muted: el.hasAttribute("muted")
|
|
49186
|
+
});
|
|
48864
49187
|
const playbackRate = Number.parseFloat(element.dataAttributes["playback-rate"] ?? "1") || 1;
|
|
48865
49188
|
const objectFit = styles["object-fit"] || "contain";
|
|
48866
49189
|
const objectPosition = styles["object-position"] || "center";
|
|
@@ -48880,13 +49203,7 @@ function MediaSection({
|
|
|
48880
49203
|
setRemoveProgress(null);
|
|
48881
49204
|
setCreatePlate(false);
|
|
48882
49205
|
}, [srcAttr]);
|
|
48883
|
-
const
|
|
48884
|
-
await onSetHtmlAttribute("src", result.outputPath);
|
|
48885
|
-
if (isVideo) {
|
|
48886
|
-
await onSetAttribute("has-audio", "");
|
|
48887
|
-
await onSetHtmlAttribute("muted", "true");
|
|
48888
|
-
}
|
|
48889
|
-
};
|
|
49206
|
+
const mediaEdit = { element, projectId, projectSrc, commit: onSetAttributeBatch };
|
|
48890
49207
|
const runBackgroundRemoval = async () => {
|
|
48891
49208
|
if (!onRemoveBackground || !projectSrc || removeBusy) return;
|
|
48892
49209
|
track2("button", "Remove background");
|
|
@@ -48898,13 +49215,8 @@ function MediaSection({
|
|
|
48898
49215
|
quality,
|
|
48899
49216
|
onProgress: setRemoveProgress
|
|
48900
49217
|
});
|
|
48901
|
-
await
|
|
48902
|
-
setRemoveProgress({
|
|
48903
|
-
status: "complete",
|
|
48904
|
-
progress: 100,
|
|
48905
|
-
stage: "Applied cutout",
|
|
48906
|
-
...result
|
|
48907
|
-
});
|
|
49218
|
+
const stage = await commitCutout(mediaEdit, result.outputPath, hasAudio);
|
|
49219
|
+
setRemoveProgress({ status: "complete", progress: 100, stage, ...result });
|
|
48908
49220
|
} catch (error) {
|
|
48909
49221
|
setRemoveProgress({
|
|
48910
49222
|
status: "failed",
|
|
@@ -49109,9 +49421,7 @@ function MediaSection({
|
|
|
49109
49421
|
{
|
|
49110
49422
|
trackName: "Muted",
|
|
49111
49423
|
value: hasMuted ? "on" : "off",
|
|
49112
|
-
onChange: (next) =>
|
|
49113
|
-
void onSetHtmlAttribute("muted", next === "on" ? "true" : null);
|
|
49114
|
-
},
|
|
49424
|
+
onChange: (next) => void commitMutedToggle(mediaEdit, next === "on"),
|
|
49115
49425
|
options: [
|
|
49116
49426
|
{ label: "On", value: "on" },
|
|
49117
49427
|
{ label: "Off", value: "off" }
|
|
@@ -49127,15 +49437,7 @@ function MediaSection({
|
|
|
49127
49437
|
{
|
|
49128
49438
|
trackName: "Has audio track",
|
|
49129
49439
|
value: hasAudio ? "yes" : "no",
|
|
49130
|
-
onChange: (next) =>
|
|
49131
|
-
if (next === "yes") {
|
|
49132
|
-
void onSetAttribute("has-audio", "true");
|
|
49133
|
-
void onSetHtmlAttribute("muted", null);
|
|
49134
|
-
} else {
|
|
49135
|
-
void onSetAttribute("has-audio", "");
|
|
49136
|
-
void onSetHtmlAttribute("muted", "true");
|
|
49137
|
-
}
|
|
49138
|
-
},
|
|
49440
|
+
onChange: (next) => void commitHasAudioToggle(mediaEdit, next === "yes"),
|
|
49139
49441
|
options: [
|
|
49140
49442
|
{ label: "Yes", value: "yes" },
|
|
49141
49443
|
{ label: "No", value: "no" }
|
|
@@ -58136,14 +58438,14 @@ function motionSectionLabel(args) {
|
|
|
58136
58438
|
// src/components/editor/propertyPanelAudioFxGroup.tsx
|
|
58137
58439
|
import { useMemo as useMemo58, useState as useState81 } from "react";
|
|
58138
58440
|
import {
|
|
58139
|
-
HF_AUDIO_FX_ATTR as
|
|
58441
|
+
HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR7,
|
|
58140
58442
|
HF_AUDIO_FX_DATA_KEY as HF_AUDIO_FX_DATA_KEY2,
|
|
58141
58443
|
parseAudioFxChain as parseAudioFxChain4,
|
|
58142
58444
|
serializeAudioFxChain as serializeAudioFxChain5
|
|
58143
58445
|
} from "@hyperframes/core/audio-fx";
|
|
58144
58446
|
import {
|
|
58145
58447
|
classifyAudioName as classifyAudioName4,
|
|
58146
|
-
HF_AUDIO_CARVE_ATTR as
|
|
58448
|
+
HF_AUDIO_CARVE_ATTR as HF_AUDIO_CARVE_ATTR4,
|
|
58147
58449
|
normalizeCarveSettings as normalizeCarveSettings2
|
|
58148
58450
|
} from "@hyperframes/core/audio-carve";
|
|
58149
58451
|
import {
|
|
@@ -60464,13 +60766,13 @@ function useFxChainObserved(element, chain, carve, automation, onSetAttributeQui
|
|
|
60464
60766
|
// src/components/editor/useFxCarve.ts
|
|
60465
60767
|
import { useEffect as useEffect86, useRef as useRef101 } from "react";
|
|
60466
60768
|
import {
|
|
60467
|
-
HF_AUDIO_FX_ATTR as
|
|
60769
|
+
HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR5,
|
|
60468
60770
|
serializeAudioFxChain as serializeAudioFxChain3
|
|
60469
60771
|
} from "@hyperframes/core/audio-fx";
|
|
60470
60772
|
import {
|
|
60471
60773
|
clipsOverlap,
|
|
60472
60774
|
DEFAULT_CARVE as DEFAULT_CARVE3,
|
|
60473
|
-
HF_AUDIO_CARVE_ATTR as
|
|
60775
|
+
HF_AUDIO_CARVE_ATTR as HF_AUDIO_CARVE_ATTR3
|
|
60474
60776
|
} from "@hyperframes/core/audio-carve";
|
|
60475
60777
|
import { resolveAudioGroups as resolveAudioGroups3, resolveCarveSourceIds as resolveCarveSourceIds2 } from "@hyperframes/core/audio-groups";
|
|
60476
60778
|
|
|
@@ -60637,7 +60939,7 @@ async function dropCarveOutput(chain, automation, onSetAttributeQuiet) {
|
|
|
60637
60939
|
const kept = chain.nodes.filter((n) => !n.fromCarve);
|
|
60638
60940
|
if (kept.length !== chain.nodes.length) {
|
|
60639
60941
|
await onSetAttributeQuiet(
|
|
60640
|
-
|
|
60942
|
+
HF_AUDIO_FX_ATTR5,
|
|
60641
60943
|
kept.length ? serializeAudioFxChain3({ version: 1, nodes: kept }) : null
|
|
60642
60944
|
);
|
|
60643
60945
|
}
|
|
@@ -60690,7 +60992,7 @@ function useFxCarve(element, chain, carve, automation, onSetAttributeQuiet, writ
|
|
|
60690
60992
|
});
|
|
60691
60993
|
const generatedOutputStands = Boolean(next?.enabled) && (next?.sources.length ?? 0) > 0;
|
|
60692
60994
|
if (!generatedOutputStands) await dropCarveOutput(chain, automation, onSetAttributeQuiet);
|
|
60693
|
-
await onSetAttributeQuiet(
|
|
60995
|
+
await onSetAttributeQuiet(HF_AUDIO_CARVE_ATTR3, next ? JSON.stringify(next) : null);
|
|
60694
60996
|
if (carveNeedsReanalysis(carve, next)) await analyse(next);
|
|
60695
60997
|
};
|
|
60696
60998
|
const autoCarveInFlight = useRef101(false);
|
|
@@ -60703,7 +61005,7 @@ function useFxCarve(element, chain, carve, automation, onSetAttributeQuiet, writ
|
|
|
60703
61005
|
const persistMeasuredCarve = async (measured) => {
|
|
60704
61006
|
const { bands, carved, duck, voiceMix } = measured;
|
|
60705
61007
|
const { next, carvedNodes, duckNode } = mintCarveNodes(chain, carved, duck);
|
|
60706
|
-
await onSetAttributeQuiet(
|
|
61008
|
+
await onSetAttributeQuiet(HF_AUDIO_FX_ATTR5, serializeAudioFxChain3(next));
|
|
60707
61009
|
const lanes = carveLanes(carvedNodes, duckNode, duck, voiceMix, bands);
|
|
60708
61010
|
const carriedOver = withoutCarveLanes(automation, chain);
|
|
60709
61011
|
if (lanes.length > 0 || carriedOver.lanes.length !== automation.lanes.length) {
|
|
@@ -60763,7 +61065,7 @@ function useFxCarve(element, chain, carve, automation, onSetAttributeQuiet, writ
|
|
|
60763
61065
|
|
|
60764
61066
|
// src/components/editor/propertyPanelAudioFxGroup.tsx
|
|
60765
61067
|
import {
|
|
60766
|
-
HF_AUDIO_GROUP_ATTR as
|
|
61068
|
+
HF_AUDIO_GROUP_ATTR as HF_AUDIO_GROUP_ATTR4,
|
|
60767
61069
|
HF_AUDIO_GROUP_TAG as HF_AUDIO_GROUP_TAG4,
|
|
60768
61070
|
resolveAudioGroups as resolveAudioGroups4
|
|
60769
61071
|
} from "@hyperframes/core/audio-groups";
|
|
@@ -60771,7 +61073,7 @@ import {
|
|
|
60771
61073
|
// src/components/editor/useFxLevelling.ts
|
|
60772
61074
|
import { useRef as useRef102, useState as useState80 } from "react";
|
|
60773
61075
|
import {
|
|
60774
|
-
HF_AUDIO_FX_ATTR as
|
|
61076
|
+
HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR6,
|
|
60775
61077
|
serializeAudioFxChain as serializeAudioFxChain4
|
|
60776
61078
|
} from "@hyperframes/core/audio-fx";
|
|
60777
61079
|
import { levellingResult, removeLevelling } from "@hyperframes/core/audio-leveller";
|
|
@@ -60816,7 +61118,7 @@ function useFxLevelling(element, chain, automation, onSetAttributeQuiet, onSetAt
|
|
|
60816
61118
|
const result = measureLevelling(audio);
|
|
60817
61119
|
if (!result) return;
|
|
60818
61120
|
trackLeveller("run");
|
|
60819
|
-
await onSetAttributeQuiet(
|
|
61121
|
+
await onSetAttributeQuiet(HF_AUDIO_FX_ATTR6, serializeAudioFxChain4(result.chain));
|
|
60820
61122
|
const lane = result.lane;
|
|
60821
61123
|
if (lane) {
|
|
60822
61124
|
void onSetAttributeQuiet(
|
|
@@ -60835,7 +61137,7 @@ function useFxLevelling(element, chain, automation, onSetAttributeQuiet, onSetAt
|
|
|
60835
61137
|
const stopLevelAudition = () => {
|
|
60836
61138
|
setAuditioningLevel(false);
|
|
60837
61139
|
void onSetAttributeLive(
|
|
60838
|
-
|
|
61140
|
+
HF_AUDIO_FX_ATTR6,
|
|
60839
61141
|
chain.nodes.length ? serializeAudioFxChain4(chain) : null
|
|
60840
61142
|
);
|
|
60841
61143
|
void onSetAttributeLive(HF_AUDIO_AUTOMATION_ATTR, automationAttrValue(automation) || null);
|
|
@@ -60847,7 +61149,7 @@ function useFxLevelling(element, chain, automation, onSetAttributeQuiet, onSetAt
|
|
|
60847
61149
|
if (!audio || run !== auditionRun.current) return;
|
|
60848
61150
|
const result = measureLevelling(audio);
|
|
60849
61151
|
if (!result || run !== auditionRun.current) return;
|
|
60850
|
-
void onSetAttributeLive(
|
|
61152
|
+
void onSetAttributeLive(HF_AUDIO_FX_ATTR6, serializeAudioFxChain4(result.chain));
|
|
60851
61153
|
const lane = result.lane;
|
|
60852
61154
|
if (lane) {
|
|
60853
61155
|
void onSetAttributeLive(
|
|
@@ -60868,7 +61170,7 @@ function useFxLevelling(element, chain, automation, onSetAttributeQuiet, onSetAt
|
|
|
60868
61170
|
const removeLeveller = () => {
|
|
60869
61171
|
trackLeveller("removed");
|
|
60870
61172
|
const { chain: next, removedTarget } = removeLevelling(chain);
|
|
60871
|
-
void onSetAttributeQuiet(
|
|
61173
|
+
void onSetAttributeQuiet(HF_AUDIO_FX_ATTR6, serializeAudioFxChain4(next));
|
|
60872
61174
|
if (removedTarget) {
|
|
60873
61175
|
void onSetAttributeQuiet(
|
|
60874
61176
|
HF_AUDIO_AUTOMATION_ATTR,
|
|
@@ -60892,7 +61194,7 @@ function auditionSpansFor(element) {
|
|
|
60892
61194
|
if (element.tagName?.toLowerCase() !== HF_AUDIO_GROUP_TAG4 || !element.id) return [];
|
|
60893
61195
|
const doc = element.element?.ownerDocument;
|
|
60894
61196
|
if (!doc) return [];
|
|
60895
|
-
return [...doc.querySelectorAll(`audio[${
|
|
61197
|
+
return [...doc.querySelectorAll(`audio[${HF_AUDIO_GROUP_ATTR4}]`)].filter((member) => member.getAttribute(HF_AUDIO_GROUP_ATTR4) === element.id).flatMap((member) => {
|
|
60896
61198
|
const span = auditionSpan(
|
|
60897
61199
|
member.getAttribute("data-start") ?? void 0,
|
|
60898
61200
|
member.getAttribute("data-duration") ?? void 0
|
|
@@ -61038,7 +61340,7 @@ function AudioFxGroup({
|
|
|
61038
61340
|
// bypassing an effect is applied to the running graph, so a reload would
|
|
61039
61341
|
// only interrupt the audio to reach the same state.
|
|
61040
61342
|
onSetAttributeQuiet(
|
|
61041
|
-
|
|
61343
|
+
HF_AUDIO_FX_ATTR7,
|
|
61042
61344
|
next.nodes.length ? serializeAudioFxChain5(next) : null
|
|
61043
61345
|
)
|
|
61044
61346
|
),
|
|
@@ -61048,11 +61350,11 @@ function AudioFxGroup({
|
|
|
61048
61350
|
// Live writes skip the preview refresh entirely, so dragging a knob no
|
|
61049
61351
|
// longer reloads the composition and restarts playback on every pixel.
|
|
61050
61352
|
// The gesture-end write above is the one that resyncs.
|
|
61051
|
-
onSetAttributeLive(
|
|
61353
|
+
onSetAttributeLive(HF_AUDIO_FX_ATTR7, next.nodes.length ? serializeAudioFxChain5(next) : null)
|
|
61052
61354
|
),
|
|
61053
61355
|
carve,
|
|
61054
61356
|
onCarveChange: (next) => void setCarve(next),
|
|
61055
|
-
onCarvePreview: (next) => onSetAttributeLive(
|
|
61357
|
+
onCarvePreview: (next) => onSetAttributeLive(HF_AUDIO_CARVE_ATTR4, JSON.stringify(next)),
|
|
61056
61358
|
sourceOptions,
|
|
61057
61359
|
trackKind: classifyAudioName4(element.id, element.element?.getAttribute("src")),
|
|
61058
61360
|
onLevel: () => void runLeveller(),
|
|
@@ -61072,7 +61374,7 @@ function AudioFxGroup({
|
|
|
61072
61374
|
// src/components/editor/useVolumeAutomation.ts
|
|
61073
61375
|
import {
|
|
61074
61376
|
HF_AUDIO_AUTOMATION_DATA_KEY as HF_AUDIO_AUTOMATION_DATA_KEY2,
|
|
61075
|
-
RATE_TARGET,
|
|
61377
|
+
RATE_TARGET as RATE_TARGET2,
|
|
61076
61378
|
resolveAutomationRange as resolveAutomationRange6,
|
|
61077
61379
|
sampleAutomationLane as sampleAutomationLane9,
|
|
61078
61380
|
VOLUME_TARGET as VOLUME_TARGET2
|
|
@@ -61105,7 +61407,7 @@ function useVolumeAutomation(element, currentTime, onSetAttributeQuiet) {
|
|
|
61105
61407
|
const current = Number.isFinite(parsed) ? parsed : 1;
|
|
61106
61408
|
const volume = laneBinding(VOLUME_TARGET2, current);
|
|
61107
61409
|
const rateAttr = Number.parseFloat(element.dataAttributes?.["playback-rate"] ?? "");
|
|
61108
|
-
const rate = laneBinding(
|
|
61410
|
+
const rate = laneBinding(RATE_TARGET2, Number.isFinite(rateAttr) && rateAttr > 0 ? rateAttr : 1);
|
|
61109
61411
|
return {
|
|
61110
61412
|
volumeAutomated: volume.automated,
|
|
61111
61413
|
automatedVolumeValue: volume.automatedValue,
|
|
@@ -61260,12 +61562,14 @@ function finiteSeconds(seconds) {
|
|
|
61260
61562
|
// src/components/editor/propertyPanelFlatMediaSection.tsx
|
|
61261
61563
|
import { Fragment as Fragment43, jsx as jsx160, jsxs as jsxs122 } from "react/jsx-runtime";
|
|
61262
61564
|
function FlatMediaSection({
|
|
61565
|
+
projectId = null,
|
|
61263
61566
|
projectDir,
|
|
61264
61567
|
element,
|
|
61265
61568
|
styles,
|
|
61266
61569
|
onSetStyle,
|
|
61267
61570
|
onSetAttribute,
|
|
61268
61571
|
onSetHtmlAttribute,
|
|
61572
|
+
onSetAttributeBatch,
|
|
61269
61573
|
onRemoveBackground,
|
|
61270
61574
|
volumeAutomated,
|
|
61271
61575
|
onAutomateVolume,
|
|
@@ -61293,7 +61597,11 @@ function FlatMediaSection({
|
|
|
61293
61597
|
const fadeMax = clipDuration2 > 0 ? clipDuration2 : 10;
|
|
61294
61598
|
const hasLoop = el.hasAttribute("loop");
|
|
61295
61599
|
const hasMuted = el.hasAttribute("muted");
|
|
61296
|
-
const hasAudio =
|
|
61600
|
+
const hasAudio = isVideoAudible({
|
|
61601
|
+
tag: el.tagName,
|
|
61602
|
+
hasAudioAttr: element.dataAttributes["has-audio"],
|
|
61603
|
+
muted: el.hasAttribute("muted")
|
|
61604
|
+
});
|
|
61297
61605
|
const objectFit = styles["object-fit"] || "contain";
|
|
61298
61606
|
const objectPosition = styles["object-position"] || "center";
|
|
61299
61607
|
const srcAttr = el.getAttribute("src") ?? "";
|
|
@@ -61309,13 +61617,7 @@ function FlatMediaSection({
|
|
|
61309
61617
|
setRemoveProgress(null);
|
|
61310
61618
|
setCreatePlate(false);
|
|
61311
61619
|
}, [srcAttr]);
|
|
61312
|
-
const
|
|
61313
|
-
await onSetHtmlAttribute("src", result.outputPath);
|
|
61314
|
-
if (isVideo) {
|
|
61315
|
-
await onSetAttribute("has-audio", "");
|
|
61316
|
-
await onSetHtmlAttribute("muted", "true");
|
|
61317
|
-
}
|
|
61318
|
-
};
|
|
61620
|
+
const mediaEdit = { element, projectId, projectSrc, commit: onSetAttributeBatch };
|
|
61319
61621
|
const runBackgroundRemoval = async () => {
|
|
61320
61622
|
if (!onRemoveBackground || !projectSrc || removeBusy) return;
|
|
61321
61623
|
track2("button", "Remove background");
|
|
@@ -61327,8 +61629,8 @@ function FlatMediaSection({
|
|
|
61327
61629
|
quality,
|
|
61328
61630
|
onProgress: setRemoveProgress
|
|
61329
61631
|
});
|
|
61330
|
-
await
|
|
61331
|
-
setRemoveProgress({ status: "complete", progress: 100, stage
|
|
61632
|
+
const stage = await commitCutout(mediaEdit, result.outputPath, hasAudio);
|
|
61633
|
+
setRemoveProgress({ status: "complete", progress: 100, stage, ...result });
|
|
61332
61634
|
} catch (error) {
|
|
61333
61635
|
setRemoveProgress({
|
|
61334
61636
|
status: "failed",
|
|
@@ -61551,7 +61853,7 @@ function FlatMediaSection({
|
|
|
61551
61853
|
{
|
|
61552
61854
|
label: "Muted",
|
|
61553
61855
|
checked: hasMuted,
|
|
61554
|
-
onChange: (next) => void
|
|
61856
|
+
onChange: (next) => void commitMutedToggle(mediaEdit, next)
|
|
61555
61857
|
}
|
|
61556
61858
|
),
|
|
61557
61859
|
isVideo && /* @__PURE__ */ jsx160(
|
|
@@ -61559,15 +61861,7 @@ function FlatMediaSection({
|
|
|
61559
61861
|
{
|
|
61560
61862
|
label: "Has audio track",
|
|
61561
61863
|
checked: hasAudio,
|
|
61562
|
-
onChange: (next) =>
|
|
61563
|
-
if (next) {
|
|
61564
|
-
void onSetAttribute("has-audio", "true");
|
|
61565
|
-
void onSetHtmlAttribute("muted", null);
|
|
61566
|
-
} else {
|
|
61567
|
-
void onSetAttribute("has-audio", "");
|
|
61568
|
-
void onSetHtmlAttribute("muted", "true");
|
|
61569
|
-
}
|
|
61570
|
-
}
|
|
61864
|
+
onChange: (next) => void commitHasAudioToggle(mediaEdit, next)
|
|
61571
61865
|
}
|
|
61572
61866
|
)
|
|
61573
61867
|
] }),
|
|
@@ -65069,7 +65363,6 @@ function PropertyPanelFlat({
|
|
|
65069
65363
|
clipboardCopied,
|
|
65070
65364
|
onCopyElementInfo,
|
|
65071
65365
|
projectId,
|
|
65072
|
-
projectDir,
|
|
65073
65366
|
assets,
|
|
65074
65367
|
previewIframeRef,
|
|
65075
65368
|
onClearSelection,
|
|
@@ -65081,8 +65374,6 @@ function PropertyPanelFlat({
|
|
|
65081
65374
|
onSetAttributeLive,
|
|
65082
65375
|
onSetAttributeQuiet,
|
|
65083
65376
|
onApplyColorGradingScope,
|
|
65084
|
-
onSetHtmlAttribute,
|
|
65085
|
-
onRemoveBackground,
|
|
65086
65377
|
onSetText,
|
|
65087
65378
|
onSetTextFieldStyle,
|
|
65088
65379
|
onPreviewTextFieldStyle,
|
|
@@ -65137,7 +65428,8 @@ function PropertyPanelFlat({
|
|
|
65137
65428
|
onUnroll,
|
|
65138
65429
|
onUpdateKeyframeEase,
|
|
65139
65430
|
onUpdateSegmentEase,
|
|
65140
|
-
onSetAllKeyframeEases
|
|
65431
|
+
onSetAllKeyframeEases,
|
|
65432
|
+
...forwardedProps
|
|
65141
65433
|
}) {
|
|
65142
65434
|
const [openGroupId, setOpenGroupId] = useState93(
|
|
65143
65435
|
() => isTextEditableSelection(element) ? "text" : showEditableSections ? "style" : sections.media ? "media" : (
|
|
@@ -65446,13 +65738,12 @@ function PropertyPanelFlat({
|
|
|
65446
65738
|
content: /* @__PURE__ */ jsx172(
|
|
65447
65739
|
FlatMediaSection,
|
|
65448
65740
|
{
|
|
65449
|
-
|
|
65741
|
+
...forwardedProps,
|
|
65742
|
+
projectId,
|
|
65450
65743
|
element,
|
|
65451
65744
|
styles,
|
|
65452
65745
|
onSetStyle,
|
|
65453
65746
|
onSetAttribute,
|
|
65454
|
-
onSetHtmlAttribute,
|
|
65455
|
-
onRemoveBackground,
|
|
65456
65747
|
...volumeAutomation
|
|
65457
65748
|
}
|
|
65458
65749
|
)
|
|
@@ -65730,7 +66021,6 @@ import { createElement as createElement3 } from "react";
|
|
|
65730
66021
|
var PropertyPanel = memo36(function PropertyPanel2(props) {
|
|
65731
66022
|
const {
|
|
65732
66023
|
projectId,
|
|
65733
|
-
projectDir,
|
|
65734
66024
|
assets,
|
|
65735
66025
|
element,
|
|
65736
66026
|
multiSelectCount = 0,
|
|
@@ -65744,8 +66034,6 @@ var PropertyPanel = memo36(function PropertyPanel2(props) {
|
|
|
65744
66034
|
onSetAttribute,
|
|
65745
66035
|
onSetAttributeLive,
|
|
65746
66036
|
onApplyColorGradingScope,
|
|
65747
|
-
onSetHtmlAttribute,
|
|
65748
|
-
onRemoveBackground,
|
|
65749
66037
|
onSetManualOffset,
|
|
65750
66038
|
onSetManualSize,
|
|
65751
66039
|
onSetManualRotation,
|
|
@@ -65984,18 +66272,7 @@ var PropertyPanel = memo36(function PropertyPanel2(props) {
|
|
|
65984
66272
|
},
|
|
65985
66273
|
selectionIdentityKey2(element)
|
|
65986
66274
|
),
|
|
65987
|
-
sections.media && /* @__PURE__ */ jsx175(
|
|
65988
|
-
MediaSection,
|
|
65989
|
-
{
|
|
65990
|
-
projectDir,
|
|
65991
|
-
element,
|
|
65992
|
-
styles,
|
|
65993
|
-
onSetStyle,
|
|
65994
|
-
onSetAttribute,
|
|
65995
|
-
onSetHtmlAttribute,
|
|
65996
|
-
onRemoveBackground
|
|
65997
|
-
}
|
|
65998
|
-
),
|
|
66275
|
+
sections.media && /* @__PURE__ */ jsx175(MediaSection, { ...props, element, styles }),
|
|
65999
66276
|
sections.layout && /* @__PURE__ */ jsxs136(Section, { title: "Layout", icon: /* @__PURE__ */ jsx175(Move, { size: 15 }), children: [
|
|
66000
66277
|
/* @__PURE__ */ jsxs136("div", { className: RESPONSIVE_GRID, children: [
|
|
66001
66278
|
/* @__PURE__ */ jsxs136("div", { className: "flex items-center gap-1", children: [
|
|
@@ -69990,20 +70267,43 @@ function diffRestoreDocs(prevDoc, nextDoc) {
|
|
|
69990
70267
|
});
|
|
69991
70268
|
return prevRest === nextRest ? changedElementKeys : null;
|
|
69992
70269
|
}
|
|
69993
|
-
function syncElementAttributes(target, source) {
|
|
70270
|
+
function syncElementAttributes(target, source, base2) {
|
|
70271
|
+
const draws = base2 && studioGestureDraws(target);
|
|
70272
|
+
const merged = draws ? keepDrawn(target, source, base2, draws) : source;
|
|
69994
70273
|
for (const name of [...target.getAttributeNames()]) {
|
|
69995
|
-
if (!
|
|
70274
|
+
if (!merged.hasAttribute(name)) target.removeAttribute(name);
|
|
69996
70275
|
}
|
|
69997
|
-
for (const name of
|
|
69998
|
-
target.setAttribute(name,
|
|
70276
|
+
for (const name of merged.getAttributeNames()) {
|
|
70277
|
+
target.setAttribute(name, merged.getAttribute(name) ?? "");
|
|
69999
70278
|
}
|
|
70000
70279
|
}
|
|
70001
|
-
function
|
|
70002
|
-
const
|
|
70280
|
+
function keepDrawn(live, source, base2, draws) {
|
|
70281
|
+
const merged = source.cloneNode(false);
|
|
70282
|
+
const keepsMark = draws.includes("translate");
|
|
70283
|
+
for (const name of live.getAttributeNames().filter((n) => n !== "style")) {
|
|
70284
|
+
const value = live.getAttribute(name);
|
|
70285
|
+
const mark = keepsMark && name === STUDIO_ORIGINAL_INLINE_TRANSLATE_ATTR;
|
|
70286
|
+
if (mark || value !== base2.getAttribute(name)) merged.setAttribute(name, value);
|
|
70287
|
+
}
|
|
70288
|
+
keepDrawnStyle(live, base2, merged, draws);
|
|
70289
|
+
return merged;
|
|
70290
|
+
}
|
|
70291
|
+
function keepDrawnStyle(live, base2, merged, draws) {
|
|
70292
|
+
const value = (prop) => live.style.getPropertyValue(prop);
|
|
70293
|
+
const drawn = (prop) => draws.includes(prop) || value(prop) !== base2.style.getPropertyValue(prop);
|
|
70294
|
+
const props = /* @__PURE__ */ new Set([...Array.from(live.style), ...Array.from(base2.style), ...draws]);
|
|
70295
|
+
for (const prop of [...props].filter(drawn)) {
|
|
70296
|
+
if (value(prop))
|
|
70297
|
+
merged.style.setProperty(prop, value(prop), live.style.getPropertyPriority(prop));
|
|
70298
|
+
else merged.style.removeProperty(prop);
|
|
70299
|
+
}
|
|
70300
|
+
}
|
|
70301
|
+
function syncStaleEditMarks(doc, file) {
|
|
70302
|
+
const [restoredDoc, previousDoc] = [file.restored, file.previous].map(parseSavedSource);
|
|
70003
70303
|
for (const live of doc.querySelectorAll(STUDIO_EDIT_ATTRS.map((attr) => `[${attr}]`).join())) {
|
|
70004
70304
|
const source = findAuthoredElement(restoredDoc, live);
|
|
70005
70305
|
if (source && STUDIO_EDIT_ATTRS.some((a) => live.getAttribute(a) !== source.getAttribute(a))) {
|
|
70006
|
-
syncElementAttributes(live, source);
|
|
70306
|
+
syncElementAttributes(live, source, findAuthoredElement(previousDoc, live) ?? void 0);
|
|
70007
70307
|
}
|
|
70008
70308
|
}
|
|
70009
70309
|
}
|
|
@@ -70023,7 +70323,7 @@ function liveScopes(doc, path, isActive) {
|
|
|
70023
70323
|
if (isActive) return [doc];
|
|
70024
70324
|
return Array.from(doc.querySelectorAll(`[data-composition-file="${CSS.escape(path)}"]`));
|
|
70025
70325
|
}
|
|
70026
|
-
function scopeTargets(scope, keys, restoredByKey) {
|
|
70326
|
+
function scopeTargets(scope, keys, restoredByKey, previousByKey) {
|
|
70027
70327
|
const liveByKey = identityElementMap(scope);
|
|
70028
70328
|
if (!liveByKey) return null;
|
|
70029
70329
|
const targets = [];
|
|
@@ -70031,7 +70331,7 @@ function scopeTargets(scope, keys, restoredByKey) {
|
|
|
70031
70331
|
const live = liveByKey.get(key2);
|
|
70032
70332
|
const restored = restoredByKey.get(key2);
|
|
70033
70333
|
if (!live || !restored || live.hasAttribute("data-hf-inner-root")) return null;
|
|
70034
|
-
targets.push({ live, restored });
|
|
70334
|
+
targets.push({ live, restored, previous: previousByKey.get(key2) });
|
|
70035
70335
|
}
|
|
70036
70336
|
return targets;
|
|
70037
70337
|
}
|
|
@@ -70042,9 +70342,10 @@ function fileTargets(doc, path, isActive, file) {
|
|
|
70042
70342
|
if (scripted === null) return null;
|
|
70043
70343
|
const keys = diffRestoreDocs(prevDoc, nextDoc);
|
|
70044
70344
|
const restoredByKey = identityElementMap(nextDoc);
|
|
70045
|
-
|
|
70345
|
+
const previousByKey = identityElementMap(prevDoc);
|
|
70346
|
+
if (!keys || !restoredByKey || !previousByKey) return null;
|
|
70046
70347
|
const found = liveScopes(doc, path, isActive).map(
|
|
70047
|
-
(scope) => scopeTargets(scope, keys, restoredByKey)
|
|
70348
|
+
(scope) => scopeTargets(scope, keys, restoredByKey, previousByKey)
|
|
70048
70349
|
);
|
|
70049
70350
|
if (!found.length || found.includes(null)) return null;
|
|
70050
70351
|
return { targets: found.flat(), scripted };
|
|
@@ -70074,11 +70375,14 @@ function showRestoreInPlace(iframe, activeCompPath, files, currentTime) {
|
|
|
70074
70375
|
const win = iframe?.contentWindow;
|
|
70075
70376
|
const plan = doc && win ? planRestoreTargets(doc, activeCompPath ?? "index.html", files) : null;
|
|
70076
70377
|
if (!iframe || !win || !plan || plan.scripted) return null;
|
|
70077
|
-
const before = plan.targets.map(
|
|
70078
|
-
|
|
70378
|
+
const before = plan.targets.map(
|
|
70379
|
+
({ live, restored }) => [live, live.cloneNode(false), restored]
|
|
70380
|
+
);
|
|
70381
|
+
for (const { live, restored, previous } of plan.targets)
|
|
70382
|
+
syncElementAttributes(live, restored, previous);
|
|
70079
70383
|
const putBack = (time) => {
|
|
70080
70384
|
if (iframe.contentDocument !== doc) return false;
|
|
70081
|
-
for (const [live, attributes] of before) syncElementAttributes(live, attributes);
|
|
70385
|
+
for (const [live, attributes, shown2] of before) syncElementAttributes(live, attributes, shown2);
|
|
70082
70386
|
return finalizeInPlace(iframe, win, time);
|
|
70083
70387
|
};
|
|
70084
70388
|
if (finalizeInPlace(iframe, win, currentTime)) return putBack;
|
|
@@ -70099,16 +70403,23 @@ function applyUndoRestoreToPreview(iframe, activeCompPath, files, currentTime, r
|
|
|
70099
70403
|
reloadPreview();
|
|
70100
70404
|
return "full";
|
|
70101
70405
|
}
|
|
70102
|
-
for (const target of plan.targets)
|
|
70406
|
+
for (const target of plan.targets)
|
|
70407
|
+
syncElementAttributes(target.live, target.restored, target.previous);
|
|
70103
70408
|
const active = files[activeDocPath];
|
|
70104
70409
|
const restoredScript = active ? extractGsapScriptText(active.restored) : null;
|
|
70105
70410
|
const previousScript = active ? extractGsapScriptText(active.previous) : null;
|
|
70106
|
-
|
|
70107
|
-
|
|
70411
|
+
const reparse = plan.scripted ? plan.targets.map(({ live }) => live).filter((el) => "_gsap" in el) : [];
|
|
70412
|
+
if (reparse.length && !restoredScript) {
|
|
70413
|
+
reloadPreview();
|
|
70414
|
+
return "full";
|
|
70415
|
+
}
|
|
70416
|
+
if (restoredScript && (restoredScript !== previousScript || reparse.length)) {
|
|
70417
|
+
syncStaleEditMarks(doc, active);
|
|
70108
70418
|
const result = applySoftReload(iframe, restoredScript, {
|
|
70109
70419
|
onAsyncFailure: reloadPreview,
|
|
70110
70420
|
currentTimeOverride: currentTime,
|
|
70111
|
-
authoredHtml: active.restored
|
|
70421
|
+
authoredHtml: active.restored,
|
|
70422
|
+
reparse
|
|
70112
70423
|
});
|
|
70113
70424
|
if (result === "cannot-soft-reload") {
|
|
70114
70425
|
reloadPreview();
|
|
@@ -70122,11 +70433,11 @@ function applyUndoRestoreToPreview(iframe, activeCompPath, files, currentTime, r
|
|
|
70122
70433
|
}
|
|
70123
70434
|
|
|
70124
70435
|
// src/player/lib/automationStoreSync.ts
|
|
70125
|
-
import { HF_AUDIO_AUTOMATION_ATTR as
|
|
70126
|
-
import { HF_AUDIO_FX_ATTR as
|
|
70436
|
+
import { HF_AUDIO_AUTOMATION_ATTR as HF_AUDIO_AUTOMATION_ATTR5 } from "@hyperframes/core/audio-automation";
|
|
70437
|
+
import { HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR8 } from "@hyperframes/core/audio-fx";
|
|
70127
70438
|
import {
|
|
70128
|
-
HF_AUDIO_FADE_IN_ATTR as
|
|
70129
|
-
HF_AUDIO_FADE_OUT_ATTR as
|
|
70439
|
+
HF_AUDIO_FADE_IN_ATTR as HF_AUDIO_FADE_IN_ATTR3,
|
|
70440
|
+
HF_AUDIO_FADE_OUT_ATTR as HF_AUDIO_FADE_OUT_ATTR3,
|
|
70130
70441
|
readElementFades,
|
|
70131
70442
|
readFadeSeconds as readFadeSeconds2
|
|
70132
70443
|
} from "@hyperframes/core/audio-fade";
|
|
@@ -70134,8 +70445,8 @@ function syncedFields(doc, element, node) {
|
|
|
70134
70445
|
const group = element.audioGroup ? groupInfoFor(doc, element.audioGroup) : null;
|
|
70135
70446
|
const fades = readElementFades(node);
|
|
70136
70447
|
return {
|
|
70137
|
-
automation: node.getAttribute(
|
|
70138
|
-
fxChain: node.getAttribute(
|
|
70448
|
+
automation: node.getAttribute(HF_AUDIO_AUTOMATION_ATTR5) ?? void 0,
|
|
70449
|
+
fxChain: node.getAttribute(HF_AUDIO_FX_ATTR8) ?? void 0,
|
|
70139
70450
|
fadeIn: storedFade(fades.fadeIn),
|
|
70140
70451
|
fadeOut: storedFade(fades.fadeOut),
|
|
70141
70452
|
audioGroupAutomation: group?.automation,
|
|
@@ -70161,10 +70472,10 @@ function syncStoredAutomationFromPreview(doc) {
|
|
|
70161
70472
|
}
|
|
70162
70473
|
var storedFade = (seconds) => seconds > 0 ? seconds : void 0;
|
|
70163
70474
|
var STORED_FIELD = {
|
|
70164
|
-
[
|
|
70165
|
-
[
|
|
70166
|
-
[
|
|
70167
|
-
[
|
|
70475
|
+
[HF_AUDIO_AUTOMATION_ATTR5]: (value) => ({ automation: value ?? void 0 }),
|
|
70476
|
+
[HF_AUDIO_FX_ATTR8]: (value) => ({ fxChain: value ?? void 0 }),
|
|
70477
|
+
[HF_AUDIO_FADE_IN_ATTR3]: (value) => ({ fadeIn: storedFade(readFadeSeconds2(value)) }),
|
|
70478
|
+
[HF_AUDIO_FADE_OUT_ATTR3]: (value) => ({ fadeOut: storedFade(readFadeSeconds2(value)) })
|
|
70168
70479
|
};
|
|
70169
70480
|
function syncStoredElementAttribute(target, attr, value) {
|
|
70170
70481
|
const fields = STORED_FIELD[attr]?.(value);
|
|
@@ -70272,6 +70583,10 @@ function usePreviewPersistence({
|
|
|
70272
70583
|
const result = await drainPendingDomEditSaves();
|
|
70273
70584
|
if (result.status !== "clean") throw result.error;
|
|
70274
70585
|
}, [drainPendingDomEditSaves]);
|
|
70586
|
+
const settlePendingEdits = useCallback104(async () => {
|
|
70587
|
+
await flushStudioPendingEdits();
|
|
70588
|
+
await domEditSaveQueueRef.current?.waitForIdle();
|
|
70589
|
+
}, []);
|
|
70275
70590
|
const resetDomEditSaveQueueBreaker = useCallback104(() => {
|
|
70276
70591
|
domEditSaveQueueRef.current?.reset();
|
|
70277
70592
|
setDomEditSaveQueuePaused(null);
|
|
@@ -70335,6 +70650,7 @@ function usePreviewPersistence({
|
|
|
70335
70650
|
queueDomEditSave,
|
|
70336
70651
|
drainPendingDomEditSaves,
|
|
70337
70652
|
waitForPendingDomEditSaves,
|
|
70653
|
+
settlePendingEdits,
|
|
70338
70654
|
domEditSaveQueuePaused,
|
|
70339
70655
|
resetDomEditSaveQueueBreaker,
|
|
70340
70656
|
applyCurrentStudioManualEditsToPreview,
|
|
@@ -71024,8 +71340,8 @@ function useTimelineEditGate(canEdit, showToast) {
|
|
|
71024
71340
|
}
|
|
71025
71341
|
|
|
71026
71342
|
// src/hooks/timelineAudioGroupVolume.ts
|
|
71027
|
-
import { HF_AUDIO_FX_ATTR as
|
|
71028
|
-
import { HF_AUDIO_AUTOMATION_ATTR as
|
|
71343
|
+
import { HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR9 } from "@hyperframes/core/audio-fx";
|
|
71344
|
+
import { HF_AUDIO_AUTOMATION_ATTR as HF_AUDIO_AUTOMATION_ATTR6 } from "@hyperframes/core/audio-automation";
|
|
71029
71345
|
|
|
71030
71346
|
// src/hooks/liveLanes.ts
|
|
71031
71347
|
import { useRef as useRef132, useState as useState109 } from "react";
|
|
@@ -71164,8 +71480,8 @@ var GROUP_ATTR_TO_MIRROR = {
|
|
|
71164
71480
|
"data-hidden": (value) => ({ audioGroupHidden: value !== null }),
|
|
71165
71481
|
"data-volume": (value) => ({ audioGroupVolume: mirroredGroupVolume(value) }),
|
|
71166
71482
|
"data-label": (value, groupId) => ({ audioGroupLabel: value ?? groupId }),
|
|
71167
|
-
[
|
|
71168
|
-
[
|
|
71483
|
+
[HF_AUDIO_FX_ATTR9]: (value) => ({ audioGroupFxChain: value ?? void 0 }),
|
|
71484
|
+
[HF_AUDIO_AUTOMATION_ATTR6]: (value) => ({ audioGroupAutomation: value ?? void 0 })
|
|
71169
71485
|
};
|
|
71170
71486
|
function syncStoredGroupAttribute(groupId, attr, value) {
|
|
71171
71487
|
const toPatch = GROUP_ATTR_TO_MIRROR[attr];
|
|
@@ -71653,11 +71969,7 @@ function useTrackPendingTimelineEdit() {
|
|
|
71653
71969
|
const key2 = fn;
|
|
71654
71970
|
const cached5 = wrappedRef.current.get(key2);
|
|
71655
71971
|
if (cached5) return cached5;
|
|
71656
|
-
const wrapped = (
|
|
71657
|
-
const result = fn(...args);
|
|
71658
|
-
trackStudioPendingEdit(result);
|
|
71659
|
-
return result;
|
|
71660
|
-
};
|
|
71972
|
+
const wrapped = trackedStudioEdit(fn);
|
|
71661
71973
|
wrappedRef.current.set(key2, wrapped);
|
|
71662
71974
|
return wrapped;
|
|
71663
71975
|
}, []);
|
|
@@ -71665,7 +71977,7 @@ function useTrackPendingTimelineEdit() {
|
|
|
71665
71977
|
|
|
71666
71978
|
// src/hooks/timelineAudioGroupCreate.ts
|
|
71667
71979
|
import { useCallback as useCallback114 } from "react";
|
|
71668
|
-
import { HF_AUDIO_GROUP_ATTR as
|
|
71980
|
+
import { HF_AUDIO_GROUP_ATTR as HF_AUDIO_GROUP_ATTR5, HF_AUDIO_GROUP_TAG as HF_AUDIO_GROUP_TAG5 } from "@hyperframes/core/audio-groups";
|
|
71669
71981
|
|
|
71670
71982
|
// src/hooks/timelineTrackVisibility.ts
|
|
71671
71983
|
import { useCallback as useCallback113 } from "react";
|
|
@@ -71929,8 +72241,8 @@ function patchLiveAudioGroupState(iframe, elements, groupId, activeCompPath, res
|
|
|
71929
72241
|
const target = findTimelineElementInIframe(iframe, element, activeCompPath);
|
|
71930
72242
|
if (!target) continue;
|
|
71931
72243
|
const next = restore ? restore.get(element) ?? null : groupId;
|
|
71932
|
-
if (next) target.setAttribute(
|
|
71933
|
-
else target.removeAttribute(
|
|
72244
|
+
if (next) target.setAttribute(HF_AUDIO_GROUP_ATTR5, next);
|
|
72245
|
+
else target.removeAttribute(HF_AUDIO_GROUP_ATTR5);
|
|
71934
72246
|
}
|
|
71935
72247
|
invalidateGroupInfoCache(iframe?.contentDocument);
|
|
71936
72248
|
}
|
|
@@ -71938,7 +72250,7 @@ function captureAudioGroupState(iframe, elements, activeCompPath) {
|
|
|
71938
72250
|
const prior = /* @__PURE__ */ new Map();
|
|
71939
72251
|
for (const element of elements) {
|
|
71940
72252
|
const target = findTimelineElementInIframe(iframe, element, activeCompPath);
|
|
71941
|
-
prior.set(element, target?.getAttribute(
|
|
72253
|
+
prior.set(element, target?.getAttribute(HF_AUDIO_GROUP_ATTR5) ?? null);
|
|
71942
72254
|
}
|
|
71943
72255
|
return prior;
|
|
71944
72256
|
}
|
|
@@ -71994,7 +72306,7 @@ async function createAudioGroupAndAssignMembers({
|
|
|
71994
72306
|
reseekPreviewRuntime(previewIframe);
|
|
71995
72307
|
const groupOperation = {
|
|
71996
72308
|
type: "attribute",
|
|
71997
|
-
property:
|
|
72309
|
+
property: HF_AUDIO_GROUP_ATTR5,
|
|
71998
72310
|
value: groupId
|
|
71999
72311
|
};
|
|
72000
72312
|
const groupPath = activeCompPath || "index.html";
|
|
@@ -73863,30 +74175,8 @@ function injectPreviewImportedFont(doc, asset) {
|
|
|
73863
74175
|
style.textContent = importedFontFaceCss(asset);
|
|
73864
74176
|
doc.head.appendChild(style);
|
|
73865
74177
|
}
|
|
73866
|
-
function
|
|
73867
|
-
|
|
73868
|
-
if (html2.includes(css2)) return html2;
|
|
73869
|
-
const styleRe = /<style\b[^>]*data-hf-studio-fonts=(["'])true\1[^>]*>([\s\S]*?)<\/style>/i;
|
|
73870
|
-
const styleMatch = styleRe.exec(html2);
|
|
73871
|
-
if (styleMatch) {
|
|
73872
|
-
const nextCss = `${styleMatch[2].trim()}
|
|
73873
|
-
${css2}`.trim();
|
|
73874
|
-
return html2.replace(
|
|
73875
|
-
styleMatch[0],
|
|
73876
|
-
() => `<style data-hf-studio-fonts="true">
|
|
73877
|
-
${nextCss}
|
|
73878
|
-
</style>`
|
|
73879
|
-
);
|
|
73880
|
-
}
|
|
73881
|
-
const styleTag = `<style data-hf-studio-fonts="true">
|
|
73882
|
-
${css2}
|
|
73883
|
-
</style>`;
|
|
73884
|
-
if (/<\/head>/i.test(html2)) {
|
|
73885
|
-
return html2.replace(/<\/head>/i, () => ` ${styleTag}
|
|
73886
|
-
</head>`);
|
|
73887
|
-
}
|
|
73888
|
-
return `${styleTag}
|
|
73889
|
-
${html2}`;
|
|
74178
|
+
function importedFontFaceCssFor(asset, sourceFile) {
|
|
74179
|
+
return importedFontFaceCss(asset, toRelativeProjectAssetPath(sourceFile, asset.path));
|
|
73890
74180
|
}
|
|
73891
74181
|
|
|
73892
74182
|
// src/hooks/domEditPersistFailure.ts
|
|
@@ -73904,6 +74194,13 @@ var DomEditPersistUnsafeValueError = class extends Error {
|
|
|
73904
74194
|
this.alreadyToasted = options.alreadyToasted ?? false;
|
|
73905
74195
|
}
|
|
73906
74196
|
};
|
|
74197
|
+
var DomEditPersistPreparedWriteError = class extends Error {
|
|
74198
|
+
alreadyToasted = true;
|
|
74199
|
+
constructor(targetPath) {
|
|
74200
|
+
super(`The edit saved but its follow-up write to ${targetPath} did not`);
|
|
74201
|
+
this.name = "DomEditPersistPreparedWriteError";
|
|
74202
|
+
}
|
|
74203
|
+
};
|
|
73907
74204
|
function summarizeOperations(operations) {
|
|
73908
74205
|
return operations.map((op) => `${op.type}:${op.property}`).join(", ");
|
|
73909
74206
|
}
|
|
@@ -73923,7 +74220,7 @@ function getSelectionLabel(selection) {
|
|
|
73923
74220
|
return selection.label || selection.selector || selection.id || "this element";
|
|
73924
74221
|
}
|
|
73925
74222
|
function wasAlreadyToasted(error) {
|
|
73926
|
-
return (error instanceof DomEditPersistUnsafeValueError || error instanceof StudioSaveHttpError) && error.alreadyToasted;
|
|
74223
|
+
return (error instanceof DomEditPersistUnsafeValueError || error instanceof DomEditPersistPreparedWriteError || error instanceof StudioSaveHttpError) && error.alreadyToasted;
|
|
73927
74224
|
}
|
|
73928
74225
|
function reportDomEditPersistFailure(selection, operations, error, showToast) {
|
|
73929
74226
|
const detail = getErrorDetail(error);
|
|
@@ -74154,16 +74451,16 @@ async function postPatchElement(projectId, targetPath, body, showToast) {
|
|
|
74154
74451
|
}
|
|
74155
74452
|
async function writePreparedContent(targetPath, patchedContent, prepare, writeProjectFile, showToast) {
|
|
74156
74453
|
const preparedContent = prepare(patchedContent, targetPath);
|
|
74157
|
-
if (preparedContent === patchedContent) return patchedContent;
|
|
74454
|
+
if (preparedContent === patchedContent) return { content: patchedContent, failed: false };
|
|
74158
74455
|
try {
|
|
74159
74456
|
await writeProjectFile(targetPath, preparedContent, patchedContent);
|
|
74160
|
-
return preparedContent;
|
|
74457
|
+
return { content: preparedContent, failed: false };
|
|
74161
74458
|
} catch (error) {
|
|
74162
74459
|
showToast(
|
|
74163
74460
|
`Saved, but couldn't finish updating ${targetPath}: ${getErrorDetail2(error)}`,
|
|
74164
74461
|
"error"
|
|
74165
74462
|
);
|
|
74166
|
-
return patchedContent;
|
|
74463
|
+
return { content: patchedContent, failed: true };
|
|
74167
74464
|
}
|
|
74168
74465
|
}
|
|
74169
74466
|
|
|
@@ -74191,7 +74488,8 @@ function useDomEditPersist({
|
|
|
74191
74488
|
if (options?.shouldSave && !options.shouldSave()) return;
|
|
74192
74489
|
const targetPath = selection.sourceFile || activeCompPath || "index.html";
|
|
74193
74490
|
const completePersistence = (result, changed) => {
|
|
74194
|
-
if (options?.skipRefresh && changed
|
|
74491
|
+
if (options?.skipRefresh && changed && !options.deferRender)
|
|
74492
|
+
reseekPreviewRuntime(previewIframeRef.current);
|
|
74195
74493
|
return result;
|
|
74196
74494
|
};
|
|
74197
74495
|
const readTarget = async () => {
|
|
@@ -74202,7 +74500,12 @@ function useDomEditPersist({
|
|
|
74202
74500
|
return options?.shouldSave && !options.shouldSave() ? null : content;
|
|
74203
74501
|
};
|
|
74204
74502
|
const patchTarget = buildDomEditPatchTarget(selection);
|
|
74205
|
-
const
|
|
74503
|
+
const font = options?.importedFont;
|
|
74504
|
+
const patchBody = {
|
|
74505
|
+
target: patchTarget,
|
|
74506
|
+
operations,
|
|
74507
|
+
...font ? { fontFaceCss: importedFontFaceCssFor(font, targetPath) } : {}
|
|
74508
|
+
};
|
|
74206
74509
|
const unsafeFields = findUnsafeDomPatchValues2(patchBody);
|
|
74207
74510
|
if (unsafeFields.length > 0) {
|
|
74208
74511
|
const fields = formatUnsafeFieldList(unsafeFields);
|
|
@@ -74211,7 +74514,7 @@ function useDomEditPersist({
|
|
|
74211
74514
|
alreadyToasted: true
|
|
74212
74515
|
});
|
|
74213
74516
|
}
|
|
74214
|
-
if (onTrySdkPersist && !options?.prepareContent) {
|
|
74517
|
+
if (onTrySdkPersist && !font && !options?.prepareContent) {
|
|
74215
74518
|
const originalContent = await readTarget();
|
|
74216
74519
|
if (originalContent === null) return;
|
|
74217
74520
|
const cutover = await onTrySdkPersist(selection, operations, originalContent, targetPath, {
|
|
@@ -74233,19 +74536,22 @@ function useDomEditPersist({
|
|
|
74233
74536
|
coalesceMs: options?.coalesceMs
|
|
74234
74537
|
};
|
|
74235
74538
|
const prepare = options?.prepareContent;
|
|
74539
|
+
let preparedWriteFailed = false;
|
|
74236
74540
|
const saved2 = await serializeStudioFileMutations(writeProjectFile, [targetPath], async () => {
|
|
74237
74541
|
const originalContent = await readTarget();
|
|
74238
74542
|
if (originalContent === null) return null;
|
|
74239
74543
|
const patchData2 = await postPatchElement(pid, targetPath, patchBody, showToast);
|
|
74240
74544
|
if (!patchData2.changed) return { patchData: patchData2, patchedContent: null, finalContent: null };
|
|
74241
74545
|
const patchedContent2 = typeof patchData2.content === "string" ? patchData2.content : originalContent;
|
|
74242
|
-
const
|
|
74546
|
+
const prepared = prepare ? await writePreparedContent(
|
|
74243
74547
|
targetPath,
|
|
74244
74548
|
patchedContent2,
|
|
74245
74549
|
prepare,
|
|
74246
74550
|
writeProjectFile,
|
|
74247
74551
|
showToast
|
|
74248
|
-
) : patchedContent2;
|
|
74552
|
+
) : { content: patchedContent2, failed: false };
|
|
74553
|
+
preparedWriteFailed = prepared.failed;
|
|
74554
|
+
const finalContent2 = prepared.content;
|
|
74249
74555
|
await editHistory.recordEdit({
|
|
74250
74556
|
...history2,
|
|
74251
74557
|
files: { [targetPath]: { before: originalContent, after: finalContent2 } }
|
|
@@ -74278,10 +74584,12 @@ function useDomEditPersist({
|
|
|
74278
74584
|
if (!options?.skipRefresh) {
|
|
74279
74585
|
reloadPreview();
|
|
74280
74586
|
}
|
|
74281
|
-
|
|
74587
|
+
const outcome = completePersistence(
|
|
74282
74588
|
finalContent === patchedContent && typeof patchData.path === "string" && typeof patchData.version === "string" ? { sourceFile: patchData.path, version: patchData.version, changed: true } : void 0,
|
|
74283
74589
|
true
|
|
74284
74590
|
);
|
|
74591
|
+
if (preparedWriteFailed) throw new DomEditPersistPreparedWriteError(targetPath);
|
|
74592
|
+
return outcome;
|
|
74285
74593
|
},
|
|
74286
74594
|
[
|
|
74287
74595
|
activeCompPath,
|
|
@@ -74299,9 +74607,10 @@ function useDomEditPersist({
|
|
|
74299
74607
|
(selection, operations, options) => {
|
|
74300
74608
|
const expectedProjectId = projectIdRef.current;
|
|
74301
74609
|
if (!expectedProjectId) return Promise.reject(new Error("No active project"));
|
|
74302
|
-
|
|
74610
|
+
const save = () => queueDomEditSave(
|
|
74303
74611
|
() => performPersistDomEditOperations(selection, operations, options, expectedProjectId)
|
|
74304
74612
|
);
|
|
74613
|
+
return selection.element ? countStudioManualEditSave(selection.element, save) : save();
|
|
74305
74614
|
},
|
|
74306
74615
|
[performPersistDomEditOperations, projectIdRef, queueDomEditSave]
|
|
74307
74616
|
);
|
|
@@ -74321,7 +74630,8 @@ function useDomEditPositionPatchCommit({
|
|
|
74321
74630
|
label: options.label,
|
|
74322
74631
|
coalesceKey: options.coalesceKey,
|
|
74323
74632
|
coalesceMs: options.coalesceMs,
|
|
74324
|
-
skipRefresh: options.skipRefresh ?? true
|
|
74633
|
+
skipRefresh: options.skipRefresh ?? true,
|
|
74634
|
+
deferRender: options.deferRender
|
|
74325
74635
|
}).then(() => void 0).catch((error) => {
|
|
74326
74636
|
if (error instanceof DomEditSaveQueueOpenError) throw error;
|
|
74327
74637
|
if (!wasAlreadyToasted(error)) {
|
|
@@ -74457,7 +74767,7 @@ async function commitDomStyles(context, selection, styles) {
|
|
|
74457
74767
|
label: "Edit layer style",
|
|
74458
74768
|
// Inline styles are already live, so a reload would only blank the preview.
|
|
74459
74769
|
skipRefresh: true,
|
|
74460
|
-
|
|
74770
|
+
importedFont: importedFont ?? void 0
|
|
74461
74771
|
}),
|
|
74462
74772
|
shouldRevert: ownsAny,
|
|
74463
74773
|
revert: () => {
|
|
@@ -74479,7 +74789,7 @@ async function commitDomStyles(context, selection, styles) {
|
|
|
74479
74789
|
|
|
74480
74790
|
// src/hooks/useDomEditAttributeCommits.ts
|
|
74481
74791
|
import { useCallback as useCallback123, useRef as useRef141 } from "react";
|
|
74482
|
-
import { HF_AUDIO_GROUP_ATTR as
|
|
74792
|
+
import { HF_AUDIO_GROUP_ATTR as HF_AUDIO_GROUP_ATTR6, HF_AUDIO_GROUP_TAG as HF_AUDIO_GROUP_TAG7 } from "@hyperframes/core/audio-groups";
|
|
74483
74793
|
function resolveFullAttrName(attr, prefixData) {
|
|
74484
74794
|
return prefixData && !attr.startsWith("data-") ? `data-${attr}` : attr;
|
|
74485
74795
|
}
|
|
@@ -74487,13 +74797,20 @@ function resolveOptimisticAttributeValue(attr, value) {
|
|
|
74487
74797
|
if (value === null) return null;
|
|
74488
74798
|
return value === "false" && HTML_BOOLEAN_ATTRIBUTES.has(attr) ? null : value;
|
|
74489
74799
|
}
|
|
74800
|
+
function previewAttributeName(op) {
|
|
74801
|
+
return op.type === "attribute" ? resolveFullAttrName(op.property, true) : op.property;
|
|
74802
|
+
}
|
|
74803
|
+
function previewAttributeValue(op) {
|
|
74804
|
+
if (op.type === "html-attribute") return resolveOptimisticAttributeValue(op.property, op.value);
|
|
74805
|
+
return op.value === "" ? null : op.value;
|
|
74806
|
+
}
|
|
74490
74807
|
function setOrRemovePreviewAttribute(el, fullAttr, value) {
|
|
74491
74808
|
if (value === null) {
|
|
74492
74809
|
el.removeAttribute(fullAttr);
|
|
74493
74810
|
} else {
|
|
74494
74811
|
el.setAttribute(fullAttr, value);
|
|
74495
74812
|
}
|
|
74496
|
-
if (el.tagName.toLowerCase() === HF_AUDIO_GROUP_TAG7 || fullAttr ===
|
|
74813
|
+
if (el.tagName.toLowerCase() === HF_AUDIO_GROUP_TAG7 || fullAttr === HF_AUDIO_GROUP_ATTR6) {
|
|
74497
74814
|
invalidateGroupInfoCache(el.ownerDocument);
|
|
74498
74815
|
}
|
|
74499
74816
|
}
|
|
@@ -74583,25 +74900,20 @@ function useDomEditAttributeCommits({
|
|
|
74583
74900
|
previewIframeRef
|
|
74584
74901
|
]
|
|
74585
74902
|
);
|
|
74586
|
-
const
|
|
74587
|
-
async (selection,
|
|
74903
|
+
const commitAttributeOps = useCallback123(
|
|
74904
|
+
async (selection, ops, options) => {
|
|
74588
74905
|
const iframe = previewIframeRef.current;
|
|
74589
|
-
const entries =
|
|
74590
|
-
|
|
74591
|
-
|
|
74592
|
-
value
|
|
74906
|
+
const entries = ops.map((op) => ({
|
|
74907
|
+
fullAttr: previewAttributeName(op),
|
|
74908
|
+
value: previewAttributeValue(op)
|
|
74593
74909
|
}));
|
|
74594
|
-
const commitKey = `${options.coalescePrefix}:${
|
|
74910
|
+
const commitKey = `${options.coalescePrefix}:${ops.map((op) => op.property).sort().join(",")}:${getDomEditTargetKey(selection)}`;
|
|
74595
74911
|
const isLatestCommit = bumpDomEditCommitMapVersion(
|
|
74596
74912
|
domAttributeCommitVersionRef.current,
|
|
74597
74913
|
commitKey
|
|
74598
74914
|
);
|
|
74599
|
-
const ops = entries.map((entry2) => ({
|
|
74600
|
-
type: "attribute",
|
|
74601
|
-
property: entry2.attr,
|
|
74602
|
-
value: entry2.value
|
|
74603
|
-
}));
|
|
74604
74915
|
let captured = null;
|
|
74916
|
+
let landed = false;
|
|
74605
74917
|
await runDomEditCommit({
|
|
74606
74918
|
capture: () => {
|
|
74607
74919
|
captured = captureMultiAttributeElement(
|
|
@@ -74614,16 +74926,16 @@ function useDomEditAttributeCommits({
|
|
|
74614
74926
|
apply: () => {
|
|
74615
74927
|
if (!captured) return;
|
|
74616
74928
|
for (const entry2 of entries) {
|
|
74617
|
-
|
|
74618
|
-
setOrRemovePreviewAttribute(captured.element, entry2.fullAttr, nextValue);
|
|
74929
|
+
setOrRemovePreviewAttribute(captured.element, entry2.fullAttr, entry2.value);
|
|
74619
74930
|
}
|
|
74620
74931
|
},
|
|
74621
74932
|
persist: () => persistDomEditOperations(selection, ops, {
|
|
74622
74933
|
label: options.label,
|
|
74623
74934
|
coalesceKey: commitKey,
|
|
74624
|
-
skipRefresh: options.skipRefresh
|
|
74935
|
+
skipRefresh: options.skipRefresh,
|
|
74936
|
+
prepareContent: options.prepareContent
|
|
74625
74937
|
}),
|
|
74626
|
-
shouldRevert: () => isLatestCommit(),
|
|
74938
|
+
shouldRevert: (error) => isLatestCommit() && !(error instanceof DomEditPersistPreparedWriteError),
|
|
74627
74939
|
revert: () => {
|
|
74628
74940
|
if (!captured) return;
|
|
74629
74941
|
for (const entry2 of entries) {
|
|
@@ -74636,10 +74948,19 @@ function useDomEditAttributeCommits({
|
|
|
74636
74948
|
},
|
|
74637
74949
|
onError: (error) => reportDomEditPersistFailure(selection, ops, error, showToast),
|
|
74638
74950
|
shouldResync: () => isLatestCommit() && !!options.refreshAfter,
|
|
74639
|
-
resync: () =>
|
|
74640
|
-
|
|
74951
|
+
resync: () => {
|
|
74952
|
+
refreshDomEditSelectionFromPreview(selection);
|
|
74953
|
+
if (options.syncAutomation) {
|
|
74954
|
+
syncStoredAutomationFromPreview(previewIframeRef.current?.contentDocument ?? null);
|
|
74955
|
+
}
|
|
74956
|
+
},
|
|
74957
|
+
onSettled: (ok) => {
|
|
74958
|
+
landed = ok;
|
|
74959
|
+
options.onSettled?.(ok);
|
|
74960
|
+
},
|
|
74641
74961
|
onFinally: isLatestCommit.release
|
|
74642
74962
|
});
|
|
74963
|
+
return landed;
|
|
74643
74964
|
},
|
|
74644
74965
|
[
|
|
74645
74966
|
activeCompPath,
|
|
@@ -74651,14 +74972,33 @@ function useDomEditAttributeCommits({
|
|
|
74651
74972
|
);
|
|
74652
74973
|
const handleDomAttributesCommit = useCallback123(
|
|
74653
74974
|
async (selection, attrs) => {
|
|
74654
|
-
await
|
|
74655
|
-
|
|
74656
|
-
|
|
74657
|
-
|
|
74658
|
-
|
|
74659
|
-
|
|
74975
|
+
await commitAttributeOps(
|
|
74976
|
+
selection,
|
|
74977
|
+
Object.entries(attrs).map(([property, value]) => ({
|
|
74978
|
+
type: "attribute",
|
|
74979
|
+
property,
|
|
74980
|
+
value
|
|
74981
|
+
})),
|
|
74982
|
+
{
|
|
74983
|
+
label: "Edit timing",
|
|
74984
|
+
coalescePrefix: "attrs",
|
|
74985
|
+
skipRefresh: false,
|
|
74986
|
+
refreshAfter: true
|
|
74987
|
+
}
|
|
74988
|
+
);
|
|
74660
74989
|
},
|
|
74661
|
-
[
|
|
74990
|
+
[commitAttributeOps]
|
|
74991
|
+
);
|
|
74992
|
+
const handleDomAttributeBatchCommit = useCallback123(
|
|
74993
|
+
(selection, ops, options) => commitAttributeOps(selection, ops, {
|
|
74994
|
+
label: options.label,
|
|
74995
|
+
coalescePrefix: "attr-batch",
|
|
74996
|
+
skipRefresh: false,
|
|
74997
|
+
refreshAfter: true,
|
|
74998
|
+
syncAutomation: true,
|
|
74999
|
+
prepareContent: options.prepareContent
|
|
75000
|
+
}),
|
|
75001
|
+
[commitAttributeOps]
|
|
74662
75002
|
);
|
|
74663
75003
|
const handleDomAttributeCommit = useCallback123(
|
|
74664
75004
|
async (attr, value) => {
|
|
@@ -74759,7 +75099,8 @@ function useDomEditAttributeCommits({
|
|
|
74759
75099
|
handleDomAttributeLiveCommit,
|
|
74760
75100
|
handleDomAttributeQuietCommit,
|
|
74761
75101
|
handleDomHtmlAttributeCommit,
|
|
74762
|
-
handleDomAttributesCommit
|
|
75102
|
+
handleDomAttributesCommit,
|
|
75103
|
+
handleDomAttributeBatchCommit
|
|
74763
75104
|
};
|
|
74764
75105
|
}
|
|
74765
75106
|
|
|
@@ -74799,7 +75140,8 @@ function useDomEditTextCommits({
|
|
|
74799
75140
|
handleDomAttributeLiveCommit,
|
|
74800
75141
|
handleDomAttributeQuietCommit,
|
|
74801
75142
|
handleDomHtmlAttributeCommit,
|
|
74802
|
-
handleDomAttributesCommit
|
|
75143
|
+
handleDomAttributesCommit,
|
|
75144
|
+
handleDomAttributeBatchCommit
|
|
74803
75145
|
} = useDomEditAttributeCommits({
|
|
74804
75146
|
activeCompPath,
|
|
74805
75147
|
previewIframeRef,
|
|
@@ -75010,7 +75352,7 @@ function useDomEditTextCommits({
|
|
|
75010
75352
|
await persistDomEditOperations(selection, textCommit.operations, {
|
|
75011
75353
|
label: "Edit text",
|
|
75012
75354
|
skipRefresh: true,
|
|
75013
|
-
|
|
75355
|
+
importedFont: importedFont ?? void 0
|
|
75014
75356
|
});
|
|
75015
75357
|
},
|
|
75016
75358
|
shouldRevert: () => isLatestTextCommit(),
|
|
@@ -75122,6 +75464,7 @@ function useDomEditTextCommits({
|
|
|
75122
75464
|
handleDomAttributeQuietCommit,
|
|
75123
75465
|
handleDomHtmlAttributeCommit,
|
|
75124
75466
|
handleDomAttributesCommit,
|
|
75467
|
+
handleDomAttributeBatchCommit,
|
|
75125
75468
|
handleDomTextCommit,
|
|
75126
75469
|
handleDomTextCommitForSelection,
|
|
75127
75470
|
handleDomRichTextCommit,
|
|
@@ -75283,7 +75626,7 @@ function useDomGeometryCommits({
|
|
|
75283
75626
|
[commitPositionPatchToHtml, readOnlyPreview, showToast]
|
|
75284
75627
|
);
|
|
75285
75628
|
const handleDomBoxSizeCommit = useCallback125(
|
|
75286
|
-
(selection, next, offset, restore) => {
|
|
75629
|
+
(selection, next, offset, restore, undoKey) => {
|
|
75287
75630
|
if (readOnlyPreview) return Promise.resolve();
|
|
75288
75631
|
const element = selection.element;
|
|
75289
75632
|
const beforeSize = captureStudioBoxSize(element);
|
|
@@ -75296,7 +75639,7 @@ function useDomGeometryCommits({
|
|
|
75296
75639
|
if (offset) patches.push(...writePlainMove(element, offset));
|
|
75297
75640
|
return commitPositionPatchToHtml(selection, patches, {
|
|
75298
75641
|
label: "Resize layer box",
|
|
75299
|
-
coalesceKey: `box-size:${getDomEditTargetKey(selection)}`
|
|
75642
|
+
...undoKey ? { coalesceKey: undoKey, coalesceMs: Number.POSITIVE_INFINITY, deferRender: true } : { coalesceKey: `box-size:${getDomEditTargetKey(selection)}` }
|
|
75300
75643
|
}).catch((error) => {
|
|
75301
75644
|
restoreStudioBoxSize(element, beforeSize);
|
|
75302
75645
|
if (offset) restoreStudioPathOffset(element, beforeOffset);
|
|
@@ -75492,6 +75835,7 @@ function useDomEditCommits({
|
|
|
75492
75835
|
handleDomAttributeQuietCommit,
|
|
75493
75836
|
handleDomHtmlAttributeCommit,
|
|
75494
75837
|
handleDomAttributesCommit,
|
|
75838
|
+
handleDomAttributeBatchCommit,
|
|
75495
75839
|
handleDomTextCommit,
|
|
75496
75840
|
handleDomTextCommitForSelection,
|
|
75497
75841
|
handleDomRichTextCommit,
|
|
@@ -75548,6 +75892,7 @@ function useDomEditCommits({
|
|
|
75548
75892
|
handleDomAttributeQuietCommit,
|
|
75549
75893
|
handleDomHtmlAttributeCommit,
|
|
75550
75894
|
handleDomAttributesCommit,
|
|
75895
|
+
handleDomAttributeBatchCommit,
|
|
75551
75896
|
handleDomTextCommit,
|
|
75552
75897
|
handleDomTextCommitForSelection,
|
|
75553
75898
|
handleDomRichTextCommit,
|
|
@@ -77037,7 +77382,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
77037
77382
|
}
|
|
77038
77383
|
return run();
|
|
77039
77384
|
};
|
|
77040
|
-
const commit = (selection, mutation, options) => {
|
|
77385
|
+
const commit = trackedStudioEdit((selection, mutation, options) => {
|
|
77041
77386
|
if (!activeProjectId) return Promise.resolve();
|
|
77042
77387
|
const file = selection.sourceFile || activeCompPath || "index.html";
|
|
77043
77388
|
return serializeCommit(
|
|
@@ -77045,8 +77390,8 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
77045
77390
|
options.serializeKey,
|
|
77046
77391
|
() => runCommit(activeProjectId, activeCompPath, file, selection, mutation, options)
|
|
77047
77392
|
);
|
|
77048
|
-
};
|
|
77049
|
-
commit.batch = (calls, options) => {
|
|
77393
|
+
});
|
|
77394
|
+
commit.batch = trackedStudioEdit((calls, options) => {
|
|
77050
77395
|
if (!activeProjectId) return Promise.resolve();
|
|
77051
77396
|
const file = calls[0]?.selection.sourceFile || activeCompPath || "index.html";
|
|
77052
77397
|
return serializeCommit(
|
|
@@ -77054,7 +77399,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
77054
77399
|
options.serializeKey,
|
|
77055
77400
|
() => runBatchCommit(activeProjectId, activeCompPath, file, calls, options)
|
|
77056
77401
|
);
|
|
77057
|
-
};
|
|
77402
|
+
});
|
|
77058
77403
|
return commit;
|
|
77059
77404
|
}, [runCommit, runBatchCommit, activeCompPath, activeProjectId, writeProjectFile]);
|
|
77060
77405
|
const trackGsapSaveFailure = useGsapSaveFailureTelemetry(activeCompPath);
|
|
@@ -77803,6 +78148,7 @@ function preflightGsapResizeIntercept(selection, animations, iframe, fetchedAnim
|
|
|
77803
78148
|
|
|
77804
78149
|
// src/hooks/gsapResizeIntercept.ts
|
|
77805
78150
|
var IDENTITY_ONE_PROPS = /* @__PURE__ */ new Set(["opacity", "autoAlpha", "scale", "scaleX", "scaleY"]);
|
|
78151
|
+
var SIZE_PROPS = /* @__PURE__ */ new Set(["width", "height"]);
|
|
77806
78152
|
function synthesizeIdentityProps(source) {
|
|
77807
78153
|
const id = {};
|
|
77808
78154
|
for (const [k, v] of Object.entries(source)) {
|
|
@@ -77830,7 +78176,7 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
77830
78176
|
const fetchedAnimations = fetchFallbackAnimations ? await fetchFallbackAnimations() : [];
|
|
77831
78177
|
const outcome = preflightGsapResizeIntercept(selection, animations, iframe, fetchedAnimations);
|
|
77832
78178
|
if (outcome.status === "blocked") return outcome;
|
|
77833
|
-
const { resizeGroup, resizeProperties, workingAnimations } = resizeRoute(
|
|
78179
|
+
const { allKnownAnimations, resizeGroup, resizeProperties, workingAnimations } = resizeRoute(
|
|
77834
78180
|
animations,
|
|
77835
78181
|
fetchedAnimations
|
|
77836
78182
|
);
|
|
@@ -77851,6 +78197,10 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
77851
78197
|
size
|
|
77852
78198
|
});
|
|
77853
78199
|
if (!anim || isInstantHold(anim)) {
|
|
78200
|
+
const scriptWritesSize = allKnownAnimations.some(
|
|
78201
|
+
(a) => animationWritesAnyProperty(a, SIZE_PROPS)
|
|
78202
|
+
);
|
|
78203
|
+
if (!scriptWritesSize) return { status: "element-size" };
|
|
77854
78204
|
const sel = selectorFromSelection(selection) ?? writeTargetSelector(selection);
|
|
77855
78205
|
if (!sel) return { status: "blocked", reason: "no-selector" };
|
|
77856
78206
|
const sizeSet = resizeGroup === "size" ? anim ?? findSizeSetAnimation(workingAnimations, sel, selection.element) : findSizeSetAnimation(workingAnimations, sel, selection.element);
|
|
@@ -78080,7 +78430,6 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
|
|
|
78080
78430
|
);
|
|
78081
78431
|
return { status: "persisted", ownsDragOffset: await finalizeScaleResizeCommit() };
|
|
78082
78432
|
}
|
|
78083
|
-
const SIZE_PROPS = /* @__PURE__ */ new Set(["width", "height"]);
|
|
78084
78433
|
const backfillDefaults = {};
|
|
78085
78434
|
for (const k of Object.keys(runtimeProps)) {
|
|
78086
78435
|
if (SIZE_PROPS.has(k)) continue;
|
|
@@ -78692,7 +79041,9 @@ function useGsapAwareEditing({
|
|
|
78692
79041
|
makeFetchFallback(selection)
|
|
78693
79042
|
);
|
|
78694
79043
|
assertGsapEditPersisted(outcome);
|
|
78695
|
-
|
|
79044
|
+
if (outcome.status === "element-size")
|
|
79045
|
+
await handleDomBoxSizeCommit(selection, next, void 0, void 0, coalesceKey);
|
|
79046
|
+
else cropUndoKey = coalesceKey;
|
|
78696
79047
|
const ownsDragOffset = outcome.status === "persisted" && outcome.ownsDragOffset === true;
|
|
78697
79048
|
logResize("intercept-handled", {
|
|
78698
79049
|
scaleRoute,
|
|
@@ -79306,6 +79657,7 @@ function useDomEditSession({
|
|
|
79306
79657
|
handleDomAttributeQuietCommit,
|
|
79307
79658
|
handleDomHtmlAttributeCommit,
|
|
79308
79659
|
handleDomAttributesCommit,
|
|
79660
|
+
handleDomAttributeBatchCommit,
|
|
79309
79661
|
handleDomTextCommit,
|
|
79310
79662
|
handleDomTextCommitForSelection,
|
|
79311
79663
|
handleDomRichTextCommit,
|
|
@@ -79570,6 +79922,7 @@ function useDomEditSession({
|
|
|
79570
79922
|
handleDomAttributeQuietCommit,
|
|
79571
79923
|
handleDomHtmlAttributeCommit,
|
|
79572
79924
|
handleDomAttributesCommit,
|
|
79925
|
+
handleDomAttributeBatchCommit,
|
|
79573
79926
|
handleDomPathOffsetCommit: handleGsapAwarePathOffsetCommit,
|
|
79574
79927
|
handleDomGroupPathOffsetCommit: handleGsapAwareGroupPathOffsetCommit,
|
|
79575
79928
|
handleDomZIndexReorderCommit,
|
|
@@ -81103,7 +81456,7 @@ function useAppHotkeys({
|
|
|
81103
81456
|
showToast,
|
|
81104
81457
|
syncHistoryPreviewAfterApply,
|
|
81105
81458
|
showHistoryRestoreNow,
|
|
81106
|
-
|
|
81459
|
+
settlePendingEdits,
|
|
81107
81460
|
handleCopy: handleCopy2,
|
|
81108
81461
|
handlePaste: handlePaste2,
|
|
81109
81462
|
handleCut,
|
|
@@ -81127,7 +81480,7 @@ function useAppHotkeys({
|
|
|
81127
81480
|
showToast,
|
|
81128
81481
|
syncHistoryPreviewAfterApply,
|
|
81129
81482
|
showHistoryRestoreNow,
|
|
81130
|
-
waitForPendingDomEditSaves,
|
|
81483
|
+
waitForPendingDomEditSaves: settlePendingEdits,
|
|
81131
81484
|
onAfterUndoRedo,
|
|
81132
81485
|
activeCompPath,
|
|
81133
81486
|
forceReloadSdkSession
|
|
@@ -89472,6 +89825,10 @@ function makeSlideshowNotesController() {
|
|
|
89472
89825
|
}
|
|
89473
89826
|
};
|
|
89474
89827
|
}
|
|
89828
|
+
function isPanelUndoKey(e) {
|
|
89829
|
+
if (!(e.metaKey || e.ctrlKey) || e.shiftKey || e.key.toLowerCase() !== "z") return false;
|
|
89830
|
+
return !isTypingTarget(e.target);
|
|
89831
|
+
}
|
|
89475
89832
|
function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
89476
89833
|
const { editingFile } = useFileManagerContext();
|
|
89477
89834
|
const compHtml = editingFile?.content ?? null;
|
|
@@ -89677,13 +90034,10 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
89677
90034
|
{
|
|
89678
90035
|
className: "flex flex-col h-full overflow-y-auto text-text-0",
|
|
89679
90036
|
onKeyDown: (e) => {
|
|
89680
|
-
if ((e
|
|
89681
|
-
|
|
89682
|
-
|
|
89683
|
-
|
|
89684
|
-
e.stopPropagation();
|
|
89685
|
-
handleUndo();
|
|
89686
|
-
}
|
|
90037
|
+
if (!isPanelUndoKey(e)) return;
|
|
90038
|
+
e.preventDefault();
|
|
90039
|
+
e.stopPropagation();
|
|
90040
|
+
handleUndo();
|
|
89687
90041
|
},
|
|
89688
90042
|
children: [
|
|
89689
90043
|
persistError && /* @__PURE__ */ jsxs173(
|
|
@@ -91235,6 +91589,7 @@ function StudioRightPanels({
|
|
|
91235
91589
|
handleDomAttributeQuietCommit,
|
|
91236
91590
|
handleDomHtmlAttributeCommit,
|
|
91237
91591
|
handleDomAttributesCommit,
|
|
91592
|
+
handleDomAttributeBatchCommit,
|
|
91238
91593
|
handleDomPathOffsetCommit,
|
|
91239
91594
|
handleDomBoxSizeCommit,
|
|
91240
91595
|
handleDomRotationCommit,
|
|
@@ -91357,6 +91712,7 @@ function StudioRightPanels({
|
|
|
91357
91712
|
onSetStyle: handleDomStyleCommit,
|
|
91358
91713
|
onSetAttribute: handleDomAttributeCommit,
|
|
91359
91714
|
onSetAttributes: handleDomAttributesCommit,
|
|
91715
|
+
onSetAttributeBatch: handleDomAttributeBatchCommit,
|
|
91360
91716
|
onSetAttributeLive: setAttributeWhileDragging,
|
|
91361
91717
|
onSetAttributeQuiet: handleDomAttributeQuietCommit,
|
|
91362
91718
|
onApplyColorGradingScope: handleApplyColorGradingScope,
|
|
@@ -91800,17 +92156,11 @@ function useEnableKeyframes(sessionRef) {
|
|
|
91800
92156
|
|
|
91801
92157
|
// src/hooks/useKeyframeKeyboard.ts
|
|
91802
92158
|
import { useEffect as useEffect147, useCallback as useCallback183 } from "react";
|
|
91803
|
-
function isTextInput2(el) {
|
|
91804
|
-
if (!el) return false;
|
|
91805
|
-
const tag = el.tagName;
|
|
91806
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
91807
|
-
return el.isContentEditable === true;
|
|
91808
|
-
}
|
|
91809
92159
|
function useKeyframeKeyboard({ enabled, onAddKeyframe }) {
|
|
91810
92160
|
const handler = useCallback183(
|
|
91811
92161
|
(e) => {
|
|
91812
92162
|
if (!enabled || !onAddKeyframe) return;
|
|
91813
|
-
if (
|
|
92163
|
+
if (isTypingTarget(document.activeElement)) return;
|
|
91814
92164
|
if (e.metaKey || e.ctrlKey) return;
|
|
91815
92165
|
if (e.key.toLowerCase() !== STUDIO_PLAIN_KEYS.addKeyframe) return;
|
|
91816
92166
|
e.preventDefault();
|
|
@@ -92099,10 +92449,7 @@ function TimelineToolbar({
|
|
|
92099
92449
|
const onKeyDown = (e) => {
|
|
92100
92450
|
if (e.key !== "n" && e.key !== "N") return;
|
|
92101
92451
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
92102
|
-
|
|
92103
|
-
if (target?.isContentEditable) return;
|
|
92104
|
-
const tag = target?.tagName?.toLowerCase() ?? "";
|
|
92105
|
-
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
92452
|
+
if (isTypingTarget(e.target)) return;
|
|
92106
92453
|
const store = usePlayerStore.getState();
|
|
92107
92454
|
store.setTimelineSnapEnabled(!store.timelineSnapEnabled);
|
|
92108
92455
|
};
|
|
@@ -92841,7 +93188,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
92841
93188
|
showToast,
|
|
92842
93189
|
syncHistoryPreviewAfterApply: previewPersistence.syncHistoryPreviewAfterApply,
|
|
92843
93190
|
showHistoryRestoreNow: previewPersistence.showHistoryRestoreNow,
|
|
92844
|
-
|
|
93191
|
+
settlePendingEdits: previewPersistence.settlePendingEdits,
|
|
92845
93192
|
handleCopy: handleCopy2,
|
|
92846
93193
|
handlePaste: handlePaste2,
|
|
92847
93194
|
handleCut,
|
|
@@ -92900,9 +93247,10 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
|
|
|
92900
93247
|
readOnlyPreview
|
|
92901
93248
|
});
|
|
92902
93249
|
domEditSelectionBridgeRef.current = domEditSession.domEditSelection;
|
|
92903
|
-
|
|
93250
|
+
const { handleDomZIndexReorderCommit: zCommit, handleDomEditElementDelete: del } = domEditSession;
|
|
93251
|
+
handleDomZIndexReorderCommitRef.current = trackedStudioEdit(zCommit);
|
|
92904
93252
|
clearDomSelectionRef.current = domEditSession.clearDomSelection;
|
|
92905
|
-
handleDomEditElementDeleteRef.current =
|
|
93253
|
+
handleDomEditElementDeleteRef.current = trackedStudioEdit(del);
|
|
92906
93254
|
resetKeyframesRef.current = domEditSession.handleResetSelectedElementKeyframes;
|
|
92907
93255
|
invalidateGsapCacheRef.current = domEditSession.invalidateGsapCache;
|
|
92908
93256
|
deleteSelectedKeyframesRef.current = () => deleteSelectedKeyframes(domEditSession);
|
|
@@ -93794,11 +94142,13 @@ export {
|
|
|
93794
94142
|
buildGridSnapEdges,
|
|
93795
94143
|
buildPickerAgentContextPreview,
|
|
93796
94144
|
buildPickerAgentPrompt,
|
|
94145
|
+
buildStudioTools,
|
|
93797
94146
|
buttonBase,
|
|
93798
94147
|
buttonSizes,
|
|
93799
94148
|
buttonVariants,
|
|
93800
94149
|
cn,
|
|
93801
94150
|
collectSnapContext,
|
|
94151
|
+
collectStudioLookScene,
|
|
93802
94152
|
extractSnapTargets,
|
|
93803
94153
|
fieldBase,
|
|
93804
94154
|
fieldText,
|