"use client"; import { useState } from "react"; import { TransitionUpIcon } from "@opencut/ui/icons"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; import { Button } from "../ui/button"; import { Label } from "../ui/label"; import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; import { Progress } from "../ui/progress"; import { Checkbox } from "../ui/checkbox"; import { cn } from "@/utils/ui"; import { getExportMimeType, getExportFileExtension } from "@/utils/export"; import { Check, Copy, Download, RotateCcw, X } from "lucide-react"; import { EXPORT_FORMAT_VALUES, EXPORT_QUALITY_VALUES, ExportFormat, ExportQuality, ExportResult, } from "@/types/export"; import { PropertyGroup } from "./properties-panel/property-item"; import { useEditor } from "@/hooks/use-editor"; import { DEFAULT_EXPORT_OPTIONS } from "@/constants/export-constants"; export function ExportButton() { const [isExportPopoverOpen, setIsExportPopoverOpen] = useState(false); const editor = useEditor(); const handleExport = () => { setIsExportPopoverOpen(true); }; const hasProject = !!editor.project.getActive(); return ( {hasProject && } ); } function ExportPopover({ onOpenChange, }: { onOpenChange: (open: boolean) => void; }) { const editor = useEditor(); const activeProject = editor.project.getActive(); const [format, setFormat] = useState( DEFAULT_EXPORT_OPTIONS.format, ); const [quality, setQuality] = useState( DEFAULT_EXPORT_OPTIONS.quality, ); const [includeAudio, setIncludeAudio] = useState( DEFAULT_EXPORT_OPTIONS.includeAudio || true, ); const [isExporting, setIsExporting] = useState(false); const [progress, setProgress] = useState(0); const [exportResult, setExportResult] = useState(null); const handleExport = async () => { if (!activeProject) return; setIsExporting(true); setProgress(0); setExportResult(null); const result = await editor.project.export({ options: { format, quality, fps: activeProject.settings.fps, includeAudio, onProgress: ({ progress }) => setProgress(progress), onCancel: () => false, // TODO: add cancel functionality }, }); setIsExporting(false); setExportResult(result); if (result.success && result.buffer) { // Download the file const mimeType = getExportMimeType({ format }); const extension = getExportFileExtension({ format }); const blob = new Blob([result.buffer], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${activeProject.metadata.name}${extension}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); onOpenChange(false); setExportResult(null); setProgress(0); } }; const handleClose = () => { if (!isExporting) { onOpenChange(false); setExportResult(null); setProgress(0); } }; return ( <> {exportResult && !exportResult.success ? ( ) : ( <>

{isExporting ? "Exporting project" : "Export project"}

{!isExporting && ( <>
{ if (isExportFormat(value)) { setFormat(value); } }} >
{ if (isExportQuality(value)) { setQuality(value); } }} >
setIncludeAudio(!!checked) } />
)} {isExporting && (

{Math.round(progress * 100)}%

100%

)}
)}
); } function isExportFormat(value: string): value is ExportFormat { return EXPORT_FORMAT_VALUES.some((formatValue) => formatValue === value); } function isExportQuality(value: string): value is ExportQuality { return EXPORT_QUALITY_VALUES.some((qualityValue) => qualityValue === value); } function ExportError({ error, onRetry, }: { error: string; onRetry: () => void; }) { const [copied, setCopied] = useState(false); const handleCopy = async () => { await navigator.clipboard.writeText(error); setCopied(true); setTimeout(() => setCopied(false), 1000); }; return (

Export failed

{error}

); }