diff --git a/packages/studio/src/components/nle/NLELayout.tsx b/packages/studio/src/components/nle/NLELayout.tsx index 2a2fe70b9..068603f12 100644 --- a/packages/studio/src/components/nle/NLELayout.tsx +++ b/packages/studio/src/components/nle/NLELayout.tsx @@ -13,6 +13,7 @@ import type { TimelineElement } from "../../player"; import type { BlockedTimelineEditIntent } from "../../player/components/timelineEditing"; import { NLEPreview } from "./NLEPreview"; import { CompositionBreadcrumb } from "./CompositionBreadcrumb"; +import { usePreviewBlockDrop } from "./usePreviewBlockDrop"; import { useCompositionStack } from "./useCompositionStack"; import { TIMELINE_TOGGLE_SHORTCUT_LABEL, @@ -136,6 +137,22 @@ export const NLELayout = memo(function NLELayout({ usePlayerStore.getState().reset(); } + const stageRefForDrop = useRef(null); + const handleStageRef = useCallback((ref: React.RefObject) => { + stageRefForDrop.current = ref.current; + }, []); + + const { + isDragOver: previewDragOver, + handleDragOver: handlePreviewDragOver, + handleDragLeave: handlePreviewDragLeave, + handleDrop: handlePreviewDrop, + } = usePreviewBlockDrop({ + portrait, + stageRef: stageRefForDrop as React.RefObject, + onBlockDrop: onPreviewBlockDrop, + }); + // Lightweight reload: change iframe src instead of destroying the Player. // refreshPlayer() saves the seek position and appends a cache-busting _t // param — the Player instance stays alive so the adapter is available for @@ -349,7 +366,13 @@ export const NLELayout = memo(function NLELayout({ > {/* Preview + player controls */}
-
+
+ {previewDragOver && ( +
+ )} {!isFullscreen && previewOverlay}
diff --git a/packages/studio/src/components/nle/NLEPreview.tsx b/packages/studio/src/components/nle/NLEPreview.tsx index ec3ea2aa2..7f2d7e543 100644 --- a/packages/studio/src/components/nle/NLEPreview.tsx +++ b/packages/studio/src/components/nle/NLEPreview.tsx @@ -12,8 +12,6 @@ import { type PreviewZoomState, } from "./previewZoom"; import { readStudioUiPreferences, writeStudioUiPreferences } from "../../utils/studioUiPreferences"; -import { usePreviewBlockDrop } from "./usePreviewBlockDrop"; - interface NLEPreviewProps { projectId: string; iframeRef: Ref; @@ -22,7 +20,7 @@ interface NLEPreviewProps { portrait?: boolean; directUrl?: string; suppressLoadingOverlay?: boolean; - onBlockDrop?: (blockName: string, position: { left: number; top: number }) => void; + onStageRef?: (ref: React.RefObject) => void; } export function getPreviewPlayerKey({ @@ -92,11 +90,14 @@ export const NLEPreview = memo(function NLEPreview({ portrait, directUrl, suppressLoadingOverlay, - onBlockDrop, + onStageRef, }: NLEPreviewProps) { const activeKey = getPreviewPlayerKey({ projectId, directUrl }); const viewportRef = useRef(null); const stageRef = useRef(null); + useEffect(() => { + onStageRef?.(stageRef); + }, [onStageRef]); const [stageSize, setStageSize] = useState(() => resolvePreviewStageSize(0, 0, portrait)); const zoomRef = useRef(loadInitialZoom()); @@ -367,13 +368,6 @@ export const NLEPreview = memo(function NLEPreview({ }; }, [applyPan]); - const { - isDragOver: previewDragOver, - handleDragOver: handlePreviewDragOver, - handleDragLeave: handlePreviewDragLeave, - handleDrop: handlePreviewDrop, - } = usePreviewBlockDrop({ portrait, stageRef, onBlockDrop }); - const initial = zoomRef.current; return ( @@ -383,9 +377,6 @@ export const NLEPreview = memo(function NLEPreview({ className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-none focus:ring-1 focus:ring-studio-accent/40 bg-neutral-700" tabIndex={0} aria-label="Composition preview" - onDragOver={handlePreviewDragOver} - onDragLeave={handlePreviewDragLeave} - onDrop={handlePreviewDrop} >
- {previewDragOver && ( -
- )}