@vosjs/elements 0.2.0 → 0.2.1

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/bundle.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED by bundle.mjs — injectable IIFE defining globalThis.__vosElementsFactory
2
- export const elementsBundleCode = "\"use strict\";\nvar __vosElementsFactory = (() => {\n var __defProp = Object.defineProperty;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\n var __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n };\n var __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n };\n var __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== \"symbol\" ? key + \"\" : key, value);\n\n // src/index.ts\n var index_exports = {};\n __export(index_exports, {\n createVosElements: () => createVosElements,\n renderElements: () => renderElements\n });\n\n // src/assetCache.ts\n var AssetCache = {\n images: /* @__PURE__ */ new Map(),\n videos: /* @__PURE__ */ new Map(),\n svgContents: /* @__PURE__ */ new Map(),\n getImage(url) {\n return this.images.get(url) || null;\n },\n getVideo(url) {\n return this.videos.get(url) || null;\n },\n getSVG(url) {\n return this.svgContents.get(url) || null;\n },\n dispose() {\n this.videos.forEach((cached) => {\n cached.element.pause();\n cached.element.src = \"\";\n cached.element.load();\n if (cached.blobUrl) {\n URL.revokeObjectURL(cached.blobUrl);\n }\n });\n this.images.clear();\n this.videos.clear();\n this.svgContents.clear();\n }\n };\n function extractAssetUrls(elementsConfig) {\n const assets = {\n images: [],\n videos: [],\n svgs: []\n };\n for (const el of elementsConfig) {\n if (el.type === \"image\" && el.src) {\n assets.images.push(el.src);\n } else if (el.type === \"video\" && el.src) {\n assets.videos.push(el.src);\n } else if (el.type === \"svg\" && el.src) {\n assets.svgs.push(el.src);\n }\n }\n assets.images = [...new Set(assets.images)];\n assets.videos = [...new Set(assets.videos)];\n assets.svgs = [...new Set(assets.svgs)];\n return assets;\n }\n async function preloadImage(url) {\n if (AssetCache.images.has(url)) return;\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise((resolve) => {\n img.onload = () => resolve();\n img.onerror = () => {\n console.warn(\"Failed to preload image:\", url);\n resolve();\n };\n img.src = url;\n });\n AssetCache.images.set(url, {\n element: img,\n width: img.naturalWidth,\n height: img.naturalHeight\n });\n }\n async function preloadVideo(url) {\n if (AssetCache.videos.has(url)) return;\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\"Failed to fetch video: \" + response.status);\n }\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.preload = \"auto\";\n await new Promise((resolve, reject) => {\n video.oncanplaythrough = () => resolve();\n video.onerror = reject;\n video.src = blobUrl;\n video.load();\n });\n video.currentTime = 0;\n await new Promise((resolve) => {\n video.onseeked = () => resolve();\n setTimeout(() => resolve(), 100);\n });\n video.pause();\n console.log(\"Video preloaded:\", url, \"duration:\", video.duration);\n AssetCache.videos.set(url, {\n element: video,\n blobUrl,\n duration: video.duration,\n width: video.videoWidth,\n height: video.videoHeight\n });\n } catch (e) {\n console.warn(\"Failed to preload video:\", url, e);\n }\n }\n async function preloadSVG(url) {\n if (AssetCache.svgContents.has(url)) return;\n try {\n if (url.startsWith(\"<svg\") || url.startsWith(\"<?xml\")) {\n AssetCache.svgContents.set(url, url);\n return;\n }\n if (url.startsWith(\"data:image/svg\")) {\n const base64 = url.split(\",\")[1];\n const svgContent2 = atob(base64);\n AssetCache.svgContents.set(url, svgContent2);\n return;\n }\n const response = await fetch(url);\n const svgContent = await response.text();\n AssetCache.svgContents.set(url, svgContent);\n } catch (e) {\n console.warn(\"Failed to preload SVG:\", url, e);\n AssetCache.svgContents.set(url, \"\");\n }\n }\n async function preloadAssets(elementsConfig) {\n const assets = extractAssetUrls(elementsConfig);\n const total = assets.images.length + assets.videos.length + assets.svgs.length;\n if (total === 0) return;\n let loaded = 0;\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: \"PRELOAD_PROGRESS\", loaded: 0, total },\n \"*\"\n );\n }\n const preloadWithProgress = async (fn, url) => {\n await fn(url);\n loaded++;\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: \"PRELOAD_PROGRESS\", loaded, total },\n \"*\"\n );\n }\n };\n const promises = [\n ...assets.images.map((url) => preloadWithProgress(preloadImage, url)),\n ...assets.videos.map((url) => preloadWithProgress(preloadVideo, url)),\n ...assets.svgs.map((url) => preloadWithProgress(preloadSVG, url))\n ];\n await Promise.all(promises);\n }\n\n // src/createElementProps.ts\n function createElementProps(_THREE, mesh, initialX, initialY, initialOpacity = 1, videoElement = null, videoSource = null, videoTexture = null) {\n const baseScaleX = mesh.scale.x;\n const baseScaleY = mesh.scale.y;\n const state = {\n x: initialX,\n y: initialY,\n z: 0,\n opacity: initialOpacity,\n scale: 1,\n scaleX: 1,\n scaleY: 1,\n rotation: 0,\n rotationX: 0,\n rotationY: 0,\n zIndex: mesh.userData.zIndex ?? 0,\n // Video-specific properties (only meaningful if videoElement is provided)\n currentTime: videoElement ? videoElement.currentTime : 0,\n playing: false,\n startOffset: 0\n };\n const updateMeshPosition = () => {\n mesh.position.x = state.x;\n mesh.position.y = -state.y;\n mesh.position.z = state.z;\n };\n const updateMeshTransform = () => {\n mesh.scale.set(\n baseScaleX * state.scale * state.scaleX,\n baseScaleY * state.scale * state.scaleY,\n 1\n );\n mesh.rotation.set(\n state.rotationX * Math.PI / 180,\n state.rotationY * Math.PI / 180,\n state.rotation * Math.PI / 180\n );\n };\n const updateMeshOpacity = () => {\n const mat = mesh.material;\n mat.opacity = state.opacity;\n mat.needsUpdate = true;\n };\n const updateVideoPlayback = () => {\n if (!videoElement) return;\n const vos = window.__vos__;\n const shouldPlay = state.playing && !vos?.isPaused;\n if (shouldPlay) {\n if (videoElement.paused) {\n const timeDiff = Math.abs(videoElement.currentTime - state.currentTime);\n if (timeDiff > 0.5) {\n videoElement.currentTime = state.currentTime;\n }\n videoElement.play().catch(() => {\n });\n }\n } else {\n videoElement.pause();\n if (Math.abs(videoElement.currentTime - state.currentTime) > 0.05) {\n videoElement.currentTime = state.currentTime;\n }\n }\n };\n if (videoElement) {\n const vos = window.__vos__;\n if (vos?.videoCallbacks) {\n vos.videoCallbacks.add(updateVideoPlayback);\n }\n }\n const updateVideoCurrentTime = () => {\n if (videoSource) {\n const vos2 = window.__vos__;\n const p = videoSource.seekTo(state.currentTime).then(() => {\n if (videoTexture) videoTexture.needsUpdate = true;\n }).catch((e) => console.error(\"[vos] frame decode failed\", e));\n vos2?.registerDecode?.(p);\n return;\n }\n if (!videoElement) return;\n const vos = window.__vos__;\n if (!state.playing || vos?.isPaused) {\n videoElement.currentTime = state.currentTime;\n }\n };\n return new Proxy(state, {\n set(target, prop, value) {\n target[prop] = value;\n switch (prop) {\n case \"x\":\n case \"y\":\n case \"z\":\n updateMeshPosition();\n break;\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotation\":\n case \"rotationX\":\n case \"rotationY\":\n updateMeshTransform();\n break;\n case \"opacity\":\n updateMeshOpacity();\n break;\n case \"zIndex\":\n mesh.renderOrder = value;\n mesh.userData.zIndex = value;\n break;\n case \"currentTime\":\n updateVideoCurrentTime();\n break;\n case \"playing\":\n case \"startOffset\":\n updateVideoPlayback();\n break;\n }\n return true;\n },\n get(target, prop) {\n if (prop === \"duration\" && videoElement) {\n return videoElement.duration;\n }\n return target[prop];\n }\n });\n }\n\n // src/renderers/image.ts\n async function renderImageElement(element, _resolution, THREE) {\n const { src, size = {} } = element;\n let img;\n const cached = AssetCache.getImage(src);\n if (cached) {\n img = cached.element;\n } else {\n img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = src;\n });\n }\n let width = size.width ?? img.naturalWidth;\n let height = size.height ?? img.naturalHeight;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n const targetHeight = typeof size.height === \"number\" ? size.height : img.naturalHeight;\n width = img.naturalWidth / img.naturalHeight * targetHeight;\n height = targetHeight;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n const targetWidth = typeof size.width === \"number\" ? size.width : img.naturalWidth;\n height = img.naturalHeight / img.naturalWidth * targetWidth;\n width = targetWidth;\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n const fit = size.fit ?? \"fill\";\n if (fit === \"contain\" || fit === \"cover\") {\n const scale = fit === \"contain\" ? Math.min(width / img.naturalWidth, height / img.naturalHeight) : Math.max(width / img.naturalWidth, height / img.naturalHeight);\n const drawWidth = img.naturalWidth * scale;\n const drawHeight = img.naturalHeight * scale;\n const offsetX = (width - drawWidth) / 2;\n const offsetY = (height - drawHeight) / 2;\n ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);\n } else {\n ctx.drawImage(img, 0, 0, width, height);\n }\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(width, height);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, width, height };\n }\n\n // src/renderers/svg.ts\n async function renderSVGElement(element, _resolution, THREE) {\n const { src, size = {}, colors = {} } = element;\n let svgContent = AssetCache.getSVG(src);\n if (!svgContent) {\n if (src.startsWith(\"http\") || src.startsWith(\"/\")) {\n const response = await fetch(src);\n svgContent = await response.text();\n } else {\n svgContent = src;\n }\n }\n Object.entries(colors).forEach(([selector, color]) => {\n const regex = new RegExp(`(${selector}[^>]*)(fill|stroke)=\"[^\"]*\"`, \"g\");\n svgContent = svgContent.replace(regex, `$1$2=\"${color}\"`);\n });\n const parser = new DOMParser();\n const svgDoc = parser.parseFromString(svgContent, \"image/svg+xml\");\n const svgElement = svgDoc.documentElement;\n const viewBox = svgElement.getAttribute(\"viewBox\");\n let svgWidth = parseFloat(svgElement.getAttribute(\"width\") || \"\") || 100;\n let svgHeight = parseFloat(svgElement.getAttribute(\"height\") || \"\") || 100;\n if (viewBox) {\n const [, , vbW, vbH] = viewBox.split(\" \").map(Number);\n svgWidth = vbW || svgWidth;\n svgHeight = vbH || svgHeight;\n }\n let width = size.width ?? svgWidth;\n let height = size.height ?? svgHeight;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n width = svgWidth / svgHeight * size.height;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n height = svgHeight / svgWidth * size.width;\n }\n svgElement.setAttribute(\"width\", String(width));\n svgElement.setAttribute(\"height\", String(height));\n const updatedSvg = new XMLSerializer().serializeToString(svgElement);\n const blob = new Blob([updatedSvg], { type: \"image/svg+xml\" });\n const url = URL.createObjectURL(blob);\n const img = new Image();\n await new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = url;\n });\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n ctx.drawImage(img, 0, 0, width, height);\n URL.revokeObjectURL(url);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(width, height);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, width, height };\n }\n\n // src/renderers/text.ts\n function renderTextElement(element, _resolution, THREE) {\n const { content, font = {} } = element;\n const fontSize = font.size ?? 24;\n const fontFamily = font.family ?? \"Inter, system-ui, sans-serif\";\n const fontWeight = font.weight ?? \"normal\";\n const fontStyle = font.style ?? \"normal\";\n const color = font.color ?? \"#ffffff\";\n const align = font.align ?? \"left\";\n const letterSpacing = font.letterSpacing ?? 0;\n const lineHeight = font.lineHeight ?? 1.2;\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.font = fontString;\n const lines = content.split(\"\\n\");\n let maxWidth = 0;\n lines.forEach((line) => {\n let w = 0;\n for (let i = 0; i < line.length; i++) {\n w += ctx.measureText(line[i]).width;\n if (i < line.length - 1) w += letterSpacing;\n }\n if (w > maxWidth) maxWidth = w;\n });\n const totalHeight = lines.length * fontSize * lineHeight;\n const padding = Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 10;\n const canvasWidth = Math.ceil(maxWidth + padding * 2);\n const canvasHeight = Math.ceil(totalHeight + padding * 2);\n canvas.width = canvasWidth;\n canvas.height = canvasHeight;\n ctx.font = fontString;\n ctx.textBaseline = \"top\";\n ctx.textAlign = align;\n ctx.clearRect(0, 0, canvasWidth, canvasHeight);\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color;\n ctx.shadowBlur = element.shadow.blur;\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0;\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0;\n }\n let textX = padding;\n if (align === \"center\") textX = canvasWidth / 2;\n else if (align === \"right\") textX = canvasWidth - padding;\n lines.forEach((line, i) => {\n const y = padding + i * fontSize * lineHeight;\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color;\n ctx.lineWidth = element.stroke.width;\n ctx.lineJoin = \"round\";\n ctx.strokeText(line, textX, y);\n }\n ctx.fillStyle = color;\n ctx.fillText(line, textX, y);\n });\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, canvas, width: canvasWidth, height: canvasHeight };\n }\n function renderTextSegment(text, font, element, THREE) {\n const fontSize = font.size ?? 24;\n const fontFamily = font.family ?? \"Inter, system-ui, sans-serif\";\n const fontWeight = font.weight ?? \"normal\";\n const fontStyle = font.style ?? \"normal\";\n const color = font.color ?? \"#ffffff\";\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.font = fontString;\n const metrics = ctx.measureText(text);\n const padding = Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 4;\n const canvasWidth = Math.ceil(metrics.width + padding * 2);\n const canvasHeight = Math.ceil(fontSize * 1.4 + padding * 2);\n canvas.width = canvasWidth;\n canvas.height = canvasHeight;\n ctx.font = fontString;\n ctx.textBaseline = \"top\";\n ctx.textAlign = \"left\";\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color;\n ctx.shadowBlur = element.shadow.blur;\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0;\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0;\n }\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color;\n ctx.lineWidth = element.stroke.width;\n ctx.lineJoin = \"round\";\n ctx.strokeText(text, padding, padding);\n }\n ctx.fillStyle = color;\n ctx.fillText(text, padding, padding);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight);\n const mesh = new THREE.Mesh(geometry, material);\n return {\n mesh,\n width: canvasWidth,\n height: canvasHeight,\n textWidth: metrics.width\n };\n }\n function renderSplitTextElement(element, _resolution, THREE) {\n const { content, font = {}, split } = element;\n const splitType = split?.type ?? \"chars\";\n const letterSpacing = font.letterSpacing ?? 0;\n let segments;\n if (splitType === \"chars\") {\n segments = content.split(\"\");\n } else if (splitType === \"words\") {\n segments = content.split(/\\s+/);\n } else {\n segments = content.split(\"\\n\");\n }\n const meshes = [];\n let totalWidth = 0;\n segments.forEach((text, i) => {\n if (text.length === 0) return;\n const result = renderTextSegment(text, font, element, THREE);\n meshes.push({\n mesh: result.mesh,\n width: result.width,\n height: result.height,\n textWidth: result.textWidth,\n text\n });\n totalWidth += result.textWidth;\n if (i < segments.length - 1) {\n totalWidth += splitType === \"words\" ? (font.size ?? 24) * 0.4 : letterSpacing;\n }\n });\n const totalHeight = meshes[0]?.height ?? 0;\n let currentX = -totalWidth / 2;\n meshes.forEach((item, i) => {\n item.offsetX = currentX + item.textWidth / 2;\n item.offsetY = 0;\n currentX += item.textWidth;\n if (i < meshes.length - 1) {\n currentX += splitType === \"words\" ? (font.size ?? 24) * 0.4 : letterSpacing;\n }\n });\n return { meshes, totalWidth, totalHeight };\n }\n\n // src/video/sampleIndex.ts\n function buildSyncIndices(samples) {\n const out = [];\n for (let i = 0; i < samples.length; i++) if (samples[i].isSync) out.push(i);\n return out;\n }\n function targetDecodeIndex(samples, tSec) {\n let di = 0;\n let best = -Infinity;\n for (let i = 0; i < samples.length; i++) {\n const c = samples[i].cts;\n if (c <= tSec && c > best) {\n best = c;\n di = i;\n }\n }\n return di;\n }\n function gopBounds(syncIndices, totalSamples, targetDI) {\n let ki = 0;\n for (const si of syncIndices) {\n if (si <= targetDI) ki = si;\n else break;\n }\n let ni = totalSamples;\n for (const si of syncIndices) {\n if (si > targetDI) {\n ni = si;\n break;\n }\n }\n return [ki, ni];\n }\n\n // src/video/FrameAccurateVideoSource.ts\n var MP4BOX_URL = \"https://esm.sh/mp4box@0.5.2\";\n var mp4boxPromise = null;\n function loadMp4Box() {\n if (!mp4boxPromise) {\n mp4boxPromise = import(\n /* @vite-ignore */\n MP4BOX_URL\n ).then((m) => m.default ?? m);\n }\n return mp4boxPromise;\n }\n function isFrameAccurateSupported() {\n return typeof VideoDecoder !== \"undefined\" && typeof EncodedVideoChunk !== \"undefined\";\n }\n var FrameAccurateVideoSource = class _FrameAccurateVideoSource {\n constructor() {\n __publicField(this, \"canvas\");\n __publicField(this, \"ctx\");\n __publicField(this, \"decoder\");\n __publicField(this, \"samples\", []);\n // DECODE order (as delivered by mp4box)\n __publicField(this, \"syncIndices\", []);\n __publicField(this, \"onFrame\", null);\n __publicField(this, \"lastError\", null);\n __publicField(this, \"durationSec\", 0);\n __publicField(this, \"fps\", 30);\n __publicField(this, \"codedWidth\", 0);\n __publicField(this, \"codedHeight\", 0);\n this.canvas = document.createElement(\"canvas\");\n this.ctx = this.canvas.getContext(\"2d\");\n }\n static async create(src) {\n const self = new _FrameAccurateVideoSource();\n self.decoder = new VideoDecoder({\n output: (f) => self.onFrame?.(f),\n error: (e) => {\n self.lastError = e;\n console.error(\"[vos] VideoDecoder error\", e);\n }\n });\n const buf = await fetch(src).then((r) => {\n if (!r.ok) throw new Error(`[vos] failed to fetch video: ${src} (${r.status})`);\n return r.arrayBuffer();\n });\n await self.demux(buf);\n self.canvas.width = self.codedWidth;\n self.canvas.height = self.codedHeight;\n await self.seekTo(0);\n return self;\n }\n async demux(data) {\n const MP4Box = await loadMp4Box();\n return new Promise((resolve, reject) => {\n const file = MP4Box.createFile();\n file.onError = (e) => reject(new Error(String(e)));\n file.onReady = (info) => {\n const track = info.videoTracks?.[0];\n if (!track) return reject(new Error(\"[vos] no video track in mp4\"));\n this.codedWidth = track.video.width;\n this.codedHeight = track.video.height;\n this.durationSec = info.duration / info.timescale;\n this.fps = track.nb_samples / this.durationSec || 30;\n this.decoder.configure({\n codec: track.codec,\n description: this.getDescription(MP4Box, file, track.id),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight\n });\n file.setExtractionOptions(track.id, null, { nbSamples: Infinity });\n file.start();\n };\n file.onSamples = (_id, _user, arr) => {\n for (const s of arr) {\n this.samples.push({\n cts: s.cts / s.timescale,\n isSync: !!s.is_sync,\n chunk: new EncodedVideoChunk({\n type: s.is_sync ? \"key\" : \"delta\",\n timestamp: Math.round(s.cts / s.timescale * 1e6),\n duration: Math.round(s.duration / s.timescale * 1e6),\n data: s.data\n })\n });\n }\n };\n data.fileStart = 0;\n file.appendBuffer(data);\n file.flush();\n queueMicrotask(() => {\n if (!this.samples.length) return reject(new Error(\"[vos] no samples extracted\"));\n this.syncIndices = buildSyncIndices(this.samples);\n resolve();\n });\n });\n }\n getDescription(MP4Box, file, trackId) {\n const trak = file.getTrackById(trackId);\n for (const entry of trak.mdia.minf.stbl.stsd.entries) {\n const box = entry.avcC ?? entry.hvcC ?? entry.vpcC ?? entry.av1C;\n if (box) {\n const stream = new MP4Box.DataStream(void 0, 0, MP4Box.DataStream.BIG_ENDIAN);\n box.write(stream);\n return new Uint8Array(stream.buffer, 8);\n }\n }\n throw new Error(\"[vos] no codec description (avcC/hvcC/vpcC/av1C) found\");\n }\n /**\n * Decode the exact frame at presentation time `t` and draw it to `canvas`.\n * Decodes the whole GOP (keyframe → next keyframe) in decode order, then\n * selects the output by PTS. Returns when the canvas is updated.\n */\n async seekTo(tSec) {\n if (!this.samples.length) return;\n const targetDI = targetDecodeIndex(this.samples, tSec);\n const [ki, ni] = gopBounds(this.syncIndices, this.samples.length, targetDI);\n const targetTs = Math.round(this.samples[targetDI].cts * 1e6);\n this.lastError = null;\n const collected = [];\n this.onFrame = (f) => collected.push(f);\n for (let i = ki; i < ni; i++) this.decoder.decode(this.samples[i].chunk);\n await this.decoder.flush();\n this.onFrame = null;\n if (this.lastError) throw this.lastError;\n let best;\n let bestD = Infinity;\n for (const f of collected) {\n const d = Math.abs(f.timestamp - targetTs);\n if (d < bestD) {\n bestD = d;\n best = f;\n }\n }\n if (best) this.ctx.drawImage(best, 0, 0);\n for (const f of collected) f.close();\n }\n dispose() {\n try {\n this.decoder.close();\n } catch {\n }\n }\n };\n\n // src/renderers/video.ts\n function resolveSize(size, intrinsicW, intrinsicH) {\n let width = size.width ?? intrinsicW;\n let height = size.height ?? intrinsicH;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n const targetHeight = typeof size.height === \"number\" ? size.height : intrinsicH;\n width = intrinsicW / intrinsicH * targetHeight;\n height = targetHeight;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n const targetWidth = typeof size.width === \"number\" ? size.width : intrinsicW;\n height = intrinsicH / intrinsicW * targetWidth;\n width = targetWidth;\n }\n return { width, height };\n }\n function applyTextureSettings(texture, THREE) {\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.format = THREE.RGBAFormat;\n texture.generateMipmaps = false;\n texture.colorSpace = THREE.SRGBColorSpace;\n }\n async function renderVideoElement(element, _resolution, THREE) {\n const {\n src,\n size = {},\n loop = true,\n muted = true,\n playbackRate = 1,\n startTime = 0,\n frameSource = \"html5\"\n } = element;\n const useWebCodecs = frameSource === \"webcodecs\" || frameSource === \"auto\" && isFrameAccurateSupported();\n if (useWebCodecs) {\n const source = await FrameAccurateVideoSource.create(src);\n const { width: width2, height: height2 } = resolveSize(size, source.codedWidth, source.codedHeight);\n const texture2 = new THREE.CanvasTexture(source.canvas);\n applyTextureSettings(texture2, THREE);\n const material2 = new THREE.MeshBasicMaterial({\n map: texture2,\n transparent: true,\n depthWrite: false\n });\n const mesh2 = new THREE.Mesh(new THREE.PlaneGeometry(width2, height2), material2);\n mesh2.userData.videoSource = source;\n mesh2.userData.texture = texture2;\n texture2.needsUpdate = true;\n return { mesh: mesh2, width: width2, height: height2, video: null, videoSource: source, texture: texture2 };\n }\n let video;\n const cached = AssetCache.getVideo(src);\n if (cached) {\n video = cached.element;\n video.loop = loop;\n video.muted = muted;\n video.playbackRate = playbackRate;\n } else {\n video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.src = src;\n video.loop = loop;\n video.muted = muted;\n video.playbackRate = playbackRate;\n video.playsInline = true;\n video.preload = \"auto\";\n await new Promise((resolve, reject) => {\n video.oncanplaythrough = () => resolve();\n video.onerror = reject;\n video.load();\n });\n }\n video.currentTime = startTime;\n video.pause();\n const { width, height } = resolveSize(size, video.videoWidth, video.videoHeight);\n const texture = new THREE.VideoTexture(video);\n applyTextureSettings(texture, THREE);\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(width, height);\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData.video = video;\n mesh.userData.texture = texture;\n return { mesh, width, height, video, videoSource: null, texture };\n }\n\n // src/renderElements.ts\n function calculatePosition(position, resolution, elementWidth, elementHeight) {\n const { width, height } = resolution;\n const halfW = elementWidth / 2;\n const halfH = elementHeight / 2;\n if (typeof position === \"string\") {\n switch (position) {\n case \"center\":\n return { x: width / 2 - halfW, y: height / 2 - halfH };\n case \"top-left\":\n return { x: 0, y: 0 };\n case \"top-center\":\n return { x: width / 2 - halfW, y: 0 };\n case \"top-right\":\n return { x: width - elementWidth, y: 0 };\n case \"center-left\":\n return { x: 0, y: height / 2 - halfH };\n case \"center-right\":\n return { x: width - elementWidth, y: height / 2 - halfH };\n case \"bottom-left\":\n return { x: 0, y: height - elementHeight };\n case \"bottom-center\":\n return { x: width / 2 - halfW, y: height - elementHeight };\n case \"bottom-right\":\n return { x: width - elementWidth, y: height - elementHeight };\n default:\n return { x: 0, y: 0 };\n }\n }\n const x = typeof position.x === \"string\" ? parseFloat(position.x) / 100 * width : position.x;\n const y = typeof position.y === \"string\" ? parseFloat(position.y) / 100 * height : position.y;\n return { x, y };\n }\n var DESIGN_HEIGHT = 1080;\n async function renderElements(elementsConfig, overlayScenes, resolution, THREE) {\n const getScene = (config) => overlayScenes[config.zIndex ?? 100];\n await preloadAssets(elementsConfig);\n const elementMap = /* @__PURE__ */ new Map();\n const resolutionScale = resolution.height / DESIGN_HEIGHT;\n for (let i = 0; i < elementsConfig.length; i++) {\n const config = elementsConfig[i];\n const id = config.id ?? `element_${i}`;\n try {\n let mesh;\n let canvas = null;\n let elementWidth = 0;\n let elementHeight = 0;\n let segments = null;\n const segmentMeshes = [];\n let videoElement = null;\n let videoSource = null;\n let videoTexture = null;\n if (config.type === \"text\" && config.split) {\n const splitResult = renderSplitTextElement(config, resolution, THREE);\n elementWidth = splitResult.totalWidth;\n elementHeight = splitResult.totalHeight;\n const scaledWidth = elementWidth * resolutionScale;\n const scaledHeight = elementHeight * resolutionScale;\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight\n );\n const basePosX = x - resolution.width / 2 + scaledWidth / 2;\n const basePosY = -(y - resolution.height / 2 + scaledHeight / 2);\n let transformX = 0;\n let transformY = 0;\n if (config.transform) {\n transformX = (config.transform.translateX ?? 0) * resolutionScale;\n transformY = -((config.transform.translateY ?? 0) * resolutionScale);\n }\n const zIndex = config.zIndex ?? 100;\n segments = splitResult.meshes.map((item, si) => {\n const segMesh = item.mesh;\n segMesh.scale.set(resolutionScale, resolutionScale, 1);\n segMesh.position.x = basePosX + item.offsetX * resolutionScale + transformX;\n segMesh.position.y = basePosY + item.offsetY * resolutionScale + transformY;\n segMesh.position.z = 0;\n segMesh.renderOrder = zIndex + i * 0.01 + si * 1e-3;\n if (config.opacity !== void 0) {\n segMesh.material.opacity = config.opacity;\n }\n getScene(config).add(segMesh);\n segmentMeshes.push(segMesh);\n return createElementProps(\n THREE,\n segMesh,\n segMesh.position.x,\n -segMesh.position.y,\n config.opacity ?? 1\n );\n });\n mesh = splitResult.meshes[0]?.mesh ?? new THREE.Mesh();\n } else if (config.type === \"text\") {\n const result = renderTextElement(config, resolution, THREE);\n mesh = result.mesh;\n canvas = result.canvas;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"image\") {\n const result = await renderImageElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"svg\") {\n const result = await renderSVGElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"video\") {\n const result = await renderVideoElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n mesh.userData.video = result.video;\n videoElement = result.video;\n videoSource = result.videoSource;\n videoTexture = result.texture;\n videoElement?.pause();\n } else {\n mesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })\n );\n console.warn(`Element type \"${config.type}\" not implemented`);\n }\n if (!config.split) {\n const scaledWidth = elementWidth * resolutionScale;\n const scaledHeight = elementHeight * resolutionScale;\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight\n );\n const posX = x - resolution.width / 2 + scaledWidth / 2;\n const posY = -(y - resolution.height / 2 + scaledHeight / 2);\n mesh.scale.set(resolutionScale, resolutionScale, 1);\n mesh.position.x = posX;\n mesh.position.y = posY;\n mesh.position.z = 0;\n const zIndex = config.zIndex ?? 100;\n mesh.renderOrder = zIndex + i * 0.01;\n if (config.opacity !== void 0) {\n ;\n mesh.material.opacity = config.opacity;\n mesh.material.transparent = true;\n }\n if (config.transform) {\n const t = config.transform;\n if (t.translateX) mesh.position.x += t.translateX * resolutionScale;\n if (t.translateY) mesh.position.y -= t.translateY * resolutionScale;\n if (t.translateZ) mesh.position.z += t.translateZ;\n if (t.scale)\n mesh.scale.set(\n t.scale * resolutionScale,\n t.scale * resolutionScale,\n 1\n );\n if (t.rotateZ || t.rotation) {\n mesh.rotation.z = (t.rotateZ ?? t.rotation ?? 0) * Math.PI / 180;\n }\n }\n getScene(config).add(mesh);\n }\n const props = createElementProps(\n THREE,\n mesh,\n mesh.position.x,\n -mesh.position.y,\n config.opacity ?? 1,\n videoElement,\n videoSource,\n videoTexture\n );\n const elementInstance = {\n config,\n mesh,\n node: null,\n props,\n segments,\n setContent: (_content) => {\n if (config.type === \"text\" && canvas) {\n console.warn(\"setContent not fully implemented in inline renderer\");\n }\n },\n destroy: () => {\n videoSource?.dispose?.();\n const targetScene = getScene(config);\n if (segmentMeshes.length > 0) {\n segmentMeshes.forEach((m) => {\n targetScene.remove(m);\n m.geometry.dispose();\n const mat = m.material;\n if (mat.map) mat.map.dispose();\n mat.dispose();\n });\n } else {\n targetScene.remove(mesh);\n mesh.geometry.dispose();\n const mat = mesh.material;\n if (mat.map) mat.map.dispose();\n mat.dispose();\n }\n }\n };\n elementMap.set(id, elementInstance);\n } catch (error) {\n console.warn(\n `[vos] Failed to render element \"${id}\" (${config.type}):`,\n error\n );\n const fallbackMesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })\n );\n getScene(config).add(fallbackMesh);\n const props = createElementProps(THREE, fallbackMesh, 0, 0, 0);\n elementMap.set(id, {\n config,\n mesh: fallbackMesh,\n node: null,\n props,\n segments: null,\n setContent: () => {\n },\n destroy: () => {\n getScene(config).remove(fallbackMesh);\n fallbackMesh.geometry.dispose();\n fallbackMesh.material.dispose();\n }\n });\n }\n }\n return elementMap;\n }\n\n // src/index.ts\n function createVosElements(THREE) {\n return {\n renderElements: (elementsConfig, overlayScenes, resolution) => renderElements(elementsConfig, overlayScenes, resolution, THREE),\n disposeElements: (elementMap) => {\n elementMap.forEach((instance) => instance.destroy?.());\n elementMap.clear();\n }\n };\n }\n return __toCommonJS(index_exports);\n})();\n"
2
+ export const elementsBundleCode = "\"use strict\";\nvar __vosElementsFactory = (() => {\n var __defProp = Object.defineProperty;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\n var __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n };\n var __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n };\n var __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== \"symbol\" ? key + \"\" : key, value);\n\n // src/index.ts\n var index_exports = {};\n __export(index_exports, {\n createVosElements: () => createVosElements,\n renderElements: () => renderElements\n });\n\n // src/assetCache.ts\n var AssetCache = {\n images: /* @__PURE__ */ new Map(),\n videos: /* @__PURE__ */ new Map(),\n svgContents: /* @__PURE__ */ new Map(),\n getImage(url) {\n return this.images.get(url) || null;\n },\n getVideo(url) {\n return this.videos.get(url) || null;\n },\n getSVG(url) {\n return this.svgContents.get(url) || null;\n },\n dispose() {\n this.videos.forEach((cached) => {\n cached.element.pause();\n cached.element.src = \"\";\n cached.element.load();\n if (cached.blobUrl) {\n URL.revokeObjectURL(cached.blobUrl);\n }\n });\n this.images.clear();\n this.videos.clear();\n this.svgContents.clear();\n }\n };\n function extractAssetUrls(elementsConfig) {\n const assets = {\n images: [],\n videos: [],\n svgs: []\n };\n for (const el of elementsConfig) {\n if (el.type === \"image\" && el.src) {\n assets.images.push(el.src);\n } else if (el.type === \"video\" && el.src) {\n assets.videos.push(el.src);\n } else if (el.type === \"svg\" && el.src) {\n assets.svgs.push(el.src);\n }\n }\n assets.images = [...new Set(assets.images)];\n assets.videos = [...new Set(assets.videos)];\n assets.svgs = [...new Set(assets.svgs)];\n return assets;\n }\n async function preloadImage(url) {\n if (AssetCache.images.has(url)) return;\n const img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise((resolve) => {\n img.onload = () => resolve();\n img.onerror = () => {\n console.warn(\"Failed to preload image:\", url);\n resolve();\n };\n img.src = url;\n });\n AssetCache.images.set(url, {\n element: img,\n width: img.naturalWidth,\n height: img.naturalHeight\n });\n }\n async function preloadVideo(url) {\n if (AssetCache.videos.has(url)) return;\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\"Failed to fetch video: \" + response.status);\n }\n const blob = await response.blob();\n const blobUrl = URL.createObjectURL(blob);\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.preload = \"auto\";\n await new Promise((resolve, reject) => {\n video.oncanplaythrough = () => resolve();\n video.onerror = reject;\n video.src = blobUrl;\n video.load();\n });\n video.currentTime = 0;\n await new Promise((resolve) => {\n video.onseeked = () => resolve();\n setTimeout(() => resolve(), 100);\n });\n video.pause();\n console.log(\"Video preloaded:\", url, \"duration:\", video.duration);\n AssetCache.videos.set(url, {\n element: video,\n blobUrl,\n duration: video.duration,\n width: video.videoWidth,\n height: video.videoHeight\n });\n } catch (e) {\n console.warn(\"Failed to preload video:\", url, e);\n }\n }\n async function preloadSVG(url) {\n if (AssetCache.svgContents.has(url)) return;\n try {\n if (url.startsWith(\"<svg\") || url.startsWith(\"<?xml\")) {\n AssetCache.svgContents.set(url, url);\n return;\n }\n if (url.startsWith(\"data:image/svg\")) {\n const base64 = url.split(\",\")[1];\n const svgContent2 = atob(base64);\n AssetCache.svgContents.set(url, svgContent2);\n return;\n }\n const response = await fetch(url);\n const svgContent = await response.text();\n AssetCache.svgContents.set(url, svgContent);\n } catch (e) {\n console.warn(\"Failed to preload SVG:\", url, e);\n AssetCache.svgContents.set(url, \"\");\n }\n }\n async function preloadAssets(elementsConfig) {\n const assets = extractAssetUrls(elementsConfig);\n const total = assets.images.length + assets.videos.length + assets.svgs.length;\n if (total === 0) return;\n let loaded = 0;\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: \"PRELOAD_PROGRESS\", loaded: 0, total },\n \"*\"\n );\n }\n const preloadWithProgress = async (fn, url) => {\n await fn(url);\n loaded++;\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: \"PRELOAD_PROGRESS\", loaded, total },\n \"*\"\n );\n }\n };\n const promises = [\n ...assets.images.map((url) => preloadWithProgress(preloadImage, url)),\n ...assets.videos.map((url) => preloadWithProgress(preloadVideo, url)),\n ...assets.svgs.map((url) => preloadWithProgress(preloadSVG, url))\n ];\n await Promise.all(promises);\n }\n\n // src/createElementProps.ts\n function createElementProps(_THREE, mesh, initialX, initialY, initialOpacity = 1, videoElement = null, videoSource = null, videoTexture = null) {\n const baseScaleX = mesh.scale.x;\n const baseScaleY = mesh.scale.y;\n const state = {\n x: initialX,\n y: initialY,\n z: 0,\n opacity: initialOpacity,\n scale: 1,\n scaleX: 1,\n scaleY: 1,\n rotation: 0,\n rotationX: 0,\n rotationY: 0,\n zIndex: mesh.userData.zIndex ?? 0,\n // Video-specific properties (only meaningful if videoElement is provided)\n currentTime: videoElement ? videoElement.currentTime : 0,\n playing: false,\n startOffset: 0\n };\n const updateMeshPosition = () => {\n mesh.position.x = state.x;\n mesh.position.y = -state.y;\n mesh.position.z = state.z;\n };\n const updateMeshTransform = () => {\n mesh.scale.set(\n baseScaleX * state.scale * state.scaleX,\n baseScaleY * state.scale * state.scaleY,\n 1\n );\n mesh.rotation.set(\n state.rotationX * Math.PI / 180,\n state.rotationY * Math.PI / 180,\n state.rotation * Math.PI / 180\n );\n };\n const updateMeshOpacity = () => {\n const mat = mesh.material;\n mat.opacity = state.opacity;\n mat.needsUpdate = true;\n };\n const updateVideoPlayback = () => {\n if (!videoElement) return;\n const vos = window.__vos__;\n const shouldPlay = state.playing && !vos?.isPaused;\n if (shouldPlay) {\n if (videoElement.paused) {\n const timeDiff = Math.abs(videoElement.currentTime - state.currentTime);\n if (timeDiff > 0.5) {\n videoElement.currentTime = state.currentTime;\n }\n videoElement.play().catch(() => {\n });\n }\n } else {\n videoElement.pause();\n if (Math.abs(videoElement.currentTime - state.currentTime) > 0.05) {\n videoElement.currentTime = state.currentTime;\n }\n }\n };\n if (videoElement) {\n const vos = window.__vos__;\n if (vos?.videoCallbacks) {\n vos.videoCallbacks.add(updateVideoPlayback);\n }\n }\n const updateVideoCurrentTime = () => {\n if (videoSource) {\n const vos2 = window.__vos__;\n const p = videoSource.seekTo(state.currentTime).then(() => {\n if (videoTexture) videoTexture.needsUpdate = true;\n }).catch((e) => console.error(\"[vos] frame decode failed\", e));\n vos2?.registerDecode?.(p);\n return;\n }\n if (!videoElement) return;\n const vos = window.__vos__;\n if (!state.playing || vos?.isPaused) {\n videoElement.currentTime = state.currentTime;\n }\n };\n return new Proxy(state, {\n set(target, prop, value) {\n target[prop] = value;\n switch (prop) {\n case \"x\":\n case \"y\":\n case \"z\":\n updateMeshPosition();\n break;\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotation\":\n case \"rotationX\":\n case \"rotationY\":\n updateMeshTransform();\n break;\n case \"opacity\":\n updateMeshOpacity();\n break;\n case \"zIndex\":\n mesh.renderOrder = value;\n mesh.userData.zIndex = value;\n break;\n case \"currentTime\":\n updateVideoCurrentTime();\n break;\n case \"playing\":\n case \"startOffset\":\n updateVideoPlayback();\n break;\n }\n return true;\n },\n get(target, prop) {\n if (prop === \"duration\" && videoElement) {\n return videoElement.duration;\n }\n return target[prop];\n }\n });\n }\n\n // src/renderers/image.ts\n async function renderImageElement(element, _resolution, THREE) {\n const { src, size = {} } = element;\n let img;\n const cached = AssetCache.getImage(src);\n if (cached) {\n img = cached.element;\n } else {\n img = new Image();\n img.crossOrigin = \"anonymous\";\n await new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = src;\n });\n }\n let width = size.width ?? img.naturalWidth;\n let height = size.height ?? img.naturalHeight;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n const targetHeight = typeof size.height === \"number\" ? size.height : img.naturalHeight;\n width = img.naturalWidth / img.naturalHeight * targetHeight;\n height = targetHeight;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n const targetWidth = typeof size.width === \"number\" ? size.width : img.naturalWidth;\n height = img.naturalHeight / img.naturalWidth * targetWidth;\n width = targetWidth;\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n const fit = size.fit ?? \"fill\";\n if (fit === \"contain\" || fit === \"cover\") {\n const scale = fit === \"contain\" ? Math.min(width / img.naturalWidth, height / img.naturalHeight) : Math.max(width / img.naturalWidth, height / img.naturalHeight);\n const drawWidth = img.naturalWidth * scale;\n const drawHeight = img.naturalHeight * scale;\n const offsetX = (width - drawWidth) / 2;\n const offsetY = (height - drawHeight) / 2;\n ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);\n } else {\n ctx.drawImage(img, 0, 0, width, height);\n }\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(width, height);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, width, height };\n }\n\n // src/renderers/svg.ts\n async function renderSVGElement(element, _resolution, THREE) {\n const { src, size = {}, colors = {} } = element;\n let svgContent = AssetCache.getSVG(src);\n if (!svgContent) {\n if (src.startsWith(\"http\") || src.startsWith(\"/\")) {\n const response = await fetch(src);\n svgContent = await response.text();\n } else {\n svgContent = src;\n }\n }\n Object.entries(colors).forEach(([selector, color]) => {\n const regex = new RegExp(`(${selector}[^>]*)(fill|stroke)=\"[^\"]*\"`, \"g\");\n svgContent = svgContent.replace(regex, `$1$2=\"${color}\"`);\n });\n const parser = new DOMParser();\n const svgDoc = parser.parseFromString(svgContent, \"image/svg+xml\");\n const svgElement = svgDoc.documentElement;\n const viewBox = svgElement.getAttribute(\"viewBox\");\n let svgWidth = parseFloat(svgElement.getAttribute(\"width\") || \"\") || 100;\n let svgHeight = parseFloat(svgElement.getAttribute(\"height\") || \"\") || 100;\n if (viewBox) {\n const [, , vbW, vbH] = viewBox.split(\" \").map(Number);\n svgWidth = vbW || svgWidth;\n svgHeight = vbH || svgHeight;\n }\n let width = size.width ?? svgWidth;\n let height = size.height ?? svgHeight;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n width = svgWidth / svgHeight * size.height;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n height = svgHeight / svgWidth * size.width;\n }\n svgElement.setAttribute(\"width\", String(width));\n svgElement.setAttribute(\"height\", String(height));\n const updatedSvg = new XMLSerializer().serializeToString(svgElement);\n const blob = new Blob([updatedSvg], { type: \"image/svg+xml\" });\n const url = URL.createObjectURL(blob);\n const img = new Image();\n await new Promise((resolve, reject) => {\n img.onload = () => resolve();\n img.onerror = reject;\n img.src = url;\n });\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n ctx.drawImage(img, 0, 0, width, height);\n URL.revokeObjectURL(url);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(width, height);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, width, height };\n }\n\n // src/renderers/text.ts\n function renderTextElement(element, _resolution, THREE) {\n const { content, font = {} } = element;\n const fontSize = font.size ?? 24;\n const fontFamily = font.family ?? \"Inter, system-ui, sans-serif\";\n const fontWeight = font.weight ?? \"normal\";\n const fontStyle = font.style ?? \"normal\";\n const color = font.color ?? \"#ffffff\";\n const align = font.align ?? \"left\";\n const letterSpacing = font.letterSpacing ?? 0;\n const lineHeight = font.lineHeight ?? 1.2;\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.font = fontString;\n const lines = content.split(\"\\n\");\n let maxWidth = 0;\n lines.forEach((line) => {\n let w = 0;\n for (let i = 0; i < line.length; i++) {\n w += ctx.measureText(line[i]).width;\n if (i < line.length - 1) w += letterSpacing;\n }\n if (w > maxWidth) maxWidth = w;\n });\n const totalHeight = lines.length * fontSize * lineHeight;\n const padding = Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 10;\n const canvasWidth = Math.ceil(maxWidth + padding * 2);\n const canvasHeight = Math.ceil(totalHeight + padding * 2);\n canvas.width = canvasWidth;\n canvas.height = canvasHeight;\n ctx.font = fontString;\n ctx.textBaseline = \"top\";\n ctx.textAlign = align;\n ctx.clearRect(0, 0, canvasWidth, canvasHeight);\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color;\n ctx.shadowBlur = element.shadow.blur;\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0;\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0;\n }\n let textX = padding;\n if (align === \"center\") textX = canvasWidth / 2;\n else if (align === \"right\") textX = canvasWidth - padding;\n lines.forEach((line, i) => {\n const y = padding + i * fontSize * lineHeight;\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color;\n ctx.lineWidth = element.stroke.width;\n ctx.lineJoin = \"round\";\n ctx.strokeText(line, textX, y);\n }\n ctx.fillStyle = color;\n ctx.fillText(line, textX, y);\n });\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight);\n const mesh = new THREE.Mesh(geometry, material);\n return { mesh, canvas, width: canvasWidth, height: canvasHeight };\n }\n function renderTextSegment(text, font, element, THREE) {\n const fontSize = font.size ?? 24;\n const fontFamily = font.family ?? \"Inter, system-ui, sans-serif\";\n const fontWeight = font.weight ?? \"normal\";\n const fontStyle = font.style ?? \"normal\";\n const color = font.color ?? \"#ffffff\";\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n ctx.font = fontString;\n const metrics = ctx.measureText(text);\n const padding = Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 4;\n const canvasWidth = Math.ceil(metrics.width + padding * 2);\n const canvasHeight = Math.ceil(fontSize * 1.4 + padding * 2);\n canvas.width = canvasWidth;\n canvas.height = canvasHeight;\n ctx.font = fontString;\n ctx.textBaseline = \"top\";\n ctx.textAlign = \"left\";\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color;\n ctx.shadowBlur = element.shadow.blur;\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0;\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0;\n }\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color;\n ctx.lineWidth = element.stroke.width;\n ctx.lineJoin = \"round\";\n ctx.strokeText(text, padding, padding);\n }\n ctx.fillStyle = color;\n ctx.fillText(text, padding, padding);\n const texture = new THREE.CanvasTexture(canvas);\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.needsUpdate = true;\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight);\n const mesh = new THREE.Mesh(geometry, material);\n return {\n mesh,\n width: canvasWidth,\n height: canvasHeight,\n textWidth: metrics.width\n };\n }\n function renderSplitTextElement(element, _resolution, THREE) {\n const { content, font = {}, split } = element;\n const splitType = split?.type ?? \"chars\";\n const letterSpacing = font.letterSpacing ?? 0;\n let segments;\n if (splitType === \"chars\") {\n segments = content.split(\"\");\n } else if (splitType === \"words\") {\n segments = content.split(/\\s+/);\n } else {\n segments = content.split(\"\\n\");\n }\n const meshes = [];\n let totalWidth = 0;\n segments.forEach((text, i) => {\n if (text.length === 0) return;\n const result = renderTextSegment(text, font, element, THREE);\n meshes.push({\n mesh: result.mesh,\n width: result.width,\n height: result.height,\n textWidth: result.textWidth,\n text\n });\n totalWidth += result.textWidth;\n if (i < segments.length - 1) {\n totalWidth += splitType === \"words\" ? (font.size ?? 24) * 0.4 : letterSpacing;\n }\n });\n const totalHeight = meshes[0]?.height ?? 0;\n let currentX = -totalWidth / 2;\n meshes.forEach((item, i) => {\n item.offsetX = currentX + item.textWidth / 2;\n item.offsetY = 0;\n currentX += item.textWidth;\n if (i < meshes.length - 1) {\n currentX += splitType === \"words\" ? (font.size ?? 24) * 0.4 : letterSpacing;\n }\n });\n return { meshes, totalWidth, totalHeight };\n }\n\n // src/video/sampleIndex.ts\n function buildSyncIndices(samples) {\n const out = [];\n for (let i = 0; i < samples.length; i++) if (samples[i].isSync) out.push(i);\n return out;\n }\n function targetDecodeIndex(samples, tSec) {\n let di = 0;\n let best = -Infinity;\n for (let i = 0; i < samples.length; i++) {\n const c = samples[i].cts;\n if (c <= tSec && c > best) {\n best = c;\n di = i;\n }\n }\n return di;\n }\n function gopBounds(syncIndices, totalSamples, targetDI) {\n let ki = 0;\n for (const si of syncIndices) {\n if (si <= targetDI) ki = si;\n else break;\n }\n let ni = totalSamples;\n for (const si of syncIndices) {\n if (si > targetDI) {\n ni = si;\n break;\n }\n }\n return [ki, ni];\n }\n\n // src/video/FrameAccurateVideoSource.ts\n var MP4BOX_URL = \"https://esm.sh/mp4box@0.5.2\";\n var mp4boxPromise = null;\n function loadMp4Box() {\n if (!mp4boxPromise) {\n mp4boxPromise = import(\n /* @vite-ignore */\n MP4BOX_URL\n ).then((m) => m.default ?? m);\n }\n return mp4boxPromise;\n }\n function isFrameAccurateSupported() {\n return typeof VideoDecoder !== \"undefined\" && typeof EncodedVideoChunk !== \"undefined\";\n }\n var FrameAccurateVideoSource = class _FrameAccurateVideoSource {\n constructor() {\n __publicField(this, \"canvas\");\n __publicField(this, \"ctx\");\n __publicField(this, \"decoder\");\n __publicField(this, \"samples\", []);\n // DECODE order (as delivered by mp4box)\n __publicField(this, \"syncIndices\", []);\n __publicField(this, \"onFrame\", null);\n __publicField(this, \"lastError\", null);\n __publicField(this, \"durationSec\", 0);\n __publicField(this, \"fps\", 30);\n __publicField(this, \"codedWidth\", 0);\n __publicField(this, \"codedHeight\", 0);\n this.canvas = document.createElement(\"canvas\");\n this.ctx = this.canvas.getContext(\"2d\");\n }\n static async create(src) {\n const self = new _FrameAccurateVideoSource();\n self.decoder = new VideoDecoder({\n output: (f) => self.onFrame?.(f),\n error: (e) => {\n self.lastError = e;\n console.error(\"[vos] VideoDecoder error\", e);\n }\n });\n const buf = await fetch(src).then((r) => {\n if (!r.ok) throw new Error(`[vos] failed to fetch video: ${src} (${r.status})`);\n return r.arrayBuffer();\n });\n await self.demux(buf);\n self.canvas.width = self.codedWidth;\n self.canvas.height = self.codedHeight;\n await self.seekTo(0);\n return self;\n }\n async demux(data) {\n const MP4Box = await loadMp4Box();\n return new Promise((resolve, reject) => {\n const file = MP4Box.createFile();\n let expected = Infinity;\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n if (!this.samples.length) {\n reject(new Error(\"[vos] no samples extracted\"));\n return;\n }\n this.syncIndices = buildSyncIndices(this.samples);\n resolve();\n };\n file.onError = (e) => {\n if (settled) return;\n settled = true;\n reject(new Error(String(e)));\n };\n file.onReady = (info) => {\n const track = info.videoTracks?.[0];\n if (!track) {\n settled = true;\n reject(new Error(\"[vos] no video track in mp4\"));\n return;\n }\n this.codedWidth = track.video.width;\n this.codedHeight = track.video.height;\n this.durationSec = info.duration / info.timescale;\n this.fps = track.nb_samples / this.durationSec || 30;\n expected = track.nb_samples || Infinity;\n this.decoder.configure({\n codec: track.codec,\n description: this.getDescription(MP4Box, file, track.id),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight\n });\n file.setExtractionOptions(track.id, null, { nbSamples: Infinity });\n file.start();\n };\n file.onSamples = (_id, _user, arr) => {\n for (const s of arr) {\n this.samples.push({\n cts: s.cts / s.timescale,\n isSync: !!s.is_sync,\n chunk: new EncodedVideoChunk({\n type: s.is_sync ? \"key\" : \"delta\",\n timestamp: Math.round(s.cts / s.timescale * 1e6),\n duration: Math.round(s.duration / s.timescale * 1e6),\n data: s.data\n })\n });\n }\n if (this.samples.length >= expected) finish();\n };\n data.fileStart = 0;\n file.appendBuffer(data);\n file.flush();\n setTimeout(finish, 2e3);\n });\n }\n getDescription(MP4Box, file, trackId) {\n const trak = file.getTrackById(trackId);\n for (const entry of trak.mdia.minf.stbl.stsd.entries) {\n const box = entry.avcC ?? entry.hvcC ?? entry.vpcC ?? entry.av1C;\n if (box) {\n const stream = new MP4Box.DataStream(void 0, 0, MP4Box.DataStream.BIG_ENDIAN);\n box.write(stream);\n return new Uint8Array(stream.buffer, 8);\n }\n }\n throw new Error(\"[vos] no codec description (avcC/hvcC/vpcC/av1C) found\");\n }\n /**\n * Decode the exact frame at presentation time `t` and draw it to `canvas`.\n * Decodes the whole GOP (keyframe → next keyframe) in decode order, then\n * selects the output by PTS. Returns when the canvas is updated.\n */\n async seekTo(tSec) {\n if (!this.samples.length) return;\n const targetDI = targetDecodeIndex(this.samples, tSec);\n const [ki, ni] = gopBounds(this.syncIndices, this.samples.length, targetDI);\n const targetTs = Math.round(this.samples[targetDI].cts * 1e6);\n this.lastError = null;\n const collected = [];\n this.onFrame = (f) => collected.push(f);\n for (let i = ki; i < ni; i++) this.decoder.decode(this.samples[i].chunk);\n await this.decoder.flush();\n this.onFrame = null;\n if (this.lastError) throw this.lastError;\n let best;\n let bestD = Infinity;\n for (const f of collected) {\n const d = Math.abs(f.timestamp - targetTs);\n if (d < bestD) {\n bestD = d;\n best = f;\n }\n }\n if (best) this.ctx.drawImage(best, 0, 0);\n for (const f of collected) f.close();\n }\n dispose() {\n try {\n this.decoder.close();\n } catch {\n }\n }\n };\n\n // src/renderers/video.ts\n function resolveSize(size, intrinsicW, intrinsicH) {\n let width = size.width ?? intrinsicW;\n let height = size.height ?? intrinsicH;\n if (size.width === \"auto\" && size.height !== \"auto\" && size.height) {\n const targetHeight = typeof size.height === \"number\" ? size.height : intrinsicH;\n width = intrinsicW / intrinsicH * targetHeight;\n height = targetHeight;\n } else if (size.height === \"auto\" && size.width !== \"auto\" && size.width) {\n const targetWidth = typeof size.width === \"number\" ? size.width : intrinsicW;\n height = intrinsicH / intrinsicW * targetWidth;\n width = targetWidth;\n }\n return { width, height };\n }\n function applyTextureSettings(texture, THREE) {\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.format = THREE.RGBAFormat;\n texture.generateMipmaps = false;\n texture.colorSpace = THREE.SRGBColorSpace;\n }\n async function renderVideoElement(element, _resolution, THREE) {\n const frameSource = element.frameSource ?? \"html5\";\n const useWebCodecs = frameSource === \"webcodecs\" || frameSource === \"auto\" && isFrameAccurateSupported();\n if (useWebCodecs) {\n try {\n return await renderWebCodecsVideo(element, THREE);\n } catch (err) {\n console.warn(\n \"[vos] frame-accurate video unavailable; falling back to html5 \\u2014\",\n err instanceof Error ? err.message : err\n );\n }\n }\n return renderHtml5Video(element, THREE);\n }\n async function renderWebCodecsVideo(element, THREE) {\n const { src, size = {} } = element;\n const source = await FrameAccurateVideoSource.create(src);\n const { width, height } = resolveSize(size, source.codedWidth, source.codedHeight);\n const texture = new THREE.CanvasTexture(source.canvas);\n applyTextureSettings(texture, THREE);\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material);\n mesh.userData.videoSource = source;\n mesh.userData.texture = texture;\n texture.needsUpdate = true;\n return { mesh, width, height, video: null, videoSource: source, texture };\n }\n async function renderHtml5Video(element, THREE) {\n const {\n src,\n size = {},\n loop = true,\n muted = true,\n playbackRate = 1,\n startTime = 0\n } = element;\n let video;\n const cached = AssetCache.getVideo(src);\n if (cached) {\n video = cached.element;\n video.loop = loop;\n video.muted = muted;\n video.playbackRate = playbackRate;\n } else {\n video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.src = src;\n video.loop = loop;\n video.muted = muted;\n video.playbackRate = playbackRate;\n video.playsInline = true;\n video.preload = \"auto\";\n await new Promise((resolve, reject) => {\n video.oncanplaythrough = () => resolve();\n video.onerror = reject;\n video.load();\n });\n }\n video.currentTime = startTime;\n video.pause();\n const { width, height } = resolveSize(size, video.videoWidth, video.videoHeight);\n const texture = new THREE.VideoTexture(video);\n applyTextureSettings(texture, THREE);\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false\n });\n const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material);\n mesh.userData.video = video;\n mesh.userData.texture = texture;\n return { mesh, width, height, video, videoSource: null, texture };\n }\n\n // src/renderElements.ts\n function calculatePosition(position, resolution, elementWidth, elementHeight) {\n const { width, height } = resolution;\n const halfW = elementWidth / 2;\n const halfH = elementHeight / 2;\n if (typeof position === \"string\") {\n switch (position) {\n case \"center\":\n return { x: width / 2 - halfW, y: height / 2 - halfH };\n case \"top-left\":\n return { x: 0, y: 0 };\n case \"top-center\":\n return { x: width / 2 - halfW, y: 0 };\n case \"top-right\":\n return { x: width - elementWidth, y: 0 };\n case \"center-left\":\n return { x: 0, y: height / 2 - halfH };\n case \"center-right\":\n return { x: width - elementWidth, y: height / 2 - halfH };\n case \"bottom-left\":\n return { x: 0, y: height - elementHeight };\n case \"bottom-center\":\n return { x: width / 2 - halfW, y: height - elementHeight };\n case \"bottom-right\":\n return { x: width - elementWidth, y: height - elementHeight };\n default:\n return { x: 0, y: 0 };\n }\n }\n const x = typeof position.x === \"string\" ? parseFloat(position.x) / 100 * width : position.x;\n const y = typeof position.y === \"string\" ? parseFloat(position.y) / 100 * height : position.y;\n return { x, y };\n }\n var DESIGN_HEIGHT = 1080;\n async function renderElements(elementsConfig, overlayScenes, resolution, THREE) {\n const getScene = (config) => overlayScenes[config.zIndex ?? 100];\n await preloadAssets(elementsConfig);\n const elementMap = /* @__PURE__ */ new Map();\n const resolutionScale = resolution.height / DESIGN_HEIGHT;\n for (let i = 0; i < elementsConfig.length; i++) {\n const config = elementsConfig[i];\n const id = config.id ?? `element_${i}`;\n try {\n let mesh;\n let canvas = null;\n let elementWidth = 0;\n let elementHeight = 0;\n let segments = null;\n const segmentMeshes = [];\n let videoElement = null;\n let videoSource = null;\n let videoTexture = null;\n if (config.type === \"text\" && config.split) {\n const splitResult = renderSplitTextElement(config, resolution, THREE);\n elementWidth = splitResult.totalWidth;\n elementHeight = splitResult.totalHeight;\n const scaledWidth = elementWidth * resolutionScale;\n const scaledHeight = elementHeight * resolutionScale;\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight\n );\n const basePosX = x - resolution.width / 2 + scaledWidth / 2;\n const basePosY = -(y - resolution.height / 2 + scaledHeight / 2);\n let transformX = 0;\n let transformY = 0;\n if (config.transform) {\n transformX = (config.transform.translateX ?? 0) * resolutionScale;\n transformY = -((config.transform.translateY ?? 0) * resolutionScale);\n }\n const zIndex = config.zIndex ?? 100;\n segments = splitResult.meshes.map((item, si) => {\n const segMesh = item.mesh;\n segMesh.scale.set(resolutionScale, resolutionScale, 1);\n segMesh.position.x = basePosX + item.offsetX * resolutionScale + transformX;\n segMesh.position.y = basePosY + item.offsetY * resolutionScale + transformY;\n segMesh.position.z = 0;\n segMesh.renderOrder = zIndex + i * 0.01 + si * 1e-3;\n if (config.opacity !== void 0) {\n segMesh.material.opacity = config.opacity;\n }\n getScene(config).add(segMesh);\n segmentMeshes.push(segMesh);\n return createElementProps(\n THREE,\n segMesh,\n segMesh.position.x,\n -segMesh.position.y,\n config.opacity ?? 1\n );\n });\n mesh = splitResult.meshes[0]?.mesh ?? new THREE.Mesh();\n } else if (config.type === \"text\") {\n const result = renderTextElement(config, resolution, THREE);\n mesh = result.mesh;\n canvas = result.canvas;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"image\") {\n const result = await renderImageElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"svg\") {\n const result = await renderSVGElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n } else if (config.type === \"video\") {\n const result = await renderVideoElement(config, resolution, THREE);\n mesh = result.mesh;\n elementWidth = result.width;\n elementHeight = result.height;\n mesh.userData.video = result.video;\n videoElement = result.video;\n videoSource = result.videoSource;\n videoTexture = result.texture;\n videoElement?.pause();\n } else {\n mesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })\n );\n console.warn(`Element type \"${config.type}\" not implemented`);\n }\n if (!config.split) {\n const scaledWidth = elementWidth * resolutionScale;\n const scaledHeight = elementHeight * resolutionScale;\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight\n );\n const posX = x - resolution.width / 2 + scaledWidth / 2;\n const posY = -(y - resolution.height / 2 + scaledHeight / 2);\n mesh.scale.set(resolutionScale, resolutionScale, 1);\n mesh.position.x = posX;\n mesh.position.y = posY;\n mesh.position.z = 0;\n const zIndex = config.zIndex ?? 100;\n mesh.renderOrder = zIndex + i * 0.01;\n if (config.opacity !== void 0) {\n ;\n mesh.material.opacity = config.opacity;\n mesh.material.transparent = true;\n }\n if (config.transform) {\n const t = config.transform;\n if (t.translateX) mesh.position.x += t.translateX * resolutionScale;\n if (t.translateY) mesh.position.y -= t.translateY * resolutionScale;\n if (t.translateZ) mesh.position.z += t.translateZ;\n if (t.scale)\n mesh.scale.set(\n t.scale * resolutionScale,\n t.scale * resolutionScale,\n 1\n );\n if (t.rotateZ || t.rotation) {\n mesh.rotation.z = (t.rotateZ ?? t.rotation ?? 0) * Math.PI / 180;\n }\n }\n getScene(config).add(mesh);\n }\n const props = createElementProps(\n THREE,\n mesh,\n mesh.position.x,\n -mesh.position.y,\n config.opacity ?? 1,\n videoElement,\n videoSource,\n videoTexture\n );\n const elementInstance = {\n config,\n mesh,\n node: null,\n props,\n segments,\n setContent: (_content) => {\n if (config.type === \"text\" && canvas) {\n console.warn(\"setContent not fully implemented in inline renderer\");\n }\n },\n destroy: () => {\n videoSource?.dispose?.();\n const targetScene = getScene(config);\n if (segmentMeshes.length > 0) {\n segmentMeshes.forEach((m) => {\n targetScene.remove(m);\n m.geometry.dispose();\n const mat = m.material;\n if (mat.map) mat.map.dispose();\n mat.dispose();\n });\n } else {\n targetScene.remove(mesh);\n mesh.geometry.dispose();\n const mat = mesh.material;\n if (mat.map) mat.map.dispose();\n mat.dispose();\n }\n }\n };\n elementMap.set(id, elementInstance);\n } catch (error) {\n console.warn(\n `[vos] Failed to render element \"${id}\" (${config.type}):`,\n error\n );\n const fallbackMesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })\n );\n getScene(config).add(fallbackMesh);\n const props = createElementProps(THREE, fallbackMesh, 0, 0, 0);\n elementMap.set(id, {\n config,\n mesh: fallbackMesh,\n node: null,\n props,\n segments: null,\n setContent: () => {\n },\n destroy: () => {\n getScene(config).remove(fallbackMesh);\n fallbackMesh.geometry.dispose();\n fallbackMesh.material.dispose();\n }\n });\n }\n }\n return elementMap;\n }\n\n // src/index.ts\n function createVosElements(THREE) {\n return {\n renderElements: (elementsConfig, overlayScenes, resolution) => renderElements(elementsConfig, overlayScenes, resolution, THREE),\n disposeElements: (elementMap) => {\n elementMap.forEach((instance) => instance.destroy?.());\n elementMap.clear();\n }\n };\n }\n return __toCommonJS(index_exports);\n})();\n"
package/dist/index.js CHANGED
@@ -650,14 +650,35 @@ var FrameAccurateVideoSource = class _FrameAccurateVideoSource {
650
650
  const MP4Box = await loadMp4Box();
651
651
  return new Promise((resolve, reject) => {
652
652
  const file = MP4Box.createFile();
653
- file.onError = (e) => reject(new Error(String(e)));
653
+ let expected = Infinity;
654
+ let settled = false;
655
+ const finish = () => {
656
+ if (settled) return;
657
+ settled = true;
658
+ if (!this.samples.length) {
659
+ reject(new Error("[vos] no samples extracted"));
660
+ return;
661
+ }
662
+ this.syncIndices = buildSyncIndices(this.samples);
663
+ resolve();
664
+ };
665
+ file.onError = (e) => {
666
+ if (settled) return;
667
+ settled = true;
668
+ reject(new Error(String(e)));
669
+ };
654
670
  file.onReady = (info) => {
655
671
  const track = info.videoTracks?.[0];
656
- if (!track) return reject(new Error("[vos] no video track in mp4"));
672
+ if (!track) {
673
+ settled = true;
674
+ reject(new Error("[vos] no video track in mp4"));
675
+ return;
676
+ }
657
677
  this.codedWidth = track.video.width;
658
678
  this.codedHeight = track.video.height;
659
679
  this.durationSec = info.duration / info.timescale;
660
680
  this.fps = track.nb_samples / this.durationSec || 30;
681
+ expected = track.nb_samples || Infinity;
661
682
  this.decoder.configure({
662
683
  codec: track.codec,
663
684
  description: this.getDescription(MP4Box, file, track.id),
@@ -680,15 +701,12 @@ var FrameAccurateVideoSource = class _FrameAccurateVideoSource {
680
701
  })
681
702
  });
682
703
  }
704
+ if (this.samples.length >= expected) finish();
683
705
  };
684
706
  data.fileStart = 0;
685
707
  file.appendBuffer(data);
686
708
  file.flush();
687
- queueMicrotask(() => {
688
- if (!this.samples.length) return reject(new Error("[vos] no samples extracted"));
689
- this.syncIndices = buildSyncIndices(this.samples);
690
- resolve();
691
- });
709
+ setTimeout(finish, 2e3);
692
710
  });
693
711
  }
694
712
  getDescription(MP4Box, file, trackId) {
@@ -763,32 +781,46 @@ function applyTextureSettings(texture, THREE) {
763
781
  texture.colorSpace = THREE.SRGBColorSpace;
764
782
  }
765
783
  async function renderVideoElement(element, _resolution, THREE) {
784
+ const frameSource = element.frameSource ?? "html5";
785
+ const useWebCodecs = frameSource === "webcodecs" || frameSource === "auto" && isFrameAccurateSupported();
786
+ if (useWebCodecs) {
787
+ try {
788
+ return await renderWebCodecsVideo(element, THREE);
789
+ } catch (err) {
790
+ console.warn(
791
+ "[vos] frame-accurate video unavailable; falling back to html5 \u2014",
792
+ err instanceof Error ? err.message : err
793
+ );
794
+ }
795
+ }
796
+ return renderHtml5Video(element, THREE);
797
+ }
798
+ async function renderWebCodecsVideo(element, THREE) {
799
+ const { src, size = {} } = element;
800
+ const source = await FrameAccurateVideoSource.create(src);
801
+ const { width, height } = resolveSize(size, source.codedWidth, source.codedHeight);
802
+ const texture = new THREE.CanvasTexture(source.canvas);
803
+ applyTextureSettings(texture, THREE);
804
+ const material = new THREE.MeshBasicMaterial({
805
+ map: texture,
806
+ transparent: true,
807
+ depthWrite: false
808
+ });
809
+ const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material);
810
+ mesh.userData.videoSource = source;
811
+ mesh.userData.texture = texture;
812
+ texture.needsUpdate = true;
813
+ return { mesh, width, height, video: null, videoSource: source, texture };
814
+ }
815
+ async function renderHtml5Video(element, THREE) {
766
816
  const {
767
817
  src,
768
818
  size = {},
769
819
  loop = true,
770
820
  muted = true,
771
821
  playbackRate = 1,
772
- startTime = 0,
773
- frameSource = "html5"
822
+ startTime = 0
774
823
  } = element;
775
- const useWebCodecs = frameSource === "webcodecs" || frameSource === "auto" && isFrameAccurateSupported();
776
- if (useWebCodecs) {
777
- const source = await FrameAccurateVideoSource.create(src);
778
- const { width: width2, height: height2 } = resolveSize(size, source.codedWidth, source.codedHeight);
779
- const texture2 = new THREE.CanvasTexture(source.canvas);
780
- applyTextureSettings(texture2, THREE);
781
- const material2 = new THREE.MeshBasicMaterial({
782
- map: texture2,
783
- transparent: true,
784
- depthWrite: false
785
- });
786
- const mesh2 = new THREE.Mesh(new THREE.PlaneGeometry(width2, height2), material2);
787
- mesh2.userData.videoSource = source;
788
- mesh2.userData.texture = texture2;
789
- texture2.needsUpdate = true;
790
- return { mesh: mesh2, width: width2, height: height2, video: null, videoSource: source, texture: texture2 };
791
- }
792
824
  let video;
793
825
  const cached = AssetCache.getVideo(src);
794
826
  if (cached) {
@@ -821,8 +853,7 @@ async function renderVideoElement(element, _resolution, THREE) {
821
853
  transparent: true,
822
854
  depthWrite: false
823
855
  });
824
- const geometry = new THREE.PlaneGeometry(width, height);
825
- const mesh = new THREE.Mesh(geometry, material);
856
+ const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material);
826
857
  mesh.userData.video = video;
827
858
  mesh.userData.texture = texture;
828
859
  return { mesh, width, height, video, videoSource: null, texture };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/assetCache.ts","../src/createElementProps.ts","../src/renderers/image.ts","../src/renderers/svg.ts","../src/renderers/text.ts","../src/video/sampleIndex.ts","../src/video/FrameAccurateVideoSource.ts","../src/renderers/video.ts","../src/renderElements.ts","../src/index.ts"],"sourcesContent":["/**\n * Global asset cache - stores preloaded assets by URL.\n * Handles images, videos, and SVGs.\n */\nexport const AssetCache = {\n images: new Map<\n string,\n { element: HTMLImageElement; width: number; height: number }\n >(),\n videos: new Map<\n string,\n {\n element: HTMLVideoElement\n blobUrl?: string\n duration: number\n width: number\n height: number\n }\n >(),\n svgContents: new Map<string, string>(),\n\n getImage(url: string) {\n return this.images.get(url) || null\n },\n getVideo(url: string) {\n return this.videos.get(url) || null\n },\n getSVG(url: string) {\n return this.svgContents.get(url) || null\n },\n\n dispose() {\n this.videos.forEach((cached) => {\n cached.element.pause()\n cached.element.src = ''\n cached.element.load()\n if (cached.blobUrl) {\n URL.revokeObjectURL(cached.blobUrl)\n }\n })\n this.images.clear()\n this.videos.clear()\n this.svgContents.clear()\n },\n}\n\n/**\n * Extract all asset URLs from elements config\n */\nfunction extractAssetUrls(elementsConfig: any[]) {\n const assets: {\n images: string[]\n videos: string[]\n svgs: string[]\n } = {\n images: [],\n videos: [],\n svgs: [],\n }\n\n for (const el of elementsConfig) {\n if (el.type === 'image' && el.src) {\n assets.images.push(el.src)\n } else if (el.type === 'video' && el.src) {\n assets.videos.push(el.src)\n } else if (el.type === 'svg' && el.src) {\n assets.svgs.push(el.src)\n }\n }\n\n assets.images = [...new Set(assets.images)]\n assets.videos = [...new Set(assets.videos)]\n assets.svgs = [...new Set(assets.svgs)]\n\n return assets\n}\n\nasync function preloadImage(url: string) {\n if (AssetCache.images.has(url)) return\n\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n await new Promise<void>((resolve) => {\n img.onload = () => resolve()\n img.onerror = () => {\n console.warn('Failed to preload image:', url)\n resolve()\n }\n img.src = url\n })\n\n AssetCache.images.set(url, {\n element: img,\n width: img.naturalWidth,\n height: img.naturalHeight,\n })\n}\n\nasync function preloadVideo(url: string) {\n if (AssetCache.videos.has(url)) return\n\n try {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error('Failed to fetch video: ' + response.status)\n }\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n\n const video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = true\n video.playsInline = true\n video.preload = 'auto'\n\n await new Promise<void>((resolve, reject) => {\n video.oncanplaythrough = () => resolve()\n video.onerror = reject\n video.src = blobUrl\n video.load()\n })\n\n video.currentTime = 0\n await new Promise<void>((resolve) => {\n video.onseeked = () => resolve()\n setTimeout(() => resolve(), 100)\n })\n\n video.pause()\n\n console.log('Video preloaded:', url, 'duration:', video.duration)\n\n AssetCache.videos.set(url, {\n element: video,\n blobUrl,\n duration: video.duration,\n width: video.videoWidth,\n height: video.videoHeight,\n })\n } catch (e) {\n console.warn('Failed to preload video:', url, e)\n }\n}\n\nasync function preloadSVG(url: string) {\n if (AssetCache.svgContents.has(url)) return\n\n try {\n if (url.startsWith('<svg') || url.startsWith('<?xml')) {\n AssetCache.svgContents.set(url, url)\n return\n }\n\n if (url.startsWith('data:image/svg')) {\n const base64 = url.split(',')[1]\n const svgContent = atob(base64)\n AssetCache.svgContents.set(url, svgContent)\n return\n }\n\n const response = await fetch(url)\n const svgContent = await response.text()\n AssetCache.svgContents.set(url, svgContent)\n } catch (e) {\n console.warn('Failed to preload SVG:', url, e)\n AssetCache.svgContents.set(url, '')\n }\n}\n\n/**\n * Preload all assets from elements config.\n * Reports progress via postMessage to parent.\n */\nexport async function preloadAssets(elementsConfig: any[]) {\n const assets = extractAssetUrls(elementsConfig)\n const total = assets.images.length + assets.videos.length + assets.svgs.length\n\n if (total === 0) return\n\n let loaded = 0\n\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: 'PRELOAD_PROGRESS', loaded: 0, total },\n '*',\n )\n }\n\n const preloadWithProgress = async (\n fn: (url: string) => Promise<void>,\n url: string,\n ) => {\n await fn(url)\n loaded++\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: 'PRELOAD_PROGRESS', loaded, total },\n '*',\n )\n }\n }\n\n const promises = [\n ...assets.images.map((url) => preloadWithProgress(preloadImage, url)),\n ...assets.videos.map((url) => preloadWithProgress(preloadVideo, url)),\n ...assets.svgs.map((url) => preloadWithProgress(preloadSVG, url)),\n ]\n\n await Promise.all(promises)\n}\n","import type * as THREE_NS from 'three'\n\n/**\n * Create GSAP-animatable props proxy for an element.\n * For video elements, pass the videoElement to enable currentTime animation.\n */\ninterface FrameAccurateSource {\n seekTo: (tSec: number) => Promise<void>\n}\n\nexport function createElementProps(\n _THREE: typeof THREE_NS,\n mesh: THREE_NS.Mesh,\n initialX: number,\n initialY: number,\n initialOpacity = 1,\n videoElement: HTMLVideoElement | null = null,\n videoSource: FrameAccurateSource | null = null,\n videoTexture: THREE_NS.Texture | null = null,\n) {\n // Capture base scale (set by renderer for resolution scaling)\n const baseScaleX = mesh.scale.x\n const baseScaleY = mesh.scale.y\n\n const state: Record<string, any> = {\n x: initialX,\n y: initialY,\n z: 0,\n opacity: initialOpacity,\n scale: 1,\n scaleX: 1,\n scaleY: 1,\n rotation: 0,\n rotationX: 0,\n rotationY: 0,\n zIndex: mesh.userData.zIndex ?? 0,\n // Video-specific properties (only meaningful if videoElement is provided)\n currentTime: videoElement ? videoElement.currentTime : 0,\n playing: false,\n startOffset: 0,\n }\n\n const updateMeshPosition = () => {\n mesh.position.x = state.x\n mesh.position.y = -state.y\n mesh.position.z = state.z\n }\n\n const updateMeshTransform = () => {\n mesh.scale.set(\n baseScaleX * state.scale * state.scaleX,\n baseScaleY * state.scale * state.scaleY,\n 1,\n )\n mesh.rotation.set(\n (state.rotationX * Math.PI) / 180,\n (state.rotationY * Math.PI) / 180,\n (state.rotation * Math.PI) / 180,\n )\n }\n\n const updateMeshOpacity = () => {\n const mat = mesh.material as THREE_NS.MeshBasicMaterial\n mat.opacity = state.opacity\n mat.needsUpdate = true\n }\n\n const updateVideoPlayback = () => {\n if (!videoElement) return\n\n // Video plays only if:\n // 1. It is marked as 'playing' (active in timeline)\n // 2. The global timeline is NOT paused\n const vos = (window as any).__vos__\n const shouldPlay = state.playing && !vos?.isPaused\n\n if (shouldPlay) {\n if (videoElement.paused) {\n const timeDiff = Math.abs(videoElement.currentTime - state.currentTime)\n if (timeDiff > 0.5) {\n videoElement.currentTime = state.currentTime\n }\n videoElement.play().catch(() => {})\n }\n } else {\n videoElement.pause()\n if (Math.abs(videoElement.currentTime - state.currentTime) > 0.05) {\n videoElement.currentTime = state.currentTime\n }\n }\n }\n\n // Register callback for global pause/resume\n if (videoElement) {\n const vos = (window as any).__vos__\n if (vos?.videoCallbacks) {\n vos.videoCallbacks.add(updateVideoPlayback)\n }\n }\n\n const updateVideoCurrentTime = () => {\n // Frame-accurate path: decode the exact frame and register the decode so\n // waitForVideosReady() awaits it (deterministic export/scrub).\n if (videoSource) {\n const vos = (window as any).__vos__\n const p = videoSource\n .seekTo(state.currentTime)\n .then(() => {\n if (videoTexture) videoTexture.needsUpdate = true\n })\n .catch((e: unknown) => console.error('[vos] frame decode failed', e))\n vos?.registerDecode?.(p)\n return\n }\n // Legacy HTMLVideoElement path.\n if (!videoElement) return\n const vos = (window as any).__vos__\n if (!state.playing || vos?.isPaused) {\n videoElement.currentTime = state.currentTime\n }\n }\n\n return new Proxy(state, {\n set(target, prop, value) {\n target[prop as string] = value\n switch (prop) {\n case 'x':\n case 'y':\n case 'z':\n updateMeshPosition()\n break\n case 'scale':\n case 'scaleX':\n case 'scaleY':\n case 'rotation':\n case 'rotationX':\n case 'rotationY':\n updateMeshTransform()\n break\n case 'opacity':\n updateMeshOpacity()\n break\n case 'zIndex':\n mesh.renderOrder = value\n mesh.userData.zIndex = value\n break\n case 'currentTime':\n updateVideoCurrentTime()\n break\n case 'playing':\n case 'startOffset':\n updateVideoPlayback()\n break\n }\n return true\n },\n get(target, prop) {\n if (prop === 'duration' && videoElement) {\n return videoElement.duration\n }\n return target[prop as string]\n },\n })\n}\n","import { AssetCache } from '../assetCache'\nimport type * as THREE_NS from 'three'\n\n/**\n * Load and render an image element\n */\nexport async function renderImageElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { src, size = {} } = element\n\n let img: HTMLImageElement\n const cached = AssetCache.getImage(src)\n if (cached) {\n img = cached.element\n } else {\n img = new Image()\n img.crossOrigin = 'anonymous'\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = reject\n img.src = src\n })\n }\n\n let width = size.width ?? img.naturalWidth\n let height = size.height ?? img.naturalHeight\n\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n const targetHeight =\n typeof size.height === 'number' ? size.height : img.naturalHeight\n width = (img.naturalWidth / img.naturalHeight) * targetHeight\n height = targetHeight\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n const targetWidth =\n typeof size.width === 'number' ? size.width : img.naturalWidth\n height = (img.naturalHeight / img.naturalWidth) * targetWidth\n width = targetWidth\n }\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const ctx = canvas.getContext('2d')!\n\n const fit = size.fit ?? 'fill'\n if (fit === 'contain' || fit === 'cover') {\n const scale =\n fit === 'contain'\n ? Math.min(width / img.naturalWidth, height / img.naturalHeight)\n : Math.max(width / img.naturalWidth, height / img.naturalHeight)\n const drawWidth = img.naturalWidth * scale\n const drawHeight = img.naturalHeight * scale\n const offsetX = (width - drawWidth) / 2\n const offsetY = (height - drawHeight) / 2\n ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight)\n } else {\n ctx.drawImage(img, 0, 0, width, height)\n }\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(width, height)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, width, height }\n}\n","import { AssetCache } from '../assetCache'\nimport type * as THREE_NS from 'three'\n\n/**\n * Load and render an SVG element\n */\nexport async function renderSVGElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { src, size = {}, colors = {} } = element\n\n let svgContent = AssetCache.getSVG(src)\n if (!svgContent) {\n if (src.startsWith('http') || src.startsWith('/')) {\n const response = await fetch(src)\n svgContent = await response.text()\n } else {\n svgContent = src\n }\n }\n\n Object.entries(colors).forEach(([selector, color]) => {\n const regex = new RegExp(`(${selector}[^>]*)(fill|stroke)=\"[^\"]*\"`, 'g')\n svgContent = svgContent!.replace(regex, `$1$2=\"${color}\"`)\n })\n\n const parser = new DOMParser()\n const svgDoc = parser.parseFromString(svgContent!, 'image/svg+xml')\n const svgElement = svgDoc.documentElement\n\n const viewBox = svgElement.getAttribute('viewBox')\n let svgWidth = parseFloat(svgElement.getAttribute('width') || '') || 100\n let svgHeight = parseFloat(svgElement.getAttribute('height') || '') || 100\n\n if (viewBox) {\n const [, , vbW, vbH] = viewBox.split(' ').map(Number)\n svgWidth = vbW || svgWidth\n svgHeight = vbH || svgHeight\n }\n\n let width = size.width ?? svgWidth\n let height = size.height ?? svgHeight\n\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n width = (svgWidth / svgHeight) * size.height\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n height = (svgHeight / svgWidth) * size.width\n }\n\n svgElement.setAttribute('width', String(width))\n svgElement.setAttribute('height', String(height))\n const updatedSvg = new XMLSerializer().serializeToString(svgElement)\n\n const blob = new Blob([updatedSvg], { type: 'image/svg+xml' })\n const url = URL.createObjectURL(blob)\n\n const img = new Image()\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = reject\n img.src = url\n })\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const ctx = canvas.getContext('2d')!\n ctx.drawImage(img, 0, 0, width, height)\n\n URL.revokeObjectURL(url)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(width, height)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, width, height }\n}\n","import type * as THREE_NS from 'three'\n\n/**\n * Render text to canvas and create textured plane\n */\nexport function renderTextElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { content, font = {} } = element\n const fontSize = font.size ?? 24\n const fontFamily = font.family ?? 'Inter, system-ui, sans-serif'\n const fontWeight = font.weight ?? 'normal'\n const fontStyle = font.style ?? 'normal'\n const color = font.color ?? '#ffffff'\n const align = font.align ?? 'left'\n const letterSpacing = font.letterSpacing ?? 0\n const lineHeight = font.lineHeight ?? 1.2\n\n const canvas = document.createElement('canvas')\n const ctx = canvas.getContext('2d')!\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`\n ctx.font = fontString\n\n const lines = content.split('\\n')\n let maxWidth = 0\n lines.forEach((line: string) => {\n let w = 0\n for (let i = 0; i < line.length; i++) {\n w += ctx.measureText(line[i]).width\n if (i < line.length - 1) w += letterSpacing\n }\n if (w > maxWidth) maxWidth = w\n })\n\n const totalHeight = lines.length * fontSize * lineHeight\n const padding =\n Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 10\n const canvasWidth = Math.ceil(maxWidth + padding * 2)\n const canvasHeight = Math.ceil(totalHeight + padding * 2)\n\n canvas.width = canvasWidth\n canvas.height = canvasHeight\n ctx.font = fontString\n ctx.textBaseline = 'top'\n ctx.textAlign = align as CanvasTextAlign\n ctx.clearRect(0, 0, canvasWidth, canvasHeight)\n\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color\n ctx.shadowBlur = element.shadow.blur\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0\n }\n\n let textX = padding\n if (align === 'center') textX = canvasWidth / 2\n else if (align === 'right') textX = canvasWidth - padding\n\n lines.forEach((line: string, i: number) => {\n const y = padding + i * fontSize * lineHeight\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color\n ctx.lineWidth = element.stroke.width\n ctx.lineJoin = 'round'\n ctx.strokeText(line, textX, y)\n }\n ctx.fillStyle = color\n ctx.fillText(line, textX, y)\n })\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, canvas, width: canvasWidth, height: canvasHeight }\n}\n\n/**\n * Render a single text segment (char/word) to canvas and create mesh\n */\nexport function renderTextSegment(\n text: string,\n font: any,\n element: any,\n THREE: typeof THREE_NS,\n) {\n const fontSize = font.size ?? 24\n const fontFamily = font.family ?? 'Inter, system-ui, sans-serif'\n const fontWeight = font.weight ?? 'normal'\n const fontStyle = font.style ?? 'normal'\n const color = font.color ?? '#ffffff'\n\n const canvas = document.createElement('canvas')\n const ctx = canvas.getContext('2d')!\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`\n ctx.font = fontString\n\n const metrics = ctx.measureText(text)\n const padding =\n Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 4\n const canvasWidth = Math.ceil(metrics.width + padding * 2)\n const canvasHeight = Math.ceil(fontSize * 1.4 + padding * 2)\n\n canvas.width = canvasWidth\n canvas.height = canvasHeight\n ctx.font = fontString\n ctx.textBaseline = 'top'\n ctx.textAlign = 'left'\n\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color\n ctx.shadowBlur = element.shadow.blur\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0\n }\n\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color\n ctx.lineWidth = element.stroke.width\n ctx.lineJoin = 'round'\n ctx.strokeText(text, padding, padding)\n }\n ctx.fillStyle = color\n ctx.fillText(text, padding, padding)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight)\n const mesh = new THREE.Mesh(geometry, material)\n\n return {\n mesh,\n width: canvasWidth,\n height: canvasHeight,\n textWidth: metrics.width,\n }\n}\n\n/**\n * Render split text - creates multiple meshes (one per char/word/line)\n */\nexport function renderSplitTextElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { content, font = {}, split } = element\n const splitType = split?.type ?? 'chars'\n const letterSpacing = font.letterSpacing ?? 0\n\n let segments: string[]\n if (splitType === 'chars') {\n segments = content.split('')\n } else if (splitType === 'words') {\n segments = content.split(/\\s+/)\n } else {\n segments = content.split('\\n')\n }\n\n const meshes: any[] = []\n let totalWidth = 0\n\n segments.forEach((text: string, i: number) => {\n if (text.length === 0) return\n\n const result = renderTextSegment(text, font, element, THREE)\n meshes.push({\n mesh: result.mesh,\n width: result.width,\n height: result.height,\n textWidth: result.textWidth,\n text,\n })\n totalWidth += result.textWidth\n if (i < segments.length - 1) {\n totalWidth +=\n splitType === 'words' ? (font.size ?? 24) * 0.4 : letterSpacing\n }\n })\n\n const totalHeight = meshes[0]?.height ?? 0\n let currentX = -totalWidth / 2\n\n meshes.forEach((item: any, i: number) => {\n item.offsetX = currentX + item.textWidth / 2\n item.offsetY = 0\n currentX += item.textWidth\n if (i < meshes.length - 1) {\n currentX +=\n splitType === 'words' ? (font.size ?? 24) * 0.4 : letterSpacing\n }\n })\n\n return { meshes, totalWidth, totalHeight }\n}\n","/**\n * Pure sample-indexing helpers for frame-accurate video seeking.\n *\n * Separated from the WebCodecs glue so the tricky part — selecting the right\n * decode range for a presentation time, given B-frames — is unit-testable\n * without a browser. (Feeding samples in CTS/presentation order instead of\n * decode order is what produced `EncodingError` in the S2 spike; these helpers\n * encode the correct decode-order GOP selection.)\n */\n\nexport interface SampleMeta {\n /** Composition (presentation) timestamp in seconds. */\n cts: number\n /** Whether this is a sync sample (keyframe / IDR). */\n isSync: boolean\n}\n\n/** Decode-order indices of all keyframes, ascending. */\nexport function buildSyncIndices(samples: SampleMeta[]): number[] {\n const out: number[] = []\n for (let i = 0; i < samples.length; i++) if (samples[i].isSync) out.push(i)\n return out\n}\n\n/**\n * Decode-order index of the sample whose CTS is the largest ≤ t.\n * Samples are in DECODE order (not sorted by CTS), so this is a linear scan.\n */\nexport function targetDecodeIndex(samples: SampleMeta[], tSec: number): number {\n let di = 0\n let best = -Infinity\n for (let i = 0; i < samples.length; i++) {\n const c = samples[i].cts\n if (c <= tSec && c > best) {\n best = c\n di = i\n }\n }\n return di\n}\n\n/**\n * GOP bounds [ki, ni) in decode order that contain `targetDI`:\n * ki = keyframe at/before targetDI, ni = next keyframe after it (or end).\n * Decode samples[ki..ni-1] in order, then select the output by PTS.\n */\nexport function gopBounds(\n syncIndices: number[],\n totalSamples: number,\n targetDI: number,\n): [number, number] {\n let ki = 0\n for (const si of syncIndices) {\n if (si <= targetDI) ki = si\n else break\n }\n let ni = totalSamples\n for (const si of syncIndices) {\n if (si > targetDI) {\n ni = si\n break\n }\n }\n return [ki, ni]\n}\n","/**\n * Frame-accurate video source: WebCodecs `VideoDecoder` + mp4box demux.\n *\n * `seekTo(t)` decodes the exact frame at presentation time `t` (deterministically,\n * including B-frames) and draws it to `canvas` — which the caller wraps in a\n * THREE.CanvasTexture. This replaces `HTMLVideoElement.currentTime` sync, which is\n * NOT frame-accurate (audio-clock-backed, keyframe-snaps, async).\n *\n * Validated by the S2 spike (deterministic + ~12ms/seek @1080p). mp4box is loaded\n * from esm.sh at runtime (consistent with vos's CDN addon strategy; keeps the\n * injectable elements bundle lean). Requires a secure context with WebCodecs.\n */\nimport {\n buildSyncIndices,\n gopBounds,\n targetDecodeIndex,\n type SampleMeta,\n} from './sampleIndex'\n\nconst MP4BOX_URL = 'https://esm.sh/mp4box@0.5.2'\n\nlet mp4boxPromise: Promise<any> | null = null\nfunction loadMp4Box(): Promise<any> {\n if (!mp4boxPromise) {\n mp4boxPromise = import(/* @vite-ignore */ MP4BOX_URL).then((m) => m.default ?? m)\n }\n return mp4boxPromise\n}\n\ninterface DecodeSample extends SampleMeta {\n chunk: EncodedVideoChunk\n}\n\nexport function isFrameAccurateSupported(): boolean {\n return typeof VideoDecoder !== 'undefined' && typeof EncodedVideoChunk !== 'undefined'\n}\n\nexport class FrameAccurateVideoSource {\n readonly canvas: HTMLCanvasElement\n private ctx: CanvasRenderingContext2D\n private decoder!: VideoDecoder\n private samples: DecodeSample[] = [] // DECODE order (as delivered by mp4box)\n private syncIndices: number[] = []\n private onFrame: ((f: VideoFrame) => void) | null = null\n private lastError: unknown = null\n durationSec = 0\n fps = 30\n codedWidth = 0\n codedHeight = 0\n\n private constructor() {\n this.canvas = document.createElement('canvas')\n this.ctx = this.canvas.getContext('2d')!\n }\n\n static async create(src: string): Promise<FrameAccurateVideoSource> {\n const self = new FrameAccurateVideoSource()\n self.decoder = new VideoDecoder({\n output: (f) => self.onFrame?.(f),\n error: (e) => {\n self.lastError = e\n console.error('[vos] VideoDecoder error', e)\n },\n })\n const buf = await fetch(src).then((r) => {\n if (!r.ok) throw new Error(`[vos] failed to fetch video: ${src} (${r.status})`)\n return r.arrayBuffer()\n })\n await self.demux(buf)\n self.canvas.width = self.codedWidth\n self.canvas.height = self.codedHeight\n await self.seekTo(0)\n return self\n }\n\n private async demux(data: ArrayBuffer): Promise<void> {\n const MP4Box = await loadMp4Box()\n return new Promise<void>((resolve, reject) => {\n const file = MP4Box.createFile()\n file.onError = (e: unknown) => reject(new Error(String(e)))\n file.onReady = (info: any) => {\n const track = info.videoTracks?.[0]\n if (!track) return reject(new Error('[vos] no video track in mp4'))\n this.codedWidth = track.video.width\n this.codedHeight = track.video.height\n this.durationSec = info.duration / info.timescale\n this.fps = track.nb_samples / this.durationSec || 30\n this.decoder.configure({\n codec: track.codec,\n description: this.getDescription(MP4Box, file, track.id),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n })\n file.setExtractionOptions(track.id, null, { nbSamples: Infinity })\n file.start()\n }\n file.onSamples = (_id: number, _user: unknown, arr: any[]) => {\n for (const s of arr) {\n this.samples.push({\n cts: s.cts / s.timescale,\n isSync: !!s.is_sync,\n chunk: new EncodedVideoChunk({\n type: s.is_sync ? 'key' : 'delta',\n timestamp: Math.round((s.cts / s.timescale) * 1e6),\n duration: Math.round((s.duration / s.timescale) * 1e6),\n data: s.data,\n }),\n })\n }\n }\n ;(data as any).fileStart = 0\n file.appendBuffer(data)\n file.flush()\n // Keep DECODE order (do NOT sort by cts) — B-frames need decode order.\n queueMicrotask(() => {\n if (!this.samples.length) return reject(new Error('[vos] no samples extracted'))\n this.syncIndices = buildSyncIndices(this.samples)\n resolve()\n })\n })\n }\n\n private getDescription(MP4Box: any, file: any, trackId: number): Uint8Array {\n const trak = file.getTrackById(trackId)\n for (const entry of trak.mdia.minf.stbl.stsd.entries) {\n const box = entry.avcC ?? entry.hvcC ?? entry.vpcC ?? entry.av1C\n if (box) {\n const stream = new MP4Box.DataStream(undefined, 0, MP4Box.DataStream.BIG_ENDIAN)\n box.write(stream)\n return new Uint8Array(stream.buffer, 8) // strip 8-byte box header\n }\n }\n throw new Error('[vos] no codec description (avcC/hvcC/vpcC/av1C) found')\n }\n\n /**\n * Decode the exact frame at presentation time `t` and draw it to `canvas`.\n * Decodes the whole GOP (keyframe → next keyframe) in decode order, then\n * selects the output by PTS. Returns when the canvas is updated.\n */\n async seekTo(tSec: number): Promise<void> {\n if (!this.samples.length) return\n const targetDI = targetDecodeIndex(this.samples, tSec)\n const [ki, ni] = gopBounds(this.syncIndices, this.samples.length, targetDI)\n const targetTs = Math.round(this.samples[targetDI].cts * 1e6)\n\n this.lastError = null\n const collected: VideoFrame[] = []\n this.onFrame = (f) => collected.push(f)\n for (let i = ki; i < ni; i++) this.decoder.decode(this.samples[i].chunk)\n await this.decoder.flush()\n this.onFrame = null\n if (this.lastError) throw this.lastError\n\n let best: VideoFrame | undefined\n let bestD = Infinity\n for (const f of collected) {\n const d = Math.abs(f.timestamp - targetTs)\n if (d < bestD) {\n bestD = d\n best = f\n }\n }\n if (best) this.ctx.drawImage(best, 0, 0)\n for (const f of collected) f.close()\n }\n\n dispose(): void {\n try {\n this.decoder.close()\n } catch {\n // already closed\n }\n }\n}\n","import { AssetCache } from '../assetCache'\nimport {\n FrameAccurateVideoSource,\n isFrameAccurateSupported,\n} from '../video/FrameAccurateVideoSource'\nimport type * as THREE_NS from 'three'\n\n/** Resolve element dimensions from an intrinsic w/h + optional size config (with 'auto'). */\nfunction resolveSize(size: any, intrinsicW: number, intrinsicH: number) {\n let width = size.width ?? intrinsicW\n let height = size.height ?? intrinsicH\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n const targetHeight = typeof size.height === 'number' ? size.height : intrinsicH\n width = (intrinsicW / intrinsicH) * targetHeight\n height = targetHeight\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n const targetWidth = typeof size.width === 'number' ? size.width : intrinsicW\n height = (intrinsicH / intrinsicW) * targetWidth\n width = targetWidth\n }\n return { width, height }\n}\n\nfunction applyTextureSettings(texture: THREE_NS.Texture, THREE: typeof THREE_NS) {\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.format = THREE.RGBAFormat\n texture.generateMipmaps = false\n texture.colorSpace = THREE.SRGBColorSpace\n}\n\n/**\n * Load and render a video element.\n *\n * `frameSource`:\n * - 'html5' (default): HTMLVideoElement + VideoTexture (legacy; currentTime sync, NOT frame-accurate)\n * - 'webcodecs': frame-accurate WebCodecs decode (deterministic export/scrub)\n * - 'auto': webcodecs when supported, else html5\n */\nexport async function renderVideoElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const {\n src,\n size = {},\n loop = true,\n muted = true,\n playbackRate = 1,\n startTime = 0,\n frameSource = 'html5',\n } = element\n\n const useWebCodecs =\n frameSource === 'webcodecs' ||\n (frameSource === 'auto' && isFrameAccurateSupported())\n\n // ---- Frame-accurate WebCodecs path ----------------------------------------\n if (useWebCodecs) {\n const source = await FrameAccurateVideoSource.create(src)\n const { width, height } = resolveSize(size, source.codedWidth, source.codedHeight)\n\n const texture = new THREE.CanvasTexture(source.canvas)\n applyTextureSettings(texture, THREE)\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material)\n mesh.userData.videoSource = source\n mesh.userData.texture = texture\n\n // initial frame already drawn at t=0 by create()\n texture.needsUpdate = true\n\n return { mesh, width, height, video: null, videoSource: source, texture }\n }\n\n // ---- Legacy HTMLVideoElement path -----------------------------------------\n let video: HTMLVideoElement\n const cached = AssetCache.getVideo(src)\n if (cached) {\n video = cached.element\n video.loop = loop\n video.muted = muted\n video.playbackRate = playbackRate\n } else {\n video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.src = src\n video.loop = loop\n video.muted = muted\n video.playbackRate = playbackRate\n video.playsInline = true\n video.preload = 'auto'\n\n await new Promise<void>((resolve, reject) => {\n video.oncanplaythrough = () => resolve()\n video.onerror = reject\n video.load()\n })\n }\n\n video.currentTime = startTime\n video.pause()\n\n const { width, height } = resolveSize(size, video.videoWidth, video.videoHeight)\n\n const texture = new THREE.VideoTexture(video)\n applyTextureSettings(texture, THREE)\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(width, height)\n const mesh = new THREE.Mesh(geometry, material)\n\n mesh.userData.video = video\n mesh.userData.texture = texture\n\n return { mesh, width, height, video, videoSource: null, texture }\n}\n","import { preloadAssets } from './assetCache'\nimport { createElementProps } from './createElementProps'\nimport { renderImageElement } from './renderers/image'\nimport { renderSVGElement } from './renderers/svg'\nimport { renderSplitTextElement, renderTextElement } from './renderers/text'\nimport { renderVideoElement } from './renderers/video'\nimport type * as THREE_NS from 'three'\n\n/**\n * Calculate position from config\n */\nfunction calculatePosition(\n position: any,\n resolution: any,\n elementWidth: number,\n elementHeight: number,\n) {\n const { width, height } = resolution\n const halfW = elementWidth / 2\n const halfH = elementHeight / 2\n\n if (typeof position === 'string') {\n switch (position) {\n case 'center':\n return { x: width / 2 - halfW, y: height / 2 - halfH }\n case 'top-left':\n return { x: 0, y: 0 }\n case 'top-center':\n return { x: width / 2 - halfW, y: 0 }\n case 'top-right':\n return { x: width - elementWidth, y: 0 }\n case 'center-left':\n return { x: 0, y: height / 2 - halfH }\n case 'center-right':\n return { x: width - elementWidth, y: height / 2 - halfH }\n case 'bottom-left':\n return { x: 0, y: height - elementHeight }\n case 'bottom-center':\n return { x: width / 2 - halfW, y: height - elementHeight }\n case 'bottom-right':\n return { x: width - elementWidth, y: height - elementHeight }\n default:\n return { x: 0, y: 0 }\n }\n }\n\n const x =\n typeof position.x === 'string'\n ? (parseFloat(position.x) / 100) * width\n : position.x\n const y =\n typeof position.y === 'string'\n ? (parseFloat(position.y) / 100) * height\n : position.y\n return { x, y }\n}\n\n// Design resolution baseline\nconst DESIGN_HEIGHT = 1080\n\n/**\n * Render all elements to a dedicated overlay scene with pixel-space camera.\n */\nexport async function renderElements(\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n THREE: typeof THREE_NS,\n) {\n const getScene = (config: any) => overlayScenes[config.zIndex ?? 100]\n await preloadAssets(elementsConfig)\n\n const elementMap = new Map()\n const resolutionScale = resolution.height / DESIGN_HEIGHT\n\n for (let i = 0; i < elementsConfig.length; i++) {\n const config = elementsConfig[i]\n const id = config.id ?? `element_${i}`\n\n try {\n let mesh: THREE_NS.Mesh\n let canvas: HTMLCanvasElement | null = null\n let elementWidth = 0\n let elementHeight = 0\n let segments: any = null\n const segmentMeshes: THREE_NS.Mesh[] = []\n let videoElement: HTMLVideoElement | null = null\n let videoSource: any = null\n let videoTexture: THREE_NS.Texture | null = null\n\n if (config.type === 'text' && config.split) {\n const splitResult = renderSplitTextElement(config, resolution, THREE)\n elementWidth = splitResult.totalWidth\n elementHeight = splitResult.totalHeight\n\n const scaledWidth = elementWidth * resolutionScale\n const scaledHeight = elementHeight * resolutionScale\n\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight,\n )\n const basePosX = x - resolution.width / 2 + scaledWidth / 2\n const basePosY = -(y - resolution.height / 2 + scaledHeight / 2)\n\n let transformX = 0\n let transformY = 0\n if (config.transform) {\n transformX = (config.transform.translateX ?? 0) * resolutionScale\n transformY = -((config.transform.translateY ?? 0) * resolutionScale)\n }\n\n const zIndex = config.zIndex ?? 100\n\n segments = splitResult.meshes.map((item: any, si: number) => {\n const segMesh = item.mesh\n segMesh.scale.set(resolutionScale, resolutionScale, 1)\n segMesh.position.x =\n basePosX + item.offsetX * resolutionScale + transformX\n segMesh.position.y =\n basePosY + item.offsetY * resolutionScale + transformY\n segMesh.position.z = 0\n segMesh.renderOrder = zIndex + i * 0.01 + si * 0.001\n\n if (config.opacity !== undefined) {\n segMesh.material.opacity = config.opacity\n }\n\n getScene(config).add(segMesh)\n segmentMeshes.push(segMesh)\n\n return createElementProps(\n THREE,\n segMesh,\n segMesh.position.x,\n -segMesh.position.y,\n config.opacity ?? 1,\n )\n })\n\n mesh = splitResult.meshes[0]?.mesh ?? new THREE.Mesh()\n } else if (config.type === 'text') {\n const result = renderTextElement(config, resolution, THREE)\n mesh = result.mesh\n canvas = result.canvas\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'image') {\n const result = await renderImageElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'svg') {\n const result = await renderSVGElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'video') {\n const result = await renderVideoElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n mesh.userData.video = result.video\n videoElement = result.video\n videoSource = result.videoSource\n videoTexture = result.texture\n videoElement?.pause() // null on the webcodecs path\n } else {\n mesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 }),\n )\n console.warn(`Element type \"${config.type}\" not implemented`)\n }\n\n if (!config.split) {\n const scaledWidth = elementWidth * resolutionScale\n const scaledHeight = elementHeight * resolutionScale\n\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight,\n )\n\n const posX = x - resolution.width / 2 + scaledWidth / 2\n const posY = -(y - resolution.height / 2 + scaledHeight / 2)\n\n mesh.scale.set(resolutionScale, resolutionScale, 1)\n mesh.position.x = posX\n mesh.position.y = posY\n mesh.position.z = 0\n\n const zIndex = config.zIndex ?? 100\n mesh.renderOrder = zIndex + i * 0.01\n\n if (config.opacity !== undefined) {\n ;(mesh.material as THREE_NS.MeshBasicMaterial).opacity =\n config.opacity\n ;(mesh.material as THREE_NS.MeshBasicMaterial).transparent = true\n }\n\n if (config.transform) {\n const t = config.transform\n if (t.translateX) mesh.position.x += t.translateX * resolutionScale\n if (t.translateY) mesh.position.y -= t.translateY * resolutionScale\n if (t.translateZ) mesh.position.z += t.translateZ\n if (t.scale)\n mesh.scale.set(\n t.scale * resolutionScale,\n t.scale * resolutionScale,\n 1,\n )\n if (t.rotateZ || t.rotation) {\n mesh.rotation.z = ((t.rotateZ ?? t.rotation ?? 0) * Math.PI) / 180\n }\n }\n\n getScene(config).add(mesh)\n }\n\n const props = createElementProps(\n THREE,\n mesh,\n mesh.position.x,\n -mesh.position.y,\n config.opacity ?? 1,\n videoElement,\n videoSource,\n videoTexture,\n )\n\n const elementInstance = {\n config,\n mesh,\n node: null,\n props,\n segments,\n setContent: (_content: string) => {\n if (config.type === 'text' && canvas) {\n console.warn('setContent not fully implemented in inline renderer')\n }\n },\n destroy: () => {\n videoSource?.dispose?.()\n const targetScene = getScene(config)\n if (segmentMeshes.length > 0) {\n segmentMeshes.forEach((m) => {\n targetScene.remove(m)\n m.geometry.dispose()\n const mat = m.material as THREE_NS.MeshBasicMaterial\n if (mat.map) mat.map.dispose()\n mat.dispose()\n })\n } else {\n targetScene.remove(mesh)\n mesh.geometry.dispose()\n const mat = mesh.material as THREE_NS.MeshBasicMaterial\n if (mat.map) mat.map.dispose()\n mat.dispose()\n }\n },\n }\n\n elementMap.set(id, elementInstance)\n } catch (error) {\n console.warn(\n `[vos] Failed to render element \"${id}\" (${config.type}):`,\n error,\n )\n // Insert transparent placeholder so layout/animation refs still work\n const fallbackMesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 }),\n )\n getScene(config).add(fallbackMesh)\n const props = createElementProps(THREE, fallbackMesh, 0, 0, 0)\n elementMap.set(id, {\n config,\n mesh: fallbackMesh,\n node: null,\n props,\n segments: null,\n setContent: () => {},\n destroy: () => {\n getScene(config).remove(fallbackMesh)\n fallbackMesh.geometry.dispose()\n fallbackMesh.material.dispose()\n },\n })\n }\n }\n\n return elementMap\n}\n","import { renderElements } from './renderElements'\nimport type * as THREE_NS from 'three'\n\nexport interface VosElements {\n renderElements: (\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n THREE: typeof THREE_NS,\n ) => Promise<Map<string, any>>\n disposeElements: (elementMap: Map<string, any>) => void\n}\n\n/**\n * Factory: create the Vos element system bound to a THREE instance.\n */\nexport function createVosElements(THREE: typeof THREE_NS): VosElements {\n return {\n renderElements: (\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n ) => renderElements(elementsConfig, overlayScenes, resolution, THREE),\n disposeElements: (elementMap: Map<string, any>) => {\n elementMap.forEach((instance) => instance.destroy?.())\n elementMap.clear()\n },\n }\n}\n\nexport { renderElements } from './renderElements'\n"],"mappings":";AAIO,IAAM,aAAa;AAAA,EACxB,QAAQ,oBAAI,IAGV;AAAA,EACF,QAAQ,oBAAI,IASV;AAAA,EACF,aAAa,oBAAI,IAAoB;AAAA,EAErC,SAAS,KAAa;AACpB,WAAO,KAAK,OAAO,IAAI,GAAG,KAAK;AAAA,EACjC;AAAA,EACA,SAAS,KAAa;AACpB,WAAO,KAAK,OAAO,IAAI,GAAG,KAAK;AAAA,EACjC;AAAA,EACA,OAAO,KAAa;AAClB,WAAO,KAAK,YAAY,IAAI,GAAG,KAAK;AAAA,EACtC;AAAA,EAEA,UAAU;AACR,SAAK,OAAO,QAAQ,CAAC,WAAW;AAC9B,aAAO,QAAQ,MAAM;AACrB,aAAO,QAAQ,MAAM;AACrB,aAAO,QAAQ,KAAK;AACpB,UAAI,OAAO,SAAS;AAClB,YAAI,gBAAgB,OAAO,OAAO;AAAA,MACpC;AAAA,IACF,CAAC;AACD,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,MAAM;AAClB,SAAK,YAAY,MAAM;AAAA,EACzB;AACF;AAKA,SAAS,iBAAiB,gBAAuB;AAC/C,QAAM,SAIF;AAAA,IACF,QAAQ,CAAC;AAAA,IACT,QAAQ,CAAC;AAAA,IACT,MAAM,CAAC;AAAA,EACT;AAEA,aAAW,MAAM,gBAAgB;AAC/B,QAAI,GAAG,SAAS,WAAW,GAAG,KAAK;AACjC,aAAO,OAAO,KAAK,GAAG,GAAG;AAAA,IAC3B,WAAW,GAAG,SAAS,WAAW,GAAG,KAAK;AACxC,aAAO,OAAO,KAAK,GAAG,GAAG;AAAA,IAC3B,WAAW,GAAG,SAAS,SAAS,GAAG,KAAK;AACtC,aAAO,KAAK,KAAK,GAAG,GAAG;AAAA,IACzB;AAAA,EACF;AAEA,SAAO,SAAS,CAAC,GAAG,IAAI,IAAI,OAAO,MAAM,CAAC;AAC1C,SAAO,SAAS,CAAC,GAAG,IAAI,IAAI,OAAO,MAAM,CAAC;AAC1C,SAAO,OAAO,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC;AAEtC,SAAO;AACT;AAEA,eAAe,aAAa,KAAa;AACvC,MAAI,WAAW,OAAO,IAAI,GAAG,EAAG;AAEhC,QAAM,MAAM,IAAI,MAAM;AACtB,MAAI,cAAc;AAElB,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,QAAI,SAAS,MAAM,QAAQ;AAC3B,QAAI,UAAU,MAAM;AAClB,cAAQ,KAAK,4BAA4B,GAAG;AAC5C,cAAQ;AAAA,IACV;AACA,QAAI,MAAM;AAAA,EACZ,CAAC;AAED,aAAW,OAAO,IAAI,KAAK;AAAA,IACzB,SAAS;AAAA,IACT,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI;AAAA,EACd,CAAC;AACH;AAEA,eAAe,aAAa,KAAa;AACvC,MAAI,WAAW,OAAO,IAAI,GAAG,EAAG;AAEhC,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM;AAAA,IAC7D;AACA,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,UAAU,IAAI,gBAAgB,IAAI;AAExC,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AACpB,UAAM,QAAQ;AACd,UAAM,cAAc;AACpB,UAAM,UAAU;AAEhB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,mBAAmB,MAAM,QAAQ;AACvC,YAAM,UAAU;AAChB,YAAM,MAAM;AACZ,YAAM,KAAK;AAAA,IACb,CAAC;AAED,UAAM,cAAc;AACpB,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,YAAM,WAAW,MAAM,QAAQ;AAC/B,iBAAW,MAAM,QAAQ,GAAG,GAAG;AAAA,IACjC,CAAC;AAED,UAAM,MAAM;AAEZ,YAAQ,IAAI,oBAAoB,KAAK,aAAa,MAAM,QAAQ;AAEhE,eAAW,OAAO,IAAI,KAAK;AAAA,MACzB,SAAS;AAAA,MACT;AAAA,MACA,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH,SAAS,GAAG;AACV,YAAQ,KAAK,4BAA4B,KAAK,CAAC;AAAA,EACjD;AACF;AAEA,eAAe,WAAW,KAAa;AACrC,MAAI,WAAW,YAAY,IAAI,GAAG,EAAG;AAErC,MAAI;AACF,QAAI,IAAI,WAAW,MAAM,KAAK,IAAI,WAAW,OAAO,GAAG;AACrD,iBAAW,YAAY,IAAI,KAAK,GAAG;AACnC;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,gBAAgB,GAAG;AACpC,YAAM,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC;AAC/B,YAAMA,cAAa,KAAK,MAAM;AAC9B,iBAAW,YAAY,IAAI,KAAKA,WAAU;AAC1C;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG;AAChC,UAAM,aAAa,MAAM,SAAS,KAAK;AACvC,eAAW,YAAY,IAAI,KAAK,UAAU;AAAA,EAC5C,SAAS,GAAG;AACV,YAAQ,KAAK,0BAA0B,KAAK,CAAC;AAC7C,eAAW,YAAY,IAAI,KAAK,EAAE;AAAA,EACpC;AACF;AAMA,eAAsB,cAAc,gBAAuB;AACzD,QAAM,SAAS,iBAAiB,cAAc;AAC9C,QAAM,QAAQ,OAAO,OAAO,SAAS,OAAO,OAAO,SAAS,OAAO,KAAK;AAExE,MAAI,UAAU,EAAG;AAEjB,MAAI,SAAS;AAEb,MAAI,OAAO,WAAW,QAAQ;AAC5B,WAAO,OAAO;AAAA,MACZ,EAAE,MAAM,oBAAoB,QAAQ,GAAG,MAAM;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,sBAAsB,OAC1B,IACA,QACG;AACH,UAAM,GAAG,GAAG;AACZ;AACA,QAAI,OAAO,WAAW,QAAQ;AAC5B,aAAO,OAAO;AAAA,QACZ,EAAE,MAAM,oBAAoB,QAAQ,MAAM;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf,GAAG,OAAO,OAAO,IAAI,CAAC,QAAQ,oBAAoB,cAAc,GAAG,CAAC;AAAA,IACpE,GAAG,OAAO,OAAO,IAAI,CAAC,QAAQ,oBAAoB,cAAc,GAAG,CAAC;AAAA,IACpE,GAAG,OAAO,KAAK,IAAI,CAAC,QAAQ,oBAAoB,YAAY,GAAG,CAAC;AAAA,EAClE;AAEA,QAAM,QAAQ,IAAI,QAAQ;AAC5B;;;ACxMO,SAAS,mBACd,QACA,MACA,UACA,UACA,iBAAiB,GACjB,eAAwC,MACxC,cAA0C,MAC1C,eAAwC,MACxC;AAEA,QAAM,aAAa,KAAK,MAAM;AAC9B,QAAM,aAAa,KAAK,MAAM;AAE9B,QAAM,QAA6B;AAAA,IACjC,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,SAAS;AAAA,IACT,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ,KAAK,SAAS,UAAU;AAAA;AAAA,IAEhC,aAAa,eAAe,aAAa,cAAc;AAAA,IACvD,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAEA,QAAM,qBAAqB,MAAM;AAC/B,SAAK,SAAS,IAAI,MAAM;AACxB,SAAK,SAAS,IAAI,CAAC,MAAM;AACzB,SAAK,SAAS,IAAI,MAAM;AAAA,EAC1B;AAEA,QAAM,sBAAsB,MAAM;AAChC,SAAK,MAAM;AAAA,MACT,aAAa,MAAM,QAAQ,MAAM;AAAA,MACjC,aAAa,MAAM,QAAQ,MAAM;AAAA,MACjC;AAAA,IACF;AACA,SAAK,SAAS;AAAA,MACX,MAAM,YAAY,KAAK,KAAM;AAAA,MAC7B,MAAM,YAAY,KAAK,KAAM;AAAA,MAC7B,MAAM,WAAW,KAAK,KAAM;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM;AAC9B,UAAM,MAAM,KAAK;AACjB,QAAI,UAAU,MAAM;AACpB,QAAI,cAAc;AAAA,EACpB;AAEA,QAAM,sBAAsB,MAAM;AAChC,QAAI,CAAC,aAAc;AAKnB,UAAM,MAAO,OAAe;AAC5B,UAAM,aAAa,MAAM,WAAW,CAAC,KAAK;AAE1C,QAAI,YAAY;AACd,UAAI,aAAa,QAAQ;AACvB,cAAM,WAAW,KAAK,IAAI,aAAa,cAAc,MAAM,WAAW;AACtE,YAAI,WAAW,KAAK;AAClB,uBAAa,cAAc,MAAM;AAAA,QACnC;AACA,qBAAa,KAAK,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACpC;AAAA,IACF,OAAO;AACL,mBAAa,MAAM;AACnB,UAAI,KAAK,IAAI,aAAa,cAAc,MAAM,WAAW,IAAI,MAAM;AACjE,qBAAa,cAAc,MAAM;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAGA,MAAI,cAAc;AAChB,UAAM,MAAO,OAAe;AAC5B,QAAI,KAAK,gBAAgB;AACvB,UAAI,eAAe,IAAI,mBAAmB;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,yBAAyB,MAAM;AAGnC,QAAI,aAAa;AACf,YAAMC,OAAO,OAAe;AAC5B,YAAM,IAAI,YACP,OAAO,MAAM,WAAW,EACxB,KAAK,MAAM;AACV,YAAI,aAAc,cAAa,cAAc;AAAA,MAC/C,CAAC,EACA,MAAM,CAAC,MAAe,QAAQ,MAAM,6BAA6B,CAAC,CAAC;AACtE,MAAAA,MAAK,iBAAiB,CAAC;AACvB;AAAA,IACF;AAEA,QAAI,CAAC,aAAc;AACnB,UAAM,MAAO,OAAe;AAC5B,QAAI,CAAC,MAAM,WAAW,KAAK,UAAU;AACnC,mBAAa,cAAc,MAAM;AAAA,IACnC;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,OAAO;AAAA,IACtB,IAAI,QAAQ,MAAM,OAAO;AACvB,aAAO,IAAc,IAAI;AACzB,cAAQ,MAAM;AAAA,QACZ,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,6BAAmB;AACnB;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,8BAAoB;AACpB;AAAA,QACF,KAAK;AACH,4BAAkB;AAClB;AAAA,QACF,KAAK;AACH,eAAK,cAAc;AACnB,eAAK,SAAS,SAAS;AACvB;AAAA,QACF,KAAK;AACH,iCAAuB;AACvB;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,8BAAoB;AACpB;AAAA,MACJ;AACA,aAAO;AAAA,IACT;AAAA,IACA,IAAI,QAAQ,MAAM;AAChB,UAAI,SAAS,cAAc,cAAc;AACvC,eAAO,aAAa;AAAA,MACtB;AACA,aAAO,OAAO,IAAc;AAAA,IAC9B;AAAA,EACF,CAAC;AACH;;;AC7JA,eAAsB,mBACpB,SACA,aACA,OACA;AACA,QAAM,EAAE,KAAK,OAAO,CAAC,EAAE,IAAI;AAE3B,MAAI;AACJ,QAAM,SAAS,WAAW,SAAS,GAAG;AACtC,MAAI,QAAQ;AACV,UAAM,OAAO;AAAA,EACf,OAAO;AACL,UAAM,IAAI,MAAM;AAChB,QAAI,cAAc;AAClB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAI,SAAS,MAAM,QAAQ;AAC3B,UAAI,UAAU;AACd,UAAI,MAAM;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,MAAI,QAAQ,KAAK,SAAS,IAAI;AAC9B,MAAI,SAAS,KAAK,UAAU,IAAI;AAEhC,MAAI,KAAK,UAAU,UAAU,KAAK,WAAW,UAAU,KAAK,QAAQ;AAClE,UAAM,eACJ,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,IAAI;AACtD,YAAS,IAAI,eAAe,IAAI,gBAAiB;AACjD,aAAS;AAAA,EACX,WAAW,KAAK,WAAW,UAAU,KAAK,UAAU,UAAU,KAAK,OAAO;AACxE,UAAM,cACJ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,IAAI;AACpD,aAAU,IAAI,gBAAgB,IAAI,eAAgB;AAClD,YAAQ;AAAA,EACV;AAEA,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,MAAM,OAAO,WAAW,IAAI;AAElC,QAAM,MAAM,KAAK,OAAO;AACxB,MAAI,QAAQ,aAAa,QAAQ,SAAS;AACxC,UAAM,QACJ,QAAQ,YACJ,KAAK,IAAI,QAAQ,IAAI,cAAc,SAAS,IAAI,aAAa,IAC7D,KAAK,IAAI,QAAQ,IAAI,cAAc,SAAS,IAAI,aAAa;AACnE,UAAM,YAAY,IAAI,eAAe;AACrC,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,WAAW,QAAQ,aAAa;AACtC,UAAM,WAAW,SAAS,cAAc;AACxC,QAAI,UAAU,KAAK,SAAS,SAAS,WAAW,UAAU;AAAA,EAC5D,OAAO;AACL,QAAI,UAAU,KAAK,GAAG,GAAG,OAAO,MAAM;AAAA,EACxC;AAEA,QAAM,UAAU,IAAI,MAAM,cAAc,MAAM;AAC9C,UAAQ,YAAY,MAAM;AAC1B,UAAQ,YAAY,MAAM;AAC1B,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,KAAK;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AAED,QAAM,WAAW,IAAI,MAAM,cAAc,OAAO,MAAM;AACtD,QAAM,OAAO,IAAI,MAAM,KAAK,UAAU,QAAQ;AAE9C,SAAO,EAAE,MAAM,OAAO,OAAO;AAC/B;;;ACvEA,eAAsB,iBACpB,SACA,aACA,OACA;AACA,QAAM,EAAE,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC,EAAE,IAAI;AAExC,MAAI,aAAa,WAAW,OAAO,GAAG;AACtC,MAAI,CAAC,YAAY;AACf,QAAI,IAAI,WAAW,MAAM,KAAK,IAAI,WAAW,GAAG,GAAG;AACjD,YAAM,WAAW,MAAM,MAAM,GAAG;AAChC,mBAAa,MAAM,SAAS,KAAK;AAAA,IACnC,OAAO;AACL,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU,KAAK,MAAM;AACpD,UAAM,QAAQ,IAAI,OAAO,IAAI,QAAQ,+BAA+B,GAAG;AACvE,iBAAa,WAAY,QAAQ,OAAO,SAAS,KAAK,GAAG;AAAA,EAC3D,CAAC;AAED,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAM,SAAS,OAAO,gBAAgB,YAAa,eAAe;AAClE,QAAM,aAAa,OAAO;AAE1B,QAAM,UAAU,WAAW,aAAa,SAAS;AACjD,MAAI,WAAW,WAAW,WAAW,aAAa,OAAO,KAAK,EAAE,KAAK;AACrE,MAAI,YAAY,WAAW,WAAW,aAAa,QAAQ,KAAK,EAAE,KAAK;AAEvE,MAAI,SAAS;AACX,UAAM,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AACpD,eAAW,OAAO;AAClB,gBAAY,OAAO;AAAA,EACrB;AAEA,MAAI,QAAQ,KAAK,SAAS;AAC1B,MAAI,SAAS,KAAK,UAAU;AAE5B,MAAI,KAAK,UAAU,UAAU,KAAK,WAAW,UAAU,KAAK,QAAQ;AAClE,YAAS,WAAW,YAAa,KAAK;AAAA,EACxC,WAAW,KAAK,WAAW,UAAU,KAAK,UAAU,UAAU,KAAK,OAAO;AACxE,aAAU,YAAY,WAAY,KAAK;AAAA,EACzC;AAEA,aAAW,aAAa,SAAS,OAAO,KAAK,CAAC;AAC9C,aAAW,aAAa,UAAU,OAAO,MAAM,CAAC;AAChD,QAAM,aAAa,IAAI,cAAc,EAAE,kBAAkB,UAAU;AAEnE,QAAM,OAAO,IAAI,KAAK,CAAC,UAAU,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC7D,QAAM,MAAM,IAAI,gBAAgB,IAAI;AAEpC,QAAM,MAAM,IAAI,MAAM;AACtB,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,QAAI,SAAS,MAAM,QAAQ;AAC3B,QAAI,UAAU;AACd,QAAI,MAAM;AAAA,EACZ,CAAC;AAED,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,MAAI,UAAU,KAAK,GAAG,GAAG,OAAO,MAAM;AAEtC,MAAI,gBAAgB,GAAG;AAEvB,QAAM,UAAU,IAAI,MAAM,cAAc,MAAM;AAC9C,UAAQ,YAAY,MAAM;AAC1B,UAAQ,YAAY,MAAM;AAC1B,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,KAAK;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AAED,QAAM,WAAW,IAAI,MAAM,cAAc,OAAO,MAAM;AACtD,QAAM,OAAO,IAAI,MAAM,KAAK,UAAU,QAAQ;AAE9C,SAAO,EAAE,MAAM,OAAO,OAAO;AAC/B;;;ACnFO,SAAS,kBACd,SACA,aACA,OACA;AACA,QAAM,EAAE,SAAS,OAAO,CAAC,EAAE,IAAI;AAC/B,QAAM,WAAW,KAAK,QAAQ;AAC9B,QAAM,aAAa,KAAK,UAAU;AAClC,QAAM,aAAa,KAAK,UAAU;AAClC,QAAM,YAAY,KAAK,SAAS;AAChC,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,gBAAgB,KAAK,iBAAiB;AAC5C,QAAM,aAAa,KAAK,cAAc;AAEtC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,QAAM,aAAa,GAAG,SAAS,IAAI,UAAU,IAAI,QAAQ,MAAM,UAAU;AACzE,MAAI,OAAO;AAEX,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI,WAAW;AACf,QAAM,QAAQ,CAAC,SAAiB;AAC9B,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,WAAK,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE;AAC9B,UAAI,IAAI,KAAK,SAAS,EAAG,MAAK;AAAA,IAChC;AACA,QAAI,IAAI,SAAU,YAAW;AAAA,EAC/B,CAAC;AAED,QAAM,cAAc,MAAM,SAAS,WAAW;AAC9C,QAAM,UACJ,KAAK,IAAI,QAAQ,QAAQ,SAAS,GAAG,QAAQ,QAAQ,QAAQ,CAAC,IAAI,IAAI;AACxE,QAAM,cAAc,KAAK,KAAK,WAAW,UAAU,CAAC;AACpD,QAAM,eAAe,KAAK,KAAK,cAAc,UAAU,CAAC;AAExD,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,MAAI,OAAO;AACX,MAAI,eAAe;AACnB,MAAI,YAAY;AAChB,MAAI,UAAU,GAAG,GAAG,aAAa,YAAY;AAE7C,MAAI,QAAQ,QAAQ;AAClB,QAAI,cAAc,QAAQ,OAAO;AACjC,QAAI,aAAa,QAAQ,OAAO;AAChC,QAAI,gBAAgB,QAAQ,OAAO,WAAW;AAC9C,QAAI,gBAAgB,QAAQ,OAAO,WAAW;AAAA,EAChD;AAEA,MAAI,QAAQ;AACZ,MAAI,UAAU,SAAU,SAAQ,cAAc;AAAA,WACrC,UAAU,QAAS,SAAQ,cAAc;AAElD,QAAM,QAAQ,CAAC,MAAc,MAAc;AACzC,UAAM,IAAI,UAAU,IAAI,WAAW;AACnC,QAAI,QAAQ,QAAQ;AAClB,UAAI,cAAc,QAAQ,OAAO;AACjC,UAAI,YAAY,QAAQ,OAAO;AAC/B,UAAI,WAAW;AACf,UAAI,WAAW,MAAM,OAAO,CAAC;AAAA,IAC/B;AACA,QAAI,YAAY;AAChB,QAAI,SAAS,MAAM,OAAO,CAAC;AAAA,EAC7B,CAAC;AAED,QAAM,UAAU,IAAI,MAAM,cAAc,MAAM;AAC9C,UAAQ,YAAY,MAAM;AAC1B,UAAQ,YAAY,MAAM;AAC1B,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,KAAK;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AAED,QAAM,WAAW,IAAI,MAAM,cAAc,aAAa,YAAY;AAClE,QAAM,OAAO,IAAI,MAAM,KAAK,UAAU,QAAQ;AAE9C,SAAO,EAAE,MAAM,QAAQ,OAAO,aAAa,QAAQ,aAAa;AAClE;AAKO,SAAS,kBACd,MACA,MACA,SACA,OACA;AACA,QAAM,WAAW,KAAK,QAAQ;AAC9B,QAAM,aAAa,KAAK,UAAU;AAClC,QAAM,aAAa,KAAK,UAAU;AAClC,QAAM,YAAY,KAAK,SAAS;AAChC,QAAM,QAAQ,KAAK,SAAS;AAE5B,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,MAAM,OAAO,WAAW,IAAI;AAClC,QAAM,aAAa,GAAG,SAAS,IAAI,UAAU,IAAI,QAAQ,MAAM,UAAU;AACzE,MAAI,OAAO;AAEX,QAAM,UAAU,IAAI,YAAY,IAAI;AACpC,QAAM,UACJ,KAAK,IAAI,QAAQ,QAAQ,SAAS,GAAG,QAAQ,QAAQ,QAAQ,CAAC,IAAI,IAAI;AACxE,QAAM,cAAc,KAAK,KAAK,QAAQ,QAAQ,UAAU,CAAC;AACzD,QAAM,eAAe,KAAK,KAAK,WAAW,MAAM,UAAU,CAAC;AAE3D,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,MAAI,OAAO;AACX,MAAI,eAAe;AACnB,MAAI,YAAY;AAEhB,MAAI,QAAQ,QAAQ;AAClB,QAAI,cAAc,QAAQ,OAAO;AACjC,QAAI,aAAa,QAAQ,OAAO;AAChC,QAAI,gBAAgB,QAAQ,OAAO,WAAW;AAC9C,QAAI,gBAAgB,QAAQ,OAAO,WAAW;AAAA,EAChD;AAEA,MAAI,QAAQ,QAAQ;AAClB,QAAI,cAAc,QAAQ,OAAO;AACjC,QAAI,YAAY,QAAQ,OAAO;AAC/B,QAAI,WAAW;AACf,QAAI,WAAW,MAAM,SAAS,OAAO;AAAA,EACvC;AACA,MAAI,YAAY;AAChB,MAAI,SAAS,MAAM,SAAS,OAAO;AAEnC,QAAM,UAAU,IAAI,MAAM,cAAc,MAAM;AAC9C,UAAQ,YAAY,MAAM;AAC1B,UAAQ,YAAY,MAAM;AAC1B,UAAQ,cAAc;AAEtB,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,KAAK;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AAED,QAAM,WAAW,IAAI,MAAM,cAAc,aAAa,YAAY;AAClE,QAAM,OAAO,IAAI,MAAM,KAAK,UAAU,QAAQ;AAE9C,SAAO;AAAA,IACL;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW,QAAQ;AAAA,EACrB;AACF;AAKO,SAAS,uBACd,SACA,aACA,OACA;AACA,QAAM,EAAE,SAAS,OAAO,CAAC,GAAG,MAAM,IAAI;AACtC,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,gBAAgB,KAAK,iBAAiB;AAE5C,MAAI;AACJ,MAAI,cAAc,SAAS;AACzB,eAAW,QAAQ,MAAM,EAAE;AAAA,EAC7B,WAAW,cAAc,SAAS;AAChC,eAAW,QAAQ,MAAM,KAAK;AAAA,EAChC,OAAO;AACL,eAAW,QAAQ,MAAM,IAAI;AAAA,EAC/B;AAEA,QAAM,SAAgB,CAAC;AACvB,MAAI,aAAa;AAEjB,WAAS,QAAQ,CAAC,MAAc,MAAc;AAC5C,QAAI,KAAK,WAAW,EAAG;AAEvB,UAAM,SAAS,kBAAkB,MAAM,MAAM,SAAS,KAAK;AAC3D,WAAO,KAAK;AAAA,MACV,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AACD,kBAAc,OAAO;AACrB,QAAI,IAAI,SAAS,SAAS,GAAG;AAC3B,oBACE,cAAc,WAAW,KAAK,QAAQ,MAAM,MAAM;AAAA,IACtD;AAAA,EACF,CAAC;AAED,QAAM,cAAc,OAAO,CAAC,GAAG,UAAU;AACzC,MAAI,WAAW,CAAC,aAAa;AAE7B,SAAO,QAAQ,CAAC,MAAW,MAAc;AACvC,SAAK,UAAU,WAAW,KAAK,YAAY;AAC3C,SAAK,UAAU;AACf,gBAAY,KAAK;AACjB,QAAI,IAAI,OAAO,SAAS,GAAG;AACzB,kBACE,cAAc,WAAW,KAAK,QAAQ,MAAM,MAAM;AAAA,IACtD;AAAA,EACF,CAAC;AAED,SAAO,EAAE,QAAQ,YAAY,YAAY;AAC3C;;;ACrMO,SAAS,iBAAiB,SAAiC;AAChE,QAAM,MAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,IAAK,KAAI,QAAQ,CAAC,EAAE,OAAQ,KAAI,KAAK,CAAC;AAC1E,SAAO;AACT;AAMO,SAAS,kBAAkB,SAAuB,MAAsB;AAC7E,MAAI,KAAK;AACT,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,UAAM,IAAI,QAAQ,CAAC,EAAE;AACrB,QAAI,KAAK,QAAQ,IAAI,MAAM;AACzB,aAAO;AACP,WAAK;AAAA,IACP;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,UACd,aACA,cACA,UACkB;AAClB,MAAI,KAAK;AACT,aAAW,MAAM,aAAa;AAC5B,QAAI,MAAM,SAAU,MAAK;AAAA,QACpB;AAAA,EACP;AACA,MAAI,KAAK;AACT,aAAW,MAAM,aAAa;AAC5B,QAAI,KAAK,UAAU;AACjB,WAAK;AACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC,IAAI,EAAE;AAChB;;;AC7CA,IAAM,aAAa;AAEnB,IAAI,gBAAqC;AACzC,SAAS,aAA2B;AAClC,MAAI,CAAC,eAAe;AAClB,oBAAgB;AAAA;AAAA,MAA0B;AAAA,MAAY,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC;AAAA,EAClF;AACA,SAAO;AACT;AAMO,SAAS,2BAAoC;AAClD,SAAO,OAAO,iBAAiB,eAAe,OAAO,sBAAsB;AAC7E;AAEO,IAAM,2BAAN,MAAM,0BAAyB;AAAA,EAC3B;AAAA,EACD;AAAA,EACA;AAAA,EACA,UAA0B,CAAC;AAAA;AAAA,EAC3B,cAAwB,CAAC;AAAA,EACzB,UAA4C;AAAA,EAC5C,YAAqB;AAAA,EAC7B,cAAc;AAAA,EACd,MAAM;AAAA,EACN,aAAa;AAAA,EACb,cAAc;AAAA,EAEN,cAAc;AACpB,SAAK,SAAS,SAAS,cAAc,QAAQ;AAC7C,SAAK,MAAM,KAAK,OAAO,WAAW,IAAI;AAAA,EACxC;AAAA,EAEA,aAAa,OAAO,KAAgD;AAClE,UAAM,OAAO,IAAI,0BAAyB;AAC1C,SAAK,UAAU,IAAI,aAAa;AAAA,MAC9B,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC;AAAA,MAC/B,OAAO,CAAC,MAAM;AACZ,aAAK,YAAY;AACjB,gBAAQ,MAAM,4BAA4B,CAAC;AAAA,MAC7C;AAAA,IACF,CAAC;AACD,UAAM,MAAM,MAAM,MAAM,GAAG,EAAE,KAAK,CAAC,MAAM;AACvC,UAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,gCAAgC,GAAG,KAAK,EAAE,MAAM,GAAG;AAC9E,aAAO,EAAE,YAAY;AAAA,IACvB,CAAC;AACD,UAAM,KAAK,MAAM,GAAG;AACpB,SAAK,OAAO,QAAQ,KAAK;AACzB,SAAK,OAAO,SAAS,KAAK;AAC1B,UAAM,KAAK,OAAO,CAAC;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,MAAM,MAAkC;AACpD,UAAM,SAAS,MAAM,WAAW;AAChC,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAM,OAAO,OAAO,WAAW;AAC/B,WAAK,UAAU,CAAC,MAAe,OAAO,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAC1D,WAAK,UAAU,CAAC,SAAc;AAC5B,cAAM,QAAQ,KAAK,cAAc,CAAC;AAClC,YAAI,CAAC,MAAO,QAAO,OAAO,IAAI,MAAM,6BAA6B,CAAC;AAClE,aAAK,aAAa,MAAM,MAAM;AAC9B,aAAK,cAAc,MAAM,MAAM;AAC/B,aAAK,cAAc,KAAK,WAAW,KAAK;AACxC,aAAK,MAAM,MAAM,aAAa,KAAK,eAAe;AAClD,aAAK,QAAQ,UAAU;AAAA,UACrB,OAAO,MAAM;AAAA,UACb,aAAa,KAAK,eAAe,QAAQ,MAAM,MAAM,EAAE;AAAA,UACvD,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,QACpB,CAAC;AACD,aAAK,qBAAqB,MAAM,IAAI,MAAM,EAAE,WAAW,SAAS,CAAC;AACjE,aAAK,MAAM;AAAA,MACb;AACA,WAAK,YAAY,CAAC,KAAa,OAAgB,QAAe;AAC5D,mBAAW,KAAK,KAAK;AACnB,eAAK,QAAQ,KAAK;AAAA,YAChB,KAAK,EAAE,MAAM,EAAE;AAAA,YACf,QAAQ,CAAC,CAAC,EAAE;AAAA,YACZ,OAAO,IAAI,kBAAkB;AAAA,cAC3B,MAAM,EAAE,UAAU,QAAQ;AAAA,cAC1B,WAAW,KAAK,MAAO,EAAE,MAAM,EAAE,YAAa,GAAG;AAAA,cACjD,UAAU,KAAK,MAAO,EAAE,WAAW,EAAE,YAAa,GAAG;AAAA,cACrD,MAAM,EAAE;AAAA,YACV,CAAC;AAAA,UACH,CAAC;AAAA,QACH;AAAA,MACF;AACC,MAAC,KAAa,YAAY;AAC3B,WAAK,aAAa,IAAI;AACtB,WAAK,MAAM;AAEX,qBAAe,MAAM;AACnB,YAAI,CAAC,KAAK,QAAQ,OAAQ,QAAO,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAC/E,aAAK,cAAc,iBAAiB,KAAK,OAAO;AAChD,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,QAAa,MAAW,SAA6B;AAC1E,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,eAAW,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,SAAS;AACpD,YAAM,MAAM,MAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ,MAAM;AAC5D,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,OAAO,WAAW,QAAW,GAAG,OAAO,WAAW,UAAU;AAC/E,YAAI,MAAM,MAAM;AAChB,eAAO,IAAI,WAAW,OAAO,QAAQ,CAAC;AAAA,MACxC;AAAA,IACF;AACA,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,MAA6B;AACxC,QAAI,CAAC,KAAK,QAAQ,OAAQ;AAC1B,UAAM,WAAW,kBAAkB,KAAK,SAAS,IAAI;AACrD,UAAM,CAAC,IAAI,EAAE,IAAI,UAAU,KAAK,aAAa,KAAK,QAAQ,QAAQ,QAAQ;AAC1E,UAAM,WAAW,KAAK,MAAM,KAAK,QAAQ,QAAQ,EAAE,MAAM,GAAG;AAE5D,SAAK,YAAY;AACjB,UAAM,YAA0B,CAAC;AACjC,SAAK,UAAU,CAAC,MAAM,UAAU,KAAK,CAAC;AACtC,aAAS,IAAI,IAAI,IAAI,IAAI,IAAK,MAAK,QAAQ,OAAO,KAAK,QAAQ,CAAC,EAAE,KAAK;AACvE,UAAM,KAAK,QAAQ,MAAM;AACzB,SAAK,UAAU;AACf,QAAI,KAAK,UAAW,OAAM,KAAK;AAE/B,QAAI;AACJ,QAAI,QAAQ;AACZ,eAAW,KAAK,WAAW;AACzB,YAAM,IAAI,KAAK,IAAI,EAAE,YAAY,QAAQ;AACzC,UAAI,IAAI,OAAO;AACb,gBAAQ;AACR,eAAO;AAAA,MACT;AAAA,IACF;AACA,QAAI,KAAM,MAAK,IAAI,UAAU,MAAM,GAAG,CAAC;AACvC,eAAW,KAAK,UAAW,GAAE,MAAM;AAAA,EACrC;AAAA,EAEA,UAAgB;AACd,QAAI;AACF,WAAK,QAAQ,MAAM;AAAA,IACrB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;ACtKA,SAAS,YAAY,MAAW,YAAoB,YAAoB;AACtE,MAAI,QAAQ,KAAK,SAAS;AAC1B,MAAI,SAAS,KAAK,UAAU;AAC5B,MAAI,KAAK,UAAU,UAAU,KAAK,WAAW,UAAU,KAAK,QAAQ;AAClE,UAAM,eAAe,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AACrE,YAAS,aAAa,aAAc;AACpC,aAAS;AAAA,EACX,WAAW,KAAK,WAAW,UAAU,KAAK,UAAU,UAAU,KAAK,OAAO;AACxE,UAAM,cAAc,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAClE,aAAU,aAAa,aAAc;AACrC,YAAQ;AAAA,EACV;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;AAEA,SAAS,qBAAqB,SAA2B,OAAwB;AAC/E,UAAQ,YAAY,MAAM;AAC1B,UAAQ,YAAY,MAAM;AAC1B,UAAQ,SAAS,MAAM;AACvB,UAAQ,kBAAkB;AAC1B,UAAQ,aAAa,MAAM;AAC7B;AAUA,eAAsB,mBACpB,SACA,aACA,OACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB,IAAI;AAEJ,QAAM,eACJ,gBAAgB,eACf,gBAAgB,UAAU,yBAAyB;AAGtD,MAAI,cAAc;AAChB,UAAM,SAAS,MAAM,yBAAyB,OAAO,GAAG;AACxD,UAAM,EAAE,OAAAC,QAAO,QAAAC,QAAO,IAAI,YAAY,MAAM,OAAO,YAAY,OAAO,WAAW;AAEjF,UAAMC,WAAU,IAAI,MAAM,cAAc,OAAO,MAAM;AACrD,yBAAqBA,UAAS,KAAK;AAEnC,UAAMC,YAAW,IAAI,MAAM,kBAAkB;AAAA,MAC3C,KAAKD;AAAA,MACL,aAAa;AAAA,MACb,YAAY;AAAA,IACd,CAAC;AACD,UAAME,QAAO,IAAI,MAAM,KAAK,IAAI,MAAM,cAAcJ,QAAOC,OAAM,GAAGE,SAAQ;AAC5E,IAAAC,MAAK,SAAS,cAAc;AAC5B,IAAAA,MAAK,SAAS,UAAUF;AAGxB,IAAAA,SAAQ,cAAc;AAEtB,WAAO,EAAE,MAAAE,OAAM,OAAAJ,QAAO,QAAAC,SAAQ,OAAO,MAAM,aAAa,QAAQ,SAAAC,SAAQ;AAAA,EAC1E;AAGA,MAAI;AACJ,QAAM,SAAS,WAAW,SAAS,GAAG;AACtC,MAAI,QAAQ;AACV,YAAQ,OAAO;AACf,UAAM,OAAO;AACb,UAAM,QAAQ;AACd,UAAM,eAAe;AAAA,EACvB,OAAO;AACL,YAAQ,SAAS,cAAc,OAAO;AACtC,UAAM,cAAc;AACpB,UAAM,MAAM;AACZ,UAAM,OAAO;AACb,UAAM,QAAQ;AACd,UAAM,eAAe;AACrB,UAAM,cAAc;AACpB,UAAM,UAAU;AAEhB,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,mBAAmB,MAAM,QAAQ;AACvC,YAAM,UAAU;AAChB,YAAM,KAAK;AAAA,IACb,CAAC;AAAA,EACH;AAEA,QAAM,cAAc;AACpB,QAAM,MAAM;AAEZ,QAAM,EAAE,OAAO,OAAO,IAAI,YAAY,MAAM,MAAM,YAAY,MAAM,WAAW;AAE/E,QAAM,UAAU,IAAI,MAAM,aAAa,KAAK;AAC5C,uBAAqB,SAAS,KAAK;AAEnC,QAAM,WAAW,IAAI,MAAM,kBAAkB;AAAA,IAC3C,KAAK;AAAA,IACL,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AAED,QAAM,WAAW,IAAI,MAAM,cAAc,OAAO,MAAM;AACtD,QAAM,OAAO,IAAI,MAAM,KAAK,UAAU,QAAQ;AAE9C,OAAK,SAAS,QAAQ;AACtB,OAAK,SAAS,UAAU;AAExB,SAAO,EAAE,MAAM,OAAO,QAAQ,OAAO,aAAa,MAAM,QAAQ;AAClE;;;ACpHA,SAAS,kBACP,UACA,YACA,cACA,eACA;AACA,QAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,QAAM,QAAQ,eAAe;AAC7B,QAAM,QAAQ,gBAAgB;AAE9B,MAAI,OAAO,aAAa,UAAU;AAChC,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,SAAS,IAAI,MAAM;AAAA,MACvD,KAAK;AACH,eAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,MACtB,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,EAAE;AAAA,MACtC,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,cAAc,GAAG,EAAE;AAAA,MACzC,KAAK;AACH,eAAO,EAAE,GAAG,GAAG,GAAG,SAAS,IAAI,MAAM;AAAA,MACvC,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,cAAc,GAAG,SAAS,IAAI,MAAM;AAAA,MAC1D,KAAK;AACH,eAAO,EAAE,GAAG,GAAG,GAAG,SAAS,cAAc;AAAA,MAC3C,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,IAAI,OAAO,GAAG,SAAS,cAAc;AAAA,MAC3D,KAAK;AACH,eAAO,EAAE,GAAG,QAAQ,cAAc,GAAG,SAAS,cAAc;AAAA,MAC9D;AACE,eAAO,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,IACJ,OAAO,SAAS,MAAM,WACjB,WAAW,SAAS,CAAC,IAAI,MAAO,QACjC,SAAS;AACf,QAAM,IACJ,OAAO,SAAS,MAAM,WACjB,WAAW,SAAS,CAAC,IAAI,MAAO,SACjC,SAAS;AACf,SAAO,EAAE,GAAG,EAAE;AAChB;AAGA,IAAM,gBAAgB;AAKtB,eAAsB,eACpB,gBACA,eACA,YACA,OACA;AACA,QAAM,WAAW,CAAC,WAAgB,cAAc,OAAO,UAAU,GAAG;AACpE,QAAM,cAAc,cAAc;AAElC,QAAM,aAAa,oBAAI,IAAI;AAC3B,QAAM,kBAAkB,WAAW,SAAS;AAE5C,WAAS,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;AAC9C,UAAM,SAAS,eAAe,CAAC;AAC/B,UAAM,KAAK,OAAO,MAAM,WAAW,CAAC;AAEpC,QAAI;AACF,UAAI;AACJ,UAAI,SAAmC;AACvC,UAAI,eAAe;AACnB,UAAI,gBAAgB;AACpB,UAAI,WAAgB;AACpB,YAAM,gBAAiC,CAAC;AACxC,UAAI,eAAwC;AAC5C,UAAI,cAAmB;AACvB,UAAI,eAAwC;AAE5C,UAAI,OAAO,SAAS,UAAU,OAAO,OAAO;AAC1C,cAAM,cAAc,uBAAuB,QAAQ,YAAY,KAAK;AACpE,uBAAe,YAAY;AAC3B,wBAAgB,YAAY;AAE5B,cAAM,cAAc,eAAe;AACnC,cAAM,eAAe,gBAAgB;AAErC,cAAM,EAAE,GAAG,EAAE,IAAI;AAAA,UACf,OAAO;AAAA,UACP;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,WAAW,IAAI,WAAW,QAAQ,IAAI,cAAc;AAC1D,cAAM,WAAW,EAAE,IAAI,WAAW,SAAS,IAAI,eAAe;AAE9D,YAAI,aAAa;AACjB,YAAI,aAAa;AACjB,YAAI,OAAO,WAAW;AACpB,wBAAc,OAAO,UAAU,cAAc,KAAK;AAClD,uBAAa,GAAG,OAAO,UAAU,cAAc,KAAK;AAAA,QACtD;AAEA,cAAM,SAAS,OAAO,UAAU;AAEhC,mBAAW,YAAY,OAAO,IAAI,CAAC,MAAW,OAAe;AAC3D,gBAAM,UAAU,KAAK;AACrB,kBAAQ,MAAM,IAAI,iBAAiB,iBAAiB,CAAC;AACrD,kBAAQ,SAAS,IACf,WAAW,KAAK,UAAU,kBAAkB;AAC9C,kBAAQ,SAAS,IACf,WAAW,KAAK,UAAU,kBAAkB;AAC9C,kBAAQ,SAAS,IAAI;AACrB,kBAAQ,cAAc,SAAS,IAAI,OAAO,KAAK;AAE/C,cAAI,OAAO,YAAY,QAAW;AAChC,oBAAQ,SAAS,UAAU,OAAO;AAAA,UACpC;AAEA,mBAAS,MAAM,EAAE,IAAI,OAAO;AAC5B,wBAAc,KAAK,OAAO;AAE1B,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA,QAAQ,SAAS;AAAA,YACjB,CAAC,QAAQ,SAAS;AAAA,YAClB,OAAO,WAAW;AAAA,UACpB;AAAA,QACF,CAAC;AAED,eAAO,YAAY,OAAO,CAAC,GAAG,QAAQ,IAAI,MAAM,KAAK;AAAA,MACvD,WAAW,OAAO,SAAS,QAAQ;AACjC,cAAM,SAAS,kBAAkB,QAAQ,YAAY,KAAK;AAC1D,eAAO,OAAO;AACd,iBAAS,OAAO;AAChB,uBAAe,OAAO;AACtB,wBAAgB,OAAO;AAAA,MACzB,WAAW,OAAO,SAAS,SAAS;AAClC,cAAM,SAAS,MAAM,mBAAmB,QAAQ,YAAY,KAAK;AACjE,eAAO,OAAO;AACd,uBAAe,OAAO;AACtB,wBAAgB,OAAO;AAAA,MACzB,WAAW,OAAO,SAAS,OAAO;AAChC,cAAM,SAAS,MAAM,iBAAiB,QAAQ,YAAY,KAAK;AAC/D,eAAO,OAAO;AACd,uBAAe,OAAO;AACtB,wBAAgB,OAAO;AAAA,MACzB,WAAW,OAAO,SAAS,SAAS;AAClC,cAAM,SAAS,MAAM,mBAAmB,QAAQ,YAAY,KAAK;AACjE,eAAO,OAAO;AACd,uBAAe,OAAO;AACtB,wBAAgB,OAAO;AACvB,aAAK,SAAS,QAAQ,OAAO;AAC7B,uBAAe,OAAO;AACtB,sBAAc,OAAO;AACrB,uBAAe,OAAO;AACtB,sBAAc,MAAM;AAAA,MACtB,OAAO;AACL,eAAO,IAAI,MAAM;AAAA,UACf,IAAI,MAAM,cAAc,KAAK,GAAG;AAAA,UAChC,IAAI,MAAM,kBAAkB,EAAE,aAAa,MAAM,SAAS,EAAE,CAAC;AAAA,QAC/D;AACA,gBAAQ,KAAK,iBAAiB,OAAO,IAAI,mBAAmB;AAAA,MAC9D;AAEA,UAAI,CAAC,OAAO,OAAO;AACjB,cAAM,cAAc,eAAe;AACnC,cAAM,eAAe,gBAAgB;AAErC,cAAM,EAAE,GAAG,EAAE,IAAI;AAAA,UACf,OAAO;AAAA,UACP;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEA,cAAM,OAAO,IAAI,WAAW,QAAQ,IAAI,cAAc;AACtD,cAAM,OAAO,EAAE,IAAI,WAAW,SAAS,IAAI,eAAe;AAE1D,aAAK,MAAM,IAAI,iBAAiB,iBAAiB,CAAC;AAClD,aAAK,SAAS,IAAI;AAClB,aAAK,SAAS,IAAI;AAClB,aAAK,SAAS,IAAI;AAElB,cAAM,SAAS,OAAO,UAAU;AAChC,aAAK,cAAc,SAAS,IAAI;AAEhC,YAAI,OAAO,YAAY,QAAW;AAChC;AAAC,UAAC,KAAK,SAAwC,UAC7C,OAAO;AACR,UAAC,KAAK,SAAwC,cAAc;AAAA,QAC/D;AAEA,YAAI,OAAO,WAAW;AACpB,gBAAM,IAAI,OAAO;AACjB,cAAI,EAAE,WAAY,MAAK,SAAS,KAAK,EAAE,aAAa;AACpD,cAAI,EAAE,WAAY,MAAK,SAAS,KAAK,EAAE,aAAa;AACpD,cAAI,EAAE,WAAY,MAAK,SAAS,KAAK,EAAE;AACvC,cAAI,EAAE;AACJ,iBAAK,MAAM;AAAA,cACT,EAAE,QAAQ;AAAA,cACV,EAAE,QAAQ;AAAA,cACV;AAAA,YACF;AACF,cAAI,EAAE,WAAW,EAAE,UAAU;AAC3B,iBAAK,SAAS,KAAM,EAAE,WAAW,EAAE,YAAY,KAAK,KAAK,KAAM;AAAA,UACjE;AAAA,QACF;AAEA,iBAAS,MAAM,EAAE,IAAI,IAAI;AAAA,MAC3B;AAEA,YAAM,QAAQ;AAAA,QACZ;AAAA,QACA;AAAA,QACA,KAAK,SAAS;AAAA,QACd,CAAC,KAAK,SAAS;AAAA,QACf,OAAO,WAAW;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,kBAAkB;AAAA,QACtB;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,YAAY,CAAC,aAAqB;AAChC,cAAI,OAAO,SAAS,UAAU,QAAQ;AACpC,oBAAQ,KAAK,qDAAqD;AAAA,UACpE;AAAA,QACF;AAAA,QACA,SAAS,MAAM;AACb,uBAAa,UAAU;AACvB,gBAAM,cAAc,SAAS,MAAM;AACnC,cAAI,cAAc,SAAS,GAAG;AAC5B,0BAAc,QAAQ,CAAC,MAAM;AAC3B,0BAAY,OAAO,CAAC;AACpB,gBAAE,SAAS,QAAQ;AACnB,oBAAM,MAAM,EAAE;AACd,kBAAI,IAAI,IAAK,KAAI,IAAI,QAAQ;AAC7B,kBAAI,QAAQ;AAAA,YACd,CAAC;AAAA,UACH,OAAO;AACL,wBAAY,OAAO,IAAI;AACvB,iBAAK,SAAS,QAAQ;AACtB,kBAAM,MAAM,KAAK;AACjB,gBAAI,IAAI,IAAK,KAAI,IAAI,QAAQ;AAC7B,gBAAI,QAAQ;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,IAAI,IAAI,eAAe;AAAA,IACpC,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,mCAAmC,EAAE,MAAM,OAAO,IAAI;AAAA,QACtD;AAAA,MACF;AAEA,YAAM,eAAe,IAAI,MAAM;AAAA,QAC7B,IAAI,MAAM,cAAc,KAAK,GAAG;AAAA,QAChC,IAAI,MAAM,kBAAkB,EAAE,aAAa,MAAM,SAAS,EAAE,CAAC;AAAA,MAC/D;AACA,eAAS,MAAM,EAAE,IAAI,YAAY;AACjC,YAAM,QAAQ,mBAAmB,OAAO,cAAc,GAAG,GAAG,CAAC;AAC7D,iBAAW,IAAI,IAAI;AAAA,QACjB;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,UAAU;AAAA,QACV,YAAY,MAAM;AAAA,QAAC;AAAA,QACnB,SAAS,MAAM;AACb,mBAAS,MAAM,EAAE,OAAO,YAAY;AACpC,uBAAa,SAAS,QAAQ;AAC9B,uBAAa,SAAS,QAAQ;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;ACzRO,SAAS,kBAAkB,OAAqC;AACrE,SAAO;AAAA,IACL,gBAAgB,CACd,gBACA,eACA,eACG,eAAe,gBAAgB,eAAe,YAAY,KAAK;AAAA,IACpE,iBAAiB,CAAC,eAAiC;AACjD,iBAAW,QAAQ,CAAC,aAAa,SAAS,UAAU,CAAC;AACrD,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF;AACF;","names":["svgContent","vos","width","height","texture","material","mesh"]}
1
+ {"version":3,"sources":["../src/assetCache.ts","../src/createElementProps.ts","../src/renderers/image.ts","../src/renderers/svg.ts","../src/renderers/text.ts","../src/video/sampleIndex.ts","../src/video/FrameAccurateVideoSource.ts","../src/renderers/video.ts","../src/renderElements.ts","../src/index.ts"],"sourcesContent":["/**\n * Global asset cache - stores preloaded assets by URL.\n * Handles images, videos, and SVGs.\n */\nexport const AssetCache = {\n images: new Map<\n string,\n { element: HTMLImageElement; width: number; height: number }\n >(),\n videos: new Map<\n string,\n {\n element: HTMLVideoElement\n blobUrl?: string\n duration: number\n width: number\n height: number\n }\n >(),\n svgContents: new Map<string, string>(),\n\n getImage(url: string) {\n return this.images.get(url) || null\n },\n getVideo(url: string) {\n return this.videos.get(url) || null\n },\n getSVG(url: string) {\n return this.svgContents.get(url) || null\n },\n\n dispose() {\n this.videos.forEach((cached) => {\n cached.element.pause()\n cached.element.src = ''\n cached.element.load()\n if (cached.blobUrl) {\n URL.revokeObjectURL(cached.blobUrl)\n }\n })\n this.images.clear()\n this.videos.clear()\n this.svgContents.clear()\n },\n}\n\n/**\n * Extract all asset URLs from elements config\n */\nfunction extractAssetUrls(elementsConfig: any[]) {\n const assets: {\n images: string[]\n videos: string[]\n svgs: string[]\n } = {\n images: [],\n videos: [],\n svgs: [],\n }\n\n for (const el of elementsConfig) {\n if (el.type === 'image' && el.src) {\n assets.images.push(el.src)\n } else if (el.type === 'video' && el.src) {\n assets.videos.push(el.src)\n } else if (el.type === 'svg' && el.src) {\n assets.svgs.push(el.src)\n }\n }\n\n assets.images = [...new Set(assets.images)]\n assets.videos = [...new Set(assets.videos)]\n assets.svgs = [...new Set(assets.svgs)]\n\n return assets\n}\n\nasync function preloadImage(url: string) {\n if (AssetCache.images.has(url)) return\n\n const img = new Image()\n img.crossOrigin = 'anonymous'\n\n await new Promise<void>((resolve) => {\n img.onload = () => resolve()\n img.onerror = () => {\n console.warn('Failed to preload image:', url)\n resolve()\n }\n img.src = url\n })\n\n AssetCache.images.set(url, {\n element: img,\n width: img.naturalWidth,\n height: img.naturalHeight,\n })\n}\n\nasync function preloadVideo(url: string) {\n if (AssetCache.videos.has(url)) return\n\n try {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error('Failed to fetch video: ' + response.status)\n }\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n\n const video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.muted = true\n video.playsInline = true\n video.preload = 'auto'\n\n await new Promise<void>((resolve, reject) => {\n video.oncanplaythrough = () => resolve()\n video.onerror = reject\n video.src = blobUrl\n video.load()\n })\n\n video.currentTime = 0\n await new Promise<void>((resolve) => {\n video.onseeked = () => resolve()\n setTimeout(() => resolve(), 100)\n })\n\n video.pause()\n\n console.log('Video preloaded:', url, 'duration:', video.duration)\n\n AssetCache.videos.set(url, {\n element: video,\n blobUrl,\n duration: video.duration,\n width: video.videoWidth,\n height: video.videoHeight,\n })\n } catch (e) {\n console.warn('Failed to preload video:', url, e)\n }\n}\n\nasync function preloadSVG(url: string) {\n if (AssetCache.svgContents.has(url)) return\n\n try {\n if (url.startsWith('<svg') || url.startsWith('<?xml')) {\n AssetCache.svgContents.set(url, url)\n return\n }\n\n if (url.startsWith('data:image/svg')) {\n const base64 = url.split(',')[1]\n const svgContent = atob(base64)\n AssetCache.svgContents.set(url, svgContent)\n return\n }\n\n const response = await fetch(url)\n const svgContent = await response.text()\n AssetCache.svgContents.set(url, svgContent)\n } catch (e) {\n console.warn('Failed to preload SVG:', url, e)\n AssetCache.svgContents.set(url, '')\n }\n}\n\n/**\n * Preload all assets from elements config.\n * Reports progress via postMessage to parent.\n */\nexport async function preloadAssets(elementsConfig: any[]) {\n const assets = extractAssetUrls(elementsConfig)\n const total = assets.images.length + assets.videos.length + assets.svgs.length\n\n if (total === 0) return\n\n let loaded = 0\n\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: 'PRELOAD_PROGRESS', loaded: 0, total },\n '*',\n )\n }\n\n const preloadWithProgress = async (\n fn: (url: string) => Promise<void>,\n url: string,\n ) => {\n await fn(url)\n loaded++\n if (window.parent !== window) {\n window.parent.postMessage(\n { type: 'PRELOAD_PROGRESS', loaded, total },\n '*',\n )\n }\n }\n\n const promises = [\n ...assets.images.map((url) => preloadWithProgress(preloadImage, url)),\n ...assets.videos.map((url) => preloadWithProgress(preloadVideo, url)),\n ...assets.svgs.map((url) => preloadWithProgress(preloadSVG, url)),\n ]\n\n await Promise.all(promises)\n}\n","import type * as THREE_NS from 'three'\n\n/**\n * Create GSAP-animatable props proxy for an element.\n * For video elements, pass the videoElement to enable currentTime animation.\n */\ninterface FrameAccurateSource {\n seekTo: (tSec: number) => Promise<void>\n}\n\nexport function createElementProps(\n _THREE: typeof THREE_NS,\n mesh: THREE_NS.Mesh,\n initialX: number,\n initialY: number,\n initialOpacity = 1,\n videoElement: HTMLVideoElement | null = null,\n videoSource: FrameAccurateSource | null = null,\n videoTexture: THREE_NS.Texture | null = null,\n) {\n // Capture base scale (set by renderer for resolution scaling)\n const baseScaleX = mesh.scale.x\n const baseScaleY = mesh.scale.y\n\n const state: Record<string, any> = {\n x: initialX,\n y: initialY,\n z: 0,\n opacity: initialOpacity,\n scale: 1,\n scaleX: 1,\n scaleY: 1,\n rotation: 0,\n rotationX: 0,\n rotationY: 0,\n zIndex: mesh.userData.zIndex ?? 0,\n // Video-specific properties (only meaningful if videoElement is provided)\n currentTime: videoElement ? videoElement.currentTime : 0,\n playing: false,\n startOffset: 0,\n }\n\n const updateMeshPosition = () => {\n mesh.position.x = state.x\n mesh.position.y = -state.y\n mesh.position.z = state.z\n }\n\n const updateMeshTransform = () => {\n mesh.scale.set(\n baseScaleX * state.scale * state.scaleX,\n baseScaleY * state.scale * state.scaleY,\n 1,\n )\n mesh.rotation.set(\n (state.rotationX * Math.PI) / 180,\n (state.rotationY * Math.PI) / 180,\n (state.rotation * Math.PI) / 180,\n )\n }\n\n const updateMeshOpacity = () => {\n const mat = mesh.material as THREE_NS.MeshBasicMaterial\n mat.opacity = state.opacity\n mat.needsUpdate = true\n }\n\n const updateVideoPlayback = () => {\n if (!videoElement) return\n\n // Video plays only if:\n // 1. It is marked as 'playing' (active in timeline)\n // 2. The global timeline is NOT paused\n const vos = (window as any).__vos__\n const shouldPlay = state.playing && !vos?.isPaused\n\n if (shouldPlay) {\n if (videoElement.paused) {\n const timeDiff = Math.abs(videoElement.currentTime - state.currentTime)\n if (timeDiff > 0.5) {\n videoElement.currentTime = state.currentTime\n }\n videoElement.play().catch(() => {})\n }\n } else {\n videoElement.pause()\n if (Math.abs(videoElement.currentTime - state.currentTime) > 0.05) {\n videoElement.currentTime = state.currentTime\n }\n }\n }\n\n // Register callback for global pause/resume\n if (videoElement) {\n const vos = (window as any).__vos__\n if (vos?.videoCallbacks) {\n vos.videoCallbacks.add(updateVideoPlayback)\n }\n }\n\n const updateVideoCurrentTime = () => {\n // Frame-accurate path: decode the exact frame and register the decode so\n // waitForVideosReady() awaits it (deterministic export/scrub).\n if (videoSource) {\n const vos = (window as any).__vos__\n const p = videoSource\n .seekTo(state.currentTime)\n .then(() => {\n if (videoTexture) videoTexture.needsUpdate = true\n })\n .catch((e: unknown) => console.error('[vos] frame decode failed', e))\n vos?.registerDecode?.(p)\n return\n }\n // Legacy HTMLVideoElement path.\n if (!videoElement) return\n const vos = (window as any).__vos__\n if (!state.playing || vos?.isPaused) {\n videoElement.currentTime = state.currentTime\n }\n }\n\n return new Proxy(state, {\n set(target, prop, value) {\n target[prop as string] = value\n switch (prop) {\n case 'x':\n case 'y':\n case 'z':\n updateMeshPosition()\n break\n case 'scale':\n case 'scaleX':\n case 'scaleY':\n case 'rotation':\n case 'rotationX':\n case 'rotationY':\n updateMeshTransform()\n break\n case 'opacity':\n updateMeshOpacity()\n break\n case 'zIndex':\n mesh.renderOrder = value\n mesh.userData.zIndex = value\n break\n case 'currentTime':\n updateVideoCurrentTime()\n break\n case 'playing':\n case 'startOffset':\n updateVideoPlayback()\n break\n }\n return true\n },\n get(target, prop) {\n if (prop === 'duration' && videoElement) {\n return videoElement.duration\n }\n return target[prop as string]\n },\n })\n}\n","import { AssetCache } from '../assetCache'\nimport type * as THREE_NS from 'three'\n\n/**\n * Load and render an image element\n */\nexport async function renderImageElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { src, size = {} } = element\n\n let img: HTMLImageElement\n const cached = AssetCache.getImage(src)\n if (cached) {\n img = cached.element\n } else {\n img = new Image()\n img.crossOrigin = 'anonymous'\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = reject\n img.src = src\n })\n }\n\n let width = size.width ?? img.naturalWidth\n let height = size.height ?? img.naturalHeight\n\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n const targetHeight =\n typeof size.height === 'number' ? size.height : img.naturalHeight\n width = (img.naturalWidth / img.naturalHeight) * targetHeight\n height = targetHeight\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n const targetWidth =\n typeof size.width === 'number' ? size.width : img.naturalWidth\n height = (img.naturalHeight / img.naturalWidth) * targetWidth\n width = targetWidth\n }\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const ctx = canvas.getContext('2d')!\n\n const fit = size.fit ?? 'fill'\n if (fit === 'contain' || fit === 'cover') {\n const scale =\n fit === 'contain'\n ? Math.min(width / img.naturalWidth, height / img.naturalHeight)\n : Math.max(width / img.naturalWidth, height / img.naturalHeight)\n const drawWidth = img.naturalWidth * scale\n const drawHeight = img.naturalHeight * scale\n const offsetX = (width - drawWidth) / 2\n const offsetY = (height - drawHeight) / 2\n ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight)\n } else {\n ctx.drawImage(img, 0, 0, width, height)\n }\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(width, height)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, width, height }\n}\n","import { AssetCache } from '../assetCache'\nimport type * as THREE_NS from 'three'\n\n/**\n * Load and render an SVG element\n */\nexport async function renderSVGElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { src, size = {}, colors = {} } = element\n\n let svgContent = AssetCache.getSVG(src)\n if (!svgContent) {\n if (src.startsWith('http') || src.startsWith('/')) {\n const response = await fetch(src)\n svgContent = await response.text()\n } else {\n svgContent = src\n }\n }\n\n Object.entries(colors).forEach(([selector, color]) => {\n const regex = new RegExp(`(${selector}[^>]*)(fill|stroke)=\"[^\"]*\"`, 'g')\n svgContent = svgContent!.replace(regex, `$1$2=\"${color}\"`)\n })\n\n const parser = new DOMParser()\n const svgDoc = parser.parseFromString(svgContent!, 'image/svg+xml')\n const svgElement = svgDoc.documentElement\n\n const viewBox = svgElement.getAttribute('viewBox')\n let svgWidth = parseFloat(svgElement.getAttribute('width') || '') || 100\n let svgHeight = parseFloat(svgElement.getAttribute('height') || '') || 100\n\n if (viewBox) {\n const [, , vbW, vbH] = viewBox.split(' ').map(Number)\n svgWidth = vbW || svgWidth\n svgHeight = vbH || svgHeight\n }\n\n let width = size.width ?? svgWidth\n let height = size.height ?? svgHeight\n\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n width = (svgWidth / svgHeight) * size.height\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n height = (svgHeight / svgWidth) * size.width\n }\n\n svgElement.setAttribute('width', String(width))\n svgElement.setAttribute('height', String(height))\n const updatedSvg = new XMLSerializer().serializeToString(svgElement)\n\n const blob = new Blob([updatedSvg], { type: 'image/svg+xml' })\n const url = URL.createObjectURL(blob)\n\n const img = new Image()\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = reject\n img.src = url\n })\n\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const ctx = canvas.getContext('2d')!\n ctx.drawImage(img, 0, 0, width, height)\n\n URL.revokeObjectURL(url)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(width, height)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, width, height }\n}\n","import type * as THREE_NS from 'three'\n\n/**\n * Render text to canvas and create textured plane\n */\nexport function renderTextElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { content, font = {} } = element\n const fontSize = font.size ?? 24\n const fontFamily = font.family ?? 'Inter, system-ui, sans-serif'\n const fontWeight = font.weight ?? 'normal'\n const fontStyle = font.style ?? 'normal'\n const color = font.color ?? '#ffffff'\n const align = font.align ?? 'left'\n const letterSpacing = font.letterSpacing ?? 0\n const lineHeight = font.lineHeight ?? 1.2\n\n const canvas = document.createElement('canvas')\n const ctx = canvas.getContext('2d')!\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`\n ctx.font = fontString\n\n const lines = content.split('\\n')\n let maxWidth = 0\n lines.forEach((line: string) => {\n let w = 0\n for (let i = 0; i < line.length; i++) {\n w += ctx.measureText(line[i]).width\n if (i < line.length - 1) w += letterSpacing\n }\n if (w > maxWidth) maxWidth = w\n })\n\n const totalHeight = lines.length * fontSize * lineHeight\n const padding =\n Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 10\n const canvasWidth = Math.ceil(maxWidth + padding * 2)\n const canvasHeight = Math.ceil(totalHeight + padding * 2)\n\n canvas.width = canvasWidth\n canvas.height = canvasHeight\n ctx.font = fontString\n ctx.textBaseline = 'top'\n ctx.textAlign = align as CanvasTextAlign\n ctx.clearRect(0, 0, canvasWidth, canvasHeight)\n\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color\n ctx.shadowBlur = element.shadow.blur\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0\n }\n\n let textX = padding\n if (align === 'center') textX = canvasWidth / 2\n else if (align === 'right') textX = canvasWidth - padding\n\n lines.forEach((line: string, i: number) => {\n const y = padding + i * fontSize * lineHeight\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color\n ctx.lineWidth = element.stroke.width\n ctx.lineJoin = 'round'\n ctx.strokeText(line, textX, y)\n }\n ctx.fillStyle = color\n ctx.fillText(line, textX, y)\n })\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight)\n const mesh = new THREE.Mesh(geometry, material)\n\n return { mesh, canvas, width: canvasWidth, height: canvasHeight }\n}\n\n/**\n * Render a single text segment (char/word) to canvas and create mesh\n */\nexport function renderTextSegment(\n text: string,\n font: any,\n element: any,\n THREE: typeof THREE_NS,\n) {\n const fontSize = font.size ?? 24\n const fontFamily = font.family ?? 'Inter, system-ui, sans-serif'\n const fontWeight = font.weight ?? 'normal'\n const fontStyle = font.style ?? 'normal'\n const color = font.color ?? '#ffffff'\n\n const canvas = document.createElement('canvas')\n const ctx = canvas.getContext('2d')!\n const fontString = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`\n ctx.font = fontString\n\n const metrics = ctx.measureText(text)\n const padding =\n Math.max(element.stroke?.width ?? 0, element.shadow?.blur ?? 0) * 2 + 4\n const canvasWidth = Math.ceil(metrics.width + padding * 2)\n const canvasHeight = Math.ceil(fontSize * 1.4 + padding * 2)\n\n canvas.width = canvasWidth\n canvas.height = canvasHeight\n ctx.font = fontString\n ctx.textBaseline = 'top'\n ctx.textAlign = 'left'\n\n if (element.shadow) {\n ctx.shadowColor = element.shadow.color\n ctx.shadowBlur = element.shadow.blur\n ctx.shadowOffsetX = element.shadow.offsetX ?? 0\n ctx.shadowOffsetY = element.shadow.offsetY ?? 0\n }\n\n if (element.stroke) {\n ctx.strokeStyle = element.stroke.color\n ctx.lineWidth = element.stroke.width\n ctx.lineJoin = 'round'\n ctx.strokeText(text, padding, padding)\n }\n ctx.fillStyle = color\n ctx.fillText(text, padding, padding)\n\n const texture = new THREE.CanvasTexture(canvas)\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.needsUpdate = true\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n\n const geometry = new THREE.PlaneGeometry(canvasWidth, canvasHeight)\n const mesh = new THREE.Mesh(geometry, material)\n\n return {\n mesh,\n width: canvasWidth,\n height: canvasHeight,\n textWidth: metrics.width,\n }\n}\n\n/**\n * Render split text - creates multiple meshes (one per char/word/line)\n */\nexport function renderSplitTextElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const { content, font = {}, split } = element\n const splitType = split?.type ?? 'chars'\n const letterSpacing = font.letterSpacing ?? 0\n\n let segments: string[]\n if (splitType === 'chars') {\n segments = content.split('')\n } else if (splitType === 'words') {\n segments = content.split(/\\s+/)\n } else {\n segments = content.split('\\n')\n }\n\n const meshes: any[] = []\n let totalWidth = 0\n\n segments.forEach((text: string, i: number) => {\n if (text.length === 0) return\n\n const result = renderTextSegment(text, font, element, THREE)\n meshes.push({\n mesh: result.mesh,\n width: result.width,\n height: result.height,\n textWidth: result.textWidth,\n text,\n })\n totalWidth += result.textWidth\n if (i < segments.length - 1) {\n totalWidth +=\n splitType === 'words' ? (font.size ?? 24) * 0.4 : letterSpacing\n }\n })\n\n const totalHeight = meshes[0]?.height ?? 0\n let currentX = -totalWidth / 2\n\n meshes.forEach((item: any, i: number) => {\n item.offsetX = currentX + item.textWidth / 2\n item.offsetY = 0\n currentX += item.textWidth\n if (i < meshes.length - 1) {\n currentX +=\n splitType === 'words' ? (font.size ?? 24) * 0.4 : letterSpacing\n }\n })\n\n return { meshes, totalWidth, totalHeight }\n}\n","/**\n * Pure sample-indexing helpers for frame-accurate video seeking.\n *\n * Separated from the WebCodecs glue so the tricky part — selecting the right\n * decode range for a presentation time, given B-frames — is unit-testable\n * without a browser. (Feeding samples in CTS/presentation order instead of\n * decode order is what produced `EncodingError` in the S2 spike; these helpers\n * encode the correct decode-order GOP selection.)\n */\n\nexport interface SampleMeta {\n /** Composition (presentation) timestamp in seconds. */\n cts: number\n /** Whether this is a sync sample (keyframe / IDR). */\n isSync: boolean\n}\n\n/** Decode-order indices of all keyframes, ascending. */\nexport function buildSyncIndices(samples: SampleMeta[]): number[] {\n const out: number[] = []\n for (let i = 0; i < samples.length; i++) if (samples[i].isSync) out.push(i)\n return out\n}\n\n/**\n * Decode-order index of the sample whose CTS is the largest ≤ t.\n * Samples are in DECODE order (not sorted by CTS), so this is a linear scan.\n */\nexport function targetDecodeIndex(samples: SampleMeta[], tSec: number): number {\n let di = 0\n let best = -Infinity\n for (let i = 0; i < samples.length; i++) {\n const c = samples[i].cts\n if (c <= tSec && c > best) {\n best = c\n di = i\n }\n }\n return di\n}\n\n/**\n * GOP bounds [ki, ni) in decode order that contain `targetDI`:\n * ki = keyframe at/before targetDI, ni = next keyframe after it (or end).\n * Decode samples[ki..ni-1] in order, then select the output by PTS.\n */\nexport function gopBounds(\n syncIndices: number[],\n totalSamples: number,\n targetDI: number,\n): [number, number] {\n let ki = 0\n for (const si of syncIndices) {\n if (si <= targetDI) ki = si\n else break\n }\n let ni = totalSamples\n for (const si of syncIndices) {\n if (si > targetDI) {\n ni = si\n break\n }\n }\n return [ki, ni]\n}\n","/**\n * Frame-accurate video source: WebCodecs `VideoDecoder` + mp4box demux.\n *\n * `seekTo(t)` decodes the exact frame at presentation time `t` (deterministically,\n * including B-frames) and draws it to `canvas` — which the caller wraps in a\n * THREE.CanvasTexture. This replaces `HTMLVideoElement.currentTime` sync, which is\n * NOT frame-accurate (audio-clock-backed, keyframe-snaps, async).\n *\n * Validated by the S2 spike (deterministic + ~12ms/seek @1080p). mp4box is loaded\n * from esm.sh at runtime (consistent with vos's CDN addon strategy; keeps the\n * injectable elements bundle lean). Requires a secure context with WebCodecs.\n */\nimport {\n buildSyncIndices,\n gopBounds,\n targetDecodeIndex,\n type SampleMeta,\n} from './sampleIndex'\n\nconst MP4BOX_URL = 'https://esm.sh/mp4box@0.5.2'\n\nlet mp4boxPromise: Promise<any> | null = null\nfunction loadMp4Box(): Promise<any> {\n if (!mp4boxPromise) {\n mp4boxPromise = import(/* @vite-ignore */ MP4BOX_URL).then((m) => m.default ?? m)\n }\n return mp4boxPromise\n}\n\ninterface DecodeSample extends SampleMeta {\n chunk: EncodedVideoChunk\n}\n\nexport function isFrameAccurateSupported(): boolean {\n return typeof VideoDecoder !== 'undefined' && typeof EncodedVideoChunk !== 'undefined'\n}\n\nexport class FrameAccurateVideoSource {\n readonly canvas: HTMLCanvasElement\n private ctx: CanvasRenderingContext2D\n private decoder!: VideoDecoder\n private samples: DecodeSample[] = [] // DECODE order (as delivered by mp4box)\n private syncIndices: number[] = []\n private onFrame: ((f: VideoFrame) => void) | null = null\n private lastError: unknown = null\n durationSec = 0\n fps = 30\n codedWidth = 0\n codedHeight = 0\n\n private constructor() {\n this.canvas = document.createElement('canvas')\n this.ctx = this.canvas.getContext('2d')!\n }\n\n static async create(src: string): Promise<FrameAccurateVideoSource> {\n const self = new FrameAccurateVideoSource()\n self.decoder = new VideoDecoder({\n output: (f) => self.onFrame?.(f),\n error: (e) => {\n self.lastError = e\n console.error('[vos] VideoDecoder error', e)\n },\n })\n const buf = await fetch(src).then((r) => {\n if (!r.ok) throw new Error(`[vos] failed to fetch video: ${src} (${r.status})`)\n return r.arrayBuffer()\n })\n await self.demux(buf)\n self.canvas.width = self.codedWidth\n self.canvas.height = self.codedHeight\n await self.seekTo(0)\n return self\n }\n\n private async demux(data: ArrayBuffer): Promise<void> {\n const MP4Box = await loadMp4Box()\n return new Promise<void>((resolve, reject) => {\n const file = MP4Box.createFile()\n let expected = Infinity\n let settled = false\n const finish = () => {\n if (settled) return\n settled = true\n if (!this.samples.length) {\n reject(new Error('[vos] no samples extracted'))\n return\n }\n this.syncIndices = buildSyncIndices(this.samples)\n resolve()\n }\n file.onError = (e: unknown) => {\n if (settled) return\n settled = true\n reject(new Error(String(e)))\n }\n file.onReady = (info: any) => {\n const track = info.videoTracks?.[0]\n if (!track) {\n settled = true\n reject(new Error('[vos] no video track in mp4'))\n return\n }\n this.codedWidth = track.video.width\n this.codedHeight = track.video.height\n this.durationSec = info.duration / info.timescale\n this.fps = track.nb_samples / this.durationSec || 30\n expected = track.nb_samples || Infinity\n this.decoder.configure({\n codec: track.codec,\n description: this.getDescription(MP4Box, file, track.id),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n })\n file.setExtractionOptions(track.id, null, { nbSamples: Infinity })\n file.start()\n }\n file.onSamples = (_id: number, _user: unknown, arr: any[]) => {\n for (const s of arr) {\n // DECODE order preserved (do NOT sort by cts) — B-frames need decode order.\n this.samples.push({\n cts: s.cts / s.timescale,\n isSync: !!s.is_sync,\n chunk: new EncodedVideoChunk({\n type: s.is_sync ? 'key' : 'delta',\n timestamp: Math.round((s.cts / s.timescale) * 1e6),\n duration: Math.round((s.duration / s.timescale) * 1e6),\n data: s.data,\n }),\n })\n }\n if (this.samples.length >= expected) finish()\n }\n ;(data as any).fileStart = 0\n file.appendBuffer(data)\n file.flush()\n // mp4box may deliver samples synchronously (during flush) or across later\n // tasks. Settle shortly after flush with whatever arrived — resolve if any\n // samples were extracted, else reject. Avoids both the race (premature\n // \"no samples\") and hangs.\n setTimeout(finish, 2000)\n })\n }\n\n private getDescription(MP4Box: any, file: any, trackId: number): Uint8Array {\n const trak = file.getTrackById(trackId)\n for (const entry of trak.mdia.minf.stbl.stsd.entries) {\n const box = entry.avcC ?? entry.hvcC ?? entry.vpcC ?? entry.av1C\n if (box) {\n const stream = new MP4Box.DataStream(undefined, 0, MP4Box.DataStream.BIG_ENDIAN)\n box.write(stream)\n return new Uint8Array(stream.buffer, 8) // strip 8-byte box header\n }\n }\n throw new Error('[vos] no codec description (avcC/hvcC/vpcC/av1C) found')\n }\n\n /**\n * Decode the exact frame at presentation time `t` and draw it to `canvas`.\n * Decodes the whole GOP (keyframe → next keyframe) in decode order, then\n * selects the output by PTS. Returns when the canvas is updated.\n */\n async seekTo(tSec: number): Promise<void> {\n if (!this.samples.length) return\n const targetDI = targetDecodeIndex(this.samples, tSec)\n const [ki, ni] = gopBounds(this.syncIndices, this.samples.length, targetDI)\n const targetTs = Math.round(this.samples[targetDI].cts * 1e6)\n\n this.lastError = null\n const collected: VideoFrame[] = []\n this.onFrame = (f) => collected.push(f)\n for (let i = ki; i < ni; i++) this.decoder.decode(this.samples[i].chunk)\n await this.decoder.flush()\n this.onFrame = null\n if (this.lastError) throw this.lastError\n\n let best: VideoFrame | undefined\n let bestD = Infinity\n for (const f of collected) {\n const d = Math.abs(f.timestamp - targetTs)\n if (d < bestD) {\n bestD = d\n best = f\n }\n }\n if (best) this.ctx.drawImage(best, 0, 0)\n for (const f of collected) f.close()\n }\n\n dispose(): void {\n try {\n this.decoder.close()\n } catch {\n // already closed\n }\n }\n}\n","import { AssetCache } from '../assetCache'\nimport {\n FrameAccurateVideoSource,\n isFrameAccurateSupported,\n} from '../video/FrameAccurateVideoSource'\nimport type * as THREE_NS from 'three'\n\n/** Resolve element dimensions from an intrinsic w/h + optional size config (with 'auto'). */\nfunction resolveSize(size: any, intrinsicW: number, intrinsicH: number) {\n let width = size.width ?? intrinsicW\n let height = size.height ?? intrinsicH\n if (size.width === 'auto' && size.height !== 'auto' && size.height) {\n const targetHeight = typeof size.height === 'number' ? size.height : intrinsicH\n width = (intrinsicW / intrinsicH) * targetHeight\n height = targetHeight\n } else if (size.height === 'auto' && size.width !== 'auto' && size.width) {\n const targetWidth = typeof size.width === 'number' ? size.width : intrinsicW\n height = (intrinsicH / intrinsicW) * targetWidth\n width = targetWidth\n }\n return { width, height }\n}\n\nfunction applyTextureSettings(texture: THREE_NS.Texture, THREE: typeof THREE_NS) {\n texture.minFilter = THREE.LinearFilter\n texture.magFilter = THREE.LinearFilter\n texture.format = THREE.RGBAFormat\n texture.generateMipmaps = false\n texture.colorSpace = THREE.SRGBColorSpace\n}\n\n/**\n * Load and render a video element.\n *\n * `frameSource`:\n * - 'html5' (default): HTMLVideoElement + VideoTexture (legacy; currentTime sync, NOT frame-accurate)\n * - 'webcodecs': frame-accurate WebCodecs decode (deterministic export/scrub)\n * - 'auto': webcodecs when supported, else html5\n */\nexport async function renderVideoElement(\n element: any,\n _resolution: any,\n THREE: typeof THREE_NS,\n) {\n const frameSource: string = element.frameSource ?? 'html5'\n const useWebCodecs =\n frameSource === 'webcodecs' ||\n (frameSource === 'auto' && isFrameAccurateSupported())\n\n // ---- Frame-accurate WebCodecs path ----------------------------------------\n // On any failure (non-MP4 container, unsupported codec, decode error) we fall\n // through to the robust HTMLVideoElement path instead of failing the element —\n // black video is never an acceptable outcome.\n if (useWebCodecs) {\n try {\n return await renderWebCodecsVideo(element, THREE)\n } catch (err) {\n console.warn(\n '[vos] frame-accurate video unavailable; falling back to html5 —',\n err instanceof Error ? err.message : err,\n )\n }\n }\n\n return renderHtml5Video(element, THREE)\n}\n\nasync function renderWebCodecsVideo(element: any, THREE: typeof THREE_NS) {\n const { src, size = {} } = element\n const source = await FrameAccurateVideoSource.create(src)\n const { width, height } = resolveSize(size, source.codedWidth, source.codedHeight)\n\n const texture = new THREE.CanvasTexture(source.canvas)\n applyTextureSettings(texture, THREE)\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material)\n mesh.userData.videoSource = source\n mesh.userData.texture = texture\n texture.needsUpdate = true // initial frame drawn at t=0 by create()\n\n return { mesh, width, height, video: null, videoSource: source, texture }\n}\n\n// ---- Legacy HTMLVideoElement path -------------------------------------------\nasync function renderHtml5Video(element: any, THREE: typeof THREE_NS) {\n const {\n src,\n size = {},\n loop = true,\n muted = true,\n playbackRate = 1,\n startTime = 0,\n } = element\n\n let video: HTMLVideoElement\n const cached = AssetCache.getVideo(src)\n if (cached) {\n video = cached.element\n video.loop = loop\n video.muted = muted\n video.playbackRate = playbackRate\n } else {\n video = document.createElement('video')\n video.crossOrigin = 'anonymous'\n video.src = src\n video.loop = loop\n video.muted = muted\n video.playbackRate = playbackRate\n video.playsInline = true\n video.preload = 'auto'\n\n await new Promise<void>((resolve, reject) => {\n video.oncanplaythrough = () => resolve()\n video.onerror = reject\n video.load()\n })\n }\n\n video.currentTime = startTime\n video.pause()\n\n const { width, height } = resolveSize(size, video.videoWidth, video.videoHeight)\n\n const texture = new THREE.VideoTexture(video)\n applyTextureSettings(texture, THREE)\n\n const material = new THREE.MeshBasicMaterial({\n map: texture,\n transparent: true,\n depthWrite: false,\n })\n const mesh = new THREE.Mesh(new THREE.PlaneGeometry(width, height), material)\n mesh.userData.video = video\n mesh.userData.texture = texture\n\n return { mesh, width, height, video, videoSource: null, texture }\n}\n","import { preloadAssets } from './assetCache'\nimport { createElementProps } from './createElementProps'\nimport { renderImageElement } from './renderers/image'\nimport { renderSVGElement } from './renderers/svg'\nimport { renderSplitTextElement, renderTextElement } from './renderers/text'\nimport { renderVideoElement } from './renderers/video'\nimport type * as THREE_NS from 'three'\n\n/**\n * Calculate position from config\n */\nfunction calculatePosition(\n position: any,\n resolution: any,\n elementWidth: number,\n elementHeight: number,\n) {\n const { width, height } = resolution\n const halfW = elementWidth / 2\n const halfH = elementHeight / 2\n\n if (typeof position === 'string') {\n switch (position) {\n case 'center':\n return { x: width / 2 - halfW, y: height / 2 - halfH }\n case 'top-left':\n return { x: 0, y: 0 }\n case 'top-center':\n return { x: width / 2 - halfW, y: 0 }\n case 'top-right':\n return { x: width - elementWidth, y: 0 }\n case 'center-left':\n return { x: 0, y: height / 2 - halfH }\n case 'center-right':\n return { x: width - elementWidth, y: height / 2 - halfH }\n case 'bottom-left':\n return { x: 0, y: height - elementHeight }\n case 'bottom-center':\n return { x: width / 2 - halfW, y: height - elementHeight }\n case 'bottom-right':\n return { x: width - elementWidth, y: height - elementHeight }\n default:\n return { x: 0, y: 0 }\n }\n }\n\n const x =\n typeof position.x === 'string'\n ? (parseFloat(position.x) / 100) * width\n : position.x\n const y =\n typeof position.y === 'string'\n ? (parseFloat(position.y) / 100) * height\n : position.y\n return { x, y }\n}\n\n// Design resolution baseline\nconst DESIGN_HEIGHT = 1080\n\n/**\n * Render all elements to a dedicated overlay scene with pixel-space camera.\n */\nexport async function renderElements(\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n THREE: typeof THREE_NS,\n) {\n const getScene = (config: any) => overlayScenes[config.zIndex ?? 100]\n await preloadAssets(elementsConfig)\n\n const elementMap = new Map()\n const resolutionScale = resolution.height / DESIGN_HEIGHT\n\n for (let i = 0; i < elementsConfig.length; i++) {\n const config = elementsConfig[i]\n const id = config.id ?? `element_${i}`\n\n try {\n let mesh: THREE_NS.Mesh\n let canvas: HTMLCanvasElement | null = null\n let elementWidth = 0\n let elementHeight = 0\n let segments: any = null\n const segmentMeshes: THREE_NS.Mesh[] = []\n let videoElement: HTMLVideoElement | null = null\n let videoSource: any = null\n let videoTexture: THREE_NS.Texture | null = null\n\n if (config.type === 'text' && config.split) {\n const splitResult = renderSplitTextElement(config, resolution, THREE)\n elementWidth = splitResult.totalWidth\n elementHeight = splitResult.totalHeight\n\n const scaledWidth = elementWidth * resolutionScale\n const scaledHeight = elementHeight * resolutionScale\n\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight,\n )\n const basePosX = x - resolution.width / 2 + scaledWidth / 2\n const basePosY = -(y - resolution.height / 2 + scaledHeight / 2)\n\n let transformX = 0\n let transformY = 0\n if (config.transform) {\n transformX = (config.transform.translateX ?? 0) * resolutionScale\n transformY = -((config.transform.translateY ?? 0) * resolutionScale)\n }\n\n const zIndex = config.zIndex ?? 100\n\n segments = splitResult.meshes.map((item: any, si: number) => {\n const segMesh = item.mesh\n segMesh.scale.set(resolutionScale, resolutionScale, 1)\n segMesh.position.x =\n basePosX + item.offsetX * resolutionScale + transformX\n segMesh.position.y =\n basePosY + item.offsetY * resolutionScale + transformY\n segMesh.position.z = 0\n segMesh.renderOrder = zIndex + i * 0.01 + si * 0.001\n\n if (config.opacity !== undefined) {\n segMesh.material.opacity = config.opacity\n }\n\n getScene(config).add(segMesh)\n segmentMeshes.push(segMesh)\n\n return createElementProps(\n THREE,\n segMesh,\n segMesh.position.x,\n -segMesh.position.y,\n config.opacity ?? 1,\n )\n })\n\n mesh = splitResult.meshes[0]?.mesh ?? new THREE.Mesh()\n } else if (config.type === 'text') {\n const result = renderTextElement(config, resolution, THREE)\n mesh = result.mesh\n canvas = result.canvas\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'image') {\n const result = await renderImageElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'svg') {\n const result = await renderSVGElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n } else if (config.type === 'video') {\n const result = await renderVideoElement(config, resolution, THREE)\n mesh = result.mesh\n elementWidth = result.width\n elementHeight = result.height\n mesh.userData.video = result.video\n videoElement = result.video\n videoSource = result.videoSource\n videoTexture = result.texture\n videoElement?.pause() // null on the webcodecs path\n } else {\n mesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 }),\n )\n console.warn(`Element type \"${config.type}\" not implemented`)\n }\n\n if (!config.split) {\n const scaledWidth = elementWidth * resolutionScale\n const scaledHeight = elementHeight * resolutionScale\n\n const { x, y } = calculatePosition(\n config.position,\n resolution,\n scaledWidth,\n scaledHeight,\n )\n\n const posX = x - resolution.width / 2 + scaledWidth / 2\n const posY = -(y - resolution.height / 2 + scaledHeight / 2)\n\n mesh.scale.set(resolutionScale, resolutionScale, 1)\n mesh.position.x = posX\n mesh.position.y = posY\n mesh.position.z = 0\n\n const zIndex = config.zIndex ?? 100\n mesh.renderOrder = zIndex + i * 0.01\n\n if (config.opacity !== undefined) {\n ;(mesh.material as THREE_NS.MeshBasicMaterial).opacity =\n config.opacity\n ;(mesh.material as THREE_NS.MeshBasicMaterial).transparent = true\n }\n\n if (config.transform) {\n const t = config.transform\n if (t.translateX) mesh.position.x += t.translateX * resolutionScale\n if (t.translateY) mesh.position.y -= t.translateY * resolutionScale\n if (t.translateZ) mesh.position.z += t.translateZ\n if (t.scale)\n mesh.scale.set(\n t.scale * resolutionScale,\n t.scale * resolutionScale,\n 1,\n )\n if (t.rotateZ || t.rotation) {\n mesh.rotation.z = ((t.rotateZ ?? t.rotation ?? 0) * Math.PI) / 180\n }\n }\n\n getScene(config).add(mesh)\n }\n\n const props = createElementProps(\n THREE,\n mesh,\n mesh.position.x,\n -mesh.position.y,\n config.opacity ?? 1,\n videoElement,\n videoSource,\n videoTexture,\n )\n\n const elementInstance = {\n config,\n mesh,\n node: null,\n props,\n segments,\n setContent: (_content: string) => {\n if (config.type === 'text' && canvas) {\n console.warn('setContent not fully implemented in inline renderer')\n }\n },\n destroy: () => {\n videoSource?.dispose?.()\n const targetScene = getScene(config)\n if (segmentMeshes.length > 0) {\n segmentMeshes.forEach((m) => {\n targetScene.remove(m)\n m.geometry.dispose()\n const mat = m.material as THREE_NS.MeshBasicMaterial\n if (mat.map) mat.map.dispose()\n mat.dispose()\n })\n } else {\n targetScene.remove(mesh)\n mesh.geometry.dispose()\n const mat = mesh.material as THREE_NS.MeshBasicMaterial\n if (mat.map) mat.map.dispose()\n mat.dispose()\n }\n },\n }\n\n elementMap.set(id, elementInstance)\n } catch (error) {\n console.warn(\n `[vos] Failed to render element \"${id}\" (${config.type}):`,\n error,\n )\n // Insert transparent placeholder so layout/animation refs still work\n const fallbackMesh = new THREE.Mesh(\n new THREE.PlaneGeometry(100, 100),\n new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 }),\n )\n getScene(config).add(fallbackMesh)\n const props = createElementProps(THREE, fallbackMesh, 0, 0, 0)\n elementMap.set(id, {\n config,\n mesh: fallbackMesh,\n node: null,\n props,\n segments: null,\n setContent: () => {},\n destroy: () => {\n getScene(config).remove(fallbackMesh)\n fallbackMesh.geometry.dispose()\n fallbackMesh.material.dispose()\n },\n })\n }\n }\n\n return elementMap\n}\n","import { renderElements } from './renderElements'\nimport type * as THREE_NS from 'three'\n\nexport interface VosElements {\n renderElements: (\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n THREE: typeof THREE_NS,\n ) => Promise<Map<string, any>>\n disposeElements: (elementMap: Map<string, any>) => void\n}\n\n/**\n * Factory: create the Vos element system bound to a THREE instance.\n */\nexport function createVosElements(THREE: typeof THREE_NS): VosElements {\n return {\n renderElements: (\n elementsConfig: any[],\n overlayScenes: Record<number, THREE_NS.Scene>,\n resolution: any,\n ) => renderElements(elementsConfig, overlayScenes, resolution, THREE),\n disposeElements: (elementMap: Map<string, any>) => {\n elementMap.forEach((instance) => instance.destroy?.())\n elementMap.clear()\n },\n }\n}\n\nexport { renderElements } from './renderElements'\n"],"mappings":"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","names":["svgContent","vos"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vosjs/elements",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Vos element system — text / image / SVG / video renderers for Three.js overlays, shipped as both a typed ESM factory and an injectable IIFE bundle.",
5
5
  "type": "module",
6
6
  "license": "MIT",