import React, { useState } from "react"; import { useEditor } from "./EditorContext"; import VideoPlayer from "../../components/VideoPlayer"; import HotspotOverlay from "../../components/HotspotOverlay"; import ClickRippleOverlay from "../../components/ClickRippleOverlay"; import WalkthroughPlayer from "../../components/WalkthroughPlayer"; import TerminalPlayer from "../../components/TerminalPlayer"; import Timeline from "../../components/Timeline"; interface EditorMainProps { videoRef: React.RefObject; } const EditorMain: React.FC = ({ videoRef }) => { const { editorMode, activeTab, videoUrl, playing, setPlaying, currentFrame, totalFrames, togglePlay, placingHotspot, setPlacingHotspot, placingHotspotStyle, setPlacingHotspotStyle, handleStageClick, hotspots, editingHotspots, setEditingHotspots, selectedHotspotId, setSelectedHotspotId, seekToFrame, steps, handleSelectStep, splitSelectedStepAtCurrentFrame, walkthroughMode, walkthroughSteps, seekToTimestamp, setWalkthroughMode, terminalUrl, currentTime, duration, fps, markers, isProcessing, clicks, showClickRipples, handleUpdateHotspot, handleDeleteHotspot, handleVideoLoadError, } = useEditor(); const editEnabled = editorMode === "edit"; const editableHotspots = editEnabled && editingHotspots; const canPlaceHotspot = editEnabled && placingHotspot; const allowHotspotNavigation = editorMode === "preview"; const [toolTooltip, setToolTooltip] = useState<{ key: "hotspot" | "callout" | "chapter"; label: string; rect: { left: number; top: number; width: number; height: number }; } | null>(null); const startPlacing = (style: "pulse" | "callout") => { setWalkthroughMode(false); setEditingHotspots(true); setSelectedHotspotId(null); setPlacingHotspotStyle(style); // Toggle off if user clicks the already-active tool. if (placingHotspot && placingHotspotStyle === style) { setPlacingHotspot(false); return; } setPlacingHotspot(true); }; const toolActive = (tool: "hotspot" | "callout") => { if (!editEnabled) return false; if (!placingHotspot) return false; if (tool === "hotspot") return placingHotspotStyle === "pulse"; return placingHotspotStyle === "callout"; }; const showCanvasTools = editEnabled && !isProcessing && (activeTab === "video" || activeTab === "both"); return (
{toolTooltip && (
{toolTooltip.label}
)} {showCanvasTools && (
{( [ { key: "hotspot" as const, label: "Hotspot", active: toolActive("hotspot"), onClick: () => startPlacing("pulse"), icon: ( ), }, { key: "callout" as const, label: "Callout", active: toolActive("callout"), onClick: () => startPlacing("callout"), icon: ( ), }, { key: "chapter" as const, label: "Chapter", active: false, onClick: () => splitSelectedStepAtCurrentFrame(), icon: ( ), }, ] as const ).map((tool) => ( ))}
{canPlaceHotspot && (
Click on the video to place hotspot
)}
)}
{(activeTab === "video" || activeTab === "both") && (
{isProcessing ? (
Processing recording...
This may take a moment
) : ( setPlaying(true)} onPause={() => setPlaying(false)} onEnded={() => setPlaying(false)} onLoadError={handleVideoLoadError} onStageClick={ canPlaceHotspot ? handleStageClick : undefined } > { if (destination.type === "step") { handleSelectStep(destination.stepId); return; } if (destination.type === "this_step") { return; } const currentStepIndex = steps.findIndex( (step) => (step.hotspot_ids ?? []).includes( hotspotId ) ); if (currentStepIndex < 0) return; const next = steps[currentStepIndex + 1]; if (!next) return; handleSelectStep(next.id); } : undefined } /> {walkthroughMode && ( seekToTimestamp(timestampMs / 1000) } onExit={() => { setWalkthroughMode(false); setPlaying(false); videoRef.current?.pause(); }} /> )} )}
)} {(activeTab === "terminal" || activeTab === "both") && terminalUrl && (
)}
); }; export default EditorMain;