feat: enhance Recording Studio with reorderable events, origin options, and hover previews
This commit is contained in:
@@ -11,6 +11,7 @@ interface RecordModeContextType {
|
||||
updateEvent: (id: string, event: Partial<RecordEvent>) => void;
|
||||
removeEvent: (id: string) => void;
|
||||
clearEvents: () => void;
|
||||
setEvents: (events: RecordEvent[]) => void;
|
||||
isPlaying: boolean;
|
||||
playEvents: () => void;
|
||||
stopPlayback: () => void;
|
||||
@@ -21,6 +22,9 @@ interface RecordModeContextType {
|
||||
saveSession: (name: string) => void;
|
||||
isFeedbackActive: boolean;
|
||||
setIsFeedbackActive: (active: boolean) => void;
|
||||
reorderEvents: (startIndex: number, endIndex: number) => void;
|
||||
hoveredEventId: string | null;
|
||||
setHoveredEventId: (id: string | null) => void;
|
||||
}
|
||||
|
||||
const RecordModeContext = createContext<RecordModeContextType | null>(null);
|
||||
@@ -47,6 +51,10 @@ export function useRecordMode(): RecordModeContextType {
|
||||
isFeedbackActive: false,
|
||||
setIsFeedbackActive: () => { },
|
||||
saveSession: () => { },
|
||||
reorderEvents: () => { },
|
||||
hoveredEventId: null,
|
||||
setHoveredEventId: () => { },
|
||||
setEvents: () => { },
|
||||
};
|
||||
}
|
||||
return context;
|
||||
@@ -60,6 +68,7 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
const [zoomLevel, setZoomLevel] = useState(1);
|
||||
const [isBlurry, setIsBlurry] = useState(false);
|
||||
const [isFeedbackActive, setIsFeedbackActiveState] = useState(false);
|
||||
const [hoveredEventId, setHoveredEventId] = useState<string | null>(null);
|
||||
const [isEmbedded, setIsEmbedded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -124,13 +133,30 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
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
|
||||
};
|
||||
|
||||
// Calculate Point based on Origin
|
||||
let targetX = currentRect.left + currentRect.width / 2;
|
||||
let targetY = currentRect.top + currentRect.height / 2;
|
||||
|
||||
if (event.clickOrigin === 'top-left') {
|
||||
targetX = currentRect.left + 5;
|
||||
targetY = currentRect.top + 5;
|
||||
} else if (event.clickOrigin === 'top-right') {
|
||||
targetX = currentRect.right - 5;
|
||||
targetY = currentRect.top + 5;
|
||||
} else if (event.clickOrigin === 'bottom-left') {
|
||||
targetX = currentRect.left + 5;
|
||||
targetY = currentRect.bottom - 5;
|
||||
} else if (event.clickOrigin === 'bottom-right') {
|
||||
targetX = currentRect.right - 5;
|
||||
targetY = currentRect.bottom - 5;
|
||||
}
|
||||
|
||||
const eventCoords = {
|
||||
clientX: targetX,
|
||||
clientY: targetY
|
||||
};
|
||||
const dispatchMouse = (type: string) => {
|
||||
el.dispatchEvent(new MouseEvent(type, {
|
||||
view: window,
|
||||
@@ -153,6 +179,27 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
return () => window.removeEventListener('message', handlePlaybackMessage);
|
||||
}, [isEmbedded]);
|
||||
|
||||
// Sync Hover Preview to Iframe
|
||||
useEffect(() => {
|
||||
if (isEmbedded || !isActive) return;
|
||||
|
||||
const event = events.find(e => e.id === hoveredEventId);
|
||||
const iframe = document.querySelector('iframe[name="record-mode-iframe"]') as HTMLIFrameElement;
|
||||
if (iframe?.contentWindow) {
|
||||
iframe.contentWindow.postMessage({
|
||||
type: 'SET_HOVER_SELECTOR',
|
||||
selector: event?.selector || null
|
||||
}, '*');
|
||||
}
|
||||
}, [hoveredEventId, events, isActive, isEmbedded]);
|
||||
|
||||
const reorderEvents = (startIndex: number, endIndex: number) => {
|
||||
const result = Array.from(events);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
result.splice(endIndex, 0, removed);
|
||||
setEvents(result);
|
||||
};
|
||||
|
||||
const addEvent = (event: Omit<RecordEvent, 'id' | 'timestamp'>) => {
|
||||
const newEvent: RecordEvent = {
|
||||
...event,
|
||||
@@ -230,9 +277,28 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
} else if (event.type === 'click') {
|
||||
const currentRect = el.getBoundingClientRect();
|
||||
|
||||
// Calculate Point based on Origin (same as above for parity)
|
||||
let targetX = currentRect.left + currentRect.width / 2;
|
||||
let targetY = currentRect.top + currentRect.height / 2;
|
||||
|
||||
if (event.clickOrigin === 'top-left') {
|
||||
targetX = currentRect.left + 5;
|
||||
targetY = currentRect.top + 5;
|
||||
} else if (event.clickOrigin === 'top-right') {
|
||||
targetX = currentRect.right - 5;
|
||||
targetY = currentRect.top + 5;
|
||||
} else if (event.clickOrigin === 'bottom-left') {
|
||||
targetX = currentRect.left + 5;
|
||||
targetY = currentRect.bottom - 5;
|
||||
} else if (event.clickOrigin === 'bottom-right') {
|
||||
targetX = currentRect.right - 5;
|
||||
targetY = currentRect.bottom - 5;
|
||||
}
|
||||
|
||||
const eventCoords = {
|
||||
clientX: currentRect.left + currentRect.width / 2,
|
||||
clientY: currentRect.top + currentRect.height / 2
|
||||
clientX: targetX,
|
||||
clientY: targetY
|
||||
};
|
||||
const dispatchMouse = (type: string) => {
|
||||
el.dispatchEvent(new MouseEvent(type, {
|
||||
@@ -282,6 +348,7 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
updateEvent,
|
||||
removeEvent,
|
||||
clearEvents,
|
||||
setEvents,
|
||||
isPlaying,
|
||||
playEvents,
|
||||
stopPlayback,
|
||||
@@ -292,6 +359,9 @@ export function RecordModeProvider({ children }: { children: React.ReactNode })
|
||||
saveSession,
|
||||
isFeedbackActive,
|
||||
setIsFeedbackActive,
|
||||
reorderEvents,
|
||||
hoveredEventId,
|
||||
setHoveredEventId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user