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 [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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user