@editframe/elements 0.34.8-beta → 0.34.9-beta

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.
@@ -193,27 +193,130 @@ function restoreHiddenNodes(removed) {
193
193
  }
194
194
  }
195
195
  /**
196
- * Helper to copy styles from host and content elements to a canvas clone.
197
- * Reduces code duplication for shadow canvas and shadow img cases.
196
+ * Unified CSS property sync for all elements (canvas clones and regular elements).
197
+ * Single source of truth using the SYNC_PROPERTIES array.
198
+ *
199
+ * @param source - Source element to read styles from
200
+ * @param clone - Clone element to write styles to
201
+ * @param contentSource - Optional content element for width/height (canvas clones only)
202
+ */
203
+ function syncElementStyles(source, clone, contentSource) {
204
+ const cloneStyle = clone.style;
205
+ const propLen = SYNC_PROPERTIES.length;
206
+ const tagName = source.tagName;
207
+ if (HAS_COMPUTED_STYLE_MAP) {
208
+ let srcMap;
209
+ let contentMap;
210
+ try {
211
+ srcMap = source.computedStyleMap();
212
+ if (contentSource) contentMap = contentSource.computedStyleMap();
213
+ } catch {
214
+ return;
215
+ }
216
+ for (let j = 0; j < propLen; j++) {
217
+ const kebab = SYNC_PROPERTIES_KEBAB[j];
218
+ const camel = SYNC_PROPERTIES[j];
219
+ const styleMap = contentSource && (camel === "width" || camel === "height") ? contentMap : srcMap;
220
+ if (!styleMap) continue;
221
+ const srcVal = styleMap.get(kebab);
222
+ if (!srcVal) continue;
223
+ const strVal = srcVal.toString();
224
+ if (camel === "display") {
225
+ cloneStyle.display = strVal === "none" && !(tagName && (tagName === "EF-CAPTIONS-ACTIVE-WORD" || tagName === "EF-CAPTIONS-BEFORE-ACTIVE-WORD" || tagName === "EF-CAPTIONS-AFTER-ACTIVE-WORD" || tagName === "EF-CAPTIONS-SEGMENT")) ? "block" : strVal;
226
+ continue;
227
+ }
228
+ if (camel === "clipPath") continue;
229
+ if (isDefaultValue(camel, strVal)) {
230
+ if (cloneStyle[camel]) cloneStyle[camel] = "";
231
+ continue;
232
+ }
233
+ cloneStyle[camel] = strVal;
234
+ }
235
+ } else {
236
+ let cs;
237
+ let contentCs;
238
+ try {
239
+ cs = getComputedStyle(source);
240
+ if (contentSource) contentCs = getComputedStyle(contentSource);
241
+ } catch {
242
+ return;
243
+ }
244
+ const srcStyle = cs;
245
+ const contentStyle = contentCs;
246
+ for (const prop of SYNC_PROPERTIES) {
247
+ const srcVal = contentSource && (prop === "width" || prop === "height") ? contentStyle?.[prop] : srcStyle[prop];
248
+ if (!srcVal) continue;
249
+ if (prop === "display") {
250
+ cloneStyle.display = srcVal === "none" && !(tagName && (tagName === "EF-CAPTIONS-ACTIVE-WORD" || tagName === "EF-CAPTIONS-BEFORE-ACTIVE-WORD" || tagName === "EF-CAPTIONS-AFTER-ACTIVE-WORD" || tagName === "EF-CAPTIONS-SEGMENT")) ? "block" : srcVal;
251
+ continue;
252
+ }
253
+ if (prop === "clipPath") continue;
254
+ if (isDefaultValue(prop, srcVal)) {
255
+ if (cloneStyle[prop]) cloneStyle[prop] = "";
256
+ continue;
257
+ }
258
+ cloneStyle[prop] = srcVal;
259
+ }
260
+ }
261
+ cloneStyle.animation = "none";
262
+ cloneStyle.transition = "none";
263
+ }
264
+ /**
265
+ * Refresh canvas pixel content from shadow DOM source.
266
+ * Handles both shadow canvas and shadow img sources.
267
+ */
268
+ function refreshCanvasPixels(node) {
269
+ const { source, clone } = node;
270
+ const canvas = clone;
271
+ const shadowCanvas = source.shadowRoot?.querySelector("canvas");
272
+ const shadowImg = source.shadowRoot?.querySelector("img");
273
+ if (shadowCanvas) {
274
+ if (canvas.width !== shadowCanvas.width) canvas.width = shadowCanvas.width;
275
+ if (canvas.height !== shadowCanvas.height) canvas.height = shadowCanvas.height;
276
+ const ctx = canvas.getContext("2d");
277
+ if (ctx && shadowCanvas.width > 0 && shadowCanvas.height > 0) try {
278
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
279
+ ctx.drawImage(shadowCanvas, 0, 0);
280
+ } catch (e) {
281
+ logger.warn("[refreshCanvasPixels] Canvas draw failed:", e);
282
+ }
283
+ } else if (shadowImg?.complete && shadowImg.naturalWidth > 0) {
284
+ if (canvas.width !== shadowImg.naturalWidth) canvas.width = shadowImg.naturalWidth;
285
+ if (canvas.height !== shadowImg.naturalHeight) canvas.height = shadowImg.naturalHeight;
286
+ const ctx = canvas.getContext("2d");
287
+ if (ctx) {
288
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
289
+ try {
290
+ ctx.drawImage(shadowImg, 0, 0);
291
+ } catch {}
292
+ }
293
+ }
294
+ }
295
+ /**
296
+ * Sync text content from light DOM to clone.
198
297
  */
199
- function copyCanvasCloneStyles(clone, hostCs, contentCs) {
200
- const s = clone.style;
201
- s.position = hostCs.position;
202
- s.top = hostCs.top;
203
- s.right = hostCs.right;
204
- s.bottom = hostCs.bottom;
205
- s.left = hostCs.left;
206
- s.margin = hostCs.margin;
207
- s.zIndex = hostCs.zIndex;
208
- s.transform = hostCs.transform;
209
- s.transformOrigin = hostCs.transformOrigin;
210
- s.opacity = hostCs.opacity;
211
- s.visibility = hostCs.visibility;
212
- s.width = contentCs.width;
213
- s.height = contentCs.height;
214
- s.display = "block";
215
- s.animation = "none";
216
- s.transition = "none";
298
+ function syncTextContent(source, clone) {
299
+ const srcTextNode = source.childNodes[0];
300
+ if (srcTextNode?.nodeType === Node.TEXT_NODE) {
301
+ const srcText = srcTextNode.textContent || "";
302
+ const cloneTextNode = clone.childNodes[0];
303
+ if (cloneTextNode?.nodeType === Node.TEXT_NODE) {
304
+ if (cloneTextNode.textContent !== srcText) cloneTextNode.textContent = srcText;
305
+ } else if (!clone.childNodes.length) clone.appendChild(document.createTextNode(srcText));
306
+ }
307
+ }
308
+ /**
309
+ * Sync input element value.
310
+ */
311
+ function syncInputValue(source, clone) {
312
+ if (source instanceof HTMLInputElement) {
313
+ const srcVal = source.value;
314
+ const cloneInput = clone;
315
+ if (cloneInput.value !== srcVal) {
316
+ cloneInput.value = srcVal;
317
+ cloneInput.setAttribute("value", srcVal);
318
+ }
319
+ }
217
320
  }
218
321
  /**
219
322
  * Build clone tree structure with minimal overhead.
@@ -262,7 +365,7 @@ function buildCloneStructure(source, timeMs) {
262
365
  ctx.drawImage(shadowCanvas, 0, 0);
263
366
  } catch {}
264
367
  try {
265
- copyCanvasCloneStyles(clone$1, getComputedStyle(srcEl), getComputedStyle(shadowCanvas));
368
+ syncElementStyles(srcEl, clone$1, shadowCanvas);
266
369
  } catch {}
267
370
  canvasSourceMap.set(clone$1, srcEl);
268
371
  const node$1 = {
@@ -287,7 +390,7 @@ function buildCloneStructure(source, timeMs) {
287
390
  ctx.drawImage(shadowImg, 0, 0);
288
391
  } catch {}
289
392
  try {
290
- copyCanvasCloneStyles(clone$1, getComputedStyle(srcEl), getComputedStyle(shadowImg));
393
+ syncElementStyles(srcEl, clone$1, shadowImg);
291
394
  } catch {}
292
395
  canvasSourceMap.set(clone$1, srcEl);
293
396
  const node$1 = {
@@ -385,142 +488,17 @@ function buildCloneStructure(source, timeMs) {
385
488
  }
386
489
  /**
387
490
  * Sync a single node's styles (extracted for reuse).
491
+ * Now uses unified style syncing with clear separation of concerns:
492
+ * 1. Canvas pixel refresh (if canvas clone)
493
+ * 2. Unified CSS property sync (all elements)
494
+ * 3. Content sync (text, input values)
388
495
  */
389
496
  function syncNodeStyles(node) {
390
497
  const { source, clone, isCanvasClone } = node;
391
- if (isCanvasClone) {
392
- const canvas = clone;
393
- const shadowCanvas = source.shadowRoot?.querySelector("canvas");
394
- const shadowImg = source.shadowRoot?.querySelector("img");
395
- if (shadowCanvas) {
396
- if (canvas.width !== shadowCanvas.width) canvas.width = shadowCanvas.width;
397
- if (canvas.height !== shadowCanvas.height) canvas.height = shadowCanvas.height;
398
- const ctx = canvas.getContext("2d");
399
- if (ctx && shadowCanvas.width > 0 && shadowCanvas.height > 0) try {
400
- ctx.clearRect(0, 0, canvas.width, canvas.height);
401
- ctx.drawImage(shadowCanvas, 0, 0);
402
- } catch (e) {
403
- logger.warn("[syncNodeStyles] Canvas draw failed:", e);
404
- }
405
- try {
406
- const canvasCs = getComputedStyle(shadowCanvas);
407
- const hostCs = getComputedStyle(source);
408
- const s = canvas.style;
409
- s.position = hostCs.position;
410
- s.top = hostCs.top;
411
- s.left = hostCs.left;
412
- s.right = hostCs.right;
413
- s.bottom = hostCs.bottom;
414
- s.margin = hostCs.margin;
415
- s.transform = hostCs.transform;
416
- s.transformOrigin = hostCs.transformOrigin;
417
- s.opacity = hostCs.opacity;
418
- s.visibility = hostCs.visibility;
419
- s.zIndex = hostCs.zIndex;
420
- s.width = canvasCs.width;
421
- s.height = canvasCs.height;
422
- s.backfaceVisibility = hostCs.backfaceVisibility;
423
- s.transformStyle = hostCs.transformStyle;
424
- } catch {}
425
- } else if (shadowImg?.complete && shadowImg.naturalWidth > 0) {
426
- if (canvas.width !== shadowImg.naturalWidth) canvas.width = shadowImg.naturalWidth;
427
- if (canvas.height !== shadowImg.naturalHeight) canvas.height = shadowImg.naturalHeight;
428
- const ctx = canvas.getContext("2d");
429
- if (ctx) {
430
- ctx.clearRect(0, 0, canvas.width, canvas.height);
431
- try {
432
- ctx.drawImage(shadowImg, 0, 0);
433
- } catch {}
434
- }
435
- try {
436
- const imgCs = getComputedStyle(shadowImg);
437
- const hostCs = getComputedStyle(source);
438
- const s = canvas.style;
439
- s.position = hostCs.position;
440
- s.top = hostCs.top;
441
- s.left = hostCs.left;
442
- s.right = hostCs.right;
443
- s.bottom = hostCs.bottom;
444
- s.margin = hostCs.margin;
445
- s.transform = hostCs.transform;
446
- s.transformOrigin = hostCs.transformOrigin;
447
- s.opacity = hostCs.opacity;
448
- s.visibility = hostCs.visibility;
449
- s.zIndex = hostCs.zIndex;
450
- s.width = imgCs.width;
451
- s.height = imgCs.height;
452
- s.backfaceVisibility = hostCs.backfaceVisibility;
453
- s.transformStyle = hostCs.transformStyle;
454
- } catch {}
455
- }
456
- }
457
- const cloneStyle = clone.style;
458
- const propLen = SYNC_PROPERTIES.length;
459
- const tagName = source.tagName;
460
- if (HAS_COMPUTED_STYLE_MAP) {
461
- let srcMap;
462
- try {
463
- srcMap = source.computedStyleMap();
464
- } catch {
465
- return;
466
- }
467
- for (let j = 0; j < propLen; j++) {
468
- const kebab = SYNC_PROPERTIES_KEBAB[j];
469
- const camel = SYNC_PROPERTIES[j];
470
- const srcVal = srcMap.get(kebab);
471
- if (!srcVal) continue;
472
- const strVal = srcVal.toString();
473
- if (camel === "display") {
474
- cloneStyle.display = strVal === "none" && !(tagName && (tagName === "EF-CAPTIONS-ACTIVE-WORD" || tagName === "EF-CAPTIONS-BEFORE-ACTIVE-WORD" || tagName === "EF-CAPTIONS-AFTER-ACTIVE-WORD" || tagName === "EF-CAPTIONS-SEGMENT")) ? "block" : strVal;
475
- continue;
476
- }
477
- if (camel === "clipPath") continue;
478
- if (isDefaultValue(camel, strVal)) {
479
- if (cloneStyle[camel]) cloneStyle[camel] = "";
480
- continue;
481
- }
482
- cloneStyle[camel] = strVal;
483
- }
484
- } else {
485
- let cs;
486
- try {
487
- cs = getComputedStyle(source);
488
- } catch {
489
- return;
490
- }
491
- const srcStyle = cs;
492
- for (const prop of SYNC_PROPERTIES) {
493
- const srcVal = srcStyle[prop];
494
- if (prop === "display") {
495
- cloneStyle.display = srcVal === "none" && !(tagName && (tagName === "EF-CAPTIONS-ACTIVE-WORD" || tagName === "EF-CAPTIONS-BEFORE-ACTIVE-WORD" || tagName === "EF-CAPTIONS-AFTER-ACTIVE-WORD" || tagName === "EF-CAPTIONS-SEGMENT")) ? "block" : srcVal;
496
- continue;
497
- }
498
- if (prop === "clipPath") continue;
499
- if (isDefaultValue(prop, srcVal)) {
500
- if (cloneStyle[prop]) cloneStyle[prop] = "";
501
- continue;
502
- }
503
- cloneStyle[prop] = srcVal;
504
- }
505
- }
506
- cloneStyle.animation = "none";
507
- cloneStyle.transition = "none";
508
- const srcTextNode = source.childNodes[0];
509
- if (srcTextNode?.nodeType === Node.TEXT_NODE) {
510
- const srcText = srcTextNode.textContent || "";
511
- const cloneTextNode = clone.childNodes[0];
512
- if (cloneTextNode?.nodeType === Node.TEXT_NODE) {
513
- if (cloneTextNode.textContent !== srcText) cloneTextNode.textContent = srcText;
514
- } else if (!clone.childNodes.length) clone.appendChild(document.createTextNode(srcText));
515
- }
516
- if (source instanceof HTMLInputElement) {
517
- const srcVal = source.value;
518
- const cloneInput = clone;
519
- if (cloneInput.value !== srcVal) {
520
- cloneInput.value = srcVal;
521
- cloneInput.setAttribute("value", srcVal);
522
- }
523
- }
498
+ if (isCanvasClone) refreshCanvasPixels(node);
499
+ syncElementStyles(source, clone, isCanvasClone ? source.shadowRoot?.querySelector("canvas") || source.shadowRoot?.querySelector("img") || void 0 : void 0);
500
+ syncTextContent(source, clone);
501
+ syncInputValue(source, clone);
524
502
  }
525
503
  let syncStats = {
526
504
  nodesVisited: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"renderTimegroupPreview.js","names":["CSS_SAFE_DEFAULT_VALUES: Record<string, string | string[]>","removed: RemovedNodeInfo[]","node: CloneNode","node","clone","syncState: SyncState","srcMap: StylePropertyMapReadOnly","cs: CSSStyleDeclaration","syncStats: SyncStats","rules: string[]"],"sources":["../../src/preview/renderTimegroupPreview.ts"],"sourcesContent":["/**\n * Canvas refresh fix:\n * Canvas pixels must be explicitly cleared and redrawn in syncNodeStyles\n * to ensure video frames are captured correctly during foreignObject rendering.\n * Without clearRect, stale frames from previous seeks may be serialized.\n * \n * See FOREIGNOBJECT_BUG_FIX.md for detailed explanation.\n */\n\nimport { logger } from \"./logger.js\";\n\n/**\n * Elements to skip entirely when building the preview.\n */\nconst SKIP_TAGS = new Set([\n \"EF-AUDIO\",\n \"EF-THUMBNAIL-STRIP\",\n \"EF-FILMSTRIP\",\n \"EF-TIMELINE\",\n \"EF-WORKBENCH\",\n \"SCRIPT\",\n \"STYLE\",\n]);\n\n/**\n * All CSS properties to sync (camelCase for style[] access).\n */\nconst SYNC_PROPERTIES = [\n \"display\", \"visibility\", \"opacity\",\n \"position\", \"top\", \"right\", \"bottom\", \"left\", \"zIndex\",\n \"width\", \"height\", \"minWidth\", \"minHeight\", \"maxWidth\", \"maxHeight\",\n \"flex\", \"flexFlow\", \"justifyContent\", \"alignItems\", \"alignContent\", \"alignSelf\", \"gap\",\n \"gridTemplate\", \"gridColumn\", \"gridRow\", \"gridArea\",\n \"margin\", \"padding\", \"boxSizing\",\n \"border\", \"borderTop\", \"borderRight\", \"borderBottom\", \"borderLeft\", \"borderRadius\",\n \"background\", \"color\", \"boxShadow\", \"filter\", \"backdropFilter\", \"clipPath\",\n \"font\", \"textAlign\", \"textDecoration\", \"textTransform\",\n \"letterSpacing\", \"whiteSpace\", \"textOverflow\", \"lineHeight\",\n \"transform\", \"transformOrigin\", \"transformStyle\",\n \"perspective\", \"perspectiveOrigin\", \"backfaceVisibility\",\n \"cursor\", \"pointerEvents\", \"userSelect\", \"overflow\",\n] as const;\n\n/**\n * Kebab-case versions for computedStyleMap.get() - pre-computed for speed.\n */\nconst SYNC_PROPERTIES_KEBAB = SYNC_PROPERTIES.map(prop =>\n prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)\n);\n\n/**\n * Feature detection: computedStyleMap is ~15% faster for style syncing.\n */\nconst HAS_COMPUTED_STYLE_MAP = typeof Element !== \"undefined\" && typeof Element.prototype.computedStyleMap === \"function\";\n\n/**\n * CSS initial/default values for SAFE-TO-SKIP properties.\n * Only includes NON-INHERITED properties where skipping the default\n * won't affect visual output.\n * \n * EXCLUDED (must always serialize):\n * - Inherited properties (color, font, text-*, visibility, cursor)\n * - Display (affects layout significantly)\n * - Properties where \"auto\" computes to a specific value\n * \n * INCLUDED (safe to skip):\n * - Transform/filter effects (none = no effect)\n * - Box shadows (none = no shadow)\n * - Borders when none/0 (no visual impact)\n * - backdrop-filter (none = no effect)\n */\nconst CSS_SAFE_DEFAULT_VALUES: Record<string, string | string[]> = {\n // Transforms & effects - safe to skip \"none\" (no visual impact)\n transform: \"none\",\n filter: \"none\",\n backdropFilter: \"none\",\n boxShadow: \"none\",\n \n // Borders - safe to skip when none/0\n border: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderTop: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderRight: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderBottom: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderLeft: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderRadius: [\"0px\", \"0\"],\n \n // Positioning - safe to skip \"static\"\n position: \"static\",\n \n // Z-index - \"auto\" is safe when position is static\n zIndex: \"auto\",\n \n // 3D transforms - safe to skip defaults\n transformStyle: \"flat\",\n perspective: \"none\",\n backfaceVisibility: \"visible\",\n};\n\n/**\n * Check if a value matches a safe-to-skip default.\n */\nfunction isDefaultValue(prop: string, value: string): boolean {\n const defaults = CSS_SAFE_DEFAULT_VALUES[prop];\n if (!defaults) return false;\n if (Array.isArray(defaults)) {\n return defaults.includes(value);\n }\n return defaults === value;\n}\n\n// Re-export temporal types from shared module\nexport {\n type TemporalElement,\n isTemporal,\n getTemporalBounds,\n isVisibleAtTime,\n} from \"./previewTypes.js\";\n\n// Import for internal use\nimport {\n getTemporalBounds,\n} from \"./previewTypes.js\";\n\n/**\n * Tree node representing a source/clone pair with children.\n * This replaces the flat array approach for cleaner recursive traversal.\n */\nexport interface CloneNode {\n source: Element;\n clone: HTMLElement;\n children: CloneNode[];\n isCanvasClone: boolean;\n /** Cached temporal bounds for this node */\n bounds: { startMs: number; endMs: number };\n /** Parent node reference for ancestor visibility checks */\n parent: CloneNode | null;\n}\n\n/** Tree-based sync state */\nexport interface CloneTree {\n root: CloneNode | null;\n}\n\n/** Sync state with tree structure and delta tracking */\nexport interface SyncState {\n tree: CloneTree;\n nodeCount: number; // Total number of nodes (for debugging/logging)\n /** Maps clone canvases to their original source elements (ef-video, ef-image, etc.) */\n canvasSourceMap: WeakMap<HTMLCanvasElement, Element>;\n /** Previous frame's visible set for delta tracking */\n previousVisibleSet: Set<CloneNode>;\n /** Current frame's visible set (updated by syncStyles) */\n currentVisibleSet: Set<CloneNode>;\n}\n\n/** Info needed to restore a removed node */\ninterface RemovedNodeInfo {\n node: CloneNode;\n parent: Node;\n nextSibling: Node | null;\n}\n\n/**\n * Remove hidden nodes from the clone DOM for serialization.\n * Returns info needed to restore them afterward.\n * \n * This physically removes non-visible nodes so they won't be serialized,\n * avoiding the cost of serializing hidden elements and their resources.\n */\nexport function removeHiddenNodesForSerialization(state: SyncState): RemovedNodeInfo[] {\n const removed: RemovedNodeInfo[] = [];\n const visibleSet = state.currentVisibleSet;\n \n // Traverse all nodes and remove those not in visible set\n function visit(node: CloneNode): void {\n // First recurse to children (before potentially removing this node)\n for (const child of node.children) {\n visit(child);\n }\n \n // If this node isn't visible, remove it from DOM\n if (!visibleSet.has(node)) {\n const parent = node.clone.parentNode;\n if (parent) {\n const nextSibling = node.clone.nextSibling;\n parent.removeChild(node.clone);\n removed.push({ node, parent, nextSibling });\n }\n }\n }\n \n if (state.tree.root) {\n visit(state.tree.root);\n }\n \n return removed;\n}\n\n/**\n * Restore previously removed hidden nodes to the clone DOM.\n * Must be called after serialization to maintain tree integrity for next frame.\n */\nexport function restoreHiddenNodes(removed: RemovedNodeInfo[]): void {\n // Restore in reverse order to maintain correct DOM positions\n for (let i = removed.length - 1; i >= 0; i--) {\n const { node, parent, nextSibling } = removed[i]!;\n if (nextSibling) {\n parent.insertBefore(node.clone, nextSibling);\n } else {\n parent.appendChild(node.clone);\n }\n }\n}\n\n/**\n * Get visible canvases from the current visible set.\n * Use this to skip encoding hidden canvases during serialization.\n */\nexport function getVisibleCanvases(state: SyncState): Set<HTMLCanvasElement> {\n const visibleCanvases = new Set<HTMLCanvasElement>();\n for (const node of state.currentVisibleSet) {\n if (node.clone instanceof HTMLCanvasElement) {\n visibleCanvases.add(node.clone);\n }\n }\n return visibleCanvases;\n}\n\n/**\n * Traverse all nodes in the clone tree, calling the callback for each.\n */\nexport function traverseCloneTree(state: SyncState, callback: (node: CloneNode) => void): void {\n function visit(node: CloneNode): void {\n callback(node);\n for (const child of node.children) {\n visit(child);\n }\n }\n if (state.tree.root) {\n visit(state.tree.root);\n }\n}\n\n/**\n * Helper to copy styles from host and content elements to a canvas clone.\n * Reduces code duplication for shadow canvas and shadow img cases.\n */\nfunction copyCanvasCloneStyles(\n clone: HTMLCanvasElement,\n hostCs: CSSStyleDeclaration,\n contentCs: CSSStyleDeclaration\n): void {\n const s = clone.style;\n s.position = hostCs.position;\n s.top = hostCs.top;\n s.right = hostCs.right;\n s.bottom = hostCs.bottom;\n s.left = hostCs.left;\n s.margin = hostCs.margin;\n s.zIndex = hostCs.zIndex;\n s.transform = hostCs.transform;\n s.transformOrigin = hostCs.transformOrigin;\n s.opacity = hostCs.opacity;\n s.visibility = hostCs.visibility;\n s.width = contentCs.width;\n s.height = contentCs.height;\n s.display = \"block\";\n s.animation = \"none\";\n s.transition = \"none\";\n}\n\n/**\n * Build clone tree structure with minimal overhead.\n * Caches temporal bounds on each node for visibility checks.\n * Optionally syncs styles in the same pass if timeMs is provided.\n */\nexport function buildCloneStructure(source: Element, timeMs?: number): {\n container: HTMLDivElement;\n syncState: SyncState;\n} {\n const container = document.createElement(\"div\");\n container.style.cssText = \"position:absolute;top:0;left:0;width:100%;height:100%\";\n \n let nodeCount = 0;\n const canvasSourceMap = new WeakMap<HTMLCanvasElement, Element>();\n \n function cloneElement(srcEl: Element, parentNode: CloneNode | null): CloneNode | null {\n if (SKIP_TAGS.has(srcEl.tagName)) return null;\n \n // Get temporal bounds upfront for indexing\n const bounds = getTemporalBounds(srcEl);\n \n // SVG - clone entire subtree (no children tracking needed)\n if (srcEl instanceof SVGElement) {\n const svgClone = srcEl.cloneNode(true) as SVGElement;\n const node: CloneNode = {\n source: srcEl,\n clone: svgClone as unknown as HTMLElement,\n children: [],\n isCanvasClone: false,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n \n // Canvas - copy pixels\n if (srcEl instanceof HTMLCanvasElement) {\n const canvas = document.createElement(\"canvas\");\n canvas.width = srcEl.width;\n canvas.height = srcEl.height;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(srcEl, 0, 0); } catch {}\n }\n // Raw canvas elements don't need style syncing, just return clone\n // return null;\n }\n \n // Custom elements with shadow canvas (e.g., ef-video, ef-image)\n const isCustom = srcEl.tagName.includes(\"-\");\n if (isCustom && srcEl.shadowRoot) {\n const shadowCanvas = srcEl.shadowRoot.querySelector(\"canvas\");\n if (shadowCanvas) {\n const clone = document.createElement(\"canvas\");\n clone.width = shadowCanvas.width || srcEl.clientWidth;\n clone.height = shadowCanvas.height || srcEl.clientHeight;\n // Mark ef-image canvases to preserve transparency (use PNG instead of JPEG)\n // ef-video doesn't need this since videos don't have transparency\n if (srcEl.tagName === \"EF-IMAGE\" || srcEl.tagName === \"EF-WAVEFORM\") {\n clone.dataset.preserveAlpha = \"true\";\n }\n \n const ctx = clone.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(shadowCanvas, 0, 0); } catch {}\n }\n \n // Copy initial CSS styles - OPTIMIZATION: Cache getComputedStyle results\n try {\n const hostCs = getComputedStyle(srcEl);\n const canvasCs = getComputedStyle(shadowCanvas);\n copyCanvasCloneStyles(clone, hostCs, canvasCs);\n } catch {}\n \n // Map clone canvas to source element for RenderContext caching\n canvasSourceMap.set(clone, srcEl);\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: true,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n \n const shadowImg = srcEl.shadowRoot.querySelector(\"img\");\n if (shadowImg?.complete && shadowImg.naturalWidth > 0) {\n const clone = document.createElement(\"canvas\");\n clone.width = shadowImg.naturalWidth;\n clone.height = shadowImg.naturalHeight;\n // Mark as image-sourced canvas to preserve transparency (use PNG instead of JPEG)\n clone.dataset.preserveAlpha = \"true\";\n const ctx = clone.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(shadowImg, 0, 0); } catch {}\n }\n \n // Copy initial CSS styles - OPTIMIZATION: Cache getComputedStyle results\n try {\n const hostCs = getComputedStyle(srcEl);\n const imgCs = getComputedStyle(shadowImg);\n copyCanvasCloneStyles(clone, hostCs, imgCs);\n } catch {}\n \n // Map clone canvas to source element for RenderContext caching\n canvasSourceMap.set(clone, srcEl);\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: true,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n }\n \n // Standard element clone\n const clone = document.createElement(isCustom ? \"div\" : srcEl.tagName.toLowerCase()) as HTMLElement;\n \n // Copy attributes - OPTIMIZATION: Early exit if no attributes\n const attrs = srcEl.attributes;\n const attrLen = attrs.length;\n if (attrLen > 0) {\n for (let i = 0; i < attrLen; i++) {\n const attr = attrs[i]!;\n const name = attr.name.toLowerCase();\n if (name === \"id\" || name.startsWith(\"on\")) continue;\n if (isCustom && name !== \"class\" && !name.startsWith(\"data-\")) continue;\n try { clone.setAttribute(attr.name, attr.value); } catch {}\n }\n }\n \n if (srcEl instanceof HTMLImageElement && srcEl.src) {\n (clone as HTMLImageElement).src = srcEl.src;\n }\n if (srcEl instanceof HTMLInputElement) {\n (clone as HTMLInputElement).value = srcEl.value;\n }\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: false,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n \n // Shadow DOM children - OPTIMIZATION: Early exit if no childNodes\n if (srcEl.shadowRoot) {\n const shadowChildren = srcEl.shadowRoot.childNodes;\n const shadowLen = shadowChildren.length;\n if (shadowLen > 0) {\n // For text segments, ALWAYS create a text node placeholder even if empty.\n // Caption elements now use light DOM, so they don't need special handling here.\n const isTextSegment = srcEl.tagName === 'EF-TEXT-SEGMENT';\n let hasTextNode = false;\n \n for (let i = 0; i < shadowLen; i++) {\n const child = shadowChildren[i]!;\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent?.trim();\n // Always include text for text segments (even if whitespace-only, e.g., \" \")\n if (text || isTextSegment) {\n clone.appendChild(document.createTextNode(child.textContent || \"\"));\n hasTextNode = true;\n }\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n const el = child as Element;\n if (el.tagName === \"STYLE\" || el.tagName === \"SLOT\") continue;\n const childNode = cloneElement(el, node);\n if (childNode) {\n node.children.push(childNode);\n clone.appendChild(childNode.clone);\n }\n }\n }\n \n // For text segments, ensure there's always a text node for syncStyles to update\n if (isTextSegment && !hasTextNode) {\n clone.appendChild(document.createTextNode(\"\"));\n }\n }\n }\n \n // Light DOM children - OPTIMIZATION: Use indexed loop for performance\n const lightChildren = srcEl.childNodes;\n const lightLen = lightChildren.length;\n for (let i = 0; i < lightLen; i++) {\n const child = lightChildren[i]!;\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent?.trim();\n if (text) clone.appendChild(document.createTextNode(text));\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n const childNode = cloneElement(child as Element, node);\n if (childNode) {\n node.children.push(childNode);\n clone.appendChild(childNode.clone);\n }\n }\n }\n \n return node;\n }\n \n const root = cloneElement(source, null);\n if (root) container.appendChild(root.clone);\n \n const syncState: SyncState = {\n tree: { root },\n nodeCount,\n canvasSourceMap,\n previousVisibleSet: new Set(),\n currentVisibleSet: new Set(),\n };\n \n // Sync styles in the same pass if timeMs is provided\n if (timeMs !== undefined && root) {\n syncStylesWithIndex(syncState, timeMs);\n }\n \n return {\n container,\n syncState,\n };\n}\n\n/**\n * Sync a single node's styles (extracted for reuse).\n */\nfunction syncNodeStyles(node: CloneNode): void {\n const { source, clone, isCanvasClone } = node;\n \n // Canvas clone - refresh pixels AND sync CSS styles\n if (isCanvasClone) {\n const canvas = clone as HTMLCanvasElement;\n const shadowCanvas = source.shadowRoot?.querySelector(\"canvas\");\n const shadowImg = source.shadowRoot?.querySelector(\"img\");\n \n if (shadowCanvas) {\n if (canvas.width !== shadowCanvas.width) canvas.width = shadowCanvas.width;\n if (canvas.height !== shadowCanvas.height) canvas.height = shadowCanvas.height;\n \n const ctx = canvas.getContext(\"2d\");\n if (ctx && shadowCanvas.width > 0 && shadowCanvas.height > 0) {\n try {\n // Clear canvas before drawing to ensure clean refresh\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(shadowCanvas, 0, 0);\n } catch (e) {\n // Canvas draw can fail if source is in invalid state - log and continue\n logger.warn(\"[syncNodeStyles] Canvas draw failed:\", e);\n }\n }\n \n try {\n const canvasCs = getComputedStyle(shadowCanvas);\n const hostCs = getComputedStyle(source);\n const s = canvas.style;\n \n // Browser optimizes redundant style writes internally\n s.position = hostCs.position;\n s.top = hostCs.top;\n s.left = hostCs.left;\n s.right = hostCs.right;\n s.bottom = hostCs.bottom;\n s.margin = hostCs.margin;\n s.transform = hostCs.transform;\n s.transformOrigin = hostCs.transformOrigin;\n s.opacity = hostCs.opacity;\n s.visibility = hostCs.visibility;\n s.zIndex = hostCs.zIndex;\n s.width = canvasCs.width;\n s.height = canvasCs.height;\n s.backfaceVisibility = hostCs.backfaceVisibility;\n s.transformStyle = hostCs.transformStyle;\n } catch {}\n } else if (shadowImg?.complete && shadowImg.naturalWidth > 0) {\n if (canvas.width !== shadowImg.naturalWidth) canvas.width = shadowImg.naturalWidth;\n if (canvas.height !== shadowImg.naturalHeight) canvas.height = shadowImg.naturalHeight;\n \n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n // Clear canvas before drawing to ensure clean refresh\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n try { ctx.drawImage(shadowImg, 0, 0); } catch {}\n }\n \n try {\n const imgCs = getComputedStyle(shadowImg);\n const hostCs = getComputedStyle(source);\n const s = canvas.style;\n \n // Browser optimizes redundant style writes internally\n s.position = hostCs.position;\n s.top = hostCs.top;\n s.left = hostCs.left;\n s.right = hostCs.right;\n s.bottom = hostCs.bottom;\n s.margin = hostCs.margin;\n s.transform = hostCs.transform;\n s.transformOrigin = hostCs.transformOrigin;\n s.opacity = hostCs.opacity;\n s.visibility = hostCs.visibility;\n s.zIndex = hostCs.zIndex;\n s.width = imgCs.width;\n s.height = imgCs.height;\n s.backfaceVisibility = hostCs.backfaceVisibility;\n s.transformStyle = hostCs.transformStyle;\n } catch {}\n }\n // return;\n }\n \n // Regular element - sync CSS properties directly (browser optimizes redundant writes)\n const cloneStyle = clone.style as any;\n const propLen = SYNC_PROPERTIES.length;\n const tagName = (source as HTMLElement).tagName;\n \n if (HAS_COMPUTED_STYLE_MAP) {\n let srcMap: StylePropertyMapReadOnly;\n try {\n srcMap = source.computedStyleMap();\n } catch { return; }\n \n for (let j = 0; j < propLen; j++) {\n const kebab = SYNC_PROPERTIES_KEBAB[j]!;\n const camel = SYNC_PROPERTIES[j]!;\n const srcVal = srcMap.get(kebab);\n if (!srcVal) continue;\n \n const strVal = srcVal.toString();\n \n if (camel === \"display\") {\n // For caption child elements, preserve display:none when explicitly set\n // (they use it to hide empty content, not for temporal visibility)\n const isCaptionChild = tagName && (\n tagName === 'EF-CAPTIONS-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-BEFORE-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-AFTER-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-SEGMENT'\n );\n const targetDisplay = (strVal === \"none\" && !isCaptionChild) ? \"block\" : strVal;\n cloneStyle.display = targetDisplay;\n continue;\n }\n \n // Skip clipPath - clones always have clipPath: none for rendering\n // (source may have clip-path: inset(100%) from proxy mode)\n if (camel === \"clipPath\") continue;\n \n // OPTIMIZATION: Skip default values to reduce serialized HTML size\n // If the computed value is the CSS default, don't set it as inline style\n if (isDefaultValue(camel, strVal)) {\n // Remove from inline style if it was previously set\n if (cloneStyle[camel]) cloneStyle[camel] = \"\";\n continue;\n }\n \n cloneStyle[camel] = strVal;\n }\n } else {\n let cs: CSSStyleDeclaration;\n try {\n cs = getComputedStyle(source);\n } catch { return; }\n \n const srcStyle = cs as any;\n for (const prop of SYNC_PROPERTIES) {\n const srcVal = srcStyle[prop];\n \n if (prop === \"display\") {\n // For caption child elements, preserve display:none when explicitly set\n // (they use it to hide empty content, not for temporal visibility)\n const isCaptionChild = tagName && (\n tagName === 'EF-CAPTIONS-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-BEFORE-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-AFTER-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-SEGMENT'\n );\n const targetDisplay = (srcVal === \"none\" && !isCaptionChild) ? \"block\" : srcVal;\n cloneStyle.display = targetDisplay;\n continue;\n }\n \n // Skip clipPath - clones always have clipPath: none for rendering\n // (source may have clip-path: inset(100%) from proxy mode)\n if (prop === \"clipPath\") continue;\n \n // OPTIMIZATION: Skip default values to reduce serialized HTML size\n if (isDefaultValue(prop, srcVal)) {\n if (cloneStyle[prop]) cloneStyle[prop] = \"\";\n continue;\n }\n \n cloneStyle[prop] = srcVal;\n }\n }\n \n // Disable animations/transitions to prevent re-animation (browser optimizes redundant writes)\n cloneStyle.animation = \"none\";\n cloneStyle.transition = \"none\";\n \n // Sync text content from light DOM\n // Caption child elements now use light DOM, so this handles them naturally\n const srcTextNode = source.childNodes[0];\n if (srcTextNode?.nodeType === Node.TEXT_NODE) {\n const srcText = srcTextNode.textContent || \"\";\n const cloneTextNode = clone.childNodes[0];\n \n if (cloneTextNode?.nodeType === Node.TEXT_NODE) {\n // Update existing text node\n if (cloneTextNode.textContent !== srcText) cloneTextNode.textContent = srcText;\n } else if (!clone.childNodes.length) {\n // Only create text node if clone has NO children (was empty when initially cloned)\n // Don't set textContent as it would delete element children!\n clone.appendChild(document.createTextNode(srcText));\n }\n }\n \n // Sync input value\n if (source instanceof HTMLInputElement) {\n const srcVal = source.value;\n const cloneInput = clone as HTMLInputElement;\n if (cloneInput.value !== srcVal) {\n cloneInput.value = srcVal;\n cloneInput.setAttribute(\"value\", srcVal);\n }\n }\n}\n\n// Performance instrumentation counters\ninterface SyncStats {\n nodesVisited: number;\n nodesCulledByParent: number;\n nodesCulledByTemporal: number;\n nodesProcessed: number;\n nodesFullSync: number; // Newly visible nodes (full sync)\n nodesIncrementalSync: number; // Still visible nodes (incremental sync)\n nodesHidden: number; // Newly hidden nodes\n indexQueryTimeMs: number;\n syncTimeMs: number;\n}\n\nlet syncStats: SyncStats = {\n nodesVisited: 0,\n nodesCulledByParent: 0,\n nodesCulledByTemporal: 0,\n nodesProcessed: 0,\n nodesFullSync: 0,\n nodesIncrementalSync: 0,\n nodesHidden: 0,\n indexQueryTimeMs: 0,\n syncTimeMs: 0,\n};\n\n/**\n * Visibility delta between frames.\n * Used for incremental updates - only sync what changed.\n */\ninterface VisibilityDelta {\n nowVisible: Set<CloneNode>; // Need full style sync + show\n stillVisible: Set<CloneNode>; // Only sync animated properties (or skip if same time)\n nowHidden: Set<CloneNode>; // Just set display:none\n}\n\n/**\n * Compute visibility delta between previous and current frame.\n */\nfunction computeVisibilityDelta(\n previousSet: Set<CloneNode>,\n currentSet: Set<CloneNode>,\n): VisibilityDelta {\n const nowVisible = new Set<CloneNode>();\n const stillVisible = new Set<CloneNode>();\n const nowHidden = new Set<CloneNode>();\n \n // Find nodes that became visible or stayed visible\n for (const node of currentSet) {\n if (previousSet.has(node)) {\n stillVisible.add(node);\n } else {\n nowVisible.add(node);\n }\n }\n \n // Find nodes that became hidden\n for (const node of previousSet) {\n if (!currentSet.has(node)) {\n nowHidden.add(node);\n }\n }\n \n return { nowVisible, stillVisible, nowHidden };\n}\n\n/**\n * Build visible set by recursive traversal with bounds checking.\n * Queries fresh bounds from source elements each time - bounds are computed\n * dynamically by timegroups based on composition mode.\n */\nfunction buildVisibleSetRecursive(\n node: CloneNode,\n timeMs: number,\n visibleSet: Set<CloneNode>,\n): void {\n const { children, source } = node;\n \n // Get fresh bounds from source element (not cached - timegroup bounds are dynamic)\n const bounds = getTemporalBounds(source);\n \n // Check if this node is visible at current time\n const isVisible = timeMs >= bounds.startMs && timeMs <= bounds.endMs;\n \n if (isVisible) {\n visibleSet.add(node);\n // Recurse to children\n for (const child of children) {\n buildVisibleSetRecursive(child, timeMs, visibleSet);\n }\n }\n // If not visible, skip entire subtree\n}\n\n/**\n * Sync styles with recursive visibility check and delta tracking.\n * \n * DELTA TRACKING: Tracks visibility changes between frames to minimize work:\n * - nowVisible nodes: Full style sync + show\n * - stillVisible nodes: Incremental sync (source DOM may have changed)\n * - nowHidden nodes: Just hide (display:none)\n */\nfunction syncStylesWithIndex(state: SyncState, timeMs: number): void {\n const queryStart = performance.now();\n \n // Build the set of visible nodes by recursive traversal\n const visibleSet = new Set<CloneNode>();\n if (state.tree.root) {\n buildVisibleSetRecursive(state.tree.root, timeMs, visibleSet);\n }\n \n // Compute delta from previous frame\n const delta = computeVisibilityDelta(state.previousVisibleSet, visibleSet);\n \n syncStats.indexQueryTimeMs = performance.now() - queryStart;\n \n // Now traverse the tree but use the delta for O(1) sync decisions\n const syncStart = performance.now();\n if (state.tree.root) {\n syncNodeWithDelta(state.tree.root, visibleSet, delta);\n }\n syncStats.syncTimeMs = performance.now() - syncStart;\n \n // Update state for next frame and expose current visible set\n state.previousVisibleSet = visibleSet;\n state.currentVisibleSet = visibleSet;\n}\n\n/**\n * Sync a node using visibility delta for incremental updates.\n * \n * DELTA TRACKING optimization:\n * - nowVisible: Full style sync (element just appeared)\n * - stillVisible: Incremental sync (source DOM may have changed)\n * - nowHidden: Just hide the element\n * - Not in any set: Skip entirely (was already hidden)\n */\nfunction syncNodeWithDelta(\n node: CloneNode,\n visibleSet: Set<CloneNode>,\n delta: VisibilityDelta,\n): void {\n syncStats.nodesVisited++;\n \n const isVisible = visibleSet.has(node);\n \n if (!isVisible) {\n // Node is not visible - ALWAYS set display:none\n // This handles both \"just became hidden\" and \"initial build with node outside time range\"\n node.clone.style.display = \"none\";\n if (delta.nowHidden.has(node)) {\n syncStats.nodesHidden++;\n }\n // Already hidden nodes: skip (don't even recurse to children)\n syncStats.nodesCulledByTemporal++;\n return;\n }\n \n // Node is visible - determine sync strategy\n if (delta.nowVisible.has(node)) {\n // Just became visible - need full style sync\n syncNodeStyles(node);\n syncStats.nodesFullSync++;\n } else if (delta.stillVisible.has(node)) {\n // Was visible, still visible - still need to sync\n // Source DOM properties can change independently of time (input values, text, etc.)\n // TODO: Phase 5 could track property changes for smarter incremental sync\n syncNodeStyles(node);\n syncStats.nodesIncrementalSync++;\n }\n \n syncStats.nodesProcessed++;\n \n // Recurse to children\n for (const child of node.children) {\n syncNodeWithDelta(child, visibleSet, delta);\n }\n}\n\n/**\n * Legacy recursive sync (kept for comparison/fallback).\n * Returns early if the node is temporally culled, skipping ALL descendants.\n * @deprecated Use syncStylesWithIndex for better performance\n */\nexport function syncNodeRecursiveLegacy(node: CloneNode, timeMs: number): void {\n const { clone, children, bounds } = node;\n syncStats.nodesVisited++;\n \n // Temporal culling - check if this node is visible at current time\n // NOTE: Canvas clones now participate in temporal culling (lazy canvas copying).\n // Invalid bounds [0,0] are treated as [-Infinity, Infinity] by getTemporalBounds.\n {\n // OPTIMIZATION: Check if parent is already hidden to skip bounds computation\n const parent = clone.parentElement;\n if (parent instanceof HTMLElement) {\n // If parent has display:none, this element is already hidden - skip bounds check\n if (parent.style.display === \"none\") {\n clone.style.display = \"none\";\n syncStats.nodesCulledByParent++;\n return;\n }\n }\n \n // Use cached bounds from node instead of calling getTemporalBounds\n const { startMs, endMs } = bounds;\n if (timeMs < startMs || timeMs > endMs) {\n // Hide this element and BAIL OUT - skip all descendants automatically!\n clone.style.display = \"none\";\n syncStats.nodesCulledByTemporal++;\n return;\n }\n }\n \n // Sync this node's styles\n syncNodeStyles(node);\n syncStats.nodesProcessed++;\n \n // Recursively sync children\n for (const child of children) {\n syncNodeRecursiveLegacy(child, timeMs);\n }\n}\n\n/**\n * Sync all CSS properties from source elements to their clones.\n * Uses interval index for O(log n + k) visibility queries instead of O(n) traversal.\n * Uses delta tracking for incremental updates between frames.\n */\nexport function syncStyles(state: SyncState, timeMs: number): void {\n // Reset stats\n syncStats = {\n nodesVisited: 0,\n nodesCulledByParent: 0,\n nodesCulledByTemporal: 0,\n nodesProcessed: 0,\n nodesFullSync: 0,\n nodesIncrementalSync: 0,\n nodesHidden: 0,\n indexQueryTimeMs: 0,\n syncTimeMs: 0,\n };\n \n // Use interval-index-based sync with delta tracking\n syncStylesWithIndex(state, timeMs);\n}\n\n/**\n * Collect document styles for shadow DOM injection.\n */\nexport function collectDocumentStyles(): string {\n const rules: string[] = [];\n try {\n for (const sheet of document.styleSheets) {\n try {\n if (sheet.cssRules) {\n for (const rule of sheet.cssRules) {\n rules.push(rule.cssText);\n }\n }\n } catch {}\n }\n } catch {}\n return rules.join(\"\\n\");\n}\n\n\n// Backward-compatible aliases\nexport const syncStaticStyles = syncStyles;\nexport const syncAnimatedStyles = syncStyles;\n\n/**\n * Override clip-path, opacity, and optionally transform on the root clone element.\n * The source may have these properties set for proxy mode or workbench scaling.\n * \n * @param syncState - The sync state containing the clone tree\n * @param fullReset - If true, also resets opacity and transform (for capture operations)\n */\nexport function overrideRootCloneStyles(syncState: SyncState, fullReset: boolean = false): void {\n const rootClone = syncState.tree.root?.clone;\n if (!rootClone) return;\n \n rootClone.style.clipPath = \"none\";\n if (fullReset) {\n rootClone.style.opacity = \"1\";\n rootClone.style.transform = \"none\";\n }\n}\n\n/**\n * Create a live preview of a timegroup with a refresh function.\n * Used by EFWorkbench for the \"computed\" preview mode.\n * \n * @param source - The source timegroup to preview\n * @returns Object with preview container and refresh function\n */\nexport function renderTimegroupPreview(source: Element): {\n container: HTMLDivElement;\n refresh: (timeMs?: number) => void;\n} {\n const { container, syncState } = buildCloneStructure(source);\n \n // Initial style sync\n syncStyles(syncState, 0);\n \n return {\n container,\n refresh: (timeMs?: number) => {\n syncStyles(syncState, timeMs ?? 0);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"renderTimegroupPreview.js","names":["CSS_SAFE_DEFAULT_VALUES: Record<string, string | string[]>","removed: RemovedNodeInfo[]","srcMap: StylePropertyMapReadOnly","contentMap: StylePropertyMapReadOnly | undefined","cs: CSSStyleDeclaration","contentCs: CSSStyleDeclaration | undefined","node: CloneNode","node","clone","syncState: SyncState","syncStats: SyncStats","rules: string[]"],"sources":["../../src/preview/renderTimegroupPreview.ts"],"sourcesContent":["/**\n * Canvas refresh fix:\n * Canvas pixels must be explicitly cleared and redrawn in syncNodeStyles\n * to ensure video frames are captured correctly during foreignObject rendering.\n * Without clearRect, stale frames from previous seeks may be serialized.\n * \n * See FOREIGNOBJECT_BUG_FIX.md for detailed explanation.\n */\n\nimport { logger } from \"./logger.js\";\n\n/**\n * Elements to skip entirely when building the preview.\n */\nconst SKIP_TAGS = new Set([\n \"EF-AUDIO\",\n \"EF-THUMBNAIL-STRIP\",\n \"EF-FILMSTRIP\",\n \"EF-TIMELINE\",\n \"EF-WORKBENCH\",\n \"SCRIPT\",\n \"STYLE\",\n]);\n\n/**\n * All CSS properties to sync (camelCase for style[] access).\n */\nconst SYNC_PROPERTIES = [\n \"display\", \"visibility\", \"opacity\",\n \"position\", \"top\", \"right\", \"bottom\", \"left\", \"zIndex\",\n \"width\", \"height\", \"minWidth\", \"minHeight\", \"maxWidth\", \"maxHeight\",\n \"flex\", \"flexFlow\", \"justifyContent\", \"alignItems\", \"alignContent\", \"alignSelf\", \"gap\",\n \"gridTemplate\", \"gridColumn\", \"gridRow\", \"gridArea\",\n \"margin\", \"padding\", \"boxSizing\",\n \"border\", \"borderTop\", \"borderRight\", \"borderBottom\", \"borderLeft\", \"borderRadius\",\n \"background\", \"color\", \"boxShadow\", \"filter\", \"backdropFilter\", \"clipPath\",\n \"font\", \"textAlign\", \"textDecoration\", \"textTransform\",\n \"letterSpacing\", \"whiteSpace\", \"textOverflow\", \"lineHeight\",\n \"transform\", \"transformOrigin\", \"transformStyle\",\n \"perspective\", \"perspectiveOrigin\", \"backfaceVisibility\",\n \"cursor\", \"pointerEvents\", \"userSelect\", \"overflow\",\n] as const;\n\n/**\n * Kebab-case versions for computedStyleMap.get() - pre-computed for speed.\n */\nconst SYNC_PROPERTIES_KEBAB = SYNC_PROPERTIES.map(prop =>\n prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`)\n);\n\n/**\n * Feature detection: computedStyleMap is ~15% faster for style syncing.\n */\nconst HAS_COMPUTED_STYLE_MAP = typeof Element !== \"undefined\" && typeof Element.prototype.computedStyleMap === \"function\";\n\n/**\n * CSS initial/default values for SAFE-TO-SKIP properties.\n * Only includes NON-INHERITED properties where skipping the default\n * won't affect visual output.\n * \n * EXCLUDED (must always serialize):\n * - Inherited properties (color, font, text-*, visibility, cursor)\n * - Display (affects layout significantly)\n * - Properties where \"auto\" computes to a specific value\n * \n * INCLUDED (safe to skip):\n * - Transform/filter effects (none = no effect)\n * - Box shadows (none = no shadow)\n * - Borders when none/0 (no visual impact)\n * - backdrop-filter (none = no effect)\n */\nconst CSS_SAFE_DEFAULT_VALUES: Record<string, string | string[]> = {\n // Transforms & effects - safe to skip \"none\" (no visual impact)\n transform: \"none\",\n filter: \"none\",\n backdropFilter: \"none\",\n boxShadow: \"none\",\n \n // Borders - safe to skip when none/0\n border: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderTop: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderRight: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderBottom: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderLeft: [\"none\", \"0px none\", \"0px\", \"0px none rgb(0, 0, 0)\"],\n borderRadius: [\"0px\", \"0\"],\n \n // Positioning - safe to skip \"static\"\n position: \"static\",\n \n // Z-index - \"auto\" is safe when position is static\n zIndex: \"auto\",\n \n // 3D transforms - safe to skip defaults\n transformStyle: \"flat\",\n perspective: \"none\",\n backfaceVisibility: \"visible\",\n};\n\n/**\n * Check if a value matches a safe-to-skip default.\n */\nfunction isDefaultValue(prop: string, value: string): boolean {\n const defaults = CSS_SAFE_DEFAULT_VALUES[prop];\n if (!defaults) return false;\n if (Array.isArray(defaults)) {\n return defaults.includes(value);\n }\n return defaults === value;\n}\n\n// Re-export temporal types from shared module\nexport {\n type TemporalElement,\n isTemporal,\n getTemporalBounds,\n isVisibleAtTime,\n} from \"./previewTypes.js\";\n\n// Import for internal use\nimport {\n getTemporalBounds,\n} from \"./previewTypes.js\";\n\n/**\n * Tree node representing a source/clone pair with children.\n * This replaces the flat array approach for cleaner recursive traversal.\n */\nexport interface CloneNode {\n source: Element;\n clone: HTMLElement;\n children: CloneNode[];\n isCanvasClone: boolean;\n /** Cached temporal bounds for this node */\n bounds: { startMs: number; endMs: number };\n /** Parent node reference for ancestor visibility checks */\n parent: CloneNode | null;\n}\n\n/** Tree-based sync state */\nexport interface CloneTree {\n root: CloneNode | null;\n}\n\n/** Sync state with tree structure and delta tracking */\nexport interface SyncState {\n tree: CloneTree;\n nodeCount: number; // Total number of nodes (for debugging/logging)\n /** Maps clone canvases to their original source elements (ef-video, ef-image, etc.) */\n canvasSourceMap: WeakMap<HTMLCanvasElement, Element>;\n /** Previous frame's visible set for delta tracking */\n previousVisibleSet: Set<CloneNode>;\n /** Current frame's visible set (updated by syncStyles) */\n currentVisibleSet: Set<CloneNode>;\n}\n\n/** Info needed to restore a removed node */\ninterface RemovedNodeInfo {\n node: CloneNode;\n parent: Node;\n nextSibling: Node | null;\n}\n\n/**\n * Remove hidden nodes from the clone DOM for serialization.\n * Returns info needed to restore them afterward.\n * \n * This physically removes non-visible nodes so they won't be serialized,\n * avoiding the cost of serializing hidden elements and their resources.\n */\nexport function removeHiddenNodesForSerialization(state: SyncState): RemovedNodeInfo[] {\n const removed: RemovedNodeInfo[] = [];\n const visibleSet = state.currentVisibleSet;\n \n // Traverse all nodes and remove those not in visible set\n function visit(node: CloneNode): void {\n // First recurse to children (before potentially removing this node)\n for (const child of node.children) {\n visit(child);\n }\n \n // If this node isn't visible, remove it from DOM\n if (!visibleSet.has(node)) {\n const parent = node.clone.parentNode;\n if (parent) {\n const nextSibling = node.clone.nextSibling;\n parent.removeChild(node.clone);\n removed.push({ node, parent, nextSibling });\n }\n }\n }\n \n if (state.tree.root) {\n visit(state.tree.root);\n }\n \n return removed;\n}\n\n/**\n * Restore previously removed hidden nodes to the clone DOM.\n * Must be called after serialization to maintain tree integrity for next frame.\n */\nexport function restoreHiddenNodes(removed: RemovedNodeInfo[]): void {\n // Restore in reverse order to maintain correct DOM positions\n for (let i = removed.length - 1; i >= 0; i--) {\n const { node, parent, nextSibling } = removed[i]!;\n if (nextSibling) {\n parent.insertBefore(node.clone, nextSibling);\n } else {\n parent.appendChild(node.clone);\n }\n }\n}\n\n/**\n * Get visible canvases from the current visible set.\n * Use this to skip encoding hidden canvases during serialization.\n */\nexport function getVisibleCanvases(state: SyncState): Set<HTMLCanvasElement> {\n const visibleCanvases = new Set<HTMLCanvasElement>();\n for (const node of state.currentVisibleSet) {\n if (node.clone instanceof HTMLCanvasElement) {\n visibleCanvases.add(node.clone);\n }\n }\n return visibleCanvases;\n}\n\n/**\n * Traverse all nodes in the clone tree, calling the callback for each.\n */\nexport function traverseCloneTree(state: SyncState, callback: (node: CloneNode) => void): void {\n function visit(node: CloneNode): void {\n callback(node);\n for (const child of node.children) {\n visit(child);\n }\n }\n if (state.tree.root) {\n visit(state.tree.root);\n }\n}\n\n/**\n * Unified CSS property sync for all elements (canvas clones and regular elements).\n * Single source of truth using the SYNC_PROPERTIES array.\n * \n * @param source - Source element to read styles from\n * @param clone - Clone element to write styles to\n * @param contentSource - Optional content element for width/height (canvas clones only)\n */\nfunction syncElementStyles(\n source: Element,\n clone: HTMLElement,\n contentSource?: Element,\n): void {\n const cloneStyle = clone.style as any;\n const propLen = SYNC_PROPERTIES.length;\n const tagName = (source as HTMLElement).tagName;\n \n if (HAS_COMPUTED_STYLE_MAP) {\n let srcMap: StylePropertyMapReadOnly;\n let contentMap: StylePropertyMapReadOnly | undefined;\n \n try {\n srcMap = source.computedStyleMap();\n if (contentSource) {\n contentMap = contentSource.computedStyleMap();\n }\n } catch { return; }\n \n for (let j = 0; j < propLen; j++) {\n const kebab = SYNC_PROPERTIES_KEBAB[j]!;\n const camel = SYNC_PROPERTIES[j]!;\n \n // For canvas clones, width/height come from content element (shadow canvas/img)\n const useContentSource = contentSource && (camel === \"width\" || camel === \"height\");\n const styleMap = useContentSource ? contentMap : srcMap;\n if (!styleMap) continue;\n \n const srcVal = styleMap.get(kebab);\n if (!srcVal) continue;\n \n const strVal = srcVal.toString();\n \n if (camel === \"display\") {\n // For caption child elements, preserve display:none when explicitly set\n // (they use it to hide empty content, not for temporal visibility)\n const isCaptionChild = tagName && (\n tagName === 'EF-CAPTIONS-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-BEFORE-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-AFTER-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-SEGMENT'\n );\n const targetDisplay = (strVal === \"none\" && !isCaptionChild) ? \"block\" : strVal;\n cloneStyle.display = targetDisplay;\n continue;\n }\n \n // Skip clipPath - clones always have clipPath: none for rendering\n // (source may have clip-path: inset(100%) from proxy mode)\n if (camel === \"clipPath\") continue;\n \n // OPTIMIZATION: Skip default values to reduce serialized HTML size\n // If the computed value is the CSS default, don't set it as inline style\n if (isDefaultValue(camel, strVal)) {\n // Remove from inline style if it was previously set\n if (cloneStyle[camel]) cloneStyle[camel] = \"\";\n continue;\n }\n \n cloneStyle[camel] = strVal;\n }\n } else {\n let cs: CSSStyleDeclaration;\n let contentCs: CSSStyleDeclaration | undefined;\n \n try {\n cs = getComputedStyle(source);\n if (contentSource) {\n contentCs = getComputedStyle(contentSource);\n }\n } catch { return; }\n \n const srcStyle = cs as any;\n const contentStyle = contentCs as any;\n \n for (const prop of SYNC_PROPERTIES) {\n // For canvas clones, width/height come from content element (shadow canvas/img)\n const useContentSource = contentSource && (prop === \"width\" || prop === \"height\");\n const srcVal = useContentSource ? contentStyle?.[prop] : srcStyle[prop];\n if (!srcVal) continue;\n \n if (prop === \"display\") {\n // For caption child elements, preserve display:none when explicitly set\n // (they use it to hide empty content, not for temporal visibility)\n const isCaptionChild = tagName && (\n tagName === 'EF-CAPTIONS-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-BEFORE-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-AFTER-ACTIVE-WORD' ||\n tagName === 'EF-CAPTIONS-SEGMENT'\n );\n const targetDisplay = (srcVal === \"none\" && !isCaptionChild) ? \"block\" : srcVal;\n cloneStyle.display = targetDisplay;\n continue;\n }\n \n // Skip clipPath - clones always have clipPath: none for rendering\n // (source may have clip-path: inset(100%) from proxy mode)\n if (prop === \"clipPath\") continue;\n \n // OPTIMIZATION: Skip default values to reduce serialized HTML size\n if (isDefaultValue(prop, srcVal)) {\n if (cloneStyle[prop]) cloneStyle[prop] = \"\";\n continue;\n }\n \n cloneStyle[prop] = srcVal;\n }\n }\n \n // Disable animations/transitions to prevent re-animation (browser optimizes redundant writes)\n cloneStyle.animation = \"none\";\n cloneStyle.transition = \"none\";\n}\n\n/**\n * Refresh canvas pixel content from shadow DOM source.\n * Handles both shadow canvas and shadow img sources.\n */\nfunction refreshCanvasPixels(node: CloneNode): void {\n const { source, clone } = node;\n const canvas = clone as HTMLCanvasElement;\n const shadowCanvas = source.shadowRoot?.querySelector(\"canvas\");\n const shadowImg = source.shadowRoot?.querySelector(\"img\");\n \n if (shadowCanvas) {\n // Update buffer dimensions if needed\n if (canvas.width !== shadowCanvas.width) canvas.width = shadowCanvas.width;\n if (canvas.height !== shadowCanvas.height) canvas.height = shadowCanvas.height;\n \n // Copy pixels with explicit clear\n const ctx = canvas.getContext(\"2d\");\n if (ctx && shadowCanvas.width > 0 && shadowCanvas.height > 0) {\n try {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(shadowCanvas, 0, 0);\n } catch (e) {\n logger.warn(\"[refreshCanvasPixels] Canvas draw failed:\", e);\n }\n }\n } else if (shadowImg?.complete && shadowImg.naturalWidth > 0) {\n // Update buffer dimensions if needed\n if (canvas.width !== shadowImg.naturalWidth) canvas.width = shadowImg.naturalWidth;\n if (canvas.height !== shadowImg.naturalHeight) canvas.height = shadowImg.naturalHeight;\n \n // Copy pixels with explicit clear\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n try { ctx.drawImage(shadowImg, 0, 0); } catch {}\n }\n }\n}\n\n/**\n * Sync text content from light DOM to clone.\n */\nfunction syncTextContent(source: Element, clone: HTMLElement): void {\n const srcTextNode = source.childNodes[0];\n if (srcTextNode?.nodeType === Node.TEXT_NODE) {\n const srcText = srcTextNode.textContent || \"\";\n const cloneTextNode = clone.childNodes[0];\n \n if (cloneTextNode?.nodeType === Node.TEXT_NODE) {\n // Update existing text node\n if (cloneTextNode.textContent !== srcText) cloneTextNode.textContent = srcText;\n } else if (!clone.childNodes.length) {\n // Only create text node if clone has NO children (was empty when initially cloned)\n // Don't set textContent as it would delete element children!\n clone.appendChild(document.createTextNode(srcText));\n }\n }\n}\n\n/**\n * Sync input element value.\n */\nfunction syncInputValue(source: Element, clone: HTMLElement): void {\n if (source instanceof HTMLInputElement) {\n const srcVal = source.value;\n const cloneInput = clone as HTMLInputElement;\n if (cloneInput.value !== srcVal) {\n cloneInput.value = srcVal;\n cloneInput.setAttribute(\"value\", srcVal);\n }\n }\n}\n\n/**\n * Build clone tree structure with minimal overhead.\n * Caches temporal bounds on each node for visibility checks.\n * Optionally syncs styles in the same pass if timeMs is provided.\n */\nexport function buildCloneStructure(source: Element, timeMs?: number): {\n container: HTMLDivElement;\n syncState: SyncState;\n} {\n const container = document.createElement(\"div\");\n container.style.cssText = \"position:absolute;top:0;left:0;width:100%;height:100%\";\n \n let nodeCount = 0;\n const canvasSourceMap = new WeakMap<HTMLCanvasElement, Element>();\n \n function cloneElement(srcEl: Element, parentNode: CloneNode | null): CloneNode | null {\n if (SKIP_TAGS.has(srcEl.tagName)) return null;\n \n // Get temporal bounds upfront for indexing\n const bounds = getTemporalBounds(srcEl);\n \n // SVG - clone entire subtree (no children tracking needed)\n if (srcEl instanceof SVGElement) {\n const svgClone = srcEl.cloneNode(true) as SVGElement;\n const node: CloneNode = {\n source: srcEl,\n clone: svgClone as unknown as HTMLElement,\n children: [],\n isCanvasClone: false,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n \n // Canvas - copy pixels\n if (srcEl instanceof HTMLCanvasElement) {\n const canvas = document.createElement(\"canvas\");\n canvas.width = srcEl.width;\n canvas.height = srcEl.height;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(srcEl, 0, 0); } catch {}\n }\n // Raw canvas elements don't need style syncing, just return clone\n // return null;\n }\n \n // Custom elements with shadow canvas (e.g., ef-video, ef-image)\n const isCustom = srcEl.tagName.includes(\"-\");\n if (isCustom && srcEl.shadowRoot) {\n const shadowCanvas = srcEl.shadowRoot.querySelector(\"canvas\");\n if (shadowCanvas) {\n const clone = document.createElement(\"canvas\");\n clone.width = shadowCanvas.width || srcEl.clientWidth;\n clone.height = shadowCanvas.height || srcEl.clientHeight;\n // Mark ef-image canvases to preserve transparency (use PNG instead of JPEG)\n // ef-video doesn't need this since videos don't have transparency\n if (srcEl.tagName === \"EF-IMAGE\" || srcEl.tagName === \"EF-WAVEFORM\") {\n clone.dataset.preserveAlpha = \"true\";\n }\n \n const ctx = clone.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(shadowCanvas, 0, 0); } catch {}\n }\n \n // Copy initial CSS styles using unified sync\n // Pass shadowCanvas as contentSource for width/height\n try {\n syncElementStyles(srcEl, clone, shadowCanvas);\n } catch {}\n \n // Map clone canvas to source element for RenderContext caching\n canvasSourceMap.set(clone, srcEl);\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: true,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n \n const shadowImg = srcEl.shadowRoot.querySelector(\"img\");\n if (shadowImg?.complete && shadowImg.naturalWidth > 0) {\n const clone = document.createElement(\"canvas\");\n clone.width = shadowImg.naturalWidth;\n clone.height = shadowImg.naturalHeight;\n // Mark as image-sourced canvas to preserve transparency (use PNG instead of JPEG)\n clone.dataset.preserveAlpha = \"true\";\n const ctx = clone.getContext(\"2d\");\n if (ctx) {\n try { ctx.drawImage(shadowImg, 0, 0); } catch {}\n }\n \n // Copy initial CSS styles using unified sync\n // Pass shadowImg as contentSource for width/height\n try {\n syncElementStyles(srcEl, clone, shadowImg);\n } catch {}\n \n // Map clone canvas to source element for RenderContext caching\n canvasSourceMap.set(clone, srcEl);\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: true,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n return node;\n }\n }\n \n // Standard element clone\n const clone = document.createElement(isCustom ? \"div\" : srcEl.tagName.toLowerCase()) as HTMLElement;\n \n // Copy attributes - OPTIMIZATION: Early exit if no attributes\n const attrs = srcEl.attributes;\n const attrLen = attrs.length;\n if (attrLen > 0) {\n for (let i = 0; i < attrLen; i++) {\n const attr = attrs[i]!;\n const name = attr.name.toLowerCase();\n if (name === \"id\" || name.startsWith(\"on\")) continue;\n if (isCustom && name !== \"class\" && !name.startsWith(\"data-\")) continue;\n try { clone.setAttribute(attr.name, attr.value); } catch {}\n }\n }\n \n if (srcEl instanceof HTMLImageElement && srcEl.src) {\n (clone as HTMLImageElement).src = srcEl.src;\n }\n if (srcEl instanceof HTMLInputElement) {\n (clone as HTMLInputElement).value = srcEl.value;\n }\n \n const node: CloneNode = {\n source: srcEl,\n clone,\n children: [],\n isCanvasClone: false,\n bounds,\n parent: parentNode,\n };\n nodeCount++;\n \n // Shadow DOM children - OPTIMIZATION: Early exit if no childNodes\n if (srcEl.shadowRoot) {\n const shadowChildren = srcEl.shadowRoot.childNodes;\n const shadowLen = shadowChildren.length;\n if (shadowLen > 0) {\n // For text segments, ALWAYS create a text node placeholder even if empty.\n // Caption elements now use light DOM, so they don't need special handling here.\n const isTextSegment = srcEl.tagName === 'EF-TEXT-SEGMENT';\n let hasTextNode = false;\n \n for (let i = 0; i < shadowLen; i++) {\n const child = shadowChildren[i]!;\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent?.trim();\n // Always include text for text segments (even if whitespace-only, e.g., \" \")\n if (text || isTextSegment) {\n clone.appendChild(document.createTextNode(child.textContent || \"\"));\n hasTextNode = true;\n }\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n const el = child as Element;\n if (el.tagName === \"STYLE\" || el.tagName === \"SLOT\") continue;\n const childNode = cloneElement(el, node);\n if (childNode) {\n node.children.push(childNode);\n clone.appendChild(childNode.clone);\n }\n }\n }\n \n // For text segments, ensure there's always a text node for syncStyles to update\n if (isTextSegment && !hasTextNode) {\n clone.appendChild(document.createTextNode(\"\"));\n }\n }\n }\n \n // Light DOM children - OPTIMIZATION: Use indexed loop for performance\n const lightChildren = srcEl.childNodes;\n const lightLen = lightChildren.length;\n for (let i = 0; i < lightLen; i++) {\n const child = lightChildren[i]!;\n if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent?.trim();\n if (text) clone.appendChild(document.createTextNode(text));\n } else if (child.nodeType === Node.ELEMENT_NODE) {\n const childNode = cloneElement(child as Element, node);\n if (childNode) {\n node.children.push(childNode);\n clone.appendChild(childNode.clone);\n }\n }\n }\n \n return node;\n }\n \n const root = cloneElement(source, null);\n if (root) container.appendChild(root.clone);\n \n const syncState: SyncState = {\n tree: { root },\n nodeCount,\n canvasSourceMap,\n previousVisibleSet: new Set(),\n currentVisibleSet: new Set(),\n };\n \n // Sync styles in the same pass if timeMs is provided\n if (timeMs !== undefined && root) {\n syncStylesWithIndex(syncState, timeMs);\n }\n \n return {\n container,\n syncState,\n };\n}\n\n/**\n * Sync a single node's styles (extracted for reuse).\n * Now uses unified style syncing with clear separation of concerns:\n * 1. Canvas pixel refresh (if canvas clone)\n * 2. Unified CSS property sync (all elements)\n * 3. Content sync (text, input values)\n */\nfunction syncNodeStyles(node: CloneNode): void {\n const { source, clone, isCanvasClone } = node;\n \n // 1. Canvas-specific: Refresh pixel content from shadow DOM\n if (isCanvasClone) {\n refreshCanvasPixels(node);\n }\n \n // 2. Unified: Sync ALL CSS properties using SYNC_PROPERTIES array\n // For canvas clones, pass content source (shadow canvas/img) for width/height\n const contentSource = isCanvasClone\n ? (source.shadowRoot?.querySelector(\"canvas\") || source.shadowRoot?.querySelector(\"img\") || undefined)\n : undefined;\n syncElementStyles(source, clone, contentSource);\n \n // 3. Element-specific: Sync text content and input values\n syncTextContent(source, clone);\n syncInputValue(source, clone);\n}\n\n// Performance instrumentation counters\ninterface SyncStats {\n nodesVisited: number;\n nodesCulledByParent: number;\n nodesCulledByTemporal: number;\n nodesProcessed: number;\n nodesFullSync: number; // Newly visible nodes (full sync)\n nodesIncrementalSync: number; // Still visible nodes (incremental sync)\n nodesHidden: number; // Newly hidden nodes\n indexQueryTimeMs: number;\n syncTimeMs: number;\n}\n\nlet syncStats: SyncStats = {\n nodesVisited: 0,\n nodesCulledByParent: 0,\n nodesCulledByTemporal: 0,\n nodesProcessed: 0,\n nodesFullSync: 0,\n nodesIncrementalSync: 0,\n nodesHidden: 0,\n indexQueryTimeMs: 0,\n syncTimeMs: 0,\n};\n\n/**\n * Visibility delta between frames.\n * Used for incremental updates - only sync what changed.\n */\ninterface VisibilityDelta {\n nowVisible: Set<CloneNode>; // Need full style sync + show\n stillVisible: Set<CloneNode>; // Only sync animated properties (or skip if same time)\n nowHidden: Set<CloneNode>; // Just set display:none\n}\n\n/**\n * Compute visibility delta between previous and current frame.\n */\nfunction computeVisibilityDelta(\n previousSet: Set<CloneNode>,\n currentSet: Set<CloneNode>,\n): VisibilityDelta {\n const nowVisible = new Set<CloneNode>();\n const stillVisible = new Set<CloneNode>();\n const nowHidden = new Set<CloneNode>();\n \n // Find nodes that became visible or stayed visible\n for (const node of currentSet) {\n if (previousSet.has(node)) {\n stillVisible.add(node);\n } else {\n nowVisible.add(node);\n }\n }\n \n // Find nodes that became hidden\n for (const node of previousSet) {\n if (!currentSet.has(node)) {\n nowHidden.add(node);\n }\n }\n \n return { nowVisible, stillVisible, nowHidden };\n}\n\n/**\n * Build visible set by recursive traversal with bounds checking.\n * Queries fresh bounds from source elements each time - bounds are computed\n * dynamically by timegroups based on composition mode.\n */\nfunction buildVisibleSetRecursive(\n node: CloneNode,\n timeMs: number,\n visibleSet: Set<CloneNode>,\n): void {\n const { children, source } = node;\n \n // Get fresh bounds from source element (not cached - timegroup bounds are dynamic)\n const bounds = getTemporalBounds(source);\n \n // Check if this node is visible at current time\n const isVisible = timeMs >= bounds.startMs && timeMs <= bounds.endMs;\n \n if (isVisible) {\n visibleSet.add(node);\n // Recurse to children\n for (const child of children) {\n buildVisibleSetRecursive(child, timeMs, visibleSet);\n }\n }\n // If not visible, skip entire subtree\n}\n\n/**\n * Sync styles with recursive visibility check and delta tracking.\n * \n * DELTA TRACKING: Tracks visibility changes between frames to minimize work:\n * - nowVisible nodes: Full style sync + show\n * - stillVisible nodes: Incremental sync (source DOM may have changed)\n * - nowHidden nodes: Just hide (display:none)\n */\nfunction syncStylesWithIndex(state: SyncState, timeMs: number): void {\n const queryStart = performance.now();\n \n // Build the set of visible nodes by recursive traversal\n const visibleSet = new Set<CloneNode>();\n if (state.tree.root) {\n buildVisibleSetRecursive(state.tree.root, timeMs, visibleSet);\n }\n \n // Compute delta from previous frame\n const delta = computeVisibilityDelta(state.previousVisibleSet, visibleSet);\n \n syncStats.indexQueryTimeMs = performance.now() - queryStart;\n \n // Now traverse the tree but use the delta for O(1) sync decisions\n const syncStart = performance.now();\n if (state.tree.root) {\n syncNodeWithDelta(state.tree.root, visibleSet, delta);\n }\n syncStats.syncTimeMs = performance.now() - syncStart;\n \n // Update state for next frame and expose current visible set\n state.previousVisibleSet = visibleSet;\n state.currentVisibleSet = visibleSet;\n}\n\n/**\n * Sync a node using visibility delta for incremental updates.\n * \n * DELTA TRACKING optimization:\n * - nowVisible: Full style sync (element just appeared)\n * - stillVisible: Incremental sync (source DOM may have changed)\n * - nowHidden: Just hide the element\n * - Not in any set: Skip entirely (was already hidden)\n */\nfunction syncNodeWithDelta(\n node: CloneNode,\n visibleSet: Set<CloneNode>,\n delta: VisibilityDelta,\n): void {\n syncStats.nodesVisited++;\n \n const isVisible = visibleSet.has(node);\n \n if (!isVisible) {\n // Node is not visible - ALWAYS set display:none\n // This handles both \"just became hidden\" and \"initial build with node outside time range\"\n node.clone.style.display = \"none\";\n if (delta.nowHidden.has(node)) {\n syncStats.nodesHidden++;\n }\n // Already hidden nodes: skip (don't even recurse to children)\n syncStats.nodesCulledByTemporal++;\n return;\n }\n \n // Node is visible - determine sync strategy\n if (delta.nowVisible.has(node)) {\n // Just became visible - need full style sync\n syncNodeStyles(node);\n syncStats.nodesFullSync++;\n } else if (delta.stillVisible.has(node)) {\n // Was visible, still visible - still need to sync\n // Source DOM properties can change independently of time (input values, text, etc.)\n // TODO: Phase 5 could track property changes for smarter incremental sync\n syncNodeStyles(node);\n syncStats.nodesIncrementalSync++;\n }\n \n syncStats.nodesProcessed++;\n \n // Recurse to children\n for (const child of node.children) {\n syncNodeWithDelta(child, visibleSet, delta);\n }\n}\n\n/**\n * Legacy recursive sync (kept for comparison/fallback).\n * Returns early if the node is temporally culled, skipping ALL descendants.\n * @deprecated Use syncStylesWithIndex for better performance\n */\nexport function syncNodeRecursiveLegacy(node: CloneNode, timeMs: number): void {\n const { clone, children, bounds } = node;\n syncStats.nodesVisited++;\n \n // Temporal culling - check if this node is visible at current time\n // NOTE: Canvas clones now participate in temporal culling (lazy canvas copying).\n // Invalid bounds [0,0] are treated as [-Infinity, Infinity] by getTemporalBounds.\n {\n // OPTIMIZATION: Check if parent is already hidden to skip bounds computation\n const parent = clone.parentElement;\n if (parent instanceof HTMLElement) {\n // If parent has display:none, this element is already hidden - skip bounds check\n if (parent.style.display === \"none\") {\n clone.style.display = \"none\";\n syncStats.nodesCulledByParent++;\n return;\n }\n }\n \n // Use cached bounds from node instead of calling getTemporalBounds\n const { startMs, endMs } = bounds;\n if (timeMs < startMs || timeMs > endMs) {\n // Hide this element and BAIL OUT - skip all descendants automatically!\n clone.style.display = \"none\";\n syncStats.nodesCulledByTemporal++;\n return;\n }\n }\n \n // Sync this node's styles\n syncNodeStyles(node);\n syncStats.nodesProcessed++;\n \n // Recursively sync children\n for (const child of children) {\n syncNodeRecursiveLegacy(child, timeMs);\n }\n}\n\n/**\n * Sync all CSS properties from source elements to their clones.\n * Uses interval index for O(log n + k) visibility queries instead of O(n) traversal.\n * Uses delta tracking for incremental updates between frames.\n */\nexport function syncStyles(state: SyncState, timeMs: number): void {\n // Reset stats\n syncStats = {\n nodesVisited: 0,\n nodesCulledByParent: 0,\n nodesCulledByTemporal: 0,\n nodesProcessed: 0,\n nodesFullSync: 0,\n nodesIncrementalSync: 0,\n nodesHidden: 0,\n indexQueryTimeMs: 0,\n syncTimeMs: 0,\n };\n \n // Use interval-index-based sync with delta tracking\n syncStylesWithIndex(state, timeMs);\n}\n\n/**\n * Collect document styles for shadow DOM injection.\n */\nexport function collectDocumentStyles(): string {\n const rules: string[] = [];\n try {\n for (const sheet of document.styleSheets) {\n try {\n if (sheet.cssRules) {\n for (const rule of sheet.cssRules) {\n rules.push(rule.cssText);\n }\n }\n } catch {}\n }\n } catch {}\n return rules.join(\"\\n\");\n}\n\n\n// Backward-compatible aliases\nexport const syncStaticStyles = syncStyles;\nexport const syncAnimatedStyles = syncStyles;\n\n/**\n * Override clip-path, opacity, and optionally transform on the root clone element.\n * The source may have these properties set for proxy mode or workbench scaling.\n * \n * @param syncState - The sync state containing the clone tree\n * @param fullReset - If true, also resets opacity and transform (for capture operations)\n */\nexport function overrideRootCloneStyles(syncState: SyncState, fullReset: boolean = false): void {\n const rootClone = syncState.tree.root?.clone;\n if (!rootClone) return;\n \n rootClone.style.clipPath = \"none\";\n if (fullReset) {\n rootClone.style.opacity = \"1\";\n rootClone.style.transform = \"none\";\n }\n}\n\n/**\n * Create a live preview of a timegroup with a refresh function.\n * Used by EFWorkbench for the \"computed\" preview mode.\n * \n * @param source - The source timegroup to preview\n * @returns Object with preview container and refresh function\n */\nexport function renderTimegroupPreview(source: Element): {\n container: HTMLDivElement;\n refresh: (timeMs?: number) => void;\n} {\n const { container, syncState } = buildCloneStructure(source);\n \n // Initial style sync\n syncStyles(syncState, 0);\n \n return {\n container,\n refresh: (timeMs?: number) => {\n syncStyles(syncState, timeMs ?? 0);\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.34.8-beta",
3
+ "version": "0.34.9-beta",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -13,7 +13,7 @@
13
13
  "license": "UNLICENSED",
14
14
  "dependencies": {
15
15
  "@bramus/style-observer": "^1.3.0",
16
- "@editframe/assets": "0.34.8-beta",
16
+ "@editframe/assets": "0.34.9-beta",
17
17
  "@lit/context": "^1.1.6",
18
18
  "@opentelemetry/api": "^1.9.0",
19
19
  "@opentelemetry/context-zone": "^1.26.0",