diff --git a/components/record-mode/RecordModeContext.tsx b/components/record-mode/RecordModeContext.tsx index a40c88f4..c99926a0 100644 --- a/components/record-mode/RecordModeContext.tsx +++ b/components/record-mode/RecordModeContext.tsx @@ -60,6 +60,15 @@ export function RecordModeProvider({ children }: { children: React.ReactNode }) const [zoomLevel, setZoomLevel] = useState(1); const [isBlurry, setIsBlurry] = useState(false); const [isFeedbackActive, setIsFeedbackActiveState] = useState(false); + const [isEmbedded, setIsEmbedded] = useState(false); + + useEffect(() => { + const embedded = typeof window !== 'undefined' && + (window.location.search.includes('embedded=true') || + window.name === 'record-mode-iframe' || + window.self !== window.top); + setIsEmbedded(embedded); + }, []); // Synchronous mutually exclusive setters const setIsActive = (active: boolean) => { @@ -102,6 +111,48 @@ export function RecordModeProvider({ children }: { children: React.ReactNode }) } }, [isFeedbackActive, isActive]); + // GUEST LISTENERS: Execute events coming from host + useEffect(() => { + if (!isEmbedded) return; + + const handlePlaybackMessage = (e: MessageEvent) => { + if (e.data.type === 'PLAY_EVENT') { + const { event } = e.data; + if (event.selector) { + const el = document.querySelector(event.selector) as HTMLElement; + if (el) { + if (event.type === 'scroll') { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } else if (event.type === 'click') { + // Get CURRENT rect for industrial precision + const currentRect = el.getBoundingClientRect(); + const eventCoords = { + clientX: currentRect.left + currentRect.width / 2, + clientY: currentRect.top + currentRect.height / 2 + }; + + const dispatchMouse = (type: string) => { + el.dispatchEvent(new MouseEvent(type, { + view: window, + bubbles: true, + cancelable: true, + ...eventCoords + })); + }; + dispatchMouse('mousedown'); + dispatchMouse('mouseup'); + dispatchMouse('click'); + el.click(); + } + } + } + } + }; + + window.addEventListener('message', handlePlaybackMessage); + return () => window.removeEventListener('message', handlePlaybackMessage); + }, [isEmbedded]); + const addEvent = (event: Omit) => { const newEvent: RecordEvent = { ...event, @@ -151,42 +202,51 @@ export function RecordModeProvider({ children }: { children: React.ReactNode }) for (const event of sortedEvents) { if (!isPlayingRef.current) break; - // 1. Move Cursor - if (event.rect) { + // 1. Move Cursor (Host logic) + if (event.rect && !isEmbedded) { + const iframe = document.querySelector('iframe[name="record-mode-iframe"]') as HTMLIFrameElement; + const iframeRect = iframe?.getBoundingClientRect(); + + // Add iframe offset to guest-relative coordinates setCursorPosition({ - x: event.rect.x + event.rect.width / 2, - y: event.rect.y + event.rect.height / 2, + x: (iframeRect?.left || 0) + event.rect.x + event.rect.width / 2, + y: (iframeRect?.top || 0) + event.rect.y + event.rect.height / 2, }); } // 2. Handle Action if (event.selector) { - const el = document.querySelector(event.selector) as HTMLElement; - if (el) { - if (event.type === 'scroll') { - el.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } else if (event.type === 'click') { - // Precise industrial click sequence: mousedown -> mouseup -> click - const eventCoords = { - clientX: event.rect ? event.rect.x + event.rect.width / 2 : 0, - clientY: event.rect ? event.rect.y + event.rect.height / 2 : 0 - }; - - const dispatchMouse = (type: string) => { - el.dispatchEvent(new MouseEvent(type, { - view: window, - bubbles: true, - cancelable: true, - ...eventCoords - })); - }; - - dispatchMouse('mousedown'); - dispatchMouse('mouseup'); - dispatchMouse('click'); - - // Fallback for native elements - el.click(); + if (!isEmbedded) { + // HOST: Delegate to Iframe + const iframe = document.querySelector('iframe[name="record-mode-iframe"]') as HTMLIFrameElement; + if (iframe?.contentWindow) { + iframe.contentWindow.postMessage({ type: 'PLAY_EVENT', event }, '*'); + } + } else { + // GUEST (Self-execution failsafe) + const el = document.querySelector(event.selector) as HTMLElement; + if (el) { + if (event.type === 'scroll') { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } else if (event.type === 'click') { + const currentRect = el.getBoundingClientRect(); + const eventCoords = { + clientX: currentRect.left + currentRect.width / 2, + clientY: currentRect.top + currentRect.height / 2 + }; + const dispatchMouse = (type: string) => { + el.dispatchEvent(new MouseEvent(type, { + view: window, + bubbles: true, + cancelable: true, + ...eventCoords + })); + }; + dispatchMouse('mousedown'); + dispatchMouse('mouseup'); + dispatchMouse('click'); + el.click(); + } } } }