From b1be35ba8cdf9dc1a33fe00d3187a525eb873591 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 28 Dec 2025 13:06:30 +0100 Subject: [PATCH] - Add timelapse editor with trim, speed adjustment, and music overlay - Add TimelapseEditorModal component with visual timeline scrubber - Add timelapse processing endpoints (info, thumbnails, process) - Add TimelapseProcessor service using FFmpeg for video processing - Support trim (start/end), speed (0.25x-4x), and audio overlay - Add Edit button to TimelapseViewer - Fix browser caching with timestamp query parameter - Change default timelapse playback speed from 2x to 1x --- CHANGELOG.md | 2 + README.md | 1 + backend/app/api/routes/archives.py | 171 +++- backend/app/schemas/timelapse.py | 30 + backend/app/services/timelapse_processor.py | 264 ++++++ frontend/src/api/client.ts | 52 +- .../src/components/TimelapseEditorModal.tsx | 546 ++++++++++++ frontend/src/components/TimelapseViewer.tsx | 33 +- frontend/src/pages/ArchivesPage.tsx | 5 + .../{index-BonMKEhM.js => index-BB4adzNL.js} | 805 +++++++++--------- static/assets/index-Cwam9OQ3.css | 1 + static/assets/index-DGJjegsB.css | 1 - static/index.html | 4 +- 13 files changed, 1507 insertions(+), 408 deletions(-) create mode 100644 backend/app/schemas/timelapse.py create mode 100644 backend/app/services/timelapse_processor.py create mode 100644 frontend/src/components/TimelapseEditorModal.tsx rename static/assets/{index-BonMKEhM.js => index-BB4adzNL.js} (67%) create mode 100644 static/assets/index-Cwam9OQ3.css delete mode 100644 static/assets/index-DGJjegsB.css diff --git a/CHANGELOG.md b/CHANGELOG.md index a0f496a99..84959e4ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file. ## [0.1.6b] - 2025-12-28 ### Added +- **Timelapse editor** - Edit timelapse videos with trim, speed adjustment (0.25x-4x), and music overlay. Uses FFmpeg for server-side processing with browser-based preview. - **Docker printer discovery** - Subnet scanning for discovering printers when running in Docker with `network_mode: host`. Automatically detects Docker environment and shows subnet input field in Add Printer dialog. - **Printer model mapping** - Discovery now shows friendly model names (X1C, H2D, P1S) instead of raw SSDP codes (BL-P001, O1D, C11). - **Discovery API tests** - Comprehensive test coverage for discovery endpoints. @@ -14,6 +15,7 @@ All notable changes to Bambuddy will be documented in this file. - **Attachment file validation** - File type validation for project attachments (images, documents, 3D files, archives, scripts, configs). ### Changed +- **Timelapse viewer** - Default playback speed changed from 2x to 1x. - **GitHub issue template** - Added mandatory printer firmware version field and LAN-only mode checkbox for better bug reports. - **Docker compose** - Clearer comments explaining `network_mode: host` requirement for printer discovery and camera streaming. - **Project card design** - Enhanced visual polish with gradients, shadows, and glow effects on hover. diff --git a/README.md b/README.md index f52034b4d..b22effda9 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ - 3D model preview (Three.js) - Duplicate detection & full-text search - Photo attachments & failure analysis +- Timelapse editor (trim, speed, music) - Re-print to any connected printer - Archive comparison (side-by-side diff) diff --git a/backend/app/api/routes/archives.py b/backend/app/api/routes/archives.py index 737cc67f8..634776ab9 100644 --- a/backend/app/api/routes/archives.py +++ b/backend/app/api/routes/archives.py @@ -3,7 +3,7 @@ import logging import zipfile from pathlib import Path -from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile +from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile from fastapi.responses import FileResponse, Response from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession @@ -875,10 +875,17 @@ async def get_timelapse(archive_id: int, db: AsyncSession = Depends(get_db)): if not timelapse_path.exists(): raise HTTPException(404, "Timelapse file not found") + # Use file modification time as ETag to bust cache after processing + mtime = int(timelapse_path.stat().st_mtime) + return FileResponse( path=timelapse_path, media_type="video/mp4", filename=f"{archive.print_name or 'timelapse'}.mp4", + headers={ + "Cache-Control": "no-cache, must-revalidate", + "ETag": f'"{mtime}"', + }, ) @@ -1160,6 +1167,168 @@ async def upload_timelapse( return {"status": "attached", "filename": file.filename} +@router.get("/{archive_id}/timelapse/info") +async def get_timelapse_info(archive_id: int, db: AsyncSession = Depends(get_db)): + """Get timelapse video metadata for editor.""" + from backend.app.schemas.timelapse import TimelapseInfoResponse + from backend.app.services.timelapse_processor import TimelapseProcessor + + service = ArchiveService(db) + archive = await service.get_archive(archive_id) + if not archive or not archive.timelapse_path: + raise HTTPException(404, "Timelapse not found") + + timelapse_path = settings.base_dir / archive.timelapse_path + if not timelapse_path.exists(): + raise HTTPException(404, "Timelapse file not found") + + try: + processor = TimelapseProcessor(timelapse_path) + info = await processor.get_info() + return TimelapseInfoResponse(**info) + except Exception as e: + logger.error(f"Failed to get timelapse info: {e}") + raise HTTPException(500, f"Failed to get video info: {str(e)}") + + +@router.get("/{archive_id}/timelapse/thumbnails") +async def get_timelapse_thumbnails( + archive_id: int, + count: int = Query(10, ge=1, le=30), + width: int = Query(160, ge=80, le=320), + db: AsyncSession = Depends(get_db), +): + """Generate timeline thumbnail frames for visual scrubbing.""" + import base64 + + from backend.app.schemas.timelapse import ThumbnailResponse + from backend.app.services.timelapse_processor import TimelapseProcessor + + service = ArchiveService(db) + archive = await service.get_archive(archive_id) + if not archive or not archive.timelapse_path: + raise HTTPException(404, "Timelapse not found") + + timelapse_path = settings.base_dir / archive.timelapse_path + if not timelapse_path.exists(): + raise HTTPException(404, "Timelapse file not found") + + try: + processor = TimelapseProcessor(timelapse_path) + thumbnails = await processor.generate_thumbnails(count, width) + + return ThumbnailResponse( + thumbnails=[base64.b64encode(data).decode() for _, data in thumbnails], + timestamps=[ts for ts, _ in thumbnails], + ) + except Exception as e: + logger.error(f"Failed to generate thumbnails: {e}") + raise HTTPException(500, f"Failed to generate thumbnails: {str(e)}") + + +@router.post("/{archive_id}/timelapse/process") +async def process_timelapse( + archive_id: int, + trim_start: float = Form(0), + trim_end: float = Form(None), + speed: float = Form(1.0), + save_mode: str = Form("new"), + output_filename: str = Form(None), + audio: UploadFile = File(None), + db: AsyncSession = Depends(get_db), +): + """Process timelapse with trim, speed, and optional audio overlay.""" + import shutil + import tempfile + + from backend.app.schemas.timelapse import ProcessResponse + from backend.app.services.timelapse_processor import TimelapseProcessor + + # Validate speed + if not 0.25 <= speed <= 4.0: + raise HTTPException(400, "Speed must be between 0.25 and 4.0") + + if save_mode not in ("replace", "new"): + raise HTTPException(400, "save_mode must be 'replace' or 'new'") + + service = ArchiveService(db) + archive = await service.get_archive(archive_id) + if not archive or not archive.timelapse_path: + raise HTTPException(404, "Timelapse not found") + + timelapse_path = settings.base_dir / archive.timelapse_path + if not timelapse_path.exists(): + raise HTTPException(404, "Timelapse file not found") + + archive_dir = timelapse_path.parent + + # Handle audio file + audio_temp_path = None + if audio and audio.filename: + # Validate audio file extension + if not audio.filename.lower().endswith((".mp3", ".wav", ".m4a", ".aac", ".ogg")): + raise HTTPException(400, "Audio must be .mp3, .wav, .m4a, .aac, or .ogg") + + audio_content = await audio.read() + suffix = Path(audio.filename).suffix + audio_temp_path = Path(tempfile.gettempdir()) / f"audio_{archive_id}{suffix}" + audio_temp_path.write_bytes(audio_content) + + try: + processor = TimelapseProcessor(timelapse_path) + + # Determine output path + if save_mode == "replace": + # Process to temp file first, then replace + temp_output = Path(tempfile.gettempdir()) / f"processed_{archive_id}.mp4" + output_path = temp_output + else: + # Save as new file alongside original + filename = output_filename or f"{archive.print_name or 'timelapse'}_edited.mp4" + # Sanitize filename + filename = "".join(c for c in filename if c.isalnum() or c in "._- ") + if not filename.endswith(".mp4"): + filename += ".mp4" + output_path = archive_dir / filename + + success = await processor.process( + output_path=output_path, + trim_start=trim_start, + trim_end=trim_end, + speed=speed, + audio_path=audio_temp_path, + ) + + if not success: + raise HTTPException(500, "Video processing failed") + + # Handle save mode + if save_mode == "replace": + # Replace original file + shutil.move(str(output_path), str(timelapse_path)) + final_path = archive.timelapse_path + message = "Timelapse replaced successfully" + else: + final_path = str(output_path.relative_to(settings.base_dir)) + message = f"Saved as {output_path.name}" + + return ProcessResponse( + status="completed", + output_path=final_path, + message=message, + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"Timelapse processing failed: {e}") + raise HTTPException(500, f"Processing failed: {str(e)}") + finally: + # Cleanup temp audio file + if audio_temp_path and audio_temp_path.exists(): + audio_temp_path.unlink() + + # ============================================ # Photo Endpoints # ============================================ diff --git a/backend/app/schemas/timelapse.py b/backend/app/schemas/timelapse.py new file mode 100644 index 000000000..045b10bf4 --- /dev/null +++ b/backend/app/schemas/timelapse.py @@ -0,0 +1,30 @@ +"""Schemas for timelapse video processing.""" + +from pydantic import BaseModel, Field + + +class TimelapseInfoResponse(BaseModel): + """Video metadata response.""" + + duration: float = Field(description="Video duration in seconds") + width: int = Field(description="Video width in pixels") + height: int = Field(description="Video height in pixels") + fps: float = Field(description="Frames per second") + codec: str = Field(description="Video codec name") + file_size: int = Field(description="File size in bytes") + has_audio: bool = Field(description="Whether video has audio track") + + +class ThumbnailResponse(BaseModel): + """Timeline thumbnail response.""" + + thumbnails: list[str] = Field(description="Base64 encoded JPEG thumbnails") + timestamps: list[float] = Field(description="Timestamp for each thumbnail in seconds") + + +class ProcessResponse(BaseModel): + """Processing result response.""" + + status: str = Field(description="Processing status: completed, error") + output_path: str | None = Field(default=None, description="Relative path to output file") + message: str = Field(description="Status message") diff --git a/backend/app/services/timelapse_processor.py b/backend/app/services/timelapse_processor.py new file mode 100644 index 000000000..2abcbf451 --- /dev/null +++ b/backend/app/services/timelapse_processor.py @@ -0,0 +1,264 @@ +"""Timelapse video processing service using FFmpeg.""" + +import asyncio +import json +import logging +import tempfile +from pathlib import Path + +from backend.app.services.camera import get_ffmpeg_path + +logger = logging.getLogger(__name__) + + +class TimelapseProcessor: + """Service for processing timelapse videos with FFmpeg.""" + + def __init__(self, input_path: Path): + self.input_path = input_path + self.ffmpeg = get_ffmpeg_path() + if not self.ffmpeg: + raise RuntimeError("FFmpeg not found") + # Derive ffprobe path from ffmpeg path + self.ffprobe = self.ffmpeg.replace("ffmpeg", "ffprobe") + + async def get_info(self) -> dict: + """Get video metadata using ffprobe.""" + cmd = [ + self.ffprobe, + "-v", + "quiet", + "-print_format", + "json", + "-show_format", + "-show_streams", + str(self.input_path), + ] + + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + + if process.returncode != 0: + logger.error(f"ffprobe failed: {stderr.decode()}") + raise RuntimeError(f"ffprobe failed: {stderr.decode()}") + + data = json.loads(stdout.decode()) + video_stream = next( + (s for s in data.get("streams", []) if s.get("codec_type") == "video"), + {}, + ) + audio_stream = next( + (s for s in data.get("streams", []) if s.get("codec_type") == "audio"), + None, + ) + + # Parse frame rate (can be "30/1" or "29.97") + fps = 30.0 + r_frame_rate = video_stream.get("r_frame_rate", "30/1") + try: + if "/" in r_frame_rate: + num, den = r_frame_rate.split("/") + fps = float(num) / float(den) + else: + fps = float(r_frame_rate) + except (ValueError, ZeroDivisionError): + pass + + return { + "duration": float(data.get("format", {}).get("duration", 0)), + "width": video_stream.get("width", 0), + "height": video_stream.get("height", 0), + "fps": fps, + "codec": video_stream.get("codec_name", "unknown"), + "file_size": int(data.get("format", {}).get("size", 0)), + "has_audio": audio_stream is not None, + } + + async def generate_thumbnails( + self, + count: int = 10, + width: int = 160, + ) -> list[tuple[float, bytes]]: + """Generate evenly-spaced thumbnail frames.""" + info = await self.get_info() + duration = info["duration"] + + if duration <= 0: + return [] + + interval = duration / max(count, 1) + thumbnails = [] + + with tempfile.TemporaryDirectory() as tmpdir: + for i in range(count): + timestamp = i * interval + output_path = Path(tmpdir) / f"thumb_{i:03d}.jpg" + + cmd = [ + self.ffmpeg, + "-y", + "-ss", + str(timestamp), + "-i", + str(self.input_path), + "-vframes", + "1", + "-vf", + f"scale={width}:-1", + "-q:v", + "5", + str(output_path), + ] + + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await process.communicate() + + if output_path.exists(): + thumbnails.append((timestamp, output_path.read_bytes())) + + return thumbnails + + async def process( + self, + output_path: Path, + trim_start: float = 0, + trim_end: float | None = None, + speed: float = 1.0, + audio_path: Path | None = None, + audio_volume: float = 1.0, + ) -> bool: + """Process video with trim, speed, and optional audio overlay. + + Args: + output_path: Where to save the processed video + trim_start: Start time in seconds + trim_end: End time in seconds (None = full duration) + speed: Speed multiplier (0.25 to 4.0) + audio_path: Optional music file to overlay + audio_volume: Volume for audio overlay (0.0 to 1.0) + + Returns: + True if processing succeeded, False otherwise + """ + # Build FFmpeg command + cmd = [self.ffmpeg, "-y"] + + # Input seeking (fast seek before input) + if trim_start > 0: + cmd.extend(["-ss", str(trim_start)]) + + cmd.extend(["-i", str(self.input_path)]) + + # Add audio input if provided + if audio_path: + cmd.extend(["-i", str(audio_path)]) + + # Duration limit + if trim_end is not None and trim_end > trim_start: + duration = trim_end - trim_start + cmd.extend(["-t", str(duration)]) + + # Build filters - use filter_complex when we have audio overlay + video_filter = "" + if speed != 1.0: + # setpts changes video speed: PTS/speed = faster, PTS*speed = slower + setpts_value = 1.0 / speed + video_filter = f"setpts={setpts_value}*PTS" + + if audio_path: + # Use filter_complex for audio overlay (can't mix with -vf/-af) + filter_parts = [] + + # Video filter + if video_filter: + filter_parts.append(f"[0:v]{video_filter}[v]") + video_out = "[v]" + else: + video_out = "0:v" + + # Audio filter with volume + filter_parts.append(f"[1:a]volume={audio_volume}[a]") + + cmd.extend(["-filter_complex", ";".join(filter_parts)]) + cmd.extend(["-map", video_out, "-map", "[a]"]) + cmd.extend(["-shortest"]) + elif speed != 1.0: + # No audio overlay - use simple -vf and -af + if video_filter: + cmd.extend(["-vf", video_filter]) + # Adjust original audio speed with atempo + atempo_chain = self._build_atempo_chain(speed) + if atempo_chain: + cmd.extend(["-af", atempo_chain]) + + # Output settings + cmd.extend( + [ + "-c:v", + "libx264", + "-preset", + "fast", + "-crf", + "23", + "-c:a", + "aac", + "-b:a", + "128k", + "-movflags", + "+faststart", # Enable streaming + str(output_path), + ] + ) + + logger.info(f"Processing timelapse: {' '.join(cmd)}") + + # Run FFmpeg + process = await asyncio.create_subprocess_exec( + *cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + _, stderr = await process.communicate() + + if process.returncode != 0: + logger.error(f"FFmpeg processing failed: {stderr.decode()}") + return False + + return output_path.exists() + + def _build_atempo_chain(self, speed: float) -> str: + """Build atempo filter chain. + + atempo filter only supports values between 0.5 and 2.0, + so we chain multiple filters for extreme speeds. + """ + if speed == 1.0: + return "" + + filters = [] + remaining_speed = speed + + # Handle speeds > 2.0 by chaining atempo=2.0 + while remaining_speed > 2.0: + filters.append("atempo=2.0") + remaining_speed /= 2.0 + + # Handle speeds < 0.5 by chaining atempo=0.5 + while remaining_speed < 0.5: + filters.append("atempo=0.5") + remaining_speed *= 2.0 + + # Add final atempo for remaining adjustment + if 0.5 <= remaining_speed <= 2.0 and remaining_speed != 1.0: + filters.append(f"atempo={remaining_speed:.4f}") + + return ",".join(filters) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 66d4dda06..ce534a01e 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1404,7 +1404,7 @@ export const api = { getArchiveThumbnail: (id: number) => `${API_BASE}/archives/${id}/thumbnail`, getArchiveDownload: (id: number) => `${API_BASE}/archives/${id}/download`, getArchiveGcode: (id: number) => `${API_BASE}/archives/${id}/gcode`, - getArchiveTimelapse: (id: number) => `${API_BASE}/archives/${id}/timelapse`, + getArchiveTimelapse: (id: number) => `${API_BASE}/archives/${id}/timelapse?v=${Date.now()}`, scanArchiveTimelapse: (id: number) => request<{ status: string; @@ -1432,6 +1432,56 @@ export const api = { } return response.json(); }, + // Timelapse Editor + getTimelapseInfo: (archiveId: number) => + request<{ + duration: number; + width: number; + height: number; + fps: number; + codec: string; + file_size: number; + has_audio: boolean; + }>(`/archives/${archiveId}/timelapse/info`), + getTimelapseThumbnails: (archiveId: number, count: number = 10) => + request<{ + thumbnails: string[]; + timestamps: number[]; + }>(`/archives/${archiveId}/timelapse/thumbnails?count=${count}`), + processTimelapse: async ( + archiveId: number, + params: { + trimStart?: number; + trimEnd?: number; + speed?: number; + saveMode: 'replace' | 'new'; + outputFilename?: string; + }, + audioFile?: File + ): Promise<{ status: string; output_path: string | null; message: string }> => { + const formData = new FormData(); + formData.append('trim_start', String(params.trimStart ?? 0)); + if (params.trimEnd !== undefined) { + formData.append('trim_end', String(params.trimEnd)); + } + formData.append('speed', String(params.speed ?? 1)); + formData.append('save_mode', params.saveMode); + if (params.outputFilename) { + formData.append('output_filename', params.outputFilename); + } + if (audioFile) { + formData.append('audio', audioFile); + } + const response = await fetch(`${API_BASE}/archives/${archiveId}/timelapse/process`, { + method: 'POST', + body: formData, + }); + if (!response.ok) { + const error = await response.json().catch(() => ({})); + throw new Error(error.detail || `HTTP ${response.status}`); + } + return response.json(); + }, // Photos getArchivePhotoUrl: (archiveId: number, filename: string) => `${API_BASE}/archives/${archiveId}/photos/${encodeURIComponent(filename)}`, diff --git a/frontend/src/components/TimelapseEditorModal.tsx b/frontend/src/components/TimelapseEditorModal.tsx new file mode 100644 index 000000000..21035d1c2 --- /dev/null +++ b/frontend/src/components/TimelapseEditorModal.tsx @@ -0,0 +1,546 @@ +import { useState, useRef, useEffect, useCallback } from 'react'; +import { useQuery, useMutation } from '@tanstack/react-query'; +import { + X, + Save, + Film, + Play, + Pause, + Scissors, + Gauge, + Music, + Upload, + Trash2, + Volume2, + VolumeX, + Loader2, +} from 'lucide-react'; +import { Button } from './Button'; +import { api } from '../api/client'; +import { useToast } from '../contexts/ToastContext'; + +interface TimelapseEditorModalProps { + archiveId: number; + timelapseSrc: string; + onClose: () => void; + onSave?: () => void; +} + +const SPEED_OPTIONS = [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]; + +function formatTime(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, '0')}`; +} + +export function TimelapseEditorModal({ + archiveId, + timelapseSrc, + onClose, + onSave, +}: TimelapseEditorModalProps) { + const { showToast } = useToast(); + const videoRef = useRef(null); + const audioRef = useRef(null); + + // Video state + const [isPlaying, setIsPlaying] = useState(false); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); + + // Editor state + const [trimStart, setTrimStart] = useState(0); + const [trimEnd, setTrimEnd] = useState(0); + const [speed, setSpeed] = useState(1); + const [audioFile, setAudioFile] = useState(null); + const [audioUrl, setAudioUrl] = useState(null); + const [audioVolume, setAudioVolume] = useState(0.8); + const [audioMuted, setAudioMuted] = useState(false); + + + // Fetch video info + const { data: videoInfo, isLoading: isLoadingInfo } = useQuery({ + queryKey: ['timelapse-info', archiveId], + queryFn: () => api.getTimelapseInfo(archiveId), + }); + + // Fetch thumbnails + const { data: thumbnailData } = useQuery({ + queryKey: ['timelapse-thumbnails', archiveId], + queryFn: () => api.getTimelapseThumbnails(archiveId, 15), + }); + + // Process mutation + const processMutation = useMutation({ + mutationFn: () => + api.processTimelapse( + archiveId, + { + trimStart, + trimEnd, + speed, + saveMode: 'replace', + }, + audioFile || undefined + ), + onSuccess: (data) => { + showToast(data.message, 'success'); + onSave?.(); + onClose(); + }, + onError: (error: Error) => { + showToast(error.message || 'Processing failed', 'error'); + }, + }); + + // Initialize trimEnd when duration is available + useEffect(() => { + if (videoInfo?.duration && trimEnd === 0) { + setTrimEnd(videoInfo.duration); + } + }, [videoInfo?.duration, trimEnd]); + + // Close on Escape + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [onClose]); + + // Video event handlers + useEffect(() => { + const video = videoRef.current; + if (!video) return; + + const handleTimeUpdate = () => { + const time = video.currentTime; + setCurrentTime(time); + + // Loop within trim region + if (time >= trimEnd) { + video.currentTime = trimStart; + } + }; + + const handleDurationChange = () => { + setDuration(video.duration); + if (trimEnd === 0) { + setTrimEnd(video.duration); + } + }; + + const handlePlay = () => setIsPlaying(true); + const handlePause = () => setIsPlaying(false); + + video.addEventListener('timeupdate', handleTimeUpdate); + video.addEventListener('durationchange', handleDurationChange); + video.addEventListener('play', handlePlay); + video.addEventListener('pause', handlePause); + + return () => { + video.removeEventListener('timeupdate', handleTimeUpdate); + video.removeEventListener('durationchange', handleDurationChange); + video.removeEventListener('play', handlePlay); + video.removeEventListener('pause', handlePause); + }; + }, [trimStart, trimEnd]); + + // Sync audio with video + useEffect(() => { + const audio = audioRef.current; + const video = videoRef.current; + if (!audio || !video || !audioUrl) return; + + audio.currentTime = video.currentTime; + audio.playbackRate = video.playbackRate; + + if (isPlaying && !audioMuted) { + audio.play().catch(() => {}); + } else { + audio.pause(); + } + }, [isPlaying, audioUrl, audioMuted]); + + // Update audio volume + useEffect(() => { + if (audioRef.current) { + audioRef.current.volume = audioMuted ? 0 : audioVolume; + } + }, [audioVolume, audioMuted]); + + // Update playback rate + useEffect(() => { + if (videoRef.current) { + videoRef.current.playbackRate = speed; + } + if (audioRef.current) { + audioRef.current.playbackRate = speed; + } + }, [speed]); + + const togglePlay = useCallback(() => { + const video = videoRef.current; + if (!video) return; + + if (isPlaying) { + video.pause(); + } else { + // Start from trim start if before it + if (video.currentTime < trimStart) { + video.currentTime = trimStart; + } + video.play(); + } + }, [isPlaying, trimStart]); + + const handleSeek = (time: number) => { + const video = videoRef.current; + if (!video) return; + video.currentTime = Math.max(trimStart, Math.min(trimEnd, time)); + }; + + const handleAudioUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + // Cleanup previous URL + if (audioUrl) { + URL.revokeObjectURL(audioUrl); + } + + setAudioFile(file); + setAudioUrl(URL.createObjectURL(file)); + }; + + const removeAudio = () => { + if (audioUrl) { + URL.revokeObjectURL(audioUrl); + } + setAudioFile(null); + setAudioUrl(null); + }; + + // Cleanup on unmount + useEffect(() => { + return () => { + if (audioUrl) { + URL.revokeObjectURL(audioUrl); + } + }; + }, [audioUrl]); + + const trimmedDuration = trimEnd - trimStart; + const outputDuration = trimmedDuration / speed; + + if (isLoadingInfo) { + return ( +
+
+ + Loading video info... +
+
+ ); + } + + return ( +
+
+ {/* Header */} +
+

+ + Edit Timelapse +

+
+ + +
+
+ + {/* Content */} +
+ {/* Video Preview */} +
+
+ + {/* Timeline with Thumbnails */} +
+
+ + Trim + + {formatTime(trimStart)} - {formatTime(trimEnd)} ({formatTime(trimmedDuration)}) + +
+ + {/* Thumbnail strip */} +
+ {/* Thumbnails background */} +
+ {thumbnailData?.thumbnails.map((thumb, i) => ( +
+ ))} +
+ + {/* Trim overlay - grayed out areas */} +
+
+ + {/* Selected region border */} +
+ + {/* Current time indicator */} +
+ + {/* Trim handles */} + { + const val = parseFloat(e.target.value); + if (val < trimEnd - 1) { + setTrimStart(val); + if (videoRef.current && videoRef.current.currentTime < val) { + videoRef.current.currentTime = val; + } + } + }} + className="absolute inset-0 w-full opacity-0 cursor-ew-resize" + style={{ clipPath: 'inset(0 50% 0 0)' }} + /> + { + const val = parseFloat(e.target.value); + if (val > trimStart + 1) { + setTrimEnd(val); + } + }} + className="absolute inset-0 w-full opacity-0 cursor-ew-resize" + style={{ clipPath: 'inset(0 0 0 50%)' }} + /> +
+ + {/* Playback scrubber */} + handleSeek(parseFloat(e.target.value))} + className="w-full h-1 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer + [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 + [&::-webkit-slider-thumb]:bg-bambu-green [&::-webkit-slider-thumb]:rounded-full + [&::-webkit-slider-thumb]:cursor-pointer" + /> + + {/* Play controls */} +
+ +
+
+ + {/* Speed Control */} +
+
+ + Speed + {speed}x (output: {formatTime(outputDuration)}) +
+
+ {SPEED_OPTIONS.map((s) => ( + + ))} +
+
+ + {/* Audio Upload */} +
+
+ + Music Overlay +
+ + {audioFile ? ( +
+ +
+

{audioFile.name}

+

+ {(audioFile.size / 1024 / 1024).toFixed(1)} MB +

+
+ + {/* Volume control */} + + setAudioVolume(parseFloat(e.target.value))} + className="w-20 h-1 bg-bambu-dark-tertiary rounded-lg appearance-none cursor-pointer + [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:h-3 + [&::-webkit-slider-thumb]:bg-bambu-green [&::-webkit-slider-thumb]:rounded-full" + /> + + +
+ ) : ( + + )} +
+ + {/* Summary */} +
+

+ Original: {formatTime(duration)} @ {videoInfo?.width}x{videoInfo?.height} +

+

+ Output: {formatTime(outputDuration)} @ {speed}x speed + {audioFile && ` + music overlay`} +

+
+
+ + {/* Processing overlay */} + {processMutation.isPending && ( +
+ +

Processing timelapse...

+

This may take a few moments

+
+ )} +
+
+ ); +} diff --git a/frontend/src/components/TimelapseViewer.tsx b/frontend/src/components/TimelapseViewer.tsx index 07c30c1e4..fd58c1f76 100644 --- a/frontend/src/components/TimelapseViewer.tsx +++ b/frontend/src/components/TimelapseViewer.tsx @@ -1,22 +1,33 @@ import { useState, useRef, useEffect } from 'react'; -import { X, Download, Film, Play, Pause, SkipBack, SkipForward } from 'lucide-react'; +import { X, Download, Film, Play, Pause, SkipBack, SkipForward, Pencil } from 'lucide-react'; import { Button } from './Button'; +import { TimelapseEditorModal } from './TimelapseEditorModal'; interface TimelapseViewerProps { src: string; title: string; downloadFilename: string; + archiveId?: number; onClose: () => void; + onEdit?: () => void; } const SPEED_OPTIONS = [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]; -export function TimelapseViewer({ src, title, downloadFilename, onClose }: TimelapseViewerProps) { +export function TimelapseViewer({ + src, + title, + downloadFilename, + archiveId, + onClose, + onEdit, +}: TimelapseViewerProps) { const videoRef = useRef(null); const [isPlaying, setIsPlaying] = useState(true); - const [playbackRate, setPlaybackRate] = useState(2); // Default to 2x for timelapse + const [playbackRate, setPlaybackRate] = useState(1); // Default to 1x const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); + const [showEditor, setShowEditor] = useState(false); useEffect(() => { const video = videoRef.current; @@ -109,6 +120,12 @@ export function TimelapseViewer({ src, title, downloadFilename, onClose }: Timel {title}
+ {archiveId && ( + + )}
+ + {/* Timelapse Editor Modal */} + {showEditor && archiveId && ( + setShowEditor(false)} + onSave={onEdit} + /> + )}
); } diff --git a/frontend/src/pages/ArchivesPage.tsx b/frontend/src/pages/ArchivesPage.tsx index 95bd9f40a..a76295c95 100644 --- a/frontend/src/pages/ArchivesPage.tsx +++ b/frontend/src/pages/ArchivesPage.tsx @@ -785,7 +785,12 @@ function ArchiveCard({ src={api.getArchiveTimelapse(archive.id)} title={`${archive.print_name || archive.filename} - Timelapse`} downloadFilename={`${archive.print_name || archive.filename}_timelapse.mp4`} + archiveId={archive.id} onClose={() => setShowTimelapse(false)} + onEdit={() => { + queryClient.invalidateQueries({ queryKey: ['archives'] }); + setShowTimelapse(false); // Close viewer to reload fresh video + }} /> )} diff --git a/static/assets/index-BonMKEhM.js b/static/assets/index-BB4adzNL.js similarity index 67% rename from static/assets/index-BonMKEhM.js rename to static/assets/index-BB4adzNL.js index 849126643..47b7b9261 100644 --- a/static/assets/index-BonMKEhM.js +++ b/static/assets/index-BB4adzNL.js @@ -1,20 +1,20 @@ -function Pre(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();var pv=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function mo(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var SA={exports:{}},ox={};var lF;function Rre(){if(lF)return ox;lF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var s=null;if(a!==void 0&&(s=""+a),i.key!==void 0&&(s=""+i.key),"key"in i){a={};for(var o in i)o!=="key"&&(a[o]=i[o])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:s,ref:i!==void 0?i:null,props:a}}return ox.Fragment=e,ox.jsx=n,ox.jsxs=n,ox}var cF;function jre(){return cF||(cF=1,SA.exports=Rre()),SA.exports}var l=jre(),MA={exports:{}},en={};var uF;function Dre(){if(uF)return en;uF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),o=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),g=Symbol.iterator;function p(z){return z===null||typeof z!="object"?null:(z=g&&z[g]||z["@@iterator"],typeof z=="function"?z:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,y={};function b(z,$,J){this.props=z,this.context=$,this.refs=y,this.updater=J||x}b.prototype.isReactComponent={},b.prototype.setState=function(z,$){if(typeof z!="object"&&typeof z!="function"&&z!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,z,$,"setState")},b.prototype.forceUpdate=function(z){this.updater.enqueueForceUpdate(this,z,"forceUpdate")};function E(){}E.prototype=b.prototype;function w(z,$,J){this.props=z,this.context=$,this.refs=y,this.updater=J||x}var A=w.prototype=new E;A.constructor=w,v(A,b.prototype),A.isPureReactComponent=!0;var T=Array.isArray;function N(){}var C={H:null,A:null,T:null,S:null},U=Object.prototype.hasOwnProperty;function k(z,$,J){var ie=J.ref;return{$$typeof:t,type:z,key:$,ref:ie!==void 0?ie:null,props:J}}function j(z,$){return k(z.type,$,z.props)}function I(z){return typeof z=="object"&&z!==null&&z.$$typeof===t}function L(z){var $={"=":"=0",":":"=2"};return"$"+z.replace(/[=:]/g,function(J){return $[J]})}var q=/\/+/g;function R(z,$){return typeof z=="object"&&z!==null&&z.key!=null?L(""+z.key):$.toString(36)}function W(z){switch(z.status){case"fulfilled":return z.value;case"rejected":throw z.reason;default:switch(typeof z.status=="string"?z.then(N,N):(z.status="pending",z.then(function($){z.status==="pending"&&(z.status="fulfilled",z.value=$)},function($){z.status==="pending"&&(z.status="rejected",z.reason=$)})),z.status){case"fulfilled":return z.value;case"rejected":throw z.reason}}throw z}function K(z,$,J,ie,pe){var fe=typeof z;(fe==="undefined"||fe==="boolean")&&(z=null);var ye=!1;if(z===null)ye=!0;else switch(fe){case"bigint":case"string":case"number":ye=!0;break;case"object":switch(z.$$typeof){case t:case e:ye=!0;break;case d:return ye=z._init,K(ye(z._payload),$,J,ie,pe)}}if(ye)return pe=pe(z),ye=ie===""?"."+R(z,0):ie,T(pe)?(J="",ye!=null&&(J=ye.replace(q,"$&/")+"/"),K(pe,$,J,"",function(we){return we})):pe!=null&&(I(pe)&&(pe=j(pe,J+(pe.key==null||z&&z.key===pe.key?"":(""+pe.key).replace(q,"$&/")+"/")+ye)),$.push(pe)),1;ye=0;var Re=ie===""?".":ie+":";if(T(z))for(var re=0;re>>1,Q=K[Y];if(0>>1;Yi(J,ee))iei(pe,J)?(K[Y]=pe,K[ie]=ee,Y=ie):(K[Y]=J,K[$]=ee,Y=$);else if(iei(pe,ee))K[Y]=pe,K[ie]=ee,Y=ie;else break e}}return G}function i(K,G){var ee=K.sortIndex-G.sortIndex;return ee!==0?ee:K.id-G.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,o=s.now();t.unstable_now=function(){return s.now()-o}}var c=[],u=[],d=1,f=null,g=3,p=!1,x=!1,v=!1,y=!1,b=typeof setTimeout=="function"?setTimeout:null,E=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function A(K){for(var G=n(u);G!==null;){if(G.callback===null)r(u);else if(G.startTime<=K)r(u),G.sortIndex=G.expirationTime,e(c,G);else break;G=n(u)}}function T(K){if(v=!1,A(K),!x)if(n(c)!==null)x=!0,N||(N=!0,L());else{var G=n(u);G!==null&&W(T,G.startTime-K)}}var N=!1,C=-1,U=5,k=-1;function j(){return y?!0:!(t.unstable_now()-kK&&j());){var Y=f.callback;if(typeof Y=="function"){f.callback=null,g=f.priorityLevel;var Q=Y(f.expirationTime<=K);if(K=t.unstable_now(),typeof Q=="function"){f.callback=Q,A(K),G=!0;break t}f===n(c)&&r(c),A(K)}else r(c);f=n(c)}if(f!==null)G=!0;else{var z=n(u);z!==null&&W(T,z.startTime-K),G=!1}}break e}finally{f=null,g=ee,p=!1}G=void 0}}finally{G?L():N=!1}}}var L;if(typeof w=="function")L=function(){w(I)};else if(typeof MessageChannel<"u"){var q=new MessageChannel,R=q.port2;q.port1.onmessage=I,L=function(){R.postMessage(null)}}else L=function(){b(I,0)};function W(K,G){C=b(function(){K(t.unstable_now())},G)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(K){K.callback=null},t.unstable_forceFrameRate=function(K){0>K||125Y?(K.sortIndex=ee,e(u,K),n(c)===null&&K===n(u)&&(v?(E(C),C=-1):v=!0,W(T,ee-Y))):(K.sortIndex=Q,e(c,K),x||p||(x=!0,N||(N=!0,L()))),K},t.unstable_shouldYield=j,t.unstable_wrapCallback=function(K){var G=g;return function(){var ee=g;g=G;try{return K.apply(this,arguments)}finally{g=ee}}}})(TA)),TA}var fF;function Ire(){return fF||(fF=1,AA.exports=Lre()),AA.exports}var CA={exports:{}},ia={};var mF;function Fre(){if(mF)return ia;mF=1;var t=Kp();function e(c){var u="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),CA.exports=Fre(),CA.exports}var gF;function Ure(){if(gF)return lx;gF=1;var t=Ire(),e=Kp(),n=C9();function r(h){var m="https://react.dev/errors/"+h;if(1Q||(h.current=Y[Q],Y[Q]=null,Q--)}function J(h,m){Q++,Y[Q]=h.current,h.current=m}var ie=z(null),pe=z(null),fe=z(null),ye=z(null);function Re(h,m){switch(J(fe,m),J(pe,h),J(ie,null),m.nodeType){case 9:case 11:h=(h=m.documentElement)&&(h=h.namespaceURI)?P5(h):0;break;default:if(h=m.tagName,m=m.namespaceURI)m=P5(m),h=R5(m,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}$(ie),J(ie,h)}function re(){$(ie),$(pe),$(fe)}function we(h){h.memoizedState!==null&&J(ye,h);var m=ie.current,_=R5(m,h.type);m!==_&&(J(pe,h),J(ie,_))}function Oe(h){pe.current===h&&($(ie),$(pe)),ye.current===h&&($(ye),rx._currentValue=ee)}var ce,Ie;function B(h){if(ce===void 0)try{throw Error()}catch(_){var m=_.stack.trim().match(/\n( *(at )?)/);ce=m&&m[1]||"",Ie=-1<_.stack.indexOf(` +function Dre(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();var xv=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function mo(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var ET={exports:{}},cx={};var uF;function Ore(){if(uF)return cx;uF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var s=null;if(a!==void 0&&(s=""+a),i.key!==void 0&&(s=""+i.key),"key"in i){a={};for(var o in i)o!=="key"&&(a[o]=i[o])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:s,ref:i!==void 0?i:null,props:a}}return cx.Fragment=e,cx.jsx=n,cx.jsxs=n,cx}var dF;function Lre(){return dF||(dF=1,ET.exports=Ore()),ET.exports}var l=Lre(),TT={exports:{}},en={};var hF;function Ire(){if(hF)return en;hF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),o=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),f=Symbol.for("react.activity"),g=Symbol.iterator;function p(z){return z===null||typeof z!="object"?null:(z=g&&z[g]||z["@@iterator"],typeof z=="function"?z:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,y={};function b(z,H,J){this.props=z,this.context=H,this.refs=y,this.updater=J||x}b.prototype.isReactComponent={},b.prototype.setState=function(z,H){if(typeof z!="object"&&typeof z!="function"&&z!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,z,H,"setState")},b.prototype.forceUpdate=function(z){this.updater.enqueueForceUpdate(this,z,"forceUpdate")};function E(){}E.prototype=b.prototype;function w(z,H,J){this.props=z,this.context=H,this.refs=y,this.updater=J||x}var A=w.prototype=new E;A.constructor=w,v(A,b.prototype),A.isPureReactComponent=!0;var T=Array.isArray;function k(){}var C={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function N(z,H,J){var ie=J.ref;return{$$typeof:t,type:z,key:H,ref:ie!==void 0?ie:null,props:J}}function j(z,H){return N(z.type,H,z.props)}function I(z){return typeof z=="object"&&z!==null&&z.$$typeof===t}function L(z){var H={"=":"=0",":":"=2"};return"$"+z.replace(/[=:]/g,function(J){return H[J]})}var q=/\/+/g;function R(z,H){return typeof z=="object"&&z!==null&&z.key!=null?L(""+z.key):H.toString(36)}function W(z){switch(z.status){case"fulfilled":return z.value;case"rejected":throw z.reason;default:switch(typeof z.status=="string"?z.then(k,k):(z.status="pending",z.then(function(H){z.status==="pending"&&(z.status="fulfilled",z.value=H)},function(H){z.status==="pending"&&(z.status="rejected",z.reason=H)})),z.status){case"fulfilled":return z.value;case"rejected":throw z.reason}}throw z}function K(z,H,J,ie,me){var fe=typeof z;(fe==="undefined"||fe==="boolean")&&(z=null);var ye=!1;if(z===null)ye=!0;else switch(fe){case"bigint":case"string":case"number":ye=!0;break;case"object":switch(z.$$typeof){case t:case e:ye=!0;break;case d:return ye=z._init,K(ye(z._payload),H,J,ie,me)}}if(ye)return me=me(z),ye=ie===""?"."+R(z,0):ie,T(me)?(J="",ye!=null&&(J=ye.replace(q,"$&/")+"/"),K(me,H,J,"",function(we){return we})):me!=null&&(I(me)&&(me=j(me,J+(me.key==null||z&&z.key===me.key?"":(""+me.key).replace(q,"$&/")+"/")+ye)),H.push(me)),1;ye=0;var Re=ie===""?".":ie+":";if(T(z))for(var re=0;re>>1,Q=K[Y];if(0>>1;Yi(J,ee))iei(me,J)?(K[Y]=me,K[ie]=ee,Y=ie):(K[Y]=J,K[H]=ee,Y=H);else if(iei(me,ee))K[Y]=me,K[ie]=ee,Y=ie;else break e}}return G}function i(K,G){var ee=K.sortIndex-G.sortIndex;return ee!==0?ee:K.id-G.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,o=s.now();t.unstable_now=function(){return s.now()-o}}var c=[],u=[],d=1,f=null,g=3,p=!1,x=!1,v=!1,y=!1,b=typeof setTimeout=="function"?setTimeout:null,E=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function A(K){for(var G=n(u);G!==null;){if(G.callback===null)r(u);else if(G.startTime<=K)r(u),G.sortIndex=G.expirationTime,e(c,G);else break;G=n(u)}}function T(K){if(v=!1,A(K),!x)if(n(c)!==null)x=!0,k||(k=!0,L());else{var G=n(u);G!==null&&W(T,G.startTime-K)}}var k=!1,C=-1,F=5,N=-1;function j(){return y?!0:!(t.unstable_now()-NK&&j());){var Y=f.callback;if(typeof Y=="function"){f.callback=null,g=f.priorityLevel;var Q=Y(f.expirationTime<=K);if(K=t.unstable_now(),typeof Q=="function"){f.callback=Q,A(K),G=!0;break t}f===n(c)&&r(c),A(K)}else r(c);f=n(c)}if(f!==null)G=!0;else{var z=n(u);z!==null&&W(T,z.startTime-K),G=!1}}break e}finally{f=null,g=ee,p=!1}G=void 0}}finally{G?L():k=!1}}}var L;if(typeof w=="function")L=function(){w(I)};else if(typeof MessageChannel<"u"){var q=new MessageChannel,R=q.port2;q.port1.onmessage=I,L=function(){R.postMessage(null)}}else L=function(){b(I,0)};function W(K,G){C=b(function(){K(t.unstable_now())},G)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(K){K.callback=null},t.unstable_forceFrameRate=function(K){0>K||125Y?(K.sortIndex=ee,e(u,K),n(c)===null&&K===n(u)&&(v?(E(C),C=-1):v=!0,W(T,ee-Y))):(K.sortIndex=Q,e(c,K),x||p||(x=!0,k||(k=!0,L()))),K},t.unstable_shouldYield=j,t.unstable_wrapCallback=function(K){var G=g;return function(){var ee=g;g=G;try{return K.apply(this,arguments)}finally{g=ee}}}})(NT)),NT}var pF;function zre(){return pF||(pF=1,CT.exports=Ure()),CT.exports}var kT={exports:{}},ia={};var gF;function Bre(){if(gF)return ia;gF=1;var t=Yp();function e(c){var u="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),kT.exports=Bre(),kT.exports}var bF;function Hre(){if(bF)return ux;bF=1;var t=zre(),e=Yp(),n=k9();function r(h){var m="https://react.dev/errors/"+h;if(1Q||(h.current=Y[Q],Y[Q]=null,Q--)}function J(h,m){Q++,Y[Q]=h.current,h.current=m}var ie=z(null),me=z(null),fe=z(null),ye=z(null);function Re(h,m){switch(J(fe,m),J(me,h),J(ie,null),m.nodeType){case 9:case 11:h=(h=m.documentElement)&&(h=h.namespaceURI)?j5(h):0;break;default:if(h=m.tagName,m=m.namespaceURI)m=j5(m),h=D5(m,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}H(ie),J(ie,h)}function re(){H(ie),H(me),H(fe)}function we(h){h.memoizedState!==null&&J(ye,h);var m=ie.current,_=D5(m,h.type);m!==_&&(J(me,h),J(ie,_))}function Oe(h){me.current===h&&(H(ie),H(me)),ye.current===h&&(H(ye),ax._currentValue=ee)}var ce,Ie;function B(h){if(ce===void 0)try{throw Error()}catch(_){var m=_.stack.trim().match(/\n( *(at )?)/);ce=m&&m[1]||"",Ie=-1<_.stack.indexOf(` at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return` -`+ce+h+Ie}var H=!1;function ne(h,m){if(!h||H)return"";H=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var S={DetermineComponentFrameRoot:function(){try{if(m){var ht=function(){throw Error()};if(Object.defineProperty(ht.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(ht,[])}catch(st){var Je=st}Reflect.construct(h,[],ht)}else{try{ht.call()}catch(st){Je=st}h.call(ht.prototype)}}else{try{throw Error()}catch(st){Je=st}(ht=h())&&typeof ht.catch=="function"&&ht.catch(function(){})}}catch(st){if(st&&Je&&typeof st.stack=="string")return[st.stack,Je.stack]}return[null,null]}};S.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var P=Object.getOwnPropertyDescriptor(S.DetermineComponentFrameRoot,"name");P&&P.configurable&&Object.defineProperty(S.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var D=S.DetermineComponentFrameRoot(),X=D[0],le=D[1];if(X&&le){var Te=X.split(` +`+ce+h+Ie}var $=!1;function ne(h,m){if(!h||$)return"";$=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var S={DetermineComponentFrameRoot:function(){try{if(m){var ht=function(){throw Error()};if(Object.defineProperty(ht.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(ht,[])}catch(st){var Je=st}Reflect.construct(h,[],ht)}else{try{ht.call()}catch(st){Je=st}h.call(ht.prototype)}}else{try{throw Error()}catch(st){Je=st}(ht=h())&&typeof ht.catch=="function"&&ht.catch(function(){})}}catch(st){if(st&&Je&&typeof st.stack=="string")return[st.stack,Je.stack]}return[null,null]}};S.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var P=Object.getOwnPropertyDescriptor(S.DetermineComponentFrameRoot,"name");P&&P.configurable&&Object.defineProperty(S.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var D=S.DetermineComponentFrameRoot(),X=D[0],le=D[1];if(X&&le){var Ae=X.split(` `),Ze=le.split(` -`);for(P=S=0;SP||Te[S]!==Ze[P]){var ct=` -`+Te[S].replace(" at new "," at ");return h.displayName&&ct.includes("")&&(ct=ct.replace("",h.displayName)),ct}while(1<=S&&0<=P);break}}}finally{H=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?B(_):""}function V(h,m){switch(h.tag){case 26:case 27:case 5:return B(h.type);case 16:return B("Lazy");case 13:return h.child!==m&&m!==null?B("Suspense Fallback"):B("Suspense");case 19:return B("SuspenseList");case 0:case 15:return ne(h.type,!1);case 11:return ne(h.type.render,!1);case 1:return ne(h.type,!0);case 31:return B("Activity");default:return""}}function Z(h){try{var m="",_=null;do m+=V(h,_),_=h,h=h.return;while(h);return m}catch(S){return` +`);for(P=S=0;SP||Ae[S]!==Ze[P]){var ct=` +`+Ae[S].replace(" at new "," at ");return h.displayName&&ct.includes("")&&(ct=ct.replace("",h.displayName)),ct}while(1<=S&&0<=P);break}}}finally{$=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?B(_):""}function V(h,m){switch(h.tag){case 26:case 27:case 5:return B(h.type);case 16:return B("Lazy");case 13:return h.child!==m&&m!==null?B("Suspense Fallback"):B("Suspense");case 19:return B("SuspenseList");case 0:case 15:return ne(h.type,!1);case 11:return ne(h.type.render,!1);case 1:return ne(h.type,!0);case 31:return B("Activity");default:return""}}function Z(h){try{var m="",_=null;do m+=V(h,_),_=h,h=h.return;while(h);return m}catch(S){return` Error generating stack: `+S.message+` -`+S.stack}}var se=Object.prototype.hasOwnProperty,xe=t.unstable_scheduleCallback,Ee=t.unstable_cancelCallback,Me=t.unstable_shouldYield,F=t.unstable_requestPaint,O=t.unstable_now,te=t.unstable_getCurrentPriorityLevel,ve=t.unstable_ImmediatePriority,Se=t.unstable_UserBlockingPriority,be=t.unstable_NormalPriority,rt=t.unstable_LowPriority,He=t.unstable_IdlePriority,de=t.log,We=t.unstable_setDisableYieldValue,Ue=null,oe=null;function tt(h){if(typeof de=="function"&&We(h),oe&&typeof oe.setStrictMode=="function")try{oe.setStrictMode(Ue,h)}catch{}}var $e=Math.clz32?Math.clz32:he,Ye=Math.log,ut=Math.LN2;function he(h){return h>>>=0,h===0?32:31-(Ye(h)/ut|0)|0}var Qe=256,ge=262144,ze=4194304;function ke(h){var m=h&42;if(m!==0)return m;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function _e(h,m,_){var S=h.pendingLanes;if(S===0)return 0;var P=0,D=h.suspendedLanes,X=h.pingedLanes;h=h.warmLanes;var le=S&134217727;return le!==0?(S=le&~D,S!==0?P=ke(S):(X&=le,X!==0?P=ke(X):_||(_=le&~h,_!==0&&(P=ke(_))))):(le=S&~D,le!==0?P=ke(le):X!==0?P=ke(X):_||(_=S&~h,_!==0&&(P=ke(_)))),P===0?0:m!==0&&m!==P&&(m&D)===0&&(D=P&-P,_=m&-m,D>=_||D===32&&(_&4194048)!==0)?m:P}function ue(h,m){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&m)===0}function ot(h,m){switch(h){case 1:case 2:case 4:case 8:case 64:return m+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return m+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function xt(){var h=ze;return ze<<=1,(ze&62914560)===0&&(ze=4194304),h}function Et(h){for(var m=[],_=0;31>_;_++)m.push(h);return m}function Qt(h,m){h.pendingLanes|=m,m!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function Ot(h,m,_,S,P,D){var X=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var le=h.entanglements,Te=h.expirationTimes,Ze=h.hiddenUpdates;for(_=X&~_;0<_;){var ct=31-$e(_),ht=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Ir=/[\n"\\]/g;function jn(h){return h.replace(Ir,function(m){return"\\"+m.charCodeAt(0).toString(16)+" "})}function xo(h,m,_,S,P,D,X,le){h.name="",X!=null&&typeof X!="function"&&typeof X!="symbol"&&typeof X!="boolean"?h.type=X:h.removeAttribute("type"),m!=null?X==="number"?(m===0&&h.value===""||h.value!=m)&&(h.value=""+hn(m)):h.value!==""+hn(m)&&(h.value=""+hn(m)):X!=="submit"&&X!=="reset"||h.removeAttribute("value"),m!=null?Ai(h,X,hn(m)):_!=null?Ai(h,X,hn(_)):S!=null&&h.removeAttribute("value"),P==null&&D!=null&&(h.defaultChecked=!!D),P!=null&&(h.checked=P&&typeof P!="function"&&typeof P!="symbol"),le!=null&&typeof le!="function"&&typeof le!="symbol"&&typeof le!="boolean"?h.name=""+hn(le):h.removeAttribute("name")}function Ln(h,m,_,S,P,D,X,le){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(h.type=D),m!=null||_!=null){if(!(D!=="submit"&&D!=="reset"||m!=null)){Jt(h);return}_=_!=null?""+hn(_):"",m=m!=null?""+hn(m):_,le||m===h.value||(h.value=m),h.defaultValue=m}S=S??P,S=typeof S!="function"&&typeof S!="symbol"&&!!S,h.checked=le?h.checked:!!S,h.defaultChecked=!!S,X!=null&&typeof X!="function"&&typeof X!="symbol"&&typeof X!="boolean"&&(h.name=X),Jt(h)}function Ai(h,m,_){m==="number"&&Cr(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function li(h,m,_,S){if(h=h.options,m){m={};for(var P=0;P<_.length;P++)m["$"+_[P]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),SE=!1;if(Pl)try{var _g={};Object.defineProperty(_g,"passive",{get:function(){SE=!0}}),window.addEventListener("test",_g,_g),window.removeEventListener("test",_g,_g)}catch{SE=!1}var Zc=null,ME=null,oy=null;function RL(){if(oy)return oy;var h,m=ME,_=m.length,S,P="value"in Zc?Zc.value:Zc.textContent,D=P.length;for(h=0;h<_&&m[h]===P[h];h++);var X=_-h;for(S=1;S<=X&&m[_-S]===P[D-S];S++);return oy=P.slice(h,1=Mg),FL=" ",UL=!1;function zL(h,m){switch(h){case"keyup":return Jte.indexOf(m.keyCode)!==-1;case"keydown":return m.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function BL(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var Sf=!1;function tne(h,m){switch(h){case"compositionend":return BL(m);case"keypress":return m.which!==32?null:(UL=!0,FL);case"textInput":return h=m.data,h===FL&&UL?null:h;default:return null}}function nne(h,m){if(Sf)return h==="compositionend"||!NE&&zL(h,m)?(h=RL(),oy=ME=Zc=null,Sf=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(m.ctrlKey||m.altKey||m.metaKey)||m.ctrlKey&&m.altKey){if(m.char&&1=m)return{node:_,offset:m-h};h=S}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=XL(_)}}function ZL(h,m){return h&&m?h===m?!0:h&&h.nodeType===3?!1:m&&m.nodeType===3?ZL(h,m.parentNode):"contains"in h?h.contains(m):h.compareDocumentPosition?!!(h.compareDocumentPosition(m)&16):!1:!1}function QL(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var m=Cr(h.document);m instanceof h.HTMLIFrameElement;){try{var _=typeof m.contentWindow.location.href=="string"}catch{_=!1}if(_)h=m.contentWindow;else break;m=Cr(h.document)}return m}function RE(h){var m=h&&h.nodeName&&h.nodeName.toLowerCase();return m&&(m==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||m==="textarea"||h.contentEditable==="true")}var une=Pl&&"documentMode"in document&&11>=document.documentMode,Mf=null,jE=null,Cg=null,DE=!1;function JL(h,m,_){var S=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;DE||Mf==null||Mf!==Cr(S)||(S=Mf,"selectionStart"in S&&RE(S)?S={start:S.selectionStart,end:S.selectionEnd}:(S=(S.ownerDocument&&S.ownerDocument.defaultView||window).getSelection(),S={anchorNode:S.anchorNode,anchorOffset:S.anchorOffset,focusNode:S.focusNode,focusOffset:S.focusOffset}),Cg&&Tg(Cg,S)||(Cg=S,S=ev(jE,"onSelect"),0>=X,P-=X,Wo=1<<32-$e(m)+P|_<rn?(vn=It,It=null):vn=It.sibling;var Cn=Je(Be,It,Xe[rn],dt);if(Cn===null){It===null&&(It=vn);break}h&&It&&Cn.alternate===null&&m(Be,It),je=D(Cn,je,rn),Tn===null?zt=Cn:Tn.sibling=Cn,Tn=Cn,It=vn}if(rn===Xe.length)return _(Be,It),wn&&jl(Be,rn),zt;if(It===null){for(;rnrn?(vn=It,It=null):vn=It.sibling;var yu=Je(Be,It,Cn.value,dt);if(yu===null){It===null&&(It=vn);break}h&&It&&yu.alternate===null&&m(Be,It),je=D(yu,je,rn),Tn===null?zt=yu:Tn.sibling=yu,Tn=yu,It=vn}if(Cn.done)return _(Be,It),wn&&jl(Be,rn),zt;if(It===null){for(;!Cn.done;rn++,Cn=Xe.next())Cn=ht(Be,Cn.value,dt),Cn!==null&&(je=D(Cn,je,rn),Tn===null?zt=Cn:Tn.sibling=Cn,Tn=Cn);return wn&&jl(Be,rn),zt}for(It=S(It);!Cn.done;rn++,Cn=Xe.next())Cn=st(It,Be,rn,Cn.value,dt),Cn!==null&&(h&&Cn.alternate!==null&&It.delete(Cn.key===null?rn:Cn.key),je=D(Cn,je,rn),Tn===null?zt=Cn:Tn.sibling=Cn,Tn=Cn);return h&&It.forEach(function(kre){return m(Be,kre)}),wn&&jl(Be,rn),zt}function Wn(Be,je,Xe,dt){if(typeof Xe=="object"&&Xe!==null&&Xe.type===v&&Xe.key===null&&(Xe=Xe.props.children),typeof Xe=="object"&&Xe!==null){switch(Xe.$$typeof){case p:e:{for(var zt=Xe.key;je!==null;){if(je.key===zt){if(zt=Xe.type,zt===v){if(je.tag===7){_(Be,je.sibling),dt=P(je,Xe.props.children),dt.return=Be,Be=dt;break e}}else if(je.elementType===zt||typeof zt=="object"&&zt!==null&&zt.$$typeof===U&&$d(zt)===je.type){_(Be,je.sibling),dt=P(je,Xe.props),Dg(dt,Xe),dt.return=Be,Be=dt;break e}_(Be,je);break}else m(Be,je);je=je.sibling}Xe.type===v?(dt=Fd(Xe.props.children,Be.mode,dt,Xe.key),dt.return=Be,Be=dt):(dt=xy(Xe.type,Xe.key,Xe.props,null,Be.mode,dt),Dg(dt,Xe),dt.return=Be,Be=dt)}return X(Be);case x:e:{for(zt=Xe.key;je!==null;){if(je.key===zt)if(je.tag===4&&je.stateNode.containerInfo===Xe.containerInfo&&je.stateNode.implementation===Xe.implementation){_(Be,je.sibling),dt=P(je,Xe.children||[]),dt.return=Be,Be=dt;break e}else{_(Be,je);break}else m(Be,je);je=je.sibling}dt=BE(Xe,Be.mode,dt),dt.return=Be,Be=dt}return X(Be);case U:return Xe=$d(Xe),Wn(Be,je,Xe,dt)}if(W(Xe))return Dt(Be,je,Xe,dt);if(L(Xe)){if(zt=L(Xe),typeof zt!="function")throw Error(r(150));return Xe=zt.call(Xe),Gt(Be,je,Xe,dt)}if(typeof Xe.then=="function")return Wn(Be,je,My(Xe),dt);if(Xe.$$typeof===w)return Wn(Be,je,vy(Be,Xe),dt);Ey(Be,Xe)}return typeof Xe=="string"&&Xe!==""||typeof Xe=="number"||typeof Xe=="bigint"?(Xe=""+Xe,je!==null&&je.tag===6?(_(Be,je.sibling),dt=P(je,Xe),dt.return=Be,Be=dt):(_(Be,je),dt=zE(Xe,Be.mode,dt),dt.return=Be,Be=dt),X(Be)):_(Be,je)}return function(Be,je,Xe,dt){try{jg=0;var zt=Wn(Be,je,Xe,dt);return Of=null,zt}catch(It){if(It===Df||It===wy)throw It;var Tn=cs(29,It,null,Be.mode);return Tn.lanes=dt,Tn.return=Be,Tn}finally{}}}var Gd=w4(!0),S4=w4(!1),nu=!1;function JE(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function e2(h,m){h=h.updateQueue,m.updateQueue===h&&(m.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function ru(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function iu(h,m,_){var S=h.updateQueue;if(S===null)return null;if(S=S.shared,(Pn&2)!==0){var P=S.pending;return P===null?m.next=m:(m.next=P.next,P.next=m),S.pending=m,m=gy(h),s4(h,null,_),m}return py(h,S,m,_),gy(h)}function Og(h,m,_){if(m=m.updateQueue,m!==null&&(m=m.shared,(_&4194048)!==0)){var S=m.lanes;S&=h.pendingLanes,_|=S,m.lanes=_,On(h,_)}}function t2(h,m){var _=h.updateQueue,S=h.alternate;if(S!==null&&(S=S.updateQueue,_===S)){var P=null,D=null;if(_=_.firstBaseUpdate,_!==null){do{var X={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};D===null?P=D=X:D=D.next=X,_=_.next}while(_!==null);D===null?P=D=m:D=D.next=m}else P=D=m;_={baseState:S.baseState,firstBaseUpdate:P,lastBaseUpdate:D,shared:S.shared,callbacks:S.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=m:h.next=m,_.lastBaseUpdate=m}var n2=!1;function Lg(){if(n2){var h=jf;if(h!==null)throw h}}function Ig(h,m,_,S){n2=!1;var P=h.updateQueue;nu=!1;var D=P.firstBaseUpdate,X=P.lastBaseUpdate,le=P.shared.pending;if(le!==null){P.shared.pending=null;var Te=le,Ze=Te.next;Te.next=null,X===null?D=Ze:X.next=Ze,X=Te;var ct=h.alternate;ct!==null&&(ct=ct.updateQueue,le=ct.lastBaseUpdate,le!==X&&(le===null?ct.firstBaseUpdate=Ze:le.next=Ze,ct.lastBaseUpdate=Te))}if(D!==null){var ht=P.baseState;X=0,ct=Ze=Te=null,le=D;do{var Je=le.lane&-536870913,st=Je!==le.lane;if(st?(yn&Je)===Je:(S&Je)===Je){Je!==0&&Je===Rf&&(n2=!0),ct!==null&&(ct=ct.next={lane:0,tag:le.tag,payload:le.payload,callback:null,next:null});e:{var Dt=h,Gt=le;Je=m;var Wn=_;switch(Gt.tag){case 1:if(Dt=Gt.payload,typeof Dt=="function"){ht=Dt.call(Wn,ht,Je);break e}ht=Dt;break e;case 3:Dt.flags=Dt.flags&-65537|128;case 0:if(Dt=Gt.payload,Je=typeof Dt=="function"?Dt.call(Wn,ht,Je):Dt,Je==null)break e;ht=f({},ht,Je);break e;case 2:nu=!0}}Je=le.callback,Je!==null&&(h.flags|=64,st&&(h.flags|=8192),st=P.callbacks,st===null?P.callbacks=[Je]:st.push(Je))}else st={lane:Je,tag:le.tag,payload:le.payload,callback:le.callback,next:null},ct===null?(Ze=ct=st,Te=ht):ct=ct.next=st,X|=Je;if(le=le.next,le===null){if(le=P.shared.pending,le===null)break;st=le,le=st.next,st.next=null,P.lastBaseUpdate=st,P.shared.pending=null}}while(!0);ct===null&&(Te=ht),P.baseState=Te,P.firstBaseUpdate=Ze,P.lastBaseUpdate=ct,D===null&&(P.shared.lanes=0),cu|=X,h.lanes=X,h.memoizedState=ht}}function M4(h,m){if(typeof h!="function")throw Error(r(191,h));h.call(m)}function E4(h,m){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)M4(_[h],m)}var Lf=z(null),Ay=z(0);function A4(h,m){h=Vl,J(Ay,h),J(Lf,m),Vl=h|m.baseLanes}function r2(){J(Ay,Vl),J(Lf,Lf.current)}function i2(){Vl=Ay.current,$(Lf),$(Ay)}var us=z(null),Ls=null;function au(h){var m=h.alternate;J(Fr,Fr.current&1),J(us,h),Ls===null&&(m===null||Lf.current!==null||m.memoizedState!==null)&&(Ls=h)}function a2(h){J(Fr,Fr.current),J(us,h),Ls===null&&(Ls=h)}function T4(h){h.tag===22?(J(Fr,Fr.current),J(us,h),Ls===null&&(Ls=h)):su()}function su(){J(Fr,Fr.current),J(us,us.current)}function ds(h){$(us),Ls===h&&(Ls=null),$(Fr)}var Fr=z(0);function Ty(h){for(var m=h;m!==null;){if(m.tag===13){var _=m.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||dA(_)||hA(_)))return m}else if(m.tag===19&&(m.memoizedProps.revealOrder==="forwards"||m.memoizedProps.revealOrder==="backwards"||m.memoizedProps.revealOrder==="unstable_legacy-backwards"||m.memoizedProps.revealOrder==="together")){if((m.flags&128)!==0)return m}else if(m.child!==null){m.child.return=m,m=m.child;continue}if(m===h)break;for(;m.sibling===null;){if(m.return===null||m.return===h)return null;m=m.return}m.sibling.return=m.return,m=m.sibling}return null}var Ll=0,nn=null,Vn=null,Kr=null,Cy=!1,If=!1,Wd=!1,Ny=0,Fg=0,Ff=null,vne=0;function Nr(){throw Error(r(321))}function s2(h,m){if(m===null)return!1;for(var _=0;_D?D:8;var X=K.T,le={};K.T=le,_2(h,!1,m,_);try{var Te=P(),Ze=K.S;if(Ze!==null&&Ze(le,Te),Te!==null&&typeof Te=="object"&&typeof Te.then=="function"){var ct=yne(Te,S);zg(h,m,ct,ms(h))}else zg(h,m,S,ms(h))}catch(ht){zg(h,m,{then:function(){},status:"rejected",reason:ht},ms())}finally{G.p=D,X!==null&&le.types!==null&&(X.types=le.types),K.T=X}}function Ene(){}function y2(h,m,_,S){if(h.tag!==5)throw Error(r(476));var P=rI(h).queue;nI(h,P,m,ee,_===null?Ene:function(){return iI(h),_(S)})}function rI(h){var m=h.memoizedState;if(m!==null)return m;m={memoizedState:ee,baseState:ee,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Il,lastRenderedState:ee},next:null};var _={};return m.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Il,lastRenderedState:_},next:null},h.memoizedState=m,h=h.alternate,h!==null&&(h.memoizedState=m),m}function iI(h){var m=rI(h);m.next===null&&(m=h.alternate.memoizedState),zg(h,m.next.queue,{},ms())}function v2(){return $i(rx)}function aI(){return Ur().memoizedState}function sI(){return Ur().memoizedState}function Ane(h){for(var m=h.return;m!==null;){switch(m.tag){case 24:case 3:var _=ms();h=ru(_);var S=iu(m,h,_);S!==null&&(Ba(S,m,_),Og(S,m,_)),m={cache:XE()},h.payload=m;return}m=m.return}}function Tne(h,m,_){var S=ms();_={lane:S,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},Oy(h)?lI(m,_):(_=FE(h,m,_,S),_!==null&&(Ba(_,h,S),cI(_,m,S)))}function oI(h,m,_){var S=ms();zg(h,m,_,S)}function zg(h,m,_,S){var P={lane:S,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(Oy(h))lI(m,P);else{var D=h.alternate;if(h.lanes===0&&(D===null||D.lanes===0)&&(D=m.lastRenderedReducer,D!==null))try{var X=m.lastRenderedState,le=D(X,_);if(P.hasEagerState=!0,P.eagerState=le,ls(le,X))return py(h,m,P,0),er===null&&my(),!1}catch{}finally{}if(_=FE(h,m,P,S),_!==null)return Ba(_,h,S),cI(_,m,S),!0}return!1}function _2(h,m,_,S){if(S={lane:2,revertLane:J2(),gesture:null,action:S,hasEagerState:!1,eagerState:null,next:null},Oy(h)){if(m)throw Error(r(479))}else m=FE(h,_,S,2),m!==null&&Ba(m,h,2)}function Oy(h){var m=h.alternate;return h===nn||m!==null&&m===nn}function lI(h,m){If=Cy=!0;var _=h.pending;_===null?m.next=m:(m.next=_.next,_.next=m),h.pending=m}function cI(h,m,_){if((_&4194048)!==0){var S=m.lanes;S&=h.pendingLanes,_|=S,m.lanes=_,On(h,_)}}var Bg={readContext:$i,use:Py,useCallback:Nr,useContext:Nr,useEffect:Nr,useImperativeHandle:Nr,useLayoutEffect:Nr,useInsertionEffect:Nr,useMemo:Nr,useReducer:Nr,useRef:Nr,useState:Nr,useDebugValue:Nr,useDeferredValue:Nr,useTransition:Nr,useSyncExternalStore:Nr,useId:Nr,useHostTransitionStatus:Nr,useFormState:Nr,useActionState:Nr,useOptimistic:Nr,useMemoCache:Nr,useCacheRefresh:Nr};Bg.useEffectEvent=Nr;var uI={readContext:$i,use:Py,useCallback:function(h,m){return ya().memoizedState=[h,m===void 0?null:m],h},useContext:$i,useEffect:q4,useImperativeHandle:function(h,m,_){_=_!=null?_.concat([h]):null,jy(4194308,4,Z4.bind(null,m,h),_)},useLayoutEffect:function(h,m){return jy(4194308,4,h,m)},useInsertionEffect:function(h,m){jy(4,2,h,m)},useMemo:function(h,m){var _=ya();m=m===void 0?null:m;var S=h();if(Wd){tt(!0);try{h()}finally{tt(!1)}}return _.memoizedState=[S,m],S},useReducer:function(h,m,_){var S=ya();if(_!==void 0){var P=_(m);if(Wd){tt(!0);try{_(m)}finally{tt(!1)}}}else P=m;return S.memoizedState=S.baseState=P,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:P},S.queue=h,h=h.dispatch=Tne.bind(null,nn,h),[S.memoizedState,h]},useRef:function(h){var m=ya();return h={current:h},m.memoizedState=h},useState:function(h){h=m2(h);var m=h.queue,_=oI.bind(null,nn,m);return m.dispatch=_,[h.memoizedState,_]},useDebugValue:x2,useDeferredValue:function(h,m){var _=ya();return b2(_,h,m)},useTransition:function(){var h=m2(!1);return h=nI.bind(null,nn,h.queue,!0,!1),ya().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,m,_){var S=nn,P=ya();if(wn){if(_===void 0)throw Error(r(407));_=_()}else{if(_=m(),er===null)throw Error(r(349));(yn&127)!==0||P4(S,m,_)}P.memoizedState=_;var D={value:_,getSnapshot:m};return P.queue=D,q4(j4.bind(null,S,D,h),[h]),S.flags|=2048,Uf(9,{destroy:void 0},R4.bind(null,S,D,_,m),null),_},useId:function(){var h=ya(),m=er.identifierPrefix;if(wn){var _=qo,S=Wo;_=(S&~(1<<32-$e(S)-1)).toString(32)+_,m="_"+m+"R_"+_,_=Ny++,0<_&&(m+="H"+_.toString(32)),m+="_"}else _=vne++,m="_"+m+"r_"+_.toString(32)+"_";return h.memoizedState=m},useHostTransitionStatus:v2,useFormState:H4,useActionState:H4,useOptimistic:function(h){var m=ya();m.memoizedState=m.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return m.queue=_,m=_2.bind(null,nn,!0,_),_.dispatch=m,[h,m]},useMemoCache:d2,useCacheRefresh:function(){return ya().memoizedState=Ane.bind(null,nn)},useEffectEvent:function(h){var m=ya(),_={impl:h};return m.memoizedState=_,function(){if((Pn&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},w2={readContext:$i,use:Py,useCallback:J4,useContext:$i,useEffect:g2,useImperativeHandle:Q4,useInsertionEffect:X4,useLayoutEffect:Y4,useMemo:eI,useReducer:Ry,useRef:W4,useState:function(){return Ry(Il)},useDebugValue:x2,useDeferredValue:function(h,m){var _=Ur();return tI(_,Vn.memoizedState,h,m)},useTransition:function(){var h=Ry(Il)[0],m=Ur().memoizedState;return[typeof h=="boolean"?h:Ug(h),m]},useSyncExternalStore:k4,useId:aI,useHostTransitionStatus:v2,useFormState:$4,useActionState:$4,useOptimistic:function(h,m){var _=Ur();return L4(_,Vn,h,m)},useMemoCache:d2,useCacheRefresh:sI};w2.useEffectEvent=K4;var dI={readContext:$i,use:Py,useCallback:J4,useContext:$i,useEffect:g2,useImperativeHandle:Q4,useInsertionEffect:X4,useLayoutEffect:Y4,useMemo:eI,useReducer:f2,useRef:W4,useState:function(){return f2(Il)},useDebugValue:x2,useDeferredValue:function(h,m){var _=Ur();return Vn===null?b2(_,h,m):tI(_,Vn.memoizedState,h,m)},useTransition:function(){var h=f2(Il)[0],m=Ur().memoizedState;return[typeof h=="boolean"?h:Ug(h),m]},useSyncExternalStore:k4,useId:aI,useHostTransitionStatus:v2,useFormState:G4,useActionState:G4,useOptimistic:function(h,m){var _=Ur();return Vn!==null?L4(_,Vn,h,m):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:d2,useCacheRefresh:sI};dI.useEffectEvent=K4;function S2(h,m,_,S){m=h.memoizedState,_=_(S,m),_=_==null?m:f({},m,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var M2={enqueueSetState:function(h,m,_){h=h._reactInternals;var S=ms(),P=ru(S);P.payload=m,_!=null&&(P.callback=_),m=iu(h,P,S),m!==null&&(Ba(m,h,S),Og(m,h,S))},enqueueReplaceState:function(h,m,_){h=h._reactInternals;var S=ms(),P=ru(S);P.tag=1,P.payload=m,_!=null&&(P.callback=_),m=iu(h,P,S),m!==null&&(Ba(m,h,S),Og(m,h,S))},enqueueForceUpdate:function(h,m){h=h._reactInternals;var _=ms(),S=ru(_);S.tag=2,m!=null&&(S.callback=m),m=iu(h,S,_),m!==null&&(Ba(m,h,_),Og(m,h,_))}};function hI(h,m,_,S,P,D,X){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(S,D,X):m.prototype&&m.prototype.isPureReactComponent?!Tg(_,S)||!Tg(P,D):!0}function fI(h,m,_,S){h=m.state,typeof m.componentWillReceiveProps=="function"&&m.componentWillReceiveProps(_,S),typeof m.UNSAFE_componentWillReceiveProps=="function"&&m.UNSAFE_componentWillReceiveProps(_,S),m.state!==h&&M2.enqueueReplaceState(m,m.state,null)}function qd(h,m){var _=m;if("ref"in m){_={};for(var S in m)S!=="ref"&&(_[S]=m[S])}if(h=h.defaultProps){_===m&&(_=f({},_));for(var P in h)_[P]===void 0&&(_[P]=h[P])}return _}function mI(h){fy(h)}function pI(h){console.error(h)}function gI(h){fy(h)}function Ly(h,m){try{var _=h.onUncaughtError;_(m.value,{componentStack:m.stack})}catch(S){setTimeout(function(){throw S})}}function xI(h,m,_){try{var S=h.onCaughtError;S(_.value,{componentStack:_.stack,errorBoundary:m.tag===1?m.stateNode:null})}catch(P){setTimeout(function(){throw P})}}function E2(h,m,_){return _=ru(_),_.tag=3,_.payload={element:null},_.callback=function(){Ly(h,m)},_}function bI(h){return h=ru(h),h.tag=3,h}function yI(h,m,_,S){var P=_.type.getDerivedStateFromError;if(typeof P=="function"){var D=S.value;h.payload=function(){return P(D)},h.callback=function(){xI(m,_,S)}}var X=_.stateNode;X!==null&&typeof X.componentDidCatch=="function"&&(h.callback=function(){xI(m,_,S),typeof P!="function"&&(uu===null?uu=new Set([this]):uu.add(this));var le=S.stack;this.componentDidCatch(S.value,{componentStack:le!==null?le:""})})}function Cne(h,m,_,S,P){if(_.flags|=32768,S!==null&&typeof S=="object"&&typeof S.then=="function"){if(m=_.alternate,m!==null&&Pf(m,_,P,!0),_=us.current,_!==null){switch(_.tag){case 31:case 13:return Ls===null?Ky():_.alternate===null&&kr===0&&(kr=3),_.flags&=-257,_.flags|=65536,_.lanes=P,S===Sy?_.flags|=16384:(m=_.updateQueue,m===null?_.updateQueue=new Set([S]):m.add(S),Y2(h,S,P)),!1;case 22:return _.flags|=65536,S===Sy?_.flags|=16384:(m=_.updateQueue,m===null?(m={transitions:null,markerInstances:null,retryQueue:new Set([S])},_.updateQueue=m):(_=m.retryQueue,_===null?m.retryQueue=new Set([S]):_.add(S)),Y2(h,S,P)),!1}throw Error(r(435,_.tag))}return Y2(h,S,P),Ky(),!1}if(wn)return m=us.current,m!==null?((m.flags&65536)===0&&(m.flags|=256),m.flags|=65536,m.lanes=P,S!==VE&&(h=Error(r(422),{cause:S}),kg(Rs(h,_)))):(S!==VE&&(m=Error(r(423),{cause:S}),kg(Rs(m,_))),h=h.current.alternate,h.flags|=65536,P&=-P,h.lanes|=P,S=Rs(S,_),P=E2(h.stateNode,S,P),t2(h,P),kr!==4&&(kr=2)),!1;var D=Error(r(520),{cause:S});if(D=Rs(D,_),Xg===null?Xg=[D]:Xg.push(D),kr!==4&&(kr=2),m===null)return!0;S=Rs(S,_),_=m;do{switch(_.tag){case 3:return _.flags|=65536,h=P&-P,_.lanes|=h,h=E2(_.stateNode,S,h),t2(_,h),!1;case 1:if(m=_.type,D=_.stateNode,(_.flags&128)===0&&(typeof m.getDerivedStateFromError=="function"||D!==null&&typeof D.componentDidCatch=="function"&&(uu===null||!uu.has(D))))return _.flags|=65536,P&=-P,_.lanes|=P,P=bI(P),yI(P,h,_,S),t2(_,P),!1}_=_.return}while(_!==null);return!1}var A2=Error(r(461)),Xr=!1;function Vi(h,m,_,S){m.child=h===null?S4(m,null,_,S):Gd(m,h.child,_,S)}function vI(h,m,_,S,P){_=_.render;var D=m.ref;if("ref"in S){var X={};for(var le in S)le!=="ref"&&(X[le]=S[le])}else X=S;return Bd(m),S=o2(h,m,_,X,D,P),le=l2(),h!==null&&!Xr?(c2(h,m,P),Fl(h,m,P)):(wn&&le&&HE(m),m.flags|=1,Vi(h,m,S,P),m.child)}function _I(h,m,_,S,P){if(h===null){var D=_.type;return typeof D=="function"&&!UE(D)&&D.defaultProps===void 0&&_.compare===null?(m.tag=15,m.type=D,wI(h,m,D,S,P)):(h=xy(_.type,null,S,m,m.mode,P),h.ref=m.ref,h.return=m,m.child=h)}if(D=h.child,!D2(h,P)){var X=D.memoizedProps;if(_=_.compare,_=_!==null?_:Tg,_(X,S)&&h.ref===m.ref)return Fl(h,m,P)}return m.flags|=1,h=Rl(D,S),h.ref=m.ref,h.return=m,m.child=h}function wI(h,m,_,S,P){if(h!==null){var D=h.memoizedProps;if(Tg(D,S)&&h.ref===m.ref)if(Xr=!1,m.pendingProps=S=D,D2(h,P))(h.flags&131072)!==0&&(Xr=!0);else return m.lanes=h.lanes,Fl(h,m,P)}return T2(h,m,_,S,P)}function SI(h,m,_,S){var P=S.children,D=h!==null?h.memoizedState:null;if(h===null&&m.stateNode===null&&(m.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),S.mode==="hidden"){if((m.flags&128)!==0){if(D=D!==null?D.baseLanes|_:_,h!==null){for(S=m.child=h.child,P=0;S!==null;)P=P|S.lanes|S.childLanes,S=S.sibling;S=P&~D}else S=0,m.child=null;return MI(h,m,D,_,S)}if((_&536870912)!==0)m.memoizedState={baseLanes:0,cachePool:null},h!==null&&_y(m,D!==null?D.cachePool:null),D!==null?A4(m,D):r2(),T4(m);else return S=m.lanes=536870912,MI(h,m,D!==null?D.baseLanes|_:_,_,S)}else D!==null?(_y(m,D.cachePool),A4(m,D),su(),m.memoizedState=null):(h!==null&&_y(m,null),r2(),su());return Vi(h,m,P,_),m.child}function Hg(h,m){return h!==null&&h.tag===22||m.stateNode!==null||(m.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),m.sibling}function MI(h,m,_,S,P){var D=ZE();return D=D===null?null:{parent:qr._currentValue,pool:D},m.memoizedState={baseLanes:_,cachePool:D},h!==null&&_y(m,null),r2(),T4(m),h!==null&&Pf(h,m,S,!0),m.childLanes=P,null}function Iy(h,m){return m=Uy({mode:m.mode,children:m.children},h.mode),m.ref=h.ref,h.child=m,m.return=h,m}function EI(h,m,_){return Gd(m,h.child,null,_),h=Iy(m,m.pendingProps),h.flags|=2,ds(m),m.memoizedState=null,h}function Nne(h,m,_){var S=m.pendingProps,P=(m.flags&128)!==0;if(m.flags&=-129,h===null){if(wn){if(S.mode==="hidden")return h=Iy(m,S),m.lanes=536870912,Hg(null,h);if(a2(m),(h=lr)?(h=I5(h,Os),h=h!==null&&h.data==="&"?h:null,h!==null&&(m.memoizedState={dehydrated:h,treeContext:Qc!==null?{id:Wo,overflow:qo}:null,retryLane:536870912,hydrationErrors:null},_=l4(h),_.return=m,m.child=_,Hi=m,lr=null)):h=null,h===null)throw eu(m);return m.lanes=536870912,null}return Iy(m,S)}var D=h.memoizedState;if(D!==null){var X=D.dehydrated;if(a2(m),P)if(m.flags&256)m.flags&=-257,m=EI(h,m,_);else if(m.memoizedState!==null)m.child=h.child,m.flags|=128,m=null;else throw Error(r(558));else if(Xr||Pf(h,m,_,!1),P=(_&h.childLanes)!==0,Xr||P){if(S=er,S!==null&&(X=Mi(S,_),X!==0&&X!==D.retryLane))throw D.retryLane=X,Id(h,X),Ba(S,h,X),A2;Ky(),m=EI(h,m,_)}else h=D.treeContext,lr=Is(X.nextSibling),Hi=m,wn=!0,Jc=null,Os=!1,h!==null&&d4(m,h),m=Iy(m,S),m.flags|=4096;return m}return h=Rl(h.child,{mode:S.mode,children:S.children}),h.ref=m.ref,m.child=h,h.return=m,h}function Fy(h,m){var _=m.ref;if(_===null)h!==null&&h.ref!==null&&(m.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(h===null||h.ref!==_)&&(m.flags|=4194816)}}function T2(h,m,_,S,P){return Bd(m),_=o2(h,m,_,S,void 0,P),S=l2(),h!==null&&!Xr?(c2(h,m,P),Fl(h,m,P)):(wn&&S&&HE(m),m.flags|=1,Vi(h,m,_,P),m.child)}function AI(h,m,_,S,P,D){return Bd(m),m.updateQueue=null,_=N4(m,S,_,P),C4(h),S=l2(),h!==null&&!Xr?(c2(h,m,D),Fl(h,m,D)):(wn&&S&&HE(m),m.flags|=1,Vi(h,m,_,D),m.child)}function TI(h,m,_,S,P){if(Bd(m),m.stateNode===null){var D=Tf,X=_.contextType;typeof X=="object"&&X!==null&&(D=$i(X)),D=new _(S,D),m.memoizedState=D.state!==null&&D.state!==void 0?D.state:null,D.updater=M2,m.stateNode=D,D._reactInternals=m,D=m.stateNode,D.props=S,D.state=m.memoizedState,D.refs={},JE(m),X=_.contextType,D.context=typeof X=="object"&&X!==null?$i(X):Tf,D.state=m.memoizedState,X=_.getDerivedStateFromProps,typeof X=="function"&&(S2(m,_,X,S),D.state=m.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof D.getSnapshotBeforeUpdate=="function"||typeof D.UNSAFE_componentWillMount!="function"&&typeof D.componentWillMount!="function"||(X=D.state,typeof D.componentWillMount=="function"&&D.componentWillMount(),typeof D.UNSAFE_componentWillMount=="function"&&D.UNSAFE_componentWillMount(),X!==D.state&&M2.enqueueReplaceState(D,D.state,null),Ig(m,S,D,P),Lg(),D.state=m.memoizedState),typeof D.componentDidMount=="function"&&(m.flags|=4194308),S=!0}else if(h===null){D=m.stateNode;var le=m.memoizedProps,Te=qd(_,le);D.props=Te;var Ze=D.context,ct=_.contextType;X=Tf,typeof ct=="object"&&ct!==null&&(X=$i(ct));var ht=_.getDerivedStateFromProps;ct=typeof ht=="function"||typeof D.getSnapshotBeforeUpdate=="function",le=m.pendingProps!==le,ct||typeof D.UNSAFE_componentWillReceiveProps!="function"&&typeof D.componentWillReceiveProps!="function"||(le||Ze!==X)&&fI(m,D,S,X),nu=!1;var Je=m.memoizedState;D.state=Je,Ig(m,S,D,P),Lg(),Ze=m.memoizedState,le||Je!==Ze||nu?(typeof ht=="function"&&(S2(m,_,ht,S),Ze=m.memoizedState),(Te=nu||hI(m,_,Te,S,Je,Ze,X))?(ct||typeof D.UNSAFE_componentWillMount!="function"&&typeof D.componentWillMount!="function"||(typeof D.componentWillMount=="function"&&D.componentWillMount(),typeof D.UNSAFE_componentWillMount=="function"&&D.UNSAFE_componentWillMount()),typeof D.componentDidMount=="function"&&(m.flags|=4194308)):(typeof D.componentDidMount=="function"&&(m.flags|=4194308),m.memoizedProps=S,m.memoizedState=Ze),D.props=S,D.state=Ze,D.context=X,S=Te):(typeof D.componentDidMount=="function"&&(m.flags|=4194308),S=!1)}else{D=m.stateNode,e2(h,m),X=m.memoizedProps,ct=qd(_,X),D.props=ct,ht=m.pendingProps,Je=D.context,Ze=_.contextType,Te=Tf,typeof Ze=="object"&&Ze!==null&&(Te=$i(Ze)),le=_.getDerivedStateFromProps,(Ze=typeof le=="function"||typeof D.getSnapshotBeforeUpdate=="function")||typeof D.UNSAFE_componentWillReceiveProps!="function"&&typeof D.componentWillReceiveProps!="function"||(X!==ht||Je!==Te)&&fI(m,D,S,Te),nu=!1,Je=m.memoizedState,D.state=Je,Ig(m,S,D,P),Lg();var st=m.memoizedState;X!==ht||Je!==st||nu||h!==null&&h.dependencies!==null&&yy(h.dependencies)?(typeof le=="function"&&(S2(m,_,le,S),st=m.memoizedState),(ct=nu||hI(m,_,ct,S,Je,st,Te)||h!==null&&h.dependencies!==null&&yy(h.dependencies))?(Ze||typeof D.UNSAFE_componentWillUpdate!="function"&&typeof D.componentWillUpdate!="function"||(typeof D.componentWillUpdate=="function"&&D.componentWillUpdate(S,st,Te),typeof D.UNSAFE_componentWillUpdate=="function"&&D.UNSAFE_componentWillUpdate(S,st,Te)),typeof D.componentDidUpdate=="function"&&(m.flags|=4),typeof D.getSnapshotBeforeUpdate=="function"&&(m.flags|=1024)):(typeof D.componentDidUpdate!="function"||X===h.memoizedProps&&Je===h.memoizedState||(m.flags|=4),typeof D.getSnapshotBeforeUpdate!="function"||X===h.memoizedProps&&Je===h.memoizedState||(m.flags|=1024),m.memoizedProps=S,m.memoizedState=st),D.props=S,D.state=st,D.context=Te,S=ct):(typeof D.componentDidUpdate!="function"||X===h.memoizedProps&&Je===h.memoizedState||(m.flags|=4),typeof D.getSnapshotBeforeUpdate!="function"||X===h.memoizedProps&&Je===h.memoizedState||(m.flags|=1024),S=!1)}return D=S,Fy(h,m),S=(m.flags&128)!==0,D||S?(D=m.stateNode,_=S&&typeof _.getDerivedStateFromError!="function"?null:D.render(),m.flags|=1,h!==null&&S?(m.child=Gd(m,h.child,null,P),m.child=Gd(m,null,_,P)):Vi(h,m,_,P),m.memoizedState=D.state,h=m.child):h=Fl(h,m,P),h}function CI(h,m,_,S){return Ud(),m.flags|=256,Vi(h,m,_,S),m.child}var C2={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function N2(h){return{baseLanes:h,cachePool:x4()}}function k2(h,m,_){return h=h!==null?h.childLanes&~_:0,m&&(h|=fs),h}function NI(h,m,_){var S=m.pendingProps,P=!1,D=(m.flags&128)!==0,X;if((X=D)||(X=h!==null&&h.memoizedState===null?!1:(Fr.current&2)!==0),X&&(P=!0,m.flags&=-129),X=(m.flags&32)!==0,m.flags&=-33,h===null){if(wn){if(P?au(m):su(),(h=lr)?(h=I5(h,Os),h=h!==null&&h.data!=="&"?h:null,h!==null&&(m.memoizedState={dehydrated:h,treeContext:Qc!==null?{id:Wo,overflow:qo}:null,retryLane:536870912,hydrationErrors:null},_=l4(h),_.return=m,m.child=_,Hi=m,lr=null)):h=null,h===null)throw eu(m);return hA(h)?m.lanes=32:m.lanes=536870912,null}var le=S.children;return S=S.fallback,P?(su(),P=m.mode,le=Uy({mode:"hidden",children:le},P),S=Fd(S,P,_,null),le.return=m,S.return=m,le.sibling=S,m.child=le,S=m.child,S.memoizedState=N2(_),S.childLanes=k2(h,X,_),m.memoizedState=C2,Hg(null,S)):(au(m),P2(m,le))}var Te=h.memoizedState;if(Te!==null&&(le=Te.dehydrated,le!==null)){if(D)m.flags&256?(au(m),m.flags&=-257,m=R2(h,m,_)):m.memoizedState!==null?(su(),m.child=h.child,m.flags|=128,m=null):(su(),le=S.fallback,P=m.mode,S=Uy({mode:"visible",children:S.children},P),le=Fd(le,P,_,null),le.flags|=2,S.return=m,le.return=m,S.sibling=le,m.child=S,Gd(m,h.child,null,_),S=m.child,S.memoizedState=N2(_),S.childLanes=k2(h,X,_),m.memoizedState=C2,m=Hg(null,S));else if(au(m),hA(le)){if(X=le.nextSibling&&le.nextSibling.dataset,X)var Ze=X.dgst;X=Ze,S=Error(r(419)),S.stack="",S.digest=X,kg({value:S,source:null,stack:null}),m=R2(h,m,_)}else if(Xr||Pf(h,m,_,!1),X=(_&h.childLanes)!==0,Xr||X){if(X=er,X!==null&&(S=Mi(X,_),S!==0&&S!==Te.retryLane))throw Te.retryLane=S,Id(h,S),Ba(X,h,S),A2;dA(le)||Ky(),m=R2(h,m,_)}else dA(le)?(m.flags|=192,m.child=h.child,m=null):(h=Te.treeContext,lr=Is(le.nextSibling),Hi=m,wn=!0,Jc=null,Os=!1,h!==null&&d4(m,h),m=P2(m,S.children),m.flags|=4096);return m}return P?(su(),le=S.fallback,P=m.mode,Te=h.child,Ze=Te.sibling,S=Rl(Te,{mode:"hidden",children:S.children}),S.subtreeFlags=Te.subtreeFlags&65011712,Ze!==null?le=Rl(Ze,le):(le=Fd(le,P,_,null),le.flags|=2),le.return=m,S.return=m,S.sibling=le,m.child=S,Hg(null,S),S=m.child,le=h.child.memoizedState,le===null?le=N2(_):(P=le.cachePool,P!==null?(Te=qr._currentValue,P=P.parent!==Te?{parent:Te,pool:Te}:P):P=x4(),le={baseLanes:le.baseLanes|_,cachePool:P}),S.memoizedState=le,S.childLanes=k2(h,X,_),m.memoizedState=C2,Hg(h.child,S)):(au(m),_=h.child,h=_.sibling,_=Rl(_,{mode:"visible",children:S.children}),_.return=m,_.sibling=null,h!==null&&(X=m.deletions,X===null?(m.deletions=[h],m.flags|=16):X.push(h)),m.child=_,m.memoizedState=null,_)}function P2(h,m){return m=Uy({mode:"visible",children:m},h.mode),m.return=h,h.child=m}function Uy(h,m){return h=cs(22,h,null,m),h.lanes=0,h}function R2(h,m,_){return Gd(m,h.child,null,_),h=P2(m,m.pendingProps.children),h.flags|=2,m.memoizedState=null,h}function kI(h,m,_){h.lanes|=m;var S=h.alternate;S!==null&&(S.lanes|=m),qE(h.return,m,_)}function j2(h,m,_,S,P,D){var X=h.memoizedState;X===null?h.memoizedState={isBackwards:m,rendering:null,renderingStartTime:0,last:S,tail:_,tailMode:P,treeForkCount:D}:(X.isBackwards=m,X.rendering=null,X.renderingStartTime=0,X.last=S,X.tail=_,X.tailMode=P,X.treeForkCount=D)}function PI(h,m,_){var S=m.pendingProps,P=S.revealOrder,D=S.tail;S=S.children;var X=Fr.current,le=(X&2)!==0;if(le?(X=X&1|2,m.flags|=128):X&=1,J(Fr,X),Vi(h,m,S,_),S=wn?Ng:0,!le&&h!==null&&(h.flags&128)!==0)e:for(h=m.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&kI(h,_,m);else if(h.tag===19)kI(h,_,m);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===m)break e;for(;h.sibling===null;){if(h.return===null||h.return===m)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch(P){case"forwards":for(_=m.child,P=null;_!==null;)h=_.alternate,h!==null&&Ty(h)===null&&(P=_),_=_.sibling;_=P,_===null?(P=m.child,m.child=null):(P=_.sibling,_.sibling=null),j2(m,!1,P,_,D,S);break;case"backwards":case"unstable_legacy-backwards":for(_=null,P=m.child,m.child=null;P!==null;){if(h=P.alternate,h!==null&&Ty(h)===null){m.child=P;break}h=P.sibling,P.sibling=_,_=P,P=h}j2(m,!0,_,null,D,S);break;case"together":j2(m,!1,null,null,void 0,S);break;default:m.memoizedState=null}return m.child}function Fl(h,m,_){if(h!==null&&(m.dependencies=h.dependencies),cu|=m.lanes,(_&m.childLanes)===0)if(h!==null){if(Pf(h,m,_,!1),(_&m.childLanes)===0)return null}else return null;if(h!==null&&m.child!==h.child)throw Error(r(153));if(m.child!==null){for(h=m.child,_=Rl(h,h.pendingProps),m.child=_,_.return=m;h.sibling!==null;)h=h.sibling,_=_.sibling=Rl(h,h.pendingProps),_.return=m;_.sibling=null}return m.child}function D2(h,m){return(h.lanes&m)!==0?!0:(h=h.dependencies,!!(h!==null&&yy(h)))}function kne(h,m,_){switch(m.tag){case 3:Re(m,m.stateNode.containerInfo),tu(m,qr,h.memoizedState.cache),Ud();break;case 27:case 5:we(m);break;case 4:Re(m,m.stateNode.containerInfo);break;case 10:tu(m,m.type,m.memoizedProps.value);break;case 31:if(m.memoizedState!==null)return m.flags|=128,a2(m),null;break;case 13:var S=m.memoizedState;if(S!==null)return S.dehydrated!==null?(au(m),m.flags|=128,null):(_&m.child.childLanes)!==0?NI(h,m,_):(au(m),h=Fl(h,m,_),h!==null?h.sibling:null);au(m);break;case 19:var P=(h.flags&128)!==0;if(S=(_&m.childLanes)!==0,S||(Pf(h,m,_,!1),S=(_&m.childLanes)!==0),P){if(S)return PI(h,m,_);m.flags|=128}if(P=m.memoizedState,P!==null&&(P.rendering=null,P.tail=null,P.lastEffect=null),J(Fr,Fr.current),S)break;return null;case 22:return m.lanes=0,SI(h,m,_,m.pendingProps);case 24:tu(m,qr,h.memoizedState.cache)}return Fl(h,m,_)}function RI(h,m,_){if(h!==null)if(h.memoizedProps!==m.pendingProps)Xr=!0;else{if(!D2(h,_)&&(m.flags&128)===0)return Xr=!1,kne(h,m,_);Xr=(h.flags&131072)!==0}else Xr=!1,wn&&(m.flags&1048576)!==0&&u4(m,Ng,m.index);switch(m.lanes=0,m.tag){case 16:e:{var S=m.pendingProps;if(h=$d(m.elementType),m.type=h,typeof h=="function")UE(h)?(S=qd(h,S),m.tag=1,m=TI(null,m,h,S,_)):(m.tag=0,m=T2(null,m,h,S,_));else{if(h!=null){var P=h.$$typeof;if(P===A){m.tag=11,m=vI(null,m,h,S,_);break e}else if(P===C){m.tag=14,m=_I(null,m,h,S,_);break e}}throw m=R(h)||h,Error(r(306,m,""))}}return m;case 0:return T2(h,m,m.type,m.pendingProps,_);case 1:return S=m.type,P=qd(S,m.pendingProps),TI(h,m,S,P,_);case 3:e:{if(Re(m,m.stateNode.containerInfo),h===null)throw Error(r(387));S=m.pendingProps;var D=m.memoizedState;P=D.element,e2(h,m),Ig(m,S,null,_);var X=m.memoizedState;if(S=X.cache,tu(m,qr,S),S!==D.cache&&KE(m,[qr],_,!0),Lg(),S=X.element,D.isDehydrated)if(D={element:S,isDehydrated:!1,cache:X.cache},m.updateQueue.baseState=D,m.memoizedState=D,m.flags&256){m=CI(h,m,S,_);break e}else if(S!==P){P=Rs(Error(r(424)),m),kg(P),m=CI(h,m,S,_);break e}else{switch(h=m.stateNode.containerInfo,h.nodeType){case 9:h=h.body;break;default:h=h.nodeName==="HTML"?h.ownerDocument.body:h}for(lr=Is(h.firstChild),Hi=m,wn=!0,Jc=null,Os=!0,_=S4(m,null,S,_),m.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling}else{if(Ud(),S===P){m=Fl(h,m,_);break e}Vi(h,m,S,_)}m=m.child}return m;case 26:return Fy(h,m),h===null?(_=$5(m.type,null,m.pendingProps,null))?m.memoizedState=_:wn||(_=m.type,h=m.pendingProps,S=tv(fe.current).createElement(_),S[yr]=m,S[Lr]=h,Gi(S,_,h),Le(S),m.stateNode=S):m.memoizedState=$5(m.type,h.memoizedProps,m.pendingProps,h.memoizedState),null;case 27:return we(m),h===null&&wn&&(S=m.stateNode=z5(m.type,m.pendingProps,fe.current),Hi=m,Os=!0,P=lr,mu(m.type)?(fA=P,lr=Is(S.firstChild)):lr=P),Vi(h,m,m.pendingProps.children,_),Fy(h,m),h===null&&(m.flags|=4194304),m.child;case 5:return h===null&&wn&&((P=S=lr)&&(S=sre(S,m.type,m.pendingProps,Os),S!==null?(m.stateNode=S,Hi=m,lr=Is(S.firstChild),Os=!1,P=!0):P=!1),P||eu(m)),we(m),P=m.type,D=m.pendingProps,X=h!==null?h.memoizedProps:null,S=D.children,lA(P,D)?S=null:X!==null&&lA(P,X)&&(m.flags|=32),m.memoizedState!==null&&(P=o2(h,m,_ne,null,null,_),rx._currentValue=P),Fy(h,m),Vi(h,m,S,_),m.child;case 6:return h===null&&wn&&((h=_=lr)&&(_=ore(_,m.pendingProps,Os),_!==null?(m.stateNode=_,Hi=m,lr=null,h=!0):h=!1),h||eu(m)),null;case 13:return NI(h,m,_);case 4:return Re(m,m.stateNode.containerInfo),S=m.pendingProps,h===null?m.child=Gd(m,null,S,_):Vi(h,m,S,_),m.child;case 11:return vI(h,m,m.type,m.pendingProps,_);case 7:return Vi(h,m,m.pendingProps,_),m.child;case 8:return Vi(h,m,m.pendingProps.children,_),m.child;case 12:return Vi(h,m,m.pendingProps.children,_),m.child;case 10:return S=m.pendingProps,tu(m,m.type,S.value),Vi(h,m,S.children,_),m.child;case 9:return P=m.type._context,S=m.pendingProps.children,Bd(m),P=$i(P),S=S(P),m.flags|=1,Vi(h,m,S,_),m.child;case 14:return _I(h,m,m.type,m.pendingProps,_);case 15:return wI(h,m,m.type,m.pendingProps,_);case 19:return PI(h,m,_);case 31:return Nne(h,m,_);case 22:return SI(h,m,_,m.pendingProps);case 24:return Bd(m),S=$i(qr),h===null?(P=ZE(),P===null&&(P=er,D=XE(),P.pooledCache=D,D.refCount++,D!==null&&(P.pooledCacheLanes|=_),P=D),m.memoizedState={parent:S,cache:P},JE(m),tu(m,qr,P)):((h.lanes&_)!==0&&(e2(h,m),Ig(m,null,null,_),Lg()),P=h.memoizedState,D=m.memoizedState,P.parent!==S?(P={parent:S,cache:S},m.memoizedState=P,m.lanes===0&&(m.memoizedState=m.updateQueue.baseState=P),tu(m,qr,S)):(S=D.cache,tu(m,qr,S),S!==P.cache&&KE(m,[qr],_,!0))),Vi(h,m,m.pendingProps.children,_),m.child;case 29:throw m.pendingProps}throw Error(r(156,m.tag))}function Ul(h){h.flags|=4}function O2(h,m,_,S,P){if((m=(h.mode&32)!==0)&&(m=!1),m){if(h.flags|=16777216,(P&335544128)===P)if(h.stateNode.complete)h.flags|=8192;else if(a5())h.flags|=8192;else throw Vd=Sy,QE}else h.flags&=-16777217}function jI(h,m){if(m.type!=="stylesheet"||(m.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!K5(m))if(a5())h.flags|=8192;else throw Vd=Sy,QE}function zy(h,m){m!==null&&(h.flags|=4),h.flags&16384&&(m=h.tag!==22?xt():536870912,h.lanes|=m,$f|=m)}function $g(h,m){if(!wn)switch(h.tailMode){case"hidden":m=h.tail;for(var _=null;m!==null;)m.alternate!==null&&(_=m),m=m.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var S=null;_!==null;)_.alternate!==null&&(S=_),_=_.sibling;S===null?m||h.tail===null?h.tail=null:h.tail.sibling=null:S.sibling=null}}function cr(h){var m=h.alternate!==null&&h.alternate.child===h.child,_=0,S=0;if(m)for(var P=h.child;P!==null;)_|=P.lanes|P.childLanes,S|=P.subtreeFlags&65011712,S|=P.flags&65011712,P.return=h,P=P.sibling;else for(P=h.child;P!==null;)_|=P.lanes|P.childLanes,S|=P.subtreeFlags,S|=P.flags,P.return=h,P=P.sibling;return h.subtreeFlags|=S,h.childLanes=_,m}function Pne(h,m,_){var S=m.pendingProps;switch($E(m),m.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return cr(m),null;case 1:return cr(m),null;case 3:return _=m.stateNode,S=null,h!==null&&(S=h.memoizedState.cache),m.memoizedState.cache!==S&&(m.flags|=2048),Ol(qr),re(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(kf(m)?Ul(m):h===null||h.memoizedState.isDehydrated&&(m.flags&256)===0||(m.flags|=1024,GE())),cr(m),null;case 26:var P=m.type,D=m.memoizedState;return h===null?(Ul(m),D!==null?(cr(m),jI(m,D)):(cr(m),O2(m,P,null,S,_))):D?D!==h.memoizedState?(Ul(m),cr(m),jI(m,D)):(cr(m),m.flags&=-16777217):(h=h.memoizedProps,h!==S&&Ul(m),cr(m),O2(m,P,h,S,_)),null;case 27:if(Oe(m),_=fe.current,P=m.type,h!==null&&m.stateNode!=null)h.memoizedProps!==S&&Ul(m);else{if(!S){if(m.stateNode===null)throw Error(r(166));return cr(m),null}h=ie.current,kf(m)?h4(m):(h=z5(P,S,_),m.stateNode=h,Ul(m))}return cr(m),null;case 5:if(Oe(m),P=m.type,h!==null&&m.stateNode!=null)h.memoizedProps!==S&&Ul(m);else{if(!S){if(m.stateNode===null)throw Error(r(166));return cr(m),null}if(D=ie.current,kf(m))h4(m);else{var X=tv(fe.current);switch(D){case 1:D=X.createElementNS("http://www.w3.org/2000/svg",P);break;case 2:D=X.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;default:switch(P){case"svg":D=X.createElementNS("http://www.w3.org/2000/svg",P);break;case"math":D=X.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;case"script":D=X.createElement("div"),D.innerHTML=" - + +