diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3a1ac58..cda8dbef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ All notable changes to Bambuddy will be documented in this file. - **Slice modal: pick the build plate (#1337, reported by @digitalskies)** — Slicing a plain STL through the integrated slicer always defaulted to whatever `curr_bed_type` lived in the chosen process preset (typically `Cool Plate`), which the slicer CLI then rejected for high-temp filaments with `Plate 1: Cool Plate does not support filament 1`. The user had no way to switch plates short of cloning the process preset in BambuStudio, which defeats the point of the in-app slicer. The Slice modal now exposes a `Build plate` dropdown with the six canonical BambuStudio / OrcaSlicer plates (Cool Plate, Cool Plate SuperTack, Engineering Plate, High Temp Plate, Textured PEI Plate, Smooth PEI Plate) plus an explicit `Auto (use process preset)` option that preserves the previous behavior. The dropdown sits between Process profile and Filament rows so it stays visible regardless of how many filament slots the picked plate uses (a long filament list would otherwise push it off the modal's `max-h-[85vh]` scroll viewport) and is **always enabled** — including when the user picks a Printer Preset Bundle from the top BundlePicker. When the user picks a specific plate, the new `bed_type` field on `SliceRequest` ([`backend/app/schemas/slicer.py`](backend/app/schemas/slicer.py)) flows through the dispatcher via two paths: (1) **resolved-preset path** — the route helper `_patch_process_bed_type` in [`backend/app/api/routes/library.py`](backend/app/api/routes/library.py) overwrites `curr_bed_type` on the resolved process JSON before forwarding to the sidecar (no preset cloning required); (2) **bundle dispatch path** — `slice_with_bundle` in [`backend/app/services/slicer_api.py`](backend/app/services/slicer_api.py) adds a `bedType` form field to the sidecar multipart so the sidecar can pass `--curr_bed_type` through to the CLI, which lets the override take effect even though Bambuddy can't patch the bundle's process JSON locally (the sidecar materialises it from the stored .bbscfg). Sidecar versions that don't recognise the field silently no-op — the slice still runs, just with the bundle's default plate; the slicer-API fork at maziggy/orca-slicer-api will need the matching change for the bundle path to take full effect. **i18n parity:** 8 new keys (`slice.bedType.{label,auto,coolPlate,coolPlateSuperTack,engineering,highTemp,texturedPEI,smoothPEI}`) added to all 8 locales — full German translation, English fallbacks elsewhere per project convention. **Regression tests:** 4 in [`test_slice_request_bed_type.py`](backend/tests/unit/test_slice_request_bed_type.py) (`bed_type` defaults to None, accepts the six canonical strings, rejects overlong input via the schema's `max_length=64`; `_patch_process_bed_type` overwrites an existing value, adds the field when missing, and returns the input unchanged for malformed JSON or non-dict roots), 4 in [`test_library_slice_api.py`](backend/tests/integration/test_library_slice_api.py) (resolved-preset path: with `bed_type` set, the sidecar receives `"curr_bed_type": "Textured PEI Plate"` in the presetProfile multipart part; without it, `curr_bed_type` stays out of the body entirely. bundle dispatch path: `bedType` form field carries the override through to the sidecar; omitting `bed_type` keeps the form field out of the request so the bundle's own `curr_bed_type` is preserved), 2 in [`SliceModal.test.tsx`](frontend/src/__tests__/components/SliceModal.test.tsx) (dropdown selection puts `bed_type` on the request; leaving it on Auto omits the field). 59 backend slice tests + 34 SliceModal tests pass; build and i18n parity script clean. ### Fixed +- **Print Queue page no longer 404-storms thumbnail / plates / plate-thumbnail when an item points at a soft-deleted archive, and pending queue items for a soft-deleted archive are now cancelled with a clear reason instead of silently stuck-pending forever** ([#1348](https://github.com/maziggy/bambuddy/issues/1348) follow-up) — Symptom: opening the Queue page or any of its sub-views fired `GET /archives/{id}/thumbnail`, `GET /archives/{id}/plates`, and `GET /archives/{id}/plate-thumbnail/{n}` for queue rows pointing at archives that had been soft-deleted (#1343 leaves the row but removes files from disk), all returning 404. Frontend's `onError` handler hid the broken `` so it was visually clean, but the network tab and any pending-print logic still saw three 404s per affected row. Two underlying problems wearing one mask: cosmetic 404 storm, AND functional — a queue item whose 3MF was removed from disk can never actually dispatch, so it sits in `pending` forever with no clue to the user about why. **Two-part fix in the same shape as the print-log followup above:** (1) New helper `_cancel_pending_queue_items(db, archive_id)` in [`backend/app/services/archive.py`](backend/app/services/archive.py) sets `status='cancelled'` + `waiting_reason='Source archive deleted'` on every pending queue item linked to the archive; called from `soft_delete_archive` alongside the existing `_null_print_log_thumbnail_paths` cleanup. Only `pending` is touched — `printing` is a rare race that the printer-side fail-path catches anyway, and completed / failed / cancelled rows are historical audit-trail. Hard-delete is already covered by `ON DELETE CASCADE` on `print_queue.archive_id`. (2) Queue API serializer in [`backend/app/api/routes/print_queue.py:224`](backend/app/api/routes/print_queue.py) now checks `item.archive.deleted_at` before populating any archive-derived field — when soft-deleted, the whole block (`archive_name`, `archive_thumbnail`, `print_time_seconds`, `filament_used_grams`, plate-specific re-reads, …) is skipped and the new `archive_deleted: bool = True` flag on [`PrintQueueItemResponse`](backend/app/schemas/print_queue.py) signals the soft-deleted state. The `archive_thumbnail` suppression alone covers the thumbnail render in [`QueuePage.tsx:434`](frontend/src/pages/QueuePage.tsx), [`CompactHistoryRow.tsx:45`](frontend/src/components/CompactHistoryRow.tsx), and [`QueueTimelineView.tsx:72`](frontend/src/components/QueueTimelineView.tsx) because they all gate on it. The `/plates` query at [`QueuePage.tsx:329`](frontend/src/pages/QueuePage.tsx) was gated on `archive_id` only — `archive_id` is the real FK and stays exposed in the response (the queue scheduler still needs it for audit / dispatch checks), so the query was added a new `&& !item.archive_deleted` clause to respect the new flag. **Regression tests** in [`backend/tests/integration/test_print_queue_api.py`](backend/tests/integration/test_print_queue_api.py): `test_soft_delete_archive_cancels_pending_queue_items` pins the cancel-only-pending behavior (completed rows untouched), `test_queue_api_hides_archive_surface_when_soft_deleted` pins the suppression + `archive_deleted=True` for soft-deleted archives, `test_queue_api_still_exposes_archive_surface_when_live` pins the sanity guard that live archives' fields keep flowing through. All 3 new tests + 146 in the queue/archives/obico sweep pass; ruff clean. + - **Print log no longer 404-storms on the thumbnail endpoint for entries whose archive was deleted or whose print failed before a thumbnail was extracted** ([#1348](https://github.com/maziggy/bambuddy/issues/1348) follow-up) — Symptom in DevTools when opening Archives → Print Log: a 404 per orphaned entry per render. Visually clean (the `` handler in [`frontend/src/pages/ArchivesPage.tsx:3763`](frontend/src/pages/ArchivesPage.tsx) hides the broken image), but noisy and wasteful. **Root cause** in [`backend/app/api/routes/print_log.py:91-113`](backend/app/api/routes/print_log.py): `PrintLogEntry.thumbnail_path` is copied by value from `archive.thumbnail_path` at write-time ([`main.py:3615`](backend/app/main.py)) but the FK on `archive_id` is `ON DELETE SET NULL` (#1378) — so log entries survive archive deletion to preserve stats history, but the cached thumbnail_path string keeps pointing at a file that was removed when the archive's directory was deleted. Same shape for failed prints that recorded an expected thumbnail path before the extractor wrote (or skipped writing) the file. **Two-part fix:** (1) `get_print_log_thumbnail` self-heals when the file is missing — it NULLs `thumbnail_path` on the entry and commits before returning 404, so the frontend's `entry.thumbnail_path && ` gate keeps the next page render from re-requesting. (2) New helper `_null_print_log_thumbnail_paths(db, archive_id)` in [`backend/app/services/archive.py`](backend/app/services/archive.py) is called from `soft_delete_archive` and `delete_archive` before the on-disk files are removed — eager clear so future deletes don't cause the one-time storm at all. The route handler covers stragglers (failed prints, files manually moved, etc.). **Regression tests** in [`backend/tests/integration/test_archives_api.py`](backend/tests/integration/test_archives_api.py): `test_soft_delete_clears_thumbnail_path_on_linked_log_entries` pins the eager clear on the soft-delete route, `test_hard_delete_clears_thumbnail_path_before_fk_cascade` pins it on `ArchiveService.delete_archive` (used by the auto-purge sweeper), `test_print_log_thumbnail_route_lazy_nulls_missing_file` pins the route's self-heal for failed-print orphans where the file was never written. All 4 new tests + 61 in the archives/print-log sweep pass; ruff clean. - **Camera stream no longer freezes every ~30 s when Obico fault detection is enabled on the same printer the user is viewing** ([#1348](https://github.com/maziggy/bambuddy/issues/1348), reported by @SL666) — Symptom on an X1-class printer running firmware that allows only one concurrent camera connection: opening the live camera worked initially, then the stream hung within seconds and "cancelled" — clicking the in-UI refresh restored it for another few seconds before it hung again, ad infinitum. Disabling Obico fault detection made the stream stable. **Root cause** in `_capture_frame` at [`backend/app/services/obico_detection.py:209-220`](backend/app/services/obico_detection.py): the buffer-reuse path that was supposed to make Obico polling free (#1271) only worked when `_last_frames[printer_id]` was populated. In every race window where `_active_streams` had a registered entry but the JPEG buffer was empty — stream startup before the first frame lands (1–3 s on RTSP), or any moment the upstream ffmpeg was mid-reconnect after a 30 s read timeout — `try_get_active_buffered_frame(printer_id)` returned `None` and the caller fell through to `capture_camera_frame_bytes(...)`, which spawned its own ffmpeg + TLS proxy and opened a competing RTSP socket on the printer. On firmwares that only allow one camera connection, that second socket forced the printer to drop the live fan-out connection — viewers' ffmpeg hit its 30 s read timeout, looped through 30 reconnect attempts at 0.2 s each (all racing the next Obico poll 10 s later), exhausted retries, and the broadcaster pump exited. The user's viewer disconnected with no obvious cause in the log because the Obico capture itself looked successful (`Successfully captured camera frame bytes: 83484 bytes`). **Fix:** the buffer-reuse gate was widened from "do we have a frame in the buffer?" to "is any fan-out stream registered for this printer?" — even when the buffer is momentarily empty. New helper `is_stream_active(printer_id) -> bool` at [`backend/app/api/routes/camera.py:82`](backend/app/api/routes/camera.py) checks `_active_streams` / `_active_chamber_streams` independently of buffer state. `_capture_frame` now consults `is_stream_active` first: if True, it returns the buffered frame when available or `None` (skip this poll cycle) when not — it **never** opens a competing socket while a viewer is attached, regardless of buffer state. The poll loop retries 10 s later, by which time the buffer is virtually guaranteed to be populated. The fresh-socket path still fires unchanged when no viewer is connected (Obico's primary use case: detection on idle/headless prints). Cost of the fix: at most one missed Obico detection cycle per viewer-attach (~10 s lag); benefit: zero competing-socket events while any viewer is connected. `try_get_active_buffered_frame` was refactored to delegate to `is_stream_active` so the two helpers stay in lockstep — its `/camera/snapshot` caller at [`camera.py:859`](backend/app/api/routes/camera.py) is unchanged behaviorally (snapshot is user-initiated single-shot; falling through to a fresh capture if buffer is momentarily empty is the desired behavior there). **Regression tests** in [`backend/tests/unit/test_obico_detection.py`](backend/tests/unit/test_obico_detection.py): new `test_skips_poll_when_stream_active_but_buffer_empty` reproduces the exact race (viewer registered, buffer empty) and pins that `_capture_frame` returns `None` and `capture_camera_frame_bytes` is NOT called; existing `test_returns_buffered_frame_when_stream_active` and `test_falls_back_to_fresh_capture_when_no_stream` were updated to patch the new `is_stream_active` helper alongside `try_get_active_buffered_frame`. All 31 obico tests + 113 in the wider camera/obico sweep pass; ruff clean. diff --git a/backend/app/api/routes/print_queue.py b/backend/app/api/routes/print_queue.py index ebe5642ed..3888ea7ea 100644 --- a/backend/app/api/routes/print_queue.py +++ b/backend/app/api/routes/print_queue.py @@ -222,24 +222,36 @@ def _enrich_response(item: PrintQueueItem) -> PrintQueueItemResponse: } response = PrintQueueItemResponse(**item_dict) if item.archive: - response.archive_name = item.archive.print_name or item.archive.filename - response.archive_thumbnail = item.archive.thumbnail_path - response.print_time_seconds = item.archive.print_time_seconds - response.filament_used_grams = item.archive.filament_used_grams - response.filament_type = item.archive.filament_type - response.filament_color = item.archive.filament_color - response.layer_height = item.archive.layer_height - response.nozzle_diameter = item.archive.nozzle_diameter - response.sliced_for_model = item.archive.sliced_for_model - if item.plate_id: - archive_path = settings.base_dir / item.archive.file_path - if archive_path.exists(): - plate_time = _extract_print_time_from_3mf(archive_path, item.plate_id) - plate_weight = sum(f["used_g"] for f in extract_filament_usage_from_3mf(archive_path, item.plate_id)) - if plate_time is not None: - response.print_time_seconds = plate_time - if plate_weight > 0: - response.filament_used_grams = plate_weight + # Soft-deleted archive: files are gone from disk but the row stays + # (its filament/cost contribution still flows into stats per #1343). + # Suppress the archive-derived UI surface so the queue page doesn't + # 404-storm the thumbnail / plates / plate-thumbnail endpoints — the + # frontend's existing truthy gate on archive_thumbnail covers it + # (#1348 follow-up). The archive_deleted flag lets the UI render a + # "source deleted" badge on these rows. + if item.archive.deleted_at is not None: + response.archive_deleted = True + else: + response.archive_name = item.archive.print_name or item.archive.filename + response.archive_thumbnail = item.archive.thumbnail_path + response.print_time_seconds = item.archive.print_time_seconds + response.filament_used_grams = item.archive.filament_used_grams + response.filament_type = item.archive.filament_type + response.filament_color = item.archive.filament_color + response.layer_height = item.archive.layer_height + response.nozzle_diameter = item.archive.nozzle_diameter + response.sliced_for_model = item.archive.sliced_for_model + if item.plate_id: + archive_path = settings.base_dir / item.archive.file_path + if archive_path.exists(): + plate_time = _extract_print_time_from_3mf(archive_path, item.plate_id) + plate_weight = sum( + f["used_g"] for f in extract_filament_usage_from_3mf(archive_path, item.plate_id) + ) + if plate_time is not None: + response.print_time_seconds = plate_time + if plate_weight > 0: + response.filament_used_grams = plate_weight if item.library_file: response.library_file_name = ( item.library_file.file_metadata.get("print_name") if item.library_file.file_metadata else None diff --git a/backend/app/schemas/print_queue.py b/backend/app/schemas/print_queue.py index ad192b086..a672bfa35 100644 --- a/backend/app/schemas/print_queue.py +++ b/backend/app/schemas/print_queue.py @@ -104,6 +104,13 @@ class PrintQueueItemResponse(BaseModel): # Nested info for UI (populated in route) archive_name: str | None = None archive_thumbnail: str | None = None + # True when the linked archive has been soft-deleted (its files are gone + # from disk). In that case the *archive_name* / *archive_thumbnail* / + # downstream metadata fields are intentionally left None so the frontend + # doesn't 404-storm the now-missing thumbnail / plates / plate-thumbnail + # endpoints (#1348 follow-up). Frontends can render a "source deleted" + # badge based on this flag. + archive_deleted: bool = False library_file_name: str | None = None # Name of library file (if library_file_id is set) library_file_thumbnail: str | None = None # Thumbnail of library file printer_name: str | None = None diff --git a/backend/app/services/archive.py b/backend/app/services/archive.py index d709c91ec..cc3928846 100644 --- a/backend/app/services/archive.py +++ b/backend/app/services/archive.py @@ -845,6 +845,30 @@ async def _null_print_log_thumbnail_paths(db: AsyncSession, archive_id: int) -> await db.execute(sa_update(PrintLogEntry).where(PrintLogEntry.archive_id == archive_id).values(thumbnail_path=None)) +async def _cancel_pending_queue_items(db: AsyncSession, archive_id: int) -> None: + """Cancel pending queue items pointing at *archive_id* (#1348 follow-up). + + Called from ``soft_delete_archive`` only — hard-delete is covered by the + ``ON DELETE CASCADE`` on ``print_queue.archive_id``. A queue item + pointing at an archive whose 3MF has been removed from disk can never + actually dispatch, so cancelling at delete time both (a) tells the user + why the item disappeared from the pending list, and (b) stops the queue + page from 404-storming the archive thumbnail / plates / plate-thumbnail + endpoints when the row is rendered. Only ``pending`` items are touched; + ``printing`` is a rare race the printer-side fail-path catches, and + completed / failed / cancelled rows are historical and untouched. + """ + from sqlalchemy import update as sa_update + + from backend.app.models.print_queue import PrintQueueItem + + await db.execute( + sa_update(PrintQueueItem) + .where(PrintQueueItem.archive_id == archive_id, PrintQueueItem.status == "pending") + .values(status="cancelled", waiting_reason="Source archive deleted") + ) + + class ArchiveService: """Service for archiving print jobs.""" @@ -1271,6 +1295,7 @@ class ArchiveService: dir_to_delete = self._resolve_archive_dir_for_delete(archive) await _null_print_log_thumbnail_paths(self.db, archive_id) + await _cancel_pending_queue_items(self.db, archive_id) archive.deleted_at = datetime.now(timezone.utc) await self.db.commit() diff --git a/backend/tests/integration/test_print_queue_api.py b/backend/tests/integration/test_print_queue_api.py index fbffe6458..2f23d19a2 100644 --- a/backend/tests/integration/test_print_queue_api.py +++ b/backend/tests/integration/test_print_queue_api.py @@ -1833,3 +1833,85 @@ class TestAbortedStatusNormalisation: """Verify 404 for non-existent batch.""" response = await async_client.get("/api/v1/queue/batches/9999") assert response.status_code == 404 + + # ======================================================================== + # Soft-deleted archive handling (#1348 follow-up) + # ======================================================================== + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_soft_delete_archive_cancels_pending_queue_items( + self, async_client: AsyncClient, printer_factory, archive_factory, queue_item_factory, db_session + ): + """Soft-deleting an archive cancels its pending queue items with a + clear reason. The 3MF is gone from disk so the item can never + dispatch — leaving it in 'pending' would 404-storm the queue page + and confuse the user about why nothing prints.""" + from backend.app.services.archive import ArchiveService + + printer = await printer_factory() + archive = await archive_factory(thumbnail_path="archives/test/test/thumbnail.png") + pending = await queue_item_factory(printer_id=printer.id, archive_id=archive.id, status="pending") + completed = await queue_item_factory(printer_id=printer.id, archive_id=archive.id, status="completed") + + service = ArchiveService(db_session) + assert await service.soft_delete_archive(archive.id) is True + + await db_session.refresh(pending) + await db_session.refresh(completed) + assert pending.status == "cancelled" + assert pending.waiting_reason == "Source archive deleted" + # Historical rows untouched — they're audit-trail. + assert completed.status == "completed" + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_queue_api_hides_archive_surface_when_soft_deleted( + self, async_client: AsyncClient, printer_factory, archive_factory, queue_item_factory, db_session + ): + """Queue serializer must NOT populate archive_thumbnail / archive_name + when the archive is soft-deleted — otherwise the frontend renders a + broken and 404-storms the thumbnail / plates / plate-thumbnail + endpoints. archive_deleted=True signals the soft-deleted state so + the UI can render a 'source deleted' badge.""" + from datetime import datetime, timezone + + printer = await printer_factory() + archive = await archive_factory( + print_name="Test Print", + thumbnail_path="archives/test/test/thumbnail.png", + deleted_at=datetime.now(timezone.utc), # Pre-soft-deleted + ) + item = await queue_item_factory(printer_id=printer.id, archive_id=archive.id, status="cancelled") + + resp = await async_client.get("/api/v1/queue/") + assert resp.status_code == 200 + body = resp.json() + row = next((r for r in body if r["id"] == item.id), None) + assert row is not None + assert row["archive_deleted"] is True + assert row["archive_thumbnail"] is None, "must not expose stale thumbnail path for soft-deleted archive" + assert row["archive_name"] is None + + @pytest.mark.asyncio + @pytest.mark.integration + async def test_queue_api_still_exposes_archive_surface_when_live( + self, async_client: AsyncClient, printer_factory, archive_factory, queue_item_factory, db_session + ): + """Sanity guard: the soft-delete suppression must not affect live + archives. archive_name / archive_thumbnail still flow through and + archive_deleted stays False.""" + printer = await printer_factory() + archive = await archive_factory( + print_name="Live Archive", + thumbnail_path="archives/test/live/thumbnail.png", + ) + item = await queue_item_factory(printer_id=printer.id, archive_id=archive.id, status="pending") + + resp = await async_client.get("/api/v1/queue/") + assert resp.status_code == 200 + row = next((r for r in resp.json() if r["id"] == item.id), None) + assert row is not None + assert row["archive_deleted"] is False + assert row["archive_name"] == "Live Archive" + assert row["archive_thumbnail"] == "archives/test/live/thumbnail.png" diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 88372bb2c..e49045ebb 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1691,6 +1691,10 @@ export interface PrintQueueItem { created_at: string; archive_name?: string | null; archive_thumbnail?: string | null; + // True when the linked archive has been soft-deleted; archive_name / + // archive_thumbnail / downstream metadata are left null in that case so + // the UI doesn't 404-storm the now-missing endpoints (#1348 follow-up). + archive_deleted?: boolean; library_file_name?: string | null; library_file_thumbnail?: string | null; printer_name?: string | null; diff --git a/frontend/src/pages/QueuePage.tsx b/frontend/src/pages/QueuePage.tsx index 122f734f8..a106d6d6c 100644 --- a/frontend/src/pages/QueuePage.tsx +++ b/frontend/src/pages/QueuePage.tsx @@ -325,11 +325,13 @@ function SortableQueueItem({ // Determine if we're printing a library file const isLibraryFile = !!item.library_file_id && !item.archive_id; - // Fetch archive plate details + // Fetch archive plate details. Skip when the linked archive has been + // soft-deleted (#1348 follow-up): its 3MF is gone from disk so the + // /plates endpoint just 404-storms the queue page. const { data: archivePlatesData } = useQuery({ queryKey: ['archive-plates', item.archive_id], queryFn: () => api.getArchivePlates(item.archive_id!), - enabled: !!item.archive_id && !isLibraryFile, + enabled: !!item.archive_id && !isLibraryFile && !item.archive_deleted, }); // Fetch library file plate details diff --git a/static/assets/index-CRZ2F5mH.js b/static/assets/index-CFzqkJEl.js similarity index 99% rename from static/assets/index-CRZ2F5mH.js rename to static/assets/index-CFzqkJEl.js index d49eac41b..f8f0206c7 100644 --- a/static/assets/index-CRZ2F5mH.js +++ b/static/assets/index-CFzqkJEl.js @@ -8689,7 +8689,7 @@ Accuracy: ${t.time_accuracy.toFixed(0)}%`:t.actual_time_seconds?`Actual: ${ls(t. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel. `},Ctt={onDragStart(t){let{active:e}=t;return"Picked up draggable item "+e.id+"."},onDragOver(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was moved over droppable area "+n.id+".":"Draggable item "+e.id+" is no longer over a droppable area."},onDragEnd(t){let{active:e,over:n}=t;return n?"Draggable item "+e.id+" was dropped over droppable area "+n.id:"Draggable item "+e.id+" was dropped."},onDragCancel(t){let{active:e}=t;return"Dragging was cancelled. Draggable item "+e.id+" was dropped."}};function Ptt(t){let{announcements:e=Ctt,container:n,hiddenTextDescribedById:a,screenReaderInstructions:i=Ntt}=t;const{announce:s,announcement:o}=Stt(),l=x_("DndLiveRegion"),[c,d]=y.useState(!1);if(y.useEffect(()=>{d(!0)},[]),_tt(y.useMemo(()=>({onDragStart(m){let{active:p}=m;s(e.onDragStart({active:p}))},onDragMove(m){let{active:p,over:f}=m;e.onDragMove&&s(e.onDragMove({active:p,over:f}))},onDragOver(m){let{active:p,over:f}=m;s(e.onDragOver({active:p,over:f}))},onDragEnd(m){let{active:p,over:f}=m;s(e.onDragEnd({active:p,over:f}))},onDragCancel(m){let{active:p,over:f}=m;s(e.onDragCancel({active:p,over:f}))}}),[s,e])),!c)return null;const u=ua.createElement(ua.Fragment,null,ua.createElement(vtt,{id:a,value:i.draggable}),ua.createElement(wtt,{id:l,announcement:o}));return n?lc.createPortal(u,n):u}var Ii;(function(t){t.DragStart="dragStart",t.DragMove="dragMove",t.DragEnd="dragEnd",t.DragCancel="dragCancel",t.DragOver="dragOver",t.RegisterDroppable="registerDroppable",t.SetDroppableDisabled="setDroppableDisabled",t.UnregisterDroppable="unregisterDroppable"})(Ii||(Ii={}));function mT(){}function pT(t,e){return y.useMemo(()=>({sensor:t,options:e??{}}),[t,e])}function rce(){for(var t=arguments.length,e=new Array(t),n=0;n[...e].filter(a=>a!=null),[...e])}const hd=Object.freeze({x:0,y:0});function ace(t,e){return Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2))}function ice(t,e){let{data:{value:n}}=t,{data:{value:a}}=e;return n-a}function Ttt(t,e){let{data:{value:n}}=t,{data:{value:a}}=e;return a-n}function zY(t){let{left:e,top:n,height:a,width:i}=t;return[{x:e,y:n},{x:e+i,y:n},{x:e,y:n+a},{x:e+i,y:n+a}]}function sce(t,e){if(!t||t.length===0)return null;const[n]=t;return n[e]}function UY(t,e,n){return e===void 0&&(e=t.left),n===void 0&&(n=t.top),{x:e+t.width*.5,y:n+t.height*.5}}const oce=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=t;const i=UY(e,e.left,e.top),s=[];for(const o of a){const{id:l}=o,c=n.get(l);if(c){const d=ace(UY(c),i);s.push({id:l,data:{droppableContainer:o,value:d}})}}return s.sort(ice)},Att=t=>{let{collisionRect:e,droppableRects:n,droppableContainers:a}=t;const i=zY(e),s=[];for(const o of a){const{id:l}=o,c=n.get(l);if(c){const d=zY(c),u=i.reduce((p,f,g)=>p+ace(d[g],f),0),m=Number((u/4).toFixed(4));s.push({id:l,data:{droppableContainer:o,value:m}})}}return s.sort(ice)};function jtt(t,e){const n=Math.max(e.top,t.top),a=Math.max(e.left,t.left),i=Math.min(e.left+e.width,t.left+t.width),s=Math.min(e.top+e.height,t.top+t.height),o=i-a,l=s-n;if(a{let{collisionRect:e,droppableRects:n,droppableContainers:a}=t;const i=[];for(const s of a){const{id:o}=s,l=n.get(o);if(l){const c=jtt(l,e);c>0&&i.push({id:o,data:{droppableContainer:s,value:c}})}}return i.sort(Ttt)};function Ett(t,e,n){return{...t,scaleX:e&&n?e.width/n.width:1,scaleY:e&&n?e.height/n.height:1}}function lce(t,e){return t&&e?{x:t.left-e.left,y:t.top-e.top}:hd}function Dtt(t){return function(n){for(var a=arguments.length,i=new Array(a>1?a-1:0),s=1;s({...o,top:o.top+t*l.y,bottom:o.bottom+t*l.y,left:o.left+t*l.x,right:o.right+t*l.x}),{...n})}}const Ftt=Dtt(1);function Ltt(t){if(t.startsWith("matrix3d(")){const e=t.slice(9,-1).split(/, /);return{x:+e[12],y:+e[13],scaleX:+e[0],scaleY:+e[5]}}else if(t.startsWith("matrix(")){const e=t.slice(7,-1).split(/, /);return{x:+e[4],y:+e[5],scaleX:+e[0],scaleY:+e[3]}}return null}function Rtt(t,e,n){const a=Ltt(e);if(!a)return t;const{scaleX:i,scaleY:s,x:o,y:l}=a,c=t.left-o-(1-i)*parseFloat(n),d=t.top-l-(1-s)*parseFloat(n.slice(n.indexOf(" ")+1)),u=i?t.width/i:t.width,m=s?t.height/s:t.height;return{width:u,height:m,top:d,right:c+u,bottom:d+m,left:c}}const Itt={ignoreTransform:!1};function hv(t,e){e===void 0&&(e=Itt);let n=t.getBoundingClientRect();if(e.ignoreTransform){const{transform:d,transformOrigin:u}=nl(t).getComputedStyle(t);d&&(n=Rtt(n,d,u))}const{top:a,left:i,width:s,height:o,bottom:l,right:c}=n;return{top:a,left:i,width:s,height:o,bottom:l,right:c}}function BY(t){return hv(t,{ignoreTransform:!0})}function Ott(t){const e=t.innerWidth,n=t.innerHeight;return{top:0,left:0,right:e,bottom:n,width:e,height:n}}function ztt(t,e){return e===void 0&&(e=nl(t).getComputedStyle(t)),e.position==="fixed"}function Utt(t,e){e===void 0&&(e=nl(t).getComputedStyle(t));const n=/(auto|scroll|overlay)/;return["overflow","overflowX","overflowY"].some(i=>{const s=e[i];return typeof s=="string"?n.test(s):!1})}function ZA(t,e){const n=[];function a(i){if(e!=null&&n.length>=e||!i)return n;if(Z5(i)&&i.scrollingElement!=null&&!n.includes(i.scrollingElement))return n.push(i.scrollingElement),n;if(!g_(i)||ece(i)||n.includes(i))return n;const s=nl(t).getComputedStyle(i);return i!==t&&Utt(i,s)&&n.push(i),ztt(i,s)?n:a(i.parentNode)}return t?a(t):n}function cce(t){const[e]=ZA(t,1);return e??null}function tF(t){return!QA||!t?null:mv(t)?t:Q5(t)?Z5(t)||t===pv(t).scrollingElement?window:g_(t)?t:null:null}function dce(t){return mv(t)?t.scrollX:t.scrollLeft}function uce(t){return mv(t)?t.scrollY:t.scrollTop}function $I(t){return{x:dce(t),y:uce(t)}}var as;(function(t){t[t.Forward=1]="Forward",t[t.Backward=-1]="Backward"})(as||(as={}));function mce(t){return!QA||!t?!1:t===document.scrollingElement}function pce(t){const e={x:0,y:0},n=mce(t)?{height:window.innerHeight,width:window.innerWidth}:{height:t.clientHeight,width:t.clientWidth},a={x:t.scrollWidth-n.width,y:t.scrollHeight-n.height},i=t.scrollTop<=e.y,s=t.scrollLeft<=e.x,o=t.scrollTop>=a.y,l=t.scrollLeft>=a.x;return{isTop:i,isLeft:s,isBottom:o,isRight:l,maxScroll:a,minScroll:e}}const Btt={x:.2,y:.2};function Htt(t,e,n,a,i){let{top:s,left:o,right:l,bottom:c}=n;a===void 0&&(a=10),i===void 0&&(i=Btt);const{isTop:d,isBottom:u,isLeft:m,isRight:p}=pce(t),f={x:0,y:0},g={x:0,y:0},w={height:e.height*i.y,width:e.width*i.x};return!d&&s<=e.top+w.height?(f.y=as.Backward,g.y=a*Math.abs((e.top+w.height-s)/w.height)):!u&&c>=e.bottom-w.height&&(f.y=as.Forward,g.y=a*Math.abs((e.bottom-w.height-c)/w.height)),!p&&l>=e.right-w.width?(f.x=as.Forward,g.x=a*Math.abs((e.right-w.width-l)/w.width)):!m&&o<=e.left+w.width&&(f.x=as.Backward,g.x=a*Math.abs((e.left+w.width-o)/w.width)),{direction:f,speed:g}}function qtt(t){if(t===document.scrollingElement){const{innerWidth:s,innerHeight:o}=window;return{top:0,left:0,right:s,bottom:o,width:s,height:o}}const{top:e,left:n,right:a,bottom:i}=t.getBoundingClientRect();return{top:e,left:n,right:a,bottom:i,width:t.clientWidth,height:t.clientHeight}}function hce(t){return t.reduce((e,n)=>hy(e,$I(n)),hd)}function $tt(t){return t.reduce((e,n)=>e+dce(n),0)}function Vtt(t){return t.reduce((e,n)=>e+uce(n),0)}function Gtt(t,e){if(e===void 0&&(e=hv),!t)return;const{top:n,left:a,bottom:i,right:s}=e(t);cce(t)&&(i<=0||s<=0||n>=window.innerHeight||a>=window.innerWidth)&&t.scrollIntoView({block:"center",inline:"center"})}const Wtt=[["x",["left","right"],$tt],["y",["top","bottom"],Vtt]];class tU{constructor(e,n){this.rect=void 0,this.width=void 0,this.height=void 0,this.top=void 0,this.bottom=void 0,this.right=void 0,this.left=void 0;const a=ZA(n),i=hce(a);this.rect={...e},this.width=e.width,this.height=e.height;for(const[s,o,l]of Wtt)for(const c of o)Object.defineProperty(this,c,{get:()=>{const d=l(a),u=i[s]-d;return this.rect[c]+u},enumerable:!0});Object.defineProperty(this,"rect",{enumerable:!1})}}class xw{constructor(e){this.target=void 0,this.listeners=[],this.removeAll=()=>{this.listeners.forEach(n=>{var a;return(a=this.target)==null?void 0:a.removeEventListener(...n)})},this.target=e}add(e,n,a){var i;(i=this.target)==null||i.addEventListener(e,n,a),this.listeners.push([e,n,a])}}function Ktt(t){const{EventTarget:e}=nl(t);return t instanceof e?t:pv(t)}function nF(t,e){const n=Math.abs(t.x),a=Math.abs(t.y);return typeof e=="number"?Math.sqrt(n**2+a**2)>e:"x"in e&&"y"in e?n>e.x&&a>e.y:"x"in e?n>e.x:"y"in e?a>e.y:!1}var tc;(function(t){t.Click="click",t.DragStart="dragstart",t.Keydown="keydown",t.ContextMenu="contextmenu",t.Resize="resize",t.SelectionChange="selectionchange",t.VisibilityChange="visibilitychange"})(tc||(tc={}));function HY(t){t.preventDefault()}function Xtt(t){t.stopPropagation()}var Ar;(function(t){t.Space="Space",t.Down="ArrowDown",t.Right="ArrowRight",t.Left="ArrowLeft",t.Up="ArrowUp",t.Esc="Escape",t.Enter="Enter",t.Tab="Tab"})(Ar||(Ar={}));const fce={start:[Ar.Space,Ar.Enter],cancel:[Ar.Esc],end:[Ar.Space,Ar.Enter,Ar.Tab]},Ytt=(t,e)=>{let{currentCoordinates:n}=e;switch(t.code){case Ar.Right:return{...n,x:n.x+25};case Ar.Left:return{...n,x:n.x-25};case Ar.Down:return{...n,y:n.y+25};case Ar.Up:return{...n,y:n.y-25}}};class JA{constructor(e){this.props=void 0,this.autoScrollEnabled=!1,this.referenceCoordinates=void 0,this.listeners=void 0,this.windowListeners=void 0,this.props=e;const{event:{target:n}}=e;this.props=e,this.listeners=new xw(pv(n)),this.windowListeners=new xw(nl(n)),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleCancel=this.handleCancel.bind(this),this.attach()}attach(){this.handleStart(),this.windowListeners.add(tc.Resize,this.handleCancel),this.windowListeners.add(tc.VisibilityChange,this.handleCancel),setTimeout(()=>this.listeners.add(tc.Keydown,this.handleKeyDown))}handleStart(){const{activeNode:e,onStart:n}=this.props,a=e.node.current;a&&Gtt(a),n(hd)}handleKeyDown(e){if(eU(e)){const{active:n,context:a,options:i}=this.props,{keyboardCodes:s=fce,coordinateGetter:o=Ytt,scrollBehavior:l="smooth"}=i,{code:c}=e;if(s.end.includes(c)){this.handleEnd(e);return}if(s.cancel.includes(c)){this.handleCancel(e);return}const{collisionRect:d}=a.current,u=d?{x:d.left,y:d.top}:hd;this.referenceCoordinates||(this.referenceCoordinates=u);const m=o(e,{active:n,context:a.current,currentCoordinates:u});if(m){const p=yS(m,u),f={x:0,y:0},{scrollableAncestors:g}=a.current;for(const w of g){const b=e.code,{isTop:x,isRight:S,isLeft:N,isBottom:C,maxScroll:P,minScroll:T}=pce(w),A=qtt(w),F={x:Math.min(b===Ar.Right?A.right-A.width/2:A.right,Math.max(b===Ar.Right?A.left:A.left+A.width/2,m.x)),y:Math.min(b===Ar.Down?A.bottom-A.height/2:A.bottom,Math.max(b===Ar.Down?A.top:A.top+A.height/2,m.y))},_=b===Ar.Right&&!S||b===Ar.Left&&!N,E=b===Ar.Down&&!C||b===Ar.Up&&!x;if(_&&F.x!==m.x){const H=w.scrollLeft+p.x,I=b===Ar.Right&&H<=P.x||b===Ar.Left&&H>=T.x;if(I&&!p.y){w.scrollTo({left:H,behavior:l});return}I?f.x=w.scrollLeft-H:f.x=b===Ar.Right?w.scrollLeft-P.x:w.scrollLeft-T.x,f.x&&w.scrollBy({left:-f.x,behavior:l});break}else if(E&&F.y!==m.y){const H=w.scrollTop+p.y,I=b===Ar.Down&&H<=P.y||b===Ar.Up&&H>=T.y;if(I&&!p.x){w.scrollTo({top:H,behavior:l});return}I?f.y=w.scrollTop-H:f.y=b===Ar.Down?w.scrollTop-P.y:w.scrollTop-T.y,f.y&&w.scrollBy({top:-f.y,behavior:l});break}}this.handleMove(e,hy(yS(m,this.referenceCoordinates),f))}}}handleMove(e,n){const{onMove:a}=this.props;e.preventDefault(),a(n)}handleEnd(e){const{onEnd:n}=this.props;e.preventDefault(),this.detach(),n()}handleCancel(e){const{onCancel:n}=this.props;e.preventDefault(),this.detach(),n()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll()}}JA.activators=[{eventName:"onKeyDown",handler:(t,e,n)=>{let{keyboardCodes:a=fce,onActivation:i}=e,{active:s}=n;const{code:o}=t.nativeEvent;if(a.start.includes(o)){const l=s.activatorNode.current;return l&&t.target!==l?!1:(t.preventDefault(),i?.({event:t.nativeEvent}),!0)}return!1}}];function qY(t){return!!(t&&"distance"in t)}function $Y(t){return!!(t&&"delay"in t)}class nU{constructor(e,n,a){var i;a===void 0&&(a=Ktt(e.event.target)),this.props=void 0,this.events=void 0,this.autoScrollEnabled=!0,this.document=void 0,this.activated=!1,this.initialCoordinates=void 0,this.timeoutId=null,this.listeners=void 0,this.documentListeners=void 0,this.windowListeners=void 0,this.props=e,this.events=n;const{event:s}=e,{target:o}=s;this.props=e,this.events=n,this.document=pv(o),this.documentListeners=new xw(this.document),this.listeners=new xw(a),this.windowListeners=new xw(nl(o)),this.initialCoordinates=(i=qI(s))!=null?i:hd,this.handleStart=this.handleStart.bind(this),this.handleMove=this.handleMove.bind(this),this.handleEnd=this.handleEnd.bind(this),this.handleCancel=this.handleCancel.bind(this),this.handleKeydown=this.handleKeydown.bind(this),this.removeTextSelection=this.removeTextSelection.bind(this),this.attach()}attach(){const{events:e,props:{options:{activationConstraint:n,bypassActivationConstraint:a}}}=this;if(this.listeners.add(e.move.name,this.handleMove,{passive:!1}),this.listeners.add(e.end.name,this.handleEnd),e.cancel&&this.listeners.add(e.cancel.name,this.handleCancel),this.windowListeners.add(tc.Resize,this.handleCancel),this.windowListeners.add(tc.DragStart,HY),this.windowListeners.add(tc.VisibilityChange,this.handleCancel),this.windowListeners.add(tc.ContextMenu,HY),this.documentListeners.add(tc.Keydown,this.handleKeydown),n){if(a!=null&&a({event:this.props.event,activeNode:this.props.activeNode,options:this.props.options}))return this.handleStart();if($Y(n)){this.timeoutId=setTimeout(this.handleStart,n.delay),this.handlePending(n);return}if(qY(n)){this.handlePending(n);return}}this.handleStart()}detach(){this.listeners.removeAll(),this.windowListeners.removeAll(),setTimeout(this.documentListeners.removeAll,50),this.timeoutId!==null&&(clearTimeout(this.timeoutId),this.timeoutId=null)}handlePending(e,n){const{active:a,onPending:i}=this.props;i(a,e,this.initialCoordinates,n)}handleStart(){const{initialCoordinates:e}=this,{onStart:n}=this.props;e&&(this.activated=!0,this.documentListeners.add(tc.Click,Xtt,{capture:!0}),this.removeTextSelection(),this.documentListeners.add(tc.SelectionChange,this.removeTextSelection),n(e))}handleMove(e){var n;const{activated:a,initialCoordinates:i,props:s}=this,{onMove:o,options:{activationConstraint:l}}=s;if(!i)return;const c=(n=qI(e))!=null?n:hd,d=yS(i,c);if(!a&&l){if(qY(l)){if(l.tolerance!=null&&nF(d,l.tolerance))return this.handleCancel();if(nF(d,l.distance))return this.handleStart()}if($Y(l)&&nF(d,l.tolerance))return this.handleCancel();this.handlePending(l,d);return}e.cancelable&&e.preventDefault(),o(c)}handleEnd(){const{onAbort:e,onEnd:n}=this.props;this.detach(),this.activated||e(this.props.active),n()}handleCancel(){const{onAbort:e,onCancel:n}=this.props;this.detach(),this.activated||e(this.props.active),n()}handleKeydown(e){e.code===Ar.Esc&&this.handleCancel()}removeTextSelection(){var e;(e=this.document.getSelection())==null||e.removeAllRanges()}}const Qtt={cancel:{name:"pointercancel"},move:{name:"pointermove"},end:{name:"pointerup"}};class ej extends nU{constructor(e){const{event:n}=e,a=pv(n.target);super(e,Qtt,a)}}ej.activators=[{eventName:"onPointerDown",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:a}=e;return!n.isPrimary||n.button!==0?!1:(a?.({event:n}),!0)}}];const Ztt={move:{name:"mousemove"},end:{name:"mouseup"}};var VI;(function(t){t[t.RightClick=2]="RightClick"})(VI||(VI={}));class Jtt extends nU{constructor(e){super(e,Ztt,pv(e.event.target))}}Jtt.activators=[{eventName:"onMouseDown",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:a}=e;return n.button===VI.RightClick?!1:(a?.({event:n}),!0)}}];const rF={cancel:{name:"touchcancel"},move:{name:"touchmove"},end:{name:"touchend"}};class ent extends nU{constructor(e){super(e,rF)}static setup(){return window.addEventListener(rF.move.name,e,{capture:!1,passive:!1}),function(){window.removeEventListener(rF.move.name,e)};function e(){}}}ent.activators=[{eventName:"onTouchStart",handler:(t,e)=>{let{nativeEvent:n}=t,{onActivation:a}=e;const{touches:i}=n;return i.length>1?!1:(a?.({event:n}),!0)}}];var yw;(function(t){t[t.Pointer=0]="Pointer",t[t.DraggableRect=1]="DraggableRect"})(yw||(yw={}));var hT;(function(t){t[t.TreeOrder=0]="TreeOrder",t[t.ReversedTreeOrder=1]="ReversedTreeOrder"})(hT||(hT={}));function tnt(t){let{acceleration:e,activator:n=yw.Pointer,canScroll:a,draggingRect:i,enabled:s,interval:o=5,order:l=hT.TreeOrder,pointerCoordinates:c,scrollableAncestors:d,scrollableAncestorRects:u,delta:m,threshold:p}=t;const f=rnt({delta:m,disabled:!s}),[g,w]=ftt(),b=y.useRef({x:0,y:0}),x=y.useRef({x:0,y:0}),S=y.useMemo(()=>{switch(n){case yw.Pointer:return c?{top:c.y,bottom:c.y,left:c.x,right:c.x}:null;case yw.DraggableRect:return i}},[n,i,c]),N=y.useRef(null),C=y.useCallback(()=>{const T=N.current;if(!T)return;const A=b.current.x*x.current.x,F=b.current.y*x.current.y;T.scrollBy(A,F)},[]),P=y.useMemo(()=>l===hT.TreeOrder?[...d].reverse():d,[l,d]);y.useEffect(()=>{if(!s||!d.length||!S){w();return}for(const T of P){if(a?.(T)===!1)continue;const A=d.indexOf(T),F=u[A];if(!F)continue;const{direction:_,speed:E}=Htt(T,F,S,e,p);for(const H of["x","y"])f[H][_[H]]||(E[H]=0,_[H]=0);if(E.x>0||E.y>0){w(),N.current=T,g(C,o),b.current=E,x.current=_;return}}b.current={x:0,y:0},x.current={x:0,y:0},w()},[e,C,a,w,s,o,JSON.stringify(S),JSON.stringify(f),g,d,P,u,JSON.stringify(p)])}const nnt={x:{[as.Backward]:!1,[as.Forward]:!1},y:{[as.Backward]:!1,[as.Forward]:!1}};function rnt(t){let{delta:e,disabled:n}=t;const a=HI(e);return b_(i=>{if(n||!a||!i)return nnt;const s={x:Math.sign(e.x-a.x),y:Math.sign(e.y-a.y)};return{x:{[as.Backward]:i.x[as.Backward]||s.x===-1,[as.Forward]:i.x[as.Forward]||s.x===1},y:{[as.Backward]:i.y[as.Backward]||s.y===-1,[as.Forward]:i.y[as.Forward]||s.y===1}}},[n,e,a])}function ant(t,e){const n=e!=null?t.get(e):void 0,a=n?n.node.current:null;return b_(i=>{var s;return e==null?null:(s=a??i)!=null?s:null},[a,e])}function int(t,e){return y.useMemo(()=>t.reduce((n,a)=>{const{sensor:i}=a,s=i.activators.map(o=>({eventName:o.eventName,handler:e(o.handler,a)}));return[...n,...s]},[]),[t,e])}var vS;(function(t){t[t.Always=0]="Always",t[t.BeforeDragging=1]="BeforeDragging",t[t.WhileDragging=2]="WhileDragging"})(vS||(vS={}));var GI;(function(t){t.Optimized="optimized"})(GI||(GI={}));const VY=new Map;function snt(t,e){let{dragging:n,dependencies:a,config:i}=e;const[s,o]=y.useState(null),{frequency:l,measure:c,strategy:d}=i,u=y.useRef(t),m=b(),p=xS(m),f=y.useCallback(function(x){x===void 0&&(x=[]),!p.current&&o(S=>S===null?x:S.concat(x.filter(N=>!S.includes(N))))},[p]),g=y.useRef(null),w=b_(x=>{if(m&&!n)return VY;if(!x||x===VY||u.current!==t||s!=null){const S=new Map;for(let N of t){if(!N)continue;if(s&&s.length>0&&!s.includes(N.id)&&N.rect.current){S.set(N.id,N.rect.current);continue}const C=N.node.current,P=C?new tU(c(C),C):null;N.rect.current=P,P&&S.set(N.id,P)}return S}return x},[t,s,n,m,c]);return y.useEffect(()=>{u.current=t},[t]),y.useEffect(()=>{m||f()},[n,m]),y.useEffect(()=>{s&&s.length>0&&o(null)},[JSON.stringify(s)]),y.useEffect(()=>{m||typeof l!="number"||g.current!==null||(g.current=setTimeout(()=>{f(),g.current=null},l))},[l,m,f,...a]),{droppableRects:w,measureDroppableContainers:f,measuringScheduled:s!=null};function b(){switch(d){case vS.Always:return!1;case vS.BeforeDragging:return n;default:return!n}}}function gce(t,e){return b_(n=>t?n||(typeof e=="function"?e(t):t):null,[e,t])}function ont(t,e){return gce(t,e)}function lnt(t){let{callback:e,disabled:n}=t;const a=J5(e),i=y.useMemo(()=>{if(n||typeof window>"u"||typeof window.MutationObserver>"u")return;const{MutationObserver:s}=window;return new s(a)},[a,n]);return y.useEffect(()=>()=>i?.disconnect(),[i]),i}function tj(t){let{callback:e,disabled:n}=t;const a=J5(e),i=y.useMemo(()=>{if(n||typeof window>"u"||typeof window.ResizeObserver>"u")return;const{ResizeObserver:s}=window;return new s(a)},[n]);return y.useEffect(()=>()=>i?.disconnect(),[i]),i}function cnt(t){return new tU(hv(t),t)}function GY(t,e,n){e===void 0&&(e=cnt);const[a,i]=y.useState(null);function s(){i(c=>{if(!t)return null;if(t.isConnected===!1){var d;return(d=c??n)!=null?d:null}const u=e(t);return JSON.stringify(c)===JSON.stringify(u)?c:u})}const o=lnt({callback(c){if(t)for(const d of c){const{type:u,target:m}=d;if(u==="childList"&&m instanceof HTMLElement&&m.contains(t)){s();break}}}}),l=tj({callback:s});return wu(()=>{s(),t?(l?.observe(t),o?.observe(document.body,{childList:!0,subtree:!0})):(l?.disconnect(),o?.disconnect())},[t]),a}function dnt(t){const e=gce(t);return lce(t,e)}const WY=[];function unt(t){const e=y.useRef(t),n=b_(a=>t?a&&a!==WY&&t&&e.current&&t.parentNode===e.current.parentNode?a:ZA(t):WY,[t]);return y.useEffect(()=>{e.current=t},[t]),n}function mnt(t){const[e,n]=y.useState(null),a=y.useRef(t),i=y.useCallback(s=>{const o=tF(s.target);o&&n(l=>l?(l.set(o,$I(o)),new Map(l)):null)},[]);return y.useEffect(()=>{const s=a.current;if(t!==s){o(s);const l=t.map(c=>{const d=tF(c);return d?(d.addEventListener("scroll",i,{passive:!0}),[d,$I(d)]):null}).filter(c=>c!=null);n(l.length?new Map(l):null),a.current=t}return()=>{o(t),o(s)};function o(l){l.forEach(c=>{const d=tF(c);d?.removeEventListener("scroll",i)})}},[i,t]),y.useMemo(()=>t.length?e?Array.from(e.values()).reduce((s,o)=>hy(s,o),hd):hce(t):hd,[t,e])}function KY(t,e){e===void 0&&(e=[]);const n=y.useRef(null);return y.useEffect(()=>{n.current=null},e),y.useEffect(()=>{const a=t!==hd;a&&!n.current&&(n.current=t),!a&&n.current&&(n.current=null)},[t]),n.current?yS(t,n.current):hd}function pnt(t){y.useEffect(()=>{if(!QA)return;const e=t.map(n=>{let{sensor:a}=n;return a.setup==null?void 0:a.setup()});return()=>{for(const n of e)n?.()}},t.map(e=>{let{sensor:n}=e;return n}))}function hnt(t,e){return y.useMemo(()=>t.reduce((n,a)=>{let{eventName:i,handler:s}=a;return n[i]=o=>{s(o,e)},n},{}),[t,e])}function bce(t){return y.useMemo(()=>t?Ott(t):null,[t])}const XY=[];function fnt(t,e){e===void 0&&(e=hv);const[n]=t,a=bce(n?nl(n):null),[i,s]=y.useState(XY);function o(){s(()=>t.length?t.map(c=>mce(c)?a:new tU(e(c),c)):XY)}const l=tj({callback:o});return wu(()=>{l?.disconnect(),o(),t.forEach(c=>l?.observe(c))},[t]),i}function gnt(t){if(!t)return null;if(t.children.length>1)return t;const e=t.children[0];return g_(e)?e:t}function bnt(t){let{measure:e}=t;const[n,a]=y.useState(null),i=y.useCallback(d=>{for(const{target:u}of d)if(g_(u)){a(m=>{const p=e(u);return m?{...m,width:p.width,height:p.height}:p});break}},[e]),s=tj({callback:i}),o=y.useCallback(d=>{const u=gnt(d);s?.disconnect(),u&&s?.observe(u),a(u?e(u):null)},[e,s]),[l,c]=uT(o);return y.useMemo(()=>({nodeRef:l,rect:n,setRef:c}),[n,l,c])}const xnt=[{sensor:ej,options:{}},{sensor:JA,options:{}}],ynt={current:{}},BN={draggable:{measure:BY},droppable:{measure:BY,strategy:vS.WhileDragging,frequency:GI.Optimized},dragOverlay:{measure:hv}};class vw extends Map{get(e){var n;return e!=null&&(n=super.get(e))!=null?n:void 0}toArray(){return Array.from(this.values())}getEnabled(){return this.toArray().filter(e=>{let{disabled:n}=e;return!n})}getNodeFor(e){var n,a;return(n=(a=this.get(e))==null?void 0:a.node.current)!=null?n:void 0}}const vnt={activatorEvent:null,active:null,activeNode:null,activeNodeRect:null,collisions:null,containerNodeRect:null,draggableNodes:new Map,droppableRects:new Map,droppableContainers:new vw,over:null,dragOverlay:{nodeRef:{current:null},rect:null,setRef:mT},scrollableAncestors:[],scrollableAncestorRects:[],measuringConfiguration:BN,measureDroppableContainers:mT,windowRect:null,measuringScheduled:!1},wnt={activatorEvent:null,activators:[],active:null,activeNodeRect:null,ariaDescribedById:{draggable:""},dispatch:mT,draggableNodes:new Map,over:null,measureDroppableContainers:mT},nj=y.createContext(wnt),xce=y.createContext(vnt);function Snt(){return{draggable:{active:null,initialCoordinates:{x:0,y:0},nodes:new Map,translate:{x:0,y:0}},droppable:{containers:new vw}}}function _nt(t,e){switch(e.type){case Ii.DragStart:return{...t,draggable:{...t.draggable,initialCoordinates:e.initialCoordinates,active:e.active}};case Ii.DragMove:return t.draggable.active==null?t:{...t,draggable:{...t.draggable,translate:{x:e.coordinates.x-t.draggable.initialCoordinates.x,y:e.coordinates.y-t.draggable.initialCoordinates.y}}};case Ii.DragEnd:case Ii.DragCancel:return{...t,draggable:{...t.draggable,active:null,initialCoordinates:{x:0,y:0},translate:{x:0,y:0}}};case Ii.RegisterDroppable:{const{element:n}=e,{id:a}=n,i=new vw(t.droppable.containers);return i.set(a,n),{...t,droppable:{...t.droppable,containers:i}}}case Ii.SetDroppableDisabled:{const{id:n,key:a,disabled:i}=e,s=t.droppable.containers.get(n);if(!s||a!==s.key)return t;const o=new vw(t.droppable.containers);return o.set(n,{...s,disabled:i}),{...t,droppable:{...t.droppable,containers:o}}}case Ii.UnregisterDroppable:{const{id:n,key:a}=e,i=t.droppable.containers.get(n);if(!i||a!==i.key)return t;const s=new vw(t.droppable.containers);return s.delete(n),{...t,droppable:{...t.droppable,containers:s}}}default:return t}}function knt(t){let{disabled:e}=t;const{active:n,activatorEvent:a,draggableNodes:i}=y.useContext(nj),s=HI(a),o=HI(n?.id);return y.useEffect(()=>{if(!e&&!a&&s&&o!=null){if(!eU(s)||document.activeElement===s.target)return;const l=i.get(o);if(!l)return;const{activatorNode:c,node:d}=l;if(!c.current&&!d.current)return;requestAnimationFrame(()=>{for(const u of[c.current,d.current]){if(!u)continue;const m=xtt(u);if(m){m.focus();break}}})}},[a,e,i,o,s]),null}function Nnt(t,e){let{transform:n,...a}=e;return t!=null&&t.length?t.reduce((i,s)=>s({transform:i,...a}),n):n}function Cnt(t){return y.useMemo(()=>({draggable:{...BN.draggable,...t?.draggable},droppable:{...BN.droppable,...t?.droppable},dragOverlay:{...BN.dragOverlay,...t?.dragOverlay}}),[t?.draggable,t?.droppable,t?.dragOverlay])}function Pnt(t){let{activeNode:e,measure:n,initialRect:a,config:i=!0}=t;const s=y.useRef(!1),{x:o,y:l}=typeof i=="boolean"?{x:i,y:i}:i;wu(()=>{if(!o&&!l||!e){s.current=!1;return}if(s.current||!a)return;const d=e?.node.current;if(!d||d.isConnected===!1)return;const u=n(d),m=lce(u,a);if(o||(m.x=0),l||(m.y=0),s.current=!0,Math.abs(m.x)>0||Math.abs(m.y)>0){const p=cce(d);p&&p.scrollBy({top:m.y,left:m.x})}},[e,o,l,a,n])}const yce=y.createContext({...hd,scaleX:1,scaleY:1});var dh;(function(t){t[t.Uninitialized=0]="Uninitialized",t[t.Initializing=1]="Initializing",t[t.Initialized=2]="Initialized"})(dh||(dh={}));const vce=y.memo(function(e){var n,a,i,s;let{id:o,accessibility:l,autoScroll:c=!0,children:d,sensors:u=xnt,collisionDetection:m=Mtt,measuring:p,modifiers:f,...g}=e;const w=y.useReducer(_nt,void 0,Snt),[b,x]=w,[S,N]=ktt(),[C,P]=y.useState(dh.Uninitialized),T=C===dh.Initialized,{draggable:{active:A,nodes:F,translate:_},droppable:{containers:E}}=b,H=A!=null?F.get(A):null,I=y.useRef({initial:null,translated:null}),U=y.useMemo(()=>{var Qe;return A!=null?{id:A,data:(Qe=H?.data)!=null?Qe:ynt,rect:I}:null},[A,H]),R=y.useRef(null),[Q,re]=y.useState(null),[J,ce]=y.useState(null),pe=xS(g,Object.values(g)),ue=x_("DndDescribedBy",o),Y=y.useMemo(()=>E.getEnabled(),[E]),ie=Cnt(p),{droppableRects:me,measureDroppableContainers:ge,measuringScheduled:Ce}=snt(Y,{dragging:T,dependencies:[_.x,_.y],config:ie.droppable}),$=ant(F,A),W=y.useMemo(()=>J?qI(J):null,[J]),D=Ne(),j=ont($,ie.draggable.measure);Pnt({activeNode:A!=null?F.get(A):null,config:D.layoutShiftCompensation,initialRect:j,measure:ie.draggable.measure});const B=GY($,ie.draggable.measure,j),X=GY($?$.parentElement:null),O=y.useRef({activatorEvent:null,active:null,activeNode:$,collisionRect:null,collisions:null,droppableRects:me,draggableNodes:F,draggingNode:null,draggingNodeRect:null,droppableContainers:E,over:null,scrollableAncestors:[],scrollAdjustedTranslate:null}),se=E.getNodeFor((n=O.current.over)==null?void 0:n.id),z=bnt({measure:ie.dragOverlay.measure}),G=(a=z.nodeRef.current)!=null?a:$,K=T?(i=z.rect)!=null?i:B:null,Z=!!(z.nodeRef.current&&z.rect),oe=dnt(Z?null:B),ee=bce(G?nl(G):null),fe=unt(T?se??$:null),de=fnt(fe),le=Nnt(f,{transform:{x:_.x-oe.x,y:_.y-oe.y,scaleX:1,scaleY:1},activatorEvent:J,active:U,activeNodeRect:B,containerNodeRect:X,draggingNodeRect:K,over:O.current.over,overlayNodeRect:z.rect,scrollableAncestors:fe,scrollableAncestorRects:de,windowRect:ee}),q=W?hy(W,_):null,L=mnt(fe),ne=KY(L),be=KY(L,[B]),we=hy(le,ne),ke=K?Ftt(K,le):null,ye=U&&ke?m({active:U,collisionRect:ke,droppableRects:me,droppableContainers:Y,pointerCoordinates:q}):null,xe=sce(ye,"id"),[Me,Oe]=y.useState(null),Ie=Z?le:hy(le,be),Be=Ett(Ie,(s=Me?.rect)!=null?s:null,B),Ue=y.useRef(null),Ke=y.useCallback((Qe,ze)=>{let{sensor:_e,options:De}=ze;if(R.current==null)return;const We=F.get(R.current);if(!We)return;const nt=Qe.nativeEvent,Ae=new _e({active:R.current,activeNode:We,event:nt,options:De,context:O,onAbort(ot){if(!F.get(ot))return;const{onDragAbort:zt}=pe.current,Wt={id:ot};zt?.(Wt),S({type:"onDragAbort",event:Wt})},onPending(ot,rt,zt,Wt){if(!F.get(ot))return;const{onDragPending:Zt}=pe.current,un={id:ot,constraint:rt,initialCoordinates:zt,offset:Wt};Zt?.(un),S({type:"onDragPending",event:un})},onStart(ot){const rt=R.current;if(rt==null)return;const zt=F.get(rt);if(!zt)return;const{onDragStart:Wt}=pe.current,Jt={activatorEvent:nt,active:{id:rt,data:zt.data,rect:I}};lc.unstable_batchedUpdates(()=>{Wt?.(Jt),P(dh.Initializing),x({type:Ii.DragStart,initialCoordinates:ot,active:rt}),S({type:"onDragStart",event:Jt}),re(Ue.current),ce(nt)})},onMove(ot){x({type:Ii.DragMove,coordinates:ot})},onEnd:it(Ii.DragEnd),onCancel:it(Ii.DragCancel)});Ue.current=Ae;function it(ot){return async function(){const{active:zt,collisions:Wt,over:Jt,scrollAdjustedTranslate:Zt}=O.current;let un=null;if(zt&&Zt){const{cancelDrop:sn}=pe.current;un={activatorEvent:nt,active:zt,collisions:Wt,delta:Zt,over:Jt},ot===Ii.DragEnd&&typeof sn=="function"&&await Promise.resolve(sn(un))&&(ot=Ii.DragCancel)}R.current=null,lc.unstable_batchedUpdates(()=>{x({type:ot}),P(dh.Uninitialized),Oe(null),re(null),ce(null),Ue.current=null;const sn=ot===Ii.DragEnd?"onDragEnd":"onDragCancel";if(un){const dt=pe.current[sn];dt?.(un),S({type:sn,event:un})}})}}},[F]),He=y.useCallback((Qe,ze)=>(_e,De)=>{const We=_e.nativeEvent,nt=F.get(De);if(R.current!==null||!nt||We.dndKit||We.defaultPrevented)return;const Ae={active:nt};Qe(_e,ze.options,Ae)===!0&&(We.dndKit={capturedBy:ze.sensor},R.current=De,Ke(_e,ze))},[F,Ke]),at=int(u,He);pnt(u),wu(()=>{B&&C===dh.Initializing&&P(dh.Initialized)},[B,C]),y.useEffect(()=>{const{onDragMove:Qe}=pe.current,{active:ze,activatorEvent:_e,collisions:De,over:We}=O.current;if(!ze||!_e)return;const nt={active:ze,activatorEvent:_e,collisions:De,delta:{x:we.x,y:we.y},over:We};lc.unstable_batchedUpdates(()=>{Qe?.(nt),S({type:"onDragMove",event:nt})})},[we.x,we.y]),y.useEffect(()=>{const{active:Qe,activatorEvent:ze,collisions:_e,droppableContainers:De,scrollAdjustedTranslate:We}=O.current;if(!Qe||R.current==null||!ze||!We)return;const{onDragOver:nt}=pe.current,Ae=De.get(xe),it=Ae&&Ae.rect.current?{id:Ae.id,rect:Ae.rect.current,data:Ae.data,disabled:Ae.disabled}:null,ot={active:Qe,activatorEvent:ze,collisions:_e,delta:{x:We.x,y:We.y},over:it};lc.unstable_batchedUpdates(()=>{Oe(it),nt?.(ot),S({type:"onDragOver",event:ot})})},[xe]),wu(()=>{O.current={activatorEvent:J,active:U,activeNode:$,collisionRect:ke,collisions:ye,droppableRects:me,draggableNodes:F,draggingNode:G,draggingNodeRect:K,droppableContainers:E,over:Me,scrollableAncestors:fe,scrollAdjustedTranslate:we},I.current={initial:K,translated:ke}},[U,$,ye,ke,F,G,K,me,E,Me,fe,we]),tnt({...D,delta:_,draggingRect:ke,pointerCoordinates:q,scrollableAncestors:fe,scrollableAncestorRects:de});const Ee=y.useMemo(()=>({active:U,activeNode:$,activeNodeRect:B,activatorEvent:J,collisions:ye,containerNodeRect:X,dragOverlay:z,draggableNodes:F,droppableContainers:E,droppableRects:me,over:Me,measureDroppableContainers:ge,scrollableAncestors:fe,scrollableAncestorRects:de,measuringConfiguration:ie,measuringScheduled:Ce,windowRect:ee}),[U,$,B,J,ye,X,z,F,E,me,Me,ge,fe,de,ie,Ce,ee]),tt=y.useMemo(()=>({activatorEvent:J,activators:at,active:U,activeNodeRect:B,ariaDescribedById:{draggable:ue},dispatch:x,draggableNodes:F,over:Me,measureDroppableContainers:ge}),[J,at,U,B,x,ue,F,Me,ge]);return ua.createElement(nce.Provider,{value:N},ua.createElement(nj.Provider,{value:tt},ua.createElement(xce.Provider,{value:Ee},ua.createElement(yce.Provider,{value:Be},d)),ua.createElement(knt,{disabled:l?.restoreFocus===!1})),ua.createElement(Ptt,{...l,hiddenTextDescribedById:ue}));function Ne(){const Qe=Q?.autoScrollEnabled===!1,ze=typeof c=="object"?c.enabled===!1:c===!1,_e=T&&!Qe&&!ze;return typeof c=="object"?{...c,enabled:_e}:{enabled:_e}}}),Tnt=y.createContext(null),YY="button",Ant="Draggable";function jnt(t){let{id:e,data:n,disabled:a=!1,attributes:i}=t;const s=x_(Ant),{activators:o,activatorEvent:l,active:c,activeNodeRect:d,ariaDescribedById:u,draggableNodes:m,over:p}=y.useContext(nj),{role:f=YY,roleDescription:g="draggable",tabIndex:w=0}=i??{},b=c?.id===e,x=y.useContext(b?yce:Tnt),[S,N]=uT(),[C,P]=uT(),T=hnt(o,e),A=xS(n);wu(()=>(m.set(e,{id:e,key:s,node:S,activatorNode:C,data:A}),()=>{const _=m.get(e);_&&_.key===s&&m.delete(e)}),[m,e]);const F=y.useMemo(()=>({role:f,tabIndex:w,"aria-disabled":a,"aria-pressed":b&&f===YY?!0:void 0,"aria-roledescription":g,"aria-describedby":u.draggable}),[a,f,w,b,g,u.draggable]);return{active:c,activatorEvent:l,activeNodeRect:d,attributes:F,isDragging:b,listeners:a?void 0:T,node:S,over:p,setNodeRef:N,setActivatorNodeRef:P,transform:x}}function Mnt(){return y.useContext(xce)}const Ent="Droppable",Dnt={timeout:25};function Fnt(t){let{data:e,disabled:n=!1,id:a,resizeObserverConfig:i}=t;const s=x_(Ent),{active:o,dispatch:l,over:c,measureDroppableContainers:d}=y.useContext(nj),u=y.useRef({disabled:n}),m=y.useRef(!1),p=y.useRef(null),f=y.useRef(null),{disabled:g,updateMeasurementsFor:w,timeout:b}={...Dnt,...i},x=xS(w??a),S=y.useCallback(()=>{if(!m.current){m.current=!0;return}f.current!=null&&clearTimeout(f.current),f.current=setTimeout(()=>{d(Array.isArray(x.current)?x.current:[x.current]),f.current=null},b)},[b]),N=tj({callback:S,disabled:g||!o}),C=y.useCallback((F,_)=>{N&&(_&&(N.unobserve(_),m.current=!1),F&&N.observe(F))},[N]),[P,T]=uT(C),A=xS(e);return y.useEffect(()=>{!N||!P.current||(N.disconnect(),m.current=!1,N.observe(P.current))},[P,N]),y.useEffect(()=>(l({type:Ii.RegisterDroppable,element:{id:a,key:s,disabled:n,node:P,rect:p,data:A}}),()=>l({type:Ii.UnregisterDroppable,key:s,id:a})),[a]),y.useEffect(()=>{n!==u.current.disabled&&(l({type:Ii.SetDroppableDisabled,id:a,key:s,disabled:n}),u.current.disabled=n)},[a,s,n,l]),{active:o,rect:p,isOver:c?.id===a,node:P,over:c,setNodeRef:T}}function rj(t,e,n){const a=t.slice();return a.splice(n<0?a.length+n:n,0,a.splice(e,1)[0]),a}function Lnt(t,e){return t.reduce((n,a,i)=>{const s=e.get(a);return s&&(n[i]=s),n},Array(t.length))}function sN(t){return t!==null&&t>=0}function Rnt(t,e){if(t===e)return!0;if(t.length!==e.length)return!1;for(let n=0;n{let{rects:e,activeIndex:n,overIndex:a,index:i}=t;const s=rj(e,a,n),o=e[i],l=s[i];return!l||!o?null:{x:l.left-o.left,y:l.top-o.top,scaleX:l.width/o.width,scaleY:l.height/o.height}},oN={scaleX:1,scaleY:1},Ont=t=>{var e;let{activeIndex:n,activeNodeRect:a,index:i,rects:s,overIndex:o}=t;const l=(e=s[n])!=null?e:a;if(!l)return null;if(i===n){const d=s[o];return d?{x:0,y:nn&&i<=o?{x:0,y:-l.height-c,...oN}:i=o?{x:0,y:l.height+c,...oN}:{x:0,y:0,...oN}};function znt(t,e,n){const a=t[e],i=t[e-1],s=t[e+1];return a?na.map(T=>typeof T=="object"&&"id"in T?T.id:T),[a]),g=o!=null,w=o?f.indexOf(o.id):-1,b=d?f.indexOf(d.id):-1,x=y.useRef(f),S=!Rnt(f,x.current),N=b!==-1&&w===-1||S,C=Int(s);wu(()=>{S&&g&&u(f)},[S,f,g,u]),y.useEffect(()=>{x.current=f},[f]);const P=y.useMemo(()=>({activeIndex:w,containerId:m,disabled:C,disableTransforms:N,items:f,overIndex:b,useDragOverlay:p,sortedRects:Lnt(f,c),strategy:i}),[w,m,C.draggable,C.droppable,N,f,b,c,p,i]);return ua.createElement(Sce.Provider,{value:P},e)}const Unt=t=>{let{id:e,items:n,activeIndex:a,overIndex:i}=t;return rj(n,a,i).indexOf(e)},Bnt=t=>{let{containerId:e,isSorting:n,wasDragging:a,index:i,items:s,newIndex:o,previousItems:l,previousContainerId:c,transition:d}=t;return!d||!a||l!==s&&i===o?!1:n?!0:o!==i&&e===c},Hnt={duration:200,easing:"ease"},kce="transform",qnt=Hy.Transition.toString({property:kce,duration:0,easing:"linear"}),$nt={roleDescription:"sortable"};function Vnt(t){let{disabled:e,index:n,node:a,rect:i}=t;const[s,o]=y.useState(null),l=y.useRef(n);return wu(()=>{if(!e&&n!==l.current&&a.current){const c=i.current;if(c){const d=hv(a.current,{ignoreTransform:!0}),u={x:c.left-d.left,y:c.top-d.top,scaleX:c.width/d.width,scaleY:c.height/d.height};(u.x||u.y)&&o(u)}}n!==l.current&&(l.current=n)},[e,n,a,i]),y.useEffect(()=>{s&&o(null)},[s]),s}function Nce(t){let{animateLayoutChanges:e=Bnt,attributes:n,disabled:a,data:i,getNewIndex:s=Unt,id:o,strategy:l,resizeObserverConfig:c,transition:d=Hnt}=t;const{items:u,containerId:m,activeIndex:p,disabled:f,disableTransforms:g,sortedRects:w,overIndex:b,useDragOverlay:x,strategy:S}=y.useContext(Sce),N=Gnt(a,f),C=u.indexOf(o),P=y.useMemo(()=>({sortable:{containerId:m,index:C,items:u},...i}),[m,i,C,u]),T=y.useMemo(()=>u.slice(u.indexOf(o)),[u,o]),{rect:A,node:F,isOver:_,setNodeRef:E}=Fnt({id:o,data:P,disabled:N.droppable,resizeObserverConfig:{updateMeasurementsFor:T,...c}}),{active:H,activatorEvent:I,activeNodeRect:U,attributes:R,setNodeRef:Q,listeners:re,isDragging:J,over:ce,setActivatorNodeRef:pe,transform:ue}=jnt({id:o,data:P,attributes:{...$nt,...n},disabled:N.draggable}),Y=htt(E,Q),ie=!!H,me=ie&&!g&&sN(p)&&sN(b),ge=!x&&J,Ce=ge&&me?ue:null,W=me?Ce??(l??S)({rects:w,activeNodeRect:U,activeIndex:p,overIndex:b,index:C}):null,D=sN(p)&&sN(b)?s({id:o,items:u,activeIndex:p,overIndex:b}):C,j=H?.id,B=y.useRef({activeId:j,items:u,newIndex:D,containerId:m}),X=u!==B.current.items,O=e({active:H,containerId:m,isDragging:J,isSorting:ie,id:o,index:C,items:u,newIndex:B.current.newIndex,previousItems:B.current.items,previousContainerId:B.current.containerId,transition:d,wasDragging:B.current.activeId!=null}),se=Vnt({disabled:!O,index:C,node:F,rect:A});return y.useEffect(()=>{ie&&B.current.newIndex!==D&&(B.current.newIndex=D),m!==B.current.containerId&&(B.current.containerId=m),u!==B.current.items&&(B.current.items=u)},[ie,D,m,u]),y.useEffect(()=>{if(j===B.current.activeId)return;if(j!=null&&B.current.activeId==null){B.current.activeId=j;return}const G=setTimeout(()=>{B.current.activeId=j},50);return()=>clearTimeout(G)},[j]),{active:H,activeIndex:p,attributes:R,data:P,rect:A,index:C,newIndex:D,items:u,isOver:_,isSorting:ie,isDragging:J,listeners:re,node:F,overIndex:b,over:ce,setNodeRef:Y,setActivatorNodeRef:pe,setDroppableNodeRef:E,setDraggableNodeRef:Q,transform:se??W,transition:z()};function z(){if(se||X&&B.current.newIndex===C)return qnt;if(!(ge&&!eU(I)||!d)&&(ie||O))return Hy.Transition.toString({...d,property:kce})}}function Gnt(t,e){var n,a;return typeof t=="boolean"?{draggable:t,droppable:!1}:{draggable:(n=t?.draggable)!=null?n:e.draggable,droppable:(a=t?.droppable)!=null?a:e.droppable}}function fT(t){if(!t)return!1;const e=t.data.current;return!!(e&&"sortable"in e&&typeof e.sortable=="object"&&"containerId"in e.sortable&&"items"in e.sortable&&"index"in e.sortable)}const Wnt=[Ar.Down,Ar.Right,Ar.Up,Ar.Left],Cce=(t,e)=>{let{context:{active:n,collisionRect:a,droppableRects:i,droppableContainers:s,over:o,scrollableAncestors:l}}=e;if(Wnt.includes(t.code)){if(t.preventDefault(),!n||!a)return;const c=[];s.getEnabled().forEach(m=>{if(!m||m!=null&&m.disabled)return;const p=i.get(m.id);if(p)switch(t.code){case Ar.Down:a.topp.top&&c.push(m);break;case Ar.Left:a.left>p.left&&c.push(m);break;case Ar.Right:a.left1&&(u=d[1].id),u!=null){const m=s.get(n.id),p=s.get(u),f=p?i.get(p.id):null,g=p?.node.current;if(g&&f&&m&&p){const b=ZA(g).some((T,A)=>l[A]!==T),x=Pce(m,p),S=Knt(m,p),N=b||!x?{x:0,y:0}:{x:S?a.width-f.width:0,y:S?a.height-f.height:0},C={x:f.left,y:f.top};return N.x&&N.y?C:yS(C,N)}}}};function Pce(t,e){return!fT(t)||!fT(e)?!1:t.data.current.sortable.containerId===e.data.current.sortable.containerId}function Knt(t,e){return!fT(t)||!fT(e)||!Pce(t,e)?!1:t.data.current.sortable.index=1e3?`${(t/1e3).toFixed(1)}kg`:`${Math.round(t)}g`}function Ynt({activeCount:t,pendingCount:e,totalTime:n,totalWeight:a,historyCount:i,t:s}){const o=[{icon:ps,value:t,label:s("queue.summary.printing"),color:"text-blue-400"},{icon:Zn,value:e,label:s("queue.summary.queued"),color:"text-yellow-400"},{icon:Qd,value:ls(n),label:s("queue.summary.totalTime"),color:"text-bambu-green"},{icon:HZ,value:Xnt(a),label:s("queue.summary.totalWeight"),color:"text-purple-400"},{icon:_r,value:i,label:s("queue.summary.history"),color:"text-bambu-gray"}];return r.jsx("div",{className:"flex items-center gap-3 sm:gap-5 flex-wrap px-4 py-3 bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary mb-6",children:o.map((l,c)=>r.jsxs("div",{className:"flex items-center gap-3",children:[c>0&&r.jsx("span",{className:"hidden sm:block text-bambu-dark-tertiary",children:"|"}),r.jsxs("div",{className:"flex items-center gap-1.5",children:[r.jsx(l.icon,{className:`w-4 h-4 ${l.color}`}),r.jsx("span",{className:"text-sm font-semibold text-white",children:l.value}),r.jsx("span",{className:"text-xs sm:text-sm text-bambu-gray",children:l.label})]})]},c))})}const QY={completed:{icon:_r,color:"text-emerald-400",border:"border-l-emerald-500"},failed:{icon:ja,color:"text-red-400",border:"border-l-red-500"},skipped:{icon:CT,color:"text-orange-400",border:"border-l-gray-500"},cancelled:{icon:qt,color:"text-gray-400",border:"border-l-gray-500"}};function Qnt({item:t,onRequeue:e,onRemove:n,timeFormat:a="system",hasPermission:i,canModify:s,t:o}){const l=QY[t.status]||QY.cancelled,c=l.icon,d=t.archive_name||t.library_file_name||`File #${t.archive_id||t.library_file_id}`,u=t.archive_thumbnail?ae.getArchiveThumbnail(t.archive_id):t.library_file_thumbnail?ae.getLibraryFileThumbnailUrl(t.library_file_id):null,m=t.completed_at||t.created_at;return r.jsxs("div",{className:`flex items-center gap-2 sm:gap-3 px-3 py-2 bg-bambu-dark-secondary rounded-lg border border-bambu-dark-tertiary border-l-[3px] ${l.border}`,children:[r.jsx(c,{className:`w-4 h-4 shrink-0 ${l.color}`}),r.jsx("div",{className:"w-8 h-8 shrink-0 bg-bambu-dark rounded overflow-hidden",children:u?r.jsx("img",{src:u,alt:"",className:"w-full h-full object-cover"}):r.jsx("div",{className:"w-full h-full flex items-center justify-center text-bambu-gray",children:r.jsx(sa,{className:"w-4 h-4"})})}),r.jsx("span",{className:"text-sm text-white font-medium truncate min-w-0 flex-1",children:d}),t.printer_name&&r.jsxs("span",{className:"hidden sm:flex items-center gap-1 text-xs text-bambu-gray shrink-0",children:[r.jsx(fr,{className:"w-3 h-3"}),r.jsx("span",{className:"truncate max-w-[100px]",children:t.printer_name})]}),t.print_time_seconds&&r.jsxs("span",{className:"hidden sm:flex items-center gap-1 text-xs text-bambu-gray shrink-0",children:[r.jsx(Qd,{className:"w-3 h-3"}),ls(t.print_time_seconds)]}),r.jsx("span",{className:"text-xs text-bambu-gray shrink-0",children:Eh(m,a,o)}),r.jsxs("div",{className:"flex items-center gap-0.5 shrink-0",children:[r.jsx(Te,{variant:"ghost",size:"sm",onClick:e,disabled:!i("queue:create"),title:i("queue:create")?o("queue.actions.requeue"):o("queue.permissions.noRequeue"),className:"text-bambu-green hover:text-bambu-green/80 hover:bg-bambu-green/10 p-1.5",children:r.jsx(tr,{className:"w-3.5 h-3.5"})}),r.jsx(Te,{variant:"ghost",size:"sm",onClick:n,disabled:!s("queue","delete",t.created_by_id),title:s("queue","delete",t.created_by_id)?o("common.remove"):o("queue.permissions.noRemove"),className:"p-1.5",children:r.jsx(en,{className:"w-3.5 h-3.5"})})]})]})}function j0(t){const e=new Date(t);return e.setHours(0,0,0,0),e}function Znt(t){return t.toLocaleDateString(void 0,{weekday:"short",month:"short",day:"numeric"})}function Jnt(t){return t.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})}function ert(t,e){if(t<=0)return e("queue.timeline.time.anyMoment");const n=Math.round(t/6e4);if(n<60)return e("queue.timeline.time.minutesLeft",{minutes:n});const a=Math.floor(n/60),i=n%60;return i===0?e("queue.timeline.time.hoursLeft",{hours:a}):e("queue.timeline.time.hoursMinutesLeft",{hours:a,minutes:i})}function trt(t){const e=new Date;return e.setHours(t,0,0,0),e.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})}function nrt({event:t,now:e,onItemClick:n,t:a}){const i=t.item,s=i.archive_name||i.library_file_name||a("common.unknown"),o=i.printer_name||(i.target_model?`${a("queue.filter.any")} ${i.target_model}`:a("queue.timeline.unassigned")),l=t.type==="printing",c=t.estimatedEnd.getTime()-e.getTime(),d=i.archive_thumbnail?ae.getArchiveThumbnail(i.archive_id):i.library_file_thumbnail?ae.getLibraryFileThumbnailUrl(i.library_file_id):null;return r.jsxs("div",{className:`flex items-center gap-3 px-3 sm:px-4 py-3 bg-bambu-dark-secondary rounded-xl border cursor-pointer transition-all hover:border-bambu-green/40 - ${l?"border-blue-500/30":"border-bambu-dark-tertiary"}`,onClick:()=>n(i),children:[r.jsx("div",{className:`w-1 self-stretch rounded-full shrink-0 ${l?"bg-blue-500":"bg-bambu-green/40"}`}),r.jsx("div",{className:"w-10 h-10 shrink-0 bg-bambu-dark rounded-lg overflow-hidden",children:d?r.jsx("img",{src:d,alt:"",className:"w-full h-full object-cover"}):r.jsx("div",{className:"w-full h-full flex items-center justify-center text-bambu-gray",children:r.jsx(sa,{className:"w-5 h-5"})})}),r.jsxs("div",{className:"flex-1 min-w-0",children:[r.jsx("p",{className:"text-sm text-white font-medium truncate",children:s}),r.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[r.jsxs("span",{className:"flex items-center gap-1 text-xs text-bambu-gray",children:[r.jsx(fr,{className:"w-3 h-3"}),r.jsx("span",{className:"truncate max-w-[120px] sm:max-w-none",children:o})]}),i.print_time_seconds&&r.jsx("span",{className:"hidden sm:inline text-xs text-bambu-gray",children:ls(i.print_time_seconds)})]}),l&&t.progress!=null&&r.jsxs("div",{className:"flex items-center gap-2 mt-1.5",children:[r.jsx("div",{className:"flex-1 bg-bambu-dark-tertiary rounded-full h-1.5",children:r.jsx("div",{className:"bg-blue-500 h-1.5 rounded-full transition-all",style:{width:`${t.progress}%`}})}),r.jsxs("span",{className:"text-xs text-blue-400 shrink-0",children:[Math.round(t.progress),"%"]})]})]}),r.jsxs("div",{className:"text-right shrink-0",children:[r.jsx("p",{className:"text-sm text-white font-medium",children:Jnt(t.estimatedEnd)}),r.jsx("p",{className:`text-xs mt-0.5 ${l?"text-blue-400":"text-bambu-gray"}`,children:ert(c,a)})]})]})}function rrt({queueItems:t,printerStatuses:e,onItemClick:n,t:a}){const[i,s]=y.useState(()=>j0(new Date)),[o,l]=y.useState(()=>new Date),[c,d]=y.useState("all");y.useEffect(()=>{const F=setInterval(()=>l(new Date),6e4);return()=>clearInterval(F)},[]);const u=o.getTime(),m=j0(new Date).getTime()===j0(i).getTime(),p=y.useMemo(()=>{const F=[],_=new Map;for(const E of t)if(E.status==="printing"){const H=E.printer_id!=null?e[E.printer_id]:void 0,I=Yn(E.started_at)||new Date;let U;if(H?.remaining_time!=null&&H.remaining_time>0)U=new Date(u+H.remaining_time*60*1e3);else if(E.print_time_seconds){const R=H?.progress||0,Q=Math.max(0,1-R/100);U=new Date(u+E.print_time_seconds*Q*1e3)}else U=new Date(u+36e5);F.push({item:E,estimatedStart:I,estimatedEnd:U,progress:H?.progress??void 0,type:"printing"})}else if(E.status==="pending"){const H=E.printer_id;_.has(H)||_.set(H,[]),_.get(H).push(E)}for(const[E,H]of _){H.sort((U,R)=>U.position-R.position);let I=u;for(const U of F)U.item.printer_id===E&&U.type==="printing"&&(I=Math.max(I,U.estimatedEnd.getTime()));for(const U of H){const R=Yn(U.scheduled_time);if(R){const ce=Date.now()+15552e6;R.getTime()<=ce&&(I=Math.max(I,R.getTime()))}const Q=(U.print_time_seconds||3600)*1e3,re=new Date(I),J=new Date(I+Q);F.push({item:U,estimatedStart:re,estimatedEnd:J,type:"queued"}),I=J.getTime()}}return F.sort((E,H)=>E.estimatedEnd.getTime()-H.estimatedEnd.getTime()),F},[t,e,u]),f=j0(i).getTime(),g=f+1440*60*1e3-1,w=y.useMemo(()=>p.filter(F=>{const _=F.estimatedEnd.getTime();return _g?!1:c==="printing"?F.type==="printing":c==="queued"?F.type==="queued":!0}),[p,f,g,c]),b=y.useMemo(()=>{const F=new Map;for(const _ of w){const E=_.estimatedEnd.getHours();F.has(E)||F.set(E,[]),F.get(E).push(_)}return Array.from(F.entries()).sort(([_],[E])=>_-E)},[w]),x=p.filter(F=>F.type==="printing"&&F.estimatedEnd.getTime()>=f&&F.estimatedEnd.getTime()<=g).length,S=p.filter(F=>F.type==="queued"&&F.estimatedEnd.getTime()>=f&&F.estimatedEnd.getTime()<=g).length,N=y.useMemo(()=>{let F=0;for(const _ of p)F=Math.max(F,_.estimatedEnd.getTime());return F>0?new Date(F):null},[p]),C=()=>s(j0(new Date)),P=()=>{const F=new Date(i);F.setDate(F.getDate()-1),s(F)},T=()=>{const F=new Date(i);F.setDate(F.getDate()+1),s(F)},A=[{key:"all",label:a("queue.timeline.filterAll"),count:x+S},{key:"printing",label:a("queue.timeline.filterPrinting"),count:x},{key:"queued",label:a("queue.timeline.filterQueued"),count:S}];return r.jsxs("div",{children:[r.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5",children:[r.jsxs("div",{className:"flex items-center gap-2",children:[r.jsx(Te,{variant:"ghost",size:"sm",onClick:P,className:"p-1.5",children:r.jsx(Zo,{className:"w-4 h-4"})}),r.jsx("span",{className:"text-sm font-medium text-white min-w-[140px] text-center",children:Znt(i)}),r.jsx(Te,{variant:"ghost",size:"sm",onClick:T,className:"p-1.5",children:r.jsx($a,{className:"w-4 h-4"})}),!m&&r.jsx(Te,{variant:"ghost",size:"sm",onClick:C,className:"text-xs text-bambu-green",children:a("queue.timeline.day.today")})]}),N&&r.jsxs("span",{className:"text-xs text-bambu-gray flex items-center gap-1.5",children:[r.jsx(Zn,{className:"w-3.5 h-3.5"}),a("queue.timeline.allDoneBy",{time:N.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})})]})]}),r.jsx("div",{className:"flex gap-2 mb-5",children:A.map(F=>r.jsxs("button",{onClick:()=>d(F.key),className:`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${c===F.key?"bg-bambu-green text-white":"bg-bambu-dark-secondary border border-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,children:[F.label,F.count>0&&r.jsx("span",{className:`ml-1.5 text-xs ${c===F.key?"text-white/70":"text-bambu-gray"}`,children:F.count})]},F.key))}),b.length>0?r.jsx("div",{className:"space-y-6",children:b.map(([F,_])=>r.jsxs("div",{children:[r.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[r.jsx("span",{className:"text-xs font-medium text-bambu-gray w-14 shrink-0",children:trt(F)}),r.jsx("div",{className:"flex-1 h-px bg-bambu-dark-tertiary"})]}),r.jsx("div",{className:"space-y-2 sm:ml-[68px]",children:_.map(E=>r.jsx(nrt,{event:E,now:o,onItemClick:n,t:a},E.item.id))})]},F))}):r.jsxs("div",{className:"flex flex-col items-center justify-center py-16 text-bambu-gray",children:[r.jsx(sa,{className:"w-12 h-12 mb-3 opacity-30"}),r.jsx("p",{className:"text-sm",children:a("queue.timeline.noData")})]})]})}function art(t,e=!1){return e&&t>=1e3?`${(t/1e3).toFixed(1)}kg`:`${Math.round(t)}g`}function irt({status:t,waitingReason:e,printerState:n,t:a}){if(t==="pending"&&e)return r.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border text-purple-400 bg-purple-400/10 border-purple-400/20",children:[r.jsx(Zn,{className:"w-3.5 h-3.5"}),a("queue.status.waiting")]});if(t==="printing"&&n==="PAUSE")return r.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border text-yellow-400 bg-yellow-400/10 border-yellow-400/20",children:[r.jsx(ES,{className:"w-3.5 h-3.5"}),a("queue.status.paused")]});const i={pending:{icon:Zn,color:"text-status-warning bg-status-warning/10 border-status-warning/20",label:a("queue.status.pending")},printing:{icon:ps,color:"text-blue-400 bg-blue-400/10 border-blue-400/20",label:a("queue.status.printing")},completed:{icon:_r,color:"text-status-ok bg-status-ok/10 border-status-ok/20",label:a("queue.status.completed")},failed:{icon:ja,color:"text-status-error bg-status-error/10 border-status-error/20",label:a("queue.status.failed")},skipped:{icon:CT,color:"text-orange-400 bg-orange-400/10 border-orange-400/20",label:a("queue.status.skipped")},cancelled:{icon:qt,color:"text-gray-400 bg-gray-400/10 border-gray-400/20",label:a("queue.status.cancelled")}},{icon:s,color:o,label:l}=i[t];return r.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border ${o}`,children:[r.jsx(s,{className:"w-3.5 h-3.5"}),l]})}function srt({selectedCount:t,printers:e,onSave:n,onClose:a,isSaving:i,canControlPrinter:s,t:o}){const[l,c]=y.useState("unchanged"),[d,u]=y.useState("unchanged"),[m,p]=y.useState("unchanged"),[f,g]=y.useState("unchanged"),[w,b]=y.useState("unchanged"),[x,S]=y.useState("unchanged"),[N,C]=y.useState("unchanged"),[P,T]=y.useState("unchanged"),[A,F]=y.useState("unchanged"),[_,E]=y.useState("unchanged"),H=()=>{const U={};l!=="unchanged"&&(U.printer_id=l),d!=="unchanged"&&(U.manual_start=d),m!=="unchanged"&&(U.auto_off_after=m),f!=="unchanged"&&(U.require_previous_success=f),w!=="unchanged"&&(U.bed_levelling=w),x!=="unchanged"&&(U.flow_cali=x),N!=="unchanged"&&(U.vibration_cali=N),P!=="unchanged"&&(U.layer_inspect=P),A!=="unchanged"&&(U.timelapse=A),_!=="unchanged"&&(U.use_ams=_),n(U)},I=l!=="unchanged"||d!=="unchanged"||m!=="unchanged"||f!=="unchanged"||w!=="unchanged"||x!=="unchanged"||N!=="unchanged"||P!=="unchanged"||A!=="unchanged"||_!=="unchanged";return r.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",children:r.jsxs("div",{className:"bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-lg max-h-[90vh] overflow-y-auto",children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary",children:[r.jsx("h2",{className:"text-lg font-semibold text-white",children:o("queue.bulkEdit.title",{count:t})}),r.jsx("button",{onClick:a,className:"p-1 hover:bg-bambu-dark rounded",children:r.jsx(qt,{className:"w-5 h-5 text-bambu-gray"})})]}),r.jsxs("div",{className:"p-4 space-y-4",children:[r.jsx("p",{className:"text-sm text-bambu-gray",children:o("queue.bulkEdit.description")}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.printer")}),r.jsxs("select",{value:l===null?"null":l==="unchanged"?"unchanged":String(l),onChange:U=>{const R=U.target.value;c(R==="unchanged"?"unchanged":R==="null"?null:Number(R))},className:"w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",children:[r.jsx("option",{value:"unchanged",children:o("queue.bulkEdit.noChange")}),r.jsx("option",{value:"null",children:o("queue.filter.unassigned")}),e.map(U=>r.jsx("option",{value:U.id,children:U.name},U.id))]})]}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.queueOptions")}),r.jsxs("div",{className:"space-y-2",children:[r.jsx(om,{label:o("queue.bulkEdit.staged"),value:d,onChange:u,t:o}),r.jsx(om,{label:o("queue.bulkEdit.autoPowerOff"),value:m,onChange:p,disabled:!s,t:o}),r.jsx(om,{label:o("queue.bulkEdit.requirePrevious"),value:f,onChange:g,t:o})]})]}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.printOptions")}),r.jsxs("div",{className:"space-y-2",children:[r.jsx(om,{label:o("queue.bulkEdit.bedLevelling"),value:w,onChange:b,t:o}),r.jsx(om,{label:o("queue.bulkEdit.flowCalibration"),value:x,onChange:S,t:o}),r.jsx(om,{label:o("queue.bulkEdit.vibrationCalibration"),value:N,onChange:C,t:o}),r.jsx(om,{label:o("queue.bulkEdit.layerInspection"),value:P,onChange:T,t:o}),r.jsx(om,{label:o("queue.bulkEdit.timelapse"),value:A,onChange:F,t:o}),r.jsx(om,{label:o("queue.bulkEdit.useAms"),value:_,onChange:E,t:o})]})]})]}),r.jsxs("div",{className:"flex justify-end gap-3 p-4 border-t border-bambu-dark-tertiary",children:[r.jsx(Te,{variant:"secondary",onClick:a,children:o("common.cancel")}),r.jsx(Te,{onClick:H,disabled:!I||i,children:o(i?"common.saving":"queue.bulkEdit.applyChanges")})]})]})})}function om({label:t,value:e,onChange:n,disabled:a,t:i}){return r.jsxs("div",{className:`flex items-center justify-between py-1 ${a?"opacity-50":""}`,children:[r.jsx("span",{className:"text-sm text-bambu-gray",children:t}),r.jsxs("div",{className:"flex items-center gap-1 bg-bambu-dark rounded-lg p-0.5",children:[r.jsx("button",{onClick:()=>n("unchanged"),disabled:a,className:`px-2 py-1 text-xs rounded ${e==="unchanged"?"bg-bambu-dark-tertiary text-white":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:"—"}),r.jsx("button",{onClick:()=>n(!1),disabled:a,className:`px-2 py-1 text-xs rounded ${e===!1?"bg-red-500/20 text-red-400":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:i("common.off")}),r.jsx("button",{onClick:()=>n(!0),disabled:a,className:`px-2 py-1 text-xs rounded ${e===!0?"bg-bambu-green/20 text-bambu-green":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:i("common.on")})]})]})}function ZY({item:t,position:e,onEdit:n,onCancel:a,onRemove:i,onStop:s,onRequeue:o,onStart:l,timeFormat:c="system",isSelected:d=!1,onToggleSelect:u,hasPermission:m,canModify:p,printerState:f,t:g}){const{data:w}=Ge({queryKey:["printerStatus",t.printer_id],queryFn:()=>ae.getPrinterStatus(t.printer_id),refetchInterval:3e4,enabled:t.printer_id!=null&&f==="printing"}),b=!!t.library_file_id&&!t.archive_id,{data:x}=Ge({queryKey:["archive-plates",t.archive_id],queryFn:()=>ae.getArchivePlates(t.archive_id),enabled:!!t.archive_id&&!b}),{data:S}=Ge({queryKey:["library-file-plates",t.library_file_id],queryFn:()=>ae.getLibraryFilePlates(t.library_file_id),enabled:b&&!!t.library_file_id}),N=b?S:x,C=N?.plates??[],P=m("queue:reorder"),{attributes:T,listeners:A,setNodeRef:F,transform:_,transition:E,isDragging:H}=Nce({id:t.id,disabled:t.status!=="pending"||!P}),I={transform:Hy.Transform.toString(_),transition:E},U=t.status==="printing",R=t.status==="pending",Q=["completed","failed","skipped","cancelled"].includes(t.status),re=R&&u;return r.jsxs("div",{ref:F,style:I,className:` + ${l?"border-blue-500/30":"border-bambu-dark-tertiary"}`,onClick:()=>n(i),children:[r.jsx("div",{className:`w-1 self-stretch rounded-full shrink-0 ${l?"bg-blue-500":"bg-bambu-green/40"}`}),r.jsx("div",{className:"w-10 h-10 shrink-0 bg-bambu-dark rounded-lg overflow-hidden",children:d?r.jsx("img",{src:d,alt:"",className:"w-full h-full object-cover"}):r.jsx("div",{className:"w-full h-full flex items-center justify-center text-bambu-gray",children:r.jsx(sa,{className:"w-5 h-5"})})}),r.jsxs("div",{className:"flex-1 min-w-0",children:[r.jsx("p",{className:"text-sm text-white font-medium truncate",children:s}),r.jsxs("div",{className:"flex items-center gap-2 mt-0.5",children:[r.jsxs("span",{className:"flex items-center gap-1 text-xs text-bambu-gray",children:[r.jsx(fr,{className:"w-3 h-3"}),r.jsx("span",{className:"truncate max-w-[120px] sm:max-w-none",children:o})]}),i.print_time_seconds&&r.jsx("span",{className:"hidden sm:inline text-xs text-bambu-gray",children:ls(i.print_time_seconds)})]}),l&&t.progress!=null&&r.jsxs("div",{className:"flex items-center gap-2 mt-1.5",children:[r.jsx("div",{className:"flex-1 bg-bambu-dark-tertiary rounded-full h-1.5",children:r.jsx("div",{className:"bg-blue-500 h-1.5 rounded-full transition-all",style:{width:`${t.progress}%`}})}),r.jsxs("span",{className:"text-xs text-blue-400 shrink-0",children:[Math.round(t.progress),"%"]})]})]}),r.jsxs("div",{className:"text-right shrink-0",children:[r.jsx("p",{className:"text-sm text-white font-medium",children:Jnt(t.estimatedEnd)}),r.jsx("p",{className:`text-xs mt-0.5 ${l?"text-blue-400":"text-bambu-gray"}`,children:ert(c,a)})]})]})}function rrt({queueItems:t,printerStatuses:e,onItemClick:n,t:a}){const[i,s]=y.useState(()=>j0(new Date)),[o,l]=y.useState(()=>new Date),[c,d]=y.useState("all");y.useEffect(()=>{const F=setInterval(()=>l(new Date),6e4);return()=>clearInterval(F)},[]);const u=o.getTime(),m=j0(new Date).getTime()===j0(i).getTime(),p=y.useMemo(()=>{const F=[],_=new Map;for(const E of t)if(E.status==="printing"){const H=E.printer_id!=null?e[E.printer_id]:void 0,I=Yn(E.started_at)||new Date;let U;if(H?.remaining_time!=null&&H.remaining_time>0)U=new Date(u+H.remaining_time*60*1e3);else if(E.print_time_seconds){const R=H?.progress||0,Q=Math.max(0,1-R/100);U=new Date(u+E.print_time_seconds*Q*1e3)}else U=new Date(u+36e5);F.push({item:E,estimatedStart:I,estimatedEnd:U,progress:H?.progress??void 0,type:"printing"})}else if(E.status==="pending"){const H=E.printer_id;_.has(H)||_.set(H,[]),_.get(H).push(E)}for(const[E,H]of _){H.sort((U,R)=>U.position-R.position);let I=u;for(const U of F)U.item.printer_id===E&&U.type==="printing"&&(I=Math.max(I,U.estimatedEnd.getTime()));for(const U of H){const R=Yn(U.scheduled_time);if(R){const ce=Date.now()+15552e6;R.getTime()<=ce&&(I=Math.max(I,R.getTime()))}const Q=(U.print_time_seconds||3600)*1e3,re=new Date(I),J=new Date(I+Q);F.push({item:U,estimatedStart:re,estimatedEnd:J,type:"queued"}),I=J.getTime()}}return F.sort((E,H)=>E.estimatedEnd.getTime()-H.estimatedEnd.getTime()),F},[t,e,u]),f=j0(i).getTime(),g=f+1440*60*1e3-1,w=y.useMemo(()=>p.filter(F=>{const _=F.estimatedEnd.getTime();return _g?!1:c==="printing"?F.type==="printing":c==="queued"?F.type==="queued":!0}),[p,f,g,c]),b=y.useMemo(()=>{const F=new Map;for(const _ of w){const E=_.estimatedEnd.getHours();F.has(E)||F.set(E,[]),F.get(E).push(_)}return Array.from(F.entries()).sort(([_],[E])=>_-E)},[w]),x=p.filter(F=>F.type==="printing"&&F.estimatedEnd.getTime()>=f&&F.estimatedEnd.getTime()<=g).length,S=p.filter(F=>F.type==="queued"&&F.estimatedEnd.getTime()>=f&&F.estimatedEnd.getTime()<=g).length,N=y.useMemo(()=>{let F=0;for(const _ of p)F=Math.max(F,_.estimatedEnd.getTime());return F>0?new Date(F):null},[p]),C=()=>s(j0(new Date)),P=()=>{const F=new Date(i);F.setDate(F.getDate()-1),s(F)},T=()=>{const F=new Date(i);F.setDate(F.getDate()+1),s(F)},A=[{key:"all",label:a("queue.timeline.filterAll"),count:x+S},{key:"printing",label:a("queue.timeline.filterPrinting"),count:x},{key:"queued",label:a("queue.timeline.filterQueued"),count:S}];return r.jsxs("div",{children:[r.jsxs("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-5",children:[r.jsxs("div",{className:"flex items-center gap-2",children:[r.jsx(Te,{variant:"ghost",size:"sm",onClick:P,className:"p-1.5",children:r.jsx(Zo,{className:"w-4 h-4"})}),r.jsx("span",{className:"text-sm font-medium text-white min-w-[140px] text-center",children:Znt(i)}),r.jsx(Te,{variant:"ghost",size:"sm",onClick:T,className:"p-1.5",children:r.jsx($a,{className:"w-4 h-4"})}),!m&&r.jsx(Te,{variant:"ghost",size:"sm",onClick:C,className:"text-xs text-bambu-green",children:a("queue.timeline.day.today")})]}),N&&r.jsxs("span",{className:"text-xs text-bambu-gray flex items-center gap-1.5",children:[r.jsx(Zn,{className:"w-3.5 h-3.5"}),a("queue.timeline.allDoneBy",{time:N.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})})]})]}),r.jsx("div",{className:"flex gap-2 mb-5",children:A.map(F=>r.jsxs("button",{onClick:()=>d(F.key),className:`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${c===F.key?"bg-bambu-green text-white":"bg-bambu-dark-secondary border border-bambu-dark-tertiary text-bambu-gray hover:text-white"}`,children:[F.label,F.count>0&&r.jsx("span",{className:`ml-1.5 text-xs ${c===F.key?"text-white/70":"text-bambu-gray"}`,children:F.count})]},F.key))}),b.length>0?r.jsx("div",{className:"space-y-6",children:b.map(([F,_])=>r.jsxs("div",{children:[r.jsxs("div",{className:"flex items-center gap-3 mb-3",children:[r.jsx("span",{className:"text-xs font-medium text-bambu-gray w-14 shrink-0",children:trt(F)}),r.jsx("div",{className:"flex-1 h-px bg-bambu-dark-tertiary"})]}),r.jsx("div",{className:"space-y-2 sm:ml-[68px]",children:_.map(E=>r.jsx(nrt,{event:E,now:o,onItemClick:n,t:a},E.item.id))})]},F))}):r.jsxs("div",{className:"flex flex-col items-center justify-center py-16 text-bambu-gray",children:[r.jsx(sa,{className:"w-12 h-12 mb-3 opacity-30"}),r.jsx("p",{className:"text-sm",children:a("queue.timeline.noData")})]})]})}function art(t,e=!1){return e&&t>=1e3?`${(t/1e3).toFixed(1)}kg`:`${Math.round(t)}g`}function irt({status:t,waitingReason:e,printerState:n,t:a}){if(t==="pending"&&e)return r.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border text-purple-400 bg-purple-400/10 border-purple-400/20",children:[r.jsx(Zn,{className:"w-3.5 h-3.5"}),a("queue.status.waiting")]});if(t==="printing"&&n==="PAUSE")return r.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border text-yellow-400 bg-yellow-400/10 border-yellow-400/20",children:[r.jsx(ES,{className:"w-3.5 h-3.5"}),a("queue.status.paused")]});const i={pending:{icon:Zn,color:"text-status-warning bg-status-warning/10 border-status-warning/20",label:a("queue.status.pending")},printing:{icon:ps,color:"text-blue-400 bg-blue-400/10 border-blue-400/20",label:a("queue.status.printing")},completed:{icon:_r,color:"text-status-ok bg-status-ok/10 border-status-ok/20",label:a("queue.status.completed")},failed:{icon:ja,color:"text-status-error bg-status-error/10 border-status-error/20",label:a("queue.status.failed")},skipped:{icon:CT,color:"text-orange-400 bg-orange-400/10 border-orange-400/20",label:a("queue.status.skipped")},cancelled:{icon:qt,color:"text-gray-400 bg-gray-400/10 border-gray-400/20",label:a("queue.status.cancelled")}},{icon:s,color:o,label:l}=i[t];return r.jsxs("span",{className:`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium border ${o}`,children:[r.jsx(s,{className:"w-3.5 h-3.5"}),l]})}function srt({selectedCount:t,printers:e,onSave:n,onClose:a,isSaving:i,canControlPrinter:s,t:o}){const[l,c]=y.useState("unchanged"),[d,u]=y.useState("unchanged"),[m,p]=y.useState("unchanged"),[f,g]=y.useState("unchanged"),[w,b]=y.useState("unchanged"),[x,S]=y.useState("unchanged"),[N,C]=y.useState("unchanged"),[P,T]=y.useState("unchanged"),[A,F]=y.useState("unchanged"),[_,E]=y.useState("unchanged"),H=()=>{const U={};l!=="unchanged"&&(U.printer_id=l),d!=="unchanged"&&(U.manual_start=d),m!=="unchanged"&&(U.auto_off_after=m),f!=="unchanged"&&(U.require_previous_success=f),w!=="unchanged"&&(U.bed_levelling=w),x!=="unchanged"&&(U.flow_cali=x),N!=="unchanged"&&(U.vibration_cali=N),P!=="unchanged"&&(U.layer_inspect=P),A!=="unchanged"&&(U.timelapse=A),_!=="unchanged"&&(U.use_ams=_),n(U)},I=l!=="unchanged"||d!=="unchanged"||m!=="unchanged"||f!=="unchanged"||w!=="unchanged"||x!=="unchanged"||N!=="unchanged"||P!=="unchanged"||A!=="unchanged"||_!=="unchanged";return r.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm",children:r.jsxs("div",{className:"bg-bambu-dark-secondary rounded-xl border border-bambu-dark-tertiary w-full max-w-lg max-h-[90vh] overflow-y-auto",children:[r.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-bambu-dark-tertiary",children:[r.jsx("h2",{className:"text-lg font-semibold text-white",children:o("queue.bulkEdit.title",{count:t})}),r.jsx("button",{onClick:a,className:"p-1 hover:bg-bambu-dark rounded",children:r.jsx(qt,{className:"w-5 h-5 text-bambu-gray"})})]}),r.jsxs("div",{className:"p-4 space-y-4",children:[r.jsx("p",{className:"text-sm text-bambu-gray",children:o("queue.bulkEdit.description")}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.printer")}),r.jsxs("select",{value:l===null?"null":l==="unchanged"?"unchanged":String(l),onChange:U=>{const R=U.target.value;c(R==="unchanged"?"unchanged":R==="null"?null:Number(R))},className:"w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none",children:[r.jsx("option",{value:"unchanged",children:o("queue.bulkEdit.noChange")}),r.jsx("option",{value:"null",children:o("queue.filter.unassigned")}),e.map(U=>r.jsx("option",{value:U.id,children:U.name},U.id))]})]}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.queueOptions")}),r.jsxs("div",{className:"space-y-2",children:[r.jsx(om,{label:o("queue.bulkEdit.staged"),value:d,onChange:u,t:o}),r.jsx(om,{label:o("queue.bulkEdit.autoPowerOff"),value:m,onChange:p,disabled:!s,t:o}),r.jsx(om,{label:o("queue.bulkEdit.requirePrevious"),value:f,onChange:g,t:o})]})]}),r.jsxs("div",{children:[r.jsx("label",{className:"block text-sm font-medium text-white mb-2",children:o("queue.bulkEdit.printOptions")}),r.jsxs("div",{className:"space-y-2",children:[r.jsx(om,{label:o("queue.bulkEdit.bedLevelling"),value:w,onChange:b,t:o}),r.jsx(om,{label:o("queue.bulkEdit.flowCalibration"),value:x,onChange:S,t:o}),r.jsx(om,{label:o("queue.bulkEdit.vibrationCalibration"),value:N,onChange:C,t:o}),r.jsx(om,{label:o("queue.bulkEdit.layerInspection"),value:P,onChange:T,t:o}),r.jsx(om,{label:o("queue.bulkEdit.timelapse"),value:A,onChange:F,t:o}),r.jsx(om,{label:o("queue.bulkEdit.useAms"),value:_,onChange:E,t:o})]})]})]}),r.jsxs("div",{className:"flex justify-end gap-3 p-4 border-t border-bambu-dark-tertiary",children:[r.jsx(Te,{variant:"secondary",onClick:a,children:o("common.cancel")}),r.jsx(Te,{onClick:H,disabled:!I||i,children:o(i?"common.saving":"queue.bulkEdit.applyChanges")})]})]})})}function om({label:t,value:e,onChange:n,disabled:a,t:i}){return r.jsxs("div",{className:`flex items-center justify-between py-1 ${a?"opacity-50":""}`,children:[r.jsx("span",{className:"text-sm text-bambu-gray",children:t}),r.jsxs("div",{className:"flex items-center gap-1 bg-bambu-dark rounded-lg p-0.5",children:[r.jsx("button",{onClick:()=>n("unchanged"),disabled:a,className:`px-2 py-1 text-xs rounded ${e==="unchanged"?"bg-bambu-dark-tertiary text-white":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:"—"}),r.jsx("button",{onClick:()=>n(!1),disabled:a,className:`px-2 py-1 text-xs rounded ${e===!1?"bg-red-500/20 text-red-400":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:i("common.off")}),r.jsx("button",{onClick:()=>n(!0),disabled:a,className:`px-2 py-1 text-xs rounded ${e===!0?"bg-bambu-green/20 text-bambu-green":"text-bambu-gray hover:text-white"} disabled:cursor-not-allowed`,children:i("common.on")})]})]})}function ZY({item:t,position:e,onEdit:n,onCancel:a,onRemove:i,onStop:s,onRequeue:o,onStart:l,timeFormat:c="system",isSelected:d=!1,onToggleSelect:u,hasPermission:m,canModify:p,printerState:f,t:g}){const{data:w}=Ge({queryKey:["printerStatus",t.printer_id],queryFn:()=>ae.getPrinterStatus(t.printer_id),refetchInterval:3e4,enabled:t.printer_id!=null&&f==="printing"}),b=!!t.library_file_id&&!t.archive_id,{data:x}=Ge({queryKey:["archive-plates",t.archive_id],queryFn:()=>ae.getArchivePlates(t.archive_id),enabled:!!t.archive_id&&!b&&!t.archive_deleted}),{data:S}=Ge({queryKey:["library-file-plates",t.library_file_id],queryFn:()=>ae.getLibraryFilePlates(t.library_file_id),enabled:b&&!!t.library_file_id}),N=b?S:x,C=N?.plates??[],P=m("queue:reorder"),{attributes:T,listeners:A,setNodeRef:F,transform:_,transition:E,isDragging:H}=Nce({id:t.id,disabled:t.status!=="pending"||!P}),I={transform:Hy.Transform.toString(_),transition:E},U=t.status==="printing",R=t.status==="pending",Q=["completed","failed","skipped","cancelled"].includes(t.status),re=R&&u;return r.jsxs("div",{ref:F,style:I,className:` group relative bg-bambu-dark-secondary rounded-xl border transition-all duration-200 border-l-[3px] ${U?"border-l-blue-500":R?"border-l-yellow-500":t.status==="completed"?"border-l-emerald-500":t.status==="failed"?"border-l-red-500":"border-l-gray-500"} ${H?"opacity-50 scale-[1.02] shadow-xl z-50":""} diff --git a/static/index.html b/static/index.html index 680506842..237d97b9a 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@ - +