mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
* chore(studio): remove all console.* calls from studio package * chore(studio): address review — remove dead stubs, restore consent notice - Delete empty if-blocks left after console removal (snapTargetCollection, Player asset-poll, useTimelineSyncCallbacks 5s probe, useGestureRecording dev guard + now-unused isDevBuild) and the stale "surface in dev" comment. - Drop the dangling no-console pragma + dead duplicate-id branch in sourcePatcher. - Restore the one-time telemetry consent disclosure in showNoticeOnce (kept behind a pragma — it is a user-facing notice, not debug noise). - Remove the missed timelineIcons console.warn while preserving the `tag || "div"` null-safety fallback. - Route caption auto-save failures (a data-loss path) through telemetry instead of swallowing silently. - Restore the accidentally-clobbered css-var-fonts output.mp4 fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(runtime): immediateRender for set tweens + array timeline normalization - Set tweens now emit immediateRender:true so they render on page load without requiring the runtime to seek past position 0 - Runtime IIFE normalizes array timelines (window.__timelines = [tl]) to keyed objects, and auto-adds data-start on root elements - Drag teardown clears translate:none to prevent #1673 fly-off - Position-only set tweens hidden from timeline diamonds (3 cache paths) - Parser: ease-only keyframe update preserves existing properties * fix(runtime): address review — restore perf gate, debug surface, scrub restore - Restore the #1651 skipForInjectedVideo gate in media.ts that was dropped on restack — avoids ~2400 wasted per-tick seeks on video-heavy renders. - Restore the console.debug body + docstring bullet of swallow() in diagnostics.ts: the __hfDebug opt-in debug surface had been gutted to an empty if-block. - Rebind: after the progress-cycle set() kick, seek to state.currentTime via totalTime() instead of snapping to 0, so a rebind after scrub / soft-reload restore keeps the playhead. - Array __timelines normalization + data-start default now resolve the root via a shared findRootCompositionEl() that honors data-root="true" first (matches resolveRootCompositionElement, which now delegates to it). - Ease-only keyframe update leaves a primitive (non-object) keyframe value untouched instead of wiping it to {}; add a preservation unit test. - Document the boundDuration<=0 progress(1) kick + restore the STATIC-case comment in gsapRuntimeBridge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(studio): marquee multi-selection + off-canvas indicators - Click+drag on empty canvas draws dashed selection rectangle - SAT/OBB intersection handles rotated/scaled/skewed elements - Shift+marquee adds to existing selection - Click on empty canvas deselects - Off-canvas elements show dashed outline indicators (clickable) - Dashed border only shows outside canvas, solid inside (clip-path) - 12 geometry unit tests * feat(studio): address review — group-aware off-canvas indicators + fixes - Off-canvas indicator suppression now skips every selected element (primary AND marquee group members), not just the primary, so group members no longer render a doubled overlay (group rect + dashed indicator). - Drop selection from the off-canvas layout effect deps; the selected-element filter runs at render time. Avoids re-walking geometry on each selection change. - applyMarqueeSelection now honors STUDIO_INSPECTOR_PANELS_ENABLED. - Restore the stale-selection clear in useDomEditPreviewSync when the selected element no longer resolves after a re-sync. Drag-release stays handled by suppressNextBoxClickRef. - Off-canvas indicator is keyboard-accessible; canvas cursor driven by marquee rect state, not a render-time ref read. - Rename partiallyOutside -> extendsOutsideComp + comment the clip-path hit-test. - Extract OffCanvasIndicators into its own component (DomEditOverlay was already over the 600-LOC cap on this branch; extraction brings it under). - Declare onUpdateKeyframeEase on PropertyPanelProps so this branch typechecks standalone (handler + wiring already here; only the type had leaked upstack). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
135 lines
4.9 KiB
TypeScript
135 lines
4.9 KiB
TypeScript
/**
|
|
* Side effects for syncing the DOM edit selection with the preview iframe on
|
|
* load/refresh, and for auto-revealing source in the Code tab.
|
|
* Extracted from useDomEditSession to keep file sizes under the 600-line limit.
|
|
*/
|
|
import { useEffect, useRef } from "react";
|
|
import { STUDIO_INSPECTOR_PANELS_ENABLED } from "../components/editor/manualEditingAvailability";
|
|
import { findElementForSelection, type DomEditSelection } from "../components/editor/domEditing";
|
|
import { reapplyPositionEditsAfterSeek } from "../components/editor/manualEdits";
|
|
import type { SidebarTab } from "../components/sidebar/LeftSidebar";
|
|
import type { PatchTarget } from "../utils/sourcePatcher";
|
|
|
|
interface UseDomEditPreviewSyncParams {
|
|
previewIframe: HTMLIFrameElement | null;
|
|
activeCompPath: string | null;
|
|
captionEditMode: boolean;
|
|
domEditSelectionRef: React.MutableRefObject<DomEditSelection | null>;
|
|
domEditSelection: DomEditSelection | null;
|
|
applyDomSelection: (
|
|
selection: DomEditSelection | null,
|
|
options?: { revealPanel?: boolean; preserveGroup?: boolean },
|
|
) => void;
|
|
buildDomSelectionFromTarget: (element: HTMLElement) => Promise<DomEditSelection | null>;
|
|
refreshPreviewDocumentVersion: () => void;
|
|
syncPreviewHistoryHotkey: (iframe: HTMLIFrameElement | null) => void;
|
|
applyStudioManualEditsToPreviewRef: React.MutableRefObject<
|
|
(iframe: HTMLIFrameElement) => Promise<void>
|
|
>;
|
|
openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void;
|
|
getSidebarTab?: () => SidebarTab;
|
|
gsapCacheVersion?: number;
|
|
}
|
|
|
|
export function useDomEditPreviewSync({
|
|
previewIframe,
|
|
activeCompPath,
|
|
captionEditMode,
|
|
domEditSelectionRef,
|
|
domEditSelection,
|
|
applyDomSelection,
|
|
buildDomSelectionFromTarget,
|
|
refreshPreviewDocumentVersion,
|
|
syncPreviewHistoryHotkey,
|
|
applyStudioManualEditsToPreviewRef,
|
|
openSourceForSelection,
|
|
getSidebarTab,
|
|
gsapCacheVersion,
|
|
}: UseDomEditPreviewSyncParams): void {
|
|
// Sync selection from preview document on load / refresh
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
useEffect(() => {
|
|
if (!previewIframe) return;
|
|
|
|
// fallow-ignore-next-line complexity
|
|
const syncSelectionFromDocument = async () => {
|
|
if (!STUDIO_INSPECTOR_PANELS_ENABLED || captionEditMode) return;
|
|
const currentSelection = domEditSelectionRef.current;
|
|
if (!currentSelection) return;
|
|
let doc: Document | null = null;
|
|
try {
|
|
doc = previewIframe.contentDocument;
|
|
} catch {
|
|
return;
|
|
}
|
|
if (!doc) return;
|
|
|
|
reapplyPositionEditsAfterSeek(doc);
|
|
|
|
const nextElement = findElementForSelection(doc, currentSelection, activeCompPath);
|
|
if (!nextElement) {
|
|
// The selected element no longer resolves in the (re-synced) document
|
|
// — comp/hot reload, activeCompPath swap, or post-save replacement.
|
|
// Clear so overlay geometry isn't computed on a stale, detached node.
|
|
// (Drag-release-in-gray-zone is handled separately by
|
|
// suppressNextBoxClickRef; the dragged element still resolves here.)
|
|
applyDomSelection(null, { revealPanel: false });
|
|
return;
|
|
}
|
|
|
|
const nextSelection = await buildDomSelectionFromTarget(nextElement);
|
|
if (nextSelection) {
|
|
applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
|
|
}
|
|
};
|
|
|
|
syncPreviewHistoryHotkey(previewIframe);
|
|
void applyStudioManualEditsToPreviewRef.current(previewIframe);
|
|
void syncSelectionFromDocument();
|
|
refreshPreviewDocumentVersion();
|
|
|
|
const handleLoad = () => {
|
|
syncPreviewHistoryHotkey(previewIframe);
|
|
void applyStudioManualEditsToPreviewRef.current(previewIframe);
|
|
void syncSelectionFromDocument();
|
|
refreshPreviewDocumentVersion();
|
|
};
|
|
|
|
previewIframe.addEventListener("load", handleLoad);
|
|
return () => {
|
|
previewIframe.removeEventListener("load", handleLoad);
|
|
};
|
|
}, [
|
|
activeCompPath,
|
|
applyDomSelection,
|
|
buildDomSelectionFromTarget,
|
|
captionEditMode,
|
|
domEditSelectionRef,
|
|
previewIframe,
|
|
refreshPreviewDocumentVersion,
|
|
syncPreviewHistoryHotkey,
|
|
applyStudioManualEditsToPreviewRef,
|
|
gsapCacheVersion,
|
|
]);
|
|
|
|
// Auto-reveal source when an element is selected while the Code tab is active.
|
|
// Use a ref for the callback so the effect only fires on selection changes,
|
|
// not when openSourceForSelection is recreated due to editingFile content updates.
|
|
const openSourceRef = useRef(openSourceForSelection);
|
|
openSourceRef.current = openSourceForSelection;
|
|
useEffect(
|
|
// fallow-ignore-next-line complexity
|
|
() => {
|
|
if (!domEditSelection || !openSourceRef.current || !getSidebarTab) return;
|
|
if (!domEditSelection.sourceFile) return;
|
|
if (getSidebarTab() !== "code") return;
|
|
openSourceRef.current(domEditSelection.sourceFile, {
|
|
id: domEditSelection.id,
|
|
selector: domEditSelection.selector,
|
|
selectorIndex: domEditSelection.selectorIndex,
|
|
});
|
|
},
|
|
[domEditSelection, getSidebarTab],
|
|
);
|
|
}
|