fix: industrial accuracy for record mode events via cross-window sync
This commit is contained in:
@@ -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<RecordEvent, 'id' | 'timestamp'>) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user