hyperframes 0.7.59 → 0.7.61
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/bin/hyperframes.mjs +11 -0
- package/dist/cli.js +13956 -9914
- package/dist/hyperframe-runtime.js +27 -27
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +27 -27
- package/dist/hyperframes-player.global.js +2 -2
- package/dist/runtimeVersion.js +17 -0
- package/dist/skills/hyperframes/SKILL.md +10 -0
- package/dist/skills/hyperframes-cli/SKILL.md +1 -1
- package/dist/skills/hyperframes-cli/references/preview-render.md +21 -1
- package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +1 -1
- package/dist/studio/assets/hyperframes-player-Xvx2hkrc.js +459 -0
- package/dist/studio/assets/{index-CrkAdJkb.js → index-2mxh_HSy.js} +201 -201
- package/dist/studio/assets/{index-B995FG46.js → index-BCpoiv9S.js} +1 -1
- package/dist/studio/assets/index-BhWig0mx.css +1 -0
- package/dist/studio/assets/{index-FvzmPhfG.js → index-D-uFclFj.js} +1 -1
- package/dist/studio/index.html +2 -2
- package/dist/studio/index.js +1441 -770
- package/dist/studio/index.js.map +1 -1
- package/dist/templates/_shared/AGENTS.md +1 -1
- package/dist/templates/_shared/CLAUDE.md +1 -1
- package/package.json +4 -2
- package/dist/studio/assets/hyperframes-player-Bm07FLkl.js +0 -459
- package/dist/studio/assets/index-Dj5p8U_A.css +0 -1
package/dist/studio/index.js
CHANGED
|
@@ -686,6 +686,9 @@ function formatFrameTime(time, duration, fps = STUDIO_PREVIEW_FPS) {
|
|
|
686
686
|
return `${currentFrame}f / ${totalFrames}f`;
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
+
// src/player/lib/timelineIframeHelpers.ts
|
|
690
|
+
import { readClipTiming } from "@hyperframes/core/composition-contract";
|
|
691
|
+
|
|
689
692
|
// src/player/lib/playbackAdapter.ts
|
|
690
693
|
function isFinitePositive(value) {
|
|
691
694
|
return Number.isFinite(value) && value > 0;
|
|
@@ -1239,95 +1242,130 @@ function stopScrubPreviewAudio() {
|
|
|
1239
1242
|
scrubPrevMuted = null;
|
|
1240
1243
|
scrubPrevVolume = null;
|
|
1241
1244
|
}
|
|
1245
|
+
function timelineDuration(iframeWin, compositionId) {
|
|
1246
|
+
return iframeWin.__timelines?.[compositionId]?.duration?.() ?? 0;
|
|
1247
|
+
}
|
|
1248
|
+
function createTimedElementLookup(doc) {
|
|
1249
|
+
const timedById = /* @__PURE__ */ new Map();
|
|
1250
|
+
for (const timed of doc.querySelectorAll("[data-start]")) {
|
|
1251
|
+
for (const id of [
|
|
1252
|
+
timed.id,
|
|
1253
|
+
timed.getAttribute("data-hf-id"),
|
|
1254
|
+
timed.getAttribute("data-composition-id")
|
|
1255
|
+
]) {
|
|
1256
|
+
if (id) timedById.set(id, timed);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return timedById;
|
|
1260
|
+
}
|
|
1261
|
+
function createReferenceEndResolver(timedById, iframeWin) {
|
|
1262
|
+
const resolveEnd = (refId, visiting) => {
|
|
1263
|
+
if (visiting.has(refId)) return null;
|
|
1264
|
+
const referenced = timedById.get(refId);
|
|
1265
|
+
if (!referenced) return null;
|
|
1266
|
+
const next = new Set(visiting).add(refId);
|
|
1267
|
+
const timing = readClipTiming(referenced, {
|
|
1268
|
+
resolveReferenceEnd: (nestedId) => resolveEnd(nestedId, next)
|
|
1269
|
+
});
|
|
1270
|
+
if (timing.end != null) return timing.end;
|
|
1271
|
+
const compositionId = referenced.getAttribute("data-composition-id");
|
|
1272
|
+
const duration = compositionId ? timelineDuration(iframeWin, compositionId) : 0;
|
|
1273
|
+
return timing.start == null || duration <= 0 ? null : timing.start + duration;
|
|
1274
|
+
};
|
|
1275
|
+
return resolveEnd;
|
|
1276
|
+
}
|
|
1277
|
+
function clampCompositionWindow(start, duration, rootDuration) {
|
|
1278
|
+
if (!Number.isFinite(duration) || duration <= 0) return null;
|
|
1279
|
+
const safeStart = Number.isFinite(start) ? start : 0;
|
|
1280
|
+
if (!Number.isFinite(rootDuration) || rootDuration <= 0) {
|
|
1281
|
+
return { start: safeStart, duration };
|
|
1282
|
+
}
|
|
1283
|
+
if (safeStart >= rootDuration) return null;
|
|
1284
|
+
const clamped = Math.min(duration, Math.max(0, rootDuration - safeStart));
|
|
1285
|
+
return clamped > 0 ? { start: safeStart, duration: clamped } : null;
|
|
1286
|
+
}
|
|
1287
|
+
function nonEmpty(value, fallback) {
|
|
1288
|
+
return value || fallback;
|
|
1289
|
+
}
|
|
1290
|
+
function optionalNonEmpty(value) {
|
|
1291
|
+
return value || void 0;
|
|
1292
|
+
}
|
|
1293
|
+
function attachCompositionSource(entry, element, compositionSrc) {
|
|
1294
|
+
if (compositionSrc) return { ...entry, compositionSrc };
|
|
1295
|
+
const innerVideo = element.querySelector("video[src]");
|
|
1296
|
+
if (!innerVideo) return entry;
|
|
1297
|
+
return { ...entry, src: optionalNonEmpty(innerVideo.getAttribute("src")), tag: "video" };
|
|
1298
|
+
}
|
|
1299
|
+
function buildMissingCompositionEntry(params) {
|
|
1300
|
+
const { doc, iframeWin, element, compositionId, rootDuration, fallbackIndex, resolveEnd } = params;
|
|
1301
|
+
const timing = readClipTiming(element, {
|
|
1302
|
+
resolveReferenceEnd: (refId) => resolveEnd(refId, /* @__PURE__ */ new Set([compositionId]))
|
|
1303
|
+
});
|
|
1304
|
+
const window2 = clampCompositionWindow(
|
|
1305
|
+
timing.start ?? 0,
|
|
1306
|
+
timing.duration ?? timelineDuration(iframeWin, compositionId),
|
|
1307
|
+
rootDuration
|
|
1308
|
+
);
|
|
1309
|
+
if (!window2) return null;
|
|
1310
|
+
const preferredId = nonEmpty(element.id, compositionId);
|
|
1311
|
+
const compositionSrc = element.getAttribute("data-composition-src") ?? element.getAttribute("data-composition-file");
|
|
1312
|
+
const selector = getTimelineElementSelector(element);
|
|
1313
|
+
const sourceFile = getTimelineElementSourceFile(element);
|
|
1314
|
+
const selectorIndex = getTimelineElementSelectorIndex(doc, element, selector);
|
|
1315
|
+
const label = getTimelineElementDisplayLabel({
|
|
1316
|
+
id: preferredId,
|
|
1317
|
+
label: element.getAttribute("data-timeline-label") ?? element.getAttribute("data-label"),
|
|
1318
|
+
tag: element.tagName
|
|
1319
|
+
});
|
|
1320
|
+
const identity = buildTimelineElementIdentity({
|
|
1321
|
+
preferredId,
|
|
1322
|
+
label,
|
|
1323
|
+
fallbackIndex,
|
|
1324
|
+
domId: optionalNonEmpty(element.id),
|
|
1325
|
+
selector,
|
|
1326
|
+
selectorIndex,
|
|
1327
|
+
sourceFile
|
|
1328
|
+
});
|
|
1329
|
+
const entry = {
|
|
1330
|
+
id: identity.id,
|
|
1331
|
+
label,
|
|
1332
|
+
key: identity.key,
|
|
1333
|
+
tag: element.tagName.toLowerCase(),
|
|
1334
|
+
start: window2.start,
|
|
1335
|
+
duration: window2.duration,
|
|
1336
|
+
track: timing.trackIndex,
|
|
1337
|
+
domId: optionalNonEmpty(element.id),
|
|
1338
|
+
hfId: optionalNonEmpty(element.getAttribute("data-hf-id")),
|
|
1339
|
+
selector,
|
|
1340
|
+
selectorIndex,
|
|
1341
|
+
sourceFile,
|
|
1342
|
+
zIndex: readTimelineElementZIndex(element)
|
|
1343
|
+
};
|
|
1344
|
+
return attachCompositionSource(entry, element, compositionSrc);
|
|
1345
|
+
}
|
|
1242
1346
|
function buildMissingCompositionElements(doc, iframeWin, currentEls, rootDuration) {
|
|
1243
1347
|
const existingIds = new Set(currentEls.map((e) => e.id));
|
|
1244
1348
|
const rootComp = doc.querySelector("[data-composition-id]");
|
|
1245
1349
|
const rootCompId = rootComp?.getAttribute("data-composition-id");
|
|
1246
1350
|
const hosts = doc.querySelectorAll("[data-composition-id][data-start]");
|
|
1247
1351
|
const missing = [];
|
|
1248
|
-
|
|
1352
|
+
const resolveEnd = createReferenceEndResolver(createTimedElementLookup(doc), iframeWin);
|
|
1353
|
+
for (const host of hosts) {
|
|
1249
1354
|
const el = host;
|
|
1250
1355
|
const compId = el.getAttribute("data-composition-id");
|
|
1251
|
-
if (!compId || compId === rootCompId)
|
|
1252
|
-
if (existingIds.has(el.id) || existingIds.has(compId))
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
let refStart = parseFloat(refStartAttr);
|
|
1260
|
-
if (isNaN(refStart)) {
|
|
1261
|
-
const refRef = doc.getElementById(refStartAttr) || doc.querySelector(`[data-composition-id="${CSS.escape(refStartAttr)}"]`);
|
|
1262
|
-
const rrStart = parseFloat(refRef?.getAttribute("data-start") ?? "0") || 0;
|
|
1263
|
-
const rrCompId = refRef?.getAttribute("data-composition-id");
|
|
1264
|
-
const rrDur = parseFloat(refRef?.getAttribute("data-duration") ?? "") || (rrCompId ? iframeWin.__timelines?.[rrCompId]?.duration?.() ?? 0 : 0);
|
|
1265
|
-
refStart = rrStart + rrDur;
|
|
1266
|
-
}
|
|
1267
|
-
const refCompId = ref.getAttribute("data-composition-id");
|
|
1268
|
-
const refDur = parseFloat(ref.getAttribute("data-duration") ?? "") || (refCompId ? iframeWin.__timelines?.[refCompId]?.duration?.() ?? 0 : 0);
|
|
1269
|
-
start = refStart + refDur;
|
|
1270
|
-
} else {
|
|
1271
|
-
start = 0;
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
let dur = parseFloat(el.getAttribute("data-duration") ?? "");
|
|
1275
|
-
if (isNaN(dur) || dur <= 0) {
|
|
1276
|
-
dur = iframeWin.__timelines?.[compId]?.duration?.() ?? 0;
|
|
1277
|
-
}
|
|
1278
|
-
if (!Number.isFinite(dur) || dur <= 0) return;
|
|
1279
|
-
if (!Number.isFinite(start)) start = 0;
|
|
1280
|
-
if (Number.isFinite(rootDuration) && rootDuration > 0) {
|
|
1281
|
-
if (start >= rootDuration) return;
|
|
1282
|
-
dur = Math.min(dur, Math.max(0, rootDuration - start));
|
|
1283
|
-
if (dur <= 0) return;
|
|
1284
|
-
}
|
|
1285
|
-
const trackStr = el.getAttribute("data-track-index");
|
|
1286
|
-
const track = trackStr != null ? parseInt(trackStr, 10) : 0;
|
|
1287
|
-
const compSrc = el.getAttribute("data-composition-src") || el.getAttribute("data-composition-file");
|
|
1288
|
-
const selector = getTimelineElementSelector(el);
|
|
1289
|
-
const sourceFile = getTimelineElementSourceFile(el);
|
|
1290
|
-
const selectorIndex = getTimelineElementSelectorIndex(doc, el, selector);
|
|
1291
|
-
const label = getTimelineElementDisplayLabel({
|
|
1292
|
-
id: el.id || compId || null,
|
|
1293
|
-
label: el.getAttribute("data-timeline-label") ?? el.getAttribute("data-label"),
|
|
1294
|
-
tag: el.tagName
|
|
1295
|
-
});
|
|
1296
|
-
const identity = buildTimelineElementIdentity({
|
|
1297
|
-
preferredId: el.id || compId || null,
|
|
1298
|
-
label,
|
|
1356
|
+
if (!compId || compId === rootCompId) continue;
|
|
1357
|
+
if (existingIds.has(el.id) || existingIds.has(compId)) continue;
|
|
1358
|
+
const entry = buildMissingCompositionEntry({
|
|
1359
|
+
doc,
|
|
1360
|
+
iframeWin,
|
|
1361
|
+
element: el,
|
|
1362
|
+
compositionId: compId,
|
|
1363
|
+
rootDuration,
|
|
1299
1364
|
fallbackIndex: missing.length,
|
|
1300
|
-
|
|
1301
|
-
selector,
|
|
1302
|
-
selectorIndex,
|
|
1303
|
-
sourceFile
|
|
1365
|
+
resolveEnd
|
|
1304
1366
|
});
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
label,
|
|
1308
|
-
key: identity.key,
|
|
1309
|
-
tag: el.tagName.toLowerCase(),
|
|
1310
|
-
start,
|
|
1311
|
-
duration: dur,
|
|
1312
|
-
track: isNaN(track) ? 0 : track,
|
|
1313
|
-
domId: el.id || void 0,
|
|
1314
|
-
hfId: el.getAttribute("data-hf-id") || void 0,
|
|
1315
|
-
selector,
|
|
1316
|
-
selectorIndex,
|
|
1317
|
-
sourceFile,
|
|
1318
|
-
zIndex: readTimelineElementZIndex(el)
|
|
1319
|
-
};
|
|
1320
|
-
if (compSrc) {
|
|
1321
|
-
entry.compositionSrc = compSrc;
|
|
1322
|
-
} else {
|
|
1323
|
-
const innerVideo = el.querySelector("video[src]");
|
|
1324
|
-
if (innerVideo) {
|
|
1325
|
-
entry.src = innerVideo.getAttribute("src") || void 0;
|
|
1326
|
-
entry.tag = "video";
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
missing.push(entry);
|
|
1330
|
-
});
|
|
1367
|
+
if (entry) missing.push(entry);
|
|
1368
|
+
}
|
|
1331
1369
|
let patched = false;
|
|
1332
1370
|
const updatedEls = currentEls.map((existing) => {
|
|
1333
1371
|
if (existing.compositionSrc) return existing;
|
|
@@ -2915,6 +2953,7 @@ function FileManagerProvider({
|
|
|
2915
2953
|
readProjectFile,
|
|
2916
2954
|
writeProjectFile,
|
|
2917
2955
|
readOptionalProjectFile,
|
|
2956
|
+
observeProjectFileVersion,
|
|
2918
2957
|
updateEditingFileContent,
|
|
2919
2958
|
revealSourceOffset,
|
|
2920
2959
|
openSourceForSelection,
|
|
@@ -2951,6 +2990,7 @@ function FileManagerProvider({
|
|
|
2951
2990
|
readProjectFile,
|
|
2952
2991
|
writeProjectFile,
|
|
2953
2992
|
readOptionalProjectFile,
|
|
2993
|
+
observeProjectFileVersion,
|
|
2954
2994
|
updateEditingFileContent,
|
|
2955
2995
|
revealSourceOffset,
|
|
2956
2996
|
openSourceForSelection,
|
|
@@ -2984,6 +3024,7 @@ function FileManagerProvider({
|
|
|
2984
3024
|
readProjectFile,
|
|
2985
3025
|
writeProjectFile,
|
|
2986
3026
|
readOptionalProjectFile,
|
|
3027
|
+
observeProjectFileVersion,
|
|
2987
3028
|
updateEditingFileContent,
|
|
2988
3029
|
revealSourceOffset,
|
|
2989
3030
|
openSourceForSelection,
|
|
@@ -3308,6 +3349,7 @@ import { useMemo as useMemo3 } from "react";
|
|
|
3308
3349
|
|
|
3309
3350
|
// src/player/lib/timelineDOM.ts
|
|
3310
3351
|
import { resolveCssStackingContextId } from "@hyperframes/core/runtime/stacking-context";
|
|
3352
|
+
import { readClipTiming as readClipTiming2 } from "@hyperframes/core/composition-contract";
|
|
3311
3353
|
function resolveClipTag(clip) {
|
|
3312
3354
|
return clip.tagName || clip.kind || "div";
|
|
3313
3355
|
}
|
|
@@ -3463,22 +3505,18 @@ function parseTimelineFromDOM(doc, rootDuration) {
|
|
|
3463
3505
|
if (node === rootComp) return;
|
|
3464
3506
|
if (isTimelineIgnoredElement(node)) return;
|
|
3465
3507
|
const el = node;
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
if (isNaN(start)) return;
|
|
3508
|
+
const timing = readClipTiming2(el);
|
|
3509
|
+
const start = timing.start;
|
|
3510
|
+
if (start == null) return;
|
|
3470
3511
|
if (Number.isFinite(rootDuration) && rootDuration > 0 && start >= rootDuration) return;
|
|
3471
3512
|
const tagLower = el.tagName.toLowerCase();
|
|
3472
|
-
let dur = 0;
|
|
3473
|
-
|
|
3474
|
-
if (durStr != null) dur = parseFloat(durStr);
|
|
3475
|
-
if (isNaN(dur) || dur <= 0) dur = Math.max(0, rootDuration - start);
|
|
3513
|
+
let dur = timing.duration ?? 0;
|
|
3514
|
+
if (dur <= 0) dur = Math.max(0, rootDuration - start);
|
|
3476
3515
|
if (Number.isFinite(rootDuration) && rootDuration > 0) {
|
|
3477
3516
|
dur = Math.min(dur, Math.max(0, rootDuration - start));
|
|
3478
3517
|
}
|
|
3479
3518
|
if (!Number.isFinite(dur) || dur <= 0) return;
|
|
3480
|
-
const
|
|
3481
|
-
const track = trackStr != null ? parseInt(trackStr, 10) : trackCounter++;
|
|
3519
|
+
const track = timing.trackSource === "default" ? trackCounter++ : timing.trackIndex;
|
|
3482
3520
|
const compId = el.getAttribute("data-composition-id");
|
|
3483
3521
|
const selector = getTimelineElementSelector(el);
|
|
3484
3522
|
const sourceFile = getTimelineElementSourceFile(el);
|
|
@@ -3504,7 +3542,7 @@ function parseTimelineFromDOM(doc, rootDuration) {
|
|
|
3504
3542
|
tag: tagLower,
|
|
3505
3543
|
start,
|
|
3506
3544
|
duration: dur,
|
|
3507
|
-
track
|
|
3545
|
+
track,
|
|
3508
3546
|
domId: el.id || void 0,
|
|
3509
3547
|
hfId: el.getAttribute("data-hf-id") || void 0,
|
|
3510
3548
|
selector,
|
|
@@ -6495,7 +6533,7 @@ var STUDIO_SDK_RESOLVER_SHADOW_ENABLED = resolveStudioBooleanEnvFlag(
|
|
|
6495
6533
|
var STUDIO_FLAT_INSPECTOR_ENABLED = resolveStudioBooleanEnvFlag(
|
|
6496
6534
|
env,
|
|
6497
6535
|
["VITE_STUDIO_ENABLE_FLAT_INSPECTOR", "VITE_STUDIO_FLAT_INSPECTOR_ENABLED"],
|
|
6498
|
-
|
|
6536
|
+
true
|
|
6499
6537
|
);
|
|
6500
6538
|
var STUDIO_MANUAL_EDITING_DISABLED_TITLE = "Manual editing is temporarily disabled";
|
|
6501
6539
|
|
|
@@ -6920,6 +6958,20 @@ var StudioSaveNetworkError = class extends Error {
|
|
|
6920
6958
|
this.name = "StudioSaveNetworkError";
|
|
6921
6959
|
}
|
|
6922
6960
|
};
|
|
6961
|
+
var StudioFileConflictError = class extends StudioSaveHttpError {
|
|
6962
|
+
filePath;
|
|
6963
|
+
currentVersion;
|
|
6964
|
+
currentContent;
|
|
6965
|
+
attemptedContent;
|
|
6966
|
+
constructor(input) {
|
|
6967
|
+
super(`Save conflict: ${input.filePath} changed outside this Studio session`, 409);
|
|
6968
|
+
this.name = "StudioFileConflictError";
|
|
6969
|
+
this.filePath = input.filePath;
|
|
6970
|
+
this.currentVersion = input.currentVersion;
|
|
6971
|
+
this.currentContent = input.currentContent;
|
|
6972
|
+
this.attemptedContent = input.attemptedContent;
|
|
6973
|
+
}
|
|
6974
|
+
};
|
|
6923
6975
|
function readNumericProperty(value, key) {
|
|
6924
6976
|
const record = value;
|
|
6925
6977
|
const property = record[key];
|
|
@@ -7093,14 +7145,14 @@ async function saveProjectFilesWithHistory({
|
|
|
7093
7145
|
const writtenPaths = [];
|
|
7094
7146
|
try {
|
|
7095
7147
|
for (const path of changedPaths) {
|
|
7096
|
-
await writeFile(path, snapshots[path].after);
|
|
7148
|
+
await writeFile(path, snapshots[path].after, snapshots[path].before);
|
|
7097
7149
|
writtenPaths.push(path);
|
|
7098
7150
|
}
|
|
7099
7151
|
await recordEdit({ label, kind, coalesceKey, coalesceMs, files: snapshots });
|
|
7100
7152
|
} catch (error) {
|
|
7101
7153
|
try {
|
|
7102
7154
|
for (const path of writtenPaths.reverse()) {
|
|
7103
|
-
await writeFile(path, snapshots[path].before);
|
|
7155
|
+
await writeFile(path, snapshots[path].before, snapshots[path].after);
|
|
7104
7156
|
}
|
|
7105
7157
|
} catch (rollbackError) {
|
|
7106
7158
|
throw new AggregateError(
|
|
@@ -8748,7 +8800,7 @@ function snapTimelineTime(time, targets, thresholdSecs) {
|
|
|
8748
8800
|
}
|
|
8749
8801
|
return best ? { time: best.time, target: best } : { time, target: null };
|
|
8750
8802
|
}
|
|
8751
|
-
function snapMoveToTargets(start, duration, targets, pixelsPerSecond,
|
|
8803
|
+
function snapMoveToTargets(start, duration, targets, pixelsPerSecond, timelineDuration2) {
|
|
8752
8804
|
if (targets.length === 0) return { start, snapTime: null, snapType: null };
|
|
8753
8805
|
const thresholdSecs = TIMELINE_SNAP_PX / Math.max(pixelsPerSecond, 1);
|
|
8754
8806
|
const startSnap = snapTimelineTime(start, targets, thresholdSecs);
|
|
@@ -8764,7 +8816,7 @@ function snapMoveToTargets(start, duration, targets, pixelsPerSecond, timelineDu
|
|
|
8764
8816
|
candidate = endSnap.time - duration;
|
|
8765
8817
|
target = endSnap.target;
|
|
8766
8818
|
}
|
|
8767
|
-
const maxStart = Math.max(0,
|
|
8819
|
+
const maxStart = Math.max(0, timelineDuration2 - duration);
|
|
8768
8820
|
const roundedCandidate = Math.round(candidate * 1e3) / 1e3;
|
|
8769
8821
|
const clamped = Math.max(0, Math.min(maxStart, roundedCandidate));
|
|
8770
8822
|
if (target && Math.abs(clamped - roundedCandidate) > 1e-6) target = null;
|
|
@@ -24480,11 +24532,11 @@ async function buildCandidateEdit(live, deps, mutate, sourceSnapshot) {
|
|
|
24480
24532
|
function isCutoverResult(value) {
|
|
24481
24533
|
return "status" in value;
|
|
24482
24534
|
}
|
|
24483
|
-
async function rollbackWrite(targetPath, originalContent, deps, cause) {
|
|
24535
|
+
async function rollbackWrite(targetPath, originalContent, expectedCurrentContent, deps, cause) {
|
|
24484
24536
|
try {
|
|
24485
24537
|
deps.domEditSaveTimestampRef.current = Date.now();
|
|
24486
24538
|
markSelfWrite(targetPath, originalContent);
|
|
24487
|
-
await deps.writeProjectFile(targetPath, originalContent);
|
|
24539
|
+
await deps.writeProjectFile(targetPath, originalContent, expectedCurrentContent);
|
|
24488
24540
|
return cause;
|
|
24489
24541
|
} catch (rollbackError) {
|
|
24490
24542
|
return new AggregateError(
|
|
@@ -24497,7 +24549,7 @@ async function writeAndRecord(after, targetPath, originalContent, deps, options)
|
|
|
24497
24549
|
deps.domEditSaveTimestampRef.current = Date.now();
|
|
24498
24550
|
markSelfWrite(targetPath, after);
|
|
24499
24551
|
try {
|
|
24500
|
-
await deps.writeProjectFile(targetPath, after);
|
|
24552
|
+
await deps.writeProjectFile(targetPath, after, originalContent);
|
|
24501
24553
|
} catch (error) {
|
|
24502
24554
|
return asCutoverError(error);
|
|
24503
24555
|
}
|
|
@@ -24511,7 +24563,7 @@ async function writeAndRecord(after, targetPath, originalContent, deps, options)
|
|
|
24511
24563
|
});
|
|
24512
24564
|
return null;
|
|
24513
24565
|
} catch (error) {
|
|
24514
|
-
return rollbackWrite(targetPath, originalContent, deps, asCutoverError(error));
|
|
24566
|
+
return rollbackWrite(targetPath, originalContent, after, deps, asCutoverError(error));
|
|
24515
24567
|
}
|
|
24516
24568
|
}
|
|
24517
24569
|
function refreshCommittedEdit(after, deps, options) {
|
|
@@ -28329,6 +28381,7 @@ function CommitField({
|
|
|
28329
28381
|
value,
|
|
28330
28382
|
disabled,
|
|
28331
28383
|
liveCommit,
|
|
28384
|
+
align = "left",
|
|
28332
28385
|
onCommit
|
|
28333
28386
|
}) {
|
|
28334
28387
|
const [draft, setDraft] = useState35(value);
|
|
@@ -28401,7 +28454,7 @@ function CommitField({
|
|
|
28401
28454
|
scheduleCommit(nextDraft);
|
|
28402
28455
|
},
|
|
28403
28456
|
title: parseNumericToken(value) ? "Scroll or use Arrow keys to adjust" : void 0,
|
|
28404
|
-
className:
|
|
28457
|
+
className: `min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600 ${align === "right" ? "text-right" : "text-left"}`
|
|
28405
28458
|
}
|
|
28406
28459
|
);
|
|
28407
28460
|
}
|
|
@@ -31826,7 +31879,7 @@ function PromotableControl({
|
|
|
31826
31879
|
bound && /* @__PURE__ */ jsxs58(
|
|
31827
31880
|
"span",
|
|
31828
31881
|
{
|
|
31829
|
-
className: "pointer-events-none absolute right-1.5
|
|
31882
|
+
className: "pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-studio-accent",
|
|
31830
31883
|
title: `Bound to variable "${promote.boundId}"`,
|
|
31831
31884
|
children: [
|
|
31832
31885
|
"\u25C6 ",
|
|
@@ -31843,7 +31896,7 @@ function PromotableControl({
|
|
|
31843
31896
|
e.stopPropagation();
|
|
31844
31897
|
promote.promote();
|
|
31845
31898
|
},
|
|
31846
|
-
className: "absolute right-1.5
|
|
31899
|
+
className: "absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded bg-neutral-800/80 px-1 py-px font-mono text-[8px] font-medium text-neutral-400 opacity-70 transition-colors hover:bg-studio-accent/20 hover:text-studio-accent hover:opacity-100",
|
|
31847
31900
|
children: "\u25C7 var"
|
|
31848
31901
|
}
|
|
31849
31902
|
)
|
|
@@ -36177,33 +36230,39 @@ function FlatSelectRow({
|
|
|
36177
36230
|
return /* @__PURE__ */ jsxs75("div", { className: "group flex min-h-[30px] items-center justify-between", children: [
|
|
36178
36231
|
/* @__PURE__ */ jsx88("span", { className: `text-[11px] ${VALUE_TIER_LABEL_CLASS[tier]}`, children: label }),
|
|
36179
36232
|
/* @__PURE__ */ jsxs75("span", { className: "flex items-center gap-2", children: [
|
|
36180
|
-
/* @__PURE__ */ jsxs75(
|
|
36181
|
-
|
|
36182
|
-
|
|
36183
|
-
{
|
|
36184
|
-
|
|
36185
|
-
|
|
36186
|
-
|
|
36187
|
-
|
|
36188
|
-
|
|
36189
|
-
|
|
36190
|
-
|
|
36191
|
-
|
|
36192
|
-
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36196
|
-
|
|
36197
|
-
|
|
36198
|
-
|
|
36199
|
-
|
|
36200
|
-
|
|
36201
|
-
|
|
36202
|
-
|
|
36203
|
-
|
|
36204
|
-
|
|
36205
|
-
|
|
36206
|
-
|
|
36233
|
+
/* @__PURE__ */ jsxs75(
|
|
36234
|
+
"label",
|
|
36235
|
+
{
|
|
36236
|
+
className: `flex items-center gap-1.5 border-b pb-px ${tier === "explicitCustom" ? "border-panel-accent/30 group-hover:border-panel-accent/70" : "border-panel-border-input/50 group-hover:border-panel-border-input"}`,
|
|
36237
|
+
children: [
|
|
36238
|
+
/* @__PURE__ */ jsx88(
|
|
36239
|
+
"select",
|
|
36240
|
+
{
|
|
36241
|
+
value,
|
|
36242
|
+
disabled,
|
|
36243
|
+
"aria-label": ariaLabel || label || void 0,
|
|
36244
|
+
onChange: (e) => {
|
|
36245
|
+
track("select", trackName);
|
|
36246
|
+
onChange(e.target.value);
|
|
36247
|
+
},
|
|
36248
|
+
className: `appearance-none bg-transparent text-right font-mono text-[11px] outline-none disabled:cursor-not-allowed ${VALUE_TIER_VALUE_CLASS[tier]}`,
|
|
36249
|
+
children: renderedOptions.map((option) => /* @__PURE__ */ jsx88("option", { value: option.value, children: option.label }, option.value))
|
|
36250
|
+
}
|
|
36251
|
+
),
|
|
36252
|
+
/* @__PURE__ */ jsx88(
|
|
36253
|
+
"svg",
|
|
36254
|
+
{
|
|
36255
|
+
width: "10",
|
|
36256
|
+
height: "10",
|
|
36257
|
+
viewBox: "0 0 10 10",
|
|
36258
|
+
fill: "currentColor",
|
|
36259
|
+
className: "flex-shrink-0 text-panel-text-5",
|
|
36260
|
+
children: /* @__PURE__ */ jsx88("path", { d: "M2 3l3 4 3-4z" })
|
|
36261
|
+
}
|
|
36262
|
+
)
|
|
36263
|
+
]
|
|
36264
|
+
}
|
|
36265
|
+
),
|
|
36207
36266
|
tier === "explicitCustom" && onReset && /* @__PURE__ */ jsx88(
|
|
36208
36267
|
"button",
|
|
36209
36268
|
{
|
|
@@ -36244,13 +36303,14 @@ function FlatRow({
|
|
|
36244
36303
|
"span",
|
|
36245
36304
|
{
|
|
36246
36305
|
"data-flat-row-value": "true",
|
|
36247
|
-
className: `min-w-0 border-b pb-px font-mono text-[11px] ${VALUE_TIER_VALUE_CLASS[tier]} ${tier === "explicitCustom" ? "border-
|
|
36306
|
+
className: `min-w-0 border-b pb-px font-mono text-[11px] ${VALUE_TIER_VALUE_CLASS[tier]} ${tier === "explicitCustom" ? "border-panel-accent/30 group-hover:border-panel-accent/70" : "border-panel-border-input/50 group-hover:border-panel-border-input"}`,
|
|
36248
36307
|
children: /* @__PURE__ */ jsx89(
|
|
36249
36308
|
CommitField,
|
|
36250
36309
|
{
|
|
36251
36310
|
value,
|
|
36252
36311
|
disabled,
|
|
36253
36312
|
liveCommit,
|
|
36313
|
+
align: "right",
|
|
36254
36314
|
onCommit: (nextValue) => {
|
|
36255
36315
|
track("metric", label);
|
|
36256
36316
|
onCommit(nextValue);
|
|
@@ -36680,7 +36740,7 @@ function FlatTextFieldEditor({
|
|
|
36680
36740
|
children: "Weight"
|
|
36681
36741
|
}
|
|
36682
36742
|
),
|
|
36683
|
-
/* @__PURE__ */ jsxs77("label", { className: "flex items-center gap-1.5", children: [
|
|
36743
|
+
/* @__PURE__ */ jsxs77("label", { className: "flex items-center gap-1.5 border-b border-panel-border-input/50 pb-px hover:border-panel-border-input", children: [
|
|
36684
36744
|
/* @__PURE__ */ jsx90(
|
|
36685
36745
|
"select",
|
|
36686
36746
|
{
|
|
@@ -36822,7 +36882,7 @@ function FlatTextSection({
|
|
|
36822
36882
|
const activeField = textFields.find((field) => field.key === activeFieldKey) ?? textFields[0];
|
|
36823
36883
|
if (!activeField) return null;
|
|
36824
36884
|
if (textFields.length > 1) {
|
|
36825
|
-
return /* @__PURE__ */ jsxs77("div", { className: "space-y-
|
|
36885
|
+
return /* @__PURE__ */ jsxs77("div", { className: "space-y-2.5", children: [
|
|
36826
36886
|
/* @__PURE__ */ jsx90(
|
|
36827
36887
|
FlatTextLayerList,
|
|
36828
36888
|
{
|
|
@@ -36851,7 +36911,7 @@ function FlatTextSection({
|
|
|
36851
36911
|
)
|
|
36852
36912
|
] });
|
|
36853
36913
|
}
|
|
36854
|
-
return /* @__PURE__ */ jsxs77("div", { className: "space-y-
|
|
36914
|
+
return /* @__PURE__ */ jsxs77("div", { className: "space-y-2.5", children: [
|
|
36855
36915
|
/* @__PURE__ */ jsx90(
|
|
36856
36916
|
FlatTextFieldEditor,
|
|
36857
36917
|
{
|
|
@@ -36965,17 +37025,6 @@ var STROKE_STYLE_OPTIONS = [
|
|
|
36965
37025
|
"inset",
|
|
36966
37026
|
"outset"
|
|
36967
37027
|
];
|
|
36968
|
-
function formatStrokeSummary(widthPx, style) {
|
|
36969
|
-
return `${widthPx}px ${style}`;
|
|
36970
|
-
}
|
|
36971
|
-
function parseStrokeSummary(text) {
|
|
36972
|
-
const match = /^\s*(-?\d+(?:\.\d+)?)px\s+(\S+)\s*$/.exec(text);
|
|
36973
|
-
if (!match) return null;
|
|
36974
|
-
const widthPx = Number.parseFloat(match[1]);
|
|
36975
|
-
const style = match[2];
|
|
36976
|
-
if (!Number.isFinite(widthPx) || !style) return null;
|
|
36977
|
-
return { widthPx, style };
|
|
36978
|
-
}
|
|
36979
37028
|
|
|
36980
37029
|
// src/components/editor/propertyPanelFlatMaskInsetRows.tsx
|
|
36981
37030
|
import { Fragment as Fragment28, jsx as jsx91, jsxs as jsxs78 } from "react/jsx-runtime";
|
|
@@ -37164,24 +37213,17 @@ function FlatStrokeRow({
|
|
|
37164
37213
|
const borderWidthValue = parsePxMetricValue(styles["border-width"] ?? "") ?? parsePxMetricValue(styles["border-top-width"] ?? "") ?? 0;
|
|
37165
37214
|
const borderStyleValue = styles["border-style"] || styles["border-top-style"] || "none";
|
|
37166
37215
|
const borderColorValue = styles["border-color"] || styles["border-top-color"] || "rgba(255, 255, 255, 0.18)";
|
|
37167
|
-
const
|
|
37168
|
-
const tier = resolveValueTier(
|
|
37169
|
-
styles["border-width"] != null || styles["border-style"] != null ? summary : void 0,
|
|
37170
|
-
formatStrokeSummary(0, "none")
|
|
37171
|
-
);
|
|
37216
|
+
const widthDisplay = formatPxMetricValue(borderWidthValue);
|
|
37172
37217
|
return /* @__PURE__ */ jsxs79(Fragment29, { children: [
|
|
37173
37218
|
/* @__PURE__ */ jsx92(
|
|
37174
37219
|
FlatRow,
|
|
37175
37220
|
{
|
|
37176
|
-
label: "Stroke",
|
|
37177
|
-
value:
|
|
37178
|
-
tier,
|
|
37221
|
+
label: "Stroke width",
|
|
37222
|
+
value: widthDisplay,
|
|
37223
|
+
tier: resolveValueTier(styles["border-width"], "0px"),
|
|
37179
37224
|
disabled,
|
|
37180
37225
|
onCommit: async (next) => {
|
|
37181
|
-
const
|
|
37182
|
-
if (!parsed) return;
|
|
37183
|
-
if (!STROKE_STYLE_OPTIONS.includes(parsed.style)) return;
|
|
37184
|
-
const normalizedWidth = normalizePanelPxValue(`${parsed.widthPx}px`, {
|
|
37226
|
+
const normalizedWidth = normalizePanelPxValue(next, {
|
|
37185
37227
|
min: 0,
|
|
37186
37228
|
max: 200,
|
|
37187
37229
|
fallback: borderWidthValue
|
|
@@ -37189,24 +37231,11 @@ function FlatStrokeRow({
|
|
|
37189
37231
|
if (!normalizedWidth) return;
|
|
37190
37232
|
for (const [property, value] of buildStrokeWidthStyleUpdates(
|
|
37191
37233
|
normalizedWidth,
|
|
37192
|
-
|
|
37234
|
+
borderStyleValue
|
|
37193
37235
|
)) {
|
|
37194
37236
|
await onSetStyle(property, value);
|
|
37195
37237
|
}
|
|
37196
|
-
|
|
37197
|
-
await onSetStyle(property, value);
|
|
37198
|
-
}
|
|
37199
|
-
},
|
|
37200
|
-
suffix: /* @__PURE__ */ jsxs79(Fragment29, { children: [
|
|
37201
|
-
/* @__PURE__ */ jsx92(
|
|
37202
|
-
"span",
|
|
37203
|
-
{
|
|
37204
|
-
className: "h-4 w-4 flex-shrink-0 rounded border border-panel-border-input",
|
|
37205
|
-
style: { backgroundColor: borderColorValue }
|
|
37206
|
-
}
|
|
37207
|
-
),
|
|
37208
|
-
/* @__PURE__ */ jsx92("span", { className: "font-mono text-[10px] text-panel-text-3", children: borderColorValue })
|
|
37209
|
-
] })
|
|
37238
|
+
}
|
|
37210
37239
|
}
|
|
37211
37240
|
),
|
|
37212
37241
|
/* @__PURE__ */ jsx92(
|
|
@@ -37912,7 +37941,7 @@ function FlatTimingRow({
|
|
|
37912
37941
|
};
|
|
37913
37942
|
const cell = (label, value, onCommit) => /* @__PURE__ */ jsxs82("div", { className: "grid gap-px", children: [
|
|
37914
37943
|
/* @__PURE__ */ jsx95("span", { className: "text-[9px] text-panel-text-4", children: label }),
|
|
37915
|
-
/* @__PURE__ */ jsx95("span", { className: "border-b border-
|
|
37944
|
+
/* @__PURE__ */ jsx95("span", { className: "border-b border-panel-border-input/50 font-mono text-[11px] text-panel-text-0 hover:border-panel-border-input", children: /* @__PURE__ */ jsx95(
|
|
37916
37945
|
CommitField,
|
|
37917
37946
|
{
|
|
37918
37947
|
value,
|
|
@@ -38724,7 +38753,7 @@ function FlatColorGradingSection({
|
|
|
38724
38753
|
track("select", "Custom LUT");
|
|
38725
38754
|
applyLut(src || null, src && lut?.src === src ? lut.intensity : 1);
|
|
38726
38755
|
},
|
|
38727
|
-
className: "bg-transparent font-mono text-[10px] text-panel-text-3 outline-none",
|
|
38756
|
+
className: "border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-none hover:border-panel-border-input",
|
|
38728
38757
|
children: [
|
|
38729
38758
|
/* @__PURE__ */ jsx98("option", { value: "", children: "None" }),
|
|
38730
38759
|
lutAssets.map((asset) => /* @__PURE__ */ jsx98("option", { value: asset, children: asset.split("/").pop() ?? asset }, asset))
|
|
@@ -38876,7 +38905,7 @@ function FlatColorGradingSection({
|
|
|
38876
38905
|
onSetApplyScope(e.target.value);
|
|
38877
38906
|
},
|
|
38878
38907
|
disabled: applyBusy,
|
|
38879
|
-
className: "bg-transparent font-mono text-[11px] text-panel-text-0 outline-none disabled:opacity-50",
|
|
38908
|
+
className: "border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-none hover:border-panel-border-input disabled:opacity-50",
|
|
38880
38909
|
children: [
|
|
38881
38910
|
/* @__PURE__ */ jsx98("option", { value: "source-file", children: "Current file media" }),
|
|
38882
38911
|
/* @__PURE__ */ jsx98("option", { value: "project", children: "All project media" })
|
|
@@ -39091,49 +39120,51 @@ function PropertyPanelFlat({
|
|
|
39091
39120
|
)
|
|
39092
39121
|
});
|
|
39093
39122
|
}
|
|
39094
|
-
|
|
39095
|
-
|
|
39096
|
-
|
|
39097
|
-
|
|
39098
|
-
|
|
39099
|
-
|
|
39100
|
-
|
|
39101
|
-
|
|
39102
|
-
|
|
39103
|
-
|
|
39104
|
-
|
|
39105
|
-
|
|
39106
|
-
|
|
39107
|
-
|
|
39108
|
-
|
|
39109
|
-
|
|
39110
|
-
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
39114
|
-
|
|
39115
|
-
|
|
39116
|
-
|
|
39117
|
-
|
|
39118
|
-
|
|
39119
|
-
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
39123
|
-
|
|
39124
|
-
|
|
39125
|
-
|
|
39126
|
-
|
|
39127
|
-
|
|
39128
|
-
|
|
39129
|
-
|
|
39130
|
-
|
|
39131
|
-
|
|
39132
|
-
|
|
39133
|
-
|
|
39134
|
-
|
|
39135
|
-
|
|
39136
|
-
|
|
39123
|
+
if (sections.layout) {
|
|
39124
|
+
groups.push({
|
|
39125
|
+
id: "layout",
|
|
39126
|
+
title: "Layout",
|
|
39127
|
+
// No scrub accessory: FlatRow/CommitField has no pointer-drag scrubbing
|
|
39128
|
+
// (wheel/arrow keys only) — advertising "drag values to scrub" here lies.
|
|
39129
|
+
summary: `${formatPxMetricValue(displayX)},${formatPxMetricValue(displayY)} \xB7 ${Math.round(displayW)}\xD7${Math.round(displayH)}`,
|
|
39130
|
+
content: /* @__PURE__ */ jsx99(
|
|
39131
|
+
FlatLayoutSection,
|
|
39132
|
+
{
|
|
39133
|
+
element,
|
|
39134
|
+
styles,
|
|
39135
|
+
onSetStyle,
|
|
39136
|
+
disabled: !element.capabilities.canEditStyles,
|
|
39137
|
+
displayX,
|
|
39138
|
+
displayY,
|
|
39139
|
+
displayW,
|
|
39140
|
+
displayH,
|
|
39141
|
+
displayR,
|
|
39142
|
+
manualOffsetEditingDisabled,
|
|
39143
|
+
manualSizeEditingDisabled,
|
|
39144
|
+
manualRotationEditingDisabled,
|
|
39145
|
+
commitManualOffset,
|
|
39146
|
+
commitManualSize,
|
|
39147
|
+
commitManualRotation,
|
|
39148
|
+
gsapAnimId,
|
|
39149
|
+
navKeyframes,
|
|
39150
|
+
currentPct,
|
|
39151
|
+
seekFromKfPct,
|
|
39152
|
+
animIdForProp,
|
|
39153
|
+
resolveAnimIdForProp: animIdForProp,
|
|
39154
|
+
gsapRuntimeValues,
|
|
39155
|
+
gsapKeyframes: navKeyframes,
|
|
39156
|
+
elStart,
|
|
39157
|
+
elDuration,
|
|
39158
|
+
onCommitAnimatedProperty,
|
|
39159
|
+
onCommitAnimatedProperties,
|
|
39160
|
+
onSeekToTime,
|
|
39161
|
+
onRemoveKeyframe,
|
|
39162
|
+
onConvertToKeyframes,
|
|
39163
|
+
onLivePreviewProps: createGsapLivePreview(previewIframeRef ?? { current: null })
|
|
39164
|
+
}
|
|
39165
|
+
)
|
|
39166
|
+
});
|
|
39167
|
+
}
|
|
39137
39168
|
if (showMotionGroup) {
|
|
39138
39169
|
groups.push({
|
|
39139
39170
|
id: "motion",
|
|
@@ -39600,8 +39631,8 @@ var PropertyPanel = memo31(function PropertyPanel2(props) {
|
|
|
39600
39631
|
const manualSizeEditingDisabled = !element.capabilities.canApplyManualSize;
|
|
39601
39632
|
const manualRotationEditingDisabled = !element.capabilities.canApplyManualRotation;
|
|
39602
39633
|
const sourceLabel = element.id ? `#${element.id}` : element.selector ?? "";
|
|
39603
|
-
const showEditableSections = element.capabilities.canEditStyles;
|
|
39604
39634
|
const sections = resolveEditingSections(domEditSelectionToFacts(element, gsapAnimations.length));
|
|
39635
|
+
const showEditableSections = element.capabilities.canEditStyles && sections.style;
|
|
39605
39636
|
const manualOffset = readStudioPathOffset(element.element);
|
|
39606
39637
|
const manualSize = readStudioBoxSize(element.element);
|
|
39607
39638
|
const resolvedWidth = manualSize.width > 0 ? manualSize.width : parsePxMetricValue(styles.width ?? "") ?? element.boundingBox.width;
|
|
@@ -39765,7 +39796,7 @@ var PropertyPanel = memo31(function PropertyPanel2(props) {
|
|
|
39765
39796
|
onRemoveBackground
|
|
39766
39797
|
}
|
|
39767
39798
|
),
|
|
39768
|
-
/* @__PURE__ */ jsxs89(Section, { title: "Layout", icon: /* @__PURE__ */ jsx102(Move, { size: 15 }), children: [
|
|
39799
|
+
sections.layout && /* @__PURE__ */ jsxs89(Section, { title: "Layout", icon: /* @__PURE__ */ jsx102(Move, { size: 15 }), children: [
|
|
39769
39800
|
/* @__PURE__ */ jsxs89("div", { className: RESPONSIVE_GRID, children: [
|
|
39770
39801
|
/* @__PURE__ */ jsxs89("div", { className: "flex items-center gap-1", children: [
|
|
39771
39802
|
/* @__PURE__ */ jsx102("div", { className: "flex-1", children: /* @__PURE__ */ jsx102(
|
|
@@ -40765,7 +40796,7 @@ var FileTree = memo33(function FileTree2({
|
|
|
40765
40796
|
});
|
|
40766
40797
|
|
|
40767
40798
|
// src/App.tsx
|
|
40768
|
-
import { useState as
|
|
40799
|
+
import { useState as useState120, useCallback as useCallback142, useRef as useRef120, useMemo as useMemo50, useEffect as useEffect102, useLayoutEffect as useLayoutEffect4 } from "react";
|
|
40769
40800
|
|
|
40770
40801
|
// src/components/renders/useRenderQueue.ts
|
|
40771
40802
|
import { useState as useState64, useEffect as useEffect52, useCallback as useCallback58, useRef as useRef63, useMemo as useMemo31 } from "react";
|
|
@@ -42174,7 +42205,9 @@ function usePanelLayout(initialState2) {
|
|
|
42174
42205
|
const trackedSetRightPanelTab = useCallback62(
|
|
42175
42206
|
(tab) => {
|
|
42176
42207
|
if (tab === "design" || tab === "layers") {
|
|
42177
|
-
setRightInspectorPanes(
|
|
42208
|
+
setRightInspectorPanes(
|
|
42209
|
+
STUDIO_FLAT_INSPECTOR_ENABLED ? { design: tab === "design", layers: tab === "layers" } : (panes) => ({ ...panes, [tab]: true })
|
|
42210
|
+
);
|
|
42178
42211
|
}
|
|
42179
42212
|
setRightPanelTab(tab);
|
|
42180
42213
|
trackStudioEvent("tab_switch", { panel: "right_panel", tab });
|
|
@@ -42188,6 +42221,9 @@ function usePanelLayout(initialState2) {
|
|
|
42188
42221
|
return next;
|
|
42189
42222
|
});
|
|
42190
42223
|
}, []);
|
|
42224
|
+
const setExclusiveRightInspectorPane = useCallback62((pane) => {
|
|
42225
|
+
setRightInspectorPanes({ design: pane === "design", layers: pane === "layers" });
|
|
42226
|
+
}, []);
|
|
42191
42227
|
return {
|
|
42192
42228
|
leftWidth,
|
|
42193
42229
|
setLeftWidth,
|
|
@@ -42201,6 +42237,7 @@ function usePanelLayout(initialState2) {
|
|
|
42201
42237
|
setRightPanelTab: trackedSetRightPanelTab,
|
|
42202
42238
|
rightInspectorPanes,
|
|
42203
42239
|
toggleRightInspectorPane,
|
|
42240
|
+
setExclusiveRightInspectorPane,
|
|
42204
42241
|
toggleLeftSidebar,
|
|
42205
42242
|
handlePanelResizeStart,
|
|
42206
42243
|
handlePanelResizeMove,
|
|
@@ -42209,7 +42246,24 @@ function usePanelLayout(initialState2) {
|
|
|
42209
42246
|
}
|
|
42210
42247
|
|
|
42211
42248
|
// src/hooks/useFileManager.ts
|
|
42212
|
-
import { useState as useState70, useCallback as useCallback65, useRef as useRef71 } from "react";
|
|
42249
|
+
import { useState as useState70, useCallback as useCallback65, useMemo as useMemo34, useRef as useRef71 } from "react";
|
|
42250
|
+
|
|
42251
|
+
// src/utils/studioFileVersion.ts
|
|
42252
|
+
async function studioFileContentVersion(content) {
|
|
42253
|
+
const bytes = new TextEncoder().encode(content);
|
|
42254
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
42255
|
+
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
42256
|
+
""
|
|
42257
|
+
);
|
|
42258
|
+
return `"sha256:${hex}"`;
|
|
42259
|
+
}
|
|
42260
|
+
async function studioExpectedFileVersion(versions, path, expectedContent) {
|
|
42261
|
+
if (expectedContent !== void 0) return studioFileContentVersion(expectedContent);
|
|
42262
|
+
return versions.get(path);
|
|
42263
|
+
}
|
|
42264
|
+
function createStudioWriteToken() {
|
|
42265
|
+
return globalThis.crypto.randomUUID();
|
|
42266
|
+
}
|
|
42213
42267
|
|
|
42214
42268
|
// src/hooks/useFileTree.ts
|
|
42215
42269
|
import { useState as useState69, useCallback as useCallback63, useEffect as useEffect55, useMemo as useMemo33 } from "react";
|
|
@@ -42354,6 +42408,17 @@ function useFileManager({
|
|
|
42354
42408
|
const projectIdRef = useRef71(projectId);
|
|
42355
42409
|
projectIdRef.current = projectId;
|
|
42356
42410
|
const importedFontAssetsRef = useRef71([]);
|
|
42411
|
+
const fileVersionScope = useMemo34(
|
|
42412
|
+
() => ({ projectId, versions: /* @__PURE__ */ new Map() }),
|
|
42413
|
+
[projectId]
|
|
42414
|
+
);
|
|
42415
|
+
const fileVersions = fileVersionScope.versions;
|
|
42416
|
+
const observeProjectFileVersion = useCallback65(
|
|
42417
|
+
(path, version) => {
|
|
42418
|
+
fileVersions.set(path, version);
|
|
42419
|
+
},
|
|
42420
|
+
[fileVersions]
|
|
42421
|
+
);
|
|
42357
42422
|
const {
|
|
42358
42423
|
projectDir,
|
|
42359
42424
|
fileTree,
|
|
@@ -42373,14 +42438,35 @@ function useFileManager({
|
|
|
42373
42438
|
if (!response.ok) throw new Error(`Failed to read ${path}`);
|
|
42374
42439
|
const data = await response.json();
|
|
42375
42440
|
if (typeof data.content !== "string") throw new Error(`Missing file contents for ${path}`);
|
|
42441
|
+
fileVersions.set(path, data.version ?? response.headers.get("etag"));
|
|
42376
42442
|
return data.content;
|
|
42377
42443
|
},
|
|
42378
|
-
[projectId]
|
|
42444
|
+
[fileVersions, projectId]
|
|
42379
42445
|
);
|
|
42380
42446
|
const writeProjectFile = useCallback65(
|
|
42381
|
-
async (path, content) => {
|
|
42447
|
+
async (path, content, expectedContent) => {
|
|
42382
42448
|
if (!projectId) throw new Error("No active project");
|
|
42383
42449
|
const writeProjectId = projectId;
|
|
42450
|
+
let expectedVersion = await studioExpectedFileVersion(fileVersions, path, expectedContent);
|
|
42451
|
+
if (expectedVersion === void 0) {
|
|
42452
|
+
const preflight = await fetch(
|
|
42453
|
+
`/api/projects/${encodeURIComponent(writeProjectId)}/files/${encodeURIComponent(path)}`
|
|
42454
|
+
);
|
|
42455
|
+
if (preflight.ok) {
|
|
42456
|
+
const data = await preflight.json();
|
|
42457
|
+
throw new StudioFileConflictError({
|
|
42458
|
+
filePath: path,
|
|
42459
|
+
currentVersion: data.version ?? preflight.headers.get("etag"),
|
|
42460
|
+
currentContent: data.content ?? null,
|
|
42461
|
+
attemptedContent: content
|
|
42462
|
+
});
|
|
42463
|
+
} else if (preflight.status === 404) {
|
|
42464
|
+
expectedVersion = null;
|
|
42465
|
+
} else {
|
|
42466
|
+
throw await createStudioSaveHttpError(preflight, `Failed to read ${path} before save`);
|
|
42467
|
+
}
|
|
42468
|
+
}
|
|
42469
|
+
const writeToken = createStudioWriteToken();
|
|
42384
42470
|
await retryStudioSave(async () => {
|
|
42385
42471
|
let response;
|
|
42386
42472
|
try {
|
|
@@ -42388,7 +42474,11 @@ function useFileManager({
|
|
|
42388
42474
|
`/api/projects/${encodeURIComponent(writeProjectId)}/files/${encodeURIComponent(path)}`,
|
|
42389
42475
|
{
|
|
42390
42476
|
method: "PUT",
|
|
42391
|
-
headers: {
|
|
42477
|
+
headers: {
|
|
42478
|
+
"Content-Type": "text/plain",
|
|
42479
|
+
"X-Hyperframes-Write-Token": writeToken,
|
|
42480
|
+
...expectedVersion ? { "If-Match": expectedVersion } : { "If-None-Match": "*" }
|
|
42481
|
+
},
|
|
42392
42482
|
body: content
|
|
42393
42483
|
}
|
|
42394
42484
|
);
|
|
@@ -42397,13 +42487,32 @@ function useFileManager({
|
|
|
42397
42487
|
cause: error
|
|
42398
42488
|
});
|
|
42399
42489
|
}
|
|
42490
|
+
if (response.status === 409) {
|
|
42491
|
+
const conflict = await response.json().catch(() => null);
|
|
42492
|
+
const currentVersion = conflict?.currentVersion ?? null;
|
|
42493
|
+
if (currentVersion && conflict?.currentContent === content) {
|
|
42494
|
+
fileVersions.set(path, currentVersion);
|
|
42495
|
+
return;
|
|
42496
|
+
}
|
|
42497
|
+
throw new StudioFileConflictError({
|
|
42498
|
+
filePath: path,
|
|
42499
|
+
currentVersion,
|
|
42500
|
+
currentContent: conflict?.currentContent ?? null,
|
|
42501
|
+
attemptedContent: content
|
|
42502
|
+
});
|
|
42503
|
+
}
|
|
42400
42504
|
if (!response.ok) throw await createStudioSaveHttpError(response, `Failed to save ${path}`);
|
|
42505
|
+
const result = await response.json();
|
|
42506
|
+
const version = result.version ?? response.headers.get("etag");
|
|
42507
|
+
if (!version)
|
|
42508
|
+
throw new Error(`Save response for ${path} did not include a content version`);
|
|
42509
|
+
fileVersions.set(path, version);
|
|
42401
42510
|
});
|
|
42402
42511
|
if (projectIdRef.current === writeProjectId && editingPathRef.current === path) {
|
|
42403
42512
|
setEditingFile({ path, content });
|
|
42404
42513
|
}
|
|
42405
42514
|
},
|
|
42406
|
-
[projectId]
|
|
42515
|
+
[fileVersions, projectId]
|
|
42407
42516
|
);
|
|
42408
42517
|
const updateEditingFileContent = useCallback65((path, content) => {
|
|
42409
42518
|
if (editingPathRef.current === path) {
|
|
@@ -42418,9 +42527,10 @@ function useFileManager({
|
|
|
42418
42527
|
);
|
|
42419
42528
|
if (!response.ok) throw new Error(`Failed to read ${path}`);
|
|
42420
42529
|
const data = await response.json();
|
|
42530
|
+
fileVersions.set(path, data.version ?? response.headers.get("etag"));
|
|
42421
42531
|
return typeof data.content === "string" ? data.content : "";
|
|
42422
42532
|
},
|
|
42423
|
-
[projectId]
|
|
42533
|
+
[fileVersions, projectId]
|
|
42424
42534
|
);
|
|
42425
42535
|
const { saveRafRef, handleContentChange } = useEditorSave({
|
|
42426
42536
|
editingPathRef,
|
|
@@ -42450,13 +42560,14 @@ function useFileManager({
|
|
|
42450
42560
|
return r.json();
|
|
42451
42561
|
}).then((data) => {
|
|
42452
42562
|
if (data.content != null) {
|
|
42563
|
+
fileVersions.set(path, data.version ?? null);
|
|
42453
42564
|
setEditingFile({ path, content: data.content });
|
|
42454
42565
|
}
|
|
42455
42566
|
}).catch((err) => {
|
|
42456
42567
|
showToast(err instanceof Error ? err.message : `Failed to load ${path}`, "error");
|
|
42457
42568
|
});
|
|
42458
42569
|
},
|
|
42459
|
-
[showToast]
|
|
42570
|
+
[fileVersions, showToast]
|
|
42460
42571
|
);
|
|
42461
42572
|
const openSourceForSelection = useCallback65(
|
|
42462
42573
|
(sourceFile, target) => {
|
|
@@ -42477,6 +42588,7 @@ function useFileManager({
|
|
|
42477
42588
|
}).then((r) => r.json()).then((data) => {
|
|
42478
42589
|
if (requestId !== revealRequestIdRef.current) return;
|
|
42479
42590
|
if (data.content != null) {
|
|
42591
|
+
fileVersions.set(sourceFile, data.version ?? null);
|
|
42480
42592
|
setEditingFile({ path: sourceFile, content: data.content });
|
|
42481
42593
|
const match = findTagByTarget(data.content, target);
|
|
42482
42594
|
setRevealSourceOffset(match ? match.start : null);
|
|
@@ -42484,7 +42596,7 @@ function useFileManager({
|
|
|
42484
42596
|
}).catch(() => {
|
|
42485
42597
|
});
|
|
42486
42598
|
},
|
|
42487
|
-
[editingFile?.content]
|
|
42599
|
+
[editingFile?.content, fileVersions]
|
|
42488
42600
|
);
|
|
42489
42601
|
const uploadProjectFiles = useCallback65(
|
|
42490
42602
|
async (files, dir) => {
|
|
@@ -42689,6 +42801,7 @@ function useFileManager({
|
|
|
42689
42801
|
readProjectFile,
|
|
42690
42802
|
writeProjectFile,
|
|
42691
42803
|
readOptionalProjectFile,
|
|
42804
|
+
observeProjectFileVersion,
|
|
42692
42805
|
updateEditingFileContent,
|
|
42693
42806
|
// Click-to-source
|
|
42694
42807
|
revealSourceOffset,
|
|
@@ -42751,7 +42864,8 @@ function createDomEditSaveQueue(options = {}) {
|
|
|
42751
42864
|
return result;
|
|
42752
42865
|
} catch (error) {
|
|
42753
42866
|
consecutiveFailures += 1;
|
|
42754
|
-
if (consecutiveFailures >= failureThreshold)
|
|
42867
|
+
if (getStudioSaveStatusCode(error) === 409 || consecutiveFailures >= failureThreshold)
|
|
42868
|
+
open(error);
|
|
42755
42869
|
throw error;
|
|
42756
42870
|
}
|
|
42757
42871
|
};
|
|
@@ -42991,7 +43105,7 @@ function usePreviewPersistence({
|
|
|
42991
43105
|
if (!domEditSaveQueueRef.current) {
|
|
42992
43106
|
domEditSaveQueueRef.current = createDomEditSaveQueue({
|
|
42993
43107
|
onOpen: (event) => {
|
|
42994
|
-
const message = "Auto-save is paused. Check your connection.";
|
|
43108
|
+
const message = event.statusCode === 409 ? "Save paused: this file changed elsewhere. Reload and review the latest version before reapplying your edit." : "Auto-save is paused. Check your connection.";
|
|
42995
43109
|
setDomEditSaveQueuePaused(message);
|
|
42996
43110
|
showToastRef.current(message, "error");
|
|
42997
43111
|
trackStudioEvent("save_queue_paused", {
|
|
@@ -43136,7 +43250,10 @@ async function splitHtmlElement(projectId, targetPath, patchTarget, splitTime, n
|
|
|
43136
43250
|
}
|
|
43137
43251
|
);
|
|
43138
43252
|
if (!response.ok) throw new Error("Split request failed");
|
|
43139
|
-
|
|
43253
|
+
const data = await response.json();
|
|
43254
|
+
const version = data.version ?? response.headers.get("etag");
|
|
43255
|
+
if (!version) throw new Error("Split response did not include a content version");
|
|
43256
|
+
return { ...data, version };
|
|
43140
43257
|
}
|
|
43141
43258
|
async function splitGsapAnimations(projectId, targetPath, originalId, newId, splitTime, elementStart, elementDuration) {
|
|
43142
43259
|
const response = await fetch(
|
|
@@ -43164,6 +43281,7 @@ async function splitGsapAnimations(projectId, targetPath, originalId, newId, spl
|
|
|
43164
43281
|
const data = await response.json();
|
|
43165
43282
|
return {
|
|
43166
43283
|
content: data.ok && data.after ? data.after : null,
|
|
43284
|
+
version: data.version ?? response.headers.get("etag") ?? void 0,
|
|
43167
43285
|
skippedSelectors: data.skippedSelectors
|
|
43168
43286
|
};
|
|
43169
43287
|
}
|
|
@@ -43174,9 +43292,9 @@ function getOriginalContent(originals, path) {
|
|
|
43174
43292
|
}
|
|
43175
43293
|
return original;
|
|
43176
43294
|
}
|
|
43177
|
-
async function restoreFilesToOriginal(originals,
|
|
43178
|
-
for (const path of
|
|
43179
|
-
await writeProjectFile(path, getOriginalContent(originals, path));
|
|
43295
|
+
async function restoreFilesToOriginal(originals, snapshots, writeProjectFile) {
|
|
43296
|
+
for (const [path, snapshot] of snapshots) {
|
|
43297
|
+
await writeProjectFile(path, getOriginalContent(originals, path), snapshot.after);
|
|
43180
43298
|
}
|
|
43181
43299
|
}
|
|
43182
43300
|
async function readOriginalFiles(pid, elements, activeCompPath) {
|
|
@@ -43189,21 +43307,28 @@ async function readOriginalFiles(pid, elements, activeCompPath) {
|
|
|
43189
43307
|
}
|
|
43190
43308
|
return originals;
|
|
43191
43309
|
}
|
|
43192
|
-
async function splitElementsAtTime(pid, elements, splitTime, activeCompPath, originals, snapshots, writeProjectFile) {
|
|
43310
|
+
async function splitElementsAtTime(pid, elements, splitTime, activeCompPath, originals, snapshots, writeProjectFile, observeProjectFileVersion) {
|
|
43193
43311
|
let count = 0;
|
|
43194
43312
|
for (const element of elements) {
|
|
43195
|
-
const result = await executeSplit(
|
|
43313
|
+
const result = await executeSplit(
|
|
43314
|
+
pid,
|
|
43315
|
+
element,
|
|
43316
|
+
splitTime,
|
|
43317
|
+
activeCompPath,
|
|
43318
|
+
writeProjectFile,
|
|
43319
|
+
observeProjectFileVersion
|
|
43320
|
+
);
|
|
43196
43321
|
if (!result.changed) continue;
|
|
43197
43322
|
snapshots.set(result.targetPath, {
|
|
43198
43323
|
before: getOriginalContent(originals, result.targetPath),
|
|
43199
43324
|
after: result.patchedContent
|
|
43200
43325
|
});
|
|
43201
|
-
await writeProjectFile(result.targetPath, result.patchedContent);
|
|
43326
|
+
await writeProjectFile(result.targetPath, result.patchedContent, result.patchedContent);
|
|
43202
43327
|
count++;
|
|
43203
43328
|
}
|
|
43204
43329
|
return count;
|
|
43205
43330
|
}
|
|
43206
|
-
async function executeSplit(pid, element, splitTime, activeCompPath, writeProjectFile) {
|
|
43331
|
+
async function executeSplit(pid, element, splitTime, activeCompPath, writeProjectFile, observeProjectFileVersion) {
|
|
43207
43332
|
const patchTarget = buildPatchTarget(element);
|
|
43208
43333
|
if (!patchTarget) throw new Error("Clip is missing a patchable target.");
|
|
43209
43334
|
const targetPath = element.sourceFile || activeCompPath || "index.html";
|
|
@@ -43225,6 +43350,7 @@ async function executeSplit(pid, element, splitTime, activeCompPath, writeProjec
|
|
|
43225
43350
|
if (!splitResult.changed) {
|
|
43226
43351
|
return { targetPath, originalContent, patchedContent: originalContent, changed: false };
|
|
43227
43352
|
}
|
|
43353
|
+
observeProjectFileVersion?.(targetPath, splitResult.version);
|
|
43228
43354
|
let patchedContent = typeof splitResult.content === "string" ? splitResult.content : originalContent;
|
|
43229
43355
|
let skippedSelectors;
|
|
43230
43356
|
if (element.domId) {
|
|
@@ -43239,9 +43365,10 @@ async function executeSplit(pid, element, splitTime, activeCompPath, writeProjec
|
|
|
43239
43365
|
element.duration
|
|
43240
43366
|
);
|
|
43241
43367
|
if (gsapResult.content) patchedContent = gsapResult.content;
|
|
43368
|
+
if (gsapResult.version) observeProjectFileVersion?.(targetPath, gsapResult.version);
|
|
43242
43369
|
if (gsapResult.skippedSelectors?.length) skippedSelectors = gsapResult.skippedSelectors;
|
|
43243
43370
|
} catch (gsapError) {
|
|
43244
|
-
await writeProjectFile(targetPath, originalContent);
|
|
43371
|
+
await writeProjectFile(targetPath, originalContent, patchedContent);
|
|
43245
43372
|
throw gsapError;
|
|
43246
43373
|
}
|
|
43247
43374
|
}
|
|
@@ -43252,6 +43379,7 @@ function useRazorSplit({
|
|
|
43252
43379
|
activeCompPath,
|
|
43253
43380
|
showToast,
|
|
43254
43381
|
writeProjectFile,
|
|
43382
|
+
observeProjectFileVersion,
|
|
43255
43383
|
recordEdit,
|
|
43256
43384
|
domEditSaveTimestampRef,
|
|
43257
43385
|
reloadPreview,
|
|
@@ -43270,7 +43398,14 @@ function useRazorSplit({
|
|
|
43270
43398
|
const pid = projectIdRef.current;
|
|
43271
43399
|
if (!pid || !canSplitElementAt(element, splitTime)) return;
|
|
43272
43400
|
try {
|
|
43273
|
-
const { targetPath, originalContent, patchedContent, changed, skippedSelectors } = await executeSplit(
|
|
43401
|
+
const { targetPath, originalContent, patchedContent, changed, skippedSelectors } = await executeSplit(
|
|
43402
|
+
pid,
|
|
43403
|
+
element,
|
|
43404
|
+
splitTime,
|
|
43405
|
+
activeCompPath,
|
|
43406
|
+
writeProjectFile,
|
|
43407
|
+
observeProjectFileVersion
|
|
43408
|
+
);
|
|
43274
43409
|
if (!changed) {
|
|
43275
43410
|
showToast("Failed to split clip \u2014 playhead may be outside the clip", "error");
|
|
43276
43411
|
return;
|
|
@@ -43305,6 +43440,7 @@ function useRazorSplit({
|
|
|
43305
43440
|
recordEdit,
|
|
43306
43441
|
showToast,
|
|
43307
43442
|
writeProjectFile,
|
|
43443
|
+
observeProjectFileVersion,
|
|
43308
43444
|
domEditSaveTimestampRef,
|
|
43309
43445
|
reloadPreview,
|
|
43310
43446
|
forceReloadSdkSession,
|
|
@@ -43312,6 +43448,7 @@ function useRazorSplit({
|
|
|
43312
43448
|
]
|
|
43313
43449
|
);
|
|
43314
43450
|
const handleRazorSplitAll = useCallback68(
|
|
43451
|
+
// fallow-ignore-next-line complexity
|
|
43315
43452
|
async (splitTime) => {
|
|
43316
43453
|
if (isRecordingRef?.current) {
|
|
43317
43454
|
showToast("Cannot edit timeline while recording", "error");
|
|
@@ -43333,7 +43470,8 @@ function useRazorSplit({
|
|
|
43333
43470
|
activeCompPath,
|
|
43334
43471
|
originals,
|
|
43335
43472
|
finalSnapshots,
|
|
43336
|
-
writeProjectFile
|
|
43473
|
+
writeProjectFile,
|
|
43474
|
+
observeProjectFileVersion
|
|
43337
43475
|
);
|
|
43338
43476
|
if (splitCount === 0) return;
|
|
43339
43477
|
domEditSaveTimestampRef.current = Date.now();
|
|
@@ -43348,7 +43486,7 @@ function useRazorSplit({
|
|
|
43348
43486
|
showToast(`Split ${splitCount} clips at ${splitTime.toFixed(2)}s`, "info");
|
|
43349
43487
|
} catch (error) {
|
|
43350
43488
|
try {
|
|
43351
|
-
await restoreFilesToOriginal(originals, finalSnapshots
|
|
43489
|
+
await restoreFilesToOriginal(originals, finalSnapshots, writeProjectFile);
|
|
43352
43490
|
} catch {
|
|
43353
43491
|
}
|
|
43354
43492
|
const message = error instanceof Error ? error.message : "Failed to split clips";
|
|
@@ -43360,6 +43498,7 @@ function useRazorSplit({
|
|
|
43360
43498
|
recordEdit,
|
|
43361
43499
|
showToast,
|
|
43362
43500
|
writeProjectFile,
|
|
43501
|
+
observeProjectFileVersion,
|
|
43363
43502
|
domEditSaveTimestampRef,
|
|
43364
43503
|
reloadPreview,
|
|
43365
43504
|
forceReloadSdkSession,
|
|
@@ -44059,6 +44198,7 @@ function useTimelineEditing({
|
|
|
44059
44198
|
timelineElements,
|
|
44060
44199
|
showToast,
|
|
44061
44200
|
writeProjectFile,
|
|
44201
|
+
observeProjectFileVersion,
|
|
44062
44202
|
recordEdit,
|
|
44063
44203
|
domEditSaveTimestampRef,
|
|
44064
44204
|
reloadPreview,
|
|
@@ -44440,6 +44580,7 @@ function useTimelineEditing({
|
|
|
44440
44580
|
activeCompPath,
|
|
44441
44581
|
showToast,
|
|
44442
44582
|
writeProjectFile,
|
|
44583
|
+
observeProjectFileVersion,
|
|
44443
44584
|
recordEdit,
|
|
44444
44585
|
domEditSaveTimestampRef,
|
|
44445
44586
|
reloadPreview,
|
|
@@ -46576,7 +46717,7 @@ function useDomEditCommits({
|
|
|
46576
46717
|
const preparedContent = options.prepareContent(patchedContent, targetPath);
|
|
46577
46718
|
if (preparedContent !== patchedContent) {
|
|
46578
46719
|
try {
|
|
46579
|
-
await writeProjectFile(targetPath, preparedContent);
|
|
46720
|
+
await writeProjectFile(targetPath, preparedContent, patchedContent);
|
|
46580
46721
|
finalContent = preparedContent;
|
|
46581
46722
|
} catch (error) {
|
|
46582
46723
|
showToast(
|
|
@@ -46854,7 +46995,7 @@ function useGroupCommits(params) {
|
|
|
46854
46995
|
}
|
|
46855
46996
|
|
|
46856
46997
|
// src/hooks/useGsapScriptCommits.ts
|
|
46857
|
-
import { useCallback as useCallback87, useMemo as
|
|
46998
|
+
import { useCallback as useCallback87, useMemo as useMemo35, useRef as useRef83 } from "react";
|
|
46858
46999
|
import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
|
|
46859
47000
|
|
|
46860
47001
|
// src/hooks/gsapRuntimePatch.ts
|
|
@@ -48101,7 +48242,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
48101
48242
|
if (!result) return;
|
|
48102
48243
|
await finalizeSuccessfulMutation(pid, compositionPath, last.selection, last.mutation, targetPath, result, options);
|
|
48103
48244
|
}, [showToast, finalizeSuccessfulMutation]);
|
|
48104
|
-
const commitMutation =
|
|
48245
|
+
const commitMutation = useMemo35(() => {
|
|
48105
48246
|
const serializeFile = (file, task) => {
|
|
48106
48247
|
if (writeProjectFile) {
|
|
48107
48248
|
return serializeStudioFileMutation(writeProjectFile, file, task);
|
|
@@ -48166,7 +48307,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
48166
48307
|
},
|
|
48167
48308
|
[activeProjectId]
|
|
48168
48309
|
);
|
|
48169
|
-
const sdkDeps =
|
|
48310
|
+
const sdkDeps = useMemo35(
|
|
48170
48311
|
() => writeProjectFile ? {
|
|
48171
48312
|
editHistory: { recordEdit: editHistory.recordEdit },
|
|
48172
48313
|
writeProjectFile,
|
|
@@ -50426,7 +50567,7 @@ function useStudioSdkSessions(projectId, activeCompPath, domEditSaveTimestampRef
|
|
|
50426
50567
|
}
|
|
50427
50568
|
|
|
50428
50569
|
// src/hooks/useBlockHandlers.ts
|
|
50429
|
-
import { useCallback as useCallback96, useMemo as
|
|
50570
|
+
import { useCallback as useCallback96, useMemo as useMemo36, useRef as useRef89, useState as useState76 } from "react";
|
|
50430
50571
|
|
|
50431
50572
|
// src/utils/blockInstaller.ts
|
|
50432
50573
|
function getMaxZIndexFromIframe(iframe) {
|
|
@@ -50565,7 +50706,7 @@ function useBlockHandlers({
|
|
|
50565
50706
|
setRightPanelTab
|
|
50566
50707
|
}) {
|
|
50567
50708
|
const [activeBlockParams, setActiveBlockParams] = useState76(null);
|
|
50568
|
-
const blockCtx =
|
|
50709
|
+
const blockCtx = useMemo36(
|
|
50569
50710
|
() => ({
|
|
50570
50711
|
activeCompPath: blockCtxDeps.activeCompPath,
|
|
50571
50712
|
timelineElements: blockCtxDeps.timelineElements,
|
|
@@ -50678,8 +50819,20 @@ function useBlockHandlers({
|
|
|
50678
50819
|
};
|
|
50679
50820
|
}
|
|
50680
50821
|
|
|
50822
|
+
// src/hooks/useAddAssetAtPlayhead.ts
|
|
50823
|
+
import { useCallback as useCallback97 } from "react";
|
|
50824
|
+
function useAddAssetAtPlayhead(handleTimelineAssetDrop) {
|
|
50825
|
+
return useCallback97(
|
|
50826
|
+
(assetPath) => handleTimelineAssetDrop(assetPath, {
|
|
50827
|
+
start: usePlayerStore.getState().currentTime,
|
|
50828
|
+
track: 0
|
|
50829
|
+
}),
|
|
50830
|
+
[handleTimelineAssetDrop]
|
|
50831
|
+
);
|
|
50832
|
+
}
|
|
50833
|
+
|
|
50681
50834
|
// src/hooks/useAppHotkeys.ts
|
|
50682
|
-
import { useCallback as
|
|
50835
|
+
import { useCallback as useCallback98, useEffect as useEffect66, useRef as useRef90 } from "react";
|
|
50683
50836
|
function iframeContentWindow(iframe) {
|
|
50684
50837
|
try {
|
|
50685
50838
|
return iframe?.contentWindow ?? null;
|
|
@@ -50889,22 +51042,22 @@ function useAppHotkeys({
|
|
|
50889
51042
|
}) {
|
|
50890
51043
|
const previewHotkeyWindowRef = useRef90(null);
|
|
50891
51044
|
const previewHistoryCleanupRef = useRef90(null);
|
|
50892
|
-
const readHistoryFile =
|
|
51045
|
+
const readHistoryFile = useCallback98(
|
|
50893
51046
|
(path) => path === STUDIO_MOTION_PATH ? readOptionalProjectFile(path) : readProjectFile(path),
|
|
50894
51047
|
[readOptionalProjectFile, readProjectFile]
|
|
50895
51048
|
);
|
|
50896
|
-
const writeHistoryFile =
|
|
51049
|
+
const writeHistoryFile = useCallback98(
|
|
50897
51050
|
async (path, content) => {
|
|
50898
51051
|
domEditSaveTimestampRef.current = Date.now();
|
|
50899
51052
|
await writeProjectFile(path, content);
|
|
50900
51053
|
},
|
|
50901
51054
|
[domEditSaveTimestampRef, writeProjectFile]
|
|
50902
51055
|
);
|
|
50903
|
-
const serializeHistoryFiles =
|
|
51056
|
+
const serializeHistoryFiles = useCallback98(
|
|
50904
51057
|
(paths, task) => serializeStudioFileMutations(writeProjectFile, paths, task),
|
|
50905
51058
|
[writeProjectFile]
|
|
50906
51059
|
);
|
|
50907
|
-
const applyHistory =
|
|
51060
|
+
const applyHistory = useCallback98(
|
|
50908
51061
|
async (direction) => {
|
|
50909
51062
|
if (tryApplyBeatHistory(direction, editHistory.state, showToast)) return;
|
|
50910
51063
|
await waitForPendingDomEditSaves();
|
|
@@ -50942,8 +51095,8 @@ function useAppHotkeys({
|
|
|
50942
51095
|
forceReloadSdkSession
|
|
50943
51096
|
]
|
|
50944
51097
|
);
|
|
50945
|
-
const handleUndo =
|
|
50946
|
-
const handleRedo =
|
|
51098
|
+
const handleUndo = useCallback98(() => applyHistory("undo"), [applyHistory]);
|
|
51099
|
+
const handleRedo = useCallback98(() => applyHistory("redo"), [applyHistory]);
|
|
50947
51100
|
const cbRef = useRef90(null);
|
|
50948
51101
|
cbRef.current = {
|
|
50949
51102
|
handleTimelineElementDelete,
|
|
@@ -50963,7 +51116,7 @@ function useAppHotkeys({
|
|
|
50963
51116
|
domEditSelectionRef,
|
|
50964
51117
|
showToast
|
|
50965
51118
|
};
|
|
50966
|
-
const handleAppKeyDown =
|
|
51119
|
+
const handleAppKeyDown = useCallback98((event) => {
|
|
50967
51120
|
const cb = cbRef.current;
|
|
50968
51121
|
const key = event.key.toLowerCase();
|
|
50969
51122
|
if (event.metaKey || event.ctrlKey) {
|
|
@@ -50976,7 +51129,7 @@ function useAppHotkeys({
|
|
|
50976
51129
|
window.addEventListener("keydown", handleAppKeyDown, true);
|
|
50977
51130
|
return () => window.removeEventListener("keydown", handleAppKeyDown, true);
|
|
50978
51131
|
}, [handleAppKeyDown]);
|
|
50979
|
-
const syncPreviewTimelineHotkey =
|
|
51132
|
+
const syncPreviewTimelineHotkey = useCallback98(
|
|
50980
51133
|
(iframe) => {
|
|
50981
51134
|
const nextWindow = iframeContentWindow(iframe);
|
|
50982
51135
|
if (previewHotkeyWindowRef.current === nextWindow) return;
|
|
@@ -51001,7 +51154,7 @@ function useAppHotkeys({
|
|
|
51001
51154
|
},
|
|
51002
51155
|
[handleAppKeyDown]
|
|
51003
51156
|
);
|
|
51004
|
-
const handleHistoryHotkey =
|
|
51157
|
+
const handleHistoryHotkey = useCallback98((event) => {
|
|
51005
51158
|
if (!(event.metaKey || event.ctrlKey) || shouldIgnoreHistoryShortcut(event.target)) return;
|
|
51006
51159
|
handleUndoRedoKey(
|
|
51007
51160
|
event,
|
|
@@ -51009,7 +51162,7 @@ function useAppHotkeys({
|
|
|
51009
51162
|
() => void cbRef.current.handleRedo()
|
|
51010
51163
|
);
|
|
51011
51164
|
}, []);
|
|
51012
|
-
const syncPreviewHistoryHotkey =
|
|
51165
|
+
const syncPreviewHistoryHotkey = useCallback98(
|
|
51013
51166
|
(iframe) => {
|
|
51014
51167
|
previewHistoryCleanupRef.current?.();
|
|
51015
51168
|
previewHistoryCleanupRef.current = null;
|
|
@@ -51047,7 +51200,7 @@ function useAppHotkeys({
|
|
|
51047
51200
|
}
|
|
51048
51201
|
|
|
51049
51202
|
// src/hooks/useClipboard.ts
|
|
51050
|
-
import { useCallback as
|
|
51203
|
+
import { useCallback as useCallback99, useRef as useRef91 } from "react";
|
|
51051
51204
|
|
|
51052
51205
|
// src/utils/clipboardPayload.ts
|
|
51053
51206
|
function insertAsSibling(source, newHtml, selector, selectorIndex) {
|
|
@@ -51157,7 +51310,7 @@ function useClipboard({
|
|
|
51157
51310
|
const clipboardRef = useRef91(null);
|
|
51158
51311
|
const projectIdRef = useRef91(projectId);
|
|
51159
51312
|
projectIdRef.current = projectId;
|
|
51160
|
-
const handleCopy =
|
|
51313
|
+
const handleCopy = useCallback99(() => {
|
|
51161
51314
|
const { selectedElementId, elements } = usePlayerStore.getState();
|
|
51162
51315
|
if (selectedElementId) {
|
|
51163
51316
|
const element = elements.find((el) => (el.key ?? el.id) === selectedElementId);
|
|
@@ -51211,7 +51364,7 @@ function useClipboard({
|
|
|
51211
51364
|
}
|
|
51212
51365
|
return false;
|
|
51213
51366
|
}, [activeCompPath, domEditSelectionRef, previewIframeRef, showToast]);
|
|
51214
|
-
const handlePaste =
|
|
51367
|
+
const handlePaste = useCallback99(async () => {
|
|
51215
51368
|
const payload = clipboardRef.current;
|
|
51216
51369
|
if (!payload) {
|
|
51217
51370
|
showToast("Nothing to paste.", "info");
|
|
@@ -51267,7 +51420,7 @@ function useClipboard({
|
|
|
51267
51420
|
showToast,
|
|
51268
51421
|
writeProjectFile
|
|
51269
51422
|
]);
|
|
51270
|
-
const handleCut =
|
|
51423
|
+
const handleCut = useCallback99(async () => {
|
|
51271
51424
|
const copied = handleCopy();
|
|
51272
51425
|
if (!copied) return false;
|
|
51273
51426
|
const { selectedElementId, elements } = usePlayerStore.getState();
|
|
@@ -51629,11 +51782,11 @@ function useCaptionDetection({
|
|
|
51629
51782
|
}
|
|
51630
51783
|
|
|
51631
51784
|
// src/hooks/useRenderClipContent.ts
|
|
51632
|
-
import { useCallback as
|
|
51785
|
+
import { useCallback as useCallback102 } from "react";
|
|
51633
51786
|
import { createElement as createElement2 } from "react";
|
|
51634
51787
|
|
|
51635
51788
|
// src/player/components/AudioWaveform.tsx
|
|
51636
|
-
import { memo as memo34, useRef as useRef92, useState as useState77, useCallback as
|
|
51789
|
+
import { memo as memo34, useRef as useRef92, useState as useState77, useCallback as useCallback100, useEffect as useEffect68 } from "react";
|
|
51637
51790
|
import { jsx as jsx112, jsxs as jsxs98 } from "react/jsx-runtime";
|
|
51638
51791
|
var BAR_W = 2;
|
|
51639
51792
|
var GAP = 1;
|
|
@@ -51710,7 +51863,7 @@ var AudioWaveform = memo34(function AudioWaveform2({
|
|
|
51710
51863
|
cancelled = true;
|
|
51711
51864
|
};
|
|
51712
51865
|
}, [audioUrl, waveformUrl, cacheKey, peaks]);
|
|
51713
|
-
const draw =
|
|
51866
|
+
const draw = useCallback100(() => {
|
|
51714
51867
|
const container = containerRef.current;
|
|
51715
51868
|
const barsEl = barsRef.current;
|
|
51716
51869
|
if (!container || !barsEl || !peaks) return;
|
|
@@ -51731,7 +51884,7 @@ var AudioWaveform = memo34(function AudioWaveform2({
|
|
|
51731
51884
|
}
|
|
51732
51885
|
barsEl.innerHTML = html2;
|
|
51733
51886
|
}, [peaks, trimStartFraction, trimEndFraction]);
|
|
51734
|
-
const setContainerRef =
|
|
51887
|
+
const setContainerRef = useCallback100(
|
|
51735
51888
|
(el) => {
|
|
51736
51889
|
roRef.current?.disconnect();
|
|
51737
51890
|
containerRef.current = el;
|
|
@@ -51775,7 +51928,7 @@ var AudioWaveform = memo34(function AudioWaveform2({
|
|
|
51775
51928
|
});
|
|
51776
51929
|
|
|
51777
51930
|
// src/player/components/ImageThumbnail.tsx
|
|
51778
|
-
import { memo as memo35, useRef as useRef93, useState as useState78, useCallback as
|
|
51931
|
+
import { memo as memo35, useRef as useRef93, useState as useState78, useCallback as useCallback101, useEffect as useEffect69 } from "react";
|
|
51779
51932
|
import { jsx as jsx113, jsxs as jsxs99 } from "react/jsx-runtime";
|
|
51780
51933
|
var ImageThumbnail = memo35(function ImageThumbnail2({
|
|
51781
51934
|
imageSrc,
|
|
@@ -51788,7 +51941,7 @@ var ImageThumbnail = memo35(function ImageThumbnail2({
|
|
|
51788
51941
|
const [aspect, setAspect] = useState78(16 / 9);
|
|
51789
51942
|
const ioRef = useRef93(null);
|
|
51790
51943
|
const roRef = useRef93(null);
|
|
51791
|
-
const setContainerRef =
|
|
51944
|
+
const setContainerRef = useCallback101((el) => {
|
|
51792
51945
|
ioRef.current?.disconnect();
|
|
51793
51946
|
roRef.current?.disconnect();
|
|
51794
51947
|
if (!el) return;
|
|
@@ -51941,7 +52094,7 @@ function useRenderClipContent({
|
|
|
51941
52094
|
activePreviewUrl,
|
|
51942
52095
|
effectiveTimelineDuration
|
|
51943
52096
|
}) {
|
|
51944
|
-
return
|
|
52097
|
+
return useCallback102(
|
|
51945
52098
|
// Pre-existing clip-content dispatcher; reduced by extracting renderAudioClip.
|
|
51946
52099
|
// fallow-ignore-next-line complexity
|
|
51947
52100
|
(el, style) => {
|
|
@@ -52013,11 +52166,11 @@ function useRenderClipContent({
|
|
|
52013
52166
|
}
|
|
52014
52167
|
|
|
52015
52168
|
// src/hooks/useConsoleErrorCapture.ts
|
|
52016
|
-
import { useCallback as
|
|
52169
|
+
import { useCallback as useCallback103, useEffect as useEffect70, useRef as useRef94, useState as useState79 } from "react";
|
|
52017
52170
|
function useConsoleErrorCapture(previewIframe) {
|
|
52018
52171
|
const [consoleErrors, setConsoleErrors] = useState79(null);
|
|
52019
52172
|
const consoleErrorsRef = useRef94([]);
|
|
52020
|
-
const resetErrors =
|
|
52173
|
+
const resetErrors = useCallback103(() => {
|
|
52021
52174
|
consoleErrorsRef.current = [];
|
|
52022
52175
|
setConsoleErrors(null);
|
|
52023
52176
|
}, []);
|
|
@@ -52086,7 +52239,7 @@ function useConsoleErrorCapture(previewIframe) {
|
|
|
52086
52239
|
}
|
|
52087
52240
|
|
|
52088
52241
|
// src/hooks/useFrameCapture.ts
|
|
52089
|
-
import { useState as useState80, useCallback as
|
|
52242
|
+
import { useState as useState80, useCallback as useCallback104, useRef as useRef95 } from "react";
|
|
52090
52243
|
|
|
52091
52244
|
// src/utils/projectRouting.ts
|
|
52092
52245
|
var PROJECT_HASH_PREFIX = "#project/";
|
|
@@ -52175,10 +52328,10 @@ function useFrameCapture({
|
|
|
52175
52328
|
setCaptureFrameTime(usePlayerStore.getState().currentTime);
|
|
52176
52329
|
return liveTime.subscribe(setCaptureFrameTime);
|
|
52177
52330
|
});
|
|
52178
|
-
const refreshCaptureFrameTime =
|
|
52331
|
+
const refreshCaptureFrameTime = useCallback104(() => {
|
|
52179
52332
|
setCaptureFrameTime(usePlayerStore.getState().currentTime);
|
|
52180
52333
|
}, []);
|
|
52181
|
-
const handleCaptureFrameClick =
|
|
52334
|
+
const handleCaptureFrameClick = useCallback104(
|
|
52182
52335
|
async (event) => {
|
|
52183
52336
|
if (!projectId) return;
|
|
52184
52337
|
event.preventDefault();
|
|
@@ -52255,7 +52408,7 @@ function useFrameCapture({
|
|
|
52255
52408
|
}
|
|
52256
52409
|
|
|
52257
52410
|
// src/hooks/useLintModal.ts
|
|
52258
|
-
import { useState as useState81, useCallback as
|
|
52411
|
+
import { useState as useState81, useCallback as useCallback105, useEffect as useEffect71, useRef as useRef96, useMemo as useMemo37 } from "react";
|
|
52259
52412
|
function parseFinding(f) {
|
|
52260
52413
|
return {
|
|
52261
52414
|
severity: f.severity === "error" ? "error" : "warning",
|
|
@@ -52270,7 +52423,7 @@ function useLintModal(projectId, refreshKey) {
|
|
|
52270
52423
|
const [linting, setLinting] = useState81(false);
|
|
52271
52424
|
const [backgroundFindings, setBackgroundFindings] = useState81([]);
|
|
52272
52425
|
const autoLintRanRef = useRef96(false);
|
|
52273
|
-
const runLint =
|
|
52426
|
+
const runLint = useCallback105(
|
|
52274
52427
|
async (opts) => {
|
|
52275
52428
|
if (!projectId) return;
|
|
52276
52429
|
if (!opts?.background) setLinting(true);
|
|
@@ -52295,7 +52448,7 @@ function useLintModal(projectId, refreshKey) {
|
|
|
52295
52448
|
},
|
|
52296
52449
|
[projectId]
|
|
52297
52450
|
);
|
|
52298
|
-
const handleLint =
|
|
52451
|
+
const handleLint = useCallback105(() => runLint(), [runLint]);
|
|
52299
52452
|
const prevProjectIdRef = useRef96(projectId);
|
|
52300
52453
|
useEffect71(() => {
|
|
52301
52454
|
if (projectId !== prevProjectIdRef.current) {
|
|
@@ -52311,8 +52464,8 @@ function useLintModal(projectId, refreshKey) {
|
|
|
52311
52464
|
const timer = setTimeout(() => void runLint({ background: true }), 1e3);
|
|
52312
52465
|
return () => clearTimeout(timer);
|
|
52313
52466
|
}, [projectId, refreshKey, runLint]);
|
|
52314
|
-
const closeLintModal =
|
|
52315
|
-
const groupFindings =
|
|
52467
|
+
const closeLintModal = useCallback105(() => setLintModal(null), []);
|
|
52468
|
+
const groupFindings = useCallback105(
|
|
52316
52469
|
(keyFn) => {
|
|
52317
52470
|
const map = /* @__PURE__ */ new Map();
|
|
52318
52471
|
for (const f of backgroundFindings) {
|
|
@@ -52327,8 +52480,8 @@ function useLintModal(projectId, refreshKey) {
|
|
|
52327
52480
|
},
|
|
52328
52481
|
[backgroundFindings]
|
|
52329
52482
|
);
|
|
52330
|
-
const findingsByElement =
|
|
52331
|
-
const findingsByFile =
|
|
52483
|
+
const findingsByElement = useMemo37(() => groupFindings((f) => f.elementId), [groupFindings]);
|
|
52484
|
+
const findingsByFile = useMemo37(() => groupFindings((f) => f.file), [groupFindings]);
|
|
52332
52485
|
useEffect71(() => {
|
|
52333
52486
|
usePlayerStore.getState().setLintFindingsByElement(findingsByElement);
|
|
52334
52487
|
}, [findingsByElement]);
|
|
@@ -52344,7 +52497,7 @@ function useLintModal(projectId, refreshKey) {
|
|
|
52344
52497
|
}
|
|
52345
52498
|
|
|
52346
52499
|
// src/hooks/useToast.ts
|
|
52347
|
-
import { useState as useState82, useCallback as
|
|
52500
|
+
import { useState as useState82, useCallback as useCallback106, useRef as useRef97 } from "react";
|
|
52348
52501
|
var AUTO_DISMISS_MS2 = 4e3;
|
|
52349
52502
|
var EXIT_MS = 160;
|
|
52350
52503
|
var MAX_TOASTS = 3;
|
|
@@ -52352,21 +52505,21 @@ var nextToastId = 1;
|
|
|
52352
52505
|
function useToast() {
|
|
52353
52506
|
const [toasts, setToasts] = useState82([]);
|
|
52354
52507
|
const timersRef = useRef97(/* @__PURE__ */ new Map());
|
|
52355
|
-
const clearTimer =
|
|
52508
|
+
const clearTimer = useCallback106((id) => {
|
|
52356
52509
|
const timer = timersRef.current.get(id);
|
|
52357
52510
|
if (timer) {
|
|
52358
52511
|
clearTimeout(timer);
|
|
52359
52512
|
timersRef.current.delete(id);
|
|
52360
52513
|
}
|
|
52361
52514
|
}, []);
|
|
52362
|
-
const removeToast =
|
|
52515
|
+
const removeToast = useCallback106(
|
|
52363
52516
|
(id) => {
|
|
52364
52517
|
clearTimer(id);
|
|
52365
52518
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
52366
52519
|
},
|
|
52367
52520
|
[clearTimer]
|
|
52368
52521
|
);
|
|
52369
|
-
const dismissToast =
|
|
52522
|
+
const dismissToast = useCallback106(
|
|
52370
52523
|
(id) => {
|
|
52371
52524
|
clearTimer(id);
|
|
52372
52525
|
setToasts((prev) => prev.map((t) => t.id === id ? { ...t, leaving: true } : t));
|
|
@@ -52375,7 +52528,7 @@ function useToast() {
|
|
|
52375
52528
|
},
|
|
52376
52529
|
[clearTimer, removeToast]
|
|
52377
52530
|
);
|
|
52378
|
-
const showToast =
|
|
52531
|
+
const showToast = useCallback106(
|
|
52379
52532
|
(message, tone = "error") => {
|
|
52380
52533
|
const id = nextToastId++;
|
|
52381
52534
|
setToasts((prev) => {
|
|
@@ -52401,14 +52554,14 @@ function useToast() {
|
|
|
52401
52554
|
}
|
|
52402
52555
|
|
|
52403
52556
|
// src/hooks/useCompositionContentLoader.ts
|
|
52404
|
-
import { useCallback as
|
|
52557
|
+
import { useCallback as useCallback107 } from "react";
|
|
52405
52558
|
function useCompositionContentLoader({
|
|
52406
52559
|
projectId,
|
|
52407
52560
|
setEditingFile,
|
|
52408
52561
|
setActiveCompPath,
|
|
52409
52562
|
showToast
|
|
52410
52563
|
}) {
|
|
52411
|
-
return
|
|
52564
|
+
return useCallback107(
|
|
52412
52565
|
(comp) => {
|
|
52413
52566
|
setActiveCompPath(comp.endsWith(".html") ? comp : null);
|
|
52414
52567
|
setEditingFile({ path: comp, content: null });
|
|
@@ -52427,7 +52580,7 @@ function useCompositionContentLoader({
|
|
|
52427
52580
|
}
|
|
52428
52581
|
|
|
52429
52582
|
// src/hooks/useStudioUrlState.ts
|
|
52430
|
-
import { useCallback as
|
|
52583
|
+
import { useCallback as useCallback108, useEffect as useEffect72, useRef as useRef98, useState as useState83 } from "react";
|
|
52431
52584
|
|
|
52432
52585
|
// src/utils/studioUrlState.ts
|
|
52433
52586
|
var VALID_TABS = ["layers", "design", "renders", "slideshow", "variables"];
|
|
@@ -52560,7 +52713,7 @@ function useStudioUrlState({
|
|
|
52560
52713
|
const [selectionHydrated, setSelectionHydrated] = useState83(initialState2.selection == null);
|
|
52561
52714
|
const pendingSelectionRef = useRef98(initialState2.selection);
|
|
52562
52715
|
const stableTimeRef = useRef98(initialState2.currentTime);
|
|
52563
|
-
const buildUrlState =
|
|
52716
|
+
const buildUrlState = useCallback108(
|
|
52564
52717
|
() => ({
|
|
52565
52718
|
activeCompPath,
|
|
52566
52719
|
currentTime: stableTimeRef.current,
|
|
@@ -52571,7 +52724,7 @@ function useStudioUrlState({
|
|
|
52571
52724
|
}),
|
|
52572
52725
|
[activeCompPath, domEditSelection, rightCollapsed, rightPanelTab]
|
|
52573
52726
|
);
|
|
52574
|
-
const applyUrlSelection =
|
|
52727
|
+
const applyUrlSelection = useCallback108(
|
|
52575
52728
|
(selection) => {
|
|
52576
52729
|
if (!selection) {
|
|
52577
52730
|
applyDomSelection(null, { revealPanel: false });
|
|
@@ -52687,7 +52840,7 @@ function useStudioUrlState({
|
|
|
52687
52840
|
}
|
|
52688
52841
|
|
|
52689
52842
|
// src/hooks/useStudioContextValue.ts
|
|
52690
|
-
import { useCallback as
|
|
52843
|
+
import { useCallback as useCallback109, useMemo as useMemo38, useRef as useRef99, useState as useState84 } from "react";
|
|
52691
52844
|
function buildStudioContextValue(input) {
|
|
52692
52845
|
return {
|
|
52693
52846
|
projectId: input.projectId,
|
|
@@ -52712,7 +52865,7 @@ function buildStudioContextValue(input) {
|
|
|
52712
52865
|
};
|
|
52713
52866
|
}
|
|
52714
52867
|
function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, isPlaying, isGestureRecording) {
|
|
52715
|
-
return
|
|
52868
|
+
return useMemo38(() => {
|
|
52716
52869
|
const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
|
|
52717
52870
|
const layersPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && inspectorTabActive && rightInspectorPanes.layers;
|
|
52718
52871
|
const designPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && inspectorTabActive && rightInspectorPanes.design;
|
|
@@ -52733,21 +52886,21 @@ function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, i
|
|
|
52733
52886
|
function useDragOverlay(onImportFiles) {
|
|
52734
52887
|
const [active, setActive] = useState84(false);
|
|
52735
52888
|
const counterRef = useRef99(0);
|
|
52736
|
-
const onDragOver =
|
|
52889
|
+
const onDragOver = useCallback109((e) => {
|
|
52737
52890
|
if (!e.dataTransfer.types.includes("Files")) return;
|
|
52738
52891
|
e.preventDefault();
|
|
52739
52892
|
}, []);
|
|
52740
|
-
const onDragEnter =
|
|
52893
|
+
const onDragEnter = useCallback109((e) => {
|
|
52741
52894
|
if (!e.dataTransfer.types.includes("Files")) return;
|
|
52742
52895
|
e.preventDefault();
|
|
52743
52896
|
counterRef.current++;
|
|
52744
52897
|
setActive(true);
|
|
52745
52898
|
}, []);
|
|
52746
|
-
const onDragLeave =
|
|
52899
|
+
const onDragLeave = useCallback109(() => {
|
|
52747
52900
|
counterRef.current--;
|
|
52748
52901
|
if (counterRef.current === 0) setActive(false);
|
|
52749
52902
|
}, []);
|
|
52750
|
-
const onDrop =
|
|
52903
|
+
const onDrop = useCallback109(
|
|
52751
52904
|
(e) => {
|
|
52752
52905
|
counterRef.current = 0;
|
|
52753
52906
|
setActive(false);
|
|
@@ -52760,7 +52913,7 @@ function useDragOverlay(onImportFiles) {
|
|
|
52760
52913
|
return { active, onDragOver, onDragEnter, onDragLeave, onDrop };
|
|
52761
52914
|
}
|
|
52762
52915
|
function useGlobalFileDrop(handleTimelineFileDrop) {
|
|
52763
|
-
const onDrop =
|
|
52916
|
+
const onDrop = useCallback109(
|
|
52764
52917
|
(files) => {
|
|
52765
52918
|
const start = usePlayerStore.getState().currentTime;
|
|
52766
52919
|
void handleTimelineFileDrop(Array.from(files), { start, track: 0 });
|
|
@@ -52771,10 +52924,10 @@ function useGlobalFileDrop(handleTimelineFileDrop) {
|
|
|
52771
52924
|
}
|
|
52772
52925
|
|
|
52773
52926
|
// src/components/StudioHeader.tsx
|
|
52774
|
-
import { useRef as
|
|
52927
|
+
import { useRef as useRef101 } from "react";
|
|
52775
52928
|
|
|
52776
52929
|
// src/contexts/PanelLayoutContext.tsx
|
|
52777
|
-
import { createContext as createContext8, useContext as useContext8, useMemo as
|
|
52930
|
+
import { createContext as createContext8, useContext as useContext8, useMemo as useMemo39 } from "react";
|
|
52778
52931
|
import { jsx as jsx114 } from "react/jsx-runtime";
|
|
52779
52932
|
var PanelLayoutContext = createContext8(null);
|
|
52780
52933
|
function usePanelLayoutContext() {
|
|
@@ -52796,6 +52949,7 @@ function PanelLayoutProvider({
|
|
|
52796
52949
|
setRightPanelTab,
|
|
52797
52950
|
rightInspectorPanes,
|
|
52798
52951
|
toggleRightInspectorPane,
|
|
52952
|
+
setExclusiveRightInspectorPane,
|
|
52799
52953
|
toggleLeftSidebar,
|
|
52800
52954
|
handlePanelResizeStart,
|
|
52801
52955
|
handlePanelResizeMove,
|
|
@@ -52803,7 +52957,7 @@ function PanelLayoutProvider({
|
|
|
52803
52957
|
},
|
|
52804
52958
|
children
|
|
52805
52959
|
}) {
|
|
52806
|
-
const stable =
|
|
52960
|
+
const stable = useMemo39(
|
|
52807
52961
|
() => ({
|
|
52808
52962
|
leftWidth,
|
|
52809
52963
|
setLeftWidth,
|
|
@@ -52817,6 +52971,7 @@ function PanelLayoutProvider({
|
|
|
52817
52971
|
setRightPanelTab,
|
|
52818
52972
|
rightInspectorPanes,
|
|
52819
52973
|
toggleRightInspectorPane,
|
|
52974
|
+
setExclusiveRightInspectorPane,
|
|
52820
52975
|
toggleLeftSidebar,
|
|
52821
52976
|
handlePanelResizeStart,
|
|
52822
52977
|
handlePanelResizeMove,
|
|
@@ -52835,6 +52990,7 @@ function PanelLayoutProvider({
|
|
|
52835
52990
|
setRightPanelTab,
|
|
52836
52991
|
rightInspectorPanes,
|
|
52837
52992
|
toggleRightInspectorPane,
|
|
52993
|
+
setExclusiveRightInspectorPane,
|
|
52838
52994
|
toggleLeftSidebar,
|
|
52839
52995
|
handlePanelResizeStart,
|
|
52840
52996
|
handlePanelResizeMove,
|
|
@@ -52847,10 +53003,11 @@ function PanelLayoutProvider({
|
|
|
52847
53003
|
// src/contexts/ViewModeContext.tsx
|
|
52848
53004
|
import {
|
|
52849
53005
|
createContext as createContext9,
|
|
52850
|
-
useCallback as
|
|
53006
|
+
useCallback as useCallback110,
|
|
52851
53007
|
useContext as useContext9,
|
|
52852
53008
|
useEffect as useEffect73,
|
|
52853
|
-
useMemo as
|
|
53009
|
+
useMemo as useMemo40,
|
|
53010
|
+
useRef as useRef100,
|
|
52854
53011
|
useState as useState85
|
|
52855
53012
|
} from "react";
|
|
52856
53013
|
import { jsx as jsx115 } from "react/jsx-runtime";
|
|
@@ -52869,18 +53026,59 @@ function writeViewModeToUrl(mode) {
|
|
|
52869
53026
|
}
|
|
52870
53027
|
window.history.replaceState(window.history.state, "", url);
|
|
52871
53028
|
}
|
|
53029
|
+
function readHistoryLocation() {
|
|
53030
|
+
if (typeof window === "undefined") return null;
|
|
53031
|
+
return {
|
|
53032
|
+
href: window.location.href,
|
|
53033
|
+
state: window.history.state
|
|
53034
|
+
};
|
|
53035
|
+
}
|
|
52872
53036
|
function useViewModeState() {
|
|
52873
53037
|
const [viewMode, setMode] = useState85(() => readViewModeFromUrl());
|
|
53038
|
+
const guardsRef = useRef100(/* @__PURE__ */ new Set());
|
|
53039
|
+
const acceptedLocationRef = useRef100(readHistoryLocation());
|
|
53040
|
+
const canSetViewMode = useCallback110((mode) => {
|
|
53041
|
+
for (const guard of guardsRef.current) {
|
|
53042
|
+
if (!guard(mode)) return false;
|
|
53043
|
+
}
|
|
53044
|
+
return true;
|
|
53045
|
+
}, []);
|
|
52874
53046
|
useEffect73(() => {
|
|
52875
|
-
const onPopState = () =>
|
|
53047
|
+
const onPopState = () => {
|
|
53048
|
+
const mode = readViewModeFromUrl();
|
|
53049
|
+
if (mode !== viewMode && !canSetViewMode(mode)) {
|
|
53050
|
+
const acceptedLocation = acceptedLocationRef.current;
|
|
53051
|
+
if (acceptedLocation) {
|
|
53052
|
+
window.history.pushState(acceptedLocation.state, "", acceptedLocation.href);
|
|
53053
|
+
}
|
|
53054
|
+
return;
|
|
53055
|
+
}
|
|
53056
|
+
setMode(mode);
|
|
53057
|
+
acceptedLocationRef.current = readHistoryLocation();
|
|
53058
|
+
};
|
|
52876
53059
|
window.addEventListener("popstate", onPopState);
|
|
52877
53060
|
return () => window.removeEventListener("popstate", onPopState);
|
|
53061
|
+
}, [canSetViewMode, viewMode]);
|
|
53062
|
+
const setViewMode = useCallback110(
|
|
53063
|
+
(mode) => {
|
|
53064
|
+
if (!canSetViewMode(mode)) return false;
|
|
53065
|
+
setMode(mode);
|
|
53066
|
+
writeViewModeToUrl(mode);
|
|
53067
|
+
acceptedLocationRef.current = readHistoryLocation();
|
|
53068
|
+
return true;
|
|
53069
|
+
},
|
|
53070
|
+
[canSetViewMode]
|
|
53071
|
+
);
|
|
53072
|
+
const registerViewModeGuard = useCallback110((guard) => {
|
|
53073
|
+
guardsRef.current.add(guard);
|
|
53074
|
+
return () => {
|
|
53075
|
+
guardsRef.current.delete(guard);
|
|
53076
|
+
};
|
|
52878
53077
|
}, []);
|
|
52879
|
-
|
|
52880
|
-
|
|
52881
|
-
|
|
52882
|
-
|
|
52883
|
-
return useMemo39(() => ({ viewMode, setViewMode }), [viewMode, setViewMode]);
|
|
53078
|
+
return useMemo40(
|
|
53079
|
+
() => ({ viewMode, setViewMode, registerViewModeGuard }),
|
|
53080
|
+
[viewMode, setViewMode, registerViewModeGuard]
|
|
53081
|
+
);
|
|
52884
53082
|
}
|
|
52885
53083
|
var ViewModeContext = createContext9(null);
|
|
52886
53084
|
function useViewMode() {
|
|
@@ -53085,11 +53283,10 @@ var VIEW_MODE_OPTIONS = [
|
|
|
53085
53283
|
];
|
|
53086
53284
|
function ViewModeToggle() {
|
|
53087
53285
|
const { viewMode, setViewMode } = useViewMode();
|
|
53088
|
-
const tabRefs =
|
|
53286
|
+
const tabRefs = useRef101([]);
|
|
53089
53287
|
const selectMode = (mode) => {
|
|
53090
53288
|
if (mode === viewMode) return;
|
|
53091
|
-
trackStudioEvent("view_mode_toggle", { mode });
|
|
53092
|
-
setViewMode(mode);
|
|
53289
|
+
if (setViewMode(mode)) trackStudioEvent("view_mode_toggle", { mode });
|
|
53093
53290
|
};
|
|
53094
53291
|
const handleKeyDown = (e, index) => {
|
|
53095
53292
|
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
@@ -53317,10 +53514,10 @@ function StudioHeader({
|
|
|
53317
53514
|
}
|
|
53318
53515
|
|
|
53319
53516
|
// src/hooks/useGestureCommit.ts
|
|
53320
|
-
import { useState as useState87, useCallback as
|
|
53517
|
+
import { useState as useState87, useCallback as useCallback112, useRef as useRef103, useEffect as useEffect75 } from "react";
|
|
53321
53518
|
|
|
53322
53519
|
// src/hooks/useGestureRecording.ts
|
|
53323
|
-
import { useCallback as
|
|
53520
|
+
import { useCallback as useCallback111, useEffect as useEffect74, useRef as useRef102, useState as useState86 } from "react";
|
|
53324
53521
|
function readBasePosition(element, iframeEl) {
|
|
53325
53522
|
let baseOpacity = 1;
|
|
53326
53523
|
let baseScale = 1;
|
|
@@ -53439,10 +53636,10 @@ function createRecordingRefs() {
|
|
|
53439
53636
|
function useGestureRecording() {
|
|
53440
53637
|
const [isRecording, setIsRecording] = useState86(false);
|
|
53441
53638
|
const [recordingDuration, setRecordingDuration] = useState86(0);
|
|
53442
|
-
const isRecordingRef =
|
|
53443
|
-
const refs =
|
|
53444
|
-
const samplesRef =
|
|
53445
|
-
const trailRef =
|
|
53639
|
+
const isRecordingRef = useRef102(false);
|
|
53640
|
+
const refs = useRef102(createRecordingRefs());
|
|
53641
|
+
const samplesRef = useRef102(refs.current.samples);
|
|
53642
|
+
const trailRef = useRef102(refs.current.trail);
|
|
53446
53643
|
useEffect74(() => {
|
|
53447
53644
|
const r = refs.current;
|
|
53448
53645
|
return () => {
|
|
@@ -53451,7 +53648,7 @@ function useGestureRecording() {
|
|
|
53451
53648
|
isRecordingRef.current = false;
|
|
53452
53649
|
};
|
|
53453
53650
|
}, []);
|
|
53454
|
-
const startRecording =
|
|
53651
|
+
const startRecording = useCallback111(
|
|
53455
53652
|
(element, iframeEl, elementEndTime) => {
|
|
53456
53653
|
if (isRecordingRef.current) return;
|
|
53457
53654
|
isRecordingRef.current = true;
|
|
@@ -53551,7 +53748,7 @@ function useGestureRecording() {
|
|
|
53551
53748
|
[]
|
|
53552
53749
|
// No deps — uses refs only for all mutable state
|
|
53553
53750
|
);
|
|
53554
|
-
const stopRecording =
|
|
53751
|
+
const stopRecording = useCallback111(() => {
|
|
53555
53752
|
if (!isRecordingRef.current) return [];
|
|
53556
53753
|
isRecordingRef.current = false;
|
|
53557
53754
|
const r = refs.current;
|
|
@@ -53581,7 +53778,7 @@ function useGestureRecording() {
|
|
|
53581
53778
|
setIsRecording(false);
|
|
53582
53779
|
return frozen;
|
|
53583
53780
|
}, []);
|
|
53584
|
-
const clearSamples =
|
|
53781
|
+
const clearSamples = useCallback111(() => {
|
|
53585
53782
|
const r = refs.current;
|
|
53586
53783
|
r.samples = [];
|
|
53587
53784
|
r.trail = [];
|
|
@@ -53838,13 +54035,13 @@ function useGestureCommit({
|
|
|
53838
54035
|
}) {
|
|
53839
54036
|
const gestureRecording = useGestureRecording();
|
|
53840
54037
|
const [gestureState, setGestureState] = useState87("idle");
|
|
53841
|
-
const gestureStateRef =
|
|
53842
|
-
const recordingAutoStopRef =
|
|
53843
|
-
const recordingStartTimeRef =
|
|
53844
|
-
const commitInFlightRef =
|
|
53845
|
-
const capturedSelectionRef =
|
|
54038
|
+
const gestureStateRef = useRef103("idle");
|
|
54039
|
+
const recordingAutoStopRef = useRef103(void 0);
|
|
54040
|
+
const recordingStartTimeRef = useRef103(0);
|
|
54041
|
+
const commitInFlightRef = useRef103(false);
|
|
54042
|
+
const capturedSelectionRef = useRef103(null);
|
|
53846
54043
|
useEffect75(() => () => clearInterval(recordingAutoStopRef.current), []);
|
|
53847
|
-
const stopAndCommitRecording =
|
|
54044
|
+
const stopAndCommitRecording = useCallback112(async () => {
|
|
53848
54045
|
clearInterval(recordingAutoStopRef.current);
|
|
53849
54046
|
if (commitInFlightRef.current) {
|
|
53850
54047
|
return;
|
|
@@ -54010,7 +54207,7 @@ function useGestureCommit({
|
|
|
54010
54207
|
commitInFlightRef.current = false;
|
|
54011
54208
|
}
|
|
54012
54209
|
}, [gestureRecording, showToast, isGestureRecordingRef, domEditSessionRef]);
|
|
54013
|
-
const handleToggleRecording =
|
|
54210
|
+
const handleToggleRecording = useCallback112(() => {
|
|
54014
54211
|
if (gestureStateRef.current === "recording") {
|
|
54015
54212
|
void stopAndCommitRecording();
|
|
54016
54213
|
return;
|
|
@@ -54056,7 +54253,7 @@ function useGestureCommit({
|
|
|
54056
54253
|
}
|
|
54057
54254
|
|
|
54058
54255
|
// src/components/editor/GestureTrailOverlay.tsx
|
|
54059
|
-
import { memo as memo36, useMemo as
|
|
54256
|
+
import { memo as memo36, useMemo as useMemo41 } from "react";
|
|
54060
54257
|
import { Fragment as Fragment36, jsx as jsx117, jsxs as jsxs101 } from "react/jsx-runtime";
|
|
54061
54258
|
var GestureTrailOverlay = memo36(function GestureTrailOverlay2({
|
|
54062
54259
|
samples,
|
|
@@ -54068,7 +54265,7 @@ var GestureTrailOverlay = memo36(function GestureTrailOverlay2({
|
|
|
54068
54265
|
mode,
|
|
54069
54266
|
accentColor = "#3CE6AC"
|
|
54070
54267
|
}) {
|
|
54071
|
-
const trailPoints =
|
|
54268
|
+
const trailPoints = useMemo41(() => {
|
|
54072
54269
|
if (!canvasRect) return "";
|
|
54073
54270
|
if (trail && trail.length > 1) {
|
|
54074
54271
|
return trail.map((p) => `${p.x - canvasRect.left},${p.y - canvasRect.top}`).join(" ");
|
|
@@ -54076,7 +54273,7 @@ var GestureTrailOverlay = memo36(function GestureTrailOverlay2({
|
|
|
54076
54273
|
if (samples.length === 0) return "";
|
|
54077
54274
|
return samples.filter((s) => s.properties.x != null && s.properties.y != null).map((s) => `${s.properties.x},${s.properties.y}`).join(" ");
|
|
54078
54275
|
}, [samples, trail, sampleCount, canvasRect?.left, canvasRect?.top]);
|
|
54079
|
-
const simplifiedPath =
|
|
54276
|
+
const simplifiedPath = useMemo41(() => {
|
|
54080
54277
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return "";
|
|
54081
54278
|
const pts = [];
|
|
54082
54279
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -54088,7 +54285,7 @@ var GestureTrailOverlay = memo36(function GestureTrailOverlay2({
|
|
|
54088
54285
|
if (pts.length === 0) return "";
|
|
54089
54286
|
return pts.map((p) => `${p.x},${p.y}`).join(" ");
|
|
54090
54287
|
}, [simplifiedPoints]);
|
|
54091
|
-
const diamondPositions =
|
|
54288
|
+
const diamondPositions = useMemo41(() => {
|
|
54092
54289
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return [];
|
|
54093
54290
|
const pts = [];
|
|
54094
54291
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -54168,20 +54365,20 @@ var GestureTrailOverlay = memo36(function GestureTrailOverlay2({
|
|
|
54168
54365
|
});
|
|
54169
54366
|
|
|
54170
54367
|
// src/components/StudioLeftSidebar.tsx
|
|
54171
|
-
import { useCallback as
|
|
54368
|
+
import { useCallback as useCallback119 } from "react";
|
|
54172
54369
|
|
|
54173
54370
|
// src/components/sidebar/LeftSidebar.tsx
|
|
54174
54371
|
import {
|
|
54175
54372
|
memo as memo40,
|
|
54176
54373
|
useState as useState96,
|
|
54177
|
-
useCallback as
|
|
54374
|
+
useCallback as useCallback118,
|
|
54178
54375
|
useImperativeHandle,
|
|
54179
|
-
useRef as
|
|
54376
|
+
useRef as useRef109,
|
|
54180
54377
|
forwardRef as forwardRef3
|
|
54181
54378
|
} from "react";
|
|
54182
54379
|
|
|
54183
54380
|
// src/components/sidebar/CompositionsTab.tsx
|
|
54184
|
-
import { memo as memo37, useCallback as
|
|
54381
|
+
import { memo as memo37, useCallback as useCallback113, useEffect as useEffect76, useRef as useRef104, useState as useState88 } from "react";
|
|
54185
54382
|
import { jsx as jsx118, jsxs as jsxs102 } from "react/jsx-runtime";
|
|
54186
54383
|
var DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
|
|
54187
54384
|
var CARD_W = 80;
|
|
@@ -54250,10 +54447,10 @@ function CompCard({
|
|
|
54250
54447
|
}) {
|
|
54251
54448
|
const [hovered, setHovered] = useState88(false);
|
|
54252
54449
|
const [stageSize, setStageSize] = useState88(DEFAULT_PREVIEW_STAGE);
|
|
54253
|
-
const iframeRef =
|
|
54254
|
-
const hoverTimer =
|
|
54255
|
-
const syncTimer =
|
|
54256
|
-
const requestIframePlaybackSync =
|
|
54450
|
+
const iframeRef = useRef104(null);
|
|
54451
|
+
const hoverTimer = useRef104(null);
|
|
54452
|
+
const syncTimer = useRef104(null);
|
|
54453
|
+
const requestIframePlaybackSync = useCallback113((shouldPlay) => {
|
|
54257
54454
|
if (syncTimer.current) {
|
|
54258
54455
|
clearTimeout(syncTimer.current);
|
|
54259
54456
|
syncTimer.current = null;
|
|
@@ -54411,7 +54608,7 @@ var CompositionsTab = memo37(function CompositionsTab2({
|
|
|
54411
54608
|
});
|
|
54412
54609
|
|
|
54413
54610
|
// src/components/sidebar/AssetsTab.tsx
|
|
54414
|
-
import { memo as memo38, useState as useState93, useCallback as
|
|
54611
|
+
import { memo as memo38, useState as useState93, useCallback as useCallback116, useRef as useRef107, useMemo as useMemo43, useEffect as useEffect81 } from "react";
|
|
54415
54612
|
|
|
54416
54613
|
// src/components/sidebar/assetHelpers.ts
|
|
54417
54614
|
function getCategory(path) {
|
|
@@ -54461,7 +54658,7 @@ var CATEGORY_LABELS = {
|
|
|
54461
54658
|
var FILTER_ORDER = ["audio", "images", "video", "fonts"];
|
|
54462
54659
|
|
|
54463
54660
|
// src/components/sidebar/AudioRow.tsx
|
|
54464
|
-
import { useState as useState89, useRef as
|
|
54661
|
+
import { useState as useState89, useRef as useRef105, useEffect as useEffect77, useCallback as useCallback114 } from "react";
|
|
54465
54662
|
|
|
54466
54663
|
// src/components/sidebar/AssetContextMenu.tsx
|
|
54467
54664
|
import { jsx as jsx119, jsxs as jsxs103 } from "react/jsx-runtime";
|
|
@@ -54590,24 +54787,24 @@ function AudioRow({
|
|
|
54590
54787
|
const [contextMenu, setContextMenu] = useState89(null);
|
|
54591
54788
|
const [playing, setPlaying] = useState89(false);
|
|
54592
54789
|
const [bars, setBars] = useState89([]);
|
|
54593
|
-
const audioRef =
|
|
54594
|
-
const actxRef =
|
|
54595
|
-
const analyserRef =
|
|
54596
|
-
const sourceRef =
|
|
54597
|
-
const animRef =
|
|
54790
|
+
const audioRef = useRef105(null);
|
|
54791
|
+
const actxRef = useRef105(null);
|
|
54792
|
+
const analyserRef = useRef105(null);
|
|
54793
|
+
const sourceRef = useRef105(null);
|
|
54794
|
+
const animRef = useRef105(0);
|
|
54598
54795
|
const name = basename2(asset);
|
|
54599
54796
|
const subtype = getAudioSubtype(asset);
|
|
54600
54797
|
const serveUrl = resolveMediaPreviewUrl(asset, projectId);
|
|
54601
|
-
const pointerDownRef =
|
|
54798
|
+
const pointerDownRef = useRef105(null);
|
|
54602
54799
|
const setSelectedElementId = usePlayerStore((s) => s.setSelectedElementId);
|
|
54603
54800
|
const requestClipReveal = usePlayerStore((s) => s.requestClipReveal);
|
|
54604
54801
|
const elements = usePlayerStore((s) => s.elements);
|
|
54605
54802
|
const setPreviewAsset = useAssetPreviewStore((s) => s.setPreviewAsset);
|
|
54606
54803
|
const clearPreviewAsset = useAssetPreviewStore((s) => s.clearPreviewAsset);
|
|
54607
|
-
const handlePointerDown =
|
|
54804
|
+
const handlePointerDown = useCallback114((e) => {
|
|
54608
54805
|
pointerDownRef.current = { x: e.clientX, y: e.clientY };
|
|
54609
54806
|
}, []);
|
|
54610
|
-
const handlePointerUp =
|
|
54807
|
+
const handlePointerUp = useCallback114(
|
|
54611
54808
|
(e) => {
|
|
54612
54809
|
const origin = pointerDownRef.current;
|
|
54613
54810
|
pointerDownRef.current = null;
|
|
@@ -54671,7 +54868,7 @@ function AudioRow({
|
|
|
54671
54868
|
}
|
|
54672
54869
|
return () => cancelAnimationFrame(animRef.current);
|
|
54673
54870
|
}, [playing]);
|
|
54674
|
-
const togglePlay =
|
|
54871
|
+
const togglePlay = useCallback114(async () => {
|
|
54675
54872
|
if (playing) {
|
|
54676
54873
|
audioRef.current?.pause();
|
|
54677
54874
|
setPlaying(false);
|
|
@@ -54781,7 +54978,7 @@ function AudioRow({
|
|
|
54781
54978
|
}
|
|
54782
54979
|
|
|
54783
54980
|
// src/components/sidebar/GlobalAssetsView.tsx
|
|
54784
|
-
import { useEffect as useEffect78, useMemo as
|
|
54981
|
+
import { useEffect as useEffect78, useMemo as useMemo42, useState as useState90 } from "react";
|
|
54785
54982
|
import { jsx as jsx121, jsxs as jsxs105 } from "react/jsx-runtime";
|
|
54786
54983
|
function globalAssetRows(records, query = "") {
|
|
54787
54984
|
const q = query.trim().toLowerCase();
|
|
@@ -54808,7 +55005,7 @@ function GlobalAssetsView({ searchQuery }) {
|
|
|
54808
55005
|
cancelled = true;
|
|
54809
55006
|
};
|
|
54810
55007
|
}, []);
|
|
54811
|
-
const rows =
|
|
55008
|
+
const rows = useMemo42(() => globalAssetRows(records ?? [], searchQuery), [records, searchQuery]);
|
|
54812
55009
|
if (records === null) {
|
|
54813
55010
|
return /* @__PURE__ */ jsx121("p", { className: "px-4 py-3 text-[11px] text-panel-text-5", children: "Loading global assets\u2026" });
|
|
54814
55011
|
}
|
|
@@ -54840,7 +55037,7 @@ function GlobalAssetsView({ searchQuery }) {
|
|
|
54840
55037
|
}
|
|
54841
55038
|
|
|
54842
55039
|
// src/components/sidebar/AssetCard.tsx
|
|
54843
|
-
import { useState as useState92, useEffect as useEffect80, useRef as
|
|
55040
|
+
import { useState as useState92, useEffect as useEffect80, useRef as useRef106, useCallback as useCallback115 } from "react";
|
|
54844
55041
|
|
|
54845
55042
|
// src/components/ui/VideoFrameThumbnail.tsx
|
|
54846
55043
|
import { useState as useState91, useEffect as useEffect79 } from "react";
|
|
@@ -54965,16 +55162,16 @@ function AssetCard({
|
|
|
54965
55162
|
const probedDuration = useProbedDuration(serveUrl, !isVideo || duration != null);
|
|
54966
55163
|
const resolvedDuration = duration ?? probedDuration ?? void 0;
|
|
54967
55164
|
const durationLabel = formatDuration(resolvedDuration ?? 0);
|
|
54968
|
-
const pointerDownRef =
|
|
55165
|
+
const pointerDownRef = useRef106(null);
|
|
54969
55166
|
const setSelectedElementId = usePlayerStore((s) => s.setSelectedElementId);
|
|
54970
55167
|
const requestClipReveal = usePlayerStore((s) => s.requestClipReveal);
|
|
54971
55168
|
const elements = usePlayerStore((s) => s.elements);
|
|
54972
55169
|
const setPreviewAsset = useAssetPreviewStore((s) => s.setPreviewAsset);
|
|
54973
55170
|
const clearPreviewAsset = useAssetPreviewStore((s) => s.clearPreviewAsset);
|
|
54974
|
-
const handlePointerDown =
|
|
55171
|
+
const handlePointerDown = useCallback115((e) => {
|
|
54975
55172
|
pointerDownRef.current = { x: e.clientX, y: e.clientY };
|
|
54976
55173
|
}, []);
|
|
54977
|
-
const handlePointerUp =
|
|
55174
|
+
const handlePointerUp = useCallback115(
|
|
54978
55175
|
(e) => {
|
|
54979
55176
|
const origin = pointerDownRef.current;
|
|
54980
55177
|
pointerDownRef.current = null;
|
|
@@ -55167,7 +55364,7 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55167
55364
|
onRename,
|
|
55168
55365
|
onAddAssetToTimeline
|
|
55169
55366
|
}) {
|
|
55170
|
-
const fileInputRef =
|
|
55367
|
+
const fileInputRef = useRef107(null);
|
|
55171
55368
|
const [dragOver, setDragOver] = useState93(false);
|
|
55172
55369
|
const [copiedPath, setCopiedPath] = useState93(null);
|
|
55173
55370
|
const [activeFilter, setActiveFilter] = useState93("all");
|
|
@@ -55175,7 +55372,7 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55175
55372
|
const [searchQuery, setSearchQuery] = useState93("");
|
|
55176
55373
|
const [viewMode, setViewMode] = useState93("local");
|
|
55177
55374
|
const [manifest, setManifest] = useState93(/* @__PURE__ */ new Map());
|
|
55178
|
-
const manifest404Ref =
|
|
55375
|
+
const manifest404Ref = useRef107(/* @__PURE__ */ new Set());
|
|
55179
55376
|
const assetsKey = assets.join("|");
|
|
55180
55377
|
useEffect81(() => {
|
|
55181
55378
|
if (manifest404Ref.current.has(projectId)) return;
|
|
@@ -55204,7 +55401,7 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55204
55401
|
cancelled = true;
|
|
55205
55402
|
};
|
|
55206
55403
|
}, [projectId, assetsKey]);
|
|
55207
|
-
const handleDrop =
|
|
55404
|
+
const handleDrop = useCallback116(
|
|
55208
55405
|
(e) => {
|
|
55209
55406
|
e.preventDefault();
|
|
55210
55407
|
setDragOver(false);
|
|
@@ -55212,7 +55409,7 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55212
55409
|
},
|
|
55213
55410
|
[onImport]
|
|
55214
55411
|
);
|
|
55215
|
-
const handleCopyPath =
|
|
55412
|
+
const handleCopyPath = useCallback116(async (path) => {
|
|
55216
55413
|
const copied = await copyTextToClipboard(path);
|
|
55217
55414
|
if (copied) {
|
|
55218
55415
|
setCopiedPath(path);
|
|
@@ -55220,8 +55417,8 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55220
55417
|
}
|
|
55221
55418
|
}, []);
|
|
55222
55419
|
const elements = usePlayerStore((s) => s.elements);
|
|
55223
|
-
const usedPaths =
|
|
55224
|
-
const mediaAssets =
|
|
55420
|
+
const usedPaths = useMemo43(() => deriveUsedPaths(elements), [elements]);
|
|
55421
|
+
const mediaAssets = useMemo43(() => {
|
|
55225
55422
|
const media = assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a));
|
|
55226
55423
|
const all = filterByUsage(media, usedPaths, usageFilter);
|
|
55227
55424
|
if (!searchQuery) return all;
|
|
@@ -55233,7 +55430,7 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55233
55430
|
return rec?.description?.toLowerCase().includes(q);
|
|
55234
55431
|
});
|
|
55235
55432
|
}, [assets, searchQuery, manifest, usageFilter, usedPaths]);
|
|
55236
|
-
const categorized =
|
|
55433
|
+
const categorized = useMemo43(() => {
|
|
55237
55434
|
const groups = { audio: [], images: [], video: [], fonts: [] };
|
|
55238
55435
|
for (const a of mediaAssets) {
|
|
55239
55436
|
const cat = getCategory(a);
|
|
@@ -55248,12 +55445,12 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55248
55445
|
}
|
|
55249
55446
|
return groups;
|
|
55250
55447
|
}, [mediaAssets, usedPaths]);
|
|
55251
|
-
const counts =
|
|
55448
|
+
const counts = useMemo43(() => {
|
|
55252
55449
|
const c = { all: mediaAssets.length };
|
|
55253
55450
|
for (const cat of FILTER_ORDER) c[cat] = categorized[cat].length;
|
|
55254
55451
|
return c;
|
|
55255
55452
|
}, [mediaAssets, categorized]);
|
|
55256
|
-
const usageCounts =
|
|
55453
|
+
const usageCounts = useMemo43(
|
|
55257
55454
|
() => countUsage(
|
|
55258
55455
|
assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a)),
|
|
55259
55456
|
usedPaths
|
|
@@ -55496,11 +55693,11 @@ var AssetsTab = memo38(function AssetsTab2({
|
|
|
55496
55693
|
});
|
|
55497
55694
|
|
|
55498
55695
|
// src/components/sidebar/BlocksTab.tsx
|
|
55499
|
-
import { memo as memo39, useState as useState95, useCallback as
|
|
55696
|
+
import { memo as memo39, useState as useState95, useCallback as useCallback117, useRef as useRef108, useEffect as useEffect83 } from "react";
|
|
55500
55697
|
import { createPortal as createPortal7 } from "react-dom";
|
|
55501
55698
|
|
|
55502
55699
|
// src/hooks/useBlockCatalog.ts
|
|
55503
|
-
import { useState as useState94, useEffect as useEffect82, useMemo as
|
|
55700
|
+
import { useState as useState94, useEffect as useEffect82, useMemo as useMemo44 } from "react";
|
|
55504
55701
|
|
|
55505
55702
|
// src/utils/blockCategories.ts
|
|
55506
55703
|
import {
|
|
@@ -55554,7 +55751,7 @@ function useBlockCatalog() {
|
|
|
55554
55751
|
cancelled = true;
|
|
55555
55752
|
};
|
|
55556
55753
|
}, []);
|
|
55557
|
-
const filteredBlocks =
|
|
55754
|
+
const filteredBlocks = useMemo44(() => {
|
|
55558
55755
|
let result = blocks;
|
|
55559
55756
|
if (category) {
|
|
55560
55757
|
result = result.filter((b) => b.category === category);
|
|
@@ -55790,16 +55987,16 @@ function BlockCard({
|
|
|
55790
55987
|
}) {
|
|
55791
55988
|
const [hovered, setHovered] = useState95(false);
|
|
55792
55989
|
const [adding, setAdding] = useState95(false);
|
|
55793
|
-
const hoverTimer =
|
|
55990
|
+
const hoverTimer = useRef108(null);
|
|
55794
55991
|
const colors = getCategoryColors(category);
|
|
55795
55992
|
const needsWebGL = tags?.includes("html-in-canvas") || tags?.includes("webgl");
|
|
55796
|
-
const handleEnter =
|
|
55993
|
+
const handleEnter = useCallback117(() => {
|
|
55797
55994
|
hoverTimer.current = setTimeout(() => {
|
|
55798
55995
|
setHovered(true);
|
|
55799
55996
|
onPreview?.({ videoUrl, posterUrl, title });
|
|
55800
55997
|
}, 300);
|
|
55801
55998
|
}, [onPreview, videoUrl, posterUrl, title]);
|
|
55802
|
-
const handleLeave =
|
|
55999
|
+
const handleLeave = useCallback117(() => {
|
|
55803
56000
|
if (hoverTimer.current) {
|
|
55804
56001
|
clearTimeout(hoverTimer.current);
|
|
55805
56002
|
hoverTimer.current = null;
|
|
@@ -55812,7 +56009,7 @@ function BlockCard({
|
|
|
55812
56009
|
if (hoverTimer.current) clearTimeout(hoverTimer.current);
|
|
55813
56010
|
};
|
|
55814
56011
|
}, []);
|
|
55815
|
-
const handleAdd =
|
|
56012
|
+
const handleAdd = useCallback117(
|
|
55816
56013
|
(e) => {
|
|
55817
56014
|
e.stopPropagation();
|
|
55818
56015
|
if (adding || !onAdd) return;
|
|
@@ -55823,7 +56020,7 @@ function BlockCard({
|
|
|
55823
56020
|
[onAdd, adding]
|
|
55824
56021
|
);
|
|
55825
56022
|
const { activeCompPath, compositionDimensions } = useStudioShellContext();
|
|
55826
|
-
const handleShowPrompt =
|
|
56023
|
+
const handleShowPrompt = useCallback117(
|
|
55827
56024
|
(e) => {
|
|
55828
56025
|
e.stopPropagation();
|
|
55829
56026
|
const state = usePlayerStore.getState();
|
|
@@ -55968,11 +56165,11 @@ function PromptPreviewModal({
|
|
|
55968
56165
|
}) {
|
|
55969
56166
|
const [value, setValue] = useState95(prompt);
|
|
55970
56167
|
const [copied, setCopied] = useState95(false);
|
|
55971
|
-
const textareaRef =
|
|
56168
|
+
const textareaRef = useRef108(null);
|
|
55972
56169
|
useEffect83(() => {
|
|
55973
56170
|
requestAnimationFrame(() => textareaRef.current?.focus());
|
|
55974
56171
|
}, []);
|
|
55975
|
-
const handleCopy =
|
|
56172
|
+
const handleCopy = useCallback117(() => {
|
|
55976
56173
|
navigator.clipboard.writeText(value);
|
|
55977
56174
|
setCopied(true);
|
|
55978
56175
|
setTimeout(() => setCopied(false), 1500);
|
|
@@ -56096,14 +56293,14 @@ var LeftSidebar = memo40(
|
|
|
56096
56293
|
onAddAssetToTimeline
|
|
56097
56294
|
}, ref) {
|
|
56098
56295
|
const [tab, setTab] = useState96(getPersistedTab);
|
|
56099
|
-
const tabRef =
|
|
56296
|
+
const tabRef = useRef109(tab);
|
|
56100
56297
|
tabRef.current = tab;
|
|
56101
|
-
const selectTab =
|
|
56298
|
+
const selectTab = useCallback118((t) => {
|
|
56102
56299
|
setTab(t);
|
|
56103
56300
|
localStorage.setItem(STORAGE_KEY, t);
|
|
56104
56301
|
trackStudioEvent("tab_switch", { panel: "left_sidebar", tab: t });
|
|
56105
56302
|
}, []);
|
|
56106
|
-
const getTab =
|
|
56303
|
+
const getTab = useCallback118(() => tabRef.current, []);
|
|
56107
56304
|
useImperativeHandle(ref, () => ({ selectTab, getTab }), [selectTab, getTab]);
|
|
56108
56305
|
return /* @__PURE__ */ jsx126(
|
|
56109
56306
|
"div",
|
|
@@ -56444,7 +56641,7 @@ function StudioLeftSidebar({
|
|
|
56444
56641
|
handleImportFiles,
|
|
56445
56642
|
handleContentChange
|
|
56446
56643
|
} = useFileManagerContext();
|
|
56447
|
-
const handleRenderComposition =
|
|
56644
|
+
const handleRenderComposition = useCallback119(
|
|
56448
56645
|
async (comp) => {
|
|
56449
56646
|
await waitForPendingDomEditSaves();
|
|
56450
56647
|
const { format, quality, fps } = getPersistedRenderSettings();
|
|
@@ -56564,13 +56761,13 @@ function StudioLeftSidebar({
|
|
|
56564
56761
|
}
|
|
56565
56762
|
|
|
56566
56763
|
// src/components/StudioRightPanel.tsx
|
|
56567
|
-
import { useCallback as
|
|
56764
|
+
import { useCallback as useCallback135, useEffect as useEffect90, useRef as useRef115 } from "react";
|
|
56568
56765
|
|
|
56569
56766
|
// src/components/editor/LayersPanel.tsx
|
|
56570
|
-
import { memo as memo41, useState as useState99, useCallback as
|
|
56767
|
+
import { memo as memo41, useState as useState99, useCallback as useCallback121, useEffect as useEffect84, useRef as useRef111 } from "react";
|
|
56571
56768
|
|
|
56572
56769
|
// src/components/editor/useLayerDrag.ts
|
|
56573
|
-
import { useRef as
|
|
56770
|
+
import { useRef as useRef110, useState as useState98, useCallback as useCallback120 } from "react";
|
|
56574
56771
|
var DRAG_THRESHOLD_PX3 = 4;
|
|
56575
56772
|
function isLayerDraggable(layer) {
|
|
56576
56773
|
if (!(layer.selector || layer.id)) return false;
|
|
@@ -56637,10 +56834,10 @@ function useLayerDrag({
|
|
|
56637
56834
|
onReorder,
|
|
56638
56835
|
onSingleSibling
|
|
56639
56836
|
}) {
|
|
56640
|
-
const dragRef =
|
|
56837
|
+
const dragRef = useRef110(null);
|
|
56641
56838
|
const [dragKey, setDragKey] = useState98(null);
|
|
56642
56839
|
const [insertionLineY, setInsertionLineY] = useState98(null);
|
|
56643
|
-
const handleRowPointerDown =
|
|
56840
|
+
const handleRowPointerDown = useCallback120(
|
|
56644
56841
|
(layerIndex, e) => {
|
|
56645
56842
|
if (e.button !== 0) return;
|
|
56646
56843
|
const layer = visibleLayers[layerIndex];
|
|
@@ -56667,7 +56864,7 @@ function useLayerDrag({
|
|
|
56667
56864
|
},
|
|
56668
56865
|
[visibleLayers, scrollContainerRef, onSingleSibling]
|
|
56669
56866
|
);
|
|
56670
|
-
const handleContainerPointerMove =
|
|
56867
|
+
const handleContainerPointerMove = useCallback120(
|
|
56671
56868
|
(e) => {
|
|
56672
56869
|
const drag = dragRef.current;
|
|
56673
56870
|
if (!drag) return;
|
|
@@ -56693,7 +56890,7 @@ function useLayerDrag({
|
|
|
56693
56890
|
},
|
|
56694
56891
|
[visibleLayers, scrollContainerRef]
|
|
56695
56892
|
);
|
|
56696
|
-
const handleContainerPointerUp =
|
|
56893
|
+
const handleContainerPointerUp = useCallback120(() => {
|
|
56697
56894
|
const drag = dragRef.current;
|
|
56698
56895
|
dragRef.current = null;
|
|
56699
56896
|
setDragKey(null);
|
|
@@ -56842,15 +57039,15 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56842
57039
|
} = useDomEditContext();
|
|
56843
57040
|
const [layers, setLayers] = useState99([]);
|
|
56844
57041
|
const [collapsed, setCollapsed] = useState99({});
|
|
56845
|
-
const prevDocVersionRef =
|
|
56846
|
-
const scrollContainerRef =
|
|
57042
|
+
const prevDocVersionRef = useRef111(0);
|
|
57043
|
+
const scrollContainerRef = useRef111(null);
|
|
56847
57044
|
const mirrorLayerReorderToTimeline = useLayerReorderTimelineMirror();
|
|
56848
57045
|
const { scheduleReveal } = useLayerRevealOverride({
|
|
56849
57046
|
isPlaying,
|
|
56850
57047
|
selectedElement: domEditSelection?.element ?? null
|
|
56851
57048
|
});
|
|
56852
57049
|
const isMasterView = !activeCompPath || activeCompPath === "index.html";
|
|
56853
|
-
const collectLayers =
|
|
57050
|
+
const collectLayers = useCallback121(() => {
|
|
56854
57051
|
const iframe = previewIframeRef.current;
|
|
56855
57052
|
if (!iframe) return;
|
|
56856
57053
|
let doc = null;
|
|
@@ -56897,7 +57094,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56897
57094
|
throttle.cancel();
|
|
56898
57095
|
};
|
|
56899
57096
|
}, [collectLayers]);
|
|
56900
|
-
const resolveSelection =
|
|
57097
|
+
const resolveSelection = useCallback121(
|
|
56901
57098
|
(layer) => {
|
|
56902
57099
|
let el = layer.element;
|
|
56903
57100
|
if (!el.isConnected) {
|
|
@@ -56917,7 +57114,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56917
57114
|
},
|
|
56918
57115
|
[activeCompPath, isMasterView, previewIframeRef, activeGroupElement]
|
|
56919
57116
|
);
|
|
56920
|
-
const seekToLayer =
|
|
57117
|
+
const seekToLayer = useCallback121(
|
|
56921
57118
|
async (layer) => {
|
|
56922
57119
|
const selection = await resolveSelection(layer);
|
|
56923
57120
|
if (!selection) return;
|
|
@@ -56946,7 +57143,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56946
57143
|
},
|
|
56947
57144
|
[currentTime, resolveSelection, timelineElements]
|
|
56948
57145
|
);
|
|
56949
|
-
const handleSelectLayer =
|
|
57146
|
+
const handleSelectLayer = useCallback121(
|
|
56950
57147
|
async (layer) => {
|
|
56951
57148
|
const selection = await resolveSelection(layer);
|
|
56952
57149
|
if (!selection) return;
|
|
@@ -56956,7 +57153,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56956
57153
|
},
|
|
56957
57154
|
[resolveSelection, applyDomSelection, seekToLayer, scheduleReveal]
|
|
56958
57155
|
);
|
|
56959
|
-
const handleLayerDoubleClick =
|
|
57156
|
+
const handleLayerDoubleClick = useCallback121(
|
|
56960
57157
|
async (layer) => {
|
|
56961
57158
|
const selection = await resolveSelection(layer);
|
|
56962
57159
|
if (selection?.element.hasAttribute("data-hf-group")) {
|
|
@@ -56967,7 +57164,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56967
57164
|
},
|
|
56968
57165
|
[resolveSelection, setActiveGroupElement, handleSelectLayer]
|
|
56969
57166
|
);
|
|
56970
|
-
const handleLayerHover =
|
|
57167
|
+
const handleLayerHover = useCallback121(
|
|
56971
57168
|
async (layer) => {
|
|
56972
57169
|
if (!layer) {
|
|
56973
57170
|
updateDomEditHoverSelection(null);
|
|
@@ -56978,11 +57175,11 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
56978
57175
|
},
|
|
56979
57176
|
[resolveSelection, updateDomEditHoverSelection]
|
|
56980
57177
|
);
|
|
56981
|
-
const toggleCollapse =
|
|
57178
|
+
const toggleCollapse = useCallback121((key, e) => {
|
|
56982
57179
|
e.stopPropagation();
|
|
56983
57180
|
setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
56984
57181
|
}, []);
|
|
56985
|
-
const handleReorder =
|
|
57182
|
+
const handleReorder = useCallback121(
|
|
56986
57183
|
(event) => {
|
|
56987
57184
|
const { siblingLayers, fromIndex, toIndex } = event;
|
|
56988
57185
|
const reordered = [...siblingLayers];
|
|
@@ -57045,7 +57242,7 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
57045
57242
|
);
|
|
57046
57243
|
const selectedKey = domEditSelection ? getDomEditLayerKey(domEditSelection) : null;
|
|
57047
57244
|
const visibleLayers = getVisibleLayers(layers, collapsed);
|
|
57048
|
-
const handleSingleSibling =
|
|
57245
|
+
const handleSingleSibling = useCallback121(() => {
|
|
57049
57246
|
showToast("Only one layer at this level", "info");
|
|
57050
57247
|
}, [showToast]);
|
|
57051
57248
|
const {
|
|
@@ -57174,10 +57371,10 @@ var LayersPanel = memo41(function LayersPanel2() {
|
|
|
57174
57371
|
});
|
|
57175
57372
|
|
|
57176
57373
|
// src/captions/components/CaptionPropertyPanel.tsx
|
|
57177
|
-
import { memo as memo43, useCallback as
|
|
57374
|
+
import { memo as memo43, useCallback as useCallback123, useState as useState100 } from "react";
|
|
57178
57375
|
|
|
57179
57376
|
// src/captions/components/CaptionAnimationPanel.tsx
|
|
57180
|
-
import { memo as memo42, useCallback as
|
|
57377
|
+
import { memo as memo42, useCallback as useCallback122 } from "react";
|
|
57181
57378
|
|
|
57182
57379
|
// src/captions/components/shared.tsx
|
|
57183
57380
|
import { jsx as jsx130, jsxs as jsxs113 } from "react/jsx-runtime";
|
|
@@ -57340,25 +57537,25 @@ var CaptionAnimationPanel = memo42(function CaptionAnimationPanel2() {
|
|
|
57340
57537
|
}
|
|
57341
57538
|
const group = resolvedGroupId ? model?.groups.get(resolvedGroupId) : void 0;
|
|
57342
57539
|
const animation = group?.animation;
|
|
57343
|
-
const handleEntranceChange =
|
|
57540
|
+
const handleEntranceChange = useCallback122(
|
|
57344
57541
|
(update) => {
|
|
57345
57542
|
if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "entrance", update);
|
|
57346
57543
|
},
|
|
57347
57544
|
[resolvedGroupId, updateGroupAnimation]
|
|
57348
57545
|
);
|
|
57349
|
-
const handleHighlightChange =
|
|
57546
|
+
const handleHighlightChange = useCallback122(
|
|
57350
57547
|
(update) => {
|
|
57351
57548
|
if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "highlight", update);
|
|
57352
57549
|
},
|
|
57353
57550
|
[resolvedGroupId, updateGroupAnimation]
|
|
57354
57551
|
);
|
|
57355
|
-
const handleExitChange =
|
|
57552
|
+
const handleExitChange = useCallback122(
|
|
57356
57553
|
(update) => {
|
|
57357
57554
|
if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "exit", update);
|
|
57358
57555
|
},
|
|
57359
57556
|
[resolvedGroupId, updateGroupAnimation]
|
|
57360
57557
|
);
|
|
57361
|
-
const handleApplyToAll =
|
|
57558
|
+
const handleApplyToAll = useCallback122(() => {
|
|
57362
57559
|
if (animation) applyAnimationToAll(animation);
|
|
57363
57560
|
}, [animation, applyAnimationToAll]);
|
|
57364
57561
|
if (!group || !resolvedGroupId || !animation) {
|
|
@@ -57436,7 +57633,7 @@ var CaptionPropertyPanel = memo43(function CaptionPropertyPanel2({
|
|
|
57436
57633
|
...groupStyle,
|
|
57437
57634
|
...segmentOverrides
|
|
57438
57635
|
};
|
|
57439
|
-
const applyToIframeDom =
|
|
57636
|
+
const applyToIframeDom = useCallback123(
|
|
57440
57637
|
(updates) => {
|
|
57441
57638
|
const iframe = iframeRef.current;
|
|
57442
57639
|
if (!iframe || !model) return;
|
|
@@ -57510,7 +57707,7 @@ var CaptionPropertyPanel = memo43(function CaptionPropertyPanel2({
|
|
|
57510
57707
|
},
|
|
57511
57708
|
[iframeRef, model, selectedSegmentIds]
|
|
57512
57709
|
);
|
|
57513
|
-
const handleStyleChange =
|
|
57710
|
+
const handleStyleChange = useCallback123(
|
|
57514
57711
|
(updates) => {
|
|
57515
57712
|
if (selectedGroupId) {
|
|
57516
57713
|
updateGroupStyle(selectedGroupId, updates);
|
|
@@ -57610,7 +57807,7 @@ var CaptionPropertyPanel = memo43(function CaptionPropertyPanel2({
|
|
|
57610
57807
|
});
|
|
57611
57808
|
|
|
57612
57809
|
// src/components/editor/BlockParamsPanel.tsx
|
|
57613
|
-
import { memo as memo44, useState as useState101, useCallback as
|
|
57810
|
+
import { memo as memo44, useState as useState101, useCallback as useCallback124 } from "react";
|
|
57614
57811
|
import { jsx as jsx133, jsxs as jsxs116 } from "react/jsx-runtime";
|
|
57615
57812
|
var BlockParamsPanel = memo44(function BlockParamsPanel2({
|
|
57616
57813
|
blockTitle,
|
|
@@ -57625,7 +57822,7 @@ var BlockParamsPanel = memo44(function BlockParamsPanel2({
|
|
|
57625
57822
|
}
|
|
57626
57823
|
return initial;
|
|
57627
57824
|
});
|
|
57628
|
-
const handleChange =
|
|
57825
|
+
const handleChange = useCallback124((key, value) => {
|
|
57629
57826
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
57630
57827
|
}, []);
|
|
57631
57828
|
return /* @__PURE__ */ jsxs116("div", { className: "flex flex-col h-full", children: [
|
|
@@ -57735,10 +57932,10 @@ function ParamControl({
|
|
|
57735
57932
|
}
|
|
57736
57933
|
|
|
57737
57934
|
// src/components/renders/RenderQueue.tsx
|
|
57738
|
-
import { memo as memo46, useState as useState103, useRef as
|
|
57935
|
+
import { memo as memo46, useState as useState103, useRef as useRef112, useEffect as useEffect85, useId as useId2 } from "react";
|
|
57739
57936
|
|
|
57740
57937
|
// src/components/renders/RenderQueueItem.tsx
|
|
57741
|
-
import { memo as memo45, useCallback as
|
|
57938
|
+
import { memo as memo45, useCallback as useCallback125, useState as useState102 } from "react";
|
|
57742
57939
|
import { Fragment as Fragment42, jsx as jsx134, jsxs as jsxs117 } from "react/jsx-runtime";
|
|
57743
57940
|
function formatDuration2(ms) {
|
|
57744
57941
|
if (ms < 1e3) return `${ms}ms`;
|
|
@@ -57761,10 +57958,10 @@ var RenderQueueItem = memo45(function RenderQueueItem2({
|
|
|
57761
57958
|
const [videoReady, setVideoReady] = useState102(false);
|
|
57762
57959
|
const [confirmingDelete, setConfirmingDelete] = useState102(false);
|
|
57763
57960
|
const fileSrc = `/api/projects/${projectId}/renders/file/${job.filename}`;
|
|
57764
|
-
const handleOpen =
|
|
57961
|
+
const handleOpen = useCallback125(() => {
|
|
57765
57962
|
window.open(fileSrc, "_blank");
|
|
57766
57963
|
}, [fileSrc]);
|
|
57767
|
-
const handleDownload =
|
|
57964
|
+
const handleDownload = useCallback125(
|
|
57768
57965
|
(e) => {
|
|
57769
57966
|
e.stopPropagation();
|
|
57770
57967
|
const a = document.createElement("a");
|
|
@@ -58025,7 +58222,7 @@ var FORMAT_INFO = {
|
|
|
58025
58222
|
};
|
|
58026
58223
|
function FormatInfoTooltip({ format }) {
|
|
58027
58224
|
const [open, setOpen] = useState103(false);
|
|
58028
|
-
const timeoutRef =
|
|
58225
|
+
const timeoutRef = useRef112(void 0);
|
|
58029
58226
|
const panelId = useId2();
|
|
58030
58227
|
const show = () => {
|
|
58031
58228
|
clearTimeout(timeoutRef.current);
|
|
@@ -58239,7 +58436,7 @@ var RenderQueue = memo46(function RenderQueue2({
|
|
|
58239
58436
|
onDismissActionError,
|
|
58240
58437
|
compositionDimensions
|
|
58241
58438
|
}) {
|
|
58242
|
-
const listRef =
|
|
58439
|
+
const listRef = useRef112(null);
|
|
58243
58440
|
useEffect85(() => {
|
|
58244
58441
|
if (listRef.current) {
|
|
58245
58442
|
listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" });
|
|
@@ -58348,11 +58545,11 @@ var RenderQueue = memo46(function RenderQueue2({
|
|
|
58348
58545
|
});
|
|
58349
58546
|
|
|
58350
58547
|
// src/components/panels/SlideshowPanel.tsx
|
|
58351
|
-
import { useState as useState105, useEffect as useEffect86, useCallback as
|
|
58548
|
+
import { useState as useState105, useEffect as useEffect86, useCallback as useCallback127, useRef as useRef113 } from "react";
|
|
58352
58549
|
import { parseSlideshowManifest } from "@hyperframes/core/slideshow";
|
|
58353
58550
|
|
|
58354
58551
|
// src/components/panels/SlideshowSubPanels.tsx
|
|
58355
|
-
import { useState as useState104, useCallback as
|
|
58552
|
+
import { useState as useState104, useCallback as useCallback126, useId as useId3 } from "react";
|
|
58356
58553
|
import { jsx as jsx136, jsxs as jsxs119 } from "react/jsx-runtime";
|
|
58357
58554
|
function SectionHeader({
|
|
58358
58555
|
children,
|
|
@@ -58537,7 +58734,7 @@ function BranchTree({
|
|
|
58537
58734
|
}) {
|
|
58538
58735
|
const [newLabel, setNewLabel] = useState104("");
|
|
58539
58736
|
const inputId = useId3();
|
|
58540
|
-
const handleCreate =
|
|
58737
|
+
const handleCreate = useCallback126(() => {
|
|
58541
58738
|
const label = newLabel.trim();
|
|
58542
58739
|
if (!label) return;
|
|
58543
58740
|
onCreateSequence(label);
|
|
@@ -58598,7 +58795,7 @@ function BranchItem({
|
|
|
58598
58795
|
}) {
|
|
58599
58796
|
const [editing, setEditing] = useState104(false);
|
|
58600
58797
|
const [draft, setDraft] = useState104(seq.label);
|
|
58601
|
-
const commitRename =
|
|
58798
|
+
const commitRename = useCallback126(() => {
|
|
58602
58799
|
const label = draft.trim();
|
|
58603
58800
|
if (label && label !== seq.label) onRename(seq.id, label);
|
|
58604
58801
|
setEditing(false);
|
|
@@ -58696,7 +58893,7 @@ function HotspotTool({
|
|
|
58696
58893
|
const selectedElementId = domEditSelection?.element?.id ?? null;
|
|
58697
58894
|
const selectedHfId = domEditSelection?.hfId ?? null;
|
|
58698
58895
|
const elementKey2 = selectedElementId || selectedHfId;
|
|
58699
|
-
const handleMakeHotspot =
|
|
58896
|
+
const handleMakeHotspot = useCallback126(() => {
|
|
58700
58897
|
if (!selectedSceneId || !targetSequenceId || !elementKey2) return;
|
|
58701
58898
|
const id = `hotspot-${elementKey2}-${generateId()}`;
|
|
58702
58899
|
const label = hotspotLabel.trim() || elementKey2;
|
|
@@ -58985,8 +59182,8 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
58985
59182
|
);
|
|
58986
59183
|
const currentTime = usePlayerStore((s) => s.currentTime);
|
|
58987
59184
|
const { domEditSelection } = useDomEditSelectionContext();
|
|
58988
|
-
const manifestRef =
|
|
58989
|
-
const notesCtrlRef =
|
|
59185
|
+
const manifestRef = useRef113(manifest);
|
|
59186
|
+
const notesCtrlRef = useRef113(makeSlideshowNotesController());
|
|
58990
59187
|
useEffect86(() => {
|
|
58991
59188
|
if (!compHtml) {
|
|
58992
59189
|
notesCtrlRef.current.flush();
|
|
@@ -59000,7 +59197,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59000
59197
|
manifestRef.current = parsed;
|
|
59001
59198
|
setSelectedSequenceId(null);
|
|
59002
59199
|
}, [compHtml]);
|
|
59003
|
-
const applyManifest =
|
|
59200
|
+
const applyManifest = useCallback127(
|
|
59004
59201
|
async (next) => {
|
|
59005
59202
|
const merged = notesCtrlRef.current.mergeIntoDiscrete(next);
|
|
59006
59203
|
setManifest(merged);
|
|
@@ -59013,7 +59210,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59013
59210
|
},
|
|
59014
59211
|
[onPersist]
|
|
59015
59212
|
);
|
|
59016
|
-
const applyNotesManifest =
|
|
59213
|
+
const applyNotesManifest = useCallback127(
|
|
59017
59214
|
(next) => {
|
|
59018
59215
|
setManifest(next);
|
|
59019
59216
|
manifestRef.current = next;
|
|
@@ -59027,7 +59224,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59027
59224
|
ctrl.flush();
|
|
59028
59225
|
};
|
|
59029
59226
|
}, []);
|
|
59030
|
-
const toggleSection =
|
|
59227
|
+
const toggleSection = useCallback127((key) => {
|
|
59031
59228
|
setExpandedSections((prev) => {
|
|
59032
59229
|
const next = new Set(prev);
|
|
59033
59230
|
if (next.has(key)) {
|
|
@@ -59041,25 +59238,25 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59041
59238
|
const activeSlides = selectedSequenceId ? (manifest.slideSequences ?? []).find((s) => s.id === selectedSequenceId)?.slides ?? [] : manifest.slides;
|
|
59042
59239
|
const selectedSlide = activeSlides.find((s) => s.sceneId === selectedSceneId);
|
|
59043
59240
|
const sequences = manifest.slideSequences ?? [];
|
|
59044
|
-
const handleSelectBranchSlide =
|
|
59241
|
+
const handleSelectBranchSlide = useCallback127((sequenceId, sceneId) => {
|
|
59045
59242
|
setSelectedSceneId(sceneId);
|
|
59046
59243
|
setSelectedSequenceId(sequenceId);
|
|
59047
59244
|
}, []);
|
|
59048
|
-
const handleToggleSlide =
|
|
59245
|
+
const handleToggleSlide = useCallback127(
|
|
59049
59246
|
(sceneId) => {
|
|
59050
59247
|
applyManifest(toggleMainLineSlide(manifestRef.current, sceneId)).catch(() => {
|
|
59051
59248
|
});
|
|
59052
59249
|
},
|
|
59053
59250
|
[applyManifest]
|
|
59054
59251
|
);
|
|
59055
|
-
const handleReorder =
|
|
59252
|
+
const handleReorder = useCallback127(
|
|
59056
59253
|
(sceneId, dir) => {
|
|
59057
59254
|
applyManifest(reorderMainLineSlide(manifestRef.current, sceneId, dir)).catch(() => {
|
|
59058
59255
|
});
|
|
59059
59256
|
},
|
|
59060
59257
|
[applyManifest]
|
|
59061
59258
|
);
|
|
59062
|
-
const handleSetNotes =
|
|
59259
|
+
const handleSetNotes = useCallback127(
|
|
59063
59260
|
(notes) => {
|
|
59064
59261
|
if (!selectedSceneId) return;
|
|
59065
59262
|
applyNotesManifest(
|
|
@@ -59068,7 +59265,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59068
59265
|
},
|
|
59069
59266
|
[selectedSceneId, selectedSequenceId, applyNotesManifest]
|
|
59070
59267
|
);
|
|
59071
|
-
const handleMarkFragment =
|
|
59268
|
+
const handleMarkFragment = useCallback127(() => {
|
|
59072
59269
|
if (!selectedSceneId) return;
|
|
59073
59270
|
applyManifest(
|
|
59074
59271
|
addFragment(
|
|
@@ -59080,7 +59277,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59080
59277
|
).catch(() => {
|
|
59081
59278
|
});
|
|
59082
59279
|
}, [selectedSceneId, selectedSequenceId, currentTime, applyManifest]);
|
|
59083
|
-
const handleRemoveFragment =
|
|
59280
|
+
const handleRemoveFragment = useCallback127(
|
|
59084
59281
|
(time) => {
|
|
59085
59282
|
if (!selectedSceneId) return;
|
|
59086
59283
|
applyManifest(
|
|
@@ -59090,7 +59287,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59090
59287
|
},
|
|
59091
59288
|
[selectedSceneId, selectedSequenceId, applyManifest]
|
|
59092
59289
|
);
|
|
59093
|
-
const handleCreateSequence =
|
|
59290
|
+
const handleCreateSequence = useCallback127(
|
|
59094
59291
|
(label) => {
|
|
59095
59292
|
const id = `seq-${generateId()}`;
|
|
59096
59293
|
applyManifest(createSequence(manifestRef.current, id, label)).catch(() => {
|
|
@@ -59098,14 +59295,14 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59098
59295
|
},
|
|
59099
59296
|
[applyManifest]
|
|
59100
59297
|
);
|
|
59101
|
-
const handleRenameSequence =
|
|
59298
|
+
const handleRenameSequence = useCallback127(
|
|
59102
59299
|
(id, label) => {
|
|
59103
59300
|
applyManifest(renameSequence(manifestRef.current, id, label)).catch(() => {
|
|
59104
59301
|
});
|
|
59105
59302
|
},
|
|
59106
59303
|
[applyManifest]
|
|
59107
59304
|
);
|
|
59108
|
-
const handleDeleteSequence =
|
|
59305
|
+
const handleDeleteSequence = useCallback127(
|
|
59109
59306
|
(id) => {
|
|
59110
59307
|
const seq = (manifestRef.current.slideSequences ?? []).find((s) => s.id === id);
|
|
59111
59308
|
const count = seq?.slides.length ?? 0;
|
|
@@ -59119,7 +59316,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59119
59316
|
},
|
|
59120
59317
|
[applyManifest]
|
|
59121
59318
|
);
|
|
59122
|
-
const handleAssign =
|
|
59319
|
+
const handleAssign = useCallback127(
|
|
59123
59320
|
(sequenceId, sceneId, assign) => {
|
|
59124
59321
|
applyManifest(assignToBranch(manifestRef.current, sequenceId, sceneId, assign)).catch(
|
|
59125
59322
|
() => {
|
|
@@ -59128,7 +59325,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59128
59325
|
},
|
|
59129
59326
|
[applyManifest]
|
|
59130
59327
|
);
|
|
59131
|
-
const handleAddHotspot =
|
|
59328
|
+
const handleAddHotspot = useCallback127(
|
|
59132
59329
|
(sceneId, hotspot) => {
|
|
59133
59330
|
applyManifest(
|
|
59134
59331
|
addHotspot(manifestRef.current, sceneId, hotspot, selectedSequenceId ?? void 0)
|
|
@@ -59137,7 +59334,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59137
59334
|
},
|
|
59138
59335
|
[selectedSequenceId, applyManifest]
|
|
59139
59336
|
);
|
|
59140
|
-
const handleRemoveHotspot =
|
|
59337
|
+
const handleRemoveHotspot = useCallback127(
|
|
59141
59338
|
(sceneId, hotspotId) => {
|
|
59142
59339
|
applyManifest(
|
|
59143
59340
|
removeHotspot(manifestRef.current, sceneId, hotspotId, selectedSequenceId ?? void 0)
|
|
@@ -59241,10 +59438,10 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
59241
59438
|
}
|
|
59242
59439
|
|
|
59243
59440
|
// src/components/panels/VariablesPanel.tsx
|
|
59244
|
-
import { memo as memo47, useCallback as
|
|
59441
|
+
import { memo as memo47, useCallback as useCallback131, useEffect as useEffect88, useMemo as useMemo45, useState as useState109 } from "react";
|
|
59245
59442
|
|
|
59246
59443
|
// src/hooks/useVariablesPersist.ts
|
|
59247
|
-
import { useCallback as
|
|
59444
|
+
import { useCallback as useCallback128 } from "react";
|
|
59248
59445
|
function useVariablesPersist({
|
|
59249
59446
|
sdkSession,
|
|
59250
59447
|
activeCompPath,
|
|
@@ -59255,7 +59452,7 @@ function useVariablesPersist({
|
|
|
59255
59452
|
domEditSaveTimestampRef,
|
|
59256
59453
|
publishSdkSession
|
|
59257
59454
|
}) {
|
|
59258
|
-
return
|
|
59455
|
+
return useCallback128(
|
|
59259
59456
|
async (label, mutate) => {
|
|
59260
59457
|
if (!sdkSession) return false;
|
|
59261
59458
|
const path = activeCompPath ?? "index.html";
|
|
@@ -59292,10 +59489,10 @@ function useVariablesPersist({
|
|
|
59292
59489
|
}
|
|
59293
59490
|
|
|
59294
59491
|
// src/components/panels/VariablesOtherCompositions.tsx
|
|
59295
|
-
import { useCallback as
|
|
59492
|
+
import { useCallback as useCallback130, useState as useState108 } from "react";
|
|
59296
59493
|
|
|
59297
59494
|
// src/hooks/useProjectCompositionVariables.ts
|
|
59298
|
-
import { useCallback as
|
|
59495
|
+
import { useCallback as useCallback129, useEffect as useEffect87, useState as useState106 } from "react";
|
|
59299
59496
|
import { openComposition as openComposition4 } from "@hyperframes/sdk";
|
|
59300
59497
|
async function readGroup(path, readProjectFile) {
|
|
59301
59498
|
let content;
|
|
@@ -59338,7 +59535,7 @@ function useProjectCompositionVariables(fileTree, excludePath, readProjectFile,
|
|
|
59338
59535
|
}
|
|
59339
59536
|
function useEditVariablesInFile(deps) {
|
|
59340
59537
|
const { readProjectFile, writeProjectFile, recordEdit, reloadPreview, domEditSaveTimestampRef } = deps;
|
|
59341
|
-
return
|
|
59538
|
+
return useCallback129(
|
|
59342
59539
|
async (path, label, mutate) => {
|
|
59343
59540
|
const originalContent = await readProjectFile(path);
|
|
59344
59541
|
await persistSdkSerialize(
|
|
@@ -59718,7 +59915,7 @@ function VariablesOtherCompositions({
|
|
|
59718
59915
|
domEditSaveTimestampRef
|
|
59719
59916
|
});
|
|
59720
59917
|
const [editingKey, setEditingKey] = useState108(null);
|
|
59721
|
-
const onSave =
|
|
59918
|
+
const onSave = useCallback130(
|
|
59722
59919
|
(path, decl) => {
|
|
59723
59920
|
setEditingKey(null);
|
|
59724
59921
|
void editInFile(
|
|
@@ -59729,7 +59926,7 @@ function VariablesOtherCompositions({
|
|
|
59729
59926
|
},
|
|
59730
59927
|
[editInFile]
|
|
59731
59928
|
);
|
|
59732
|
-
const onRemove =
|
|
59929
|
+
const onRemove = useCallback130(
|
|
59733
59930
|
(path, id) => {
|
|
59734
59931
|
void editInFile(
|
|
59735
59932
|
path,
|
|
@@ -59945,27 +60142,27 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
59945
60142
|
domEditSaveTimestampRef,
|
|
59946
60143
|
publishSdkSession
|
|
59947
60144
|
});
|
|
59948
|
-
const declarations =
|
|
60145
|
+
const declarations = useMemo45(
|
|
59949
60146
|
() => sdkSession?.getVariableDeclarations() ?? [],
|
|
59950
60147
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59951
60148
|
[sdkSession, refreshKey, revision]
|
|
59952
60149
|
);
|
|
59953
|
-
const usage =
|
|
60150
|
+
const usage = useMemo45(
|
|
59954
60151
|
() => sdkSession?.getVariableUsage() ?? null,
|
|
59955
60152
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59956
60153
|
[sdkSession, refreshKey, revision]
|
|
59957
60154
|
);
|
|
59958
|
-
const issues =
|
|
60155
|
+
const issues = useMemo45(
|
|
59959
60156
|
() => previewValues && sdkSession ? sdkSession.validateVariableValues(previewValues) : [],
|
|
59960
60157
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59961
60158
|
[sdkSession, previewValues, refreshKey, revision]
|
|
59962
60159
|
);
|
|
59963
|
-
const effectiveValues =
|
|
60160
|
+
const effectiveValues = useMemo45(
|
|
59964
60161
|
() => sdkSession?.getVariableValues(previewValues ?? void 0) ?? {},
|
|
59965
60162
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59966
60163
|
[sdkSession, previewValues, refreshKey, revision]
|
|
59967
60164
|
);
|
|
59968
|
-
const copyToClipboard =
|
|
60165
|
+
const copyToClipboard = useCallback131(
|
|
59969
60166
|
(text, what) => {
|
|
59970
60167
|
void copyTextToClipboard(text).then(
|
|
59971
60168
|
(ok) => showToast(
|
|
@@ -59976,7 +60173,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
59976
60173
|
},
|
|
59977
60174
|
[showToast]
|
|
59978
60175
|
);
|
|
59979
|
-
const dropPreviewOverride =
|
|
60176
|
+
const dropPreviewOverride = useCallback131(
|
|
59980
60177
|
(id) => {
|
|
59981
60178
|
if (previewValues && id in previewValues) {
|
|
59982
60179
|
const next = { ...previewValues };
|
|
@@ -59986,7 +60183,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
59986
60183
|
},
|
|
59987
60184
|
[previewValues, setPreviewValues]
|
|
59988
60185
|
);
|
|
59989
|
-
const commitPreviewValue =
|
|
60186
|
+
const commitPreviewValue = useCallback131(
|
|
59990
60187
|
(id, value, declDefault) => {
|
|
59991
60188
|
const next = { ...previewValues ?? {} };
|
|
59992
60189
|
if (JSON.stringify(value) === JSON.stringify(declDefault)) {
|
|
@@ -59999,7 +60196,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
59999
60196
|
},
|
|
60000
60197
|
[previewValues, setPreviewValues, reloadPreview]
|
|
60001
60198
|
);
|
|
60002
|
-
const runSchemaEdit =
|
|
60199
|
+
const runSchemaEdit = useCallback131(
|
|
60003
60200
|
async (label, mutate) => {
|
|
60004
60201
|
try {
|
|
60005
60202
|
const changed = await persistVariables(label, mutate);
|
|
@@ -60013,7 +60210,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
60013
60210
|
},
|
|
60014
60211
|
[persistVariables, showToast]
|
|
60015
60212
|
);
|
|
60016
|
-
const handleAdd =
|
|
60213
|
+
const handleAdd = useCallback131(
|
|
60017
60214
|
(decl) => {
|
|
60018
60215
|
if (!sdkSession) return;
|
|
60019
60216
|
const check = sdkSession.can({ type: "declareVariable", declaration: decl });
|
|
@@ -60026,7 +60223,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
60026
60223
|
},
|
|
60027
60224
|
[sdkSession, runSchemaEdit, showToast]
|
|
60028
60225
|
);
|
|
60029
|
-
const handleUpdate =
|
|
60226
|
+
const handleUpdate = useCallback131(
|
|
60030
60227
|
(decl) => {
|
|
60031
60228
|
if (!sdkSession) return;
|
|
60032
60229
|
const check = sdkSession.can({
|
|
@@ -60046,7 +60243,7 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
60046
60243
|
},
|
|
60047
60244
|
[sdkSession, runSchemaEdit, showToast]
|
|
60048
60245
|
);
|
|
60049
|
-
const handleRemove =
|
|
60246
|
+
const handleRemove = useCallback131(
|
|
60050
60247
|
(id) => {
|
|
60051
60248
|
if (!sdkSession) return;
|
|
60052
60249
|
const check = sdkSession.can({ type: "removeVariableDeclaration", id });
|
|
@@ -60062,18 +60259,18 @@ var VariablesPanel = memo47(function VariablesPanel2({
|
|
|
60062
60259
|
},
|
|
60063
60260
|
[sdkSession, runSchemaEdit, dropPreviewOverride, showToast]
|
|
60064
60261
|
);
|
|
60065
|
-
const handleSetDefault =
|
|
60262
|
+
const handleSetDefault = useCallback131(
|
|
60066
60263
|
(id, value) => {
|
|
60067
60264
|
void runSchemaEdit(`Set default for "${id}"`, (s) => s.setVariableValue(id, value));
|
|
60068
60265
|
dropPreviewOverride(id);
|
|
60069
60266
|
},
|
|
60070
60267
|
[runSchemaEdit, dropPreviewOverride]
|
|
60071
60268
|
);
|
|
60072
|
-
const resetPreview =
|
|
60269
|
+
const resetPreview = useCallback131(() => {
|
|
60073
60270
|
setPreviewValues(null);
|
|
60074
60271
|
reloadPreview();
|
|
60075
60272
|
}, [setPreviewValues, reloadPreview]);
|
|
60076
|
-
const handleBind =
|
|
60273
|
+
const handleBind = useCallback131(
|
|
60077
60274
|
// Guard chain (session, selection, type-compat) — one branch per guard.
|
|
60078
60275
|
// fallow-ignore-next-line complexity
|
|
60079
60276
|
(action, id) => {
|
|
@@ -60206,7 +60403,7 @@ function PanelTabButton({
|
|
|
60206
60403
|
}
|
|
60207
60404
|
|
|
60208
60405
|
// src/hooks/useSlideshowPersist.ts
|
|
60209
|
-
import { useCallback as
|
|
60406
|
+
import { useCallback as useCallback132 } from "react";
|
|
60210
60407
|
|
|
60211
60408
|
// src/utils/setSlideshowManifest.ts
|
|
60212
60409
|
import {
|
|
@@ -60264,7 +60461,7 @@ function useSlideshowPersist({
|
|
|
60264
60461
|
publishSdkSession,
|
|
60265
60462
|
coalesceKey
|
|
60266
60463
|
}) {
|
|
60267
|
-
return
|
|
60464
|
+
return useCallback132(
|
|
60268
60465
|
async (manifest) => {
|
|
60269
60466
|
if (!sdkSession) return;
|
|
60270
60467
|
const path = activeCompPath ?? "index.html";
|
|
@@ -60298,26 +60495,65 @@ function useSlideshowPersist({
|
|
|
60298
60495
|
);
|
|
60299
60496
|
}
|
|
60300
60497
|
|
|
60498
|
+
// src/hooks/useSlideshowTabState.ts
|
|
60499
|
+
import { useEffect as useEffect89, useMemo as useMemo46 } from "react";
|
|
60500
|
+
import { SLIDESHOW_ISLAND_TYPE as SLIDESHOW_ISLAND_TYPE2, slideshowIslandRegex as slideshowIslandRegex2 } from "@hyperframes/core/slideshow";
|
|
60501
|
+
function useSlideshowTabState(params) {
|
|
60502
|
+
const { editingFileContent, previewIframeRef, refreshKey, rightPanelTab, setRightPanelTab } = params;
|
|
60503
|
+
const isSlideshowComposition = useMemo46(() => {
|
|
60504
|
+
if (!editingFileContent || !editingFileContent.includes(SLIDESHOW_ISLAND_TYPE2)) return false;
|
|
60505
|
+
return slideshowIslandRegex2("i").test(editingFileContent);
|
|
60506
|
+
}, [editingFileContent]);
|
|
60507
|
+
const slideshowScenes = useMemo46(() => {
|
|
60508
|
+
try {
|
|
60509
|
+
const win = previewIframeRef.current?.contentWindow;
|
|
60510
|
+
return (win?.__clipManifest?.scenes ?? []).map((s) => ({
|
|
60511
|
+
id: s.id,
|
|
60512
|
+
label: s.label,
|
|
60513
|
+
start: s.start,
|
|
60514
|
+
duration: s.duration
|
|
60515
|
+
}));
|
|
60516
|
+
} catch {
|
|
60517
|
+
return [];
|
|
60518
|
+
}
|
|
60519
|
+
}, [previewIframeRef, rightPanelTab, refreshKey]);
|
|
60520
|
+
useEffect89(() => {
|
|
60521
|
+
if (rightPanelTab === "slideshow" && !isSlideshowComposition) {
|
|
60522
|
+
setRightPanelTab("renders");
|
|
60523
|
+
}
|
|
60524
|
+
}, [rightPanelTab, isSlideshowComposition, setRightPanelTab]);
|
|
60525
|
+
return { isSlideshowComposition, slideshowScenes };
|
|
60526
|
+
}
|
|
60527
|
+
|
|
60301
60528
|
// src/components/DesignPanelPromoteProvider.tsx
|
|
60302
|
-
import { useCallback as
|
|
60529
|
+
import { useCallback as useCallback133 } from "react";
|
|
60303
60530
|
import { jsx as jsx143 } from "react/jsx-runtime";
|
|
60304
60531
|
function DesignPanelPromoteProvider({
|
|
60305
60532
|
selection,
|
|
60306
60533
|
projectId,
|
|
60307
60534
|
activeCompPath,
|
|
60308
60535
|
showToast,
|
|
60536
|
+
forceReloadSharedSdkSession,
|
|
60309
60537
|
children,
|
|
60310
60538
|
...persistDeps
|
|
60311
60539
|
}) {
|
|
60312
60540
|
const targetPath = selection?.sourceFile || activeCompPath || "index.html";
|
|
60313
60541
|
const handle = useSdkSession(projectId, targetPath, persistDeps.domEditSaveTimestampRef);
|
|
60314
|
-
const
|
|
60542
|
+
const rawPersist = useVariablesPersist({
|
|
60315
60543
|
...persistDeps,
|
|
60316
60544
|
sdkSession: handle.session,
|
|
60317
60545
|
publishSdkSession: handle.publish,
|
|
60318
60546
|
activeCompPath: targetPath
|
|
60319
60547
|
});
|
|
60320
|
-
const
|
|
60548
|
+
const persist2 = useCallback133(
|
|
60549
|
+
async (label, mutate) => {
|
|
60550
|
+
const committed = await rawPersist(label, mutate);
|
|
60551
|
+
if (committed) forceReloadSharedSdkSession?.();
|
|
60552
|
+
return committed;
|
|
60553
|
+
},
|
|
60554
|
+
[rawPersist, forceReloadSharedSdkSession]
|
|
60555
|
+
);
|
|
60556
|
+
const handlePersistError = useCallback133(
|
|
60321
60557
|
(error) => showToast(getStudioSaveErrorMessage(error), "error"),
|
|
60322
60558
|
[showToast]
|
|
60323
60559
|
);
|
|
@@ -60552,14 +60788,14 @@ async function applyColorGradingScopeUpdate({
|
|
|
60552
60788
|
}
|
|
60553
60789
|
|
|
60554
60790
|
// src/hooks/useInspectorSplitResize.ts
|
|
60555
|
-
import { useCallback as
|
|
60791
|
+
import { useCallback as useCallback134, useRef as useRef114, useState as useState110 } from "react";
|
|
60556
60792
|
var MIN_INSPECTOR_SPLIT_PERCENT = 20;
|
|
60557
60793
|
var MAX_INSPECTOR_SPLIT_PERCENT = 75;
|
|
60558
60794
|
function useInspectorSplitResize() {
|
|
60559
60795
|
const [layersPanePercent, setLayersPanePercent] = useState110(40);
|
|
60560
|
-
const splitContainerRef =
|
|
60561
|
-
const splitDragRef =
|
|
60562
|
-
const handleInspectorSplitResizeStart =
|
|
60796
|
+
const splitContainerRef = useRef114(null);
|
|
60797
|
+
const splitDragRef = useRef114(null);
|
|
60798
|
+
const handleInspectorSplitResizeStart = useCallback134(
|
|
60563
60799
|
(event) => {
|
|
60564
60800
|
event.preventDefault();
|
|
60565
60801
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
@@ -60572,7 +60808,7 @@ function useInspectorSplitResize() {
|
|
|
60572
60808
|
},
|
|
60573
60809
|
[layersPanePercent]
|
|
60574
60810
|
);
|
|
60575
|
-
const handleInspectorSplitResizeMove =
|
|
60811
|
+
const handleInspectorSplitResizeMove = useCallback134((event) => {
|
|
60576
60812
|
const drag = splitDragRef.current;
|
|
60577
60813
|
if (!drag || drag.height <= 0) return;
|
|
60578
60814
|
const deltaPercent = (event.clientY - drag.startY) / drag.height * 100;
|
|
@@ -60582,7 +60818,7 @@ function useInspectorSplitResize() {
|
|
|
60582
60818
|
);
|
|
60583
60819
|
setLayersPanePercent(next);
|
|
60584
60820
|
}, []);
|
|
60585
|
-
const handleInspectorSplitResizeEnd =
|
|
60821
|
+
const handleInspectorSplitResizeEnd = useCallback134(() => {
|
|
60586
60822
|
splitDragRef.current = null;
|
|
60587
60823
|
}, []);
|
|
60588
60824
|
return {
|
|
@@ -60605,6 +60841,7 @@ function StudioRightPanel({
|
|
|
60605
60841
|
onToggleRecording,
|
|
60606
60842
|
sdkSession,
|
|
60607
60843
|
publishSdkSession,
|
|
60844
|
+
forceReloadSdkSession,
|
|
60608
60845
|
reloadPreview,
|
|
60609
60846
|
domEditSaveTimestampRef,
|
|
60610
60847
|
recordEdit,
|
|
@@ -60617,6 +60854,7 @@ function StudioRightPanel({
|
|
|
60617
60854
|
setRightPanelTab,
|
|
60618
60855
|
rightInspectorPanes,
|
|
60619
60856
|
toggleRightInspectorPane,
|
|
60857
|
+
setExclusiveRightInspectorPane,
|
|
60620
60858
|
handlePanelResizeStart,
|
|
60621
60859
|
handlePanelResizeMove,
|
|
60622
60860
|
handlePanelResizeEnd
|
|
@@ -60683,7 +60921,8 @@ function StudioRightPanel({
|
|
|
60683
60921
|
refreshFileTree,
|
|
60684
60922
|
readProjectFile,
|
|
60685
60923
|
writeProjectFile,
|
|
60686
|
-
fileTree
|
|
60924
|
+
fileTree,
|
|
60925
|
+
editingFile
|
|
60687
60926
|
} = useFileManagerContext();
|
|
60688
60927
|
const onPersistSlideshow = useSlideshowPersist({
|
|
60689
60928
|
sdkSession,
|
|
@@ -60713,8 +60952,8 @@ function StudioRightPanel({
|
|
|
60713
60952
|
handleInspectorSplitResizeMove,
|
|
60714
60953
|
handleInspectorSplitResizeEnd
|
|
60715
60954
|
} = useInspectorSplitResize();
|
|
60716
|
-
const backgroundRemovalAbortRef =
|
|
60717
|
-
|
|
60955
|
+
const backgroundRemovalAbortRef = useRef115(null);
|
|
60956
|
+
useEffect90(
|
|
60718
60957
|
() => () => {
|
|
60719
60958
|
backgroundRemovalAbortRef.current?.abort();
|
|
60720
60959
|
},
|
|
@@ -60722,19 +60961,13 @@ function StudioRightPanel({
|
|
|
60722
60961
|
);
|
|
60723
60962
|
const renderJobs = renderQueue.jobs;
|
|
60724
60963
|
const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
|
|
60725
|
-
const slideshowScenes =
|
|
60726
|
-
|
|
60727
|
-
|
|
60728
|
-
|
|
60729
|
-
|
|
60730
|
-
|
|
60731
|
-
|
|
60732
|
-
duration: s.duration
|
|
60733
|
-
}));
|
|
60734
|
-
} catch {
|
|
60735
|
-
return [];
|
|
60736
|
-
}
|
|
60737
|
-
}, [previewIframeRef, rightPanelTab, refreshKey]);
|
|
60964
|
+
const { isSlideshowComposition, slideshowScenes } = useSlideshowTabState({
|
|
60965
|
+
editingFileContent: editingFile?.content,
|
|
60966
|
+
previewIframeRef,
|
|
60967
|
+
refreshKey,
|
|
60968
|
+
rightPanelTab,
|
|
60969
|
+
setRightPanelTab
|
|
60970
|
+
});
|
|
60738
60971
|
const designPaneOpen = inspectorTabActive && rightInspectorPanes.design && designPanelActive;
|
|
60739
60972
|
const layersPaneOpen = inspectorTabActive && rightInspectorPanes.layers && STUDIO_INSPECTOR_PANELS_ENABLED;
|
|
60740
60973
|
const handleInspectorPaneButtonClick = (pane) => {
|
|
@@ -60742,9 +60975,13 @@ function StudioRightPanel({
|
|
|
60742
60975
|
setRightPanelTab(pane);
|
|
60743
60976
|
return;
|
|
60744
60977
|
}
|
|
60978
|
+
if (STUDIO_FLAT_INSPECTOR_ENABLED) {
|
|
60979
|
+
setExclusiveRightInspectorPane(pane);
|
|
60980
|
+
return;
|
|
60981
|
+
}
|
|
60745
60982
|
toggleRightInspectorPane(pane);
|
|
60746
60983
|
};
|
|
60747
|
-
const handleApplyColorGradingScope =
|
|
60984
|
+
const handleApplyColorGradingScope = useCallback135(
|
|
60748
60985
|
async (scope, value) => applyColorGradingScopeUpdate({
|
|
60749
60986
|
scope,
|
|
60750
60987
|
value,
|
|
@@ -60779,7 +61016,7 @@ function StudioRightPanel({
|
|
|
60779
61016
|
writeProjectFile
|
|
60780
61017
|
]
|
|
60781
61018
|
);
|
|
60782
|
-
const handleRemoveBackground =
|
|
61019
|
+
const handleRemoveBackground = useCallback135(
|
|
60783
61020
|
// fallow-ignore-next-line complexity
|
|
60784
61021
|
async (inputPath, options) => {
|
|
60785
61022
|
const response = await fetch(
|
|
@@ -60832,6 +61069,7 @@ function StudioRightPanel({
|
|
|
60832
61069
|
recordEdit,
|
|
60833
61070
|
reloadPreview,
|
|
60834
61071
|
domEditSaveTimestampRef,
|
|
61072
|
+
forceReloadSharedSdkSession: forceReloadSdkSession,
|
|
60835
61073
|
children: /* @__PURE__ */ jsx144(
|
|
60836
61074
|
PropertyPanel,
|
|
60837
61075
|
{
|
|
@@ -60988,7 +61226,7 @@ function StudioRightPanel({
|
|
|
60988
61226
|
onClick: () => setRightPanelTab("renders")
|
|
60989
61227
|
}
|
|
60990
61228
|
),
|
|
60991
|
-
/* @__PURE__ */ jsx144(
|
|
61229
|
+
isSlideshowComposition && /* @__PURE__ */ jsx144(
|
|
60992
61230
|
PanelTabButton,
|
|
60993
61231
|
{
|
|
60994
61232
|
label: "Slideshow",
|
|
@@ -61017,7 +61255,7 @@ function StudioRightPanel({
|
|
|
61017
61255
|
onClose: onCloseBlockParams ?? (() => {
|
|
61018
61256
|
})
|
|
61019
61257
|
}
|
|
61020
|
-
) : rightPanelTab === "slideshow" ? /* @__PURE__ */ jsx144(
|
|
61258
|
+
) : rightPanelTab === "slideshow" && isSlideshowComposition ? /* @__PURE__ */ jsx144(
|
|
61021
61259
|
SlideshowPanel,
|
|
61022
61260
|
{
|
|
61023
61261
|
scenes: slideshowScenes,
|
|
@@ -61033,7 +61271,7 @@ function StudioRightPanel({
|
|
|
61033
61271
|
domEditSaveTimestampRef,
|
|
61034
61272
|
recordEdit
|
|
61035
61273
|
}
|
|
61036
|
-
) : layersPaneOpen && designPaneOpen ? /* @__PURE__ */ jsxs124("div", { ref: splitContainerRef, className: "flex h-full min-h-0 min-w-0 flex-col", children: [
|
|
61274
|
+
) : layersPaneOpen && designPaneOpen && !STUDIO_FLAT_INSPECTOR_ENABLED ? /* @__PURE__ */ jsxs124("div", { ref: splitContainerRef, className: "flex h-full min-h-0 min-w-0 flex-col", children: [
|
|
61037
61275
|
/* @__PURE__ */ jsx144(
|
|
61038
61276
|
"div",
|
|
61039
61277
|
{
|
|
@@ -61083,11 +61321,11 @@ function StudioRightPanel({
|
|
|
61083
61321
|
}
|
|
61084
61322
|
|
|
61085
61323
|
// src/components/TimelineToolbar.tsx
|
|
61086
|
-
import { useEffect as
|
|
61324
|
+
import { useEffect as useEffect92, useRef as useRef116 } from "react";
|
|
61087
61325
|
import { Magnet, MagnifyingGlassMinus, MagnifyingGlassPlus } from "@phosphor-icons/react";
|
|
61088
61326
|
|
|
61089
61327
|
// src/hooks/useEnableKeyframes.ts
|
|
61090
|
-
import { useCallback as
|
|
61328
|
+
import { useCallback as useCallback136 } from "react";
|
|
61091
61329
|
var enableKeyframesTransactionCounter = 0;
|
|
61092
61330
|
function animatedProps(anim) {
|
|
61093
61331
|
if (!anim) return ["x", "y"];
|
|
@@ -61304,7 +61542,7 @@ async function applyArcWaypointAtPlayhead(session, sel, arcAnim, t, iframe) {
|
|
|
61304
61542
|
);
|
|
61305
61543
|
}
|
|
61306
61544
|
function useEnableKeyframes(sessionRef) {
|
|
61307
|
-
return
|
|
61545
|
+
return useCallback136(async () => {
|
|
61308
61546
|
const session = sessionRef.current;
|
|
61309
61547
|
if (!session) return;
|
|
61310
61548
|
const sel = session.domEditSelection;
|
|
@@ -61386,7 +61624,7 @@ function useEnableKeyframes(sessionRef) {
|
|
|
61386
61624
|
}
|
|
61387
61625
|
|
|
61388
61626
|
// src/hooks/useKeyframeKeyboard.ts
|
|
61389
|
-
import { useEffect as
|
|
61627
|
+
import { useEffect as useEffect91, useCallback as useCallback137 } from "react";
|
|
61390
61628
|
function isTextInput(el) {
|
|
61391
61629
|
if (!el) return false;
|
|
61392
61630
|
const tag = el.tagName;
|
|
@@ -61403,7 +61641,7 @@ function useKeyframeKeyboard({
|
|
|
61403
61641
|
onToggleExpand,
|
|
61404
61642
|
onNudgeKeyframe
|
|
61405
61643
|
}) {
|
|
61406
|
-
const handler =
|
|
61644
|
+
const handler = useCallback137(
|
|
61407
61645
|
(e) => {
|
|
61408
61646
|
if (!enabled2) return;
|
|
61409
61647
|
if (isTextInput(document.activeElement)) return;
|
|
@@ -61454,7 +61692,7 @@ function useKeyframeKeyboard({
|
|
|
61454
61692
|
onNudgeKeyframe
|
|
61455
61693
|
]
|
|
61456
61694
|
);
|
|
61457
|
-
|
|
61695
|
+
useEffect91(() => {
|
|
61458
61696
|
if (!enabled2) return;
|
|
61459
61697
|
window.addEventListener("keydown", handler, { capture: true });
|
|
61460
61698
|
return () => window.removeEventListener("keydown", handler, { capture: true });
|
|
@@ -61465,7 +61703,7 @@ function useKeyframeKeyboard({
|
|
|
61465
61703
|
import { Fragment as Fragment45, jsx as jsx145, jsxs as jsxs125 } from "react/jsx-runtime";
|
|
61466
61704
|
function useKeyframeToggle(session) {
|
|
61467
61705
|
const currentTime = usePlayerStore((s) => s.currentTime);
|
|
61468
|
-
const sessionRef =
|
|
61706
|
+
const sessionRef = useRef116(session);
|
|
61469
61707
|
sessionRef.current = session;
|
|
61470
61708
|
const onToggle = useEnableKeyframes(
|
|
61471
61709
|
sessionRef
|
|
@@ -61509,7 +61747,7 @@ function TimelineToolbar({ domEditSession, onSplitElement }) {
|
|
|
61509
61747
|
enabled: STUDIO_KEYFRAMES_ENABLED && Boolean(onToggleKeyframe),
|
|
61510
61748
|
onAddKeyframe: onToggleKeyframe
|
|
61511
61749
|
});
|
|
61512
|
-
|
|
61750
|
+
useEffect92(() => {
|
|
61513
61751
|
const onKeyDown = (e) => {
|
|
61514
61752
|
if (e.key !== "n" && e.key !== "N") return;
|
|
61515
61753
|
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
@@ -61767,20 +62005,20 @@ function TimelineToolbar({ domEditSession, onSplitElement }) {
|
|
|
61767
62005
|
}
|
|
61768
62006
|
|
|
61769
62007
|
// src/components/storyboard/StoryboardView.tsx
|
|
61770
|
-
import { useState as
|
|
62008
|
+
import { useState as useState118 } from "react";
|
|
61771
62009
|
import { Copy as Copy2, Check as Check2 } from "@phosphor-icons/react";
|
|
61772
62010
|
|
|
61773
62011
|
// src/hooks/useStoryboard.ts
|
|
61774
|
-
import { useCallback as
|
|
62012
|
+
import { useCallback as useCallback138, useEffect as useEffect93, useRef as useRef117, useState as useState111 } from "react";
|
|
61775
62013
|
function useStoryboard(projectId) {
|
|
61776
62014
|
const [data, setData] = useState111(null);
|
|
61777
62015
|
const [loading, setLoading] = useState111(true);
|
|
61778
62016
|
const [error, setError] = useState111(null);
|
|
61779
62017
|
const [reloadKey, setReloadKey] = useState111(0);
|
|
61780
|
-
const hasDataRef =
|
|
61781
|
-
const lastProjectRef =
|
|
61782
|
-
const reload =
|
|
61783
|
-
|
|
62018
|
+
const hasDataRef = useRef117(false);
|
|
62019
|
+
const lastProjectRef = useRef117(null);
|
|
62020
|
+
const reload = useCallback138(() => setReloadKey((k) => k + 1), []);
|
|
62021
|
+
useEffect93(() => {
|
|
61784
62022
|
if (!projectId) return;
|
|
61785
62023
|
let cancelled = false;
|
|
61786
62024
|
if (lastProjectRef.current !== projectId) {
|
|
@@ -61812,7 +62050,7 @@ function useStoryboard(projectId) {
|
|
|
61812
62050
|
}
|
|
61813
62051
|
|
|
61814
62052
|
// src/hooks/useProjectSignaturePoll.ts
|
|
61815
|
-
import { useEffect as
|
|
62053
|
+
import { useEffect as useEffect94, useRef as useRef118 } from "react";
|
|
61816
62054
|
var POLL_INTERVAL_MS = 2e3;
|
|
61817
62055
|
async function fetchProjectSignature(projectId) {
|
|
61818
62056
|
try {
|
|
@@ -61825,11 +62063,11 @@ async function fetchProjectSignature(projectId) {
|
|
|
61825
62063
|
}
|
|
61826
62064
|
}
|
|
61827
62065
|
function useProjectSignaturePoll(projectId, currentSignature, onChange) {
|
|
61828
|
-
const signatureRef =
|
|
61829
|
-
const onChangeRef =
|
|
62066
|
+
const signatureRef = useRef118(currentSignature);
|
|
62067
|
+
const onChangeRef = useRef118(onChange);
|
|
61830
62068
|
signatureRef.current = currentSignature;
|
|
61831
62069
|
onChangeRef.current = onChange;
|
|
61832
|
-
|
|
62070
|
+
useEffect94(() => {
|
|
61833
62071
|
if (!projectId) return;
|
|
61834
62072
|
let disposed = false;
|
|
61835
62073
|
let inFlight = false;
|
|
@@ -61856,7 +62094,7 @@ function useProjectSignaturePoll(projectId, currentSignature, onChange) {
|
|
|
61856
62094
|
}
|
|
61857
62095
|
|
|
61858
62096
|
// src/components/storyboard/StoryboardLoaded.tsx
|
|
61859
|
-
import { useEffect as
|
|
62097
|
+
import { useEffect as useEffect100, useMemo as useMemo49, useState as useState117 } from "react";
|
|
61860
62098
|
|
|
61861
62099
|
// src/components/storyboard/StoryboardDirection.tsx
|
|
61862
62100
|
import { jsx as jsx146, jsxs as jsxs126 } from "react/jsx-runtime";
|
|
@@ -61879,7 +62117,7 @@ function StoryboardDirection({ globals, frameCount }) {
|
|
|
61879
62117
|
}
|
|
61880
62118
|
|
|
61881
62119
|
// src/components/storyboard/FramePoster.tsx
|
|
61882
|
-
import { useEffect as
|
|
62120
|
+
import { useEffect as useEffect95, useState as useState112 } from "react";
|
|
61883
62121
|
import { jsx as jsx147 } from "react/jsx-runtime";
|
|
61884
62122
|
function FramePoster({
|
|
61885
62123
|
projectId,
|
|
@@ -61890,7 +62128,7 @@ function FramePoster({
|
|
|
61890
62128
|
posterVersion
|
|
61891
62129
|
}) {
|
|
61892
62130
|
const [failed2, setFailed] = useState112(false);
|
|
61893
|
-
|
|
62131
|
+
useEffect95(() => setFailed(false), [src, seconds, posterVersion]);
|
|
61894
62132
|
if (failed2) {
|
|
61895
62133
|
return /* @__PURE__ */ jsx147("div", { className: "flex h-full w-full items-center justify-center text-[11px] text-neutral-600", children: "Preview unavailable" });
|
|
61896
62134
|
}
|
|
@@ -62075,41 +62313,24 @@ function StoryboardGrid({
|
|
|
62075
62313
|
)) });
|
|
62076
62314
|
}
|
|
62077
62315
|
|
|
62078
|
-
// src/components/storyboard/StoryboardStatusLegend.tsx
|
|
62079
|
-
import { jsx as jsx150, jsxs as jsxs128 } from "react/jsx-runtime";
|
|
62080
|
-
function StoryboardStatusLegend() {
|
|
62081
|
-
return /* @__PURE__ */ jsxs128("div", { className: "flex flex-wrap items-center gap-x-5 gap-y-1.5 text-[11px] text-neutral-500", children: [
|
|
62082
|
-
/* @__PURE__ */ jsx150("span", { className: "uppercase tracking-wider text-neutral-600", children: "Status" }),
|
|
62083
|
-
FRAME_STATUS_ORDER.map((status, i) => /* @__PURE__ */ jsxs128("span", { className: "flex items-center gap-1.5", children: [
|
|
62084
|
-
i > 0 && /* @__PURE__ */ jsx150("span", { className: "text-neutral-700", children: "\u2192" }),
|
|
62085
|
-
/* @__PURE__ */ jsx150("span", { className: `h-2 w-2 rounded-full ${FRAME_STATUS_META[status].dotClass}` }),
|
|
62086
|
-
/* @__PURE__ */ jsx150("span", { className: "font-medium text-neutral-300", children: FRAME_STATUS_META[status].label }),
|
|
62087
|
-
/* @__PURE__ */ jsxs128("span", { className: "text-neutral-500", children: [
|
|
62088
|
-
"\u2014 ",
|
|
62089
|
-
FRAME_STATUS_META[status].description
|
|
62090
|
-
] })
|
|
62091
|
-
] }, status))
|
|
62092
|
-
] });
|
|
62093
|
-
}
|
|
62094
|
-
|
|
62095
62316
|
// src/components/storyboard/StoryboardScriptPanel.tsx
|
|
62096
|
-
import { jsx as
|
|
62317
|
+
import { jsx as jsx150, jsxs as jsxs128 } from "react/jsx-runtime";
|
|
62097
62318
|
function StoryboardScriptPanel({ script }) {
|
|
62098
62319
|
if (!script.exists) return null;
|
|
62099
|
-
return /* @__PURE__ */
|
|
62100
|
-
/* @__PURE__ */
|
|
62320
|
+
return /* @__PURE__ */ jsxs128("details", { className: "mt-10 rounded-lg border border-neutral-800 bg-neutral-900/50", children: [
|
|
62321
|
+
/* @__PURE__ */ jsxs128("summary", { className: "cursor-pointer select-none px-4 py-3 text-sm font-medium text-neutral-300", children: [
|
|
62101
62322
|
"Narration script",
|
|
62102
|
-
/* @__PURE__ */
|
|
62323
|
+
/* @__PURE__ */ jsx150("span", { className: "ml-2 font-normal text-neutral-500", children: script.path })
|
|
62103
62324
|
] }),
|
|
62104
|
-
/* @__PURE__ */
|
|
62325
|
+
/* @__PURE__ */ jsx150("pre", { className: "max-h-[420px] overflow-auto whitespace-pre-wrap border-t border-neutral-800 px-4 py-3 text-xs leading-relaxed text-neutral-400", children: script.content })
|
|
62105
62326
|
] });
|
|
62106
62327
|
}
|
|
62107
62328
|
|
|
62108
62329
|
// src/components/storyboard/StoryboardSourceEditor.tsx
|
|
62109
|
-
import { useCallback as
|
|
62330
|
+
import { useCallback as useCallback139, useEffect as useEffect96, useMemo as useMemo47, useRef as useRef119, useState as useState113 } from "react";
|
|
62110
62331
|
import { marked } from "marked";
|
|
62111
62332
|
import DOMPurify from "dompurify";
|
|
62112
|
-
import { jsx as
|
|
62333
|
+
import { jsx as jsx151, jsxs as jsxs129 } from "react/jsx-runtime";
|
|
62113
62334
|
var DISCARD_PROMPT = "Discard unsaved markdown changes?";
|
|
62114
62335
|
function useEditableFile(path, onSaved) {
|
|
62115
62336
|
const { readProjectFile, writeProjectFile } = useFileManagerContext();
|
|
@@ -62118,7 +62339,7 @@ function useEditableFile(path, onSaved) {
|
|
|
62118
62339
|
const [loading, setLoading] = useState113(true);
|
|
62119
62340
|
const [saving, setSaving] = useState113(false);
|
|
62120
62341
|
const [error, setError] = useState113(null);
|
|
62121
|
-
|
|
62342
|
+
useEffect96(() => {
|
|
62122
62343
|
if (!path) return;
|
|
62123
62344
|
let cancelled = false;
|
|
62124
62345
|
setLoading(true);
|
|
@@ -62136,7 +62357,7 @@ function useEditableFile(path, onSaved) {
|
|
|
62136
62357
|
cancelled = true;
|
|
62137
62358
|
};
|
|
62138
62359
|
}, [path, readProjectFile]);
|
|
62139
|
-
const save =
|
|
62360
|
+
const save = useCallback139(() => {
|
|
62140
62361
|
if (saving) return;
|
|
62141
62362
|
setSaving(true);
|
|
62142
62363
|
setError(null);
|
|
@@ -62155,8 +62376,8 @@ function hardenLinks(node) {
|
|
|
62155
62376
|
}
|
|
62156
62377
|
function useMarkdownPreview(source) {
|
|
62157
62378
|
const [debounced, setDebounced] = useState113(source);
|
|
62158
|
-
const primed =
|
|
62159
|
-
|
|
62379
|
+
const primed = useRef119(false);
|
|
62380
|
+
useEffect96(() => {
|
|
62160
62381
|
if (!primed.current && source !== "") {
|
|
62161
62382
|
primed.current = true;
|
|
62162
62383
|
setDebounced(source);
|
|
@@ -62165,7 +62386,7 @@ function useMarkdownPreview(source) {
|
|
|
62165
62386
|
const id = window.setTimeout(() => setDebounced(source), 200);
|
|
62166
62387
|
return () => window.clearTimeout(id);
|
|
62167
62388
|
}, [source]);
|
|
62168
|
-
return
|
|
62389
|
+
return useMemo47(() => {
|
|
62169
62390
|
const html2 = marked.parse(debounced, { async: false });
|
|
62170
62391
|
DOMPurify.addHook("afterSanitizeAttributes", hardenLinks);
|
|
62171
62392
|
try {
|
|
@@ -62188,8 +62409,8 @@ function StoryboardSourceEditor({
|
|
|
62188
62409
|
const activePath = files.some((f) => f.path === selected) ? selected : files[0]?.path ?? "";
|
|
62189
62410
|
const file = useEditableFile(activePath, onSaved);
|
|
62190
62411
|
const previewHtml = useMarkdownPreview(file.content);
|
|
62191
|
-
|
|
62192
|
-
|
|
62412
|
+
useEffect96(() => onDirtyChange?.(file.dirty), [onDirtyChange, file.dirty]);
|
|
62413
|
+
useEffect96(() => {
|
|
62193
62414
|
if (!file.dirty) return;
|
|
62194
62415
|
const onBeforeUnload = (event) => {
|
|
62195
62416
|
event.preventDefault();
|
|
@@ -62203,7 +62424,7 @@ function StoryboardSourceEditor({
|
|
|
62203
62424
|
if (file.dirty && !window.confirm(DISCARD_PROMPT)) return;
|
|
62204
62425
|
setSelected(path);
|
|
62205
62426
|
};
|
|
62206
|
-
return /* @__PURE__ */
|
|
62427
|
+
return /* @__PURE__ */ jsxs129(
|
|
62207
62428
|
"div",
|
|
62208
62429
|
{
|
|
62209
62430
|
className: "flex flex-1 min-h-0 flex-col",
|
|
@@ -62213,8 +62434,8 @@ function StoryboardSourceEditor({
|
|
|
62213
62434
|
if (file.dirty && !file.saving) file.save();
|
|
62214
62435
|
},
|
|
62215
62436
|
children: [
|
|
62216
|
-
/* @__PURE__ */
|
|
62217
|
-
files.map((f) => /* @__PURE__ */
|
|
62437
|
+
/* @__PURE__ */ jsxs129("div", { className: "flex items-center gap-1 border-b border-neutral-800 px-4 py-2", children: [
|
|
62438
|
+
files.map((f) => /* @__PURE__ */ jsx151(
|
|
62218
62439
|
"button",
|
|
62219
62440
|
{
|
|
62220
62441
|
type: "button",
|
|
@@ -62224,10 +62445,10 @@ function StoryboardSourceEditor({
|
|
|
62224
62445
|
},
|
|
62225
62446
|
f.path
|
|
62226
62447
|
)),
|
|
62227
|
-
/* @__PURE__ */
|
|
62228
|
-
file.error && /* @__PURE__ */
|
|
62229
|
-
/* @__PURE__ */
|
|
62230
|
-
/* @__PURE__ */
|
|
62448
|
+
/* @__PURE__ */ jsxs129("div", { className: "ml-auto flex items-center gap-3", children: [
|
|
62449
|
+
file.error && /* @__PURE__ */ jsx151("span", { className: "text-xs text-red-400", children: file.error }),
|
|
62450
|
+
/* @__PURE__ */ jsx151("span", { className: "text-xs text-neutral-500", children: file.saving ? "Saving\u2026" : file.dirty ? "Unsaved changes" : "Saved" }),
|
|
62451
|
+
/* @__PURE__ */ jsx151(
|
|
62231
62452
|
"button",
|
|
62232
62453
|
{
|
|
62233
62454
|
type: "button",
|
|
@@ -62239,12 +62460,12 @@ function StoryboardSourceEditor({
|
|
|
62239
62460
|
)
|
|
62240
62461
|
] })
|
|
62241
62462
|
] }),
|
|
62242
|
-
/* @__PURE__ */
|
|
62243
|
-
/* @__PURE__ */
|
|
62463
|
+
/* @__PURE__ */ jsxs129("div", { className: "flex flex-1 min-h-0", children: [
|
|
62464
|
+
/* @__PURE__ */ jsx151("div", { className: "w-1/2 min-w-0 border-r border-neutral-800", children: file.loading ? /* @__PURE__ */ jsxs129("div", { className: "p-4 text-sm text-neutral-500", children: [
|
|
62244
62465
|
"Loading ",
|
|
62245
62466
|
activePath,
|
|
62246
62467
|
"\u2026"
|
|
62247
|
-
] }) : /* @__PURE__ */
|
|
62468
|
+
] }) : /* @__PURE__ */ jsx151(
|
|
62248
62469
|
SourceEditor,
|
|
62249
62470
|
{
|
|
62250
62471
|
content: file.content,
|
|
@@ -62253,7 +62474,7 @@ function StoryboardSourceEditor({
|
|
|
62253
62474
|
onChange: file.setContent
|
|
62254
62475
|
}
|
|
62255
62476
|
) }),
|
|
62256
|
-
/* @__PURE__ */
|
|
62477
|
+
/* @__PURE__ */ jsx151("div", { className: "w-1/2 min-w-0 overflow-auto bg-neutral-950 px-6 py-4", children: /* @__PURE__ */ jsx151("div", { className: PREVIEW_PROSE, dangerouslySetInnerHTML: { __html: previewHtml } }) })
|
|
62257
62478
|
] })
|
|
62258
62479
|
]
|
|
62259
62480
|
}
|
|
@@ -62261,9 +62482,38 @@ function StoryboardSourceEditor({
|
|
|
62261
62482
|
}
|
|
62262
62483
|
|
|
62263
62484
|
// src/components/storyboard/StoryboardFrameFocus.tsx
|
|
62264
|
-
import { useCallback as
|
|
62265
|
-
import {
|
|
62266
|
-
|
|
62485
|
+
import { useCallback as useCallback140, useEffect as useEffect98, useState as useState115 } from "react";
|
|
62486
|
+
import { setFrameVoiceover } from "@hyperframes/core/storyboard";
|
|
62487
|
+
|
|
62488
|
+
// src/components/storyboard/AgentChatMessageButton.tsx
|
|
62489
|
+
import { useEffect as useEffect97, useState as useState114 } from "react";
|
|
62490
|
+
import { jsx as jsx152 } from "react/jsx-runtime";
|
|
62491
|
+
var APPLY_STORYBOARD_FEEDBACK_MESSAGE = "Read the storyboard feedback I saved in .hyperframes/frame-comments.json and revise the frames.";
|
|
62492
|
+
function AgentChatMessageButton({
|
|
62493
|
+
message,
|
|
62494
|
+
label = "Copy prompt for agent",
|
|
62495
|
+
onCopied
|
|
62496
|
+
}) {
|
|
62497
|
+
const [copyState, setCopyState] = useState114("idle");
|
|
62498
|
+
useEffect97(() => {
|
|
62499
|
+
if (copyState !== "copied") return;
|
|
62500
|
+
const timeout = window.setTimeout(() => setCopyState("again"), 3e3);
|
|
62501
|
+
return () => window.clearTimeout(timeout);
|
|
62502
|
+
}, [copyState]);
|
|
62503
|
+
const copyMessage = async () => {
|
|
62504
|
+
try {
|
|
62505
|
+
await navigator.clipboard.writeText(message);
|
|
62506
|
+
setCopyState("copied");
|
|
62507
|
+
onCopied?.();
|
|
62508
|
+
} catch {
|
|
62509
|
+
setCopyState("failed");
|
|
62510
|
+
}
|
|
62511
|
+
};
|
|
62512
|
+
return /* @__PURE__ */ jsx152(Button, { size: "sm", variant: "secondary", onClick: () => void copyMessage(), children: copyState === "copied" ? "Copied \u2014 paste in your agent chat" : copyState === "again" ? "Copy again" : copyState === "failed" ? "Copy failed" : label });
|
|
62513
|
+
}
|
|
62514
|
+
|
|
62515
|
+
// src/components/storyboard/StoryboardFrameFocus.tsx
|
|
62516
|
+
import { jsx as jsx153, jsxs as jsxs130 } from "react/jsx-runtime";
|
|
62267
62517
|
function StoryboardFrameFocus({
|
|
62268
62518
|
projectId,
|
|
62269
62519
|
storyboardPath,
|
|
@@ -62273,24 +62523,38 @@ function StoryboardFrameFocus({
|
|
|
62273
62523
|
onNavigate,
|
|
62274
62524
|
onSaved,
|
|
62275
62525
|
onSelectComposition,
|
|
62526
|
+
scriptExists,
|
|
62527
|
+
commentDraft,
|
|
62528
|
+
onCommentDraftChange,
|
|
62529
|
+
pendingComment,
|
|
62530
|
+
pendingCommentCount,
|
|
62531
|
+
commentDraftCount,
|
|
62532
|
+
commentsSubmitState,
|
|
62533
|
+
commentsSubmitError,
|
|
62534
|
+
feedbackMessageCopied,
|
|
62535
|
+
onFeedbackMessageCopied,
|
|
62536
|
+
onSaveFeedback,
|
|
62276
62537
|
posterVersion
|
|
62277
62538
|
}) {
|
|
62278
62539
|
const { readProjectFile, writeProjectFile } = useFileManagerContext();
|
|
62279
|
-
const { setViewMode } = useViewMode();
|
|
62280
|
-
const [draft, setDraft] =
|
|
62281
|
-
const [
|
|
62282
|
-
const [
|
|
62283
|
-
const
|
|
62540
|
+
const { setViewMode, registerViewModeGuard } = useViewMode();
|
|
62541
|
+
const [draft, setDraft] = useState115(frame.voiceover ?? "");
|
|
62542
|
+
const [savedVoiceover, setSavedVoiceover] = useState115(frame.voiceover ?? "");
|
|
62543
|
+
const [busy, setBusy] = useState115(false);
|
|
62544
|
+
const [error, setError] = useState115(null);
|
|
62545
|
+
const applyEdit = useCallback140(
|
|
62284
62546
|
async (edit) => {
|
|
62285
|
-
if (busy) return;
|
|
62547
|
+
if (busy) return false;
|
|
62286
62548
|
setBusy(true);
|
|
62287
62549
|
setError(null);
|
|
62288
62550
|
try {
|
|
62289
62551
|
const source = await readProjectFile(storyboardPath);
|
|
62290
62552
|
await writeProjectFile(storyboardPath, edit(source));
|
|
62291
62553
|
onSaved();
|
|
62554
|
+
return true;
|
|
62292
62555
|
} catch (err) {
|
|
62293
62556
|
setError(err instanceof Error ? err.message : "failed to save");
|
|
62557
|
+
return false;
|
|
62294
62558
|
} finally {
|
|
62295
62559
|
setBusy(false);
|
|
62296
62560
|
}
|
|
@@ -62298,12 +62562,13 @@ function StoryboardFrameFocus({
|
|
|
62298
62562
|
[readProjectFile, writeProjectFile, storyboardPath, onSaved, busy]
|
|
62299
62563
|
);
|
|
62300
62564
|
const title = frame.title ?? `Frame ${frame.index}`;
|
|
62301
|
-
const dirty = draft !==
|
|
62302
|
-
const canOpenPreview = frame.srcExists && Boolean(frame.src);
|
|
62303
|
-
const saveVoiceover =
|
|
62304
|
-
|
|
62565
|
+
const dirty = draft !== savedVoiceover;
|
|
62566
|
+
const canOpenPreview = frame.status !== "outline" && frame.srcExists && Boolean(frame.src);
|
|
62567
|
+
const saveVoiceover = useCallback140(async () => {
|
|
62568
|
+
const saved = await applyEdit((src) => setFrameVoiceover(src, frame.index, draft));
|
|
62569
|
+
if (saved) setSavedVoiceover(draft);
|
|
62305
62570
|
}, [applyEdit, frame.index, draft]);
|
|
62306
|
-
|
|
62571
|
+
useEffect98(() => {
|
|
62307
62572
|
if (!dirty) return;
|
|
62308
62573
|
const onBeforeUnload = (e) => {
|
|
62309
62574
|
e.preventDefault();
|
|
@@ -62311,14 +62576,18 @@ function StoryboardFrameFocus({
|
|
|
62311
62576
|
window.addEventListener("beforeunload", onBeforeUnload);
|
|
62312
62577
|
return () => window.removeEventListener("beforeunload", onBeforeUnload);
|
|
62313
62578
|
}, [dirty]);
|
|
62314
|
-
const confirmLeave = (
|
|
62579
|
+
const confirmLeave = useCallback140(
|
|
62580
|
+
() => !dirty || window.confirm("Discard unsaved voiceover changes?"),
|
|
62581
|
+
[dirty]
|
|
62582
|
+
);
|
|
62583
|
+
useEffect98(() => registerViewModeGuard(confirmLeave), [confirmLeave, registerViewModeGuard]);
|
|
62315
62584
|
const handleBack = () => {
|
|
62316
62585
|
if (confirmLeave()) onBack();
|
|
62317
62586
|
};
|
|
62318
62587
|
const handleNavigate = (delta) => {
|
|
62319
62588
|
if (confirmLeave()) onNavigate(delta);
|
|
62320
62589
|
};
|
|
62321
|
-
|
|
62590
|
+
useEffect98(() => {
|
|
62322
62591
|
const onKey = (e) => {
|
|
62323
62592
|
const el = document.activeElement;
|
|
62324
62593
|
if (el instanceof HTMLTextAreaElement || el instanceof HTMLInputElement) return;
|
|
@@ -62330,11 +62599,11 @@ function StoryboardFrameFocus({
|
|
|
62330
62599
|
return () => window.removeEventListener("keydown", onKey);
|
|
62331
62600
|
});
|
|
62332
62601
|
const openInPreview = () => {
|
|
62602
|
+
if (!setViewMode("timeline")) return;
|
|
62333
62603
|
if (frame.src) onSelectComposition(frame.src);
|
|
62334
|
-
setViewMode("timeline");
|
|
62335
62604
|
};
|
|
62336
|
-
return /* @__PURE__ */
|
|
62337
|
-
/* @__PURE__ */
|
|
62605
|
+
return /* @__PURE__ */ jsxs130("div", { className: "flex w-full max-w-[100vw] flex-1 min-h-0 min-w-0 flex-col overflow-hidden bg-neutral-950 text-neutral-200", children: [
|
|
62606
|
+
/* @__PURE__ */ jsxs130("div", { className: "flex items-center gap-3 border-b border-neutral-800 px-4 py-2", children: [
|
|
62338
62607
|
/* @__PURE__ */ jsx153(
|
|
62339
62608
|
"button",
|
|
62340
62609
|
{
|
|
@@ -62344,13 +62613,13 @@ function StoryboardFrameFocus({
|
|
|
62344
62613
|
children: "\u2190 Board"
|
|
62345
62614
|
}
|
|
62346
62615
|
),
|
|
62347
|
-
/* @__PURE__ */
|
|
62616
|
+
/* @__PURE__ */ jsxs130("span", { className: "min-w-0 flex-1 truncate text-sm font-medium text-neutral-200", children: [
|
|
62348
62617
|
"Frame ",
|
|
62349
62618
|
frame.number ?? frame.index,
|
|
62350
62619
|
" \u2014 ",
|
|
62351
62620
|
title
|
|
62352
62621
|
] }),
|
|
62353
|
-
/* @__PURE__ */
|
|
62622
|
+
/* @__PURE__ */ jsxs130("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
62354
62623
|
/* @__PURE__ */ jsx153(
|
|
62355
62624
|
NavButton,
|
|
62356
62625
|
{
|
|
@@ -62369,8 +62638,35 @@ function StoryboardFrameFocus({
|
|
|
62369
62638
|
)
|
|
62370
62639
|
] })
|
|
62371
62640
|
] }),
|
|
62372
|
-
/* @__PURE__ */
|
|
62373
|
-
/* @__PURE__ */
|
|
62641
|
+
(commentDraftCount > 0 || pendingCommentCount > 0) && /* @__PURE__ */ jsxs130("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b border-neutral-800 bg-neutral-900/80 px-4 py-2", children: [
|
|
62642
|
+
/* @__PURE__ */ jsxs130("div", { children: [
|
|
62643
|
+
/* @__PURE__ */ jsx153("div", { className: "text-xs font-medium text-neutral-200", children: commentDraftCount > 0 ? "Feedback ready to save" : "Feedback saved" }),
|
|
62644
|
+
/* @__PURE__ */ jsx153("div", { className: "text-[11px] text-neutral-500", children: commentDraftCount > 0 ? "Save this batch and copy the message for your agent." : feedbackMessageCopied ? "Message copied \u2014 paste it in your terminal or IDE agent chat." : "The agent has not been notified yet." })
|
|
62645
|
+
] }),
|
|
62646
|
+
commentDraftCount > 0 ? /* @__PURE__ */ jsxs130(
|
|
62647
|
+
Button,
|
|
62648
|
+
{
|
|
62649
|
+
size: "sm",
|
|
62650
|
+
variant: "primary",
|
|
62651
|
+
onClick: onSaveFeedback,
|
|
62652
|
+
loading: commentsSubmitState === "saving",
|
|
62653
|
+
children: [
|
|
62654
|
+
"Save & copy message (",
|
|
62655
|
+
commentDraftCount,
|
|
62656
|
+
")"
|
|
62657
|
+
]
|
|
62658
|
+
}
|
|
62659
|
+
) : /* @__PURE__ */ jsx153(
|
|
62660
|
+
AgentChatMessageButton,
|
|
62661
|
+
{
|
|
62662
|
+
message: APPLY_STORYBOARD_FEEDBACK_MESSAGE,
|
|
62663
|
+
label: feedbackMessageCopied ? "Copy again" : "Copy prompt for agent",
|
|
62664
|
+
onCopied: onFeedbackMessageCopied
|
|
62665
|
+
}
|
|
62666
|
+
)
|
|
62667
|
+
] }),
|
|
62668
|
+
/* @__PURE__ */ jsxs130("div", { className: "flex flex-1 min-h-0 flex-col overflow-auto lg:flex-row lg:overflow-hidden", children: [
|
|
62669
|
+
/* @__PURE__ */ jsx153("div", { className: "flex w-full shrink-0 items-center justify-center bg-neutral-900/40 p-4 sm:p-8 lg:h-full lg:w-3/5", children: /* @__PURE__ */ jsx153("div", { className: "aspect-video w-full max-w-[900px] overflow-hidden rounded-lg border border-neutral-800 bg-neutral-900", children: canOpenPreview && frame.src ? /* @__PURE__ */ jsx153(
|
|
62374
62670
|
FramePoster,
|
|
62375
62671
|
{
|
|
62376
62672
|
projectId,
|
|
@@ -62380,32 +62676,32 @@ function StoryboardFrameFocus({
|
|
|
62380
62676
|
fit: "contain",
|
|
62381
62677
|
posterVersion
|
|
62382
62678
|
}
|
|
62383
|
-
) : /* @__PURE__ */ jsx153(
|
|
62384
|
-
/* @__PURE__ */
|
|
62385
|
-
/* @__PURE__ */ jsx153(
|
|
62386
|
-
|
|
62387
|
-
{
|
|
62388
|
-
status: frame.status,
|
|
62389
|
-
busy,
|
|
62390
|
-
onSet: (s) => applyEdit((src) => setFrameStatus(src, frame.index, s))
|
|
62391
|
-
}
|
|
62392
|
-
),
|
|
62393
|
-
/* @__PURE__ */ jsxs131("div", { className: "flex flex-wrap gap-x-6 gap-y-1 text-[11px] text-neutral-500", children: [
|
|
62394
|
-
frame.duration && /* @__PURE__ */ jsxs131("span", { children: [
|
|
62679
|
+
) : /* @__PURE__ */ jsx153(FramePlan, { frame }) }) }),
|
|
62680
|
+
/* @__PURE__ */ jsxs130("div", { className: "w-full shrink-0 space-y-6 border-t border-neutral-800 px-4 py-5 sm:px-6 lg:h-full lg:w-2/5 lg:overflow-auto lg:border-t-0 lg:border-l", children: [
|
|
62681
|
+
/* @__PURE__ */ jsx153(ReadOnlyStatus, { status: frame.status }),
|
|
62682
|
+
/* @__PURE__ */ jsxs130("div", { className: "flex flex-wrap gap-x-6 gap-y-1 text-[11px] text-neutral-500", children: [
|
|
62683
|
+
frame.duration && /* @__PURE__ */ jsxs130("span", { children: [
|
|
62395
62684
|
"Duration ",
|
|
62396
62685
|
frame.duration
|
|
62397
62686
|
] }),
|
|
62398
|
-
frame.transitionIn && /* @__PURE__ */
|
|
62687
|
+
frame.transitionIn && /* @__PURE__ */ jsxs130("span", { children: [
|
|
62399
62688
|
"Transition ",
|
|
62400
62689
|
frame.transitionIn
|
|
62401
62690
|
] })
|
|
62402
62691
|
] }),
|
|
62403
|
-
/* @__PURE__ */
|
|
62404
|
-
/* @__PURE__ */
|
|
62405
|
-
/* @__PURE__ */
|
|
62406
|
-
"
|
|
62407
|
-
|
|
62408
|
-
|
|
62692
|
+
/* @__PURE__ */ jsxs130("section", { children: [
|
|
62693
|
+
/* @__PURE__ */ jsxs130("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
62694
|
+
/* @__PURE__ */ jsxs130(
|
|
62695
|
+
"h3",
|
|
62696
|
+
{
|
|
62697
|
+
className: "text-xs font-semibold uppercase tracking-wider text-neutral-400",
|
|
62698
|
+
title: "Storyboard voiceover is a guide; SCRIPT.md is the final TTS source.",
|
|
62699
|
+
children: [
|
|
62700
|
+
"\u{1F399} Voiceover ",
|
|
62701
|
+
/* @__PURE__ */ jsx153("span", { className: "font-normal normal-case text-neutral-600", children: "guide" })
|
|
62702
|
+
]
|
|
62703
|
+
}
|
|
62704
|
+
),
|
|
62409
62705
|
/* @__PURE__ */ jsx153(
|
|
62410
62706
|
Button,
|
|
62411
62707
|
{
|
|
@@ -62414,8 +62710,7 @@ function StoryboardFrameFocus({
|
|
|
62414
62710
|
onClick: saveVoiceover,
|
|
62415
62711
|
disabled: !dirty,
|
|
62416
62712
|
loading: busy,
|
|
62417
|
-
|
|
62418
|
-
children: busy ? "Saving\u2026" : "Save"
|
|
62713
|
+
children: busy ? "Saving\u2026" : "Save voiceover"
|
|
62419
62714
|
}
|
|
62420
62715
|
)
|
|
62421
62716
|
] }),
|
|
@@ -62424,22 +62719,53 @@ function StoryboardFrameFocus({
|
|
|
62424
62719
|
{
|
|
62425
62720
|
value: draft,
|
|
62426
62721
|
onChange: (e) => setDraft(e.target.value),
|
|
62427
|
-
onBlur: () => {
|
|
62428
|
-
if (dirty && !busy) void saveVoiceover();
|
|
62429
|
-
},
|
|
62430
62722
|
rows: 3,
|
|
62431
62723
|
placeholder: "What the narrator says over this frame\u2026",
|
|
62432
62724
|
className: "w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-2 text-sm text-neutral-200 outline-none focus:border-neutral-600"
|
|
62433
62725
|
}
|
|
62434
62726
|
),
|
|
62435
|
-
/* @__PURE__ */ jsx153("p", { className: "mt-1 text-[11px] text-neutral-600", children: "
|
|
62727
|
+
/* @__PURE__ */ jsx153("p", { className: "mt-1 text-[11px] text-neutral-600", children: dirty ? "Unsaved changes" : scriptExists ? "Saved. SCRIPT.md drives final TTS." : "Saved to STORYBOARD.md. This voiceover guides narration for the frame." }),
|
|
62436
62728
|
error && /* @__PURE__ */ jsx153("p", { className: "mt-1 text-[11px] text-red-400", children: error })
|
|
62437
62729
|
] }),
|
|
62438
|
-
|
|
62730
|
+
/* @__PURE__ */ jsxs130("section", { children: [
|
|
62731
|
+
/* @__PURE__ */ jsx153("div", { className: "mb-1", children: /* @__PURE__ */ jsx153("h3", { className: "text-xs font-semibold uppercase tracking-wider text-neutral-400", children: "Frame feedback" }) }),
|
|
62732
|
+
/* @__PURE__ */ jsx153(
|
|
62733
|
+
"textarea",
|
|
62734
|
+
{
|
|
62735
|
+
value: commentDraft,
|
|
62736
|
+
onChange: (e) => onCommentDraftChange(e.target.value),
|
|
62737
|
+
rows: 3,
|
|
62738
|
+
placeholder: "Tell your agent what to change in this frame\u2026",
|
|
62739
|
+
"aria-label": `Comment on ${title}`,
|
|
62740
|
+
className: "w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-2 text-sm text-neutral-200 placeholder:text-neutral-600 outline-none focus:border-sky-700"
|
|
62741
|
+
}
|
|
62742
|
+
),
|
|
62743
|
+
pendingCommentCount > 0 ? /* @__PURE__ */ jsxs130("div", { className: "mt-2 flex flex-wrap items-center justify-between gap-2 rounded-md border border-sky-900/70 bg-sky-950/20 px-2.5 py-2", children: [
|
|
62744
|
+
/* @__PURE__ */ jsx153("p", { className: "text-[11px] text-sky-200", children: "Paste the agent prompt in your terminal or IDE chat." }),
|
|
62745
|
+
/* @__PURE__ */ jsx153(
|
|
62746
|
+
AgentChatMessageButton,
|
|
62747
|
+
{
|
|
62748
|
+
message: APPLY_STORYBOARD_FEEDBACK_MESSAGE,
|
|
62749
|
+
onCopied: onFeedbackMessageCopied
|
|
62750
|
+
}
|
|
62751
|
+
)
|
|
62752
|
+
] }) : /* @__PURE__ */ jsx153("p", { className: "mt-1 text-[11px] text-neutral-600", children: commentDraftCount > 0 ? "Your change is ready. Save it using the review bar above." : "Add a change to prepare feedback for the agent." }),
|
|
62753
|
+
pendingComment && /* @__PURE__ */ jsxs130("p", { className: "mt-1 text-[11px] text-sky-400/90", children: [
|
|
62754
|
+
/* @__PURE__ */ jsx153("span", { className: "font-medium", children: "Pending:" }),
|
|
62755
|
+
" \u201C",
|
|
62756
|
+
pendingComment,
|
|
62757
|
+
"\u201D"
|
|
62758
|
+
] }),
|
|
62759
|
+
commentsSubmitError && /* @__PURE__ */ jsxs130("p", { className: "mt-1 text-[11px] text-red-400", children: [
|
|
62760
|
+
"Couldn\u2019t submit: ",
|
|
62761
|
+
commentsSubmitError
|
|
62762
|
+
] })
|
|
62763
|
+
] }),
|
|
62764
|
+
frame.narrative && /* @__PURE__ */ jsxs130("section", { children: [
|
|
62439
62765
|
/* @__PURE__ */ jsx153("h3", { className: "mb-1 text-xs font-semibold uppercase tracking-wider text-neutral-400", children: "Narrative" }),
|
|
62440
62766
|
/* @__PURE__ */ jsx153("p", { className: "whitespace-pre-wrap text-sm text-neutral-300", children: frame.narrative })
|
|
62441
62767
|
] }),
|
|
62442
|
-
/* @__PURE__ */ jsx153(Button, { size: "sm", variant: "secondary", onClick: openInPreview,
|
|
62768
|
+
canOpenPreview ? /* @__PURE__ */ jsx153(Button, { size: "sm", variant: "secondary", onClick: openInPreview, children: "Open in Preview \u2192" }) : /* @__PURE__ */ jsx153("div", { className: "rounded-md border border-neutral-800 bg-neutral-900/60 px-3 py-2 text-xs text-neutral-500", children: "Preview becomes available after your agent builds this frame." })
|
|
62443
62769
|
] })
|
|
62444
62770
|
] })
|
|
62445
62771
|
] });
|
|
@@ -62460,31 +62786,280 @@ function NavButton({
|
|
|
62460
62786
|
}
|
|
62461
62787
|
);
|
|
62462
62788
|
}
|
|
62463
|
-
function
|
|
62464
|
-
status
|
|
62465
|
-
|
|
62466
|
-
onSet
|
|
62467
|
-
}) {
|
|
62468
|
-
return /* @__PURE__ */ jsxs131("div", { className: "flex items-center gap-2", children: [
|
|
62789
|
+
function ReadOnlyStatus({ status }) {
|
|
62790
|
+
const meta = FRAME_STATUS_META[status];
|
|
62791
|
+
return /* @__PURE__ */ jsxs130("div", { className: "flex items-center gap-2", children: [
|
|
62469
62792
|
/* @__PURE__ */ jsx153("span", { className: "text-xs font-semibold uppercase tracking-wider text-neutral-500", children: "Status" }),
|
|
62470
|
-
/* @__PURE__ */ jsx153("
|
|
62471
|
-
|
|
62793
|
+
/* @__PURE__ */ jsx153("span", { className: `rounded px-2 py-1 text-xs font-medium ${meta.chipClass}`, children: meta.label }),
|
|
62794
|
+
/* @__PURE__ */ jsx153("span", { className: "text-[11px] text-neutral-600", children: "Updated by your agent" })
|
|
62795
|
+
] });
|
|
62796
|
+
}
|
|
62797
|
+
function FramePlan({ frame }) {
|
|
62798
|
+
const title = frame.title ?? `Frame ${frame.index}`;
|
|
62799
|
+
const isOutline = frame.status === "outline";
|
|
62800
|
+
return /* @__PURE__ */ jsx153("div", { className: "flex h-full w-full items-center justify-center bg-[radial-gradient(circle_at_center,_rgba(38,38,38,0.8),_rgba(10,10,10,1))] p-10", children: /* @__PURE__ */ jsxs130("div", { className: "max-w-xl text-center", children: [
|
|
62801
|
+
/* @__PURE__ */ jsx153("span", { className: "rounded-full border border-neutral-700 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider text-neutral-400", children: isOutline ? "Planned frame" : "Preview unavailable" }),
|
|
62802
|
+
/* @__PURE__ */ jsx153("h2", { className: "mt-4 text-2xl font-semibold text-neutral-100", children: title }),
|
|
62803
|
+
frame.scene && /* @__PURE__ */ jsx153("p", { className: "mt-3 text-base leading-relaxed text-neutral-300", children: frame.scene }),
|
|
62804
|
+
!frame.scene && frame.narrative && /* @__PURE__ */ jsx153("p", { className: "mt-3 line-clamp-4 text-sm leading-relaxed text-neutral-400", children: frame.narrative }),
|
|
62805
|
+
/* @__PURE__ */ jsx153("p", { className: "mt-5 text-xs text-neutral-600", children: isOutline ? "A visual preview will appear when your agent builds the sketch." : frame.src ? `Frame file not found: ${frame.src}` : "This frame does not link to a source file." })
|
|
62806
|
+
] }) });
|
|
62807
|
+
}
|
|
62808
|
+
|
|
62809
|
+
// src/components/storyboard/storyboardReviewStage.ts
|
|
62810
|
+
function deriveStoryboardHandoffStep(draftCount, pendingCount) {
|
|
62811
|
+
if (draftCount > 0) return 2;
|
|
62812
|
+
if (pendingCount > 0) return 3;
|
|
62813
|
+
return 1;
|
|
62814
|
+
}
|
|
62815
|
+
function deriveStoryboardReviewStage(frames) {
|
|
62816
|
+
const counts = { outline: 0, built: 0, animated: 0 };
|
|
62817
|
+
for (const frame of frames) counts[frame.status] += 1;
|
|
62818
|
+
let stage;
|
|
62819
|
+
if (frames.length === 0) stage = "empty";
|
|
62820
|
+
else if (counts.outline === frames.length) stage = "plan-review";
|
|
62821
|
+
else if (counts.outline > 0) stage = "sketch-in-progress";
|
|
62822
|
+
else if (counts.built === frames.length) stage = "sketch-review";
|
|
62823
|
+
else if (counts.built > 0) stage = "animation-in-progress";
|
|
62824
|
+
else stage = "final-review";
|
|
62825
|
+
return { stage, counts, frameCount: frames.length };
|
|
62826
|
+
}
|
|
62827
|
+
|
|
62828
|
+
// src/components/storyboard/StoryboardReviewGuide.tsx
|
|
62829
|
+
import { jsx as jsx154, jsxs as jsxs131 } from "react/jsx-runtime";
|
|
62830
|
+
var GUIDE_COPY = {
|
|
62831
|
+
empty: {
|
|
62832
|
+
eyebrow: "Waiting for a plan",
|
|
62833
|
+
title: "The storyboard has no frames yet",
|
|
62834
|
+
body: "Ask your agent to draft the story plan. Frames will appear here automatically."
|
|
62835
|
+
},
|
|
62836
|
+
"plan-review": {
|
|
62837
|
+
eyebrow: "Ready for review",
|
|
62838
|
+
title: "Review the story plan",
|
|
62839
|
+
body: "Check the sequence, scene direction, and voiceover before visual work begins. Leave frame comments, save them, then reply in your terminal or IDE agent chat."
|
|
62840
|
+
},
|
|
62841
|
+
"sketch-in-progress": {
|
|
62842
|
+
eyebrow: "Build in progress",
|
|
62843
|
+
title: "Visual sketches are in progress",
|
|
62844
|
+
body: "New posters appear automatically as your agent builds them. You can comment now; wait until no frames remain in Outline before approving the layouts."
|
|
62845
|
+
},
|
|
62846
|
+
"sketch-review": {
|
|
62847
|
+
eyebrow: "Ready for review",
|
|
62848
|
+
title: "Review the visual direction",
|
|
62849
|
+
body: "Check composition, hierarchy, and copy. Save frame comments, then reply in your terminal or IDE agent chat."
|
|
62850
|
+
},
|
|
62851
|
+
"animation-in-progress": {
|
|
62852
|
+
eyebrow: "Build in progress",
|
|
62853
|
+
title: "Animation is in progress",
|
|
62854
|
+
body: "The board refreshes as frames advance. Review completed frames now; the final review is ready when every frame is Animated."
|
|
62855
|
+
},
|
|
62856
|
+
"final-review": {
|
|
62857
|
+
eyebrow: "Ready for review",
|
|
62858
|
+
title: "Review motion and timing",
|
|
62859
|
+
body: "Every frame is animated. Open a frame in Preview to review it in the timeline, or leave frame comments for another revision."
|
|
62860
|
+
}
|
|
62861
|
+
};
|
|
62862
|
+
var REVIEW_ACTION_COPY = {
|
|
62863
|
+
empty: { body: "Add comments as previews arrive." },
|
|
62864
|
+
"plan-review": {
|
|
62865
|
+
body: "Add comments where you want changes. If everything looks right, approve this pass in agent chat.",
|
|
62866
|
+
approvalMessage: "Approve this storyboard plan and continue to visual sketches."
|
|
62867
|
+
},
|
|
62868
|
+
"sketch-in-progress": {
|
|
62869
|
+
body: "Add comments as previews arrive. You\u2019ll be prompted to approve when this pass is ready."
|
|
62870
|
+
},
|
|
62871
|
+
"sketch-review": {
|
|
62872
|
+
body: "Add comments where you want changes. If everything looks right, approve this pass in agent chat.",
|
|
62873
|
+
approvalMessage: "Approve these storyboard sketches and continue to animation."
|
|
62874
|
+
},
|
|
62875
|
+
"animation-in-progress": {
|
|
62876
|
+
body: "Add comments as previews arrive. You\u2019ll be prompted to approve when this pass is ready."
|
|
62877
|
+
},
|
|
62878
|
+
"final-review": {
|
|
62879
|
+
body: "Add comments where you want changes. If everything looks right, approve this pass in agent chat.",
|
|
62880
|
+
approvalMessage: "Approve this final storyboard review and continue to rendering."
|
|
62881
|
+
}
|
|
62882
|
+
};
|
|
62883
|
+
var REVIEW_STEP_STATES = {
|
|
62884
|
+
[-1]: {
|
|
62885
|
+
textClass: "text-emerald-400",
|
|
62886
|
+
numberClass: "border-emerald-700 bg-emerald-500/10",
|
|
62887
|
+
ariaCurrent: void 0,
|
|
62888
|
+
marker: () => "\u2713"
|
|
62889
|
+
},
|
|
62890
|
+
[0]: {
|
|
62891
|
+
textClass: "text-sky-300",
|
|
62892
|
+
numberClass: "border-sky-500 bg-sky-500/15",
|
|
62893
|
+
ariaCurrent: "step",
|
|
62894
|
+
marker: (number) => number
|
|
62895
|
+
},
|
|
62896
|
+
[1]: {
|
|
62897
|
+
textClass: "text-neutral-600",
|
|
62898
|
+
numberClass: "border-neutral-700",
|
|
62899
|
+
ariaCurrent: void 0,
|
|
62900
|
+
marker: (number) => number
|
|
62901
|
+
}
|
|
62902
|
+
};
|
|
62903
|
+
var REVIEW_STEP_SEPARATOR_CLASS = {
|
|
62904
|
+
1: "invisible",
|
|
62905
|
+
2: "",
|
|
62906
|
+
3: ""
|
|
62907
|
+
};
|
|
62908
|
+
function StoryboardReviewGuide({
|
|
62909
|
+
frames,
|
|
62910
|
+
draftCount,
|
|
62911
|
+
pendingCount,
|
|
62912
|
+
onFeedbackMessageCopied
|
|
62913
|
+
}) {
|
|
62914
|
+
const summary = deriveStoryboardReviewStage(frames);
|
|
62915
|
+
const copy = GUIDE_COPY[summary.stage];
|
|
62916
|
+
const handoffStep = deriveStoryboardHandoffStep(draftCount, pendingCount);
|
|
62917
|
+
const progress = progressLabel(summary);
|
|
62918
|
+
return /* @__PURE__ */ jsxs131("section", { className: "mt-5 rounded-lg border border-neutral-800 bg-neutral-900/60 px-4 py-3", children: [
|
|
62919
|
+
/* @__PURE__ */ jsxs131("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
62920
|
+
/* @__PURE__ */ jsxs131("div", { className: "max-w-3xl", children: [
|
|
62921
|
+
/* @__PURE__ */ jsx154("div", { className: "text-[10px] font-semibold uppercase tracking-wider text-sky-400", children: copy.eyebrow }),
|
|
62922
|
+
/* @__PURE__ */ jsx154("h2", { className: "mt-0.5 text-sm font-semibold text-neutral-100", children: copy.title }),
|
|
62923
|
+
/* @__PURE__ */ jsx154("p", { className: "mt-1 text-xs leading-relaxed text-neutral-400", children: copy.body })
|
|
62924
|
+
] }),
|
|
62925
|
+
summary.frameCount > 0 && /* @__PURE__ */ jsxs131("div", { className: "shrink-0", children: [
|
|
62926
|
+
/* @__PURE__ */ jsx154("div", { className: "mb-1.5 text-right text-[11px] font-medium text-neutral-300", children: progress }),
|
|
62927
|
+
/* @__PURE__ */ jsx154("div", { className: "flex flex-wrap justify-end gap-1.5", "aria-label": "Frame status summary", children: FRAME_STATUS_ORDER.map((status) => /* @__PURE__ */ jsxs131(
|
|
62928
|
+
"span",
|
|
62929
|
+
{
|
|
62930
|
+
className: `rounded px-2 py-1 text-[10px] font-medium ${FRAME_STATUS_META[status].chipClass}`,
|
|
62931
|
+
children: [
|
|
62932
|
+
summary.counts[status],
|
|
62933
|
+
" ",
|
|
62934
|
+
FRAME_STATUS_META[status].label
|
|
62935
|
+
]
|
|
62936
|
+
},
|
|
62937
|
+
status
|
|
62938
|
+
)) })
|
|
62939
|
+
] })
|
|
62940
|
+
] }),
|
|
62941
|
+
summary.frameCount > 0 && /* @__PURE__ */ jsxs131("div", { className: "mt-3 border-t border-neutral-800 pt-3", children: [
|
|
62942
|
+
/* @__PURE__ */ jsx154(ReviewSteps, { current: handoffStep }),
|
|
62943
|
+
/* @__PURE__ */ jsx154(
|
|
62944
|
+
NextAction,
|
|
62945
|
+
{
|
|
62946
|
+
stage: summary.stage,
|
|
62947
|
+
step: handoffStep,
|
|
62948
|
+
draftCount,
|
|
62949
|
+
onFeedbackMessageCopied
|
|
62950
|
+
}
|
|
62951
|
+
)
|
|
62952
|
+
] })
|
|
62953
|
+
] });
|
|
62954
|
+
}
|
|
62955
|
+
function ReviewSteps({ current }) {
|
|
62956
|
+
const steps = ["Review frames", "Save feedback", "Reply in agent chat"];
|
|
62957
|
+
return /* @__PURE__ */ jsx154("ol", { className: "hidden items-center gap-2 sm:flex", "aria-label": "Storyboard review workflow", children: steps.map((label, index) => /* @__PURE__ */ jsx154(
|
|
62958
|
+
ReviewStep,
|
|
62959
|
+
{
|
|
62960
|
+
label,
|
|
62961
|
+
number: index + 1,
|
|
62962
|
+
current
|
|
62963
|
+
},
|
|
62964
|
+
label
|
|
62965
|
+
)) });
|
|
62966
|
+
}
|
|
62967
|
+
function ReviewStep({
|
|
62968
|
+
label,
|
|
62969
|
+
number,
|
|
62970
|
+
current
|
|
62971
|
+
}) {
|
|
62972
|
+
const offset = Math.sign(number - current);
|
|
62973
|
+
const state = REVIEW_STEP_STATES[offset];
|
|
62974
|
+
return /* @__PURE__ */ jsxs131("li", { className: "flex items-center gap-2", children: [
|
|
62975
|
+
/* @__PURE__ */ jsx154(
|
|
62976
|
+
"span",
|
|
62472
62977
|
{
|
|
62473
|
-
|
|
62474
|
-
|
|
62475
|
-
"
|
|
62476
|
-
|
|
62477
|
-
|
|
62478
|
-
|
|
62479
|
-
|
|
62480
|
-
|
|
62481
|
-
|
|
62482
|
-
|
|
62978
|
+
className: `text-neutral-700 ${REVIEW_STEP_SEPARATOR_CLASS[number]}`,
|
|
62979
|
+
"aria-hidden": "true",
|
|
62980
|
+
children: "\u2192"
|
|
62981
|
+
}
|
|
62982
|
+
),
|
|
62983
|
+
/* @__PURE__ */ jsxs131(
|
|
62984
|
+
"span",
|
|
62985
|
+
{
|
|
62986
|
+
"aria-current": state.ariaCurrent,
|
|
62987
|
+
className: `flex items-center gap-1.5 text-[11px] font-medium ${state.textClass}`,
|
|
62988
|
+
children: [
|
|
62989
|
+
/* @__PURE__ */ jsx154(
|
|
62990
|
+
"span",
|
|
62991
|
+
{
|
|
62992
|
+
className: `flex h-5 w-5 items-center justify-center rounded-full border text-[10px] ${state.numberClass}`,
|
|
62993
|
+
children: state.marker(number)
|
|
62994
|
+
}
|
|
62995
|
+
),
|
|
62996
|
+
label
|
|
62997
|
+
]
|
|
62998
|
+
}
|
|
62999
|
+
)
|
|
62483
63000
|
] });
|
|
62484
63001
|
}
|
|
63002
|
+
function NextAction({
|
|
63003
|
+
stage,
|
|
63004
|
+
step,
|
|
63005
|
+
draftCount,
|
|
63006
|
+
onFeedbackMessageCopied
|
|
63007
|
+
}) {
|
|
63008
|
+
if (step === 3) {
|
|
63009
|
+
return /* @__PURE__ */ jsx154(AgentHandoffAction, { onFeedbackMessageCopied });
|
|
63010
|
+
}
|
|
63011
|
+
if (step === 2) return /* @__PURE__ */ jsx154(SaveFeedbackAction, { draftCount });
|
|
63012
|
+
return /* @__PURE__ */ jsx154(ReviewFramesAction, { stage });
|
|
63013
|
+
}
|
|
63014
|
+
function AgentHandoffAction({ onFeedbackMessageCopied }) {
|
|
63015
|
+
return /* @__PURE__ */ jsxs131("div", { className: "mt-3 flex flex-col gap-3 rounded-md border border-sky-900/70 bg-sky-950/20 px-3 py-2.5 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
63016
|
+
/* @__PURE__ */ jsxs131("div", { className: "max-w-3xl", children: [
|
|
63017
|
+
/* @__PURE__ */ jsx154("div", { className: "text-xs font-semibold text-sky-200", children: "Next: return to your agent chat" }),
|
|
63018
|
+
/* @__PURE__ */ jsx154("p", { className: "mt-0.5 text-[11px] text-neutral-400", children: "Feedback is saved, but the agent has not been notified. Paste this prompt in your terminal or IDE agent chat." })
|
|
63019
|
+
] }),
|
|
63020
|
+
/* @__PURE__ */ jsx154(
|
|
63021
|
+
AgentChatMessageButton,
|
|
63022
|
+
{
|
|
63023
|
+
message: APPLY_STORYBOARD_FEEDBACK_MESSAGE,
|
|
63024
|
+
onCopied: onFeedbackMessageCopied
|
|
63025
|
+
}
|
|
63026
|
+
)
|
|
63027
|
+
] });
|
|
63028
|
+
}
|
|
63029
|
+
function SaveFeedbackAction({ draftCount }) {
|
|
63030
|
+
return /* @__PURE__ */ jsx154("div", { className: "mt-3 rounded-md border border-neutral-800 bg-neutral-950/40 px-3 py-2.5", children: /* @__PURE__ */ jsxs131("div", { children: [
|
|
63031
|
+
/* @__PURE__ */ jsx154("div", { className: "text-xs font-semibold text-neutral-200", children: "Next: save your feedback" }),
|
|
63032
|
+
/* @__PURE__ */ jsxs131("p", { className: "mt-0.5 text-[11px] text-neutral-500", children: [
|
|
63033
|
+
draftCount,
|
|
63034
|
+
" frame",
|
|
63035
|
+
draftCount === 1 ? " has" : "s have",
|
|
63036
|
+
" feedback ready. Use Save & copy message above to prepare this batch for your agent."
|
|
63037
|
+
] })
|
|
63038
|
+
] }) });
|
|
63039
|
+
}
|
|
63040
|
+
function ReviewFramesAction({ stage }) {
|
|
63041
|
+
const copy = REVIEW_ACTION_COPY[stage];
|
|
63042
|
+
return /* @__PURE__ */ jsxs131("div", { className: "mt-3 flex flex-col gap-3 rounded-md border border-neutral-800 bg-neutral-950/40 px-3 py-2.5 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
63043
|
+
/* @__PURE__ */ jsxs131("div", { children: [
|
|
63044
|
+
/* @__PURE__ */ jsx154("div", { className: "text-xs font-semibold text-neutral-200", children: "Next: review the frames" }),
|
|
63045
|
+
/* @__PURE__ */ jsx154("p", { className: "mt-0.5 text-[11px] text-neutral-500", children: copy.body })
|
|
63046
|
+
] }),
|
|
63047
|
+
copy.approvalMessage && /* @__PURE__ */ jsx154(AgentChatMessageButton, { message: copy.approvalMessage, label: "Copy approval message" })
|
|
63048
|
+
] });
|
|
63049
|
+
}
|
|
63050
|
+
function progressLabel(summary) {
|
|
63051
|
+
if (summary.stage === "sketch-in-progress" || summary.stage === "sketch-review") {
|
|
63052
|
+
const ready = summary.frameCount - summary.counts.outline;
|
|
63053
|
+
return `${ready} of ${summary.frameCount} visual sketches ready`;
|
|
63054
|
+
}
|
|
63055
|
+
if (summary.stage === "animation-in-progress" || summary.stage === "final-review") {
|
|
63056
|
+
return `${summary.counts.animated} of ${summary.frameCount} animations ready`;
|
|
63057
|
+
}
|
|
63058
|
+
return `${summary.frameCount} plan frame${summary.frameCount === 1 ? "" : "s"} ready`;
|
|
63059
|
+
}
|
|
62485
63060
|
|
|
62486
63061
|
// src/components/storyboard/useFrameComments.ts
|
|
62487
|
-
import { useCallback as
|
|
63062
|
+
import { useCallback as useCallback141, useEffect as useEffect99, useMemo as useMemo48, useState as useState116 } from "react";
|
|
62488
63063
|
|
|
62489
63064
|
// src/components/storyboard/frameComments.ts
|
|
62490
63065
|
var FRAME_COMMENTS_PATH = ".hyperframes/frame-comments.json";
|
|
@@ -62546,32 +63121,34 @@ function buildCommentsFile(frames, drafts, previous, submittedAt) {
|
|
|
62546
63121
|
// src/components/storyboard/useFrameComments.ts
|
|
62547
63122
|
function useFrameComments(frames) {
|
|
62548
63123
|
const { writeProjectFile, readOptionalProjectFile } = useFileManagerContext();
|
|
62549
|
-
const [drafts, setDrafts] =
|
|
62550
|
-
const [submitState, setSubmitState] =
|
|
62551
|
-
const [
|
|
62552
|
-
const
|
|
63124
|
+
const [drafts, setDrafts] = useState116({});
|
|
63125
|
+
const [submitState, setSubmitState] = useState116("idle");
|
|
63126
|
+
const [submitError, setSubmitError] = useState116(null);
|
|
63127
|
+
const [pending, setPending] = useState116(null);
|
|
63128
|
+
const refreshPending = useCallback141(async () => {
|
|
62553
63129
|
try {
|
|
62554
63130
|
const parsed = parseCommentsFile(await readOptionalProjectFile(FRAME_COMMENTS_PATH));
|
|
62555
63131
|
setPending(parsed && parsed.comments.length > 0 ? parsed.comments : null);
|
|
62556
63132
|
} catch {
|
|
62557
63133
|
}
|
|
62558
63134
|
}, [readOptionalProjectFile]);
|
|
62559
|
-
|
|
63135
|
+
useEffect99(() => {
|
|
62560
63136
|
void refreshPending();
|
|
62561
63137
|
const onFocus = () => void refreshPending();
|
|
62562
63138
|
window.addEventListener("focus", onFocus);
|
|
62563
63139
|
return () => window.removeEventListener("focus", onFocus);
|
|
62564
63140
|
}, [refreshPending]);
|
|
62565
|
-
const setDraft =
|
|
63141
|
+
const setDraft = useCallback141((index, text) => {
|
|
62566
63142
|
setDrafts((prev) => ({ ...prev, [index]: text }));
|
|
62567
63143
|
}, []);
|
|
62568
|
-
const draftCount =
|
|
63144
|
+
const draftCount = useMemo48(
|
|
62569
63145
|
() => Object.values(drafts).filter((text) => text.trim().length > 0).length,
|
|
62570
63146
|
[drafts]
|
|
62571
63147
|
);
|
|
62572
|
-
const submit =
|
|
62573
|
-
if (draftCount === 0 || submitState === "saving") return;
|
|
63148
|
+
const submit = useCallback141(async () => {
|
|
63149
|
+
if (draftCount === 0 || submitState === "saving") return false;
|
|
62574
63150
|
setSubmitState("saving");
|
|
63151
|
+
setSubmitError(null);
|
|
62575
63152
|
try {
|
|
62576
63153
|
const previous = parseCommentsFile(await readOptionalProjectFile(FRAME_COMMENTS_PATH));
|
|
62577
63154
|
const file = buildCommentsFile(frames, drafts, previous, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -62579,16 +63156,28 @@ function useFrameComments(frames) {
|
|
|
62579
63156
|
`);
|
|
62580
63157
|
setDrafts({});
|
|
62581
63158
|
setPending(file.comments);
|
|
62582
|
-
|
|
63159
|
+
return true;
|
|
63160
|
+
} catch (err) {
|
|
63161
|
+
setSubmitError(err instanceof Error ? err.message : "Failed to submit comments");
|
|
63162
|
+
return false;
|
|
62583
63163
|
} finally {
|
|
62584
63164
|
setSubmitState("idle");
|
|
62585
63165
|
}
|
|
62586
63166
|
}, [draftCount, submitState, frames, drafts, readOptionalProjectFile, writeProjectFile]);
|
|
62587
|
-
return {
|
|
63167
|
+
return {
|
|
63168
|
+
drafts,
|
|
63169
|
+
setDraft,
|
|
63170
|
+
draftCount,
|
|
63171
|
+
submitState,
|
|
63172
|
+
submitError,
|
|
63173
|
+
submit,
|
|
63174
|
+
pending,
|
|
63175
|
+
refreshPending
|
|
63176
|
+
};
|
|
62588
63177
|
}
|
|
62589
63178
|
|
|
62590
63179
|
// src/components/storyboard/StoryboardLoaded.tsx
|
|
62591
|
-
import { jsx as
|
|
63180
|
+
import { Fragment as Fragment46, jsx as jsx155, jsxs as jsxs132 } from "react/jsx-runtime";
|
|
62592
63181
|
function clampIndex(index, count) {
|
|
62593
63182
|
return Math.max(1, Math.min(count, index));
|
|
62594
63183
|
}
|
|
@@ -62598,15 +63187,29 @@ function StoryboardLoaded({
|
|
|
62598
63187
|
reload,
|
|
62599
63188
|
onSelectComposition
|
|
62600
63189
|
}) {
|
|
62601
|
-
const [subView, setSubView] =
|
|
62602
|
-
const [sourceDirty, setSourceDirty] =
|
|
62603
|
-
const [focusedIndex, setFocusedIndex] =
|
|
63190
|
+
const [subView, setSubView] = useState117("board");
|
|
63191
|
+
const [sourceDirty, setSourceDirty] = useState117(false);
|
|
63192
|
+
const [focusedIndex, setFocusedIndex] = useState117(null);
|
|
63193
|
+
const [feedbackMessageCopied, setFeedbackMessageCopied] = useState117(false);
|
|
62604
63194
|
const comments = useFrameComments(data.frames);
|
|
62605
63195
|
const { refreshPending } = comments;
|
|
62606
|
-
|
|
63196
|
+
useEffect100(() => {
|
|
62607
63197
|
void refreshPending();
|
|
62608
63198
|
}, [data.signature, refreshPending]);
|
|
62609
|
-
|
|
63199
|
+
useEffect100(() => {
|
|
63200
|
+
if (comments.draftCount > 0) setFeedbackMessageCopied(false);
|
|
63201
|
+
}, [comments.draftCount]);
|
|
63202
|
+
const saveFeedbackAndCopyMessage = async () => {
|
|
63203
|
+
const saved = await comments.submit();
|
|
63204
|
+
if (!saved) return;
|
|
63205
|
+
try {
|
|
63206
|
+
await navigator.clipboard.writeText(APPLY_STORYBOARD_FEEDBACK_MESSAGE);
|
|
63207
|
+
setFeedbackMessageCopied(true);
|
|
63208
|
+
} catch {
|
|
63209
|
+
setFeedbackMessageCopied(false);
|
|
63210
|
+
}
|
|
63211
|
+
};
|
|
63212
|
+
const sourceFiles = useMemo49(() => {
|
|
62610
63213
|
const files = [{ path: data.path, label: data.path }];
|
|
62611
63214
|
if (data.script?.exists) files.push({ path: data.script.path, label: data.script.path });
|
|
62612
63215
|
return files;
|
|
@@ -62620,7 +63223,7 @@ function StoryboardLoaded({
|
|
|
62620
63223
|
};
|
|
62621
63224
|
const focusedFrame = focusedIndex != null ? data.frames.find((f) => f.index === focusedIndex) ?? null : null;
|
|
62622
63225
|
if (focusedFrame) {
|
|
62623
|
-
return /* @__PURE__ */
|
|
63226
|
+
return /* @__PURE__ */ jsx155(
|
|
62624
63227
|
StoryboardFrameFocus,
|
|
62625
63228
|
{
|
|
62626
63229
|
projectId,
|
|
@@ -62631,28 +63234,57 @@ function StoryboardLoaded({
|
|
|
62631
63234
|
onNavigate: (delta) => setFocusedIndex(clampIndex(focusedFrame.index + delta, data.frames.length)),
|
|
62632
63235
|
onSaved: reload,
|
|
62633
63236
|
onSelectComposition,
|
|
63237
|
+
scriptExists: Boolean(data.script?.exists),
|
|
63238
|
+
commentDraft: comments.drafts[focusedFrame.index] ?? "",
|
|
63239
|
+
onCommentDraftChange: (text) => comments.setDraft(focusedFrame.index, text),
|
|
63240
|
+
pendingComment: comments.pending?.find((entry) => entry.frame === focusedFrame.index)?.text ?? null,
|
|
63241
|
+
pendingCommentCount: comments.pending?.length ?? 0,
|
|
63242
|
+
commentDraftCount: comments.draftCount,
|
|
63243
|
+
commentsSubmitState: comments.submitState,
|
|
63244
|
+
commentsSubmitError: comments.submitError,
|
|
63245
|
+
feedbackMessageCopied,
|
|
63246
|
+
onFeedbackMessageCopied: () => setFeedbackMessageCopied(true),
|
|
63247
|
+
onSaveFeedback: () => void saveFeedbackAndCopyMessage(),
|
|
62634
63248
|
posterVersion: data.signature
|
|
62635
63249
|
},
|
|
62636
63250
|
focusedFrame.index
|
|
62637
63251
|
);
|
|
62638
63252
|
}
|
|
62639
|
-
return /* @__PURE__ */ jsxs132("div", { className: "flex flex-1 min-h-0 flex-col bg-neutral-950 text-neutral-200", children: [
|
|
62640
|
-
/* @__PURE__ */ jsxs132("div", { className: "flex items-center gap-3 border-b border-neutral-800 px-4 py-2", children: [
|
|
62641
|
-
/* @__PURE__ */
|
|
62642
|
-
subView === "board" && /* @__PURE__ */
|
|
63253
|
+
return /* @__PURE__ */ jsxs132("div", { className: "flex w-full max-w-[100vw] flex-1 min-h-0 min-w-0 flex-col overflow-hidden bg-neutral-950 text-neutral-200", children: [
|
|
63254
|
+
/* @__PURE__ */ jsxs132("div", { className: "flex flex-wrap items-center gap-3 border-b border-neutral-800 px-4 py-2", children: [
|
|
63255
|
+
/* @__PURE__ */ jsx155(SubViewToggle, { value: subView, onChange: changeSubView }),
|
|
63256
|
+
subView === "board" && /* @__PURE__ */ jsx155(
|
|
62643
63257
|
CommentsSubmitBar,
|
|
62644
63258
|
{
|
|
62645
63259
|
draftCount: comments.draftCount,
|
|
62646
63260
|
pendingCount: comments.pending?.length ?? 0,
|
|
62647
63261
|
submitState: comments.submitState,
|
|
62648
|
-
|
|
63262
|
+
submitError: comments.submitError,
|
|
63263
|
+
messageCopied: feedbackMessageCopied,
|
|
63264
|
+
onSave: () => void saveFeedbackAndCopyMessage(),
|
|
63265
|
+
onMessageCopied: () => setFeedbackMessageCopied(true)
|
|
62649
63266
|
}
|
|
62650
63267
|
)
|
|
62651
63268
|
] }),
|
|
62652
|
-
subView === "board" ? /* @__PURE__ */
|
|
62653
|
-
/* @__PURE__ */
|
|
62654
|
-
/* @__PURE__ */
|
|
62655
|
-
|
|
63269
|
+
subView === "board" ? /* @__PURE__ */ jsx155("div", { className: "flex-1 min-h-0 overflow-auto", children: /* @__PURE__ */ jsxs132("div", { className: "mx-auto max-w-[1400px] px-4 py-5 sm:px-8 sm:py-8", children: [
|
|
63270
|
+
/* @__PURE__ */ jsx155(StoryboardDirection, { globals: data.globals, frameCount: data.frames.length }),
|
|
63271
|
+
/* @__PURE__ */ jsx155(
|
|
63272
|
+
StoryboardReviewGuide,
|
|
63273
|
+
{
|
|
63274
|
+
frames: data.frames,
|
|
63275
|
+
draftCount: comments.draftCount,
|
|
63276
|
+
pendingCount: comments.pending?.length ?? 0,
|
|
63277
|
+
onFeedbackMessageCopied: () => setFeedbackMessageCopied(true)
|
|
63278
|
+
}
|
|
63279
|
+
),
|
|
63280
|
+
/* @__PURE__ */ jsx155(
|
|
63281
|
+
StoryboardWarnings,
|
|
63282
|
+
{
|
|
63283
|
+
warnings: data.warnings,
|
|
63284
|
+
onOpenSource: () => changeSubView("source")
|
|
63285
|
+
}
|
|
63286
|
+
),
|
|
63287
|
+
/* @__PURE__ */ jsx155(
|
|
62656
63288
|
StoryboardGrid,
|
|
62657
63289
|
{
|
|
62658
63290
|
projectId,
|
|
@@ -62664,8 +63296,8 @@ function StoryboardLoaded({
|
|
|
62664
63296
|
posterVersion: data.signature
|
|
62665
63297
|
}
|
|
62666
63298
|
),
|
|
62667
|
-
data.script && /* @__PURE__ */
|
|
62668
|
-
] }) }) : /* @__PURE__ */
|
|
63299
|
+
data.script && /* @__PURE__ */ jsx155(StoryboardScriptPanel, { script: data.script })
|
|
63300
|
+
] }) }) : /* @__PURE__ */ jsx155(
|
|
62669
63301
|
StoryboardSourceEditor,
|
|
62670
63302
|
{
|
|
62671
63303
|
files: sourceFiles,
|
|
@@ -62679,24 +63311,67 @@ function CommentsSubmitBar({
|
|
|
62679
63311
|
draftCount,
|
|
62680
63312
|
pendingCount,
|
|
62681
63313
|
submitState,
|
|
62682
|
-
|
|
62683
|
-
|
|
62684
|
-
|
|
62685
|
-
|
|
62686
|
-
|
|
62687
|
-
|
|
62688
|
-
|
|
62689
|
-
"
|
|
63314
|
+
submitError,
|
|
63315
|
+
messageCopied,
|
|
63316
|
+
onSave,
|
|
63317
|
+
onMessageCopied
|
|
63318
|
+
}) {
|
|
63319
|
+
return /* @__PURE__ */ jsxs132("div", { className: "ml-auto flex min-w-0 flex-1 flex-wrap items-center justify-end gap-2 sm:flex-none", children: [
|
|
63320
|
+
pendingCount > 0 && /* @__PURE__ */ jsxs132(Fragment46, { children: [
|
|
63321
|
+
/* @__PURE__ */ jsx155("span", { className: "text-xs text-sky-300", children: messageCopied ? "Feedback saved \xB7 Message copied \u2014 paste it in your terminal or IDE agent chat." : "Feedback saved \xB7 Agent not notified." }),
|
|
63322
|
+
/* @__PURE__ */ jsx155(
|
|
63323
|
+
AgentChatMessageButton,
|
|
63324
|
+
{
|
|
63325
|
+
message: APPLY_STORYBOARD_FEEDBACK_MESSAGE,
|
|
63326
|
+
label: messageCopied ? "Copy again" : "Copy prompt for agent",
|
|
63327
|
+
onCopied: onMessageCopied
|
|
63328
|
+
}
|
|
63329
|
+
)
|
|
62690
63330
|
] }),
|
|
62691
|
-
/* @__PURE__ */
|
|
63331
|
+
pendingCount === 0 && draftCount === 0 && /* @__PURE__ */ jsx155("span", { className: "text-xs text-neutral-500", children: "Add frame comments to request changes." }),
|
|
63332
|
+
submitError && /* @__PURE__ */ jsxs132("span", { className: "max-w-64 truncate text-xs text-red-400", title: submitError, children: [
|
|
63333
|
+
"Couldn\u2019t submit: ",
|
|
63334
|
+
submitError
|
|
63335
|
+
] }),
|
|
63336
|
+
draftCount > 0 && /* @__PURE__ */ jsxs132(
|
|
62692
63337
|
Button,
|
|
62693
63338
|
{
|
|
62694
63339
|
variant: "primary",
|
|
62695
63340
|
size: "sm",
|
|
62696
63341
|
loading: submitState === "saving",
|
|
62697
|
-
disabled:
|
|
62698
|
-
onClick:
|
|
62699
|
-
children:
|
|
63342
|
+
disabled: submitState === "saving",
|
|
63343
|
+
onClick: onSave,
|
|
63344
|
+
children: [
|
|
63345
|
+
"Save & copy message (",
|
|
63346
|
+
draftCount,
|
|
63347
|
+
")"
|
|
63348
|
+
]
|
|
63349
|
+
}
|
|
63350
|
+
)
|
|
63351
|
+
] });
|
|
63352
|
+
}
|
|
63353
|
+
function StoryboardWarnings({
|
|
63354
|
+
warnings,
|
|
63355
|
+
onOpenSource
|
|
63356
|
+
}) {
|
|
63357
|
+
if (warnings.length === 0) return null;
|
|
63358
|
+
return /* @__PURE__ */ jsxs132("details", { className: "mt-3 rounded-lg border border-amber-900/60 bg-amber-950/20 px-4 py-2 text-xs text-amber-200", children: [
|
|
63359
|
+
/* @__PURE__ */ jsxs132("summary", { className: "cursor-pointer font-medium", children: [
|
|
63360
|
+
warnings.length,
|
|
63361
|
+
" storyboard warning",
|
|
63362
|
+
warnings.length === 1 ? "" : "s"
|
|
63363
|
+
] }),
|
|
63364
|
+
/* @__PURE__ */ jsx155("ul", { className: "mt-2 space-y-1 text-amber-200/80", children: warnings.map((warning, index) => /* @__PURE__ */ jsxs132("li", { children: [
|
|
63365
|
+
warning.line ? `Line ${warning.line}: ` : "",
|
|
63366
|
+
warning.message
|
|
63367
|
+
] }, `${warning.line ?? "unknown"}-${index}`)) }),
|
|
63368
|
+
/* @__PURE__ */ jsx155(
|
|
63369
|
+
"button",
|
|
63370
|
+
{
|
|
63371
|
+
type: "button",
|
|
63372
|
+
onClick: onOpenSource,
|
|
63373
|
+
className: "mt-2 rounded text-amber-100 underline underline-offset-2 hover:text-white",
|
|
63374
|
+
children: "Open source to fix"
|
|
62700
63375
|
}
|
|
62701
63376
|
)
|
|
62702
63377
|
] });
|
|
@@ -62714,14 +63389,14 @@ function SubViewToggle({ value, onChange }) {
|
|
|
62714
63389
|
const next = SUB_VIEWS[(currentIndex + delta + SUB_VIEWS.length) % SUB_VIEWS.length];
|
|
62715
63390
|
if (next) onChange(next.value);
|
|
62716
63391
|
};
|
|
62717
|
-
return /* @__PURE__ */
|
|
63392
|
+
return /* @__PURE__ */ jsx155(
|
|
62718
63393
|
"div",
|
|
62719
63394
|
{
|
|
62720
63395
|
className: "flex items-center gap-0.5 rounded-md bg-neutral-900 p-0.5",
|
|
62721
63396
|
role: "tablist",
|
|
62722
63397
|
"aria-label": "Storyboard view",
|
|
62723
63398
|
onKeyDown: handleKeyDown,
|
|
62724
|
-
children: SUB_VIEWS.map((option) => /* @__PURE__ */
|
|
63399
|
+
children: SUB_VIEWS.map((option) => /* @__PURE__ */ jsx155(
|
|
62725
63400
|
"button",
|
|
62726
63401
|
{
|
|
62727
63402
|
type: "button",
|
|
@@ -62739,25 +63414,25 @@ function SubViewToggle({ value, onChange }) {
|
|
|
62739
63414
|
}
|
|
62740
63415
|
|
|
62741
63416
|
// src/components/storyboard/StoryboardView.tsx
|
|
62742
|
-
import { jsx as
|
|
63417
|
+
import { jsx as jsx156, jsxs as jsxs133 } from "react/jsx-runtime";
|
|
62743
63418
|
function StoryboardView({ projectId, onSelectComposition }) {
|
|
62744
63419
|
const { data, loading, error, reload } = useStoryboard(projectId);
|
|
62745
63420
|
useProjectSignaturePoll(projectId, data?.signature, reload);
|
|
62746
|
-
if (loading) return /* @__PURE__ */
|
|
63421
|
+
if (loading) return /* @__PURE__ */ jsx156(StoryboardFrame, { children: /* @__PURE__ */ jsx156(Message, { children: "Loading storyboard\u2026" }) });
|
|
62747
63422
|
if (error) {
|
|
62748
63423
|
return /* @__PURE__ */ jsxs133(StoryboardFrame, { children: [
|
|
62749
63424
|
/* @__PURE__ */ jsxs133(Message, { tone: "error", children: [
|
|
62750
63425
|
"Couldn\u2019t load the storyboard: ",
|
|
62751
63426
|
error
|
|
62752
63427
|
] }),
|
|
62753
|
-
/* @__PURE__ */
|
|
63428
|
+
/* @__PURE__ */ jsx156("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx156(Button, { size: "sm", variant: "secondary", onClick: reload, children: "Retry" }) })
|
|
62754
63429
|
] });
|
|
62755
63430
|
}
|
|
62756
|
-
if (!data) return /* @__PURE__ */
|
|
63431
|
+
if (!data) return /* @__PURE__ */ jsx156(StoryboardFrame, { children: null });
|
|
62757
63432
|
if (!data.exists) {
|
|
62758
|
-
return /* @__PURE__ */
|
|
63433
|
+
return /* @__PURE__ */ jsx156(StoryboardFrame, { children: /* @__PURE__ */ jsx156(EmptyState, { path: data.path }) });
|
|
62759
63434
|
}
|
|
62760
|
-
return /* @__PURE__ */
|
|
63435
|
+
return /* @__PURE__ */ jsx156(
|
|
62761
63436
|
StoryboardLoaded,
|
|
62762
63437
|
{
|
|
62763
63438
|
projectId,
|
|
@@ -62768,10 +63443,10 @@ function StoryboardView({ projectId, onSelectComposition }) {
|
|
|
62768
63443
|
);
|
|
62769
63444
|
}
|
|
62770
63445
|
function StoryboardFrame({ children }) {
|
|
62771
|
-
return /* @__PURE__ */
|
|
63446
|
+
return /* @__PURE__ */ jsx156("div", { className: "flex-1 min-h-0 overflow-auto bg-neutral-950 text-neutral-200", children: /* @__PURE__ */ jsx156("div", { className: "mx-auto max-w-[1400px] px-8 py-8", children }) });
|
|
62772
63447
|
}
|
|
62773
63448
|
function Message({ children, tone = "muted" }) {
|
|
62774
|
-
return /* @__PURE__ */
|
|
63449
|
+
return /* @__PURE__ */ jsx156(
|
|
62775
63450
|
"div",
|
|
62776
63451
|
{
|
|
62777
63452
|
className: `px-6 py-12 text-center text-sm ${tone === "error" ? "text-red-400" : "text-neutral-500"}`,
|
|
@@ -62804,7 +63479,7 @@ Add one \`## Frame N\` section per beat. Keep the arc tight.
|
|
|
62804
63479
|
Then run the review loop from the hyperframes-core skill (references/review-loop.md): present the plan as a proposal, offer wireframe sketches on this board, and build on the confirmed layouts.`;
|
|
62805
63480
|
}
|
|
62806
63481
|
function EmptyState({ path }) {
|
|
62807
|
-
const [copied, setCopied] =
|
|
63482
|
+
const [copied, setCopied] = useState118(false);
|
|
62808
63483
|
const prompt = handoffPrompt(path);
|
|
62809
63484
|
const onCopy = async () => {
|
|
62810
63485
|
if (await copyTextToClipboard(prompt)) {
|
|
@@ -62814,67 +63489,67 @@ function EmptyState({ path }) {
|
|
|
62814
63489
|
};
|
|
62815
63490
|
return /* @__PURE__ */ jsxs133("div", { children: [
|
|
62816
63491
|
/* @__PURE__ */ jsxs133("div", { className: "rounded-lg border border-dashed border-neutral-800 px-6 py-10 text-center", children: [
|
|
62817
|
-
/* @__PURE__ */
|
|
63492
|
+
/* @__PURE__ */ jsx156("h2", { className: "text-base font-semibold text-neutral-300", children: "No storyboard yet" }),
|
|
62818
63493
|
/* @__PURE__ */ jsxs133("p", { className: "mx-auto mt-2 max-w-md text-sm text-neutral-500", children: [
|
|
62819
63494
|
"Add a ",
|
|
62820
|
-
/* @__PURE__ */
|
|
63495
|
+
/* @__PURE__ */ jsx156("code", { className: "rounded bg-neutral-900 px-1 py-0.5 text-neutral-400", children: path }),
|
|
62821
63496
|
" ",
|
|
62822
63497
|
"at the project root to plan this video frame by frame. Hand this prompt to your coding agent to scaffold it."
|
|
62823
63498
|
] }),
|
|
62824
63499
|
/* @__PURE__ */ jsxs133("div", { className: "mx-auto mt-6 max-w-2xl overflow-hidden rounded-lg border border-neutral-800 bg-neutral-900 text-left", children: [
|
|
62825
63500
|
/* @__PURE__ */ jsxs133("div", { className: "flex items-center justify-between border-b border-neutral-800 px-3 py-2", children: [
|
|
62826
|
-
/* @__PURE__ */
|
|
62827
|
-
/* @__PURE__ */
|
|
63501
|
+
/* @__PURE__ */ jsx156("span", { className: "font-mono text-xs text-neutral-500", children: "Prompt for your agent" }),
|
|
63502
|
+
/* @__PURE__ */ jsx156(
|
|
62828
63503
|
Button,
|
|
62829
63504
|
{
|
|
62830
63505
|
size: "sm",
|
|
62831
63506
|
variant: "secondary",
|
|
62832
63507
|
onClick: onCopy,
|
|
62833
|
-
icon: copied ? /* @__PURE__ */
|
|
63508
|
+
icon: copied ? /* @__PURE__ */ jsx156(Check2, { size: 14 }) : /* @__PURE__ */ jsx156(Copy2, { size: 14 }),
|
|
62834
63509
|
children: copied ? "Copied" : "Copy prompt"
|
|
62835
63510
|
}
|
|
62836
63511
|
)
|
|
62837
63512
|
] }),
|
|
62838
|
-
/* @__PURE__ */
|
|
63513
|
+
/* @__PURE__ */ jsx156("pre", { className: "max-h-64 overflow-auto px-4 py-3 font-mono text-xs leading-relaxed text-neutral-400 whitespace-pre-wrap", children: prompt })
|
|
62839
63514
|
] })
|
|
62840
63515
|
] }),
|
|
62841
|
-
/* @__PURE__ */
|
|
63516
|
+
/* @__PURE__ */ jsx156(SkeletonPreview, {})
|
|
62842
63517
|
] });
|
|
62843
63518
|
}
|
|
62844
63519
|
function SkeletonPreview() {
|
|
62845
63520
|
return /* @__PURE__ */ jsxs133("div", { "aria-hidden": "true", className: "mt-10 select-none opacity-40", children: [
|
|
62846
|
-
/* @__PURE__ */
|
|
62847
|
-
/* @__PURE__ */
|
|
62848
|
-
/* @__PURE__ */
|
|
62849
|
-
/* @__PURE__ */
|
|
62850
|
-
/* @__PURE__ */
|
|
62851
|
-
/* @__PURE__ */
|
|
63521
|
+
/* @__PURE__ */ jsx156("div", { className: "mb-4 text-center text-xs uppercase tracking-wide text-neutral-600", children: "Preview" }),
|
|
63522
|
+
/* @__PURE__ */ jsx156("div", { className: "grid gap-x-6 gap-y-8 [grid-template-columns:repeat(auto-fill,minmax(300px,1fr))]", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs133("div", { className: "rounded-lg border border-neutral-800 bg-neutral-900/40 p-3", children: [
|
|
63523
|
+
/* @__PURE__ */ jsx156("div", { className: "aspect-video w-full rounded bg-neutral-800/60" }),
|
|
63524
|
+
/* @__PURE__ */ jsx156("div", { className: "mt-3 h-3 w-2/3 rounded bg-neutral-800/60" }),
|
|
63525
|
+
/* @__PURE__ */ jsx156("div", { className: "mt-2 h-2.5 w-full rounded bg-neutral-800/40" }),
|
|
63526
|
+
/* @__PURE__ */ jsx156("div", { className: "mt-1.5 h-2.5 w-4/5 rounded bg-neutral-800/40" })
|
|
62852
63527
|
] }, i)) })
|
|
62853
63528
|
] });
|
|
62854
63529
|
}
|
|
62855
63530
|
|
|
62856
63531
|
// src/components/StudioSplash.tsx
|
|
62857
|
-
import { jsx as
|
|
63532
|
+
import { jsx as jsx157, jsxs as jsxs134 } from "react/jsx-runtime";
|
|
62858
63533
|
function StudioSplash({ waiting }) {
|
|
62859
|
-
return /* @__PURE__ */
|
|
62860
|
-
/* @__PURE__ */
|
|
63534
|
+
return /* @__PURE__ */ jsx157("div", { className: "h-full w-full bg-neutral-950 flex items-center justify-center", children: waiting ? /* @__PURE__ */ jsxs134("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
|
|
63535
|
+
/* @__PURE__ */ jsx157("div", { className: "w-4 h-4 rounded-full border-2 border-neutral-700 border-t-neutral-500 animate-spin motion-reduce:animate-none" }),
|
|
62861
63536
|
/* @__PURE__ */ jsxs134("p", { className: "text-xs text-neutral-600", children: [
|
|
62862
63537
|
"Waiting for preview server\u2026 run",
|
|
62863
63538
|
" ",
|
|
62864
|
-
/* @__PURE__ */
|
|
63539
|
+
/* @__PURE__ */ jsx157("code", { className: "text-neutral-500 font-mono", children: "npm run dev" })
|
|
62865
63540
|
] })
|
|
62866
63541
|
] }) : /* @__PURE__ */ jsxs134("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
|
|
62867
|
-
/* @__PURE__ */
|
|
62868
|
-
/* @__PURE__ */
|
|
63542
|
+
/* @__PURE__ */ jsx157("div", { className: "w-4 h-4 rounded-full bg-studio-accent animate-pulse motion-reduce:animate-none" }),
|
|
63543
|
+
/* @__PURE__ */ jsx157("p", { className: "text-xs text-neutral-600", children: "Connecting to project\u2026" })
|
|
62869
63544
|
] }) });
|
|
62870
63545
|
}
|
|
62871
63546
|
|
|
62872
63547
|
// src/hooks/useServerConnection.ts
|
|
62873
|
-
import { useEffect as
|
|
63548
|
+
import { useEffect as useEffect101, useState as useState119 } from "react";
|
|
62874
63549
|
function useServerConnection() {
|
|
62875
|
-
const [projectId, setProjectId] =
|
|
62876
|
-
const [resolving, setResolving] =
|
|
62877
|
-
const [waitingForServer, setWaitingForServer] =
|
|
63550
|
+
const [projectId, setProjectId] = useState119(null);
|
|
63551
|
+
const [resolving, setResolving] = useState119(true);
|
|
63552
|
+
const [waitingForServer, setWaitingForServer] = useState119(false);
|
|
62878
63553
|
useMountEffect(() => {
|
|
62879
63554
|
const hashProjectId = parseProjectIdFromHash(window.location.hash);
|
|
62880
63555
|
let cancelled = false;
|
|
@@ -62911,7 +63586,7 @@ function useServerConnection() {
|
|
|
62911
63586
|
if (retryTimer !== null) clearTimeout(retryTimer);
|
|
62912
63587
|
};
|
|
62913
63588
|
});
|
|
62914
|
-
|
|
63589
|
+
useEffect101(() => {
|
|
62915
63590
|
const onHashChange = () => {
|
|
62916
63591
|
const next = parseProjectIdFromHash(window.location.hash);
|
|
62917
63592
|
if (next && next !== projectId) setProjectId(next);
|
|
@@ -62923,56 +63598,56 @@ function useServerConnection() {
|
|
|
62923
63598
|
}
|
|
62924
63599
|
|
|
62925
63600
|
// src/App.tsx
|
|
62926
|
-
import { jsx as
|
|
63601
|
+
import { jsx as jsx158, jsxs as jsxs135 } from "react/jsx-runtime";
|
|
62927
63602
|
function StudioApp() {
|
|
62928
63603
|
const { projectId, resolving, waitingForServer } = useServerConnection();
|
|
62929
|
-
const initialUrlStateRef =
|
|
63604
|
+
const initialUrlStateRef = useRef120(readStudioUrlStateFromWindow());
|
|
62930
63605
|
const viewModeValue = useViewModeState();
|
|
62931
|
-
|
|
63606
|
+
useEffect102(() => {
|
|
62932
63607
|
if (resolving || waitingForServer) return;
|
|
62933
63608
|
if (hasFiredSessionStart()) return;
|
|
62934
63609
|
markSessionStartFired();
|
|
62935
63610
|
trackStudioSessionStart({ has_project: projectId != null });
|
|
62936
63611
|
}, [projectId, resolving, waitingForServer]);
|
|
62937
|
-
const [activeCompPath, setActiveCompPath] =
|
|
62938
|
-
const [activeCompPathHydrated, setActiveCompPathHydrated] =
|
|
63612
|
+
const [activeCompPath, setActiveCompPath] = useState120(null);
|
|
63613
|
+
const [activeCompPathHydrated, setActiveCompPathHydrated] = useState120(
|
|
62939
63614
|
() => initialUrlStateRef.current.activeCompPath == null
|
|
62940
63615
|
);
|
|
62941
|
-
const [compIdToSrc, setCompIdToSrc] =
|
|
62942
|
-
const [previewIframe, setPreviewIframe] =
|
|
62943
|
-
const [compositionLoading, setCompositionLoading] =
|
|
62944
|
-
const [refreshKey, setRefreshKey] =
|
|
63616
|
+
const [compIdToSrc, setCompIdToSrc] = useState120(/* @__PURE__ */ new Map());
|
|
63617
|
+
const [previewIframe, setPreviewIframe] = useState120(null);
|
|
63618
|
+
const [compositionLoading, setCompositionLoading] = useState120(true);
|
|
63619
|
+
const [refreshKey, setRefreshKey] = useState120(0);
|
|
62945
63620
|
const [previewDocumentVersion, refreshPreviewDocumentVersion] = usePreviewDocumentVersion();
|
|
62946
|
-
const [blockPreview, setBlockPreview] =
|
|
62947
|
-
const previewIframeRef =
|
|
62948
|
-
const activeCompPathRef =
|
|
63621
|
+
const [blockPreview, setBlockPreview] = useState120(null);
|
|
63622
|
+
const previewIframeRef = useRef120(null);
|
|
63623
|
+
const activeCompPathRef = useRef120(activeCompPath);
|
|
62949
63624
|
activeCompPathRef.current = activeCompPath;
|
|
62950
|
-
const leftSidebarRef =
|
|
63625
|
+
const leftSidebarRef = useRef120(null);
|
|
62951
63626
|
const renderQueue = useRenderQueue(projectId);
|
|
62952
63627
|
const captionEditMode = useCaptionStore((s) => s.isEditMode);
|
|
62953
63628
|
const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
|
|
62954
63629
|
const captionSync = useCaptionSync(projectId);
|
|
62955
63630
|
const timelineElements = usePlayerStore((s) => s.elements);
|
|
62956
63631
|
const setSelectedTimelineElementId = usePlayerStore((s) => s.setSelectedElementId);
|
|
62957
|
-
const
|
|
63632
|
+
const timelineDuration2 = usePlayerStore((s) => s.duration);
|
|
62958
63633
|
const isPlaying = usePlayerStore((s) => s.isPlaying);
|
|
62959
63634
|
const isMasterView = !activeCompPath || activeCompPath === "index.html";
|
|
62960
63635
|
const activePreviewUrl = activeCompPath ? `/api/projects/${projectId}/preview/comp/${activeCompPath}` : null;
|
|
62961
|
-
const effectiveTimelineDuration =
|
|
63636
|
+
const effectiveTimelineDuration = useMemo50(() => {
|
|
62962
63637
|
const maxEnd = timelineElements.length > 0 ? Math.max(...timelineElements.map((el) => el.start + el.duration)) : 0;
|
|
62963
|
-
return Math.max(
|
|
62964
|
-
}, [
|
|
63638
|
+
return Math.max(timelineDuration2, maxEnd);
|
|
63639
|
+
}, [timelineDuration2, timelineElements]);
|
|
62965
63640
|
const { toasts, showToast, dismissToast } = useToast();
|
|
62966
63641
|
const panelLayout = usePanelLayout({
|
|
62967
63642
|
rightCollapsed: initialUrlStateRef.current.rightCollapsed,
|
|
62968
63643
|
rightPanelTab: initialUrlStateRef.current.rightPanelTab
|
|
62969
63644
|
});
|
|
62970
63645
|
const editHistory = usePersistentEditHistory({ projectId });
|
|
62971
|
-
const domEditSaveTimestampRef =
|
|
62972
|
-
const handleDomZIndexReorderCommitRef =
|
|
62973
|
-
const pendingTimelineEditPathRef =
|
|
62974
|
-
const isGestureRecordingRef =
|
|
62975
|
-
const reloadPreview =
|
|
63646
|
+
const domEditSaveTimestampRef = useRef120(0);
|
|
63647
|
+
const handleDomZIndexReorderCommitRef = useRef120(null);
|
|
63648
|
+
const pendingTimelineEditPathRef = useRef120(/* @__PURE__ */ new Set());
|
|
63649
|
+
const isGestureRecordingRef = useRef120(false);
|
|
63650
|
+
const reloadPreview = useCallback142(() => setRefreshKey((k) => k + 1), []);
|
|
62976
63651
|
const fileManager = useFileManager({
|
|
62977
63652
|
projectId,
|
|
62978
63653
|
showToast,
|
|
@@ -62980,7 +63655,7 @@ function StudioApp() {
|
|
|
62980
63655
|
domEditSaveTimestampRef,
|
|
62981
63656
|
setRefreshKey
|
|
62982
63657
|
});
|
|
62983
|
-
const masterCompPath =
|
|
63658
|
+
const masterCompPath = useMemo50(
|
|
62984
63659
|
() => resolveMasterCompositionPath(fileManager.fileTree),
|
|
62985
63660
|
[fileManager.fileTree]
|
|
62986
63661
|
);
|
|
@@ -62990,7 +63665,7 @@ function StudioApp() {
|
|
|
62990
63665
|
domEditSaveTimestampRef,
|
|
62991
63666
|
masterCompPath
|
|
62992
63667
|
);
|
|
62993
|
-
|
|
63668
|
+
useEffect102(() => {
|
|
62994
63669
|
if (activeCompPathHydrated) return;
|
|
62995
63670
|
if (!fileManager.fileTreeLoaded) return;
|
|
62996
63671
|
const nextCompPath = normalizeStudioCompositionPath(
|
|
@@ -63018,6 +63693,7 @@ function StudioApp() {
|
|
|
63018
63693
|
timelineElements,
|
|
63019
63694
|
showToast,
|
|
63020
63695
|
writeProjectFile: fileManager.writeProjectFile,
|
|
63696
|
+
observeProjectFileVersion: fileManager.observeProjectFileVersion,
|
|
63021
63697
|
recordEdit: editHistory.recordEdit,
|
|
63022
63698
|
domEditSaveTimestampRef,
|
|
63023
63699
|
reloadPreview,
|
|
@@ -63030,20 +63706,14 @@ function StudioApp() {
|
|
|
63030
63706
|
forceReloadSdkSession: sdkHandle.forceReload,
|
|
63031
63707
|
handleDomZIndexReorderCommitRef
|
|
63032
63708
|
});
|
|
63033
|
-
const handleTimelineElementsMove =
|
|
63709
|
+
const handleTimelineElementsMove = useCallback142(
|
|
63034
63710
|
async (edits, coalesceKey, operation = "timing", coalesceMs) => {
|
|
63035
63711
|
const deps = { handleTimelineGroupMove: timelineEditing.handleTimelineGroupMove };
|
|
63036
63712
|
await persistTimelineMoveEditsAtomically(edits, coalesceKey, operation, deps, coalesceMs);
|
|
63037
63713
|
},
|
|
63038
63714
|
[timelineEditing.handleTimelineGroupMove]
|
|
63039
63715
|
);
|
|
63040
|
-
const handleAddAssetAtPlayhead =
|
|
63041
|
-
(assetPath) => timelineEditing.handleTimelineAssetDrop(assetPath, {
|
|
63042
|
-
start: usePlayerStore.getState().currentTime,
|
|
63043
|
-
track: 0
|
|
63044
|
-
}),
|
|
63045
|
-
[timelineEditing]
|
|
63046
|
-
);
|
|
63716
|
+
const handleAddAssetAtPlayhead = useAddAssetAtPlayhead(timelineEditing.handleTimelineAssetDrop);
|
|
63047
63717
|
const {
|
|
63048
63718
|
activeBlockParams,
|
|
63049
63719
|
setActiveBlockParams,
|
|
@@ -63066,18 +63736,18 @@ function StudioApp() {
|
|
|
63066
63736
|
setRightCollapsed: panelLayout.setRightCollapsed,
|
|
63067
63737
|
setRightPanelTab: panelLayout.setRightPanelTab
|
|
63068
63738
|
});
|
|
63069
|
-
const clearDomSelectionRef =
|
|
63739
|
+
const clearDomSelectionRef = useRef120(() => {
|
|
63070
63740
|
});
|
|
63071
|
-
const domEditSelectionBridgeRef =
|
|
63072
|
-
const handleDomEditElementDeleteRef =
|
|
63741
|
+
const domEditSelectionBridgeRef = useRef120(null);
|
|
63742
|
+
const handleDomEditElementDeleteRef = useRef120(
|
|
63073
63743
|
async () => {
|
|
63074
63744
|
}
|
|
63075
63745
|
);
|
|
63076
63746
|
const domEditDeleteBridge = (s) => handleDomEditElementDeleteRef.current(s);
|
|
63077
|
-
const resetKeyframesRef =
|
|
63078
|
-
const deleteSelectedKeyframesRef =
|
|
63747
|
+
const resetKeyframesRef = useRef120(() => false);
|
|
63748
|
+
const deleteSelectedKeyframesRef = useRef120(() => {
|
|
63079
63749
|
});
|
|
63080
|
-
const invalidateGsapCacheRef =
|
|
63750
|
+
const invalidateGsapCacheRef = useRef120(() => {
|
|
63081
63751
|
});
|
|
63082
63752
|
const { handleCopy, handlePaste, handleCut } = useClipboard({
|
|
63083
63753
|
projectId,
|
|
@@ -63119,7 +63789,7 @@ function StudioApp() {
|
|
|
63119
63789
|
forceReloadSdkSession: sdkHandle.forceReload,
|
|
63120
63790
|
onToggleRecording: STUDIO_KEYFRAMES_ENABLED ? () => handleToggleRecordingRef.current() : void 0
|
|
63121
63791
|
});
|
|
63122
|
-
const sidebarTabRef =
|
|
63792
|
+
const sidebarTabRef = useRef120({
|
|
63123
63793
|
select: (t) => leftSidebarRef.current?.selectTab(t),
|
|
63124
63794
|
get: () => leftSidebarRef.current?.getTab() ?? "compositions"
|
|
63125
63795
|
});
|
|
@@ -63207,9 +63877,9 @@ function StudioApp() {
|
|
|
63207
63877
|
resetErrors: resetConsoleErrors
|
|
63208
63878
|
} = useConsoleErrorCapture(previewIframe);
|
|
63209
63879
|
const dragOverlay = useGlobalFileDrop(timelineEditing.handleTimelineFileDrop);
|
|
63210
|
-
const handleToggleRecordingRef =
|
|
63880
|
+
const handleToggleRecordingRef = useRef120(() => {
|
|
63211
63881
|
});
|
|
63212
|
-
const domEditSessionRef =
|
|
63882
|
+
const domEditSessionRef = useRef120(domEditSession);
|
|
63213
63883
|
domEditSessionRef.current = domEditSession;
|
|
63214
63884
|
const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
|
|
63215
63885
|
domEditSessionRef,
|
|
@@ -63219,7 +63889,7 @@ function StudioApp() {
|
|
|
63219
63889
|
});
|
|
63220
63890
|
handleToggleRecordingRef.current = handleToggleRecording;
|
|
63221
63891
|
const recordingToggle = STUDIO_KEYFRAMES_ENABLED ? handleToggleRecording : void 0;
|
|
63222
|
-
const canvasRectRef =
|
|
63892
|
+
const canvasRectRef = useRef120(null);
|
|
63223
63893
|
useLayoutEffect4(() => {
|
|
63224
63894
|
if (gestureState !== "recording" || !previewIframe) {
|
|
63225
63895
|
canvasRectRef.current = null;
|
|
@@ -63228,7 +63898,7 @@ function StudioApp() {
|
|
|
63228
63898
|
const r = previewIframe.getBoundingClientRect();
|
|
63229
63899
|
canvasRectRef.current = { left: r.left, top: r.top, width: r.width, height: r.height };
|
|
63230
63900
|
}, [gestureState, previewIframe]);
|
|
63231
|
-
const handlePreviewIframeRef =
|
|
63901
|
+
const handlePreviewIframeRef = useCallback142(
|
|
63232
63902
|
(iframe) => {
|
|
63233
63903
|
previewIframeRef.current = iframe;
|
|
63234
63904
|
setPreviewIframe(iframe);
|
|
@@ -63296,8 +63966,8 @@ function StudioApp() {
|
|
|
63296
63966
|
handlePreviewIframeRef,
|
|
63297
63967
|
refreshPreviewDocumentVersion
|
|
63298
63968
|
});
|
|
63299
|
-
const timelineToolbar =
|
|
63300
|
-
() => /* @__PURE__ */
|
|
63969
|
+
const timelineToolbar = useMemo50(
|
|
63970
|
+
() => /* @__PURE__ */ jsx158(
|
|
63301
63971
|
TimelineToolbar,
|
|
63302
63972
|
{
|
|
63303
63973
|
domEditSession,
|
|
@@ -63307,8 +63977,8 @@ function StudioApp() {
|
|
|
63307
63977
|
[domEditSession, timelineEditing.handleTimelineElementSplit]
|
|
63308
63978
|
);
|
|
63309
63979
|
if (resolving || waitingForServer || !projectId)
|
|
63310
|
-
return /* @__PURE__ */
|
|
63311
|
-
return /* @__PURE__ */
|
|
63980
|
+
return /* @__PURE__ */ jsx158(StudioSplash, { waiting: waitingForServer });
|
|
63981
|
+
return /* @__PURE__ */ jsx158(StudioShellProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx158(StudioPlaybackProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx158(ViewModeProvider, { value: viewModeValue, children: /* @__PURE__ */ jsx158(PanelLayoutProvider, { value: panelLayout, children: /* @__PURE__ */ jsx158(FileManagerProvider, { value: fileManager, children: /* @__PURE__ */ jsx158(DomEditProvider, { value: domEditSession, children: /* @__PURE__ */ jsxs135(
|
|
63312
63982
|
"div",
|
|
63313
63983
|
{
|
|
63314
63984
|
className: "flex flex-col h-full w-full bg-neutral-950 relative",
|
|
@@ -63317,7 +63987,7 @@ function StudioApp() {
|
|
|
63317
63987
|
onDragLeave: dragOverlay.onDragLeave,
|
|
63318
63988
|
onDrop: dragOverlay.onDrop,
|
|
63319
63989
|
children: [
|
|
63320
|
-
/* @__PURE__ */
|
|
63990
|
+
/* @__PURE__ */ jsx158(
|
|
63321
63991
|
StudioHeader,
|
|
63322
63992
|
{
|
|
63323
63993
|
captureFrameHref: frameCapture.captureFrameHref,
|
|
@@ -63335,25 +64005,25 @@ function StudioApp() {
|
|
|
63335
64005
|
}
|
|
63336
64006
|
}
|
|
63337
64007
|
),
|
|
63338
|
-
previewPersistence.domEditSaveQueuePaused && /* @__PURE__ */
|
|
64008
|
+
previewPersistence.domEditSaveQueuePaused && /* @__PURE__ */ jsx158(
|
|
63339
64009
|
SaveQueuePausedBanner,
|
|
63340
64010
|
{
|
|
63341
64011
|
message: previewPersistence.domEditSaveQueuePaused,
|
|
63342
64012
|
onRetry: previewPersistence.resetDomEditSaveQueueBreaker
|
|
63343
64013
|
}
|
|
63344
64014
|
),
|
|
63345
|
-
viewModeValue.viewMode === "storyboard" && /* @__PURE__ */
|
|
64015
|
+
viewModeValue.viewMode === "storyboard" && /* @__PURE__ */ jsx158(
|
|
63346
64016
|
StoryboardView,
|
|
63347
64017
|
{
|
|
63348
64018
|
projectId,
|
|
63349
64019
|
onSelectComposition: handleSelectComposition
|
|
63350
64020
|
}
|
|
63351
64021
|
),
|
|
63352
|
-
/* @__PURE__ */
|
|
64022
|
+
/* @__PURE__ */ jsx158(
|
|
63353
64023
|
EditorShell,
|
|
63354
64024
|
{
|
|
63355
64025
|
hidden: viewModeValue.viewMode === "storyboard",
|
|
63356
|
-
left: /* @__PURE__ */
|
|
64026
|
+
left: /* @__PURE__ */ jsx158(
|
|
63357
64027
|
StudioLeftSidebar,
|
|
63358
64028
|
{
|
|
63359
64029
|
leftSidebarRef,
|
|
@@ -63367,7 +64037,7 @@ function StudioApp() {
|
|
|
63367
64037
|
onAddAssetToTimeline: handleAddAssetAtPlayhead
|
|
63368
64038
|
}
|
|
63369
64039
|
),
|
|
63370
|
-
right: panelLayout.rightCollapsed ? null : /* @__PURE__ */
|
|
64040
|
+
right: panelLayout.rightCollapsed ? null : /* @__PURE__ */ jsx158(
|
|
63371
64041
|
StudioRightPanel,
|
|
63372
64042
|
{
|
|
63373
64043
|
designPanelActive,
|
|
@@ -63381,6 +64051,7 @@ function StudioApp() {
|
|
|
63381
64051
|
onToggleRecording: recordingToggle,
|
|
63382
64052
|
sdkSession: sdkHandle.session,
|
|
63383
64053
|
publishSdkSession: sdkHandle.publish,
|
|
64054
|
+
forceReloadSdkSession: sdkHandle.forceReload,
|
|
63384
64055
|
reloadPreview,
|
|
63385
64056
|
domEditSaveTimestampRef,
|
|
63386
64057
|
recordEdit: editHistory.recordEdit,
|
|
@@ -63410,7 +64081,7 @@ function StudioApp() {
|
|
|
63410
64081
|
recordingState: gestureState,
|
|
63411
64082
|
onToggleRecording: recordingToggle,
|
|
63412
64083
|
blockPreview,
|
|
63413
|
-
gestureOverlay: gestureState === "recording" && previewIframe ? /* @__PURE__ */
|
|
64084
|
+
gestureOverlay: gestureState === "recording" && previewIframe ? /* @__PURE__ */ jsx158(
|
|
63414
64085
|
GestureTrailOverlay,
|
|
63415
64086
|
{
|
|
63416
64087
|
samples: gestureRecording.samplesRef.current,
|
|
@@ -63423,7 +64094,7 @@ function StudioApp() {
|
|
|
63423
64094
|
) : void 0
|
|
63424
64095
|
}
|
|
63425
64096
|
),
|
|
63426
|
-
/* @__PURE__ */
|
|
64097
|
+
/* @__PURE__ */ jsx158(
|
|
63427
64098
|
StudioOverlays,
|
|
63428
64099
|
{
|
|
63429
64100
|
projectId,
|
|
@@ -63445,32 +64116,32 @@ function StudioApp() {
|
|
|
63445
64116
|
}
|
|
63446
64117
|
|
|
63447
64118
|
// src/hooks/useElementPicker.ts
|
|
63448
|
-
import { useState as
|
|
64119
|
+
import { useState as useState121, useCallback as useCallback143, useRef as useRef121 } from "react";
|
|
63449
64120
|
function useElementPicker(iframeRef, options) {
|
|
63450
|
-
const [isPickMode, setIsPickMode] =
|
|
63451
|
-
const [pickedElement, setPickedElement] =
|
|
63452
|
-
const activeOverrideRef =
|
|
63453
|
-
const getActiveIframe =
|
|
64121
|
+
const [isPickMode, setIsPickMode] = useState121(false);
|
|
64122
|
+
const [pickedElement, setPickedElement] = useState121(null);
|
|
64123
|
+
const activeOverrideRef = useRef121(null);
|
|
64124
|
+
const getActiveIframe = useCallback143(() => {
|
|
63454
64125
|
return activeOverrideRef.current ?? iframeRef.current;
|
|
63455
64126
|
}, [iframeRef]);
|
|
63456
|
-
const setActiveIframe =
|
|
64127
|
+
const setActiveIframe = useCallback143((el) => {
|
|
63457
64128
|
activeOverrideRef.current = el;
|
|
63458
64129
|
}, []);
|
|
63459
|
-
const enablePick =
|
|
64130
|
+
const enablePick = useCallback143(() => {
|
|
63460
64131
|
try {
|
|
63461
64132
|
postRuntimeControlMessage(getActiveIframe()?.contentWindow, "enable-pick-mode");
|
|
63462
64133
|
setIsPickMode(true);
|
|
63463
64134
|
} catch {
|
|
63464
64135
|
}
|
|
63465
64136
|
}, [getActiveIframe]);
|
|
63466
|
-
const disablePick =
|
|
64137
|
+
const disablePick = useCallback143(() => {
|
|
63467
64138
|
try {
|
|
63468
64139
|
postRuntimeControlMessage(getActiveIframe()?.contentWindow, "disable-pick-mode");
|
|
63469
64140
|
} catch {
|
|
63470
64141
|
}
|
|
63471
64142
|
setIsPickMode(false);
|
|
63472
64143
|
}, [getActiveIframe]);
|
|
63473
|
-
const clearPick =
|
|
64144
|
+
const clearPick = useCallback143(() => {
|
|
63474
64145
|
setPickedElement(null);
|
|
63475
64146
|
}, []);
|
|
63476
64147
|
useMountEffect(() => {
|
|
@@ -63523,9 +64194,9 @@ function useElementPicker(iframeRef, options) {
|
|
|
63523
64194
|
window.addEventListener("message", handleMessage);
|
|
63524
64195
|
return () => window.removeEventListener("message", handleMessage);
|
|
63525
64196
|
});
|
|
63526
|
-
const optionsRef =
|
|
64197
|
+
const optionsRef = useRef121(options);
|
|
63527
64198
|
optionsRef.current = options;
|
|
63528
|
-
const syncToSource =
|
|
64199
|
+
const syncToSource = useCallback143(
|
|
63529
64200
|
(elementId, selector, op) => {
|
|
63530
64201
|
const opts = optionsRef.current;
|
|
63531
64202
|
if (!opts?.workspaceFiles || !opts.onSyncFiles || !elementId) return;
|
|
@@ -63541,7 +64212,7 @@ function useElementPicker(iframeRef, options) {
|
|
|
63541
64212
|
},
|
|
63542
64213
|
[]
|
|
63543
64214
|
);
|
|
63544
|
-
const setStyle =
|
|
64215
|
+
const setStyle = useCallback143(
|
|
63545
64216
|
(prop, value) => {
|
|
63546
64217
|
const activeIframe = getActiveIframe();
|
|
63547
64218
|
if (!pickedElement?.selector || !activeIframe) return;
|
|
@@ -63583,7 +64254,7 @@ function useElementPicker(iframeRef, options) {
|
|
|
63583
64254
|
},
|
|
63584
64255
|
[pickedElement, getActiveIframe, syncToSource]
|
|
63585
64256
|
);
|
|
63586
|
-
const setDataAttr =
|
|
64257
|
+
const setDataAttr = useCallback143(
|
|
63587
64258
|
(attr, value) => {
|
|
63588
64259
|
const activeIframe = getActiveIframe();
|
|
63589
64260
|
if (!pickedElement?.selector || !activeIframe) return;
|
|
@@ -63611,7 +64282,7 @@ function useElementPicker(iframeRef, options) {
|
|
|
63611
64282
|
},
|
|
63612
64283
|
[pickedElement, getActiveIframe, syncToSource]
|
|
63613
64284
|
);
|
|
63614
|
-
const setTextContent =
|
|
64285
|
+
const setTextContent = useCallback143(
|
|
63615
64286
|
(text) => {
|
|
63616
64287
|
const activeIframe = getActiveIframe();
|
|
63617
64288
|
if (!pickedElement?.selector || !activeIframe) return;
|
|
@@ -63634,7 +64305,7 @@ function useElementPicker(iframeRef, options) {
|
|
|
63634
64305
|
},
|
|
63635
64306
|
[pickedElement, getActiveIframe, syncToSource]
|
|
63636
64307
|
);
|
|
63637
|
-
const activeIframeRef =
|
|
64308
|
+
const activeIframeRef = useRef121(null);
|
|
63638
64309
|
activeIframeRef.current = getActiveIframe();
|
|
63639
64310
|
return {
|
|
63640
64311
|
isPickMode,
|