fix: industrial accuracy for record mode events via cross-window sync

This commit is contained in:
2026-02-15 18:10:59 +01:00
parent 4e762ebfdf
commit 460eeec0bb

View File

@@ -60,6 +60,15 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
const [zoomLevel, setZoomLevel] = useState(1); const [zoomLevel, setZoomLevel] = useState(1);
const [isBlurry, setIsBlurry] = useState(false); const [isBlurry, setIsBlurry] = useState(false);
const [isFeedbackActive, setIsFeedbackActiveState] = 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 // Synchronous mutually exclusive setters
const setIsActive = (active: boolean) => { const setIsActive = (active: boolean) => {
@@ -102,6 +111,48 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
} }
}, [isFeedbackActive, isActive]); }, [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<RecordEvent, 'id' | 'timestamp'>) => { const addEvent = (event: Omit<RecordEvent, 'id' | 'timestamp'>) => {
const newEvent: RecordEvent = { const newEvent: RecordEvent = {
...event, ...event,
@@ -151,42 +202,51 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
for (const event of sortedEvents) { for (const event of sortedEvents) {
if (!isPlayingRef.current) break; if (!isPlayingRef.current) break;
// 1. Move Cursor // 1. Move Cursor (Host logic)
if (event.rect) { 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({ setCursorPosition({
x: event.rect.x + event.rect.width / 2, x: (iframeRect?.left || 0) + event.rect.x + event.rect.width / 2,
y: event.rect.y + event.rect.height / 2, y: (iframeRect?.top || 0) + event.rect.y + event.rect.height / 2,
}); });
} }
// 2. Handle Action // 2. Handle Action
if (event.selector) { if (event.selector) {
const el = document.querySelector(event.selector) as HTMLElement; if (!isEmbedded) {
if (el) { // HOST: Delegate to Iframe
if (event.type === 'scroll') { const iframe = document.querySelector('iframe[name="record-mode-iframe"]') as HTMLIFrameElement;
el.scrollIntoView({ behavior: 'smooth', block: 'center' }); if (iframe?.contentWindow) {
} else if (event.type === 'click') { iframe.contentWindow.postMessage({ type: 'PLAY_EVENT', event }, '*');
// Precise industrial click sequence: mousedown -> mouseup -> click }
const eventCoords = { } else {
clientX: event.rect ? event.rect.x + event.rect.width / 2 : 0, // GUEST (Self-execution failsafe)
clientY: event.rect ? event.rect.y + event.rect.height / 2 : 0 const el = document.querySelector(event.selector) as HTMLElement;
}; if (el) {
if (event.type === 'scroll') {
const dispatchMouse = (type: string) => { el.scrollIntoView({ behavior: 'smooth', block: 'center' });
el.dispatchEvent(new MouseEvent(type, { } else if (event.type === 'click') {
view: window, const currentRect = el.getBoundingClientRect();
bubbles: true, const eventCoords = {
cancelable: true, clientX: currentRect.left + currentRect.width / 2,
...eventCoords clientY: currentRect.top + currentRect.height / 2
})); };
}; const dispatchMouse = (type: string) => {
el.dispatchEvent(new MouseEvent(type, {
dispatchMouse('mousedown'); view: window,
dispatchMouse('mouseup'); bubbles: true,
dispatchMouse('click'); cancelable: true,
...eventCoords
// Fallback for native elements }));
el.click(); };
dispatchMouse('mousedown');
dispatchMouse('mouseup');
dispatchMouse('click');
el.click();
}
} }
} }
} }