diff --git a/README.md b/README.md index 3f4899375..289b5af18 100644 --- a/README.md +++ b/README.md @@ -505,10 +505,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. --- -If you like Bambuddy and want to support it, you can buy Martin a coffee. - ---- - ## 📄 License MIT License — see [LICENSE](LICENSE) for details. @@ -523,6 +519,10 @@ MIT License — see [LICENSE](LICENSE) for details. --- +If you like Bambuddy and want to support it, you can buy Martin a coffee. + +--- +

Made with ❤️ for the 3D printing community

diff --git a/backend/app/api/routes/archives.py b/backend/app/api/routes/archives.py index 8aff35dcb..85241c422 100644 --- a/backend/app/api/routes/archives.py +++ b/backend/app/api/routes/archives.py @@ -78,6 +78,7 @@ def archive_to_response( "nozzle_diameter": archive.nozzle_diameter, "bed_temperature": archive.bed_temperature, "nozzle_temperature": archive.nozzle_temperature, + "sliced_for_model": archive.sliced_for_model, "status": archive.status, "started_at": archive.started_at, "completed_at": archive.completed_at, diff --git a/backend/app/api/routes/print_queue.py b/backend/app/api/routes/print_queue.py index a8ed36a0f..f74e21749 100644 --- a/backend/app/api/routes/print_queue.py +++ b/backend/app/api/routes/print_queue.py @@ -42,6 +42,7 @@ def _enrich_response(item: PrintQueueItem) -> PrintQueueItemResponse: item_dict = { "id": item.id, "printer_id": item.printer_id, + "target_model": item.target_model, "archive_id": item.archive_id, "library_file_id": item.library_file_id, "position": item.position, @@ -124,12 +125,24 @@ async def add_to_queue( if not data.archive_id and not data.library_file_id: raise HTTPException(400, "Either archive_id or library_file_id must be provided") + # Cannot specify both printer_id and target_model + if data.printer_id and data.target_model: + raise HTTPException(400, "Cannot specify both printer_id and target_model") + # Validate printer exists (if assigned) if data.printer_id is not None: result = await db.execute(select(Printer).where(Printer.id == data.printer_id)) if not result.scalar_one_or_none(): raise HTTPException(400, "Printer not found") + # Validate target_model has active printers + if data.target_model: + result = await db.execute( + select(Printer).where(Printer.model == data.target_model).where(Printer.is_active == True) # noqa: E712 + ) + if not result.scalars().first(): + raise HTTPException(400, f"No active printers for model: {data.target_model}") + # Validate archive exists (if provided) if data.archive_id: result = await db.execute(select(PrintArchive).where(PrintArchive.id == data.archive_id)) @@ -142,7 +155,7 @@ async def add_to_queue( if not result.scalar_one_or_none(): raise HTTPException(400, "Library file not found") - # Get next position for this printer (or for unassigned items) + # Get next position for this printer (or for unassigned/model-based items) if data.printer_id is not None: result = await db.execute( select(func.max(PrintQueueItem.position)) @@ -150,7 +163,7 @@ async def add_to_queue( .where(PrintQueueItem.status == "pending") ) else: - # For unassigned items, get max position across all unassigned + # For unassigned/model-based items, get max position across all unassigned result = await db.execute( select(func.max(PrintQueueItem.position)) .where(PrintQueueItem.printer_id.is_(None)) @@ -160,6 +173,7 @@ async def add_to_queue( item = PrintQueueItem( printer_id=data.printer_id, + target_model=data.target_model, archive_id=data.archive_id, library_file_id=data.library_file_id, scheduled_time=data.scheduled_time, @@ -185,7 +199,8 @@ async def add_to_queue( await db.refresh(item, ["archive", "printer", "library_file"]) source_name = f"archive {data.archive_id}" if data.archive_id else f"library file {data.library_file_id}" - logger.info(f"Added {source_name} to queue for printer {data.printer_id or 'unassigned'}") + target_desc = data.printer_id or (f"model {data.target_model}" if data.target_model else "unassigned") + logger.info(f"Added {source_name} to queue for {target_desc}") # MQTT relay - publish queue job added try: @@ -287,12 +302,26 @@ async def update_queue_item( update_data = data.model_dump(exclude_unset=True) + # Cannot specify both printer_id and target_model + new_printer_id = update_data.get("printer_id", item.printer_id) + new_target_model = update_data.get("target_model", item.target_model) + if new_printer_id and new_target_model: + raise HTTPException(400, "Cannot specify both printer_id and target_model") + # Validate new printer_id if being changed (and not None) if "printer_id" in update_data and update_data["printer_id"] is not None: result = await db.execute(select(Printer).where(Printer.id == update_data["printer_id"])) if not result.scalar_one_or_none(): raise HTTPException(400, "Printer not found") + # Validate target_model has active printers + if "target_model" in update_data and update_data["target_model"]: + result = await db.execute( + select(Printer).where(Printer.model == update_data["target_model"]).where(Printer.is_active == True) # noqa: E712 + ) + if not result.scalars().first(): + raise HTTPException(400, f"No active printers for model: {update_data['target_model']}") + # Serialize ams_mapping to JSON for TEXT column storage if "ams_mapping" in update_data: update_data["ams_mapping"] = json.dumps(update_data["ams_mapping"]) if update_data["ams_mapping"] else None diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 4b2506d76..ca4fb74b8 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -760,6 +760,18 @@ async def run_migrations(conn): except Exception: pass + # Migration: Add sliced_for_model column to print_archives for printer model from 3MF + try: + await conn.execute(text("ALTER TABLE print_archives ADD COLUMN sliced_for_model VARCHAR(50)")) + except Exception: + pass + + # Migration: Add target_model column to print_queue for model-based assignment + try: + await conn.execute(text("ALTER TABLE print_queue ADD COLUMN target_model VARCHAR(50)")) + except Exception: + pass + async def seed_notification_templates(): """Seed default notification templates if they don't exist.""" diff --git a/backend/app/models/archive.py b/backend/app/models/archive.py index 6e99ea491..01e230827 100644 --- a/backend/app/models/archive.py +++ b/backend/app/models/archive.py @@ -35,6 +35,9 @@ class PrintArchive(Base): bed_temperature: Mapped[int | None] = mapped_column(Integer) nozzle_temperature: Mapped[int | None] = mapped_column(Integer) + # Printer model this file was sliced for (extracted from 3MF metadata) + sliced_for_model: Mapped[str | None] = mapped_column(String(50), nullable=True) + # Print result status: Mapped[str] = mapped_column(String(20), default="completed") started_at: Mapped[datetime | None] = mapped_column(DateTime) diff --git a/backend/app/models/print_queue.py b/backend/app/models/print_queue.py index 64ccc4980..cd33ee121 100644 --- a/backend/app/models/print_queue.py +++ b/backend/app/models/print_queue.py @@ -15,6 +15,9 @@ class PrintQueueItem(Base): # Links printer_id: Mapped[int | None] = mapped_column(ForeignKey("printers.id", ondelete="CASCADE"), nullable=True) + # Target printer model for model-based assignment (mutually exclusive with printer_id) + # When set, scheduler assigns to any idle printer of matching model + target_model: Mapped[str | None] = mapped_column(String(50), nullable=True) # Either archive_id OR library_file_id must be set (archive created at print start from library file) archive_id: Mapped[int | None] = mapped_column(ForeignKey("print_archives.id", ondelete="CASCADE"), nullable=True) library_file_id: Mapped[int | None] = mapped_column( diff --git a/backend/app/schemas/archive.py b/backend/app/schemas/archive.py index f968fca72..42e005007 100644 --- a/backend/app/schemas/archive.py +++ b/backend/app/schemas/archive.py @@ -63,6 +63,8 @@ class ArchiveResponse(BaseModel): bed_temperature: int | None nozzle_temperature: int | None + sliced_for_model: str | None = None # Printer model this file was sliced for + status: str started_at: datetime | None completed_at: datetime | None diff --git a/backend/app/schemas/print_queue.py b/backend/app/schemas/print_queue.py index d5bdbc780..f64a0be4a 100644 --- a/backend/app/schemas/print_queue.py +++ b/backend/app/schemas/print_queue.py @@ -17,6 +17,7 @@ UTCDatetime = Annotated[datetime | None, PlainSerializer(serialize_utc_datetime) class PrintQueueItemCreate(BaseModel): printer_id: int | None = None # None = unassigned, user assigns later + target_model: str | None = None # Target printer model (mutually exclusive with printer_id) # Either archive_id OR library_file_id must be provided archive_id: int | None = None library_file_id: int | None = None @@ -40,6 +41,7 @@ class PrintQueueItemCreate(BaseModel): class PrintQueueItemUpdate(BaseModel): printer_id: int | None = None + target_model: str | None = None # Target printer model (mutually exclusive with printer_id) position: int | None = None scheduled_time: datetime | None = None require_previous_success: bool | None = None @@ -59,6 +61,7 @@ class PrintQueueItemUpdate(BaseModel): class PrintQueueItemResponse(BaseModel): id: int printer_id: int | None # None = unassigned + target_model: str | None = None # Target printer model for model-based assignment archive_id: int | None # None if library_file_id is set (archive created at print start) library_file_id: int | None # For queue items from library files position: int diff --git a/backend/app/services/archive.py b/backend/app/services/archive.py index ba28004ec..d20906f81 100644 --- a/backend/app/services/archive.py +++ b/backend/app/services/archive.py @@ -67,6 +67,19 @@ class ThreeMFParser: content = zf.read("Metadata/slice_info.config").decode() root = ET.fromstring(content) + # Extract printer_model_id from plate metadata + # Format: + for meta in root.findall(".//metadata"): + key = meta.get("key") + value = meta.get("value") + if key == "printer_model_id" and value: + from backend.app.utils.printer_models import normalize_printer_model_id + + normalized = normalize_printer_model_id(value) + if normalized: + self.metadata["sliced_for_model"] = normalized + break + # Find the plate element (single-plate exports only have one plate) plate = root.find(".//plate") @@ -156,7 +169,7 @@ class ThreeMFParser: pass def _parse_gcode_header(self, zf: zipfile.ZipFile): - """Parse G-code file header for total layer count.""" + """Parse G-code file header for total layer count and printer model.""" import re try: @@ -165,15 +178,25 @@ class ThreeMFParser: if not gcode_files: return - # Read first 2KB of G-code (header contains the layer count) + # Read first 4KB of G-code (header contains metadata) gcode_path = gcode_files[0] with zf.open(gcode_path) as f: - header = f.read(2048).decode("utf-8", errors="ignore") + header = f.read(4096).decode("utf-8", errors="ignore") # Look for "; total layer number: XX" pattern match = re.search(r";\s*total\s+layer\s+number[:\s]+(\d+)", header, re.IGNORECASE) if match: self.metadata["total_layers"] = int(match.group(1)) + + # Look for printer_model in gcode header (fallback if not found in slice_info) + # Format: "; printer_model = Bambu Lab X1 Carbon" or "; printer_model = X1C" + if "sliced_for_model" not in self.metadata: + match = re.search(r";\s*printer_model\s*=\s*(.+)", header, re.IGNORECASE) + if match: + from backend.app.utils.printer_models import normalize_printer_model + + raw_model = match.group(1).strip() + self.metadata["sliced_for_model"] = normalize_printer_model(raw_model) except Exception: pass @@ -256,6 +279,12 @@ class ThreeMFParser: elif isinstance(val, (int, float, str)): self.metadata["nozzle_temperature"] = int(float(val)) break + + # Printer model (extract and normalize) + if "printer_model" in data: + from backend.app.utils.printer_models import normalize_printer_model + + self.metadata["sliced_for_model"] = normalize_printer_model(data["printer_model"]) except Exception: pass @@ -877,6 +906,7 @@ class ArchiveService: nozzle_diameter=metadata.get("nozzle_diameter"), bed_temperature=metadata.get("bed_temperature"), nozzle_temperature=metadata.get("nozzle_temperature"), + sliced_for_model=metadata.get("sliced_for_model"), makerworld_url=metadata.get("makerworld_url"), designer=metadata.get("designer"), status=status, diff --git a/backend/app/services/print_scheduler.py b/backend/app/services/print_scheduler.py index 603499d79..1c9728bca 100644 --- a/backend/app/services/print_scheduler.py +++ b/backend/app/services/print_scheduler.py @@ -62,13 +62,10 @@ class PrintScheduler: if not items: return - # Group by printer - only process first item per printer - processed_printers = set() + # Track busy printers to avoid assigning multiple items to same printer + busy_printers: set[int] = set() for item in items: - if item.printer_id in processed_printers: - continue - # Check scheduled time first (scheduled_time is stored in UTC from ISO string) if item.scheduled_time and item.scheduled_time > datetime.utcnow(): continue @@ -77,46 +74,91 @@ class PrintScheduler: if item.manual_start: continue - # Check if printer is idle - printer_idle = self._is_printer_idle(item.printer_id) - printer_connected = printer_manager.is_connected(item.printer_id) + if item.printer_id: + # Specific printer assignment (existing behavior) + if item.printer_id in busy_printers: + continue - # If printer not connected, try to power on via smart plug - if not printer_connected: - plug = await self._get_smart_plug(db, item.printer_id) - if plug and plug.auto_on and plug.enabled: - logger.info(f"Printer {item.printer_id} offline, attempting to power on via smart plug") - powered_on = await self._power_on_and_wait(plug, item.printer_id, db) - if powered_on: - printer_connected = True - printer_idle = self._is_printer_idle(item.printer_id) + # Check if printer is idle + printer_idle = self._is_printer_idle(item.printer_id) + printer_connected = printer_manager.is_connected(item.printer_id) + + # If printer not connected, try to power on via smart plug + if not printer_connected: + plug = await self._get_smart_plug(db, item.printer_id) + if plug and plug.auto_on and plug.enabled: + logger.info(f"Printer {item.printer_id} offline, attempting to power on via smart plug") + powered_on = await self._power_on_and_wait(plug, item.printer_id, db) + if powered_on: + printer_connected = True + printer_idle = self._is_printer_idle(item.printer_id) + else: + logger.warning(f"Could not power on printer {item.printer_id} via smart plug") + busy_printers.add(item.printer_id) + continue else: - logger.warning(f"Could not power on printer {item.printer_id} via smart plug") - processed_printers.add(item.printer_id) + # No plug or auto_on disabled + busy_printers.add(item.printer_id) continue - else: - # No plug or auto_on disabled - processed_printers.add(item.printer_id) + + # Check if printer is idle (busy with another print) + if not printer_idle: + busy_printers.add(item.printer_id) continue - # Check if printer is idle (busy with another print) - if not printer_idle: - processed_printers.add(item.printer_id) - continue + # Check condition (previous print success) + if item.require_previous_success: + if not await self._check_previous_success(db, item): + item.status = "skipped" + item.error_message = "Previous print failed or was aborted" + item.completed_at = datetime.now() + await db.commit() + logger.info(f"Skipped queue item {item.id} - previous print failed") + continue - # Check condition (previous print success) - if item.require_previous_success: - if not await self._check_previous_success(db, item): - item.status = "skipped" - item.error_message = "Previous print failed or was aborted" - item.completed_at = datetime.now() - await db.commit() - logger.info(f"Skipped queue item {item.id} - previous print failed") - continue + # Start the print + await self._start_print(db, item) + busy_printers.add(item.printer_id) - # Start the print - await self._start_print(db, item) - processed_printers.add(item.printer_id) + elif item.target_model: + # Model-based assignment - find any idle printer of matching model + printer_id = await self._find_idle_printer_for_model(db, item.target_model, busy_printers) + if printer_id: + # Check condition (previous print success) before assigning + if item.require_previous_success: + if not await self._check_previous_success(db, item): + item.status = "skipped" + item.error_message = "Previous print failed or was aborted" + item.completed_at = datetime.now() + await db.commit() + logger.info(f"Skipped queue item {item.id} - previous print failed") + continue + + # Assign printer and start + item.printer_id = printer_id + logger.info(f"Model-based assignment: queue item {item.id} assigned to printer {printer_id}") + await self._start_print(db, item) + busy_printers.add(printer_id) + + async def _find_idle_printer_for_model(self, db: AsyncSession, model: str, exclude_ids: set[int]) -> int | None: + """Find an idle, connected printer matching the model. + + Args: + db: Database session + model: Printer model to match (e.g., "X1C", "P1S") + exclude_ids: Printer IDs to exclude (already busy) + + Returns: + Printer ID if found, None otherwise + """ + result = await db.execute( + select(Printer).where(Printer.model == model).where(Printer.is_active == True) # noqa: E712 + ) + for printer in result.scalars().all(): + if printer.id not in exclude_ids: + if self._is_printer_idle(printer.id) and printer_manager.is_connected(printer.id): + return printer.id + return None def _is_printer_idle(self, printer_id: int) -> bool: """Check if a printer is connected and idle.""" diff --git a/backend/app/utils/printer_models.py b/backend/app/utils/printer_models.py new file mode 100644 index 000000000..f412bf474 --- /dev/null +++ b/backend/app/utils/printer_models.py @@ -0,0 +1,86 @@ +"""Printer model normalization utilities. + +Converts 3MF printer model names (e.g., "Bambu Lab X1 Carbon") to +normalized short names (e.g., "X1C") that match database storage. +""" + +# Map from 3MF printer_model strings to normalized short names +PRINTER_MODEL_MAP = { + "Bambu Lab X1 Carbon": "X1C", + "Bambu Lab X1": "X1", + "Bambu Lab X1E": "X1E", + "Bambu Lab P1S": "P1S", + "Bambu Lab P1P": "P1P", + "Bambu Lab P2S": "P2S", + "Bambu Lab A1": "A1", + "Bambu Lab A1 Mini": "A1 Mini", + "Bambu Lab A1 mini": "A1 Mini", + "Bambu Lab H2D": "H2D", + "Bambu Lab H2D Pro": "H2D Pro", +} + +# Map from printer_model_id (internal codes in slice_info.config) to short names +# These are the codes Bambu Studio uses internally +PRINTER_MODEL_ID_MAP = { + # X1 series + "C11": "X1C", + "C12": "X1", + "C13": "X1E", + # P1 series + "P1P": "P1P", + "P1S": "P1S", + # P2 series + "P2S": "P2S", + # A1 series + "A11": "A1", + "A12": "A1 Mini", + "N1": "A1", + "N2S": "A1 Mini", + "A04": "A1 Mini", + # H2D series (Office/H series) + "O1D": "H2D", + "O2D": "H2D Pro", +} + + +def normalize_printer_model_id(model_id: str | None) -> str | None: + """Convert printer_model_id (internal code) to normalized short name. + + Args: + model_id: The printer_model_id from slice_info.config (e.g., "C11", "O1D") + + Returns: + Normalized short name (e.g., "X1C", "H2D") or the original ID if unknown. + """ + if not model_id: + return None + + # Check known mappings + if model_id in PRINTER_MODEL_ID_MAP: + return PRINTER_MODEL_ID_MAP[model_id] + + # Return original if unknown (might already be a short name) + return model_id + + +def normalize_printer_model(raw_model: str | None) -> str | None: + """Convert 3MF printer_model to normalized short name. + + Args: + raw_model: The printer_model string from 3MF metadata + (e.g., "Bambu Lab X1 Carbon") + + Returns: + Normalized short name (e.g., "X1C") or None if input is empty. + Unknown models have "Bambu Lab " prefix stripped. + """ + if not raw_model: + return None + + # Check known mappings first + if raw_model in PRINTER_MODEL_MAP: + return PRINTER_MODEL_MAP[raw_model] + + # Strip "Bambu Lab " prefix for unknown models + stripped = raw_model.replace("Bambu Lab ", "").strip() + return stripped or None diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 6ccef4b6e..00f4aef2a 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -300,6 +300,7 @@ export interface Archive { nozzle_diameter: number | null; bed_temperature: number | null; nozzle_temperature: number | null; + sliced_for_model: string | null; // Printer model this file was sliced for status: string; started_at: string | null; completed_at: string | null; @@ -1000,6 +1001,7 @@ export interface DiscoveredTasmotaDevice { export interface PrintQueueItem { id: number; printer_id: number | null; // null = unassigned + target_model: string | null; // Target printer model for model-based assignment // Either archive_id OR library_file_id must be set (archive created at print start) archive_id: number | null; library_file_id: number | null; @@ -1032,6 +1034,7 @@ export interface PrintQueueItem { export interface PrintQueueItemCreate { printer_id?: number | null; // null = unassigned + target_model?: string | null; // Target printer model (mutually exclusive with printer_id) // Either archive_id OR library_file_id must be provided archive_id?: number | null; library_file_id?: number | null; @@ -1052,6 +1055,7 @@ export interface PrintQueueItemCreate { export interface PrintQueueItemUpdate { printer_id?: number | null; // null = unassign + target_model?: string | null; // Target printer model (mutually exclusive with printer_id) position?: number; scheduled_time?: string | null; require_previous_success?: boolean; diff --git a/frontend/src/components/PrintModal/PrinterSelector.tsx b/frontend/src/components/PrintModal/PrinterSelector.tsx index d363e1763..9b618c5b7 100644 --- a/frontend/src/components/PrintModal/PrinterSelector.tsx +++ b/frontend/src/components/PrintModal/PrinterSelector.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { Printer as PrinterIcon, @@ -9,6 +9,7 @@ import { Circle, RefreshCw, Wand2, + Users, } from 'lucide-react'; import { api } from '../../api/client'; import { getColorName } from '../../utils/colors'; @@ -16,7 +17,7 @@ import { normalizeColorForCompare, colorsAreSimilar, } from '../../utils/amsHelpers'; -import type { PrinterSelectorProps } from './types'; +import type { PrinterSelectorProps, AssignmentMode } from './types'; import type { PrinterMappingResult, PerPrinterConfig } from '../../hooks/useMultiPrinterFilamentMapping'; import type { FilamentRequirement, LoadedFilament } from '../../hooks/useFilamentMapping'; @@ -29,6 +30,16 @@ interface PrinterSelectorWithMappingProps extends PrinterSelectorProps { onAutoConfigurePrinter?: (printerId: number) => void; /** Callback to update printer config */ onUpdatePrinterConfig?: (printerId: number, config: Partial) => void; + /** Current assignment mode */ + assignmentMode?: AssignmentMode; + /** Handler for assignment mode change */ + onAssignmentModeChange?: (mode: AssignmentMode) => void; + /** Selected target model (when assignmentMode is 'model') */ + targetModel?: string | null; + /** Handler for target model change */ + onTargetModelChange?: (model: string | null) => void; + /** Suggested model from sliced file (for pre-selection) */ + slicedForModel?: string | null; } /** @@ -212,9 +223,42 @@ export function PrinterSelector({ filamentReqs, onAutoConfigurePrinter, onUpdatePrinterConfig, + assignmentMode = 'printer', + onAssignmentModeChange, + targetModel, + onTargetModelChange, + slicedForModel, }: PrinterSelectorWithMappingProps) { + // State for showing all printers vs only matching model + const [showAllPrinters, setShowAllPrinters] = useState(false); + // Filter printers based on showInactive flag - const displayPrinters = showInactive ? printers : printers.filter((p) => p.is_active); + const activePrinters = showInactive ? printers : printers.filter((p) => p.is_active); + + // Filter by sliced model (only in printer mode, when slicedForModel is set) + const displayPrinters = useMemo(() => { + if (assignmentMode !== 'printer' || !slicedForModel || showAllPrinters) { + return activePrinters; + } + // Filter to only show printers matching the sliced model + const matching = activePrinters.filter((p) => p.model === slicedForModel); + // If no matching printers, show all + return matching.length > 0 ? matching : activePrinters; + }, [activePrinters, assignmentMode, slicedForModel, showAllPrinters]); + + // Check if there are hidden printers due to model filtering + const hiddenPrinterCount = activePrinters.length - displayPrinters.length; + + // Get unique models from available printers (for model-based assignment) + const uniqueModels = useMemo(() => { + const models = activePrinters + .map(p => p.model) + .filter((m): m is string => Boolean(m)); + return [...new Set(models)].sort(); + }, [activePrinters]); + + // Check if model-based assignment is available (need callbacks and multiple printers of same model) + const modelAssignmentAvailable = onAssignmentModeChange && onTargetModelChange && uniqueModels.length > 0; const showMappingOptions = allowMultiple && selectedPrinterIds.length > 1 && @@ -285,8 +329,56 @@ export function PrinterSelector({ return (

- {/* Multi-select header */} - {allowMultiple && displayPrinters.length > 1 && ( + {/* Assignment mode toggle (model vs specific printer) */} + {modelAssignmentAvailable && ( +
+ + +
+ )} + + {/* Model info (when in model mode) */} + {assignmentMode === 'model' && modelAssignmentAvailable && targetModel && ( +

+ Scheduler will assign to first available idle {targetModel} printer +

+ )} + + {/* Multi-select header (only in printer mode) */} + {assignmentMode === 'printer' && allowMultiple && displayPrinters.length > 1 && (
{selectedCount === 0 @@ -316,7 +408,8 @@ export function PrinterSelector({
)} - {displayPrinters.map((printer) => { + {/* Printer list (only in printer mode) */} + {assignmentMode === 'printer' && displayPrinters.map((printer) => { const selected = isSelected(printer.id); const mappingResult = getPrinterMappingResult(printer.id); const hasOverride = mappingResult && !mappingResult.config.useDefault; @@ -430,13 +523,45 @@ export function PrinterSelector({ ); })} - {/* Warning when no printer selected */} - {selectedCount === 0 && ( + {/* Show hidden printers toggle */} + {assignmentMode === 'printer' && hiddenPrinterCount > 0 && !showAllPrinters && ( + + )} + + {/* Show matching only toggle */} + {assignmentMode === 'printer' && showAllPrinters && slicedForModel && ( + + )} + + {/* Warning when no printer selected (only in printer mode) */} + {assignmentMode === 'printer' && selectedCount === 0 && (

Select at least one printer

)} + + {/* Warning when no model selected (only in model mode) */} + {assignmentMode === 'model' && !targetModel && ( +

+ + Select a target printer model +

+ )}
); } diff --git a/frontend/src/components/PrintModal/index.tsx b/frontend/src/components/PrintModal/index.tsx index 24c2908db..5096b5b65 100644 --- a/frontend/src/components/PrintModal/index.tsx +++ b/frontend/src/components/PrintModal/index.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useMemo } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { X, Printer, Loader2, Calendar, Pencil, AlertCircle } from 'lucide-react'; +import { X, Printer, Loader2, Calendar, Pencil, AlertCircle, AlertTriangle } from 'lucide-react'; import { api } from '../../api/client'; import type { PrintQueueItemCreate, PrintQueueItemUpdate } from '../../api/client'; import { Card, CardContent } from '../Card'; @@ -19,6 +19,7 @@ import type { PrintOptions, ScheduleOptions, ScheduleType, + AssignmentMode, } from './types'; import { DEFAULT_PRINT_OPTIONS, DEFAULT_SCHEDULE_OPTIONS } from './types'; @@ -117,6 +118,23 @@ export function PrintModal({ // Per-printer override configs (for multi-printer selection) const [perPrinterConfigs, setPerPrinterConfigs] = useState>({}); + // Assignment mode: 'printer' (specific) or 'model' (any of model) + const [assignmentMode, setAssignmentMode] = useState(() => { + // Initialize from queue item if editing with target_model + if (mode === 'edit-queue-item' && queueItem?.target_model) { + return 'model'; + } + return 'printer'; + }); + + // Target model for model-based assignment + const [targetModel, setTargetModel] = useState(() => { + if (mode === 'edit-queue-item' && queueItem?.target_model) { + return queueItem.target_model; + } + return null; + }); + // Track initial values for clearing mappings on change (edit mode only) const [initialPrinterIds] = useState(() => (mode === 'edit-queue-item' && queueItem?.printer_id ? [queueItem.printer_id] : [])); const [initialPlateId] = useState(() => (mode === 'edit-queue-item' && queueItem ? queueItem.plate_id : null)); @@ -145,6 +163,16 @@ export function PrintModal({ queryFn: api.getPrinters, }); + // Fetch archive details to get sliced_for_model + const { data: archiveDetails } = useQuery({ + queryKey: ['archive', archiveId], + queryFn: () => api.getArchive(archiveId!), + enabled: !!archiveId && !isLibraryFile, + }); + + // Get sliced_for_model from archive or library file + const slicedForModel = archiveDetails?.sliced_for_model || null; + // Fetch plates for archives const { data: archivePlatesData, isError: archivePlatesError } = useQuery({ queryKey: ['archive-plates', archiveId], @@ -304,14 +332,20 @@ export function PrintModal({ const handleSubmit = async (e?: React.FormEvent) => { e?.preventDefault(); - // Validate printer selection - if (selectedPrinters.length === 0) { + // Validate printer/model selection + if (assignmentMode === 'printer' && selectedPrinters.length === 0) { showToast('Please select at least one printer', 'error'); return; } + if (assignmentMode === 'model' && !targetModel) { + showToast('Please select a target printer model', 'error'); + return; + } setIsSubmitting(true); - setSubmitProgress({ current: 0, total: selectedPrinters.length }); + // For model-based assignment, we just make one API call + const totalCount = assignmentMode === 'model' ? 1 : selectedPrinters.length; + setSubmitProgress({ current: 0, total: totalCount }); const results: { success: number; failed: number; errors: string[] } = { success: 0, @@ -332,15 +366,16 @@ export function PrintModal({ }; // Common queue data for add-to-queue and edit modes - const getQueueData = (printerId: number): PrintQueueItemCreate => ({ - printer_id: printerId, + const getQueueData = (printerId: number | null): PrintQueueItemCreate => ({ + printer_id: assignmentMode === 'printer' ? printerId : null, + target_model: assignmentMode === 'model' ? targetModel : null, // Use library_file_id for library files, archive_id for archives archive_id: isLibraryFile ? undefined : archiveId, library_file_id: isLibraryFile ? libraryFileId : undefined, require_previous_success: scheduleOptions.requirePreviousSuccess, auto_off_after: scheduleOptions.autoOffAfter, manual_start: scheduleOptions.scheduleType === 'manual', - ams_mapping: getMappingForPrinter(printerId), + ams_mapping: printerId ? getMappingForPrinter(printerId) : undefined, plate_id: selectedPlate, scheduled_time: scheduleOptions.scheduleType === 'scheduled' && scheduleOptions.scheduledTime ? new Date(scheduleOptions.scheduledTime).toISOString() @@ -348,35 +383,24 @@ export function PrintModal({ ...printOptions, }); - for (let i = 0; i < selectedPrinters.length; i++) { - const printerId = selectedPrinters[i]; - setSubmitProgress({ current: i + 1, total: selectedPrinters.length }); - + // Model-based assignment: single API call + if (assignmentMode === 'model') { + setSubmitProgress({ current: 1, total: 1 }); try { if (mode === 'reprint') { - // Reprint mode - start print immediately - const printerMapping = getMappingForPrinter(printerId); - if (isLibraryFile) { - await api.printLibraryFile(libraryFileId!, printerId, { - ams_mapping: printerMapping, - ...printOptions, - }); - } else { - await api.reprintArchive(archiveId!, printerId, { - plate_id: selectedPlate ?? undefined, - ams_mapping: printerMapping, - ...printOptions, - }); - } - } else if (mode === 'edit-queue-item' && i === 0) { - // Edit mode - update the original queue item for the first printer - const printerMapping = getMappingForPrinter(printerId); + // Model-based reprint not supported (need specific printer for immediate print) + showToast('Model-based assignment only works with queue mode', 'error'); + setIsSubmitting(false); + return; + } else if (mode === 'edit-queue-item') { + // Edit mode - update with target_model const updateData: PrintQueueItemUpdate = { - printer_id: printerId, + printer_id: null, + target_model: targetModel, require_previous_success: scheduleOptions.requirePreviousSuccess, auto_off_after: scheduleOptions.autoOffAfter, manual_start: scheduleOptions.scheduleType === 'manual', - ams_mapping: printerMapping, + ams_mapping: undefined, plate_id: selectedPlate, scheduled_time: scheduleOptions.scheduleType === 'scheduled' && scheduleOptions.scheduledTime ? new Date(scheduleOptions.scheduledTime).toISOString() @@ -385,14 +409,63 @@ export function PrintModal({ }; await updateQueueMutation.mutateAsync(updateData); } else { - // Add-to-queue mode OR edit mode with additional printers - await addToQueueMutation.mutateAsync(getQueueData(printerId)); + // Add-to-queue mode with model-based assignment + await addToQueueMutation.mutateAsync(getQueueData(null)); } results.success++; } catch (error) { results.failed++; - const printerName = printers?.find(p => p.id === printerId)?.name || `Printer ${printerId}`; - results.errors.push(`${printerName}: ${(error as Error).message}`); + results.errors.push((error as Error).message); + } + } else { + // Printer-based assignment: loop through selected printers + for (let i = 0; i < selectedPrinters.length; i++) { + const printerId = selectedPrinters[i]; + setSubmitProgress({ current: i + 1, total: selectedPrinters.length }); + + try { + if (mode === 'reprint') { + // Reprint mode - start print immediately + const printerMapping = getMappingForPrinter(printerId); + if (isLibraryFile) { + await api.printLibraryFile(libraryFileId!, printerId, { + ams_mapping: printerMapping, + ...printOptions, + }); + } else { + await api.reprintArchive(archiveId!, printerId, { + plate_id: selectedPlate ?? undefined, + ams_mapping: printerMapping, + ...printOptions, + }); + } + } else if (mode === 'edit-queue-item' && i === 0) { + // Edit mode - update the original queue item for the first printer + const printerMapping = getMappingForPrinter(printerId); + const updateData: PrintQueueItemUpdate = { + printer_id: printerId, + target_model: null, + require_previous_success: scheduleOptions.requirePreviousSuccess, + auto_off_after: scheduleOptions.autoOffAfter, + manual_start: scheduleOptions.scheduleType === 'manual', + ams_mapping: printerMapping, + plate_id: selectedPlate, + scheduled_time: scheduleOptions.scheduleType === 'scheduled' && scheduleOptions.scheduledTime + ? new Date(scheduleOptions.scheduledTime).toISOString() + : null, + ...printOptions, + }; + await updateQueueMutation.mutateAsync(updateData); + } else { + // Add-to-queue mode OR edit mode with additional printers + await addToQueueMutation.mutateAsync(getQueueData(printerId)); + } + results.success++; + } catch (error) { + results.failed++; + const printerName = printers?.find(p => p.id === printerId)?.name || `Printer ${printerId}`; + results.errors.push(`${printerName}: ${(error as Error).message}`); + } } } @@ -400,11 +473,15 @@ export function PrintModal({ // Show result toast if (results.failed === 0) { - const action = mode === 'reprint' ? 'sent to' : (mode === 'edit-queue-item' ? 'updated/queued for' : 'queued for'); - if (results.success === 1) { - showToast(mode === 'edit-queue-item' ? 'Queue item updated' : `Print ${action} printer`); + if (assignmentMode === 'model') { + showToast(mode === 'edit-queue-item' ? 'Queue item updated' : `Queued for any ${targetModel}`); } else { - showToast(`Print ${action} ${results.success} printers`); + const action = mode === 'reprint' ? 'sent to' : (mode === 'edit-queue-item' ? 'updated/queued for' : 'queued for'); + if (results.success === 1) { + showToast(mode === 'edit-queue-item' ? 'Queue item updated' : `Print ${action} printer`); + } else { + showToast(`Print ${action} ${results.success} printers`); + } } queryClient.invalidateQueries({ queryKey: ['queue'] }); onSuccess?.(); @@ -422,14 +499,18 @@ export function PrintModal({ const canSubmit = useMemo(() => { if (isPending) return false; - // Need at least one selected printer - if (selectedPrinters.length === 0) return false; + // Need valid printer/model selection + if (assignmentMode === 'printer' && selectedPrinters.length === 0) return false; + if (assignmentMode === 'model' && !targetModel) return false; + + // Model-based assignment only works in queue modes (not immediate reprint) + if (assignmentMode === 'model' && mode === 'reprint') return false; // For multi-plate archive files, need a selected plate (library files skip this) if (!isLibraryFile && isMultiPlate && !selectedPlate) return false; return true; - }, [selectedPrinters.length, isMultiPlate, selectedPlate, isPending, isLibraryFile]); + }, [selectedPrinters.length, assignmentMode, targetModel, mode, isMultiPlate, selectedPlate, isPending, isLibraryFile]); // Modal title and action button text based on mode const getModalConfig = () => { @@ -541,8 +622,29 @@ export function PrintModal({ filamentReqs={effectiveFilamentReqs} onAutoConfigurePrinter={multiPrinterMapping.autoConfigurePrinter} onUpdatePrinterConfig={multiPrinterMapping.updatePrinterConfig} + assignmentMode={mode === 'reprint' ? 'printer' : assignmentMode} + onAssignmentModeChange={mode !== 'reprint' ? setAssignmentMode : undefined} + targetModel={targetModel} + onTargetModelChange={mode !== 'reprint' ? setTargetModel : undefined} + slicedForModel={slicedForModel} /> + {/* Compatibility warning when sliced model doesn't match selected printer */} + {slicedForModel && assignmentMode === 'printer' && selectedPrinters.length === 1 && (() => { + const selectedPrinter = printers?.find(p => p.id === selectedPrinters[0]); + if (selectedPrinter && selectedPrinter.model && slicedForModel !== selectedPrinter.model) { + return ( +
+ + + File was sliced for {slicedForModel}, but printing on {selectedPrinter.model} + +
+ ); + } + return null; + })()} + {/* Warning when archive data couldn't be loaded */} {archiveDataMissing && (
@@ -565,7 +667,7 @@ export function PrintModal({ )} {/* Print options */} - {(mode === 'reprint' || effectivePrinterCount > 0) && ( + {(mode === 'reprint' || effectivePrinterCount > 0 || (assignmentMode === 'model' && targetModel)) && ( )} diff --git a/frontend/src/components/PrintModal/types.ts b/frontend/src/components/PrintModal/types.ts index b48caa41e..d3e702cb9 100644 --- a/frontend/src/components/PrintModal/types.ts +++ b/frontend/src/components/PrintModal/types.ts @@ -104,6 +104,13 @@ export interface PlatesResponse { plates: PlateInfo[]; } +/** + * Assignment mode for queue items. + * - 'printer': Assign to specific printer(s) + * - 'model': Assign to any printer of a specific model (load balancing) + */ +export type AssignmentMode = 'printer' | 'model'; + /** * Props for the PrinterSelector component. */ @@ -115,6 +122,16 @@ export interface PrinterSelectorProps { allowMultiple?: boolean; /** Show inactive printers (for edit mode where original assignment may be inactive) */ showInactive?: boolean; + /** Current assignment mode */ + assignmentMode?: AssignmentMode; + /** Handler for assignment mode change */ + onAssignmentModeChange?: (mode: AssignmentMode) => void; + /** Selected target model (when assignmentMode is 'model') */ + targetModel?: string | null; + /** Handler for target model change */ + onTargetModelChange?: (model: string | null) => void; + /** Suggested model from sliced file (for pre-selection) */ + slicedForModel?: string | null; } /** diff --git a/frontend/src/components/UploadModal.tsx b/frontend/src/components/UploadModal.tsx index 254a16e54..78a0b4658 100644 --- a/frontend/src/components/UploadModal.tsx +++ b/frontend/src/components/UploadModal.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, useRef, useEffect } from 'react'; -import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Upload, X, File, CheckCircle, AlertCircle, Loader2 } from 'lucide-react'; import { api } from '../api/client'; import type { BulkUploadResult } from '../api/client'; @@ -27,7 +27,6 @@ export function UploadModal({ onClose, initialFiles }: UploadModalProps) { initialFiles?.filter(f => f.name.endsWith('.3mf')).map(file => ({ file, status: 'pending' as const })) || [] ); const [isDragging, setIsDragging] = useState(false); - const [selectedPrinter, setSelectedPrinter] = useState(); const [uploadResult, setUploadResult] = useState(null); // Close on Escape key @@ -39,14 +38,9 @@ export function UploadModal({ onClose, initialFiles }: UploadModalProps) { return () => window.removeEventListener('keydown', handleKeyDown); }, [onClose]); - const { data: printers } = useQuery({ - queryKey: ['printers'], - queryFn: api.getPrinters, - }); - const uploadMutation = useMutation({ mutationFn: (filesToUpload: File[]) => - api.uploadArchivesBulk(filesToUpload, selectedPrinter), + api.uploadArchivesBulk(filesToUpload), onSuccess: (result) => { setUploadResult(result); queryClient.invalidateQueries({ queryKey: ['archives'] }); @@ -200,26 +194,11 @@ export function UploadModal({ onClose, initialFiles }: UploadModalProps) {
- {/* Optional Printer Selection */} + {/* Info about printer model extraction */}
- - +

+ The printer model will be automatically extracted from the 3MF file metadata. +

{/* File List */} diff --git a/frontend/src/pages/ArchivesPage.tsx b/frontend/src/pages/ArchivesPage.tsx index 8d023585d..bb1c9b58a 100644 --- a/frontend/src/pages/ArchivesPage.tsx +++ b/frontend/src/pages/ArchivesPage.tsx @@ -716,6 +716,12 @@ function ArchiveCard({ {archive.object_count} object{archive.object_count > 1 ? 's' : ''} )} + {archive.sliced_for_model && ( +
+ + {archive.sliced_for_model} +
+ )} {archive.filament_type && (
{archive.filament_type} @@ -1531,9 +1537,20 @@ function ArchiveListRow({ )}
- {archive.filament_type && ( + {(archive.filament_type || archive.sliced_for_model) && (
- {archive.filament_type} + {archive.sliced_for_model && ( + + + {archive.sliced_for_model} + + )} + {archive.sliced_for_model && archive.filament_type && ( + · + )} + {archive.filament_type && ( + {archive.filament_type} + )} {archive.filament_color && (
{archive.filament_color.split(',').map((color, i) => ( @@ -2674,7 +2691,7 @@ export function ArchivesPage() {
- + - {item.printer_id === null ? 'Unassigned' : (item.printer_name || `Printer #${item.printer_id}`)} + {item.target_model + ? `Any ${item.target_model}` + : item.printer_id === null + ? 'Unassigned' + : (item.printer_name || `Printer #${item.printer_id}`)} {item.print_time_seconds && ( diff --git a/static/assets/index-BkOQ27rS.js b/static/assets/index-BkOQ27rS.js deleted file mode 100644 index d0e566d52..000000000 --- a/static/assets/index-BkOQ27rS.js +++ /dev/null @@ -1,8729 +0,0 @@ -function Jse(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 o of a.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).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 m_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function pl(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var WA={exports:{}},ob={};var ez;function eoe(){if(ez)return ob;ez=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var o=null;if(a!==void 0&&(o=""+a),i.key!==void 0&&(o=""+i.key),"key"in i){a={};for(var l in i)l!=="key"&&(a[l]=i[l])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:a}}return ob.Fragment=e,ob.jsx=n,ob.jsxs=n,ob}var tz;function toe(){return tz||(tz=1,WA.exports=eoe()),WA.exports}var s=toe(),qA={exports:{}},gn={};var nz;function noe(){if(nz)return gn;nz=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"),o=Symbol.for("react.context"),l=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"),m=Symbol.iterator;function p(z){return z===null||typeof z!="object"?null:(z=m&&z[m]||z["@@iterator"],typeof z=="function"?z:null)}var b={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,x={};function v(z,G,ne){this.props=z,this.context=G,this.refs=x,this.updater=ne||b}v.prototype.isReactComponent={},v.prototype.setState=function(z,G){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,G,"setState")},v.prototype.forceUpdate=function(z){this.updater.enqueueForceUpdate(this,z,"forceUpdate")};function S(){}S.prototype=v.prototype;function M(z,G,ne){this.props=z,this.context=G,this.refs=x,this.updater=ne||b}var A=M.prototype=new S;A.constructor=M,y(A,v.prototype),A.isPureReactComponent=!0;var T=Array.isArray;function k(){}var E={H:null,A:null,T:null,S:null},O=Object.prototype.hasOwnProperty;function C(z,G,ne){var ie=ne.ref;return{$$typeof:t,type:z,key:G,ref:ie!==void 0?ie:null,props:ne}}function D(z,G){return C(z.type,G,z.props)}function U(z){return typeof z=="object"&&z!==null&&z.$$typeof===t}function L(z){var G={"=":"=0",":":"=2"};return"$"+z.replace(/[=:]/g,function(ne){return G[ne]})}var q=/\/+/g;function j(z,G){return typeof z=="object"&&z!==null&&z.key!=null?L(""+z.key):G.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(G){z.status==="pending"&&(z.status="fulfilled",z.value=G)},function(G){z.status==="pending"&&(z.status="rejected",z.reason=G)})),z.status){case"fulfilled":return z.value;case"rejected":throw z.reason}}throw z}function K(z,G,ne,ie,ue){var me=typeof z;(me==="undefined"||me==="boolean")&&(z=null);var ce=!1;if(z===null)ce=!0;else switch(me){case"bigint":case"string":case"number":ce=!0;break;case"object":switch(z.$$typeof){case t:case e:ce=!0;break;case d:return ce=z._init,K(ce(z._payload),G,ne,ie,ue)}}if(ce)return ue=ue(z),ce=ie===""?"."+j(z,0):ie,T(ue)?(ne="",ce!=null&&(ne=ce.replace(q,"$&/")+"/"),K(ue,G,ne,"",function(Ne){return Ne})):ue!=null&&(U(ue)&&(ue=D(ue,ne+(ue.key==null||z&&z.key===ue.key?"":(""+ue.key).replace(q,"$&/")+"/")+ce)),G.push(ue)),1;ce=0;var Ae=ie===""?".":ie+":";if(T(z))for(var pe=0;pe>>1,ee=K[re];if(0>>1;rei(ne,Z))iei(ue,ne)?(K[re]=ue,K[ie]=Z,re=ie):(K[re]=ne,K[G]=Z,re=G);else if(iei(ue,Z))K[re]=ue,K[ie]=Z,re=ie;else break e}}return V}function i(K,V){var Z=K.sortIndex-V.sortIndex;return Z!==0?Z:K.id-V.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 o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,f=null,m=3,p=!1,b=!1,y=!1,x=!1,v=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,M=typeof setImmediate<"u"?setImmediate:null;function A(K){for(var V=n(u);V!==null;){if(V.callback===null)r(u);else if(V.startTime<=K)r(u),V.sortIndex=V.expirationTime,e(c,V);else break;V=n(u)}}function T(K){if(y=!1,A(K),!b)if(n(c)!==null)b=!0,k||(k=!0,L());else{var V=n(u);V!==null&&W(T,V.startTime-K)}}var k=!1,E=-1,O=5,C=-1;function D(){return x?!0:!(t.unstable_now()-CK&&D());){var re=f.callback;if(typeof re=="function"){f.callback=null,m=f.priorityLevel;var ee=re(f.expirationTime<=K);if(K=t.unstable_now(),typeof ee=="function"){f.callback=ee,A(K),V=!0;break t}f===n(c)&&r(c),A(K)}else r(c);f=n(c)}if(f!==null)V=!0;else{var z=n(u);z!==null&&W(T,z.startTime-K),V=!1}}break e}finally{f=null,m=Z,p=!1}V=void 0}}finally{V?L():k=!1}}}var L;if(typeof M=="function")L=function(){M(U)};else if(typeof MessageChannel<"u"){var q=new MessageChannel,j=q.port2;q.port1.onmessage=U,L=function(){j.postMessage(null)}}else L=function(){v(U,0)};function W(K,V){E=v(function(){K(t.unstable_now())},V)}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||125re?(K.sortIndex=Z,e(u,K),n(c)===null&&K===n(u)&&(y?(S(E),E=-1):y=!0,W(T,Z-re))):(K.sortIndex=ee,e(c,K),b||p||(b=!0,k||(k=!0,L()))),K},t.unstable_shouldYield=D,t.unstable_wrapCallback=function(K){var V=m;return function(){var Z=m;m=V;try{return K.apply(this,arguments)}finally{m=Z}}}})(YA)),YA}var az;function aoe(){return az||(az=1,XA.exports=ioe()),XA.exports}var QA={exports:{}},ja={};var sz;function soe(){if(sz)return ja;sz=1;var t=Kg();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(),QA.exports=soe(),QA.exports}var lz;function ooe(){if(lz)return lb;lz=1;var t=aoe(),e=Kg(),n=_q();function r(h){var g="https://react.dev/errors/"+h;if(1ee||(h.current=re[ee],re[ee]=null,ee--)}function ne(h,g){ee++,re[ee]=h.current,h.current=g}var ie=z(null),ue=z(null),me=z(null),ce=z(null);function Ae(h,g){switch(ne(me,g),ne(ue,h),ne(ie,null),g.nodeType){case 9:case 11:h=(h=g.documentElement)&&(h=h.namespaceURI)?SU(h):0;break;default:if(h=g.tagName,g=g.namespaceURI)g=SU(g),h=MU(g,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}G(ie),ne(ie,h)}function pe(){G(ie),G(ue),G(me)}function Ne(h){h.memoizedState!==null&&ne(ce,h);var g=ie.current,_=MU(g,h.type);g!==_&&(ne(ue,h),ne(ie,_))}function fe(h){ue.current===h&&(G(ie),G(ue)),ce.current===h&&(G(ce),rb._currentValue=Z)}var oe,Be;function H(h){if(oe===void 0)try{throw Error()}catch(_){var g=_.stack.trim().match(/\n( *(at )?)/);oe=g&&g[1]||"",Be=-1<_.stack.indexOf(` - at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return` -`+oe+h+Be}var J=!1;function X(h,g){if(!h||J)return"";J=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var N={DetermineComponentFrameRoot:function(){try{if(g){var vt=function(){throw Error()};if(Object.defineProperty(vt.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(vt,[])}catch(ft){var st=ft}Reflect.construct(h,[],vt)}else{try{vt.call()}catch(ft){st=ft}h.call(vt.prototype)}}else{try{throw Error()}catch(ft){st=ft}(vt=h())&&typeof vt.catch=="function"&&vt.catch(function(){})}}catch(ft){if(ft&&st&&typeof ft.stack=="string")return[ft.stack,st.stack]}return[null,null]}};N.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var R=Object.getOwnPropertyDescriptor(N.DetermineComponentFrameRoot,"name");R&&R.configurable&&Object.defineProperty(N.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var F=N.DetermineComponentFrameRoot(),Q=F[0],ge=F[1];if(Q&&ge){var De=Q.split(` -`),it=ge.split(` -`);for(R=N=0;NR||De[N]!==it[R]){var xt=` -`+De[N].replace(" at new "," at ");return h.displayName&&xt.includes("")&&(xt=xt.replace("",h.displayName)),xt}while(1<=N&&0<=R);break}}}finally{J=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?H(_):""}function B(h,g){switch(h.tag){case 26:case 27:case 5:return H(h.type);case 16:return H("Lazy");case 13:return h.child!==g&&g!==null?H("Suspense Fallback"):H("Suspense");case 19:return H("SuspenseList");case 0:case 15:return X(h.type,!1);case 11:return X(h.type.render,!1);case 1:return X(h.type,!0);case 31:return H("Activity");default:return""}}function I(h){try{var g="",_=null;do g+=B(h,_),_=h,h=h.return;while(h);return g}catch(N){return` -Error generating stack: `+N.message+` -`+N.stack}}var le=Object.prototype.hasOwnProperty,Te=t.unstable_scheduleCallback,Le=t.unstable_cancelCallback,Ee=t.unstable_shouldYield,$=t.unstable_requestPaint,P=t.unstable_now,Y=t.unstable_getCurrentPriorityLevel,xe=t.unstable_ImmediatePriority,we=t.unstable_UserBlockingPriority,ve=t.unstable_NormalPriority,Qe=t.unstable_LowPriority,Ie=t.unstable_IdlePriority,qe=t.log,Je=t.unstable_setDisableYieldValue,Ze=null,se=null;function te(h){if(typeof qe=="function"&&Je(h),se&&typeof se.setStrictMode=="function")try{se.setStrictMode(Ze,h)}catch{}}var Me=Math.clz32?Math.clz32:ae,Fe=Math.log,ot=Math.LN2;function ae(h){return h>>>=0,h===0?32:31-(Fe(h)/ot|0)|0}var je=256,be=262144,Oe=4194304;function Ue(h){var g=h&42;if(g!==0)return g;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 Ce(h,g,_){var N=h.pendingLanes;if(N===0)return 0;var R=0,F=h.suspendedLanes,Q=h.pingedLanes;h=h.warmLanes;var ge=N&134217727;return ge!==0?(N=ge&~F,N!==0?R=Ue(N):(Q&=ge,Q!==0?R=Ue(Q):_||(_=ge&~h,_!==0&&(R=Ue(_))))):(ge=N&~F,ge!==0?R=Ue(ge):Q!==0?R=Ue(Q):_||(_=N&~h,_!==0&&(R=Ue(_)))),R===0?0:g!==0&&g!==R&&(g&F)===0&&(F=R&-R,_=g&-g,F>=_||F===32&&(_&4194048)!==0)?g:R}function We(h,g){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&g)===0}function pt(h,g){switch(h){case 1:case 2:case 4:case 8:case 64:return g+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 g+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 kt(){var h=Oe;return Oe<<=1,(Oe&62914560)===0&&(Oe=4194304),h}function Lt(h){for(var g=[],_=0;31>_;_++)g.push(h);return g}function ln(h,g){h.pendingLanes|=g,g!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function Gt(h,g,_,N,R,F){var Q=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var ge=h.entanglements,De=h.expirationTimes,it=h.hiddenUpdates;for(_=Q&~_;0<_;){var xt=31-Me(_),vt=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var Rt=/[\n"\\]/g;function $t(h){return h.replace(Rt,function(g){return"\\"+g.charCodeAt(0).toString(16)+" "})}function Ln(h,g,_,N,R,F,Q,ge){h.name="",Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"?h.type=Q:h.removeAttribute("type"),g!=null?Q==="number"?(g===0&&h.value===""||h.value!=g)&&(h.value=""+ye(g)):h.value!==""+ye(g)&&(h.value=""+ye(g)):Q!=="submit"&&Q!=="reset"||h.removeAttribute("value"),g!=null?Zt(h,Q,ye(g)):_!=null?Zt(h,Q,ye(_)):N!=null&&h.removeAttribute("value"),R==null&&F!=null&&(h.defaultChecked=!!F),R!=null&&(h.checked=R&&typeof R!="function"&&typeof R!="symbol"),ge!=null&&typeof ge!="function"&&typeof ge!="symbol"&&typeof ge!="boolean"?h.name=""+ye(ge):h.removeAttribute("name")}function bi(h,g,_,N,R,F,Q,ge){if(F!=null&&typeof F!="function"&&typeof F!="symbol"&&typeof F!="boolean"&&(h.type=F),g!=null||_!=null){if(!(F!=="submit"&&F!=="reset"||g!=null)){Mt(h);return}_=_!=null?""+ye(_):"",g=g!=null?""+ye(g):_,ge||g===h.value||(h.value=g),h.defaultValue=g}N=N??R,N=typeof N!="function"&&typeof N!="symbol"&&!!N,h.checked=ge?h.checked:!!N,h.defaultChecked=!!N,Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"&&(h.name=Q),Mt(h)}function Zt(h,g,_){g==="number"&&Tt(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function cn(h,g,_,N){if(h=h.options,g){g={};for(var R=0;R<_.length;R++)g["$"+_[R]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),WE=!1;if(gc)try{var _x={};Object.defineProperty(_x,"passive",{get:function(){WE=!0}}),window.addEventListener("test",_x,_x),window.removeEventListener("test",_x,_x)}catch{WE=!1}var Ru=null,qE=null,sv=null;function MI(){if(sv)return sv;var h,g=qE,_=g.length,N,R="value"in Ru?Ru.value:Ru.textContent,F=R.length;for(h=0;h<_&&g[h]===R[h];h++);var Q=_-h;for(N=1;N<=Q&&g[_-N]===R[F-N];N++);return sv=R.slice(h,1=Mx),kI=" ",jI=!1;function PI(h,g){switch(h){case"keyup":return _ae.indexOf(g.keyCode)!==-1;case"keydown":return g.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function RI(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var Sm=!1;function Sae(h,g){switch(h){case"compositionend":return RI(g);case"keypress":return g.which!==32?null:(jI=!0,kI);case"textInput":return h=g.data,h===kI&&jI?null:h;default:return null}}function Mae(h,g){if(Sm)return h==="compositionend"||!ZE&&PI(h,g)?(h=MI(),sv=qE=Ru=null,Sm=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(g.ctrlKey||g.altKey||g.metaKey)||g.ctrlKey&&g.altKey){if(g.char&&1=g)return{node:_,offset:g-h};h=N}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=BI(_)}}function HI(h,g){return h&&g?h===g?!0:h&&h.nodeType===3?!1:g&&g.nodeType===3?HI(h,g.parentNode):"contains"in h?h.contains(g):h.compareDocumentPosition?!!(h.compareDocumentPosition(g)&16):!1:!1}function VI(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var g=Tt(h.document);g instanceof h.HTMLIFrameElement;){try{var _=typeof g.contentWindow.location.href=="string"}catch{_=!1}if(_)h=g.contentWindow;else break;g=Tt(h.document)}return g}function t2(h){var g=h&&h.nodeName&&h.nodeName.toLowerCase();return g&&(g==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||g==="textarea"||h.contentEditable==="true")}var Pae=gc&&"documentMode"in document&&11>=document.documentMode,Mm=null,n2=null,Tx=null,r2=!1;function GI(h,g,_){var N=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;r2||Mm==null||Mm!==Tt(N)||(N=Mm,"selectionStart"in N&&t2(N)?N={start:N.selectionStart,end:N.selectionEnd}:(N=(N.ownerDocument&&N.ownerDocument.defaultView||window).getSelection(),N={anchorNode:N.anchorNode,anchorOffset:N.anchorOffset,focusNode:N.focusNode,focusOffset:N.focusOffset}),Tx&&Ax(Tx,N)||(Tx=N,N=Jv(n2,"onSelect"),0>=Q,R-=Q,_l=1<<32-Me(g)+R|_<vn?(Fn=Jt,Jt=null):Fn=Jt.sibling;var Xn=st(Ye,Jt,nt[vn],bt);if(Xn===null){Jt===null&&(Jt=Fn);break}h&&Jt&&Xn.alternate===null&&g(Ye,Jt),ze=F(Xn,ze,vn),Kn===null?tn=Xn:Kn.sibling=Xn,Kn=Xn,Jt=Fn}if(vn===nt.length)return _(Ye,Jt),zn&&bc(Ye,vn),tn;if(Jt===null){for(;vnvn?(Fn=Jt,Jt=null):Fn=Jt.sibling;var td=st(Ye,Jt,Xn.value,bt);if(td===null){Jt===null&&(Jt=Fn);break}h&&Jt&&td.alternate===null&&g(Ye,Jt),ze=F(td,ze,vn),Kn===null?tn=td:Kn.sibling=td,Kn=td,Jt=Fn}if(Xn.done)return _(Ye,Jt),zn&&bc(Ye,vn),tn;if(Jt===null){for(;!Xn.done;vn++,Xn=nt.next())Xn=vt(Ye,Xn.value,bt),Xn!==null&&(ze=F(Xn,ze,vn),Kn===null?tn=Xn:Kn.sibling=Xn,Kn=Xn);return zn&&bc(Ye,vn),tn}for(Jt=N(Jt);!Xn.done;vn++,Xn=nt.next())Xn=ft(Jt,Ye,vn,Xn.value,bt),Xn!==null&&(h&&Xn.alternate!==null&&Jt.delete(Xn.key===null?vn:Xn.key),ze=F(Xn,ze,vn),Kn===null?tn=Xn:Kn.sibling=Xn,Kn=Xn);return h&&Jt.forEach(function(Zse){return g(Ye,Zse)}),zn&&bc(Ye,vn),tn}function dr(Ye,ze,nt,bt){if(typeof nt=="object"&&nt!==null&&nt.type===y&&nt.key===null&&(nt=nt.props.children),typeof nt=="object"&&nt!==null){switch(nt.$$typeof){case p:e:{for(var tn=nt.key;ze!==null;){if(ze.key===tn){if(tn=nt.type,tn===y){if(ze.tag===7){_(Ye,ze.sibling),bt=R(ze,nt.props.children),bt.return=Ye,Ye=bt;break e}}else if(ze.elementType===tn||typeof tn=="object"&&tn!==null&&tn.$$typeof===O&&Rh(tn)===ze.type){_(Ye,ze.sibling),bt=R(ze,nt.props),Dx(bt,nt),bt.return=Ye,Ye=bt;break e}_(Ye,ze);break}else g(Ye,ze);ze=ze.sibling}nt.type===y?(bt=Th(nt.props.children,Ye.mode,bt,nt.key),bt.return=Ye,Ye=bt):(bt=gv(nt.type,nt.key,nt.props,null,Ye.mode,bt),Dx(bt,nt),bt.return=Ye,Ye=bt)}return Q(Ye);case b:e:{for(tn=nt.key;ze!==null;){if(ze.key===tn)if(ze.tag===4&&ze.stateNode.containerInfo===nt.containerInfo&&ze.stateNode.implementation===nt.implementation){_(Ye,ze.sibling),bt=R(ze,nt.children||[]),bt.return=Ye,Ye=bt;break e}else{_(Ye,ze);break}else g(Ye,ze);ze=ze.sibling}bt=u2(nt,Ye.mode,bt),bt.return=Ye,Ye=bt}return Q(Ye);case O:return nt=Rh(nt),dr(Ye,ze,nt,bt)}if(W(nt))return Qt(Ye,ze,nt,bt);if(L(nt)){if(tn=L(nt),typeof tn!="function")throw Error(r(150));return nt=tn.call(nt),an(Ye,ze,nt,bt)}if(typeof nt.then=="function")return dr(Ye,ze,Sv(nt),bt);if(nt.$$typeof===M)return dr(Ye,ze,yv(Ye,nt),bt);Mv(Ye,nt)}return typeof nt=="string"&&nt!==""||typeof nt=="number"||typeof nt=="bigint"?(nt=""+nt,ze!==null&&ze.tag===6?(_(Ye,ze.sibling),bt=R(ze,nt),bt.return=Ye,Ye=bt):(_(Ye,ze),bt=c2(nt,Ye.mode,bt),bt.return=Ye,Ye=bt),Q(Ye)):_(Ye,ze)}return function(Ye,ze,nt,bt){try{Rx=0;var tn=dr(Ye,ze,nt,bt);return Om=null,tn}catch(Jt){if(Jt===Dm||Jt===_v)throw Jt;var Kn=Ps(29,Jt,null,Ye.mode);return Kn.lanes=bt,Kn.return=Ye,Kn}}}var Oh=mF(!0),pF=mF(!1),Fu=!1;function w2(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function S2(h,g){h=h.updateQueue,g.updateQueue===h&&(g.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function Uu(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function zu(h,g,_){var N=h.updateQueue;if(N===null)return null;if(N=N.shared,(Jn&2)!==0){var R=N.pending;return R===null?g.next=g:(g.next=R.next,R.next=g),N.pending=g,g=pv(h),ZI(h,null,_),g}return mv(h,N,g,_),pv(h)}function Ox(h,g,_){if(g=g.updateQueue,g!==null&&(g=g.shared,(_&4194048)!==0)){var N=g.lanes;N&=h.pendingLanes,_|=N,g.lanes=_,gt(h,_)}}function M2(h,g){var _=h.updateQueue,N=h.alternate;if(N!==null&&(N=N.updateQueue,_===N)){var R=null,F=null;if(_=_.firstBaseUpdate,_!==null){do{var Q={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};F===null?R=F=Q:F=F.next=Q,_=_.next}while(_!==null);F===null?R=F=g:F=F.next=g}else R=F=g;_={baseState:N.baseState,firstBaseUpdate:R,lastBaseUpdate:F,shared:N.shared,callbacks:N.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=g:h.next=g,_.lastBaseUpdate=g}var N2=!1;function Lx(){if(N2){var h=Rm;if(h!==null)throw h}}function Ix(h,g,_,N){N2=!1;var R=h.updateQueue;Fu=!1;var F=R.firstBaseUpdate,Q=R.lastBaseUpdate,ge=R.shared.pending;if(ge!==null){R.shared.pending=null;var De=ge,it=De.next;De.next=null,Q===null?F=it:Q.next=it,Q=De;var xt=h.alternate;xt!==null&&(xt=xt.updateQueue,ge=xt.lastBaseUpdate,ge!==Q&&(ge===null?xt.firstBaseUpdate=it:ge.next=it,xt.lastBaseUpdate=De))}if(F!==null){var vt=R.baseState;Q=0,xt=it=De=null,ge=F;do{var st=ge.lane&-536870913,ft=st!==ge.lane;if(ft?(In&st)===st:(N&st)===st){st!==0&&st===Pm&&(N2=!0),xt!==null&&(xt=xt.next={lane:0,tag:ge.tag,payload:ge.payload,callback:null,next:null});e:{var Qt=h,an=ge;st=g;var dr=_;switch(an.tag){case 1:if(Qt=an.payload,typeof Qt=="function"){vt=Qt.call(dr,vt,st);break e}vt=Qt;break e;case 3:Qt.flags=Qt.flags&-65537|128;case 0:if(Qt=an.payload,st=typeof Qt=="function"?Qt.call(dr,vt,st):Qt,st==null)break e;vt=f({},vt,st);break e;case 2:Fu=!0}}st=ge.callback,st!==null&&(h.flags|=64,ft&&(h.flags|=8192),ft=R.callbacks,ft===null?R.callbacks=[st]:ft.push(st))}else ft={lane:st,tag:ge.tag,payload:ge.payload,callback:ge.callback,next:null},xt===null?(it=xt=ft,De=vt):xt=xt.next=ft,Q|=st;if(ge=ge.next,ge===null){if(ge=R.shared.pending,ge===null)break;ft=ge,ge=ft.next,ft.next=null,R.lastBaseUpdate=ft,R.shared.pending=null}}while(!0);xt===null&&(De=vt),R.baseState=De,R.firstBaseUpdate=it,R.lastBaseUpdate=xt,F===null&&(R.shared.lanes=0),Gu|=Q,h.lanes=Q,h.memoizedState=vt}}function gF(h,g){if(typeof h!="function")throw Error(r(191,h));h.call(g)}function xF(h,g){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)gF(_[h],g)}var Lm=z(null),Nv=z(0);function bF(h,g){h=Cc,ne(Nv,h),ne(Lm,g),Cc=h|g.baseLanes}function E2(){ne(Nv,Cc),ne(Lm,Lm.current)}function A2(){Cc=Nv.current,G(Lm),G(Nv)}var Rs=z(null),lo=null;function Bu(h){var g=h.alternate;ne(fi,fi.current&1),ne(Rs,h),lo===null&&(g===null||Lm.current!==null||g.memoizedState!==null)&&(lo=h)}function T2(h){ne(fi,fi.current),ne(Rs,h),lo===null&&(lo=h)}function yF(h){h.tag===22?(ne(fi,fi.current),ne(Rs,h),lo===null&&(lo=h)):$u()}function $u(){ne(fi,fi.current),ne(Rs,Rs.current)}function Ds(h){G(Rs),lo===h&&(lo=null),G(fi)}var fi=z(0);function Ev(h){for(var g=h;g!==null;){if(g.tag===13){var _=g.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||DA(_)||OA(_)))return g}else if(g.tag===19&&(g.memoizedProps.revealOrder==="forwards"||g.memoizedProps.revealOrder==="backwards"||g.memoizedProps.revealOrder==="unstable_legacy-backwards"||g.memoizedProps.revealOrder==="together")){if((g.flags&128)!==0)return g}else if(g.child!==null){g.child.return=g,g=g.child;continue}if(g===h)break;for(;g.sibling===null;){if(g.return===null||g.return===h)return null;g=g.return}g.sibling.return=g.return,g=g.sibling}return null}var _c=0,yn=null,cr=null,vi=null,Av=!1,Im=!1,Lh=!1,Tv=0,Fx=0,Fm=null,$ae=0;function Zr(){throw Error(r(321))}function C2(h,g){if(g===null)return!1;for(var _=0;_F?F:8;var Q=K.T,ge={};K.T=ge,V2(h,!1,g,_);try{var De=R(),it=K.S;if(it!==null&&it(ge,De),De!==null&&typeof De=="object"&&typeof De.then=="function"){var xt=Bae(De,N);zx(h,g,xt,Is(h))}else zx(h,g,N,Is(h))}catch(vt){zx(h,g,{then:function(){},status:"rejected",reason:vt},Is())}finally{V.p=F,Q!==null&&ge.types!==null&&(Q.types=ge.types),K.T=Q}}function qae(){}function $2(h,g,_,N){if(h.tag!==5)throw Error(r(476));var R=XF(h).queue;KF(h,R,g,Z,_===null?qae:function(){return YF(h),_(N)})}function XF(h){var g=h.memoizedState;if(g!==null)return g;g={memoizedState:Z,baseState:Z,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wc,lastRenderedState:Z},next:null};var _={};return g.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:wc,lastRenderedState:_},next:null},h.memoizedState=g,h=h.alternate,h!==null&&(h.memoizedState=g),g}function YF(h){var g=XF(h);g.next===null&&(g=h.alternate.memoizedState),zx(h,g.next.queue,{},Is())}function H2(){return ha(rb)}function QF(){return mi().memoizedState}function ZF(){return mi().memoizedState}function Kae(h){for(var g=h.return;g!==null;){switch(g.tag){case 24:case 3:var _=Is();h=Uu(_);var N=zu(g,h,_);N!==null&&(ps(N,g,_),Ox(N,g,_)),g={cache:b2()},h.payload=g;return}g=g.return}}function Xae(h,g,_){var N=Is();_={lane:N,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},Dv(h)?e5(g,_):(_=o2(h,g,_,N),_!==null&&(ps(_,h,N),t5(_,g,N)))}function JF(h,g,_){var N=Is();zx(h,g,_,N)}function zx(h,g,_,N){var R={lane:N,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(Dv(h))e5(g,R);else{var F=h.alternate;if(h.lanes===0&&(F===null||F.lanes===0)&&(F=g.lastRenderedReducer,F!==null))try{var Q=g.lastRenderedState,ge=F(Q,_);if(R.hasEagerState=!0,R.eagerState=ge,js(ge,Q))return mv(h,g,R,0),vr===null&&fv(),!1}catch{}if(_=o2(h,g,R,N),_!==null)return ps(_,h,N),t5(_,g,N),!0}return!1}function V2(h,g,_,N){if(N={lane:2,revertLane:wA(),gesture:null,action:N,hasEagerState:!1,eagerState:null,next:null},Dv(h)){if(g)throw Error(r(479))}else g=o2(h,_,N,2),g!==null&&ps(g,h,2)}function Dv(h){var g=h.alternate;return h===yn||g!==null&&g===yn}function e5(h,g){Im=Av=!0;var _=h.pending;_===null?g.next=g:(g.next=_.next,_.next=g),h.pending=g}function t5(h,g,_){if((_&4194048)!==0){var N=g.lanes;N&=h.pendingLanes,_|=N,g.lanes=_,gt(h,_)}}var Bx={readContext:ha,use:kv,useCallback:Zr,useContext:Zr,useEffect:Zr,useImperativeHandle:Zr,useLayoutEffect:Zr,useInsertionEffect:Zr,useMemo:Zr,useReducer:Zr,useRef:Zr,useState:Zr,useDebugValue:Zr,useDeferredValue:Zr,useTransition:Zr,useSyncExternalStore:Zr,useId:Zr,useHostTransitionStatus:Zr,useFormState:Zr,useActionState:Zr,useOptimistic:Zr,useMemoCache:Zr,useCacheRefresh:Zr};Bx.useEffectEvent=Zr;var n5={readContext:ha,use:kv,useCallback:function(h,g){return Wa().memoizedState=[h,g===void 0?null:g],h},useContext:ha,useEffect:UF,useImperativeHandle:function(h,g,_){_=_!=null?_.concat([h]):null,Pv(4194308,4,HF.bind(null,g,h),_)},useLayoutEffect:function(h,g){return Pv(4194308,4,h,g)},useInsertionEffect:function(h,g){Pv(4,2,h,g)},useMemo:function(h,g){var _=Wa();g=g===void 0?null:g;var N=h();if(Lh){te(!0);try{h()}finally{te(!1)}}return _.memoizedState=[N,g],N},useReducer:function(h,g,_){var N=Wa();if(_!==void 0){var R=_(g);if(Lh){te(!0);try{_(g)}finally{te(!1)}}}else R=g;return N.memoizedState=N.baseState=R,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:R},N.queue=h,h=h.dispatch=Xae.bind(null,yn,h),[N.memoizedState,h]},useRef:function(h){var g=Wa();return h={current:h},g.memoizedState=h},useState:function(h){h=I2(h);var g=h.queue,_=JF.bind(null,yn,g);return g.dispatch=_,[h.memoizedState,_]},useDebugValue:z2,useDeferredValue:function(h,g){var _=Wa();return B2(_,h,g)},useTransition:function(){var h=I2(!1);return h=KF.bind(null,yn,h.queue,!0,!1),Wa().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,g,_){var N=yn,R=Wa();if(zn){if(_===void 0)throw Error(r(407));_=_()}else{if(_=g(),vr===null)throw Error(r(349));(In&127)!==0||SF(N,g,_)}R.memoizedState=_;var F={value:_,getSnapshot:g};return R.queue=F,UF(NF.bind(null,N,F,h),[h]),N.flags|=2048,Um(9,{destroy:void 0},MF.bind(null,N,F,_,g),null),_},useId:function(){var h=Wa(),g=vr.identifierPrefix;if(zn){var _=wl,N=_l;_=(N&~(1<<32-Me(N)-1)).toString(32)+_,g="_"+g+"R_"+_,_=Tv++,0<_&&(g+="H"+_.toString(32)),g+="_"}else _=$ae++,g="_"+g+"r_"+_.toString(32)+"_";return h.memoizedState=g},useHostTransitionStatus:H2,useFormState:DF,useActionState:DF,useOptimistic:function(h){var g=Wa();g.memoizedState=g.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return g.queue=_,g=V2.bind(null,yn,!0,_),_.dispatch=g,[h,g]},useMemoCache:D2,useCacheRefresh:function(){return Wa().memoizedState=Kae.bind(null,yn)},useEffectEvent:function(h){var g=Wa(),_={impl:h};return g.memoizedState=_,function(){if((Jn&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},G2={readContext:ha,use:kv,useCallback:GF,useContext:ha,useEffect:U2,useImperativeHandle:VF,useInsertionEffect:BF,useLayoutEffect:$F,useMemo:WF,useReducer:jv,useRef:FF,useState:function(){return jv(wc)},useDebugValue:z2,useDeferredValue:function(h,g){var _=mi();return qF(_,cr.memoizedState,h,g)},useTransition:function(){var h=jv(wc)[0],g=mi().memoizedState;return[typeof h=="boolean"?h:Ux(h),g]},useSyncExternalStore:wF,useId:QF,useHostTransitionStatus:H2,useFormState:OF,useActionState:OF,useOptimistic:function(h,g){var _=mi();return TF(_,cr,h,g)},useMemoCache:D2,useCacheRefresh:ZF};G2.useEffectEvent=zF;var r5={readContext:ha,use:kv,useCallback:GF,useContext:ha,useEffect:U2,useImperativeHandle:VF,useInsertionEffect:BF,useLayoutEffect:$F,useMemo:WF,useReducer:L2,useRef:FF,useState:function(){return L2(wc)},useDebugValue:z2,useDeferredValue:function(h,g){var _=mi();return cr===null?B2(_,h,g):qF(_,cr.memoizedState,h,g)},useTransition:function(){var h=L2(wc)[0],g=mi().memoizedState;return[typeof h=="boolean"?h:Ux(h),g]},useSyncExternalStore:wF,useId:QF,useHostTransitionStatus:H2,useFormState:IF,useActionState:IF,useOptimistic:function(h,g){var _=mi();return cr!==null?TF(_,cr,h,g):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:D2,useCacheRefresh:ZF};r5.useEffectEvent=zF;function W2(h,g,_,N){g=h.memoizedState,_=_(N,g),_=_==null?g:f({},g,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var q2={enqueueSetState:function(h,g,_){h=h._reactInternals;var N=Is(),R=Uu(N);R.payload=g,_!=null&&(R.callback=_),g=zu(h,R,N),g!==null&&(ps(g,h,N),Ox(g,h,N))},enqueueReplaceState:function(h,g,_){h=h._reactInternals;var N=Is(),R=Uu(N);R.tag=1,R.payload=g,_!=null&&(R.callback=_),g=zu(h,R,N),g!==null&&(ps(g,h,N),Ox(g,h,N))},enqueueForceUpdate:function(h,g){h=h._reactInternals;var _=Is(),N=Uu(_);N.tag=2,g!=null&&(N.callback=g),g=zu(h,N,_),g!==null&&(ps(g,h,_),Ox(g,h,_))}};function i5(h,g,_,N,R,F,Q){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(N,F,Q):g.prototype&&g.prototype.isPureReactComponent?!Ax(_,N)||!Ax(R,F):!0}function a5(h,g,_,N){h=g.state,typeof g.componentWillReceiveProps=="function"&&g.componentWillReceiveProps(_,N),typeof g.UNSAFE_componentWillReceiveProps=="function"&&g.UNSAFE_componentWillReceiveProps(_,N),g.state!==h&&q2.enqueueReplaceState(g,g.state,null)}function Ih(h,g){var _=g;if("ref"in g){_={};for(var N in g)N!=="ref"&&(_[N]=g[N])}if(h=h.defaultProps){_===g&&(_=f({},_));for(var R in h)_[R]===void 0&&(_[R]=h[R])}return _}function s5(h){hv(h)}function o5(h){console.error(h)}function l5(h){hv(h)}function Ov(h,g){try{var _=h.onUncaughtError;_(g.value,{componentStack:g.stack})}catch(N){setTimeout(function(){throw N})}}function c5(h,g,_){try{var N=h.onCaughtError;N(_.value,{componentStack:_.stack,errorBoundary:g.tag===1?g.stateNode:null})}catch(R){setTimeout(function(){throw R})}}function K2(h,g,_){return _=Uu(_),_.tag=3,_.payload={element:null},_.callback=function(){Ov(h,g)},_}function u5(h){return h=Uu(h),h.tag=3,h}function d5(h,g,_,N){var R=_.type.getDerivedStateFromError;if(typeof R=="function"){var F=N.value;h.payload=function(){return R(F)},h.callback=function(){c5(g,_,N)}}var Q=_.stateNode;Q!==null&&typeof Q.componentDidCatch=="function"&&(h.callback=function(){c5(g,_,N),typeof R!="function"&&(Wu===null?Wu=new Set([this]):Wu.add(this));var ge=N.stack;this.componentDidCatch(N.value,{componentStack:ge!==null?ge:""})})}function Yae(h,g,_,N,R){if(_.flags|=32768,N!==null&&typeof N=="object"&&typeof N.then=="function"){if(g=_.alternate,g!==null&&jm(g,_,R,!0),_=Rs.current,_!==null){switch(_.tag){case 31:case 13:return lo===null?qv():_.alternate===null&&Jr===0&&(Jr=3),_.flags&=-257,_.flags|=65536,_.lanes=R,N===wv?_.flags|=16384:(g=_.updateQueue,g===null?_.updateQueue=new Set([N]):g.add(N),yA(h,N,R)),!1;case 22:return _.flags|=65536,N===wv?_.flags|=16384:(g=_.updateQueue,g===null?(g={transitions:null,markerInstances:null,retryQueue:new Set([N])},_.updateQueue=g):(_=g.retryQueue,_===null?g.retryQueue=new Set([N]):_.add(N)),yA(h,N,R)),!1}throw Error(r(435,_.tag))}return yA(h,N,R),qv(),!1}if(zn)return g=Rs.current,g!==null?((g.flags&65536)===0&&(g.flags|=256),g.flags|=65536,g.lanes=R,N!==f2&&(h=Error(r(422),{cause:N}),kx(io(h,_)))):(N!==f2&&(g=Error(r(423),{cause:N}),kx(io(g,_))),h=h.current.alternate,h.flags|=65536,R&=-R,h.lanes|=R,N=io(N,_),R=K2(h.stateNode,N,R),M2(h,R),Jr!==4&&(Jr=2)),!1;var F=Error(r(520),{cause:N});if(F=io(F,_),Xx===null?Xx=[F]:Xx.push(F),Jr!==4&&(Jr=2),g===null)return!0;N=io(N,_),_=g;do{switch(_.tag){case 3:return _.flags|=65536,h=R&-R,_.lanes|=h,h=K2(_.stateNode,N,h),M2(_,h),!1;case 1:if(g=_.type,F=_.stateNode,(_.flags&128)===0&&(typeof g.getDerivedStateFromError=="function"||F!==null&&typeof F.componentDidCatch=="function"&&(Wu===null||!Wu.has(F))))return _.flags|=65536,R&=-R,_.lanes|=R,R=u5(R),d5(R,h,_,N),M2(_,R),!1}_=_.return}while(_!==null);return!1}var X2=Error(r(461)),_i=!1;function fa(h,g,_,N){g.child=h===null?pF(g,null,_,N):Oh(g,h.child,_,N)}function h5(h,g,_,N,R){_=_.render;var F=g.ref;if("ref"in N){var Q={};for(var ge in N)ge!=="ref"&&(Q[ge]=N[ge])}else Q=N;return jh(g),N=k2(h,g,_,Q,F,R),ge=j2(),h!==null&&!_i?(P2(h,g,R),Sc(h,g,R)):(zn&&ge&&d2(g),g.flags|=1,fa(h,g,N,R),g.child)}function f5(h,g,_,N,R){if(h===null){var F=_.type;return typeof F=="function"&&!l2(F)&&F.defaultProps===void 0&&_.compare===null?(g.tag=15,g.type=F,m5(h,g,F,N,R)):(h=gv(_.type,null,N,g,g.mode,R),h.ref=g.ref,h.return=g,g.child=h)}if(F=h.child,!rA(h,R)){var Q=F.memoizedProps;if(_=_.compare,_=_!==null?_:Ax,_(Q,N)&&h.ref===g.ref)return Sc(h,g,R)}return g.flags|=1,h=xc(F,N),h.ref=g.ref,h.return=g,g.child=h}function m5(h,g,_,N,R){if(h!==null){var F=h.memoizedProps;if(Ax(F,N)&&h.ref===g.ref)if(_i=!1,g.pendingProps=N=F,rA(h,R))(h.flags&131072)!==0&&(_i=!0);else return g.lanes=h.lanes,Sc(h,g,R)}return Y2(h,g,_,N,R)}function p5(h,g,_,N){var R=N.children,F=h!==null?h.memoizedState:null;if(h===null&&g.stateNode===null&&(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),N.mode==="hidden"){if((g.flags&128)!==0){if(F=F!==null?F.baseLanes|_:_,h!==null){for(N=g.child=h.child,R=0;N!==null;)R=R|N.lanes|N.childLanes,N=N.sibling;N=R&~F}else N=0,g.child=null;return g5(h,g,F,_,N)}if((_&536870912)!==0)g.memoizedState={baseLanes:0,cachePool:null},h!==null&&vv(g,F!==null?F.cachePool:null),F!==null?bF(g,F):E2(),yF(g);else return N=g.lanes=536870912,g5(h,g,F!==null?F.baseLanes|_:_,_,N)}else F!==null?(vv(g,F.cachePool),bF(g,F),$u(),g.memoizedState=null):(h!==null&&vv(g,null),E2(),$u());return fa(h,g,R,_),g.child}function $x(h,g){return h!==null&&h.tag===22||g.stateNode!==null||(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),g.sibling}function g5(h,g,_,N,R){var F=v2();return F=F===null?null:{parent:yi._currentValue,pool:F},g.memoizedState={baseLanes:_,cachePool:F},h!==null&&vv(g,null),E2(),yF(g),h!==null&&jm(h,g,N,!0),g.childLanes=R,null}function Lv(h,g){return g=Fv({mode:g.mode,children:g.children},h.mode),g.ref=h.ref,h.child=g,g.return=h,g}function x5(h,g,_){return Oh(g,h.child,null,_),h=Lv(g,g.pendingProps),h.flags|=2,Ds(g),g.memoizedState=null,h}function Qae(h,g,_){var N=g.pendingProps,R=(g.flags&128)!==0;if(g.flags&=-129,h===null){if(zn){if(N.mode==="hidden")return h=Lv(g,N),g.lanes=536870912,$x(null,h);if(T2(g),(h=Ar)?(h=CU(h,oo),h=h!==null&&h.data==="&"?h:null,h!==null&&(g.memoizedState={dehydrated:h,treeContext:Du!==null?{id:_l,overflow:wl}:null,retryLane:536870912,hydrationErrors:null},_=eF(h),_.return=g,g.child=_,da=g,Ar=null)):h=null,h===null)throw Lu(g);return g.lanes=536870912,null}return Lv(g,N)}var F=h.memoizedState;if(F!==null){var Q=F.dehydrated;if(T2(g),R)if(g.flags&256)g.flags&=-257,g=x5(h,g,_);else if(g.memoizedState!==null)g.child=h.child,g.flags|=128,g=null;else throw Error(r(558));else if(_i||jm(h,g,_,!1),R=(_&h.childLanes)!==0,_i||R){if(N=vr,N!==null&&(Q=Kt(N,_),Q!==0&&Q!==F.retryLane))throw F.retryLane=Q,Ah(h,Q),ps(N,h,Q),X2;qv(),g=x5(h,g,_)}else h=F.treeContext,Ar=co(Q.nextSibling),da=g,zn=!0,Ou=null,oo=!1,h!==null&&rF(g,h),g=Lv(g,N),g.flags|=4096;return g}return h=xc(h.child,{mode:N.mode,children:N.children}),h.ref=g.ref,g.child=h,h.return=g,h}function Iv(h,g){var _=g.ref;if(_===null)h!==null&&h.ref!==null&&(g.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(h===null||h.ref!==_)&&(g.flags|=4194816)}}function Y2(h,g,_,N,R){return jh(g),_=k2(h,g,_,N,void 0,R),N=j2(),h!==null&&!_i?(P2(h,g,R),Sc(h,g,R)):(zn&&N&&d2(g),g.flags|=1,fa(h,g,_,R),g.child)}function b5(h,g,_,N,R,F){return jh(g),g.updateQueue=null,_=_F(g,N,_,R),vF(h),N=j2(),h!==null&&!_i?(P2(h,g,F),Sc(h,g,F)):(zn&&N&&d2(g),g.flags|=1,fa(h,g,_,F),g.child)}function y5(h,g,_,N,R){if(jh(g),g.stateNode===null){var F=Am,Q=_.contextType;typeof Q=="object"&&Q!==null&&(F=ha(Q)),F=new _(N,F),g.memoizedState=F.state!==null&&F.state!==void 0?F.state:null,F.updater=q2,g.stateNode=F,F._reactInternals=g,F=g.stateNode,F.props=N,F.state=g.memoizedState,F.refs={},w2(g),Q=_.contextType,F.context=typeof Q=="object"&&Q!==null?ha(Q):Am,F.state=g.memoizedState,Q=_.getDerivedStateFromProps,typeof Q=="function"&&(W2(g,_,Q,N),F.state=g.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof F.getSnapshotBeforeUpdate=="function"||typeof F.UNSAFE_componentWillMount!="function"&&typeof F.componentWillMount!="function"||(Q=F.state,typeof F.componentWillMount=="function"&&F.componentWillMount(),typeof F.UNSAFE_componentWillMount=="function"&&F.UNSAFE_componentWillMount(),Q!==F.state&&q2.enqueueReplaceState(F,F.state,null),Ix(g,N,F,R),Lx(),F.state=g.memoizedState),typeof F.componentDidMount=="function"&&(g.flags|=4194308),N=!0}else if(h===null){F=g.stateNode;var ge=g.memoizedProps,De=Ih(_,ge);F.props=De;var it=F.context,xt=_.contextType;Q=Am,typeof xt=="object"&&xt!==null&&(Q=ha(xt));var vt=_.getDerivedStateFromProps;xt=typeof vt=="function"||typeof F.getSnapshotBeforeUpdate=="function",ge=g.pendingProps!==ge,xt||typeof F.UNSAFE_componentWillReceiveProps!="function"&&typeof F.componentWillReceiveProps!="function"||(ge||it!==Q)&&a5(g,F,N,Q),Fu=!1;var st=g.memoizedState;F.state=st,Ix(g,N,F,R),Lx(),it=g.memoizedState,ge||st!==it||Fu?(typeof vt=="function"&&(W2(g,_,vt,N),it=g.memoizedState),(De=Fu||i5(g,_,De,N,st,it,Q))?(xt||typeof F.UNSAFE_componentWillMount!="function"&&typeof F.componentWillMount!="function"||(typeof F.componentWillMount=="function"&&F.componentWillMount(),typeof F.UNSAFE_componentWillMount=="function"&&F.UNSAFE_componentWillMount()),typeof F.componentDidMount=="function"&&(g.flags|=4194308)):(typeof F.componentDidMount=="function"&&(g.flags|=4194308),g.memoizedProps=N,g.memoizedState=it),F.props=N,F.state=it,F.context=Q,N=De):(typeof F.componentDidMount=="function"&&(g.flags|=4194308),N=!1)}else{F=g.stateNode,S2(h,g),Q=g.memoizedProps,xt=Ih(_,Q),F.props=xt,vt=g.pendingProps,st=F.context,it=_.contextType,De=Am,typeof it=="object"&&it!==null&&(De=ha(it)),ge=_.getDerivedStateFromProps,(it=typeof ge=="function"||typeof F.getSnapshotBeforeUpdate=="function")||typeof F.UNSAFE_componentWillReceiveProps!="function"&&typeof F.componentWillReceiveProps!="function"||(Q!==vt||st!==De)&&a5(g,F,N,De),Fu=!1,st=g.memoizedState,F.state=st,Ix(g,N,F,R),Lx();var ft=g.memoizedState;Q!==vt||st!==ft||Fu||h!==null&&h.dependencies!==null&&bv(h.dependencies)?(typeof ge=="function"&&(W2(g,_,ge,N),ft=g.memoizedState),(xt=Fu||i5(g,_,xt,N,st,ft,De)||h!==null&&h.dependencies!==null&&bv(h.dependencies))?(it||typeof F.UNSAFE_componentWillUpdate!="function"&&typeof F.componentWillUpdate!="function"||(typeof F.componentWillUpdate=="function"&&F.componentWillUpdate(N,ft,De),typeof F.UNSAFE_componentWillUpdate=="function"&&F.UNSAFE_componentWillUpdate(N,ft,De)),typeof F.componentDidUpdate=="function"&&(g.flags|=4),typeof F.getSnapshotBeforeUpdate=="function"&&(g.flags|=1024)):(typeof F.componentDidUpdate!="function"||Q===h.memoizedProps&&st===h.memoizedState||(g.flags|=4),typeof F.getSnapshotBeforeUpdate!="function"||Q===h.memoizedProps&&st===h.memoizedState||(g.flags|=1024),g.memoizedProps=N,g.memoizedState=ft),F.props=N,F.state=ft,F.context=De,N=xt):(typeof F.componentDidUpdate!="function"||Q===h.memoizedProps&&st===h.memoizedState||(g.flags|=4),typeof F.getSnapshotBeforeUpdate!="function"||Q===h.memoizedProps&&st===h.memoizedState||(g.flags|=1024),N=!1)}return F=N,Iv(h,g),N=(g.flags&128)!==0,F||N?(F=g.stateNode,_=N&&typeof _.getDerivedStateFromError!="function"?null:F.render(),g.flags|=1,h!==null&&N?(g.child=Oh(g,h.child,null,R),g.child=Oh(g,null,_,R)):fa(h,g,_,R),g.memoizedState=F.state,h=g.child):h=Sc(h,g,R),h}function v5(h,g,_,N){return Ch(),g.flags|=256,fa(h,g,_,N),g.child}var Q2={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function Z2(h){return{baseLanes:h,cachePool:cF()}}function J2(h,g,_){return h=h!==null?h.childLanes&~_:0,g&&(h|=Ls),h}function _5(h,g,_){var N=g.pendingProps,R=!1,F=(g.flags&128)!==0,Q;if((Q=F)||(Q=h!==null&&h.memoizedState===null?!1:(fi.current&2)!==0),Q&&(R=!0,g.flags&=-129),Q=(g.flags&32)!==0,g.flags&=-33,h===null){if(zn){if(R?Bu(g):$u(),(h=Ar)?(h=CU(h,oo),h=h!==null&&h.data!=="&"?h:null,h!==null&&(g.memoizedState={dehydrated:h,treeContext:Du!==null?{id:_l,overflow:wl}:null,retryLane:536870912,hydrationErrors:null},_=eF(h),_.return=g,g.child=_,da=g,Ar=null)):h=null,h===null)throw Lu(g);return OA(h)?g.lanes=32:g.lanes=536870912,null}var ge=N.children;return N=N.fallback,R?($u(),R=g.mode,ge=Fv({mode:"hidden",children:ge},R),N=Th(N,R,_,null),ge.return=g,N.return=g,ge.sibling=N,g.child=ge,N=g.child,N.memoizedState=Z2(_),N.childLanes=J2(h,Q,_),g.memoizedState=Q2,$x(null,N)):(Bu(g),eA(g,ge))}var De=h.memoizedState;if(De!==null&&(ge=De.dehydrated,ge!==null)){if(F)g.flags&256?(Bu(g),g.flags&=-257,g=tA(h,g,_)):g.memoizedState!==null?($u(),g.child=h.child,g.flags|=128,g=null):($u(),ge=N.fallback,R=g.mode,N=Fv({mode:"visible",children:N.children},R),ge=Th(ge,R,_,null),ge.flags|=2,N.return=g,ge.return=g,N.sibling=ge,g.child=N,Oh(g,h.child,null,_),N=g.child,N.memoizedState=Z2(_),N.childLanes=J2(h,Q,_),g.memoizedState=Q2,g=$x(null,N));else if(Bu(g),OA(ge)){if(Q=ge.nextSibling&&ge.nextSibling.dataset,Q)var it=Q.dgst;Q=it,N=Error(r(419)),N.stack="",N.digest=Q,kx({value:N,source:null,stack:null}),g=tA(h,g,_)}else if(_i||jm(h,g,_,!1),Q=(_&h.childLanes)!==0,_i||Q){if(Q=vr,Q!==null&&(N=Kt(Q,_),N!==0&&N!==De.retryLane))throw De.retryLane=N,Ah(h,N),ps(Q,h,N),X2;DA(ge)||qv(),g=tA(h,g,_)}else DA(ge)?(g.flags|=192,g.child=h.child,g=null):(h=De.treeContext,Ar=co(ge.nextSibling),da=g,zn=!0,Ou=null,oo=!1,h!==null&&rF(g,h),g=eA(g,N.children),g.flags|=4096);return g}return R?($u(),ge=N.fallback,R=g.mode,De=h.child,it=De.sibling,N=xc(De,{mode:"hidden",children:N.children}),N.subtreeFlags=De.subtreeFlags&65011712,it!==null?ge=xc(it,ge):(ge=Th(ge,R,_,null),ge.flags|=2),ge.return=g,N.return=g,N.sibling=ge,g.child=N,$x(null,N),N=g.child,ge=h.child.memoizedState,ge===null?ge=Z2(_):(R=ge.cachePool,R!==null?(De=yi._currentValue,R=R.parent!==De?{parent:De,pool:De}:R):R=cF(),ge={baseLanes:ge.baseLanes|_,cachePool:R}),N.memoizedState=ge,N.childLanes=J2(h,Q,_),g.memoizedState=Q2,$x(h.child,N)):(Bu(g),_=h.child,h=_.sibling,_=xc(_,{mode:"visible",children:N.children}),_.return=g,_.sibling=null,h!==null&&(Q=g.deletions,Q===null?(g.deletions=[h],g.flags|=16):Q.push(h)),g.child=_,g.memoizedState=null,_)}function eA(h,g){return g=Fv({mode:"visible",children:g},h.mode),g.return=h,h.child=g}function Fv(h,g){return h=Ps(22,h,null,g),h.lanes=0,h}function tA(h,g,_){return Oh(g,h.child,null,_),h=eA(g,g.pendingProps.children),h.flags|=2,g.memoizedState=null,h}function w5(h,g,_){h.lanes|=g;var N=h.alternate;N!==null&&(N.lanes|=g),g2(h.return,g,_)}function nA(h,g,_,N,R,F){var Q=h.memoizedState;Q===null?h.memoizedState={isBackwards:g,rendering:null,renderingStartTime:0,last:N,tail:_,tailMode:R,treeForkCount:F}:(Q.isBackwards=g,Q.rendering=null,Q.renderingStartTime=0,Q.last=N,Q.tail=_,Q.tailMode=R,Q.treeForkCount=F)}function S5(h,g,_){var N=g.pendingProps,R=N.revealOrder,F=N.tail;N=N.children;var Q=fi.current,ge=(Q&2)!==0;if(ge?(Q=Q&1|2,g.flags|=128):Q&=1,ne(fi,Q),fa(h,g,N,_),N=zn?Cx:0,!ge&&h!==null&&(h.flags&128)!==0)e:for(h=g.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&w5(h,_,g);else if(h.tag===19)w5(h,_,g);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===g)break e;for(;h.sibling===null;){if(h.return===null||h.return===g)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch(R){case"forwards":for(_=g.child,R=null;_!==null;)h=_.alternate,h!==null&&Ev(h)===null&&(R=_),_=_.sibling;_=R,_===null?(R=g.child,g.child=null):(R=_.sibling,_.sibling=null),nA(g,!1,R,_,F,N);break;case"backwards":case"unstable_legacy-backwards":for(_=null,R=g.child,g.child=null;R!==null;){if(h=R.alternate,h!==null&&Ev(h)===null){g.child=R;break}h=R.sibling,R.sibling=_,_=R,R=h}nA(g,!0,_,null,F,N);break;case"together":nA(g,!1,null,null,void 0,N);break;default:g.memoizedState=null}return g.child}function Sc(h,g,_){if(h!==null&&(g.dependencies=h.dependencies),Gu|=g.lanes,(_&g.childLanes)===0)if(h!==null){if(jm(h,g,_,!1),(_&g.childLanes)===0)return null}else return null;if(h!==null&&g.child!==h.child)throw Error(r(153));if(g.child!==null){for(h=g.child,_=xc(h,h.pendingProps),g.child=_,_.return=g;h.sibling!==null;)h=h.sibling,_=_.sibling=xc(h,h.pendingProps),_.return=g;_.sibling=null}return g.child}function rA(h,g){return(h.lanes&g)!==0?!0:(h=h.dependencies,!!(h!==null&&bv(h)))}function Zae(h,g,_){switch(g.tag){case 3:Ae(g,g.stateNode.containerInfo),Iu(g,yi,h.memoizedState.cache),Ch();break;case 27:case 5:Ne(g);break;case 4:Ae(g,g.stateNode.containerInfo);break;case 10:Iu(g,g.type,g.memoizedProps.value);break;case 31:if(g.memoizedState!==null)return g.flags|=128,T2(g),null;break;case 13:var N=g.memoizedState;if(N!==null)return N.dehydrated!==null?(Bu(g),g.flags|=128,null):(_&g.child.childLanes)!==0?_5(h,g,_):(Bu(g),h=Sc(h,g,_),h!==null?h.sibling:null);Bu(g);break;case 19:var R=(h.flags&128)!==0;if(N=(_&g.childLanes)!==0,N||(jm(h,g,_,!1),N=(_&g.childLanes)!==0),R){if(N)return S5(h,g,_);g.flags|=128}if(R=g.memoizedState,R!==null&&(R.rendering=null,R.tail=null,R.lastEffect=null),ne(fi,fi.current),N)break;return null;case 22:return g.lanes=0,p5(h,g,_,g.pendingProps);case 24:Iu(g,yi,h.memoizedState.cache)}return Sc(h,g,_)}function M5(h,g,_){if(h!==null)if(h.memoizedProps!==g.pendingProps)_i=!0;else{if(!rA(h,_)&&(g.flags&128)===0)return _i=!1,Zae(h,g,_);_i=(h.flags&131072)!==0}else _i=!1,zn&&(g.flags&1048576)!==0&&nF(g,Cx,g.index);switch(g.lanes=0,g.tag){case 16:e:{var N=g.pendingProps;if(h=Rh(g.elementType),g.type=h,typeof h=="function")l2(h)?(N=Ih(h,N),g.tag=1,g=y5(null,g,h,N,_)):(g.tag=0,g=Y2(null,g,h,N,_));else{if(h!=null){var R=h.$$typeof;if(R===A){g.tag=11,g=h5(null,g,h,N,_);break e}else if(R===E){g.tag=14,g=f5(null,g,h,N,_);break e}}throw g=j(h)||h,Error(r(306,g,""))}}return g;case 0:return Y2(h,g,g.type,g.pendingProps,_);case 1:return N=g.type,R=Ih(N,g.pendingProps),y5(h,g,N,R,_);case 3:e:{if(Ae(g,g.stateNode.containerInfo),h===null)throw Error(r(387));N=g.pendingProps;var F=g.memoizedState;R=F.element,S2(h,g),Ix(g,N,null,_);var Q=g.memoizedState;if(N=Q.cache,Iu(g,yi,N),N!==F.cache&&x2(g,[yi],_,!0),Lx(),N=Q.element,F.isDehydrated)if(F={element:N,isDehydrated:!1,cache:Q.cache},g.updateQueue.baseState=F,g.memoizedState=F,g.flags&256){g=v5(h,g,N,_);break e}else if(N!==R){R=io(Error(r(424)),g),kx(R),g=v5(h,g,N,_);break e}else for(h=g.stateNode.containerInfo,h.nodeType===9?h=h.body:h=h.nodeName==="HTML"?h.ownerDocument.body:h,Ar=co(h.firstChild),da=g,zn=!0,Ou=null,oo=!0,_=pF(g,null,N,_),g.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling;else{if(Ch(),N===R){g=Sc(h,g,_);break e}fa(h,g,N,_)}g=g.child}return g;case 26:return Iv(h,g),h===null?(_=OU(g.type,null,g.pendingProps,null))?g.memoizedState=_:zn||(_=g.type,h=g.pendingProps,N=e_(me.current).createElement(_),N[sn]=g,N[Rr]=h,ma(N,_,h),de(N),g.stateNode=N):g.memoizedState=OU(g.type,h.memoizedProps,g.pendingProps,h.memoizedState),null;case 27:return Ne(g),h===null&&zn&&(N=g.stateNode=PU(g.type,g.pendingProps,me.current),da=g,oo=!0,R=Ar,Yu(g.type)?(LA=R,Ar=co(N.firstChild)):Ar=R),fa(h,g,g.pendingProps.children,_),Iv(h,g),h===null&&(g.flags|=4194304),g.child;case 5:return h===null&&zn&&((R=N=Ar)&&(N=Tse(N,g.type,g.pendingProps,oo),N!==null?(g.stateNode=N,da=g,Ar=co(N.firstChild),oo=!1,R=!0):R=!1),R||Lu(g)),Ne(g),R=g.type,F=g.pendingProps,Q=h!==null?h.memoizedProps:null,N=F.children,jA(R,F)?N=null:Q!==null&&jA(R,Q)&&(g.flags|=32),g.memoizedState!==null&&(R=k2(h,g,Hae,null,null,_),rb._currentValue=R),Iv(h,g),fa(h,g,N,_),g.child;case 6:return h===null&&zn&&((h=_=Ar)&&(_=Cse(_,g.pendingProps,oo),_!==null?(g.stateNode=_,da=g,Ar=null,h=!0):h=!1),h||Lu(g)),null;case 13:return _5(h,g,_);case 4:return Ae(g,g.stateNode.containerInfo),N=g.pendingProps,h===null?g.child=Oh(g,null,N,_):fa(h,g,N,_),g.child;case 11:return h5(h,g,g.type,g.pendingProps,_);case 7:return fa(h,g,g.pendingProps,_),g.child;case 8:return fa(h,g,g.pendingProps.children,_),g.child;case 12:return fa(h,g,g.pendingProps.children,_),g.child;case 10:return N=g.pendingProps,Iu(g,g.type,N.value),fa(h,g,N.children,_),g.child;case 9:return R=g.type._context,N=g.pendingProps.children,jh(g),R=ha(R),N=N(R),g.flags|=1,fa(h,g,N,_),g.child;case 14:return f5(h,g,g.type,g.pendingProps,_);case 15:return m5(h,g,g.type,g.pendingProps,_);case 19:return S5(h,g,_);case 31:return Qae(h,g,_);case 22:return p5(h,g,_,g.pendingProps);case 24:return jh(g),N=ha(yi),h===null?(R=v2(),R===null&&(R=vr,F=b2(),R.pooledCache=F,F.refCount++,F!==null&&(R.pooledCacheLanes|=_),R=F),g.memoizedState={parent:N,cache:R},w2(g),Iu(g,yi,R)):((h.lanes&_)!==0&&(S2(h,g),Ix(g,null,null,_),Lx()),R=h.memoizedState,F=g.memoizedState,R.parent!==N?(R={parent:N,cache:N},g.memoizedState=R,g.lanes===0&&(g.memoizedState=g.updateQueue.baseState=R),Iu(g,yi,N)):(N=F.cache,Iu(g,yi,N),N!==R.cache&&x2(g,[yi],_,!0))),fa(h,g,g.pendingProps.children,_),g.child;case 29:throw g.pendingProps}throw Error(r(156,g.tag))}function Mc(h){h.flags|=4}function iA(h,g,_,N,R){if((g=(h.mode&32)!==0)&&(g=!1),g){if(h.flags|=16777216,(R&335544128)===R)if(h.stateNode.complete)h.flags|=8192;else if(Q5())h.flags|=8192;else throw Dh=wv,_2}else h.flags&=-16777217}function N5(h,g){if(g.type!=="stylesheet"||(g.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!zU(g))if(Q5())h.flags|=8192;else throw Dh=wv,_2}function Uv(h,g){g!==null&&(h.flags|=4),h.flags&16384&&(g=h.tag!==22?kt():536870912,h.lanes|=g,Hm|=g)}function Hx(h,g){if(!zn)switch(h.tailMode){case"hidden":g=h.tail;for(var _=null;g!==null;)g.alternate!==null&&(_=g),g=g.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var N=null;_!==null;)_.alternate!==null&&(N=_),_=_.sibling;N===null?g||h.tail===null?h.tail=null:h.tail.sibling=null:N.sibling=null}}function Tr(h){var g=h.alternate!==null&&h.alternate.child===h.child,_=0,N=0;if(g)for(var R=h.child;R!==null;)_|=R.lanes|R.childLanes,N|=R.subtreeFlags&65011712,N|=R.flags&65011712,R.return=h,R=R.sibling;else for(R=h.child;R!==null;)_|=R.lanes|R.childLanes,N|=R.subtreeFlags,N|=R.flags,R.return=h,R=R.sibling;return h.subtreeFlags|=N,h.childLanes=_,g}function Jae(h,g,_){var N=g.pendingProps;switch(h2(g),g.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Tr(g),null;case 1:return Tr(g),null;case 3:return _=g.stateNode,N=null,h!==null&&(N=h.memoizedState.cache),g.memoizedState.cache!==N&&(g.flags|=2048),vc(yi),pe(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(km(g)?Mc(g):h===null||h.memoizedState.isDehydrated&&(g.flags&256)===0||(g.flags|=1024,m2())),Tr(g),null;case 26:var R=g.type,F=g.memoizedState;return h===null?(Mc(g),F!==null?(Tr(g),N5(g,F)):(Tr(g),iA(g,R,null,N,_))):F?F!==h.memoizedState?(Mc(g),Tr(g),N5(g,F)):(Tr(g),g.flags&=-16777217):(h=h.memoizedProps,h!==N&&Mc(g),Tr(g),iA(g,R,h,N,_)),null;case 27:if(fe(g),_=me.current,R=g.type,h!==null&&g.stateNode!=null)h.memoizedProps!==N&&Mc(g);else{if(!N){if(g.stateNode===null)throw Error(r(166));return Tr(g),null}h=ie.current,km(g)?iF(g):(h=PU(R,N,_),g.stateNode=h,Mc(g))}return Tr(g),null;case 5:if(fe(g),R=g.type,h!==null&&g.stateNode!=null)h.memoizedProps!==N&&Mc(g);else{if(!N){if(g.stateNode===null)throw Error(r(166));return Tr(g),null}if(F=ie.current,km(g))iF(g);else{var Q=e_(me.current);switch(F){case 1:F=Q.createElementNS("http://www.w3.org/2000/svg",R);break;case 2:F=Q.createElementNS("http://www.w3.org/1998/Math/MathML",R);break;default:switch(R){case"svg":F=Q.createElementNS("http://www.w3.org/2000/svg",R);break;case"math":F=Q.createElementNS("http://www.w3.org/1998/Math/MathML",R);break;case"script":F=Q.createElement("div"),F.innerHTML=" +