@hyperframes/studio-server 0.7.15

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/manualEditsRenderScript.ts"],"sourcesContent":["// fallow-ignore-file code-duplication\nexport interface StudioManualEditsRenderScriptOptions {\n activeCompositionPath?: string | null;\n}\n\nexport const STUDIO_MANUAL_EDITS_PATH = \".hyperframes/studio-manual-edits.json\";\n\nexport function createStudioManualEditsRenderBodyScript(\n manifestContent: string,\n options: StudioManualEditsRenderScriptOptions = {},\n): string | null {\n if (!manifestContent.trim()) return null;\n return `(${studioManualEditsRenderRuntime.toString()})(${JSON.stringify(manifestContent)}, ${JSON.stringify(options.activeCompositionPath ?? null)});`;\n}\n\n/**\n * Returns a self-contained IIFE string that re-applies studio position edits\n * (translate, rotate) after every GSAP seek by querying data attributes baked\n * into the HTML. Works without a JSON manifest — positions are already inlined\n * as CSS custom properties on the elements.\n */\nexport function createStudioPositionSeekReapplyScript(): string {\n return `(${studioPositionSeekReapplyRuntime.toString()})();`;\n}\n\nfunction studioPositionSeekReapplyRuntime(): void {\n const OFFSET_X_PROP = \"--hf-studio-offset-x\";\n const OFFSET_Y_PROP = \"--hf-studio-offset-y\";\n const WIDTH_PROP = \"--hf-studio-width\";\n const HEIGHT_PROP = \"--hf-studio-height\";\n const ROTATION_PROP = \"--hf-studio-rotation\";\n const PATH_OFFSET_ATTR = \"data-hf-studio-path-offset\";\n const BOX_SIZE_ATTR = \"data-hf-studio-box-size\";\n const ROTATION_ATTR = \"data-hf-studio-rotation\";\n const ORIGINAL_TRANSLATE_ATTR = \"data-hf-studio-original-translate\";\n const ORIGINAL_ROTATE_ATTR = \"data-hf-studio-original-rotate\";\n const MOTION_ATTR = \"data-hf-studio-motion\";\n const MOTION_TL_KEY = \"studio-motion\";\n const WRAPPED_PROP = \"__hfStudioPositionSeekReapplyWrapped\";\n\n if (\n !document.querySelector(\"[\" + PATH_OFFSET_ATTR + '=\"true\"]') &&\n !document.querySelector(\"[\" + BOX_SIZE_ATTR + '=\"true\"]') &&\n !document.querySelector(\"[\" + ROTATION_ATTR + '=\"true\"]') &&\n !document.querySelector(\"[\" + MOTION_ATTR + \"]\")\n )\n return;\n\n const splitTopLevelWhitespace = (value: string): string[] => {\n const parts: string[] = [];\n let depth = 0;\n let current = \"\";\n for (const char of value.trim()) {\n if (char === \"(\") depth += 1;\n if (char === \")\") depth = Math.max(0, depth - 1);\n if (/\\s/.test(char) && depth === 0) {\n if (current) parts.push(current);\n current = \"\";\n } else {\n current += char;\n }\n }\n if (current) parts.push(current);\n return parts;\n };\n\n const composeTranslate = (element: HTMLElement, x: string, y: string): string => {\n const original = element.getAttribute(ORIGINAL_TRANSLATE_ATTR)?.trim();\n if (!original || original === \"none\") return x + \" \" + y;\n const parts = splitTopLevelWhitespace(original);\n if (parts.length === 1) return \"calc(\" + parts[0] + \" + \" + x + \") \" + y;\n if (parts.length >= 2) {\n const z = parts.length >= 3 ? \" \" + parts[2] : \"\";\n return \"calc(\" + parts[0] + \" + \" + x + \") calc(\" + parts[1] + \" + \" + y + \")\" + z;\n }\n return x + \" \" + y;\n };\n\n const isSimpleRotateAngle = (value: string): boolean =>\n /^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:deg|rad|turn|grad)$/.test(value.trim());\n\n const composeRotation = (element: HTMLElement, rotationValue: string): string => {\n const original = element.getAttribute(ORIGINAL_ROTATE_ATTR)?.trim();\n if (!original || original === \"none\" || !isSimpleRotateAngle(original)) return rotationValue;\n return \"calc(\" + original + \" + \" + rotationValue + \")\";\n };\n\n let lastSeekTime = 0;\n let cachedMotionKey = \"\";\n\n const finiteNum = (v: unknown): number | null =>\n typeof v === \"number\" && Number.isFinite(v) ? v : null;\n\n const computeMotionKey = (motionEls: NodeListOf<Element>): string => {\n let key = \"\";\n for (let i = 0; i < motionEls.length; i++) {\n const json = (motionEls[i] as HTMLElement).getAttribute?.(MOTION_ATTR);\n if (json) key += (key ? \"\\n\" : \"\") + json;\n }\n return key;\n };\n\n const reapplyMotionTimeline = (): void => {\n const motionEls = document.querySelectorAll(\"[\" + MOTION_ATTR + \"]\");\n if (motionEls.length === 0) {\n cachedMotionKey = \"\";\n return;\n }\n const win = window as Window & {\n gsap?: {\n timeline?: (opts: Record<string, unknown>) => Record<string, unknown>;\n set?: (el: HTMLElement, vars: Record<string, unknown>) => void;\n registerPlugin?: (plugin: unknown) => void;\n };\n CustomEase?: { create?: (id: string, data: string) => void };\n __timelines?: Record<string, Record<string, unknown>>;\n };\n const gsap = win.gsap;\n if (!gsap || typeof gsap.timeline !== \"function\") return;\n win.__timelines = win.__timelines || {};\n\n // Cache the timeline keyed by the concatenated motion JSON strings.\n // On each seek, if the key hasn't changed, just seek the existing timeline\n // instead of rebuilding it (avoids kill+recreate on every frame).\n const motionKey = computeMotionKey(motionEls);\n const existing = win.__timelines[MOTION_TL_KEY];\n if (\n motionKey &&\n motionKey === cachedMotionKey &&\n existing &&\n typeof existing.totalTime === \"function\"\n ) {\n (existing.totalTime as (t: number, s: boolean) => void)(lastSeekTime, false);\n return;\n }\n\n if (existing && typeof existing.kill === \"function\") (existing.kill as () => void)();\n const tl = gsap.timeline({ paused: true, defaults: { overwrite: \"auto\" } });\n const fromTo = tl.fromTo as (\n el: HTMLElement,\n from: Record<string, unknown>,\n to: Record<string, unknown>,\n pos: number,\n ) => void;\n if (typeof fromTo !== \"function\") return;\n let applied = 0;\n for (let i = 0; i < motionEls.length; i++) {\n const el = motionEls[i] as HTMLElement;\n if (!(el instanceof HTMLElement)) continue;\n const json = el.getAttribute(MOTION_ATTR);\n if (!json) continue;\n try {\n const m = JSON.parse(json) as Record<string, unknown>;\n const start = finiteNum(m.start);\n const duration = finiteNum(m.duration);\n if (start == null || duration == null || duration <= 0) continue;\n const ease = typeof m.ease === \"string\" ? m.ease : \"none\";\n const from = (m.from && typeof m.from === \"object\" ? m.from : {}) as Record<\n string,\n unknown\n >;\n const to = (m.to && typeof m.to === \"object\" ? m.to : {}) as Record<string, unknown>;\n const customEase = m.customEase as { id?: string; data?: string } | null | undefined;\n let resolvedEase = ease;\n if (customEase?.id && customEase?.data && win.CustomEase?.create) {\n try {\n gsap.registerPlugin?.(win.CustomEase);\n win.CustomEase.create(customEase.id, customEase.data);\n resolvedEase = customEase.id;\n } catch {\n /* use default ease */\n }\n }\n fromTo.call(\n tl,\n el,\n { ...from },\n { ...to, duration, ease: resolvedEase, overwrite: \"auto\", immediateRender: false },\n start,\n );\n applied += 1;\n } catch {\n /* malformed JSON — skip */\n }\n }\n if (applied === 0) {\n cachedMotionKey = \"\";\n if (typeof (tl as { kill?: () => void }).kill === \"function\")\n (tl as { kill: () => void }).kill();\n return;\n }\n cachedMotionKey = motionKey;\n win.__timelines[MOTION_TL_KEY] = tl;\n if (typeof tl.pause === \"function\") (tl.pause as () => void)();\n if (typeof tl.totalTime === \"function\")\n (tl.totalTime as (t: number, s: boolean) => void)(lastSeekTime, false);\n };\n\n const stripGsapTranslateFromTransform = (el: HTMLElement): void => {\n const transform = el.style.getPropertyValue(\"transform\");\n if (!transform || transform === \"none\") return;\n const win = el.ownerDocument.defaultView as (Window & typeof globalThis) | null;\n const MatrixCtor = (win as unknown as { DOMMatrix?: typeof DOMMatrix })?.DOMMatrix;\n if (!MatrixCtor) return;\n try {\n const m = new MatrixCtor(transform);\n if (m.m41 === 0 && m.m42 === 0) return;\n m.m41 = 0;\n m.m42 = 0;\n if (m.is2D && m.a === 1 && m.b === 0 && m.c === 0 && m.d === 1) {\n el.style.removeProperty(\"transform\");\n } else {\n el.style.setProperty(\"transform\", m.toString());\n }\n } catch {\n /* non-parseable transform — leave as-is */\n }\n };\n\n const reapplyAll = (): void => {\n const offsetEls = document.querySelectorAll(\"[\" + PATH_OFFSET_ATTR + '=\"true\"]');\n for (let i = 0; i < offsetEls.length; i++) {\n const el = offsetEls[i] as HTMLElement;\n if (!(el instanceof HTMLElement)) continue;\n const x = el.style.getPropertyValue(OFFSET_X_PROP);\n const y = el.style.getPropertyValue(OFFSET_Y_PROP);\n if (x || y) {\n el.style.setProperty(\n \"translate\",\n composeTranslate(\n el,\n \"var(\" + OFFSET_X_PROP + \", 0px)\",\n \"var(\" + OFFSET_Y_PROP + \", 0px)\",\n ),\n );\n stripGsapTranslateFromTransform(el);\n }\n }\n const boxSizeEls = document.querySelectorAll(\"[\" + BOX_SIZE_ATTR + '=\"true\"]');\n for (let i = 0; i < boxSizeEls.length; i++) {\n const el = boxSizeEls[i] as HTMLElement;\n if (!(el instanceof HTMLElement)) continue;\n const w = el.style.getPropertyValue(WIDTH_PROP);\n const h = el.style.getPropertyValue(HEIGHT_PROP);\n if (w) el.style.setProperty(\"width\", w);\n if (h) el.style.setProperty(\"height\", h);\n }\n const rotEls = document.querySelectorAll(\"[\" + ROTATION_ATTR + '=\"true\"]');\n for (let i = 0; i < rotEls.length; i++) {\n const el = rotEls[i] as HTMLElement;\n if (!(el instanceof HTMLElement)) continue;\n const rot = el.style.getPropertyValue(ROTATION_PROP);\n if (rot) {\n el.style.setProperty(\"rotate\", composeRotation(el, \"var(\" + ROTATION_PROP + \", 0deg)\"));\n stripGsapTranslateFromTransform(el);\n }\n }\n reapplyMotionTimeline();\n };\n\n const runtimeWindow = window as Window & {\n __hf?: Record<string, unknown>;\n __player?: Record<string, unknown>;\n };\n\n const isWrapped = (fn: (time: number) => unknown): boolean =>\n Boolean((fn as unknown as Record<string, unknown>)[WRAPPED_PROP]);\n\n const markWrapped = (fn: (time: number) => unknown): void => {\n try {\n Object.defineProperty(fn, WRAPPED_PROP, {\n configurable: false,\n enumerable: false,\n value: true,\n });\n } catch {\n try {\n (fn as unknown as Record<string, unknown>)[WRAPPED_PROP] = true;\n } catch {\n /* ignore */\n }\n }\n };\n\n const wrapFn = (get: () => unknown, set: (fn: (time: number) => unknown) => void): boolean => {\n const fn = get();\n if (typeof fn !== \"function\") return false;\n const seek = fn as (time: number) => unknown;\n if (isWrapped(seek)) {\n reapplyAll();\n return true;\n }\n const wrapped = function (this: unknown, time: number): unknown {\n lastSeekTime = typeof time === \"number\" && Number.isFinite(time) ? Math.max(0, time) : 0;\n const result = seek.call(this, time);\n reapplyAll();\n return result;\n };\n markWrapped(wrapped);\n set(wrapped);\n reapplyAll();\n return true;\n };\n\n const wrapSeekFunctions = (): boolean => {\n const a = wrapFn(\n () => runtimeWindow.__hf?.[\"seek\"],\n (fn) => {\n if (runtimeWindow.__hf) runtimeWindow.__hf[\"seek\"] = fn;\n },\n );\n const b = wrapFn(\n () => runtimeWindow.__player?.[\"renderSeek\"],\n (fn) => {\n if (runtimeWindow.__player) runtimeWindow.__player[\"renderSeek\"] = fn;\n },\n );\n return a || b;\n };\n\n const installSeekTrap = (\n obj: Record<string, unknown> | undefined,\n key: string,\n getter: () => unknown,\n setter: (fn: (time: number) => unknown) => void,\n ): void => {\n if (!obj) return;\n try {\n let current = obj[key];\n Object.defineProperty(obj, key, {\n configurable: true,\n enumerable: true,\n get() {\n return current;\n },\n set(value: unknown) {\n current = value;\n if (typeof value === \"function\" && !isWrapped(value as (time: number) => unknown)) {\n wrapFn(getter, setter);\n }\n },\n });\n } catch {\n /* non-configurable — fall back to polling */\n }\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => reapplyAll(), { once: true });\n } else {\n reapplyAll();\n }\n\n wrapSeekFunctions();\n installSeekTrap(\n runtimeWindow.__hf,\n \"seek\",\n () => runtimeWindow.__hf?.[\"seek\"],\n (fn) => {\n if (runtimeWindow.__hf) runtimeWindow.__hf[\"seek\"] = fn;\n },\n );\n installSeekTrap(\n runtimeWindow.__player as Record<string, unknown> | undefined,\n \"renderSeek\",\n () => runtimeWindow.__player?.[\"renderSeek\"],\n (fn) => {\n if (runtimeWindow.__player) runtimeWindow.__player[\"renderSeek\"] = fn;\n },\n );\n let remaining = 120;\n const interval = setInterval(() => {\n wrapSeekFunctions();\n remaining -= 1;\n if (remaining <= 0) clearInterval(interval);\n }, 50);\n}\n\nfunction studioManualEditsRenderRuntime(\n manifestContent: string,\n activeCompositionPath: string | null,\n): void {\n const OFFSET_X_PROP = \"--hf-studio-offset-x\";\n const OFFSET_Y_PROP = \"--hf-studio-offset-y\";\n const WIDTH_PROP = \"--hf-studio-width\";\n const HEIGHT_PROP = \"--hf-studio-height\";\n const ROTATION_PROP = \"--hf-studio-rotation\";\n const PATH_OFFSET_ATTR = \"data-hf-studio-path-offset\";\n const BOX_SIZE_ATTR = \"data-hf-studio-box-size\";\n const ROTATION_ATTR = \"data-hf-studio-rotation\";\n const ORIGINAL_TRANSLATE_ATTR = \"data-hf-studio-original-translate\";\n const ORIGINAL_ROTATE_ATTR = \"data-hf-studio-original-rotate\";\n const WRAPPED_SEEK_PROP = \"__hfStudioManualEditsWrapped\";\n const ROTATION_TRANSFORM_ORIGIN = \"center center\";\n\n const finiteNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\n const objectRecord = (value: unknown): Record<string, unknown> | null =>\n value && typeof value === \"object\" ? (value as Record<string, unknown>) : null;\n\n const runtimeWindow = window as Window & {\n __hf?: { seek?: (time: number) => unknown };\n __hfStudioManualEditsApply?: () => number;\n __player?: { renderSeek?: (time: number) => unknown };\n };\n\n const parsedManifest = (() => {\n try {\n return objectRecord(JSON.parse(manifestContent));\n } catch {\n return null;\n }\n })();\n const manifestEdits = Array.isArray(parsedManifest?.edits) ? parsedManifest.edits : [];\n if (manifestEdits.length === 0) return;\n\n const sourceFileForElement = (element: HTMLElement): string => {\n let current: HTMLElement | null = element;\n while (current) {\n const sourceFile =\n current.getAttribute(\"data-composition-file\") ??\n current.getAttribute(\"data-composition-src\");\n if (sourceFile) return sourceFile;\n current = current.parentElement;\n }\n return activeCompositionPath ?? \"index.html\";\n };\n\n const elementMatchesSourceFile = (element: HTMLElement, sourceFile: string): boolean =>\n sourceFileForElement(element) === sourceFile;\n\n const styleUsesStudioOffset = (value: string): boolean =>\n value.includes(OFFSET_X_PROP) || value.includes(OFFSET_Y_PROP);\n\n const styleUsesStudioRotation = (value: string): boolean => value.includes(ROTATION_PROP);\n\n const splitTopLevelWhitespace = (value: string): string[] => {\n const parts: string[] = [];\n let depth = 0;\n let current = \"\";\n for (const char of value.trim()) {\n if (char === \"(\") depth += 1;\n if (char === \")\") depth = Math.max(0, depth - 1);\n if (/\\s/.test(char) && depth === 0) {\n if (current) parts.push(current);\n current = \"\";\n } else {\n current += char;\n }\n }\n if (current) parts.push(current);\n return parts;\n };\n\n const composeTranslate = (element: HTMLElement, x: string, y: string): string => {\n const original = element.getAttribute(ORIGINAL_TRANSLATE_ATTR)?.trim();\n if (!original || original === \"none\") return `${x} ${y}`;\n\n const parts = splitTopLevelWhitespace(original);\n if (parts.length === 1) return `calc(${parts[0]} + ${x}) ${y}`;\n if (parts.length === 2) return `calc(${parts[0]} + ${x}) calc(${parts[1]} + ${y})`;\n if (parts.length === 3) {\n return `calc(${parts[0]} + ${x}) calc(${parts[1]} + ${y}) ${parts[2]}`;\n }\n return `${x} ${y}`;\n };\n\n const readStyleOrComputed = (element: HTMLElement, property: string): string => {\n try {\n return (\n element.style.getPropertyValue(property) ||\n getComputedStyle(element).getPropertyValue(property)\n );\n } catch {\n return element.style.getPropertyValue(property);\n }\n };\n\n const readTransformLonghandBase = (\n element: HTMLElement,\n property: \"translate\" | \"rotate\",\n ): string => {\n const value = readStyleOrComputed(element, property).trim();\n return value === \"none\" ? \"\" : value;\n };\n\n const preparePathOffsetBase = (element: HTMLElement): void => {\n const currentTranslate = readTransformLonghandBase(element, \"translate\");\n const hasMarker = element.hasAttribute(PATH_OFFSET_ATTR);\n const wasResetByAnimation = !styleUsesStudioOffset(currentTranslate);\n if (!hasMarker) {\n element.setAttribute(ORIGINAL_TRANSLATE_ATTR, wasResetByAnimation ? currentTranslate : \"\");\n } else if (wasResetByAnimation) {\n element.setAttribute(ORIGINAL_TRANSLATE_ATTR, currentTranslate);\n }\n };\n\n const prepareRotationBase = (element: HTMLElement): void => {\n const currentRotate = readTransformLonghandBase(element, \"rotate\");\n const hasMarker = element.hasAttribute(ROTATION_ATTR);\n const wasResetByAnimation = !styleUsesStudioRotation(currentRotate);\n if (!hasMarker) {\n element.setAttribute(ORIGINAL_ROTATE_ATTR, wasResetByAnimation ? currentRotate : \"\");\n } else if (wasResetByAnimation) {\n element.setAttribute(ORIGINAL_ROTATE_ATTR, currentRotate);\n }\n };\n\n const querySelectorCandidates = (selector: string): HTMLElement[] => {\n const isCandidate = (element: Element): element is HTMLElement =>\n element instanceof HTMLElement;\n\n const className = selector.match(/^\\.([A-Za-z0-9_-]+)$/)?.[1];\n if (className) {\n return Array.from(document.getElementsByTagName(\"*\")).filter(\n (element): element is HTMLElement =>\n isCandidate(element) && element.classList.contains(className),\n );\n }\n\n if (/^[A-Za-z][A-Za-z0-9-]*$/.test(selector)) {\n return Array.from(document.getElementsByTagName(selector)).filter(isCandidate);\n }\n\n return Array.from(document.querySelectorAll(selector)).filter(isCandidate);\n };\n\n const resolveTarget = (edit: Record<string, unknown>): HTMLElement | null => {\n const targetRecord = objectRecord(edit.target);\n if (!targetRecord) return null;\n\n const sourceFile = typeof targetRecord.sourceFile === \"string\" ? targetRecord.sourceFile : \"\";\n if (!sourceFile) return null;\n\n const id = typeof targetRecord.id === \"string\" ? targetRecord.id : \"\";\n if (id) {\n const byId = document.getElementById(id);\n if (byId instanceof HTMLElement && elementMatchesSourceFile(byId, sourceFile)) return byId;\n\n const matchesById = [\n document.documentElement,\n ...Array.from(document.getElementsByTagName(\"*\")),\n ].filter(\n (element): element is HTMLElement =>\n element instanceof HTMLElement &&\n element.id === id &&\n elementMatchesSourceFile(element, sourceFile),\n );\n if (matchesById[0]) return matchesById[0];\n }\n\n const selector = typeof targetRecord.selector === \"string\" ? targetRecord.selector : \"\";\n if (!selector) return null;\n\n try {\n const matches = querySelectorCandidates(selector).filter((element) =>\n elementMatchesSourceFile(element, sourceFile),\n );\n const selectorIndex = finiteNumber(targetRecord.selectorIndex) ?? 0;\n return matches[Math.max(0, Math.floor(selectorIndex))] ?? null;\n } catch {\n return null;\n }\n };\n\n const roundRotationAngle = (angle: number): number => Math.round(angle * 10) / 10;\n\n const isSimpleRotateAngle = (value: string): boolean =>\n /^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:deg|rad|turn|grad)$/.test(value.trim());\n\n const composeRotation = (element: HTMLElement, rotationValue: string): string => {\n const original = element.getAttribute(ORIGINAL_ROTATE_ATTR)?.trim();\n if (!original || original === \"none\" || !isSimpleRotateAngle(original)) {\n return rotationValue;\n }\n return `calc(${original} + ${rotationValue})`;\n };\n\n const applyPathOffset = (element: HTMLElement, edit: Record<string, unknown>): void => {\n const x = finiteNumber(edit.x);\n const y = finiteNumber(edit.y);\n if (x == null || y == null) return;\n preparePathOffsetBase(element);\n element.setAttribute(PATH_OFFSET_ATTR, \"true\");\n element.style.setProperty(OFFSET_X_PROP, `${Math.round(x)}px`);\n element.style.setProperty(OFFSET_Y_PROP, `${Math.round(y)}px`);\n element.style.setProperty(\n \"translate\",\n composeTranslate(element, `var(${OFFSET_X_PROP}, 0px)`, `var(${OFFSET_Y_PROP}, 0px)`),\n );\n };\n\n const readParentFlexBasisPixels = (\n element: HTMLElement,\n size: { width: number; height: number },\n ): number | null => {\n const parent = element.parentElement;\n if (!parent) return null;\n const styles = getComputedStyle(parent);\n if (styles.display !== \"flex\" && styles.display !== \"inline-flex\") return null;\n return Math.round(\n Math.max(1, styles.flexDirection.startsWith(\"column\") ? size.height : size.width),\n );\n };\n\n const applyBoxSize = (element: HTMLElement, edit: Record<string, unknown>): void => {\n const width = finiteNumber(edit.width);\n const height = finiteNumber(edit.height);\n if (width == null || height == null || width <= 0 || height <= 0) return;\n\n const rounded = {\n width: Math.round(Math.max(1, width)),\n height: Math.round(Math.max(1, height)),\n };\n element.setAttribute(BOX_SIZE_ATTR, \"true\");\n element.style.setProperty(WIDTH_PROP, `${rounded.width}px`);\n element.style.setProperty(HEIGHT_PROP, `${rounded.height}px`);\n element.style.setProperty(\"box-sizing\", \"border-box\");\n element.style.setProperty(\"width\", `${rounded.width}px`);\n element.style.setProperty(\"height\", `${rounded.height}px`);\n element.style.setProperty(\"min-width\", \"0px\");\n element.style.setProperty(\"min-height\", \"0px\");\n element.style.setProperty(\"max-width\", \"none\");\n element.style.setProperty(\"max-height\", \"none\");\n\n const flexBasis = readParentFlexBasisPixels(element, rounded);\n if (flexBasis != null) {\n element.style.setProperty(\"flex-basis\", `${flexBasis}px`);\n element.style.setProperty(\"flex-grow\", \"0\");\n element.style.setProperty(\"flex-shrink\", \"0\");\n }\n if (getComputedStyle(element).display === \"inline\") {\n element.style.setProperty(\"display\", \"inline-block\");\n }\n };\n\n const applyRotation = (element: HTMLElement, edit: Record<string, unknown>): void => {\n const angle = finiteNumber(edit.angle);\n if (angle == null) return;\n prepareRotationBase(element);\n element.setAttribute(ROTATION_ATTR, \"true\");\n element.style.setProperty(ROTATION_PROP, `${roundRotationAngle(angle)}deg`);\n element.style.setProperty(\"transform-origin\", ROTATION_TRANSFORM_ORIGIN);\n element.style.setProperty(\"rotate\", composeRotation(element, `var(${ROTATION_PROP}, 0deg)`));\n };\n\n const applyManifest = (): number => {\n let applied = 0;\n for (const edit of manifestEdits) {\n const editRecord = objectRecord(edit);\n if (!editRecord) continue;\n const element = resolveTarget(editRecord);\n if (!element) continue;\n if (editRecord.kind === \"path-offset\") applyPathOffset(element, editRecord);\n if (editRecord.kind === \"box-size\") applyBoxSize(element, editRecord);\n if (editRecord.kind === \"rotation\") applyRotation(element, editRecord);\n applied += 1;\n }\n return applied;\n };\n runtimeWindow.__hfStudioManualEditsApply = applyManifest;\n\n const markWrapped = (fn: (time: number) => unknown): void => {\n try {\n Object.defineProperty(fn, WRAPPED_SEEK_PROP, {\n configurable: false,\n enumerable: false,\n value: true,\n });\n } catch {\n try {\n (fn as unknown as Record<string, unknown>)[WRAPPED_SEEK_PROP] = true;\n } catch {\n // Ignore non-extensible functions.\n }\n }\n };\n\n const isWrapped = (fn: (time: number) => unknown): boolean =>\n Boolean((fn as unknown as Record<string, unknown>)[WRAPPED_SEEK_PROP]);\n\n const wrapFunction = (\n get: () => ((time: number) => unknown) | undefined,\n set: (fn: (time: number) => unknown) => void,\n ): boolean => {\n const fn = get();\n if (!fn) return false;\n const seek = fn as (time: number) => unknown;\n if (isWrapped(seek)) {\n applyManifest();\n return true;\n }\n\n const wrappedSeek = function (this: unknown, time: number): unknown {\n const result = seek.call(this, time);\n applyManifest();\n return result;\n };\n markWrapped(wrappedSeek);\n set(wrappedSeek);\n applyManifest();\n return true;\n };\n\n const wrapSeekFunctions = (): boolean => {\n const wrappedHfSeek = wrapFunction(\n () => runtimeWindow.__hf?.seek,\n (fn) => {\n if (runtimeWindow.__hf) runtimeWindow.__hf.seek = fn;\n },\n );\n const wrappedPlayerRenderSeek = wrapFunction(\n () => runtimeWindow.__player?.renderSeek,\n (fn) => {\n if (runtimeWindow.__player) runtimeWindow.__player.renderSeek = fn;\n },\n );\n return wrappedHfSeek || wrappedPlayerRenderSeek;\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => applyManifest(), { once: true });\n } else {\n applyManifest();\n }\n\n wrapSeekFunctions();\n let remainingSeekWrapAttempts = 120;\n const seekWrapInterval = setInterval(() => {\n wrapSeekFunctions();\n remainingSeekWrapAttempts -= 1;\n if (remainingSeekWrapAttempts <= 0) clearInterval(seekWrapInterval);\n }, 50);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,9 @@
1
+ interface ScreenshotClip {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ declare function getElementScreenshotClip(selector: string, selectorIndex?: number): ScreenshotClip | undefined;
8
+
9
+ export { type ScreenshotClip, getElementScreenshotClip };
@@ -0,0 +1,26 @@
1
+ // src/helpers/screenshotClip.ts
2
+ function getElementScreenshotClip(selector, selectorIndex) {
3
+ const matches = Array.from(document.querySelectorAll(selector)).filter(
4
+ (el2) => el2 instanceof HTMLElement
5
+ );
6
+ const safeIndex = Math.max(0, Math.min(matches.length - 1, Math.floor(selectorIndex ?? 0)));
7
+ const el = matches[safeIndex] ?? null;
8
+ if (!(el instanceof HTMLElement)) return void 0;
9
+ const rect = el.getBoundingClientRect();
10
+ if (rect.width < 4 || rect.height < 4) return void 0;
11
+ const pad = 8;
12
+ const x = Math.max(0, rect.left - pad);
13
+ const y = Math.max(0, rect.top - pad);
14
+ const maxWidth = window.innerWidth - x;
15
+ const maxHeight = window.innerHeight - y;
16
+ return {
17
+ x,
18
+ y,
19
+ width: Math.max(1, Math.min(rect.width + pad * 2, maxWidth)),
20
+ height: Math.max(1, Math.min(rect.height + pad * 2, maxHeight))
21
+ };
22
+ }
23
+ export {
24
+ getElementScreenshotClip
25
+ };
26
+ //# sourceMappingURL=screenshotClip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/screenshotClip.ts"],"sourcesContent":["export interface ScreenshotClip {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport function getElementScreenshotClip(\n selector: string,\n selectorIndex?: number,\n): ScreenshotClip | undefined {\n const matches = Array.from(document.querySelectorAll(selector)).filter(\n (el): el is HTMLElement => el instanceof HTMLElement,\n );\n const safeIndex = Math.max(0, Math.min(matches.length - 1, Math.floor(selectorIndex ?? 0)));\n const el = matches[safeIndex] ?? null;\n if (!(el instanceof HTMLElement)) return undefined;\n const rect = el.getBoundingClientRect();\n if (rect.width < 4 || rect.height < 4) return undefined;\n const pad = 8;\n const x = Math.max(0, rect.left - pad);\n const y = Math.max(0, rect.top - pad);\n const maxWidth = window.innerWidth - x;\n const maxHeight = window.innerHeight - y;\n return {\n x,\n y,\n width: Math.max(1, Math.min(rect.width + pad * 2, maxWidth)),\n height: Math.max(1, Math.min(rect.height + pad * 2, maxHeight)),\n };\n}\n"],"mappings":";AAOO,SAAS,yBACd,UACA,eAC4B;AAC5B,QAAM,UAAU,MAAM,KAAK,SAAS,iBAAiB,QAAQ,CAAC,EAAE;AAAA,IAC9D,CAACA,QAA0BA,eAAc;AAAA,EAC3C;AACA,QAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,SAAS,GAAG,KAAK,MAAM,iBAAiB,CAAC,CAAC,CAAC;AAC1F,QAAM,KAAK,QAAQ,SAAS,KAAK;AACjC,MAAI,EAAE,cAAc,aAAc,QAAO;AACzC,QAAM,OAAO,GAAG,sBAAsB;AACtC,MAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,EAAG,QAAO;AAC9C,QAAM,MAAM;AACZ,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,OAAO,GAAG;AACrC,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG;AACpC,QAAM,WAAW,OAAO,aAAa;AACrC,QAAM,YAAY,OAAO,cAAc;AACvC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,QAAQ,MAAM,GAAG,QAAQ,CAAC;AAAA,IAC3D,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,MAAM,GAAG,SAAS,CAAC;AAAA,EAChE;AACF;","names":["el"]}
@@ -0,0 +1,10 @@
1
+ interface StudioMotionRenderScriptOptions {
2
+ activeCompositionPath?: string | null;
3
+ }
4
+ declare const STUDIO_MOTION_PATH = ".hyperframes/studio-motion.json";
5
+ /**
6
+ * Builds the render-time Studio motion runtime script, or null when no owned motion exists.
7
+ */
8
+ declare function createStudioMotionRenderBodyScript(manifestContent: string, options?: StudioMotionRenderScriptOptions): string | null;
9
+
10
+ export { STUDIO_MOTION_PATH, type StudioMotionRenderScriptOptions, createStudioMotionRenderBodyScript };
@@ -0,0 +1,191 @@
1
+ // src/helpers/studioMotionRenderScript.ts
2
+ var STUDIO_MOTION_PATH = ".hyperframes/studio-motion.json";
3
+ function hasStudioMotionEntries(manifestContent) {
4
+ try {
5
+ const parsed = JSON.parse(manifestContent);
6
+ return Array.isArray(parsed.motions) && parsed.motions.length > 0;
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+ function createStudioMotionRenderBodyScript(manifestContent, options = {}) {
12
+ if (!manifestContent.trim() || !hasStudioMotionEntries(manifestContent)) return null;
13
+ return `(${studioMotionRenderRuntime.toString()})(${JSON.stringify(manifestContent)}, ${JSON.stringify(options.activeCompositionPath ?? null)});`;
14
+ }
15
+ function studioMotionRenderRuntime(manifestContent, activeCompositionPath) {
16
+ const STUDIO_MOTION_TIMELINE_ID = "studio-motion";
17
+ const STUDIO_MOTION_ATTR = "data-hf-studio-motion";
18
+ const ORIGINAL_TRANSFORM_ATTR = "data-hf-studio-motion-original-transform";
19
+ const ORIGINAL_OPACITY_ATTR = "data-hf-studio-motion-original-opacity";
20
+ const ORIGINAL_VISIBILITY_ATTR = "data-hf-studio-motion-original-visibility";
21
+ const objectRecord = (value) => value && typeof value === "object" ? value : null;
22
+ const finiteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
23
+ const runtimeWindow = window;
24
+ const parseMotionValues = (value) => {
25
+ const record = objectRecord(value);
26
+ if (!record) return null;
27
+ const parsed = {};
28
+ for (const key of ["x", "y", "scale", "rotation", "opacity", "autoAlpha"]) {
29
+ const next = finiteNumber(record[key]);
30
+ if (next != null) parsed[key] = next;
31
+ }
32
+ return Object.keys(parsed).length > 0 ? parsed : null;
33
+ };
34
+ const parseCustomEase = (value) => {
35
+ const record = objectRecord(value);
36
+ if (!record) return null;
37
+ const id = typeof record.id === "string" ? record.id.trim() : "";
38
+ const data = typeof record.data === "string" ? record.data.trim() : "";
39
+ if (!id || !data) return null;
40
+ return { id, data };
41
+ };
42
+ const parsedManifest = (() => {
43
+ try {
44
+ return objectRecord(JSON.parse(manifestContent));
45
+ } catch {
46
+ return null;
47
+ }
48
+ })();
49
+ const manifestMotions = Array.isArray(parsedManifest?.motions) ? parsedManifest.motions : [];
50
+ const sourceFileForElement = (element) => {
51
+ let current = element;
52
+ while (current) {
53
+ const sourceFile = current.getAttribute("data-composition-file") ?? current.getAttribute("data-composition-src");
54
+ if (sourceFile) return sourceFile;
55
+ current = current.parentElement;
56
+ }
57
+ return activeCompositionPath ?? "index.html";
58
+ };
59
+ const elementMatchesSourceFile = (element, sourceFile) => sourceFileForElement(element) === sourceFile;
60
+ const isHTMLElement = (element) => element instanceof HTMLElement;
61
+ const querySelectorCandidates = (selector) => {
62
+ const className = selector.match(/^\.([A-Za-z0-9_-]+)$/)?.[1];
63
+ if (className) {
64
+ return Array.from(document.getElementsByTagName("*")).filter(
65
+ (element) => isHTMLElement(element) && element.classList.contains(className)
66
+ );
67
+ }
68
+ if (/^[A-Za-z][A-Za-z0-9-]*$/.test(selector)) {
69
+ return Array.from(document.getElementsByTagName(selector)).filter(isHTMLElement);
70
+ }
71
+ return Array.from(document.querySelectorAll(selector)).filter(isHTMLElement);
72
+ };
73
+ const resolveTarget = (targetRecord) => {
74
+ const sourceFile = typeof targetRecord.sourceFile === "string" ? targetRecord.sourceFile : "";
75
+ if (!sourceFile) return null;
76
+ const id = typeof targetRecord.id === "string" ? targetRecord.id : "";
77
+ if (id) {
78
+ const byId = document.getElementById(id);
79
+ if (isHTMLElement(byId) && elementMatchesSourceFile(byId, sourceFile)) return byId;
80
+ }
81
+ const selector = typeof targetRecord.selector === "string" ? targetRecord.selector : "";
82
+ if (!selector) return null;
83
+ try {
84
+ const selectorIndex = Math.max(0, Math.floor(finiteNumber(targetRecord.selectorIndex) ?? 0));
85
+ return querySelectorCandidates(selector).filter(
86
+ (element) => elementMatchesSourceFile(element, sourceFile)
87
+ )[selectorIndex] ?? null;
88
+ } catch {
89
+ return null;
90
+ }
91
+ };
92
+ const restoreElement = (element) => {
93
+ runtimeWindow.gsap?.set?.(element, { clearProps: "transform,opacity,visibility" });
94
+ element.style.transform = element.getAttribute(ORIGINAL_TRANSFORM_ATTR) ?? "";
95
+ element.style.opacity = element.getAttribute(ORIGINAL_OPACITY_ATTR) ?? "";
96
+ element.style.visibility = element.getAttribute(ORIGINAL_VISIBILITY_ATTR) ?? "";
97
+ element.removeAttribute(STUDIO_MOTION_ATTR);
98
+ element.removeAttribute(ORIGINAL_TRANSFORM_ATTR);
99
+ element.removeAttribute(ORIGINAL_OPACITY_ATTR);
100
+ element.removeAttribute(ORIGINAL_VISIBILITY_ATTR);
101
+ };
102
+ const restoreStudioMotionElements = () => {
103
+ for (const element of Array.from(document.querySelectorAll(`[${STUDIO_MOTION_ATTR}]`))) {
104
+ if (isHTMLElement(element)) restoreElement(element);
105
+ }
106
+ };
107
+ const readCurrentTime = () => {
108
+ try {
109
+ const playerTime = runtimeWindow.__player?.getTime?.();
110
+ if (typeof playerTime === "number" && Number.isFinite(playerTime)) {
111
+ return Math.max(0, playerTime);
112
+ }
113
+ } catch {
114
+ }
115
+ try {
116
+ const timelineTime = runtimeWindow.__timeline?.time?.();
117
+ if (typeof timelineTime === "number" && Number.isFinite(timelineTime)) {
118
+ return Math.max(0, timelineTime);
119
+ }
120
+ } catch {
121
+ }
122
+ return 0;
123
+ };
124
+ const resolveEase = (motion) => {
125
+ const fallback = typeof motion.ease === "string" && motion.ease.trim() ? motion.ease.trim() : "none";
126
+ const customEase = parseCustomEase(motion.customEase);
127
+ const customEasePlugin = runtimeWindow.CustomEase;
128
+ if (!customEase || typeof customEasePlugin?.create !== "function") return fallback;
129
+ try {
130
+ runtimeWindow.gsap?.registerPlugin?.(customEasePlugin);
131
+ customEasePlugin.create(customEase.id, customEase.data);
132
+ return customEase.id;
133
+ } catch {
134
+ return fallback;
135
+ }
136
+ };
137
+ const applyManifest = () => {
138
+ runtimeWindow.__timelines = runtimeWindow.__timelines ?? {};
139
+ runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID]?.kill?.();
140
+ delete runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID];
141
+ restoreStudioMotionElements();
142
+ const gsap = runtimeWindow.gsap;
143
+ if (!gsap?.timeline || manifestMotions.length === 0) return 0;
144
+ const timeline = gsap.timeline({ paused: true, defaults: { overwrite: "auto" } });
145
+ let applied = 0;
146
+ for (const motionValue of manifestMotions) {
147
+ const motion = objectRecord(motionValue);
148
+ if (!motion || motion.kind !== "gsap-motion") continue;
149
+ const targetRecord = objectRecord(motion.target);
150
+ if (!targetRecord) continue;
151
+ const target = resolveTarget(targetRecord);
152
+ if (!target || typeof timeline.fromTo !== "function") continue;
153
+ const start = finiteNumber(motion.start);
154
+ const duration = finiteNumber(motion.duration);
155
+ if (start == null || duration == null || start < 0 || duration <= 0) continue;
156
+ const from = parseMotionValues(motion.from);
157
+ const to = parseMotionValues(motion.to);
158
+ if (!from || !to) continue;
159
+ if (!target.hasAttribute(STUDIO_MOTION_ATTR)) {
160
+ target.setAttribute(ORIGINAL_TRANSFORM_ATTR, target.style.transform);
161
+ target.setAttribute(ORIGINAL_OPACITY_ATTR, target.style.opacity);
162
+ target.setAttribute(ORIGINAL_VISIBILITY_ATTR, target.style.visibility);
163
+ }
164
+ target.setAttribute(STUDIO_MOTION_ATTR, "true");
165
+ timeline.fromTo(
166
+ target,
167
+ from,
168
+ { ...to, duration, ease: resolveEase(motion), overwrite: "auto", immediateRender: false },
169
+ start
170
+ );
171
+ applied += 1;
172
+ }
173
+ if (applied === 0) {
174
+ timeline.kill?.();
175
+ return 0;
176
+ }
177
+ runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID] = timeline;
178
+ timeline.pause?.();
179
+ const currentTime = readCurrentTime();
180
+ if (typeof timeline.totalTime === "function") timeline.totalTime(currentTime, false);
181
+ else timeline.time?.(currentTime);
182
+ return applied;
183
+ };
184
+ runtimeWindow.__hfStudioMotionApply = applyManifest;
185
+ applyManifest();
186
+ }
187
+ export {
188
+ STUDIO_MOTION_PATH,
189
+ createStudioMotionRenderBodyScript
190
+ };
191
+ //# sourceMappingURL=studioMotionRenderScript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/helpers/studioMotionRenderScript.ts"],"sourcesContent":["export interface StudioMotionRenderScriptOptions {\n activeCompositionPath?: string | null;\n}\n\nexport const STUDIO_MOTION_PATH = \".hyperframes/studio-motion.json\";\n\nfunction hasStudioMotionEntries(manifestContent: string): boolean {\n try {\n const parsed = JSON.parse(manifestContent) as { motions?: unknown };\n return Array.isArray(parsed.motions) && parsed.motions.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Builds the render-time Studio motion runtime script, or null when no owned motion exists.\n */\nexport function createStudioMotionRenderBodyScript(\n manifestContent: string,\n options: StudioMotionRenderScriptOptions = {},\n): string | null {\n if (!manifestContent.trim() || !hasStudioMotionEntries(manifestContent)) return null;\n return `(${studioMotionRenderRuntime.toString()})(${JSON.stringify(manifestContent)}, ${JSON.stringify(options.activeCompositionPath ?? null)});`;\n}\n\nfunction studioMotionRenderRuntime(\n manifestContent: string,\n activeCompositionPath: string | null,\n): void {\n const STUDIO_MOTION_TIMELINE_ID = \"studio-motion\";\n const STUDIO_MOTION_ATTR = \"data-hf-studio-motion\";\n const ORIGINAL_TRANSFORM_ATTR = \"data-hf-studio-motion-original-transform\";\n const ORIGINAL_OPACITY_ATTR = \"data-hf-studio-motion-original-opacity\";\n const ORIGINAL_VISIBILITY_ATTR = \"data-hf-studio-motion-original-visibility\";\n\n const objectRecord = (value: unknown): Record<string, unknown> | null =>\n value && typeof value === \"object\" ? (value as Record<string, unknown>) : null;\n\n const finiteNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\n const runtimeWindow = window as Window & {\n gsap?: {\n timeline?: (vars?: Record<string, unknown>) => {\n fromTo?: (\n target: HTMLElement,\n from: Record<string, unknown>,\n to: Record<string, unknown>,\n at: number,\n ) => unknown;\n totalTime?: (time: number, suppressEvents?: boolean) => unknown;\n time?: (time: number) => unknown;\n pause?: () => unknown;\n kill?: () => unknown;\n };\n set?: (target: HTMLElement, vars: Record<string, unknown>) => unknown;\n registerPlugin?: (...plugins: unknown[]) => unknown;\n };\n CustomEase?: { create?: (id: string, data: string) => unknown };\n __player?: { getTime?: () => number };\n __timeline?: { time?: () => number };\n __timelines?: Record<\n string,\n | {\n kill?: () => unknown;\n }\n | undefined\n >;\n __hfStudioMotionApply?: () => number;\n };\n\n const parseMotionValues = (value: unknown): Record<string, number> | null => {\n const record = objectRecord(value);\n if (!record) return null;\n const parsed: Record<string, number> = {};\n for (const key of [\"x\", \"y\", \"scale\", \"rotation\", \"opacity\", \"autoAlpha\"]) {\n const next = finiteNumber(record[key]);\n if (next != null) parsed[key] = next;\n }\n return Object.keys(parsed).length > 0 ? parsed : null;\n };\n\n const parseCustomEase = (value: unknown): { id: string; data: string } | null => {\n const record = objectRecord(value);\n if (!record) return null;\n const id = typeof record.id === \"string\" ? record.id.trim() : \"\";\n const data = typeof record.data === \"string\" ? record.data.trim() : \"\";\n if (!id || !data) return null;\n return { id, data };\n };\n\n const parsedManifest = (() => {\n try {\n return objectRecord(JSON.parse(manifestContent));\n } catch {\n return null;\n }\n })();\n const manifestMotions = Array.isArray(parsedManifest?.motions) ? parsedManifest.motions : [];\n\n const sourceFileForElement = (element: HTMLElement): string => {\n let current: HTMLElement | null = element;\n while (current) {\n const sourceFile =\n current.getAttribute(\"data-composition-file\") ??\n current.getAttribute(\"data-composition-src\");\n if (sourceFile) return sourceFile;\n current = current.parentElement;\n }\n return activeCompositionPath ?? \"index.html\";\n };\n\n const elementMatchesSourceFile = (element: HTMLElement, sourceFile: string): boolean =>\n sourceFileForElement(element) === sourceFile;\n\n const isHTMLElement = (element: Element | null): element is HTMLElement =>\n element instanceof HTMLElement;\n\n const querySelectorCandidates = (selector: string): HTMLElement[] => {\n const className = selector.match(/^\\.([A-Za-z0-9_-]+)$/)?.[1];\n if (className) {\n return Array.from(document.getElementsByTagName(\"*\")).filter(\n (element): element is HTMLElement =>\n isHTMLElement(element) && element.classList.contains(className),\n );\n }\n if (/^[A-Za-z][A-Za-z0-9-]*$/.test(selector)) {\n return Array.from(document.getElementsByTagName(selector)).filter(isHTMLElement);\n }\n return Array.from(document.querySelectorAll(selector)).filter(isHTMLElement);\n };\n\n const resolveTarget = (targetRecord: Record<string, unknown>): HTMLElement | null => {\n const sourceFile = typeof targetRecord.sourceFile === \"string\" ? targetRecord.sourceFile : \"\";\n if (!sourceFile) return null;\n const id = typeof targetRecord.id === \"string\" ? targetRecord.id : \"\";\n if (id) {\n const byId = document.getElementById(id);\n if (isHTMLElement(byId) && elementMatchesSourceFile(byId, sourceFile)) return byId;\n }\n const selector = typeof targetRecord.selector === \"string\" ? targetRecord.selector : \"\";\n if (!selector) return null;\n try {\n const selectorIndex = Math.max(0, Math.floor(finiteNumber(targetRecord.selectorIndex) ?? 0));\n return (\n querySelectorCandidates(selector).filter((element) =>\n elementMatchesSourceFile(element, sourceFile),\n )[selectorIndex] ?? null\n );\n } catch {\n return null;\n }\n };\n\n const restoreElement = (element: HTMLElement): void => {\n runtimeWindow.gsap?.set?.(element, { clearProps: \"transform,opacity,visibility\" });\n element.style.transform = element.getAttribute(ORIGINAL_TRANSFORM_ATTR) ?? \"\";\n element.style.opacity = element.getAttribute(ORIGINAL_OPACITY_ATTR) ?? \"\";\n element.style.visibility = element.getAttribute(ORIGINAL_VISIBILITY_ATTR) ?? \"\";\n element.removeAttribute(STUDIO_MOTION_ATTR);\n element.removeAttribute(ORIGINAL_TRANSFORM_ATTR);\n element.removeAttribute(ORIGINAL_OPACITY_ATTR);\n element.removeAttribute(ORIGINAL_VISIBILITY_ATTR);\n };\n\n const restoreStudioMotionElements = (): void => {\n for (const element of Array.from(document.querySelectorAll(`[${STUDIO_MOTION_ATTR}]`))) {\n if (isHTMLElement(element)) restoreElement(element);\n }\n };\n\n const readCurrentTime = (): number => {\n try {\n const playerTime = runtimeWindow.__player?.getTime?.();\n if (typeof playerTime === \"number\" && Number.isFinite(playerTime)) {\n return Math.max(0, playerTime);\n }\n } catch {\n // fall through\n }\n try {\n const timelineTime = runtimeWindow.__timeline?.time?.();\n if (typeof timelineTime === \"number\" && Number.isFinite(timelineTime)) {\n return Math.max(0, timelineTime);\n }\n } catch {\n // fall through\n }\n return 0;\n };\n\n const resolveEase = (motion: Record<string, unknown>): string => {\n const fallback =\n typeof motion.ease === \"string\" && motion.ease.trim() ? motion.ease.trim() : \"none\";\n const customEase = parseCustomEase(motion.customEase);\n const customEasePlugin = runtimeWindow.CustomEase;\n if (!customEase || typeof customEasePlugin?.create !== \"function\") return fallback;\n try {\n runtimeWindow.gsap?.registerPlugin?.(customEasePlugin);\n customEasePlugin.create(customEase.id, customEase.data);\n return customEase.id;\n } catch {\n return fallback;\n }\n };\n\n const applyManifest = (): number => {\n runtimeWindow.__timelines = runtimeWindow.__timelines ?? {};\n runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID]?.kill?.();\n delete runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID];\n restoreStudioMotionElements();\n const gsap = runtimeWindow.gsap;\n if (!gsap?.timeline || manifestMotions.length === 0) return 0;\n\n const timeline = gsap.timeline({ paused: true, defaults: { overwrite: \"auto\" } });\n let applied = 0;\n for (const motionValue of manifestMotions) {\n const motion = objectRecord(motionValue);\n if (!motion || motion.kind !== \"gsap-motion\") continue;\n const targetRecord = objectRecord(motion.target);\n if (!targetRecord) continue;\n const target = resolveTarget(targetRecord);\n if (!target || typeof timeline.fromTo !== \"function\") continue;\n const start = finiteNumber(motion.start);\n const duration = finiteNumber(motion.duration);\n if (start == null || duration == null || start < 0 || duration <= 0) continue;\n const from = parseMotionValues(motion.from);\n const to = parseMotionValues(motion.to);\n if (!from || !to) continue;\n if (!target.hasAttribute(STUDIO_MOTION_ATTR)) {\n target.setAttribute(ORIGINAL_TRANSFORM_ATTR, target.style.transform);\n target.setAttribute(ORIGINAL_OPACITY_ATTR, target.style.opacity);\n target.setAttribute(ORIGINAL_VISIBILITY_ATTR, target.style.visibility);\n }\n target.setAttribute(STUDIO_MOTION_ATTR, \"true\");\n timeline.fromTo(\n target,\n from,\n { ...to, duration, ease: resolveEase(motion), overwrite: \"auto\", immediateRender: false },\n start,\n );\n applied += 1;\n }\n\n if (applied === 0) {\n timeline.kill?.();\n return 0;\n }\n runtimeWindow.__timelines[STUDIO_MOTION_TIMELINE_ID] = timeline;\n timeline.pause?.();\n const currentTime = readCurrentTime();\n if (typeof timeline.totalTime === \"function\") timeline.totalTime(currentTime, false);\n else timeline.time?.(currentTime);\n return applied;\n };\n\n runtimeWindow.__hfStudioMotionApply = applyManifest;\n applyManifest();\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,167 @@
1
+ import { Hono } from 'hono';
2
+ import * as _hyperframes_core from '@hyperframes/core';
3
+ import { RegistryItem } from '@hyperframes/core';
4
+ export { isSafePath } from '@hyperframes/core';
5
+ import { CanvasResolution } from '@hyperframes/parsers';
6
+ export { ScreenshotClip, getElementScreenshotClip } from './helpers/screenshotClip.js';
7
+ export { STUDIO_MANUAL_EDITS_PATH, StudioManualEditsRenderScriptOptions, createStudioManualEditsRenderBodyScript, createStudioPositionSeekReapplyScript } from './helpers/manualEditsRenderScript.js';
8
+ export { STUDIO_MOTION_PATH, StudioMotionRenderScriptOptions, createStudioMotionRenderBodyScript } from './helpers/studioMotionRenderScript.js';
9
+
10
+ /** Resolved info about a single project. */
11
+ interface ResolvedProject {
12
+ id: string;
13
+ dir: string;
14
+ title?: string;
15
+ sessionId?: string;
16
+ }
17
+ /** Observable render job state, polled by the SSE progress handler. */
18
+ interface RenderJobState {
19
+ id: string;
20
+ status: "rendering" | "complete" | "failed";
21
+ progress: number;
22
+ stage?: string;
23
+ outputPath: string;
24
+ error?: string;
25
+ }
26
+ /** Lint result from the core linter. */
27
+ interface LintResult {
28
+ findings: Array<{
29
+ severity: string;
30
+ message: string;
31
+ file?: string;
32
+ fixHint?: string;
33
+ }>;
34
+ }
35
+ /**
36
+ * Adapter interface — injected by each consumer to handle host-specific behavior.
37
+ * The shared API module calls these methods; each host (vite dev, CLI embedded)
38
+ * provides its own implementation.
39
+ */
40
+ interface StudioApiAdapter {
41
+ /** List all available projects. */
42
+ listProjects(): Promise<ResolvedProject[]> | ResolvedProject[];
43
+ /** Resolve a project ID (or session ID) to its directory. Returns null if not found. */
44
+ resolveProject(id: string): Promise<ResolvedProject | null> | ResolvedProject | null;
45
+ /** Bundle a project directory into a single HTML string. Returns null if unavailable. */
46
+ bundle(projectDir: string): Promise<string | null>;
47
+ /** Optional: cached signature for project files that should invalidate preview frame caches. */
48
+ getProjectSignature?: (projectDir: string) => string;
49
+ /** Lint a single HTML string. */
50
+ lint(html: string, opts?: {
51
+ filePath?: string;
52
+ }): Promise<LintResult> | LintResult;
53
+ /** URL to the hyperframe runtime JS (injected into preview HTML). */
54
+ runtimeUrl: string;
55
+ /**
56
+ * Optional: post-process preview HTML before Studio augments it.
57
+ * Useful when preview must mirror render-time compilation steps.
58
+ */
59
+ transformPreviewHtml?: (opts: {
60
+ html: string;
61
+ project: ResolvedProject;
62
+ activeCompositionPath: string;
63
+ }) => Promise<string> | string;
64
+ /** Directory where render output files are stored. */
65
+ rendersDir(project: ResolvedProject): string;
66
+ /**
67
+ * Start a render job. The adapter owns the async execution and must
68
+ * update the returned RenderJobState object reactively.
69
+ */
70
+ startRender(opts: {
71
+ project: ResolvedProject;
72
+ outputPath: string;
73
+ format: "mp4" | "webm" | "mov";
74
+ /**
75
+ * Frame rate as an exact rational. The HTTP layer (POST
76
+ * `/projects/:id/render`) accepts either a JSON number (integer fps,
77
+ * `30`) or a JSON string (ffmpeg-style rational, `"30000/1001"`); the
78
+ * route normalizes both into `Fps` before invoking the adapter, so
79
+ * adapter implementations only ever see the rational form.
80
+ */
81
+ fps: _hyperframes_core.Fps;
82
+ quality: string;
83
+ jobId: string;
84
+ /**
85
+ * Optional output resolution preset. See `resolveDeviceScaleFactor` in
86
+ * the producer for the integer-scale + aspect + HDR constraints.
87
+ */
88
+ outputResolution?: CanvasResolution;
89
+ /** Entry file relative to projectDir (e.g. "compositions/intro.html"). Defaults to index.html. */
90
+ composition?: string;
91
+ /**
92
+ * Telemetry id of the browser user who triggered the render. Lets the
93
+ * adapter attribute the server-emitted render_complete/render_error to
94
+ * that user so the studio render funnel is joinable. Undefined for older
95
+ * clients → falls back to the install's anonymous id.
96
+ */
97
+ distinctId?: string;
98
+ }): RenderJobState;
99
+ /** Optional: generate a JPEG thumbnail via Puppeteer or similar. */
100
+ generateThumbnail?: (opts: {
101
+ project: ResolvedProject;
102
+ compPath: string;
103
+ seekTime: number;
104
+ width: number;
105
+ height: number;
106
+ previewUrl: string;
107
+ selector?: string;
108
+ format?: "jpeg" | "png";
109
+ selectorIndex?: number;
110
+ }) => Promise<Buffer | null>;
111
+ /** Optional: resolve session ID to project (multi-project mode). */
112
+ resolveSession?: (sessionId: string) => Promise<{
113
+ projectId: string;
114
+ title: string;
115
+ } | null>;
116
+ /** Optional: list all registry items (blocks + components) for the catalog. */
117
+ listRegistryCatalog?(): Promise<RegistryItem[]>;
118
+ /** Optional: install a registry item into a project directory. */
119
+ installRegistryBlock?(opts: {
120
+ project: ResolvedProject;
121
+ blockName: string;
122
+ }): Promise<{
123
+ written: string[];
124
+ block: RegistryItem;
125
+ }>;
126
+ }
127
+
128
+ /**
129
+ * Create a Hono sub-app with all studio API routes.
130
+ *
131
+ * Both the vite dev server and CLI embedded server mount this app
132
+ * under /api, each providing their own adapter for host-specific behavior.
133
+ */
134
+ declare function createStudioApi(adapter: StudioApiAdapter): Hono;
135
+
136
+ /**
137
+ * Creates a stable preview cache-busting signature for project source plus Studio manifests.
138
+ */
139
+ declare function createProjectSignature(projectDir: string): string;
140
+
141
+ /** Recursively walk a directory and return relative file paths. */
142
+ declare function walkDir(dir: string, prefix?: string): string[];
143
+
144
+ declare const MIME_TYPES: Record<string, string>;
145
+ declare function getMimeType(path: string): string;
146
+
147
+ /**
148
+ * Build a standalone HTML page for a sub-composition.
149
+ *
150
+ * Uses the project's own index.html `<head>` so all dependencies (GSAP, fonts,
151
+ * Lottie, reset styles, runtime) are preserved — instead of building a minimal
152
+ * page from scratch that would miss important scripts/styles.
153
+ *
154
+ * Three dispatch modes, tried in order:
155
+ * 1. `<template>` wrapper → extract template content (existing compositions)
156
+ * 2. Full HTML document → parse and extract head/body separately (registry blocks)
157
+ * 3. Raw fragment → wrap in a minimal document
158
+ *
159
+ * For full-doc mode, the composition's own `<head>` content (styles, scripts,
160
+ * links, meta) is appended AFTER the project's index.html head. When both
161
+ * declare the same dependency (e.g. GSAP CDN), the composition's copy wins
162
+ * by last-write-wins script execution order — this is intentional so the
163
+ * composition can pin a specific version.
164
+ */
165
+ declare function buildSubCompositionHtml(projectDir: string, compPath: string, runtimeUrl: string, baseHref?: string): string | null;
166
+
167
+ export { type LintResult, MIME_TYPES, type RenderJobState, type ResolvedProject, type StudioApiAdapter, buildSubCompositionHtml, createProjectSignature, createStudioApi, getMimeType, walkDir };