@hyperframes/studio 0.8.86 → 0.8.88
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-w3Bbv0kd.js +473 -0
- package/dist/assets/{index-Cz3E_ZSj.js → index-B6PGJTzp.js} +1 -1
- package/dist/assets/{index-d5DKK15Q.js → index-DE6cZYR5.js} +199 -199
- package/dist/assets/{index-8Fgryj7q.js → index-DldknZPp.js} +1 -1
- package/dist/assets/{index-mzZCha_N.css → index-Dx79V1Se.css} +1 -1
- package/dist/{chunk-7PLTC6SV.js → chunk-KU4F7QZ3.js} +9 -1
- package/dist/chunk-KU4F7QZ3.js.map +1 -0
- package/dist/{chunk-3IOCVWB6.js → chunk-PQFEGZWM.js} +3 -1
- package/dist/chunk-PQFEGZWM.js.map +1 -0
- package/dist/{domEditingLayers-ZOOMN7CQ.js → domEditingLayers-3BAP6NNO.js} +2 -2
- package/dist/index.d.ts +206 -160
- package/dist/index.html +2 -2
- package/dist/index.js +2121 -1702
- package/dist/index.js.map +1 -1
- package/dist/player.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -8
- package/src/components/TimelineHistoryButtons.dom.test.tsx +14 -3
- package/src/components/TimelineToolPicker.tsx +90 -0
- package/src/components/TimelineToolbar.tsx +2 -27
- package/src/components/editor/DomEditOverlay.tsx +1 -2
- package/src/components/editor/DomEditOverlayHover.test.tsx +5 -0
- package/src/components/editor/DomEditSelectionChrome.test.tsx +0 -3
- package/src/components/editor/DomEditSelectionChrome.tsx +3 -13
- package/src/components/editor/LayersPanel.test.ts +14 -0
- package/src/components/editor/LayersPanel.tsx +2 -2
- package/src/components/editor/TopologyLens.tsx +2 -2
- package/src/components/editor/commitPreflightHandles.test.tsx +466 -0
- package/src/components/editor/domEditOverlayGestures.ts +0 -1
- package/src/components/editor/domEditOverlayStartGesture.ts +10 -1
- package/src/components/editor/domEditing.test.ts +29 -0
- package/src/components/editor/domEditingDom.ts +1 -0
- package/src/components/editor/domEditingTypes.ts +1 -0
- package/src/components/editor/marqueeCommit.test.tsx +84 -0
- package/src/components/editor/marqueeCommit.ts +13 -1
- package/src/components/editor/useColorGradingController.test.ts +24 -0
- package/src/components/editor/useColorGradingController.ts +3 -2
- package/src/components/editor/useDomEditOverlayGestures.ts +20 -5
- package/src/components/nle/useCompositionStack.test.tsx +53 -0
- package/src/components/nle/useCompositionStack.ts +4 -8
- package/src/hooks/appHotkeysDispatch.ts +8 -0
- package/src/hooks/domStyleCommit.ts +129 -0
- package/src/hooks/gsapEditOutcome.ts +2 -2
- package/src/hooks/gsapElementMatch.ts +38 -0
- package/src/hooks/gsapResizeIntercept.ts +10 -31
- package/src/hooks/gsapResizePreflight.ts +56 -0
- package/src/hooks/gsapRuntimeBridge.ts +66 -26
- package/src/hooks/keyframeCacheAstLoad.ts +4 -1
- package/src/hooks/useAppHotkeys.test.ts +58 -1
- package/src/hooks/useCommitPreflightCapabilities.ts +147 -0
- package/src/hooks/useDomEditCommits.ts +17 -182
- package/src/hooks/useDomEditPersist.ts +225 -0
- package/src/hooks/useDomEditPreviewSync.test.ts +58 -0
- package/src/hooks/useDomEditPreviewSync.ts +2 -4
- package/src/hooks/useDomEditSession.test.tsx +51 -1
- package/src/hooks/useDomEditSession.ts +11 -2
- package/src/hooks/useDomEditTextCommits.ts +15 -99
- package/src/hooks/useDomStyleCommit.test.tsx +155 -0
- package/src/hooks/useDomStyleCommit.ts +74 -0
- package/src/hooks/useGsapAnimationFetchFallback.test.ts +12 -0
- package/src/hooks/useGsapAnimationFetchFallback.ts +8 -2
- package/src/hooks/useGsapAwareEditing.test.tsx +4 -0
- package/src/hooks/useGsapAwareEditing.ts +1 -1
- package/src/hooks/useGsapTweenCache.ts +2 -48
- package/src/index.ts +10 -1
- package/src/player/components/Timeline.audioEffects.test.tsx +80 -0
- package/src/player/components/Timeline.bandEscape.test.tsx +75 -0
- package/src/player/components/Timeline.playheadRemount.test.tsx +3 -27
- package/src/player/components/Timeline.showKeyframes.test.tsx +91 -0
- package/src/player/components/TimelineGroupHeader.tsx +21 -10
- package/src/player/components/TimelineGroupLaneLabels.tsx +7 -2
- package/src/player/components/TimelineGroupRow.test.tsx +24 -33
- package/src/player/components/TimelineGroupRow.tsx +4 -1
- package/src/player/components/TimelineLanes.tsx +5 -5
- package/src/player/components/TimelineTrackHeader.test.tsx +27 -0
- package/src/player/components/TimelineTrackHeader.tsx +6 -3
- package/src/player/components/TimelineTypes.ts +2 -0
- package/src/player/components/studioShortcuts.ts +2 -0
- package/src/player/components/timelineLaneProps.ts +1 -0
- package/src/player/components/timelineMountTestEnv.ts +29 -0
- package/src/player/components/useTimelineKeyframeData.ts +14 -0
- package/src/player/components/useTimelineLabelColumn.ts +18 -0
- package/src/player/components/useTimelineLaneRowIndexes.ts +6 -1
- package/src/player/components/useTimelineProviderState.tsx +10 -14
- package/src/player/components/useTimelineRangeSelection.ts +13 -12
- package/src/player/hooks/useShadowPreviewReload.ts +2 -0
- package/src/player/hooks/useTimelinePlayer.shadowReload.test.ts +191 -0
- package/src/player/hooks/useTimelinePlayer.ts +29 -7
- package/src/player/lib/runtimeProtocol.test.ts +1 -0
- package/src/player/sceneSwap.test.ts +95 -0
- package/src/player/sceneSwap.ts +53 -0
- package/src/player/store/playerStore.ts +16 -0
- package/dist/assets/hyperframes-player-Dx-2Gpsu.js +0 -473
- package/dist/chunk-3IOCVWB6.js.map +0 -1
- package/dist/chunk-7PLTC6SV.js.map +0 -1
- /package/dist/{domEditingLayers-ZOOMN7CQ.js.map → domEditingLayers-3BAP6NNO.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.88",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"dockview-react": "8.3.1",
|
|
61
61
|
"gsap": "^3.13.0",
|
|
62
62
|
"mediabunny": "^1.45.3",
|
|
63
|
-
"@hyperframes/
|
|
64
|
-
"@hyperframes/
|
|
65
|
-
"@hyperframes/player": "0.8.
|
|
66
|
-
"@hyperframes/studio-server": "0.8.
|
|
67
|
-
"@hyperframes/
|
|
68
|
-
"@hyperframes/
|
|
63
|
+
"@hyperframes/parsers": "0.8.88",
|
|
64
|
+
"@hyperframes/core": "0.8.88",
|
|
65
|
+
"@hyperframes/player": "0.8.88",
|
|
66
|
+
"@hyperframes/studio-server": "0.8.88",
|
|
67
|
+
"@hyperframes/sdk": "0.8.88",
|
|
68
|
+
"@hyperframes/shader-transitions": "0.8.88"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@tailwindcss/vite": "^4.3.3",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"vite": "^6.4.2",
|
|
82
82
|
"vitest": "^4.1.11",
|
|
83
83
|
"zustand": "^5.0.0",
|
|
84
|
-
"@hyperframes/producer": "0.8.
|
|
84
|
+
"@hyperframes/producer": "0.8.88"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"react": "19",
|
|
@@ -96,14 +96,25 @@ it("leaves enabled buttons without the disabled attribute", () => {
|
|
|
96
96
|
expect(el.className).not.toContain("cursor-not-allowed");
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
it("orders the toolbar Undo, Redo,
|
|
99
|
+
it("orders the toolbar Undo, Redo, then the tool picker showing the active tool", () => {
|
|
100
100
|
const host = mount(<TimelineToolbar />);
|
|
101
101
|
const labels = Array.from(host.querySelectorAll("button"))
|
|
102
102
|
.map((b) => b.getAttribute("aria-label"))
|
|
103
103
|
.filter((l) => l !== null)
|
|
104
|
-
.slice(0,
|
|
104
|
+
.slice(0, 3);
|
|
105
105
|
|
|
106
|
-
expect(labels).toEqual(["Undo", "Redo", "
|
|
106
|
+
expect(labels).toEqual(["Undo", "Redo", "Timeline tool: Select"]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("shows the Split tool on the picker while the razor is active", async () => {
|
|
110
|
+
const { usePlayerStore } = await import("../player");
|
|
111
|
+
usePlayerStore.setState({ activeTool: "razor" });
|
|
112
|
+
try {
|
|
113
|
+
const host = mount(<TimelineToolbar />);
|
|
114
|
+
expect(host.querySelector('button[aria-label="Timeline tool: Split"]')).not.toBeNull();
|
|
115
|
+
} finally {
|
|
116
|
+
usePlayerStore.setState({ activeTool: "select" });
|
|
117
|
+
}
|
|
107
118
|
});
|
|
108
119
|
|
|
109
120
|
it("classifies Undo and Redo for the hotkey filters at their new location (KTD13)", () => {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { ArrowLineLeft, ArrowLineRight, CaretDown } from "@phosphor-icons/react";
|
|
3
|
+
import { usePlayerStore } from "../player";
|
|
4
|
+
import { Scissors } from "../icons/SystemIcons";
|
|
5
|
+
import { Menu, MenuItem } from "./ui";
|
|
6
|
+
import { cn } from "./ui/cn";
|
|
7
|
+
import { flatIdle } from "./timelineToolbarStyles";
|
|
8
|
+
|
|
9
|
+
function SelectIcon() {
|
|
10
|
+
return (
|
|
11
|
+
<svg width="16" height="16" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true">
|
|
12
|
+
<path d="M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" />
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ToolRow {
|
|
18
|
+
label: string;
|
|
19
|
+
shortcut: string;
|
|
20
|
+
icon: ReactNode;
|
|
21
|
+
active: boolean;
|
|
22
|
+
run: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One button for the active timeline tool; its menu switches tools or selects clips around the playhead. */
|
|
26
|
+
export function TimelineToolPicker() {
|
|
27
|
+
const activeTool = usePlayerStore((s) => s.activeTool);
|
|
28
|
+
const store = () => usePlayerStore.getState();
|
|
29
|
+
const rows: ToolRow[] = [
|
|
30
|
+
{
|
|
31
|
+
label: "Select",
|
|
32
|
+
shortcut: "V",
|
|
33
|
+
icon: <SelectIcon />,
|
|
34
|
+
active: activeTool === "select",
|
|
35
|
+
run: () => store().setActiveTool("select"),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: "Split",
|
|
39
|
+
shortcut: "B",
|
|
40
|
+
icon: <Scissors size={16} />,
|
|
41
|
+
active: activeTool === "razor",
|
|
42
|
+
run: () => store().setActiveTool("razor"),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "Select leftward",
|
|
46
|
+
shortcut: "[",
|
|
47
|
+
icon: <ArrowLineLeft size={16} aria-hidden="true" />,
|
|
48
|
+
active: false,
|
|
49
|
+
run: () => store().selectLeftward(),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: "Select rightward",
|
|
53
|
+
shortcut: "]",
|
|
54
|
+
icon: <ArrowLineRight size={16} aria-hidden="true" />,
|
|
55
|
+
active: false,
|
|
56
|
+
run: () => store().selectRightward(),
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const current = rows.find((row) => row.active) ?? rows[0];
|
|
60
|
+
return (
|
|
61
|
+
<Menu
|
|
62
|
+
aria-label="Timeline tools"
|
|
63
|
+
trigger={
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
aria-label={`Timeline tool: ${current.label}`}
|
|
67
|
+
className={cn(flatIdle, "gap-0.5")}
|
|
68
|
+
>
|
|
69
|
+
{current.icon}
|
|
70
|
+
<CaretDown size={10} weight="bold" aria-hidden="true" />
|
|
71
|
+
</button>
|
|
72
|
+
}
|
|
73
|
+
>
|
|
74
|
+
{rows.map((row) => (
|
|
75
|
+
<MenuItem
|
|
76
|
+
key={row.label}
|
|
77
|
+
shortcut={row.shortcut}
|
|
78
|
+
onClick={row.run}
|
|
79
|
+
data-active={row.active || undefined}
|
|
80
|
+
className={row.active ? "bg-neutral-800 text-white" : undefined}
|
|
81
|
+
>
|
|
82
|
+
<span className="flex items-center gap-2">
|
|
83
|
+
{row.icon}
|
|
84
|
+
{row.label}
|
|
85
|
+
</span>
|
|
86
|
+
</MenuItem>
|
|
87
|
+
))}
|
|
88
|
+
</Menu>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -26,7 +26,7 @@ import { AudioMetersIcon } from "./icons/AudioMetersIcon";
|
|
|
26
26
|
import { RippleEditIcon } from "./icons/RippleEditIcon";
|
|
27
27
|
import { flatActive, flatBtn, flatDisabled, flatIdle } from "./timelineToolbarStyles";
|
|
28
28
|
import { TimelineHistoryButtons, type TimelineHistoryButtonsProps } from "./TimelineHistoryButtons";
|
|
29
|
-
import {
|
|
29
|
+
import { TimelineToolPicker } from "./TimelineToolPicker";
|
|
30
30
|
import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
|
31
31
|
import type { DomEditSelection } from "./editor/domEditingTypes";
|
|
32
32
|
import { canSplitElement } from "../utils/timelineElementSplit";
|
|
@@ -144,8 +144,6 @@ export function TimelineToolbar({
|
|
|
144
144
|
showAddBeat = true,
|
|
145
145
|
showKeyframes = true,
|
|
146
146
|
}: TimelineToolbarProps) {
|
|
147
|
-
const activeTool = usePlayerStore((s) => s.activeTool);
|
|
148
|
-
const setActiveTool = usePlayerStore((s) => s.setActiveTool);
|
|
149
147
|
const timelineSnapEnabled = usePlayerStore((s) => s.timelineSnapEnabled);
|
|
150
148
|
const setTimelineSnapEnabled = usePlayerStore((s) => s.setTimelineSnapEnabled);
|
|
151
149
|
const rippleEditEnabled = usePlayerStore((s) => s.rippleEditEnabled);
|
|
@@ -216,30 +214,7 @@ export function TimelineToolbar({
|
|
|
216
214
|
<div className="flex items-center justify-between px-2 py-0.5">
|
|
217
215
|
<div className="flex items-center gap-0.5">
|
|
218
216
|
<TimelineHistoryButtons {...history} />
|
|
219
|
-
<
|
|
220
|
-
<button
|
|
221
|
-
type="button"
|
|
222
|
-
onClick={() => setActiveTool("select")}
|
|
223
|
-
aria-label="Selection tool"
|
|
224
|
-
aria-pressed={activeTool === "select"}
|
|
225
|
-
className={activeTool === "select" ? flatActive : flatIdle}
|
|
226
|
-
>
|
|
227
|
-
<svg width="16" height="16" viewBox="0 0 12 12" fill="currentColor">
|
|
228
|
-
<path d="M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" />
|
|
229
|
-
</svg>
|
|
230
|
-
</button>
|
|
231
|
-
</Tooltip>
|
|
232
|
-
<Tooltip label="Razor tool (B) — Shift+click splits all tracks">
|
|
233
|
-
<button
|
|
234
|
-
type="button"
|
|
235
|
-
onClick={() => setActiveTool("razor")}
|
|
236
|
-
aria-label="Razor tool"
|
|
237
|
-
aria-pressed={activeTool === "razor"}
|
|
238
|
-
className={activeTool === "razor" ? flatActive : flatIdle}
|
|
239
|
-
>
|
|
240
|
-
<Scissors size={16} />
|
|
241
|
-
</button>
|
|
242
|
-
</Tooltip>
|
|
217
|
+
<TimelineToolPicker />
|
|
243
218
|
{/* Divider: tool-mode | editing-actions */}
|
|
244
219
|
<div aria-hidden="true" className="mx-1 h-4 w-px bg-neutral-800" />
|
|
245
220
|
<Tooltip label={timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)"}>
|
|
@@ -47,7 +47,7 @@ export {
|
|
|
47
47
|
} from "./domEditOverlayGestures";
|
|
48
48
|
export type { DomEditGroupPathOffsetCommit } from "./domEditOverlayGestures";
|
|
49
49
|
|
|
50
|
-
interface DomEditOverlayProps {
|
|
50
|
+
export interface DomEditOverlayProps {
|
|
51
51
|
iframeRef: RefObject<HTMLIFrameElement | null>;
|
|
52
52
|
activeCompositionPath: string | null;
|
|
53
53
|
selection: DomEditSelection | null;
|
|
@@ -518,7 +518,6 @@ export const DomEditOverlay = memo(function DomEditOverlay({
|
|
|
518
518
|
boxClipPath={boxClipPath}
|
|
519
519
|
selectionKey={selectionKey}
|
|
520
520
|
groupSelectionCount={groupSelections.length}
|
|
521
|
-
blockedMoveRef={blockedMoveRef}
|
|
522
521
|
gestures={gestures}
|
|
523
522
|
onStyleCommit={onStyleCommitRef.current}
|
|
524
523
|
onBoxMouseDown={suppressBoxMouseDown}
|
|
@@ -6,6 +6,7 @@ import { expect, it, vi } from "vitest";
|
|
|
6
6
|
import type { DomEditSelection } from "./domEditing";
|
|
7
7
|
import "./domEditOverlayTestMocks";
|
|
8
8
|
import { DomEditOverlay } from "./DomEditOverlay";
|
|
9
|
+
import { DomEditOverlay as PackageDomEditOverlay } from "../../index";
|
|
9
10
|
|
|
10
11
|
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
11
12
|
|
|
@@ -74,3 +75,7 @@ it("rotates the hover box with the element", () => {
|
|
|
74
75
|
it("leaves the hover box untransformed at angle zero", () => {
|
|
75
76
|
expect(renderHover(0)).toBe("");
|
|
76
77
|
});
|
|
78
|
+
|
|
79
|
+
it("is exported from the package entry, so a host can mount it with its own commits", () => {
|
|
80
|
+
expect(PackageDomEditOverlay).toBe(DomEditOverlay);
|
|
81
|
+
});
|
|
@@ -53,7 +53,6 @@ describe("DomEditSelectionChrome crop composition", () => {
|
|
|
53
53
|
boxClipPath={undefined}
|
|
54
54
|
selectionKey="headline"
|
|
55
55
|
groupSelectionCount={0}
|
|
56
|
-
blockedMoveRef={createRef()}
|
|
57
56
|
gestures={{ startGesture: vi.fn() } as never}
|
|
58
57
|
onStyleCommit={vi.fn()}
|
|
59
58
|
onBoxMouseDown={vi.fn()}
|
|
@@ -109,7 +108,6 @@ describe("DomEditSelectionChrome crop composition", () => {
|
|
|
109
108
|
boxClipPath={undefined}
|
|
110
109
|
selectionKey="clip"
|
|
111
110
|
groupSelectionCount={0}
|
|
112
|
-
blockedMoveRef={createRef()}
|
|
113
111
|
gestures={{ startGesture: vi.fn() } as never}
|
|
114
112
|
onStyleCommit={vi.fn()}
|
|
115
113
|
onBoxMouseDown={vi.fn()}
|
|
@@ -170,7 +168,6 @@ describe("DomEditSelectionChrome while editing text", () => {
|
|
|
170
168
|
boxClipPath={undefined}
|
|
171
169
|
selectionKey="copy"
|
|
172
170
|
groupSelectionCount={0}
|
|
173
|
-
blockedMoveRef={createRef()}
|
|
174
171
|
gestures={{ startGesture: vi.fn() } as never}
|
|
175
172
|
onStyleCommit={vi.fn()}
|
|
176
173
|
onBoxMouseDown={vi.fn()}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RefObject } from "react";
|
|
2
2
|
import { type DomEditSelection } from "./domEditing";
|
|
3
3
|
import type { GroupOverlayItem, OverlayRect } from "./domEditOverlayGeometry";
|
|
4
|
-
import type {
|
|
4
|
+
import type { ResizeHandle } from "./domEditOverlayGestures";
|
|
5
5
|
import type { createDomEditOverlayGestureHandlers } from "./useDomEditOverlayGestures";
|
|
6
6
|
import { DomEditCropHandles } from "./DomEditCropHandles";
|
|
7
7
|
import { DomEditRotateHandle } from "./DomEditRotateHandle";
|
|
@@ -103,7 +103,7 @@ export function DomEditGroupChrome({
|
|
|
103
103
|
cursor: canManipulate && groupCanMove ? "move" : "default",
|
|
104
104
|
}}
|
|
105
105
|
onPointerDown={(e) => {
|
|
106
|
-
if (!canManipulate ||
|
|
106
|
+
if (!canManipulate || e.shiftKey) return;
|
|
107
107
|
gestures.startGroupDrag(e);
|
|
108
108
|
}}
|
|
109
109
|
onMouseDown={onBoxMouseDown}
|
|
@@ -123,7 +123,6 @@ interface DomEditSelectionChromeProps {
|
|
|
123
123
|
boxClipPath: string | undefined;
|
|
124
124
|
selectionKey: string;
|
|
125
125
|
groupSelectionCount: number;
|
|
126
|
-
blockedMoveRef: RefObject<BlockedMoveState | null>;
|
|
127
126
|
gestures: GestureHandlers;
|
|
128
127
|
onStyleCommit?: (property: string, value: string) => Promise<unknown> | void;
|
|
129
128
|
onBoxMouseDown: (e: React.MouseEvent) => void;
|
|
@@ -152,7 +151,6 @@ export function DomEditSelectionChrome({
|
|
|
152
151
|
boxClipPath,
|
|
153
152
|
selectionKey,
|
|
154
153
|
groupSelectionCount,
|
|
155
|
-
blockedMoveRef,
|
|
156
154
|
gestures,
|
|
157
155
|
onStyleCommit,
|
|
158
156
|
onBoxMouseDown,
|
|
@@ -218,15 +216,7 @@ export function DomEditSelectionChrome({
|
|
|
218
216
|
gestures.startGesture("drag", e);
|
|
219
217
|
return;
|
|
220
218
|
}
|
|
221
|
-
|
|
222
|
-
e.stopPropagation();
|
|
223
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
224
|
-
blockedMoveRef.current = {
|
|
225
|
-
pointerId: e.pointerId,
|
|
226
|
-
startX: e.clientX,
|
|
227
|
-
startY: e.clientY,
|
|
228
|
-
notified: false,
|
|
229
|
-
};
|
|
219
|
+
gestures.startBlockedMove(e, selection);
|
|
230
220
|
}}
|
|
231
221
|
onMouseDown={onBoxMouseDown}
|
|
232
222
|
onClick={onBoxClick}
|
|
@@ -9,6 +9,7 @@ import type { DomEditLayerItem } from "./domEditingTypes";
|
|
|
9
9
|
import { createRafThrottle, LayersPanel, sortLayersByZIndex } from "./LayersPanel";
|
|
10
10
|
import { isLayerDraggable } from "./useLayerDrag";
|
|
11
11
|
import { liveTime } from "../../player";
|
|
12
|
+
import { sceneSwapFor } from "../../player/sceneSwap";
|
|
12
13
|
|
|
13
14
|
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
14
15
|
|
|
@@ -292,4 +293,17 @@ describe("LayersPanel preview promotion", () => {
|
|
|
292
293
|
});
|
|
293
294
|
expect(collectedRootIds()).toEqual(["b"]);
|
|
294
295
|
});
|
|
296
|
+
it("recollects layers when edited scenes are swapped into the live document", async () => {
|
|
297
|
+
const a = makeIframe("a");
|
|
298
|
+
Object.defineProperty(a, "contentWindow", { value: { __hfSwapScenes: async () => {} } });
|
|
299
|
+
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("<html></html>"));
|
|
300
|
+
mocks.previewIframeRef.current = a;
|
|
301
|
+
usePreviewIframeStore.setState({ iframe: a });
|
|
302
|
+
act(() => root.render(createElement(LayersPanel)));
|
|
303
|
+
mocks.collect.mockClear();
|
|
304
|
+
|
|
305
|
+
await act(() => sceneSwapFor(a)!("/preview", () => true));
|
|
306
|
+
expect(collectedRootIds()).toEqual(["a"]);
|
|
307
|
+
fetchSpy.mockRestore();
|
|
308
|
+
});
|
|
295
309
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useLivePreviewIframe } from "../../player/store/previewIframeStore";
|
|
2
|
+
import { onPreviewContentReplaced } from "../../player/sceneSwap";
|
|
2
3
|
import { memo, useState, useCallback, useEffect, useRef } from "react";
|
|
3
4
|
import {
|
|
4
5
|
collectDomEditLayerItems,
|
|
@@ -164,8 +165,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
|
|
164
165
|
prevDocVersionRef.current += 1;
|
|
165
166
|
collectLayers();
|
|
166
167
|
};
|
|
167
|
-
iframe
|
|
168
|
-
return () => iframe.removeEventListener("load", handleLoad);
|
|
168
|
+
return onPreviewContentReplaced(iframe, handleLoad);
|
|
169
169
|
}, [previewIframeRef, livePreviewIframe, collectLayers]);
|
|
170
170
|
|
|
171
171
|
useEffect(() => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useLivePreviewIframe } from "../../player/store/previewIframeStore";
|
|
2
|
+
import { onPreviewContentReplaced } from "../../player/sceneSwap";
|
|
2
3
|
import {
|
|
3
4
|
useEffect,
|
|
4
5
|
useLayoutEffect,
|
|
@@ -119,8 +120,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
|
|
119
120
|
const iframe = iframeRef.current;
|
|
120
121
|
if (!iframe || !callId) return;
|
|
121
122
|
const dismiss = () => studioEditLifecycle.dismiss(callId);
|
|
122
|
-
iframe
|
|
123
|
-
return () => iframe.removeEventListener("load", dismiss);
|
|
123
|
+
return onPreviewContentReplaced(iframe, dismiss);
|
|
124
124
|
}, [callId, iframeRef, livePreviewIframe]);
|
|
125
125
|
|
|
126
126
|
useEffect(() => {
|