import { useState, useCallback, useMemo, useRef } from "react"; import { useMountEffect } from "../../hooks/useMountEffect"; import { usePlayerStore } from "../store/playerStore"; import { formatTime } from "../lib/time"; import { buildPromptCopyText, buildTimelineAgentPrompt } from "./timelineEditing"; import { copyTextToClipboard } from "../../utils/clipboard"; interface EditPopoverProps { rangeStart: number; rangeEnd: number; anchorX: number; anchorY: number; onClose: () => void; } export function EditPopover({ rangeStart, rangeEnd, anchorX, anchorY, onClose }: EditPopoverProps) { const elements = usePlayerStore((s) => s.elements); const [prompt, setPrompt] = useState(""); const [copiedAgentPrompt, setCopiedAgentPrompt] = useState(false); const [copiedPromptOnly, setCopiedPromptOnly] = useState(false); const popoverRef = useRef(null); const textareaRef = useRef(null); const start = Math.min(rangeStart, rangeEnd); const end = Math.max(rangeStart, rangeEnd); const elementsInRange = useMemo(() => { return elements.filter((el) => { const elEnd = el.start + el.duration; return el.start < end && elEnd > start; }); }, [elements, start, end]); useMountEffect(() => { setTimeout(() => textareaRef.current?.focus(), 50); }); useMountEffect(() => { const handleKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", handleKey); return () => window.removeEventListener("keydown", handleKey); }); useMountEffect(() => { const handleClick = (e: MouseEvent) => { if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) { onClose(); } }; setTimeout(() => window.addEventListener("mousedown", handleClick), 100); return () => window.removeEventListener("mousedown", handleClick); }); const buildClipboardText = useCallback(() => { return buildTimelineAgentPrompt({ rangeStart: start, rangeEnd: end, elements: elementsInRange, prompt, }); }, [start, end, elementsInRange, prompt]); const handleCopy = useCallback(async () => { const copied = await copyTextToClipboard(buildClipboardText()); if (!copied) return; setCopiedAgentPrompt(true); setTimeout(() => { setCopiedAgentPrompt(false); onClose(); }, 800); }, [buildClipboardText, onClose]); const handleCopyPrompt = useCallback(async () => { const promptText = buildPromptCopyText(prompt); if (!promptText) return; const copied = await copyTextToClipboard(promptText); if (!copied) return; setCopiedPromptOnly(true); setTimeout(() => { setCopiedPromptOnly(false); }, 800); }, [prompt]); const style: React.CSSProperties = { position: "fixed", left: Math.max(8, Math.min(anchorX - 160, window.innerWidth - 336)), top: Math.max(8, anchorY - 280), zIndex: 200, }; return (
{/* Header */}
{formatTime(start)} — {formatTime(end)}
{elementsInRange.length} element{elementsInRange.length !== 1 ? "s" : ""}
{/* Elements */} {elementsInRange.length > 0 && (
{elementsInRange.map((el) => (
#{el.id} {el.tag}
))}
)} {/* Prompt */}