From 8639f390285e61c0fa7cd4d376e60565726b4378 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 4 Jan 2026 09:10:06 +0100 Subject: [PATCH] Add resizable printer cards and Queue Only mode Features: - Resizable printer cards (S/M/L/XL) with toolbar controls on Printers page - Queue Only mode for staging prints without automatic scheduling - "Queue Only" option in add/edit queue modals - Purple "Staged" badge and Play button to release to queue - manual_start field in database with migration Fixes: - Improved camera stream stuck detection with automatic reconnection Tests: - Added 16 integration tests for print queue API endpoints --- CHANGELOG.md | 22 + README.md | 2 + backend/app/api/routes/camera.py | 4 +- backend/app/api/routes/print_queue.py | 32 + backend/app/api/routes/settings.py | 2 + backend/app/core/database.py | 6 + backend/app/main.py | 2 +- backend/app/models/print_queue.py | 1 + backend/app/schemas/print_queue.py | 3 + backend/app/services/discovery.py | 2 +- backend/app/services/print_scheduler.py | 4 + backend/app/services/smart_plug_manager.py | 14 +- backend/app/services/spoolman.py | 4 +- .../tests/integration/test_print_queue_api.py | 446 ++++++++++ frontend/src/api/client.ts | 5 + frontend/src/components/AddToQueueModal.tsx | 27 +- .../src/components/EditQueueItemModal.tsx | 33 +- frontend/src/pages/QueuePage.tsx | 34 +- .../{index-BWv5Xlm9.js => index-DXIzheUf.js} | 816 +++++++++--------- static/assets/index-YKsbWJQ9.css | 1 + static/assets/index-ta6Ao_MI.css | 1 - static/index.html | 4 +- 22 files changed, 1023 insertions(+), 442 deletions(-) create mode 100644 backend/tests/integration/test_print_queue_api.py rename static/assets/{index-BWv5Xlm9.js => index-DXIzheUf.js} (50%) create mode 100644 static/assets/index-YKsbWJQ9.css delete mode 100644 static/assets/index-ta6Ao_MI.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7a201bf..7a8c885f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to Bambuddy will be documented in this file. +## [0.1.6b6] - 2026-01-04 + +### Added +- **Resizable printer cards** - Adjust printer card size from the Printers page toolbar: + - Four sizes: Small, Medium (default), Large, XL + - Plus/minus buttons in toolbar header + - Size preference saved to localStorage + - Responsive grid adapts to selected size +- **Queue Only mode** - Stage prints without automatic scheduling: + - New "Queue Only" option when adding prints to queue + - Staged prints show purple "Staged" badge + - Play button to manually release staged prints to the queue + - Edit queue items to switch between ASAP, Scheduled, and Queue Only modes + - Useful for preparing print batches before activating + +### Fixed +- **Camera stream reconnection** - Improved detection of stuck camera streams with automatic reconnection + +### Tests +- Added integration tests for print queue API endpoints (16 new tests) +- Tests cover queue CRUD, manual_start flag, and start/cancel endpoints + ## [0.1.6b5] - 2026-01-02 ### Added diff --git a/README.md b/README.md index f68aa4357..a0a6c4195 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ - Live camera streaming (MJPEG) & snapshots - Fan status monitoring (part cooling, auxiliary, chamber) - Printer control (stop, pause, resume) +- Resizable printer cards (S/M/L/XL) - Skip objects during print - AMS slot RFID re-read - HMS error monitoring with history @@ -68,6 +69,7 @@ ### ⏰ Scheduling & Automation - Print queue with drag-and-drop - Scheduled prints (date/time) +- Queue Only mode (stage without auto-start) - Smart plug integration (Tasmota) - Energy consumption tracking - Auto power-on before print diff --git a/backend/app/api/routes/camera.py b/backend/app/api/routes/camera.py index 4c3bdcb43..70de2c24f 100644 --- a/backend/app/api/routes/camera.py +++ b/backend/app/api/routes/camera.py @@ -167,7 +167,7 @@ async def generate_rtsp_mjpeg_stream( ffmpeg = get_ffmpeg_path() if not ffmpeg: logger.error("ffmpeg not found - camera streaming requires ffmpeg") - yield (b"--frame\r\n" b"Content-Type: text/plain\r\n\r\n" b"Error: ffmpeg not installed\r\n") + yield (b"--frame\r\nContent-Type: text/plain\r\n\r\nError: ffmpeg not installed\r\n") return port = get_camera_port(model) @@ -303,7 +303,7 @@ async def generate_rtsp_mjpeg_stream( except FileNotFoundError: logger.error("ffmpeg not found - camera streaming requires ffmpeg") - yield (b"--frame\r\n" b"Content-Type: text/plain\r\n\r\n" b"Error: ffmpeg not installed\r\n") + yield (b"--frame\r\nContent-Type: text/plain\r\n\r\nError: ffmpeg not installed\r\n") except asyncio.CancelledError: logger.info(f"Camera stream task cancelled (stream_id={stream_id})") except GeneratorExit: diff --git a/backend/app/api/routes/print_queue.py b/backend/app/api/routes/print_queue.py index c975a28b3..a7460d5b1 100644 --- a/backend/app/api/routes/print_queue.py +++ b/backend/app/api/routes/print_queue.py @@ -89,6 +89,7 @@ async def add_to_queue( scheduled_time=data.scheduled_time, require_previous_success=data.require_previous_success, auto_off_after=data.auto_off_after, + manual_start=data.manual_start, position=max_pos + 1, status="pending", ) @@ -272,3 +273,34 @@ async def stop_queue_item( asyncio.create_task(cooldown_and_poweroff()) return {"message": "Print stopped" if stop_sent else "Queue item cancelled (printer was offline)"} + + +@router.post("/{item_id}/start") +async def start_queue_item( + item_id: int, + db: AsyncSession = Depends(get_db), +): + """Manually start a staged (manual_start) queue item. + + This clears the manual_start flag so the scheduler will pick it up, + or starts immediately if the printer is ready. + """ + result = await db.execute( + select(PrintQueueItem) + .options(selectinload(PrintQueueItem.archive), selectinload(PrintQueueItem.printer)) + .where(PrintQueueItem.id == item_id) + ) + item = result.scalar_one_or_none() + if not item: + raise HTTPException(404, "Queue item not found") + + if item.status != "pending": + raise HTTPException(400, f"Can only start pending items, current status: '{item.status}'") + + # Clear manual_start flag so scheduler picks it up + item.manual_start = False + await db.commit() + await db.refresh(item, ["archive", "printer"]) + + logger.info(f"Manually started queue item {item_id} (cleared manual_start flag)") + return _enrich_response(item) diff --git a/backend/app/api/routes/settings.py b/backend/app/api/routes/settings.py index 7ec9d22d5..8ccfcf701 100644 --- a/backend/app/api/routes/settings.py +++ b/backend/app/api/routes/settings.py @@ -478,6 +478,7 @@ async def export_backup( "scheduled_time": qi.scheduled_time.isoformat() if qi.scheduled_time else None, "require_previous_success": qi.require_previous_success, "auto_off_after": qi.auto_off_after, + "manual_start": qi.manual_start, "status": qi.status, "started_at": qi.started_at.isoformat() if qi.started_at else None, "completed_at": qi.completed_at.isoformat() if qi.completed_at else None, @@ -1468,6 +1469,7 @@ async def import_backup( position=qi_data.get("position", 0), require_previous_success=qi_data.get("require_previous_success", False), auto_off_after=qi_data.get("auto_off_after", False), + manual_start=qi_data.get("manual_start", False), status=qi_data.get("status", "pending"), error_message=qi_data.get("error_message"), ) diff --git a/backend/app/core/database.py b/backend/app/core/database.py index cba9269c4..91b65cb36 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -381,6 +381,12 @@ async def run_migrations(conn): except Exception: pass + # Migration: Add manual_start column to print_queue for staged prints + try: + await conn.execute(text("ALTER TABLE print_queue ADD COLUMN manual_start BOOLEAN DEFAULT 0")) + except Exception: + pass + async def seed_notification_templates(): """Seed default notification templates if they don't exist.""" diff --git a/backend/app/main.py b/backend/app/main.py index 4e6479898..4e8fe1025 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1616,7 +1616,7 @@ async def track_printer_runtime(): needs_commit = True logger.debug( f"[{printer.name}] Runtime tracking: added {int(elapsed)}s, " - f"total={printer.runtime_seconds}s ({printer.runtime_seconds/3600:.2f}h)" + f"total={printer.runtime_seconds}s ({printer.runtime_seconds / 3600:.2f}h)" ) else: logger.warning( diff --git a/backend/app/models/print_queue.py b/backend/app/models/print_queue.py index be4fe4a16..7130a885e 100644 --- a/backend/app/models/print_queue.py +++ b/backend/app/models/print_queue.py @@ -21,6 +21,7 @@ class PrintQueueItem(Base): # Scheduling position: Mapped[int] = mapped_column(Integer, default=0) # Queue order scheduled_time: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) # None = ASAP + manual_start: Mapped[bool] = mapped_column(Boolean, default=False) # Requires manual trigger to start # Conditions require_previous_success: Mapped[bool] = mapped_column(Boolean, default=False) diff --git a/backend/app/schemas/print_queue.py b/backend/app/schemas/print_queue.py index 1a05eae13..5f7cd9a07 100644 --- a/backend/app/schemas/print_queue.py +++ b/backend/app/schemas/print_queue.py @@ -21,6 +21,7 @@ class PrintQueueItemCreate(BaseModel): scheduled_time: datetime | None = None # None = ASAP (next when idle) require_previous_success: bool = False auto_off_after: bool = False # Power off printer after print completes + manual_start: bool = False # Requires manual trigger to start (staged) class PrintQueueItemUpdate(BaseModel): @@ -29,6 +30,7 @@ class PrintQueueItemUpdate(BaseModel): scheduled_time: datetime | None = None require_previous_success: bool | None = None auto_off_after: bool | None = None + manual_start: bool | None = None class PrintQueueItemResponse(BaseModel): @@ -39,6 +41,7 @@ class PrintQueueItemResponse(BaseModel): scheduled_time: UTCDatetime require_previous_success: bool auto_off_after: bool + manual_start: bool status: Literal["pending", "printing", "completed", "failed", "skipped", "cancelled"] started_at: UTCDatetime completed_at: UTCDatetime diff --git a/backend/app/services/discovery.py b/backend/app/services/discovery.py index 8872ae7e4..1a849f857 100644 --- a/backend/app/services/discovery.py +++ b/backend/app/services/discovery.py @@ -566,7 +566,7 @@ class TasmotaScanner: try: await asyncio.gather(*tasks, return_exceptions=True) except Exception as e: - logger.warning(f"Batch {i//batch_size} error: {e}") + logger.warning(f"Batch {i // batch_size} error: {e}") self._scanned = min(i + batch_size, len(hosts)) logger.info(f"Tasmota scan complete. Found {len(self._discovered)} devices.") diff --git a/backend/app/services/print_scheduler.py b/backend/app/services/print_scheduler.py index 0680bcc56..72ff455e1 100644 --- a/backend/app/services/print_scheduler.py +++ b/backend/app/services/print_scheduler.py @@ -72,6 +72,10 @@ class PrintScheduler: if item.scheduled_time and item.scheduled_time > datetime.utcnow(): continue + # Skip items that require manual start + 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) diff --git a/backend/app/services/smart_plug_manager.py b/backend/app/services/smart_plug_manager.py index 4a288c020..1b24d8a3c 100644 --- a/backend/app/services/smart_plug_manager.py +++ b/backend/app/services/smart_plug_manager.py @@ -162,9 +162,7 @@ class SmartPlugManager: ) return - logger.info( - f"Print completed successfully on printer {printer_id}, " f"scheduling turn-off for plug '{plug.name}'" - ) + logger.info(f"Print completed successfully on printer {printer_id}, scheduling turn-off for plug '{plug.name}'") if plug.off_delay_mode == "time": self._schedule_delayed_off(plug, printer_id, plug.off_delay_minutes * 60) @@ -227,7 +225,7 @@ class SmartPlugManager: # Cancel any existing task for this plug self._cancel_pending_off(plug.id) - logger.info(f"Scheduling temperature-based turn-off for plug '{plug.name}' " f"(threshold: {temp_threshold}°C)") + logger.info(f"Scheduling temperature-based turn-off for plug '{plug.name}' (threshold: {temp_threshold}°C)") # Mark as pending in database (survives restarts) asyncio.create_task(self._mark_auto_off_pending(plug.id, True)) @@ -281,9 +279,7 @@ class SmartPlugManager: f"threshold={temp_threshold}°C" ) else: - logger.info( - f"Temp check plug {plug_id}: nozzle={nozzle_temp}°C, " f"threshold={temp_threshold}°C" - ) + logger.info(f"Temp check plug {plug_id}: nozzle={nozzle_temp}°C, threshold={temp_threshold}°C") if max_nozzle_temp < temp_threshold: # All nozzles are below threshold, turn off @@ -398,7 +394,7 @@ class SmartPlugManager: elapsed = (datetime.utcnow() - plug.auto_off_pending_since).total_seconds() if elapsed > 7200: # 2 hours logger.warning( - f"Auto-off for plug '{plug.name}' was pending for {elapsed/60:.0f} minutes, " + f"Auto-off for plug '{plug.name}' was pending for {elapsed / 60:.0f} minutes, " f"clearing stale pending state" ) plug.auto_off_pending = False @@ -406,7 +402,7 @@ class SmartPlugManager: await db.commit() continue - logger.info(f"Resuming pending auto-off for plug '{plug.name}' " f"(printer {plug.printer_id})") + logger.info(f"Resuming pending auto-off for plug '{plug.name}' (printer {plug.printer_id})") # Resume the appropriate off mode if plug.off_delay_mode == "temperature": diff --git a/backend/app/services/spoolman.py b/backend/app/services/spoolman.py index 14cac485f..a3135fb13 100644 --- a/backend/app/services/spoolman.py +++ b/backend/app/services/spoolman.py @@ -577,9 +577,7 @@ class SpoolmanClient: ) # Spool not found - auto-create it - logger.info( - f"Creating new spool in Spoolman for {tray.tray_sub_brands} " f"(tray_uuid: {tray.tray_uuid[:16]}...)" - ) + logger.info(f"Creating new spool in Spoolman for {tray.tray_sub_brands} (tray_uuid: {tray.tray_uuid[:16]}...)") # First find or create the filament type filament = await self._find_or_create_filament(tray) diff --git a/backend/tests/integration/test_print_queue_api.py b/backend/tests/integration/test_print_queue_api.py new file mode 100644 index 000000000..a97a389d2 --- /dev/null +++ b/backend/tests/integration/test_print_queue_api.py @@ -0,0 +1,446 @@ +"""Integration tests for Print Queue API endpoints.""" + +import pytest +from httpx import AsyncClient + + +class TestPrintQueueAPI: + """Integration tests for /api/v1/queue endpoints.""" + + @pytest.fixture + async def printer_factory(self, db_session): + """Factory to create test printers.""" + _counter = [0] + + async def _create_printer(**kwargs): + from backend.app.models.printer import Printer + + _counter[0] += 1 + counter = _counter[0] + + defaults = { + "name": f"Test Printer {counter}", + "ip_address": f"192.168.1.{100 + counter}", + "serial_number": f"TESTSERIAL{counter:04d}", + "access_code": "12345678", + "model": "X1C", + } + defaults.update(kwargs) + + printer = Printer(**defaults) + db_session.add(printer) + await db_session.commit() + await db_session.refresh(printer) + return printer + + return _create_printer + + @pytest.fixture + async def archive_factory(self, db_session): + """Factory to create test archives.""" + _counter = [0] + + async def _create_archive(**kwargs): + from backend.app.models.archive import PrintArchive + + _counter[0] += 1 + counter = _counter[0] + + defaults = { + "filename": f"test_print_{counter}.3mf", + "print_name": f"Test Print {counter}", + "file_path": f"/tmp/test_print_{counter}.3mf", + "file_size": 1024, + "content_hash": f"testhash{counter:08d}", + "status": "completed", + } + defaults.update(kwargs) + + archive = PrintArchive(**defaults) + db_session.add(archive) + await db_session.commit() + await db_session.refresh(archive) + return archive + + return _create_archive + + @pytest.fixture + async def queue_item_factory(self, db_session, printer_factory, archive_factory): + """Factory to create test queue items.""" + _counter = [0] + + async def _create_queue_item(**kwargs): + from backend.app.models.print_queue import PrintQueueItem + + _counter[0] += 1 + counter = _counter[0] + + # Create printer and archive if not provided + if "printer_id" not in kwargs: + printer = await printer_factory() + kwargs["printer_id"] = printer.id + + if "archive_id" not in kwargs: + archive = await archive_factory() + kwargs["archive_id"] = archive.id + + defaults = { + "status": "pending", + "position": counter, + } + defaults.update(kwargs) + + item = PrintQueueItem(**defaults) + db_session.add(item) + await db_session.commit() + await db_session.refresh(item) + return item + + return _create_queue_item + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_list_queue_empty(self, async_client: AsyncClient): + """Verify empty list when no queue items exist.""" + response = await async_client.get("/api/v1/queue/") + assert response.status_code == 200 + assert isinstance(response.json(), list) + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_add_to_queue(self, async_client: AsyncClient, printer_factory, archive_factory, db_session): + """Verify item can be added to queue.""" + printer = await printer_factory() + archive = await archive_factory() + + data = { + "printer_id": printer.id, + "archive_id": archive.id, + } + response = await async_client.post("/api/v1/queue/", json=data) + assert response.status_code == 200 + result = response.json() + assert result["printer_id"] == printer.id + assert result["archive_id"] == archive.id + assert result["status"] == "pending" + assert result["manual_start"] is False + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_add_to_queue_with_manual_start( + self, async_client: AsyncClient, printer_factory, archive_factory, db_session + ): + """Verify item can be added to queue with manual_start=True.""" + printer = await printer_factory() + archive = await archive_factory() + + data = { + "printer_id": printer.id, + "archive_id": archive.id, + "manual_start": True, + } + response = await async_client.post("/api/v1/queue/", json=data) + assert response.status_code == 200 + result = response.json() + assert result["printer_id"] == printer.id + assert result["archive_id"] == archive.id + assert result["status"] == "pending" + assert result["manual_start"] is True + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify single queue item can be retrieved.""" + item = await queue_item_factory() + response = await async_client.get(f"/api/v1/queue/{item.id}") + assert response.status_code == 200 + assert response.json()["id"] == item.id + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_get_queue_item_not_found(self, async_client: AsyncClient): + """Verify 404 for non-existent queue item.""" + response = await async_client.get("/api/v1/queue/9999") + assert response.status_code == 404 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_update_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify queue item can be updated.""" + item = await queue_item_factory() + response = await async_client.patch(f"/api/v1/queue/{item.id}", json={"auto_off_after": True}) + assert response.status_code == 200 + result = response.json() + assert result["auto_off_after"] is True + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_update_queue_item_manual_start(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify queue item manual_start can be updated.""" + item = await queue_item_factory(manual_start=False) + response = await async_client.patch(f"/api/v1/queue/{item.id}", json={"manual_start": True}) + assert response.status_code == 200 + result = response.json() + assert result["manual_start"] is True + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_delete_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify queue item can be deleted.""" + item = await queue_item_factory() + response = await async_client.delete(f"/api/v1/queue/{item.id}") + assert response.status_code == 200 + assert response.json()["message"] == "Queue item deleted" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_delete_queue_item_not_found(self, async_client: AsyncClient): + """Verify 404 for deleting non-existent queue item.""" + response = await async_client.delete("/api/v1/queue/9999") + assert response.status_code == 404 + + +class TestQueueStartEndpoint: + """Tests for the /queue/{item_id}/start endpoint.""" + + @pytest.fixture + async def printer_factory(self, db_session): + """Factory to create test printers.""" + _counter = [0] + + async def _create_printer(**kwargs): + from backend.app.models.printer import Printer + + _counter[0] += 1 + counter = _counter[0] + + defaults = { + "name": f"Test Printer {counter}", + "ip_address": f"192.168.1.{100 + counter}", + "serial_number": f"TESTSERIAL{counter:04d}", + "access_code": "12345678", + "model": "X1C", + } + defaults.update(kwargs) + + printer = Printer(**defaults) + db_session.add(printer) + await db_session.commit() + await db_session.refresh(printer) + return printer + + return _create_printer + + @pytest.fixture + async def archive_factory(self, db_session): + """Factory to create test archives.""" + _counter = [0] + + async def _create_archive(**kwargs): + from backend.app.models.archive import PrintArchive + + _counter[0] += 1 + counter = _counter[0] + + defaults = { + "filename": f"test_print_{counter}.3mf", + "print_name": f"Test Print {counter}", + "file_path": f"/tmp/test_print_{counter}.3mf", + "file_size": 1024, + "content_hash": f"testhash{counter:08d}", + "status": "completed", + } + defaults.update(kwargs) + + archive = PrintArchive(**defaults) + db_session.add(archive) + await db_session.commit() + await db_session.refresh(archive) + return archive + + return _create_archive + + @pytest.fixture + async def queue_item_factory(self, db_session, printer_factory, archive_factory): + """Factory to create test queue items.""" + _counter = [0] + + async def _create_queue_item(**kwargs): + from backend.app.models.print_queue import PrintQueueItem + + _counter[0] += 1 + counter = _counter[0] + + if "printer_id" not in kwargs: + printer = await printer_factory() + kwargs["printer_id"] = printer.id + + if "archive_id" not in kwargs: + archive = await archive_factory() + kwargs["archive_id"] = archive.id + + defaults = { + "status": "pending", + "position": counter, + } + defaults.update(kwargs) + + item = PrintQueueItem(**defaults) + db_session.add(item) + await db_session.commit() + await db_session.refresh(item) + return item + + return _create_queue_item + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_start_staged_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify starting a staged (manual_start=True) queue item clears the flag.""" + item = await queue_item_factory(manual_start=True) + assert item.manual_start is True + + response = await async_client.post(f"/api/v1/queue/{item.id}/start") + assert response.status_code == 200 + result = response.json() + assert result["manual_start"] is False + assert result["status"] == "pending" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_start_non_staged_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify starting a non-staged queue item still works (idempotent).""" + item = await queue_item_factory(manual_start=False) + assert item.manual_start is False + + response = await async_client.post(f"/api/v1/queue/{item.id}/start") + assert response.status_code == 200 + result = response.json() + assert result["manual_start"] is False + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_start_queue_item_not_found(self, async_client: AsyncClient): + """Verify 404 for non-existent queue item.""" + response = await async_client.post("/api/v1/queue/9999/start") + assert response.status_code == 404 + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_start_non_pending_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify 400 error when trying to start a non-pending queue item.""" + item = await queue_item_factory(status="printing", manual_start=True) + + response = await async_client.post(f"/api/v1/queue/{item.id}/start") + assert response.status_code == 400 + assert "pending" in response.json()["detail"].lower() + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_start_completed_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify 400 error when trying to start a completed queue item.""" + item = await queue_item_factory(status="completed", manual_start=True) + + response = await async_client.post(f"/api/v1/queue/{item.id}/start") + assert response.status_code == 400 + + +class TestQueueCancelEndpoint: + """Tests for the /queue/{item_id}/cancel endpoint.""" + + @pytest.fixture + async def printer_factory(self, db_session): + """Factory to create test printers.""" + + async def _create_printer(**kwargs): + from backend.app.models.printer import Printer + + defaults = { + "name": "Cancel Test Printer", + "ip_address": "192.168.1.200", + "serial_number": "TESTCANCEL001", + "access_code": "12345678", + "model": "X1C", + } + defaults.update(kwargs) + + printer = Printer(**defaults) + db_session.add(printer) + await db_session.commit() + await db_session.refresh(printer) + return printer + + return _create_printer + + @pytest.fixture + async def archive_factory(self, db_session): + """Factory to create test archives.""" + + async def _create_archive(**kwargs): + from backend.app.models.archive import PrintArchive + + defaults = { + "filename": "cancel_test.3mf", + "print_name": "Cancel Test Print", + "file_path": "/tmp/cancel_test.3mf", + "file_size": 1024, + "content_hash": "cancelhash001", + "status": "completed", + } + defaults.update(kwargs) + + archive = PrintArchive(**defaults) + db_session.add(archive) + await db_session.commit() + await db_session.refresh(archive) + return archive + + return _create_archive + + @pytest.fixture + async def queue_item_factory(self, db_session, printer_factory, archive_factory): + """Factory to create test queue items.""" + + async def _create_queue_item(**kwargs): + from backend.app.models.print_queue import PrintQueueItem + + if "printer_id" not in kwargs: + printer = await printer_factory() + kwargs["printer_id"] = printer.id + + if "archive_id" not in kwargs: + archive = await archive_factory() + kwargs["archive_id"] = archive.id + + defaults = { + "status": "pending", + "position": 1, + } + defaults.update(kwargs) + + item = PrintQueueItem(**defaults) + db_session.add(item) + await db_session.commit() + await db_session.refresh(item) + return item + + return _create_queue_item + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_cancel_pending_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify cancelling a pending queue item.""" + item = await queue_item_factory(status="pending") + + response = await async_client.post(f"/api/v1/queue/{item.id}/cancel") + assert response.status_code == 200 + assert response.json()["message"] == "Queue item cancelled" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_cancel_non_pending_queue_item(self, async_client: AsyncClient, queue_item_factory, db_session): + """Verify 400 error when trying to cancel a non-pending queue item.""" + item = await queue_item_factory(status="printing") + + response = await async_client.post(f"/api/v1/queue/{item.id}/cancel") + assert response.status_code == 400 diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index bee96db0a..b08e8318a 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -789,6 +789,7 @@ export interface PrintQueueItem { scheduled_time: string | null; require_previous_success: boolean; auto_off_after: boolean; + manual_start: boolean; // Requires manual trigger to start (staged) status: 'pending' | 'printing' | 'completed' | 'failed' | 'skipped' | 'cancelled'; started_at: string | null; completed_at: string | null; @@ -806,6 +807,7 @@ export interface PrintQueueItemCreate { scheduled_time?: string | null; require_previous_success?: boolean; auto_off_after?: boolean; + manual_start?: boolean; // Requires manual trigger to start (staged) } export interface PrintQueueItemUpdate { @@ -814,6 +816,7 @@ export interface PrintQueueItemUpdate { scheduled_time?: string | null; require_previous_success?: boolean; auto_off_after?: boolean; + manual_start?: boolean; } // MQTT Logging types @@ -1914,6 +1917,8 @@ export const api = { request<{ message: string }>(`/queue/${id}/cancel`, { method: 'POST' }), stopQueueItem: (id: number) => request<{ message: string }>(`/queue/${id}/stop`, { method: 'POST' }), + startQueueItem: (id: number) => + request(`/queue/${id}/start`, { method: 'POST' }), // K-Profiles getKProfiles: (printerId: number, nozzleDiameter = '0.4') => diff --git a/frontend/src/components/AddToQueueModal.tsx b/frontend/src/components/AddToQueueModal.tsx index bea4742c5..c7dabe434 100644 --- a/frontend/src/components/AddToQueueModal.tsx +++ b/frontend/src/components/AddToQueueModal.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { Calendar, Clock, X, AlertCircle, Power } from 'lucide-react'; +import { Calendar, Clock, X, AlertCircle, Power, Hand } from 'lucide-react'; import { api } from '../api/client'; import type { PrintQueueItemCreate } from '../api/client'; import { Card, CardContent } from './Card'; @@ -18,7 +18,7 @@ export function AddToQueueModal({ archiveId, archiveName, onClose }: AddToQueueM const { showToast } = useToast(); const [printerId, setPrinterId] = useState(null); - const [scheduleType, setScheduleType] = useState<'asap' | 'scheduled'>('asap'); + const [scheduleType, setScheduleType] = useState<'asap' | 'scheduled' | 'manual'>('asap'); const [scheduledTime, setScheduledTime] = useState(''); const [requirePreviousSuccess, setRequirePreviousSuccess] = useState(false); const [autoOffAfter, setAutoOffAfter] = useState(false); @@ -68,6 +68,7 @@ export function AddToQueueModal({ archiveId, archiveName, onClose }: AddToQueueM archive_id: archiveId, require_previous_success: requirePreviousSuccess, auto_off_after: autoOffAfter, + manual_start: scheduleType === 'manual', }; if (scheduleType === 'scheduled' && scheduledTime) { @@ -142,7 +143,7 @@ export function AddToQueueModal({ archiveId, archiveName, onClose }: AddToQueueM
+
@@ -215,7 +228,9 @@ export function AddToQueueModal({ archiveId, archiveName, onClose }: AddToQueueM

{scheduleType === 'asap' ? 'Print will start as soon as the printer is idle.' - : 'Print will start at the scheduled time if the printer is idle. If busy, it will wait until the printer becomes available.'} + : scheduleType === 'scheduled' + ? 'Print will start at the scheduled time if the printer is idle. If busy, it will wait until the printer becomes available.' + : 'Print will be staged but won\'t start automatically. Use the Start button to release it to the queue.'}

{/* Actions */} diff --git a/frontend/src/components/EditQueueItemModal.tsx b/frontend/src/components/EditQueueItemModal.tsx index 50d15b7aa..ea2f4d3ce 100644 --- a/frontend/src/components/EditQueueItemModal.tsx +++ b/frontend/src/components/EditQueueItemModal.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { Calendar, Clock, X, AlertCircle, Power, Pencil } from 'lucide-react'; +import { Calendar, Clock, X, AlertCircle, Power, Pencil, Hand } from 'lucide-react'; import { api } from '../api/client'; import type { PrintQueueItem, PrintQueueItemUpdate } from '../api/client'; import { Card, CardContent } from './Card'; @@ -22,9 +22,11 @@ export function EditQueueItemModal({ item, onClose }: EditQueueItemModalProps) { const isPlaceholderDate = item.scheduled_time && new Date(item.scheduled_time).getTime() > Date.now() + (180 * 24 * 60 * 60 * 1000); - const [scheduleType, setScheduleType] = useState<'asap' | 'scheduled'>( - item.scheduled_time && !isPlaceholderDate ? 'scheduled' : 'asap' - ); + const [scheduleType, setScheduleType] = useState<'asap' | 'scheduled' | 'manual'>(() => { + if (item.manual_start) return 'manual'; + if (item.scheduled_time && !isPlaceholderDate) return 'scheduled'; + return 'asap'; + }); const [scheduledTime, setScheduledTime] = useState(() => { if (item.scheduled_time && !isPlaceholderDate) { // Convert ISO to local datetime-local format @@ -69,6 +71,7 @@ export function EditQueueItemModal({ item, onClose }: EditQueueItemModalProps) { printer_id: printerId, require_previous_success: requirePreviousSuccess, auto_off_after: autoOffAfter, + manual_start: scheduleType === 'manual', }; if (scheduleType === 'scheduled' && scheduledTime) { @@ -146,7 +149,7 @@ export function EditQueueItemModal({ item, onClose }: EditQueueItemModalProps) {
+
@@ -219,7 +234,9 @@ export function EditQueueItemModal({ item, onClose }: EditQueueItemModalProps) {

{scheduleType === 'asap' ? 'Print will start as soon as the printer is idle.' - : 'Print will start at the scheduled time if the printer is idle. If busy, it will wait until the printer becomes available.'} + : scheduleType === 'scheduled' + ? 'Print will start at the scheduled time if the printer is idle. If busy, it will wait until the printer becomes available.' + : 'Print will be staged but won\'t start automatically. Use the Start button to release it to the queue.'}

{/* Actions */} diff --git a/frontend/src/pages/QueuePage.tsx b/frontend/src/pages/QueuePage.tsx index aac7a56e0..03dba8c6e 100644 --- a/frontend/src/pages/QueuePage.tsx +++ b/frontend/src/pages/QueuePage.tsx @@ -40,6 +40,7 @@ import { Layers, ArrowUp, ArrowDown, + Hand, } from 'lucide-react'; import { api } from '../api/client'; import type { PrintQueueItem } from '../api/client'; @@ -101,6 +102,7 @@ function SortableQueueItem({ onRemove, onStop, onRequeue, + onStart, }: { item: PrintQueueItem; position?: number; @@ -109,6 +111,7 @@ function SortableQueueItem({ onRemove: () => void; onStop: () => void; onRequeue: () => void; + onStart: () => void; }) { const { attributes, @@ -198,7 +201,7 @@ function SortableQueueItem({ {formatDuration(item.print_time_seconds)} )} - {isPending && ( + {isPending && !item.manual_start && ( {formatRelativeTime(item.scheduled_time)} @@ -208,6 +211,12 @@ function SortableQueueItem({ {/* Options badges */}
+ {item.manual_start && ( + + + Staged + + )} {item.require_previous_success && ( Requires previous success @@ -258,6 +267,17 @@ function SortableQueueItem({ )} {isPending && ( <> + {item.manual_start && ( + + )}
@@ -689,6 +719,7 @@ export function QueuePage() { onRemove={() => {}} onStop={() => {}} onRequeue={() => {}} + onStart={() => startMutation.mutate(item.id)} /> ))} @@ -740,6 +771,7 @@ export function QueuePage() { onRemove={() => setConfirmAction({ type: 'remove', item })} onStop={() => {}} onRequeue={() => setRequeueItem(item)} + onStart={() => {}} /> ))} diff --git a/static/assets/index-BWv5Xlm9.js b/static/assets/index-DXIzheUf.js similarity index 50% rename from static/assets/index-BWv5Xlm9.js rename to static/assets/index-DXIzheUf.js index a1e2abea8..5a2b92ff3 100644 --- a/static/assets/index-BWv5Xlm9.js +++ b/static/assets/index-DXIzheUf.js @@ -1,20 +1,20 @@ -function nie(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();var Ev=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function yo(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var LA={exports:{}},px={};var EF;function rie(){if(EF)return px;EF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var s=null;if(a!==void 0&&(s=""+a),i.key!==void 0&&(s=""+i.key),"key"in i){a={};for(var l in i)l!=="key"&&(a[l]=i[l])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:s,ref:i!==void 0?i:null,props:a}}return px.Fragment=e,px.jsx=n,px.jsxs=n,px}var AF;function iie(){return AF||(AF=1,LA.exports=rie()),LA.exports}var o=iie(),IA={exports:{}},an={};var TF;function aie(){if(TF)return an;TF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),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"),g=Symbol.iterator;function p(U){return U===null||typeof U!="object"?null:(U=g&&U[g]||U["@@iterator"],typeof U=="function"?U:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function y(U,$,ee){this.props=U,this.context=$,this.refs=b,this.updater=ee||x}y.prototype.isReactComponent={},y.prototype.setState=function(U,$){if(typeof U!="object"&&typeof U!="function"&&U!=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,U,$,"setState")},y.prototype.forceUpdate=function(U){this.updater.enqueueForceUpdate(this,U,"forceUpdate")};function E(){}E.prototype=y.prototype;function w(U,$,ee){this.props=U,this.context=$,this.refs=b,this.updater=ee||x}var A=w.prototype=new E;A.constructor=w,v(A,y.prototype),A.isPureReactComponent=!0;var T=Array.isArray;function C(){}var N={H:null,A:null,T:null,S:null},O=Object.prototype.hasOwnProperty;function k(U,$,ee){var G=ee.ref;return{$$typeof:t,type:U,key:$,ref:G!==void 0?G:null,props:ee}}function j(U,$){return k(U.type,$,U.props)}function z(U){return typeof U=="object"&&U!==null&&U.$$typeof===t}function I(U){var $={"=":"=0",":":"=2"};return"$"+U.replace(/[=:]/g,function(ee){return $[ee]})}var K=/\/+/g;function R(U,$){return typeof U=="object"&&U!==null&&U.key!=null?I(""+U.key):$.toString(36)}function q(U){switch(U.status){case"fulfilled":return U.value;case"rejected":throw U.reason;default:switch(typeof U.status=="string"?U.then(C,C):(U.status="pending",U.then(function($){U.status==="pending"&&(U.status="fulfilled",U.value=$)},function($){U.status==="pending"&&(U.status="rejected",U.reason=$)})),U.status){case"fulfilled":return U.value;case"rejected":throw U.reason}}throw U}function X(U,$,ee,G,re){var le=typeof U;(le==="undefined"||le==="boolean")&&(U=null);var ge=!1;if(U===null)ge=!0;else switch(le){case"bigint":case"string":case"number":ge=!0;break;case"object":switch(U.$$typeof){case t:case e:ge=!0;break;case d:return ge=U._init,X(ge(U._payload),$,ee,G,re)}}if(ge)return re=re(U),ge=G===""?"."+R(U,0):G,T(re)?(ee="",ge!=null&&(ee=ge.replace(K,"$&/")+"/"),X(re,$,ee,"",function(ue){return ue})):re!=null&&(z(re)&&(re=j(re,ee+(re.key==null||U&&U.key===re.key?"":(""+re.key).replace(K,"$&/")+"/")+ge)),$.push(re)),1;ge=0;var Ee=G===""?".":G+":";if(T(U))for(var pe=0;pe>>1,Z=X[Q];if(0>>1;Qi(ee,te))Gi(re,ee)?(X[Q]=re,X[G]=te,Q=G):(X[Q]=ee,X[$]=te,Q=$);else if(Gi(re,te))X[Q]=re,X[G]=te,Q=G;else break e}}return W}function i(X,W){var te=X.sortIndex-W.sortIndex;return te!==0?te:X.id-W.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,l=s.now();t.unstable_now=function(){return s.now()-l}}var c=[],u=[],d=1,f=null,g=3,p=!1,x=!1,v=!1,b=!1,y=typeof setTimeout=="function"?setTimeout:null,E=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function A(X){for(var W=n(u);W!==null;){if(W.callback===null)r(u);else if(W.startTime<=X)r(u),W.sortIndex=W.expirationTime,e(c,W);else break;W=n(u)}}function T(X){if(v=!1,A(X),!x)if(n(c)!==null)x=!0,C||(C=!0,I());else{var W=n(u);W!==null&&q(T,W.startTime-X)}}var C=!1,N=-1,O=5,k=-1;function j(){return b?!0:!(t.unstable_now()-kX&&j());){var Q=f.callback;if(typeof Q=="function"){f.callback=null,g=f.priorityLevel;var Z=Q(f.expirationTime<=X);if(X=t.unstable_now(),typeof Z=="function"){f.callback=Z,A(X),W=!0;break t}f===n(c)&&r(c),A(X)}else r(c);f=n(c)}if(f!==null)W=!0;else{var U=n(u);U!==null&&q(T,U.startTime-X),W=!1}}break e}finally{f=null,g=te,p=!1}W=void 0}}finally{W?I():C=!1}}}var I;if(typeof w=="function")I=function(){w(z)};else if(typeof MessageChannel<"u"){var K=new MessageChannel,R=K.port2;K.port1.onmessage=z,I=function(){R.postMessage(null)}}else I=function(){y(z,0)};function q(X,W){N=y(function(){X(t.unstable_now())},W)}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(X){X.callback=null},t.unstable_forceFrameRate=function(X){0>X||125Q?(X.sortIndex=te,e(u,X),n(c)===null&&X===n(u)&&(v?(E(N),N=-1):v=!0,q(T,te-Q))):(X.sortIndex=Z,e(c,X),x||p||(x=!0,C||(C=!0,I()))),X},t.unstable_shouldYield=j,t.unstable_wrapCallback=function(X){var W=g;return function(){var te=g;g=W;try{return X.apply(this,arguments)}finally{g=te}}}})(zA)),zA}var kF;function lie(){return kF||(kF=1,UA.exports=oie()),UA.exports}var BA={exports:{}},da={};var PF;function cie(){if(PF)return da;PF=1;var t=tg();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(),BA.exports=cie(),BA.exports}var RF;function uie(){if(RF)return gx;RF=1;var t=lie(),e=tg(),n=JG();function r(h){var m="https://react.dev/errors/"+h;if(1Z||(h.current=Q[Z],Q[Z]=null,Z--)}function ee(h,m){Z++,Q[Z]=h.current,h.current=m}var G=U(null),re=U(null),le=U(null),ge=U(null);function Ee(h,m){switch(ee(le,m),ee(re,h),ee(G,null),m.nodeType){case 9:case 11:h=(h=m.documentElement)&&(h=h.namespaceURI)?K5(h):0;break;default:if(h=m.tagName,m=m.namespaceURI)m=K5(m),h=X5(m,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}$(G),ee(G,h)}function pe(){$(G),$(re),$(le)}function ue(h){h.memoizedState!==null&&ee(ge,h);var m=G.current,_=X5(m,h.type);m!==_&&(ee(re,h),ee(G,_))}function ke(h){re.current===h&&($(G),$(re)),ge.current===h&&($(ge),dx._currentValue=te)}var de,Re;function B(h){if(de===void 0)try{throw Error()}catch(_){var m=_.stack.trim().match(/\n( *(at )?)/);de=m&&m[1]||"",Re=-1<_.stack.indexOf(` +function rie(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();var Ev=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function yo(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var LA={exports:{}},px={};var AF;function iie(){if(AF)return px;AF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,a){var s=null;if(a!==void 0&&(s=""+a),i.key!==void 0&&(s=""+i.key),"key"in i){a={};for(var l in i)l!=="key"&&(a[l]=i[l])}else a=i;return i=a.ref,{$$typeof:t,type:r,key:s,ref:i!==void 0?i:null,props:a}}return px.Fragment=e,px.jsx=n,px.jsxs=n,px}var TF;function aie(){return TF||(TF=1,LA.exports=iie()),LA.exports}var o=aie(),IA={exports:{}},an={};var NF;function sie(){if(NF)return an;NF=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),a=Symbol.for("react.consumer"),s=Symbol.for("react.context"),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"),g=Symbol.iterator;function p(F){return F===null||typeof F!="object"?null:(F=g&&F[g]||F["@@iterator"],typeof F=="function"?F:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function y(F,$,J){this.props=F,this.context=$,this.refs=b,this.updater=J||x}y.prototype.isReactComponent={},y.prototype.setState=function(F,$){if(typeof F!="object"&&typeof F!="function"&&F!=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,F,$,"setState")},y.prototype.forceUpdate=function(F){this.updater.enqueueForceUpdate(this,F,"forceUpdate")};function E(){}E.prototype=y.prototype;function w(F,$,J){this.props=F,this.context=$,this.refs=b,this.updater=J||x}var A=w.prototype=new E;A.constructor=w,v(A,y.prototype),A.isPureReactComponent=!0;var T=Array.isArray;function C(){}var N={H:null,A:null,T:null,S:null},O=Object.prototype.hasOwnProperty;function k(F,$,J){var V=J.ref;return{$$typeof:t,type:F,key:$,ref:V!==void 0?V:null,props:J}}function j(F,$){return k(F.type,$,F.props)}function z(F){return typeof F=="object"&&F!==null&&F.$$typeof===t}function I(F){var $={"=":"=0",":":"=2"};return"$"+F.replace(/[=:]/g,function(J){return $[J]})}var K=/\/+/g;function R(F,$){return typeof F=="object"&&F!==null&&F.key!=null?I(""+F.key):$.toString(36)}function q(F){switch(F.status){case"fulfilled":return F.value;case"rejected":throw F.reason;default:switch(typeof F.status=="string"?F.then(C,C):(F.status="pending",F.then(function($){F.status==="pending"&&(F.status="fulfilled",F.value=$)},function($){F.status==="pending"&&(F.status="rejected",F.reason=$)})),F.status){case"fulfilled":return F.value;case"rejected":throw F.reason}}throw F}function X(F,$,J,V,ne){var le=typeof F;(le==="undefined"||le==="boolean")&&(F=null);var ge=!1;if(F===null)ge=!0;else switch(le){case"bigint":case"string":case"number":ge=!0;break;case"object":switch(F.$$typeof){case t:case e:ge=!0;break;case d:return ge=F._init,X(ge(F._payload),$,J,V,ne)}}if(ge)return ne=ne(F),ge=V===""?"."+R(F,0):V,T(ne)?(J="",ge!=null&&(J=ge.replace(K,"$&/")+"/"),X(ne,$,J,"",function(ue){return ue})):ne!=null&&(z(ne)&&(ne=j(ne,J+(ne.key==null||F&&F.key===ne.key?"":(""+ne.key).replace(K,"$&/")+"/")+ge)),$.push(ne)),1;ge=0;var Ee=V===""?".":V+":";if(T(F))for(var pe=0;pe>>1,te=X[Z];if(0>>1;Zi(J,ee))Vi(ne,J)?(X[Z]=ne,X[V]=ee,Z=V):(X[Z]=J,X[$]=ee,Z=$);else if(Vi(ne,ee))X[Z]=ne,X[V]=ee,Z=V;else break e}}return W}function i(X,W){var ee=X.sortIndex-W.sortIndex;return ee!==0?ee:X.id-W.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,l=s.now();t.unstable_now=function(){return s.now()-l}}var c=[],u=[],d=1,f=null,g=3,p=!1,x=!1,v=!1,b=!1,y=typeof setTimeout=="function"?setTimeout:null,E=typeof clearTimeout=="function"?clearTimeout:null,w=typeof setImmediate<"u"?setImmediate:null;function A(X){for(var W=n(u);W!==null;){if(W.callback===null)r(u);else if(W.startTime<=X)r(u),W.sortIndex=W.expirationTime,e(c,W);else break;W=n(u)}}function T(X){if(v=!1,A(X),!x)if(n(c)!==null)x=!0,C||(C=!0,I());else{var W=n(u);W!==null&&q(T,W.startTime-X)}}var C=!1,N=-1,O=5,k=-1;function j(){return b?!0:!(t.unstable_now()-kX&&j());){var Z=f.callback;if(typeof Z=="function"){f.callback=null,g=f.priorityLevel;var te=Z(f.expirationTime<=X);if(X=t.unstable_now(),typeof te=="function"){f.callback=te,A(X),W=!0;break t}f===n(c)&&r(c),A(X)}else r(c);f=n(c)}if(f!==null)W=!0;else{var F=n(u);F!==null&&q(T,F.startTime-X),W=!1}}break e}finally{f=null,g=ee,p=!1}W=void 0}}finally{W?I():C=!1}}}var I;if(typeof w=="function")I=function(){w(z)};else if(typeof MessageChannel<"u"){var K=new MessageChannel,R=K.port2;K.port1.onmessage=z,I=function(){R.postMessage(null)}}else I=function(){y(z,0)};function q(X,W){N=y(function(){X(t.unstable_now())},W)}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(X){X.callback=null},t.unstable_forceFrameRate=function(X){0>X||125Z?(X.sortIndex=ee,e(u,X),n(c)===null&&X===n(u)&&(v?(E(N),N=-1):v=!0,q(T,ee-Z))):(X.sortIndex=te,e(c,X),x||p||(x=!0,C||(C=!0,I()))),X},t.unstable_shouldYield=j,t.unstable_wrapCallback=function(X){var W=g;return function(){var ee=g;g=W;try{return X.apply(this,arguments)}finally{g=ee}}}})(zA)),zA}var PF;function cie(){return PF||(PF=1,UA.exports=lie()),UA.exports}var BA={exports:{}},da={};var jF;function uie(){if(jF)return da;jF=1;var t=tg();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(),BA.exports=uie(),BA.exports}var DF;function die(){if(DF)return gx;DF=1;var t=cie(),e=tg(),n=e9();function r(h){var m="https://react.dev/errors/"+h;if(1te||(h.current=Z[te],Z[te]=null,te--)}function J(h,m){te++,Z[te]=h.current,h.current=m}var V=F(null),ne=F(null),le=F(null),ge=F(null);function Ee(h,m){switch(J(le,m),J(ne,h),J(V,null),m.nodeType){case 9:case 11:h=(h=m.documentElement)&&(h=h.namespaceURI)?X5(h):0;break;default:if(h=m.tagName,m=m.namespaceURI)m=X5(m),h=Y5(m,h);else switch(h){case"svg":h=1;break;case"math":h=2;break;default:h=0}}$(V),J(V,h)}function pe(){$(V),$(ne),$(le)}function ue(h){h.memoizedState!==null&&J(ge,h);var m=V.current,_=Y5(m,h.type);m!==_&&(J(ne,h),J(V,_))}function ke(h){ne.current===h&&($(V),$(ne)),ge.current===h&&($(ge),dx._currentValue=ee)}var de,Re;function B(h){if(de===void 0)try{throw Error()}catch(_){var m=_.stack.trim().match(/\n( *(at )?)/);de=m&&m[1]||"",Re=-1<_.stack.indexOf(` at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return` `+de+h+Re}var H=!1;function ie(h,m){if(!h||H)return"";H=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var M={DetermineComponentFrameRoot:function(){try{if(m){var mt=function(){throw Error()};if(Object.defineProperty(mt.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(mt,[])}catch(ot){var it=ot}Reflect.construct(h,[],mt)}else{try{mt.call()}catch(ot){it=ot}h.call(mt.prototype)}}else{try{throw Error()}catch(ot){it=ot}(mt=h())&&typeof mt.catch=="function"&&mt.catch(function(){})}}catch(ot){if(ot&&it&&typeof ot.stack=="string")return[ot.stack,it.stack]}return[null,null]}};M.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var P=Object.getOwnPropertyDescriptor(M.DetermineComponentFrameRoot,"name");P&&P.configurable&&Object.defineProperty(M.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var D=M.DetermineComponentFrameRoot(),Y=D[0],ce=D[1];if(Y&&ce){var Ne=Y.split(` `),nt=ce.split(` `);for(P=M=0;MP||Ne[M]!==nt[P]){var ht=` -`+Ne[M].replace(" at new "," at ");return h.displayName&&ht.includes("")&&(ht=ht.replace("",h.displayName)),ht}while(1<=M&&0<=P);break}}}finally{H=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?B(_):""}function V(h,m){switch(h.tag){case 26:case 27:case 5:return B(h.type);case 16:return B("Lazy");case 13:return h.child!==m&&m!==null?B("Suspense Fallback"):B("Suspense");case 19:return B("SuspenseList");case 0:case 15:return ie(h.type,!1);case 11:return ie(h.type.render,!1);case 1:return ie(h.type,!0);case 31:return B("Activity");default:return""}}function J(h){try{var m="",_=null;do m+=V(h,_),_=h,h=h.return;while(h);return m}catch(M){return` +`+Ne[M].replace(" at new "," at ");return h.displayName&&ht.includes("")&&(ht=ht.replace("",h.displayName)),ht}while(1<=M&&0<=P);break}}}finally{H=!1,Error.prepareStackTrace=_}return(_=h?h.displayName||h.name:"")?B(_):""}function G(h,m){switch(h.tag){case 26:case 27:case 5:return B(h.type);case 16:return B("Lazy");case 13:return h.child!==m&&m!==null?B("Suspense Fallback"):B("Suspense");case 19:return B("SuspenseList");case 0:case 15:return ie(h.type,!1);case 11:return ie(h.type.render,!1);case 1:return ie(h.type,!0);case 31:return B("Activity");default:return""}}function Q(h){try{var m="",_=null;do m+=G(h,_),_=h,h=h.return;while(h);return m}catch(M){return` Error generating stack: `+M.message+` -`+M.stack}}var oe=Object.prototype.hasOwnProperty,xe=t.unstable_scheduleCallback,Te=t.unstable_cancelCallback,Me=t.unstable_shouldYield,F=t.unstable_requestPaint,L=t.unstable_now,ne=t.unstable_getCurrentPriorityLevel,ve=t.unstable_ImmediatePriority,Se=t.unstable_UserBlockingPriority,ye=t.unstable_NormalPriority,Ye=t.unstable_LowPriority,We=t.unstable_IdlePriority,Ke=t.log,ut=t.unstable_setDisableYieldValue,fe=null,ae=null;function Ve(h){if(typeof Ke=="function"&&ut(h),ae&&typeof ae.setStrictMode=="function")try{ae.setStrictMode(fe,h)}catch{}}var Be=Math.clz32?Math.clz32:me,Qe=Math.log,dt=Math.LN2;function me(h){return h>>>=0,h===0?32:31-(Qe(h)/dt|0)|0}var tt=256,be=262144,He=4194304;function Pe(h){var m=h&42;if(m!==0)return m;switch(h&-h){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return h&261888;case 262144:case 524288:case 1048576:case 2097152:return h&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return h&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return h}}function _e(h,m,_){var M=h.pendingLanes;if(M===0)return 0;var P=0,D=h.suspendedLanes,Y=h.pingedLanes;h=h.warmLanes;var ce=M&134217727;return ce!==0?(M=ce&~D,M!==0?P=Pe(M):(Y&=ce,Y!==0?P=Pe(Y):_||(_=ce&~h,_!==0&&(P=Pe(_))))):(ce=M&~D,ce!==0?P=Pe(ce):Y!==0?P=Pe(Y):_||(_=M&~h,_!==0&&(P=Pe(_)))),P===0?0:m!==0&&m!==P&&(m&D)===0&&(D=P&-P,_=m&-m,D>=_||D===32&&(_&4194048)!==0)?m:P}function Ze(h,m){return(h.pendingLanes&~(h.suspendedLanes&~h.pingedLanes)&m)===0}function Fe(h,m){switch(h){case 1:case 2:case 4:case 8:case 64:return m+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return m+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function pt(){var h=He;return He<<=1,(He&62914560)===0&&(He=4194304),h}function gt(h){for(var m=[],_=0;31>_;_++)m.push(h);return m}function lt(h,m){h.pendingLanes|=m,m!==268435456&&(h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0)}function At(h,m,_,M,P,D){var Y=h.pendingLanes;h.pendingLanes=_,h.suspendedLanes=0,h.pingedLanes=0,h.warmLanes=0,h.expiredLanes&=_,h.entangledLanes&=_,h.errorRecoveryDisabledLanes&=_,h.shellSuspendCounter=0;var ce=h.entanglements,Ne=h.expirationTimes,nt=h.hiddenUpdates;for(_=Y&~_;0<_;){var ht=31-Be(_),mt=1<"u")return null;try{return h.activeElement||h.body}catch{return h.body}}var qr=/[\n"\\]/g;function Vn(h){return h.replace(qr,function(m){return"\\"+m.charCodeAt(0).toString(16)+" "})}function wo(h,m,_,M,P,D,Y,ce){h.name="",Y!=null&&typeof Y!="function"&&typeof Y!="symbol"&&typeof Y!="boolean"?h.type=Y:h.removeAttribute("type"),m!=null?Y==="number"?(m===0&&h.value===""||h.value!=m)&&(h.value=""+bn(m)):h.value!==""+bn(m)&&(h.value=""+bn(m)):Y!=="submit"&&Y!=="reset"||h.removeAttribute("value"),m!=null?Pi(h,Y,bn(m)):_!=null?Pi(h,Y,bn(_)):M!=null&&h.removeAttribute("value"),P==null&&D!=null&&(h.defaultChecked=!!D),P!=null&&(h.checked=P&&typeof P!="function"&&typeof P!="symbol"),ce!=null&&typeof ce!="function"&&typeof ce!="symbol"&&typeof ce!="boolean"?h.name=""+bn(ce):h.removeAttribute("name")}function qn(h,m,_,M,P,D,Y,ce){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(h.type=D),m!=null||_!=null){if(!(D!=="submit"&&D!=="reset"||m!=null)){rn(h);return}_=_!=null?""+bn(_):"",m=m!=null?""+bn(m):_,ce||m===h.value||(h.value=m),h.defaultValue=m}M=M??P,M=typeof M!="function"&&typeof M!="symbol"&&!!M,h.checked=ce?h.checked:!!M,h.defaultChecked=!!M,Y!=null&&typeof Y!="function"&&typeof Y!="symbol"&&typeof Y!="boolean"&&(h.name=Y),rn(h)}function Pi(h,m,_){m==="number"&&Ur(h.ownerDocument)===h||h.defaultValue===""+_||(h.defaultValue=""+_)}function pi(h,m,_,M){if(h=h.options,m){m={};for(var P=0;P<_.length;P++)m["$"+_[P]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),LE=!1;if(Ul)try{var Cg={};Object.defineProperty(Cg,"passive",{get:function(){LE=!0}}),window.addEventListener("test",Cg,Cg),window.removeEventListener("test",Cg,Cg)}catch{LE=!1}var nu=null,IE=null,xy=null;function XL(){if(xy)return xy;var h,m=IE,_=m.length,M,P="value"in nu?nu.value:nu.textContent,D=P.length;for(h=0;h<_&&m[h]===P[h];h++);var Y=_-h;for(M=1;M<=Y&&m[_-M]===P[D-M];M++);return xy=P.slice(h,1=jg),t4=" ",n4=!1;function r4(h,m){switch(h){case"keyup":return Mne.indexOf(m.keyCode)!==-1;case"keydown":return m.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function i4(h){return h=h.detail,typeof h=="object"&&"data"in h?h.data:null}var Rf=!1;function Ane(h,m){switch(h){case"compositionend":return i4(m);case"keypress":return m.which!==32?null:(n4=!0,t4);case"textInput":return h=m.data,h===t4&&n4?null:h;default:return null}}function Tne(h,m){if(Rf)return h==="compositionend"||!HE&&r4(h,m)?(h=XL(),xy=IE=nu=null,Rf=!1,h):null;switch(h){case"paste":return null;case"keypress":if(!(m.ctrlKey||m.altKey||m.metaKey)||m.ctrlKey&&m.altKey){if(m.char&&1=m)return{node:_,offset:m-h};h=M}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=h4(_)}}function m4(h,m){return h&&m?h===m?!0:h&&h.nodeType===3?!1:m&&m.nodeType===3?m4(h,m.parentNode):"contains"in h?h.contains(m):h.compareDocumentPosition?!!(h.compareDocumentPosition(m)&16):!1:!1}function p4(h){h=h!=null&&h.ownerDocument!=null&&h.ownerDocument.defaultView!=null?h.ownerDocument.defaultView:window;for(var m=Ur(h.document);m instanceof h.HTMLIFrameElement;){try{var _=typeof m.contentWindow.location.href=="string"}catch{_=!1}if(_)h=m.contentWindow;else break;m=Ur(h.document)}return m}function GE(h){var m=h&&h.nodeName&&h.nodeName.toLowerCase();return m&&(m==="input"&&(h.type==="text"||h.type==="search"||h.type==="tel"||h.type==="url"||h.type==="password")||m==="textarea"||h.contentEditable==="true")}var One=Ul&&"documentMode"in document&&11>=document.documentMode,Df=null,WE=null,Lg=null,qE=!1;function g4(h,m,_){var M=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;qE||Df==null||Df!==Ur(M)||(M=Df,"selectionStart"in M&&GE(M)?M={start:M.selectionStart,end:M.selectionEnd}:(M=(M.ownerDocument&&M.ownerDocument.defaultView||window).getSelection(),M={anchorNode:M.anchorNode,anchorOffset:M.anchorOffset,focusNode:M.focusNode,focusOffset:M.focusOffset}),Lg&&Og(Lg,M)||(Lg=M,M=uv(WE,"onSelect"),0>=Y,P-=Y,Qo=1<<32-Be(m)+P|_<ln?(Nn=Ut,Ut=null):Nn=Ut.sibling;var Fn=it(Ge,Ut,et[ln],ft);if(Fn===null){Ut===null&&(Ut=Nn);break}h&&Ut&&Fn.alternate===null&&m(Ge,Ut),Oe=D(Fn,Oe,ln),In===null?Vt=Fn:In.sibling=Fn,In=Fn,Ut=Nn}if(ln===et.length)return _(Ge,Ut),kn&&Bl(Ge,ln),Vt;if(Ut===null){for(;lnln?(Nn=Ut,Ut=null):Nn=Ut.sibling;var Mu=it(Ge,Ut,Fn.value,ft);if(Mu===null){Ut===null&&(Ut=Nn);break}h&&Ut&&Mu.alternate===null&&m(Ge,Ut),Oe=D(Mu,Oe,ln),In===null?Vt=Mu:In.sibling=Mu,In=Mu,Ut=Nn}if(Fn.done)return _(Ge,Ut),kn&&Bl(Ge,ln),Vt;if(Ut===null){for(;!Fn.done;ln++,Fn=et.next())Fn=mt(Ge,Fn.value,ft),Fn!==null&&(Oe=D(Fn,Oe,ln),In===null?Vt=Fn:In.sibling=Fn,In=Fn);return kn&&Bl(Ge,ln),Vt}for(Ut=M(Ut);!Fn.done;ln++,Fn=et.next())Fn=ot(Ut,Ge,ln,Fn.value,ft),Fn!==null&&(h&&Fn.alternate!==null&&Ut.delete(Fn.key===null?ln:Fn.key),Oe=D(Fn,Oe,ln),In===null?Vt=Fn:In.sibling=Fn,In=Fn);return h&&Ut.forEach(function(tie){return m(Ge,tie)}),kn&&Bl(Ge,ln),Vt}function rr(Ge,Oe,et,ft){if(typeof et=="object"&&et!==null&&et.type===v&&et.key===null&&(et=et.props.children),typeof et=="object"&&et!==null){switch(et.$$typeof){case p:e:{for(var Vt=et.key;Oe!==null;){if(Oe.key===Vt){if(Vt=et.type,Vt===v){if(Oe.tag===7){_(Ge,Oe.sibling),ft=P(Oe,et.props.children),ft.return=Ge,Ge=ft;break e}}else if(Oe.elementType===Vt||typeof Vt=="object"&&Vt!==null&&Vt.$$typeof===O&&qd(Vt)===Oe.type){_(Ge,Oe.sibling),ft=P(Oe,et.props),Hg(ft,et),ft.return=Ge,Ge=ft;break e}_(Ge,Oe);break}else m(Ge,Oe);Oe=Oe.sibling}et.type===v?(ft=Hd(et.props.children,Ge.mode,ft,et.key),ft.return=Ge,Ge=ft):(ft=Ty(et.type,et.key,et.props,null,Ge.mode,ft),Hg(ft,et),ft.return=Ge,Ge=ft)}return Y(Ge);case x:e:{for(Vt=et.key;Oe!==null;){if(Oe.key===Vt)if(Oe.tag===4&&Oe.stateNode.containerInfo===et.containerInfo&&Oe.stateNode.implementation===et.implementation){_(Ge,Oe.sibling),ft=P(Oe,et.children||[]),ft.return=Ge,Ge=ft;break e}else{_(Ge,Oe);break}else m(Ge,Oe);Oe=Oe.sibling}ft=e2(et,Ge.mode,ft),ft.return=Ge,Ge=ft}return Y(Ge);case O:return et=qd(et),rr(Ge,Oe,et,ft)}if(q(et))return It(Ge,Oe,et,ft);if(I(et)){if(Vt=I(et),typeof Vt!="function")throw Error(r(150));return et=Vt.call(et),Zt(Ge,Oe,et,ft)}if(typeof et.then=="function")return rr(Ge,Oe,Dy(et),ft);if(et.$$typeof===w)return rr(Ge,Oe,ky(Ge,et),ft);Oy(Ge,et)}return typeof et=="string"&&et!==""||typeof et=="number"||typeof et=="bigint"?(et=""+et,Oe!==null&&Oe.tag===6?(_(Ge,Oe.sibling),ft=P(Oe,et),ft.return=Ge,Ge=ft):(_(Ge,Oe),ft=JE(et,Ge.mode,ft),ft.return=Ge,Ge=ft),Y(Ge)):_(Ge,Oe)}return function(Ge,Oe,et,ft){try{Bg=0;var Vt=rr(Ge,Oe,et,ft);return Gf=null,Vt}catch(Ut){if(Ut===Vf||Ut===jy)throw Ut;var In=hs(29,Ut,null,Ge.mode);return In.lanes=ft,In.return=Ge,In}finally{}}}var Xd=U4(!0),z4=U4(!1),ou=!1;function h2(h){h.updateQueue={baseState:h.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function f2(h,m){h=h.updateQueue,m.updateQueue===h&&(m.updateQueue={baseState:h.baseState,firstBaseUpdate:h.firstBaseUpdate,lastBaseUpdate:h.lastBaseUpdate,shared:h.shared,callbacks:null})}function lu(h){return{lane:h,tag:0,payload:null,callback:null,next:null}}function cu(h,m,_){var M=h.updateQueue;if(M===null)return null;if(M=M.shared,(Hn&2)!==0){var P=M.pending;return P===null?m.next=m:(m.next=P.next,P.next=m),M.pending=m,m=Ay(h),S4(h,null,_),m}return Ey(h,M,m,_),Ay(h)}function $g(h,m,_){if(m=m.updateQueue,m!==null&&(m=m.shared,(_&4194048)!==0)){var M=m.lanes;M&=h.pendingLanes,_|=M,m.lanes=_,Xt(h,_)}}function m2(h,m){var _=h.updateQueue,M=h.alternate;if(M!==null&&(M=M.updateQueue,_===M)){var P=null,D=null;if(_=_.firstBaseUpdate,_!==null){do{var Y={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};D===null?P=D=Y:D=D.next=Y,_=_.next}while(_!==null);D===null?P=D=m:D=D.next=m}else P=D=m;_={baseState:M.baseState,firstBaseUpdate:P,lastBaseUpdate:D,shared:M.shared,callbacks:M.callbacks},h.updateQueue=_;return}h=_.lastBaseUpdate,h===null?_.firstBaseUpdate=m:h.next=m,_.lastBaseUpdate=m}var p2=!1;function Vg(){if(p2){var h=$f;if(h!==null)throw h}}function Gg(h,m,_,M){p2=!1;var P=h.updateQueue;ou=!1;var D=P.firstBaseUpdate,Y=P.lastBaseUpdate,ce=P.shared.pending;if(ce!==null){P.shared.pending=null;var Ne=ce,nt=Ne.next;Ne.next=null,Y===null?D=nt:Y.next=nt,Y=Ne;var ht=h.alternate;ht!==null&&(ht=ht.updateQueue,ce=ht.lastBaseUpdate,ce!==Y&&(ce===null?ht.firstBaseUpdate=nt:ce.next=nt,ht.lastBaseUpdate=Ne))}if(D!==null){var mt=P.baseState;Y=0,ht=nt=Ne=null,ce=D;do{var it=ce.lane&-536870913,ot=it!==ce.lane;if(ot?(Tn&it)===it:(M&it)===it){it!==0&&it===Hf&&(p2=!0),ht!==null&&(ht=ht.next={lane:0,tag:ce.tag,payload:ce.payload,callback:null,next:null});e:{var It=h,Zt=ce;it=m;var rr=_;switch(Zt.tag){case 1:if(It=Zt.payload,typeof It=="function"){mt=It.call(rr,mt,it);break e}mt=It;break e;case 3:It.flags=It.flags&-65537|128;case 0:if(It=Zt.payload,it=typeof It=="function"?It.call(rr,mt,it):It,it==null)break e;mt=f({},mt,it);break e;case 2:ou=!0}}it=ce.callback,it!==null&&(h.flags|=64,ot&&(h.flags|=8192),ot=P.callbacks,ot===null?P.callbacks=[it]:ot.push(it))}else ot={lane:it,tag:ce.tag,payload:ce.payload,callback:ce.callback,next:null},ht===null?(nt=ht=ot,Ne=mt):ht=ht.next=ot,Y|=it;if(ce=ce.next,ce===null){if(ce=P.shared.pending,ce===null)break;ot=ce,ce=ot.next,ot.next=null,P.lastBaseUpdate=ot,P.shared.pending=null}}while(!0);ht===null&&(Ne=mt),P.baseState=Ne,P.firstBaseUpdate=nt,P.lastBaseUpdate=ht,D===null&&(P.shared.lanes=0),mu|=Y,h.lanes=Y,h.memoizedState=mt}}function B4(h,m){if(typeof h!="function")throw Error(r(191,h));h.call(m)}function H4(h,m){var _=h.callbacks;if(_!==null)for(h.callbacks=null,h=0;h<_.length;h++)B4(_[h],m)}var Wf=U(null),Ly=U(0);function $4(h,m){h=Ql,ee(Ly,h),ee(Wf,m),Ql=h|m.baseLanes}function g2(){ee(Ly,Ql),ee(Wf,Wf.current)}function x2(){Ql=Ly.current,$(Wf),$(Ly)}var fs=U(null),zs=null;function uu(h){var m=h.alternate;ee(Kr,Kr.current&1),ee(fs,h),zs===null&&(m===null||Wf.current!==null||m.memoizedState!==null)&&(zs=h)}function b2(h){ee(Kr,Kr.current),ee(fs,h),zs===null&&(zs=h)}function V4(h){h.tag===22?(ee(Kr,Kr.current),ee(fs,h),zs===null&&(zs=h)):du()}function du(){ee(Kr,Kr.current),ee(fs,fs.current)}function ms(h){$(fs),zs===h&&(zs=null),$(Kr)}var Kr=U(0);function Iy(h){for(var m=h;m!==null;){if(m.tag===13){var _=m.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||MA(_)||EA(_)))return m}else if(m.tag===19&&(m.memoizedProps.revealOrder==="forwards"||m.memoizedProps.revealOrder==="backwards"||m.memoizedProps.revealOrder==="unstable_legacy-backwards"||m.memoizedProps.revealOrder==="together")){if((m.flags&128)!==0)return m}else if(m.child!==null){m.child.return=m,m=m.child;continue}if(m===h)break;for(;m.sibling===null;){if(m.return===null||m.return===h)return null;m=m.return}m.sibling.return=m.return,m=m.sibling}return null}var Vl=0,on=null,tr=null,ni=null,Fy=!1,qf=!1,Yd=!1,Uy=0,Wg=0,Kf=null,Gne=0;function zr(){throw Error(r(321))}function y2(h,m){if(m===null)return!1;for(var _=0;_D?D:8;var Y=X.T,ce={};X.T=ce,D2(h,!1,m,_);try{var Ne=P(),nt=X.S;if(nt!==null&&nt(ce,Ne),Ne!==null&&typeof Ne=="object"&&typeof Ne.then=="function"){var ht=Vne(Ne,M);Kg(h,m,ht,xs(h))}else Kg(h,m,M,xs(h))}catch(mt){Kg(h,m,{then:function(){},status:"rejected",reason:mt},xs())}finally{W.p=D,Y!==null&&ce.types!==null&&(Y.types=ce.types),X.T=Y}}function Yne(){}function j2(h,m,_,M){if(h.tag!==5)throw Error(r(476));var P=vI(h).queue;yI(h,P,m,te,_===null?Yne:function(){return _I(h),_(M)})}function vI(h){var m=h.memoizedState;if(m!==null)return m;m={memoizedState:te,baseState:te,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Gl,lastRenderedState:te},next:null};var _={};return m.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Gl,lastRenderedState:_},next:null},h.memoizedState=m,h=h.alternate,h!==null&&(h.memoizedState=m),m}function _I(h){var m=vI(h);m.next===null&&(m=h.alternate.memoizedState),Kg(h,m.next.queue,{},xs())}function R2(){return Yi(dx)}function wI(){return Xr().memoizedState}function SI(){return Xr().memoizedState}function Zne(h){for(var m=h.return;m!==null;){switch(m.tag){case 24:case 3:var _=xs();h=lu(_);var M=cu(m,h,_);M!==null&&(qa(M,m,_),$g(M,m,_)),m={cache:l2()},h.payload=m;return}m=m.return}}function Qne(h,m,_){var M=xs();_={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},Gy(h)?EI(m,_):(_=ZE(h,m,_,M),_!==null&&(qa(_,h,M),AI(_,m,M)))}function MI(h,m,_){var M=xs();Kg(h,m,_,M)}function Kg(h,m,_,M){var P={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(Gy(h))EI(m,P);else{var D=h.alternate;if(h.lanes===0&&(D===null||D.lanes===0)&&(D=m.lastRenderedReducer,D!==null))try{var Y=m.lastRenderedState,ce=D(Y,_);if(P.hasEagerState=!0,P.eagerState=ce,ds(ce,Y))return Ey(h,m,P,0),fr===null&&My(),!1}catch{}finally{}if(_=ZE(h,m,P,M),_!==null)return qa(_,h,M),AI(_,m,M),!0}return!1}function D2(h,m,_,M){if(M={lane:2,revertLane:hA(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},Gy(h)){if(m)throw Error(r(479))}else m=ZE(h,_,M,2),m!==null&&qa(m,h,2)}function Gy(h){var m=h.alternate;return h===on||m!==null&&m===on}function EI(h,m){qf=Fy=!0;var _=h.pending;_===null?m.next=m:(m.next=_.next,_.next=m),h.pending=m}function AI(h,m,_){if((_&4194048)!==0){var M=m.lanes;M&=h.pendingLanes,_|=M,m.lanes=_,Xt(h,_)}}var Xg={readContext:Yi,use:By,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};Xg.useEffectEvent=zr;var TI={readContext:Yi,use:By,useCallback:function(h,m){return Ea().memoizedState=[h,m===void 0?null:m],h},useContext:Yi,useEffect:uI,useImperativeHandle:function(h,m,_){_=_!=null?_.concat([h]):null,$y(4194308,4,mI.bind(null,m,h),_)},useLayoutEffect:function(h,m){return $y(4194308,4,h,m)},useInsertionEffect:function(h,m){$y(4,2,h,m)},useMemo:function(h,m){var _=Ea();m=m===void 0?null:m;var M=h();if(Yd){Ve(!0);try{h()}finally{Ve(!1)}}return _.memoizedState=[M,m],M},useReducer:function(h,m,_){var M=Ea();if(_!==void 0){var P=_(m);if(Yd){Ve(!0);try{_(m)}finally{Ve(!1)}}}else P=m;return M.memoizedState=M.baseState=P,h={pending:null,lanes:0,dispatch:null,lastRenderedReducer:h,lastRenderedState:P},M.queue=h,h=h.dispatch=Qne.bind(null,on,h),[M.memoizedState,h]},useRef:function(h){var m=Ea();return h={current:h},m.memoizedState=h},useState:function(h){h=T2(h);var m=h.queue,_=MI.bind(null,on,m);return m.dispatch=_,[h.memoizedState,_]},useDebugValue:k2,useDeferredValue:function(h,m){var _=Ea();return P2(_,h,m)},useTransition:function(){var h=T2(!1);return h=yI.bind(null,on,h.queue,!0,!1),Ea().memoizedState=h,[!1,h]},useSyncExternalStore:function(h,m,_){var M=on,P=Ea();if(kn){if(_===void 0)throw Error(r(407));_=_()}else{if(_=m(),fr===null)throw Error(r(349));(Tn&127)!==0||K4(M,m,_)}P.memoizedState=_;var D={value:_,getSnapshot:m};return P.queue=D,uI(Y4.bind(null,M,D,h),[h]),M.flags|=2048,Xf(9,{destroy:void 0},X4.bind(null,M,D,_,m),null),_},useId:function(){var h=Ea(),m=fr.identifierPrefix;if(kn){var _=Jo,M=Qo;_=(M&~(1<<32-Be(M)-1)).toString(32)+_,m="_"+m+"R_"+_,_=Uy++,0<_&&(m+="H"+_.toString(32)),m+="_"}else _=Gne++,m="_"+m+"r_"+_.toString(32)+"_";return h.memoizedState=m},useHostTransitionStatus:R2,useFormState:aI,useActionState:aI,useOptimistic:function(h){var m=Ea();m.memoizedState=m.baseState=h;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return m.queue=_,m=D2.bind(null,on,!0,_),_.dispatch=m,[h,m]},useMemoCache:M2,useCacheRefresh:function(){return Ea().memoizedState=Zne.bind(null,on)},useEffectEvent:function(h){var m=Ea(),_={impl:h};return m.memoizedState=_,function(){if((Hn&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},O2={readContext:Yi,use:By,useCallback:gI,useContext:Yi,useEffect:C2,useImperativeHandle:pI,useInsertionEffect:hI,useLayoutEffect:fI,useMemo:xI,useReducer:Hy,useRef:cI,useState:function(){return Hy(Gl)},useDebugValue:k2,useDeferredValue:function(h,m){var _=Xr();return bI(_,tr.memoizedState,h,m)},useTransition:function(){var h=Hy(Gl)[0],m=Xr().memoizedState;return[typeof h=="boolean"?h:qg(h),m]},useSyncExternalStore:q4,useId:wI,useHostTransitionStatus:R2,useFormState:sI,useActionState:sI,useOptimistic:function(h,m){var _=Xr();return J4(_,tr,h,m)},useMemoCache:M2,useCacheRefresh:SI};O2.useEffectEvent=dI;var NI={readContext:Yi,use:By,useCallback:gI,useContext:Yi,useEffect:C2,useImperativeHandle:pI,useInsertionEffect:hI,useLayoutEffect:fI,useMemo:xI,useReducer:A2,useRef:cI,useState:function(){return A2(Gl)},useDebugValue:k2,useDeferredValue:function(h,m){var _=Xr();return tr===null?P2(_,h,m):bI(_,tr.memoizedState,h,m)},useTransition:function(){var h=A2(Gl)[0],m=Xr().memoizedState;return[typeof h=="boolean"?h:qg(h),m]},useSyncExternalStore:q4,useId:wI,useHostTransitionStatus:R2,useFormState:lI,useActionState:lI,useOptimistic:function(h,m){var _=Xr();return tr!==null?J4(_,tr,h,m):(_.baseState=h,[h,_.queue.dispatch])},useMemoCache:M2,useCacheRefresh:SI};NI.useEffectEvent=dI;function L2(h,m,_,M){m=h.memoizedState,_=_(M,m),_=_==null?m:f({},m,_),h.memoizedState=_,h.lanes===0&&(h.updateQueue.baseState=_)}var I2={enqueueSetState:function(h,m,_){h=h._reactInternals;var M=xs(),P=lu(M);P.payload=m,_!=null&&(P.callback=_),m=cu(h,P,M),m!==null&&(qa(m,h,M),$g(m,h,M))},enqueueReplaceState:function(h,m,_){h=h._reactInternals;var M=xs(),P=lu(M);P.tag=1,P.payload=m,_!=null&&(P.callback=_),m=cu(h,P,M),m!==null&&(qa(m,h,M),$g(m,h,M))},enqueueForceUpdate:function(h,m){h=h._reactInternals;var _=xs(),M=lu(_);M.tag=2,m!=null&&(M.callback=m),m=cu(h,M,_),m!==null&&(qa(m,h,_),$g(m,h,_))}};function CI(h,m,_,M,P,D,Y){return h=h.stateNode,typeof h.shouldComponentUpdate=="function"?h.shouldComponentUpdate(M,D,Y):m.prototype&&m.prototype.isPureReactComponent?!Og(_,M)||!Og(P,D):!0}function kI(h,m,_,M){h=m.state,typeof m.componentWillReceiveProps=="function"&&m.componentWillReceiveProps(_,M),typeof m.UNSAFE_componentWillReceiveProps=="function"&&m.UNSAFE_componentWillReceiveProps(_,M),m.state!==h&&I2.enqueueReplaceState(m,m.state,null)}function Zd(h,m){var _=m;if("ref"in m){_={};for(var M in m)M!=="ref"&&(_[M]=m[M])}if(h=h.defaultProps){_===m&&(_=f({},_));for(var P in h)_[P]===void 0&&(_[P]=h[P])}return _}function PI(h){Sy(h)}function jI(h){console.error(h)}function RI(h){Sy(h)}function Wy(h,m){try{var _=h.onUncaughtError;_(m.value,{componentStack:m.stack})}catch(M){setTimeout(function(){throw M})}}function DI(h,m,_){try{var M=h.onCaughtError;M(_.value,{componentStack:_.stack,errorBoundary:m.tag===1?m.stateNode:null})}catch(P){setTimeout(function(){throw P})}}function F2(h,m,_){return _=lu(_),_.tag=3,_.payload={element:null},_.callback=function(){Wy(h,m)},_}function OI(h){return h=lu(h),h.tag=3,h}function LI(h,m,_,M){var P=_.type.getDerivedStateFromError;if(typeof P=="function"){var D=M.value;h.payload=function(){return P(D)},h.callback=function(){DI(m,_,M)}}var Y=_.stateNode;Y!==null&&typeof Y.componentDidCatch=="function"&&(h.callback=function(){DI(m,_,M),typeof P!="function"&&(pu===null?pu=new Set([this]):pu.add(this));var ce=M.stack;this.componentDidCatch(M.value,{componentStack:ce!==null?ce:""})})}function Jne(h,m,_,M,P){if(_.flags|=32768,M!==null&&typeof M=="object"&&typeof M.then=="function"){if(m=_.alternate,m!==null&&Bf(m,_,P,!0),_=fs.current,_!==null){switch(_.tag){case 31:case 13:return zs===null?iv():_.alternate===null&&Br===0&&(Br=3),_.flags&=-257,_.flags|=65536,_.lanes=P,M===Ry?_.flags|=16384:(m=_.updateQueue,m===null?_.updateQueue=new Set([M]):m.add(M),cA(h,M,P)),!1;case 22:return _.flags|=65536,M===Ry?_.flags|=16384:(m=_.updateQueue,m===null?(m={transitions:null,markerInstances:null,retryQueue:new Set([M])},_.updateQueue=m):(_=m.retryQueue,_===null?m.retryQueue=new Set([M]):_.add(M)),cA(h,M,P)),!1}throw Error(r(435,_.tag))}return cA(h,M,P),iv(),!1}if(kn)return m=fs.current,m!==null?((m.flags&65536)===0&&(m.flags|=256),m.flags|=65536,m.lanes=P,M!==r2&&(h=Error(r(422),{cause:M}),Fg(Ls(h,_)))):(M!==r2&&(m=Error(r(423),{cause:M}),Fg(Ls(m,_))),h=h.current.alternate,h.flags|=65536,P&=-P,h.lanes|=P,M=Ls(M,_),P=F2(h.stateNode,M,P),m2(h,P),Br!==4&&(Br=2)),!1;var D=Error(r(520),{cause:M});if(D=Ls(D,_),rx===null?rx=[D]:rx.push(D),Br!==4&&(Br=2),m===null)return!0;M=Ls(M,_),_=m;do{switch(_.tag){case 3:return _.flags|=65536,h=P&-P,_.lanes|=h,h=F2(_.stateNode,M,h),m2(_,h),!1;case 1:if(m=_.type,D=_.stateNode,(_.flags&128)===0&&(typeof m.getDerivedStateFromError=="function"||D!==null&&typeof D.componentDidCatch=="function"&&(pu===null||!pu.has(D))))return _.flags|=65536,P&=-P,_.lanes|=P,P=OI(P),LI(P,h,_,M),m2(_,P),!1}_=_.return}while(_!==null);return!1}var U2=Error(r(461)),ri=!1;function Zi(h,m,_,M){m.child=h===null?z4(m,null,_,M):Xd(m,h.child,_,M)}function II(h,m,_,M,P){_=_.render;var D=m.ref;if("ref"in M){var Y={};for(var ce in M)ce!=="ref"&&(Y[ce]=M[ce])}else Y=M;return Gd(m),M=v2(h,m,_,Y,D,P),ce=_2(),h!==null&&!ri?(w2(h,m,P),Wl(h,m,P)):(kn&&ce&&t2(m),m.flags|=1,Zi(h,m,M,P),m.child)}function FI(h,m,_,M,P){if(h===null){var D=_.type;return typeof D=="function"&&!QE(D)&&D.defaultProps===void 0&&_.compare===null?(m.tag=15,m.type=D,UI(h,m,D,M,P)):(h=Ty(_.type,null,M,m,m.mode,P),h.ref=m.ref,h.return=m,m.child=h)}if(D=h.child,!q2(h,P)){var Y=D.memoizedProps;if(_=_.compare,_=_!==null?_:Og,_(Y,M)&&h.ref===m.ref)return Wl(h,m,P)}return m.flags|=1,h=zl(D,M),h.ref=m.ref,h.return=m,m.child=h}function UI(h,m,_,M,P){if(h!==null){var D=h.memoizedProps;if(Og(D,M)&&h.ref===m.ref)if(ri=!1,m.pendingProps=M=D,q2(h,P))(h.flags&131072)!==0&&(ri=!0);else return m.lanes=h.lanes,Wl(h,m,P)}return z2(h,m,_,M,P)}function zI(h,m,_,M){var P=M.children,D=h!==null?h.memoizedState:null;if(h===null&&m.stateNode===null&&(m.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),M.mode==="hidden"){if((m.flags&128)!==0){if(D=D!==null?D.baseLanes|_:_,h!==null){for(M=m.child=h.child,P=0;M!==null;)P=P|M.lanes|M.childLanes,M=M.sibling;M=P&~D}else M=0,m.child=null;return BI(h,m,D,_,M)}if((_&536870912)!==0)m.memoizedState={baseLanes:0,cachePool:null},h!==null&&Py(m,D!==null?D.cachePool:null),D!==null?$4(m,D):g2(),V4(m);else return M=m.lanes=536870912,BI(h,m,D!==null?D.baseLanes|_:_,_,M)}else D!==null?(Py(m,D.cachePool),$4(m,D),du(),m.memoizedState=null):(h!==null&&Py(m,null),g2(),du());return Zi(h,m,P,_),m.child}function Yg(h,m){return h!==null&&h.tag===22||m.stateNode!==null||(m.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),m.sibling}function BI(h,m,_,M,P){var D=u2();return D=D===null?null:{parent:ti._currentValue,pool:D},m.memoizedState={baseLanes:_,cachePool:D},h!==null&&Py(m,null),g2(),V4(m),h!==null&&Bf(h,m,M,!0),m.childLanes=P,null}function qy(h,m){return m=Xy({mode:m.mode,children:m.children},h.mode),m.ref=h.ref,h.child=m,m.return=h,m}function HI(h,m,_){return Xd(m,h.child,null,_),h=qy(m,m.pendingProps),h.flags|=2,ms(m),m.memoizedState=null,h}function ere(h,m,_){var M=m.pendingProps,P=(m.flags&128)!==0;if(m.flags&=-129,h===null){if(kn){if(M.mode==="hidden")return h=qy(m,M),m.lanes=536870912,Yg(null,h);if(b2(m),(h=vr)?(h=eF(h,Us),h=h!==null&&h.data==="&"?h:null,h!==null&&(m.memoizedState={dehydrated:h,treeContext:ru!==null?{id:Qo,overflow:Jo}:null,retryLane:536870912,hydrationErrors:null},_=E4(h),_.return=m,m.child=_,Xi=m,vr=null)):h=null,h===null)throw au(m);return m.lanes=536870912,null}return qy(m,M)}var D=h.memoizedState;if(D!==null){var Y=D.dehydrated;if(b2(m),P)if(m.flags&256)m.flags&=-257,m=HI(h,m,_);else if(m.memoizedState!==null)m.child=h.child,m.flags|=128,m=null;else throw Error(r(558));else if(ri||Bf(h,m,_,!1),P=(_&h.childLanes)!==0,ri||P){if(M=fr,M!==null&&(Y=En(M,_),Y!==0&&Y!==D.retryLane))throw D.retryLane=Y,Bd(h,Y),qa(M,h,Y),U2;iv(),m=HI(h,m,_)}else h=D.treeContext,vr=Bs(Y.nextSibling),Xi=m,kn=!0,iu=null,Us=!1,h!==null&&N4(m,h),m=qy(m,M),m.flags|=4096;return m}return h=zl(h.child,{mode:M.mode,children:M.children}),h.ref=m.ref,m.child=h,h.return=m,h}function Ky(h,m){var _=m.ref;if(_===null)h!==null&&h.ref!==null&&(m.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(h===null||h.ref!==_)&&(m.flags|=4194816)}}function z2(h,m,_,M,P){return Gd(m),_=v2(h,m,_,M,void 0,P),M=_2(),h!==null&&!ri?(w2(h,m,P),Wl(h,m,P)):(kn&&M&&t2(m),m.flags|=1,Zi(h,m,_,P),m.child)}function $I(h,m,_,M,P,D){return Gd(m),m.updateQueue=null,_=W4(m,M,_,P),G4(h),M=_2(),h!==null&&!ri?(w2(h,m,D),Wl(h,m,D)):(kn&&M&&t2(m),m.flags|=1,Zi(h,m,_,D),m.child)}function VI(h,m,_,M,P){if(Gd(m),m.stateNode===null){var D=If,Y=_.contextType;typeof Y=="object"&&Y!==null&&(D=Yi(Y)),D=new _(M,D),m.memoizedState=D.state!==null&&D.state!==void 0?D.state:null,D.updater=I2,m.stateNode=D,D._reactInternals=m,D=m.stateNode,D.props=M,D.state=m.memoizedState,D.refs={},h2(m),Y=_.contextType,D.context=typeof Y=="object"&&Y!==null?Yi(Y):If,D.state=m.memoizedState,Y=_.getDerivedStateFromProps,typeof Y=="function"&&(L2(m,_,Y,M),D.state=m.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof D.getSnapshotBeforeUpdate=="function"||typeof D.UNSAFE_componentWillMount!="function"&&typeof D.componentWillMount!="function"||(Y=D.state,typeof D.componentWillMount=="function"&&D.componentWillMount(),typeof D.UNSAFE_componentWillMount=="function"&&D.UNSAFE_componentWillMount(),Y!==D.state&&I2.enqueueReplaceState(D,D.state,null),Gg(m,M,D,P),Vg(),D.state=m.memoizedState),typeof D.componentDidMount=="function"&&(m.flags|=4194308),M=!0}else if(h===null){D=m.stateNode;var ce=m.memoizedProps,Ne=Zd(_,ce);D.props=Ne;var nt=D.context,ht=_.contextType;Y=If,typeof ht=="object"&&ht!==null&&(Y=Yi(ht));var mt=_.getDerivedStateFromProps;ht=typeof mt=="function"||typeof D.getSnapshotBeforeUpdate=="function",ce=m.pendingProps!==ce,ht||typeof D.UNSAFE_componentWillReceiveProps!="function"&&typeof D.componentWillReceiveProps!="function"||(ce||nt!==Y)&&kI(m,D,M,Y),ou=!1;var it=m.memoizedState;D.state=it,Gg(m,M,D,P),Vg(),nt=m.memoizedState,ce||it!==nt||ou?(typeof mt=="function"&&(L2(m,_,mt,M),nt=m.memoizedState),(Ne=ou||CI(m,_,Ne,M,it,nt,Y))?(ht||typeof D.UNSAFE_componentWillMount!="function"&&typeof D.componentWillMount!="function"||(typeof D.componentWillMount=="function"&&D.componentWillMount(),typeof D.UNSAFE_componentWillMount=="function"&&D.UNSAFE_componentWillMount()),typeof D.componentDidMount=="function"&&(m.flags|=4194308)):(typeof D.componentDidMount=="function"&&(m.flags|=4194308),m.memoizedProps=M,m.memoizedState=nt),D.props=M,D.state=nt,D.context=Y,M=Ne):(typeof D.componentDidMount=="function"&&(m.flags|=4194308),M=!1)}else{D=m.stateNode,f2(h,m),Y=m.memoizedProps,ht=Zd(_,Y),D.props=ht,mt=m.pendingProps,it=D.context,nt=_.contextType,Ne=If,typeof nt=="object"&&nt!==null&&(Ne=Yi(nt)),ce=_.getDerivedStateFromProps,(nt=typeof ce=="function"||typeof D.getSnapshotBeforeUpdate=="function")||typeof D.UNSAFE_componentWillReceiveProps!="function"&&typeof D.componentWillReceiveProps!="function"||(Y!==mt||it!==Ne)&&kI(m,D,M,Ne),ou=!1,it=m.memoizedState,D.state=it,Gg(m,M,D,P),Vg();var ot=m.memoizedState;Y!==mt||it!==ot||ou||h!==null&&h.dependencies!==null&&Cy(h.dependencies)?(typeof ce=="function"&&(L2(m,_,ce,M),ot=m.memoizedState),(ht=ou||CI(m,_,ht,M,it,ot,Ne)||h!==null&&h.dependencies!==null&&Cy(h.dependencies))?(nt||typeof D.UNSAFE_componentWillUpdate!="function"&&typeof D.componentWillUpdate!="function"||(typeof D.componentWillUpdate=="function"&&D.componentWillUpdate(M,ot,Ne),typeof D.UNSAFE_componentWillUpdate=="function"&&D.UNSAFE_componentWillUpdate(M,ot,Ne)),typeof D.componentDidUpdate=="function"&&(m.flags|=4),typeof D.getSnapshotBeforeUpdate=="function"&&(m.flags|=1024)):(typeof D.componentDidUpdate!="function"||Y===h.memoizedProps&&it===h.memoizedState||(m.flags|=4),typeof D.getSnapshotBeforeUpdate!="function"||Y===h.memoizedProps&&it===h.memoizedState||(m.flags|=1024),m.memoizedProps=M,m.memoizedState=ot),D.props=M,D.state=ot,D.context=Ne,M=ht):(typeof D.componentDidUpdate!="function"||Y===h.memoizedProps&&it===h.memoizedState||(m.flags|=4),typeof D.getSnapshotBeforeUpdate!="function"||Y===h.memoizedProps&&it===h.memoizedState||(m.flags|=1024),M=!1)}return D=M,Ky(h,m),M=(m.flags&128)!==0,D||M?(D=m.stateNode,_=M&&typeof _.getDerivedStateFromError!="function"?null:D.render(),m.flags|=1,h!==null&&M?(m.child=Xd(m,h.child,null,P),m.child=Xd(m,null,_,P)):Zi(h,m,_,P),m.memoizedState=D.state,h=m.child):h=Wl(h,m,P),h}function GI(h,m,_,M){return $d(),m.flags|=256,Zi(h,m,_,M),m.child}var B2={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function H2(h){return{baseLanes:h,cachePool:D4()}}function $2(h,m,_){return h=h!==null?h.childLanes&~_:0,m&&(h|=gs),h}function WI(h,m,_){var M=m.pendingProps,P=!1,D=(m.flags&128)!==0,Y;if((Y=D)||(Y=h!==null&&h.memoizedState===null?!1:(Kr.current&2)!==0),Y&&(P=!0,m.flags&=-129),Y=(m.flags&32)!==0,m.flags&=-33,h===null){if(kn){if(P?uu(m):du(),(h=vr)?(h=eF(h,Us),h=h!==null&&h.data!=="&"?h:null,h!==null&&(m.memoizedState={dehydrated:h,treeContext:ru!==null?{id:Qo,overflow:Jo}:null,retryLane:536870912,hydrationErrors:null},_=E4(h),_.return=m,m.child=_,Xi=m,vr=null)):h=null,h===null)throw au(m);return EA(h)?m.lanes=32:m.lanes=536870912,null}var ce=M.children;return M=M.fallback,P?(du(),P=m.mode,ce=Xy({mode:"hidden",children:ce},P),M=Hd(M,P,_,null),ce.return=m,M.return=m,ce.sibling=M,m.child=ce,M=m.child,M.memoizedState=H2(_),M.childLanes=$2(h,Y,_),m.memoizedState=B2,Yg(null,M)):(uu(m),V2(m,ce))}var Ne=h.memoizedState;if(Ne!==null&&(ce=Ne.dehydrated,ce!==null)){if(D)m.flags&256?(uu(m),m.flags&=-257,m=G2(h,m,_)):m.memoizedState!==null?(du(),m.child=h.child,m.flags|=128,m=null):(du(),ce=M.fallback,P=m.mode,M=Xy({mode:"visible",children:M.children},P),ce=Hd(ce,P,_,null),ce.flags|=2,M.return=m,ce.return=m,M.sibling=ce,m.child=M,Xd(m,h.child,null,_),M=m.child,M.memoizedState=H2(_),M.childLanes=$2(h,Y,_),m.memoizedState=B2,m=Yg(null,M));else if(uu(m),EA(ce)){if(Y=ce.nextSibling&&ce.nextSibling.dataset,Y)var nt=Y.dgst;Y=nt,M=Error(r(419)),M.stack="",M.digest=Y,Fg({value:M,source:null,stack:null}),m=G2(h,m,_)}else if(ri||Bf(h,m,_,!1),Y=(_&h.childLanes)!==0,ri||Y){if(Y=fr,Y!==null&&(M=En(Y,_),M!==0&&M!==Ne.retryLane))throw Ne.retryLane=M,Bd(h,M),qa(Y,h,M),U2;MA(ce)||iv(),m=G2(h,m,_)}else MA(ce)?(m.flags|=192,m.child=h.child,m=null):(h=Ne.treeContext,vr=Bs(ce.nextSibling),Xi=m,kn=!0,iu=null,Us=!1,h!==null&&N4(m,h),m=V2(m,M.children),m.flags|=4096);return m}return P?(du(),ce=M.fallback,P=m.mode,Ne=h.child,nt=Ne.sibling,M=zl(Ne,{mode:"hidden",children:M.children}),M.subtreeFlags=Ne.subtreeFlags&65011712,nt!==null?ce=zl(nt,ce):(ce=Hd(ce,P,_,null),ce.flags|=2),ce.return=m,M.return=m,M.sibling=ce,m.child=M,Yg(null,M),M=m.child,ce=h.child.memoizedState,ce===null?ce=H2(_):(P=ce.cachePool,P!==null?(Ne=ti._currentValue,P=P.parent!==Ne?{parent:Ne,pool:Ne}:P):P=D4(),ce={baseLanes:ce.baseLanes|_,cachePool:P}),M.memoizedState=ce,M.childLanes=$2(h,Y,_),m.memoizedState=B2,Yg(h.child,M)):(uu(m),_=h.child,h=_.sibling,_=zl(_,{mode:"visible",children:M.children}),_.return=m,_.sibling=null,h!==null&&(Y=m.deletions,Y===null?(m.deletions=[h],m.flags|=16):Y.push(h)),m.child=_,m.memoizedState=null,_)}function V2(h,m){return m=Xy({mode:"visible",children:m},h.mode),m.return=h,h.child=m}function Xy(h,m){return h=hs(22,h,null,m),h.lanes=0,h}function G2(h,m,_){return Xd(m,h.child,null,_),h=V2(m,m.pendingProps.children),h.flags|=2,m.memoizedState=null,h}function qI(h,m,_){h.lanes|=m;var M=h.alternate;M!==null&&(M.lanes|=m),s2(h.return,m,_)}function W2(h,m,_,M,P,D){var Y=h.memoizedState;Y===null?h.memoizedState={isBackwards:m,rendering:null,renderingStartTime:0,last:M,tail:_,tailMode:P,treeForkCount:D}:(Y.isBackwards=m,Y.rendering=null,Y.renderingStartTime=0,Y.last=M,Y.tail=_,Y.tailMode=P,Y.treeForkCount=D)}function KI(h,m,_){var M=m.pendingProps,P=M.revealOrder,D=M.tail;M=M.children;var Y=Kr.current,ce=(Y&2)!==0;if(ce?(Y=Y&1|2,m.flags|=128):Y&=1,ee(Kr,Y),Zi(h,m,M,_),M=kn?Ig:0,!ce&&h!==null&&(h.flags&128)!==0)e:for(h=m.child;h!==null;){if(h.tag===13)h.memoizedState!==null&&qI(h,_,m);else if(h.tag===19)qI(h,_,m);else if(h.child!==null){h.child.return=h,h=h.child;continue}if(h===m)break e;for(;h.sibling===null;){if(h.return===null||h.return===m)break e;h=h.return}h.sibling.return=h.return,h=h.sibling}switch(P){case"forwards":for(_=m.child,P=null;_!==null;)h=_.alternate,h!==null&&Iy(h)===null&&(P=_),_=_.sibling;_=P,_===null?(P=m.child,m.child=null):(P=_.sibling,_.sibling=null),W2(m,!1,P,_,D,M);break;case"backwards":case"unstable_legacy-backwards":for(_=null,P=m.child,m.child=null;P!==null;){if(h=P.alternate,h!==null&&Iy(h)===null){m.child=P;break}h=P.sibling,P.sibling=_,_=P,P=h}W2(m,!0,_,null,D,M);break;case"together":W2(m,!1,null,null,void 0,M);break;default:m.memoizedState=null}return m.child}function Wl(h,m,_){if(h!==null&&(m.dependencies=h.dependencies),mu|=m.lanes,(_&m.childLanes)===0)if(h!==null){if(Bf(h,m,_,!1),(_&m.childLanes)===0)return null}else return null;if(h!==null&&m.child!==h.child)throw Error(r(153));if(m.child!==null){for(h=m.child,_=zl(h,h.pendingProps),m.child=_,_.return=m;h.sibling!==null;)h=h.sibling,_=_.sibling=zl(h,h.pendingProps),_.return=m;_.sibling=null}return m.child}function q2(h,m){return(h.lanes&m)!==0?!0:(h=h.dependencies,!!(h!==null&&Cy(h)))}function tre(h,m,_){switch(m.tag){case 3:Ee(m,m.stateNode.containerInfo),su(m,ti,h.memoizedState.cache),$d();break;case 27:case 5:ue(m);break;case 4:Ee(m,m.stateNode.containerInfo);break;case 10:su(m,m.type,m.memoizedProps.value);break;case 31:if(m.memoizedState!==null)return m.flags|=128,b2(m),null;break;case 13:var M=m.memoizedState;if(M!==null)return M.dehydrated!==null?(uu(m),m.flags|=128,null):(_&m.child.childLanes)!==0?WI(h,m,_):(uu(m),h=Wl(h,m,_),h!==null?h.sibling:null);uu(m);break;case 19:var P=(h.flags&128)!==0;if(M=(_&m.childLanes)!==0,M||(Bf(h,m,_,!1),M=(_&m.childLanes)!==0),P){if(M)return KI(h,m,_);m.flags|=128}if(P=m.memoizedState,P!==null&&(P.rendering=null,P.tail=null,P.lastEffect=null),ee(Kr,Kr.current),M)break;return null;case 22:return m.lanes=0,zI(h,m,_,m.pendingProps);case 24:su(m,ti,h.memoizedState.cache)}return Wl(h,m,_)}function XI(h,m,_){if(h!==null)if(h.memoizedProps!==m.pendingProps)ri=!0;else{if(!q2(h,_)&&(m.flags&128)===0)return ri=!1,tre(h,m,_);ri=(h.flags&131072)!==0}else ri=!1,kn&&(m.flags&1048576)!==0&&T4(m,Ig,m.index);switch(m.lanes=0,m.tag){case 16:e:{var M=m.pendingProps;if(h=qd(m.elementType),m.type=h,typeof h=="function")QE(h)?(M=Zd(h,M),m.tag=1,m=VI(null,m,h,M,_)):(m.tag=0,m=z2(null,m,h,M,_));else{if(h!=null){var P=h.$$typeof;if(P===A){m.tag=11,m=II(null,m,h,M,_);break e}else if(P===N){m.tag=14,m=FI(null,m,h,M,_);break e}}throw m=R(h)||h,Error(r(306,m,""))}}return m;case 0:return z2(h,m,m.type,m.pendingProps,_);case 1:return M=m.type,P=Zd(M,m.pendingProps),VI(h,m,M,P,_);case 3:e:{if(Ee(m,m.stateNode.containerInfo),h===null)throw Error(r(387));M=m.pendingProps;var D=m.memoizedState;P=D.element,f2(h,m),Gg(m,M,null,_);var Y=m.memoizedState;if(M=Y.cache,su(m,ti,M),M!==D.cache&&o2(m,[ti],_,!0),Vg(),M=Y.element,D.isDehydrated)if(D={element:M,isDehydrated:!1,cache:Y.cache},m.updateQueue.baseState=D,m.memoizedState=D,m.flags&256){m=GI(h,m,M,_);break e}else if(M!==P){P=Ls(Error(r(424)),m),Fg(P),m=GI(h,m,M,_);break e}else{switch(h=m.stateNode.containerInfo,h.nodeType){case 9:h=h.body;break;default:h=h.nodeName==="HTML"?h.ownerDocument.body:h}for(vr=Bs(h.firstChild),Xi=m,kn=!0,iu=null,Us=!0,_=z4(m,null,M,_),m.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling}else{if($d(),M===P){m=Wl(h,m,_);break e}Zi(h,m,M,_)}m=m.child}return m;case 26:return Ky(h,m),h===null?(_=sF(m.type,null,m.pendingProps,null))?m.memoizedState=_:kn||(_=m.type,h=m.pendingProps,M=dv(le.current).createElement(_),M[Bn]=m,M[er]=h,Qi(M,_,h),Ie(M),m.stateNode=M):m.memoizedState=sF(m.type,h.memoizedProps,m.pendingProps,h.memoizedState),null;case 27:return ue(m),h===null&&kn&&(M=m.stateNode=rF(m.type,m.pendingProps,le.current),Xi=m,Us=!0,P=vr,yu(m.type)?(AA=P,vr=Bs(M.firstChild)):vr=P),Zi(h,m,m.pendingProps.children,_),Ky(h,m),h===null&&(m.flags|=4194304),m.child;case 5:return h===null&&kn&&((P=M=vr)&&(M=Pre(M,m.type,m.pendingProps,Us),M!==null?(m.stateNode=M,Xi=m,vr=Bs(M.firstChild),Us=!1,P=!0):P=!1),P||au(m)),ue(m),P=m.type,D=m.pendingProps,Y=h!==null?h.memoizedProps:null,M=D.children,_A(P,D)?M=null:Y!==null&&_A(P,Y)&&(m.flags|=32),m.memoizedState!==null&&(P=v2(h,m,Wne,null,null,_),dx._currentValue=P),Ky(h,m),Zi(h,m,M,_),m.child;case 6:return h===null&&kn&&((h=_=vr)&&(_=jre(_,m.pendingProps,Us),_!==null?(m.stateNode=_,Xi=m,vr=null,h=!0):h=!1),h||au(m)),null;case 13:return WI(h,m,_);case 4:return Ee(m,m.stateNode.containerInfo),M=m.pendingProps,h===null?m.child=Xd(m,null,M,_):Zi(h,m,M,_),m.child;case 11:return II(h,m,m.type,m.pendingProps,_);case 7:return Zi(h,m,m.pendingProps,_),m.child;case 8:return Zi(h,m,m.pendingProps.children,_),m.child;case 12:return Zi(h,m,m.pendingProps.children,_),m.child;case 10:return M=m.pendingProps,su(m,m.type,M.value),Zi(h,m,M.children,_),m.child;case 9:return P=m.type._context,M=m.pendingProps.children,Gd(m),P=Yi(P),M=M(P),m.flags|=1,Zi(h,m,M,_),m.child;case 14:return FI(h,m,m.type,m.pendingProps,_);case 15:return UI(h,m,m.type,m.pendingProps,_);case 19:return KI(h,m,_);case 31:return ere(h,m,_);case 22:return zI(h,m,_,m.pendingProps);case 24:return Gd(m),M=Yi(ti),h===null?(P=u2(),P===null&&(P=fr,D=l2(),P.pooledCache=D,D.refCount++,D!==null&&(P.pooledCacheLanes|=_),P=D),m.memoizedState={parent:M,cache:P},h2(m),su(m,ti,P)):((h.lanes&_)!==0&&(f2(h,m),Gg(m,null,null,_),Vg()),P=h.memoizedState,D=m.memoizedState,P.parent!==M?(P={parent:M,cache:M},m.memoizedState=P,m.lanes===0&&(m.memoizedState=m.updateQueue.baseState=P),su(m,ti,M)):(M=D.cache,su(m,ti,M),M!==P.cache&&o2(m,[ti],_,!0))),Zi(h,m,m.pendingProps.children,_),m.child;case 29:throw m.pendingProps}throw Error(r(156,m.tag))}function ql(h){h.flags|=4}function K2(h,m,_,M,P){if((m=(h.mode&32)!==0)&&(m=!1),m){if(h.flags|=16777216,(P&335544128)===P)if(h.stateNode.complete)h.flags|=8192;else if(w5())h.flags|=8192;else throw Kd=Ry,d2}else h.flags&=-16777217}function YI(h,m){if(m.type!=="stylesheet"||(m.state.loading&4)!==0)h.flags&=-16777217;else if(h.flags|=16777216,!dF(m))if(w5())h.flags|=8192;else throw Kd=Ry,d2}function Yy(h,m){m!==null&&(h.flags|=4),h.flags&16384&&(m=h.tag!==22?pt():536870912,h.lanes|=m,Jf|=m)}function Zg(h,m){if(!kn)switch(h.tailMode){case"hidden":m=h.tail;for(var _=null;m!==null;)m.alternate!==null&&(_=m),m=m.sibling;_===null?h.tail=null:_.sibling=null;break;case"collapsed":_=h.tail;for(var M=null;_!==null;)_.alternate!==null&&(M=_),_=_.sibling;M===null?m||h.tail===null?h.tail=null:h.tail.sibling=null:M.sibling=null}}function _r(h){var m=h.alternate!==null&&h.alternate.child===h.child,_=0,M=0;if(m)for(var P=h.child;P!==null;)_|=P.lanes|P.childLanes,M|=P.subtreeFlags&65011712,M|=P.flags&65011712,P.return=h,P=P.sibling;else for(P=h.child;P!==null;)_|=P.lanes|P.childLanes,M|=P.subtreeFlags,M|=P.flags,P.return=h,P=P.sibling;return h.subtreeFlags|=M,h.childLanes=_,m}function nre(h,m,_){var M=m.pendingProps;switch(n2(m),m.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return _r(m),null;case 1:return _r(m),null;case 3:return _=m.stateNode,M=null,h!==null&&(M=h.memoizedState.cache),m.memoizedState.cache!==M&&(m.flags|=2048),$l(ti),pe(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(h===null||h.child===null)&&(zf(m)?ql(m):h===null||h.memoizedState.isDehydrated&&(m.flags&256)===0||(m.flags|=1024,i2())),_r(m),null;case 26:var P=m.type,D=m.memoizedState;return h===null?(ql(m),D!==null?(_r(m),YI(m,D)):(_r(m),K2(m,P,null,M,_))):D?D!==h.memoizedState?(ql(m),_r(m),YI(m,D)):(_r(m),m.flags&=-16777217):(h=h.memoizedProps,h!==M&&ql(m),_r(m),K2(m,P,h,M,_)),null;case 27:if(ke(m),_=le.current,P=m.type,h!==null&&m.stateNode!=null)h.memoizedProps!==M&&ql(m);else{if(!M){if(m.stateNode===null)throw Error(r(166));return _r(m),null}h=G.current,zf(m)?C4(m):(h=rF(P,M,_),m.stateNode=h,ql(m))}return _r(m),null;case 5:if(ke(m),P=m.type,h!==null&&m.stateNode!=null)h.memoizedProps!==M&&ql(m);else{if(!M){if(m.stateNode===null)throw Error(r(166));return _r(m),null}if(D=G.current,zf(m))C4(m);else{var Y=dv(le.current);switch(D){case 1:D=Y.createElementNS("http://www.w3.org/2000/svg",P);break;case 2:D=Y.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;default:switch(P){case"svg":D=Y.createElementNS("http://www.w3.org/2000/svg",P);break;case"math":D=Y.createElementNS("http://www.w3.org/1998/Math/MathML",P);break;case"script":D=Y.createElement("div"),D.innerHTML=" - + +