mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(core): preserve position edit fold detection during seeks
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// AUTO-GENERATED by scripts/build-position-edits-render.ts - do not edit
|
||||
const POSITION_EDITS_RENDER_IIFE: string =
|
||||
'"use strict";(()=>{function g(){return globalThis}function w(t,e){if(typeof window>"u")return;let n=g(),o=n.__hf?.onSwallowed;if(o)try{o({label:t,error:e})}catch(r){}(n.__hfDebug||n.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${t} swallowed:`,e)}var E=null;function h(t,e){if(E)try{E({source:"hf-preview",type:"analytics",event:t,properties:e??{}})}catch(n){w("runtime.analytics.site1",n)}}var l="data-hf-edit-base-x",p="data-hf-edit-base-y",f="data-hf-edit-original-translate",u=t=>{let e=parseFloat(t??"");return Number.isFinite(e)?e:0},$=t=>{let e=[],n=0,o="";for(let r of t.trim())r==="("&&(n+=1),r===")"&&(n=Math.max(0,n-1)),/\\s/.test(r)&&n===0?(o&&e.push(o),o=""):o+=r;return o&&e.push(o),e},k=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,_=(t,e)=>k.test(t)&&k.test(e)?`${parseFloat(t)+parseFloat(e)}px`:`calc(${t} + ${e})`,D=(t,e,n)=>{if(!t||t==="none")return`${e} ${n}`;let[o,r,i]=$(t);if(o===void 0)return`${e} ${n}`;if(r===void 0)return`${_(o,e)} ${n}`;let s=i===void 0?"":` ${i}`;return`${_(o,e)} ${_(r,n)}${s}`},L=t=>{try{t.ownerDocument.defaultView?.gsap?.getProperty?.(t,"x")}catch{}},M=t=>{let e=t.style.getPropertyValue("translate").trim();if(e)return e==="none"?"":e;try{let n=t.ownerDocument.defaultView,o=n?n.getComputedStyle(t).getPropertyValue("translate").trim():"";return o==="none"?"":o}catch{return""}},A=new WeakMap;function H(t,e){let n=A.get(t);if(!e?.force&&n!==void 0&&t.style.getPropertyValue("translate")!==n){h("position_edit_fold_skipped",{hfId:t.getAttribute("data-hf-id")});return}let o=u(t.getAttribute("data-x"))-u(t.getAttribute(l)),r=u(t.getAttribute("data-y"))-u(t.getAttribute(p));t.hasAttribute(f)||t.setAttribute(f,M(t)),n===void 0&&L(t);let i=t.getAttribute(f)??"",s=D(i,`${o}px`,`${r}px`);t.style.setProperty("translate",s),A.set(t,t.style.getPropertyValue("translate"))}function y(t,e){let n=t.querySelectorAll(`[${l}], [${p}]`),o=t.defaultView?.HTMLElement,r=0;for(let i=0;i<n.length;i++){let s=n[i];(o?s instanceof o:typeof s.style?.setProperty=="function")&&(H(s,e),r+=1)}return r}var T="__hfPositionEditsSeekReapplyWrapped";function v(t){let e=t,n=()=>{try{y(e.document,{force:!0})}catch{}},o=a=>typeof a=="function"&&!!a[T],r=a=>{try{Object.defineProperty(a,T,{value:!0})}catch{}},i=(a,P)=>{let d=a();if(typeof d!="function"||o(d))return;let m=function(...R){let x=d.apply(this,R);return n(),x};r(m),P(m),n()},s=()=>{i(()=>e.__hf?.seek,a=>{e.__hf&&(e.__hf.seek=a)}),i(()=>e.__player?.renderSeek,a=>{e.__player&&(e.__player.renderSeek=a)})};s();let c=120,S=e.setInterval(()=>{s(),c-=1,c<=0&&e.clearInterval(S)},50)}function b(){document.querySelector(`[${l}], [${p}]`)&&(y(document),v(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",b,{once:!0}):b();})();\n';
|
||||
'"use strict";(()=>{function g(){return globalThis}function w(t,e){if(typeof window>"u")return;let n=g(),o=n.__hf?.onSwallowed;if(o)try{o({label:t,error:e})}catch(r){}(n.__hfDebug||n.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${t} swallowed:`,e)}var E=null;function h(t,e){if(E)try{E({source:"hf-preview",type:"analytics",event:t,properties:e??{}})}catch(n){w("runtime.analytics.site1",n)}}var l="data-hf-edit-base-x",p="data-hf-edit-base-y",d="data-hf-edit-original-translate",u=t=>{let e=parseFloat(t??"");return Number.isFinite(e)?e:0},$=t=>{let e=[],n=0,o="";for(let r of t.trim())r==="("&&(n+=1),r===")"&&(n=Math.max(0,n-1)),/\\s/.test(r)&&n===0?(o&&e.push(o),o=""):o+=r;return o&&e.push(o),e},k=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,f=(t,e)=>k.test(t)&&k.test(e)?`${parseFloat(t)+parseFloat(e)}px`:`calc(${t} + ${e})`,D=(t,e,n)=>{if(!t||t==="none")return`${e} ${n}`;let[o,r,i]=$(t);if(o===void 0)return`${e} ${n}`;if(r===void 0)return`${f(o,e)} ${n}`;let a=i===void 0?"":` ${i}`;return`${f(o,e)} ${f(r,n)}${a}`},L=t=>{try{t.ownerDocument.defaultView?.gsap?.getProperty?.(t,"x")}catch{}},M=t=>{let e=t.style.getPropertyValue("translate").trim();if(e)return e==="none"?"":e;try{let n=t.ownerDocument.defaultView,o=n?n.getComputedStyle(t).getPropertyValue("translate").trim():"";return o==="none"?"":o}catch{return""}},A=new WeakMap;function H(t,e){let n=A.get(t);if(!e?.force&&n!==void 0&&t.style.getPropertyValue("translate")!==n){h("position_edit_fold_skipped",{hfId:t.getAttribute("data-hf-id")});return}let o=u(t.getAttribute("data-x"))-u(t.getAttribute(l)),r=u(t.getAttribute("data-y"))-u(t.getAttribute(p));t.hasAttribute(d)||t.setAttribute(d,M(t)),n===void 0&&L(t);let i=t.getAttribute(d)??"",a=D(i,`${o}px`,`${r}px`);t.style.setProperty("translate",a),A.set(t,t.style.getPropertyValue("translate"))}function _(t){let e=t.querySelectorAll(`[${l}], [${p}]`),n=t.defaultView?.HTMLElement,o=0;for(let r=0;r<e.length;r++){let i=e[r];(n?i instanceof n:typeof i.style?.setProperty=="function")&&(H(i),o+=1)}return o}var T="__hfPositionEditsSeekReapplyWrapped";function v(t){let e=t,n=()=>{try{_(e.document)}catch{}},o=s=>typeof s=="function"&&!!s[T],r=s=>{try{Object.defineProperty(s,T,{value:!0})}catch{}},i=(s,P)=>{let c=s();if(typeof c!="function"||o(c))return;let m=function(...R){let x=c.apply(this,R);return n(),x};r(m),P(m),n()},a=()=>{i(()=>e.__hf?.seek,s=>{e.__hf&&(e.__hf.seek=s)}),i(()=>e.__player?.renderSeek,s=>{e.__player&&(e.__player.renderSeek=s)})};a();let y=120,S=e.setInterval(()=>{a(),y-=1,y<=0&&e.clearInterval(S)},50)}function b(){document.querySelector(`[${l}], [${p}]`)&&(_(document),v(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",b,{once:!0}):b();})();\n';
|
||||
|
||||
/** Returns the pre-built position-edits render IIFE as a string constant. */
|
||||
export function getPositionEditsRenderScript(): string {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { installPositionEditsSeekReapply } from "./positionEdits";
|
||||
|
||||
describe("init.ts per-seek position-edit parity", () => {
|
||||
it("reapplies the position edit after renderSeek", () => {
|
||||
it("recomputes the position edit after renderSeek", () => {
|
||||
document.body.innerHTML =
|
||||
'<h1 data-x="20" data-y="0" data-hf-edit-base-x="0" data-hf-edit-base-y="0">hi</h1>';
|
||||
const h1 = document.querySelector("h1");
|
||||
@@ -10,13 +10,13 @@ describe("init.ts per-seek position-edit parity", () => {
|
||||
|
||||
// @ts-expect-error test global
|
||||
window.__player = {
|
||||
renderSeek: () => h1.style.setProperty("translate", "none"),
|
||||
renderSeek: () => h1.setAttribute("data-x", "40"),
|
||||
};
|
||||
installPositionEditsSeekReapply(window as Window & typeof globalThis);
|
||||
// @ts-expect-error test global
|
||||
window.__player.renderSeek(1);
|
||||
|
||||
expect(h1.style.getPropertyValue("translate")).toBe("20px 0px");
|
||||
expect(h1.style.getPropertyValue("translate")).toBe("40px 0px");
|
||||
// @ts-expect-error test global
|
||||
delete window.__player;
|
||||
h1.remove();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
EDIT_BASE_X_ATTR,
|
||||
EDIT_BASE_Y_ATTR,
|
||||
@@ -211,6 +211,28 @@ describe("installPositionEditsSeekReapply", () => {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
it("wraps __hf.seek and a seek function assigned after installation", () => {
|
||||
vi.useFakeTimers();
|
||||
const el = makeElement({ "data-x": "8", "data-y": "0", "data-hf-edit-base-x": "0" });
|
||||
const calls: number[] = [];
|
||||
// @ts-expect-error test global
|
||||
window.__hf = {};
|
||||
|
||||
installPositionEditsSeekReapply(window as Window & typeof globalThis);
|
||||
// @ts-expect-error test global
|
||||
window.__hf.seek = (time: number) => calls.push(time);
|
||||
vi.advanceTimersByTime(50);
|
||||
// @ts-expect-error test global
|
||||
window.__hf.seek(3);
|
||||
|
||||
expect(calls).toEqual([3]);
|
||||
expect(el.style.getPropertyValue("translate")).toBe("8px 0px");
|
||||
// @ts-expect-error test global
|
||||
delete window.__hf;
|
||||
el.remove();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("does not throw when neither seek global exists", () => {
|
||||
expect(() =>
|
||||
installPositionEditsSeekReapply(window as Window & typeof globalThis),
|
||||
|
||||
@@ -154,7 +154,7 @@ export function applyPositionEditToElement(el: HTMLElement, opts?: { force?: boo
|
||||
* Apply all pending position edits in the document. Returns the number of
|
||||
* elements updated.
|
||||
*/
|
||||
export function applyPositionEdits(doc: Document, opts?: { force?: boolean }): number {
|
||||
export function applyPositionEdits(doc: Document): number {
|
||||
const marked = doc.querySelectorAll(`[${EDIT_BASE_X_ATTR}], [${EDIT_BASE_Y_ATTR}]`);
|
||||
// Not `instanceof HTMLElement`: `doc` is frequently an iframe's document (the
|
||||
// SDK's edit preview, a host embedding a composition), and its elements are
|
||||
@@ -170,7 +170,7 @@ export function applyPositionEdits(doc: Document, opts?: { force?: boolean }): n
|
||||
? el instanceof RealmHTMLElement
|
||||
: typeof (el as HTMLElement).style?.setProperty === "function";
|
||||
if (!isStylable) continue;
|
||||
applyPositionEditToElement(el as HTMLElement, opts);
|
||||
applyPositionEditToElement(el as HTMLElement);
|
||||
applied += 1;
|
||||
}
|
||||
return applied;
|
||||
@@ -189,7 +189,7 @@ export function installPositionEditsSeekReapply(win: Window & typeof globalThis)
|
||||
const target = win as SeekWindow;
|
||||
const reapply = (): void => {
|
||||
try {
|
||||
applyPositionEdits(target.document, { force: true });
|
||||
applyPositionEdits(target.document);
|
||||
} catch {
|
||||
// A position edit must never break the render seek path.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user