@hyperframes/studio 0.8.94 → 0.8.95
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-Bkm-3G56.js → hyperframes-player-DJJKab4_.js} +1 -1
- package/dist/assets/{index-n2ZRiO44.js → index-C1dXTZvs.js} +1 -1
- package/dist/assets/{index-BupvpoiM.js → index-Cc4by8r7.js} +176 -176
- package/dist/assets/{index-BUn5H_np.js → index-DE1dn3QB.js} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.html +1 -1
- package/dist/index.js +54 -13
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/components/editor/manualEditsSeekReapply.ts +5 -1
- package/src/player/components/Timeline.ghostLane.test.tsx +126 -0
- package/src/player/components/Timeline.rulerSeek.test.tsx +7 -0
- package/src/player/components/Timeline.test.ts +3 -3
- package/src/player/components/TimelineCanvas.tsx +30 -2
- package/src/player/components/TimelineProvider.tsx +1 -0
- package/src/player/components/timelineDragDrop.test.tsx +14 -12
- package/src/player/components/timelineLayout.test.ts +40 -11
- package/src/player/components/timelineLayout.ts +6 -6
- package/src/player/components/useTimelineProviderState.tsx +3 -1
- package/src/utils/gsapSoftReload.test.ts +51 -2
- package/src/utils/gsapSoftReload.ts +24 -10
- package/src/utils/gsapUndoRestore.test.ts +49 -0
- package/src/utils/gsapUndoRestore.ts +15 -0
|
@@ -161,6 +161,55 @@ describe("applyUndoRestoreToPreview", () => {
|
|
|
161
161
|
},
|
|
162
162
|
);
|
|
163
163
|
|
|
164
|
+
describe("an undo that re-runs a changed script matches a fresh load of the restored file", () => {
|
|
165
|
+
const script = (extra: string) => `window.__timelines["root"]=gsap.timeline();${extra}`;
|
|
166
|
+
const undoScriptEdit = (live: string, authored: string, edit: string) => {
|
|
167
|
+
const { iframe, contentWindow, doc } = buildLiveIframe(
|
|
168
|
+
`${live}<script>${script(edit)}</script>`,
|
|
169
|
+
);
|
|
170
|
+
const clearProps = (targets: HTMLElement[]) =>
|
|
171
|
+
targets.forEach((t) => t.removeAttribute("style"));
|
|
172
|
+
Object.assign(contentWindow.gsap, { set: clearProps });
|
|
173
|
+
for (const el of doc.querySelectorAll("[style*=transform]")) Object.assign(el, { _gsap: {} });
|
|
174
|
+
const files = {
|
|
175
|
+
[ROOT]: {
|
|
176
|
+
previous: wrap(`${authored}<script>${script(edit)}</script>`),
|
|
177
|
+
restored: wrap(`${authored}<script>${script("")}</script>`),
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
expect(applyUndoRestoreToPreview(iframe, ROOT, files, 3, vi.fn())).toBe("soft");
|
|
181
|
+
return doc;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
it("drops the size marks a resize left on the live element", () => {
|
|
185
|
+
const doc = undoScriptEdit(
|
|
186
|
+
`<div id="a" data-hf-studio-box-size="true" style="width: 502px; --hf-studio-width: 502px; --hf-studio-height: 334px">t</div>`,
|
|
187
|
+
`<div id="a" style="width: 300px">t</div>`,
|
|
188
|
+
`tl.set("#a",{width:502,height:334},0);`,
|
|
189
|
+
);
|
|
190
|
+
expect(doc.getElementById("a")!.hasAttribute("data-hf-studio-box-size")).toBe(false);
|
|
191
|
+
expect(doc.getElementById("a")!.getAttribute("style")).toBe("width: 300px");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("keeps the authored inline rotation of an element GSAP moved", () => {
|
|
195
|
+
const doc = undoScriptEdit(
|
|
196
|
+
`<div id="r" style="left: 10px; transform: translate(116px, 72px) rotate(20deg)">t</div>`,
|
|
197
|
+
`<div id="r" style="left: 10px; transform: rotate(20deg)">t</div>`,
|
|
198
|
+
`gsap.set("#r",{x:116,y:72});`,
|
|
199
|
+
);
|
|
200
|
+
expect(doc.getElementById("r")!.style.transform).toBe("rotate(20deg)");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("drops the translate mask GSAP wrote over a stylesheet translate", () => {
|
|
204
|
+
const doc = undoScriptEdit(
|
|
205
|
+
`<h1 id="t" style="opacity: 0.5; translate: none; transform: translate(116px, -113px)">t</h1>`,
|
|
206
|
+
`<h1 id="t">t</h1>`,
|
|
207
|
+
`gsap.set("#t",{x:116,y:-113});`,
|
|
208
|
+
);
|
|
209
|
+
expect(doc.getElementById("t")!.style.getPropertyValue("translate")).toBe("");
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
164
213
|
it("full-reloads without partially restoring when any changed live target is missing", () => {
|
|
165
214
|
const { iframe, doc } = buildLiveIframe(`<div id="a" style="z-index: 8">a</div>`);
|
|
166
215
|
const reloadPreview = vi.fn();
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
extractGsapScriptText,
|
|
9
9
|
findGsapScriptElements,
|
|
10
10
|
} from "./gsapSoftReload";
|
|
11
|
+
import { findAuthoredElement, parseSavedSource } from "./authoredSource";
|
|
12
|
+
import { STUDIO_EDIT_ATTRS } from "../components/editor/manualEditsSeekReapply";
|
|
11
13
|
import { markScenesStale } from "../player/sceneSwap";
|
|
12
14
|
|
|
13
15
|
type PreviewWindow = Window & {
|
|
@@ -135,6 +137,17 @@ function syncElementAttributes(target: Element, source: Element): void {
|
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
// A gesture folded into the script leaves marks on the live element that every seek would re-impose.
|
|
141
|
+
function syncStaleEditMarks(doc: Document, restored: string): void {
|
|
142
|
+
const restoredDoc = parseSavedSource(restored);
|
|
143
|
+
for (const live of doc.querySelectorAll(STUDIO_EDIT_ATTRS.map((attr) => `[${attr}]`).join())) {
|
|
144
|
+
const source = findAuthoredElement(restoredDoc, live);
|
|
145
|
+
if (source && STUDIO_EDIT_ATTRS.some((a) => live.getAttribute(a) !== source.getAttribute(a))) {
|
|
146
|
+
syncElementAttributes(live, source);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
138
151
|
function readGsapScriptTexts(html: string): string[] {
|
|
139
152
|
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
140
153
|
return findGsapScriptElements(doc).map((script) => script.textContent ?? "");
|
|
@@ -248,9 +261,11 @@ export function applyUndoRestoreToPreview(
|
|
|
248
261
|
const restoredScript = extractGsapScriptText(restored);
|
|
249
262
|
const previousScript = extractGsapScriptText(previous);
|
|
250
263
|
if (restoredScript && restoredScript !== previousScript) {
|
|
264
|
+
syncStaleEditMarks(doc, restored);
|
|
251
265
|
const result = applySoftReload(iframe, restoredScript, {
|
|
252
266
|
onAsyncFailure: reloadPreview,
|
|
253
267
|
currentTimeOverride: currentTime,
|
|
268
|
+
authoredHtml: restored,
|
|
254
269
|
});
|
|
255
270
|
if (result === "cannot-soft-reload") {
|
|
256
271
|
reloadPreview();
|