@hyperframes/studio 0.8.20 → 0.8.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/hyperframes-player-DmtkkaPN.js +460 -0
- package/dist/assets/{index-CBSKXKAr.js → index-BIf0zQNt.js} +164 -164
- package/dist/assets/{index-Cehosfnu.js → index-DXR3nsh2.js} +1 -1
- package/dist/assets/{index-BL1zYTW0.js → index-DivnGS5G.js} +1 -1
- package/dist/index.d.ts +28 -0
- package/dist/index.html +1 -1
- package/dist/index.js +1170 -227
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +2 -0
- package/src/contexts/StudioContext.tsx +10 -0
- package/src/hooks/useStudioContextValue.ts +9 -0
- package/src/player/hooks/timelineSyncHydration.test.ts +96 -0
- package/src/player/hooks/timelineSyncHydration.ts +47 -1
- package/src/player/hooks/useExpandedTimelineElements.test.ts +43 -0
- package/src/player/hooks/useExpandedTimelineElements.ts +52 -29
- package/src/player/hooks/useTimelineSyncCallbacks.ts +4 -0
- package/src/player/store/playerStore.ts +17 -2
- package/src/player/store/timelineElement.ts +21 -0
- package/src/webmcp/StudioAgentTools.tsx +105 -10
- package/src/webmcp/toolResult.ts +1 -1
- package/src/webmcp/tools/animationTools.test.ts +244 -0
- package/src/webmcp/tools/animationTools.ts +276 -0
- package/src/webmcp/tools/contentTools.test.ts +275 -0
- package/src/webmcp/tools/contentTools.ts +217 -0
- package/src/webmcp/tools/frameTools.test.ts +136 -0
- package/src/webmcp/tools/frameTools.ts +133 -0
- package/src/webmcp/tools/inspectTools.test.ts +197 -0
- package/src/webmcp/tools/inspectTools.ts +208 -0
- package/src/webmcp/tools/selectionTools.test.ts +164 -0
- package/src/webmcp/tools/selectionTools.ts +154 -0
- package/src/webmcp/tools/transformTools.test.ts +179 -0
- package/src/webmcp/tools/transformTools.ts +205 -0
- package/src/webmcp/useStudioAgentTools.test.tsx +71 -22
- package/src/webmcp/useStudioAgentTools.ts +195 -1
- package/src/webmcp/webmcpTestUtils.ts +91 -0
- package/dist/assets/hyperframes-player-BEKxuimO.js +0 -459
package/dist/index.js
CHANGED
|
@@ -1618,7 +1618,8 @@ function createTimelineResetState() {
|
|
|
1618
1618
|
beatPersist: null,
|
|
1619
1619
|
clipManifest: null,
|
|
1620
1620
|
clipParentMap: /* @__PURE__ */ new Map(),
|
|
1621
|
-
domClipChildren: []
|
|
1621
|
+
domClipChildren: [],
|
|
1622
|
+
subCompositionHostState: /* @__PURE__ */ new Map()
|
|
1622
1623
|
};
|
|
1623
1624
|
}
|
|
1624
1625
|
var usePlayerStore = create2((set, get) => ({
|
|
@@ -1744,6 +1745,8 @@ var usePlayerStore = create2((set, get) => ({
|
|
|
1744
1745
|
setClipParentMap: (map) => set({ clipParentMap: map }),
|
|
1745
1746
|
domClipChildren: [],
|
|
1746
1747
|
setDomClipChildren: (children) => set({ domClipChildren: children }),
|
|
1748
|
+
subCompositionHostState: /* @__PURE__ */ new Map(),
|
|
1749
|
+
setSubCompositionHostState: (state) => set({ subCompositionHostState: state }),
|
|
1747
1750
|
setIsPlaying: (playing) => {
|
|
1748
1751
|
if (get().isPlaying === playing) return;
|
|
1749
1752
|
set({ isPlaying: playing });
|
|
@@ -4352,22 +4355,12 @@ function childGroupState(flat, domChild) {
|
|
|
4352
4355
|
audioGroupAutomation: source.audioGroupAutomation
|
|
4353
4356
|
};
|
|
4354
4357
|
}
|
|
4355
|
-
function hostElementState(flat) {
|
|
4356
|
-
if (!flat) return {};
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
// Same reason as the three above: these are read off the host element, which
|
|
4362
|
-
// an expanded child is built without. Missing them, an audio child inside a
|
|
4363
|
-
// sub-composition reserved no automation height and drew no lanes, while the
|
|
4364
|
-
// property panel — reading the live DOM selection rather than this row —
|
|
4365
|
-
// still showed the chain and its toggles.
|
|
4366
|
-
fxChain: flat.fxChain,
|
|
4367
|
-
automation: flat.automation
|
|
4368
|
-
};
|
|
4369
|
-
}
|
|
4370
|
-
function buildChildElements(siblings, display2, editBasis, expandedHostKey, elements, domChildrenById) {
|
|
4358
|
+
function hostElementState(flat, live) {
|
|
4359
|
+
if (!flat) return { ...live };
|
|
4360
|
+
const { hidden, timelineLocked, timelineRole, fxChain, automation } = flat;
|
|
4361
|
+
return { hidden, timelineLocked, timelineRole, fxChain, automation, ...live };
|
|
4362
|
+
}
|
|
4363
|
+
function buildChildElements(siblings, display2, editBasis, expandedHostKey, elements, domChildrenById, hostStateById) {
|
|
4371
4364
|
const result = [];
|
|
4372
4365
|
for (const child of siblings) {
|
|
4373
4366
|
const clamped = clampChildToParent(child, display2.start, display2.end);
|
|
@@ -4388,7 +4381,10 @@ function buildChildElements(siblings, display2, editBasis, expandedHostKey, elem
|
|
|
4388
4381
|
});
|
|
4389
4382
|
result.push({
|
|
4390
4383
|
...base2,
|
|
4391
|
-
...hostElementState(
|
|
4384
|
+
...hostElementState(
|
|
4385
|
+
elements.find((element) => element.key === key2),
|
|
4386
|
+
domId ? hostStateById.get(domId) : void 0
|
|
4387
|
+
),
|
|
4392
4388
|
...childGroupState(
|
|
4393
4389
|
elements.find((element) => element.key === key2),
|
|
4394
4390
|
domId ? domChildrenById.get(domId) : void 0
|
|
@@ -4448,7 +4444,7 @@ function domSiblingClips(domClipChildren, siblingParentId, host) {
|
|
|
4448
4444
|
})
|
|
4449
4445
|
);
|
|
4450
4446
|
}
|
|
4451
|
-
function buildExpandedElements(elements, manifest, parentMap, topLevelId, siblingParentId, domClipChildren = []) {
|
|
4447
|
+
function buildExpandedElements(elements, manifest, parentMap, topLevelId, siblingParentId, domClipChildren = [], subCompositionHostState = /* @__PURE__ */ new Map()) {
|
|
4452
4448
|
const topLevelElement = elements.find((el) => el.id === topLevelId || el.domId === topLevelId);
|
|
4453
4449
|
if (!topLevelElement) return filterToTopLevel(elements, parentMap);
|
|
4454
4450
|
const parentHost = manifest.find((c) => c.id === siblingParentId);
|
|
@@ -4476,7 +4472,8 @@ function buildExpandedElements(elements, manifest, parentMap, topLevelId, siblin
|
|
|
4476
4472
|
editBasis,
|
|
4477
4473
|
parentKey,
|
|
4478
4474
|
elements,
|
|
4479
|
-
domChildrenById
|
|
4475
|
+
domChildrenById,
|
|
4476
|
+
subCompositionHostState
|
|
4480
4477
|
);
|
|
4481
4478
|
if (expanded.length === 0) return filterToTopLevel(elements, parentMap);
|
|
4482
4479
|
const drillPath = /* @__PURE__ */ new Set();
|
|
@@ -4496,6 +4493,7 @@ function useExpandedTimelineElements() {
|
|
|
4496
4493
|
const clipManifest = usePlayerStore((s) => s.clipManifest);
|
|
4497
4494
|
const clipParentMap = usePlayerStore((s) => s.clipParentMap);
|
|
4498
4495
|
const domClipChildren = usePlayerStore((s) => s.domClipChildren);
|
|
4496
|
+
const subCompositionHostState = usePlayerStore((s) => s.subCompositionHostState);
|
|
4499
4497
|
const selectedElementId = usePlayerStore((s) => s.selectedElementId);
|
|
4500
4498
|
const currentTime = usePlayerStore((s) => s.currentTime);
|
|
4501
4499
|
const { rawId, selectedRawId } = useMemo3(() => {
|
|
@@ -4526,9 +4524,18 @@ function useExpandedTimelineElements() {
|
|
|
4526
4524
|
clipParentMap,
|
|
4527
4525
|
topLevel,
|
|
4528
4526
|
immediateParent,
|
|
4529
|
-
domClipChildren
|
|
4527
|
+
domClipChildren,
|
|
4528
|
+
subCompositionHostState
|
|
4530
4529
|
);
|
|
4531
|
-
}, [
|
|
4530
|
+
}, [
|
|
4531
|
+
elements,
|
|
4532
|
+
clipManifest,
|
|
4533
|
+
clipParentMap,
|
|
4534
|
+
domClipChildren,
|
|
4535
|
+
subCompositionHostState,
|
|
4536
|
+
rawId,
|
|
4537
|
+
selectedRawId
|
|
4538
|
+
]);
|
|
4532
4539
|
}
|
|
4533
4540
|
|
|
4534
4541
|
// src/player/components/timelineTheme.ts
|
|
@@ -21204,6 +21211,7 @@ function StudioShellProvider({
|
|
|
21204
21211
|
showToast,
|
|
21205
21212
|
previewIframeRef,
|
|
21206
21213
|
editHistory,
|
|
21214
|
+
writeBlockedReason,
|
|
21207
21215
|
handleUndo,
|
|
21208
21216
|
handleRedo,
|
|
21209
21217
|
renderQueue,
|
|
@@ -21219,6 +21227,7 @@ function StudioShellProvider({
|
|
|
21219
21227
|
showToast,
|
|
21220
21228
|
previewIframeRef,
|
|
21221
21229
|
editHistory,
|
|
21230
|
+
writeBlockedReason,
|
|
21222
21231
|
handleUndo,
|
|
21223
21232
|
handleRedo,
|
|
21224
21233
|
renderQueue,
|
|
@@ -21235,6 +21244,7 @@ function StudioShellProvider({
|
|
|
21235
21244
|
setActiveCompPath,
|
|
21236
21245
|
showToast,
|
|
21237
21246
|
previewIframeRef,
|
|
21247
|
+
writeBlockedReason,
|
|
21238
21248
|
handleUndo,
|
|
21239
21249
|
handleRedo,
|
|
21240
21250
|
waitForPendingDomEditSaves,
|
|
@@ -24843,6 +24853,32 @@ function collectSubCompositionDomChildren(iframeDoc, clips, parentMap) {
|
|
|
24843
24853
|
}
|
|
24844
24854
|
return out;
|
|
24845
24855
|
}
|
|
24856
|
+
function readSubCompositionHostState(el) {
|
|
24857
|
+
const state = {};
|
|
24858
|
+
if (el.hasAttribute("data-hidden")) state.hidden = true;
|
|
24859
|
+
if (el.hasAttribute("data-timeline-locked")) state.timelineLocked = true;
|
|
24860
|
+
const timelineRole = el.getAttribute("data-timeline-role");
|
|
24861
|
+
if (timelineRole) state.timelineRole = timelineRole;
|
|
24862
|
+
const fxChain = el.getAttribute("data-fx-chain");
|
|
24863
|
+
if (fxChain) state.fxChain = fxChain;
|
|
24864
|
+
const automation = el.getAttribute("data-automation");
|
|
24865
|
+
if (automation) state.automation = automation;
|
|
24866
|
+
return Object.keys(state).length > 0 ? state : null;
|
|
24867
|
+
}
|
|
24868
|
+
function collectSubCompositionHostState(iframeDoc, clips) {
|
|
24869
|
+
const out = /* @__PURE__ */ new Map();
|
|
24870
|
+
if (!iframeDoc) return out;
|
|
24871
|
+
for (const clip of clips) {
|
|
24872
|
+
if (clip.kind !== "composition" || !clip.id) continue;
|
|
24873
|
+
const hostEl = iframeDoc.getElementById(clip.id);
|
|
24874
|
+
if (!hostEl) continue;
|
|
24875
|
+
for (const el of Array.from(hostEl.querySelectorAll("[id]"))) {
|
|
24876
|
+
const state = readSubCompositionHostState(el);
|
|
24877
|
+
if (state) out.set(el.id, state);
|
|
24878
|
+
}
|
|
24879
|
+
}
|
|
24880
|
+
return out;
|
|
24881
|
+
}
|
|
24846
24882
|
function safeContentDocument(iframe) {
|
|
24847
24883
|
try {
|
|
24848
24884
|
return iframe?.contentDocument ?? null;
|
|
@@ -25000,6 +25036,7 @@ function useTimelineSyncCallbacks({
|
|
|
25000
25036
|
const domClipChildren = collectSubCompositionDomChildren(iframeDoc, data.clips, parentMap);
|
|
25001
25037
|
usePlayerStore.getState().setClipParentMap(parentMap);
|
|
25002
25038
|
usePlayerStore.getState().setDomClipChildren(domClipChildren);
|
|
25039
|
+
usePlayerStore.getState().setSubCompositionHostState(collectSubCompositionHostState(iframeDoc, data.clips));
|
|
25003
25040
|
} catch {
|
|
25004
25041
|
}
|
|
25005
25042
|
const els = buildTimelineElementsFromClips(filtered, iframeDoc);
|
|
@@ -40584,7 +40621,7 @@ function useTimelineSelectionPreviewSync({
|
|
|
40584
40621
|
}
|
|
40585
40622
|
|
|
40586
40623
|
// src/webmcp/StudioAgentTools.tsx
|
|
40587
|
-
import { useCallback as useCallback60 } from "react";
|
|
40624
|
+
import { useCallback as useCallback60, useMemo as useMemo37 } from "react";
|
|
40588
40625
|
|
|
40589
40626
|
// src/webmcp/useStudioAgentTools.ts
|
|
40590
40627
|
import { useEffect as useEffect49, useRef as useRef68 } from "react";
|
|
@@ -40731,6 +40768,43 @@ function patchTargetAddress(target) {
|
|
|
40731
40768
|
selectorIndex: target.selectorIndex
|
|
40732
40769
|
};
|
|
40733
40770
|
}
|
|
40771
|
+
function parseElementHandle(handle) {
|
|
40772
|
+
const separatorAt = handle.indexOf(SEPARATOR);
|
|
40773
|
+
if (separatorAt <= 0) return null;
|
|
40774
|
+
const scheme = handle.slice(0, separatorAt);
|
|
40775
|
+
const rest = handle.slice(separatorAt + 1);
|
|
40776
|
+
if (!rest) return null;
|
|
40777
|
+
if (scheme === "hf" || scheme === "dom") return { scheme, value: rest, index: 0 };
|
|
40778
|
+
if (scheme !== "sel") return null;
|
|
40779
|
+
const indexAt = rest.lastIndexOf(INDEX_SEPARATOR);
|
|
40780
|
+
if (indexAt <= 0) return { scheme, value: rest, index: 0 };
|
|
40781
|
+
const index = Number(rest.slice(indexAt + 1));
|
|
40782
|
+
if (!Number.isInteger(index) || index < 0) return { scheme, value: rest, index: 0 };
|
|
40783
|
+
return { scheme, value: rest.slice(0, indexAt), index };
|
|
40784
|
+
}
|
|
40785
|
+
function resolveElementHandle(doc, handle) {
|
|
40786
|
+
const parsed = parseElementHandle(handle);
|
|
40787
|
+
if (!parsed) return null;
|
|
40788
|
+
if (parsed.scheme === "dom") return asHtmlElement(doc, doc.getElementById(parsed.value));
|
|
40789
|
+
if (parsed.scheme === "hf") {
|
|
40790
|
+
return asHtmlElement(doc, doc.querySelector(`[data-hf-id="${cssEscape(parsed.value)}"]`));
|
|
40791
|
+
}
|
|
40792
|
+
let matches;
|
|
40793
|
+
try {
|
|
40794
|
+
matches = doc.querySelectorAll(parsed.value);
|
|
40795
|
+
} catch {
|
|
40796
|
+
return null;
|
|
40797
|
+
}
|
|
40798
|
+
return asHtmlElement(doc, matches.item(parsed.index));
|
|
40799
|
+
}
|
|
40800
|
+
function cssEscape(value) {
|
|
40801
|
+
return typeof CSS?.escape === "function" ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
|
|
40802
|
+
}
|
|
40803
|
+
function asHtmlElement(doc, node) {
|
|
40804
|
+
if (!node) return null;
|
|
40805
|
+
const ctor = doc.defaultView?.HTMLElement;
|
|
40806
|
+
return ctor && node instanceof ctor ? node : null;
|
|
40807
|
+
}
|
|
40734
40808
|
|
|
40735
40809
|
// src/webmcp/tools/lookTools.ts
|
|
40736
40810
|
var DEFAULT_LIMIT = 200;
|
|
@@ -40816,6 +40890,744 @@ var STUDIO_LOOK_DESCRIPTION = [
|
|
|
40816
40890
|
"you did not do, a human pressed undo and your earlier edits are gone."
|
|
40817
40891
|
].join(" ");
|
|
40818
40892
|
|
|
40893
|
+
// src/webmcp/tools/selectionTools.ts
|
|
40894
|
+
async function studioSelect(deps, handle) {
|
|
40895
|
+
if (typeof handle !== "string" || !handle.trim()) {
|
|
40896
|
+
return toolFailure("invalid", "handle must be a non-empty string", "Call studio_look first.");
|
|
40897
|
+
}
|
|
40898
|
+
const doc = deps.getPreviewDocument();
|
|
40899
|
+
if (!doc) {
|
|
40900
|
+
return toolFailure(
|
|
40901
|
+
"blocked",
|
|
40902
|
+
"the preview is not mounted yet",
|
|
40903
|
+
"Wait for the composition to load, then retry."
|
|
40904
|
+
);
|
|
40905
|
+
}
|
|
40906
|
+
const element = resolveElementHandle(doc, handle);
|
|
40907
|
+
if (!element) {
|
|
40908
|
+
return toolFailure(
|
|
40909
|
+
"invalid",
|
|
40910
|
+
`no element matches handle ${handle}`,
|
|
40911
|
+
"The composition may have changed. Call studio_look for current handles."
|
|
40912
|
+
);
|
|
40913
|
+
}
|
|
40914
|
+
const selection = await deps.buildSelection(element);
|
|
40915
|
+
if (!selection) {
|
|
40916
|
+
return toolFailure(
|
|
40917
|
+
"blocked",
|
|
40918
|
+
`${handle} resolved to an element Studio cannot select`,
|
|
40919
|
+
"Try a parent or child element from studio_look."
|
|
40920
|
+
);
|
|
40921
|
+
}
|
|
40922
|
+
deps.applySelection(selection);
|
|
40923
|
+
return toolOk({
|
|
40924
|
+
handle: mintElementHandle(patchTargetAddress(selection)),
|
|
40925
|
+
label: selection.label,
|
|
40926
|
+
tagName: selection.tagName,
|
|
40927
|
+
box: selection.boundingBox
|
|
40928
|
+
});
|
|
40929
|
+
}
|
|
40930
|
+
function studioSeek(deps, time) {
|
|
40931
|
+
if (typeof time !== "number" || !Number.isFinite(time)) {
|
|
40932
|
+
return toolFailure("invalid", "time must be a finite number of seconds");
|
|
40933
|
+
}
|
|
40934
|
+
const before = deps.readPlayhead();
|
|
40935
|
+
deps.requestSeek(time);
|
|
40936
|
+
const after = deps.readPlayhead();
|
|
40937
|
+
const moved = after.currentTime !== before.currentTime;
|
|
40938
|
+
if (!moved && before.currentTime !== time) {
|
|
40939
|
+
return toolFailure(
|
|
40940
|
+
"blocked",
|
|
40941
|
+
`the playhead did not move; it is still at ${after.currentTime}`,
|
|
40942
|
+
"The preview may not be ready. Check studio_look, then retry."
|
|
40943
|
+
);
|
|
40944
|
+
}
|
|
40945
|
+
return toolOk({
|
|
40946
|
+
playhead: after.currentTime,
|
|
40947
|
+
duration: after.duration,
|
|
40948
|
+
isPlaying: after.isPlaying,
|
|
40949
|
+
moved
|
|
40950
|
+
});
|
|
40951
|
+
}
|
|
40952
|
+
var STUDIO_SELECT_INPUT_SCHEMA = {
|
|
40953
|
+
type: "object",
|
|
40954
|
+
properties: {
|
|
40955
|
+
handle: { type: "string", description: "An element handle from studio_look." }
|
|
40956
|
+
},
|
|
40957
|
+
required: ["handle"],
|
|
40958
|
+
additionalProperties: false
|
|
40959
|
+
};
|
|
40960
|
+
var STUDIO_SELECT_DESCRIPTION = [
|
|
40961
|
+
"Select an element in HyperFrames Studio, exactly as clicking it would:",
|
|
40962
|
+
"the human sees the same selection box and inspector.",
|
|
40963
|
+
"Takes a handle from studio_look. Most editing tools act on the CURRENT selection,",
|
|
40964
|
+
"so call this first, then the edit.",
|
|
40965
|
+
"Returns `ok: true` with the resulting selection, or `ok: false` with `kind`, `reason` and a `hint`."
|
|
40966
|
+
].join(" ");
|
|
40967
|
+
var STUDIO_SEEK_INPUT_SCHEMA = {
|
|
40968
|
+
type: "object",
|
|
40969
|
+
properties: {
|
|
40970
|
+
time: { type: "number", minimum: 0, description: "Playhead position in seconds." }
|
|
40971
|
+
},
|
|
40972
|
+
required: ["time"],
|
|
40973
|
+
additionalProperties: false
|
|
40974
|
+
};
|
|
40975
|
+
var STUDIO_SEEK_DESCRIPTION = [
|
|
40976
|
+
"Move the playhead to a time in seconds. Pauses playback.",
|
|
40977
|
+
"Out-of-range times are clamped by the player, so check the returned `playhead`",
|
|
40978
|
+
"for where it actually landed rather than assuming it matched your request.",
|
|
40979
|
+
"Returns `ok: true`, or `ok: false` with `kind`, `reason` and a `hint`."
|
|
40980
|
+
].join(" ");
|
|
40981
|
+
|
|
40982
|
+
// src/utils/projectRouting.ts
|
|
40983
|
+
var PROJECT_HASH_PREFIX = "#project/";
|
|
40984
|
+
function decodeHashProjectId(value) {
|
|
40985
|
+
try {
|
|
40986
|
+
return decodeURIComponent(value);
|
|
40987
|
+
} catch {
|
|
40988
|
+
return value;
|
|
40989
|
+
}
|
|
40990
|
+
}
|
|
40991
|
+
function normalizeHashParams(params) {
|
|
40992
|
+
if (!params) return new URLSearchParams();
|
|
40993
|
+
if (params instanceof URLSearchParams) return params;
|
|
40994
|
+
const next = new URLSearchParams();
|
|
40995
|
+
for (const [key2, value] of Object.entries(params)) {
|
|
40996
|
+
if (!key2 || value == null || value === "") continue;
|
|
40997
|
+
next.set(key2, value);
|
|
40998
|
+
}
|
|
40999
|
+
return next;
|
|
41000
|
+
}
|
|
41001
|
+
function encodeProjectId(projectId) {
|
|
41002
|
+
return encodeURIComponent(projectId);
|
|
41003
|
+
}
|
|
41004
|
+
function buildProjectHash(projectId, params) {
|
|
41005
|
+
const search = normalizeHashParams(params).toString();
|
|
41006
|
+
return `${PROJECT_HASH_PREFIX}${encodeProjectId(projectId)}${search ? `?${search}` : ""}`;
|
|
41007
|
+
}
|
|
41008
|
+
function parseProjectHashRoute(hash) {
|
|
41009
|
+
if (!hash.startsWith(PROJECT_HASH_PREFIX)) return null;
|
|
41010
|
+
const route = hash.slice(PROJECT_HASH_PREFIX.length);
|
|
41011
|
+
const queryIndex = route.indexOf("?");
|
|
41012
|
+
const encodedProjectId = queryIndex >= 0 ? route.slice(0, queryIndex) : route;
|
|
41013
|
+
if (!encodedProjectId || encodedProjectId.includes("/")) return null;
|
|
41014
|
+
const rawParams = queryIndex >= 0 ? route.slice(queryIndex + 1) : "";
|
|
41015
|
+
return {
|
|
41016
|
+
projectId: decodeHashProjectId(encodedProjectId),
|
|
41017
|
+
params: new URLSearchParams(rawParams)
|
|
41018
|
+
};
|
|
41019
|
+
}
|
|
41020
|
+
function parseProjectIdFromHash(hash) {
|
|
41021
|
+
return parseProjectHashRoute(hash)?.projectId ?? null;
|
|
41022
|
+
}
|
|
41023
|
+
function buildProjectApiPath(projectId, suffix = "") {
|
|
41024
|
+
const normalizedSuffix = suffix && !suffix.startsWith("/") ? `/${suffix}` : suffix;
|
|
41025
|
+
return `/api/projects/${encodeProjectId(projectId)}${normalizedSuffix}`;
|
|
41026
|
+
}
|
|
41027
|
+
|
|
41028
|
+
// src/utils/frameCapture.ts
|
|
41029
|
+
function normalizeCompositionPath(compositionPath) {
|
|
41030
|
+
return compositionPath && compositionPath !== "master" ? compositionPath : "index.html";
|
|
41031
|
+
}
|
|
41032
|
+
function buildFrameCaptureUrl({
|
|
41033
|
+
projectId,
|
|
41034
|
+
compositionPath,
|
|
41035
|
+
currentTime,
|
|
41036
|
+
origin = window.location.origin
|
|
41037
|
+
}) {
|
|
41038
|
+
const compPath = normalizeCompositionPath(compositionPath);
|
|
41039
|
+
const url = new URL(
|
|
41040
|
+
buildProjectApiPath(projectId, `/thumbnail/${encodeURIComponent(compPath)}`),
|
|
41041
|
+
origin
|
|
41042
|
+
);
|
|
41043
|
+
url.searchParams.set("t", Math.max(0, currentTime).toFixed(3));
|
|
41044
|
+
url.searchParams.set("format", "png");
|
|
41045
|
+
url.searchParams.set("output", "source");
|
|
41046
|
+
url.searchParams.set("v", String(Date.now()));
|
|
41047
|
+
return url.toString();
|
|
41048
|
+
}
|
|
41049
|
+
function buildFrameCaptureFilename(compositionPath, currentTime) {
|
|
41050
|
+
const compPath = normalizeCompositionPath(compositionPath);
|
|
41051
|
+
const base2 = compPath.split("/").pop()?.replace(/\.html$/i, "") || "frame";
|
|
41052
|
+
const frameTime = Math.max(0, currentTime).toFixed(3).replace(".", "-");
|
|
41053
|
+
return `${base2}-${frameTime}s.png`;
|
|
41054
|
+
}
|
|
41055
|
+
|
|
41056
|
+
// src/webmcp/tools/frameTools.ts
|
|
41057
|
+
var DEFAULT_SETTLE_MS = 150;
|
|
41058
|
+
var MAX_SETTLE_MS = 5e3;
|
|
41059
|
+
async function studioFrame(deps, input = {}) {
|
|
41060
|
+
const projectId = deps.getProjectId();
|
|
41061
|
+
if (!projectId) {
|
|
41062
|
+
return toolFailure("blocked", "no project is open");
|
|
41063
|
+
}
|
|
41064
|
+
if (input.time !== void 0) {
|
|
41065
|
+
if (typeof input.time !== "number" || !Number.isFinite(input.time) || input.time < 0) {
|
|
41066
|
+
return toolFailure("invalid", "time must be a non-negative, finite number of seconds");
|
|
41067
|
+
}
|
|
41068
|
+
deps.requestSeek(input.time);
|
|
41069
|
+
}
|
|
41070
|
+
const settledMs = clampSettle(input.settleMs);
|
|
41071
|
+
if (settledMs > 0) await deps.wait(settledMs);
|
|
41072
|
+
const { currentTime } = deps.readPlayhead();
|
|
41073
|
+
const compositionPath = deps.getCompositionPath();
|
|
41074
|
+
const url = buildFrameCaptureUrl({ projectId, compositionPath, currentTime });
|
|
41075
|
+
const probe2 = await deps.probeFrame(url);
|
|
41076
|
+
if (!probe2.ok) {
|
|
41077
|
+
return toolFailure(
|
|
41078
|
+
"failed",
|
|
41079
|
+
`the renderer returned ${probe2.status} for this frame`,
|
|
41080
|
+
"The composition may not build. Try `hyperframes check`."
|
|
41081
|
+
);
|
|
41082
|
+
}
|
|
41083
|
+
return toolOk({
|
|
41084
|
+
url,
|
|
41085
|
+
time: currentTime,
|
|
41086
|
+
compositionPath: compositionPath ?? "index.html",
|
|
41087
|
+
settledMs
|
|
41088
|
+
});
|
|
41089
|
+
}
|
|
41090
|
+
function clampSettle(requested) {
|
|
41091
|
+
if (requested === void 0) return DEFAULT_SETTLE_MS;
|
|
41092
|
+
if (typeof requested !== "number" || !Number.isFinite(requested) || requested < 0) {
|
|
41093
|
+
return DEFAULT_SETTLE_MS;
|
|
41094
|
+
}
|
|
41095
|
+
return Math.min(requested, MAX_SETTLE_MS);
|
|
41096
|
+
}
|
|
41097
|
+
var STUDIO_FRAME_INPUT_SCHEMA = {
|
|
41098
|
+
type: "object",
|
|
41099
|
+
properties: {
|
|
41100
|
+
time: {
|
|
41101
|
+
type: "number",
|
|
41102
|
+
minimum: 0,
|
|
41103
|
+
description: "Seconds. Omit to capture wherever the playhead already is."
|
|
41104
|
+
},
|
|
41105
|
+
settleMs: {
|
|
41106
|
+
type: "integer",
|
|
41107
|
+
minimum: 0,
|
|
41108
|
+
maximum: MAX_SETTLE_MS,
|
|
41109
|
+
description: `Wait this long before capturing so a just-made edit is included. Default ${DEFAULT_SETTLE_MS}.`
|
|
41110
|
+
}
|
|
41111
|
+
},
|
|
41112
|
+
additionalProperties: false
|
|
41113
|
+
};
|
|
41114
|
+
var STUDIO_FRAME_DESCRIPTION = [
|
|
41115
|
+
"Render the composition to a PNG at a given time and return its URL, so you can",
|
|
41116
|
+
"SEE the result instead of inferring it from source. Use this to judge a change:",
|
|
41117
|
+
"edit, capture the instant it affects, look, adjust.",
|
|
41118
|
+
"The frame is rendered from the file on disk, not the live preview.",
|
|
41119
|
+
"A capture taken immediately after an edit can therefore predate that edit, because",
|
|
41120
|
+
"the render cache is cleared by a file watcher. The tool waits briefly to cover that;",
|
|
41121
|
+
"raise `settleMs` if a frame still looks stale, rather than concluding the edit failed.",
|
|
41122
|
+
"Returns `ok: true` with `url` and the `time` actually captured, or `ok: false`."
|
|
41123
|
+
].join(" ");
|
|
41124
|
+
|
|
41125
|
+
// src/webmcp/tools/inspectTools.ts
|
|
41126
|
+
function describeAnimation(animation) {
|
|
41127
|
+
return {
|
|
41128
|
+
animationId: animation.id,
|
|
41129
|
+
method: animation.method,
|
|
41130
|
+
target: animation.targetSelector,
|
|
41131
|
+
position: animation.position,
|
|
41132
|
+
duration: animation.duration ?? null,
|
|
41133
|
+
ease: animation.ease ?? null,
|
|
41134
|
+
properties: animation.properties,
|
|
41135
|
+
hasKeyframes: animation.keyframes !== void 0,
|
|
41136
|
+
hasArcPath: animation.arcPath !== void 0
|
|
41137
|
+
};
|
|
41138
|
+
}
|
|
41139
|
+
function describe(selection, deps, isCurrentSelection) {
|
|
41140
|
+
const { capabilities } = selection;
|
|
41141
|
+
const gsap3 = deps.getGsapDiagnostics();
|
|
41142
|
+
const animations = isCurrentSelection ? gsap3.animations.map(describeAnimation) : [];
|
|
41143
|
+
let animationEditingBlocked = null;
|
|
41144
|
+
if (!isCurrentSelection) {
|
|
41145
|
+
animationEditingBlocked = "animations are only readable for the current selection";
|
|
41146
|
+
} else if (gsap3.multipleTimelines) {
|
|
41147
|
+
animationEditingBlocked = "this composition has multiple GSAP timelines";
|
|
41148
|
+
} else if (gsap3.unsupportedTimelinePattern) {
|
|
41149
|
+
animationEditingBlocked = "this composition's timeline pattern is not editable by Studio";
|
|
41150
|
+
}
|
|
41151
|
+
return toolOk({
|
|
41152
|
+
handle: mintElementHandle(patchTargetAddress(selection)),
|
|
41153
|
+
label: selection.label,
|
|
41154
|
+
tagName: selection.tagName,
|
|
41155
|
+
sourceFile: selection.sourceFile,
|
|
41156
|
+
box: selection.boundingBox,
|
|
41157
|
+
text: selection.textContent,
|
|
41158
|
+
textFields: selection.textFields.map((field) => ({
|
|
41159
|
+
key: field.key,
|
|
41160
|
+
label: field.label,
|
|
41161
|
+
value: field.value,
|
|
41162
|
+
tagName: field.tagName
|
|
41163
|
+
})),
|
|
41164
|
+
styles: selection.computedStyles,
|
|
41165
|
+
inlineStyles: selection.inlineStyles,
|
|
41166
|
+
dataAttributes: selection.dataAttributes,
|
|
41167
|
+
can: {
|
|
41168
|
+
editStyles: capabilities.canEditStyles,
|
|
41169
|
+
move: capabilities.canMove || capabilities.canApplyManualOffset,
|
|
41170
|
+
resize: capabilities.canResize || capabilities.canApplyManualSize,
|
|
41171
|
+
rotate: capabilities.canApplyManualRotation,
|
|
41172
|
+
crop: capabilities.canCrop,
|
|
41173
|
+
editText: selection.textFields.length > 0,
|
|
41174
|
+
reasonIfDisabled: capabilities.reasonIfDisabled ?? null
|
|
41175
|
+
},
|
|
41176
|
+
animations,
|
|
41177
|
+
animationEditingBlocked,
|
|
41178
|
+
isCurrentSelection
|
|
41179
|
+
});
|
|
41180
|
+
}
|
|
41181
|
+
async function studioInspect(deps, input = {}) {
|
|
41182
|
+
const current = deps.getCurrentSelection();
|
|
41183
|
+
if (!input.handle) {
|
|
41184
|
+
if (!current) {
|
|
41185
|
+
return toolFailure(
|
|
41186
|
+
"invalid",
|
|
41187
|
+
"nothing is selected and no handle was given",
|
|
41188
|
+
"Pass a handle from studio_look, or call studio_select first."
|
|
41189
|
+
);
|
|
41190
|
+
}
|
|
41191
|
+
return describe(current, deps, true);
|
|
41192
|
+
}
|
|
41193
|
+
const doc = deps.getPreviewDocument();
|
|
41194
|
+
if (!doc) return toolFailure("blocked", "the preview is not mounted yet");
|
|
41195
|
+
const element = resolveElementHandle(doc, input.handle);
|
|
41196
|
+
if (!element) {
|
|
41197
|
+
return toolFailure(
|
|
41198
|
+
"invalid",
|
|
41199
|
+
`no element matches handle ${input.handle}`,
|
|
41200
|
+
"Call studio_look for current handles."
|
|
41201
|
+
);
|
|
41202
|
+
}
|
|
41203
|
+
const selection = await deps.buildSelection(element);
|
|
41204
|
+
if (!selection) {
|
|
41205
|
+
return toolFailure("blocked", `${input.handle} resolved to an element Studio cannot inspect`);
|
|
41206
|
+
}
|
|
41207
|
+
return describe(selection, deps, current?.element === element);
|
|
41208
|
+
}
|
|
41209
|
+
var STUDIO_INSPECT_INPUT_SCHEMA = {
|
|
41210
|
+
type: "object",
|
|
41211
|
+
properties: {
|
|
41212
|
+
handle: {
|
|
41213
|
+
type: "string",
|
|
41214
|
+
description: "An element handle from studio_look. Omit to inspect the current selection."
|
|
41215
|
+
}
|
|
41216
|
+
},
|
|
41217
|
+
additionalProperties: false
|
|
41218
|
+
};
|
|
41219
|
+
var STUDIO_INSPECT_DESCRIPTION = [
|
|
41220
|
+
"Everything about one element: its resolved styles, its text fields, its box,",
|
|
41221
|
+
"its GSAP animations, and crucially what it will and will not accept.",
|
|
41222
|
+
"Read this BEFORE editing. `can` tells you which edits are possible and",
|
|
41223
|
+
"`can.reasonIfDisabled` says why one is not, so you can avoid a write that would be refused.",
|
|
41224
|
+
"Animations are only readable for the CURRENT selection; `animationEditingBlocked` says when",
|
|
41225
|
+
"and why animation editing is unavailable.",
|
|
41226
|
+
"Returns `ok: true`, or `ok: false` with `kind`, `reason` and a `hint`."
|
|
41227
|
+
].join(" ");
|
|
41228
|
+
|
|
41229
|
+
// src/webmcp/tools/contentTools.ts
|
|
41230
|
+
var DECLINE_HINTS = {
|
|
41231
|
+
"no-selection": { kind: "invalid", hint: "Call studio_select first." },
|
|
41232
|
+
"no-project": { kind: "blocked" },
|
|
41233
|
+
"geometry-property": {
|
|
41234
|
+
kind: "blocked",
|
|
41235
|
+
hint: "Position and size are not editable as styles. Use the transform tools."
|
|
41236
|
+
},
|
|
41237
|
+
"styles-not-editable": {
|
|
41238
|
+
kind: "blocked",
|
|
41239
|
+
hint: "studio_inspect reports why, in can.reasonIfDisabled."
|
|
41240
|
+
},
|
|
41241
|
+
"not-text-editable": {
|
|
41242
|
+
kind: "blocked",
|
|
41243
|
+
hint: "This element has no editable text. studio_inspect lists its textFields."
|
|
41244
|
+
},
|
|
41245
|
+
"persist-failed": { kind: "failed", hint: "The write did not reach the file. Check Studio." }
|
|
41246
|
+
};
|
|
41247
|
+
function fromOutcome(outcome, what) {
|
|
41248
|
+
if (outcome.ok) return null;
|
|
41249
|
+
const mapped = DECLINE_HINTS[outcome.reason] ?? { kind: "failed" };
|
|
41250
|
+
return toolFailure(mapped.kind, `${what} was not applied: ${outcome.reason}`, mapped.hint);
|
|
41251
|
+
}
|
|
41252
|
+
function guardWrite(deps) {
|
|
41253
|
+
const blocked = deps.getWriteBlockedReason();
|
|
41254
|
+
if (blocked) {
|
|
41255
|
+
return toolFailure("blocked", blocked, "Resolve it in Studio, then retry.");
|
|
41256
|
+
}
|
|
41257
|
+
if (!deps.getCurrentSelection()) {
|
|
41258
|
+
return toolFailure("invalid", "nothing is selected", "Call studio_select first.");
|
|
41259
|
+
}
|
|
41260
|
+
return null;
|
|
41261
|
+
}
|
|
41262
|
+
async function studioSetText(deps, input) {
|
|
41263
|
+
if (typeof input.text !== "string") {
|
|
41264
|
+
return toolFailure("invalid", "text must be a string");
|
|
41265
|
+
}
|
|
41266
|
+
const blocked = guardWrite(deps);
|
|
41267
|
+
if (blocked) return blocked;
|
|
41268
|
+
const selection = deps.getCurrentSelection();
|
|
41269
|
+
if (!selection) return toolFailure("invalid", "nothing is selected");
|
|
41270
|
+
const fields = selection.textFields;
|
|
41271
|
+
const requested = typeof input.field === "string" && input.field ? input.field : void 0;
|
|
41272
|
+
if (requested && !fields.some((candidate) => candidate.key === requested)) {
|
|
41273
|
+
return toolFailure(
|
|
41274
|
+
"invalid",
|
|
41275
|
+
`this element has no text field "${requested}"`,
|
|
41276
|
+
`Its fields are: ${fields.map((candidate) => candidate.key).join(", ") || "none"}.`
|
|
41277
|
+
);
|
|
41278
|
+
}
|
|
41279
|
+
const field = requested ?? (fields.length === 1 ? fields[0]?.key : void 0);
|
|
41280
|
+
if (!field) {
|
|
41281
|
+
if (fields.length === 0) {
|
|
41282
|
+
return toolFailure(
|
|
41283
|
+
"blocked",
|
|
41284
|
+
"this element has no editable text field",
|
|
41285
|
+
"studio_inspect lists an element's textFields."
|
|
41286
|
+
);
|
|
41287
|
+
}
|
|
41288
|
+
return toolFailure(
|
|
41289
|
+
"invalid",
|
|
41290
|
+
`this element has ${fields.length} text fields, so one must be named`,
|
|
41291
|
+
`Pass field as one of: ${fields.map((candidate) => candidate.key).join(", ")}.`
|
|
41292
|
+
);
|
|
41293
|
+
}
|
|
41294
|
+
const before = selection.textContent ?? null;
|
|
41295
|
+
const outcome = await deps.setText(input.text, field);
|
|
41296
|
+
const failure = fromOutcome(outcome, "the text");
|
|
41297
|
+
if (failure) return failure;
|
|
41298
|
+
return toolOk({ text: input.text, changed: before !== input.text });
|
|
41299
|
+
}
|
|
41300
|
+
async function studioSetStyle(deps, input) {
|
|
41301
|
+
const styles = input.styles;
|
|
41302
|
+
if (typeof styles !== "object" || styles === null || Array.isArray(styles)) {
|
|
41303
|
+
return toolFailure("invalid", "styles must be an object of CSS property to value");
|
|
41304
|
+
}
|
|
41305
|
+
const entries = Object.entries(styles).filter(
|
|
41306
|
+
(entry2) => typeof entry2[1] === "string"
|
|
41307
|
+
);
|
|
41308
|
+
if (entries.length === 0) {
|
|
41309
|
+
return toolFailure("invalid", "styles must contain at least one string value");
|
|
41310
|
+
}
|
|
41311
|
+
const blocked = guardWrite(deps);
|
|
41312
|
+
if (blocked) return blocked;
|
|
41313
|
+
const applied = {};
|
|
41314
|
+
const rejected = {};
|
|
41315
|
+
for (const [property, value] of entries) {
|
|
41316
|
+
const outcome = await deps.setStyle(property, value);
|
|
41317
|
+
if (outcome.ok) applied[property] = value;
|
|
41318
|
+
else rejected[property] = outcome.reason;
|
|
41319
|
+
}
|
|
41320
|
+
if (Object.keys(applied).length === 0) {
|
|
41321
|
+
const reasons = Object.entries(rejected).map(([property, reason]) => `${property}: ${reason}`).join(", ");
|
|
41322
|
+
return toolFailure("blocked", `no style was applied (${reasons})`);
|
|
41323
|
+
}
|
|
41324
|
+
return toolOk({ applied, rejected });
|
|
41325
|
+
}
|
|
41326
|
+
var STUDIO_SET_TEXT_INPUT_SCHEMA = {
|
|
41327
|
+
type: "object",
|
|
41328
|
+
properties: {
|
|
41329
|
+
text: { type: "string", description: "The new text content." },
|
|
41330
|
+
field: {
|
|
41331
|
+
type: "string",
|
|
41332
|
+
description: "Which text field to write, from studio_inspect. Omit for the element's own text."
|
|
41333
|
+
}
|
|
41334
|
+
},
|
|
41335
|
+
required: ["text"],
|
|
41336
|
+
additionalProperties: false
|
|
41337
|
+
};
|
|
41338
|
+
var STUDIO_SET_TEXT_DESCRIPTION = [
|
|
41339
|
+
"Set the text of the CURRENTLY SELECTED element. Call studio_select first.",
|
|
41340
|
+
"This is the edit a synthetic double-click cannot reach, because Studio's canvas",
|
|
41341
|
+
"takes pointer capture and recognises the double press itself.",
|
|
41342
|
+
"Returns `ok: true` with the resulting text and whether it changed, or `ok: false`",
|
|
41343
|
+
"with `kind`, `reason` and usually a `hint` naming what to do instead."
|
|
41344
|
+
].join(" ");
|
|
41345
|
+
var STUDIO_SET_STYLE_INPUT_SCHEMA = {
|
|
41346
|
+
type: "object",
|
|
41347
|
+
properties: {
|
|
41348
|
+
styles: {
|
|
41349
|
+
type: "object",
|
|
41350
|
+
description: 'CSS property to value, for example {"color": "red", "font-size": "48px"}.',
|
|
41351
|
+
additionalProperties: { type: "string" }
|
|
41352
|
+
}
|
|
41353
|
+
},
|
|
41354
|
+
required: ["styles"],
|
|
41355
|
+
additionalProperties: false
|
|
41356
|
+
};
|
|
41357
|
+
var STUDIO_SET_STYLE_DESCRIPTION = [
|
|
41358
|
+
"Set inline styles on the CURRENTLY SELECTED element. Call studio_select first.",
|
|
41359
|
+
"Each property is a separate commit, so N properties produce N undo entries.",
|
|
41360
|
+
"Position and size properties (left, top, width, height) are refused here on purpose;",
|
|
41361
|
+
"they belong to the transform tools.",
|
|
41362
|
+
"Returns `ok: true` with `applied` and `rejected` maps, so a partial success is visible",
|
|
41363
|
+
"as a partial success rather than reported as a whole one."
|
|
41364
|
+
].join(" ");
|
|
41365
|
+
|
|
41366
|
+
// src/webmcp/tools/transformTools.ts
|
|
41367
|
+
var NO_OP_HINT = "Move and rotate are written as GSAP code; a composition with no GSAP timeline has nothing to write to. studio_inspect reports the element's animations.";
|
|
41368
|
+
function readNumber(value) {
|
|
41369
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
41370
|
+
}
|
|
41371
|
+
function guard(deps) {
|
|
41372
|
+
const blocked = deps.getWriteBlockedReason();
|
|
41373
|
+
if (blocked) return toolFailure("blocked", blocked, "Resolve it in Studio, then retry.");
|
|
41374
|
+
if (!deps.getCurrentSelection()) {
|
|
41375
|
+
return toolFailure("invalid", "nothing is selected", "Call studio_select first.");
|
|
41376
|
+
}
|
|
41377
|
+
return null;
|
|
41378
|
+
}
|
|
41379
|
+
function parsePair(a, b, names, min = Number.NEGATIVE_INFINITY) {
|
|
41380
|
+
const first = readNumber(a);
|
|
41381
|
+
const second = readNumber(b);
|
|
41382
|
+
if (first === null && second === null) return { pair: null };
|
|
41383
|
+
if (first === null || second === null) {
|
|
41384
|
+
return toolFailure("invalid", `${names[0]} and ${names[1]} must be given together`);
|
|
41385
|
+
}
|
|
41386
|
+
if (first < min || second < min) {
|
|
41387
|
+
return toolFailure("invalid", `${names[0]} and ${names[1]} must be at least ${min}`);
|
|
41388
|
+
}
|
|
41389
|
+
return { pair: [first, second] };
|
|
41390
|
+
}
|
|
41391
|
+
function isFailure(value) {
|
|
41392
|
+
return "ok" in value;
|
|
41393
|
+
}
|
|
41394
|
+
function parseRequest(input) {
|
|
41395
|
+
const move = parsePair(input.x, input.y, ["x", "y"]);
|
|
41396
|
+
if (isFailure(move)) return move;
|
|
41397
|
+
const size = parsePair(input.width, input.height, ["width", "height"], 0);
|
|
41398
|
+
if (isFailure(size)) return size;
|
|
41399
|
+
const rotate2 = readNumber(input.rotate);
|
|
41400
|
+
if (!move.pair && !size.pair && rotate2 === null) {
|
|
41401
|
+
return toolFailure(
|
|
41402
|
+
"invalid",
|
|
41403
|
+
"give at least one of x, y, width, height, rotate as a finite number"
|
|
41404
|
+
);
|
|
41405
|
+
}
|
|
41406
|
+
return {
|
|
41407
|
+
move: move.pair ? { x: move.pair[0], y: move.pair[1] } : null,
|
|
41408
|
+
size: size.pair ? { width: size.pair[0], height: size.pair[1] } : null,
|
|
41409
|
+
rotate: rotate2
|
|
41410
|
+
};
|
|
41411
|
+
}
|
|
41412
|
+
async function studioTransform(deps, input) {
|
|
41413
|
+
const request = parseRequest(input);
|
|
41414
|
+
if (isFailure(request)) return request;
|
|
41415
|
+
const blocked = guard(deps);
|
|
41416
|
+
if (blocked) return blocked;
|
|
41417
|
+
const selection = deps.getCurrentSelection();
|
|
41418
|
+
if (!selection) return toolFailure("invalid", "nothing is selected");
|
|
41419
|
+
const applied = [];
|
|
41420
|
+
const unchanged = {};
|
|
41421
|
+
if (request.size) {
|
|
41422
|
+
const before = deps.readBox(selection);
|
|
41423
|
+
await deps.resizeTo(selection, request.size);
|
|
41424
|
+
const after = deps.readBox(selection);
|
|
41425
|
+
if (after.width !== before.width || after.height !== before.height) applied.push("resize");
|
|
41426
|
+
else unchanged.resize = "the element's size did not change";
|
|
41427
|
+
}
|
|
41428
|
+
if (request.move) {
|
|
41429
|
+
const before = deps.readBox(selection);
|
|
41430
|
+
await deps.moveTo(selection, request.move);
|
|
41431
|
+
const after = deps.readBox(selection);
|
|
41432
|
+
if (after.x !== before.x || after.y !== before.y) applied.push("move");
|
|
41433
|
+
else unchanged.move = `the element did not move. ${NO_OP_HINT}`;
|
|
41434
|
+
}
|
|
41435
|
+
if (request.rotate !== null) {
|
|
41436
|
+
await deps.rotateTo(selection, { angle: request.rotate });
|
|
41437
|
+
applied.push("rotate");
|
|
41438
|
+
}
|
|
41439
|
+
if (applied.length === 0) {
|
|
41440
|
+
return toolFailure(
|
|
41441
|
+
"blocked",
|
|
41442
|
+
`nothing changed: ${Object.values(unchanged).join("; ")}`,
|
|
41443
|
+
NO_OP_HINT
|
|
41444
|
+
);
|
|
41445
|
+
}
|
|
41446
|
+
return toolOk({ box: deps.readBox(selection), applied, unchanged });
|
|
41447
|
+
}
|
|
41448
|
+
var STUDIO_TRANSFORM_INPUT_SCHEMA = {
|
|
41449
|
+
type: "object",
|
|
41450
|
+
properties: {
|
|
41451
|
+
x: { type: "number", description: "New x offset in pixels. Must be paired with y." },
|
|
41452
|
+
y: { type: "number", description: "New y offset in pixels. Must be paired with x." },
|
|
41453
|
+
width: { type: "number", minimum: 0, description: "New width. Must be paired with height." },
|
|
41454
|
+
height: { type: "number", minimum: 0, description: "New height. Must be paired with width." },
|
|
41455
|
+
rotate: { type: "number", description: "Rotation in degrees." }
|
|
41456
|
+
},
|
|
41457
|
+
additionalProperties: false
|
|
41458
|
+
};
|
|
41459
|
+
var STUDIO_TRANSFORM_DESCRIPTION = [
|
|
41460
|
+
"Move, resize or rotate the CURRENTLY SELECTED element, the way a drag would.",
|
|
41461
|
+
"Call studio_select first. Give x with y, and width with height.",
|
|
41462
|
+
"The result's `box` is READ BACK after the write, not echoed from your request, and",
|
|
41463
|
+
"`applied` lists what actually took effect. Check it.",
|
|
41464
|
+
"Move and rotate are written as GSAP code, so in a composition with no GSAP timeline they",
|
|
41465
|
+
"do nothing; that shows up in `unchanged` rather than as a false success.",
|
|
41466
|
+
"Rotation is reported as dispatched rather than verified, because the CSS `rotate` property",
|
|
41467
|
+
"does not appear in the element's computed transform.",
|
|
41468
|
+
"Returns `ok: true`, or `ok: false` with `kind`, `reason` and a `hint`."
|
|
41469
|
+
].join(" ");
|
|
41470
|
+
|
|
41471
|
+
// src/webmcp/tools/animationTools.ts
|
|
41472
|
+
var METHODS = ["to", "from", "set", "fromTo"];
|
|
41473
|
+
var INSPECT_HINT = "Call studio_inspect to see the result.";
|
|
41474
|
+
function guard2(deps) {
|
|
41475
|
+
const blocked = deps.getWriteBlockedReason();
|
|
41476
|
+
if (blocked) return toolFailure("blocked", blocked, "Resolve it in Studio, then retry.");
|
|
41477
|
+
if (!deps.getCurrentSelection()) {
|
|
41478
|
+
return toolFailure("invalid", "nothing is selected", "Call studio_select first.");
|
|
41479
|
+
}
|
|
41480
|
+
return null;
|
|
41481
|
+
}
|
|
41482
|
+
function readAnimationId(value) {
|
|
41483
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
41484
|
+
}
|
|
41485
|
+
async function studioAddAnimation(deps, input) {
|
|
41486
|
+
const method = METHODS.find((candidate) => candidate === input.method);
|
|
41487
|
+
if (!method) {
|
|
41488
|
+
return toolFailure("invalid", `method must be one of ${METHODS.join(", ")}`);
|
|
41489
|
+
}
|
|
41490
|
+
const blocked = guard2(deps);
|
|
41491
|
+
if (blocked) return blocked;
|
|
41492
|
+
const { currentTime } = deps.readPlayhead();
|
|
41493
|
+
deps.addAnimation(method);
|
|
41494
|
+
return toolOk({
|
|
41495
|
+
method,
|
|
41496
|
+
insertedAtSeconds: currentTime,
|
|
41497
|
+
dispatched: true
|
|
41498
|
+
});
|
|
41499
|
+
}
|
|
41500
|
+
async function studioUpdateAnimation(deps, input) {
|
|
41501
|
+
const animationId = readAnimationId(input.animationId);
|
|
41502
|
+
if (!animationId) {
|
|
41503
|
+
return toolFailure("invalid", "animationId must be a non-empty string", INSPECT_HINT);
|
|
41504
|
+
}
|
|
41505
|
+
const updates = {};
|
|
41506
|
+
if (typeof input.duration === "number" && Number.isFinite(input.duration)) {
|
|
41507
|
+
if (input.duration < 0) return toolFailure("invalid", "duration must not be negative");
|
|
41508
|
+
updates.duration = input.duration;
|
|
41509
|
+
}
|
|
41510
|
+
if (typeof input.ease === "string" && input.ease.trim()) updates.ease = input.ease;
|
|
41511
|
+
if (typeof input.position === "number" && Number.isFinite(input.position)) {
|
|
41512
|
+
updates.position = input.position;
|
|
41513
|
+
}
|
|
41514
|
+
if (Object.keys(updates).length === 0) {
|
|
41515
|
+
return toolFailure("invalid", "give at least one of duration, ease, position");
|
|
41516
|
+
}
|
|
41517
|
+
const blocked = guard2(deps);
|
|
41518
|
+
if (blocked) return blocked;
|
|
41519
|
+
const landed = await deps.updateAnimation(animationId, updates);
|
|
41520
|
+
if (!landed) {
|
|
41521
|
+
return toolFailure(
|
|
41522
|
+
"failed",
|
|
41523
|
+
`the update to ${animationId} did not land`,
|
|
41524
|
+
"The animation id may be stale. studio_inspect lists the current ones."
|
|
41525
|
+
);
|
|
41526
|
+
}
|
|
41527
|
+
return toolOk({ animationId, updated: updates });
|
|
41528
|
+
}
|
|
41529
|
+
async function studioAddKeyframe(deps, input) {
|
|
41530
|
+
const animationId = readAnimationId(input.animationId);
|
|
41531
|
+
if (!animationId) {
|
|
41532
|
+
return toolFailure("invalid", "animationId must be a non-empty string", INSPECT_HINT);
|
|
41533
|
+
}
|
|
41534
|
+
const percent3 = input.percent;
|
|
41535
|
+
if (typeof percent3 !== "number" || !Number.isFinite(percent3) || percent3 < 0 || percent3 > 100) {
|
|
41536
|
+
return toolFailure("invalid", "percent must be a number between 0 and 100");
|
|
41537
|
+
}
|
|
41538
|
+
const raw = input.properties;
|
|
41539
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
41540
|
+
return toolFailure("invalid", "properties must be an object of GSAP property to value");
|
|
41541
|
+
}
|
|
41542
|
+
const properties = {};
|
|
41543
|
+
for (const [key2, value] of Object.entries(raw)) {
|
|
41544
|
+
if (typeof value === "number" || typeof value === "string") properties[key2] = value;
|
|
41545
|
+
}
|
|
41546
|
+
if (Object.keys(properties).length === 0) {
|
|
41547
|
+
return toolFailure("invalid", "properties must contain at least one number or string value");
|
|
41548
|
+
}
|
|
41549
|
+
const blocked = guard2(deps);
|
|
41550
|
+
if (blocked) return blocked;
|
|
41551
|
+
await deps.addKeyframe(animationId, percent3, properties);
|
|
41552
|
+
return toolOk({ animationId, percent: percent3, properties, dispatched: true });
|
|
41553
|
+
}
|
|
41554
|
+
async function studioDeleteAnimation(deps, input) {
|
|
41555
|
+
const animationId = readAnimationId(input.animationId);
|
|
41556
|
+
if (!animationId) {
|
|
41557
|
+
return toolFailure("invalid", "animationId must be a non-empty string", INSPECT_HINT);
|
|
41558
|
+
}
|
|
41559
|
+
const blocked = guard2(deps);
|
|
41560
|
+
if (blocked) return blocked;
|
|
41561
|
+
deps.deleteAnimation(animationId);
|
|
41562
|
+
return toolOk({ animationId, dispatched: true });
|
|
41563
|
+
}
|
|
41564
|
+
var DISPATCH_CAVEAT = `Reports what was dispatched, not what landed: the handler underneath does not report back. ${INSPECT_HINT}`;
|
|
41565
|
+
var STUDIO_ADD_ANIMATION_INPUT_SCHEMA = {
|
|
41566
|
+
type: "object",
|
|
41567
|
+
properties: {
|
|
41568
|
+
method: { type: "string", enum: METHODS, description: "The GSAP method to add." }
|
|
41569
|
+
},
|
|
41570
|
+
required: ["method"],
|
|
41571
|
+
additionalProperties: false
|
|
41572
|
+
};
|
|
41573
|
+
var STUDIO_ADD_ANIMATION_DESCRIPTION = [
|
|
41574
|
+
"Add a GSAP animation to the CURRENTLY SELECTED element. Call studio_select first.",
|
|
41575
|
+
"It is inserted AT THE PLAYHEAD, which this tool does not control: call studio_seek first",
|
|
41576
|
+
"to choose when it starts. The result reports where the playhead actually was.",
|
|
41577
|
+
DISPATCH_CAVEAT
|
|
41578
|
+
].join(" ");
|
|
41579
|
+
var STUDIO_UPDATE_ANIMATION_INPUT_SCHEMA = {
|
|
41580
|
+
type: "object",
|
|
41581
|
+
properties: {
|
|
41582
|
+
animationId: { type: "string", description: "An animation id from studio_inspect." },
|
|
41583
|
+
duration: { type: "number", minimum: 0, description: "Duration in seconds." },
|
|
41584
|
+
ease: { type: "string", description: "A GSAP ease, for example power2.out." },
|
|
41585
|
+
position: { type: "number", description: "Start position in seconds." }
|
|
41586
|
+
},
|
|
41587
|
+
required: ["animationId"],
|
|
41588
|
+
additionalProperties: false
|
|
41589
|
+
};
|
|
41590
|
+
var STUDIO_UPDATE_ANIMATION_DESCRIPTION = [
|
|
41591
|
+
"Change an existing animation's duration, ease or position.",
|
|
41592
|
+
"This is the one animation tool that CONFIRMS its write, so a failure here is real",
|
|
41593
|
+
"and usually means a stale animationId. Get current ids from studio_inspect."
|
|
41594
|
+
].join(" ");
|
|
41595
|
+
var STUDIO_ADD_KEYFRAME_INPUT_SCHEMA = {
|
|
41596
|
+
type: "object",
|
|
41597
|
+
properties: {
|
|
41598
|
+
animationId: { type: "string", description: "An animation id from studio_inspect." },
|
|
41599
|
+
percent: {
|
|
41600
|
+
type: "number",
|
|
41601
|
+
minimum: 0,
|
|
41602
|
+
maximum: 100,
|
|
41603
|
+
description: "Where in the tween, 0 to 100."
|
|
41604
|
+
},
|
|
41605
|
+
properties: {
|
|
41606
|
+
type: "object",
|
|
41607
|
+
description: 'GSAP property to value, for example {"y": -50, "opacity": 0}.'
|
|
41608
|
+
}
|
|
41609
|
+
},
|
|
41610
|
+
required: ["animationId", "percent", "properties"],
|
|
41611
|
+
additionalProperties: false
|
|
41612
|
+
};
|
|
41613
|
+
var STUDIO_ADD_KEYFRAME_DESCRIPTION = [
|
|
41614
|
+
"Add a keyframe to an existing animation at a percentage through it.",
|
|
41615
|
+
"All the properties land in one commit, so they are one undo entry.",
|
|
41616
|
+
DISPATCH_CAVEAT
|
|
41617
|
+
].join(" ");
|
|
41618
|
+
var STUDIO_DELETE_ANIMATION_INPUT_SCHEMA = {
|
|
41619
|
+
type: "object",
|
|
41620
|
+
properties: {
|
|
41621
|
+
animationId: { type: "string", description: "An animation id from studio_inspect." }
|
|
41622
|
+
},
|
|
41623
|
+
required: ["animationId"],
|
|
41624
|
+
additionalProperties: false
|
|
41625
|
+
};
|
|
41626
|
+
var STUDIO_DELETE_ANIMATION_DESCRIPTION = [
|
|
41627
|
+
"Remove an animation from the currently selected element. Undo reverses it.",
|
|
41628
|
+
DISPATCH_CAVEAT
|
|
41629
|
+
].join(" ");
|
|
41630
|
+
|
|
40819
41631
|
// src/webmcp/useStudioAgentTools.ts
|
|
40820
41632
|
var log2 = makeStudioDebugLogger("webmcp");
|
|
40821
41633
|
function reportRegistration(report, native) {
|
|
@@ -40844,9 +41656,117 @@ function buildStudioTools(depsRef) {
|
|
|
40844
41656
|
"studio_look",
|
|
40845
41657
|
async () => buildStudioLook(depsRef.current.getSnapshot(), input)
|
|
40846
41658
|
)
|
|
41659
|
+
},
|
|
41660
|
+
{
|
|
41661
|
+
name: "studio_select",
|
|
41662
|
+
title: "Select an element",
|
|
41663
|
+
description: STUDIO_SELECT_DESCRIPTION,
|
|
41664
|
+
inputSchema: STUDIO_SELECT_INPUT_SCHEMA,
|
|
41665
|
+
annotations: { readOnlyHint: false, untrustedContentHint: true },
|
|
41666
|
+
execute: (input) => runToolBody(
|
|
41667
|
+
"studio_select",
|
|
41668
|
+
() => studioSelect(depsRef.current, readStringInput(input, "handle"))
|
|
41669
|
+
)
|
|
41670
|
+
},
|
|
41671
|
+
{
|
|
41672
|
+
name: "studio_seek",
|
|
41673
|
+
title: "Move the playhead",
|
|
41674
|
+
description: STUDIO_SEEK_DESCRIPTION,
|
|
41675
|
+
inputSchema: STUDIO_SEEK_INPUT_SCHEMA,
|
|
41676
|
+
annotations: { readOnlyHint: false },
|
|
41677
|
+
execute: (input) => runToolBody(
|
|
41678
|
+
"studio_seek",
|
|
41679
|
+
async () => studioSeek(depsRef.current, readNumberInput(input, "time"))
|
|
41680
|
+
)
|
|
41681
|
+
},
|
|
41682
|
+
{
|
|
41683
|
+
name: "studio_frame",
|
|
41684
|
+
title: "See the composition",
|
|
41685
|
+
description: STUDIO_FRAME_DESCRIPTION,
|
|
41686
|
+
inputSchema: STUDIO_FRAME_INPUT_SCHEMA,
|
|
41687
|
+
annotations: { readOnlyHint: true, untrustedContentHint: true },
|
|
41688
|
+
execute: (input) => runToolBody("studio_frame", () => studioFrame(depsRef.current, input))
|
|
41689
|
+
},
|
|
41690
|
+
{
|
|
41691
|
+
name: "studio_inspect",
|
|
41692
|
+
title: "Inspect one element",
|
|
41693
|
+
description: STUDIO_INSPECT_DESCRIPTION,
|
|
41694
|
+
inputSchema: STUDIO_INSPECT_INPUT_SCHEMA,
|
|
41695
|
+
annotations: { readOnlyHint: true, untrustedContentHint: true },
|
|
41696
|
+
execute: (input) => runToolBody(
|
|
41697
|
+
"studio_inspect",
|
|
41698
|
+
() => studioInspect(depsRef.current, input)
|
|
41699
|
+
)
|
|
41700
|
+
},
|
|
41701
|
+
{
|
|
41702
|
+
name: "studio_set_text",
|
|
41703
|
+
title: "Set an element's text",
|
|
41704
|
+
description: STUDIO_SET_TEXT_DESCRIPTION,
|
|
41705
|
+
inputSchema: STUDIO_SET_TEXT_INPUT_SCHEMA,
|
|
41706
|
+
annotations: { readOnlyHint: false, untrustedContentHint: true },
|
|
41707
|
+
execute: (input) => runToolBody("studio_set_text", () => studioSetText(depsRef.current, input))
|
|
41708
|
+
},
|
|
41709
|
+
{
|
|
41710
|
+
name: "studio_set_style",
|
|
41711
|
+
title: "Set an element's styles",
|
|
41712
|
+
description: STUDIO_SET_STYLE_DESCRIPTION,
|
|
41713
|
+
inputSchema: STUDIO_SET_STYLE_INPUT_SCHEMA,
|
|
41714
|
+
annotations: { readOnlyHint: false },
|
|
41715
|
+
execute: (input) => runToolBody("studio_set_style", () => studioSetStyle(depsRef.current, input))
|
|
41716
|
+
},
|
|
41717
|
+
{
|
|
41718
|
+
name: "studio_transform",
|
|
41719
|
+
title: "Move, resize or rotate",
|
|
41720
|
+
description: STUDIO_TRANSFORM_DESCRIPTION,
|
|
41721
|
+
inputSchema: STUDIO_TRANSFORM_INPUT_SCHEMA,
|
|
41722
|
+
annotations: { readOnlyHint: false },
|
|
41723
|
+
execute: (input) => runToolBody(
|
|
41724
|
+
"studio_transform",
|
|
41725
|
+
() => studioTransform(depsRef.current, input)
|
|
41726
|
+
)
|
|
41727
|
+
},
|
|
41728
|
+
{
|
|
41729
|
+
name: "studio_add_animation",
|
|
41730
|
+
title: "Add an animation",
|
|
41731
|
+
description: STUDIO_ADD_ANIMATION_DESCRIPTION,
|
|
41732
|
+
inputSchema: STUDIO_ADD_ANIMATION_INPUT_SCHEMA,
|
|
41733
|
+
annotations: { readOnlyHint: false },
|
|
41734
|
+
execute: (input) => runToolBody("studio_add_animation", () => studioAddAnimation(depsRef.current, input))
|
|
41735
|
+
},
|
|
41736
|
+
{
|
|
41737
|
+
name: "studio_update_animation",
|
|
41738
|
+
title: "Change an animation",
|
|
41739
|
+
description: STUDIO_UPDATE_ANIMATION_DESCRIPTION,
|
|
41740
|
+
inputSchema: STUDIO_UPDATE_ANIMATION_INPUT_SCHEMA,
|
|
41741
|
+
annotations: { readOnlyHint: false },
|
|
41742
|
+
execute: (input) => runToolBody("studio_update_animation", () => studioUpdateAnimation(depsRef.current, input))
|
|
41743
|
+
},
|
|
41744
|
+
{
|
|
41745
|
+
name: "studio_add_keyframe",
|
|
41746
|
+
title: "Add a keyframe",
|
|
41747
|
+
description: STUDIO_ADD_KEYFRAME_DESCRIPTION,
|
|
41748
|
+
inputSchema: STUDIO_ADD_KEYFRAME_INPUT_SCHEMA,
|
|
41749
|
+
annotations: { readOnlyHint: false },
|
|
41750
|
+
execute: (input) => runToolBody("studio_add_keyframe", () => studioAddKeyframe(depsRef.current, input))
|
|
41751
|
+
},
|
|
41752
|
+
{
|
|
41753
|
+
name: "studio_delete_animation",
|
|
41754
|
+
title: "Remove an animation",
|
|
41755
|
+
description: STUDIO_DELETE_ANIMATION_DESCRIPTION,
|
|
41756
|
+
inputSchema: STUDIO_DELETE_ANIMATION_INPUT_SCHEMA,
|
|
41757
|
+
annotations: { readOnlyHint: false },
|
|
41758
|
+
execute: (input) => runToolBody("studio_delete_animation", () => studioDeleteAnimation(depsRef.current, input))
|
|
40847
41759
|
}
|
|
40848
41760
|
];
|
|
40849
41761
|
}
|
|
41762
|
+
function readStringInput(input, key2) {
|
|
41763
|
+
const value = Reflect.get(input, key2);
|
|
41764
|
+
return typeof value === "string" ? value : "";
|
|
41765
|
+
}
|
|
41766
|
+
function readNumberInput(input, key2) {
|
|
41767
|
+
const value = Reflect.get(input, key2);
|
|
41768
|
+
return typeof value === "number" ? value : Number.NaN;
|
|
41769
|
+
}
|
|
40850
41770
|
function useStudioAgentTools(deps) {
|
|
40851
41771
|
const depsRef = useRef68(deps);
|
|
40852
41772
|
depsRef.current = deps;
|
|
@@ -40878,8 +41798,27 @@ function useStudioAgentTools(deps) {
|
|
|
40878
41798
|
|
|
40879
41799
|
// src/webmcp/StudioAgentTools.tsx
|
|
40880
41800
|
function StudioAgentTools() {
|
|
40881
|
-
const { projectId, activeCompPath, editHistory } = useStudioShellContext();
|
|
40882
|
-
const {
|
|
41801
|
+
const { projectId, activeCompPath, editHistory, writeBlockedReason } = useStudioShellContext();
|
|
41802
|
+
const {
|
|
41803
|
+
domEditSelection,
|
|
41804
|
+
selectedGsapAnimations,
|
|
41805
|
+
gsapMultipleTimelines,
|
|
41806
|
+
gsapUnsupportedTimelinePattern
|
|
41807
|
+
} = useDomEditSelectionContext();
|
|
41808
|
+
const {
|
|
41809
|
+
previewIframeRef,
|
|
41810
|
+
buildDomSelectionFromTarget,
|
|
41811
|
+
applyDomSelection,
|
|
41812
|
+
handleDomTextCommit,
|
|
41813
|
+
handleDomStyleCommit,
|
|
41814
|
+
handleDomPathOffsetCommit,
|
|
41815
|
+
handleDomBoxSizeCommit,
|
|
41816
|
+
handleDomRotationCommit,
|
|
41817
|
+
handleGsapAddAnimation,
|
|
41818
|
+
handleGsapUpdateMeta,
|
|
41819
|
+
handleGsapAddKeyframeBatch,
|
|
41820
|
+
handleGsapDeleteAnimation
|
|
41821
|
+
} = useDomEditActionsContext();
|
|
40883
41822
|
const getSnapshot = useCallback60(() => {
|
|
40884
41823
|
const player = usePlayerStore.getState();
|
|
40885
41824
|
return {
|
|
@@ -40899,7 +41838,82 @@ function StudioAgentTools() {
|
|
|
40899
41838
|
}
|
|
40900
41839
|
};
|
|
40901
41840
|
}, [projectId, activeCompPath, domEditSelection, selectedGsapAnimations, editHistory]);
|
|
40902
|
-
|
|
41841
|
+
const deps = useMemo37(
|
|
41842
|
+
() => ({
|
|
41843
|
+
getSnapshot,
|
|
41844
|
+
getPreviewDocument: () => previewIframeRef.current?.contentDocument ?? null,
|
|
41845
|
+
buildSelection: (element) => buildDomSelectionFromTarget(element),
|
|
41846
|
+
applySelection: (selection) => applyDomSelection(selection, { revealPanel: true }),
|
|
41847
|
+
requestSeek: (time) => usePlayerStore.getState().requestSeek(time),
|
|
41848
|
+
readPlayhead: () => {
|
|
41849
|
+
const player = usePlayerStore.getState();
|
|
41850
|
+
return {
|
|
41851
|
+
currentTime: player.currentTime,
|
|
41852
|
+
duration: player.duration,
|
|
41853
|
+
isPlaying: player.isPlaying
|
|
41854
|
+
};
|
|
41855
|
+
},
|
|
41856
|
+
getProjectId: () => projectId,
|
|
41857
|
+
getCompositionPath: () => activeCompPath,
|
|
41858
|
+
// HEAD, not GET: the tool only needs to know the frame renders. Pulling
|
|
41859
|
+
// the PNG here would download it once for nothing, since the agent
|
|
41860
|
+
// fetches the URL itself.
|
|
41861
|
+
probeFrame: async (url) => {
|
|
41862
|
+
try {
|
|
41863
|
+
const response = await fetch(url, { method: "HEAD" });
|
|
41864
|
+
return { ok: response.ok, status: response.status };
|
|
41865
|
+
} catch {
|
|
41866
|
+
return { ok: false, status: 0 };
|
|
41867
|
+
}
|
|
41868
|
+
},
|
|
41869
|
+
wait: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
41870
|
+
getCurrentSelection: () => domEditSelection,
|
|
41871
|
+
getWriteBlockedReason: () => writeBlockedReason,
|
|
41872
|
+
setText: (value, fieldKey) => handleDomTextCommit(value, fieldKey),
|
|
41873
|
+
setStyle: (property, value) => handleDomStyleCommit(property, value),
|
|
41874
|
+
// Measured, not authored: the tool compares this before and after to
|
|
41875
|
+
// tell a real change from a handler that did nothing and resolved.
|
|
41876
|
+
readBox: (selection) => {
|
|
41877
|
+
const rect = selection.element.getBoundingClientRect();
|
|
41878
|
+
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
41879
|
+
},
|
|
41880
|
+
moveTo: (selection, next) => handleDomPathOffsetCommit(selection, next),
|
|
41881
|
+
resizeTo: (selection, next) => handleDomBoxSizeCommit(selection, next),
|
|
41882
|
+
rotateTo: (selection, next) => handleDomRotationCommit(selection, next),
|
|
41883
|
+
addAnimation: (method) => handleGsapAddAnimation(method),
|
|
41884
|
+
updateAnimation: (animationId, updates) => handleGsapUpdateMeta(animationId, updates),
|
|
41885
|
+
addKeyframe: (animationId, percent3, properties) => handleGsapAddKeyframeBatch(animationId, percent3, properties),
|
|
41886
|
+
deleteAnimation: (animationId) => handleGsapDeleteAnimation(animationId),
|
|
41887
|
+
getGsapDiagnostics: () => ({
|
|
41888
|
+
animations: selectedGsapAnimations,
|
|
41889
|
+
multipleTimelines: gsapMultipleTimelines,
|
|
41890
|
+
unsupportedTimelinePattern: gsapUnsupportedTimelinePattern
|
|
41891
|
+
})
|
|
41892
|
+
}),
|
|
41893
|
+
[
|
|
41894
|
+
getSnapshot,
|
|
41895
|
+
previewIframeRef,
|
|
41896
|
+
buildDomSelectionFromTarget,
|
|
41897
|
+
applyDomSelection,
|
|
41898
|
+
projectId,
|
|
41899
|
+
activeCompPath,
|
|
41900
|
+
writeBlockedReason,
|
|
41901
|
+
handleDomTextCommit,
|
|
41902
|
+
handleDomStyleCommit,
|
|
41903
|
+
handleDomPathOffsetCommit,
|
|
41904
|
+
handleDomBoxSizeCommit,
|
|
41905
|
+
handleDomRotationCommit,
|
|
41906
|
+
handleGsapAddAnimation,
|
|
41907
|
+
handleGsapUpdateMeta,
|
|
41908
|
+
handleGsapAddKeyframeBatch,
|
|
41909
|
+
handleGsapDeleteAnimation,
|
|
41910
|
+
domEditSelection,
|
|
41911
|
+
selectedGsapAnimations,
|
|
41912
|
+
gsapMultipleTimelines,
|
|
41913
|
+
gsapUnsupportedTimelinePattern
|
|
41914
|
+
]
|
|
41915
|
+
);
|
|
41916
|
+
useStudioAgentTools(deps);
|
|
40903
41917
|
return null;
|
|
40904
41918
|
}
|
|
40905
41919
|
|
|
@@ -41227,13 +42241,13 @@ var SourceEditor = memo28(function SourceEditor2({
|
|
|
41227
42241
|
});
|
|
41228
42242
|
|
|
41229
42243
|
// src/components/editor/PropertyPanel.tsx
|
|
41230
|
-
import { memo as memo32, useMemo as
|
|
42244
|
+
import { memo as memo32, useMemo as useMemo53, useRef as useRef99, useState as useState90 } from "react";
|
|
41231
42245
|
|
|
41232
42246
|
// src/components/editor/InspectorHeaderActions.tsx
|
|
41233
42247
|
import { Eye as Eye3, EyeSlash as EyeSlash2 } from "@phosphor-icons/react";
|
|
41234
42248
|
|
|
41235
42249
|
// src/contexts/DesignPanelInputContext.tsx
|
|
41236
|
-
import { useCallback as useCallback63, useContext as useContext6, useMemo as
|
|
42250
|
+
import { useCallback as useCallback63, useContext as useContext6, useMemo as useMemo38 } from "react";
|
|
41237
42251
|
|
|
41238
42252
|
// src/utils/designInputTracking.ts
|
|
41239
42253
|
var COALESCE_WINDOW_MS = 600;
|
|
@@ -41276,7 +42290,7 @@ function DesignPanelInputProvider({
|
|
|
41276
42290
|
children
|
|
41277
42291
|
}) {
|
|
41278
42292
|
const parent = useContext6(DesignPanelInputContext);
|
|
41279
|
-
const value =
|
|
42293
|
+
const value = useMemo38(
|
|
41280
42294
|
() => ({ ui: ui ?? parent.ui, section: section ?? parent.section }),
|
|
41281
42295
|
[ui, section, parent.ui, parent.section]
|
|
41282
42296
|
);
|
|
@@ -42891,7 +43905,7 @@ function MediaSection({
|
|
|
42891
43905
|
import { isHfColorGradingActive as isHfColorGradingActive2 } from "@hyperframes/core/color-grading";
|
|
42892
43906
|
|
|
42893
43907
|
// src/components/editor/propertyPanelColorGradingControls.tsx
|
|
42894
|
-
import { useMemo as
|
|
43908
|
+
import { useMemo as useMemo39, useRef as useRef74, useState as useState53 } from "react";
|
|
42895
43909
|
import {
|
|
42896
43910
|
HF_COLOR_GRADING_PRESETS,
|
|
42897
43911
|
normalizeHfColorGrading
|
|
@@ -43335,7 +44349,7 @@ function ColorGradingControls({
|
|
|
43335
44349
|
const [detailSettings, setDetailSettings] = useState53(null);
|
|
43336
44350
|
const [lutImporting, setLutImporting] = useState53(false);
|
|
43337
44351
|
const [lutImportError, setLutImportError] = useState53(null);
|
|
43338
|
-
const lutAssets =
|
|
44352
|
+
const lutAssets = useMemo39(
|
|
43339
44353
|
() => assets.filter((asset) => LUT_EXT.test(asset)).sort((a, b) => a.localeCompare(b)),
|
|
43340
44354
|
[assets]
|
|
43341
44355
|
);
|
|
@@ -43615,7 +44629,7 @@ function ColorGradingControls({
|
|
|
43615
44629
|
}
|
|
43616
44630
|
|
|
43617
44631
|
// src/components/editor/useColorGradingController.ts
|
|
43618
|
-
import { useCallback as useCallback68, useEffect as useEffect57, useMemo as
|
|
44632
|
+
import { useCallback as useCallback68, useEffect as useEffect57, useMemo as useMemo40, useRef as useRef76, useState as useState55 } from "react";
|
|
43619
44633
|
import {
|
|
43620
44634
|
HF_COLOR_GRADING_ATTR,
|
|
43621
44635
|
hasHfColorGradingAuthoredValues,
|
|
@@ -44027,7 +45041,7 @@ function useColorGradingController({
|
|
|
44027
45041
|
state: "pending",
|
|
44028
45042
|
message: "Waiting for runtime"
|
|
44029
45043
|
}));
|
|
44030
|
-
const selectedAssetPath =
|
|
45044
|
+
const selectedAssetPath = useMemo40(
|
|
44031
45045
|
() => selectedMediaAssetPath(element, projectId),
|
|
44032
45046
|
[element, projectId]
|
|
44033
45047
|
);
|
|
@@ -44074,7 +45088,7 @@ function useColorGradingController({
|
|
|
44074
45088
|
trackStudioPendingEdit(onSetAttributeLive(COLOR_GRADING_DATA_KEY, value));
|
|
44075
45089
|
};
|
|
44076
45090
|
}, [identityKey]);
|
|
44077
|
-
const target =
|
|
45091
|
+
const target = useMemo40(
|
|
44078
45092
|
() => ({
|
|
44079
45093
|
id: element.id ?? null,
|
|
44080
45094
|
hfId: element.hfId ?? null,
|
|
@@ -45052,7 +46066,7 @@ function ColorField({
|
|
|
45052
46066
|
}
|
|
45053
46067
|
|
|
45054
46068
|
// src/components/editor/propertyPanelFont.tsx
|
|
45055
|
-
import { useEffect as useEffect59, useMemo as
|
|
46069
|
+
import { useEffect as useEffect59, useMemo as useMemo41, useRef as useRef78, useState as useState57 } from "react";
|
|
45056
46070
|
|
|
45057
46071
|
// src/components/editor/fontAssets.ts
|
|
45058
46072
|
var FONT_EXT_RE = /\.(eot|otf|ttc|ttf|woff2?)$/i;
|
|
@@ -45274,13 +46288,13 @@ function FontFamilyField({
|
|
|
45274
46288
|
setImportingFonts(false);
|
|
45275
46289
|
}
|
|
45276
46290
|
};
|
|
45277
|
-
const projectFontAssets =
|
|
46291
|
+
const projectFontAssets = useMemo41(
|
|
45278
46292
|
() => uniqueFontOptions(
|
|
45279
46293
|
importedFonts.map((f) => ({ family: f.family, source: "Imported" }))
|
|
45280
46294
|
),
|
|
45281
46295
|
[importedFonts]
|
|
45282
46296
|
);
|
|
45283
|
-
const options =
|
|
46297
|
+
const options = useMemo41(() => {
|
|
45284
46298
|
const documentFonts = collectDocumentFontFamilies();
|
|
45285
46299
|
const googleSet = new Set(googleFonts.map((f) => f.toLowerCase()));
|
|
45286
46300
|
const taggedLocal = localFonts.map(
|
|
@@ -45300,7 +46314,7 @@ function FontFamilyField({
|
|
|
45300
46314
|
])
|
|
45301
46315
|
);
|
|
45302
46316
|
}, [currentFamily, googleFonts, localFonts, projectFontAssets]);
|
|
45303
|
-
const filteredOptions =
|
|
46317
|
+
const filteredOptions = useMemo41(() => {
|
|
45304
46318
|
const matches = options.filter((o) => fontMatchesQuery(o.family, query));
|
|
45305
46319
|
if (query.trim()) return matches.slice(0, 200);
|
|
45306
46320
|
const bySource = /* @__PURE__ */ new Map();
|
|
@@ -45536,7 +46550,7 @@ function FontFamilyField({
|
|
|
45536
46550
|
import { useEffect as useEffect61 } from "react";
|
|
45537
46551
|
|
|
45538
46552
|
// src/components/panels/VariablesBindElement.tsx
|
|
45539
|
-
import { useMemo as
|
|
46553
|
+
import { useMemo as useMemo42, useState as useState59 } from "react";
|
|
45540
46554
|
|
|
45541
46555
|
// src/components/panels/VariablesValueControls.tsx
|
|
45542
46556
|
import { useState as useState58 } from "react";
|
|
@@ -45818,7 +46832,7 @@ function VariablesBindElement({
|
|
|
45818
46832
|
}) {
|
|
45819
46833
|
const [activeKey, setActiveKey] = useState59(null);
|
|
45820
46834
|
const [idDraft, setIdDraft] = useState59("");
|
|
45821
|
-
const actions =
|
|
46835
|
+
const actions = useMemo42(() => buildBindActions(selection, sdkSession), [selection, sdkSession]);
|
|
45822
46836
|
if (actions.length === 0) return null;
|
|
45823
46837
|
const active = actions.find((a) => a.key === activeKey) ?? null;
|
|
45824
46838
|
const trimmedId = sanitizeId(idDraft);
|
|
@@ -45929,7 +46943,7 @@ function uniqueId(base2, existing) {
|
|
|
45929
46943
|
}
|
|
45930
46944
|
|
|
45931
46945
|
// src/contexts/VariablePromoteContext.tsx
|
|
45932
|
-
import { useContext as useContext7, useEffect as useEffect60, useMemo as
|
|
46946
|
+
import { useContext as useContext7, useEffect as useEffect60, useMemo as useMemo43, useState as useState60 } from "react";
|
|
45933
46947
|
import { jsx as jsx100 } from "react/jsx-runtime";
|
|
45934
46948
|
var VariablePromoteContext = createStableContext(
|
|
45935
46949
|
"VariablePromoteContext",
|
|
@@ -45952,15 +46966,15 @@ function VariablePromoteProvider({
|
|
|
45952
46966
|
if (!session) return;
|
|
45953
46967
|
return session.on("change", () => setRevision((r) => r + 1));
|
|
45954
46968
|
}, [session]);
|
|
45955
|
-
const actions =
|
|
46969
|
+
const actions = useMemo43(() => {
|
|
45956
46970
|
if (!session || !selection) return [];
|
|
45957
46971
|
return buildBindActions(selection, session);
|
|
45958
46972
|
}, [session, selection, revision]);
|
|
45959
|
-
const declarations =
|
|
46973
|
+
const declarations = useMemo43(() => {
|
|
45960
46974
|
if (!session) return [];
|
|
45961
46975
|
return session.getVariableDeclarations();
|
|
45962
46976
|
}, [session, revision]);
|
|
45963
|
-
const value =
|
|
46977
|
+
const value = useMemo43(
|
|
45964
46978
|
() => ({ session, selection, actions, declarations, persist: persist2, onPersistError }),
|
|
45965
46979
|
[session, selection, actions, declarations, persist2, onPersistError]
|
|
45966
46980
|
);
|
|
@@ -45969,7 +46983,7 @@ function VariablePromoteProvider({
|
|
|
45969
46983
|
function useVariablePromoteChannel(channel) {
|
|
45970
46984
|
const ctx3 = useContext7(VariablePromoteContext);
|
|
45971
46985
|
const key2 = channel.kind === "style" ? `style:${channel.prop}` : channel.kind;
|
|
45972
|
-
return
|
|
46986
|
+
return useMemo43(() => {
|
|
45973
46987
|
if (!ctx3 || !ctx3.session || !ctx3.selection?.hfId) return null;
|
|
45974
46988
|
const { session, selection, actions, declarations, persist: persist2, onPersistError } = ctx3;
|
|
45975
46989
|
const hfId = selection.hfId;
|
|
@@ -46404,7 +47418,7 @@ function parseColorString(value) {
|
|
|
46404
47418
|
}
|
|
46405
47419
|
|
|
46406
47420
|
// src/components/editor/propertyPanelFill.tsx
|
|
46407
|
-
import { useMemo as
|
|
47421
|
+
import { useMemo as useMemo44, useRef as useRef79, useState as useState61 } from "react";
|
|
46408
47422
|
import { jsx as jsx102, jsxs as jsxs79 } from "react/jsx-runtime";
|
|
46409
47423
|
function normalizeProjectPath(value) {
|
|
46410
47424
|
const trimmed = value.trim();
|
|
@@ -46449,8 +47463,8 @@ function ImageFillField({
|
|
|
46449
47463
|
const fileInputRef = useRef79(null);
|
|
46450
47464
|
const [uploading, setUploading] = useState61(false);
|
|
46451
47465
|
const [uploadError, setUploadError] = useState61(null);
|
|
46452
|
-
const imageAssets =
|
|
46453
|
-
const selectedAsset =
|
|
47466
|
+
const imageAssets = useMemo44(() => assets.filter((a) => IMAGE_EXT.test(a)), [assets]);
|
|
47467
|
+
const selectedAsset = useMemo44(
|
|
46454
47468
|
() => resolveSelectedAsset(value, sourceFile, imageAssets),
|
|
46455
47469
|
[imageAssets, sourceFile, value]
|
|
46456
47470
|
);
|
|
@@ -47932,7 +48946,7 @@ import { useCallback as useCallback73, useState as useState66 } from "react";
|
|
|
47932
48946
|
import { useShallow } from "zustand/react/shallow";
|
|
47933
48947
|
|
|
47934
48948
|
// src/components/editor/AnimationCard.tsx
|
|
47935
|
-
import { memo as memo30, useCallback as useCallback72, useEffect as useEffect64, useMemo as
|
|
48949
|
+
import { memo as memo30, useCallback as useCallback72, useEffect as useEffect64, useMemo as useMemo45, useRef as useRef82, useState as useState65 } from "react";
|
|
47936
48950
|
import { SUPPORTED_EASES, SUPPORTED_PROPS } from "@hyperframes/core/gsap-constants";
|
|
47937
48951
|
|
|
47938
48952
|
// src/components/editor/gsapAnimationHelpers.ts
|
|
@@ -48473,21 +49487,21 @@ var AnimationCard = memo30(function AnimationCard2({
|
|
|
48473
49487
|
segment?.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
48474
49488
|
pendingAutoScrollRef.current = false;
|
|
48475
49489
|
}, [expandedKfPct]);
|
|
48476
|
-
const usedProps =
|
|
49490
|
+
const usedProps = useMemo45(
|
|
48477
49491
|
() => new Set(Object.keys(animation.properties)),
|
|
48478
49492
|
[animation.properties]
|
|
48479
49493
|
);
|
|
48480
|
-
const availableProps =
|
|
49494
|
+
const availableProps = useMemo45(
|
|
48481
49495
|
() => SUPPORTED_PROPS.filter(
|
|
48482
49496
|
(p) => !usedProps.has(p) && (animation.method === "set" || !BOOLEAN_PROPS.has(p))
|
|
48483
49497
|
),
|
|
48484
49498
|
[usedProps, animation.method]
|
|
48485
49499
|
);
|
|
48486
|
-
const usedFromProps =
|
|
49500
|
+
const usedFromProps = useMemo45(
|
|
48487
49501
|
() => new Set(Object.keys(animation.fromProperties ?? {})),
|
|
48488
49502
|
[animation.fromProperties]
|
|
48489
49503
|
);
|
|
48490
|
-
const availableFromProps =
|
|
49504
|
+
const availableFromProps = useMemo45(
|
|
48491
49505
|
() => SUPPORTED_PROPS.filter((p) => !usedFromProps.has(p) && !BOOLEAN_PROPS.has(p)),
|
|
48492
49506
|
[usedFromProps]
|
|
48493
49507
|
);
|
|
@@ -48534,7 +49548,7 @@ var AnimationCard = memo30(function AnimationCard2({
|
|
|
48534
49548
|
const easeName = (animation.keyframes ? animation.keyframes.easeEach : void 0) ?? animation.ease ?? "none";
|
|
48535
49549
|
const easeLabel = easeName.startsWith("custom(") ? "Custom curve" : EASE_LABELS[easeName] ?? easeName;
|
|
48536
49550
|
const endTime = typeof animation.position === "number" ? animation.position + (animation.duration ?? 0) : animation.position;
|
|
48537
|
-
const summary =
|
|
49551
|
+
const summary = useMemo45(() => buildTweenSummary(animation), [animation]);
|
|
48538
49552
|
const setKeys = Object.keys(animation.properties);
|
|
48539
49553
|
if (animation.method === "set" && // `every` is vacuously true on an empty bag — require at least one key so a
|
|
48540
49554
|
// property-less set doesn't masquerade as a position row.
|
|
@@ -49682,7 +50696,7 @@ function PropertyPanel3dTransform({
|
|
|
49682
50696
|
}
|
|
49683
50697
|
|
|
49684
50698
|
// src/components/editor/PropertyPanelFlat.tsx
|
|
49685
|
-
import { useEffect as useEffect84, useMemo as
|
|
50699
|
+
import { useEffect as useEffect84, useMemo as useMemo52, useRef as useRef98, useState as useState89 } from "react";
|
|
49686
50700
|
import { useShallow as useShallow2 } from "zustand/react/shallow";
|
|
49687
50701
|
|
|
49688
50702
|
// src/components/editor/audioFxSummary.ts
|
|
@@ -51752,7 +52766,7 @@ function motionSectionLabel(args) {
|
|
|
51752
52766
|
}
|
|
51753
52767
|
|
|
51754
52768
|
// src/components/editor/propertyPanelAudioFxGroup.tsx
|
|
51755
|
-
import { useMemo as
|
|
52769
|
+
import { useMemo as useMemo48, useState as useState77 } from "react";
|
|
51756
52770
|
import {
|
|
51757
52771
|
HF_AUDIO_FX_ATTR as HF_AUDIO_FX_ATTR6,
|
|
51758
52772
|
HF_AUDIO_FX_DATA_KEY as HF_AUDIO_FX_DATA_KEY2,
|
|
@@ -51811,7 +52825,7 @@ function automationAttrValue(automation) {
|
|
|
51811
52825
|
}
|
|
51812
52826
|
|
|
51813
52827
|
// src/components/editor/propertyPanelFxSection.tsx
|
|
51814
|
-
import { useCallback as useCallback76, useEffect as useEffect71, useMemo as
|
|
52828
|
+
import { useCallback as useCallback76, useEffect as useEffect71, useMemo as useMemo47, useRef as useRef86, useState as useState75 } from "react";
|
|
51815
52829
|
|
|
51816
52830
|
// src/components/editor/audioFxRevealTarget.ts
|
|
51817
52831
|
import { parseAutomationTarget as parseAutomationTarget2 } from "@hyperframes/core/audio-automation";
|
|
@@ -52757,7 +53771,7 @@ function FxEqModule({
|
|
|
52757
53771
|
import { getAudioFxPreset as getAudioFxPreset3 } from "@hyperframes/core/audio-fx-presets";
|
|
52758
53772
|
|
|
52759
53773
|
// src/components/editor/propertyPanelFxNodeRow.tsx
|
|
52760
|
-
import { useMemo as
|
|
53774
|
+
import { useMemo as useMemo46, useState as useState74 } from "react";
|
|
52761
53775
|
import {
|
|
52762
53776
|
defaultAudioFxParams as defaultAudioFxParams3,
|
|
52763
53777
|
getAudioFxDef as getAudioFxDef3
|
|
@@ -53183,11 +54197,11 @@ function FxNodeRow({
|
|
|
53183
54197
|
trackKind
|
|
53184
54198
|
}) {
|
|
53185
54199
|
const registryDef = getAudioFxDef3(node.type);
|
|
53186
|
-
const def =
|
|
54200
|
+
const def = useMemo46(() => registryDef ? plainDef(registryDef) : null, [registryDef]);
|
|
53187
54201
|
const primary = registryDef ? primaryParamOf(registryDef) : null;
|
|
53188
54202
|
const profile = getAudioFxProfile2(node.type);
|
|
53189
|
-
const derived =
|
|
53190
|
-
const onlyPrimary =
|
|
54203
|
+
const derived = useMemo46(() => profileParam(node.type), [node.type]);
|
|
54204
|
+
const onlyPrimary = useMemo46(
|
|
53191
54205
|
() => def && primary ? { ...def, params: def.params.filter((p) => p.key === primary) } : def,
|
|
53192
54206
|
[def, primary]
|
|
53193
54207
|
);
|
|
@@ -53833,12 +54847,12 @@ function FxSection({
|
|
|
53833
54847
|
[chain.nodes, mutate, onRemoveNodeAutomation, trackKind]
|
|
53834
54848
|
);
|
|
53835
54849
|
const [carveOpen, setCarveOpen] = useState75(true);
|
|
53836
|
-
const carveNodes =
|
|
53837
|
-
const handBuilt =
|
|
54850
|
+
const carveNodes = useMemo47(() => chain.nodes.filter((n) => n.fromCarve), [chain.nodes]);
|
|
54851
|
+
const handBuilt = useMemo47(
|
|
53838
54852
|
() => chain.nodes.map((node, i) => ({ node, i })).filter(({ node }) => !node.fromCarve && !node.fromEq),
|
|
53839
54853
|
[chain.nodes]
|
|
53840
54854
|
);
|
|
53841
|
-
const runs =
|
|
54855
|
+
const runs = useMemo47(() => {
|
|
53842
54856
|
const out = [];
|
|
53843
54857
|
for (const item of handBuilt) {
|
|
53844
54858
|
const preset = item.node.fromPreset;
|
|
@@ -53849,8 +54863,8 @@ function FxSection({
|
|
|
53849
54863
|
return out;
|
|
53850
54864
|
}, [handBuilt]);
|
|
53851
54865
|
const [collapsedRuns, setCollapsedRuns] = useState75(/* @__PURE__ */ new Set());
|
|
53852
|
-
const eqIds =
|
|
53853
|
-
const positions =
|
|
54866
|
+
const eqIds = useMemo47(() => audioEqIds(chain), [chain]);
|
|
54867
|
+
const positions = useMemo47(() => {
|
|
53854
54868
|
const map = /* @__PURE__ */ new Map();
|
|
53855
54869
|
let at = (showCarve ? 1 : 0) + eqIds.length;
|
|
53856
54870
|
for (const { i } of handBuilt) map.set(i, ++at);
|
|
@@ -54586,7 +55600,7 @@ function AudioFxGroup({
|
|
|
54586
55600
|
const removeNodeAutomation = (nodeId) => removeNodesAutomation([nodeId]);
|
|
54587
55601
|
const [analysing, setAnalysing] = useState77(false);
|
|
54588
55602
|
const storeElements = usePlayerStore((s) => s.elements);
|
|
54589
|
-
const signalPath =
|
|
55603
|
+
const signalPath = useMemo48(() => {
|
|
54590
55604
|
const doc = element.element?.ownerDocument;
|
|
54591
55605
|
return audioFxSignalPath(
|
|
54592
55606
|
element.tagName?.toLowerCase(),
|
|
@@ -55063,7 +56077,7 @@ function createGsapLivePreview(iframeRef) {
|
|
|
55063
56077
|
}
|
|
55064
56078
|
|
|
55065
56079
|
// src/components/editor/propertyPanelFlatColorGradingSection.tsx
|
|
55066
|
-
import { useEffect as useEffect81, useMemo as
|
|
56080
|
+
import { useEffect as useEffect81, useMemo as useMemo50, useRef as useRef97, useState as useState85 } from "react";
|
|
55067
56081
|
import {
|
|
55068
56082
|
HF_COLOR_GRADING_GRADE_PRESETS,
|
|
55069
56083
|
normalizeHfColorGrading as normalizeHfColorGrading5,
|
|
@@ -55098,7 +56112,7 @@ function presetPreviewHandlers({
|
|
|
55098
56112
|
import { useState as useState81 } from "react";
|
|
55099
56113
|
|
|
55100
56114
|
// src/components/editor/propertyPanelColorCurveGraph.tsx
|
|
55101
|
-
import { useMemo as
|
|
56115
|
+
import { useMemo as useMemo49, useRef as useRef90 } from "react";
|
|
55102
56116
|
import {
|
|
55103
56117
|
compileHfColorCurve,
|
|
55104
56118
|
compileHfHueCurve,
|
|
@@ -55286,8 +56300,8 @@ function CurveGraph({
|
|
|
55286
56300
|
onCancel
|
|
55287
56301
|
}) {
|
|
55288
56302
|
const pointerRef = useRef90(null);
|
|
55289
|
-
const samples =
|
|
55290
|
-
const path =
|
|
56303
|
+
const samples = useMemo49(() => samplesFor(points, tab), [points, tab]);
|
|
56304
|
+
const path = useMemo49(() => curvePath(samples, tab), [samples, tab]);
|
|
55291
56305
|
const selectRelativePoint = (offset) => {
|
|
55292
56306
|
if (points.length === 0) return;
|
|
55293
56307
|
const current = selectedIndex ?? (offset > 0 ? -1 : 0);
|
|
@@ -57084,14 +58098,14 @@ function FlatColorGradingSection({
|
|
|
57084
58098
|
const lutInputRef = useRef97(null);
|
|
57085
58099
|
const [lutOpen, setLutOpen] = useState85(false);
|
|
57086
58100
|
const [detailSettingsOpen, setDetailSettingsOpen] = useState85(null);
|
|
57087
|
-
const lutAssets =
|
|
58101
|
+
const lutAssets = useMemo50(
|
|
57088
58102
|
() => assets.filter((asset) => LUT_EXT.test(asset)).sort((a, b) => a.localeCompare(b)),
|
|
57089
58103
|
[assets]
|
|
57090
58104
|
);
|
|
57091
58105
|
const lut = grading.lut;
|
|
57092
58106
|
const selectedLutName = lut?.src ? lut.src.split("/").pop() ?? lut.src : null;
|
|
57093
|
-
const resolvedGrading =
|
|
57094
|
-
const secondaryInputGrading =
|
|
58107
|
+
const resolvedGrading = useMemo50(() => resolveColorGrading(grading), [grading]);
|
|
58108
|
+
const secondaryInputGrading = useMemo50(
|
|
57095
58109
|
() => resolveColorGrading({
|
|
57096
58110
|
intensity: 1,
|
|
57097
58111
|
adjust: resolvedGrading.adjust,
|
|
@@ -57109,7 +58123,7 @@ function FlatColorGradingSection({
|
|
|
57109
58123
|
]
|
|
57110
58124
|
);
|
|
57111
58125
|
const actions = createColorGradingActions(grading, onCommitColorGrading);
|
|
57112
|
-
const scopesRefreshKey =
|
|
58126
|
+
const scopesRefreshKey = useMemo50(() => serializeHfColorGrading2(grading), [grading]);
|
|
57113
58127
|
useEffect81(() => {
|
|
57114
58128
|
if (presetPreviews.status === "idle") onRequestPresetPreviews();
|
|
57115
58129
|
}, [onRequestPresetPreviews, presetPreviews.status]);
|
|
@@ -58239,7 +59253,7 @@ function FlatEffectsSection({
|
|
|
58239
59253
|
import { useState as useState88 } from "react";
|
|
58240
59254
|
|
|
58241
59255
|
// src/hooks/useBlockCatalog.ts
|
|
58242
|
-
import { useState as useState87, useEffect as useEffect83, useMemo as
|
|
59256
|
+
import { useState as useState87, useEffect as useEffect83, useMemo as useMemo51 } from "react";
|
|
58243
59257
|
|
|
58244
59258
|
// src/utils/blockCategories.ts
|
|
58245
59259
|
import {
|
|
@@ -58306,7 +59320,7 @@ function useBlockCatalog() {
|
|
|
58306
59320
|
cancelled = true;
|
|
58307
59321
|
};
|
|
58308
59322
|
}, []);
|
|
58309
|
-
const filteredBlocks =
|
|
59323
|
+
const filteredBlocks = useMemo51(() => {
|
|
58310
59324
|
let result = blocks;
|
|
58311
59325
|
if (category) {
|
|
58312
59326
|
result = result.filter((b) => b.category === category);
|
|
@@ -58618,7 +59632,7 @@ function PropertyPanelFlat({
|
|
|
58618
59632
|
const showMotionEffects = gsapEffectHandlers !== null && !audioSelection;
|
|
58619
59633
|
const showMotionGroup = showMotionTiming || showMotionEffects;
|
|
58620
59634
|
const volumeAutomation = useVolumeAutomation(element, onSetAttributeQuiet ?? onSetAttributeLive);
|
|
58621
|
-
const audioGroupLabel =
|
|
59635
|
+
const audioGroupLabel = useMemo52(() => {
|
|
58622
59636
|
const doc = element.element?.ownerDocument;
|
|
58623
59637
|
const id = element.id;
|
|
58624
59638
|
if (!doc || !id) return void 0;
|
|
@@ -59179,12 +60193,12 @@ var PropertyPanel = memo32(function PropertyPanel2(props) {
|
|
|
59179
60193
|
const cacheEntry = usePlayerStore((s) => s.keyframeCache.get(cacheElementKey));
|
|
59180
60194
|
const iframeRef = previewIframeRef ?? { current: null };
|
|
59181
60195
|
const gsapAnimIdForMemo = element ? gsapAnimations?.find((a) => a.keyframes)?.id ?? gsapAnimations?.[0]?.id ?? null : null;
|
|
59182
|
-
const gsapRuntimeValues =
|
|
60196
|
+
const gsapRuntimeValues = useMemo53(
|
|
59183
60197
|
() => element ? readGsapRuntimeValuesForPanel(gsapAnimIdForMemo, gsapAnimations, element, iframeRef) : null,
|
|
59184
60198
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- iframeRef is stable; currentTime drives re-reads during playback
|
|
59185
60199
|
[gsapAnimIdForMemo, gsapAnimations, element, currentTime]
|
|
59186
60200
|
);
|
|
59187
|
-
const gsapBorderRadius =
|
|
60201
|
+
const gsapBorderRadius = useMemo53(
|
|
59188
60202
|
() => element ? readGsapBorderRadiusForPanel(gsapRuntimeValues, gsapAnimations, element, iframeRef) : null,
|
|
59189
60203
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
59190
60204
|
[gsapRuntimeValues, gsapAnimations, element, currentTime]
|
|
@@ -59574,11 +60588,11 @@ var PropertyPanel = memo32(function PropertyPanel2(props) {
|
|
|
59574
60588
|
});
|
|
59575
60589
|
|
|
59576
60590
|
// src/components/editor/FileTree.tsx
|
|
59577
|
-
import { memo as memo34, useState as useState92, useCallback as useCallback78, useMemo as
|
|
60591
|
+
import { memo as memo34, useState as useState92, useCallback as useCallback78, useMemo as useMemo55, useRef as useRef101 } from "react";
|
|
59578
60592
|
import { Plus as Plus2, FolderSimplePlus as FolderSimplePlus2 } from "@phosphor-icons/react";
|
|
59579
60593
|
|
|
59580
60594
|
// src/components/editor/FileTreeNodes.tsx
|
|
59581
|
-
import { memo as memo33, useState as useState91, useCallback as useCallback77, useMemo as
|
|
60595
|
+
import { memo as memo33, useState as useState91, useCallback as useCallback77, useMemo as useMemo54, useRef as useRef100, useEffect as useEffect85 } from "react";
|
|
59582
60596
|
import {
|
|
59583
60597
|
PencilSimple,
|
|
59584
60598
|
Copy,
|
|
@@ -60061,7 +61075,7 @@ var TreeFolder = memo33(function TreeFolder2({
|
|
|
60061
61075
|
}) {
|
|
60062
61076
|
const [isOpen, setIsOpen] = useState91(defaultOpen);
|
|
60063
61077
|
const toggle = useCallback77(() => setIsOpen((v) => !v), []);
|
|
60064
|
-
const children =
|
|
61078
|
+
const children = useMemo54(() => sortChildren(node.children), [node.children]);
|
|
60065
61079
|
const Chevron = isOpen ? ChevronDown : ChevronRight;
|
|
60066
61080
|
const isDragOver = dragOverFolder === node.fullPath;
|
|
60067
61081
|
const isRenaming = inlineInput?.mode === "rename" && inlineInput.originalPath === node.fullPath;
|
|
@@ -60192,8 +61206,8 @@ var FileTree = memo34(function FileTree2({
|
|
|
60192
61206
|
onImportFiles,
|
|
60193
61207
|
lintFindingsByFile
|
|
60194
61208
|
}) {
|
|
60195
|
-
const tree =
|
|
60196
|
-
const children =
|
|
61209
|
+
const tree = useMemo55(() => buildTree(files), [files]);
|
|
61210
|
+
const children = useMemo55(() => sortChildren(tree.children), [tree]);
|
|
60197
61211
|
const [contextMenu, setContextMenu] = useState92(null);
|
|
60198
61212
|
const [inlineInput, setInlineInput] = useState92(null);
|
|
60199
61213
|
const [deleteTarget, setDeleteTarget] = useState92(null);
|
|
@@ -60460,10 +61474,10 @@ var FileTree = memo34(function FileTree2({
|
|
|
60460
61474
|
});
|
|
60461
61475
|
|
|
60462
61476
|
// src/App.tsx
|
|
60463
|
-
import { useState as useState155, useCallback as useCallback176, useRef as useRef170, useMemo as
|
|
61477
|
+
import { useState as useState155, useCallback as useCallback176, useRef as useRef170, useMemo as useMemo77, useLayoutEffect as useLayoutEffect10 } from "react";
|
|
60464
61478
|
|
|
60465
61479
|
// src/components/renders/useRenderQueue.ts
|
|
60466
|
-
import { useState as useState94, useEffect as useEffect87, useCallback as useCallback80, useRef as useRef103, useMemo as
|
|
61480
|
+
import { useState as useState94, useEffect as useEffect87, useCallback as useCallback80, useRef as useRef103, useMemo as useMemo56 } from "react";
|
|
60467
61481
|
|
|
60468
61482
|
// src/components/renders/serverError.ts
|
|
60469
61483
|
async function readServerError(res) {
|
|
@@ -60987,7 +62001,7 @@ function useRenderQueue(projectId, activeCompPathRef) {
|
|
|
60987
62001
|
};
|
|
60988
62002
|
}, [projectId]);
|
|
60989
62003
|
const isRendering = jobs.some((j) => j.status === "rendering");
|
|
60990
|
-
return
|
|
62004
|
+
return useMemo56(
|
|
60991
62005
|
() => ({
|
|
60992
62006
|
jobs,
|
|
60993
62007
|
isRendering,
|
|
@@ -61044,8 +62058,8 @@ function useDialogBehavior({
|
|
|
61044
62058
|
const onCloseRef = useRef104(onClose);
|
|
61045
62059
|
onCloseRef.current = onClose;
|
|
61046
62060
|
const requestClose = useCallback81(() => {
|
|
61047
|
-
const
|
|
61048
|
-
if (
|
|
62061
|
+
const guard3 = canCloseRef.current;
|
|
62062
|
+
if (guard3 && !guard3()) return;
|
|
61049
62063
|
onCloseRef.current();
|
|
61050
62064
|
}, []);
|
|
61051
62065
|
useEffect88(() => {
|
|
@@ -62254,7 +63268,7 @@ function useCaptionSync(projectId) {
|
|
|
62254
63268
|
}
|
|
62255
63269
|
|
|
62256
63270
|
// src/hooks/usePersistentEditHistory.ts
|
|
62257
|
-
import { useCallback as useCallback84, useEffect as useEffect90, useMemo as
|
|
63271
|
+
import { useCallback as useCallback84, useEffect as useEffect90, useMemo as useMemo57, useRef as useRef110, useState as useState99 } from "react";
|
|
62258
63272
|
|
|
62259
63273
|
// src/utils/editHistory.ts
|
|
62260
63274
|
var DEFAULT_MAX_ENTRIES = 100;
|
|
@@ -62606,7 +63620,7 @@ function createPersistentEditHistoryStore({
|
|
|
62606
63620
|
};
|
|
62607
63621
|
}
|
|
62608
63622
|
function usePersistentEditHistory(options) {
|
|
62609
|
-
const storage =
|
|
63623
|
+
const storage = useMemo57(
|
|
62610
63624
|
() => options.storage ?? createIndexedDbEditHistoryStorage(),
|
|
62611
63625
|
[options.storage]
|
|
62612
63626
|
);
|
|
@@ -62875,10 +63889,10 @@ function usePanelLayout(initialState2) {
|
|
|
62875
63889
|
}
|
|
62876
63890
|
|
|
62877
63891
|
// src/hooks/useFileManager.ts
|
|
62878
|
-
import { useState as useState102, useCallback as useCallback88, useMemo as
|
|
63892
|
+
import { useState as useState102, useCallback as useCallback88, useMemo as useMemo59, useRef as useRef113 } from "react";
|
|
62879
63893
|
|
|
62880
63894
|
// src/hooks/useFileTree.ts
|
|
62881
|
-
import { useState as useState101, useCallback as useCallback86, useEffect as useEffect92, useMemo as
|
|
63895
|
+
import { useState as useState101, useCallback as useCallback86, useEffect as useEffect92, useMemo as useMemo58 } from "react";
|
|
62882
63896
|
function useFileTree({ projectId, projectIdRef }) {
|
|
62883
63897
|
const [projectDir, setProjectDir] = useState101(null);
|
|
62884
63898
|
const [fileTree, setFileTree] = useState101([]);
|
|
@@ -62914,11 +63928,11 @@ function useFileTree({ projectId, projectIdRef }) {
|
|
|
62914
63928
|
if (data.files) setFileTree(data.files);
|
|
62915
63929
|
}, [projectIdRef]);
|
|
62916
63930
|
const compositions = compositionPaths;
|
|
62917
|
-
const assets =
|
|
63931
|
+
const assets = useMemo58(
|
|
62918
63932
|
() => fileTree.filter((f) => !f.endsWith(".html") && !f.endsWith(".md") && !f.endsWith(".json")),
|
|
62919
63933
|
[fileTree]
|
|
62920
63934
|
);
|
|
62921
|
-
const fontAssets =
|
|
63935
|
+
const fontAssets = useMemo58(
|
|
62922
63936
|
() => assets.filter((asset) => FONT_EXT.test(asset)).map((asset) => ({
|
|
62923
63937
|
family: fontFamilyFromAssetPath(asset),
|
|
62924
63938
|
path: asset,
|
|
@@ -63081,7 +64095,7 @@ function useFileManager({
|
|
|
63081
64095
|
const projectIdRef = useRef113(projectId);
|
|
63082
64096
|
projectIdRef.current = projectId;
|
|
63083
64097
|
const importedFontAssetsRef = useRef113([]);
|
|
63084
|
-
const fileVersionScope =
|
|
64098
|
+
const fileVersionScope = useMemo59(
|
|
63085
64099
|
() => ({ projectId, versions: /* @__PURE__ */ new Map() }),
|
|
63086
64100
|
[projectId]
|
|
63087
64101
|
);
|
|
@@ -63881,52 +64895,6 @@ import { useCallback as useCallback100, useRef as useRef118 } from "react";
|
|
|
63881
64895
|
// src/hooks/useRazorSplit.ts
|
|
63882
64896
|
import { useCallback as useCallback91, useRef as useRef116 } from "react";
|
|
63883
64897
|
|
|
63884
|
-
// src/utils/projectRouting.ts
|
|
63885
|
-
var PROJECT_HASH_PREFIX = "#project/";
|
|
63886
|
-
function decodeHashProjectId(value) {
|
|
63887
|
-
try {
|
|
63888
|
-
return decodeURIComponent(value);
|
|
63889
|
-
} catch {
|
|
63890
|
-
return value;
|
|
63891
|
-
}
|
|
63892
|
-
}
|
|
63893
|
-
function normalizeHashParams(params) {
|
|
63894
|
-
if (!params) return new URLSearchParams();
|
|
63895
|
-
if (params instanceof URLSearchParams) return params;
|
|
63896
|
-
const next = new URLSearchParams();
|
|
63897
|
-
for (const [key2, value] of Object.entries(params)) {
|
|
63898
|
-
if (!key2 || value == null || value === "") continue;
|
|
63899
|
-
next.set(key2, value);
|
|
63900
|
-
}
|
|
63901
|
-
return next;
|
|
63902
|
-
}
|
|
63903
|
-
function encodeProjectId(projectId) {
|
|
63904
|
-
return encodeURIComponent(projectId);
|
|
63905
|
-
}
|
|
63906
|
-
function buildProjectHash(projectId, params) {
|
|
63907
|
-
const search = normalizeHashParams(params).toString();
|
|
63908
|
-
return `${PROJECT_HASH_PREFIX}${encodeProjectId(projectId)}${search ? `?${search}` : ""}`;
|
|
63909
|
-
}
|
|
63910
|
-
function parseProjectHashRoute(hash) {
|
|
63911
|
-
if (!hash.startsWith(PROJECT_HASH_PREFIX)) return null;
|
|
63912
|
-
const route = hash.slice(PROJECT_HASH_PREFIX.length);
|
|
63913
|
-
const queryIndex = route.indexOf("?");
|
|
63914
|
-
const encodedProjectId = queryIndex >= 0 ? route.slice(0, queryIndex) : route;
|
|
63915
|
-
if (!encodedProjectId || encodedProjectId.includes("/")) return null;
|
|
63916
|
-
const rawParams = queryIndex >= 0 ? route.slice(queryIndex + 1) : "";
|
|
63917
|
-
return {
|
|
63918
|
-
projectId: decodeHashProjectId(encodedProjectId),
|
|
63919
|
-
params: new URLSearchParams(rawParams)
|
|
63920
|
-
};
|
|
63921
|
-
}
|
|
63922
|
-
function parseProjectIdFromHash(hash) {
|
|
63923
|
-
return parseProjectHashRoute(hash)?.projectId ?? null;
|
|
63924
|
-
}
|
|
63925
|
-
function buildProjectApiPath(projectId, suffix = "") {
|
|
63926
|
-
const normalizedSuffix = suffix && !suffix.startsWith("/") ? `/${suffix}` : suffix;
|
|
63927
|
-
return `/api/projects/${encodeProjectId(projectId)}${normalizedSuffix}`;
|
|
63928
|
-
}
|
|
63929
|
-
|
|
63930
64898
|
// src/utils/razorSplitTransaction.ts
|
|
63931
64899
|
function targetIdentity(path, target) {
|
|
63932
64900
|
if (target.hfId) return `${path}|hf:${target.hfId}`;
|
|
@@ -68631,7 +69599,7 @@ function useGroupCommits(params) {
|
|
|
68631
69599
|
}
|
|
68632
69600
|
|
|
68633
69601
|
// src/hooks/useGsapScriptCommits.ts
|
|
68634
|
-
import { useCallback as useCallback115, useMemo as
|
|
69602
|
+
import { useCallback as useCallback115, useMemo as useMemo60, useRef as useRef126 } from "react";
|
|
68635
69603
|
import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
|
|
68636
69604
|
|
|
68637
69605
|
// src/hooks/gsapRuntimePatch.ts
|
|
@@ -69946,7 +70914,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
69946
70914
|
const { instantPatch: _instantPatch, ...batchOptions } = options;
|
|
69947
70915
|
await finalizeSuccessfulMutation(pid, compositionPath, last.selection, last.mutation, targetPath, result, instantPatches.length > 0 ? { ...batchOptions, instantPatches } : batchOptions);
|
|
69948
70916
|
}, [showToast, finalizeSuccessfulMutation]);
|
|
69949
|
-
const commitMutation =
|
|
70917
|
+
const commitMutation = useMemo60(() => {
|
|
69950
70918
|
const serializeFile = (file, task) => {
|
|
69951
70919
|
if (writeProjectFile) {
|
|
69952
70920
|
return serializeStudioFileMutation(writeProjectFile, file, task);
|
|
@@ -70011,7 +70979,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
|
|
|
70011
70979
|
},
|
|
70012
70980
|
[activeProjectId]
|
|
70013
70981
|
);
|
|
70014
|
-
const sdkDeps =
|
|
70982
|
+
const sdkDeps = useMemo60(
|
|
70015
70983
|
() => writeProjectFile ? {
|
|
70016
70984
|
editHistory: { recordEdit: editHistory.recordEdit },
|
|
70017
70985
|
writeProjectFile,
|
|
@@ -73006,7 +73974,7 @@ function useStudioExternalFileChanges({
|
|
|
73006
73974
|
}
|
|
73007
73975
|
|
|
73008
73976
|
// src/hooks/useBlockHandlers.ts
|
|
73009
|
-
import { useCallback as useCallback127, useMemo as
|
|
73977
|
+
import { useCallback as useCallback127, useMemo as useMemo61, useRef as useRef133, useState as useState109 } from "react";
|
|
73010
73978
|
|
|
73011
73979
|
// src/utils/blockInstaller.ts
|
|
73012
73980
|
function getMaxZIndexFromIframe(iframe) {
|
|
@@ -73192,7 +74160,7 @@ function useBlockHandlers({
|
|
|
73192
74160
|
setRightPanelTab
|
|
73193
74161
|
}) {
|
|
73194
74162
|
const [activeBlockParams, setActiveBlockParams] = useState109(null);
|
|
73195
|
-
const blockCtx =
|
|
74163
|
+
const blockCtx = useMemo61(
|
|
73196
74164
|
() => ({
|
|
73197
74165
|
activeCompPath: blockCtxDeps.activeCompPath,
|
|
73198
74166
|
timelineElements: blockCtxDeps.timelineElements,
|
|
@@ -74277,7 +75245,7 @@ import { useCallback as useCallback132 } from "react";
|
|
|
74277
75245
|
import { createElement as createElement3 } from "react";
|
|
74278
75246
|
|
|
74279
75247
|
// src/player/components/AudioWaveform.tsx
|
|
74280
|
-
import { memo as memo36, useCallback as useCallback130, useMemo as
|
|
75248
|
+
import { memo as memo36, useCallback as useCallback130, useMemo as useMemo62, useRef as useRef137 } from "react";
|
|
74281
75249
|
import { jsx as jsx167, jsxs as jsxs139 } from "react/jsx-runtime";
|
|
74282
75250
|
var BAR_WIDTH = 2;
|
|
74283
75251
|
var BAR_STEP = 3;
|
|
@@ -74337,7 +75305,7 @@ var AudioWaveform = memo36(function AudioWaveform2({
|
|
|
74337
75305
|
const canvasRef = useRef137(null);
|
|
74338
75306
|
const observerRef = useRef137(null);
|
|
74339
75307
|
const cacheKey = waveformUrl ?? audioUrl;
|
|
74340
|
-
const request =
|
|
75308
|
+
const request = useMemo62(
|
|
74341
75309
|
() => ({
|
|
74342
75310
|
key: createThumbnailKey({ kind: "waveform", source: cacheKey }),
|
|
74343
75311
|
projectId,
|
|
@@ -74446,7 +75414,7 @@ var AudioWaveform = memo36(function AudioWaveform2({
|
|
|
74446
75414
|
});
|
|
74447
75415
|
|
|
74448
75416
|
// src/player/components/ImageThumbnail.tsx
|
|
74449
|
-
import { memo as memo37, useCallback as useCallback131, useMemo as
|
|
75417
|
+
import { memo as memo37, useCallback as useCallback131, useMemo as useMemo63, useRef as useRef138, useState as useState110 } from "react";
|
|
74450
75418
|
import { jsx as jsx168, jsxs as jsxs140 } from "react/jsx-runtime";
|
|
74451
75419
|
var ImageThumbnail = memo37(function ImageThumbnail2({
|
|
74452
75420
|
imageSrc,
|
|
@@ -74459,7 +75427,7 @@ var ImageThumbnail = memo37(function ImageThumbnail2({
|
|
|
74459
75427
|
}) {
|
|
74460
75428
|
const [containerWidth, setContainerWidth] = useState110(0);
|
|
74461
75429
|
const observerRef = useRef138(null);
|
|
74462
|
-
const request =
|
|
75430
|
+
const request = useMemo63(
|
|
74463
75431
|
() => ({
|
|
74464
75432
|
key: createThumbnailKey({ kind: "image", source: imageSrc, rich: Number(rich) }),
|
|
74465
75433
|
projectId,
|
|
@@ -74776,36 +75744,6 @@ function useConsoleErrorCapture(previewIframe) {
|
|
|
74776
75744
|
|
|
74777
75745
|
// src/hooks/useFrameCapture.ts
|
|
74778
75746
|
import { useState as useState112, useCallback as useCallback134, useRef as useRef140 } from "react";
|
|
74779
|
-
|
|
74780
|
-
// src/utils/frameCapture.ts
|
|
74781
|
-
function normalizeCompositionPath(compositionPath) {
|
|
74782
|
-
return compositionPath && compositionPath !== "master" ? compositionPath : "index.html";
|
|
74783
|
-
}
|
|
74784
|
-
function buildFrameCaptureUrl({
|
|
74785
|
-
projectId,
|
|
74786
|
-
compositionPath,
|
|
74787
|
-
currentTime,
|
|
74788
|
-
origin = window.location.origin
|
|
74789
|
-
}) {
|
|
74790
|
-
const compPath = normalizeCompositionPath(compositionPath);
|
|
74791
|
-
const url = new URL(
|
|
74792
|
-
buildProjectApiPath(projectId, `/thumbnail/${encodeURIComponent(compPath)}`),
|
|
74793
|
-
origin
|
|
74794
|
-
);
|
|
74795
|
-
url.searchParams.set("t", Math.max(0, currentTime).toFixed(3));
|
|
74796
|
-
url.searchParams.set("format", "png");
|
|
74797
|
-
url.searchParams.set("output", "source");
|
|
74798
|
-
url.searchParams.set("v", String(Date.now()));
|
|
74799
|
-
return url.toString();
|
|
74800
|
-
}
|
|
74801
|
-
function buildFrameCaptureFilename(compositionPath, currentTime) {
|
|
74802
|
-
const compPath = normalizeCompositionPath(compositionPath);
|
|
74803
|
-
const base2 = compPath.split("/").pop()?.replace(/\.html$/i, "") || "frame";
|
|
74804
|
-
const frameTime = Math.max(0, currentTime).toFixed(3).replace(".", "-");
|
|
74805
|
-
return `${base2}-${frameTime}s.png`;
|
|
74806
|
-
}
|
|
74807
|
-
|
|
74808
|
-
// src/hooks/useFrameCapture.ts
|
|
74809
75747
|
function useFrameCapture({
|
|
74810
75748
|
projectId,
|
|
74811
75749
|
activeCompPath,
|
|
@@ -74899,7 +75837,7 @@ function useFrameCapture({
|
|
|
74899
75837
|
}
|
|
74900
75838
|
|
|
74901
75839
|
// src/hooks/useLintModal.ts
|
|
74902
|
-
import { useState as useState113, useCallback as useCallback135, useEffect as useEffect108, useRef as useRef141, useMemo as
|
|
75840
|
+
import { useState as useState113, useCallback as useCallback135, useEffect as useEffect108, useRef as useRef141, useMemo as useMemo64 } from "react";
|
|
74903
75841
|
function parseFinding(f) {
|
|
74904
75842
|
return {
|
|
74905
75843
|
severity: f.severity === "error" ? "error" : "warning",
|
|
@@ -74971,8 +75909,8 @@ function useLintModal(projectId, refreshKey) {
|
|
|
74971
75909
|
},
|
|
74972
75910
|
[backgroundFindings]
|
|
74973
75911
|
);
|
|
74974
|
-
const findingsByElement =
|
|
74975
|
-
const findingsByFile =
|
|
75912
|
+
const findingsByElement = useMemo64(() => groupFindings((f) => f.elementId), [groupFindings]);
|
|
75913
|
+
const findingsByFile = useMemo64(() => groupFindings((f) => f.file), [groupFindings]);
|
|
74976
75914
|
useEffect108(() => {
|
|
74977
75915
|
usePlayerStore.getState().setLintFindingsByElement(findingsByElement);
|
|
74978
75916
|
}, [findingsByElement]);
|
|
@@ -75454,16 +76392,16 @@ function useStudioUrlState({
|
|
|
75454
76392
|
}
|
|
75455
76393
|
|
|
75456
76394
|
// src/hooks/useEffectiveTimelineDuration.ts
|
|
75457
|
-
import { useMemo as
|
|
76395
|
+
import { useMemo as useMemo65 } from "react";
|
|
75458
76396
|
function useEffectiveTimelineDuration(timelineDuration2, timelineElements) {
|
|
75459
|
-
return
|
|
76397
|
+
return useMemo65(
|
|
75460
76398
|
() => getEffectiveTimelineDuration(timelineDuration2, timelineElements),
|
|
75461
76399
|
[timelineDuration2, timelineElements]
|
|
75462
76400
|
);
|
|
75463
76401
|
}
|
|
75464
76402
|
|
|
75465
76403
|
// src/hooks/useStudioContextValue.ts
|
|
75466
|
-
import { useCallback as useCallback139, useMemo as
|
|
76404
|
+
import { useCallback as useCallback139, useMemo as useMemo66, useRef as useRef144, useState as useState116 } from "react";
|
|
75467
76405
|
function buildStudioContextValue(input) {
|
|
75468
76406
|
return {
|
|
75469
76407
|
projectId: input.projectId,
|
|
@@ -75478,6 +76416,9 @@ function buildStudioContextValue(input) {
|
|
|
75478
76416
|
timelineElements: input.timelineElements,
|
|
75479
76417
|
isPlaying: input.isPlaying,
|
|
75480
76418
|
editHistory: input.editHistory,
|
|
76419
|
+
// Conflict first: when both are true the conflict is the one the user has
|
|
76420
|
+
// been asked to decide, and resolving it is what unblocks the queue.
|
|
76421
|
+
writeBlockedReason: input.externalFileConflict ? "an external change to this file is waiting to be resolved" : input.domEditSaveQueuePaused,
|
|
75481
76422
|
handleUndo: input.handleUndo,
|
|
75482
76423
|
handleRedo: input.handleRedo,
|
|
75483
76424
|
renderQueue: input.renderQueue,
|
|
@@ -75488,7 +76429,7 @@ function buildStudioContextValue(input) {
|
|
|
75488
76429
|
};
|
|
75489
76430
|
}
|
|
75490
76431
|
function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, isPlaying, domEditSelection, isGestureRecording) {
|
|
75491
|
-
return
|
|
76432
|
+
return useMemo66(() => {
|
|
75492
76433
|
const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
|
|
75493
76434
|
const layersPanelActive = inspectorTabActive && rightInspectorPanes.layers;
|
|
75494
76435
|
const designPanelActive = inspectorTabActive && rightInspectorPanes.design;
|
|
@@ -75561,7 +76502,7 @@ function useGlobalFileDrop(handleTimelineFileDrop) {
|
|
|
75561
76502
|
import { useRef as useRef146 } from "react";
|
|
75562
76503
|
|
|
75563
76504
|
// src/contexts/PanelLayoutContext.tsx
|
|
75564
|
-
import { useContext as useContext8, useMemo as
|
|
76505
|
+
import { useContext as useContext8, useMemo as useMemo67 } from "react";
|
|
75565
76506
|
import { jsx as jsx169 } from "react/jsx-runtime";
|
|
75566
76507
|
var PanelLayoutContext = createStableContext("PanelLayoutContext", null);
|
|
75567
76508
|
function usePanelLayoutContext() {
|
|
@@ -75591,7 +76532,7 @@ function PanelLayoutProvider({
|
|
|
75591
76532
|
},
|
|
75592
76533
|
children
|
|
75593
76534
|
}) {
|
|
75594
|
-
const stable =
|
|
76535
|
+
const stable = useMemo67(
|
|
75595
76536
|
() => ({
|
|
75596
76537
|
leftWidth,
|
|
75597
76538
|
rightWidth,
|
|
@@ -75639,7 +76580,7 @@ import {
|
|
|
75639
76580
|
useCallback as useCallback140,
|
|
75640
76581
|
useContext as useContext9,
|
|
75641
76582
|
useEffect as useEffect110,
|
|
75642
|
-
useMemo as
|
|
76583
|
+
useMemo as useMemo68,
|
|
75643
76584
|
useRef as useRef145,
|
|
75644
76585
|
useState as useState117
|
|
75645
76586
|
} from "react";
|
|
@@ -75671,8 +76612,8 @@ function useViewModeState() {
|
|
|
75671
76612
|
const guardsRef = useRef145(/* @__PURE__ */ new Set());
|
|
75672
76613
|
const acceptedLocationRef = useRef145(readHistoryLocation());
|
|
75673
76614
|
const canSetViewMode = useCallback140((mode) => {
|
|
75674
|
-
for (const
|
|
75675
|
-
if (!
|
|
76615
|
+
for (const guard3 of guardsRef.current) {
|
|
76616
|
+
if (!guard3(mode)) return false;
|
|
75676
76617
|
}
|
|
75677
76618
|
return true;
|
|
75678
76619
|
}, []);
|
|
@@ -75702,13 +76643,13 @@ function useViewModeState() {
|
|
|
75702
76643
|
},
|
|
75703
76644
|
[canSetViewMode]
|
|
75704
76645
|
);
|
|
75705
|
-
const registerViewModeGuard = useCallback140((
|
|
75706
|
-
guardsRef.current.add(
|
|
76646
|
+
const registerViewModeGuard = useCallback140((guard3) => {
|
|
76647
|
+
guardsRef.current.add(guard3);
|
|
75707
76648
|
return () => {
|
|
75708
|
-
guardsRef.current.delete(
|
|
76649
|
+
guardsRef.current.delete(guard3);
|
|
75709
76650
|
};
|
|
75710
76651
|
}, []);
|
|
75711
|
-
return
|
|
76652
|
+
return useMemo68(
|
|
75712
76653
|
() => ({ viewMode, setViewMode, registerViewModeGuard }),
|
|
75713
76654
|
[viewMode, setViewMode, registerViewModeGuard]
|
|
75714
76655
|
);
|
|
@@ -76898,7 +77839,7 @@ function useGestureCommit({
|
|
|
76898
77839
|
}
|
|
76899
77840
|
|
|
76900
77841
|
// src/components/editor/GestureTrailOverlay.tsx
|
|
76901
|
-
import { memo as memo38, useMemo as
|
|
77842
|
+
import { memo as memo38, useMemo as useMemo69 } from "react";
|
|
76902
77843
|
import { Fragment as Fragment49, jsx as jsx172, jsxs as jsxs142 } from "react/jsx-runtime";
|
|
76903
77844
|
var GestureTrailOverlay = memo38(function GestureTrailOverlay2({
|
|
76904
77845
|
samples,
|
|
@@ -76910,7 +77851,7 @@ var GestureTrailOverlay = memo38(function GestureTrailOverlay2({
|
|
|
76910
77851
|
mode,
|
|
76911
77852
|
accentColor = "#3CE6AC"
|
|
76912
77853
|
}) {
|
|
76913
|
-
const trailPoints =
|
|
77854
|
+
const trailPoints = useMemo69(() => {
|
|
76914
77855
|
if (!canvasRect) return "";
|
|
76915
77856
|
if (trail2 && trail2.length > 1) {
|
|
76916
77857
|
return trail2.map((p) => `${p.x - canvasRect.left},${p.y - canvasRect.top}`).join(" ");
|
|
@@ -76918,7 +77859,7 @@ var GestureTrailOverlay = memo38(function GestureTrailOverlay2({
|
|
|
76918
77859
|
if (samples.length === 0) return "";
|
|
76919
77860
|
return samples.filter((s) => s.properties.x != null && s.properties.y != null).map((s) => `${s.properties.x},${s.properties.y}`).join(" ");
|
|
76920
77861
|
}, [samples, trail2, sampleCount, canvasRect?.left, canvasRect?.top]);
|
|
76921
|
-
const simplifiedPath =
|
|
77862
|
+
const simplifiedPath = useMemo69(() => {
|
|
76922
77863
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return "";
|
|
76923
77864
|
const pts = [];
|
|
76924
77865
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -76930,7 +77871,7 @@ var GestureTrailOverlay = memo38(function GestureTrailOverlay2({
|
|
|
76930
77871
|
if (pts.length === 0) return "";
|
|
76931
77872
|
return pts.map((p) => `${p.x},${p.y}`).join(" ");
|
|
76932
77873
|
}, [simplifiedPoints]);
|
|
76933
|
-
const diamondPositions =
|
|
77874
|
+
const diamondPositions = useMemo69(() => {
|
|
76934
77875
|
if (!simplifiedPoints || simplifiedPoints.size === 0) return [];
|
|
76935
77876
|
const pts = [];
|
|
76936
77877
|
for (const [pct, props] of simplifiedPoints) {
|
|
@@ -77329,7 +78270,7 @@ var CompositionsTab = memo39(function CompositionsTab2({
|
|
|
77329
78270
|
});
|
|
77330
78271
|
|
|
77331
78272
|
// src/components/sidebar/AssetsTab.tsx
|
|
77332
|
-
import { memo as memo40, useState as useState126, useCallback as useCallback147, useRef as useRef153, useMemo as
|
|
78273
|
+
import { memo as memo40, useState as useState126, useCallback as useCallback147, useRef as useRef153, useMemo as useMemo71, useEffect as useEffect119 } from "react";
|
|
77333
78274
|
|
|
77334
78275
|
// src/components/ui/SearchInput.tsx
|
|
77335
78276
|
import { jsx as jsx174, jsxs as jsxs144 } from "react/jsx-runtime";
|
|
@@ -77941,7 +78882,7 @@ function AudioRow({
|
|
|
77941
78882
|
}
|
|
77942
78883
|
|
|
77943
78884
|
// src/components/sidebar/GlobalAssetsView.tsx
|
|
77944
|
-
import { useEffect as useEffect116, useMemo as
|
|
78885
|
+
import { useEffect as useEffect116, useMemo as useMemo70, useState as useState123 } from "react";
|
|
77945
78886
|
import { jsx as jsx177, jsxs as jsxs147 } from "react/jsx-runtime";
|
|
77946
78887
|
function globalAssetRows(records, query = "") {
|
|
77947
78888
|
const q = query.trim().toLowerCase();
|
|
@@ -77968,7 +78909,7 @@ function GlobalAssetsView({ searchQuery }) {
|
|
|
77968
78909
|
cancelled = true;
|
|
77969
78910
|
};
|
|
77970
78911
|
}, []);
|
|
77971
|
-
const rows =
|
|
78912
|
+
const rows = useMemo70(() => globalAssetRows(records ?? [], searchQuery), [records, searchQuery]);
|
|
77972
78913
|
if (records === null) {
|
|
77973
78914
|
return /* @__PURE__ */ jsx177("p", { className: "px-4 py-3 text-[11px] text-panel-text-5", children: "Loading global assets\u2026" });
|
|
77974
78915
|
}
|
|
@@ -78527,12 +79468,12 @@ var AssetsTab = memo40(function AssetsTab2({
|
|
|
78527
79468
|
setTimeout(() => setCopyFeedback(null), copied ? 1500 : 3e3);
|
|
78528
79469
|
}, []);
|
|
78529
79470
|
const elements = usePlayerStore((s) => s.elements);
|
|
78530
|
-
const usedPaths =
|
|
78531
|
-
const allMediaAssets =
|
|
79471
|
+
const usedPaths = useMemo71(() => deriveUsedPaths(elements), [elements]);
|
|
79472
|
+
const allMediaAssets = useMemo71(
|
|
78532
79473
|
() => assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a)),
|
|
78533
79474
|
[assets]
|
|
78534
79475
|
);
|
|
78535
|
-
const mediaAssets =
|
|
79476
|
+
const mediaAssets = useMemo71(() => {
|
|
78536
79477
|
const all = filterByUsage(allMediaAssets, usedPaths, usageFilter);
|
|
78537
79478
|
if (!searchQuery) return all;
|
|
78538
79479
|
const q = searchQuery.toLowerCase();
|
|
@@ -78542,7 +79483,7 @@ var AssetsTab = memo40(function AssetsTab2({
|
|
|
78542
79483
|
return rec?.description?.toLowerCase().includes(q);
|
|
78543
79484
|
});
|
|
78544
79485
|
}, [allMediaAssets, searchQuery, manifest, usageFilter, usedPaths]);
|
|
78545
|
-
const categorized =
|
|
79486
|
+
const categorized = useMemo71(() => {
|
|
78546
79487
|
const groups = { audio: [], images: [], video: [], fonts: [] };
|
|
78547
79488
|
for (const a of mediaAssets) {
|
|
78548
79489
|
const cat = getCategory(a);
|
|
@@ -78557,12 +79498,12 @@ var AssetsTab = memo40(function AssetsTab2({
|
|
|
78557
79498
|
}
|
|
78558
79499
|
return groups;
|
|
78559
79500
|
}, [mediaAssets, usedPaths]);
|
|
78560
|
-
const counts =
|
|
79501
|
+
const counts = useMemo71(() => {
|
|
78561
79502
|
const c = { all: mediaAssets.length };
|
|
78562
79503
|
for (const cat of FILTER_ORDER) c[cat] = categorized[cat].length;
|
|
78563
79504
|
return c;
|
|
78564
79505
|
}, [mediaAssets, categorized]);
|
|
78565
|
-
const usageCounts =
|
|
79506
|
+
const usageCounts = useMemo71(
|
|
78566
79507
|
() => countUsage(
|
|
78567
79508
|
assets.filter((a) => MEDIA_EXT.test(a) || FONT_EXT.test(a)),
|
|
78568
79509
|
usedPaths
|
|
@@ -82925,7 +83866,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
|
|
|
82925
83866
|
}
|
|
82926
83867
|
|
|
82927
83868
|
// src/components/panels/VariablesPanel.tsx
|
|
82928
|
-
import { memo as memo51, useCallback as useCallback163, useEffect as useEffect129, useMemo as
|
|
83869
|
+
import { memo as memo51, useCallback as useCallback163, useEffect as useEffect129, useMemo as useMemo72, useState as useState144 } from "react";
|
|
82929
83870
|
|
|
82930
83871
|
// src/hooks/useVariablesPersist.ts
|
|
82931
83872
|
import { useCallback as useCallback160 } from "react";
|
|
@@ -83629,22 +84570,22 @@ var VariablesPanel = memo51(function VariablesPanel2({
|
|
|
83629
84570
|
domEditSaveTimestampRef,
|
|
83630
84571
|
publishSdkSession
|
|
83631
84572
|
});
|
|
83632
|
-
const declarations =
|
|
84573
|
+
const declarations = useMemo72(
|
|
83633
84574
|
() => sdkSession?.getVariableDeclarations() ?? [],
|
|
83634
84575
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83635
84576
|
[sdkSession, refreshKey, revision]
|
|
83636
84577
|
);
|
|
83637
|
-
const usage =
|
|
84578
|
+
const usage = useMemo72(
|
|
83638
84579
|
() => sdkSession?.getVariableUsage() ?? null,
|
|
83639
84580
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83640
84581
|
[sdkSession, refreshKey, revision]
|
|
83641
84582
|
);
|
|
83642
|
-
const issues =
|
|
84583
|
+
const issues = useMemo72(
|
|
83643
84584
|
() => previewValues && sdkSession ? sdkSession.validateVariableValues(previewValues) : [],
|
|
83644
84585
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83645
84586
|
[sdkSession, previewValues, refreshKey, revision]
|
|
83646
84587
|
);
|
|
83647
|
-
const effectiveValues =
|
|
84588
|
+
const effectiveValues = useMemo72(
|
|
83648
84589
|
() => sdkSession?.getVariableValues(previewValues ?? void 0) ?? {},
|
|
83649
84590
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83650
84591
|
[sdkSession, previewValues, refreshKey, revision]
|
|
@@ -83983,15 +84924,15 @@ function useSlideshowPersist({
|
|
|
83983
84924
|
}
|
|
83984
84925
|
|
|
83985
84926
|
// src/hooks/useSlideshowTabState.ts
|
|
83986
|
-
import { useEffect as useEffect130, useMemo as
|
|
84927
|
+
import { useEffect as useEffect130, useMemo as useMemo73 } from "react";
|
|
83987
84928
|
import { SLIDESHOW_ISLAND_TYPE as SLIDESHOW_ISLAND_TYPE2, slideshowIslandRegex as slideshowIslandRegex2 } from "@hyperframes/core/slideshow";
|
|
83988
84929
|
function useSlideshowTabState(params) {
|
|
83989
84930
|
const { editingFileContent, previewIframeRef, refreshKey, rightPanelTab, setRightPanelTab } = params;
|
|
83990
|
-
const isSlideshowComposition =
|
|
84931
|
+
const isSlideshowComposition = useMemo73(() => {
|
|
83991
84932
|
if (!editingFileContent || !editingFileContent.includes(SLIDESHOW_ISLAND_TYPE2)) return false;
|
|
83992
84933
|
return slideshowIslandRegex2("i").test(editingFileContent);
|
|
83993
84934
|
}, [editingFileContent]);
|
|
83994
|
-
const slideshowScenes =
|
|
84935
|
+
const slideshowScenes = useMemo73(() => {
|
|
83995
84936
|
try {
|
|
83996
84937
|
const win = previewIframeRef.current?.contentWindow;
|
|
83997
84938
|
return (win?.__clipManifest?.scenes ?? []).map((s) => ({
|
|
@@ -85663,7 +86604,7 @@ function useProjectSignaturePoll(projectId, currentSignature, onChange) {
|
|
|
85663
86604
|
}
|
|
85664
86605
|
|
|
85665
86606
|
// src/components/storyboard/StoryboardLoaded.tsx
|
|
85666
|
-
import { useEffect as useEffect141, useMemo as
|
|
86607
|
+
import { useEffect as useEffect141, useMemo as useMemo76, useState as useState152 } from "react";
|
|
85667
86608
|
|
|
85668
86609
|
// src/components/storyboard/StoryboardDirection.tsx
|
|
85669
86610
|
import { jsx as jsx205, jsxs as jsxs170 } from "react/jsx-runtime";
|
|
@@ -85900,7 +86841,7 @@ function StoryboardScriptPanel({ script }) {
|
|
|
85900
86841
|
}
|
|
85901
86842
|
|
|
85902
86843
|
// src/components/storyboard/StoryboardSourceEditor.tsx
|
|
85903
|
-
import { useCallback as useCallback172, useEffect as useEffect137, useMemo as
|
|
86844
|
+
import { useCallback as useCallback172, useEffect as useEffect137, useMemo as useMemo74, useRef as useRef169, useState as useState148 } from "react";
|
|
85904
86845
|
import { marked } from "marked";
|
|
85905
86846
|
import DOMPurify from "dompurify";
|
|
85906
86847
|
import { jsx as jsx210, jsxs as jsxs173 } from "react/jsx-runtime";
|
|
@@ -85959,7 +86900,7 @@ function useMarkdownPreview(source) {
|
|
|
85959
86900
|
const id = window.setTimeout(() => setDebounced(source), 200);
|
|
85960
86901
|
return () => window.clearTimeout(id);
|
|
85961
86902
|
}, [source]);
|
|
85962
|
-
return
|
|
86903
|
+
return useMemo74(() => {
|
|
85963
86904
|
const html2 = marked.parse(debounced, { async: false });
|
|
85964
86905
|
DOMPurify.addHook("afterSanitizeAttributes", hardenLinks);
|
|
85965
86906
|
try {
|
|
@@ -86631,7 +87572,7 @@ function progressLabel(summary) {
|
|
|
86631
87572
|
}
|
|
86632
87573
|
|
|
86633
87574
|
// src/components/storyboard/useFrameComments.ts
|
|
86634
|
-
import { useCallback as useCallback174, useEffect as useEffect140, useMemo as
|
|
87575
|
+
import { useCallback as useCallback174, useEffect as useEffect140, useMemo as useMemo75, useState as useState151 } from "react";
|
|
86635
87576
|
|
|
86636
87577
|
// src/components/storyboard/frameComments.ts
|
|
86637
87578
|
var FRAME_COMMENTS_PATH = ".hyperframes/frame-comments.json";
|
|
@@ -86713,7 +87654,7 @@ function useFrameComments(frames) {
|
|
|
86713
87654
|
const setDraft = useCallback174((index, text) => {
|
|
86714
87655
|
setDrafts((prev) => ({ ...prev, [index]: text }));
|
|
86715
87656
|
}, []);
|
|
86716
|
-
const draftCount =
|
|
87657
|
+
const draftCount = useMemo75(
|
|
86717
87658
|
() => Object.values(drafts).filter((text) => text.trim().length > 0).length,
|
|
86718
87659
|
[drafts]
|
|
86719
87660
|
);
|
|
@@ -86781,7 +87722,7 @@ function StoryboardLoaded({
|
|
|
86781
87722
|
setFeedbackMessageCopied(false);
|
|
86782
87723
|
}
|
|
86783
87724
|
};
|
|
86784
|
-
const sourceFiles =
|
|
87725
|
+
const sourceFiles = useMemo76(() => {
|
|
86785
87726
|
const files = [{ path: data.path, label: data.path }];
|
|
86786
87727
|
if (data.script?.exists) files.push({ path: data.script.path, label: data.script.path });
|
|
86787
87728
|
return files;
|
|
@@ -87267,7 +88208,7 @@ function StudioApp() {
|
|
|
87267
88208
|
domEditSaveTimestampRef,
|
|
87268
88209
|
setRefreshKey
|
|
87269
88210
|
});
|
|
87270
|
-
const masterCompPath =
|
|
88211
|
+
const masterCompPath = useMemo77(
|
|
87271
88212
|
() => resolveMasterCompositionPath(fileManager.fileTree),
|
|
87272
88213
|
[fileManager.fileTree]
|
|
87273
88214
|
);
|
|
@@ -87587,11 +88528,13 @@ function StudioApp() {
|
|
|
87587
88528
|
handleRedo: appHotkeys.handleRedo,
|
|
87588
88529
|
renderQueue,
|
|
87589
88530
|
compositionDimensions,
|
|
88531
|
+
domEditSaveQueuePaused: previewPersistence.domEditSaveQueuePaused,
|
|
88532
|
+
externalFileConflict: externalFileChanges.blocked !== null,
|
|
87590
88533
|
waitForPendingDomEditSaves: previewPersistence.waitForPendingDomEditSaves,
|
|
87591
88534
|
handlePreviewIframeRef,
|
|
87592
88535
|
refreshPreviewDocumentVersion
|
|
87593
88536
|
});
|
|
87594
|
-
const timelineToolbar =
|
|
88537
|
+
const timelineToolbar = useMemo77(
|
|
87595
88538
|
() => /* @__PURE__ */ jsx217(
|
|
87596
88539
|
TimelineToolbar,
|
|
87597
88540
|
{
|