'use client'; import React, { createContext, useContext, useState, useEffect, useRef } from 'react'; import { RecordEvent, RecordingSession } from '@/types/record-mode'; interface RecordModeContextType { isActive: boolean; setIsActive: (active: boolean) => void; isRecording: boolean; startRecording: () => void; stopRecording: () => void; events: RecordEvent[]; addEvent: (event: Omit) => void; updateEvent: (id: string, event: Partial) => void; removeEvent: (id: string) => void; clearEvents: () => void; isPlaying: boolean; playEvents: () => void; stopPlayback: () => void; currentSession: RecordingSession | null; saveSession: (name: string) => void; } const RecordModeContext = createContext(null); export function useRecordMode() { const context = useContext(RecordModeContext); if (!context) { throw new Error('useRecordMode must be used within a RecordModeProvider'); } return context; } export function RecordModeProvider({ children }: { children: React.ReactNode }) { const [isActive, setIsActive] = useState(false); const [isRecording, setIsRecording] = useState(false); const [events, setEvents] = useState([]); const [isPlaying, setIsPlaying] = useState(false); const [currentSession, setCurrentSession] = useState(null); const startTimeRef = useRef(0); const startRecording = () => { setIsRecording(true); setEvents([]); startTimeRef.current = Date.now(); }; const stopRecording = () => { setIsRecording(false); }; const addEvent = (eventData: Omit) => { const timestamp = Date.now() - startTimeRef.current; const newEvent: RecordEvent = { id: crypto.randomUUID(), timestamp, ...eventData, }; setEvents((prev) => [...prev, newEvent]); }; const updateEvent = (id: string, updates: Partial) => { setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, ...updates } : e))); }; const removeEvent = (id: string) => { setEvents((prev) => prev.filter((e) => e.id !== id)); }; const clearEvents = () => { setEvents([]); }; const playEvents = async () => { if (events.length === 0) return; setIsPlaying(true); // Simple playback logic mostly for preview const startPlayTime = Date.now(); // Sort events by timestamp just in case const sortedEvents = [...events].sort((a, b) => a.timestamp - b.timestamp); for (const event of sortedEvents) { if (!isPlaying) break; // Check if stopped const targetTime = startPlayTime + event.timestamp; const now = Date.now(); const delay = targetTime - now; if (delay > 0) { await new Promise((r) => setTimeout(r, delay)); } // Execute event visual feedback if (document) { if (event.selector) { const el = document.querySelector(event.selector); if (el) { // Highlight or scroll to element if (event.type === 'scroll') { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); } else if (event.type === 'click') { // Visualize click const rect = el.getBoundingClientRect(); const clickMarker = document.createElement('div'); clickMarker.style.position = 'fixed'; clickMarker.style.left = `${rect.left + rect.width / 2}px`; clickMarker.style.top = `${rect.top + rect.height / 2}px`; clickMarker.style.width = '20px'; clickMarker.style.height = '20px'; clickMarker.style.borderRadius = '50%'; clickMarker.style.backgroundColor = 'rgba(255, 0, 0, 0.5)'; clickMarker.style.transform = 'translate(-50%, -50%)'; clickMarker.style.zIndex = '99999'; document.body.appendChild(clickMarker); setTimeout(() => clickMarker.remove(), 500); } } } } } setIsPlaying(false); }; const stopPlayback = () => { setIsPlaying(false); }; const saveSession = (name: string) => { const session: RecordingSession = { id: crypto.randomUUID(), name, events, createdAt: new Date().toISOString(), }; setCurrentSession(session); // Ideally save to local storage or API localStorage.setItem('klz-record-session', JSON.stringify(session)); }; // Load session on mount useEffect(() => { const saved = localStorage.getItem('klz-record-session'); if (saved) { try { setCurrentSession(JSON.parse(saved)); } catch (e) { console.error('Failed to load session', e); } } }, []); return ( {children} ); }